Skip to content

Commit 7e84566

Browse files
authored
[static ptb] Fix PTB gas stack height (#24440)
## Description - The new PTB gas metering was a bit sloppy, and did not properly pop from the stack - Added an invariant violation for when it is not zero ## Test plan - Ran tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] gRPC: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] Indexing Framework:
1 parent 9948d6d commit 7e84566

File tree

7 files changed

+129
-22
lines changed

7 files changed

+129
-22
lines changed

crates/sui-adapter-transactional-tests/tests/size_limits/event_limits_tests_out_of_gas.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { k
2222
task 4, lines 73-75:
2323
//# run Test::M1::emit_event_with_size --args 259000 --gas-budget 1000000000
2424
Error: Transaction Effects Status: Insufficient Gas.
25-
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InsufficientGas, source: Some(VMError { major_status: OUT_OF_GAS, sub_status: None, message: None, exec_state: None, location: Module(ModuleId { address: Test, name: Identifier("M1") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 26)] }), command: Some(0) } }
25+
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InsufficientGas, source: Some(VMError { major_status: OUT_OF_GAS, sub_status: None, message: None, exec_state: None, location: Module(ModuleId { address: Test, name: Identifier("M1") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 37)] }), command: Some(0) } }
2626

2727
task 5, line 76:
2828
//# run Test::M1::emit_n_events_with_size --args 3 256000 --gas-budget 1000000000 --summarize
2929
Error: Transaction Effects Status: Insufficient Gas.
30-
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InsufficientGas, source: Some(VMError { major_status: OUT_OF_GAS, sub_status: None, message: None, exec_state: None, location: Module(ModuleId { address: Test, name: Identifier("M1") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 35)] }), command: Some(0) } }
30+
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InsufficientGas, source: Some(VMError { major_status: OUT_OF_GAS, sub_status: None, message: None, exec_state: None, location: Module(ModuleId { address: Test, name: Identifier("M1") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 27)] }), command: Some(0) } }

crates/sui-adapter-transactional-tests/tests/upgrade/type_name_unit_tests.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ gas summary: computation_cost: 3000000, storage_cost: 988000, storage_rebate: 0
3232
task 6, line 177:
3333
//# run A2::m::generic_cases --args @A0 @A1
3434
mutated: object(0,1)
35-
gas summary: computation_cost: 196000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880
35+
gas summary: computation_cost: 195000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880

