From 6dcf7ed953d90db8b374c536e52bef8ea0fdc55e Mon Sep 17 00:00:00 2001 From: Dario Russi <113150618+dariorussi@users.noreply.github.com> Date: Wed, 2 Oct 2024 17:48:34 -0500 Subject: [PATCH] txcontext --- .../packages/sui-framework/Move.lock | 2 +- .../sui-framework/sources/tx_context.move | 15 ++++ crates/sui-protocol-config/src/lib.rs | 15 +++- crates/sui-types/src/base_types.rs | 4 ++ .../latest/sui-adapter/src/adapter.rs | 15 +++- .../src/programmable_transactions/context.rs | 2 +- .../programmable_transactions/execution.rs | 38 ++++++++-- .../latest/sui-move-natives/src/lib.rs | 26 +++++++ .../sui-move-natives/src/native_tx_context.rs | 33 +++++++++ .../latest/sui-move-natives/src/tx_context.rs | 70 +++++++++++++++++++ 10 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 sui-execution/latest/sui-move-natives/src/native_tx_context.rs diff --git a/crates/sui-framework/packages/sui-framework/Move.lock b/crates/sui-framework/packages/sui-framework/Move.lock index 018511436eb41..ddde3151dfbba 100644 --- a/crates/sui-framework/packages/sui-framework/Move.lock +++ b/crates/sui-framework/packages/sui-framework/Move.lock @@ -14,6 +14,6 @@ name = "MoveStdlib" source = { local = "../move-stdlib" } [move.toolchain-version] -compiler-version = "1.30.0" +compiler-version = "1.34.0" edition = "2024.beta" flavor = "sui" 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..9cffd57ededd9 100644 --- a/crates/sui-framework/packages/sui-framework/sources/tx_context.move +++ b/crates/sui-framework/packages/sui-framework/sources/tx_context.move @@ -74,6 +74,21 @@ fun ids_created(self: &TxContext): 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 fun native_sender(): address; + +#[allow(unused_function)] +native fun native_digest(): &vector; + +#[allow(unused_function)] +native fun native_epoch(): u64; + +#[allow(unused_function)] +native fun native_epoch_timestamp_ms(): u64; + +#[allow(unused_function)] +native fun native_sponsor(): address; + // ==== test-only functions ==== #[test_only] diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 6160be8d541df..5628143e4b810 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -18,7 +18,7 @@ use tracing::{info, warn}; /// The minimum and maximum protocol versions supported by this build. const MIN_PROTOCOL_VERSION: u64 = 1; -const MAX_PROTOCOL_VERSION: u64 = 61; +const MAX_PROTOCOL_VERSION: u64 = 62; // Record history of protocol version allocations here: // @@ -183,6 +183,8 @@ const MAX_PROTOCOL_VERSION: u64 = 61; // Version 61: Switch to distributed vote scoring in consensus in testnet // Further reduce minimum number of random beacon shares. // Add feature flag for Mysticeti fastpath. +// Version 62: Framework natives for transaction context and protocol config +// feature flag to turn it on/off #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -541,6 +543,10 @@ struct FeatureFlags { // Enables Mysticeti fastpath. #[serde(skip_serializing_if = "is_false")] mysticeti_fastpath: bool, + + // Make transaction context native and zero out the Move struct + #[serde(skip_serializing_if = "is_false")] + transaction_context_native: bool, } fn is_false(b: &bool) -> bool { @@ -1615,6 +1621,10 @@ impl ProtocolConfig { pub fn mysticeti_fastpath(&self) -> bool { self.feature_flags.mysticeti_fastpath } + + pub fn transaction_context_native(&self) -> bool { + self.feature_flags.transaction_context_native + } } #[cfg(not(msim))] @@ -2808,6 +2818,9 @@ impl ProtocolConfig { cfg.feature_flags.mysticeti_fastpath = true; } } + 62 => { + cfg.feature_flags.transaction_context_native = false; + } // Use this template when making changes: // // // modify an existing constant. diff --git a/crates/sui-types/src/base_types.rs b/crates/sui-types/src/base_types.rs index e31255a7167d8..4e5347fe73256 100644 --- a/crates/sui-types/src/base_types.rs +++ b/crates/sui-types/src/base_types.rs @@ -971,6 +971,10 @@ impl TxContext { self.epoch } + pub fn epoch_timestamp_ms(&self) -> CheckpointTimestamp { + self.epoch_timestamp_ms + } + /// 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); diff --git a/sui-execution/latest/sui-adapter/src/adapter.rs b/sui-execution/latest/sui-adapter/src/adapter.rs index c1856234639c7..0250e4edb615a 100644 --- a/sui-execution/latest/sui-adapter/src/adapter.rs +++ b/sui-execution/latest/sui-adapter/src/adapter.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub use checked::*; + #[sui_macros::with_checked_arithmetic] mod checked { #[cfg(feature = "gas-profiler")] @@ -23,11 +24,15 @@ mod checked { native_functions::NativeFunctionTable, }; use sui_move_natives::object_runtime; - use sui_types::metrics::BytecodeVerifierMetrics; + use sui_types::{base_types::TxContext, metrics::BytecodeVerifierMetrics}; use sui_verifier::check_for_verifier_timeout; use tracing::instrument; - use sui_move_natives::{object_runtime::ObjectRuntime, NativesCostTable}; + use sui_move_natives::{ + object_runtime::ObjectRuntime, + native_tx_context::NativeTxContext, + NativesCostTable, + }; use sui_protocol_config::ProtocolConfig; use sui_types::{ base_types::*, @@ -84,8 +89,9 @@ mod checked { is_metered: bool, protocol_config: &'r ProtocolConfig, metrics: Arc, - current_epoch_id: EpochId, + tx_context: &TxContext, ) -> NativeContextExtensions<'r> { + let current_epoch_id = tx_context.epoch(); let mut extensions = NativeContextExtensions::default(); extensions.add(ObjectRuntime::new( child_resolver, @@ -96,6 +102,9 @@ mod checked { current_epoch_id, )); extensions.add(NativesCostTable::from_protocol_config(protocol_config)); + if protocol_config.transaction_context_native() { + extensions.add(NativeTxContext::from(tx_context)); + } extensions } 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 0b8d483463a45..da12ba90855ea 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/context.rs @@ -189,7 +189,7 @@ mod checked { !gas_charger.is_unmetered(), protocol_config, metrics.clone(), - tx_context.epoch(), + tx_context, ); // Set the profiler if in CLI 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 e17b2417e6192..98b1687f2aedd 100644 --- a/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/latest/sui-adapter/src/programmable_transactions/execution.rs @@ -46,6 +46,7 @@ mod checked { RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME, }, coin::Coin, + digests::TransactionDigest, error::{command_argument_error, ExecutionError, ExecutionErrorKind}, id::{RESOLVED_SUI_ID, UID}, metrics::LimitsMetrics, @@ -760,12 +761,30 @@ mod checked { tx_context_kind: TxContextKind, mut serialized_arguments: Vec>, ) -> Result { + let is_txctx_native = context.protocol_config.transaction_context_native(); + match tx_context_kind { TxContextKind::None => (), TxContextKind::Mutable | TxContextKind::Immutable => { - serialized_arguments.push(context.tx_context.to_vec()); + // build a TxContext for Move. + // When TxContext is native, it just pushes a zero'd out value + let tx_context = if is_txctx_native { + // zero out TxContext move value. TxContext API is native + TxContext::new_from_components( + &SuiAddress::ZERO, + &TransactionDigest::ZERO, + &0u64, + 0u64, + ).to_vec() + } else { + // legacy TxContext model. Copied on every move call + context.tx_context.to_vec() + }; + // write the TxContext value either in native or non native mode + serialized_arguments.push(tx_context); } } + // script visibility checked manually for entry points let mut result = context .execute_function_bypass_visibility( @@ -784,15 +803,20 @@ mod checked { // Move VM (e.g. to account for the number of created // objects). if tx_context_kind == TxContextKind::Mutable { + // pop the TxContext value if there let Some((_, ctx_bytes, _)) = result.mutable_reference_outputs.pop() else { invariant_violation!("Missing TxContext in reference outputs"); }; - let updated_ctx: TxContext = bcs::from_bytes(&ctx_bytes).map_err(|e| { - ExecutionError::invariant_violation(format!( - "Unable to deserialize TxContext bytes. {e}" - )) - })?; - context.tx_context.update_state(updated_ctx)?; + // if in legacy mode, update the Rust TxContext value, otherwise it's in the + // native extension and it gets updated at the end of the PTB + if !is_txctx_native { + let updated_ctx = bcs::from_bytes(&ctx_bytes).map_err(|e| { + ExecutionError::invariant_violation(format!( + "Unable to deserialize TxContext bytes. {e}" + )) + })?; + context.tx_context.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 e2f50dfe1b86a..c9488c7d22d89 100644 --- a/sui-execution/latest/sui-move-natives/src/lib.rs +++ b/sui-execution/latest/sui-move-natives/src/lib.rs @@ -77,6 +77,7 @@ mod transfer; mod tx_context; mod types; mod validator; +pub mod native_tx_context; #[derive(Tid)] pub struct NativesCostTable { @@ -987,6 +988,31 @@ pub fn all_natives(silent: bool, protocol_config: &ProtocolConfig) -> NativeFunc "derive_id", make_native!(tx_context::derive_id), ), + ( + "tx_context", + "native_sender", + make_native!(tx_context::native_sender), + ), + ( + "tx_context", + "native_digest", + make_native!(tx_context::native_digest), + ), + ( + "tx_context", + "native_epoch", + make_native!(tx_context::native_epoch), + ), + ( + "tx_context", + "native_epoch_timestamp_ms", + make_native!(tx_context::native_epoch_timestamp_ms), + ), + ( + "tx_context", + "native_sponsor", + make_native!(tx_context::native_sponsor), + ), ( "types", "is_one_time_witness", diff --git a/sui-execution/latest/sui-move-natives/src/native_tx_context.rs b/sui-execution/latest/sui-move-natives/src/native_tx_context.rs new file mode 100644 index 0000000000000..a4b28d98eff07 --- /dev/null +++ b/sui-execution/latest/sui-move-natives/src/native_tx_context.rs @@ -0,0 +1,33 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use better_any::{Tid, TidAble}; +use move_core_types::account_address::AccountAddress; +use sui_types::base_types::{EpochId, TransactionDigest, TxContext}; +use sui_types::messages_checkpoint::CheckpointTimestamp; + +#[derive(Debug, Tid)] +pub struct NativeTxContext { + /// Signer/sender of the transaction + pub sender: AccountAddress, + /// Digest of the current transaction + pub digest: TransactionDigest, + /// The current epoch number + pub epoch: EpochId, + /// Timestamp that the epoch started at + pub epoch_timestamp_ms: CheckpointTimestamp, + /// Number of `ObjectID`'s generated during execution of the current transaction + pub ids_created: u64, +} + +impl From<&TxContext> for NativeTxContext { + fn from(tx_context: &TxContext) -> Self { + NativeTxContext { + sender: AccountAddress::new(tx_context.sender().to_inner()), + digest: tx_context.digest(), + epoch: tx_context.epoch(), + epoch_timestamp_ms: tx_context.epoch_timestamp_ms(), + ids_created: 0, + } + } +} 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..7ed518cffd229 100644 --- a/sui-execution/latest/sui-move-natives/src/tx_context.rs +++ b/sui-execution/latest/sui-move-natives/src/tx_context.rs @@ -54,3 +54,73 @@ pub fn derive_id( smallvec![Value::address(address)], )) } + +/*************************************************************************************************** + * native fun native_sender(): address; + **************************************************************************************************/ +pub fn native_sender( + context: &mut NativeContext, + _ty_args: Vec, + _args: VecDeque, +) -> PartialVMResult { + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::address(AccountAddress::ZERO)], + )) +} + +/*************************************************************************************************** + * native fun native_digest(): &vector; + **************************************************************************************************/ +pub fn native_digest( + context: &mut NativeContext, + _ty_args: Vec, + _args: VecDeque, +) -> PartialVMResult { + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::address(AccountAddress::ZERO)], + )) +} + +/*************************************************************************************************** + * native fun native_epoch(): u64; + **************************************************************************************************/ +pub fn native_epoch( + context: &mut NativeContext, + _ty_args: Vec, + _args: VecDeque, +) -> PartialVMResult { + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::u64(0)], + )) +} + +/*************************************************************************************************** + * native fun native_epoch_timestamp_ms(): u64; + **************************************************************************************************/ +pub fn native_epoch_timestamp_ms( + context: &mut NativeContext, + _ty_args: Vec, + _args: VecDeque, +) -> PartialVMResult { + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::u64(0)], + )) +} + +/*************************************************************************************************** + * native fun sponsor(): address; + **************************************************************************************************/ +pub fn native_sponsor( + context: &mut NativeContext, + _ty_args: Vec, + _args: VecDeque, +) -> PartialVMResult { + Ok(NativeResult::ok( + context.gas_used(), + smallvec![Value::address(AccountAddress::ZERO)], + )) +} \ No newline at end of file