diff --git a/Cargo.lock b/Cargo.lock index 9ea9d36c76ef1..b02ceca8f6773 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9509,20 +9509,6 @@ dependencies = [ "workspace-hack", ] -[[package]] -name = "sui-cost-tables" -version = "0.1.0" -dependencies = [ - "anyhow", - "move-binary-format", - "move-core-types", - "move-vm-test-utils", - "move-vm-types", - "once_cell", - "serde 1.0.152", - "workspace-hack", -] - [[package]] name = "sui-e2e-tests" version = "0.1.0" @@ -9979,7 +9965,6 @@ dependencies = [ "serde_json", "serde_yaml 0.8.26", "sui-core", - "sui-cost-tables", "sui-macros", "sui-move-build", "sui-move-natives-latest", @@ -10845,6 +10830,7 @@ dependencies = [ "move-core-types", "move-disassembler", "move-ir-types", + "move-vm-test-utils", "move-vm-types", "mysten-metrics", "mysten-network", @@ -10868,7 +10854,6 @@ dependencies = [ "static_assertions", "strum", "strum_macros", - "sui-cost-tables", "sui-enum-compat-util", "sui-macros", "sui-protocol-config", diff --git a/Cargo.toml b/Cargo.toml index b0c58b07ec0b1..428b0ba404bce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,6 @@ members = [ "crates/sui-config", "crates/sui-core", "crates/sui-cost", - "crates/sui-cost-tables", "crates/sui-e2e-tests", "crates/sui-enum-compat-util", "crates/sui-faucet", @@ -384,7 +383,7 @@ move-disassembler = { path = "external-crates/move/tools/move-disassembler" } move-package = { path = "external-crates/move/tools/move-package" } move-unit-test = { path = "external-crates/move/tools/move-unit-test" } move-vm-config = { path = "external-crates/move/move-vm/config" } -move-vm-test-utils = { path = "external-crates/move/move-vm/test-utils" } +move-vm-test-utils = { path = "external-crates/move/move-vm/test-utils", features = ["tiered-gas"] } move-vm-types = { path = "external-crates/move/move-vm/types" } move-command-line-common = { path = "external-crates/move/move-command-line-common" } move-transactional-test-runner = { path = "external-crates/move/testing-infra/transactional-test-runner" } @@ -421,7 +420,6 @@ sui-cluster-test = { path = "crates/sui-cluster-test" } sui-config = { path = "crates/sui-config" } sui-core = { path = "crates/sui-core" } sui-cost = { path = "crates/sui-cost" } -sui-cost-tables = { path = "crates/sui-cost-tables" } sui-e2e-tests = { path = "crates/sui-e2e-tests" } sui-enum-compat-util = { path = "crates/sui-enum-compat-util" } sui-faucet = { path = "crates/sui-faucet" } diff --git a/crates/sui-adapter-transactional-tests/tests/runtime_behavior/error_locations.move b/crates/sui-adapter-transactional-tests/tests/runtime_behavior/error_locations.move index b9b0f5ecbe33f..320bf4875ddd9 100644 --- a/crates/sui-adapter-transactional-tests/tests/runtime_behavior/error_locations.move +++ b/crates/sui-adapter-transactional-tests/tests/runtime_behavior/error_locations.move @@ -31,7 +31,7 @@ module test::m { //# run test::m::abort_ -//# run test::m::loop_ --gas-budget 100000 +//# run test::m::loop_ --gas-budget 1000000 //# run test::m::math diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/deleted_id_limits_tests_v_2.exp b/crates/sui-adapter-transactional-tests/tests/size_limits/deleted_id_limits_tests_v_2.exp deleted file mode 100644 index cd2c1fdc35f81..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/deleted_id_limits_tests_v_2.exp +++ /dev/null @@ -1,22 +0,0 @@ -processed 6 tasks - -task 1 'publish'. lines 8-35: -created: object(1,0) -mutated: object(0,0) -gas summary: computation_cost: 1000000, storage_cost: 69, storage_rebate: 0, non_refundable_storage_fee: 0 - -task 2 'run'. lines 36-38: -mutated: object(0,0) -gas summary: computation_cost: 10000000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 3 'run'. lines 39-41: -mutated: object(0,0) -gas summary: computation_cost: 50000000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 4 'run'. lines 42-44: -Error: Transaction Effects Status: Move Primitive Runtime Error. Location: sui::tx_context::derive_id (function index 6) at offset 0. Arithmetic error, stack overflow, max value depth, etc. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MovePrimitiveRuntimeError(MoveLocationOpt(Some(MoveLocation { module: ModuleId { address: sui, name: Identifier("tx_context") }, function: 6, instruction: 0, function_name: Some("derive_id") }))), source: Some(VMError { major_status: MEMORY_LIMIT_EXCEEDED, sub_status: Some(2), message: Some("Creating more than 2048 IDs is not allowed"), exec_state: None, location: Module(ModuleId { address: sui, name: Identifier("tx_context") }), indices: [], offsets: [(FunctionDefinitionIndex(6), 0)] }), command: Some(0) } } - -task 5 'run'. lines 45-45: -Error: Transaction Effects Status: Move Primitive Runtime Error. Location: sui::tx_context::derive_id (function index 6) at offset 0. Arithmetic error, stack overflow, max value depth, etc. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MovePrimitiveRuntimeError(MoveLocationOpt(Some(MoveLocation { module: ModuleId { address: sui, name: Identifier("tx_context") }, function: 6, instruction: 0, function_name: Some("derive_id") }))), source: Some(VMError { major_status: MEMORY_LIMIT_EXCEEDED, sub_status: Some(2), message: Some("Creating more than 2048 IDs is not allowed"), exec_state: None, location: Module(ModuleId { address: sui, name: Identifier("tx_context") }), indices: [], offsets: [(FunctionDefinitionIndex(6), 0)] }), command: Some(0) } } diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/deleted_id_limits_tests_v_2.move b/crates/sui-adapter-transactional-tests/tests/size_limits/deleted_id_limits_tests_v_2.move deleted file mode 100644 index 9c3708c0a5e9c..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/deleted_id_limits_tests_v_2.move +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// Test limits on number of deleted IDs - -//# init --addresses Test=0x0 --max-gas 100000000000000 --protocol-version 2 - -//# publish - -/// Test deleted id limits enforced -/// Right now, we should never be able to hit the delete limit because we will hit the create limit first -module Test::M1 { - use sui::tx_context::TxContext; - use sui::object::{Self, UID}; - use std::vector; - - public entry fun delete_n_ids(n: u64, ctx: &mut TxContext) { - let v: vector = vector::empty(); - let i = 0; - while (i < n) { - let id = object::new(ctx); - vector::push_back(&mut v, id); - i = i + 1; - }; - i = 0; - while (i < n) { - let id = vector::pop_back(&mut v); - object::delete(id); - i = i + 1; - }; - vector::destroy_empty(v); - } -} - -// delete below delete count limit should succeed -//# run Test::M1::delete_n_ids --args 256 --gas-budget 100000000000000 - -// delete at delete count limit should succeed -//# run Test::M1::delete_n_ids --args 2048 --gas-budget 100000000000000 - -// delete above delete count limit should fail -//# run Test::M1::delete_n_ids --args 2049 --gas-budget 100000000000000 - -// delete above delete count limit should fail -//# run Test::M1::delete_n_ids --args 4096 --gas-budget 100000000000000 diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit_v_2.exp b/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit_v_2.exp deleted file mode 100644 index 939bbe6974d9d..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit_v_2.exp +++ /dev/null @@ -1,42 +0,0 @@ -processed 9 tasks - -init: -A: object(0,0) - -task 1 'publish'. lines 8-78: -created: object(1,0) -mutated: object(0,1) -gas summary: computation_cost: 1000000, storage_cost: 131, storage_rebate: 0, non_refundable_storage_fee: 0 - -task 2 'run'. lines 79-81: -Error: Transaction Effects Status: Move object with size 256001 is larger than the maximum object size 256000 -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveObjectTooBig { object_size: 256001, max_object_size: 256000 }, source: None, command: None } } - -task 3 'run'. lines 82-84: -created: object(3,0) -mutated: object(0,0) -gas summary: computation_cost: 1000000000, storage_cost: 25625, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 4 'run'. lines 85-87: -created: object(4,0) -mutated: object(0,0) -gas summary: computation_cost: 1000000000, storage_cost: 25625, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 5 'run'. lines 88-90: -Error: Transaction Effects Status: Move object with size 256001 is larger than the maximum object size 256000 -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveObjectTooBig { object_size: 256001, max_object_size: 256000 }, source: None, command: None } } - -task 6 'run'. lines 91-91: -created: object(6,0) -mutated: object(0,0) -gas summary: computation_cost: 1000000000, storage_cost: 25622, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 7 'run'. lines 93-93: -created: object(7,0) -mutated: object(0,0) -wrapped: object(6,0) -gas summary: computation_cost: 50000000, storage_cost: 25626, storage_rebate: 25622, non_refundable_storage_fee: 0 - -task 8 'run'. lines 95-95: -Error: Transaction Effects Status: Move object with size 256001 is larger than the maximum object size 256000 -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveObjectTooBig { object_size: 256001, max_object_size: 256000 }, source: None, command: None } } diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit_v_2.move b/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit_v_2.move deleted file mode 100644 index d1e84fdfdd05a..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit_v_2.move +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// Test creating objects just below the size limit, and above it - -//# init --addresses Test=0x0 --accounts A --max-gas 100000000000000 --protocol-version 2 - -//# publish - -module Test::M1 { - use std::vector; - use sui::bcs; - use sui::object::{Self, UID}; - use sui::tx_context::{Self, TxContext}; - use sui::transfer; - - struct S has key, store { - id: UID, - contents: vector - } - - struct Wrapper has key { - id: UID, - s: S, - } - - // create an object whose Move BCS representation is `n` bytes - public fun create_object_with_size(n: u64, ctx: &mut TxContext): S { - // minimum object size for S is 32 bytes for UID + 1 byte for vector length - assert!(n > std::address::length() + 1, 0); - let contents = vector[]; - let i = 0; - let bytes_to_add = n - (std::address::length() + 1); - while (i < bytes_to_add) { - vector::push_back(&mut contents, 9); - i = i + 1; - }; - let s = S { id: object::new(ctx), contents }; - let size = vector::length(&bcs::to_bytes(&s)); - // shrink by 1 byte until we match size. mismatch happens because of len(UID) + vector length byte - while (size > n) { - let _ = vector::pop_back(&mut s.contents); - // hack: assume this doesn't change the size of the BCS length byte - size = size - 1; - }; - // double-check that we got the size right - assert!(vector::length(&bcs::to_bytes(&s)) == n, 1); - s - } - - public entry fun transfer_object_with_size(n: u64, ctx: &mut TxContext) { - transfer::public_transfer(create_object_with_size(n, ctx), tx_context::sender(ctx)) - } - - /// Add a byte to `s` - public entry fun add_byte(s: &mut S) { - vector::push_back(&mut s.contents, 9) - } - - /// Wrap `s` - public entry fun wrap(s: S, ctx: &mut TxContext) { - transfer::transfer(Wrapper { id: object::new(ctx), s }, tx_context::sender(ctx)) - } - - /// Add `n` bytes to the `s` inside `wrapper`, then unwrap it. This should fail - /// if `s` is larger than the max object size - public entry fun add_bytes_then_unwrap(wrapper: Wrapper, n: u64, ctx: &mut TxContext) { - let i = 0; - while (i < n) { - vector::push_back(&mut wrapper.s.contents, 7); - i = i + 1 - }; - let Wrapper { id, s } = wrapper; - object::delete(id); - transfer::public_transfer(s, tx_context::sender(ctx)) - } -} -// create above size limit should fail -//# run Test::M1::transfer_object_with_size --args 256001 --sender A --gas-budget 10000000000000 - -// create under size limit should succeed -//# run Test::M1::transfer_object_with_size --args 255999 --sender A --gas-budget 100000000000000 - -// create at size limit should succeed -//# run Test::M1::transfer_object_with_size --args 256000 --sender A --gas-budget 100000000000000 - -// adding 1 byte to an object at the size limit should fail -//# run Test::M1::add_byte --args object(4,0) --sender A --gas-budget 100000000000000 - -// create at size limit, wrap, increase to over size limit while wrapped, then unwrap. should fail -//# run Test::M1::transfer_object_with_size --args 255968 --sender A --gas-budget 100000000000000 - -//# run Test::M1::wrap --args object(6,0) --sender A --gas-budget 100000000000000 - -//# run Test::M1::add_bytes_then_unwrap --args object(7,0) 33 --sender A --gas-budget 100000000000000 diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/new_id_limits_tests_v_2.exp b/crates/sui-adapter-transactional-tests/tests/size_limits/new_id_limits_tests_v_2.exp deleted file mode 100644 index 5406a873a3457..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/new_id_limits_tests_v_2.exp +++ /dev/null @@ -1,26 +0,0 @@ -processed 7 tasks - -task 1 'publish'. lines 8-34: -created: object(1,0) -mutated: object(0,0) -gas summary: computation_cost: 1000000, storage_cost: 69, storage_rebate: 0, non_refundable_storage_fee: 0 - -task 2 'run'. lines 35-37: -mutated: object(0,0) -gas summary: computation_cost: 1000000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 3 'run'. lines 38-40: -mutated: object(0,0) -gas summary: computation_cost: 10000000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 4 'run'. lines 41-43: -mutated: object(0,0) -gas summary: computation_cost: 50000000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 5 'run'. lines 44-46: -Error: Transaction Effects Status: Move Primitive Runtime Error. Location: sui::tx_context::derive_id (function index 6) at offset 0. Arithmetic error, stack overflow, max value depth, etc. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MovePrimitiveRuntimeError(MoveLocationOpt(Some(MoveLocation { module: ModuleId { address: sui, name: Identifier("tx_context") }, function: 6, instruction: 0, function_name: Some("derive_id") }))), source: Some(VMError { major_status: MEMORY_LIMIT_EXCEEDED, sub_status: Some(2), message: Some("Creating more than 2048 IDs is not allowed"), exec_state: None, location: Module(ModuleId { address: sui, name: Identifier("tx_context") }), indices: [], offsets: [(FunctionDefinitionIndex(6), 0)] }), command: Some(0) } } - -task 6 'run'. lines 47-47: -Error: Transaction Effects Status: Move Primitive Runtime Error. Location: sui::tx_context::derive_id (function index 6) at offset 0. Arithmetic error, stack overflow, max value depth, etc. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MovePrimitiveRuntimeError(MoveLocationOpt(Some(MoveLocation { module: ModuleId { address: sui, name: Identifier("tx_context") }, function: 6, instruction: 0, function_name: Some("derive_id") }))), source: Some(VMError { major_status: MEMORY_LIMIT_EXCEEDED, sub_status: Some(2), message: Some("Creating more than 2048 IDs is not allowed"), exec_state: None, location: Module(ModuleId { address: sui, name: Identifier("tx_context") }), indices: [], offsets: [(FunctionDefinitionIndex(6), 0)] }), command: Some(0) } } diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/new_id_limits_tests_v_2.move b/crates/sui-adapter-transactional-tests/tests/size_limits/new_id_limits_tests_v_2.move deleted file mode 100644 index b07d1e72cbaca..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/new_id_limits_tests_v_2.move +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// Test limits on number of created IDs - -//# init --addresses Test=0x0 --max-gas 100000000000000 --protocol-version 2 - -//# publish - -/// Test create id limits enforced -module Test::M1 { - use sui::tx_context::TxContext; - use sui::object::{Self, UID}; - use std::vector; - - public entry fun create_n_ids(n: u64, ctx: &mut TxContext) { - let v: vector = vector::empty(); - let i = 0; - while (i < n) { - let id = object::new(ctx); - vector::push_back(&mut v, id); - i = i + 1; - }; - i = 0; - while (i < n) { - let id = vector::pop_back(&mut v); - object::delete(id); - i = i + 1; - }; - vector::destroy_empty(v); - } -} - -// create below create count limit should succeed -//# run Test::M1::create_n_ids --args 1 --gas-budget 100000000000000 - -// create below create count limit should succeed -//# run Test::M1::create_n_ids --args 256 --gas-budget 100000000000000 - -// create at create count limit should succeed -//# run Test::M1::create_n_ids --args 2048 --gas-budget 100000000000000 - -// create above create count limit should fail -//# run Test::M1::create_n_ids --args 2049 --gas-budget 100000000000000 - -// create above create count limit should fail -//# run Test::M1::create_n_ids --args 4096 --gas-budget 100000000000000 diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/vector_len_limits_v_2.exp b/crates/sui-adapter-transactional-tests/tests/size_limits/vector_len_limits_v_2.exp deleted file mode 100644 index 03be6cb1eef76..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/vector_len_limits_v_2.exp +++ /dev/null @@ -1,22 +0,0 @@ -processed 6 tasks - -task 1 'publish'. lines 8-30: -created: object(1,0) -mutated: object(0,0) -gas summary: computation_cost: 1000000, storage_cost: 56, storage_rebate: 0, non_refundable_storage_fee: 0 - -task 2 'run'. lines 31-33: -mutated: object(0,0) -gas summary: computation_cost: 1000000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 3 'run'. lines 34-36: -mutated: object(0,0) -gas summary: computation_cost: 5000000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 4 'run'. lines 37-39: -mutated: object(0,0) -gas summary: computation_cost: 2100351000, storage_cost: 13, storage_rebate: 13, non_refundable_storage_fee: 0 - -task 5 'run'. lines 40-40: -Error: Transaction Effects Status: Move Primitive Runtime Error. Location: Test::M1::push_n_items (function index 0) at offset 11. Arithmetic error, stack overflow, max value depth, etc. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MovePrimitiveRuntimeError(MoveLocationOpt(Some(MoveLocation { module: ModuleId { address: Test, name: Identifier("M1") }, function: 0, instruction: 11, function_name: Some("push_n_items") }))), source: Some(VMError { major_status: VECTOR_OPERATION_ERROR, sub_status: Some(4), message: Some("vector size limit is 262144"), exec_state: None, location: Module(ModuleId { address: Test, name: Identifier("M1") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 11)] }), command: Some(0) } } diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/vector_len_limits_v_2.move b/crates/sui-adapter-transactional-tests/tests/size_limits/vector_len_limits_v_2.move deleted file mode 100644 index 669dbc6147ddb..0000000000000 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/vector_len_limits_v_2.move +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// Test limits on length of vectors - -//# init --addresses Test=0x0 --max-gas 100000000000000 --protocol-version 2 - -//# publish - -/// Test vector length limits enforced -module Test::M1 { - use std::vector; - - public entry fun push_n_items(n: u64) { - let v: vector = vector::empty(); - let i = 0; - while (i < n) { - vector::push_back(&mut v, i); - i = i + 1; - }; - i = 0; - while (i < n) { - let _ = vector::pop_back(&mut v); - i = i + 1; - }; - vector::destroy_empty(v); - } -} - -// push below ven len limit should succeed -//# run Test::M1::push_n_items --args 1 --gas-budget 100000000000000 - -// push below vec len limit should succeed -//# run Test::M1::push_n_items --args 256 --gas-budget 100000000000000 - -// run at vec len limit should succeed -//# run Test::M1::push_n_items --args 262144 --gas-budget 100000000000000 - -// run above vec len limit should fail -//# run Test::M1::push_n_items --args 262145 --gas-budget 100000000000000 diff --git a/crates/sui-core/src/authority.rs b/crates/sui-core/src/authority.rs index 256e2f5c500a9..3ecf5282fed6e 100644 --- a/crates/sui-core/src/authority.rs +++ b/crates/sui-core/src/authority.rs @@ -80,7 +80,7 @@ use sui_types::effects::{ use sui_types::error::{ExecutionError, UserInputError}; use sui_types::event::{Event, EventID}; use sui_types::executable_transaction::VerifiedExecutableTransaction; -use sui_types::gas::{GasCostSummary, SuiGasStatus}; +use sui_types::gas::{GasCharger, GasCostSummary, SuiGasStatus}; use sui_types::message_envelope::Message; use sui_types::messages_checkpoint::{ CheckpointCommitment, CheckpointContents, CheckpointContentsDigest, CheckpointDigest, @@ -1201,20 +1201,22 @@ impl AuthorityState { let owned_object_refs = input_objects.filter_owned_objects(); self.check_owned_locks(&owned_object_refs).await?; - + let tx_digest = *certificate.digest(); + let protocol_config = epoch_store.protocol_config(); let shared_object_refs = input_objects.filter_shared_objects(); let transaction_dependencies = input_objects.transaction_dependencies(); let temporary_store = TemporaryStore::new( self.database.clone(), input_objects, - *certificate.digest(), - epoch_store.protocol_config(), + tx_digest, + protocol_config, ); let transaction_data = &certificate.data().intent_message().value; let (kind, signer, gas) = transaction_data.execution_parts(); + let mut gas_charger = GasCharger::new(tx_digest, gas, gas_status, protocol_config); let (inner_temp_store, effects, execution_error_opt) = epoch_store.executor().execute_transaction_to_effects( - epoch_store.protocol_config(), + protocol_config, self.metrics.limits_metrics.clone(), // TODO: would be nice to pass the whole NodeConfig here, but it creates a // cyclic dependency w/ sui-adapter @@ -1228,11 +1230,10 @@ impl AuthorityState { .epoch_start_timestamp(), temporary_store, shared_object_refs, - gas_status, - &gas, + &mut gas_charger, kind, signer, - *certificate.digest(), + tx_digest, transaction_dependencies, ); @@ -1309,29 +1310,27 @@ impl AuthorityState { let shared_object_refs = input_objects.filter_shared_objects(); + let protocol_config = epoch_store.protocol_config(); let transaction_dependencies = input_objects.transaction_dependencies(); let temporary_store = TemporaryStore::new_for_mock_transaction( self.database.clone(), input_objects, transaction_digest, - epoch_store.protocol_config(), + protocol_config, ); let (kind, signer, _) = transaction.execution_parts(); let silent = true; // don't bother with paranoid checks in dry run let enable_move_vm_paranoid_checks = false; - let executor = sui_execution::executor( - epoch_store.protocol_config(), - enable_move_vm_paranoid_checks, - silent, - ) - .expect("Creating an executor should not fail here"); + let executor = + sui_execution::executor(protocol_config, enable_move_vm_paranoid_checks, silent) + .expect("Creating an executor should not fail here"); let expensive_checks = false; let (inner_temp_store, effects, _execution_error) = executor .execute_transaction_to_effects( - epoch_store.protocol_config(), + protocol_config, self.metrics.limits_metrics.clone(), expensive_checks, self.certificate_deny_config.certificate_deny_set(), @@ -1342,8 +1341,12 @@ impl AuthorityState { .epoch_start_timestamp(), temporary_store, shared_object_refs, - gas_status, - &gas_object_refs, + &mut GasCharger::new( + transaction_digest, + gas_object_refs, + gas_status, + protocol_config, + ), kind, signer, transaction_digest, @@ -1399,35 +1402,23 @@ impl AuthorityState { }); } - transaction_kind.check_version_supported(epoch_store.protocol_config())?; + let protocol_config = epoch_store.protocol_config(); + transaction_kind.check_version_supported(protocol_config)?; + let max_tx_gas = protocol_config.max_tx_gas(); let reference_gas_price = epoch_store.reference_gas_price(); - let protocol_config = epoch_store.protocol_config(); let gas_price = match gas_price { None => reference_gas_price, Some(gas) => { if gas == 0 { - epoch_store.reference_gas_price() + reference_gas_price } else { gas } } }; - if gas_price < reference_gas_price { - return Err(UserInputError::GasPriceUnderRGP { - gas_price, - reference_gas_price, - } - .into()); - } - if protocol_config.gas_model_version() >= 4 && gas_price >= protocol_config.max_gas_price() - { - return Err(UserInputError::GasPriceTooHigh { - max_gas_price: protocol_config.max_gas_price(), - } - .into()); - } - let max_tx_gas = protocol_config.max_tx_gas(); + let gas_status = + SuiGasStatus::new(max_tx_gas, gas_price, reference_gas_price, protocol_config)?; let gas_object_id = ObjectID::random(); // give the gas object 2x the max gas to have coin balance to play with during execution @@ -1462,10 +1453,9 @@ impl AuthorityState { transaction_digest, protocol_config, ); - let gas_status = SuiGasStatus::new_with_budget(max_tx_gas, gas_price, protocol_config); let silent = true; let executor = sui_execution::executor( - epoch_store.protocol_config(), + protocol_config, self.expensive_safety_check_config .enable_move_vm_paranoid_checks(), silent, @@ -1484,8 +1474,12 @@ impl AuthorityState { .epoch_start_timestamp(), temporary_store, shared_object_refs, - gas_status, - &[gas_object_ref], + &mut GasCharger::new( + transaction_digest, + vec![gas_object_ref], + gas_status, + protocol_config, + ), transaction_kind, sender, transaction_digest, diff --git a/crates/sui-core/src/authority/authority_store.rs b/crates/sui-core/src/authority/authority_store.rs index af5e93870b731..6bbbe21bd960e 100644 --- a/crates/sui-core/src/authority/authority_store.rs +++ b/crates/sui-core/src/authority/authority_store.rs @@ -1624,20 +1624,6 @@ impl AuthorityStore { } let executor = old_epoch_store.executor(); - let chain_identifier = old_epoch_store.get_chain_identifier(); - - let protocol_version = ProtocolVersion::new( - self.get_sui_system_state_object() - .expect("Read sui system state object cannot fail") - .protocol_version(), - ); - let protocol_config = - ProtocolConfig::get_for_version(protocol_version, chain_identifier.chain()); - // Prior to gas model v2, SUI conservation is not guaranteed. - if protocol_config.gas_model_version() <= 1 { - return Ok(()); - } - info!("Starting SUI conservation check. This may take a while.."); let cur_time = Instant::now(); let mut pending_objects = vec![]; diff --git a/crates/sui-core/src/transaction_input_checker.rs b/crates/sui-core/src/transaction_input_checker.rs index ba5967b9d23c3..d61995ca0cbbd 100644 --- a/crates/sui-core/src/transaction_input_checker.rs +++ b/crates/sui-core/src/transaction_input_checker.rs @@ -7,7 +7,7 @@ use crate::transaction_signing_filter; use std::collections::{BTreeMap, HashSet}; use std::sync::Arc; use sui_config::transaction_deny_config::TransactionDenyConfig; -use sui_macros::checked_arithmetic; +// use sui_macros::checked_arithmetic; use sui_protocol_config::ProtocolConfig; use sui_types::base_types::ObjectRef; use sui_types::error::{UserInputError, UserInputResult}; @@ -21,13 +21,13 @@ use sui_types::{ base_types::{SequenceNumber, SuiAddress}, error::SuiResult, fp_ensure, - gas::{SuiCostTable, SuiGasStatus}, + gas::SuiGasStatus, object::{Object, Owner}, }; use sui_types::{SUI_CLOCK_OBJECT_ID, SUI_CLOCK_OBJECT_SHARED_VERSION}; use tracing::instrument; -checked_arithmetic! { +// checked_arithmetic! { // Entry point for all checks related to gas. // Called on both signing and execution. @@ -39,17 +39,10 @@ async fn get_gas_status( epoch_store: &AuthorityPerEpochStore, transaction: &TransactionData, ) -> SuiResult { - // Get the first coin (possibly the only one) and make it "the gas coin", then - // keep track of all others that can contribute to gas (gas smashing). - let gas_object_ref = gas.get(0).unwrap(); - // all other gas coins - let more_gas_object_refs = gas[1..].to_vec(); - check_gas( objects, epoch_store, - gas_object_ref, - more_gas_object_refs, + gas, transaction.gas_budget(), transaction.gas_price(), transaction.kind(), @@ -187,59 +180,33 @@ pub async fn check_certificate_input( async fn check_gas( objects: &[Object], epoch_store: &AuthorityPerEpochStore, - gas_payment: &ObjectRef, - more_gas_object_refs: Vec, + gas: &[ObjectRef], gas_budget: u64, gas_price: u64, tx_kind: &TransactionKind, ) -> SuiResult { - let protocol_config = epoch_store.protocol_config(); if tx_kind.is_system_tx() { - Ok(SuiGasStatus::new_unmetered(protocol_config)) + Ok(SuiGasStatus::new_unmetered()) } else { - // gas price must be bigger or equal to reference gas price + let protocol_config = epoch_store.protocol_config(); let reference_gas_price = epoch_store.reference_gas_price(); - if gas_price < reference_gas_price { - return Err(UserInputError::GasPriceUnderRGP { - gas_price, - reference_gas_price, - } - .into()); - } - if protocol_config.gas_model_version() >= 4 && gas_price >= protocol_config.max_gas_price() - { - return Err(UserInputError::GasPriceTooHigh { - max_gas_price: protocol_config.max_gas_price(), - } - .into()); - } + let gas_status = + SuiGasStatus::new(gas_budget, gas_price, reference_gas_price, protocol_config)?; + // check balance and coins consistency // load all gas coins let objects: BTreeMap<_, _> = objects.iter().map(|o| (o.id(), o)).collect(); - - let gas_object = objects.get(&gas_payment.0); - let gas_object = *gas_object.ok_or(UserInputError::ObjectNotFound { - object_id: gas_payment.0, - version: Some(gas_payment.1), - })?; - let mut more_gas_objects = vec![]; - for obj_ref in more_gas_object_refs.iter() { + let mut gas_objects = vec![]; + for obj_ref in gas { let obj = objects.get(&obj_ref.0); let obj = *obj.ok_or(UserInputError::ObjectNotFound { object_id: obj_ref.0, version: Some(obj_ref.1), })?; - more_gas_objects.push(obj); + gas_objects.push(obj); } - - // check balance and coins consistency - let cost_table = SuiCostTable::new(protocol_config); - cost_table.check_gas_balance(gas_object, more_gas_objects, gas_budget, gas_price)?; - Ok(SuiGasStatus::new_with_budget( - gas_budget, - gas_price, - protocol_config, - )) + gas_status.check_gas_balance(&gas_objects, gas_budget)?; + Ok(gas_status) } } @@ -456,4 +423,4 @@ pub fn check_non_system_packages_to_be_published( Ok(()) } -} +// } diff --git a/crates/sui-core/src/unit_tests/authority_tests.rs b/crates/sui-core/src/unit_tests/authority_tests.rs index 12899c393d5c0..1f0ab5db61a54 100644 --- a/crates/sui-core/src/unit_tests/authority_tests.rs +++ b/crates/sui-core/src/unit_tests/authority_tests.rs @@ -36,7 +36,6 @@ use sui_types::effects::TransactionEffects; use sui_types::epoch_data::EpochData; use sui_types::error::UserInputError; use sui_types::execution_status::{ExecutionFailureStatus, ExecutionStatus}; -use sui_types::gas::SuiCostTable; use sui_types::gas_coin::GasCoin; use sui_types::messages_consensus::ConsensusCommitPrologue; use sui_types::object::Data; @@ -1811,14 +1810,19 @@ async fn test_publish_dependent_module_ok() { #[tokio::test] async fn test_publish_module_no_dependencies_ok() { let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); + let authority = init_state_with_objects(vec![]).await; + let rgp = authority.reference_gas_price_for_testing().unwrap(); let gas_payment_object_id = ObjectID::random(); // Use the max budget to avoid running out of gas. - let gas_balance = SuiCostTable::new_for_testing().max_gas_budget(); + let gas_balance = { + let epoch_store = authority.epoch_store_for_testing(); + let protocol_config = epoch_store.protocol_config(); + protocol_config.max_tx_gas() + }; let gas_payment_object = Object::with_id_owner_gas_for_testing(gas_payment_object_id, sender, gas_balance); let gas_payment_object_ref = gas_payment_object.compute_object_reference(); - let authority = init_state_with_objects(vec![gas_payment_object]).await; - let rgp = authority.reference_gas_price_for_testing().unwrap(); + authority.insert_genesis_object(gas_payment_object).await; let module = file_format::empty_module(); let mut module_bytes = Vec::new(); diff --git a/crates/sui-core/src/unit_tests/gas_tests.rs b/crates/sui-core/src/unit_tests/gas_tests.rs index 55b3533926504..1e7aac7f3f7b8 100644 --- a/crates/sui-core/src/unit_tests/gas_tests.rs +++ b/crates/sui-core/src/unit_tests/gas_tests.rs @@ -14,29 +14,32 @@ use sui_protocol_config::ProtocolConfig; use sui_types::crypto::AccountKeyPair; use sui_types::effects::TransactionEvents; use sui_types::execution_status::{ExecutionFailureStatus, ExecutionStatus}; -use sui_types::gas::SuiCostTable; use sui_types::gas_coin::GasCoin; use sui_types::object::GAS_VALUE_FOR_TESTING; use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder; use sui_types::utils::to_sender_signed_transaction; use sui_types::{base_types::dbg_addr, crypto::get_key_pair}; -static MAX_GAS_BUDGET: Lazy = Lazy::new(|| SuiCostTable::new_for_testing().max_gas_budget()); -static MIN_GAS_BUDGET: Lazy = Lazy::new(|| SuiCostTable::new_for_testing().min_gas_budget()); +// The cost table is used only to get the max budget available which is not dependent on +// the gas price +static MAX_GAS_BUDGET: Lazy = Lazy::new(|| ProtocolConfig::get_for_max_version().max_tx_gas()); +// MIN_GAS_BUDGET_PRE_RGP has to be multiplied by the RGP to get the proper minimum +static MIN_GAS_BUDGET_PRE_RGP: Lazy = + Lazy::new(|| ProtocolConfig::get_for_max_version().base_tx_cost_fixed()); #[tokio::test] async fn test_tx_less_than_minimum_gas_budget() { // This test creates a transaction that sets a gas_budget less than the minimum // transaction requirement. It's expected to fail early during transaction // handling phase. - let budget = *MIN_GAS_BUDGET - 1; - let result = execute_transfer(*MAX_GAS_BUDGET, budget, false).await; + let budget = *MIN_GAS_BUDGET_PRE_RGP - 1; + let result = execute_transfer(*MAX_GAS_BUDGET, budget, false, true).await; assert_eq!( UserInputError::try_from(result.response.unwrap_err()).unwrap(), UserInputError::GasBudgetTooLow { - gas_budget: budget, - min_budget: *MIN_GAS_BUDGET + gas_budget: budget * result.rgp, + min_budget: *MIN_GAS_BUDGET_PRE_RGP * result.rgp, } ); } @@ -47,7 +50,7 @@ async fn test_tx_more_than_maximum_gas_budget() { // budget (which could lead to overflow). It's expected to fail early during transaction // handling phase. let budget = *MAX_GAS_BUDGET + 1; - let result = execute_transfer(*MAX_GAS_BUDGET, budget, false).await; + let result = execute_transfer(*MAX_GAS_BUDGET, budget, false, false).await; assert_eq!( UserInputError::try_from(result.response.unwrap_err()).unwrap(), @@ -374,7 +377,7 @@ async fn test_computation_ok_oog_storage_minimal_ok() -> SuiResult { #[tokio::test] async fn test_computation_ok_oog_storage() -> SuiResult { const GAS_PRICE: u64 = 1001; - const BUDGET: u64 = 35_000; + const BUDGET: u64 = 1_002_000; let (sender, sender_key) = get_key_pair(); check_oog_transaction( sender, @@ -436,9 +439,9 @@ async fn test_tx_gas_balance_less_than_budget() { // This test creates a transaction that uses a gas object whose balance // is not even enough to pay for the gas budget. This should fail early // during handle transaction phase. - let gas_balance = *MIN_GAS_BUDGET - 1; - let budget = *MIN_GAS_BUDGET; - let result = execute_transfer_with_price(gas_balance, budget, 1, false).await; + let gas_balance = *MIN_GAS_BUDGET_PRE_RGP - 1; + let budget = *MIN_GAS_BUDGET_PRE_RGP; + let result = execute_transfer_with_price(gas_balance, budget, 1, false, true).await; assert!(matches!( UserInputError::try_from(result.response.unwrap_err()).unwrap(), UserInputError::GasBalanceTooLow { .. } @@ -449,14 +452,14 @@ async fn test_tx_gas_balance_less_than_budget() { async fn test_native_transfer_sufficient_gas() -> SuiResult { // This test does a native transfer with sufficient gas budget and balance. // It's expected to succeed. We check that gas was charged properly. - let result = execute_transfer(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, true).await; + let result = execute_transfer(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, true, false).await; let effects = result .response .unwrap() .into_effects_for_testing() .into_data(); let gas_cost = effects.gas_cost_summary(); - assert!(gas_cost.net_gas_usage() as u64 > *MIN_GAS_BUDGET); + assert!(gas_cost.net_gas_usage() as u64 > *MIN_GAS_BUDGET_PRE_RGP); assert!(gas_cost.computation_cost > 0); assert!(gas_cost.storage_cost > 0); // Removing genesis object does not have rebate. @@ -476,7 +479,8 @@ async fn test_native_transfer_sufficient_gas() -> SuiResult { #[tokio::test] async fn test_native_transfer_gas_price_is_used() { - let result = execute_transfer_with_price(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, 1, true).await; + let result = + execute_transfer_with_price(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, 1, true, false).await; let effects = result .response .unwrap() @@ -484,7 +488,8 @@ async fn test_native_transfer_gas_price_is_used() { .into_data(); let gas_summary_1 = effects.gas_cost_summary(); - let result = execute_transfer_with_price(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, 2, true).await; + let result = + execute_transfer_with_price(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, 2, true, false).await; let effects = result .response .unwrap() @@ -500,7 +505,7 @@ async fn test_native_transfer_gas_price_is_used() { // test overflow with insufficient gas let gas_balance = *MAX_GAS_BUDGET - 1; let gas_budget = *MAX_GAS_BUDGET; - let result = execute_transfer_with_price(gas_balance, gas_budget, 1, true).await; + let result = execute_transfer_with_price(gas_balance, gas_budget, 1, true, false).await; assert!(matches!( UserInputError::try_from(result.response.unwrap_err()).unwrap(), UserInputError::GasBalanceTooLow { .. } @@ -511,10 +516,10 @@ async fn test_native_transfer_gas_price_is_used() { async fn test_transfer_sui_insufficient_gas() { let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); let recipient = dbg_addr(2); + let authority_state = TestAuthorityBuilder::new().build().await; let gas_object_id = ObjectID::random(); - let gas_object = Object::with_id_owner_gas_for_testing(gas_object_id, sender, *MIN_GAS_BUDGET); + let gas_object = Object::with_id_owner_gas_for_testing(gas_object_id, sender, *MAX_GAS_BUDGET); let gas_object_ref = gas_object.compute_object_reference(); - let authority_state = TestAuthorityBuilder::new().build().await; authority_state.insert_genesis_object(gas_object).await; let rgp = authority_state.reference_gas_price_for_testing().unwrap(); @@ -524,7 +529,13 @@ async fn test_transfer_sui_insufficient_gas() { builder.finish() }; let kind = TransactionKind::ProgrammableTransaction(pt); - let data = TransactionData::new(kind, sender, gas_object_ref, *MIN_GAS_BUDGET, rgp); + let data = TransactionData::new( + kind, + sender, + gas_object_ref, + *MIN_GAS_BUDGET_PRE_RGP * rgp, + rgp, + ); let tx = to_sender_signed_transaction(data, &sender_key); let effects = send_and_confirm_transaction(&authority_state, tx) @@ -655,8 +666,8 @@ async fn test_native_transfer_insufficient_gas_reading_objects() { // This test creates a transfer transaction with a gas budget, that's more than // the minimum budget requirement, but not enough to even read the objects from db. // This will lead to failure in lock check step during handle transaction phase. - let balance = *MIN_GAS_BUDGET + 1; - let result = execute_transfer(balance, balance, true).await; + let balance = *MIN_GAS_BUDGET_PRE_RGP + 1; + let result = execute_transfer(*MAX_GAS_BUDGET, balance, true, true).await; // The transaction should still execute to effects, but with execution status as failure. let effects = result .response @@ -675,7 +686,7 @@ async fn test_native_transfer_insufficient_gas_execution() { // to finalize the transfer object mutation effects. It will fail during // execution phase, and hence gas object will still be mutated and all budget // will be charged. - let result = execute_transfer(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, true).await; + let result = execute_transfer(*MAX_GAS_BUDGET, *MAX_GAS_BUDGET, true, false).await; let total_gas = result .response .unwrap() @@ -684,7 +695,7 @@ async fn test_native_transfer_insufficient_gas_execution() { .gas_cost_summary() .gas_used(); let budget = total_gas - 1; - let result = execute_transfer(budget, budget, true).await; + let result = execute_transfer(budget, budget, true, false).await; let effects = result .response .unwrap() @@ -865,8 +876,8 @@ async fn test_move_call_gas() -> SuiResult { #[tokio::test] async fn test_tx_gas_price_less_than_reference_gas_price() { let gas_balance = *MAX_GAS_BUDGET; - let budget = *MIN_GAS_BUDGET; - let result = execute_transfer_with_price(gas_balance, budget, 0, false).await; + let budget = *MIN_GAS_BUDGET_PRE_RGP; + let result = execute_transfer_with_price(gas_balance, budget, 0, false, true).await; assert!(matches!( UserInputError::try_from(result.response.unwrap_err()).unwrap(), UserInputError::GasPriceUnderRGP { .. } @@ -877,10 +888,16 @@ struct TransferResult { pub authority_state: Arc, pub gas_object_id: ObjectID, pub response: SuiResult, + pub rgp: u64, } -async fn execute_transfer(gas_balance: u64, gas_budget: u64, run_confirm: bool) -> TransferResult { - execute_transfer_with_price(gas_balance, gas_budget, 1, run_confirm).await +async fn execute_transfer( + gas_balance: u64, + gas_budget: u64, + run_confirm: bool, + min_budget_pre_rgp: bool, +) -> TransferResult { + execute_transfer_with_price(gas_balance, gas_budget, 1, run_confirm, min_budget_pre_rgp).await } async fn execute_transfer_with_price( @@ -888,12 +905,18 @@ async fn execute_transfer_with_price( gas_budget: u64, rgp_multiple: u64, run_confirm: bool, + min_budget_pre_rgp: bool, ) -> TransferResult { let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); let object_id: ObjectID = ObjectID::random(); let recipient = dbg_addr(2); let authority_state = init_state_with_ids(vec![(sender, object_id)]).await; let rgp = authority_state.reference_gas_price_for_testing().unwrap() * rgp_multiple; + let gas_budget = if min_budget_pre_rgp { + gas_budget * rgp + } else { + gas_budget + }; let epoch_store = authority_state.load_epoch_store_one_call_per_task(); let gas_object_id = ObjectID::random(); let gas_object = Object::with_id_owner_gas_for_testing(gas_object_id, sender, gas_balance); @@ -936,5 +959,6 @@ async fn execute_transfer_with_price( authority_state, gas_object_id, response, + rgp, } } diff --git a/crates/sui-core/src/unit_tests/pay_sui_tests.rs b/crates/sui-core/src/unit_tests/pay_sui_tests.rs index da4ef582ee745..7430171f295bf 100644 --- a/crates/sui-core/src/unit_tests/pay_sui_tests.rs +++ b/crates/sui-core/src/unit_tests/pay_sui_tests.rs @@ -49,7 +49,7 @@ async fn test_pay_sui_failure_insufficient_gas_balance_one_input_coin() { vec![100, 100], sender, sender_key, - 2200, + 2200000, ) .await; @@ -57,7 +57,7 @@ async fn test_pay_sui_failure_insufficient_gas_balance_one_input_coin() { UserInputError::try_from(res.txn_result.unwrap_err()).unwrap(), UserInputError::GasBalanceTooLow { gas_balance: 2000, - needed_gas_amount: 2200, + needed_gas_amount: 2200000, } ); } @@ -65,7 +65,7 @@ async fn test_pay_sui_failure_insufficient_gas_balance_one_input_coin() { #[tokio::test] async fn test_pay_sui_failure_insufficient_total_balance_one_input_coin() { let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); - let coin1 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 2600); + let coin1 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 500100); let recipient1 = dbg_addr(1); let recipient2 = dbg_addr(2); @@ -75,7 +75,7 @@ async fn test_pay_sui_failure_insufficient_total_balance_one_input_coin() { vec![100, 100], sender, sender_key, - 2500, + 500000, ) .await; @@ -102,7 +102,7 @@ async fn test_pay_sui_failure_insufficient_gas_balance_multiple_input_coins() { vec![100, 100], sender, sender_key, - 2000, + 2000000, ) .await; @@ -110,7 +110,7 @@ async fn test_pay_sui_failure_insufficient_gas_balance_multiple_input_coins() { UserInputError::try_from(res.txn_result.unwrap_err()).unwrap(), UserInputError::GasBalanceTooLow { gas_balance: 1500, - needed_gas_amount: 2000, + needed_gas_amount: 2000000, } ); } @@ -118,8 +118,8 @@ async fn test_pay_sui_failure_insufficient_gas_balance_multiple_input_coins() { #[tokio::test] async fn test_pay_sui_failure_insufficient_total_balance_multiple_input_coins() { let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); - let coin1 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 14000); - let coin2 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 13000); + let coin1 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 204000); + let coin2 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 303000); let recipient1 = dbg_addr(1); let recipient2 = dbg_addr(2); @@ -129,7 +129,7 @@ async fn test_pay_sui_failure_insufficient_total_balance_multiple_input_coins() vec![4000, 4000], sender, sender_key, - 20000, + 500000, ) .await; assert_eq!( @@ -294,13 +294,13 @@ async fn test_pay_all_sui_failure_insufficient_gas_one_input_coin() { let coin1 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 1800); let recipient = dbg_addr(2); - let res = execute_pay_all_sui(vec![&coin1], recipient, sender, sender_key, 2000).await; + let res = execute_pay_all_sui(vec![&coin1], recipient, sender, sender_key, 2000000).await; assert_eq!( UserInputError::try_from(res.txn_result.unwrap_err()).unwrap(), UserInputError::GasBalanceTooLow { gas_balance: 1800, - needed_gas_amount: 2000, + needed_gas_amount: 2000000, } ); } @@ -311,13 +311,14 @@ async fn test_pay_all_sui_failure_insufficient_gas_budget_multiple_input_coins() let coin1 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 1000); let coin2 = Object::with_id_owner_gas_for_testing(ObjectID::random(), sender, 1000); let recipient = dbg_addr(2); - let res = execute_pay_all_sui(vec![&coin1, &coin2], recipient, sender, sender_key, 2500).await; + let res = + execute_pay_all_sui(vec![&coin1, &coin2], recipient, sender, sender_key, 2500000).await; assert_eq!( UserInputError::try_from(res.txn_result.unwrap_err()).unwrap(), UserInputError::GasBalanceTooLow { gas_balance: 2000, - needed_gas_amount: 2500, + needed_gas_amount: 2500000, } ); } diff --git a/crates/sui-cost-tables/Cargo.toml b/crates/sui-cost-tables/Cargo.toml deleted file mode 100644 index 5e159c32131df..0000000000000 --- a/crates/sui-cost-tables/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "sui-cost-tables" -version = "0.1.0" -authors = ["Mysten Labs "] -license = "Apache-2.0" -publish = false -edition = "2021" - -[dependencies] -move-binary-format.workspace = true -move-core-types.workspace = true -move-vm-types.workspace = true -move-vm-test-utils.workspace = true -workspace-hack = { version = "0.1", path = "../workspace-hack" } - -anyhow.workspace = true -serde.workspace = true -once_cell.workspace = true - - -# This feature will be removed soon, but it's useful to have right now so we -# can compare untiered vs tiered gas account in the MoveVM across the system. -[features] -# Uncomment this line and and the line below and then rebuild to turn on tiered gas metering. -default = ["tiered-gas"] -tiered-gas = ["move-vm-test-utils/tiered-gas"] diff --git a/crates/sui-cost-tables/src/bytecode_based/mod.rs b/crates/sui-cost-tables/src/bytecode_based/mod.rs deleted file mode 100644 index 6ccd2d23abe8f..0000000000000 --- a/crates/sui-cost-tables/src/bytecode_based/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -pub mod tables; -pub mod units_types; diff --git a/crates/sui-cost-tables/src/bytecode_based/tables.rs b/crates/sui-cost-tables/src/bytecode_based/tables.rs deleted file mode 100644 index 414062a6a18e2..0000000000000 --- a/crates/sui-cost-tables/src/bytecode_based/tables.rs +++ /dev/null @@ -1,783 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::ops::Mul; - -use move_binary_format::errors::{PartialVMError, PartialVMResult}; -use move_binary_format::file_format_common::Opcodes; -use move_core_types::gas_algebra::{ - AbstractMemorySize, InternalGas, InternalGasPerAbstractMemoryUnit, NumArgs, NumBytes, -}; -use move_core_types::language_storage::ModuleId; -use move_core_types::u256::U256; -use move_core_types::vm_status::StatusCode; -use move_vm_types::gas::{GasMeter, SimpleInstruction}; -use move_vm_types::views::{TypeView, ValueView}; -use once_cell::sync::Lazy; - -use crate::bytecode_based::units_types::{CostTable, Gas, GasCost}; -use move_binary_format::{ - file_format::{ - Bytecode, ConstantPoolIndex, FieldHandleIndex, FieldInstantiationIndex, - FunctionHandleIndex, FunctionInstantiationIndex, SignatureIndex, - StructDefInstantiationIndex, StructDefinitionIndex, - }, - file_format_common::instruction_key, -}; - -/// VM flat fee -pub const VM_FLAT_FEE: Gas = Gas::new(8_000); - -/// The size in bytes for a non-string or address constant on the stack -pub const CONST_SIZE: AbstractMemorySize = AbstractMemorySize::new(16); - -/// The size in bytes for a reference on the stack -pub const REFERENCE_SIZE: AbstractMemorySize = AbstractMemorySize::new(8); - -/// The size of a struct in bytes -pub const STRUCT_SIZE: AbstractMemorySize = AbstractMemorySize::new(2); - -/// For exists checks on data that doesn't exists this is the multiplier that is used. -pub const MIN_EXISTS_DATA_SIZE: AbstractMemorySize = AbstractMemorySize::new(100); - -static ZERO_COST_SCHEDULE: Lazy = Lazy::new(zero_cost_schedule); - -/// The Move VM implementation of state for gas metering. -/// -/// Initialize with a `CostTable` and the gas provided to the transaction. -/// Provide all the proper guarantees about gas metering in the Move VM. -/// -/// Every client must use an instance of this type to interact with the Move VM. -#[derive(Debug)] -pub struct GasStatus { - cost_table: CostTable, - gas_left: InternalGas, - charge: bool, -} - -impl GasStatus { - /// Initialize the gas state with metering enabled. - /// - /// Charge for every operation and fail when there is no more gas to pay for operations. - /// This is the instantiation that must be used when executing a user script. - pub fn new(cost_table: CostTable, gas_left: Gas) -> Self { - Self { - gas_left: gas_left.to_unit(), - cost_table, - charge: true, - } - } - - pub fn is_metered(&self) -> bool { - self.charge - } - - /// Initialize the gas state with metering disabled. - /// - /// It should be used by clients in very specific cases and when executing system - /// code that does not have to charge the user. - pub fn new_unmetered() -> Self { - Self { - gas_left: InternalGas::new(0), - cost_table: ZERO_COST_SCHEDULE.clone(), - charge: false, - } - } - - /// Return the `CostTable` behind this `GasStatus`. - pub fn cost_table(&self) -> &CostTable { - self.cost_table - } - - /// Return the gas left. - pub fn remaining_gas(&self) -> Gas { - self.gas_left.to_unit_round_down() - } - - /// Charge a given amount of gas and fail if not enough gas units are left. - pub fn deduct_gas(&mut self, amount: InternalGas) -> PartialVMResult<()> { - if !self.charge { - return Ok(()); - } - - match self.gas_left.checked_sub(amount) { - Some(gas_left) => { - self.gas_left = gas_left; - Ok(()) - } - None => { - self.gas_left = InternalGas::new(0); - Err(PartialVMError::new(StatusCode::OUT_OF_GAS)) - } - } - } - - fn charge_instr(&mut self, opcode: Opcodes) -> PartialVMResult<()> { - self.deduct_gas( - self.cost_table - .instruction_cost(opcode as u8) - .total() - .into(), - ) - } - - /// Charge an instruction over data with a given size and fail if not enough gas units are left. - fn charge_instr_with_size( - &mut self, - opcode: Opcodes, - size: AbstractMemorySize, - ) -> PartialVMResult<()> { - // Make sure that the size is always non-zero - let size = std::cmp::max(1.into(), size); - debug_assert!(size > 0.into()); - self.deduct_gas( - InternalGasPerAbstractMemoryUnit::new( - self.cost_table.instruction_cost(opcode as u8).total(), - ) - .mul(size), - ) - } - - pub fn set_metering(&mut self, enabled: bool) { - self.charge = enabled - } -} - -fn get_simple_instruction_opcode(instr: SimpleInstruction) -> Opcodes { - use Opcodes::*; - use SimpleInstruction::*; - - match instr { - Nop => NOP, - Ret => RET, - - BrTrue => BR_TRUE, - BrFalse => BR_FALSE, - Branch => BRANCH, - - LdU8 => LD_U8, - LdU16 => LD_U16, - LdU32 => LD_U32, - LdU64 => LD_U64, - LdU128 => LD_U128, - LdU256 => LD_U256, - LdTrue => LD_TRUE, - LdFalse => LD_FALSE, - - FreezeRef => FREEZE_REF, - MutBorrowLoc => MUT_BORROW_LOC, - ImmBorrowLoc => IMM_BORROW_LOC, - ImmBorrowField => IMM_BORROW_FIELD, - MutBorrowField => MUT_BORROW_FIELD, - ImmBorrowFieldGeneric => IMM_BORROW_FIELD_GENERIC, - MutBorrowFieldGeneric => MUT_BORROW_FIELD_GENERIC, - - CastU8 => CAST_U8, - CastU16 => CAST_U16, - CastU32 => CAST_U32, - CastU64 => CAST_U64, - CastU128 => CAST_U128, - CastU256 => CAST_U256, - - Add => ADD, - Sub => SUB, - Mul => MUL, - Mod => MOD, - Div => DIV, - - BitOr => BIT_OR, - BitAnd => BIT_AND, - Xor => XOR, - Shl => SHL, - Shr => SHR, - - Or => OR, - And => AND, - Not => NOT, - - Lt => LT, - Gt => GT, - Le => LE, - Ge => GE, - - Abort => ABORT, - } -} - -impl GasMeter for GasStatus { - /// Charge an instruction and fail if not enough gas units are left. - fn charge_simple_instr(&mut self, instr: SimpleInstruction) -> PartialVMResult<()> { - self.charge_instr(get_simple_instruction_opcode(instr)) - } - - fn charge_call( - &mut self, - _module_id: &ModuleId, - _func_name: &str, - args: impl ExactSizeIterator, - _num_locals: NumArgs, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - self.charge_instr_with_size(Opcodes::CALL, (args.len() as u64 + 1).into()) - } - - fn charge_call_generic( - &mut self, - _module_id: &ModuleId, - _func_name: &str, - ty_args: impl ExactSizeIterator, - args: impl ExactSizeIterator, - _num_locals: NumArgs, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - self.charge_instr_with_size( - Opcodes::CALL_GENERIC, - ((ty_args.len() + args.len() + 1) as u64).into(), - ) - } - - fn charge_ld_const(&mut self, size: NumBytes) -> PartialVMResult<()> { - self.charge_instr_with_size(Opcodes::LD_CONST, u64::from(size).into()) - } - - fn charge_copy_loc(&mut self, val: impl ValueView) -> PartialVMResult<()> { - self.charge_instr_with_size(Opcodes::COPY_LOC, val.legacy_abstract_memory_size()) - } - - fn charge_move_loc(&mut self, val: impl ValueView) -> PartialVMResult<()> { - self.charge_instr_with_size(Opcodes::MOVE_LOC, val.legacy_abstract_memory_size()) - } - - fn charge_store_loc(&mut self, val: impl ValueView) -> PartialVMResult<()> { - self.charge_instr_with_size(Opcodes::ST_LOC, val.legacy_abstract_memory_size()) - } - - fn charge_pack( - &mut self, - is_generic: bool, - args: impl ExactSizeIterator, - ) -> PartialVMResult<()> { - let field_count = AbstractMemorySize::new(args.len() as u64); - self.charge_instr_with_size( - if is_generic { - Opcodes::PACK_GENERIC - } else { - Opcodes::PACK - }, - args.fold(field_count, |acc, val| { - acc + val.legacy_abstract_memory_size() - }), - ) - } - - fn charge_unpack( - &mut self, - is_generic: bool, - args: impl ExactSizeIterator, - ) -> PartialVMResult<()> { - let field_count = AbstractMemorySize::new(args.len() as u64); - self.charge_instr_with_size( - if is_generic { - Opcodes::UNPACK_GENERIC - } else { - Opcodes::UNPACK - }, - args.fold(field_count, |acc, val| { - acc + val.legacy_abstract_memory_size() - }), - ) - } - - fn charge_read_ref(&mut self, ref_val: impl ValueView) -> PartialVMResult<()> { - self.charge_instr_with_size(Opcodes::READ_REF, ref_val.legacy_abstract_memory_size()) - } - - fn charge_write_ref( - &mut self, - val: impl ValueView, - _old_val: impl ValueView, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - self.charge_instr_with_size(Opcodes::WRITE_REF, val.legacy_abstract_memory_size()) - } - - fn charge_eq(&mut self, lhs: impl ValueView, rhs: impl ValueView) -> PartialVMResult<()> { - self.charge_instr_with_size( - Opcodes::EQ, - lhs.legacy_abstract_memory_size() + rhs.legacy_abstract_memory_size(), - ) - } - - fn charge_neq(&mut self, lhs: impl ValueView, rhs: impl ValueView) -> PartialVMResult<()> { - self.charge_instr_with_size( - Opcodes::NEQ, - lhs.legacy_abstract_memory_size() + rhs.legacy_abstract_memory_size(), - ) - } - - fn charge_borrow_global( - &mut self, - _is_mut: bool, - _is_generic: bool, - _ty: impl TypeView, - _is_success: bool, - ) -> PartialVMResult<()> { - Err(PartialVMError::new( - StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, - )) - } - - fn charge_exists( - &mut self, - _is_generic: bool, - _ty: impl TypeView, - _exists: bool, - ) -> PartialVMResult<()> { - Err(PartialVMError::new( - StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, - )) - } - - fn charge_move_from( - &mut self, - _is_generic: bool, - _ty: impl TypeView, - _val: Option, - ) -> PartialVMResult<()> { - Err(PartialVMError::new( - StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, - )) - } - - fn charge_move_to( - &mut self, - is_generic: bool, - _ty: impl TypeView, - val: impl ValueView, - is_success: bool, - ) -> PartialVMResult<()> { - use Opcodes::*; - - let op = if is_generic { MOVE_TO_GENERIC } else { MOVE_TO }; - - if is_success { - self.charge_instr_with_size(op, val.legacy_abstract_memory_size())?; - } - - Ok(()) - } - - fn charge_vec_pack<'a>( - &mut self, - _ty: impl TypeView + 'a, - args: impl ExactSizeIterator, - ) -> PartialVMResult<()> { - self.charge_instr_with_size(Opcodes::VEC_PACK, (args.len() as u64).into()) - } - - fn charge_vec_len(&mut self, _ty: impl TypeView) -> PartialVMResult<()> { - self.charge_instr(Opcodes::VEC_LEN) - } - - fn charge_vec_borrow( - &mut self, - is_mut: bool, - _ty: impl TypeView, - _is_success: bool, - ) -> PartialVMResult<()> { - use Opcodes::*; - - self.charge_instr(if is_mut { - VEC_MUT_BORROW - } else { - VEC_IMM_BORROW - }) - } - - fn charge_vec_push_back( - &mut self, - _ty: impl TypeView, - val: impl ValueView, - ) -> PartialVMResult<()> { - self.charge_instr_with_size(Opcodes::VEC_PUSH_BACK, val.legacy_abstract_memory_size()) - } - - fn charge_vec_pop_back( - &mut self, - _ty: impl TypeView, - _val: Option, - ) -> PartialVMResult<()> { - self.charge_instr(Opcodes::VEC_POP_BACK) - } - - fn charge_vec_unpack( - &mut self, - _ty: impl TypeView, - expect_num_elements: NumArgs, - _elems: impl ExactSizeIterator, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - self.charge_instr_with_size( - Opcodes::VEC_PUSH_BACK, - u64::from(expect_num_elements).into(), - ) - } - - fn charge_vec_swap(&mut self, _ty: impl TypeView) -> PartialVMResult<()> { - self.charge_instr(Opcodes::VEC_SWAP) - } - - fn charge_load_resource( - &mut self, - _loaded: Option<(NumBytes, impl ValueView)>, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - Ok(()) - } - - fn charge_native_function( - &mut self, - amount: InternalGas, - _ret_vals: Option>, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - self.deduct_gas(amount) - } - - fn charge_pop(&mut self, _popped_val: impl ValueView) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - Ok(()) - } - - fn charge_ld_const_after_deserialization( - &mut self, - _val: impl ValueView, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - Ok(()) - } - - fn charge_native_function_before_execution( - &mut self, - _ty_args: impl ExactSizeIterator, - _args: impl ExactSizeIterator, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - Ok(()) - } - - fn charge_drop_frame( - &mut self, - _locals: impl Iterator, - ) -> PartialVMResult<()> { - // TODO (Gas Maintenance) - Ok(()) - } - - fn remaining_gas(&self) -> InternalGas { - if !self.charge { - return InternalGas::new(u64::MAX); - } - self.gas_left - } -} - -pub fn new_from_instructions(mut instrs: Vec<(Bytecode, GasCost)>) -> CostTable { - instrs.sort_by_key(|cost| instruction_key(&cost.0)); - - if cfg!(debug_assertions) { - let mut instructions_covered = 0; - for (index, (instr, _)) in instrs.iter().enumerate() { - let key = instruction_key(instr); - if index == (key - 1) as usize { - instructions_covered += 1; - } - } - debug_assert!( - instructions_covered == Bytecode::VARIANT_COUNT, - "all instructions must be in the cost table" - ); - } - let instruction_table = instrs - .into_iter() - .map(|(_, cost)| cost) - .collect::>(); - CostTable { instruction_table } -} - -pub fn zero_cost_instruction_table() -> Vec<(Bytecode, GasCost)> { - use Bytecode::*; - - vec![ - (MoveTo(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), - ( - MoveToGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (MoveFrom(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), - ( - MoveFromGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (BrTrue(0), GasCost::new(0, 0)), - (WriteRef, GasCost::new(0, 0)), - (Mul, GasCost::new(0, 0)), - (MoveLoc(0), GasCost::new(0, 0)), - (And, GasCost::new(0, 0)), - (Pop, GasCost::new(0, 0)), - (BitAnd, GasCost::new(0, 0)), - (ReadRef, GasCost::new(0, 0)), - (Sub, GasCost::new(0, 0)), - (MutBorrowField(FieldHandleIndex::new(0)), GasCost::new(0, 0)), - ( - MutBorrowFieldGeneric(FieldInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (ImmBorrowField(FieldHandleIndex::new(0)), GasCost::new(0, 0)), - ( - ImmBorrowFieldGeneric(FieldInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (Add, GasCost::new(0, 0)), - (CopyLoc(0), GasCost::new(0, 0)), - (StLoc(0), GasCost::new(0, 0)), - (Ret, GasCost::new(0, 0)), - (Lt, GasCost::new(0, 0)), - (LdU8(0), GasCost::new(0, 0)), - (LdU16(0), GasCost::new(0, 0)), - (LdU32(0), GasCost::new(0, 0)), - (LdU64(0), GasCost::new(0, 0)), - (LdU128(0), GasCost::new(0, 0)), - (LdU256(U256::from(0u8)), GasCost::new(0, 0)), - (CastU8, GasCost::new(0, 0)), - (CastU16, GasCost::new(0, 0)), - (CastU32, GasCost::new(0, 0)), - (CastU64, GasCost::new(0, 0)), - (CastU128, GasCost::new(0, 0)), - (CastU256, GasCost::new(0, 0)), - (Abort, GasCost::new(0, 0)), - (MutBorrowLoc(0), GasCost::new(0, 0)), - (ImmBorrowLoc(0), GasCost::new(0, 0)), - (LdConst(ConstantPoolIndex::new(0)), GasCost::new(0, 0)), - (Ge, GasCost::new(0, 0)), - (Xor, GasCost::new(0, 0)), - (Shl, GasCost::new(0, 0)), - (Shr, GasCost::new(0, 0)), - (Neq, GasCost::new(0, 0)), - (Not, GasCost::new(0, 0)), - (Call(FunctionHandleIndex::new(0)), GasCost::new(0, 0)), - ( - CallGeneric(FunctionInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (Le, GasCost::new(0, 0)), - (Branch(0), GasCost::new(0, 0)), - (Unpack(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), - ( - UnpackGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (Or, GasCost::new(0, 0)), - (LdFalse, GasCost::new(0, 0)), - (LdTrue, GasCost::new(0, 0)), - (Mod, GasCost::new(0, 0)), - (BrFalse(0), GasCost::new(0, 0)), - (Exists(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), - ( - ExistsGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (BitOr, GasCost::new(0, 0)), - (FreezeRef, GasCost::new(0, 0)), - ( - MutBorrowGlobal(StructDefinitionIndex::new(0)), - GasCost::new(0, 0), - ), - ( - MutBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - ( - ImmBorrowGlobal(StructDefinitionIndex::new(0)), - GasCost::new(0, 0), - ), - ( - ImmBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (Div, GasCost::new(0, 0)), - (Eq, GasCost::new(0, 0)), - (Gt, GasCost::new(0, 0)), - (Pack(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), - ( - PackGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(0, 0), - ), - (Nop, GasCost::new(0, 0)), - (VecPack(SignatureIndex::new(0), 0), GasCost::new(0, 0)), - (VecLen(SignatureIndex::new(0)), GasCost::new(0, 0)), - (VecImmBorrow(SignatureIndex::new(0)), GasCost::new(0, 0)), - (VecMutBorrow(SignatureIndex::new(0)), GasCost::new(0, 0)), - (VecPushBack(SignatureIndex::new(0)), GasCost::new(0, 0)), - (VecPopBack(SignatureIndex::new(0)), GasCost::new(0, 0)), - (VecUnpack(SignatureIndex::new(0), 0), GasCost::new(0, 0)), - (VecSwap(SignatureIndex::new(0)), GasCost::new(0, 0)), - ] -} - -// Only used for genesis and for tests where we need a cost table and -// don't have a genesis storage state. -pub fn zero_cost_schedule() -> CostTable { - // The actual costs for the instructions in this table _DO NOT MATTER_. This is only used - // for genesis and testing, and for these cases we don't need to worry - // about the actual gas for instructions. The only thing we care about is having an entry - // in the gas schedule for each instruction. - let instrs = zero_cost_instruction_table(); - new_from_instructions(instrs) -} - -pub fn legacy_bytecode_instruction_costs() -> Vec<(Bytecode, GasCost)> { - use Bytecode::*; - vec![ - (MoveTo(StructDefinitionIndex::new(0)), GasCost::new(13, 1)), - ( - MoveToGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(27, 1), - ), - ( - MoveFrom(StructDefinitionIndex::new(0)), - GasCost::new(459, 1), - ), - ( - MoveFromGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(13, 1), - ), - (BrTrue(0), GasCost::new(1, 1)), - (WriteRef, GasCost::new(1, 1)), - (Mul, GasCost::new(1, 1)), - (MoveLoc(0), GasCost::new(1, 1)), - (And, GasCost::new(1, 1)), - (Pop, GasCost::new(1, 1)), - (BitAnd, GasCost::new(2, 1)), - (ReadRef, GasCost::new(1, 1)), - (Sub, GasCost::new(1, 1)), - (MutBorrowField(FieldHandleIndex::new(0)), GasCost::new(1, 1)), - ( - MutBorrowFieldGeneric(FieldInstantiationIndex::new(0)), - GasCost::new(1, 1), - ), - (ImmBorrowField(FieldHandleIndex::new(0)), GasCost::new(1, 1)), - ( - ImmBorrowFieldGeneric(FieldInstantiationIndex::new(0)), - GasCost::new(1, 1), - ), - (Add, GasCost::new(1, 1)), - (CopyLoc(0), GasCost::new(1, 1)), - (StLoc(0), GasCost::new(1, 1)), - (Ret, GasCost::new(638, 1)), - (Lt, GasCost::new(1, 1)), - (LdU8(0), GasCost::new(1, 1)), - (LdU16(0), GasCost::new(1, 1)), - (LdU32(0), GasCost::new(1, 1)), - (LdU64(0), GasCost::new(1, 1)), - (LdU128(0), GasCost::new(1, 1)), - (LdU256(U256::from(0u8)), GasCost::new(2, 1)), - (CastU8, GasCost::new(2, 1)), - (CastU16, GasCost::new(1, 1)), - (CastU32, GasCost::new(1, 1)), - (CastU64, GasCost::new(1, 1)), - (CastU128, GasCost::new(1, 1)), - (CastU256, GasCost::new(2, 1)), - (Abort, GasCost::new(1, 1)), - (MutBorrowLoc(0), GasCost::new(2, 1)), - (ImmBorrowLoc(0), GasCost::new(1, 1)), - (LdConst(ConstantPoolIndex::new(0)), GasCost::new(1, 1)), - (Ge, GasCost::new(1, 1)), - (Xor, GasCost::new(1, 1)), - (Shl, GasCost::new(2, 1)), - (Shr, GasCost::new(1, 1)), - (Neq, GasCost::new(1, 1)), - (Not, GasCost::new(1, 1)), - (Call(FunctionHandleIndex::new(0)), GasCost::new(1132, 1)), - ( - CallGeneric(FunctionInstantiationIndex::new(0)), - GasCost::new(582, 1), - ), - (Le, GasCost::new(2, 1)), - (Branch(0), GasCost::new(1, 1)), - (Unpack(StructDefinitionIndex::new(0)), GasCost::new(2, 1)), - ( - UnpackGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(2, 1), - ), - (Or, GasCost::new(2, 1)), - (LdFalse, GasCost::new(1, 1)), - (LdTrue, GasCost::new(1, 1)), - (Mod, GasCost::new(1, 1)), - (BrFalse(0), GasCost::new(1, 1)), - (Exists(StructDefinitionIndex::new(0)), GasCost::new(41, 1)), - ( - ExistsGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(34, 1), - ), - (BitOr, GasCost::new(2, 1)), - (FreezeRef, GasCost::new(1, 1)), - ( - MutBorrowGlobal(StructDefinitionIndex::new(0)), - GasCost::new(21, 1), - ), - ( - MutBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(15, 1), - ), - ( - ImmBorrowGlobal(StructDefinitionIndex::new(0)), - GasCost::new(23, 1), - ), - ( - ImmBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(14, 1), - ), - (Div, GasCost::new(3, 1)), - (Eq, GasCost::new(1, 1)), - (Gt, GasCost::new(1, 1)), - (Pack(StructDefinitionIndex::new(0)), GasCost::new(2, 1)), - ( - PackGeneric(StructDefInstantiationIndex::new(0)), - GasCost::new(2, 1), - ), - (Nop, GasCost::new(1, 1)), - (VecPack(SignatureIndex::new(0), 0), GasCost::new(84, 1)), - (VecLen(SignatureIndex::new(0)), GasCost::new(98, 1)), - (VecImmBorrow(SignatureIndex::new(0)), GasCost::new(1334, 1)), - (VecMutBorrow(SignatureIndex::new(0)), GasCost::new(1902, 1)), - (VecPushBack(SignatureIndex::new(0)), GasCost::new(53, 1)), - (VecPopBack(SignatureIndex::new(0)), GasCost::new(227, 1)), - (VecUnpack(SignatureIndex::new(0), 0), GasCost::new(572, 1)), - (VecSwap(SignatureIndex::new(0)), GasCost::new(1436, 1)), - ] -} - -pub static INITIAL_COST_SCHEDULE: Lazy = Lazy::new(|| { - let mut instrs = legacy_bytecode_instruction_costs(); - // Note that the DiemVM is expecting the table sorted by instruction order. - instrs.sort_by_key(|cost| instruction_key(&cost.0)); - - new_from_instructions(instrs) -}); - -pub fn initial_cost_schedule_for_unit_tests() -> move_vm_test_utils::gas_schedule::CostTable { - move_vm_test_utils::gas_schedule::CostTable { - instruction_table: INITIAL_COST_SCHEDULE - .clone() - .instruction_table - .into_iter() - .map(|gas_cost| { - move_vm_test_utils::gas_schedule::GasCost::new( - gas_cost.instruction_gas, - gas_cost.memory_gas, - ) - }) - .collect(), - } -} diff --git a/crates/sui-cost-tables/src/bytecode_based/units_types.rs b/crates/sui-cost-tables/src/bytecode_based/units_types.rs deleted file mode 100644 index c10e14306a8e9..0000000000000 --- a/crates/sui-cost-tables/src/bytecode_based/units_types.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::ops::Add; - -use move_core_types::gas_algebra::{GasQuantity, InternalGasUnit, ToUnit, ToUnitFractional}; -use serde::{Deserialize, Serialize}; - -pub enum GasUnit {} - -pub type Gas = GasQuantity; - -impl ToUnit for GasUnit { - const MULTIPLIER: u64 = 1000; -} - -impl ToUnitFractional for InternalGasUnit { - const NOMINATOR: u64 = 1; - const DENOMINATOR: u64 = 1000; -} - -/// The cost tables, keyed by the serialized form of the bytecode instruction. We use the -/// serialized form as opposed to the instruction enum itself as the key since this will be the -/// on-chain representation of bytecode instructions in the future. -#[derive(Clone, Debug, Serialize, PartialEq, Eq, Deserialize)] -pub struct CostTable { - pub instruction_table: Vec, -} - -impl CostTable { - #[inline] - pub fn instruction_cost(&self, instr_index: u8) -> &GasCost { - debug_assert!(instr_index > 0 && instr_index <= (self.instruction_table.len() as u8)); - &self.instruction_table[(instr_index - 1) as usize] - } -} - -/// The `GasCost` tracks: -/// - instruction cost: how much time/computational power is needed to perform the instruction -/// - memory cost: how much memory is required for the instruction, and storage overhead -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct GasCost { - pub instruction_gas: u64, - pub memory_gas: u64, -} - -impl GasCost { - pub fn new(instruction_gas: u64, memory_gas: u64) -> Self { - Self { - instruction_gas, - memory_gas, - } - } - - /// Convert a GasCost to a total gas charge in `InternalGas`. - #[inline] - pub fn total(&self) -> u64 { - self.instruction_gas.add(self.memory_gas) - } -} diff --git a/crates/sui-cost-tables/src/lib.rs b/crates/sui-cost-tables/src/lib.rs deleted file mode 100644 index 6a02c43b39429..0000000000000 --- a/crates/sui-cost-tables/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -pub mod natives_tables; - -#[cfg(not(feature = "tiered-gas"))] -pub mod bytecode_based; -#[cfg(not(feature = "tiered-gas"))] -pub use bytecode_based::tables as bytecode_tables; -#[cfg(not(feature = "tiered-gas"))] -pub use bytecode_based::units_types; - -#[cfg(feature = "tiered-gas")] -pub mod tier_based; -#[cfg(feature = "tiered-gas")] -pub use tier_based::tables as bytecode_tables; -#[cfg(feature = "tiered-gas")] -pub use tier_based::units_types; diff --git a/crates/sui-cost-tables/src/natives_tables.rs b/crates/sui-cost-tables/src/natives_tables.rs deleted file mode 100644 index d09fd2d61fca8..0000000000000 --- a/crates/sui-cost-tables/src/natives_tables.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// -// Native function costs -// -// TODO: need to refactor native gas calculation so it is extensible. Currently we -// have hardcoded here the stdlib natives. -#[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] -#[repr(u8)] -pub enum SuiNativeCostIndex { - EVENT_EMIT = 0, - - OBJECT_BYTES_TO_ADDR = 1, - OBJECT_BORROW_UUID = 2, - OBJECT_DELETE_IMPL = 3, - - TRANSFER_TRANSFER_INTERNAL = 4, - TRANSFER_FREEZE_OBJECT = 5, - TRANSFER_SHARE_OBJECT = 6, - - TX_CONTEXT_DERIVE_ID = 7, - TX_CONTEXT_NEW_SIGNER_FROM_ADDR = 8, -} diff --git a/crates/sui-cost-tables/src/tier_based/mod.rs b/crates/sui-cost-tables/src/tier_based/mod.rs deleted file mode 100644 index 6ccd2d23abe8f..0000000000000 --- a/crates/sui-cost-tables/src/tier_based/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -pub mod tables; -pub mod units_types; diff --git a/crates/sui-genesis-builder/src/lib.rs b/crates/sui-genesis-builder/src/lib.rs index a382165118db6..4dc47a55c9766 100644 --- a/crates/sui-genesis-builder/src/lib.rs +++ b/crates/sui-genesis-builder/src/lib.rs @@ -29,7 +29,7 @@ use sui_types::crypto::{ }; use sui_types::effects::{TransactionEffects, TransactionEvents}; use sui_types::epoch_data::EpochData; -use sui_types::gas::SuiGasStatus; +use sui_types::gas::GasCharger; use sui_types::gas_coin::GasCoin; use sui_types::governance::StakedSui; use sui_types::in_memory_storage::InMemoryStorage; @@ -805,12 +805,13 @@ fn create_genesis_transaction( .into_inner() }; + let genesis_digest = *genesis_transaction.digest(); // execute txn to effects let (effects, events, objects) = { let temporary_store = TemporaryStore::new( InMemoryStorage::new(Vec::new()), InputObjects::new(vec![]), - *genesis_transaction.digest(), + genesis_digest, protocol_config, ); @@ -823,7 +824,7 @@ fn create_genesis_transaction( let certificate_deny_set = HashSet::new(); let shared_object_refs = vec![]; let transaction_data = &genesis_transaction.data().intent_message().value; - let (kind, signer, gas) = transaction_data.execution_parts(); + let (kind, signer, _) = transaction_data.execution_parts(); let transaction_dependencies = BTreeSet::new(); let (inner_temp_store, effects, _execution_error) = executor .execute_transaction_to_effects( @@ -835,11 +836,10 @@ fn create_genesis_transaction( epoch_data.epoch_start_timestamp(), temporary_store, shared_object_refs, - SuiGasStatus::new_unmetered(protocol_config), - &gas, + &mut GasCharger::new_unmetered(genesis_digest), kind, signer, - *genesis_transaction.digest(), + genesis_digest, transaction_dependencies, ); assert!(inner_temp_store.objects.is_empty()); @@ -959,13 +959,13 @@ fn process_package( }) .collect(); + let genesis_digest = ctx.digest(); let mut temporary_store = TemporaryStore::new( store.clone(), InputObjects::new(loaded_dependencies), - ctx.digest(), + genesis_digest, protocol_config, ); - let mut gas_status = SuiGasStatus::new_unmetered(protocol_config); let module_bytes = modules .iter() .map(|m| { @@ -985,7 +985,7 @@ fn process_package( metrics, &mut temporary_store, ctx, - &mut gas_status, + &mut GasCharger::new_unmetered(genesis_digest), pt, )?; @@ -1080,7 +1080,7 @@ pub fn generate_genesis_system_object( metrics, &mut temporary_store, genesis_ctx, - &mut SuiGasStatus::new_unmetered(&protocol_config), + &mut GasCharger::new_unmetered(genesis_digest), pt, )?; diff --git a/crates/sui-json-rpc/src/unit_tests/rpc_server_tests.rs b/crates/sui-json-rpc/src/unit_tests/rpc_server_tests.rs index 075a6315920a8..48ead42b50812 100644 --- a/crates/sui-json-rpc/src/unit_tests/rpc_server_tests.rs +++ b/crates/sui-json-rpc/src/unit_tests/rpc_server_tests.rs @@ -110,7 +110,7 @@ async fn test_public_transfer_object() -> Result<(), anyhow::Error> { let gas = objects.clone().last().unwrap().object().unwrap().object_id; let transaction_bytes: TransactionBlockBytes = http_client - .transfer_object(address, obj, Some(gas), 10_000.into(), address) + .transfer_object(address, obj, Some(gas), 1_000_000.into(), address) .await?; let tx = cluster @@ -200,7 +200,7 @@ async fn test_publish() -> Result<(), anyhow::Error> { compiled_modules_bytes, dependencies, Some(gas.object_id), - 10000.into(), + 100_000_000.into(), ) .await?; @@ -259,7 +259,7 @@ async fn test_move_call() -> Result<(), anyhow::Error> { type_args![GAS::type_tag()]?, call_args!(coin.object_id, 10)?, Some(gas.object_id), - 10_000.into(), + 10_000_000.into(), None, ) .await?; diff --git a/crates/sui-json-rpc/src/unit_tests/transaction_tests.rs b/crates/sui-json-rpc/src/unit_tests/transaction_tests.rs index cf7cadb88f7de..37255f9e21e02 100644 --- a/crates/sui-json-rpc/src/unit_tests/transaction_tests.rs +++ b/crates/sui-json-rpc/src/unit_tests/transaction_tests.rs @@ -45,7 +45,7 @@ async fn test_get_transaction_block() -> Result<(), anyhow::Error> { address, oref.object_id, Some(gas_id), - 100_000.into(), + 1_000_000.into(), address, ) .await?; @@ -121,7 +121,7 @@ async fn test_get_raw_transaction() -> Result<(), anyhow::Error> { // Make a transfer transactions let transaction_bytes: TransactionBlockBytes = http_client - .transfer_object(address, object_to_transfer, None, 10_000.into(), address) + .transfer_object(address, object_to_transfer, None, 1_000_000.into(), address) .await?; let tx = cluster .wallet @@ -181,7 +181,7 @@ async fn test_get_fullnode_transaction() -> Result<(), anyhow::Error> { let oref = obj.object().unwrap(); let data = client .transaction_builder() - .transfer_object(address, oref.object_id, Some(gas_id), 100_000, address) + .transfer_object(address, oref.object_id, Some(gas_id), 1_000_000, address) .await?; let tx = cluster.wallet.sign_transaction(&data); diff --git a/crates/sui-move/Cargo.toml b/crates/sui-move/Cargo.toml index 7d674dc55adfb..90b1a5631b5f4 100644 --- a/crates/sui-move/Cargo.toml +++ b/crates/sui-move/Cargo.toml @@ -35,7 +35,6 @@ move-vm-runtime = { path = "../../external-crates/move/move-vm/runtime" } sui-move-natives = { path = "../../sui-execution/latest/sui-move-natives", package = "sui-move-natives-latest" } sui-core = { workspace = true, optional = true } -sui-cost-tables.workspace = true sui-move-build.workspace = true sui-protocol-config.workspace = true sui-types.workspace = true diff --git a/crates/sui-move/src/unit_test.rs b/crates/sui-move/src/unit_test.rs index 58741d3c79203..216c4c29f76f3 100644 --- a/crates/sui-move/src/unit_test.rs +++ b/crates/sui-move/src/unit_test.rs @@ -13,12 +13,11 @@ use move_vm_runtime::native_extensions::NativeContextExtensions; use once_cell::sync::Lazy; use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; use sui_core::authority::TemporaryStore; -use sui_cost_tables::bytecode_tables::initial_cost_schedule_for_unit_tests; use sui_move_natives::{object_runtime::ObjectRuntime, NativesCostTable}; use sui_protocol_config::ProtocolConfig; use sui_types::{ - digests::TransactionDigest, in_memory_storage::InMemoryStorage, metrics::LimitsMetrics, - transaction::InputObjects, + digests::TransactionDigest, gas_model::tables::initial_cost_schedule_for_unit_tests, + in_memory_storage::InMemoryStorage, metrics::LimitsMetrics, transaction::InputObjects, }; // Move unit tests will halt after executing this many steps. This is a protection to avoid divergence diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 5d6c0b42ba36c..9c4c32d629440 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1347,7 +1347,7 @@ "name": "Result", "value": { "minSupportedProtocolVersion": "1", - "maxSupportedProtocolVersion": "16", + "maxSupportedProtocolVersion": "17", "protocolVersion": "6", "featureFlags": { "advance_epoch_start_time_in_safe_mode": true, @@ -1366,6 +1366,7 @@ "package_upgrades": true, "scoring_decision_with_validity_cutoff": true, "simplified_unwrap_then_delete": false, + "txn_base_cost_as_multiplier": false, "zklogin_auth": false }, "attributes": { diff --git a/crates/sui-proc-macros/src/lib.rs b/crates/sui-proc-macros/src/lib.rs index 0119f905f9779..a095896565e8f 100644 --- a/crates/sui-proc-macros/src/lib.rs +++ b/crates/sui-proc-macros/src/lib.rs @@ -40,9 +40,7 @@ pub fn init_static_initializers(_args: TokenStream, item: TokenStream) -> TokenS ::sui_simulator::telemetry_subscribers::init_for_testing(); ::sui_simulator::sui_adapter::execution_engine::get_denied_certificates(); ::sui_simulator::sui_framework::BuiltInFramework::all_package_ids(); - ::sui_simulator::sui_types::gas::SuiGasStatus::new_unmetered( - &ProtocolConfig::get_for_min_version(), - ); + ::sui_simulator::sui_types::gas::SuiGasStatus::new_unmetered(); // For reasons I can't understand, LruCache causes divergent behavior the second // time one is constructed and inserted into, so construct one before the first diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index d0194c3a46997..aa54bdc5c2dee 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -10,7 +10,7 @@ use tracing::{info, warn}; /// The minimum and maximum protocol versions supported by this build. const MIN_PROTOCOL_VERSION: u64 = 1; -const MAX_PROTOCOL_VERSION: u64 = 16; +const MAX_PROTOCOL_VERSION: u64 = 17; // Record history of protocol version allocations here: // @@ -51,6 +51,11 @@ const MAX_PROTOCOL_VERSION: u64 = 16; // to no longer consult the object store when generating unwrapped_then_deleted in the // effects; this also allows us to stop including wrapped tombstones in accumulator. // Add self-matching prevention for deepbook. +// Version 17: Gas minimum charges moved to be a multiplier over the reference gas price. In this +// protocol version the multiplier is the same as the lowest bucket of computation +// such that the minimum transaction cost is the same as the minimum computation +// bucket. +// Add a feature flag to indicate the changes semantics of `base_tx_cost_fixed` #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -216,7 +221,6 @@ struct FeatureFlags { // Enable zklogin auth #[serde(skip_serializing_if = "is_false")] zklogin_auth: bool, - // How we order transactions coming out of consensus before sending to execution. #[serde(skip_serializing_if = "ConsensusTransactionOrdering::is_none")] consensus_transaction_ordering: ConsensusTransactionOrdering, @@ -230,6 +234,10 @@ struct FeatureFlags { // regardless of their previous state in the store. #[serde(skip_serializing_if = "is_false")] simplified_unwrap_then_delete: bool, + + // If true minimum txn charge is a multiplier of the gas price + #[serde(skip_serializing_if = "is_false")] + txn_base_cost_as_multiplier: bool, } fn is_false(b: &bool) -> bool { @@ -441,7 +449,6 @@ pub struct ProtocolConfig { object_runtime_max_num_store_entries_system_tx: Option, // === Execution gas costs ==== - // note: Option, @@ -789,6 +796,10 @@ impl ProtocolConfig { pub fn simplified_unwrap_then_delete(&self) -> bool { self.feature_flags.simplified_unwrap_then_delete } + + pub fn txn_base_cost_as_multiplier(&self) -> bool { + self.feature_flags.txn_base_cost_as_multiplier + } } #[cfg(not(msim))] @@ -1166,6 +1177,10 @@ impl ProtocolConfig { cfg.feature_flags.package_upgrades = true; cfg } + // This is the first protocol version currently possible. + // Mainnet starts with version 4. Previous versions are pre mainnet and have + // all been wiped out. + // Every other chain is after version 4. 4 => { let mut cfg = Self::get_for_version_impl(version - 1, chain); // Change reward slashing rate to 100%. @@ -1255,6 +1270,13 @@ impl ProtocolConfig { cfg.feature_flags.simplified_unwrap_then_delete = true; cfg } + 17 => { + let mut cfg = Self::get_for_version_impl(version - 1, chain); + cfg.feature_flags.txn_base_cost_as_multiplier = true; + // this is a multiplier of the gas price + cfg.base_tx_cost_fixed = Some(1_000); + cfg + } // Use this template when making changes: // // // modify an existing constant. diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_17.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_17.snap new file mode 100644 index 0000000000000..7338976e1385c --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_17.snap @@ -0,0 +1,185 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 17 +feature_flags: + package_upgrades: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + txn_base_cost_as_multiplier: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 128 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 6 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 256 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +gas_model_version: 6 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +scoring_decision_mad_divisor: 2.3 +scoring_decision_cutoff_value: 2.5 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_17.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_17.snap new file mode 100644 index 0000000000000..c1ec2a517ae65 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_17.snap @@ -0,0 +1,186 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 17 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + txn_base_cost_as_multiplier: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 128 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 6 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 256 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +gas_model_version: 6 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +scoring_decision_mad_divisor: 2.3 +scoring_decision_cutoff_value: 2.5 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_17.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_17.snap new file mode 100644 index 0000000000000..9b5d58dfb1401 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_17.snap @@ -0,0 +1,187 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 17 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + txn_base_cost_as_multiplier: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 128 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 6 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 256 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +gas_model_version: 6 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +scoring_decision_mad_divisor: 2.3 +scoring_decision_cutoff_value: 2.5 + diff --git a/crates/sui-replay/src/replay.rs b/crates/sui-replay/src/replay.rs index 4ea4c24cb7638..5460aaec5eafa 100644 --- a/crates/sui-replay/src/replay.rs +++ b/crates/sui-replay/src/replay.rs @@ -45,7 +45,7 @@ use sui_types::digests::TransactionDigest; use sui_types::error::ExecutionError; use sui_types::error::{SuiError, SuiResult}; use sui_types::executable_transaction::VerifiedExecutableTransaction; -use sui_types::gas::SuiGasStatus; +use sui_types::gas::{GasCharger, SuiGasStatus}; use sui_types::metrics::LimitsMetrics; use sui_types::object::{Data, Object, Owner}; use sui_types::storage::get_module_by_id; @@ -660,14 +660,10 @@ impl LocalExec { let metrics = self.metrics.clone(); // Extract the epoch start timestamp - let (epoch_start_timestamp, _) = self + let (epoch_start_timestamp, rgp) = self .get_epoch_start_timestamp_and_rgp(tx_info.executed_epoch) .await?; - // Create the gas status - let gas_status = - SuiGasStatus::new_with_budget(tx_info.gas_budget, tx_info.gas_price, protocol_config); - // Temp store for data let temporary_store = self.to_temporary_store(tx_digest, InputObjects::new(input_objects), protocol_config); @@ -678,22 +674,27 @@ impl LocalExec { // All prep done let expensive_checks = true; let certificate_deny_set = HashSet::new(); - let res = executor.execute_transaction_to_effects( - protocol_config, - metrics, - expensive_checks, - &certificate_deny_set, - &tx_info.executed_epoch, - epoch_start_timestamp, - temporary_store, - tx_info.shared_object_refs.clone(), - gas_status, - &tx_info.gas, - override_transaction_kind.unwrap_or(tx_info.kind.clone()), - tx_info.sender, - *tx_digest, - tx_info.dependencies.clone().into_iter().collect(), - ); + let res = if let Ok(gas_status) = + SuiGasStatus::new(tx_info.gas_budget, tx_info.gas_price, rgp, protocol_config) + { + executor.execute_transaction_to_effects( + protocol_config, + metrics, + expensive_checks, + &certificate_deny_set, + &tx_info.executed_epoch, + epoch_start_timestamp, + temporary_store, + tx_info.shared_object_refs.clone(), + &mut GasCharger::new(*tx_digest, tx_info.gas.clone(), gas_status, protocol_config), + override_transaction_kind.unwrap_or(tx_info.kind.clone()), + tx_info.sender, + *tx_digest, + tx_info.dependencies.clone().into_iter().collect(), + ) + } else { + unreachable!("Transaction was valid so gas status must be valid"); + }; let all_required_objects = self.storage.all_objects(); let effects = diff --git a/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs b/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs index 0c2531c6d5109..7b22ba6b79ef3 100644 --- a/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs +++ b/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs @@ -456,7 +456,7 @@ async fn test_failed_pay_sui() { sender, pt, vec![coin1, coin2], - 2000, + 2000000, rgp, true, ) diff --git a/crates/sui-swarm-config/src/network_config_builder.rs b/crates/sui-swarm-config/src/network_config_builder.rs index 10caffe53b5fb..4d1ad48b9b6a9 100644 --- a/crates/sui-swarm-config/src/network_config_builder.rs +++ b/crates/sui-swarm-config/src/network_config_builder.rs @@ -360,7 +360,7 @@ mod test { use sui_config::genesis::Genesis; use sui_protocol_config::{Chain, ProtocolConfig, ProtocolVersion}; use sui_types::epoch_data::EpochData; - use sui_types::gas::SuiGasStatus; + use sui_types::gas::GasCharger; use sui_types::in_memory_storage::InMemoryStorage; use sui_types::metrics::LimitsMetrics; use sui_types::sui_system_state::SuiSystemStateTrait; @@ -390,10 +390,11 @@ mod test { let genesis_transaction = genesis.transaction().clone(); + let genesis_digest = *genesis_transaction.digest(); let temporary_store = TemporaryStore::new( InMemoryStorage::new(Vec::new()), InputObjects::new(vec![]), - *genesis_transaction.digest(), + genesis_digest, &protocol_config, ); @@ -410,7 +411,7 @@ mod test { let epoch = EpochData::new_test(); let shared_object_refs = vec![]; let transaction_data = &genesis_transaction.data().intent_message().value; - let (kind, signer, gas) = transaction_data.execution_parts(); + let (kind, signer, _) = transaction_data.execution_parts(); let transaction_dependencies = BTreeSet::new(); let (_inner_temp_store, effects, _execution_error) = executor @@ -423,11 +424,10 @@ mod test { epoch.epoch_start_timestamp(), temporary_store, shared_object_refs, - SuiGasStatus::new_unmetered(&protocol_config), - &gas, + &mut GasCharger::new_unmetered(genesis_digest), kind, signer, - *genesis_transaction.digest(), + genesis_digest, transaction_dependencies, ); diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap index 85c222407d5fc..f15de47670752 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap @@ -6,7 +6,7 @@ ssfn_config_info: ~ validator_config_info: ~ parameters: chain_start_timestamp_ms: 0 - protocol_version: 16 + protocol_version: 17 allow_insertion_of_extra_objects: true epoch_duration_ms: 86400000 stake_subsidy_start_epoch: 0 diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap index fec80898dcd2b..77d6993494126 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap @@ -3,7 +3,7 @@ source: crates/sui-swarm-config/tests/snapshot_tests.rs expression: genesis.sui_system_object().into_genesis_version_for_tooling() --- epoch: 0 -protocol_version: 16 +protocol_version: 17 system_state_version: 1 validators: total_stake: 20000000000000000 @@ -240,13 +240,13 @@ validators: next_epoch_worker_address: ~ extra_fields: id: - id: "0x35ab5c7b2cbb403875f3f58e5de4b2ab75cb57f925437484ae4f784d5f8ffe4a" + id: "0xff24a10d47c774ed30fbd5ea0d2fa701ef59fcc3d94c53bbc5898ebdc7bdf563" size: 0 voting_power: 10000 - operation_cap_id: "0x5b0446c736f0e0c31c9ff916dd5a0e571f86a0bd429aab8f349d8a15e05a9158" + operation_cap_id: "0xbb5eaee9ab9cede0a472996f1a80055be159e42c350f34680ffb0ef1106b8dfa" gas_price: 1000 staking_pool: - id: "0x2e157ce3163d0252891ae4e9eff3faf72da249e175fa6c19bb0ccc966972b5a2" + id: "0x362b4b67eba2c28c445bffe7b2b9032061546c0961e9153de06cca98983f655d" activation_epoch: 0 deactivation_epoch: ~ sui_balance: 20000000000000000 @@ -254,14 +254,14 @@ validators: value: 0 pool_token_balance: 20000000000000000 exchange_rates: - id: "0x912e8bd191540860bc1b5cf79648f5967bb831d3200fe6fd71621a46b63f71de" + id: "0xef82a5bbc64aee169f695e3540a0eba0d3da22c3da4a50648de22e0bcf8adfd7" size: 1 pending_stake: 0 pending_total_sui_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0xf8995e8f0fde0063d088cf2772f254b99f9cc2e65d01b4842156b4b570ce1e40" + id: "0x1cce2237c73ec7c9be3edd00aac74ad4927c6097375517d574b5d876b427f3ea" size: 0 commission_rate: 200 next_epoch_stake: 20000000000000000 @@ -269,27 +269,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0x1525c1163f1760d3f27e63d5b19a285d8b076914d9632b07b8972204a2578fb9" + id: "0x39078a7069ea994aa11831dfe1d2f929fe99672b4830050e846710d05a0821d6" size: 0 pending_active_validators: contents: - id: "0x4cdfe30f2189d5e98d97c2085f7af4c00d1165039642e171a7c8ca01f4a2a35e" + id: "0x9ebd541488731fe181e6f69b5fd59e8fa85d272be41e77d9f76d87f26eac0ebf" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x5319e3dfeb443ca2f4c7b4513688b4eda3c55a9dc60c6fdcb3751560e6330366" + id: "0x8ac934be4afa0fe38ad53d648906746a019d96251c5d22fbbcc0c3b87680fb24" size: 1 inactive_validators: - id: "0xd4a945e3b4e9d7ba6e70a81d48c423fa8206c62dc6be45bb4aad66dbba31316b" + id: "0xd20ad9f9bad31a826ac84614a15026d77b22aff5d6a666d957f34d952dfbd4d7" size: 0 validator_candidates: - id: "0x7138aadfce24dc09fddaff9501da46a28a7eb40830e179aebb04456cdf2fe6e3" + id: "0xedd5ee7906e8e91b958c62f8015825c332df9f17c4a73d59631f3d34807a3cb5" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0xcc45759e54644412ad3ae3ba837d290c9894e3fabe311fc388f9af23968c8f9d" + id: "0x19af2f53489081c25337738817db05e2ca01fc67f4faac22df3c8e3e15c6d37c" size: 0 storage_fund: total_object_storage_rebates: @@ -306,7 +306,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0xf574363060a0975f269741d141069e9e6521e4ff7879e4c2ca18e263c94fb518" + id: "0x1ff328491000e9d0ff743dd40a10c2ed9bb22659e3349b9dc95ee2c1f6c56fff" size: 0 reference_gas_price: 1000 validator_report_records: @@ -320,7 +320,7 @@ stake_subsidy: stake_subsidy_decrease_rate: 1000 extra_fields: id: - id: "0xeb4185e1d9642c8c9d13a4c00d90888ebde075a436d240587956779d2e09b719" + id: "0xdf56cf3b92223391d6a3b5e2b60b5280caad27298b8720c9e978f4a4ae3c42a8" size: 0 safe_mode: false safe_mode_storage_rewards: @@ -332,6 +332,6 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0x2aacedc6d2d264d1d4349445f567572ecbac6b22813e77c28fa16a1f25e19592" + id: "0xbbfb4388254d9c7f9ff7391e2f9c2bc3a8b234466f839a79779128d8d700490b" size: 0 diff --git a/crates/sui-types/Cargo.toml b/crates/sui-types/Cargo.toml index 75b2c0ac53be4..6d3eacfca12d3 100644 --- a/crates/sui-types/Cargo.toml +++ b/crates/sui-types/Cargo.toml @@ -42,11 +42,11 @@ move-command-line-common.workspace = true move-core-types.workspace = true move-disassembler.workspace = true move-ir-types.workspace = true +move-vm-test-utils.workspace = true move-vm-types.workspace = true narwhal-config.workspace = true narwhal-crypto.workspace = true -sui-cost-tables.workspace = true sui-protocol-config.workspace = true shared-crypto.workspace = true mysten-network.workspace = true diff --git a/crates/sui-types/src/gas.rs b/crates/sui-types/src/gas.rs index 1cf78015f5310..44f7f0d32529d 100644 --- a/crates/sui-types/src/gas.rs +++ b/crates/sui-types/src/gas.rs @@ -2,27 +2,345 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::effects::{TransactionEffects, TransactionEffectsAPI}; -use crate::sui_serde::BigInt; -use crate::sui_serde::Readable; use crate::{ - error::{ExecutionError, UserInputError, UserInputResult}, - gas_model::gas_v1::{self, SuiCostTable as SuiCostTableV1, SuiGasStatus as SuiGasStatusV1}, - gas_model::gas_v2::{self, SuiCostTable as SuiCostTableV2, SuiGasStatus as SuiGasStatusV2}, - object::Object, + base_types::{ObjectID, ObjectRef}, + digests::TransactionDigest, + effects::{TransactionEffects, TransactionEffectsAPI}, + error::{ExecutionError, SuiResult, UserInputError, UserInputResult}, + gas_model::{gas_v2::SuiGasStatus as SuiGasStatusV2, tables::GasStatus}, + is_system_package, + object::{Data, Object}, + storage::{DeleteKindWithOldVersion, WriteKind}, + sui_serde::{BigInt, Readable}, + temporary_store::TemporaryStore, }; use enum_dispatch::enum_dispatch; use itertools::MultiUnzip; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use sui_cost_tables::bytecode_tables::GasStatus; use sui_protocol_config::ProtocolConfig; +use tracing::trace; -sui_macros::checked_arithmetic! { +// sui_macros::checked_arithmetic! { + +/// Tracks all gas operations for a single transaction. +/// This is the main entry point for gas accounting. +/// All the information about gas is stored in this object. +/// The objective here is two-fold: +/// 1- Isolate al version info into a single entry point. This file and the other gas +/// related files are the only one that check for gas version. +/// 2- Isolate all gas accounting into a single implementation. Gas objects are not +/// passed around, and they are retrieved from this instance. +#[derive(Debug)] +pub struct GasCharger { + tx_digest: TransactionDigest, + gas_model_version: u64, + gas_coins: Vec, + // this is the the first gas coin in `gas_coins` and the one that all others will + // be smashed into. It can be None for system transactions when `gas_coins` is empty. + smashed_gas_coin: Option, + gas_status: SuiGasStatus, +} + +impl GasCharger { + pub fn new( + tx_digest: TransactionDigest, + gas_coins: Vec, + gas_status: SuiGasStatus, + protocol_config: &ProtocolConfig, + ) -> Self { + let gas_model_version = protocol_config.gas_model_version(); + Self { + tx_digest, + gas_model_version, + gas_coins, + smashed_gas_coin: None, + gas_status, + } + } + + pub fn new_unmetered(tx_digest: TransactionDigest) -> Self { + Self { + tx_digest, + gas_model_version: 6, // pick any of the latest, it should not matter + gas_coins: vec![], + smashed_gas_coin: None, + gas_status: SuiGasStatus::new_unmetered(), + } + } + + // TODO: there is only one caller to this function that should not exist otherwise. + // Explore way to remove it. + pub(crate) fn gas_coins(&self) -> &[ObjectRef] { + &self.gas_coins + } + + // Return the logical gas coin for this transactions or None if no gas coin was present + // (system transactions). + pub fn gas_coin(&self) -> Option { + self.smashed_gas_coin + } + + pub fn gas_budget(&self) -> u64 { + self.gas_status.gas_budget() + } + + pub fn unmetered_storage_rebate(&self) -> u64 { + self.gas_status.unmetered_storage_rebate() + } + + pub fn no_charges(&self) -> bool { + self.gas_status.gas_used() == 0 + && self.gas_status.storage_rebate() == 0 + && self.gas_status.storage_gas_units() == 0 + } + + pub fn is_unmetered(&self) -> bool { + self.gas_status.is_unmetered() + } + + pub fn move_gas_status(&mut self) -> &mut GasStatus { + self.gas_status.move_gas_status() + } + + pub fn summary(&self) -> GasCostSummary { + self.gas_status.summary() + } + + // This function is called when the transaction is about to be executed. + // It will smash all gas coins into a single one and set the logical gas coin + // to be the first one in the list. + // After this call, `gas_coin` will return it id of the gas coin. + // This function panics if errors are found while operation on the gas coins. + // Transaction and certificate input checks must have insured that all gas coins + // are correct. + pub fn smash_gas(&mut self, temporary_store: &mut TemporaryStore<'_>) { + let gas_coin_count = self.gas_coins.len(); + if gas_coin_count == 0 { + return; // self.smashed_gas_coin is None + } + // set the first coin to be the transaction only gas coin. + // All others will be smashed into this one. + let gas_coin_id = self.gas_coins[0].0; + self.smashed_gas_coin = Some(gas_coin_id); + if gas_coin_count == 1 { + return; + } + // sum the value of all gas coins + let new_balance = self + .gas_coins + .iter() + .map(|obj_ref| { + let obj = temporary_store.objects().get(&obj_ref.0).unwrap(); + let Data::Move(move_obj) = &obj.data else { + return Err(ExecutionError::invariant_violation( + "Provided non-gas coin object as input for gas!" + )); + }; + if !move_obj.type_().is_gas_coin() { + return Err(ExecutionError::invariant_violation( + "Provided non-gas coin object as input for gas!", + )); + } + Ok(move_obj.get_coin_value_unsafe()) + }) + .collect::, ExecutionError>>() + // transaction and certificate input checks must have insured that all gas coins + // are valid + .unwrap_or_else(|_| { + panic!( + "Invariant violation: non-gas coin object as input for gas in txn {}", + self.tx_digest + ) + }) + .iter() + .sum(); + let mut primary_gas_object = temporary_store + .objects() + .get(&gas_coin_id) + // unwrap should be safe because we checked that this exists in `self.objects()` above + .unwrap_or_else(|| { + panic!( + "Invariant violation: gas coin not found in store in txn {}", + self.tx_digest + ) + }) + .clone(); + // delete all gas objects except the primary_gas_object + for (id, version, _digest) in &self.gas_coins[1..] { + debug_assert_ne!(*id, primary_gas_object.id()); + temporary_store.delete_object(id, DeleteKindWithOldVersion::Normal(*version)); + } + primary_gas_object + .data + .try_as_move_mut() + // unwrap should be safe because we checked that the primary gas object was a coin object above. + .unwrap_or_else(|| { + panic!( + "Invariant violation: invalid coin object in txn {}", + self.tx_digest + ) + }) + .set_coin_value_unsafe(new_balance); + temporary_store.write_object(primary_gas_object, WriteKind::Mutate); + } + + // + // Gas charging operations + // + + pub fn track_storage_mutation(&mut self, new_size: usize, storage_rebate: u64) -> u64 { + self.gas_status + .track_storage_mutation(new_size, storage_rebate) + } + + pub fn reset_storage_cost_and_rebate(&mut self) { + self.gas_status.reset_storage_cost_and_rebate(); + } + + pub fn charge_publish_package(&mut self, size: usize) -> Result<(), ExecutionError> { + self.gas_status.charge_publish_package(size) + } + + pub fn charge_input_objects( + &mut self, + temporary_store: &TemporaryStore<'_>, + ) -> Result<(), ExecutionError> { + let objects = temporary_store.objects(); + // TODO: Charge input object count. + let _object_count = objects.len(); + // Charge bytes read + let total_size = temporary_store + .objects() + .iter() + // don't charge for loading Sui Framework or Move stdlib + .filter(|(id, _)| !is_system_package(**id)) + .map(|(_, obj)| obj.object_size_for_gas_metering()) + .sum(); + self.gas_status.charge_storage_read(total_size) + } + + /// Resets any mutations, deletions, and events recorded in the store, as well as any storage costs and + /// rebates, then Re-runs gas smashing. Effects on store are now as if we were about to begin execution + pub fn reset(&mut self, temporary_store: &mut TemporaryStore<'_>) { + temporary_store.drop_writes(); + self.gas_status.reset_storage_cost_and_rebate(); + self.smash_gas(temporary_store); + } + + /// Entry point for gas charging. + /// 1. Compute tx storage gas costs and tx storage rebates, update storage_rebate field of + /// mutated objects + /// 2. Deduct computation gas costs and storage costs, credit storage rebates. + /// The happy path of this function follows (1) + (2) and is fairly simple. + /// Most of the complexity is in the unhappy paths: + /// - if execution aborted before calling this function, we have to dump all writes + + /// re-smash gas, then charge for storage + /// - if we run out of gas while charging for storage, we have to dump all writes + + /// re-smash gas, then charge for storage again + pub fn charge_gas( + &mut self, + temporary_store: &mut TemporaryStore<'_>, + execution_result: &mut Result, + ) -> GasCostSummary { + // at this point, we have done *all* charging for computation, + // but have not yet set the storage rebate or storage gas units + debug_assert!(self.gas_status.storage_rebate() == 0); + debug_assert!(self.gas_status.storage_gas_units() == 0); + + if self.smashed_gas_coin.is_some() { + // bucketize computation cost + if let Err(err) = self.gas_status.bucketize_computation() { + if execution_result.is_ok() { + *execution_result = Err(err); + } + } + + // On error we need to dump writes, deletes, etc before charging storage gas + if execution_result.is_err() { + self.reset(temporary_store); + } + } + + // compute and collect storage charges + temporary_store.ensure_gas_and_input_mutated(self); + temporary_store.collect_storage_and_rebate(self); + + // system transactions (None smashed_gas_coin) do not have gas and so do not charge + // for storage, however they track storage values to check for conservation rules + if let Some(gas_object_id) = self.smashed_gas_coin { + if Self::dont_charge_budget_on_storage_oog(self.gas_model_version) { + self.handle_storage_and_rebate_v2(temporary_store, execution_result) + } else { + self.handle_storage_and_rebate_v1(temporary_store, execution_result) + } + + let cost_summary = self.gas_status.summary(); + let gas_used = cost_summary.net_gas_usage(); + + let mut gas_object = temporary_store.read_object(&gas_object_id).unwrap().clone(); + deduct_gas(&mut gas_object, gas_used); + trace!(gas_used, gas_obj_id =? gas_object.id(), gas_obj_ver =? gas_object.version(), "Updated gas object"); + + temporary_store.write_object(gas_object, WriteKind::Mutate); + cost_summary + } else { + GasCostSummary::default() + } + } + + fn handle_storage_and_rebate_v1( + &mut self, + temporary_store: &mut TemporaryStore<'_>, + execution_result: &mut Result, + ) { + if let Err(err) = self.gas_status.charge_storage_and_rebate() { + self.reset(temporary_store); + self.gas_status.adjust_computation_on_out_of_gas(); + temporary_store.ensure_gas_and_input_mutated(self); + temporary_store.collect_rebate(self); + if execution_result.is_ok() { + *execution_result = Err(err); + } + } + } + + fn handle_storage_and_rebate_v2( + &mut self, + temporary_store: &mut TemporaryStore<'_>, + execution_result: &mut Result, + ) { + if let Err(err) = self.gas_status.charge_storage_and_rebate() { + // we run out of gas charging storage, reset and try charging for storage again. + // Input objects are touched and so they have a storage cost + self.reset(temporary_store); + temporary_store.ensure_gas_and_input_mutated(self); + temporary_store.collect_storage_and_rebate(self); + if let Err(err) = self.gas_status.charge_storage_and_rebate() { + // we run out of gas attempting to charge for the input objects exclusively, + // deal with this edge case by not charging for storage + self.reset(temporary_store); + self.gas_status.adjust_computation_on_out_of_gas(); + temporary_store.ensure_gas_and_input_mutated(self); + temporary_store.collect_rebate(self); + if execution_result.is_ok() { + *execution_result = Err(err); + } + } else if execution_result.is_ok() { + *execution_result = Err(err); + } + } + } + + // If true do not charge the entire budget on storage OOG + pub(crate) fn dont_charge_budget_on_storage_oog(gas_model_version: u64) -> bool { + gas_model_version >= 4 + } +} #[enum_dispatch] -pub trait SuiGasStatusAPI { +pub(crate) trait SuiGasStatusAPI { fn is_unmetered(&self) -> bool; fn move_gas_status(&mut self) -> &mut GasStatus; fn bucketize_computation(&mut self) -> Result<(), ExecutionError>; @@ -34,138 +352,71 @@ pub trait SuiGasStatusAPI { fn gas_used(&self) -> u64; fn reset_storage_cost_and_rebate(&mut self); fn charge_storage_read(&mut self, size: usize) -> Result<(), ExecutionError>; - fn charge_storage_mutation( - &mut self, - new_size: usize, - storage_rebate: u64, - ) -> Result; fn charge_publish_package(&mut self, size: usize) -> Result<(), ExecutionError>; fn track_storage_mutation(&mut self, new_size: usize, storage_rebate: u64) -> u64; fn charge_storage_and_rebate(&mut self) -> Result<(), ExecutionError>; fn adjust_computation_on_out_of_gas(&mut self); } +/// Version aware enum for gas status. #[enum_dispatch(SuiGasStatusAPI)] +#[derive(Debug)] pub enum SuiGasStatus { - V1(SuiGasStatusV1), + // V1 does not exists any longer as it was a pre mainnet version. + // So we start the enum from V2 V2(SuiGasStatusV2), } impl SuiGasStatus { - pub fn new_with_budget(gas_budget: u64, gas_price: u64, config: &ProtocolConfig) -> Self { - if config.gas_model_version() <= 1 { - Self::V1(SuiGasStatusV1::new_with_budget( - gas_budget, - gas_price, - config, - )) - } else { - Self::V2(SuiGasStatusV2::new_with_budget( - gas_budget, - gas_price, - config, - )) - } - } - - pub fn new_unmetered(config: &ProtocolConfig) -> Self { - if config.gas_model_version() <= 1 { - Self::V1(SuiGasStatusV1::new_unmetered()) - } else { - Self::V2(SuiGasStatusV2::new_unmetered()) - } - } -} - -pub enum SuiCostTable { - V1(SuiCostTableV1), - V2(SuiCostTableV2), -} + pub fn new( + gas_budget: u64, + gas_price: u64, + reference_gas_price: u64, + config: &ProtocolConfig, + ) -> SuiResult { + // Common checks. We may pull them into version specific status as needed, but they + // are unlikely to change. -impl SuiCostTable { - pub fn new(config: &ProtocolConfig) -> Self { - if config.gas_model_version() <= 1 { - Self::V1(SuiCostTableV1::new(config)) - } else { - Self::V2(SuiCostTableV2::new(config)) + // gas price must be bigger or equal to reference gas price + if gas_price < reference_gas_price { + return Err(UserInputError::GasPriceUnderRGP { + gas_price, + reference_gas_price, + } + .into()); } - } - - pub fn new_for_testing() -> Self { - Self::new(&ProtocolConfig::get_for_max_version()) - } - - pub fn unmetered(config: &ProtocolConfig) -> Self { - if config.gas_model_version() <= 1 { - Self::V1(SuiCostTableV1::unmetered()) - } else { - Self::V2(SuiCostTableV2::unmetered()) + if Self::gas_price_too_high(config.gas_model_version()) + && gas_price >= config.max_gas_price() + { + return Err(UserInputError::GasPriceTooHigh { + max_gas_price: config.max_gas_price(), + } + .into()); } - } - pub fn max_gas_budget(&self) -> u64 { - match self { - Self::V1(cost_table) => cost_table.max_gas_budget, - Self::V2(cost_table) => cost_table.max_gas_budget, - } + Ok(Self::V2(SuiGasStatusV2::new_with_budget( + gas_budget, + gas_price, + reference_gas_price, + config, + ))) } - pub fn min_gas_budget(&self) -> u64 { - match self { - Self::V1(cost_table) => cost_table.min_gas_budget_external(), - Self::V2(cost_table) => cost_table.min_transaction_cost, - } + pub fn new_unmetered() -> Self { + Self::V2(SuiGasStatusV2::new_unmetered()) } - // Check whether gas arguments are legit: - // 1. Gas object has an address owner. - // 2. Gas budget is between min and max budget allowed - pub fn check_gas_balance( - &self, - gas_object: &Object, - more_gas_objs: Vec<&Object>, - gas_budget: u64, - gas_price: u64, - ) -> UserInputResult { + // This is the only public API on SuiGasStatus, all other gas related operations should + // go through `GasCharger` + pub fn check_gas_balance(&self, gas_objs: &[&Object], gas_budget: u64) -> UserInputResult { match self { - Self::V1(cost_table) => gas_v1::check_gas_balance( - gas_object, - more_gas_objs, - gas_budget, - gas_price, - cost_table, - ), - Self::V2(cost_table) => gas_v2::check_gas_balance( - gas_object, - more_gas_objs, - gas_budget, - cost_table, - ), + Self::V2(status) => status.check_gas_balance(gas_objs, gas_budget), } } - pub fn into_gas_status_for_testing( - self, - gas_budget: u64, - gas_price: u64, - storage_price: u64, - gas_rounding_step: Option, - ) -> SuiGasStatus { - match self { - Self::V1(cost_table) => SuiGasStatus::V1(SuiGasStatusV1::new_for_testing( - gas_budget, - gas_price, - storage_price, - cost_table, - )), - Self::V2(cost_table) => SuiGasStatus::V2(SuiGasStatusV2::new_for_testing( - gas_budget, - gas_price, - storage_price, - gas_rounding_step, - cost_table, - )), - } + // If true enable the check for gas price too high + fn gas_price_too_high(gas_model_version: u64) -> bool { + gas_model_version >= 4 } } @@ -217,7 +468,12 @@ pub struct GasCostSummary { } impl GasCostSummary { - pub fn new(computation_cost: u64, storage_cost: u64, storage_rebate: u64, non_refundable_storage_fee: u64) -> GasCostSummary { + pub fn new( + computation_cost: u64, + storage_cost: u64, + storage_rebate: u64, + non_refundable_storage_fee: u64, + ) -> GasCostSummary { GasCostSummary { computation_cost, storage_cost, @@ -284,17 +540,9 @@ impl std::fmt::Display for GasCostSummary { } } -/// Subtract the gas balance of \p gas_object by \p amount. -/// This function should never fail, since we checked that the budget is always -/// less than balance, and the amount is capped at the budget. - -pub fn deduct_gas_legacy(gas_object: &mut Object, deduct_amount: u64, rebate_amount: u64) { - // The object must be a gas coin as we have checked in transaction handle phase. - let gas_coin = gas_object.data.try_as_move_mut().unwrap(); - let balance = gas_coin.get_coin_value_unsafe(); - assert!(balance >= deduct_amount); - gas_coin.set_coin_value_unsafe(balance + rebate_amount - deduct_amount) -} +// +// Helper functions to deal with gas coins operations. +// pub fn deduct_gas(gas_object: &mut Object, charge_or_rebate: i64) { // The object must be a gas coin as we have checked in transaction handle phase. @@ -309,19 +557,12 @@ pub fn deduct_gas(gas_object: &mut Object, charge_or_rebate: i64) { gas_coin.set_coin_value_unsafe(new_balance) } -pub fn refund_gas(gas_object: &mut Object, amount: u64) { - // The object must be a gas coin as we have checked in transaction handle phase. - let gas_coin = gas_object.data.try_as_move_mut().unwrap(); - let balance = gas_coin.get_coin_value_unsafe(); - gas_coin.set_coin_value_unsafe(balance + amount) -} - pub fn get_gas_balance(gas_object: &Object) -> UserInputResult { if let Some(move_obj) = gas_object.data.try_as_move() { if !move_obj.type_().is_gas_coin() { return Err(UserInputError::InvalidGasObject { object_id: gas_object.id(), - }) + }); } Ok(move_obj.get_coin_value_unsafe()) } else { @@ -331,4 +572,4 @@ pub fn get_gas_balance(gas_object: &Object) -> UserInputResult { } } -} +//} diff --git a/crates/sui-types/src/gas_coin.rs b/crates/sui-types/src/gas_coin.rs index d25b49a557c2a..5961d2df893ba 100644 --- a/crates/sui-types/src/gas_coin.rs +++ b/crates/sui-types/src/gas_coin.rs @@ -33,7 +33,7 @@ pub const TOTAL_SUPPLY_MIST: u64 = TOTAL_SUPPLY_SUI * MIST_PER_SUI; pub const GAS_MODULE_NAME: &IdentStr = ident_str!("sui"); pub const GAS_STRUCT_NAME: &IdentStr = ident_str!("SUI"); -sui_macros::checked_arithmetic! { +// sui_macros::checked_arithmetic! { pub struct GAS {} impl GAS { @@ -151,4 +151,4 @@ impl Display for GasCoin { } } -} +// } diff --git a/crates/sui-types/src/gas_model/gas_v1.rs b/crates/sui-types/src/gas_model/gas_v1.rs deleted file mode 100644 index 03b7dc2e33746..0000000000000 --- a/crates/sui-types/src/gas_model/gas_v1.rs +++ /dev/null @@ -1,510 +0,0 @@ -// Copyright (c) 2021, Facebook, Inc. and its affiliates -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::error::{UserInputError, UserInputResult}; -use crate::{ - error::{ExecutionError, ExecutionErrorKind}, - gas::{get_gas_balance, GasCostSummary, SuiGasStatusAPI}, - object::{Object, Owner}, -}; -use move_core_types::{ - gas_algebra::{GasQuantity, InternalGas, InternalGasPerByte, NumBytes, UnitDiv}, - vm_status::StatusCode, -}; -use once_cell::sync::Lazy; -use std::ops::AddAssign; -use std::ops::{Add, Deref, Mul}; -use sui_cost_tables::bytecode_tables::{INITIAL_COST_SCHEDULE, ZERO_COST_SCHEDULE}; -use sui_cost_tables::units_types::CostTable; -use sui_cost_tables::{bytecode_tables::GasStatus, units_types::GasUnit}; -use sui_protocol_config::*; - -macro_rules! ok_or_gas_balance_error { - ($balance:expr, $required:expr) => { - if $balance < $required { - Err(UserInputError::GasBalanceTooLow { - gas_balance: $balance, - needed_gas_amount: $required, - }) - } else { - Ok(()) - } - }; -} - -sui_macros::checked_arithmetic! { - -// A bucket defines a range of units that will be priced the same. -// A cost for the bucket is defined to make the step function non linear. -#[allow(dead_code)] -struct ComputationBucket { - min: u64, - max: u64, - cost: u64, -} - -impl ComputationBucket { - fn new(min: u64, max: u64, cost: u64) -> Self { - ComputationBucket { min, max, cost } - } - - fn simple(min: u64, max: u64) -> Self { - ComputationBucket { - min, - max, - cost: max, - } - } -} - -fn get_bucket_cost(table: &[ComputationBucket], computation_cost: u64) -> u64 { - for bucket in table { - if bucket.max >= computation_cost { - return bucket.cost; - } - } - MAX_BUCKET_COST -} - -// for a RPG of 1000 this amounts to about 1 SUI -const MAX_BUCKET_COST: u64 = 1_000_000; - -// define the bucket table for computation charging -static COMPUTATION_BUCKETS: Lazy> = Lazy::new(|| { - vec![ - ComputationBucket::simple(0, 1_000), - ComputationBucket::simple(1_001, 5_000), - ComputationBucket::simple(5_001, 10_000), - ComputationBucket::simple(10_001, 20_000), - ComputationBucket::simple(20_001, 50_000), - ComputationBucket::new(50_001, u64::MAX, MAX_BUCKET_COST), - ] -}); - -type GasUnits = GasQuantity; -enum GasPriceUnit {} -enum SuiGasUnit {} - -type ComputeGasPricePerUnit = GasQuantity>; - -type GasPrice = GasQuantity; -type SuiGas = GasQuantity; - -// Fixed cost type -#[derive(Clone)] -struct FixedCost(InternalGas); -impl FixedCost { - fn new(x: u64) -> Self { - FixedCost(InternalGas::new(x)) - } -} -impl Deref for FixedCost { - type Target = InternalGas; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// ComputationCostPerByte is a newtype wrapper of InternalGas -/// to ensure a value of this type is used specifically for computation cost. -/// Anything that does not change the amount of bytes stored in the authority data store -/// will charge ComputationCostPerByte. -struct ComputationCostPerByte(InternalGasPerByte); - -impl ComputationCostPerByte { - fn new(x: u64) -> Self { - ComputationCostPerByte(InternalGasPerByte::new(x)) - } -} - -impl Deref for ComputationCostPerByte { - type Target = InternalGasPerByte; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// StorageCostPerByte is a newtype wrapper of InternalGas -/// to ensure a value of this type is used specifically for storage cost. -/// Anything that changes the amount of bytes stored in the authority data store -/// will charge StorageCostPerByte. -struct StorageCostPerByte(InternalGasPerByte); - -impl Deref for StorageCostPerByte { - type Target = InternalGasPerByte; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl StorageCostPerByte { - fn new(x: u64) -> Self { - StorageCostPerByte(InternalGasPerByte::new(x)) - } -} - -/// A list of constant costs of various operations in Sui. -pub struct SuiCostTable { - /// A flat fee charged for every transaction. This is also the mimmum amount of - /// gas charged for a transaction. - min_transaction_cost: FixedCost, - /// Maximum allowable budget for a transaction. - pub(crate) max_gas_budget: u64, - /// Computation cost per byte charged for package publish. This cost is primarily - /// determined by the cost to verify and link a package. Note that this does not - /// include the cost of writing the package to the store. - package_publish_per_byte_cost: ComputationCostPerByte, - /// Per byte cost to read objects from the store. This is computation cost instead of - /// storage cost because it does not change the amount of data stored on the db. - object_read_per_byte_cost: ComputationCostPerByte, - /// Unit cost of a byte in the storage. This will be used both for charging for - /// new storage as well as rebating for deleting storage. That is, we expect users to - /// get full refund on the object storage when it's deleted. - storage_per_byte_cost: StorageCostPerByte, - /// Execution cost table to be used. - pub execution_cost_table: CostTable, -} - -impl std::fmt::Debug for SuiCostTable { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // TODO: dump the fields. - write!(f, "SuiCostTable(...)") - } -} - -impl SuiCostTable { - pub(crate) fn new(c: &ProtocolConfig) -> Self { - Self { - min_transaction_cost: FixedCost::new(c.base_tx_cost_fixed()), - max_gas_budget: c.max_tx_gas(), - package_publish_per_byte_cost: ComputationCostPerByte::new( - c.package_publish_cost_per_byte(), - ), - object_read_per_byte_cost: ComputationCostPerByte::new( - c.obj_access_cost_read_per_byte(), - ), - storage_per_byte_cost: StorageCostPerByte::new(c.obj_data_cost_refundable()), - execution_cost_table: INITIAL_COST_SCHEDULE.clone(), - } - } - - pub(crate) fn unmetered() -> Self { - Self { - min_transaction_cost: FixedCost::new(0), - max_gas_budget: u64::MAX, - package_publish_per_byte_cost: ComputationCostPerByte::new(0), - object_read_per_byte_cost: ComputationCostPerByte::new(0), - storage_per_byte_cost: StorageCostPerByte::new(0), - execution_cost_table: ZERO_COST_SCHEDULE.clone(), - } - } - - pub(crate) fn min_gas_budget_external(&self) -> u64 { - u64::from(to_external(*self.min_transaction_cost)) - } -} - -fn to_external(internal_units: InternalGas) -> GasUnits { - InternalGas::to_unit_round_down(internal_units) -} - -#[derive(Debug)] -pub struct SuiGasStatus { - pub gas_status: GasStatus, - init_budget: GasUnits, - charge: bool, - computation_gas_unit_price: ComputeGasPricePerUnit, - storage_gas_unit_price: ComputeGasPricePerUnit, - /// storage_cost is the total storage gas units charged so far, due to writes into storage. - /// It will be multiplied by the storage gas unit price in the end to obtain the Sui cost. - storage_gas_units: GasUnits, - /// storage_rebate is the total storage rebate (in Sui) accumulated in this transaction. - /// It's directly coming from each mutated object's storage rebate field, which - /// was the storage cost paid when the object was last mutated. It is not affected - /// by the current storage gas unit price. - storage_rebate: SuiGas, - - cost_table: SuiCostTable, -} - -fn to_internal(external_units: GasUnits) -> InternalGas { - GasUnits::to_unit(external_units) -} - -impl SuiGasStatus { - fn new( - move_gas_status: GasStatus, - gas_budget: u64, - charge: bool, - computation_gas_unit_price: GasPrice, - storage_gas_unit_price: u64, - cost_table: SuiCostTable, - ) -> SuiGasStatus { - SuiGasStatus { - gas_status: move_gas_status, - init_budget: GasUnits::new(gas_budget), - charge, - computation_gas_unit_price: ComputeGasPricePerUnit::new( - computation_gas_unit_price.into(), - ), - storage_gas_unit_price: ComputeGasPricePerUnit::new(storage_gas_unit_price), - storage_gas_units: GasUnits::new(0), - storage_rebate: 0.into(), - cost_table, - } - } - pub(crate) fn new_for_testing( - gas_budget: u64, - computation_gas_unit_price: u64, - storage_gas_unit_price: u64, - cost_table: SuiCostTable, - ) -> SuiGasStatus { - let budget_in_unit = gas_budget / computation_gas_unit_price; // truncate the value and move to units - Self::new( - GasStatus::new(cost_table.execution_cost_table.clone(), GasUnits::new(budget_in_unit)), - budget_in_unit, - true, - computation_gas_unit_price.into(), - storage_gas_unit_price, - cost_table, - ) - } - - pub(crate) fn new_with_budget( - gas_budget: u64, - computation_gas_unit_price: u64, - config: &ProtocolConfig, - ) -> SuiGasStatus { - let storage_gas_unit_price: GasPrice = config.storage_gas_price().into(); - // truncate the value and move to units - let budget_in_unit = gas_budget / computation_gas_unit_price; - let sui_cost_table = SuiCostTable::new(config); - Self::new( - GasStatus::new(sui_cost_table.execution_cost_table.clone(), GasUnits::new(budget_in_unit)), - budget_in_unit, - true, - computation_gas_unit_price.into(), - storage_gas_unit_price.into(), - sui_cost_table, - ) - } - - pub(crate) fn new_unmetered() -> SuiGasStatus { - Self::new( - GasStatus::new_unmetered(), - 0, - false, - 0.into(), - 0, - SuiCostTable::unmetered(), - ) - } - - fn charge_storage_mutation_with_rebate( - &mut self, - new_size: usize, - storage_rebate: SuiGas, - ) -> Result { - if self.is_unmetered() { - return Ok(0); - } - - let storage_cost = - NumBytes::new(new_size as u64).mul(*self.cost_table.storage_per_byte_cost); - self.deduct_storage_cost(&storage_cost).map(|gu| { - self.storage_rebate.add_assign(storage_rebate); - gu.into() - }) - } - - fn deduct_computation_cost(&mut self, cost: &InternalGas) -> Result<(), ExecutionError> { - self.gas_status.deduct_gas(*cost).map_err(|e| { - debug_assert_eq!(e.major_status(), StatusCode::OUT_OF_GAS); - ExecutionErrorKind::InsufficientGas.into() - }) - } - - fn deduct_storage_cost(&mut self, cost: &InternalGas) -> Result { - if self.is_unmetered() { - return Ok(0.into()); - } - let ext_cost = to_external(NumBytes::new(1).mul(InternalGasPerByte::new(u64::from(*cost)))); - let charge_amount = to_internal(ext_cost); - let remaining_gas = self.gas_status.remaining_gas(); - if self.gas_status.deduct_gas(charge_amount).is_err() { - debug_assert_eq!(u64::from(self.gas_status.remaining_gas()), 0); - // Even when we run out of gas, we still keep track of the storage_cost change, - // so that at the end, we could still use it to accurately derive the - // computation cost. - self.storage_gas_units = self.storage_gas_units.add(remaining_gas); - Err(ExecutionErrorKind::InsufficientGas.into()) - } else { - self.storage_gas_units = self.storage_gas_units.add(ext_cost); - Ok(ext_cost.mul(self.storage_gas_unit_price)) - } - } - - fn gas_used_in_gas_units(&self) -> GasUnits { - let remaining_gas = self.gas_status.remaining_gas(); - self.init_budget - .checked_sub(remaining_gas) - .expect("Subtraction overflowed") - } -} - -impl SuiGasStatusAPI for SuiGasStatus { - fn is_unmetered(&self) -> bool { - !self.charge - } - - fn move_gas_status(&mut self) -> &mut GasStatus { - &mut self.gas_status - } - - fn bucketize_computation(&mut self) -> Result<(), ExecutionError> { - let computation_cost: u64 = self.gas_used(); - let bucket_cost = get_bucket_cost(&COMPUTATION_BUCKETS, computation_cost); - // charge extra on top of `computation_cost` to make the total computation - // gas cost a bucket value - let extra_charge = bucket_cost.saturating_sub(computation_cost); - if extra_charge > 0 { - self.deduct_computation_cost(&GasUnits::new(extra_charge).to_unit()) - } else { - // we hit the last bucket and the computation is already more then the - // max bucket so just charge as much as it is without buckets - Ok(()) - } - } - - /// Returns the final (computation cost, storage cost, storage rebate) of the gas meter. - /// We use initial budget, combined with remaining gas and storage cost to derive - /// computation cost. - fn summary(&self) -> GasCostSummary { - let remaining_gas = self.gas_status.remaining_gas(); - let storage_cost = self.storage_gas_units; - let computation_cost = self - .init_budget - .checked_sub(remaining_gas) - .expect("Subtraction overflowed") - .checked_sub(storage_cost) - .expect("Subtraction overflowed"); - - let computation_cost_in_sui = computation_cost.mul(self.computation_gas_unit_price).into(); - GasCostSummary { - computation_cost: computation_cost_in_sui, - storage_cost: storage_cost.mul(self.storage_gas_unit_price).into(), - storage_rebate: self.storage_rebate.into(), - // gas model v1 does not use non refundable fees - non_refundable_storage_fee: 0, - } - } - - fn gas_budget(&self) -> u64 { - // MUSTFIX: Properly compute gas budget - let max_gas_unit_price = - std::cmp::max(self.computation_gas_unit_price, self.storage_gas_unit_price); - self.init_budget.mul(max_gas_unit_price).into() - } - - fn storage_rebate(&self) -> u64 { - self.storage_rebate.into() - } - - fn storage_gas_units(&self) -> u64 { - self.storage_gas_units.into() - } - - fn unmetered_storage_rebate(&self) -> u64 { - unreachable!("unmetered_storage_rebate should not be called in v1 gas model"); - } - - fn gas_used(&self) -> u64 { - self.gas_used_in_gas_units().into() - } - - fn reset_storage_cost_and_rebate(&mut self) { - self.storage_gas_units = GasQuantity::zero(); - self.storage_rebate = GasQuantity::zero(); - } - - fn charge_storage_read(&mut self, size: usize) -> Result<(), ExecutionError> { - let cost = NumBytes::new(size as u64).mul(*self.cost_table.object_read_per_byte_cost); - self.deduct_computation_cost(&cost) - } - - fn charge_storage_mutation( - &mut self, - new_size: usize, - storage_rebate: u64, - ) -> Result { - self.charge_storage_mutation_with_rebate(new_size, storage_rebate.into()) - } - - fn charge_publish_package(&mut self, size: usize) -> Result<(), ExecutionError> { - let computation_cost = - NumBytes::new(size as u64).mul(*self.cost_table.package_publish_per_byte_cost); - - self.deduct_computation_cost(&computation_cost) - } - - fn charge_storage_and_rebate(&mut self) -> Result<(), ExecutionError> { - unreachable!("charge_storage_and_rebate should not be called in v1 gas model"); - } - - fn track_storage_mutation(&mut self, _new_size: usize, _storage_rebate: u64) -> u64 { - unreachable!("track_storage_mutation should not be called in v1 gas model"); - } - - fn adjust_computation_on_out_of_gas(&mut self) { - unreachable!("adjust_computation_on_out_of_gas should not be called in v1 gas model"); - } -} - -// Check whether gas arguments are legit: -// 1. Gas object has an address owner. -// 2. Gas budget is between min and max budget allowed -// 3. Gas balance (all gas coins together) is bigger or equal to budget -pub(crate) fn check_gas_balance( - gas_object: &Object, - more_gas_objs: Vec<&Object>, - gas_budget: u64, - gas_price: u64, - cost_table: &SuiCostTable, -) -> UserInputResult { - // 1. Gas object has an address owner. - if !(matches!(gas_object.owner, Owner::AddressOwner(_))) { - return Err(UserInputError::GasObjectNotOwnedObject { - owner: gas_object.owner, - }); - } - - // 2. Gas budget is between min and max budget allowed - let max_gas_budget = cost_table.max_gas_budget as u128 * gas_price as u128; - let min_gas_budget = cost_table.min_gas_budget_external() as u128 * gas_price as u128; - let required_gas_amount = gas_budget as u128; - if required_gas_amount > max_gas_budget { - return Err(UserInputError::GasBudgetTooHigh { - gas_budget, - max_budget: cost_table.max_gas_budget, - }); - } - if required_gas_amount < min_gas_budget { - return Err(UserInputError::GasBudgetTooLow { - gas_budget, - min_budget: cost_table.min_gas_budget_external(), - }); - } - - // 3. Gas balance (all gas coins together) is bigger or equal to budget - let mut gas_balance = get_gas_balance(gas_object)? as u128; - for extra_obj in more_gas_objs { - gas_balance += get_gas_balance(extra_obj)? as u128; - } - ok_or_gas_balance_error!(gas_balance, required_gas_amount) -} - -} diff --git a/crates/sui-types/src/gas_model/gas_v2.rs b/crates/sui-types/src/gas_model/gas_v2.rs index 94620294d7bd2..18336811b802f 100644 --- a/crates/sui-types/src/gas_model/gas_v2.rs +++ b/crates/sui-types/src/gas_model/gas_v2.rs @@ -4,33 +4,19 @@ use crate::error::{UserInputError, UserInputResult}; use crate::gas::{self, GasCostSummary, SuiGasStatusAPI}; +use crate::gas_model::units_types::CostTable; use crate::{ error::{ExecutionError, ExecutionErrorKind}, + gas_model::tables::{ + initial_cost_schedule_v1, initial_cost_schedule_v2, initial_cost_schedule_v3, + initial_cost_schedule_v4, GasStatus, ZERO_COST_SCHEDULE, + }, object::{Object, Owner}, }; use move_core_types::vm_status::StatusCode; -use std::iter; -use sui_cost_tables::bytecode_tables::{ - initial_cost_schedule_v1, initial_cost_schedule_v2, initial_cost_schedule_v3, - initial_cost_schedule_v4, GasStatus, ZERO_COST_SCHEDULE, -}; -use sui_cost_tables::units_types::CostTable; use sui_protocol_config::*; -macro_rules! ok_or_gas_balance_error { - ($balance:expr, $required:expr) => { - if $balance < $required { - Err(UserInputError::GasBalanceTooLow { - gas_balance: $balance, - needed_gas_amount: $required, - }) - } else { - Ok(()) - } - }; -} - -sui_macros::checked_arithmetic! { +// sui_macros::checked_arithmetic! { /// A bucket defines a range of units that will be priced the same. /// After execution a call to `GasStatus::bucketize` will round the computation @@ -124,9 +110,16 @@ impl std::fmt::Debug for SuiCostTable { } impl SuiCostTable { - pub(crate) fn new(c: &ProtocolConfig) -> Self { + pub(crate) fn new(c: &ProtocolConfig, gas_price: u64) -> Self { + // gas_price here is the Reference Gas Price, however we may decide + // to change it to be the price passed in the transaction + let min_transaction_cost = if c.txn_base_cost_as_multiplier() { + c.base_tx_cost_fixed() * gas_price + } else { + c.base_tx_cost_fixed() + }; Self { - min_transaction_cost: c.base_tx_cost_fixed(), + min_transaction_cost, max_gas_budget: c.max_tx_gas(), package_publish_per_byte_cost: c.package_publish_cost_per_byte(), object_read_per_byte_cost: c.obj_access_cost_read_per_byte(), @@ -163,6 +156,7 @@ fn cost_table_for_version(config: &ProtocolConfig) -> CostTable { } } +#[allow(dead_code)] #[derive(Debug)] pub struct SuiGasStatus { // GasStatus as used by the VM, that is all the VM sees @@ -184,6 +178,8 @@ pub struct SuiGasStatus { // and then conceptually // `final_computation_cost = total_computation_cost * gas_price / reference_gas_price` gas_price: u64, + // RGP as defined in the protocol config. + reference_gas_price: u64, // Gas price for storage. This is a multiplier on the final charge // as related to the storage gas price defined in the system // (`ProtocolConfig::storage_gas_price`). @@ -218,6 +214,7 @@ impl SuiGasStatus { gas_budget: u64, charge: bool, gas_price: u64, + reference_gas_price: u64, storage_gas_price: u64, rebate_rate: u64, gas_rounding_step: Option, @@ -230,6 +227,7 @@ impl SuiGasStatus { charge, computation_cost: 0, gas_price, + reference_gas_price, storage_gas_price, storage_cost: 0, storage_rebate: 0, @@ -243,6 +241,7 @@ impl SuiGasStatus { pub(crate) fn new_with_budget( gas_budget: u64, gas_price: u64, + reference_gas_price: u64, config: &ProtocolConfig, ) -> SuiGasStatus { let storage_gas_price = config.storage_gas_price(); @@ -252,10 +251,10 @@ impl SuiGasStatus { } else { gas_budget }; - let sui_cost_table = SuiCostTable::new(config); + let sui_cost_table = SuiCostTable::new(config, gas_price); let gas_rounding_step = config.gas_rounding_step_as_option(); Self::new( - GasStatus::new_v2( + GasStatus::new( sui_cost_table.execution_cost_table.clone(), computation_budget, gas_price, @@ -264,6 +263,7 @@ impl SuiGasStatus { gas_budget, true, gas_price, + reference_gas_price, storage_gas_price, config.storage_rebate_rate(), gas_rounding_step, @@ -271,38 +271,6 @@ impl SuiGasStatus { ) } - pub(crate) fn new_for_testing( - gas_budget: u64, - gas_price: u64, - storage_gas_price: u64, - gas_rounding_step: Option, - cost_table: SuiCostTable, - ) -> SuiGasStatus { - let protocol_config = ProtocolConfig::get_for_max_version(); - let rebate_rate = protocol_config.storage_rebate_rate(); - let max_computation_budget = 5_000_000; // fixed number for now - let computation_budget = if gas_budget > max_computation_budget { - max_computation_budget - } else { - gas_budget - }; - Self::new( - GasStatus::new_v2( - cost_table.execution_cost_table.clone(), - computation_budget, - gas_price, - protocol_config.gas_model_version(), - ), - gas_budget, - true, - gas_price, - storage_gas_price, - rebate_rate, - gas_rounding_step, - cost_table, - ) - } - pub fn new_unmetered() -> SuiGasStatus { Self::new( GasStatus::new_unmetered(), @@ -311,10 +279,58 @@ impl SuiGasStatus { 0, 0, 0, + 0, None, SuiCostTable::unmetered(), ) } + + // Check whether gas arguments are legit: + // 1. Gas object has an address owner. + // 2. Gas budget is between min and max budget allowed + // 3. Gas balance (all gas coins together) is bigger or equal to budget + pub(crate) fn check_gas_balance( + &self, + gas_objs: &[&Object], + gas_budget: u64, + ) -> UserInputResult { + // 1. All gas objects have an address owner + for gas_object in gas_objs { + if !(matches!(gas_object.owner, Owner::AddressOwner(_))) { + return Err(UserInputError::GasObjectNotOwnedObject { + owner: gas_object.owner, + }); + } + } + + // 2. Gas budget is between min and max budget allowed + if gas_budget > self.cost_table.max_gas_budget { + return Err(UserInputError::GasBudgetTooHigh { + gas_budget, + max_budget: self.cost_table.max_gas_budget, + }); + } + if gas_budget < self.cost_table.min_transaction_cost { + return Err(UserInputError::GasBudgetTooLow { + gas_budget, + min_budget: self.cost_table.min_transaction_cost, + }); + } + + // 3. Gas balance (all gas coins together) is bigger or equal to budget + let mut gas_balance = 0u128; + for gas_obj in gas_objs { + gas_balance += gas::get_gas_balance(gas_obj)? as u128; + } + if gas_balance < gas_budget as u128 { + Err(UserInputError::GasBalanceTooLow { + gas_balance, + needed_gas_amount: gas_budget as u128, + }) + } else { + Ok(()) + } + } } impl SuiGasStatusAPI for SuiGasStatus { @@ -400,16 +416,6 @@ impl SuiGasStatusAPI for SuiGasStatus { }) } - fn charge_storage_mutation( - &mut self, - _new_size: usize, - _storage_rebate: u64, - ) -> Result { - Err(ExecutionError::invariant_violation( - "charge_storage_mutation should not be called in v2 gas model", - )) - } - fn charge_publish_package(&mut self, size: usize) -> Result<(), ExecutionError> { self.gas_status .charge_bytes(size, self.cost_table.package_publish_per_byte_cost) @@ -467,61 +473,4 @@ impl SuiGasStatusAPI for SuiGasStatus { } } -// Check whether gas arguments are legit: -// 1. Gas object has an address owner. -// 2. Gas budget is between min and max budget allowed -// 3. Gas balance (all gas coins together) is bigger or equal to budget -pub(crate) fn check_gas_balance( - gas_object: &Object, - more_gas_objs: Vec<&Object>, - gas_budget: u64, - cost_table: &SuiCostTable, -) -> UserInputResult { - // 1. All gas objects have an address owner - for gas_object in more_gas_objs.iter().chain(iter::once(&gas_object)) { - if !(matches!(gas_object.owner, Owner::AddressOwner(_))) { - return Err(UserInputError::GasObjectNotOwnedObject { - owner: gas_object.owner, - }); - } - } - - // 2. Gas budget is between min and max budget allowed - if gas_budget > cost_table.max_gas_budget { - return Err(UserInputError::GasBudgetTooHigh { - gas_budget, - max_budget: cost_table.max_gas_budget, - }); - } - if gas_budget < cost_table.min_transaction_cost { - return Err(UserInputError::GasBudgetTooLow { - gas_budget, - min_budget: cost_table.min_transaction_cost, - }); - } - - // 3. Gas balance (all gas coins together) is bigger or equal to budget - let mut gas_balance = gas::get_gas_balance(gas_object)? as u128; - for extra_obj in more_gas_objs { - gas_balance += gas::get_gas_balance(extra_obj)? as u128; - } - ok_or_gas_balance_error!(gas_balance, gas_budget as u128) -} - -/// Subtract the gas balance of \p gas_object by \p amount. -/// This function should never fail, since we checked that the budget is always -/// less than balance, and the amount is capped at the budget. -pub fn deduct_gas(gas_object: &mut Object, charge_or_rebate: i64) { - // The object must be a gas coin as we have checked in transaction handle phase. - let gas_coin = gas_object.data.try_as_move_mut().unwrap(); - let balance = gas_coin.get_coin_value_unsafe(); - let new_balance = if charge_or_rebate < 0 { - balance + (-charge_or_rebate as u64) - } else { - assert!(balance >= charge_or_rebate as u64); - balance - charge_or_rebate as u64 - }; - gas_coin.set_coin_value_unsafe(new_balance) -} - -} +// } diff --git a/crates/sui-types/src/gas_model/mod.rs b/crates/sui-types/src/gas_model/mod.rs index b3a64e668d06b..0442bc2dd7ff5 100644 --- a/crates/sui-types/src/gas_model/mod.rs +++ b/crates/sui-types/src/gas_model/mod.rs @@ -1,5 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -pub mod gas_v1; pub mod gas_v2; +pub mod tables; +pub mod units_types; diff --git a/crates/sui-cost-tables/src/tier_based/tables.rs b/crates/sui-types/src/gas_model/tables.rs similarity index 95% rename from crates/sui-cost-tables/src/tier_based/tables.rs rename to crates/sui-types/src/gas_model/tables.rs index cf09011209351..1768ca53a08c1 100644 --- a/crates/sui-cost-tables/src/tier_based/tables.rs +++ b/crates/sui-types/src/gas_model/tables.rs @@ -14,7 +14,7 @@ use move_vm_types::loaded_data::runtime_types::Type; use move_vm_types::views::{TypeView, ValueView}; use once_cell::sync::Lazy; -use crate::tier_based::units_types::{CostTable, Gas, GasCost}; +use crate::gas_model::units_types::{CostTable, Gas, GasCost}; /// VM flat fee pub const VM_FLAT_FEE: Gas = Gas::new(8_000); @@ -78,12 +78,7 @@ impl GasStatus { /// Charge for every operation and fail when there is no more gas to pay for operations. /// This is the instantiation that must be used when executing a user script. - pub fn new_v2( - cost_table: CostTable, - budget: u64, - gas_price: u64, - gas_model_version: u64, - ) -> Self { + pub fn new(cost_table: CostTable, budget: u64, gas_price: u64, gas_model_version: u64) -> Self { assert!(gas_price > 0, "gas price cannot be 0"); let budget_in_unit = budget / gas_price; let gas_left = Self::to_internal_units(budget_in_unit); @@ -114,34 +109,6 @@ impl GasStatus { } } - pub fn new(cost_table: CostTable, gas_left: Gas) -> Self { - let (stack_height_current_tier_mult, stack_height_next_tier_start) = - cost_table.stack_height_tier(0); - let (stack_size_current_tier_mult, stack_size_next_tier_start) = - cost_table.stack_size_tier(0); - let (instructions_current_tier_mult, instructions_next_tier_start) = - cost_table.instruction_tier(0); - Self { - gas_model_version: 1, - gas_left: gas_left.to_unit(), - gas_price: 1, - initial_budget: InternalGas::new(0), - cost_table, - charge: true, - stack_height_high_water_mark: 0, - stack_height_current: 0, - stack_size_high_water_mark: 0, - stack_size_current: 0, - instructions_executed: 0, - stack_height_current_tier_mult, - stack_size_current_tier_mult, - instructions_current_tier_mult, - stack_height_next_tier_start, - stack_size_next_tier_start, - instructions_next_tier_start, - } - } - /// Initialize the gas state with metering disabled. /// /// It should be used by clients in very specific cases and when executing system @@ -342,7 +309,7 @@ impl GasStatus { // Charge the number of bytes with the cost per byte value // As more bytes are read throughout the computation the cost per bytes is increased. pub fn charge_bytes(&mut self, size: usize, cost_per_byte: u64) -> PartialVMResult<()> { - let computation_cost = if self.gas_model_version == 4 { + let computation_cost = if Self::charge_input_as_memory(self.gas_model_version) { self.increase_stack_size(size as u64)?; self.stack_size_current_tier_mult * size as u64 * cost_per_byte } else { @@ -350,6 +317,12 @@ impl GasStatus { }; self.deduct_units(computation_cost) } + + // If returns true, input object bytes are treated as memory allocated in Move and + // charged according to the bucket they end up in. + fn charge_input_as_memory(gas_model_version: u64) -> bool { + gas_model_version == 4 + } } /// Returns a tuple of (, , , ) diff --git a/crates/sui-cost-tables/src/tier_based/units_types.rs b/crates/sui-types/src/gas_model/units_types.rs similarity index 100% rename from crates/sui-cost-tables/src/tier_based/units_types.rs rename to crates/sui-types/src/gas_model/units_types.rs diff --git a/crates/sui-types/src/temporary_store.rs b/crates/sui-types/src/temporary_store.rs index b898ccd00bd6f..e5b289088246a 100644 --- a/crates/sui-types/src/temporary_store.rs +++ b/crates/sui-types/src/temporary_store.rs @@ -1,19 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use move_binary_format::CompiledModule; -use move_bytecode_utils::module_cache::GetModule; -use move_core_types::account_address::AccountAddress; -use move_core_types::language_storage::{ModuleId, StructTag}; -use move_core_types::resolver::{ModuleResolver, ResourceResolver}; -use parking_lot::RwLock; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use std::collections::{BTreeMap, HashSet}; -use std::sync::Arc; -use sui_protocol_config::ProtocolConfig; -use tracing::trace; - use crate::committee::EpochId; use crate::effects::{TransactionEffects, TransactionEvents}; use crate::execution_status::ExecutionStatus; @@ -28,8 +15,8 @@ use crate::{ }, error::{ExecutionError, SuiError, SuiResult}, event::Event, - fp_bail, gas, - gas::{GasCostSummary, SuiGasStatus, SuiGasStatusAPI}, + fp_bail, + gas::{GasCharger, GasCostSummary}, object::Owner, object::{Data, Object}, storage::{ @@ -39,6 +26,17 @@ use crate::{ transaction::InputObjects, }; use crate::{is_system_package, SUI_SYSTEM_STATE_OBJECT_ID}; +use move_binary_format::CompiledModule; +use move_bytecode_utils::module_cache::GetModule; +use move_core_types::account_address::AccountAddress; +use move_core_types::language_storage::{ModuleId, StructTag}; +use move_core_types::resolver::{ModuleResolver, ResourceResolver}; +use parking_lot::RwLock; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use std::collections::{BTreeMap, HashSet}; +use std::sync::Arc; +use sui_protocol_config::ProtocolConfig; pub type WrittenObjects = BTreeMap; pub type ObjectMap = BTreeMap; @@ -184,8 +182,6 @@ pub struct TemporaryStore<'backing> { loaded_child_objects: BTreeMap, /// Ordered sequence of events emitted by execution events: Vec, - gas_charged: Option<(ObjectID, GasCostSummary)>, - storage_rebate_rate: u64, protocol_config: ProtocolConfig, // Every object that was read from store during exec @@ -213,8 +209,6 @@ impl<'backing> TemporaryStore<'backing> { written: BTreeMap::new(), deleted: BTreeMap::new(), events: Vec::new(), - gas_charged: None, - storage_rebate_rate: protocol_config.storage_rebate_rate(), protocol_config: protocol_config.clone(), loaded_child_objects: BTreeMap::new(), runtime_read_objects: RwLock::new(BTreeMap::new()), @@ -245,8 +239,6 @@ impl<'backing> TemporaryStore<'backing> { written: BTreeMap::new(), deleted: BTreeMap::new(), events: Vec::new(), - gas_charged: None, - storage_rebate_rate: protocol_config.storage_rebate_rate(), protocol_config: protocol_config.clone(), loaded_child_objects: BTreeMap::new(), runtime_read_objects: RwLock::new(BTreeMap::new()), @@ -354,7 +346,7 @@ impl<'backing> TemporaryStore<'backing> { transaction_dependencies: Vec, gas_cost_summary: GasCostSummary, status: ExecutionStatus, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, epoch: EpochId, ) -> (InnerTemporaryStore, TransactionEffects) { let mut modified_at_versions = vec![]; @@ -379,12 +371,14 @@ impl<'backing> TemporaryStore<'backing> { // we don't really care about the effects to gas, just use the input for it. // Gas coins are guaranteed to be at least size 1 and if more than 1 // the first coin is where all the others are merged. - let gas_object_ref = gas[0]; - let updated_gas_object_info = if gas_object_ref.0 == ObjectID::ZERO { - (gas_object_ref, Owner::AddressOwner(SuiAddress::default())) - } else { - let (obj_ref, object, _kind) = &inner.written[&gas_object_ref.0]; + let updated_gas_object_info = if let Some(coin_id) = gas_charger.gas_coin() { + let (obj_ref, object, _kind) = &inner.written[&coin_id]; (*obj_ref, object.owner) + } else { + ( + (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN), + Owner::AddressOwner(SuiAddress::default()), + ) }; let mut mutated = vec![]; @@ -527,46 +521,6 @@ impl<'backing> TemporaryStore<'backing> { self.written.insert(object.id(), (object, kind)); } - pub fn smash_gas(&mut self, gas: &[ObjectRef]) -> Result { - if gas.len() > 1 { - // sum the value of all gas coins - let new_balance = gas - .iter() - .map(|obj_ref| { - let obj = self.objects().get(&obj_ref.0).unwrap(); - let Data::Move(move_obj) = &obj.data else { - return Err(ExecutionError::invariant_violation( - "Provided non-gas coin object as input for gas!" - )); - }; - if !move_obj.type_().is_gas_coin() { - return Err(ExecutionError::invariant_violation( - "Provided non-gas coin object as input for gas!", - )); - } - Ok(move_obj.get_coin_value_unsafe()) - }) - .collect::, ExecutionError>>()? - .iter() - .sum(); - // unwrap safe because we checked that this exists in `self.objects()` above - let mut primary_gas_object = self.objects().get(&gas[0].0).unwrap().clone(); - // delete all gas objects except the primary_gas_object - for (id, version, _digest) in &gas[1..] { - debug_assert_ne!(*id, primary_gas_object.id()); - self.delete_object(id, DeleteKindWithOldVersion::Normal(*version)); - } - // unwrap is safe because we checked that the primary gas object was a coin object above. - primary_gas_object - .data - .try_as_move_mut() - .unwrap() - .set_coin_value_unsafe(new_balance); - self.write_object(primary_gas_object, WriteKind::Mutate); - } - Ok(gas[0]) - } - pub fn delete_object(&mut self, id: &ObjectID, kind: DeleteKindWithOldVersion) { // there should be no deletion after write debug_assert!(self.written.get(id).is_none()); @@ -596,16 +550,6 @@ impl<'backing> TemporaryStore<'backing> { self.events.clear(); } - /// Resets any mutations, deletions, and events recorded in the store, as well as any storage costs and - /// rebates, then Re-runs gas smashing. Effects on store are now as if we were about to begin execution - pub fn reset(&mut self, gas: &[ObjectRef], gas_status: &mut SuiGasStatus) { - self.drop_writes(); - gas_status.reset_storage_cost_and_rebate(); - - self.smash_gas(gas) - .expect("Gas smashing cannot fail because it already succeeded when we did it before on the same `gas`"); - } - pub fn log_event(&mut self, event: Event) { self.events.push(event) } @@ -680,10 +624,10 @@ impl<'backing> TemporaryStore<'backing> { fn get_objects_to_authenticate( &self, sender: &SuiAddress, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, is_epoch_change: bool, ) -> SuiResult<(Vec, HashSet)> { - let gas_objs: HashSet<&ObjectID> = gas.iter().map(|g| &g.0).collect(); + let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect(); let mut objs_to_authenticate = Vec::new(); let mut authenticated_objs = HashSet::new(); for (id, obj) in &self.input_objects { @@ -788,11 +732,11 @@ impl<'backing> TemporaryStore<'backing> { pub fn check_ownership_invariants( &self, sender: &SuiAddress, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, is_epoch_change: bool, ) -> SuiResult<()> { let (mut objects_to_authenticate, mut authenticated_objects) = - self.get_objects_to_authenticate(sender, gas, is_epoch_change)?; + self.get_objects_to_authenticate(sender, gas_charger, is_epoch_change)?; // Map from an ObjectID to the ObjectID that covers it. let mut covered = BTreeMap::new(); @@ -822,349 +766,24 @@ impl<'backing> TemporaryStore<'backing> { } } -//============================================================================== -// Charge gas legacy - start -// This is the original gas charging code, all code between comment -// "Charge gas legacy - start/end" is exclusively for legacy gas -//============================================================================== impl<'backing> TemporaryStore<'backing> { - /// 1. Compute tx storage gas costs and tx storage rebates, update storage_rebate field of mutated objects - /// 2. Deduct computation gas costs and storage costs to `gas_object_id`, credit storage rebates to `gas_object_id`. - /// gas_object_id can be None if this is a system transaction. - // The happy path of this function follows (1) + (2) and is fairly simple. Most of the complexity is in the unhappy paths: - // - if execution aborted before calling this function, we have to dump all writes + re-smash gas, then charge for storage - // - if we run out of gas while charging for storage, we have to dump all writes + re-smash gas, then charge for storage again - pub fn charge_gas_legacy( - &mut self, - gas_object_id: ObjectID, - gas_status: &mut SuiGasStatus, - execution_result: &mut Result, - gas: &[ObjectRef], - ) { - // at this point, we have done *all* charging for computation, - // but have not yet set the storage rebate or storage gas units - assert!(gas_status.storage_rebate() == 0); - assert!(gas_status.storage_gas_units() == 0); - - // bucketize computation cost - if let Err(err) = gas_status.bucketize_computation() { - if execution_result.is_ok() { - *execution_result = Err(err); - } - } - if execution_result.is_err() { - // Tx execution aborted--need to dump writes, deletes, etc before charging storage gas - self.reset(gas, gas_status); - } - - if let Err(err) = self.charge_gas_for_storage_changes(gas_status, gas_object_id) { - // Ran out of gas while charging for storage changes. reset store, now at state just after gas smashing - self.reset(gas, gas_status); - - // charge for storage again. This will now account only for the storage cost of gas coins - if self - .charge_gas_for_storage_changes(gas_status, gas_object_id) - .is_err() - { - trace!("out of gas while charging for gas smashing") - } - - // if execution succeeded, but we ran out of gas while charging for storage, overwrite the successful execution result - // with an out of gas failure - if execution_result.is_ok() { - *execution_result = Err(err) - } - } - let cost_summary = gas_status.summary(); - let gas_used = cost_summary.gas_used(); - - // Important to fetch the gas object here instead of earlier, as it may have been reset - // previously in the case of error. - let mut gas_object = self.read_object(&gas_object_id).unwrap().clone(); - gas::deduct_gas_legacy( - &mut gas_object, - gas_used, - cost_summary.sender_rebate(self.storage_rebate_rate), - ); - trace!(gas_used, gas_obj_id =? gas_object.id(), gas_obj_ver =? gas_object.version(), "Updated gas object"); - self.write_object(gas_object, WriteKind::Mutate); - self.gas_charged = Some((gas_object_id, cost_summary)); - } - - /// Return the storage rebate and size of `id` at input - fn get_input_storage_rebate_and_size( - &self, - id: &ObjectID, - expected_version: SequenceNumber, - ) -> Result<(u64, usize), ExecutionError> { - if let Some(old_obj) = self.input_objects.get(id) { - Ok(( - old_obj.storage_rebate, - old_obj.object_size_for_gas_metering(), - )) - } else { - // else, this is a dynamic field, not an input object - if let Ok(Some(old_obj)) = self.store.get_object(id) { - if old_obj.version() != expected_version { - return Err(ExecutionError::invariant_violation( - "Expected to find old object with version {expected_version}", - )); - } - Ok(( - old_obj.storage_rebate, - old_obj.object_size_for_gas_metering(), - )) - } else { - Err(ExecutionError::invariant_violation( - "Looking up storage rebate of mutated object should not fail", - )) - } - } - } - - /// Compute storage gas for each mutable input object (including the gas coin), and each created object. - /// Compute storage refunds for each deleted object - /// Will *not* charge any computation gas. Returns the total size in bytes of all deleted objects + all mutated objects, - /// which the caller can use to charge computation gas - /// gas_object_id can be None if this is a system transaction. - fn charge_gas_for_storage_changes( - &mut self, - gas_status: &mut SuiGasStatus, - gas_object_id: ObjectID, - ) -> Result { - let mut total_bytes_written_deleted = 0; - - // If the gas coin was not yet written, charge gas for mutating the gas object in advance. - let gas_object = self - .read_object(&gas_object_id) - .expect("We constructed the object map so it should always have the gas object id") - .clone(); - self.written - .entry(gas_object_id) - .or_insert_with(|| (gas_object, WriteKind::Mutate)); - - self.ensure_active_inputs_mutated(); - let mut objects_to_update = vec![]; - - for (object_id, (object, write_kind)) in &mut self.written { - let (old_storage_rebate, old_object_size) = match write_kind { - WriteKind::Create | WriteKind::Unwrap => (0, 0), - WriteKind::Mutate => { - if let Some(old_obj) = self.input_objects.get(object_id) { - ( - old_obj.storage_rebate, - old_obj.object_size_for_gas_metering(), - ) - } else { - // else, this is an input object, not a dynamic field - if let Ok(Some(old_obj)) = self.store.get_object(object_id) { - let expected_version = object.version(); - if old_obj.version() != expected_version { - return Err(ExecutionError::invariant_violation( - "Expected to find old object with version {expected_version}", - )); - } - ( - old_obj.storage_rebate, - old_obj.object_size_for_gas_metering(), - ) - } else { - return Err(ExecutionError::invariant_violation( - "Looking up storage rebate of mutated object should not fail", - )); - } - } - } - }; - let new_object_size = object.object_size_for_gas_metering(); - let new_storage_rebate = - gas_status.charge_storage_mutation(new_object_size, old_storage_rebate)?; - object.storage_rebate = new_storage_rebate; - if !object.is_immutable() { - objects_to_update.push((object.clone(), *write_kind)); - } - total_bytes_written_deleted += old_object_size + new_object_size; - } - - for (object_id, kind) in &self.deleted { - match kind { - DeleteKindWithOldVersion::Wrap(version) - | DeleteKindWithOldVersion::Normal(version) => { - let (storage_rebate, object_size) = - self.get_input_storage_rebate_and_size(object_id, *version)?; - gas_status.charge_storage_mutation(0, storage_rebate)?; - total_bytes_written_deleted += object_size; - } - DeleteKindWithOldVersion::UnwrapThenDelete - | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => { - // an unwrapped object does not have a storage rebate, we will charge for storage changes via its wrapper object - } - } - } - - // Write all objects at the end only if all previous gas charges succeeded. - // This avoids polluting the temporary store state if this function failed. - for (object, write_kind) in objects_to_update { - self.write_object(object, write_kind); - } - Ok(total_bytes_written_deleted as u64) - } -} -//============================================================================== -// Charge gas legacy - end -//============================================================================== - -//============================================================================== -// Charge gas current - start -// This is the new/current/latest gas charging code, all code between comment -// "Charge gas current - start/end" is exclusively for latest gas -//============================================================================== -impl<'backing> TemporaryStore<'backing> { - /// 1. Compute tx storage gas costs and tx storage rebates, update storage_rebate field of mutated objects - /// 2. Deduct computation gas costs and storage costs to `gas_object_id`, credit storage rebates to `gas_object_id`. - // The happy path of this function follows (1) + (2) and is fairly simple. Most of the complexity is in the unhappy paths: - // - if execution aborted before calling this function, we have to dump all writes + re-smash gas, then charge for storage - // - if we run out of gas while charging for storage, we have to dump all writes + re-smash gas, then charge for storage again - pub fn charge_gas( - &mut self, - gas_object_id: Option, - gas_status: &mut SuiGasStatus, - execution_result: &mut Result, - gas: &[ObjectRef], - ) -> GasCostSummary { - // at this point, we have done *all* charging for computation, - // but have not yet set the storage rebate or storage gas units - if self.protocol_config.gas_model_version() < 2 { - assert!(gas_status.storage_rebate() == 0); - assert!(gas_status.storage_gas_units() == 0); - } else { - debug_assert!(gas_status.storage_rebate() == 0); - debug_assert!(gas_status.storage_gas_units() == 0); - } - - if gas_object_id.is_some() { - // bucketize computation cost - if let Err(err) = gas_status.bucketize_computation() { - if execution_result.is_ok() { - *execution_result = Err(err); - } - } - - // On error we need to dump writes, deletes, etc before charging storage gas - if execution_result.is_err() { - self.reset(gas, gas_status); - } - } - - // compute and collect storage charges - self.ensure_gas_and_input_mutated(gas_object_id); - self.collect_storage_and_rebate(gas_status); - // system transactions (None gas_object_id) do not have gas and so do not charge - // for storage, however they track storage values to check for conservation rules - if let Some(gas_object_id) = gas_object_id { - if self.protocol_config.gas_model_version() < 4 { - self.handle_storage_and_rebate_v1(gas, gas_object_id, gas_status, execution_result) - } else { - self.handle_storage_and_rebate_v2(gas, gas_object_id, gas_status, execution_result) - } - - let cost_summary = gas_status.summary(); - let gas_used = cost_summary.net_gas_usage(); - - let mut gas_object = self.read_object(&gas_object_id).unwrap().clone(); - gas::deduct_gas(&mut gas_object, gas_used); - trace!(gas_used, gas_obj_id =? gas_object.id(), gas_obj_ver =? gas_object.version(), "Updated gas object"); - - self.write_object(gas_object, WriteKind::Mutate); - self.gas_charged = Some((gas_object_id, cost_summary.clone())); - cost_summary - } else { - GasCostSummary::default() - } - } - - fn handle_storage_and_rebate_v1( - &mut self, - gas: &[ObjectRef], - gas_object_id: ObjectID, - gas_status: &mut SuiGasStatus, - execution_result: &mut Result, - ) { - if let Err(err) = gas_status.charge_storage_and_rebate() { - self.reset(gas, gas_status); - gas_status.adjust_computation_on_out_of_gas(); - self.ensure_gas_and_input_mutated(Some(gas_object_id)); - self.collect_rebate(gas_status); - if execution_result.is_ok() { - *execution_result = Err(err); - } - } - } - - fn handle_storage_and_rebate_v2( - &mut self, - gas: &[ObjectRef], - gas_object_id: ObjectID, - gas_status: &mut SuiGasStatus, - execution_result: &mut Result, - ) { - if let Err(err) = gas_status.charge_storage_and_rebate() { - // we run out of gas charging storage, reset and try charging for storage again. - // Input objects are touched and so they have a storage cost - self.reset(gas, gas_status); - self.ensure_gas_and_input_mutated(Some(gas_object_id)); - self.collect_storage_and_rebate(gas_status); - if let Err(err) = gas_status.charge_storage_and_rebate() { - // we run out of gas attempting to charge for the input objects exclusively, - // deal with this edge case by not charging for storage - self.reset(gas, gas_status); - gas_status.adjust_computation_on_out_of_gas(); - self.ensure_gas_and_input_mutated(Some(gas_object_id)); - self.collect_rebate(gas_status); - if execution_result.is_ok() { - *execution_result = Err(err); - } - } else if execution_result.is_ok() { - *execution_result = Err(err); - } - } - } - /// Return the storage rebate of object `id` fn get_input_storage_rebate(&self, id: &ObjectID, expected_version: SequenceNumber) -> u64 { if let Some(old_obj) = self.input_objects.get(id) { old_obj.storage_rebate } else { // else, this is a dynamic field, not an input object - if self.protocol_config.gas_model_version() < 2 { - if let Ok(Some(old_obj)) = self.store.get_object(id) { - if old_obj.version() != expected_version { - // not a lot we can do safely and under this condition everything is broken - panic!( - "Expected to find old object with version {expected_version}, found {}", - old_obj.version(), - ); - } - old_obj.storage_rebate - } else { - // not a lot we can do safely and under this condition everything is broken - panic!("Looking up storage rebate of mutated object should not fail") - } + if let Ok(Some(old_obj)) = self.store.get_object_by_key(id, expected_version) { + old_obj.storage_rebate } else { - // let's keep the if/else on gas version well separated - #[allow(clippy::collapsible-else-if)] - if let Ok(Some(old_obj)) = self.store.get_object_by_key(id, expected_version) { - old_obj.storage_rebate - } else { - // not a lot we can do safely and under this condition everything is broken - panic!("Looking up storage rebate of mutated object should not fail") - } + // not a lot we can do safely and under this condition everything is broken + panic!("Looking up storage rebate of mutated object should not fail") } } } - fn ensure_gas_and_input_mutated(&mut self, gas_object_id: Option) { - if let Some(gas_object_id) = gas_object_id { + pub(crate) fn ensure_gas_and_input_mutated(&mut self, gas_charger: &mut GasCharger) { + if let Some(gas_object_id) = gas_charger.gas_coin() { let gas_object = self .read_object(&gas_object_id) .expect("We constructed the object map so it should always have the gas object id") @@ -1182,7 +801,7 @@ impl<'backing> TemporaryStore<'backing> { /// All objects will be updated with their new (current) storage rebate/cost. /// `SuiGasStatus` `storage_rebate` and `storage_gas_units` track the transaction /// overall storage rebate and cost. - fn collect_storage_and_rebate(&mut self, gas_status: &mut SuiGasStatus) { + pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) { let mut objects_to_update = vec![]; for (object_id, (object, write_kind)) in &mut self.written { // get the object storage_rebate in input for mutated objects @@ -1193,35 +812,14 @@ impl<'backing> TemporaryStore<'backing> { old_obj.storage_rebate } else { // else, this is a dynamic field, not an input object - if self.protocol_config.gas_model_version() < 2 { - if let Ok(Some(old_obj)) = self.store.get_object(object_id) { - let expected_version = object.version(); - if old_obj.version() != expected_version { - // not a lot we can do safely and under this condition everything is broken - panic!( - "Expected to find old object with version {expected_version}, found {}", - old_obj.version(), - ); - } - old_obj.storage_rebate - } else { - // not a lot we can do safely and under this condition everything is broken - panic!( - "Looking up storage rebate of mutated object should not fail" - ); - } + let expected_version = object.version(); + if let Ok(Some(old_obj)) = + self.store.get_object_by_key(object_id, expected_version) + { + old_obj.storage_rebate } else { - let expected_version = object.version(); - if let Ok(Some(old_obj)) = - self.store.get_object_by_key(object_id, expected_version) - { - old_obj.storage_rebate - } else { - // not a lot we can do safely and under this condition everything is broken - panic!( - "Looking up storage rebate of mutated object should not fail" - ); - } + // not a lot we can do safely and under this condition everything is broken + panic!("Looking up storage rebate of mutated object should not fail"); } } } @@ -1230,14 +828,14 @@ impl<'backing> TemporaryStore<'backing> { let new_object_size = object.object_size_for_gas_metering(); // track changes and compute the new object `storage_rebate` let new_storage_rebate = - gas_status.track_storage_mutation(new_object_size, old_storage_rebate); + gas_charger.track_storage_mutation(new_object_size, old_storage_rebate); object.storage_rebate = new_storage_rebate; if !object.is_immutable() { objects_to_update.push((object.clone(), *write_kind)); } } - self.collect_rebate(gas_status); + self.collect_rebate(gas_charger); // Write all objects at the end only if all previous gas charges succeeded. // This avoids polluting the temporary store state if this function failed. @@ -1246,14 +844,14 @@ impl<'backing> TemporaryStore<'backing> { } } - fn collect_rebate(&self, gas_status: &mut SuiGasStatus) { + pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) { for (object_id, kind) in &self.deleted { match kind { DeleteKindWithOldVersion::Wrap(version) | DeleteKindWithOldVersion::Normal(version) => { // get and track the deleted object `storage_rebate` let storage_rebate = self.get_input_storage_rebate(object_id, *version); - gas_status.track_storage_mutation(0, storage_rebate); + gas_charger.track_storage_mutation(0, storage_rebate); } DeleteKindWithOldVersion::UnwrapThenDelete | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => { @@ -1313,51 +911,23 @@ impl<'backing> TemporaryStore<'backing> { Ok((input_sui, obj.storage_rebate)) } else { // not in input objects, must be a dynamic field - if self.protocol_config.gas_model_version() < 2 { - let Ok(Some(obj)) = self.store.get_object(id) else { - invariant_violation!( - "Failed looking up dynamic field {id} in SUI conservation checking" - ); - }; - if obj.version() != expected_version { - invariant_violation!( - "Version mismatching when resolving dynamic field to check conservation--\ - expected {}, got {}", - expected_version, - obj.version(), - ); - } - let input_sui = if do_expensive_checks { - obj.get_total_sui(layout_resolver).map_err(|e| { - make_invariant_violation!( - "Failed looking up input SUI in SUI conservation checking for type \ - {:?}: {e:#?}", - obj.struct_tag(), - ) - })? - } else { - 0 - }; - Ok((input_sui, obj.storage_rebate)) + let Ok(Some(obj))= self.store.get_object_by_key(id, expected_version) else { + invariant_violation!( + "Failed looking up dynamic field {id} in SUI conservation checking" + ); + }; + let input_sui = if do_expensive_checks { + obj.get_total_sui(layout_resolver).map_err(|e| { + make_invariant_violation!( + "Failed looking up input SUI in SUI conservation checking for type \ + {:?}: {e:#?}", + obj.struct_tag(), + ) + })? } else { - let Ok(Some(obj))= self.store.get_object_by_key(id, expected_version) else { - invariant_violation!( - "Failed looking up dynamic field {id} in SUI conservation checking" - ); - }; - let input_sui = if do_expensive_checks { - obj.get_total_sui(layout_resolver).map_err(|e| { - make_invariant_violation!( - "Failed looking up input SUI in SUI conservation checking for type \ - {:?}: {e:#?}", - obj.struct_tag(), - ) - })? - } else { - 0 - }; - Ok((input_sui, obj.storage_rebate)) - } + 0 + }; + Ok((input_sui, obj.storage_rebate)) } } @@ -1385,6 +955,7 @@ impl<'backing> TemporaryStore<'backing> { /// amount of SUI. We need these information for conservation check. pub fn check_sui_conserved( &self, + gas_summary: &GasCostSummary, advance_epoch_gas_summary: Option<(u64, u64)>, layout_resolver: &mut impl LayoutResolver, do_expensive_checks: bool, @@ -1491,13 +1062,6 @@ impl<'backing> TemporaryStore<'backing> { } } - // we do account for the "storage rebate inflow" (portion of the storage rebate which flows back into the storage fund). - let gas_summary = &self - .gas_charged - .as_ref() - .map(|(_, summary)| summary.clone()) - .unwrap_or_default(); - if do_expensive_checks { // note: storage_cost flows into the storage_rebate field of the output objects, which is why it is not accounted for here. // similarly, all of the storage_rebate *except* the storage_fund_rebate_inflow gets credited to the gas coin diff --git a/crates/sui-types/src/transaction.rs b/crates/sui-types/src/transaction.rs index 289873f478b7c..411b0cbaabb75 100644 --- a/crates/sui-types/src/transaction.rs +++ b/crates/sui-types/src/transaction.rs @@ -1038,7 +1038,7 @@ impl TransactionData { gas_data: GasData { price: GAS_PRICE_FOR_SYSTEM_TX, owner: sender, - payment: vec![(ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN)], + payment: vec![], budget: 0, }, expiration: TransactionExpiration::None, diff --git a/crates/transaction-fuzzer/src/account_universe/transfer_gen.rs b/crates/transaction-fuzzer/src/account_universe/transfer_gen.rs index b66d14238fb32..0ecafed08eb6e 100644 --- a/crates/transaction-fuzzer/src/account_universe/transfer_gen.rs +++ b/crates/transaction-fuzzer/src/account_universe/transfer_gen.rs @@ -232,9 +232,7 @@ pub fn gas_price_selection_strategy() -> impl Strategy { pub fn gas_budget_selection_strategy() -> impl Strategy { prop_oneof![ Just(0u64), - Just(PROTOCOL_CONFIG.base_tx_cost_fixed() - 1), - Just(PROTOCOL_CONFIG.base_tx_cost_fixed()), - Just(PROTOCOL_CONFIG.base_tx_cost_fixed() + 1), + PROTOCOL_CONFIG.base_tx_cost_fixed() / 2..=PROTOCOL_CONFIG.base_tx_cost_fixed() * 2000, 1_000_000u64..=3_000_000, Just(PROTOCOL_CONFIG.max_tx_gas() - 1), Just(PROTOCOL_CONFIG.max_tx_gas()), @@ -367,7 +365,7 @@ impl RunInfo { let enough_computation_gas = p2p.gas >= p2p.gas_price * P2P_COMPUTE_GAS_USAGE; let enough_to_succeed = payer_balance as u128 >= to_deduct; let gas_budget_too_high = p2p.gas > PROTOCOL_CONFIG.max_tx_gas(); - let gas_budget_too_low = p2p.gas < PROTOCOL_CONFIG.base_tx_cost_fixed(); + let gas_budget_too_low = p2p.gas < PROTOCOL_CONFIG.base_tx_cost_fixed() * p2p.gas_price; let not_enough_gas = p2p.gas < p2p_success_gas(p2p.gas_price); let gas_price_too_low = p2p.gas_price < rgp; let gas_price_too_high = p2p.gas_price >= PROTOCOL_CONFIG.max_gas_price(); @@ -429,7 +427,8 @@ impl AUTransactionGen for P2PTransferGenRandomGasRandomPriceRandomSponsorship { let signed_txn = self.sponsorship.sign_transaction(&account_triple, tx_data); let payer = self.sponsorship.sponsor(&mut account_triple); // *sender.current_balances.last().unwrap(); - let run_info = RunInfo::new(gas_balance, exec.get_reference_gas_price(), self); + let rgp = exec.get_reference_gas_price(); + let run_info = RunInfo::new(gas_balance, rgp, self); let status = match run_info { RunInfo { enough_max_gas: true, @@ -479,7 +478,7 @@ impl AUTransactionGen for P2PTransferGenRandomGasRandomPriceRandomSponsorship { } => Err(SuiError::UserInputError { error: UserInputError::GasBudgetTooLow { gas_budget: self.gas, - min_budget: PROTOCOL_CONFIG.base_tx_cost_fixed(), + min_budget: PROTOCOL_CONFIG.base_tx_cost_fixed() * self.gas_price, }, }), RunInfo { diff --git a/sui-execution/latest/sui-adapter/src/execution_engine.rs b/sui-execution/latest/sui-adapter/src/execution_engine.rs index 3856cf3a5e9b2..733e24e0d8d3b 100644 --- a/sui-execution/latest/sui-adapter/src/execution_engine.rs +++ b/sui-execution/latest/sui-adapter/src/execution_engine.rs @@ -12,7 +12,6 @@ use sui_types::balance::{ BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME, BALANCE_MODULE_NAME, }; -use sui_types::base_types::ObjectID; use sui_types::execution_mode::{self, ExecutionMode}; use sui_types::gas_coin::GAS; use sui_types::metrics::LimitsMetrics; @@ -23,14 +22,14 @@ use tracing::{info, instrument, trace, warn}; use crate::programmable_transactions; use crate::type_layout_resolver::TypeLayoutResolver; use move_binary_format::access::ModuleAccess; -use sui_macros::checked_arithmetic; +// use sui_macros::checked_arithmetic; use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig}; use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME}; use sui_types::committee::EpochId; use sui_types::effects::TransactionEffects; use sui_types::error::{ExecutionError, ExecutionErrorKind}; use sui_types::execution_status::ExecutionStatus; -use sui_types::gas::{GasCostSummary, SuiGasStatusAPI}; +use sui_types::gas::{GasCharger, GasCostSummary}; use sui_types::messages_consensus::ConsensusCommitPrologue; use sui_types::storage::WriteKind; #[cfg(msim)] @@ -44,12 +43,11 @@ use sui_types::transaction::{ }; use sui_types::{ base_types::{ObjectRef, SuiAddress, TransactionDigest, TxContext}, - gas::SuiGasStatus, object::Object, sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME}, SUI_FRAMEWORK_ADDRESS, }; -use sui_types::{is_system_package, SUI_FRAMEWORK_PACKAGE_ID, SUI_SYSTEM_PACKAGE_ID}; +use sui_types::{SUI_FRAMEWORK_PACKAGE_ID, SUI_SYSTEM_PACKAGE_ID}; /// If a transaction digest shows up in this list, when executing such transaction, /// we will always return `ExecutionError::CertificateDenied` without executing it (but still do @@ -81,7 +79,7 @@ fn is_certificate_denied( || get_denied_certificates().contains(transaction_digest) } -checked_arithmetic! { +// checked_arithmetic! { #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)] pub fn execute_transaction_to_effects( @@ -89,11 +87,10 @@ pub fn execute_transaction_to_effects( mut temporary_store: TemporaryStore<'_>, transaction_kind: TransactionKind, transaction_signer: SuiAddress, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, transaction_digest: TransactionDigest, mut transaction_dependencies: BTreeSet, move_vm: &Arc, - gas_status: SuiGasStatus, epoch_id: &EpochId, epoch_timestamp_ms: u64, protocol_config: &ProtocolConfig, @@ -118,10 +115,9 @@ pub fn execute_transaction_to_effects( let (gas_cost_summary, execution_result) = execute_transaction::( &mut temporary_store, transaction_kind, - gas, + gas_charger, &mut tx_ctx, move_vm, - gas_status, protocol_config, metrics, enable_expensive_checks, @@ -133,7 +129,7 @@ pub fn execute_transaction_to_effects( use ExecutionErrorKind as K; match error.kind() { K::InvariantViolation | K::VMInvariantViolation => { - #[skip_checked_arithmetic] + // #[skip_checked_arithmetic] tracing::error!( kind = ?error.kind(), tx_digest = ?transaction_digest, @@ -143,7 +139,7 @@ pub fn execute_transaction_to_effects( } K::SuiMoveVerificationError | K::VMVerificationOrDeserializationError => { - #[skip_checked_arithmetic] + // #[skip_checked_arithmetic] tracing::debug!( kind = ?error.kind(), tx_digest = ?transaction_digest, @@ -153,7 +149,7 @@ pub fn execute_transaction_to_effects( } K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => { - #[skip_checked_arithmetic] + // #[skip_checked_arithmetic] tracing::debug!( kind = ?error.kind(), tx_digest = ?transaction_digest, @@ -171,7 +167,7 @@ pub fn execute_transaction_to_effects( ExecutionStatus::Success }; - #[skip_checked_arithmetic] + // #[skip_checked_arithmetic] trace!( tx_digest = ?transaction_digest, computation_gas_cost = gas_cost_summary.computation_cost, @@ -186,7 +182,7 @@ pub fn execute_transaction_to_effects( if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() { temporary_store - .check_ownership_invariants(&transaction_signer, gas, is_epoch_change) + .check_ownership_invariants(&transaction_signer, gas_charger, is_epoch_change) .unwrap() } // else, in dev inspect mode and anything goes--don't check @@ -196,35 +192,19 @@ pub fn execute_transaction_to_effects( transaction_dependencies.into_iter().collect(), gas_cost_summary, status, - gas, + gas_charger, *epoch_id, ); (inner, effects, execution_result) } -fn charge_gas_for_object_read( - temporary_store: &TemporaryStore<'_>, - gas_status: &mut SuiGasStatus, -) -> Result<(), ExecutionError> { - // Charge gas for reading all objects from the DB. - let total_size = temporary_store - .objects() - .iter() - // don't charge for loading Sui Framework or Move stdlib - .filter(|(id, _)| !is_system_package(**id)) - .map(|(_, obj)| obj.object_size_for_gas_metering()) - .sum(); - gas_status.charge_storage_read(total_size) -} - #[instrument(name = "tx_execute", level = "debug", skip_all)] fn execute_transaction( temporary_store: &mut TemporaryStore<'_>, transaction_kind: TransactionKind, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, tx_ctx: &mut TxContext, move_vm: &Arc, - mut gas_status: SuiGasStatus, protocol_config: &ProtocolConfig, metrics: Arc, enable_expensive_checks: bool, @@ -233,16 +213,11 @@ fn execute_transaction( GasCostSummary, Result, ) { - // First smash gas into the first coin if more than 1 was provided - let gas_object_ref = match temporary_store.smash_gas(gas) { - Ok(obj_ref) => obj_ref, - Err(_) => gas[0], // this cannot fail, but we use gas[0] anyway - }; - // At this point no charge has been applied yet + gas_charger.smash_gas(temporary_store); + + // At this point no charges have been applied yet debug_assert!( - gas_status.gas_used() == 0 - && gas_status.storage_rebate() == 0 - && gas_status.storage_gas_units() == 0, + gas_charger.no_charges(), "No gas charges must be applied yet" ); let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_)); @@ -250,7 +225,7 @@ fn execute_transaction( // We must charge object read here during transaction execution, because if this fails // we must still ensure an effect is committed and all objects versions incremented - let result = charge_gas_for_object_read(temporary_store, &mut gas_status); + let result = gas_charger.charge_input_objects(temporary_store); let mut result = result.and_then(|()| { let mut execution_result = if deny_cert { Err(ExecutionError::new( @@ -261,10 +236,9 @@ fn execute_transaction( execution_loop::( temporary_store, transaction_kind, - gas_object_ref.0, tx_ctx, move_vm, - &mut gas_status, + gas_charger, protocol_config, metrics.clone(), ) @@ -276,7 +250,7 @@ fn execute_transaction( // For metered transactions, there is not soft limit. // For system transactions, we allow a soft limit with alerting, and a hard limit where we terminate match check_limit_by_meter!( - !gas_status.is_unmetered(), + !gas_charger.is_unmetered(), effects_estimated_size, protocol_config.max_serialized_tx_effects_size_bytes(), protocol_config.max_serialized_tx_effects_size_bytes_system_tx(), @@ -309,7 +283,7 @@ fn execute_transaction( let written_objects_size = temporary_store.written_objects_size(); match check_limit_by_meter!( - !gas_status.is_unmetered(), + !gas_charger.is_unmetered(), written_objects_size, normal_lim, system_lim, @@ -339,73 +313,64 @@ fn execute_transaction( execution_result }); - if protocol_config.gas_model_version() > 1 { - // We always go through the gas charging process, but for system transaction, we don't pass - // the gas object ID since it's not a valid object. - // TODO: Ideally we should make gas object ref None in the first place. - let gas_object_id = if gas_status.is_unmetered() { - None - } else { - Some(gas_object_ref.0) + let cost_summary = gas_charger.charge_gas(temporary_store, &mut result); + // === begin SUI conservation checks === + // For advance epoch transaction, we need to provide epoch rewards and rebates as extra + // information provided to check_sui_conserved, because we mint rewards, and burn + // the rebates. We also need to pass in the unmetered_storage_rebate because storage + // rebate is not reflected in the storage_rebate of gas summary. This is a bit confusing. + // We could probably clean up the code a bit. + // Put all the storage rebate accumulated in the system transaction + // to the 0x5 object so that it's not lost. + temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate()); + if !is_genesis_tx && !Mode::allow_arbitrary_values() { + // ensure that this transaction did not create or destroy SUI, try to recover if the check fails + let conservation_result = { + let mut layout_resolver = TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store)); + temporary_store.check_sui_conserved( + &cost_summary, + advance_epoch_gas_summary, + &mut layout_resolver, + enable_expensive_checks, + ) }; - let cost_summary = - temporary_store.charge_gas(gas_object_id, &mut gas_status, &mut result, gas); - // === begin SUI conservation checks === - // For advance epoch transaction, we need to provide epoch rewards and rebates as extra - // information provided to check_sui_conserved, because we mint rewards, and burn - // the rebates. We also need to pass in the unmetered_storage_rebate because storage - // rebate is not reflected in the storage_rebate of gas summary. This is a bit confusing. - // We could probably clean up the code a bit. - // Put all the storage rebate accumulated in the system transaction - // to the 0x5 object so that it's not lost. - temporary_store.conserve_unmetered_storage_rebate(gas_status.unmetered_storage_rebate()); - if !is_genesis_tx && !Mode::allow_arbitrary_values() { - // ensure that this transaction did not create or destroy SUI, try to recover if the check fails - let conservation_result = { - let mut layout_resolver = TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store)); - temporary_store.check_sui_conserved(advance_epoch_gas_summary, &mut layout_resolver, enable_expensive_checks) - }; - if let Err(conservation_err) = conservation_result { - // conservation violated. try to avoid panic by dumping all writes, charging for gas, re-checking - // conservation, and surfacing an aborted transaction with an invariant violation if all of that works - result = Err(conservation_err); - temporary_store.reset(gas, &mut gas_status); - temporary_store.charge_gas(gas_object_id, &mut gas_status, &mut result, gas); - // check conservation once more more - let mut layout_resolver = TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store)); - if let Err(recovery_err) = temporary_store.check_sui_conserved(advance_epoch_gas_summary, &mut layout_resolver, enable_expensive_checks) { - // if we still fail, it's a problem with gas - // charging that happens even in the "aborted" case--no other option but panic. - // we will create or destroy SUI otherwise - panic!( - "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ", - tx_ctx.digest(), - recovery_err, - gas_status.summary() - ) - } + if let Err(conservation_err) = conservation_result { + // conservation violated. try to avoid panic by dumping all writes, charging for gas, re-checking + // conservation, and surfacing an aborted transaction with an invariant violation if all of that works + result = Err(conservation_err); + gas_charger.reset(temporary_store); + gas_charger.charge_gas(temporary_store, &mut result); + // check conservation once more more + let mut layout_resolver = TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store)); + if let Err(recovery_err) = temporary_store.check_sui_conserved( + &cost_summary, + advance_epoch_gas_summary, + &mut layout_resolver, + enable_expensive_checks, + ) { + // if we still fail, it's a problem with gas + // charging that happens even in the "aborted" case--no other option but panic. + // we will create or destroy SUI otherwise + panic!( + "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ", + tx_ctx.digest(), + recovery_err, + gas_charger.summary() + ) } - } // else, we're in the genesis transaction which mints the SUI supply, and hence does not satisfy SUI conservation, or - // we're in the non-production dev inspect mode which allows us to violate conservation - // === end SUI conservation checks === - (cost_summary, result) - } else { - // legacy code before gas v2, leave it alone - if !gas_status.is_unmetered() { - temporary_store.charge_gas_legacy(gas_object_ref.0, &mut gas_status, &mut result, gas); } - let cost_summary = gas_status.summary(); - (cost_summary, result) - } + } // else, we're in the genesis transaction which mints the SUI supply, and hence does not satisfy SUI conservation, or + // we're in the non-production dev inspect mode which allows us to violate conservation + // === end SUI conservation checks === + (cost_summary, result) } fn execution_loop( temporary_store: &mut TemporaryStore<'_>, transaction_kind: TransactionKind, - gas_object_id: ObjectID, tx_ctx: &mut TxContext, move_vm: &Arc, - gas_status: &mut SuiGasStatus, + gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, metrics: Arc, ) -> Result { @@ -416,7 +381,7 @@ fn execution_loop( temporary_store, tx_ctx, move_vm, - gas_status, + gas_charger, protocol_config, metrics, )?; @@ -448,7 +413,7 @@ fn execution_loop( temporary_store, tx_ctx, move_vm, - gas_status, + gas_charger, protocol_config, metrics, ) @@ -462,8 +427,7 @@ fn execution_loop( move_vm, temporary_store, tx_ctx, - gas_status, - Some(gas_object_id), + gas_charger, pt, ) } @@ -610,7 +574,7 @@ fn advance_epoch( temporary_store: &mut TemporaryStore<'_>, tx_ctx: &mut TxContext, move_vm: &Arc, - gas_status: &mut SuiGasStatus, + gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, metrics: Arc, ) -> Result<(), ExecutionError> { @@ -632,8 +596,7 @@ fn advance_epoch( move_vm, temporary_store, tx_ctx, - gas_status, - None, + gas_charger, advance_epoch_pt, ); @@ -649,7 +612,7 @@ fn advance_epoch( ); temporary_store.drop_writes(); // Must reset the storage rebate since we are re-executing. - gas_status.reset_storage_cost_and_rebate(); + gas_charger.reset_storage_cost_and_rebate(); if protocol_config.get_advance_epoch_start_time_in_safe_mode() { temporary_store.advance_epoch_safe_mode(¶ms, protocol_config); @@ -662,8 +625,7 @@ fn advance_epoch( move_vm, temporary_store, tx_ctx, - gas_status, - None, + gas_charger, advance_epoch_safe_mode_pt, ) .expect("Advance epoch with safe mode must succeed"); @@ -700,8 +662,7 @@ fn advance_epoch( move_vm, temporary_store, tx_ctx, - gas_status, - None, + gas_charger, publish_pt, ) .expect("System Package Publish must succeed"); @@ -743,7 +704,7 @@ fn setup_consensus_commit( temporary_store: &mut TemporaryStore<'_>, tx_ctx: &mut TxContext, move_vm: &Arc, - gas_status: &mut SuiGasStatus, + gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, metrics: Arc, ) -> Result<(), ExecutionError> { @@ -771,10 +732,9 @@ fn setup_consensus_commit( move_vm, temporary_store, tx_ctx, - gas_status, - None, + gas_charger, pt, ) } -} +// } diff --git a/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs b/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs index 4933dfcd27970..1f30036e1ea46 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs @@ -19,6 +19,7 @@ use move_vm_runtime::{move_vm::MoveVM, session::Session}; use move_vm_types::loaded_data::runtime_types::Type; use sui_move_natives::object_runtime::{max_event_error, ObjectRuntime, RuntimeResults}; use sui_protocol_config::ProtocolConfig; +use sui_types::gas::GasCharger; use sui_types::{ balance::Balance, base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress, TxContext}, @@ -28,7 +29,6 @@ use sui_types::{ ExecutionResults, ExecutionState, InputObjectMetadata, InputValue, ObjectValue, RawValueType, ResultValue, UsageKind, }, - gas::{SuiGasStatus, SuiGasStatusAPI}, metrics::LimitsMetrics, move_package::MovePackage, object::{Data, MoveObject, Object, Owner}, @@ -50,7 +50,7 @@ use crate::adapter::{missing_unwrapped_msg, new_native_extensions}; use super::linkage_view::{LinkageInfo, LinkageView, SavedLinkage}; -sui_macros::checked_arithmetic! { +// sui_macros::checked_arithmetic! { /// Maintains all runtime state specific to programmable transactions pub struct ExecutionContext<'vm, 'state, 'a> { @@ -65,8 +65,8 @@ pub struct ExecutionContext<'vm, 'state, 'a> { /// A shared transaction context, contains transaction digest information and manages the /// creation of new object IDs pub tx_context: &'a mut TxContext, - /// The gas status used for metering - pub gas_status: &'a mut SuiGasStatus, + /// The gas charger used for metering + pub gas_charger: &'a mut GasCharger, /// The session used for interacting with Move types and calls pub session: Session<'state, 'vm, LinkageView<'state>>, /// Additional transfers not from the Move runtime @@ -108,8 +108,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { vm: &'vm MoveVM, state_view: &'state dyn ExecutionState, tx_context: &'a mut TxContext, - gas_status: &'a mut SuiGasStatus, - gas_coin_opt: Option, + gas_charger: &'a mut GasCharger, inputs: Vec, ) -> Result { let init_linkage = if protocol_config.package_upgrades_supported() { @@ -126,7 +125,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { linkage, state_view.as_child_resolver(), BTreeMap::new(), - !gas_status.is_unmetered(), + !gas_charger.is_unmetered(), protocol_config, metrics.clone(), ); @@ -143,7 +142,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { ) }) .collect::>()?; - let gas = if let Some(gas_coin) = gas_coin_opt { + let gas = if let Some(gas_coin) = gas_charger.gas_coin() { let mut gas = load_object( vm, state_view, @@ -161,7 +160,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { })) = &mut gas.inner.value else { invariant_violation!("Gas object should be a populated coin") }; - let max_gas_in_balance = gas_status.gas_budget(); + let max_gas_in_balance = gas_charger.gas_budget(); let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else { invariant_violation!( "Transaction input checker should check that there is enough gas" @@ -191,7 +190,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { linkage, state_view.as_child_resolver(), object_owner_map, - !gas_status.is_unmetered(), + !gas_charger.is_unmetered(), protocol_config, metrics.clone(), ); @@ -201,7 +200,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { vm, state_view, tx_context, - gas_status, + gas_charger, session, gas, inputs, @@ -533,7 +532,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { vm, state_view, tx_context, - gas_status, + gas_charger, session, additional_transfers, new_packages, @@ -630,7 +629,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { } // Refund unused gas if let Some(gas_id) = gas_id_opt { - refund_max_gas_budget(&mut additional_writes, gas_status, gas_id)?; + refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?; } let (res, linkage) = session.finish_with_extensions(); @@ -670,7 +669,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { linkage, state_view.as_child_resolver(), BTreeMap::new(), - !gas_status.is_unmetered(), + !gas_charger.is_unmetered(), protocol_config, metrics, ); @@ -772,7 +771,7 @@ impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> { } else { DeleteKindWithOldVersion::Wrap(old_version) } - }, + } DeleteKind::UnwrapThenDelete => { if protocol_config.simplified_unwrap_then_delete() { DeleteKindWithOldVersion::UnwrapThenDelete @@ -1219,7 +1218,7 @@ fn add_additional_write( /// now we return exactly that amount. Gas will be charged by the execution engine fn refund_max_gas_budget( additional_writes: &mut BTreeMap, - gas_status: &SuiGasStatus, + gas_charger: &mut GasCharger, gas_id: ObjectID, ) -> Result<(), ExecutionError> { let Some(AdditionalWrite { bytes,.. }) = additional_writes.get_mut(&gas_id) else { @@ -1231,7 +1230,7 @@ fn refund_max_gas_budget( let Some(new_balance) = coin .balance .value() - .checked_add(gas_status.gas_budget()) else { + .checked_add(gas_charger.gas_budget()) else { return Err(ExecutionError::new_with_source( ExecutionErrorKind::CoinBalanceOverflow, "Gas coin too large after returning the max gas budget", @@ -1296,4 +1295,4 @@ unsafe fn create_written_object<'vm, 'state>( ) } -} +// } diff --git a/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs b/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs index 1f73fe7803b93..873015b37c192 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs @@ -39,7 +39,7 @@ use sui_types::{ CommandKind, ExecutionResults, ExecutionState, ObjectContents, ObjectValue, RawValueType, Value, }, - gas::{SuiGasStatus, SuiGasStatusAPI}, + gas::GasCharger, id::{RESOLVED_SUI_ID, UID}, metrics::LimitsMetrics, move_package::{ @@ -62,7 +62,7 @@ use sui_verifier::{ use super::context::*; use crate::adapter::substitute_package_id; -sui_macros::checked_arithmetic! { +// sui_macros::checked_arithmetic! { pub fn execute( protocol_config: &ProtocolConfig, @@ -70,8 +70,7 @@ pub fn execute( vm: &MoveVM, state_view: &mut dyn ExecutionState, tx_context: &mut TxContext, - gas_status: &mut SuiGasStatus, - gas_coin: Option, + gas_charger: &mut GasCharger, pt: ProgrammableTransaction, ) -> Result { let ProgrammableTransaction { inputs, commands } = pt; @@ -81,8 +80,7 @@ pub fn execute( vm, state_view, tx_context, - gas_status, - gas_coin, + gas_charger, inputs, )?; // execute commands @@ -130,8 +128,7 @@ fn execute_command( context: &mut ExecutionContext<'_, '_, '_>, mode_results: &mut Mode::ExecutionResults, command: Command, -) -> Result<(), ExecutionError> -{ +) -> Result<(), ExecutionError> { let mut argument_updates = Mode::empty_arguments(); let results = match command { Command::MakeMoveVec(tag_opt, args) if args.is_empty() => { @@ -343,8 +340,7 @@ fn execute_move_call( type_arguments: Vec, arguments: Vec, is_init: bool, -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { // check that the function is either an entry function or a valid public function let LoadedFunctionInfo { kind, @@ -413,8 +409,7 @@ fn write_back_results( mut_ref_kinds: impl IntoIterator, return_values: impl IntoIterator>, return_value_kinds: impl IntoIterator, -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { for ((i, bytes), (j, kind)) in mut_ref_values.into_iter().zip(mut_ref_kinds) { assert_invariant!(i == j, "lost mutable input"); let arg_idx = i as usize; @@ -439,8 +434,7 @@ fn make_value( value_info: ValueKind, bytes: Vec, used_in_non_entry_move_call: bool, -) -> Result -{ +) -> Result { Ok(match value_info { ValueKind::Object { type_, @@ -471,14 +465,13 @@ fn execute_move_publish( argument_updates: &mut Mode::ArgumentUpdates, module_bytes: Vec>, dep_ids: Vec, -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { assert_invariant!( !module_bytes.is_empty(), "empty package is checked in transaction input checker" ); context - .gas_status + .gas_charger .charge_publish_package(module_bytes.iter().map(|v| v.len()).sum())?; let mut modules = deserialize_modules::(context, &module_bytes)?; @@ -550,8 +543,7 @@ fn execute_move_upgrade( dep_ids: Vec, current_package_id: ObjectID, upgrade_ticket_arg: Argument, -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { // Check that package upgrades are supported. context .protocol_config @@ -735,8 +727,7 @@ fn check_compatibility<'a>( fn fetch_package( context: &ExecutionContext<'_, '_, '_>, package_id: &ObjectID, -) -> Result -{ +) -> Result { let mut fetched_packages = fetch_packages(context, vec![package_id])?; assert_invariant!( fetched_packages.len() == 1, @@ -753,8 +744,7 @@ fn fetch_package( fn fetch_packages<'ctx, 'vm, 'state, 'a>( context: &'ctx ExecutionContext<'vm, 'state, 'a>, package_ids: impl IntoIterator, -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { let package_ids: BTreeSet<_> = package_ids.into_iter().collect(); match get_packages(&context.state_view, package_ids) { Err(e) => Err(ExecutionError::new_with_source( @@ -790,8 +780,7 @@ fn vm_move_call( type_arguments: Vec, tx_context_kind: TxContextKind, mut serialized_arguments: Vec>, -) -> Result -{ +) -> Result { match tx_context_kind { TxContextKind::None => (), TxContextKind::Mutable | TxContextKind::Immutable => { @@ -806,7 +795,7 @@ fn vm_move_call( function, type_arguments, serialized_arguments, - context.gas_status.move_gas_status(), + context.gas_charger.move_gas_status(), ) .map_err(|e| context.convert_vm_error(e))?; @@ -835,8 +824,7 @@ fn vm_move_call( fn deserialize_modules( context: &mut ExecutionContext<'_, '_, '_>, module_bytes: &[Vec], -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { let modules = module_bytes .iter() .map(|b| { @@ -862,8 +850,7 @@ fn publish_and_verify_modules( context: &mut ExecutionContext<'_, '_, '_>, package_id: ObjectID, modules: &[CompiledModule], -) -> Result<(), ExecutionError> -{ +) -> Result<(), ExecutionError> { // TODO(https://github.com/MystenLabs/sui/issues/69): avoid this redundant serialization by exposing VM API that allows us to run the linker directly on `Vec` let new_module_bytes: Vec<_> = modules .iter() @@ -880,7 +867,7 @@ fn publish_and_verify_modules( AccountAddress::from(package_id), // TODO: publish_module_bundle() currently doesn't charge gas. // Do we want to charge there? - context.gas_status.move_gas_status(), + context.gas_charger.move_gas_status(), ) .map_err(|e| context.convert_vm_error(e))?; @@ -902,8 +889,7 @@ fn init_modules( context: &mut ExecutionContext<'_, '_, '_>, argument_updates: &mut Mode::ArgumentUpdates, modules: &[CompiledModule], -) -> Result<(), ExecutionError> -{ +) -> Result<(), ExecutionError> { let modules_to_init = modules.iter().filter_map(|module| { for fdef in &module.function_defs { let fhandle = module.function_handle_at(fdef.function); @@ -980,8 +966,7 @@ fn check_visibility_and_signature( function: &IdentStr, type_arguments: &[Type], from_init: bool, -) -> Result -{ +) -> Result { if from_init { // the session is weird and does not load the module on publishing. This is a temporary // work around, since loading the function through the session will cause the module @@ -1106,8 +1091,7 @@ fn check_non_entry_signature( _module_id: &ModuleId, _function: &IdentStr, signature: &LoadedFunctionInstantiation, -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { signature .return_ .iter() @@ -1212,8 +1196,7 @@ fn build_move_args( function_kind: FunctionKind, signature: &LoadedFunctionInstantiation, args: &[Argument], -) -> Result -{ +) -> Result { // check the arity let parameters = &signature.parameters; let tx_ctx_kind = match parameters.last() { @@ -1321,8 +1304,7 @@ fn check_param_type( idx: usize, value: &Value, param_ty: &Type, -) -> Result<(), ExecutionError> -{ +) -> Result<(), ExecutionError> { let ty = match value { // For dev-spect, allow any BCS bytes. This does mean internal invariants for types can // be violated (like for string or Option) @@ -1383,8 +1365,7 @@ fn get_struct_ident(s: &StructType) -> (&AccountAddress, &IdentStr, &IdentStr) { pub fn is_tx_context( context: &mut ExecutionContext<'_, '_, '_>, t: &Type, -) -> Result -{ +) -> Result { let (is_mut, inner) = match t { Type::MutableReference(inner) => (true, inner), Type::Reference(inner) => (false, inner), @@ -1413,8 +1394,7 @@ pub fn is_tx_context( fn primitive_serialization_layout( context: &mut ExecutionContext<'_, '_, '_>, param_ty: &Type, -) -> Result, ExecutionError> -{ +) -> Result, ExecutionError> { Ok(match param_ty { Type::Signer => return Ok(None), Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { @@ -1666,4 +1646,4 @@ impl fmt::Display for PrimitiveArgumentLayout { } } -} +// } diff --git a/sui-execution/src/executor.rs b/sui-execution/src/executor.rs index a29872e45dbdc..f6c15af4ded9e 100644 --- a/sui-execution/src/executor.rs +++ b/sui-execution/src/executor.rs @@ -15,7 +15,7 @@ use sui_types::{ error::ExecutionError, execution::{ExecutionState, TypeLayoutStore}, execution_mode::ExecutionResult, - gas::SuiGasStatus, + gas::GasCharger, metrics::LimitsMetrics, temporary_store::{InnerTemporaryStore, TemporaryStore}, transaction::{ProgrammableTransaction, TransactionKind}, @@ -37,8 +37,7 @@ pub trait Executor { // Transaction Inputs temporary_store: TemporaryStore, shared_object_refs: Vec, - gas_status: SuiGasStatus, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, // Transaction transaction_kind: TransactionKind, transaction_signer: SuiAddress, @@ -63,8 +62,7 @@ pub trait Executor { // Transaction Inputs temporary_store: TemporaryStore, shared_object_refs: Vec, - gas_status: SuiGasStatus, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, // Transaction transaction_kind: TransactionKind, transaction_signer: SuiAddress, @@ -84,7 +82,7 @@ pub trait Executor { // Genesis State state_view: &mut dyn ExecutionState, tx_context: &mut TxContext, - gas_status: &mut SuiGasStatus, + gas_charger: &mut GasCharger, // Transaction pt: ProgrammableTransaction, ) -> Result<(), ExecutionError>; diff --git a/sui-execution/src/latest.rs b/sui-execution/src/latest.rs index 2ff76c832286d..06fde8ba8d97c 100644 --- a/sui-execution/src/latest.rs +++ b/sui-execution/src/latest.rs @@ -17,7 +17,7 @@ use sui_types::{ error::{ExecutionError, SuiError, SuiResult}, execution::{ExecutionState, TypeLayoutStore}, execution_mode::{self, ExecutionResult}, - gas::SuiGasStatus, + gas::GasCharger, metrics::{BytecodeVerifierMetrics, LimitsMetrics}, temporary_store::{InnerTemporaryStore, TemporaryStore}, transaction::{ProgrammableTransaction, TransactionKind}, @@ -86,8 +86,7 @@ impl executor::Executor for Executor { epoch_timestamp_ms: u64, temporary_store: TemporaryStore, shared_object_refs: Vec, - gas_status: SuiGasStatus, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, transaction_kind: TransactionKind, transaction_signer: SuiAddress, transaction_digest: TransactionDigest, @@ -102,11 +101,10 @@ impl executor::Executor for Executor { temporary_store, transaction_kind, transaction_signer, - gas, + gas_charger, transaction_digest, transaction_dependencies, &self.0, - gas_status, epoch_id, epoch_timestamp_ms, protocol_config, @@ -126,8 +124,7 @@ impl executor::Executor for Executor { epoch_timestamp_ms: u64, temporary_store: TemporaryStore, shared_object_refs: Vec, - gas_status: SuiGasStatus, - gas: &[ObjectRef], + gas_charger: &mut GasCharger, transaction_kind: TransactionKind, transaction_signer: SuiAddress, transaction_digest: TransactionDigest, @@ -142,11 +139,10 @@ impl executor::Executor for Executor { temporary_store, transaction_kind, transaction_signer, - gas, + gas_charger, transaction_digest, transaction_dependencies, &self.0, - gas_status, epoch_id, epoch_timestamp_ms, protocol_config, @@ -162,7 +158,7 @@ impl executor::Executor for Executor { metrics: Arc, state_view: &mut dyn ExecutionState, tx_context: &mut TxContext, - gas_status: &mut SuiGasStatus, + gas_charger: &mut GasCharger, pt: ProgrammableTransaction, ) -> Result<(), ExecutionError> { programmable_transactions::execution::execute::( @@ -171,8 +167,7 @@ impl executor::Executor for Executor { &self.0, state_view, tx_context, - gas_status, - None, + gas_charger, pt, ) }