diff --git a/crates/sui-core/src/unit_tests/authority_tests.rs b/crates/sui-core/src/unit_tests/authority_tests.rs index bb8afa433c39a..169c1e07172ac 100644 --- a/crates/sui-core/src/unit_tests/authority_tests.rs +++ b/crates/sui-core/src/unit_tests/authority_tests.rs @@ -1684,13 +1684,15 @@ async fn test_publish_dependent_module_ok() { let authority = init_state_with_objects(vec![gas_payment_object]).await; let rgp = authority.reference_gas_price_for_testing().unwrap(); + let gas_price = rgp; + let gas_budget = gas_price * TEST_ONLY_GAS_UNIT_FOR_PUBLISH; let data = TransactionData::new_module( sender, gas_payment_object_ref, vec![dependent_module_bytes], vec![ObjectID::from(*genesis_module.address())], - rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, - rgp, + gas_budget, + gas_price, ); let transaction = to_sender_signed_transaction(data, &sender_key); @@ -1698,7 +1700,8 @@ async fn test_publish_dependent_module_ok() { &sender, transaction.digest(), &EpochData::new_test(), - rgp, + gas_price, + gas_budget, None, ) .fresh_id(); @@ -1722,6 +1725,7 @@ async fn test_publish_module_no_dependencies_ok() { 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 = { let epoch_store = authority.epoch_store_for_testing(); @@ -1740,20 +1744,23 @@ async fn test_publish_module_no_dependencies_ok() { .unwrap(); let module_bytes = vec![module_bytes]; let dependencies = vec![]; // no dependencies + let gas_price = rgp; + let gas_budget = gas_price * TEST_ONLY_GAS_UNIT_FOR_PUBLISH; let data = TransactionData::new_module( sender, gas_payment_object_ref, module_bytes, dependencies, - rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, - rgp, + gas_budget, + gas_price, ); let transaction = to_sender_signed_transaction(data, &sender_key); let _module_object_id = TxContext::new( &sender, transaction.digest(), &EpochData::new_test(), - rgp, + gas_price, + gas_budget, None, ) .fresh_id(); diff --git a/crates/sui-framework/packages/sui-framework/sources/tx_context.move b/crates/sui-framework/packages/sui-framework/sources/tx_context.move index 1fdef9ff83a81..50d0fd5566d2c 100644 --- a/crates/sui-framework/packages/sui-framework/sources/tx_context.move +++ b/crates/sui-framework/packages/sui-framework/sources/tx_context.move @@ -11,10 +11,7 @@ const TX_HASH_LENGTH: u64 = 32; /// Expected an tx hash of length 32, but found a different length const EBadTxHashLength: u64 = 0; -#[test_only] -/// Attempt to get the most recent created object ID when none has been created. -const ENoIDsCreated: u64 = 1; - +#[allow(unused_field)] /// Information about the transaction currently being executed. /// This cannot be constructed by a transaction--it is a privileged object created by /// the VM and passed in to the entrypoint of the transaction as `&mut TxContext`. @@ -34,9 +31,10 @@ public struct TxContext has drop { /// Return the address of the user that signed the current /// transaction -public fun sender(self: &TxContext): address { - self.sender +public fun sender(_self: &TxContext): address { + native_sender() } +native fun native_sender(): address; /// Return the transaction digest (hash of transaction inputs). /// Please do not use as a source of randomness. @@ -45,34 +43,45 @@ public fun digest(self: &TxContext): &vector { } /// Return the current epoch -public fun epoch(self: &TxContext): u64 { - self.epoch +public fun epoch(_self: &TxContext): u64 { + native_epoch() } +native fun native_epoch(): u64; /// Return the epoch start time as a unix timestamp in milliseconds. -public fun epoch_timestamp_ms(self: &TxContext): u64 { - self.epoch_timestamp_ms +public fun epoch_timestamp_ms(_self: &TxContext): u64 { + native_epoch_timestamp_ms() +} +native fun native_epoch_timestamp_ms(): u64; + +/// Return the adress of the transaction sponsor or `None` if there was no sponsor. +public fun sponsor(_self: &TxContext): Option
{ + option_sponsor() } /// Create an `address` that has not been used. As it is an object address, it will never /// occur as the address for a user. /// In other words, the generated address is a globally unique object ID. -public fun fresh_object_address(ctx: &mut TxContext): address { - let ids_created = ctx.ids_created; - let id = derive_id(*&ctx.tx_hash, ids_created); - ctx.ids_created = ids_created + 1; - id +public fun fresh_object_address(_ctx: &mut TxContext): address { + fresh_id() } +native fun fresh_id(): address; #[allow(unused_function)] /// Return the number of id's created by the current transaction. /// Hidden for now, but may expose later -fun ids_created(self: &TxContext): u64 { - self.ids_created +fun ids_created(_self: &TxContext): u64 { + native_ids_created() } +native fun native_ids_created(): u64; -/// Native function for deriving an ID via hash(tx_hash || ids_created) -native fun derive_id(tx_hash: vector, ids_created: u64): address; +#[allow(unused_function)] +// native function to retrieve gas price, currently not exposed +native fun native_gas_price(): u64; + +#[allow(unused_function)] +// native function to retrieve gas budget, currently not exposed +native fun native_gas_budget(): u64; // ==== test-only functions ==== @@ -86,7 +95,24 @@ public fun new( ids_created: u64, ): TxContext { assert!(tx_hash.length() == TX_HASH_LENGTH, EBadTxHashLength); - TxContext { sender, tx_hash, epoch, epoch_timestamp_ms, ids_created } + replace( + sender, + tx_hash, + epoch, + epoch_timestamp_ms, + ids_created, + native_gas_price(), + native_gas_budget(), + native_sponsor(), + ); + // return an empty TxContext given all the info is held on the native side (call above) + TxContext { + sender: @0x0, + tx_hash, + epoch: 0, + epoch_timestamp_ms: 0, + ids_created: 0, + } } #[test_only] @@ -125,17 +151,63 @@ public fun get_ids_created(self: &TxContext): u64 { #[test_only] /// Return the most recent created object ID. public fun last_created_object_id(self: &TxContext): address { - let ids_created = self.ids_created; - assert!(ids_created > 0, ENoIDsCreated); - derive_id(*&self.tx_hash, ids_created - 1) + last_created_id() } +#[test_only] +native fun last_created_id(): address; #[test_only] public fun increment_epoch_number(self: &mut TxContext) { - self.epoch = self.epoch + 1 + let epoch = self.epoch() + 1; + replace( + native_sender(), + self.tx_hash, + native_epoch(), + epoch, + native_ids_created(), + native_gas_price(), + native_gas_budget(), + native_sponsor(), + ); } #[test_only] public fun increment_epoch_timestamp(self: &mut TxContext, delta_ms: u64) { - self.epoch_timestamp_ms = self.epoch_timestamp_ms + delta_ms + let epoch_timestamp_ms = self.epoch_timestamp_ms() + delta_ms; + replace( + native_sender(), + self.tx_hash, + native_epoch(), + epoch_timestamp_ms, + native_ids_created(), + native_gas_price(), + native_gas_budget(), + native_sponsor(), + ); +} + +fun option_sponsor(): Option
{ + let sponsor = native_sponsor(); + if (sponsor.length() == 0) { + option::none() + } else { + option::some(sponsor[0]) + } } +native fun native_sponsor(): vector
; + +#[test_only] +native fun replace( + sender: address, + tx_hash: vector, + epoch: u64, + epoch_timestamp_ms: u64, + ids_created: u64, + gas_price: u64, + gas_budget: u64, + sponsor: vector
, +); + +#[allow(unused_function)] +/// Native function for deriving an ID via hash(tx_hash || ids_created) +native fun derive_id(tx_hash: vector, ids_created: u64): address; \ No newline at end of file diff --git a/crates/sui-move/src/unit_test.rs b/crates/sui-move/src/unit_test.rs index 6d9afd93b35e5..b44ccd698c8c3 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::{test_scenario::InMemoryTestStore, transaction_context::TransactionContext}; 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, }; @@ -113,6 +116,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 +126,18 @@ 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( + &SuiAddress::ZERO, + &TransactionDigest::default(), + &0, + 0, + 0, + 0, + None, + ); + ext.add(TransactionContext::new_for_testing(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 c6afed693e97b..04bf7d329731d 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -223,6 +223,7 @@ const MAX_PROTOCOL_VERSION: u64 = 76; // Enable consensus garbage collection for testnet // Enable the new consensus commit rule for testnet. // Enable passkey auth in multisig for testnet. +// Make `TxContext` Move API native #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -639,6 +640,10 @@ struct FeatureFlags { // If true, record the additional state digest in the consensus commit prologue. #[serde(skip_serializing_if = "is_false")] record_additional_state_digest_in_prologue: 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 { @@ -1101,6 +1106,15 @@ 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_fresh_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_sponsor_cost_base: Option, + tx_context_gas_price_cost_base: Option, + tx_context_gas_budget_cost_base: Option, + tx_context_ids_created_cost_base: Option, + tx_context_replace_cost_base: Option, // Types // Cost params for the Move native function `is_one_time_witness(_: &T): bool` @@ -1827,6 +1841,7 @@ 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 } @@ -1849,6 +1864,10 @@ impl ProtocolConfig { pub fn minimize_child_object_mutations(&self) -> bool { self.feature_flags.minimize_child_object_mutations } + + pub fn move_native_context(&self) -> bool { + self.feature_flags.move_native_context + } } #[cfg(not(msim))] @@ -2141,6 +2160,15 @@ 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_fresh_id_cost_base: None, + tx_context_sender_cost_base: None, + tx_context_epoch_cost_base: None, + tx_context_epoch_timestamp_ms_cost_base: None, + tx_context_sponsor_cost_base: None, + tx_context_gas_price_cost_base: None, + tx_context_gas_budget_cost_base: None, + tx_context_ids_created_cost_base: None, + tx_context_replace_cost_base: None, // `types` module // Cost params for the Move native function `is_one_time_witness(_: &T): bool` @@ -3311,6 +3339,19 @@ impl ProtocolConfig { if chain != Chain::Mainnet { cfg.feature_flags.accept_passkey_in_multisig = true; } + + cfg.feature_flags.move_native_context = false; + + cfg.feature_flags.move_native_context = true; + cfg.tx_context_fresh_id_cost_base = Some(52); + 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_sponsor_cost_base = Some(30); + cfg.tx_context_gas_price_cost_base = Some(30); + cfg.tx_context_gas_budget_cost_base = Some(30); + cfg.tx_context_ids_created_cost_base = Some(30); + cfg.tx_context_replace_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 f5efa88d1eba8..33de6cb702c7d 100644 --- a/crates/sui-types/src/base_types.rs +++ b/crates/sui-types/src/base_types.rs @@ -1019,6 +1019,8 @@ pub struct TxContext { ids_created: u64, // gas price passed to transaction as input gas_price: u64, + // gas budget passed to transaction as input + gas_budget: u64, // address of the sponsor if any sponsor: Option, } @@ -1039,6 +1041,7 @@ impl TxContext { digest: &TransactionDigest, epoch_data: &EpochData, gas_price: u64, + gas_budget: u64, sponsor: Option, ) -> Self { Self::new_from_components( @@ -1047,6 +1050,7 @@ impl TxContext { &epoch_data.epoch_id(), epoch_data.epoch_start_timestamp(), gas_price, + gas_budget, sponsor, ) } @@ -1057,6 +1061,7 @@ impl TxContext { epoch_id: &EpochId, epoch_timestamp_ms: u64, gas_price: u64, + gas_budget: u64, sponsor: Option, ) -> Self { Self { @@ -1066,6 +1071,7 @@ impl TxContext { epoch_timestamp_ms, ids_created: 0, gas_price, + gas_budget, sponsor: sponsor.map(|s| s.into()), } } @@ -1099,12 +1105,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 { + self.sender.into() + } - 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,12 +1118,46 @@ 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 gas_price(&self) -> u64 { + self.gas_price + } + + pub fn gas_budget(&self) -> u64 { + self.gas_budget + } + + 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 { - let move_context: MoveLegacyTxContext = self.into(); + pub fn to_bcs_legacy_context(&self, is_native: bool) -> Vec { + let move_context: MoveLegacyTxContext = if is_native { + let tx_context = &TxContext { + sender: AccountAddress::ZERO, + digest: self.digest.clone(), + epoch: 0, + epoch_timestamp_ms: 0, + ids_created: 0, + gas_price: 0, + gas_budget: 0, + sponsor: None, + }; + tx_context.into() + } else { + self.into() + }; bcs::to_bytes(&move_context).unwrap() } @@ -1129,19 +1169,49 @@ impl TxContext { /// when mutable context is passed over some boundary via /// serialize/deserialize and this is the reason why this method /// consumes the other context.. - pub fn update_state(&mut self, other: MoveLegacyTxContext) -> Result<(), ExecutionError> { - if self.sender != other.sender - || self.digest != other.digest - || other.ids_created < self.ids_created - { - return Err(ExecutionError::new_with_source( - ExecutionErrorKind::InvariantViolation, - "Immutable fields for TxContext changed", - )); + pub fn update_state( + &mut self, + other: MoveLegacyTxContext, + is_native: bool, + ) -> Result<(), ExecutionError> { + if !is_native { + if self.sender != other.sender + || self.digest != other.digest + || other.ids_created < self.ids_created + { + return Err(ExecutionError::new_with_source( + ExecutionErrorKind::InvariantViolation, + "Immutable fields for TxContext changed", + )); + } + self.ids_created = other.ids_created; } - self.ids_created = other.ids_created; Ok(()) } + + // + // Move test only API + // + pub fn replace( + &mut self, + sender: AccountAddress, + tx_hash: Vec, + epoch: u64, + epoch_timestamp_ms: u64, + ids_created: u64, + gas_price: u64, + gas_budget: u64, + sponsor: Option, + ) { + self.sender = sender; + self.digest = tx_hash; + self.epoch = epoch; + self.epoch_timestamp_ms = epoch_timestamp_ms; + self.ids_created = ids_created; + self.gas_price = gas_price; + self.gas_budget = gas_budget; + self.sponsor = sponsor; + } } // TODO: rename to version diff --git a/external-crates/move/crates/move-vm-types/src/values/values_impl.rs b/external-crates/move/crates/move-vm-types/src/values/values_impl.rs index 9dfd125043e5c..a6f2298035e78 100644 --- a/external-crates/move/crates/move-vm-types/src/values/values_impl.rs +++ b/external-crates/move/crates/move-vm-types/src/values/values_impl.rs @@ -1580,6 +1580,16 @@ impl VMValueCast> for Value { } } +impl VMValueCast> for Value { + fn cast(self) -> PartialVMResult> { + match self.0 { + ValueImpl::Container(Container::VecAddress(r)) => take_unique_ownership(r), + v => Err(PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) + .with_message(format!("cannot cast {:?} to vector", v,))), + } + } +} + impl VMValueCast> for Value { fn cast(self) -> PartialVMResult> { match self.0 { diff --git a/sui-execution/latest/sui-adapter/src/adapter.rs b/sui-execution/latest/sui-adapter/src/adapter.rs index 99551458bb1e7..5b78aee4a6621 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, transaction_context::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 6f41be46017ea..ef39d15cc4e06 100644 --- a/sui-execution/latest/sui-adapter/src/execution_engine.rs +++ b/sui-execution/latest/sui-adapter/src/execution_engine.rs @@ -10,7 +10,7 @@ mod checked { use move_binary_format::CompiledModule; use move_trace_format::format::MoveTraceBuilder; use move_vm_runtime::move_vm::MoveVM; - use std::{collections::HashSet, sync::Arc}; + use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc}; use sui_types::balance::{ BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME, BALANCE_MODULE_NAME, @@ -140,14 +140,16 @@ 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, epoch_timestamp_ms, gas_price, + gas_data.budget, sponsor, ); + let tx_ctx = Rc::new(RefCell::new(tx_ctx)); let is_epoch_change = transaction_kind.is_end_of_epoch_tx(); @@ -156,7 +158,7 @@ mod checked { &mut temporary_store, transaction_kind, &mut gas_charger, - &mut tx_ctx, + tx_ctx, move_vm, protocol_config, metrics, @@ -260,7 +262,7 @@ mod checked { protocol_config: &ProtocolConfig, metrics: Arc, move_vm: &Arc, - tx_context: &mut TxContext, + tx_context: Rc>, input_objects: CheckedInputObjects, pt: ProgrammableTransaction, ) -> Result { @@ -269,11 +271,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, @@ -294,7 +296,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, @@ -318,7 +320,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 @@ -585,7 +587,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, @@ -610,7 +612,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"); } @@ -620,7 +622,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()); @@ -931,7 +933,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, @@ -955,7 +957,7 @@ mod checked { metrics.clone(), move_vm, temporary_store, - tx_ctx, + tx_ctx.clone(), gas_charger, advance_epoch_pt, trace_builder_opt, @@ -985,7 +987,7 @@ mod checked { metrics.clone(), move_vm, temporary_store, - tx_ctx, + tx_ctx.clone(), gas_charger, advance_epoch_safe_mode_pt, trace_builder_opt, @@ -1030,14 +1032,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 @@ -1060,7 +1062,7 @@ mod checked { metrics.clone(), move_vm, temporary_store, - tx_ctx, + tx_ctx.clone(), gas_charger, publish_pt, trace_builder_opt, @@ -1101,7 +1103,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, @@ -1242,7 +1244,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, @@ -1311,7 +1313,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 4048df3c2dbc5..784c1eb805c1f 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs @@ -5,10 +5,11 @@ pub use checked::*; #[sui_macros::with_checked_arithmetic] mod checked { - use std::collections::BTreeSet; use std::{ borrow::Borrow, - collections::{BTreeMap, HashMap}, + cell::RefCell, + collections::{BTreeMap, BTreeSet, HashMap}, + rc::Rc, sync::Arc, }; @@ -80,7 +81,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 +123,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 +191,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 +200,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 +239,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 +612,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 +846,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 6f765602511e3..de8493cc6b426 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs @@ -30,11 +30,13 @@ mod checked { }; use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type}; use serde::{de::DeserializeSeed, Deserialize}; - use std::time::Instant; use std::{ + cell::RefCell, collections::{BTreeMap, BTreeSet}, fmt, + rc::Rc, sync::Arc, + time::Instant, }; use sui_move_natives::object_runtime::ObjectRuntime; use sui_protocol_config::ProtocolConfig; @@ -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( @@ -840,10 +842,12 @@ mod checked { mut serialized_arguments: Vec>, trace_builder_opt: &mut Option, ) -> Result { + let is_native = context.protocol_config.move_native_context(); 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(is_native)); } } // script visibility checked manually for entry points @@ -873,7 +877,10 @@ mod checked { "Unable to deserialize TxContext bytes. {e}" )) })?; - context.tx_context.update_state(updated_ctx)?; + context + .tx_context + .borrow_mut() + .update_state(updated_ctx, is_native)?; } 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 f89dfb8ecb709..dc22f25211807 100644 --- a/sui-execution/latest/sui-move-natives/src/lib.rs +++ b/sui-execution/latest/sui-move-natives/src/lib.rs @@ -31,7 +31,12 @@ use self::{ transfer::{ TransferFreezeObjectCostParams, TransferInternalCostParams, TransferShareObjectCostParams, }, - tx_context::TxContextDeriveIdCostParams, + tx_context::{ + TxContextDeriveIdCostParams, TxContextEpochCostParams, TxContextEpochTimestampMsCostParams, + TxContextFreshIdCostParams, TxContextGasBudgetCostParams, TxContextGasPriceCostParams, + TxContextIdsCreatedCostParams, TxContextReplaceCostParams, TxContextSenderCostParams, + TxContextSponsorCostParams, + }, types::TypesIsOneTimeWitnessCostParams, validator::ValidatorValidateMetadataBcsCostParams, }; @@ -74,11 +79,14 @@ pub mod object_runtime; mod random; pub mod test_scenario; mod test_utils; +pub mod transaction_context; mod transfer; mod tx_context; mod types; mod validator; +// TODO: remove in later PRs once we define the proper cost of native functions +const DEFAULT_UNUSED_TX_CONTEXT_ENTRY_COST: u64 = 10; #[derive(Tid)] pub struct NativesCostTable { // Address natives @@ -113,6 +121,15 @@ pub struct NativesCostTable { // TxContext pub tx_context_derive_id_cost_params: TxContextDeriveIdCostParams, + pub tx_context_fresh_id_cost_params: TxContextFreshIdCostParams, + 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_sponsor_cost_params: TxContextSponsorCostParams, + pub tx_context_gas_price_cost_params: TxContextGasPriceCostParams, + pub tx_context_gas_budget_cost_params: TxContextGasBudgetCostParams, + pub tx_context_ids_created_cost_params: TxContextIdsCreatedCostParams, + pub tx_context_replace_cost_params: TxContextReplaceCostParams, // Type pub type_is_one_time_witness_cost_params: TypesIsOneTimeWitnessCostParams, @@ -338,11 +355,77 @@ 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_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_TX_CONTEXT_ENTRY_COST.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_TX_CONTEXT_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_TX_CONTEXT_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_TX_CONTEXT_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_TX_CONTEXT_ENTRY_COST.into() + }, + }, + tx_context_gas_price_cost_params: TxContextGasPriceCostParams { + tx_context_gas_price_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_gas_price_cost_base().into() + } else { + DEFAULT_UNUSED_TX_CONTEXT_ENTRY_COST.into() + }, + }, + tx_context_gas_budget_cost_params: TxContextGasBudgetCostParams { + tx_context_gas_budget_cost_base: if protocol_config.move_native_context() { + protocol_config.tx_context_gas_budget_cost_base().into() + } else { + DEFAULT_UNUSED_TX_CONTEXT_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_TX_CONTEXT_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_TX_CONTEXT_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() @@ -1022,11 +1105,53 @@ pub fn all_natives(silent: bool, protocol_config: &ProtocolConfig) -> NativeFunc "receive_impl", make_native!(transfer::receive_object_internal), ), + ( + "tx_context", + "last_created_id", + make_native!(tx_context::last_created_id), + ), ( "tx_context", "derive_id", make_native!(tx_context::derive_id), ), + ("tx_context", "fresh_id", make_native!(tx_context::fresh_id)), + ( + "tx_context", + "native_sender", + make_native!(tx_context::sender), + ), + ( + "tx_context", + "native_epoch", + make_native!(tx_context::epoch), + ), + ( + "tx_context", + "native_epoch_timestamp_ms", + make_native!(tx_context::epoch_timestamp_ms), + ), + ( + "tx_context", + "native_sponsor", + make_native!(tx_context::sponsor), + ), + ( + "tx_context", + "native_gas_price", + make_native!(tx_context::gas_price), + ), + ( + "tx_context", + "native_gas_budget", + make_native!(tx_context::gas_budget), + ), + ( + "tx_context", + "native_ids_created", + make_native!(tx_context::ids_created), + ), + ("tx_context", "replace", make_native!(tx_context::replace)), ( "types", "is_one_time_witness", diff --git a/sui-execution/latest/sui-move-natives/src/transaction_context/mod.rs b/sui-execution/latest/sui-move-natives/src/transaction_context/mod.rs new file mode 100644 index 0000000000000..6ca5886b0a0a1 --- /dev/null +++ b/sui-execution/latest/sui-move-natives/src/transaction_context/mod.rs @@ -0,0 +1,106 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use better_any::{Tid, TidAble}; +use move_binary_format::errors::{PartialVMError, PartialVMResult}; +use move_core_types::{account_address::AccountAddress, vm_status::StatusCode}; +use std::{cell::RefCell, rc::Rc}; +use sui_types::{ + base_types::{ObjectID, SuiAddress, TxContext}, + committee::EpochId, + digests::TransactionDigest, +}; + +// TransactionContext is a wrapper around TxContext that is exposed to NativeContextExtensions +// in order to provide transaction context information to Move native functions. +// Holds a Rc> to allow for mutation of the TxContext. +#[derive(Tid)] +pub struct TransactionContext { + pub(crate) tx_context: Rc>, + test_only: bool, +} + +impl TransactionContext { + pub fn new(tx_context: Rc>) -> Self { + Self { + tx_context, + test_only: false, + } + } + + pub fn new_for_testing(tx_context: Rc>) -> Self { + Self { + tx_context, + test_only: true, + } + } + + 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 sponsor(&self) -> Option { + self.tx_context.borrow().sponsor() + } + + pub fn gas_price(&self) -> u64 { + self.tx_context.borrow().gas_price() + } + + pub fn gas_budget(&self) -> u64 { + self.tx_context.borrow().gas_budget() + } + + pub fn ids_created(&self) -> u64 { + self.tx_context.borrow().ids_created() + } + + pub fn fresh_id(&self) -> ObjectID { + self.tx_context.borrow_mut().fresh_id() + } + + // + // Test only function + // + pub fn replace( + &self, + sender: AccountAddress, + tx_hash: Vec, + epoch: u64, + epoch_timestamp_ms: u64, + ids_created: u64, + gas_price: u64, + gas_budget: u64, + sponsor: Option, + ) -> PartialVMResult<()> { + if !self.test_only { + return Err( + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message("`replace` called on a non testing scenario".to_string()), + ); + } + self.tx_context.borrow_mut().replace( + sender, + tx_hash, + epoch, + epoch_timestamp_ms, + ids_created, + gas_price, + gas_budget, + sponsor, + ); + Ok(()) + } +} 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 1a208098b409d..4fe95c189eb04 100644 --- a/sui-execution/latest/sui-move-natives/src/tx_context.rs +++ b/sui-execution/latest/sui-move-natives/src/tx_context.rs @@ -8,15 +8,18 @@ use move_vm_types::{ loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value, }; use smallvec::smallvec; -use std::{collections::VecDeque, convert::TryFrom}; -use sui_types::base_types::{ObjectID, TransactionDigest}; +use std::collections::VecDeque; +use sui_types::{base_types::ObjectID, digests::TransactionDigest}; -use crate::{object_runtime::ObjectRuntime, NativesCostTable}; +use crate::{ + object_runtime::ObjectRuntime, transaction_context::TransactionContext, NativesCostTable, +}; #[derive(Clone)] pub struct TxContextDeriveIdCostParams { pub tx_context_derive_id_cost_base: InternalGas, } + /*************************************************************************************************** * native fun derive_id * Implementation of the Move native function `fun derive_id(tx_hash: vector, ids_created: u64): address` @@ -54,3 +57,389 @@ pub fn derive_id( smallvec![Value::address(address)], )) } +#[derive(Clone)] +pub struct TxContextFreshIdCostParams { + pub tx_context_fresh_id_cost_base: InternalGas, +} +/*************************************************************************************************** + * native fun fresh_id + * Implementation of the Move native function `fun fresh_id(): address` + **************************************************************************************************/ +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(); + let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut(); + object_runtime.new_id(fresh_id)?; + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::address(fresh_id.into())], + )) +} + +#[derive(Clone)] +pub struct TxContextSenderCostParams { + pub tx_context_sender_cost_base: InternalGas, +} +/*************************************************************************************************** + * native fun native_sender + * Implementation of the Move native function `fun native_sender(): address` + **************************************************************************************************/ +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, +} +/*************************************************************************************************** + * native fun native_epoch + * Implementation of the Move native function `fun native_epoch(): u64` + **************************************************************************************************/ +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, +} +/*************************************************************************************************** + * native fun native_epoch_timestamp_ms + * Implementation of the Move native function `fun native_epoch_timestamp_ms(): u64` + **************************************************************************************************/ +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 TxContextSponsorCostParams { + pub tx_context_sponsor_cost_base: InternalGas, +} +/*************************************************************************************************** + * native fun native_sponsor + * Implementation of the Move native function `fun native_sponsor(): Option
` + **************************************************************************************************/ +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 = transaction_context + .sponsor() + .map(|addr| addr.into()) + .into_iter(); + let sponsor = Value::vector_address(sponsor); + Ok(NativeResult::ok(context.gas_used(), smallvec![sponsor])) +} + +#[derive(Clone)] +pub struct TxContextGasPriceCostParams { + pub tx_context_gas_price_cost_base: InternalGas, +} +/*************************************************************************************************** + * native fun native_gas_price + * Implementation of the Move native function `fun native_gas_price(): u64` + **************************************************************************************************/ +pub fn gas_price( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_gas_price_cost_params = context + .extensions_mut() + .get::() + .tx_context_gas_price_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_gas_price_cost_params.tx_context_gas_price_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let gas_price = transaction_context.gas_price(); + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::u64(gas_price)], + )) +} + +#[derive(Clone)] +pub struct TxContextGasBudgetCostParams { + pub tx_context_gas_budget_cost_base: InternalGas, +} +/*************************************************************************************************** + * native fun native_gas_budget + * Implementation of the Move native function `fun native_gas_budget(): u64` + **************************************************************************************************/ +pub fn gas_budget( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_gas_budget_cost_params = context + .extensions_mut() + .get::() + .tx_context_gas_budget_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_gas_budget_cost_params.tx_context_gas_budget_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let gas_budget = transaction_context.gas_budget(); + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::u64(gas_budget)], + )) +} + +#[derive(Clone)] +pub struct TxContextIdsCreatedCostParams { + pub tx_context_ids_created_cost_base: InternalGas, +} +/*************************************************************************************************** + * native fun native_ids_created + * Implementation of the Move native function `fun native_ids_created(): u64` + **************************************************************************************************/ +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)], + )) +} + +// // +// // Test only function +// // +#[derive(Clone)] +pub struct TxContextReplaceCostParams { + pub tx_context_replace_cost_base: InternalGas, +} +/*************************************************************************************************** + * native fun native_replace + * Implementation of the Move native function + * ``` + * fun native_replace( + * sender: address, + * tx_hash: vector, + * epoch: u64, + * epoch_timestamp_ms: u64, + * ids_created: u64, + * ) + * ``` + * Used by all testing functions that have to change a value in the `TransactionContext`. + **************************************************************************************************/ +pub fn replace( + context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 8); + + let tx_context_replace_cost_params = context + .extensions_mut() + .get::() + .tx_context_replace_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_replace_cost_params.tx_context_replace_cost_base + ); + + let mut sponsor: Vec = pop_arg!(args, Vec); + let gas_budget: u64 = pop_arg!(args, u64); + let gas_price: u64 = pop_arg!(args, u64); + 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, + gas_price, + gas_budget, + sponsor.pop(), + )?; + + Ok(NativeResult::ok(context.gas_used(), smallvec![])) +} +// Attempt to get the most recent created object ID when none has been created. +// Lifted out of Move into this native function. +const E_NO_IDS_CREATED: u64 = 1; + +// use same protocol config and cost value as derive_id +/*************************************************************************************************** + * native fun last_created_id + * Implementation of the Move native function `fun last_created_id(): address` + **************************************************************************************************/ +pub fn last_created_id( + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.is_empty()); + + let tx_context_derive_id_cost_params = context + .extensions_mut() + .get::() + .tx_context_derive_id_cost_params + .clone(); + native_charge_gas_early_exit!( + context, + tx_context_derive_id_cost_params.tx_context_derive_id_cost_base + ); + + let transaction_context: &mut TransactionContext = context.extensions_mut().get_mut(); + let mut ids_created = transaction_context.ids_created(); + if ids_created == 0 { + return Ok(NativeResult::err(context.gas_used(), E_NO_IDS_CREATED)); + } + ids_created -= 1; + let digest = transaction_context.digest(); + let address = AccountAddress::from(ObjectID::derive_id(digest, ids_created)); + let obj_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut(); + obj_runtime.new_id(address.into())?; + + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::address(address)], + )) +} diff --git a/sui-execution/src/latest.rs b/sui-execution/src/latest.rs index 125cd9dcd8a02..f26da4fbe41fa 100644 --- a/sui-execution/src/latest.rs +++ b/sui-execution/src/latest.rs @@ -4,7 +4,7 @@ use move_binary_format::CompiledModule; use move_trace_format::format::MoveTraceBuilder; use move_vm_config::verifier::{MeterConfig, VerifierConfig}; -use std::{collections::HashSet, path::PathBuf, sync::Arc}; +use std::{cell::RefCell, collections::HashSet, path::PathBuf, rc::Rc, sync::Arc}; use sui_protocol_config::ProtocolConfig; use sui_types::execution::ExecutionTiming; use sui_types::transaction::GasData; @@ -180,21 +180,23 @@ 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, epoch_timestamp_ms, - // genesis transaction: RGP: 1, sponsor: None + // genesis transaction: RGP: 1, budget: 1M, sponsor: None 1, + 1_000_000, 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, ) diff --git a/sui-execution/src/v0.rs b/sui-execution/src/v0.rs index 3d19d57eb172a..a972dd6f47544 100644 --- a/sui-execution/src/v0.rs +++ b/sui-execution/src/v0.rs @@ -188,9 +188,10 @@ impl executor::Executor for Executor { transaction_digest, &epoch_id, epoch_timestamp_ms, - // genesis transaction: RGP: 1, sponsor: None + // genesis transaction: RGP: 1, budget: 1M, sponsor: None // Those values are unused anyway in execution versions before 3 (or latest) 1, + 1_000_000, None, ); execute_genesis_state_update( diff --git a/sui-execution/src/v1.rs b/sui-execution/src/v1.rs index 8fdf1996f4bd2..49ab00707e531 100644 --- a/sui-execution/src/v1.rs +++ b/sui-execution/src/v1.rs @@ -188,9 +188,10 @@ impl executor::Executor for Executor { transaction_digest, &epoch_id, epoch_timestamp_ms, - // genesis transaction: RGP: 1, sponsor: None + // genesis transaction: RGP: 1, budget: 1M, sponsor: None // Those values are unused anyway in execution versions before 3 (or latest) 1, + 1_000_000, None, ); execute_genesis_state_update( diff --git a/sui-execution/src/v2.rs b/sui-execution/src/v2.rs index fd332db0663b4..7b9a3c06f42ee 100644 --- a/sui-execution/src/v2.rs +++ b/sui-execution/src/v2.rs @@ -188,9 +188,10 @@ impl executor::Executor for Executor { transaction_digest, &epoch_id, epoch_timestamp_ms, - // genesis transaction: RGP: 1, sponsor: None + // genesis transaction: RGP: 1, budget: 1M, sponsor: None // Those values are unused anyway in execution versions before 3 (or latest) 1, + 1_000_000, None, ); execute_genesis_state_update( diff --git a/sui-execution/v0/sui-adapter/src/execution_engine.rs b/sui-execution/v0/sui-adapter/src/execution_engine.rs index b63e1537fe46d..a2e5705c5cbc5 100644 --- a/sui-execution/v0/sui-adapter/src/execution_engine.rs +++ b/sui-execution/v0/sui-adapter/src/execution_engine.rs @@ -89,6 +89,7 @@ mod checked { epoch_timestamp_ms, // Those values are unused in execution versions before 3 (or latest) 1, + 1_000_000, None, ); diff --git a/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs b/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs index 9cb7ff5f965d2..38118fca4014f 100644 --- a/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs @@ -819,10 +819,11 @@ mod checked { tx_context_kind: TxContextKind, mut serialized_arguments: Vec>, ) -> Result { + let is_native = context.protocol_config.move_native_context(); 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.to_bcs_legacy_context(is_native)); } } // script visibility checked manually for entry points @@ -853,7 +854,7 @@ mod checked { "Unable to deserialize TxContext bytes. {e}" )) })?; - context.tx_context.update_state(updated_ctx)?; + context.tx_context.update_state(updated_ctx, is_native)?; } Ok(result) } diff --git a/sui-execution/v1/sui-adapter/src/execution_engine.rs b/sui-execution/v1/sui-adapter/src/execution_engine.rs index 2ed9732fd468e..09c059fdfd24c 100644 --- a/sui-execution/v1/sui-adapter/src/execution_engine.rs +++ b/sui-execution/v1/sui-adapter/src/execution_engine.rs @@ -105,6 +105,7 @@ mod checked { epoch_timestamp_ms, // Those values are unused in execution versions before 3 (or latest) 1, + 1_000_000, None, ); diff --git a/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs b/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs index d68fe6e4fbd4d..68e851f894958 100644 --- a/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs @@ -754,10 +754,11 @@ mod checked { tx_context_kind: TxContextKind, mut serialized_arguments: Vec>, ) -> Result { + let is_native = context.protocol_config.move_native_context(); 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.to_bcs_legacy_context(is_native)); } } // script visibility checked manually for entry points @@ -786,7 +787,7 @@ mod checked { "Unable to deserialize TxContext bytes. {e}" )) })?; - context.tx_context.update_state(updated_ctx)?; + context.tx_context.update_state(updated_ctx, is_native)?; } Ok(result) } diff --git a/sui-execution/v2/sui-adapter/src/execution_engine.rs b/sui-execution/v2/sui-adapter/src/execution_engine.rs index 65f9293bbf8ff..73f547c27ee40 100644 --- a/sui-execution/v2/sui-adapter/src/execution_engine.rs +++ b/sui-execution/v2/sui-adapter/src/execution_engine.rs @@ -116,6 +116,7 @@ mod checked { epoch_timestamp_ms, // Those values are unused in execution versions before 3 (or latest) 1, + 1_000_000, None, ); diff --git a/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs b/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs index 0ea7d901b89f5..e9ab1c1fcee56 100644 --- a/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs @@ -760,10 +760,11 @@ mod checked { tx_context_kind: TxContextKind, mut serialized_arguments: Vec>, ) -> Result { + let is_native = context.protocol_config.move_native_context(); 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.to_bcs_legacy_context(is_native)); } } // script visibility checked manually for entry points @@ -792,7 +793,7 @@ mod checked { "Unable to deserialize TxContext bytes. {e}" )) })?; - context.tx_context.update_state(updated_ctx)?; + context.tx_context.update_state(updated_ctx, is_native)?; } Ok(result) }