diff --git a/aptos-move/aptos-vm/src/transaction_metadata.rs b/aptos-move/aptos-vm/src/transaction_metadata.rs index 65358aee33e82..09d45852a7ad2 100644 --- a/aptos-move/aptos-vm/src/transaction_metadata.rs +++ b/aptos-move/aptos-vm/src/transaction_metadata.rs @@ -2,14 +2,15 @@ // Parts of the project are originally copyright © Meta Platforms, Inc. // SPDX-License-Identifier: Apache-2.0 -use aptos_crypto::HashValue; +use aptos_crypto::{hash::CryptoHash, HashValue}; use aptos_gas_algebra::{FeePerGasUnit, Gas, NumBytes}; use aptos_types::{ account_address::AccountAddress, chain_id::ChainId, transaction::{ - user_transaction_context::UserTransactionContext, EntryFunction, Multisig, - SignedTransaction, TransactionPayload, + authenticator::TransactionAuthenticator::{FeePayer, MultiAgent}, + user_transaction_context::UserTransactionContext, + EntryFunction, Multisig, RawTransactionWithData, SignedTransaction, TransactionPayload, }, }; @@ -31,6 +32,7 @@ pub struct TransactionMetadata { pub is_keyless: bool, pub entry_function_payload: Option, pub multisig_payload: Option, + pub raw_transaction_hash: Vec, } impl TransactionMetadata { @@ -89,6 +91,34 @@ impl TransactionMetadata { TransactionPayload::Multisig(m) => Some(m.clone()), _ => None, }, + raw_transaction_hash: match txn.authenticator_ref() { + MultiAgent { + sender: _, + secondary_signer_addresses, + secondary_signers: _, + } => { + let raw_txn = RawTransactionWithData::new_multi_agent( + txn.clone().into_raw_transaction(), + secondary_signer_addresses.clone(), + ); + raw_txn.hash().to_vec() + }, + FeePayer { + sender: _, + secondary_signer_addresses, + secondary_signers: _, + fee_payer_address, + fee_payer_signer: _, + } => { + let raw_txn = RawTransactionWithData::new_fee_payer( + txn.clone().into_raw_transaction(), + secondary_signer_addresses.clone(), + *fee_payer_address, + ); + raw_txn.hash().to_vec() + }, + _ => txn.raw_transaction_ref().hash().to_vec(), + }, } } @@ -158,6 +188,10 @@ impl TransactionMetadata { self.multisig_payload.clone() } + pub fn raw_transaction_hash(&self) -> Vec { + self.raw_transaction_hash.clone() + } + pub fn as_user_transaction_context(&self) -> UserTransactionContext { UserTransactionContext::new( self.sender, @@ -170,6 +204,7 @@ impl TransactionMetadata { .map(|entry_func| entry_func.as_entry_function_payload()), self.multisig_payload() .map(|multisig| multisig.as_multisig_payload()), + self.raw_transaction_hash(), ) } } diff --git a/aptos-move/e2e-move-tests/src/tests/transaction_context.data/pack/sources/transaction_context_test.move b/aptos-move/e2e-move-tests/src/tests/transaction_context.data/pack/sources/transaction_context_test.move index 0116d1035c9b0..e5f919560d837 100644 --- a/aptos-move/e2e-move-tests/src/tests/transaction_context.data/pack/sources/transaction_context_test.move +++ b/aptos-move/e2e-move-tests/src/tests/transaction_context.data/pack/sources/transaction_context_test.move @@ -24,6 +24,7 @@ module admin::transaction_context_test { type_arg_names: vector, args: vector>, multisig_address: address, + raw_transaction_hash: vector, } /// Called when the module is first deployed at address `signer`, which is supposed to be @admin (= 0x1). @@ -44,6 +45,7 @@ module admin::transaction_context_test { args: vector[], type_arg_names: vector[], multisig_address: @0x0, + raw_transaction_hash: vector[], } ); } @@ -141,4 +143,14 @@ module admin::transaction_context_test { store.multisig_address = multisig_account; } + + entry fun store_raw_transaction_hash_from_native_txn_context(_s: &signer) acquires TransactionContextStore { + let store = borrow_global_mut(@admin); + store.raw_transaction_hash = transaction_context::raw_transaction_hash(); + } + + entry fun store_raw_transaction_hash_from_native_txn_context_multi(_s: &signer, _s2: &signer) acquires TransactionContextStore { + let store = borrow_global_mut(@admin); + store.raw_transaction_hash = transaction_context::raw_transaction_hash(); + } } diff --git a/aptos-move/e2e-move-tests/src/tests/transaction_context.rs b/aptos-move/e2e-move-tests/src/tests/transaction_context.rs index 300a362fde4e8..cf9da64575f09 100644 --- a/aptos-move/e2e-move-tests/src/tests/transaction_context.rs +++ b/aptos-move/e2e-move-tests/src/tests/transaction_context.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{assert_success, tests::common, MoveHarness}; +use aptos_crypto::hash::CryptoHash; use aptos_language_e2e_tests::account::{Account, TransactionBuilder}; use aptos_types::{ move_utils::MemberId, @@ -31,6 +32,7 @@ struct TransactionContextStore { type_arg_names: Vec, args: Vec>, multisig_address: AccountAddress, + raw_transaction_hash: Vec, } fn setup(harness: &mut MoveHarness) -> Account { @@ -468,3 +470,29 @@ fn test_transaction_context_multisig_payload() { assert!(txn_ctx_store.type_arg_names.is_empty()); assert!(txn_ctx_store.args.is_empty()); } + +#[test] +fn test_transaction_context_raw_transaction_hash() { + let mut harness = new_move_harness(); + let account = setup(&mut harness); + let signed_txn = harness.create_entry_function( + &account, + str::parse( + "0x1::transaction_context_test::store_raw_transaction_hash_from_native_txn_context", + ) + .unwrap(), + vec![], + vec![], + ); + let expected_hash = signed_txn.clone().into_raw_transaction().hash(); + let status = harness.run(signed_txn); + assert!(status.status().unwrap().is_success()); + let txn_ctx_store = harness + .read_resource::( + account.address(), + parse_struct_tag("0x1::transaction_context_test::TransactionContextStore").unwrap(), + ) + .unwrap(); + let hash = txn_ctx_store.raw_transaction_hash; + assert_eq!(hash, expected_hash.to_vec()); +} diff --git a/aptos-move/framework/aptos-framework/doc/transaction_context.md b/aptos-move/framework/aptos-framework/doc/transaction_context.md index cc7d9010ffc74..460eca3cd96ce 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_context.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_context.md @@ -9,8 +9,8 @@ - [Struct `EntryFunctionPayload`](#0x1_transaction_context_EntryFunctionPayload) - [Struct `MultisigPayload`](#0x1_transaction_context_MultisigPayload) - [Constants](#@Constants_0) -- [Function `get_txn_hash`](#0x1_transaction_context_get_txn_hash) - [Function `get_transaction_hash`](#0x1_transaction_context_get_transaction_hash) +- [Function `get_txn_hash`](#0x1_transaction_context_get_txn_hash) - [Function `generate_unique_address`](#0x1_transaction_context_generate_unique_address) - [Function `generate_auid_address`](#0x1_transaction_context_generate_auid_address) - [Function `get_script_hash`](#0x1_transaction_context_get_script_hash) @@ -39,9 +39,11 @@ - [Function `multisig_payload_internal`](#0x1_transaction_context_multisig_payload_internal) - [Function `multisig_address`](#0x1_transaction_context_multisig_address) - [Function `inner_entry_function_payload`](#0x1_transaction_context_inner_entry_function_payload) +- [Function `raw_transaction_hash`](#0x1_transaction_context_raw_transaction_hash) +- [Function `raw_transaction_hash_internal`](#0x1_transaction_context_raw_transaction_hash_internal) - [Specification](#@Specification_1) - - [Function `get_txn_hash`](#@Specification_1_get_txn_hash) - [Function `get_transaction_hash`](#@Specification_1_get_transaction_hash) + - [Function `get_txn_hash`](#@Specification_1_get_txn_hash) - [Function `generate_unique_address`](#@Specification_1_generate_unique_address) - [Function `generate_auid_address`](#@Specification_1_generate_auid_address) - [Function `get_script_hash`](#@Specification_1_get_script_hash) @@ -206,14 +208,16 @@ Transaction context is only available in the user transaction prologue, executio - + -## Function `get_txn_hash` +## Function `get_transaction_hash` -Returns the transaction hash of the current transaction. +Returns the hash of the internal session ID, providing a unique value for each transaction session +(prologue, execution, epilogue). Note that this is not the raw transaction hash used for authentication. +This function is created for to feature gate the get_txn_hash function. -
fun get_txn_hash(): vector<u8>
+
public fun get_transaction_hash(): vector<u8>
 
