Skip to content
This repository was archived by the owner on May 4, 2024. It is now read-only.

Commit 2997843

Browse files
authored
Charge native inline within budget (#940)
* charge native inline with budget
1 parent 2ee1733 commit 2997843

File tree

15 files changed

+369
-97
lines changed

15 files changed

+369
-97
lines changed

language/extensions/move-table-extension/src/lib.rs

Lines changed: 99 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ use move_core_types::{
1818
vm_status::StatusCode,
1919
};
2020
use move_vm_runtime::{
21-
native_functions,
21+
native_charge_gas_early_exit, native_functions,
2222
native_functions::{NativeContext, NativeFunction, NativeFunctionTable},
23+
native_gas_total_cost,
2324
};
2425
use move_vm_types::{
2526
loaded_data::runtime_types::Type,
@@ -350,6 +351,9 @@ fn native_new_table_handle(
350351
assert_eq!(ty_args.len(), 2);
351352
assert!(args.is_empty());
352353

354+
let mut gas_left = context.gas_budget();
355+
native_charge_gas_early_exit!(context, gas_left, gas_params.base);
356+
353357
let table_context = context.extensions().get::<NativeTableContext>();
354358
let mut table_data = table_context.table_data.borrow_mut();
355359

@@ -371,7 +375,7 @@ fn native_new_table_handle(
371375
.is_none());
372376

373377
Ok(NativeResult::ok(
374-
gas_params.base,
378+
native_gas_total_cost!(context, gas_left),
375379
smallvec![Value::address(handle)],
376380
))
377381
}
@@ -399,6 +403,8 @@ fn native_add_box(
399403
) -> PartialVMResult<NativeResult> {
400404
assert_eq!(ty_args.len(), 3);
401405
assert_eq!(args.len(), 3);
406+
let mut gas_left = context.gas_budget();
407+
native_charge_gas_early_exit!(context, gas_left, gas_params.base);
402408

403409
let table_context = context.extensions().get::<NativeTableContext>();
404410
let mut table_data = table_context.table_data.borrow_mut();
@@ -407,19 +413,31 @@ fn native_add_box(
407413
let key = args.pop_back().unwrap();
408414
let handle = get_table_handle(&pop_arg!(args, StructRef))?;
409415

410-
let mut cost = gas_params.base;
411-
412416
let table = table_data.get_or_create_table(context, handle, &ty_args[0], &ty_args[2])?;
413417

414418
let key_bytes = serialize(&table.key_layout, &key)?;
415-
cost += gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64);
419+
native_charge_gas_early_exit!(
420+
context,
421+
gas_left,
422+
gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64)
423+
);
416424

417425
let (gv, loaded) = table.get_or_create_global_value(table_context, key_bytes)?;
418-
cost += common_gas_params.calculate_load_cost(loaded);
426+
native_charge_gas_early_exit!(
427+
context,
428+
gas_left,
429+
common_gas_params.calculate_load_cost(loaded)
430+
);
419431

420432
match gv.move_to(val) {
421-
Ok(_) => Ok(NativeResult::ok(cost, smallvec![])),
422-
Err(_) => Ok(NativeResult::err(cost, ALREADY_EXISTS)),
433+
Ok(_) => Ok(NativeResult::ok(
434+
native_gas_total_cost!(context, gas_left),
435+
smallvec![],
436+
)),
437+
Err(_) => Ok(NativeResult::err(
438+
native_gas_total_cost!(context, gas_left),
439+
ALREADY_EXISTS,
440+
)),
423441
}
424442
}
425443

@@ -450,6 +468,9 @@ fn native_borrow_box(
450468
assert_eq!(ty_args.len(), 3);
451469
assert_eq!(args.len(), 2);
452470

471+
let mut gas_left = context.gas_budget();
472+
native_charge_gas_early_exit!(context, gas_left, gas_params.base);
473+
453474
let table_context = context.extensions().get::<NativeTableContext>();
454475
let mut table_data = table_context.table_data.borrow_mut();
455476

@@ -458,17 +479,29 @@ fn native_borrow_box(
458479

459480
let table = table_data.get_or_create_table(context, handle, &ty_args[0], &ty_args[2])?;
460481

461-
let mut cost = gas_params.base;
462-
463482
let key_bytes = serialize(&table.key_layout, &key)?;
464-
cost += gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64);
483+
native_charge_gas_early_exit!(
484+
context,
485+
gas_left,
486+
gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64)
487+
);
465488

466489
let (gv, loaded) = table.get_or_create_global_value(table_context, key_bytes)?;
467-
cost += common_gas_params.calculate_load_cost(loaded);
490+
native_charge_gas_early_exit!(
491+
context,
492+
gas_left,
493+
common_gas_params.calculate_load_cost(loaded)
494+
);
468495

469496
match gv.borrow_global() {
470-
Ok(ref_val) => Ok(NativeResult::ok(cost, smallvec![ref_val])),
471-
Err(_) => Ok(NativeResult::err(cost, NOT_FOUND)),
497+
Ok(ref_val) => Ok(NativeResult::ok(
498+
native_gas_total_cost!(context, gas_left),
499+
smallvec![ref_val],
500+
)),
501+
Err(_) => Ok(NativeResult::err(
502+
native_gas_total_cost!(context, gas_left),
503+
NOT_FOUND,
504+
)),
472505
}
473506
}
474507

