diff --git a/crates/sui-move/src/unit_test.rs b/crates/sui-move/src/unit_test.rs index 6d9afd93b35e5e..b6f1aa9f56eb52 100644 --- a/crates/sui-move/src/unit_test.rs +++ b/crates/sui-move/src/unit_test.rs @@ -10,13 +10,16 @@ use move_package::BuildConfig; use move_unit_test::{extensions::set_extension_hook, UnitTestingConfig}; use move_vm_runtime::native_extensions::NativeContextExtensions; use once_cell::sync::Lazy; -use std::{cell::RefCell, collections::BTreeMap, path::Path, sync::Arc}; +use std::{cell::RefCell, collections::BTreeMap, path::Path, rc::Rc, sync::Arc}; use sui_move_build::decorate_warnings; -use sui_move_natives::test_scenario::InMemoryTestStore; use sui_move_natives::{object_runtime::ObjectRuntime, NativesCostTable}; +use sui_move_natives::{object_runtime::TransactionContext, test_scenario::InMemoryTestStore}; use sui_protocol_config::ProtocolConfig; use sui_types::{ - gas_model::tables::initial_cost_schedule_for_unit_tests, in_memory_storage::InMemoryStorage, + base_types::{SuiAddress, TxContext}, + digests::TransactionDigest, + gas_model::tables::initial_cost_schedule_for_unit_tests, + in_memory_storage::InMemoryStorage, metrics::LimitsMetrics, }; @@ -60,9 +63,11 @@ impl Test { // Create a separate test store per-thread. thread_local! { static TEST_STORE_INNER: RefCell = RefCell::new(InMemoryStorage::default()); + // static TEST_TX_CONTEXT_INNER: RefCell = RefCell::new(TransactionContext); } static TEST_STORE: Lazy = Lazy::new(|| InMemoryTestStore(&TEST_STORE_INNER)); +// static TEST_TX_CONTEXT: Lazy = Lazy::new(|| InMemoryTestStore(&TEST_TX_CONTEXT_INNER)); static SET_EXTENSION_HOOK: Lazy<()> = Lazy::new(|| set_extension_hook(Box::new(new_testing_object_and_natives_cost_runtime))); @@ -113,6 +118,7 @@ fn new_testing_object_and_natives_cost_runtime(ext: &mut NativeContextExtensions let registry = prometheus::Registry::new(); let metrics = Arc::new(LimitsMetrics::new(®istry)); let store = Lazy::force(&TEST_STORE); + let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE(); ext.add(ObjectRuntime::new( store, @@ -122,9 +128,15 @@ fn new_testing_object_and_natives_cost_runtime(ext: &mut NativeContextExtensions metrics, 0, // epoch id )); - ext.add(NativesCostTable::from_protocol_config( - &ProtocolConfig::get_for_max_version_UNSAFE(), - )); - + ext.add(NativesCostTable::from_protocol_config(&protocol_config)); + let tx_context = TxContext::new_from_components( + Box::leak(Box::new(SuiAddress::ZERO)), + Box::leak(Box::new(TransactionDigest::default())), + &0, + 0, + 0, + None, + ); + ext.add(TransactionContext::new(Rc::new(RefCell::new(tx_context)))); ext.add(store); } diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 895f22ef504989..f3a1cef3f38ea2 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -217,6 +217,7 @@ const MAX_PROTOCOL_VERSION: u64 = 74; // Enable all gas costs for load_nitro_attestation. // Enable zstd compression for consensus tonic network in mainnet. // Enable the new commit rule for devnet. +// Make `TxContext` Move API native #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -622,6 +623,10 @@ struct FeatureFlags { // If true, enable zstd compression for consensus tonic network. #[serde(skip_serializing_if = "is_false")] consensus_zstd_compression: bool, + + // If true, enable `TxContext` Move API to go native. + #[serde(skip_serializing_if = "is_false")] + move_native_context: bool, } fn is_false(b: &bool) -> bool { @@ -1087,6 +1092,16 @@ pub struct ProtocolConfig { // TxContext // Cost params for the Move native function `transfer_impl(obj: T, recipient: address)` tx_context_derive_id_cost_base: Option, + tx_context_sender_cost_base: Option, + tx_context_epoch_cost_base: Option, + tx_context_epoch_timestamp_ms_cost_base: Option, + tx_context_digest_cost_base: Option, + tx_context_sponsor_cost_base: Option, + tx_context_ids_created_cost_base: Option, + tx_context_replace_cost_base: Option, + tx_context_inc_epoch_timestamp_cost_base: Option, + tx_context_inc_epoch_cost_base: Option, + tx_context_fresh_id_cost_base: Option, // Types // Cost params for the Move native function `is_one_time_witness(_: &T): bool` @@ -1802,9 +1817,14 @@ impl ProtocolConfig { pub fn consensus_zstd_compression(&self) -> bool { self.feature_flags.consensus_zstd_compression } + pub fn enable_nitro_attestation(&self) -> bool { self.feature_flags.enable_nitro_attestation } + + pub fn move_native_context(&self) -> bool { + self.feature_flags.move_native_context + } } #[cfg(not(msim))] @@ -2097,6 +2117,16 @@ impl ProtocolConfig { // `tx_context` module // Cost params for the Move native function `transfer_impl(obj: T, recipient: address)` tx_context_derive_id_cost_base: Some(52), + tx_context_sender_cost_base: None, + tx_context_epoch_cost_base: None, + tx_context_epoch_timestamp_ms_cost_base: None, + tx_context_digest_cost_base: None, + tx_context_sponsor_cost_base: None, + tx_context_ids_created_cost_base: None, + tx_context_replace_cost_base: None, + tx_context_inc_epoch_timestamp_cost_base: None, + tx_context_inc_epoch_cost_base: None, + tx_context_fresh_id_cost_base: None, // `types` module // Cost params for the Move native function `is_one_time_witness(_: &T): bool` @@ -3245,6 +3275,25 @@ impl ProtocolConfig { if chain != Chain::Mainnet && chain != Chain::Testnet { cfg.feature_flags.consensus_linearize_subdag_v2 = true; } + + if chain != Chain::Mainnet { + // Assuming a round rate of max 15/sec, then using a gc depth of 60 allow blocks within a window of ~4 seconds + // to be included before be considered garbage collected. + cfg.consensus_gc_depth = Some(60); + } + + cfg.feature_flags.move_native_context = false; + + cfg.tx_context_sender_cost_base = Some(30); + cfg.tx_context_epoch_cost_base = Some(30); + cfg.tx_context_epoch_timestamp_ms_cost_base = Some(30); + cfg.tx_context_digest_cost_base = Some(30); + cfg.tx_context_sponsor_cost_base = Some(30); + cfg.tx_context_ids_created_cost_base = Some(30); + cfg.tx_context_replace_cost_base = Some(30); + cfg.tx_context_inc_epoch_timestamp_cost_base = Some(30); + cfg.tx_context_inc_epoch_cost_base = Some(30); + cfg.tx_context_fresh_id_cost_base = Some(30); } // Use this template when making changes: // diff --git a/crates/sui-types/src/base_types.rs b/crates/sui-types/src/base_types.rs index 61dc376c1d4af5..118035a65b3ba1 100644 --- a/crates/sui-types/src/base_types.rs +++ b/crates/sui-types/src/base_types.rs @@ -1099,12 +1099,12 @@ impl TxContext { self.epoch } - /// Derive a globally unique object ID by hashing self.digest | self.ids_created - pub fn fresh_id(&mut self) -> ObjectID { - let id = ObjectID::derive_id(self.digest(), self.ids_created); + pub fn sender(&self) -> SuiAddress { + SuiAddress::from(ObjectID(self.sender)) + } - self.ids_created += 1; - id + pub fn epoch_timestamp_ms(&self) -> u64 { + self.epoch_timestamp_ms } /// Return the transaction digest, to include in new objects @@ -1112,8 +1112,20 @@ impl TxContext { TransactionDigest::new(self.digest.clone().try_into().unwrap()) } - pub fn sender(&self) -> SuiAddress { - SuiAddress::from(ObjectID(self.sender)) + pub fn sponsor(&self) -> Option { + self.sponsor.map(SuiAddress::from) + } + + pub fn ids_created(&self) -> u64 { + self.ids_created + } + + /// Derive a globally unique object ID by hashing self.digest | self.ids_created + pub fn fresh_id(&mut self) -> ObjectID { + let id = ObjectID::derive_id(self.digest(), self.ids_created); + + self.ids_created += 1; + id } pub fn to_bcs_legacy_context(&self) -> Vec { @@ -1142,6 +1154,32 @@ impl TxContext { self.ids_created = other.ids_created; Ok(()) } + + // + // Move test only API + // + pub fn inc_epoch(&mut self) { + self.epoch += 1; + } + + pub fn inc_epoch_timestamp(&mut self, delta_ms: u64) { + self.epoch_timestamp_ms += delta_ms; + } + + pub fn replace( + &mut self, + sender: AccountAddress, + tx_hash: Vec, + epoch: u64, + epoch_timestamp_ms: u64, + ids_created: u64, + ) { + self.sender = sender; + self.digest = tx_hash; + self.epoch = epoch; + self.epoch_timestamp_ms = epoch_timestamp_ms; + self.ids_created = ids_created; + } } // TODO: rename to version diff --git a/sui-execution/latest/sui-adapter/src/adapter.rs b/sui-execution/latest/sui-adapter/src/adapter.rs index 99551458bb1e70..0e6f96bb4dc41a 100644 --- a/sui-execution/latest/sui-adapter/src/adapter.rs +++ b/sui-execution/latest/sui-adapter/src/adapter.rs @@ -6,7 +6,9 @@ pub use checked::*; mod checked { #[cfg(feature = "tracing")] use move_vm_config::runtime::VMProfilerConfig; + use std::cell::RefCell; use std::path::PathBuf; + use std::rc::Rc; use std::{collections::BTreeMap, sync::Arc}; use anyhow::Result; @@ -22,7 +24,7 @@ mod checked { move_vm::MoveVM, native_extensions::NativeContextExtensions, native_functions::NativeFunctionTable, }; - use sui_move_natives::object_runtime; + use sui_move_natives::object_runtime::{self, TransactionContext}; use sui_types::metrics::BytecodeVerifierMetrics; use sui_verifier::check_for_verifier_timeout; use tracing::instrument; @@ -85,8 +87,9 @@ mod checked { is_metered: bool, protocol_config: &'r ProtocolConfig, metrics: Arc, - current_epoch_id: EpochId, + tx_context: Rc>, ) -> NativeContextExtensions<'r> { + let current_epoch_id: EpochId = tx_context.borrow().epoch(); let mut extensions = NativeContextExtensions::default(); extensions.add(ObjectRuntime::new( child_resolver, @@ -97,6 +100,7 @@ mod checked { current_epoch_id, )); extensions.add(NativesCostTable::from_protocol_config(protocol_config)); + extensions.add(TransactionContext::new(tx_context)); extensions } diff --git a/sui-execution/latest/sui-adapter/src/execution_engine.rs b/sui-execution/latest/sui-adapter/src/execution_engine.rs index f6466c4516a688..25b98febccfad7 100644 --- a/sui-execution/latest/sui-adapter/src/execution_engine.rs +++ b/sui-execution/latest/sui-adapter/src/execution_engine.rs @@ -10,6 +10,8 @@ mod checked { use move_binary_format::CompiledModule; use move_trace_format::format::MoveTraceBuilder; use move_vm_runtime::move_vm::MoveVM; + use std::cell::RefCell; + use std::rc::Rc; use std::{collections::HashSet, sync::Arc}; use sui_types::balance::{ BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME, @@ -138,7 +140,7 @@ mod checked { protocol_config, ); - let mut tx_ctx = TxContext::new_from_components( + let tx_ctx = TxContext::new_from_components( &transaction_signer, &transaction_digest, epoch_id, @@ -146,6 +148,7 @@ mod checked { gas_price, sponsor, ); + let tx_ctx = Rc::new(RefCell::new(tx_ctx)); let is_epoch_change = transaction_kind.is_end_of_epoch_tx(); @@ -154,7 +157,7 @@ mod checked { &mut temporary_store, transaction_kind, &mut gas_charger, - &mut tx_ctx, + tx_ctx, move_vm, protocol_config, metrics, @@ -258,7 +261,7 @@ mod checked { protocol_config: &ProtocolConfig, metrics: Arc, move_vm: &Arc, - tx_context: &mut TxContext, + tx_context: Rc>, input_objects: CheckedInputObjects, pt: ProgrammableTransaction, ) -> Result { @@ -267,11 +270,11 @@ mod checked { store, input_objects, vec![], - tx_context.digest(), + tx_context.borrow().digest(), protocol_config, 0, ); - let mut gas_charger = GasCharger::new_unmetered(tx_context.digest()); + let mut gas_charger = GasCharger::new_unmetered(tx_context.borrow().digest()); programmable_transactions::execution::execute::( protocol_config, metrics, @@ -292,7 +295,7 @@ mod checked { temporary_store: &mut TemporaryStore<'_>, transaction_kind: TransactionKind, gas_charger: &mut GasCharger, - tx_ctx: &mut TxContext, + tx_ctx: Rc>, move_vm: &Arc, protocol_config: &ProtocolConfig, metrics: Arc, @@ -316,7 +319,7 @@ mod checked { let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_)); let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary(); - let digest = tx_ctx.digest(); + let digest = tx_ctx.borrow().digest(); // 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 @@ -583,7 +586,7 @@ mod checked { fn execution_loop( temporary_store: &mut TemporaryStore<'_>, transaction_kind: TransactionKind, - tx_ctx: &mut TxContext, + tx_ctx: Rc>, move_vm: &Arc, gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, @@ -608,7 +611,7 @@ mod checked { Ok((Mode::empty_results(), vec![])) } TransactionKind::Genesis(GenesisTransaction { objects }) => { - if tx_ctx.epoch() != 0 { + if tx_ctx.borrow().epoch() != 0 { panic!("BUG: Genesis Transactions can only be executed in epoch 0"); } @@ -618,7 +621,7 @@ mod checked { let object = ObjectInner { data, owner, - previous_transaction: tx_ctx.digest(), + previous_transaction: tx_ctx.borrow().digest(), storage_rebate: 0, }; temporary_store.create_object(object.into()); @@ -908,7 +911,7 @@ mod checked { builder: ProgrammableTransactionBuilder, change_epoch: ChangeEpoch, temporary_store: &mut TemporaryStore<'_>, - tx_ctx: &mut TxContext, + tx_ctx: Rc>, move_vm: &Arc, gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, @@ -932,7 +935,7 @@ mod checked { metrics.clone(), move_vm, temporary_store, - tx_ctx, + tx_ctx.clone(), gas_charger, advance_epoch_pt, trace_builder_opt, @@ -962,7 +965,7 @@ mod checked { metrics.clone(), move_vm, temporary_store, - tx_ctx, + tx_ctx.clone(), gas_charger, advance_epoch_safe_mode_pt, trace_builder_opt, @@ -1007,14 +1010,14 @@ mod checked { fn process_system_packages( change_epoch: ChangeEpoch, temporary_store: &mut TemporaryStore<'_>, - tx_ctx: &mut TxContext, + tx_ctx: Rc>, move_vm: &MoveVM, gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, metrics: Arc, trace_builder_opt: &mut Option, ) { - let digest = tx_ctx.digest(); + let digest = tx_ctx.borrow().digest(); let binary_config = to_binary_config(protocol_config); for (version, modules, dependencies) in change_epoch.system_packages.into_iter() { let deserialized_modules: Vec<_> = modules @@ -1037,7 +1040,7 @@ mod checked { metrics.clone(), move_vm, temporary_store, - tx_ctx, + tx_ctx.clone(), gas_charger, publish_pt, trace_builder_opt, @@ -1078,7 +1081,7 @@ mod checked { fn setup_consensus_commit( consensus_commit_timestamp_ms: CheckpointTimestamp, temporary_store: &mut TemporaryStore<'_>, - tx_ctx: &mut TxContext, + tx_ctx: Rc>, move_vm: &Arc, gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, @@ -1219,7 +1222,7 @@ mod checked { fn setup_authenticator_state_update( update: AuthenticatorStateUpdate, temporary_store: &mut TemporaryStore<'_>, - tx_ctx: &mut TxContext, + tx_ctx: Rc>, move_vm: &Arc, gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, @@ -1288,7 +1291,7 @@ mod checked { fn setup_randomness_state_update( update: RandomnessStateUpdate, temporary_store: &mut TemporaryStore<'_>, - tx_ctx: &mut TxContext, + tx_ctx: Rc>, move_vm: &Arc, gas_charger: &mut GasCharger, protocol_config: &ProtocolConfig, 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 fdc3ad40d703f6..60259424725e55 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs @@ -5,7 +5,9 @@ pub use checked::*; #[sui_macros::with_checked_arithmetic] mod checked { + use std::cell::RefCell; use std::collections::BTreeSet; + use std::rc::Rc; use std::{ borrow::Borrow, collections::{BTreeMap, HashMap}, @@ -80,7 +82,7 @@ mod checked { pub state_view: &'state dyn ExecutionState, /// A shared transaction context, contains transaction digest information and manages the /// creation of new object IDs - pub tx_context: &'a mut TxContext, + pub tx_context: Rc>, /// The gas charger used for metering pub gas_charger: &'a mut GasCharger, /// Additional transfers not from the Move runtime @@ -122,7 +124,7 @@ mod checked { metrics: Arc, vm: &'vm MoveVM, state_view: &'state dyn ExecutionState, - tx_context: &'a mut TxContext, + tx_context: Rc>, gas_charger: &'a mut GasCharger, inputs: Vec, ) -> Result @@ -190,7 +192,7 @@ mod checked { !gas_charger.is_unmetered(), protocol_config, metrics.clone(), - tx_context.epoch(), + tx_context.clone(), ); // Set the profiler if in CLI @@ -199,7 +201,8 @@ mod checked { use move_vm_profiler::GasProfiler; use move_vm_types::gas::GasMeter; - let tx_digest = tx_context.digest(); + let ref_context: &RefCell = tx_context.borrow(); + let tx_digest = ref_context.borrow().digest(); let remaining_gas: u64 = move_vm_types::gas::GasMeter::remaining_gas(gas_charger.move_gas_status()) .into(); @@ -237,7 +240,7 @@ mod checked { /// Create a new ID and update the state pub fn fresh_id(&mut self) -> Result { - let object_id = self.tx_context.fresh_id(); + let object_id = self.tx_context.borrow_mut().fresh_id(); let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut(); object_runtime .new_id(object_id) @@ -610,7 +613,8 @@ mod checked { state_view, .. } = self; - let tx_digest = tx_context.digest(); + let ref_context: &RefCell = tx_context.borrow(); + let tx_digest = ref_context.borrow().digest(); let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id()); let mut loaded_runtime_objects = BTreeMap::new(); let mut additional_writes = BTreeMap::new(); @@ -843,7 +847,7 @@ mod checked { Event::new( module_id.address(), module_id.name(), - tx_context.sender(), + ref_context.borrow().sender(), tag, contents, ) 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 bb3e8d7f77db61..f678fc2bd58f15 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs @@ -30,6 +30,8 @@ mod checked { }; use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type}; use serde::{de::DeserializeSeed, Deserialize}; + use std::cell::RefCell; + use std::rc::Rc; use std::time::Instant; use std::{ collections::{BTreeMap, BTreeSet}, @@ -75,7 +77,7 @@ mod checked { metrics: Arc, vm: &MoveVM, state_view: &mut dyn ExecutionState, - tx_context: &mut TxContext, + tx_context: Rc>, gas_charger: &mut GasCharger, pt: ProgrammableTransaction, trace_builder_opt: &mut Option, @@ -105,7 +107,7 @@ mod checked { metrics: Arc, vm: &MoveVM, state_view: &mut dyn ExecutionState, - tx_context: &mut TxContext, + tx_context: Rc>, gas_charger: &mut GasCharger, pt: ProgrammableTransaction, trace_builder_opt: &mut Option, @@ -552,7 +554,7 @@ mod checked { // do not calculate or substitute id for predefined packages (*modules[0].self_id().address()).into() } else { - let id = context.tx_context.fresh_id(); + let id = context.tx_context.borrow_mut().fresh_id(); substitute_package_id(&mut modules, id)?; id }; @@ -666,7 +668,7 @@ mod checked { substitute_package_id(&mut modules, runtime_id)?; // Upgraded packages share their predecessor's runtime ID but get a new storage ID. - let storage_id = context.tx_context.fresh_id(); + let storage_id = context.tx_context.borrow_mut().fresh_id(); let dependencies = fetch_packages(context, &dep_ids)?; let package = context.upgrade_package( @@ -843,7 +845,7 @@ mod checked { match tx_context_kind { TxContextKind::None => (), TxContextKind::Mutable | TxContextKind::Immutable => { - serialized_arguments.push(context.tx_context.to_bcs_legacy_context()); + serialized_arguments.push(context.tx_context.borrow().to_bcs_legacy_context()); } } // script visibility checked manually for entry points @@ -873,7 +875,7 @@ mod checked { "Unable to deserialize TxContext bytes. {e}" )) })?; - context.tx_context.update_state(updated_ctx)?; + context.tx_context.borrow_mut().update_state(updated_ctx)?; } Ok(result) } diff --git a/sui-execution/latest/sui-move-natives/src/lib.rs b/sui-execution/latest/sui-move-natives/src/lib.rs index 7c3e044a0fe2e5..25b7ea770617db 100644 --- a/sui-execution/latest/sui-move-natives/src/lib.rs +++ b/sui-execution/latest/sui-move-natives/src/lib.rs @@ -31,7 +31,13 @@ use self::{ transfer::{ TransferFreezeObjectCostParams, TransferInternalCostParams, TransferShareObjectCostParams, }, - tx_context::TxContextDeriveIdCostParams, + tx_context::{ + TxContextDeriveIdCostParams, TxContextDigestCostParams, TxContextEpochCostParams, + TxContextEpochTimestampMsCostParams, TxContextFreshIdCostParams, + TxContextIdsCreatedCostParams, TxContextIncEpochCostParams, + TxContextIncEpochTimestampCostParams, TxContextReplaceCostParams, + TxContextSenderCostParams, TxContextSponsorCostParams, + }, types::TypesIsOneTimeWitnessCostParams, validator::ValidatorValidateMetadataBcsCostParams, }; @@ -79,6 +85,7 @@ mod tx_context; mod types; mod validator; +const DEFAULT_UNUSED_ENTRY_COST: u64 = 10; #[derive(Tid)] pub struct NativesCostTable { // Address natives @@ -114,6 +121,17 @@ pub struct NativesCostTable { // TxContext pub tx_context_derive_id_cost_params: TxContextDeriveIdCostParams, + pub tx_context_sender_cost_params: TxContextSenderCostParams, + pub tx_context_epoch_cost_params: TxContextEpochCostParams, + pub tx_context_epoch_timestamp_ms_cost_params: TxContextEpochTimestampMsCostParams, + pub tx_context_digest_cost_params: TxContextDigestCostParams, + pub tx_context_sponsor_cost_params: TxContextSponsorCostParams, + pub tx_context_ids_created_cost_params: TxContextIdsCreatedCostParams, + pub tx_context_replace_cost_params: TxContextReplaceCostParams, + pub tx_context_inc_epoch_timestamp_cost_params: TxContextIncEpochTimestampCostParams, + pub tx_context_inc_epoch_cost_params: TxContextIncEpochCostParams, + pub tx_context_fresh_id_cost_params: TxContextFreshIdCostParams, + // Type pub type_is_one_time_witness_cost_params: TypesIsOneTimeWitnessCostParams, @@ -338,11 +356,86 @@ impl NativesCostTable { .transfer_share_object_cost_base() .into(), }, + // tx_context tx_context_derive_id_cost_params: TxContextDeriveIdCostParams { tx_context_derive_id_cost_base: protocol_config .tx_context_derive_id_cost_base() .into(), }, + tx_context_sender_cost_params: TxContextSenderCostParams { + tx_context_sender_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_sender_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_epoch_cost_params: TxContextEpochCostParams { + tx_context_epoch_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_epoch_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_epoch_timestamp_ms_cost_params: TxContextEpochTimestampMsCostParams { + tx_context_epoch_timestamp_ms_cost_base: if protocol_config.move_native_context() { + protocol_config + .tx_context_epoch_timestamp_ms_cost_base() + .into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_digest_cost_params: TxContextDigestCostParams { + tx_context_digest_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_digest_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_sponsor_cost_params: TxContextSponsorCostParams { + tx_context_sponsor_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_sponsor_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_ids_created_cost_params: TxContextIdsCreatedCostParams { + tx_context_ids_created_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_ids_created_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_replace_cost_params: TxContextReplaceCostParams { + tx_context_replace_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_replace_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_inc_epoch_timestamp_cost_params: TxContextIncEpochTimestampCostParams { + tx_context_inc_epoch_timestamp_cost_base: if protocol_config.move_native_context() { + protocol_config + .tx_context_inc_epoch_timestamp_cost_base() + .into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_inc_epoch_cost_params: TxContextIncEpochCostParams { + tx_context_inc_epoch_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_inc_epoch_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, + tx_context_fresh_id_cost_params: TxContextFreshIdCostParams { + tx_context_fresh_id_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_fresh_id_cost_base().into() + } else { + DEFAULT_UNUSED_ENTRY_COST.into() + }, + }, type_is_one_time_witness_cost_params: TypesIsOneTimeWitnessCostParams { types_is_one_time_witness_cost_base: protocol_config .types_is_one_time_witness_cost_base() @@ -1030,6 +1123,30 @@ pub fn all_natives(silent: bool, protocol_config: &ProtocolConfig) -> NativeFunc "derive_id", make_native!(tx_context::derive_id), ), + ( + "tx_context", + "epoch_timestamp_ms", + make_native!(tx_context::epoch_timestamp_ms), + ), + ("tx_context", "digest", make_native!(tx_context::digest)), + ("tx_context", "sponsor", make_native!(tx_context::sponsor)), + ( + "tx_context", + "ids_created", + make_native!(tx_context::ids_created), + ), + ("tx_context", "fresh_id", make_native!(tx_context::fresh_id)), + ( + "tx_context", + "inc_epoch", + make_native!(tx_context::inc_epoch), + ), + ( + "tx_context", + "inc_epoch_timestamp", + make_native!(tx_context::inc_epoch_timestamp), + ), + ("tx_context", "replace", make_native!(tx_context::replace)), ( "types", "is_one_time_witness", diff --git a/sui-execution/latest/sui-move-natives/src/object_runtime/mod.rs b/sui-execution/latest/sui-move-natives/src/object_runtime/mod.rs index 9d002621c74620..f23c261978349d 100644 --- a/sui-execution/latest/sui-move-natives/src/object_runtime/mod.rs +++ b/sui-execution/latest/sui-move-natives/src/object_runtime/mod.rs @@ -24,13 +24,16 @@ use move_vm_types::{ }; use object_store::{ActiveChildObject, ChildObjectStore}; use std::{ + cell::RefCell, collections::{BTreeMap, BTreeSet}, + rc::Rc, sync::Arc, }; use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig}; use sui_types::{ - base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress}, + base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress, TxContext}, committee::EpochId, + digests::TransactionDigest, error::{ExecutionError, ExecutionErrorKind, VMMemoryLimitExceededSubStatusCode}, execution::DynamicallyLoadedObjectMetadata, id::UID, @@ -110,6 +113,12 @@ pub struct ObjectRuntime<'a> { pub(crate) metrics: Arc, } +#[derive(Tid)] +pub struct TransactionContext { + pub(crate) tx_context: Rc>, + pub(crate) digest: Option, +} + pub enum TransferResult { New, SameOwner, @@ -756,3 +765,78 @@ pub fn get_all_uids( .map_err(|e| format!("Failed to deserialize. {e:?}"))?; Ok(ids) } + +impl TransactionContext { + pub fn new(tx_context: Rc>) -> Self { + Self { + tx_context, + digest: None, + } + } + + pub fn sender(&self) -> SuiAddress { + self.tx_context.borrow().sender() + } + + pub fn epoch(&self) -> EpochId { + self.tx_context.borrow().epoch() + } + + pub fn epoch_timestamp_ms(&self) -> u64 { + self.tx_context.borrow().epoch_timestamp_ms() + } + + pub fn digest(&self) -> TransactionDigest { + self.tx_context.borrow().digest() + } + + pub fn move_digest_ref(&mut self) -> PartialVMResult { + if self.digest.is_none() { + let digest = self.tx_context.borrow().digest().into_inner(); + let digest = Value::vector_u8(digest); + self.digest = Some(GlobalValue::cached(digest)?); + } + self.digest.as_ref().unwrap().borrow_global() + } + + pub fn sponsor(&self) -> Option { + self.tx_context.borrow().sponsor() + } + + pub fn ids_created(&self) -> u64 { + self.tx_context.borrow().ids_created() + } + + // native fun derive_id(tx_hash: vector, ids_created: u64): address; + pub fn fresh_id(&self) -> ObjectID { + self.tx_context.borrow_mut().fresh_id() + } + + // + // Test only function + // + pub fn inc_epoch(&self) { + self.tx_context.borrow_mut().inc_epoch(); + } + + pub fn inc_epoch_timestamp(&self, delta_ms: u64) { + self.tx_context.borrow_mut().inc_epoch_timestamp(delta_ms); + } + + pub fn replace( + &self, + sender: AccountAddress, + tx_hash: Vec, + epoch: u64, + epoch_timestamp_ms: u64, + ids_created: u64, + ) { + self.tx_context.borrow_mut().replace( + sender, + tx_hash, + epoch, + epoch_timestamp_ms, + ids_created, + ); + } +} diff --git a/sui-execution/latest/sui-move-natives/src/tx_context.rs b/sui-execution/latest/sui-move-natives/src/tx_context.rs index 1a208098b409d9..821795e97c143b 100644 --- a/sui-execution/latest/sui-move-natives/src/tx_context.rs +++ b/sui-execution/latest/sui-move-natives/src/tx_context.rs @@ -5,13 +5,19 @@ use move_binary_format::errors::PartialVMResult; use move_core_types::{account_address::AccountAddress, gas_algebra::InternalGas}; use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext}; use move_vm_types::{ - loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value, + loaded_data::runtime_types::Type, + natives::function::NativeResult, + pop_arg, + values::{Struct, Value, Vector}, }; use smallvec::smallvec; use std::{collections::VecDeque, convert::TryFrom}; -use sui_types::base_types::{ObjectID, TransactionDigest}; +use sui_types::base_types::{ObjectID, SuiAddress, TransactionDigest}; -use crate::{object_runtime::ObjectRuntime, NativesCostTable}; +use crate::{ + object_runtime::{ObjectRuntime, TransactionContext}, + NativesCostTable, +}; #[derive(Clone)] pub struct TxContextDeriveIdCostParams { @@ -54,3 +60,324 @@ pub fn derive_id( smallvec![Value::address(address)], )) } + +#[derive(Clone)] +pub struct TxContextSenderCostParams { + pub tx_context_sender_cost_base: InternalGas, +} +#[allow(dead_code)] +pub fn sender( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_sender_cost_params = context + .extensions_mut() + .get::() + .tx_context_sender_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_sender_cost_params.tx_context_sender_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let sender = transaction_context.sender(); + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::address(sender.into())], + )) +} + +#[derive(Clone)] +pub struct TxContextEpochCostParams { + pub tx_context_epoch_cost_base: InternalGas, +} +#[allow(dead_code)] +pub fn epoch( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_epoch_cost_params = context + .extensions_mut() + .get::() + .tx_context_epoch_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_epoch_cost_params.tx_context_epoch_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let epoch = transaction_context.epoch(); + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::u64(epoch)], + )) +} + +#[derive(Clone)] +pub struct TxContextEpochTimestampMsCostParams { + pub tx_context_epoch_timestamp_ms_cost_base: InternalGas, +} +pub fn epoch_timestamp_ms( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_epoch_timestamp_ms_cost_params = context + .extensions_mut() + .get::() + .tx_context_epoch_timestamp_ms_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_epoch_timestamp_ms_cost_params.tx_context_epoch_timestamp_ms_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let timestamp = transaction_context.epoch_timestamp_ms(); + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::u64(timestamp)], + )) +} + +#[derive(Clone)] +pub struct TxContextDigestCostParams { + pub tx_context_digest_cost_base: InternalGas, +} +pub fn digest( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_digest_cost_params = context + .extensions_mut() + .get::() + .tx_context_digest_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_digest_cost_params.tx_context_digest_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let digest = transaction_context.move_digest_ref()?; + Ok(NativeResult::ok(context.gas_used(), smallvec![digest])) +} + +#[derive(Clone)] +pub struct TxContextSponsorCostParams { + pub tx_context_sponsor_cost_base: InternalGas, +} +pub fn sponsor( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_sponsor_cost_params = context + .extensions_mut() + .get::() + .tx_context_sponsor_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_sponsor_cost_params.tx_context_sponsor_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let sponsor = to_option(transaction_context.sponsor())?; + + Ok(NativeResult::ok(context.gas_used(), smallvec![sponsor])) +} + +fn to_option(value: Option) -> PartialVMResult { + let vector = Type::Vector(Box::new(Type::Address)); + match value { + Some(value) => { + let value = vec![AccountAddress::new(value.to_inner())]; + Ok(Value::struct_(Struct::pack(vec![Vector::pack( + &vector, + vec![Value::vector_address(value)], + )?]))) + } + None => Ok(Value::struct_(Struct::pack(vec![Vector::empty(&vector)?]))), + } +} + +#[derive(Clone)] +pub struct TxContextIdsCreatedCostParams { + pub tx_context_ids_created_cost_base: InternalGas, +} + +pub fn ids_created( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_ids_created_cost_params = context + .extensions_mut() + .get::() + .tx_context_ids_created_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_ids_created_cost_params.tx_context_ids_created_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let ids_created = transaction_context.ids_created(); + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::u64(ids_created)], + )) +} + +#[derive(Clone)] +pub struct TxContextFreshIdCostParams { + pub tx_context_fresh_id_cost_base: InternalGas, +} +pub fn fresh_id( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_fresh_id_cost_params = context + .extensions_mut() + .get::() + .tx_context_fresh_id_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_fresh_id_cost_params.tx_context_fresh_id_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let fresh_id = transaction_context.fresh_id(); + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::address(fresh_id.into())], + )) +} + +// // +// // Test only function +// // + +#[derive(Clone)] +pub struct TxContextIncEpochCostParams { + pub tx_context_inc_epoch_cost_base: InternalGas, +} +pub fn inc_epoch( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_inc_epoch_cost_params = context + .extensions_mut() + .get::() + .tx_context_inc_epoch_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_inc_epoch_cost_params.tx_context_inc_epoch_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + transaction_context.inc_epoch(); + + Ok(NativeResult::ok(context.gas_used(), smallvec![])) +} + +#[derive(Clone)] +pub struct TxContextIncEpochTimestampCostParams { + pub tx_context_inc_epoch_timestamp_cost_base: InternalGas, +} +pub fn inc_epoch_timestamp( + context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 1); + + let tx_context_inc_epoch_cost_params = context + .extensions_mut() + .get::() + .tx_context_inc_epoch_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_inc_epoch_cost_params.tx_context_inc_epoch_cost_base + ); + + let delta_ms = pop_arg!(args, u64); + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + transaction_context.inc_epoch_timestamp(delta_ms); + + Ok(NativeResult::ok(context.gas_used(), smallvec![])) +} + +#[derive(Clone)] +pub struct TxContextReplaceCostParams { + pub tx_context_replace_cost_base: InternalGas, +} +pub fn replace( + context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 5); + + let tx_context_inc_epoch_cost_params = context + .extensions_mut() + .get::() + .tx_context_inc_epoch_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_inc_epoch_cost_params.tx_context_inc_epoch_cost_base + ); + + let ids_created: u64 = pop_arg!(args, u64); + let epoch_timestamp_ms: u64 = pop_arg!(args, u64); + let epoch: u64 = pop_arg!(args, u64); + let tx_hash: Vec = pop_arg!(args, Vec); + let sender: AccountAddress = pop_arg!(args, AccountAddress); + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + transaction_context.replace(sender, tx_hash, epoch, epoch_timestamp_ms, ids_created); + + Ok(NativeResult::ok(context.gas_used(), smallvec![])) +} diff --git a/sui-execution/src/latest.rs b/sui-execution/src/latest.rs index 3709fcbcca22ac..59f0df30da0825 100644 --- a/sui-execution/src/latest.rs +++ b/sui-execution/src/latest.rs @@ -4,6 +4,8 @@ use move_binary_format::CompiledModule; use move_trace_format::format::MoveTraceBuilder; use move_vm_config::verifier::{MeterConfig, VerifierConfig}; +use std::cell::RefCell; +use std::rc::Rc; use std::{collections::HashSet, path::PathBuf, sync::Arc}; use sui_protocol_config::ProtocolConfig; use sui_types::execution::ExecutionTiming; @@ -180,7 +182,7 @@ impl executor::Executor for Executor { input_objects: CheckedInputObjects, pt: ProgrammableTransaction, ) -> Result { - let mut tx_context = TxContext::new_from_components( + let tx_context = TxContext::new_from_components( &SuiAddress::default(), transaction_digest, &epoch_id, @@ -189,12 +191,13 @@ impl executor::Executor for Executor { 1, None, ); + let tx_context = Rc::new(RefCell::new(tx_context)); execute_genesis_state_update( store, protocol_config, metrics, &self.0, - &mut tx_context, + tx_context, input_objects, pt, )