@@ -222,23 +226,22 @@ Returns the transaction hash of the current transaction. Implementation -
native fun get_txn_hash(): vector<u8>;
+
public fun get_transaction_hash(): vector<u8> {
+    get_txn_hash()
+}
 
- + -## Function `get_transaction_hash` +## Function `get_txn_hash` -Returns the transaction hash of the current transaction. -Internally calls the private function get_txn_hash. -This function is created for to feature gate the get_txn_hash function. -
public fun get_transaction_hash(): vector<u8>
+
fun get_txn_hash(): vector<u8>
 
@@ -247,9 +250,7 @@ This function is created for to feature gate the get_txn_hash funct Implementation -
public fun get_transaction_hash(): vector<u8> {
-    get_txn_hash()
-}
+
native fun get_txn_hash(): vector<u8>;
 
@@ -965,38 +966,59 @@ Returns the inner entry function payload of the multisig payload. - + -## Specification +## Function `raw_transaction_hash` +Returns the hash of the current raw transaction, which is used for transaction authentication. +This function calls an internal native function to retrieve the raw transaction hash. - -### Function `get_txn_hash` +
public fun raw_transaction_hash(): vector<u8>
+
-
fun get_txn_hash(): vector<u8>
+
+
+Implementation + + +
public fun raw_transaction_hash(): vector<u8> {
+    raw_transaction_hash_internal()
+}
 
