From 7a69f9ea7b426a40f30c86bddd0dedebc14d1129 Mon Sep 17 00:00:00 2001 From: George Mitenkov Date: Thu, 16 Apr 2026 13:51:55 +0100 Subject: [PATCH 1/2] [vm] Versioned enum-based transaction prologue and epilogue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the proliferating per-version prologue/epilogue functions with a single versioned entry point each. PrologueArgs and EpilogueArgs enums allow adding new arguments as new variants without creating new on-chain functions, and old variants can be pruned once fully rolled out — unlike the current 18+ functions that can never be deleted. V1 matches unified_prologue_fee_payer_v2 / unified_epilogue_v2. Both signers (sender + fee_payer) are always passed. Rust side uses a builder pattern with bcs::to_bytes for clean serialization. Gated behind VERSIONED_TRANSACTION_VALIDATION feature flag (111). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/components/feature_flags.rs | 7 + aptos-move/aptos-vm/src/lib.rs | 1 + .../aptos-vm/src/system_module_names.rs | 10 + .../aptos-vm/src/transaction_validation.rs | 219 +++++----- .../src/transaction_validation_versioned.rs | 237 ++++++++++ .../aptos-framework/doc/transaction_fee.md | 107 +++-- .../doc/transaction_validation.md | 407 ++++++++++++------ .../sources/transaction_fee.move | 35 +- .../sources/transaction_validation.move | 214 +++++---- .../sources/transaction_validation.spec.move | 36 -- .../framework/cached-packages/src/head.mrb | Bin 1344815 -> 1346211 bytes types/src/on_chain_config/aptos_features.rs | 10 + 12 files changed, 881 insertions(+), 402 deletions(-) create mode 100644 aptos-move/aptos-vm/src/transaction_validation_versioned.rs diff --git a/aptos-move/aptos-release-builder/src/components/feature_flags.rs b/aptos-move/aptos-release-builder/src/components/feature_flags.rs index 4134ac10928..959c8787e0b 100644 --- a/aptos-move/aptos-release-builder/src/components/feature_flags.rs +++ b/aptos-move/aptos-release-builder/src/components/feature_flags.rs @@ -163,6 +163,7 @@ pub enum FeatureFlag { PublicStructEnumArgs, MultisigScript, TransactionLimits, + VersionedTransactionValidation, } fn generate_features_blob(writer: &CodeWriter, data: &[u64]) { @@ -425,6 +426,9 @@ impl From for AptosFeatureFlag { FeatureFlag::PublicStructEnumArgs => AptosFeatureFlag::PUBLIC_STRUCT_ENUM_ARGS, FeatureFlag::MultisigScript => AptosFeatureFlag::MULTISIG_SCRIPT, FeatureFlag::TransactionLimits => AptosFeatureFlag::TRANSACTION_LIMITS, + FeatureFlag::VersionedTransactionValidation => { + AptosFeatureFlag::VERSIONED_TRANSACTION_VALIDATION + }, } } } @@ -614,6 +618,9 @@ impl From for FeatureFlag { AptosFeatureFlag::PUBLIC_STRUCT_ENUM_ARGS => FeatureFlag::PublicStructEnumArgs, AptosFeatureFlag::MULTISIG_SCRIPT => FeatureFlag::MultisigScript, AptosFeatureFlag::TRANSACTION_LIMITS => FeatureFlag::TransactionLimits, + AptosFeatureFlag::VERSIONED_TRANSACTION_VALIDATION => { + FeatureFlag::VersionedTransactionValidation + }, } } } diff --git a/aptos-move/aptos-vm/src/lib.rs b/aptos-move/aptos-vm/src/lib.rs index 6255fc42fff..50d6d7457de 100644 --- a/aptos-move/aptos-vm/src/lib.rs +++ b/aptos-move/aptos-vm/src/lib.rs @@ -120,6 +120,7 @@ pub mod system_module_names; pub mod testing; pub mod transaction_metadata; mod transaction_validation; +mod transaction_validation_versioned; pub mod validator_txns; pub mod verifier; diff --git a/aptos-move/aptos-vm/src/system_module_names.rs b/aptos-move/aptos-vm/src/system_module_names.rs index ee464f91fd2..16cffd2de8d 100644 --- a/aptos-move/aptos-vm/src/system_module_names.rs +++ b/aptos-move/aptos-vm/src/system_module_names.rs @@ -80,3 +80,13 @@ pub static TRANSACTION_FEE_MODULE: Lazy = Lazy::new(|| { }); pub const EMIT_FEE_STATEMENT: &IdentStr = ident_str!("emit_fee_statement"); + +pub static TRANSACTION_VALIDATION_MODULE: Lazy = Lazy::new(|| { + ModuleId::new( + account_config::CORE_CODE_ADDRESS, + ident_str!("transaction_validation").to_owned(), + ) +}); + +pub const VERSIONED_PROLOGUE_NAME: &IdentStr = ident_str!("versioned_prologue"); +pub const VERSIONED_EPILOGUE_NAME: &IdentStr = ident_str!("versioned_epilogue"); diff --git a/aptos-move/aptos-vm/src/transaction_validation.rs b/aptos-move/aptos-vm/src/transaction_validation.rs index 434aef94b59..4817d8ce4ac 100644 --- a/aptos-move/aptos-vm/src/transaction_validation.rs +++ b/aptos-move/aptos-vm/src/transaction_validation.rs @@ -18,9 +18,7 @@ use aptos_types::{ fee_statement::FeeStatement, move_utils::as_move_value::AsMoveValue, on_chain_config::Features, - transaction::{ - MultisigTransactionPayload, ReplayProtector, TransactionExecutableRef, TxnLimitsRequest, - }, + transaction::{MultisigTransactionPayload, ReplayProtector, TransactionExecutableRef}, }; use aptos_vm_logging::log_schema::AdapterLogSchema; use fail::fail_point; @@ -64,11 +62,6 @@ pub static APTOS_TRANSACTION_VALIDATION: Lazy = unified_prologue_fee_payer_v2_name: Identifier::new("unified_prologue_fee_payer_v2") .unwrap(), unified_epilogue_v2_name: Identifier::new("unified_epilogue_v2").unwrap(), - - // V3 prologues support voting-power-based high-txn-limits. - unified_prologue_v3_name: Identifier::new("unified_prologue_v3").unwrap(), - unified_prologue_fee_payer_v3_name: Identifier::new("unified_prologue_fee_payer_v3") - .unwrap(), }); /// On-chain functions used to validate transactions @@ -94,10 +87,6 @@ pub struct TransactionValidation { pub unified_prologue_v2_name: Identifier, pub unified_prologue_fee_payer_v2_name: Identifier, pub unified_epilogue_v2_name: Identifier, - - // V3 prologues support voting-power-based high-txn-limits. - pub unified_prologue_v3_name: Identifier, - pub unified_prologue_fee_payer_v3_name: Identifier, } impl TransactionValidation { @@ -133,6 +122,19 @@ pub(crate) fn run_script_prologue( traversal_context: &mut TraversalContext, is_simulation: bool, ) -> Result<(), VMStatus> { + if features.is_versioned_transaction_validation_enabled() { + return crate::transaction_validation_versioned::run_prologue( + session, + module_storage, + serialized_signers, + txn_data, + features, + log_context, + traversal_context, + is_simulation, + ); + } + let txn_replay_protector = txn_data.replay_protector(); let txn_authentication_key = txn_data.authentication_proof().optional_auth_key(); let txn_gas_price = txn_data.gas_unit_price(); @@ -166,103 +168,88 @@ pub(crate) fn run_script_prologue( } }; - let (prologue_function_name, mut serialized_args) = - if let (true, Some(fee_payer_auth_key)) = ( - txn_data.fee_payer().is_some(), - txn_data - .fee_payer_authentication_proof - .as_ref() - .map(|proof| proof.optional_auth_key()), - ) { - let serialized_args = vec![ - serialized_signers.sender(), - serialized_signers - .fee_payer() - .ok_or_else(|| VMStatus::error(StatusCode::UNREACHABLE, None))?, - txn_authentication_key - .as_move_value() - .simple_serialize() - .unwrap(), - fee_payer_auth_key - .as_move_value() - .simple_serialize() - .unwrap(), - replay_protector_move_value, - MoveValue::vector_address(txn_data.secondary_signers()) - .simple_serialize() - .unwrap(), - MoveValue::Vector(secondary_auth_keys) - .simple_serialize() - .unwrap(), - MoveValue::U64(txn_gas_price.into()) - .simple_serialize() - .unwrap(), - MoveValue::U64(txn_max_gas_units.into()) - .simple_serialize() - .unwrap(), - MoveValue::U64(txn_expiration_timestamp_secs) - .simple_serialize() - .unwrap(), - MoveValue::U8(chain_id.id()).simple_serialize().unwrap(), - MoveValue::Bool(is_simulation).simple_serialize().unwrap(), - ]; - ( - if features.is_transaction_limits_enabled() { - &APTOS_TRANSACTION_VALIDATION.unified_prologue_fee_payer_v3_name - } else if features.is_transaction_payload_v2_enabled() { - &APTOS_TRANSACTION_VALIDATION.unified_prologue_fee_payer_v2_name - } else { - &APTOS_TRANSACTION_VALIDATION.unified_prologue_fee_payer_name - }, - serialized_args, - ) - } else { - let serialized_args = vec![ - serialized_signers.sender(), - txn_authentication_key - .as_move_value() - .simple_serialize() - .unwrap(), - replay_protector_move_value, - MoveValue::vector_address(txn_data.secondary_signers()) - .simple_serialize() - .unwrap(), - MoveValue::Vector(secondary_auth_keys) - .simple_serialize() - .unwrap(), - MoveValue::U64(txn_gas_price.into()) - .simple_serialize() - .unwrap(), - MoveValue::U64(txn_max_gas_units.into()) - .simple_serialize() - .unwrap(), - MoveValue::U64(txn_expiration_timestamp_secs) - .simple_serialize() - .unwrap(), - MoveValue::U8(chain_id.id()).simple_serialize().unwrap(), - MoveValue::Bool(is_simulation).simple_serialize().unwrap(), - ]; - ( - if features.is_transaction_limits_enabled() { - &APTOS_TRANSACTION_VALIDATION.unified_prologue_v3_name - } else if features.is_transaction_payload_v2_enabled() { - &APTOS_TRANSACTION_VALIDATION.unified_prologue_v2_name - } else { - &APTOS_TRANSACTION_VALIDATION.unified_prologue_name - }, - serialized_args, - ) - }; - - // Append the user's staking-backed request when dispatching to v3 prologue. - // Governance scripts pass None (they don't need Move-side validation). - if features.is_transaction_limits_enabled() { - let user_request = txn_data.txn_limits.as_ref().and_then(|v| match v { - TxnLimitsRequest::Staking(req) => Some(req), - TxnLimitsRequest::ApprovedGovernanceScript => None, - }); - serialized_args.push(bcs::to_bytes(&user_request).unwrap()); - } + let (prologue_function_name, serialized_args) = if let (true, Some(fee_payer_auth_key)) = ( + txn_data.fee_payer().is_some(), + txn_data + .fee_payer_authentication_proof + .as_ref() + .map(|proof| proof.optional_auth_key()), + ) { + let serialized_args = vec![ + serialized_signers.sender(), + serialized_signers + .fee_payer() + .ok_or_else(|| VMStatus::error(StatusCode::UNREACHABLE, None))?, + txn_authentication_key + .as_move_value() + .simple_serialize() + .unwrap(), + fee_payer_auth_key + .as_move_value() + .simple_serialize() + .unwrap(), + replay_protector_move_value, + MoveValue::vector_address(txn_data.secondary_signers()) + .simple_serialize() + .unwrap(), + MoveValue::Vector(secondary_auth_keys) + .simple_serialize() + .unwrap(), + MoveValue::U64(txn_gas_price.into()) + .simple_serialize() + .unwrap(), + MoveValue::U64(txn_max_gas_units.into()) + .simple_serialize() + .unwrap(), + MoveValue::U64(txn_expiration_timestamp_secs) + .simple_serialize() + .unwrap(), + MoveValue::U8(chain_id.id()).simple_serialize().unwrap(), + MoveValue::Bool(is_simulation).simple_serialize().unwrap(), + ]; + ( + if features.is_transaction_payload_v2_enabled() { + &APTOS_TRANSACTION_VALIDATION.unified_prologue_fee_payer_v2_name + } else { + &APTOS_TRANSACTION_VALIDATION.unified_prologue_fee_payer_name + }, + serialized_args, + ) + } else { + let serialized_args = vec![ + serialized_signers.sender(), + txn_authentication_key + .as_move_value() + .simple_serialize() + .unwrap(), + replay_protector_move_value, + MoveValue::vector_address(txn_data.secondary_signers()) + .simple_serialize() + .unwrap(), + MoveValue::Vector(secondary_auth_keys) + .simple_serialize() + .unwrap(), + MoveValue::U64(txn_gas_price.into()) + .simple_serialize() + .unwrap(), + MoveValue::U64(txn_max_gas_units.into()) + .simple_serialize() + .unwrap(), + MoveValue::U64(txn_expiration_timestamp_secs) + .simple_serialize() + .unwrap(), + MoveValue::U8(chain_id.id()).simple_serialize().unwrap(), + MoveValue::Bool(is_simulation).simple_serialize().unwrap(), + ]; + ( + if features.is_transaction_payload_v2_enabled() { + &APTOS_TRANSACTION_VALIDATION.unified_prologue_v2_name + } else { + &APTOS_TRANSACTION_VALIDATION.unified_prologue_name + }, + serialized_args, + ) + }; session .execute_function_bypass_visibility( @@ -503,6 +490,20 @@ fn run_epilogue( traversal_context: &mut TraversalContext, is_simulation: bool, ) -> VMResult<()> { + if features.is_versioned_transaction_validation_enabled() { + return crate::transaction_validation_versioned::run_epilogue( + session, + module_storage, + serialized_signers, + gas_remaining, + fee_statement, + txn_data, + features, + traversal_context, + is_simulation, + ); + } + let txn_gas_price = txn_data.gas_unit_price(); let txn_max_gas_units = txn_data.max_gas_amount(); let is_orderless_txn = txn_data.is_orderless(); diff --git a/aptos-move/aptos-vm/src/transaction_validation_versioned.rs b/aptos-move/aptos-vm/src/transaction_validation_versioned.rs new file mode 100644 index 00000000000..781d809b694 --- /dev/null +++ b/aptos-move/aptos-vm/src/transaction_validation_versioned.rs @@ -0,0 +1,237 @@ +// Copyright (c) Aptos Foundation +// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE + +use crate::{ + aptos_vm::SerializedSigners, + errors::convert_prologue_error, + move_vm_ext::{AptosMoveResolver, SessionExt}, + system_module_names::{ + TRANSACTION_VALIDATION_MODULE, VERSIONED_EPILOGUE_NAME, VERSIONED_PROLOGUE_NAME, + }, + testing::{maybe_raise_injected_error, InjectedError}, + transaction_metadata::TransactionMetadata, +}; +use aptos_gas_algebra::Gas; +use aptos_types::{ + fee_statement::FeeStatement, + on_chain_config::Features, + transaction::{ReplayProtector, TxnLimitsRequest, UserTxnLimitsRequest}, +}; +use aptos_vm_logging::log_schema::AdapterLogSchema; +use move_binary_format::errors::VMResult; +use move_core_types::account_address::AccountAddress; +use move_vm_runtime::{ + logging::expect_no_verification_errors, module_traversal::TraversalContext, ModuleStorage, +}; +use move_vm_types::gas::UnmeteredGasMeter; +use serde::Serialize; + +/// Mirrors Move enum in `transaction_validation.move` and needs to have the +/// same BCS serialization. +#[derive(Serialize)] +enum PrologueArgs { + V1 { + txn_sender_public_key: Option>, + fee_payer_public_key_hash: Option>, + replay_protector: ReplayProtector, + secondary_signer_addresses: Vec, + secondary_signer_public_key_hashes: Vec>>, + txn_gas_price: u64, + txn_max_gas_units: u64, + txn_expiration_time: u64, + chain_id: u8, + is_simulation: bool, + txn_limits_request: Option, + }, +} + +/// Builder that collects prologue arguments and selects the appropriate enum +/// variant to build. +pub(crate) struct PrologueBuilder { + txn_sender_public_key: Option>, + fee_payer_public_key_hash: Option>, + replay_protector: ReplayProtector, + secondary_signer_addresses: Vec, + secondary_signer_public_key_hashes: Vec>>, + txn_gas_price: u64, + txn_max_gas_units: u64, + txn_expiration_time: u64, + chain_id: u8, + is_simulation: bool, + txn_limits_request: Option, +} + +impl PrologueBuilder { + pub fn new(txn_data: &TransactionMetadata, is_simulation: bool) -> Self { + Self { + txn_sender_public_key: txn_data.authentication_proof().optional_auth_key(), + fee_payer_public_key_hash: txn_data + .fee_payer_authentication_proof + .as_ref() + .and_then(|proof| proof.optional_auth_key()), + replay_protector: txn_data.replay_protector(), + secondary_signer_addresses: txn_data.secondary_signers(), + secondary_signer_public_key_hashes: txn_data + .secondary_authentication_proofs + .iter() + .map(|proof| proof.optional_auth_key()) + .collect(), + txn_gas_price: txn_data.gas_unit_price().into(), + txn_max_gas_units: txn_data.max_gas_amount().into(), + txn_expiration_time: txn_data.expiration_timestamp_secs(), + chain_id: txn_data.chain_id().id(), + is_simulation, + txn_limits_request: txn_data.txn_limits.as_ref().and_then(|v| match v { + TxnLimitsRequest::ApprovedGovernanceScript => None, + TxnLimitsRequest::Staking(req) => Some(req.clone()), + }), + } + } + + /// Selects the highest supported variant based on feature flags and BCS-serializes it. + /// Currently only V1 exists. + pub fn build(self, _features: &Features) -> Vec { + let args = PrologueArgs::V1 { + txn_sender_public_key: self.txn_sender_public_key, + fee_payer_public_key_hash: self.fee_payer_public_key_hash, + replay_protector: self.replay_protector, + secondary_signer_addresses: self.secondary_signer_addresses, + secondary_signer_public_key_hashes: self.secondary_signer_public_key_hashes, + txn_gas_price: self.txn_gas_price, + txn_max_gas_units: self.txn_max_gas_units, + txn_expiration_time: self.txn_expiration_time, + chain_id: self.chain_id, + is_simulation: self.is_simulation, + txn_limits_request: self.txn_limits_request, + }; + bcs::to_bytes(&args).expect("Failed to serialize prologue arguments") + } +} + +pub(crate) fn run_prologue( + session: &mut SessionExt, + module_storage: &impl ModuleStorage, + serialized_signers: &SerializedSigners, + txn_data: &TransactionMetadata, + features: &Features, + log_context: &AdapterLogSchema, + traversal_context: &mut TraversalContext, + is_simulation: bool, +) -> Result<(), move_core_types::vm_status::VMStatus> { + let builder = PrologueBuilder::new(txn_data, is_simulation); + let serialized_args = vec![ + serialized_signers.sender(), + serialized_signers + .fee_payer() + .unwrap_or(serialized_signers.sender()), + builder.build(features), + ]; + session + .execute_function_bypass_visibility( + &TRANSACTION_VALIDATION_MODULE, + VERSIONED_PROLOGUE_NAME, + vec![], + serialized_args, + &mut UnmeteredGasMeter, + traversal_context, + module_storage, + ) + .map(|_return_vals| ()) + .map_err(expect_no_verification_errors) + .or_else(|err| convert_prologue_error(err, log_context)) +} + +/// Mirrors Move enum in `transaction_validation.move` and needs to have the +/// same BCS serialization. +#[derive(Serialize)] +enum EpilogueArgs { + V1 { + fee_statement: FeeStatement, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + is_simulation: bool, + is_orderless_txn: bool, + }, +} + +/// Builder that collects epilogue arguments and selects the appropriate enum +/// variant based on feature flags. +pub(crate) struct EpilogueBuilder { + fee_statement: FeeStatement, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + is_simulation: bool, + is_orderless_txn: bool, +} + +impl EpilogueBuilder { + pub fn new( + fee_statement: FeeStatement, + txn_data: &TransactionMetadata, + gas_remaining: Gas, + is_simulation: bool, + ) -> Self { + Self { + fee_statement, + txn_gas_price: txn_data.gas_unit_price().into(), + txn_max_gas_units: txn_data.max_gas_amount().into(), + gas_units_remaining: gas_remaining.into(), + is_simulation, + is_orderless_txn: txn_data.is_orderless(), + } + } + + /// Selects the highest supported variant based on feature flags and BCS-serializes it. + /// Currently only V1 exists. + pub fn build(self, _features: &Features) -> Vec { + let args = EpilogueArgs::V1 { + fee_statement: self.fee_statement, + txn_gas_price: self.txn_gas_price, + txn_max_gas_units: self.txn_max_gas_units, + gas_units_remaining: self.gas_units_remaining, + is_simulation: self.is_simulation, + is_orderless_txn: self.is_orderless_txn, + }; + bcs::to_bytes(&args).expect("Failed to serialize epilogue arguments") + } +} + +pub(crate) fn run_epilogue( + session: &mut SessionExt, + module_storage: &impl ModuleStorage, + serialized_signers: &SerializedSigners, + gas_remaining: Gas, + fee_statement: FeeStatement, + txn_data: &TransactionMetadata, + features: &Features, + traversal_context: &mut TraversalContext, + is_simulation: bool, +) -> VMResult<()> { + let builder = EpilogueBuilder::new(fee_statement, txn_data, gas_remaining, is_simulation); + let serialized_args = vec![ + serialized_signers.sender(), + serialized_signers + .fee_payer() + .unwrap_or(serialized_signers.sender()), + builder.build(features), + ]; + + session + .execute_function_bypass_visibility( + &TRANSACTION_VALIDATION_MODULE, + VERSIONED_EPILOGUE_NAME, + vec![], + serialized_args, + &mut UnmeteredGasMeter, + traversal_context, + module_storage, + ) + .map(|_return_vals| ()) + .map_err(expect_no_verification_errors)?; + + maybe_raise_injected_error(InjectedError::EndOfRunEpilogue)?; + + Ok(()) +} diff --git a/aptos-move/framework/aptos-framework/doc/transaction_fee.md b/aptos-move/framework/aptos-framework/doc/transaction_fee.md index 4757ec75644..18071338ef1 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_fee.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_fee.md @@ -5,10 +5,10 @@ -- [Resource `AptosCoinCapabilities`](#0x1_transaction_fee_AptosCoinCapabilities) - [Resource `AptosFABurnCapabilities`](#0x1_transaction_fee_AptosFABurnCapabilities) - [Resource `AptosCoinMintCapability`](#0x1_transaction_fee_AptosCoinMintCapability) - [Struct `FeeStatement`](#0x1_transaction_fee_FeeStatement) +- [Resource `AptosCoinCapabilities`](#0x1_transaction_fee_AptosCoinCapabilities) - [Resource `CollectedFeesPerBlock`](#0x1_transaction_fee_CollectedFeesPerBlock) - [Constants](#@Constants_0) - [Function `burn_fee`](#0x1_transaction_fee_burn_fee) @@ -16,6 +16,7 @@ - [Function `store_aptos_coin_burn_cap`](#0x1_transaction_fee_store_aptos_coin_burn_cap) - [Function `store_aptos_coin_mint_cap`](#0x1_transaction_fee_store_aptos_coin_mint_cap) - [Function `emit_fee_statement`](#0x1_transaction_fee_emit_fee_statement) +- [Function `storage_fee_refund_octas`](#0x1_transaction_fee_storage_fee_refund_octas) - [Function `initialize_fee_collection_and_distribution`](#0x1_transaction_fee_initialize_fee_collection_and_distribution) - [Function `upgrade_burn_percentage`](#0x1_transaction_fee_upgrade_burn_percentage) - [Function `initialize_storage_refund`](#0x1_transaction_fee_initialize_storage_refund) @@ -44,35 +45,6 @@ - - -## Resource `AptosCoinCapabilities` - -Stores burn capability to burn the gas fees. - - -
#[deprecated]
-struct AptosCoinCapabilities has key
-
- - - -
-Fields - - -
-
-burn_cap: coin::BurnCapability<aptos_coin::AptosCoin> -
-
- -
-
- - -
- ## Resource `AptosFABurnCapabilities` @@ -198,6 +170,35 @@ This is meant to emitted as a module event. + + + + +## Resource `AptosCoinCapabilities` + +Stores burn capability to burn the gas fees. + + +
#[deprecated]
+struct AptosCoinCapabilities has key
+
+ + + +
+Fields + + +
+
+burn_cap: coin::BurnCapability<aptos_coin::AptosCoin> +
+
+ +
+
+ +
@@ -294,11 +295,10 @@ Burn transaction fees in epilogue. Implementation -
public(friend) fun burn_fee(
+
friend fun burn_fee(
     account: address, fee: u64
 ) {
-    let burn_ref =
-        &borrow_global<AptosFABurnCapabilities>(@aptos_framework).burn_ref;
+    let burn_ref = &AptosFABurnCapabilities[@aptos_framework].burn_ref;
     aptos_account::burn_from_fungible_store_for_gas(burn_ref, account, fee);
 }
 
@@ -323,10 +323,10 @@ Mint refund in epilogue. Implementation -
public(friend) fun mint_and_refund(
+
friend fun mint_and_refund(
     account: address, refund: u64
-) acquires AptosCoinMintCapability {
-    let mint_cap = &borrow_global<AptosCoinMintCapability>(@aptos_framework).mint_cap;
+) {
+    let mint_cap = &AptosCoinMintCapability[@aptos_framework].mint_cap;
     let refund_coin = coin::mint(refund, mint_cap);
     coin::deposit_for_gas_fee(account, refund_coin);
 }
@@ -352,7 +352,7 @@ Only called during genesis.
 Implementation
 
 
-
public(friend) fun store_aptos_coin_burn_cap(
+
friend fun store_aptos_coin_burn_cap(
     aptos_framework: &signer, burn_cap: BurnCapability<AptosCoin>
 ) {
     system_addresses::assert_aptos_framework(aptos_framework);
@@ -382,7 +382,7 @@ Only called during genesis.
 Implementation
 
 
-
public(friend) fun store_aptos_coin_mint_cap(
+
friend fun store_aptos_coin_mint_cap(
     aptos_framework: &signer, mint_cap: MintCapability<AptosCoin>
 ) {
     system_addresses::assert_aptos_framework(aptos_framework);
@@ -398,9 +398,10 @@ Only called during genesis.
 
 ## Function `emit_fee_statement`
 
+Called by epilogue only.
 
 
-
fun emit_fee_statement(fee_statement: transaction_fee::FeeStatement)
+
public(friend) fun emit_fee_statement(fee_statement: transaction_fee::FeeStatement)
 
@@ -409,13 +410,37 @@ Only called during genesis. Implementation -
fun emit_fee_statement(fee_statement: FeeStatement) {
+
friend fun emit_fee_statement(fee_statement: FeeStatement) {
     event::emit(fee_statement)
 }
 
+ + + + +## Function `storage_fee_refund_octas` + + + +
public(friend) fun storage_fee_refund_octas(self: &transaction_fee::FeeStatement): u64
+
+ + + +
+Implementation + + +
friend fun storage_fee_refund_octas(self: &FeeStatement): u64 {
+    self.storage_fee_refund_octas
+}
+
+ + +
@@ -762,7 +787,7 @@ Aborts if emit_fee_statement(fee_statement: transaction_fee::FeeStatement) +
public(friend) fun emit_fee_statement(fee_statement: transaction_fee::FeeStatement)
 
diff --git a/aptos-move/framework/aptos-framework/doc/transaction_validation.md b/aptos-move/framework/aptos-framework/doc/transaction_validation.md index 2bb21a9019c..c2c89a57497 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_validation.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_validation.md @@ -8,6 +8,8 @@ - [Enum `ReplayProtector`](#0x1_transaction_validation_ReplayProtector) - [Resource `TransactionValidation`](#0x1_transaction_validation_TransactionValidation) - [Struct `GasPermission`](#0x1_transaction_validation_GasPermission) +- [Enum `PrologueArgs`](#0x1_transaction_validation_PrologueArgs) +- [Enum `EpilogueArgs`](#0x1_transaction_validation_EpilogueArgs) - [Constants](#@Constants_0) - [Function `grant_gas_permission`](#0x1_transaction_validation_grant_gas_permission) - [Function `revoke_gas_permission`](#0x1_transaction_validation_revoke_gas_permission) @@ -35,8 +37,8 @@ - [Function `unified_prologue_v2`](#0x1_transaction_validation_unified_prologue_v2) - [Function `unified_prologue_fee_payer_v2`](#0x1_transaction_validation_unified_prologue_fee_payer_v2) - [Function `unified_epilogue_v2`](#0x1_transaction_validation_unified_epilogue_v2) -- [Function `unified_prologue_v3`](#0x1_transaction_validation_unified_prologue_v3) -- [Function `unified_prologue_fee_payer_v3`](#0x1_transaction_validation_unified_prologue_fee_payer_v3) +- [Function `versioned_prologue`](#0x1_transaction_validation_versioned_prologue) +- [Function `versioned_epilogue`](#0x1_transaction_validation_versioned_epilogue) - [Specification](#@Specification_1) - [High-level Requirements](#high-level-req) - [Module-level Specification](#module-level-spec) @@ -63,8 +65,6 @@ - [Function `unified_prologue_v2`](#@Specification_1_unified_prologue_v2) - [Function `unified_prologue_fee_payer_v2`](#@Specification_1_unified_prologue_fee_payer_v2) - [Function `unified_epilogue_v2`](#@Specification_1_unified_epilogue_v2) - - [Function `unified_prologue_v3`](#@Specification_1_unified_prologue_v3) - - [Function `unified_prologue_fee_payer_v3`](#@Specification_1_unified_prologue_fee_payer_v3)
use 0x1::account;
@@ -231,6 +231,185 @@ correct chain-specific prologue and epilogue functions
 
 
 
+
+
+
+
+## Enum `PrologueArgs`
+
+Versioned enum-based prologue and epilogue
+Arguments for versioned_prologue. A new field becomes a new enum variant.
+
+- Old variants are kept for compatibility; their on-chain layout must remain stable.
+- Only the most recent variant needs real handling here — old variants were executed
+against the framework version that shipped them and are not reached from this code.
+
+
+
enum PrologueArgs
+
+ + + +
+Variants + + +
+V1 + + +
+Fields + + +
+
+txn_sender_public_key: option::Option<vector<u8>> +
+
+ +
+
+fee_payer_public_key_hash: option::Option<vector<u8>> +
+
+ +
+
+replay_protector: transaction_validation::ReplayProtector +
+
+ +
+
+secondary_signer_addresses: vector<address> +
+
+ +
+
+secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>> +
+
+ +
+
+txn_gas_price: u64 +
+
+ +
+
+txn_max_gas_units: u64 +
+
+ +
+
+txn_expiration_time: u64 +
+
+ +
+
+chain_id: u8 +
+
+ +
+
+is_simulation: bool +
+
+ +
+
+txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest> +
+
+ +
+
+ + +
+ +
+ +
+ + + +## Enum `EpilogueArgs` + +Arguments for versioned_epilogue. A new field becomes a new enum variant. + +- Old variants are kept for compatibility; their on-chain layout must remain stable. +- Only the most recent variant needs real handling here — old variants were executed +against the framework version that shipped them and are not reached from this code. + + +
enum EpilogueArgs
+
+ + + +
+Variants + + +
+V1 + + +
+Fields + + +
+
+fee_statement: transaction_fee::FeeStatement +
+
+ +
+
+txn_gas_price: u64 +
+
+ +
+
+txn_max_gas_units: u64 +
+
+ +
+
+gas_units_remaining: u64 +
+
+ +
+
+is_simulation: bool +
+
+ +
+
+is_orderless_txn: bool +
+
+ +
+
+ + +
+ +
+
@@ -1507,12 +1686,11 @@ new set of functions to support txn payload v2 format and orderless transactions chain_id: u8, is_simulation: bool, ) { - unified_prologue_v3( - sender, - txn_sender_public_key, + prologue_common( + &sender, + &sender, replay_protector, - secondary_signer_addresses, - secondary_signer_public_key_hashes, + txn_sender_public_key, txn_gas_price, txn_max_gas_units, txn_expiration_time, @@ -1520,6 +1698,7 @@ new set of functions to support txn payload v2 format and orderless transactions is_simulation, option::none(), ); + multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation); }
@@ -1557,14 +1736,11 @@ If there is no fee_payer, fee_payer = sender chain_id: u8, is_simulation: bool, ) { - unified_prologue_fee_payer_v3( - sender, - fee_payer, - txn_sender_public_key, - fee_payer_public_key_hash, + prologue_common( + &sender, + &fee_payer, replay_protector, - secondary_signer_addresses, - secondary_signer_public_key_hashes, + txn_sender_public_key, txn_gas_price, txn_max_gas_units, txn_expiration_time, @@ -1572,6 +1748,21 @@ If there is no fee_payer, fee_payer = sender is_simulation, option::none(), ); + multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation); + if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) { + let fee_payer_address = signer::address_of(&fee_payer); + if (fee_payer_public_key_hash.is_some()) { + assert!( + fee_payer_public_key_hash == option::some(account::get_authentication_key(fee_payer_address)), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) + ); + } else { + assert!( + allow_missing_txn_authentication_key(fee_payer_address), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) + ) + }; + } }
@@ -1656,14 +1847,13 @@ If there is no fee_payer, fee_payer = sender - + -## Function `unified_prologue_v3` +## Function `versioned_prologue` -V3 functions: extend V2 with voting-power-based high-txn-limits -
fun unified_prologue_v3(sender: signer, txn_sender_public_key: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool, txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest>)
+
fun versioned_prologue(sender: signer, fee_payer: signer, args: transaction_validation::PrologueArgs)
 
@@ -1672,32 +1862,58 @@ V3 functions: extend V2 with voting-power-based high-txn-limits Implementation -
fun unified_prologue_v3(
-    sender: signer,
-    txn_sender_public_key: Option<vector<u8>>,
-    replay_protector: ReplayProtector,
-    secondary_signer_addresses: vector<address>,
-    secondary_signer_public_key_hashes: vector<Option<vector<u8>>>,
-    txn_gas_price: u64,
-    txn_max_gas_units: u64,
-    txn_expiration_time: u64,
-    chain_id: u8,
-    is_simulation: bool,
-    txn_limits_request: Option<UserTxnLimitsRequest>,
-) {
-    prologue_common(
-        &sender,
-        &sender,
-        replay_protector,
-        txn_sender_public_key,
-        txn_gas_price,
-        txn_max_gas_units,
-        txn_expiration_time,
-        chain_id,
-        is_simulation,
-        txn_limits_request,
-    );
-    multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation);
+
fun versioned_prologue(sender: signer, fee_payer: signer, args: PrologueArgs) {
+    match (args) {
+        V1 {
+            txn_sender_public_key,
+            fee_payer_public_key_hash,
+            replay_protector,
+            secondary_signer_addresses,
+            secondary_signer_public_key_hashes,
+            txn_gas_price,
+            txn_max_gas_units,
+            txn_expiration_time,
+            chain_id,
+            is_simulation,
+            txn_limits_request,
+        } => {
+            prologue_common(
+                &sender,
+                &fee_payer,
+                replay_protector,
+                txn_sender_public_key,
+                txn_gas_price,
+                txn_max_gas_units,
+                txn_expiration_time,
+                chain_id,
+                is_simulation,
+                txn_limits_request,
+            );
+            multi_agent_common_prologue(
+                secondary_signer_addresses,
+                secondary_signer_public_key_hashes,
+                is_simulation,
+            );
+
+            let fee_payer_address = signer::address_of(&fee_payer);
+            if (fee_payer_address != signer::address_of(&sender)) {
+                if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) {
+                    if (fee_payer_public_key_hash.is_some()) {
+                        let fee_payer_public_key_hash = fee_payer_public_key_hash.destroy_some();
+                        assert!(
+                            fee_payer_public_key_hash == account::get_authentication_key(fee_payer_address),
+                            error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY),
+                        );
+                    } else {
+                        assert!(
+                            allow_missing_txn_authentication_key(fee_payer_address),
+                            error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY),
+                        );
+                    };
+                };
+            };
+        },
+    }
 }
 