crates/sui-types/src/gas_model/tables.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl GasStatus {
115115
/// code that does not have to charge the user.
116116
pub fn new_unmetered() -> Self {
117117
Self {
118-
gas_model_version: 4,
118+
gas_model_version: 11,
119119
gas_left: InternalGas::new(0),
120120
gas_price: 1,
121121
initial_budget: InternalGas::new(0),
@@ -337,6 +337,14 @@ impl GasStatus {
337337
pub fn instructions_executed(&self) -> u64 {
338338
self.instructions_executed
339339
}
340+
341+
pub fn stack_height_current(&self) -> u64 {
342+
self.stack_height_current
343+
}
344+
345+
pub fn stack_size_current(&self) -> u64 {
346+
self.stack_size_current
347+
}
340348
}
341349

342350
pub fn zero_cost_schedule() -> CostTable {

sui-execution/latest/sui-adapter/src/gas_meter.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,15 @@ impl GasMeter for SuiGasMeter<'_> {
259259
// TODO(tzakian): We should account for this elsewhere as the owner of data the
260260
// reference points to won't be on the stack. For now though, we treat it as adding to the
261261
// stack size.
262+
let (pushes, pops) = if reduce_stack_size(self.0.gas_model_version) {
263+
(0, 2)
264+
} else {
265+
(1, 2)
266+
};
262267
self.0.charge(
263268
1,
264-
1,
265-
2,
269+
pushes,
270+
pops,
266271
abstract_memory_size(self.0, new_val).into(),
267272
abstract_memory_size(self.0, old_val).into(),
268273
)
@@ -355,7 +360,12 @@ impl GasMeter for SuiGasMeter<'_> {
355360

356361
fn charge_vec_swap(&mut self, _ty: impl TypeView) -> PartialVMResult<()> {
357362
let size_decrease = REFERENCE_SIZE + Type::U64.size() + Type::U64.size();
358-
self.0.charge(1, 1, 1, 0, size_decrease.into())
363+
let (pushes, pops) = if reduce_stack_size(self.0.gas_model_version) {
364+
(0, 3)
365+
} else {
366+
(1, 1)
367+
};
368+
self.0.charge(1, pushes, pops, 0, size_decrease.into())
359369
}
360370

361371
fn charge_drop_frame(
@@ -400,6 +410,11 @@ fn reweight_move_loc(gas_model_version: u64) -> bool {
400410
gas_model_version > 10
401411
}
402412

413+
fn reduce_stack_size(gas_model_version: u64) -> bool {
414+
// Reducing stack size is only done in gas model versions 10 and above.
415+
gas_model_version > 10
416+
}
417+
403418
fn size_config_for_gas_model_version(
404419
gas_model_version: u64,
405420
should_traverse_refs: bool,

sui-execution/latest/sui-adapter/src/static_programmable_transactions/execution/context.rs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use move_core_types::{
3232
use move_trace_format::format::MoveTraceBuilder;
3333
use move_vm_runtime::native_extensions::NativeContextExtensions;
3434
use move_vm_types::{
35-
gas::GasMeter,
35+
gas::{GasMeter, SimpleInstruction},
3636
values::{VMValueCast, Value as VMValue},
3737
};
3838
use std::{
@@ -78,11 +78,14 @@ macro_rules! object_runtime_mut {
7878
}
7979

8080
macro_rules! charge_gas_ {
81-
($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {{
81+
($gas_charger:expr, $env:expr, $call:ident($($args:expr),*)) => {{
8282
SuiGasMeter($gas_charger.move_gas_status_mut())
83-
.$case($value_view)
83+
.$call($($args),*)
8484
.map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
8585
}};
86+
($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {
87+
charge_gas_!($gas_charger, $env, $case($value_view))
88+
};
8689
}
8790

8891
macro_rules! charge_gas {
@@ -269,6 +272,7 @@ impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'li
269272
tx_context.clone(),
270273
);
271274

275+
debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
272276
let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
273277
tx_context.borrow().digest(),
274278
)?)])?;
@@ -515,13 +519,24 @@ impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'li
515519
}
516520
};
517521
Ok(match usage {
518-
UsageKind::Move => local.move_()?,
522+
UsageKind::Move => {
523+
let value = local.move_()?;
524+
charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
525+
value
526+
}
519527
UsageKind::Copy => {
520528
let value = local.copy()?;
521529
charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
522530
value
523531
}
524-
UsageKind::Borrow => local.borrow()?,
532+
UsageKind::Borrow => {
533+
charge_gas_!(
534+
self.gas_charger,
535+
self.env,
536+
charge_simple_instr(SimpleInstruction::MutBorrowLoc)
537+
)?;
538+
local.borrow()?
539+
}
525540
})
526541
}
527542

@@ -550,7 +565,10 @@ impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'li
550565
where
551566
VMValue: VMValueCast<V>,
552567
{
568+
let before_height = self.gas_charger.move_gas_status().stack_height_current();
553569
let value = self.argument_value(arg)?;
570+
let after_height = self.gas_charger.move_gas_status().stack_height_current();
571+
debug_assert_eq!(before_height.saturating_add(1), after_height);
554572
let value: V = value.cast()?;
555573
Ok(value)
556574
}
@@ -569,6 +587,36 @@ impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'li
569587
Ok(())
570588
}
571589

590+
pub fn charge_command(
591+
&mut self,
592+
is_move_call: bool,
593+
num_args: usize,
594+
num_return: usize,
595+
) -> Result<(), ExecutionError> {
596+
let move_gas_status = self.gas_charger.move_gas_status_mut();
597+
let before_size = move_gas_status.stack_size_current();
598+
// Pop all of the arguments
599+
// If the return values came from the Move VM directly (via a Move call), pop those
600+
// as well
601+
let num_popped = if is_move_call {
602+
num_args.checked_add(num_return).ok_or_else(|| {
603+
make_invariant_violation!("usize overflow when charging gas for command",)
604+
})?
605+
} else {
606+
num_args
607+
};
608+
move_gas_status
609+
.charge(1, 0, num_popped as u64, 0, /* unused */ 1)
610+
.map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
611+
let after_size = move_gas_status.stack_size_current();
612+
assert_invariant!(
613+
before_size == after_size,
614+
"We assume currently that the stack size is not decremented. \
615+
If this changes, we need to actually account for it here"
616+
);
617+
Ok(())
618+
}
619+
572620
pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, ExecutionError> {
573621
Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
574622
}
@@ -838,11 +886,15 @@ impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'li
838886
.map_err(|e| {
839887
make_invariant_violation!("Failed to get tx context for init function: {}", e)
840888
})?;
889+
// balance the stack after borrowing the tx context
890+
charge_gas!(self, charge_store_loc, &tx_context)?;
891+
841892
let args = if has_otw {
842893
vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
843894
} else {
844895
vec![CtxValue(tx_context)]
845896
};
897+
debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
846898
let return_values = self.execute_function_bypass_visibility(
847899
&module.self_id(),
848900
INIT_FN_NAME,
@@ -862,7 +914,8 @@ impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'li
862914
assert_invariant!(
863915
return_values.is_empty(),
864916
"init should not have return values"
865-
)
917+
);
918+
debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
866919
}
867920

868921
Ok(())
@@ -1247,6 +1300,7 @@ fn load_object_arg_impl(
12471300

12481301
let v = Value::deserialize(env, move_obj.contents(), ty)?;
12491302
charge_gas_!(meter, env, charge_copy_loc, &v)?;
1303+
charge_gas_!(meter, env, charge_store_loc, &v)?;
12501304
Ok((object_metadata, v))
12511305
}
12521306

@@ -1263,6 +1317,7 @@ fn load_withdrawal_arg(
12631317
} = withdrawal;
12641318
let loaded = Value::funds_accumulator_withdrawal(*owner, *amount);
12651319
charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1320+
charge_gas_!(meter, env, charge_store_loc, &loaded)?;
12661321
Ok(loaded)
12671322
}
12681323

@@ -1275,6 +1330,7 @@ fn load_pure_value(
12751330
let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
12761331
// ByteValue::Receiving { id, version } => Value::receiving(*id, *version),
12771332
charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1333+
charge_gas_!(meter, env, charge_store_loc, &loaded)?;
12781334
Ok(loaded)
12791335
}
12801336

@@ -1286,11 +1342,13 @@ fn load_receiving_value(
12861342
let (id, version, _) = metadata.object_ref;
12871343
let loaded = Value::receiving(id, version);
12881344
charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1345+
charge_gas_!(meter, env, charge_store_loc, &loaded)?;
12891346
Ok(loaded)
12901347
}
12911348

12921349
fn copy_value(meter: &mut GasCharger, env: &Env, value: &Value) -> Result<Value, ExecutionError> {
12931350
charge_gas_!(meter, env, charge_copy_loc, value)?;
1351+
charge_gas_!(meter, env, charge_pop, value)?;
12941352
value.copy()
12951353
}
12961354

sui-execution/latest/sui-adapter/src/static_programmable_transactions/execution/interpreter.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub fn execute_inner<'env, 'pc, 'vm, 'state, 'linkage, Mode: ExecutionMode>(
6666
where
6767
'pc: 'state,
6868
{
69+
debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
6970
let T::Transaction {
7071
bytes,
7172
objects,
@@ -147,6 +148,12 @@ fn execute_command<Mode: ExecutionMode>(
147148
drop_values,
148149
consumed_shared_objects: _,
149150
} = c;
151+
assert_invariant!(
152+
context.gas_charger.move_gas_status().stack_height_current() == 0,
153+
"stack height did not start at 0"
154+
);
155+
let is_move_call = matches!(command, T::Command__::MoveCall(_));
156+
let num_args = command.arguments_len();
150157
let mut args_to_update = vec![];
151158
let result = match command {
152159
T::Command__::MoveCall(move_call) => {
@@ -323,11 +330,16 @@ fn execute_command<Mode: ExecutionMode>(
323330
result.len() == drop_values.len(),
324331
"result values and drop values mismatch"
325332
);
333+
context.charge_command(is_move_call, num_args, result.len())?;
326334
let result = result
327335
.into_iter()
328336
.zip(drop_values)
329337
.map(|(value, drop)| if !drop { Some(value) } else { None })
330338
.collect::<Vec<_>>();
331339
context.result(result)?;
340+
assert_invariant!(
341+
context.gas_charger.move_gas_status().stack_height_current() == 0,
342+
"stack height did not end at 0"
343+
);
332344
Ok(())
333345
}

sui-execution/latest/sui-adapter/src/static_programmable_transactions/typing/ast.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::static_programmable_transactions::{
77
use indexmap::IndexSet;
88
use move_core_types::{account_address::AccountAddress, u256::U256};
99
use move_vm_types::values::VectorSpecialization;
10-
use std::{cell::OnceCell, vec};
10+
use std::cell::OnceCell;
1111
use sui_types::base_types::{ObjectID, ObjectRef};
1212

1313
//**************************************************************************************************
@@ -229,21 +229,21 @@ impl Argument__ {
229229
}
230230

231231
impl Command__ {
232-
pub fn arguments(&self) -> Vec<&Argument> {
232+
pub fn arguments(&self) -> Box<dyn Iterator<Item = &Argument> + '_> {
233233
match self {
234-
Command__::MoveCall(mc) => mc.arguments.iter().collect(),
234+
Command__::MoveCall(mc) => Box::new(mc.arguments.iter()),
235235
Command__::TransferObjects(objs, addr) => {
236-
objs.iter().chain(std::iter::once(addr)).collect()
236+
Box::new(objs.iter().chain(std::iter::once(addr)))
237237
}
238238
Command__::SplitCoins(_, coin, amounts) => {
239-
std::iter::once(coin).chain(amounts).collect()
239+
Box::new(std::iter::once(coin).chain(amounts))
240240
}
241241
Command__::MergeCoins(_, target, sources) => {
242-
std::iter::once(target).chain(sources).collect()
242+
Box::new(std::iter::once(target).chain(sources))
243243
}
244-
Command__::MakeMoveVec(_, elems) => elems.iter().collect(),
245-
Command__::Publish(_, _, _) => vec![],
246-
Command__::Upgrade(_, _, _, arg, _) => vec![arg],
244+
Command__::MakeMoveVec(_, elems) => Box::new(elems.iter()),
245+
Command__::Publish(_, _, _) => Box::new(std::iter::empty()),
246+
Command__::Upgrade(_, _, _, arg, _) => Box::new(std::iter::once(arg)),
247247
}
248248
}
249249

@@ -277,6 +277,20 @@ impl Command__ {
277277
Command__::Publish(_, _, _) => Box::new(std::iter::empty()),
278278
}
279279
}
280+
281+
pub fn arguments_len(&self) -> usize {
282+
let n = match self {
283+
Command__::MoveCall(mc) => mc.arguments.len(),
284+
Command__::TransferObjects(objs, _) => objs.len().saturating_add(1),
285+
Command__::SplitCoins(_, _, amounts) => amounts.len().saturating_add(1),
286+
Command__::MergeCoins(_, _, sources) => sources.len().saturating_add(1),
287+
Command__::MakeMoveVec(_, elems) => elems.len(),
288+
Command__::Publish(_, _, _) => 0,
289+
Command__::Upgrade(_, _, _, _, _) => 1,
290+
};
291+
debug_assert_eq!(self.arguments().count(), n);
292+
n
293+
}
280294
}
281295

282296
//**************************************************************************************************

0 commit comments

Comments
 (0)