@@ -499,6 +532,9 @@ fn native_contains_box(
499532
assert_eq!(ty_args.len(), 3);
500533
assert_eq!(args.len(), 2);
501534

535+
let mut gas_left = context.gas_budget();
536+
native_charge_gas_early_exit!(context, gas_left, gas_params.base);
537+
502538
let table_context = context.extensions().get::<NativeTableContext>();
503539
let mut table_data = table_context.table_data.borrow_mut();
504540

@@ -507,17 +543,26 @@ fn native_contains_box(
507543

508544
let table = table_data.get_or_create_table(context, handle, &ty_args[0], &ty_args[2])?;
509545

510-
let mut cost = gas_params.base;
511-
512546
let key_bytes = serialize(&table.key_layout, &key)?;
513-
cost += gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64);
547+
native_charge_gas_early_exit!(
548+
context,
549+
gas_left,
550+
gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64)
551+
);
514552

515553
let (gv, loaded) = table.get_or_create_global_value(table_context, key_bytes)?;
516-
cost += common_gas_params.calculate_load_cost(loaded);
554+
native_charge_gas_early_exit!(
555+
context,
556+
gas_left,
557+
common_gas_params.calculate_load_cost(loaded)
558+
);
517559

518560
let exists = Value::bool(gv.exists()?);
519561

520-
Ok(NativeResult::ok(cost, smallvec![exists]))
562+
Ok(NativeResult::ok(
563+
native_gas_total_cost!(context, gas_left),
564+
smallvec![exists],
565+
))
521566
}
522567