+
-
pragma opaque;
-aborts_if [abstract] false;
-ensures result == spec_get_txn_hash();
-
+ +## Function `raw_transaction_hash_internal` - +
fun raw_transaction_hash_internal(): vector<u8>
+
-
fun spec_get_txn_hash(): vector<u8>;
+
+
+Implementation + + +
native fun raw_transaction_hash_internal(): vector<u8>;
 
+
+ + + +## Specification + + ### Function `get_transaction_hash` @@ -1017,6 +1039,33 @@ Returns the inner entry function payload of the multisig payload. + + +### Function `get_txn_hash` + + +
fun get_txn_hash(): vector<u8>
+
+ + + + +
pragma opaque;
+aborts_if [abstract] false;
+ensures result == spec_get_txn_hash();
+
+ + + + + + + +
fun spec_get_txn_hash(): vector<u8>;
+
+ + + ### Function `generate_unique_address` diff --git a/aptos-move/framework/aptos-framework/sources/transaction_context.move b/aptos-move/framework/aptos-framework/sources/transaction_context.move index c3bad25371cdc..df9af23a46aff 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_context.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_context.move @@ -31,15 +31,15 @@ module aptos_framework::transaction_context { entry_function_payload: Option, } - /// Returns the transaction hash of the current transaction. - native fun get_txn_hash(): vector; - /// Returns the transaction hash of the current transaction. - /// Internally calls the private function `get_txn_hash`. + /// Returns the hash of the internal session ID, providing a unique value for each transaction session + /// (prologue, execution, epilogue). Note that this is not the raw transaction hash used for authentication. /// This function is created for to feature gate the `get_txn_hash` function. public fun get_transaction_hash(): vector { get_txn_hash() } + native fun get_txn_hash(): vector; + /// Returns a universally unique identifier (of type address) generated /// by hashing the transaction hash of this transaction and a sequence number @@ -182,6 +182,13 @@ module aptos_framework::transaction_context { payload.entry_function_payload } + /// Returns the hash of the current raw transaction, which is used for transaction authentication. + /// This function calls an internal native function to retrieve the raw transaction hash. + public fun raw_transaction_hash(): vector { + raw_transaction_hash_internal() + } + native fun raw_transaction_hash_internal(): vector; + #[test()] fun test_auid_uniquess() { use std::vector; @@ -259,4 +266,11 @@ module aptos_framework::transaction_context { // expected to fail with the error code of `invalid_state(E_TRANSACTION_CONTEXT_NOT_AVAILABLE)` let _multisig = multisig_payload(); } + + #[test] + #[expected_failure(abort_code=196609, location = Self)] + fun test_call_raw_txn_hash() { + // expected to fail with the error code of `invalid_state(E_TRANSACTION_CONTEXT_NOT_AVAILABLE)` + let _multisig = multisig_payload(); + } } diff --git a/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move b/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move index f9837e26e6a75..186b52e0d9379 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move @@ -101,9 +101,12 @@ spec aptos_framework::transaction_context { //TODO: temporary mockup pragma opaque; } - spec multisig_payload_internal(): Option { //TODO: temporary mockup pragma opaque; } + spec raw_transaction_hash_internal(): vector { + //TODO: temporary mockup + pragma opaque; + } } diff --git a/aptos-move/framework/src/natives/transaction_context.rs b/aptos-move/framework/src/natives/transaction_context.rs index 044e948a0ecb5..51855dba15124 100644 --- a/aptos-move/framework/src/natives/transaction_context.rs +++ b/aptos-move/framework/src/natives/transaction_context.rs @@ -368,6 +368,25 @@ fn native_multisig_payload_internal( } } +fn native_raw_transaction_hash_internal( + context: &mut SafeNativeContext, + _ty_args: Vec, + _args: VecDeque, +) -> SafeNativeResult> { + context.charge(TRANSACTION_CONTEXT_SENDER_BASE)?; + + let user_transaction_context_opt = get_user_transaction_context_opt_from_context(context); + if let Some(transaction_context) = user_transaction_context_opt { + Ok(smallvec![Value::vector_u8( + transaction_context.raw_txn_hash() + )]) + } else { + Err(SafeNativeError::Abort { + abort_code: error::invalid_state(abort_codes::ETRANSACTION_CONTEXT_NOT_AVAILABLE), + }) + } +} + fn get_user_transaction_context_opt_from_context<'a>( context: &'a SafeNativeContext, ) -> &'a Option { @@ -405,6 +424,10 @@ pub fn make_all( "multisig_payload_internal", native_multisig_payload_internal, ), + ( + "raw_transaction_hash_internal", + native_raw_transaction_hash_internal, + ), ]; builder.make_named_natives(natives) diff --git a/types/src/transaction/user_transaction_context.rs b/types/src/transaction/user_transaction_context.rs index 4c9d3f71d3d12..c43411e74d876 100644 --- a/types/src/transaction/user_transaction_context.rs +++ b/types/src/transaction/user_transaction_context.rs @@ -13,6 +13,7 @@ pub struct UserTransactionContext { chain_id: u8, entry_function_payload: Option, multisig_payload: Option, + raw_transaction_hash: Vec, } impl UserTransactionContext { @@ -25,6 +26,7 @@ impl UserTransactionContext { chain_id: u8, entry_function_payload: Option, multisig_payload: Option, + raw_transaction_hash: Vec, ) -> Self { Self { sender, @@ -35,6 +37,7 @@ impl UserTransactionContext { chain_id, entry_function_payload, multisig_payload, + raw_transaction_hash, } } @@ -69,6 +72,10 @@ impl UserTransactionContext { pub fn multisig_payload(&self) -> Option { self.multisig_payload.clone() } + + pub fn raw_txn_hash(&self) -> Vec { + self.raw_transaction_hash.clone() + } } #[derive(Debug, Clone)]