@@ -1705,13 +1921,13 @@ V3 functions: extend V2 with voting-power-based high-txn-limits - + -## Function `unified_prologue_fee_payer_v3` +## Function `versioned_epilogue` -
fun unified_prologue_fee_payer_v3(sender: signer, fee_payer: signer, txn_sender_public_key: option::Option<vector<u8>>, fee_payer_public_key_hash: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool, txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest>)
+
fun versioned_epilogue(account: signer, fee_payer: signer, args: transaction_validation::EpilogueArgs)
 
@@ -1720,47 +1936,28 @@ V3 functions: extend V2 with voting-power-based high-txn-limits Implementation -
fun unified_prologue_fee_payer_v3(
-    sender: signer,
-    fee_payer: signer,
-    txn_sender_public_key: Option<vector<u8>>,
-    fee_payer_public_key_hash: Option<vector<u8>>,
-    replay_protector: ReplayProtector,
-    secondary_signer_addresses: vector<address>,
-    secondary_signer_public_key_hashes: vector<Option<vector<u8>>>,
-    txn_gas_price: u64,
-    txn_max_gas_units: u64,
-    txn_expiration_time: u64,
-    chain_id: u8,
-    is_simulation: bool,
-    txn_limits_request: Option<UserTxnLimitsRequest>,
-) {
-    prologue_common(
-        &sender,
-        &fee_payer,
-        replay_protector,
-        txn_sender_public_key,
-        txn_gas_price,
-        txn_max_gas_units,
-        txn_expiration_time,
-        chain_id,
-        is_simulation,
-        txn_limits_request,
-    );
-    multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation);
-    if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) {
-        let fee_payer_address = signer::address_of(&fee_payer);
-        if (fee_payer_public_key_hash.is_some()) {
-            assert!(
-                fee_payer_public_key_hash == option::some(account::get_authentication_key(fee_payer_address)),
-                error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
+
fun versioned_epilogue(account: signer, fee_payer: signer, args: EpilogueArgs) {
+    match (args) {
+        V1 {
+            fee_statement,
+            txn_gas_price,
+            txn_max_gas_units,
+            gas_units_remaining,
+            is_simulation,
+            is_orderless_txn,
+        } => {
+            unified_epilogue_v2(
+                account,
+                fee_payer,
+                fee_statement.storage_fee_refund_octas(),
+                txn_gas_price,
+                txn_max_gas_units,
+                gas_units_remaining,
+                is_simulation,
+                is_orderless_txn,
             );
-        } else {
-            assert!(
-                allow_missing_txn_authentication_key(fee_payer_address),
-                error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
-            )
-        };
+            transaction_fee::emit_fee_statement(fee_statement);
+        },
     }
 }
 
@@ -2291,38 +2488,6 @@ Skip transaction_fee::burn_fee verification. -
pragma verify = false;
-
- - - - - -### Function `unified_prologue_v3` - - -
fun unified_prologue_v3(sender: signer, txn_sender_public_key: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool, txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest>)
-
- - - - -
pragma verify = false;
-
- - - - - -### Function `unified_prologue_fee_payer_v3` - - -
fun unified_prologue_fee_payer_v3(sender: signer, fee_payer: signer, txn_sender_public_key: option::Option<vector<u8>>, fee_payer_public_key_hash: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool, txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest>)
-
- - - -
pragma verify = false;
 
diff --git a/aptos-move/framework/aptos-framework/sources/transaction_fee.move b/aptos-move/framework/aptos-framework/sources/transaction_fee.move index 4c5f580e4e8..4c3b98dc207 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_fee.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_fee.move @@ -24,12 +24,6 @@ module aptos_framework::transaction_fee { /// No longer supported. const ENO_LONGER_SUPPORTED: u64 = 4; - #[deprecated] - /// Stores burn capability to burn the gas fees. - struct AptosCoinCapabilities has key { - burn_cap: BurnCapability - } - /// Stores burn capability to burn the gas fees. struct AptosFABurnCapabilities has key { burn_ref: BurnRef @@ -74,25 +68,24 @@ module aptos_framework::transaction_fee { } /// Burn transaction fees in epilogue. - public(friend) fun burn_fee( + friend fun burn_fee( account: address, fee: u64 ) { - let burn_ref = - &borrow_global(@aptos_framework).burn_ref; + let burn_ref = &AptosFABurnCapabilities[@aptos_framework].burn_ref; aptos_account::burn_from_fungible_store_for_gas(burn_ref, account, fee); } /// Mint refund in epilogue. - public(friend) fun mint_and_refund( + friend fun mint_and_refund( account: address, refund: u64 - ) acquires AptosCoinMintCapability { - let mint_cap = &borrow_global(@aptos_framework).mint_cap; + ) { + let mint_cap = &AptosCoinMintCapability[@aptos_framework].mint_cap; let refund_coin = coin::mint(refund, mint_cap); coin::deposit_for_gas_fee(account, refund_coin); } /// Only called during genesis. - public(friend) fun store_aptos_coin_burn_cap( + friend fun store_aptos_coin_burn_cap( aptos_framework: &signer, burn_cap: BurnCapability ) { system_addresses::assert_aptos_framework(aptos_framework); @@ -102,20 +95,30 @@ module aptos_framework::transaction_fee { } /// Only called during genesis. - public(friend) fun store_aptos_coin_mint_cap( + friend fun store_aptos_coin_mint_cap( aptos_framework: &signer, mint_cap: MintCapability ) { system_addresses::assert_aptos_framework(aptos_framework); move_to(aptos_framework, AptosCoinMintCapability { mint_cap }) } - // Called by the VM after epilogue. - fun emit_fee_statement(fee_statement: FeeStatement) { + /// Called by epilogue only. + friend fun emit_fee_statement(fee_statement: FeeStatement) { event::emit(fee_statement) } + friend fun storage_fee_refund_octas(self: &FeeStatement): u64 { + self.storage_fee_refund_octas + } + // DEPRECATED section: + #[deprecated] + /// Stores burn capability to burn the gas fees. + struct AptosCoinCapabilities has key { + burn_cap: BurnCapability + } + #[deprecated] /// DEPRECATED: Stores information about the block proposer and the amount of fees /// collected when executing the block. diff --git a/aptos-move/framework/aptos-framework/sources/transaction_validation.move b/aptos-move/framework/aptos-framework/sources/transaction_validation.move index a4199731136..4fd96f4a012 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_validation.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_validation.move @@ -13,6 +13,7 @@ module aptos_framework::transaction_validation { use aptos_framework::system_addresses; use aptos_framework::timestamp; use aptos_framework::transaction_fee; + use aptos_framework::transaction_fee::FeeStatement; use aptos_framework::transaction_limits; use aptos_framework::transaction_limits::UserTxnLimitsRequest; use aptos_framework::nonce_validation; @@ -739,12 +740,11 @@ module aptos_framework::transaction_validation { chain_id: u8, is_simulation: bool, ) { - unified_prologue_v3( - sender, - txn_sender_public_key, + prologue_common( + &sender, + &sender, replay_protector, - secondary_signer_addresses, - secondary_signer_public_key_hashes, + txn_sender_public_key, txn_gas_price, txn_max_gas_units, txn_expiration_time, @@ -752,6 +752,7 @@ module aptos_framework::transaction_validation { is_simulation, option::none(), ); + multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation); } /// If there is no fee_payer, fee_payer = sender @@ -769,14 +770,11 @@ module aptos_framework::transaction_validation { chain_id: u8, is_simulation: bool, ) { - unified_prologue_fee_payer_v3( - sender, - fee_payer, - txn_sender_public_key, - fee_payer_public_key_hash, + prologue_common( + &sender, + &fee_payer, replay_protector, - secondary_signer_addresses, - secondary_signer_public_key_hashes, + txn_sender_public_key, txn_gas_price, txn_max_gas_units, txn_expiration_time, @@ -784,6 +782,21 @@ module aptos_framework::transaction_validation { is_simulation, option::none(), ); + multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation); + if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) { + let fee_payer_address = signer::address_of(&fee_payer); + if (fee_payer_public_key_hash.is_some()) { + assert!( + fee_payer_public_key_hash == option::some(account::get_authentication_key(fee_payer_address)), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) + ); + } else { + assert!( + allow_missing_txn_authentication_key(fee_payer_address), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) + ) + }; + } } fun unified_epilogue_v2( @@ -844,79 +857,122 @@ module aptos_framework::transaction_validation { } /////////////////////////////////////////////////////////// - /// V3 functions: extend V2 with voting-power-based high-txn-limits + /// Versioned enum-based prologue and epilogue /////////////////////////////////////////////////////////// - fun unified_prologue_v3( - sender: signer, - txn_sender_public_key: Option>, - replay_protector: ReplayProtector, - secondary_signer_addresses: vector
, - secondary_signer_public_key_hashes: vector>>, - txn_gas_price: u64, - txn_max_gas_units: u64, - txn_expiration_time: u64, - chain_id: u8, - is_simulation: bool, - txn_limits_request: Option, - ) { - prologue_common( - &sender, - &sender, - replay_protector, - txn_sender_public_key, - txn_gas_price, - txn_max_gas_units, - txn_expiration_time, - chain_id, - is_simulation, - txn_limits_request, - ); - multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation); + /// Arguments for `versioned_prologue`. A new field becomes a new enum variant. + /// + /// - Old variants are kept for compatibility; their on-chain layout must remain stable. + /// - Only the most recent variant needs real handling here — old variants were executed + /// against the framework version that shipped them and are not reached from this code. + enum PrologueArgs { + V1 { + txn_sender_public_key: Option>, + fee_payer_public_key_hash: Option>, + replay_protector: ReplayProtector, + secondary_signer_addresses: vector
, + secondary_signer_public_key_hashes: vector>>, + txn_gas_price: u64, + txn_max_gas_units: u64, + txn_expiration_time: u64, + chain_id: u8, + is_simulation: bool, + txn_limits_request: Option, + }, } - fun unified_prologue_fee_payer_v3( - sender: signer, - fee_payer: signer, - txn_sender_public_key: Option>, - fee_payer_public_key_hash: Option>, - replay_protector: ReplayProtector, - secondary_signer_addresses: vector
, - secondary_signer_public_key_hashes: vector>>, - txn_gas_price: u64, - txn_max_gas_units: u64, - txn_expiration_time: u64, - chain_id: u8, - is_simulation: bool, - txn_limits_request: Option, - ) { - prologue_common( - &sender, - &fee_payer, - replay_protector, - txn_sender_public_key, - txn_gas_price, - txn_max_gas_units, - txn_expiration_time, - chain_id, - is_simulation, - txn_limits_request, - ); - multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation); - if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) { - let fee_payer_address = signer::address_of(&fee_payer); - if (fee_payer_public_key_hash.is_some()) { - assert!( - fee_payer_public_key_hash == option::some(account::get_authentication_key(fee_payer_address)), - error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) + fun versioned_prologue(sender: signer, fee_payer: signer, args: PrologueArgs) { + match (args) { + V1 { + txn_sender_public_key, + fee_payer_public_key_hash, + replay_protector, + secondary_signer_addresses, + secondary_signer_public_key_hashes, + txn_gas_price, + txn_max_gas_units, + txn_expiration_time, + chain_id, + is_simulation, + txn_limits_request, + } => { + prologue_common( + &sender, + &fee_payer, + replay_protector, + txn_sender_public_key, + txn_gas_price, + txn_max_gas_units, + txn_expiration_time, + chain_id, + is_simulation, + txn_limits_request, ); - } else { - assert!( - allow_missing_txn_authentication_key(fee_payer_address), - error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) - ) - }; + multi_agent_common_prologue( + secondary_signer_addresses, + secondary_signer_public_key_hashes, + is_simulation, + ); + + let fee_payer_address = signer::address_of(&fee_payer); + if (fee_payer_address != signer::address_of(&sender)) { + if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) { + if (fee_payer_public_key_hash.is_some()) { + let fee_payer_public_key_hash = fee_payer_public_key_hash.destroy_some(); + assert!( + fee_payer_public_key_hash == account::get_authentication_key(fee_payer_address), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), + ); + } else { + assert!( + allow_missing_txn_authentication_key(fee_payer_address), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), + ); + }; + }; + }; + }, } } + /// Arguments for `versioned_epilogue`. A new field becomes a new enum variant. + /// + /// - Old variants are kept for compatibility; their on-chain layout must remain stable. + /// - Only the most recent variant needs real handling here — old variants were executed + /// against the framework version that shipped them and are not reached from this code. + enum EpilogueArgs { + V1 { + fee_statement: FeeStatement, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + is_simulation: bool, + is_orderless_txn: bool, + }, + } + + fun versioned_epilogue(account: signer, fee_payer: signer, args: EpilogueArgs) { + match (args) { + V1 { + fee_statement, + txn_gas_price, + txn_max_gas_units, + gas_units_remaining, + is_simulation, + is_orderless_txn, + } => { + unified_epilogue_v2( + account, + fee_payer, + fee_statement.storage_fee_refund_octas(), + txn_gas_price, + txn_max_gas_units, + gas_units_remaining, + is_simulation, + is_orderless_txn, + ); + transaction_fee::emit_fee_statement(fee_statement); + }, + } + } } diff --git a/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move b/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move index 50d489c14e2..7ecddf3722a 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move @@ -443,42 +443,6 @@ spec aptos_framework::transaction_validation { pragma verify = false; } - spec unified_prologue_v3( - sender: signer, - txn_sender_public_key: Option>, - replay_protector: ReplayProtector, - secondary_signer_addresses: vector
, - secondary_signer_public_key_hashes: vector>>, - txn_gas_price: u64, - txn_max_gas_units: u64, - txn_expiration_time: u64, - chain_id: u8, - is_simulation: bool, - txn_limits_request: Option, - ) { - // TODO: temporary mockup - pragma verify = false; - } - - spec unified_prologue_fee_payer_v3( - sender: signer, - fee_payer: signer, - txn_sender_public_key: Option>, - fee_payer_public_key_hash: Option>, - replay_protector: ReplayProtector, - secondary_signer_addresses: vector
, - secondary_signer_public_key_hashes: vector>>, - txn_gas_price: u64, - txn_max_gas_units: u64, - txn_expiration_time: u64, - chain_id: u8, - is_simulation: bool, - txn_limits_request: Option, - ) { - // TODO: temporary mockup - pragma verify = false; - } - spec schema EpilogueGasPayerAbortsIf { use std::option; use aptos_std::type_info; diff --git a/aptos-move/framework/cached-packages/src/head.mrb b/aptos-move/framework/cached-packages/src/head.mrb index a38fa614ff3d3282839a82dc11921dfd1244f20f..fecf17aa3a8a79f5e0ab3b706e31635268afd85f 100644 GIT binary patch delta 10269 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 Ee+2R@ssI20 delta 8840 zcmaKR2Ut^EvvxXZBy<6(g7n^-Nben~k=}ceDkY#IARtmh5tJ$@(nLU-6ai^cq$5>O zP(h@K2*M4VbMAlc_x$%JnLTUPyfgF8?ChQ7Nl}JT)jgxAJHR`jJK#FVEe-`C2~kl+ zX-Q=@2{kblaY-RnNks{16;%;&AxTLUB?)CoDM=+|5fLe22{APhRW(sDNilJ82}Mao zH3=nQMJ3_bP7VlBpbecIFk3VHF)4Eb9B}pkopzgh`OHa>o#fjn+A4Tp@4vV27UJ>} z?)AG3D5qq1i!R`Dw3L}saWrHrFSbrOZId+`&voK#ZyMqn(U6f34&Tax*{r?KmSB63r;Z>rck+&g};t=6t*o|GwMjaD6emu$PD`i zNuu9P99F$F=-e(}*f+8J@rSqV!7cTaLF3)xhoTND__UQPI|*`D$U4DQ%i9Q0?KVA4 znCW_wL^URqfwx_}NrPg70|PS9dO9haO&+Z1>vp<$oAf}T`2`4d84yu_SybOKAh^ZM z5y9xyUxCHT_(a}+I*^dNQJWK`ecZDwRg#5mspa93m zx!nzmUv?s|5auUeFaEsWS~(M*P&zHvr=Y4KX9HSJvwvmV=n#IgGQ*z^6#3Zef{BhQ&uK7gz@!wfX-!Yb`W>zE?-)I-C&;e-3x^GFZu#Qb~nC! z+4&vh|HW>BO6Y1tJS$$=$M5>lOGukA#mC_p<_zv@&hn!zeBmopG{po>!%uk%S=9Fy zI!ZOXev@m(wJY+vEoK6~KmYczBUyN(tE|8!*$_$dj%)BeBS%F|Wt~oS)mzB*{3ky! zMmf;(F3>H%Ix*GYk)8J!a>p{}^n`-G+aEqKiZc{WGQDp1@YRkEK2ul+)bn*MbUM!5 z#lMov6nt8eF6C$}#6%U19JJ;k{bZIBL96Le_td7cbCEUGF~o?E^n>wwzES`#>q+N2 zuL&oOktoJYMtjKTtL7iGS?1&35pFCtn)}h(p6o*1@ zJ_z5+nYo7><_;;hz;lpV3XB+NAqvV(*^NAYR^O^Czbhm>u%&2GE}caZRj6GGS4i(_ zScwL`-x_KAS<6jR6z2XU_?H5+K}C77t*SOhqPV_?J7>u#-_X>Jx~OiEhSaSyUO8jO@bm%H&^ zPDpQ-*_>s--BElaS>w%5CU>$sxAW|*rvvj4^V1P%gt<#&v6t6OYekSXdy4iq|I_`J zbbi_9Y+qa2GLw0!9jDu_%bMw&<6+a`yK-_rcO?+9VG^Prp4zu5RYV(4WTGcKr-cEG zE9y2li;GUj;Pxaz@A{x#Eobn6;JAC^gl&Qqb!XR(#{s_7)0jz&g~Mqlpd(<_fCR>& zJGFANDWIZhMvLISVVWsdvk+Ps7BR0)Q#$Xh?`h-1$j*girHsG+DaI-wBSjmcgJ=R><0t>Ggo2`;2dtb}|oo)u39<^p^Gqf{zI$ioK7uxJl7qWQND!%eF zmT9}OwhSNnsl8(^r(#ux4@)Xht5gxWTA8m-`>S=De=m8iVszEOd0MowR+Pkayp#Ee zYd53FK1m(_>6UY$m8f2t>D9%h;~CYppT7louDtaFH^p(t9Zkq^ z#_U#&n`>+@t!2#;Ns2#BuPi1G^s#Cy!+FPgzbL^;(*ZnZ*B`_DSvOxnFtMQ@=LMOlPz7 zV3vmx|AtCj758KyzHKT5Nl#kS<}@esqX`Ji(Vne;s8?CZ`H?(dD;tJexg8NZrX{zn zUbCs(%4oeoaa*8DsK(Fd#J)XmC!xqt+|6tXAhvcisl}EZ>z)X75WGJ(W%gLFqIUDe z!}m0v^TrdmO1|VSOh&!JJ#0{I8CUVVbS)TRj*r26eeS48yUrBZgi4?($jHA7m)x5FIcdNkxo8NJI4BqQ>k z4J1v3nqVdayFH4;`j2-KSjycyn8sauappgzU~d@dvWz@j+{2>RUYY1#dJKA*St#^m zbTGjoE4gU^*E{8>rtf)hOD&$n zn4ML#G+k=hvmV~EN`F#IDe|HU-kK9ePVi=@&%Q&BU?Zr>k7Ti%DeAOjef$0(OU7=g zXKsd_acck84g0juGqMy%^}8uLZV;*Na3y91yxs}ZZjG1gZT;TJ^>9b~^w#W|sW(Y2 znq&QUUA&Z2?{R2*tkT5GVC$MhAZJ^j0rxgF)=Qdc(e+cq(=lk0?&YF(P6 zVSFF?%=}CWoXMacP^yUj@~D$I36}ee$5()FHB~;hMd;2yOlQ**`4&W%s(mEWlk$nY z6|hn=6rabgFyELLI6Keb!X?4dBXTV7(Uc~yp8Gm9#y>j=+In`Ruyo;Zf934-gby^Y^RjIW2U zAc~$)zN^C78g_=F=wlvR;1NbYyz#$e(&Sgg%qg(KP*(Zawi_e52Df;-B_3{GC7EBG z4~y?V-8tO-!<>J0@o;$tD|>qLhkY>qpgWo+@D|SqF9{!52a@?HN+afBsq0NU5xe_a zOP-FT3+7jY{4c%`OgxmQ6r~AxGv_L1?=5C8)4(R--U!g9ixK7MVLjGQmmtA2WYc`^ z^4p3oECfc~o$@TszVfI`XeR1JJk`bd6xamUisqnnrD@1gQ8)@0^O3+7y@5Go$mC?b zTM`_O7oGUrhO2A%u214aay38jxp&oR!ESx9ec=6M5|=+I6Vey|IR1HZ^vu>h7+ZHG zcK$R!@Tk}78UE*1;K4XODQt2HzZpN`R~6YCdEip;Q8c0#-IJym?7C0F=1bSQtg(J@ zeeruEIBy|1QuU`L=lsEKJN<9_N{b!=vm031-lB!a$!+Dqv($cbQ^7aqrEBivzwG@s z)%X0P9HG^+^@l4U-0e%8K<$m+ytVGGfqHs+9qJ$xspy6MyWeGsK0dk=eS$AxUe|3% zNRNI+cjYK=%12c!IJjS&I zUkDAG-F$fLo<~{|df>iO+;tKk5cXag3aF5DDSGj~QfN%nFd3x`=SxDdPY}nfq!tRcWTAK?R0V}2 znJ_CtqC>DWUIQ92!sebrdMe zxZ|Fd$@=bC+mu7N=ePkcbElG9-ThcH!fVyXpP5m1ji$k_C_V%EfrF-hsK0`D0q@Q9 z7OA3kJ5BN84e9(oRxF?&VfnRU4@}NgEbV|)qjla7z1|aj09r(u2esRGXeYS2`+6%D zdKLUBq7@guW%+#s#}+!(1)d*a_iB0bg)y>PlZ38gpdUm~j38EI&iGCsuWGKXTEwhSXl z%7J0YF*)YDZDrGVb8}fkOUDW6kmK4A46+=t+c|7z%14@0>#%So6dvy{=x@2~Zp^ zvF#O__!Ro+I=#I~6?aSyi}qF4U{VoWU%z5)bSFLBCrRz<(3=$cW9~bZD}%k^AL8BE zec906N>GmCA9$b8jij2AmX6w0?}tiP6#V2O-g_|!KO9vn*PpD~tFAVQ zpJ| z35BN&W5%9|w#u8UIA*&ygMJY4u~%A9yUUX{x_!FTh_1)@Ta%xoZy&FV7RX=o=%;#G ztoNKuu>*%)<~N9}h2Vhihe_zEyXy)ZY3@KMTjbr!1k9*8N+uJE-}X<@2-@7Xizq1& zH1wUaeAL?SP@Vu zL-n#YE!$L@3oo2&GM|e3vREd*|L%#YuqE9-J;OKvoS?TvUS%A&Dll}JC*|hz!s*1z z9`tt-cDd>1+J@X~S=+SJym<<_p!&3YJg96kc}-T1Lv`s2xsk^1bj+Z@+k8F$2l_m2 zW-3ZNX^EM7xX(+4*IQ~C!n|b-zSK@%GAgpXun(FYA;)Xm(Dz67P#E5Ker4nC0iCv@ z4_Oj5E4Pw(bj>v>C4KDHRqcKjS(yW&a;-I_5_lkX%yNt?p;a$y@R!Li3agrCivrdU z6st2I`Xz7@*K;nvps37|phxi+n-+=@E;Z7p;NtH+kQYpz4QQRbP}5;cU);xAYwTL1 z7*Duu&Dnq-6)Y8_c2gH6*p+nNR4J}D2z}mp%{U=IMVdXys^T*a-=NO2;LpS(rF30RxqeJUff`v;4 z#RV1zQoRiPl(}Bnm87~sDr8)6isRDNY1*6;RGh+iYL3Amrq;2cn&S3rTMC%u!N4c@ z$$4DBSIcxe?%F1Wk}ZjWTY?XK&Ep&rC@M7Fb#ZQ($85^{&upv%yd<=5l~gdNcdPTy znl^v0z(Z{+SHvynMcxt0hAg=&A3$`TT}ToWiQKqsEUiF-tns3yyr}yjYsDT?aoW`4 zxwLzQ(G#z+nim$m`K<2UfdDm4pSex%qAAaX0cIYjy`W|_OTlColRJ)gdH65uW!h*a zdh#3lyi&Ao%g7hG`LzHVlj8rTw5jxtJZF)N)B`-kwf8{(X$754(fsO>fM4>v#9I5O z)v=mn5Oda|!}?amrcrgR%!D2?4n5(kw)>M}ohq>ewd4jX*-e3-*VhM?%U9QJ4d~wTqB` zHCbuO*1P;{`LXdOP3Jvt;KaSd&HCWZ~k8n)5Wx6tmK}X2pov?g9B1xkG@{vTX7qeDcgdt z;q~Qsf0AtZ&(19ToNfA)xYcArNx2w#^zqzz^Ryq()@G^HP$~!aTDT*m!5Il$CI?WeERO1S}T{XowUXSgFJ{Vi?JQAL} zGOE?5wOi4Nc;Q&n_Z_j38fq-TBGKNwk%u#u`u4a{qwTq@hv2t&61Ug?L>l0`qC|Fb z4$$kN<>1$Y-qcC6xv~3f^pc>jxn@2r5^BDwRCEt)LXDsI7d6kA;lgmw-I%@j3-RM0 z^l=meM_nNs^7c-{G3qCwDnT*SH^uj}4RX9ye#4#(LU%rS*{EK0+K#QM+pR`UskvF| z&sa7|XuMJ_GqiWUVZ6v;;}9|D+Mo2)l2hA~L*7H0s#Ymxp+YP7cX;0KmXyfFYk?=c z+rE5>uZnJ+_7w2>aV@U)w`(Et#FFub89B2$&qY{Y8T-_mR++xjlYiE5FJK~Uc*I{l zWB@3;=|T8uD9AwaSkLQ-I*>-wk9px>HEU5nb)k3CZ&NxkHTUFOC2aX-O8WK7sN`UL zboh!UP1#o^mkWY8?wN|*+2&(uwERc)=cIFKGpsP%j1$iz(|b`Yj}K$04h<>r7Kvj_ zA_QLz)gm#7Ibmqj7N4fU^;*=2^4B;b+`ogXR~lMr|Bd_C?i7uL+e)Mjup*OJ3* z>9`|4B;NO_SZg5#^q8rl=A8i@)TlACgsLi%)ovmK#{=Pqf$=LWcZ_e$zA8Tx` zeaX1jL;B&aLUj_@qA&UyV$@*_|ABg5hbDti+NGQ>6D_Vb+L`fOcc?_|TjWfUh~;Xz zH+?~E)zC%yoq|ZYhPLK|TFsLV9~sBAH7qu{R@URCiNHqI)3_J`6QzK3ywduzEc z5rb6^tXvD|dK0@u^TW7jB1+K0E&tG1sM5G7$L0l5KZf=7!ifD5X3by^{LwguW_R=Q z6g^j;m0!uV%>kr$KxdoNM4=)=e^xU#mHlxY+RIkAh0TZu^Atb&P4K{^@T5Cc}z*mJwbD_RJXqWh;=}cJ~Id}UJLY% ze`r=Zt&5y_?+5>VW5(6{^IX^J0LUaOsfQKZ_-)Hw!9s=Ip#SjT-Nb#0uEjlq`YN#1 zvBO!)P$1{^&{AkrwQcZ0GcO-CSR=v~mK{yqW4wJhMV7^-@ViF00fFD2j#OV0xbKi^IeQX*!(_nI(=H;r?ba8*Ge@a3?J4sXfrkA#$tZm};-e zAr8rCF(FTP*dpxEAmo5ARJF8QQ-ilKxrpPsU$M6&m#Uf7XjEi=7wK7aD^8faVm(-s z6V+(T2p5x{aGV4a zUb{XnoMwQ=BiYGg1E(q#1~zH2V`&^+l1+p^x)K%V>wx%T0kXn#ah3m=q9!dBED0l% zOfbSa7`n$`lRY=uCh#Oh;m`f?=S~Mt z5*inOF6>qLE9y6D%{NxNIGX+(RX!<+zF4cF5qK`1;j!FmPi3ZdcpM2WpD$O zBn$Bo1wW!<{lqDbHXBp;DJ0L4!LWv zngANu*)D{ZglGYQ5IT}`FZ3eh+y*cp&fG925c14T!h$*rf?qT@_sndfXDqrwY4k5c_>N*Fc%zp|(coQG=&o-0kEbFkK5V2n2JUmzWkbFZ%0 zxzdw3SNf9Y%0T*D8Oogx31B2oh?~SR5y~d90hVAO0sw?VkT5h14F{sYKqLqZ27}I4 zaLC^_2@(MSgMl#O9uW*93WyYqgd)$;NF<1aC}*#88+ryqf>2;W&=i&lf42Tl2=Ko# zL?;mXca<{+#G(Mm*#b+f_)i5O91Q-C2m~5TObaC@J|heJPbR>>H_%|Yf5CwN3u6!z z4gsH+L_z+?&jt(!qKQqyA!ziOK5LeE_@5Zkf4ktoe|q`X zY>7$!4-CmaKHalIf6WvM1pl)@{#E!t5`+Gth5G+G&lwZQ^N|-aXR-8TSR*H2PrFb9 zf1dy+hX5b{28KE829OLJ;BV)B!_FbV&Bxou*~y6joyR^T09~jk3460xu7=WitR@f+ zcW||H^R{to*jm7f0D+au$#fv8EIS3PKD)qX2)?LH`3r zA&8;GG~~h{5h75=l$aHb0-{M#U^Ir99g9I@NQs$1XPM8yNR$lF=Z*iv3`}eS1<^#35}7HXNdIQ0hyk84!(hNfX8gn{VF bool { + // Transaction limits are enforced only through the versioned prologue path + // (via PrologueArgs::V1.txn_limits_request), so both flags must be on. self.is_enabled(FeatureFlag::TRANSACTION_LIMITS) + && self.is_enabled(FeatureFlag::VERSIONED_TRANSACTION_VALIDATION) + } + + pub fn is_versioned_transaction_validation_enabled(&self) -> bool { + self.is_enabled(FeatureFlag::VERSIONED_TRANSACTION_VALIDATION) } pub fn get_max_identifier_size(&self) -> u64 { From a68b19b12eaa18fd470f7c41c557d696239e74ad Mon Sep 17 00:00:00 2001 From: George Mitenkov Date: Thu, 23 Apr 2026 09:59:03 +0100 Subject: [PATCH 2/2] Address reviewer's comments Co-Authored-By: Claude Opus 4.7 (1M context) --- aptos-move/aptos-vm/src/transaction_validation.rs | 2 -- .../aptos-vm/src/transaction_validation_versioned.rs | 11 ++++------- .../sources/transaction_validation.spec.move | 10 ++++++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/aptos-move/aptos-vm/src/transaction_validation.rs b/aptos-move/aptos-vm/src/transaction_validation.rs index 4817d8ce4ac..977e39c6c97 100644 --- a/aptos-move/aptos-vm/src/transaction_validation.rs +++ b/aptos-move/aptos-vm/src/transaction_validation.rs @@ -128,7 +128,6 @@ pub(crate) fn run_script_prologue( module_storage, serialized_signers, txn_data, - features, log_context, traversal_context, is_simulation, @@ -498,7 +497,6 @@ fn run_epilogue( gas_remaining, fee_statement, txn_data, - features, traversal_context, is_simulation, ); diff --git a/aptos-move/aptos-vm/src/transaction_validation_versioned.rs b/aptos-move/aptos-vm/src/transaction_validation_versioned.rs index 781d809b694..3a76e701c15 100644 --- a/aptos-move/aptos-vm/src/transaction_validation_versioned.rs +++ b/aptos-move/aptos-vm/src/transaction_validation_versioned.rs @@ -14,7 +14,6 @@ use crate::{ use aptos_gas_algebra::Gas; use aptos_types::{ fee_statement::FeeStatement, - on_chain_config::Features, transaction::{ReplayProtector, TxnLimitsRequest, UserTxnLimitsRequest}, }; use aptos_vm_logging::log_schema::AdapterLogSchema; @@ -90,7 +89,7 @@ impl PrologueBuilder { /// Selects the highest supported variant based on feature flags and BCS-serializes it. /// Currently only V1 exists. - pub fn build(self, _features: &Features) -> Vec { + pub fn build(self) -> Vec { let args = PrologueArgs::V1 { txn_sender_public_key: self.txn_sender_public_key, fee_payer_public_key_hash: self.fee_payer_public_key_hash, @@ -113,7 +112,6 @@ pub(crate) fn run_prologue( module_storage: &impl ModuleStorage, serialized_signers: &SerializedSigners, txn_data: &TransactionMetadata, - features: &Features, log_context: &AdapterLogSchema, traversal_context: &mut TraversalContext, is_simulation: bool, @@ -124,7 +122,7 @@ pub(crate) fn run_prologue( serialized_signers .fee_payer() .unwrap_or(serialized_signers.sender()), - builder.build(features), + builder.build(), ]; session .execute_function_bypass_visibility( @@ -185,7 +183,7 @@ impl EpilogueBuilder { /// Selects the highest supported variant based on feature flags and BCS-serializes it. /// Currently only V1 exists. - pub fn build(self, _features: &Features) -> Vec { + pub fn build(self) -> Vec { let args = EpilogueArgs::V1 { fee_statement: self.fee_statement, txn_gas_price: self.txn_gas_price, @@ -205,7 +203,6 @@ pub(crate) fn run_epilogue( gas_remaining: Gas, fee_statement: FeeStatement, txn_data: &TransactionMetadata, - features: &Features, traversal_context: &mut TraversalContext, is_simulation: bool, ) -> VMResult<()> { @@ -215,7 +212,7 @@ pub(crate) fn run_epilogue( serialized_signers .fee_payer() .unwrap_or(serialized_signers.sender()), - builder.build(features), + builder.build(), ]; session diff --git a/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move b/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move index 7ecddf3722a..58e3d7dd0ce 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_validation.spec.move @@ -443,6 +443,16 @@ spec aptos_framework::transaction_validation { pragma verify = false; } + spec versioned_prologue(sender: signer, fee_payer: signer, args: PrologueArgs) { + // TODO: temporary mockup + pragma verify = false; + } + + spec versioned_epilogue(account: signer, fee_payer: signer, args: EpilogueArgs) { + // TODO: temporary mockup + pragma verify = false; + } + spec schema EpilogueGasPayerAbortsIf { use std::option; use aptos_std::type_info;