1use move_binary_format::errors::PartialVMResult;
5use move_core_types::{
6 gas_algebra::{AbstractMemorySize, InternalGas, NumArgs, NumBytes},
7 language_storage::ModuleId,
8};
9use move_vm_types::{
10 gas::{GasMeter, SimpleInstruction},
11 loaded_data::runtime_types::Type,
12 views::{TypeView, ValueView},
13};
14use sui_types::gas_model::{
15 gas_predicates::{native_function_threshold_exceeded, use_legacy_abstract_size},
16 tables::{GasStatus, REFERENCE_SIZE, STRUCT_SIZE, VEC_SIZE},
17};
18
19pub struct SuiGasMeter<'g>(pub &'g mut GasStatus);
20
21fn get_simple_instruction_stack_change(
23 instr: SimpleInstruction,
24) -> (u64, u64, AbstractMemorySize, AbstractMemorySize) {
25 use SimpleInstruction::*;
26
27 match instr {
28 Nop | Ret => (0, 0, 0.into(), 0.into()),
30 BrTrue | BrFalse => (1, 0, Type::Bool.size(), 0.into()),
31 Branch => (0, 0, 0.into(), 0.into()),
32 LdU8 => (0, 1, 0.into(), Type::U8.size()),
33 LdU16 => (0, 1, 0.into(), Type::U16.size()),
34 LdU32 => (0, 1, 0.into(), Type::U32.size()),
35 LdU64 => (0, 1, 0.into(), Type::U64.size()),
36 LdU128 => (0, 1, 0.into(), Type::U128.size()),
37 LdU256 => (0, 1, 0.into(), Type::U256.size()),
38 LdTrue | LdFalse => (0, 1, 0.into(), Type::Bool.size()),
39 FreezeRef => (1, 1, REFERENCE_SIZE, REFERENCE_SIZE),
40 ImmBorrowLoc | MutBorrowLoc => (0, 1, 0.into(), REFERENCE_SIZE),
41 ImmBorrowField | MutBorrowField | ImmBorrowFieldGeneric | MutBorrowFieldGeneric => {
42 (1, 1, REFERENCE_SIZE, REFERENCE_SIZE)
43 }
44 CastU8 => (1, 1, Type::U8.size(), Type::U8.size()),
47 CastU16 => (1, 1, Type::U8.size(), Type::U16.size()),
48 CastU32 => (1, 1, Type::U8.size(), Type::U32.size()),
49 CastU64 => (1, 1, Type::U8.size(), Type::U64.size()),
50 CastU128 => (1, 1, Type::U8.size(), Type::U128.size()),
51 CastU256 => (1, 1, Type::U8.size(), Type::U256.size()),
52 Add | Sub | Mul | Mod | Div => (2, 1, Type::U8.size() + Type::U8.size(), Type::U256.size()),
55 BitOr | BitAnd | Xor => (2, 1, Type::U8.size() + Type::U8.size(), Type::U256.size()),
56 Shl | Shr => (2, 1, Type::U8.size() + Type::U8.size(), Type::U256.size()),
57 Or | And => (
58 2,
59 1,
60 Type::Bool.size() + Type::Bool.size(),
61 Type::Bool.size(),
62 ),
63 Lt | Gt | Le | Ge => (2, 1, Type::U8.size() + Type::U8.size(), Type::Bool.size()),
64 Not => (1, 1, Type::Bool.size(), Type::Bool.size()),
65 Abort => (1, 0, Type::U64.size(), 0.into()),
66 }
67}
68
69impl GasMeter for SuiGasMeter<'_> {
70 fn charge_simple_instr(&mut self, instr: SimpleInstruction) -> PartialVMResult<()> {
72 let (pops, pushes, pop_size, push_size) = get_simple_instruction_stack_change(instr);
73 self.0
74 .charge(1, pushes, pops, push_size.into(), pop_size.into())
75 }
76
77 fn charge_pop(&mut self, popped_val: impl ValueView) -> PartialVMResult<()> {
78 self.0
79 .charge(1, 0, 1, 0, abstract_memory_size(self.0, popped_val).into())
80 }
81
82 fn charge_native_function(
83 &mut self,
84 amount: InternalGas,
85 ret_vals: Option<impl ExactSizeIterator<Item = impl ValueView>>,
86 ) -> PartialVMResult<()> {
87 let pushes = ret_vals
90 .as_ref()
91 .map(|ret_vals| ret_vals.len())
92 .unwrap_or(0) as u64;
93 let size_increase = ret_vals
95 .map(|ret_vals| {
96 ret_vals.fold(AbstractMemorySize::zero(), |acc, elem| {
97 acc + abstract_memory_size(self.0, elem)
98 })
99 })
100 .unwrap_or_else(AbstractMemorySize::zero);
101 self.0.record_native_call();
102 if native_function_threshold_exceeded(self.0.gas_model_version, self.0.num_native_calls) {
103 self.0
110 .charge(amount.into(), pushes, 0, size_increase.into(), 0)
111 } else {
112 self.0.charge(0, pushes, 0, size_increase.into(), 0)?;
116 self.0.deduct_gas(amount)
118 }
119 }
120
121 fn charge_native_function_before_execution(
122 &mut self,
123 _ty_args: impl ExactSizeIterator<Item = impl TypeView>,
124 args: impl ExactSizeIterator<Item = impl ValueView>,
125 ) -> PartialVMResult<()> {
126 let pops = args.len() as u64;
129 let stack_reduction_size = args.fold(AbstractMemorySize::new(pops), |acc, elem| {
131 acc + abstract_memory_size(self.0, elem)
132 });
133 self.0.charge(1, 0, pops, 0, stack_reduction_size.into())
137 }
138
139 fn charge_call(
140 &mut self,
141 _module_id: &ModuleId,
142 _func_name: &str,
143 args: impl ExactSizeIterator<Item = impl ValueView>,
144 _num_locals: NumArgs,
145 ) -> PartialVMResult<()> {
146 let pops = args.len() as u64;
148 let stack_reduction_size = args.fold(AbstractMemorySize::new(0), |acc, elem| {
151 acc + abstract_memory_size(self.0, elem)
152 });
153 self.0.charge(1, 0, pops, 0, stack_reduction_size.into())
154 }
155
156 fn charge_call_generic(
157 &mut self,
158 _module_id: &ModuleId,
159 _func_name: &str,
160 _ty_args: impl ExactSizeIterator<Item = impl TypeView>,
161 args: impl ExactSizeIterator<Item = impl ValueView>,
162 _num_locals: NumArgs,
163 ) -> PartialVMResult<()> {
164 let pops = args.len() as u64;
166 let stack_reduction_size = args.fold(AbstractMemorySize::new(0), |acc, elem| {
168 acc + abstract_memory_size(self.0, elem)
169 });
170 self.0.charge(1, 0, pops, 0, stack_reduction_size.into())
173 }
174
175 fn charge_ld_const(&mut self, size: NumBytes) -> PartialVMResult<()> {
176 self.0.charge(1, 1, 0, u64::from(size), 0)
178 }
179
180 fn charge_ld_const_after_deserialization(
181 &mut self,
182 _val: impl ValueView,
183 ) -> PartialVMResult<()> {
184 Ok(())
186 }
187
188 fn charge_copy_loc(&mut self, val: impl ValueView) -> PartialVMResult<()> {
189 self.0
191 .charge(1, 1, 0, abstract_memory_size(self.0, val).into(), 0)
192 }
193
194 fn charge_move_loc(&mut self, val: impl ValueView) -> PartialVMResult<()> {
195 self.0
199 .charge(1, 1, 0, abstract_memory_size(self.0, val).into(), 0)
200 }
201
202 fn charge_store_loc(&mut self, val: impl ValueView) -> PartialVMResult<()> {
203 self.0
207 .charge(1, 0, 1, 0, abstract_memory_size(self.0, val).into())
208 }
209
210 fn charge_pack(
211 &mut self,
212 _is_generic: bool,
213 args: impl ExactSizeIterator<Item = impl ValueView>,
214 ) -> PartialVMResult<()> {
215 let num_fields = args.len() as u64;
217 self.0.charge(1, 1, num_fields, STRUCT_SIZE.into(), 0)
220 }
221
222 fn charge_unpack(
223 &mut self,
224 _is_generic: bool,
225 args: impl ExactSizeIterator<Item = impl ValueView>,
226 ) -> PartialVMResult<()> {
227 let num_fields = args.len() as u64;
229 self.0.charge(1, num_fields, 1, 0, STRUCT_SIZE.into())
230 }
231
232 fn charge_variant_switch(&mut self, val: impl ValueView) -> PartialVMResult<()> {
233 self.0
235 .charge(1, 0, 1, 0, abstract_memory_size(self.0, val).into())
236 }
237
238 fn charge_read_ref(&mut self, ref_val: impl ValueView) -> PartialVMResult<()> {
239 self.0.charge(
243 1,
244 1,
245 1,
246 abstract_memory_size(self.0, ref_val).into(),
247 REFERENCE_SIZE.into(),
248 )
249 }
250
251 fn charge_write_ref(
252 &mut self,
253 new_val: impl ValueView,
254 old_val: impl ValueView,
255 ) -> PartialVMResult<()> {
256 self.0.charge(
260 1,
261 1,
262 2,
263 abstract_memory_size(self.0, new_val).into(),
264 abstract_memory_size(self.0, old_val).into(),
265 )
266 }
267
268 fn charge_eq(&mut self, lhs: impl ValueView, rhs: impl ValueView) -> PartialVMResult<()> {
269 let size_reduction = abstract_memory_size(self.0, lhs) + abstract_memory_size(self.0, rhs);
270 self.0.charge(
271 1,
272 1,
273 2,
274 (Type::Bool.size() + size_reduction).into(),
275 size_reduction.into(),
276 )
277 }
278
279 fn charge_neq(&mut self, lhs: impl ValueView, rhs: impl ValueView) -> PartialVMResult<()> {
280 let size_reduction = abstract_memory_size(self.0, lhs) + abstract_memory_size(self.0, rhs);
281 self.0
282 .charge(1, 1, 2, Type::Bool.size().into(), size_reduction.into())
283 }
284
285 fn charge_vec_pack<'a>(
286 &mut self,
287 _ty: impl TypeView + 'a,
288 args: impl ExactSizeIterator<Item = impl ValueView>,
289 ) -> PartialVMResult<()> {
290 let num_args = args.len() as u64;
292 self.0.charge(1, 1, num_args, VEC_SIZE.into(), 0)
295 }
296
297 fn charge_vec_len(&mut self, _ty: impl TypeView) -> PartialVMResult<()> {
298 self.0
299 .charge(1, 1, 1, Type::U64.size().into(), REFERENCE_SIZE.into())
300 }
301
302 fn charge_vec_borrow(
303 &mut self,
304 _is_mut: bool,
305 _ty: impl TypeView,
306 _is_success: bool,
307 ) -> PartialVMResult<()> {
308 self.0.charge(
309 1,
310 1,
311 2,
312 REFERENCE_SIZE.into(),
313 (REFERENCE_SIZE + Type::U64.size()).into(),
314 )
315 }
316
317 fn charge_vec_push_back(
318 &mut self,
319 _ty: impl TypeView,
320 _val: impl ValueView,
321 ) -> PartialVMResult<()> {
322 self.0.charge(1, 0, 2, 0, REFERENCE_SIZE.into())
324 }
325
326 fn charge_vec_pop_back(
327 &mut self,
328 _ty: impl TypeView,
329 _val: Option<impl ValueView>,
330 ) -> PartialVMResult<()> {
331 self.0.charge(1, 1, 1, 0, REFERENCE_SIZE.into())
332 }
333
334 fn charge_vec_unpack(
335 &mut self,
336 _ty: impl TypeView,
337 expect_num_elements: NumArgs,
338 _elems: impl ExactSizeIterator<Item = impl ValueView>,
339 ) -> PartialVMResult<()> {
340 let pushes = u64::from(expect_num_elements);
342 self.0.charge(1, pushes, 1, 0, VEC_SIZE.into())
344 }
345
346 fn charge_vec_swap(&mut self, _ty: impl TypeView) -> PartialVMResult<()> {
347 let size_decrease = REFERENCE_SIZE + Type::U64.size() + Type::U64.size();
348 self.0.charge(1, 1, 1, 0, size_decrease.into())
349 }
350
351 fn charge_drop_frame(
352 &mut self,
353 _locals: impl Iterator<Item = impl ValueView>,
354 ) -> PartialVMResult<()> {
355 Ok(())
356 }
357
358 fn remaining_gas(&self) -> InternalGas {
359 if !self.0.charge {
360 return InternalGas::new(u64::MAX);
361 }
362 self.0.gas_left
363 }
364}
365
366pub fn abstract_memory_size(status: &GasStatus, val: impl ValueView) -> AbstractMemorySize {
367 if use_legacy_abstract_size(status.gas_model_version) {
368 val.legacy_abstract_memory_size()
369 } else {
370 val.abstract_memory_size()
371 }
372}