Skip to content

Commit

Permalink
Conserve storage rebates in system transactions (MystenLabs#10143)
Browse files Browse the repository at this point in the history
This PR accumulates storage rebates during system transaction, and stuff
it all into 0x5 object in the result state.
This ensures that we conserve SUI during system transaction.
In order to do this, I made the following changes:
1. Always go through gas charging process even for unmetered transaction
2. In unmetered mode, we track storage rebate in a separate field.

Enabled system transaction conservation check.
  • Loading branch information
lxfind authored Mar 30, 2023
1 parent 7c19586 commit fa787a6
Show file tree
Hide file tree
Showing 10 changed files with 486 additions and 47 deletions.
39 changes: 31 additions & 8 deletions crates/sui-adapter/src/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,18 @@ fn execute_transaction<
Ok(obj_ref) => obj_ref,
Err(_) => gas[0], // this cannot fail, but we use gas[0] anyway
};
let is_system = transaction_kind.is_system_tx();
// At this point no charge has been applied yet
debug_assert!(
u64::from(gas_status.gas_used()) == 0
&& gas_status.storage_rebate() == 0
&& gas_status.storage_gas_units() == 0,
"No gas charges must be applied yet"
);
#[cfg(debug_assertions)]
let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_));
#[cfg(debug_assertions)]
let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();

// 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);
Expand Down Expand Up @@ -226,15 +230,32 @@ fn execute_transaction<
};
execution_result
});
if !gas_status.is_unmetered() {
temporary_store.charge_gas(gas_object_ref.0, &mut gas_status, &mut result, gas);
}
if !is_system {
#[cfg(debug_assertions)]
{
// 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)
};
temporary_store.charge_gas(gas_object_id, &mut gas_status, &mut result, gas);
// 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());
#[cfg(debug_assertions)]
{
// Genesis transactions mint sui supply, and hence does not satisfy SUI conservation.
if !is_genesis_tx {
// 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.
if !Mode::allow_arbitrary_values() {
// ensure that this transaction did not create or destroy SUI
temporary_store.check_sui_conserved().unwrap();
temporary_store
.check_sui_conserved(advance_epoch_gas_summary)
.unwrap();
}
// else, we're in dev-inspect mode, which lets you turn bytes into arbitrary
// objects (including coins). this can violate conservation, but it's expected
Expand Down Expand Up @@ -492,6 +513,8 @@ fn advance_epoch<S: BackingPackageStore + ParentSync + ChildObjectResolver>(
change_epoch,
);
temporary_store.drop_writes();
// Must reset the storage rebate since we are re-executing.
gas_status.reset_storage_cost_and_rebate();
let advance_epoch_safe_mode_pt = construct_advance_epoch_safe_mode_pt(&params)?;
programmable_transactions::execution::execute::<_, execution_mode::System>(
protocol_config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ expression: genesis_config
validator_config_info: ~
parameters:
chain_start_timestamp_ms: 0
protocol_version: 1
protocol_version: 3
allow_insertion_of_extra_objects: true
epoch_duration_ms: 86400000
stake_subsidy_start_epoch: 0
Expand Down
20 changes: 18 additions & 2 deletions crates/sui-protocol-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ 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 = 1;
const MAX_PROTOCOL_VERSION: u64 = 3;

// Record history of protocol version allocations here:
//
// Version 1: Original version.
// Version 2: Framework changes, including advancing epoch_start_time in safemode.
// Version 3: gas model v2, including all sui conservation fixes.

#[derive(
Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, JsonSchema,
Expand Down Expand Up @@ -117,6 +119,10 @@ struct FeatureFlags {
// If true, validators will commit to the root state digest
// in end of epoch checkpoint proposals
commit_root_state_digest: bool,
// If true, include various fixes to ensure sui conservation, including:
// - Conserving storage rebate of system transactions.
// - TBD.
gas_model_v2: bool,
}

/// Constants that change the behavior of the protocol.
Expand Down Expand Up @@ -554,6 +560,10 @@ impl ProtocolConfig {
pub fn check_commit_root_state_digest_supported(&self) -> bool {
self.feature_flags.commit_root_state_digest
}

pub fn gas_model_v2(&self) -> bool {
self.feature_flags.gas_model_v2
}
}

// getters
Expand Down Expand Up @@ -1323,7 +1333,7 @@ impl ProtocolConfig {
address_from_u256_cost_base: Some(52),

// `dynamic_field` module
// Cost params for the Move native function `hash_type_and_key<K: copy + drop + store>(parent: address, k: K): address`
// Cost params for the Move native function `hash_type_and_key<K: copy + drop + store>(parent: address, k: K): address`
dynamic_field_hash_type_and_key_cost_base: Some(100),
dynamic_field_hash_type_and_key_type_cost_per_byte: Some(2),
dynamic_field_hash_type_and_key_value_cost_per_byte: Some(2),
Expand Down Expand Up @@ -1471,6 +1481,12 @@ impl ProtocolConfig {
// When adding a new constant, set it to None in the earliest version, like this:
// new_constant: None,
},
2 => Self::get_for_version_impl(version - 1),
3 => {
let mut cfg = Self::get_for_version_impl(version - 1);
cfg.feature_flags.gas_model_v2 = true;
cfg
}

// Use this template when making changes:
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ version: 1
feature_flags:
package_upgrades: false
commit_root_state_digest: false
gas_model_v2: false
max_tx_size_bytes: 131072
max_input_objects: 2048
max_serialized_tx_effects_size_bytes: 524288
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
source: crates/sui-protocol-config/src/lib.rs
expression: "ProtocolConfig::get_for_version(cur)"
---
version: 2
feature_flags:
package_upgrades: false
commit_root_state_digest: false
gas_model_v2: false
max_tx_size_bytes: 131072
max_input_objects: 2048
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: 10000000000
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_back_edges_per_function: 10000
max_back_edges_per_module: 10000
max_verifier_meter_ticks_per_function: 6000000
max_meter_ticks_per_module: 6000000
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: 110000
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: 1
obj_data_cost_refundable: 100
obj_metadata_cost_non_refundable: 50
storage_rebate_rate: 9900
storage_fund_reinvest_rate: 500
reward_slashing_rate: 5000
storage_gas_price: 1
max_transactions_per_checkpoint: 10000
max_checkpoint_size_bytes: 31457280
buffer_stake_for_protocol_upgrade_bps: 0
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

Loading

0 comments on commit fa787a6

Please sign in to comment.