523568
pub fn make_native_contains_box(
@@ -547,6 +592,9 @@ fn native_remove_box(
547592
assert_eq!(ty_args.len(), 3);
548593
assert_eq!(args.len(), 2);
549594

595+
let mut gas_left = context.gas_budget();
596+
native_charge_gas_early_exit!(context, gas_left, gas_params.base);
597+
550598
let table_context = context.extensions().get::<NativeTableContext>();
551599
let mut table_data = table_context.table_data.borrow_mut();
552600

@@ -555,17 +603,29 @@ fn native_remove_box(
555603

556604
let table = table_data.get_or_create_table(context, handle, &ty_args[0], &ty_args[2])?;
557605

558-
let mut cost = gas_params.base;
559-
560606
let key_bytes = serialize(&table.key_layout, &key)?;
561-
cost += gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64);
607+
native_charge_gas_early_exit!(
608+
context,
609+
gas_left,
610+
gas_params.per_byte_serialized * NumBytes::new(key_bytes.len() as u64)
611+
);
562612

563613
let (gv, loaded) = table.get_or_create_global_value(table_context, key_bytes)?;
564-
cost += common_gas_params.calculate_load_cost(loaded);
614+
native_charge_gas_early_exit!(
615+
context,
616+
gas_left,
617+
common_gas_params.calculate_load_cost(loaded)
618+
);
565619

566620
match gv.move_from() {
567-
Ok(val) => Ok(NativeResult::ok(cost, smallvec![val])),
568-
Err(_) => Ok(NativeResult::err(cost, NOT_FOUND)),
621+
Ok(val) => Ok(NativeResult::ok(
622+
native_gas_total_cost!(context, gas_left),
623+
smallvec![val],
624+
)),
625+
Err(_) => Ok(NativeResult::err(
626+
native_gas_total_cost!(context, gas_left),
627+
NOT_FOUND,
628+
)),
569629
}
570630
}
571631

@@ -594,6 +654,8 @@ fn native_destroy_empty_box(
594654
assert_eq!(ty_args.len(), 3);
595655
assert_eq!(args.len(), 1);
596656

657+
let mut gas_left = context.gas_budget();
658+
native_charge_gas_early_exit!(context, gas_left, gas_params.base);
597659
let table_context = context.extensions().get::<NativeTableContext>();
598660
let mut table_data = table_context.table_data.borrow_mut();
599661

@@ -603,7 +665,10 @@ fn native_destroy_empty_box(
603665

604666
assert!(table_data.removed_tables.insert(handle));
605667

606-
Ok(NativeResult::ok(gas_params.base, smallvec![]))
668+
Ok(NativeResult::ok(
669+
native_gas_total_cost!(context, gas_left),
670+
smallvec![],
671+
))
607672
}
608673

609674
pub fn make_native_destroy_empty_box(gas_params: DestroyEmptyBoxGasParameters) -> NativeFunction {
@@ -621,14 +686,20 @@ pub struct DropUncheckedBoxGasParameters {
621686

622687
fn native_drop_unchecked_box(
623688
gas_params: &DropUncheckedBoxGasParameters,
624-
_context: &mut NativeContext,
689+
context: &mut NativeContext,
625690
ty_args: Vec<Type>,
626691
args: VecDeque<Value>,
627692
) -> PartialVMResult<NativeResult> {
628693
assert_eq!(ty_args.len(), 3);
629694
assert_eq!(args.len(), 1);
630695

631-
Ok(NativeResult::ok(gas_params.base, smallvec![]))
696+
let mut gas_left = context.gas_budget();
697+
native_charge_gas_early_exit!(context, gas_left, gas_params.base);
698+
699+
Ok(NativeResult::ok(
700+
native_gas_total_cost!(context, gas_left),
701+
smallvec![],
702+
))
632703
}
633704

634705
pub fn make_native_drop_unchecked_box(gas_params: DropUncheckedBoxGasParameters) -> NativeFunction {

language/move-core/types/src/gas_algebra.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ impl<U> GasQuantity<U> {
208208
pub fn checked_sub(self, other: Self) -> Option<Self> {
209209
self.val.checked_sub(other.val).map(Self::new)
210210
}
211+
212+
pub fn saturating_sub(self, other: Self) -> Self {
213+
self.val.saturating_sub(other.val).into()
214+
}
211215
}
212216

213217
/***************************************************************************************************

language/move-core/types/src/vm_status.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,8 @@ impl From<StatusCode> for u64 {
768768
pub mod sub_status {
769769
// Native Function Error sub-codes
770770
pub const NFE_VECTOR_ERROR_BASE: u64 = 0;
771+
// Ran out gas while executing native
772+
pub const NFE_OUT_OF_GAS: u64 = 1;
771773
// Failure in BCS deserialization
772774
pub const NFE_BCS_SERIALIZATION_FAILURE: u64 = 0x1C5;
773775
}

language/move-stdlib/src/natives/bcs.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ use move_core_types::{
88
gas_algebra::{InternalGas, InternalGasPerByte, NumBytes},
99
vm_status::sub_status::NFE_BCS_SERIALIZATION_FAILURE,
1010
};
11-
use move_vm_runtime::native_functions::{NativeContext, NativeFunction};
11+
use move_vm_runtime::{
12+
native_charge_gas_early_exit,
13+
native_functions::{NativeContext, NativeFunction},
14+
native_gas_total_cost,
15+
};
1216
use move_vm_types::{
1317
loaded_data::runtime_types::Type,
1418
natives::function::NativeResult,
@@ -46,7 +50,7 @@ fn native_to_bytes(
4650
debug_assert!(ty_args.len() == 1);
4751
debug_assert!(args.len() == 1);
4852

49-
let mut cost = 0.into();
53+
let mut gas_left = context.gas_budget();
5054

5155
// pop type and value
5256
let ref_to_val = pop_arg!(args, Reference);
@@ -56,27 +60,41 @@ fn native_to_bytes(
5660
let layout = match context.type_to_type_layout(&arg_type)? {
5761
Some(layout) => layout,
5862
None => {
59-
cost += gas_params.failure;
60-
return Ok(NativeResult::err(cost, NFE_BCS_SERIALIZATION_FAILURE));
63+
// If we run out of gas when charging for failure, we don't want the `OUT_OF_GAS` error
64+
// to mask the actual error `NFE_BCS_SERIALIZATION_FAILURE`, so we saturate deduction at 0
65+
gas_left = gas_left.saturating_sub(gas_params.failure);
66+
return Ok(NativeResult::err(
67+
native_gas_total_cost!(context, gas_left),
68+
NFE_BCS_SERIALIZATION_FAILURE,
69+
));
6170
}
6271
};
6372
// serialize value
6473
let val = ref_to_val.read_ref()?;
6574
let serialized_value = match val.simple_serialize(&layout) {
6675
Some(serialized_value) => serialized_value,
6776
None => {
68-
cost += gas_params.failure;
69-
return Ok(NativeResult::err(cost, NFE_BCS_SERIALIZATION_FAILURE));
77+
// If we run out of gas when charging for failure, we don't want the `OUT_OF_GAS` error
78+
// to mask the actual error `NFE_BCS_SERIALIZATION_FAILURE`, so we saturate deduction at 0
79+
gas_left = gas_left.saturating_sub(gas_params.failure);
80+
return Ok(NativeResult::err(
81+
native_gas_total_cost!(context, gas_left),
82+
NFE_BCS_SERIALIZATION_FAILURE,
83+
));
7084
}
7185
};
72-
cost += gas_params.per_byte_serialized
73-
* std::cmp::max(
74-
NumBytes::new(serialized_value.len() as u64),
75-
gas_params.legacy_min_output_size,
76-
);
86+
native_charge_gas_early_exit!(
87+
context,
88+
gas_left,
89+
gas_params.per_byte_serialized
90+
* std::cmp::max(
91+
NumBytes::new(serialized_value.len() as u64),
92+
gas_params.legacy_min_output_size,
93+
)
94+
);
7795

7896
Ok(NativeResult::ok(
79-
cost,
97+
native_gas_total_cost!(context, gas_left),
8098
smallvec![Value::vector_u8(serialized_value)],
8199
))
82100
}

language/move-stdlib/src/natives/event.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
use crate::natives::helpers::make_module_natives;
66
use move_binary_format::errors::PartialVMResult;
77
use move_core_types::gas_algebra::InternalGasPerAbstractMemoryUnit;
8-
use move_vm_runtime::native_functions::{NativeContext, NativeFunction};
8+
use move_vm_runtime::{
9+
native_charge_gas_early_exit,
10+
native_functions::{NativeContext, NativeFunction},
11+
native_gas_total_cost,
12+
};
913
use move_vm_types::{
1014
loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value,
1115
views::ValueView,
@@ -33,19 +37,28 @@ fn native_write_to_event_store(
3337
) -> PartialVMResult<NativeResult> {
3438
debug_assert!(ty_args.len() == 1);
3539
debug_assert!(arguments.len() == 3);
40+
let mut gas_left = context.gas_budget();
3641

3742
let ty = ty_args.pop().unwrap();
3843
let msg = arguments.pop_back().unwrap();
3944
let seq_num = pop_arg!(arguments, u64);
4045
let guid = pop_arg!(arguments, Vec<u8>);
4146

4247
let cost = gas_params.unit_cost * std::cmp::max(msg.legacy_abstract_memory_size(), 1.into());
48+
// Charge before doing work
49+
native_charge_gas_early_exit!(context, gas_left, cost);
4350

4451
if !context.save_event(guid, seq_num, ty, msg)? {
45-
return Ok(NativeResult::err(cost, 0));
52+
return Ok(NativeResult::err(
53+
native_gas_total_cost!(context, gas_left),
54+
0,
55+
));
4656
}
4757

48-
Ok(NativeResult::ok(cost, smallvec![]))
58+
Ok(NativeResult::ok(
59+
native_gas_total_cost!(context, gas_left),
60+
smallvec![],
61+
))
4962
}
5063

5164
pub fn make_native_write_to_event_store(

0 commit comments

Comments
 (0)