Skip to content

Commit

Permalink
Add a transaction context function to get the raw transaction hash
Browse files Browse the repository at this point in the history
This commit adds a transaction context function to get the raw transaction hash.
  • Loading branch information
junkil-park committed Oct 24, 2024
1 parent c070f4c commit df35fd7
Show file tree
Hide file tree
Showing 16 changed files with 517 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub enum FeatureFlag {
FederatedKeyless,
TransactionSimulationEnhancement,
CollectionOwner,
TransactionContextHashFunctionUpdate,
}

fn generate_features_blob(writer: &CodeWriter, data: &[u64]) {
Expand Down Expand Up @@ -347,6 +348,9 @@ impl From<FeatureFlag> for AptosFeatureFlag {
AptosFeatureFlag::TRANSACTION_SIMULATION_ENHANCEMENT
},
FeatureFlag::CollectionOwner => AptosFeatureFlag::COLLECTION_OWNER,
FeatureFlag::TransactionContextHashFunctionUpdate => {
AptosFeatureFlag::TRANSACTION_CONTEXT_HASH_FUNCTION_UPDATE
},
}
}
}
Expand Down Expand Up @@ -490,6 +494,9 @@ impl From<AptosFeatureFlag> for FeatureFlag {
FeatureFlag::TransactionSimulationEnhancement
},
AptosFeatureFlag::COLLECTION_OWNER => FeatureFlag::CollectionOwner,
AptosFeatureFlag::TRANSACTION_CONTEXT_HASH_FUNCTION_UPDATE => {
FeatureFlag::TransactionContextHashFunctionUpdate
},
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions aptos-move/aptos-vm/src/move_vm_ext/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,24 +76,24 @@ impl<'r, 'l> SessionExt<'r, 'l> {
resolver: &'r R,
) -> Self {
let mut extensions = NativeContextExtensions::default();
let txn_hash: [u8; 32] = session_id
let unique_session_hash: [u8; 32] = session_id
.as_uuid()
.to_vec()
.try_into()
.expect("HashValue should convert to [u8; 32]");

extensions.add(NativeTableContext::new(txn_hash, resolver));
extensions.add(NativeTableContext::new(unique_session_hash, resolver));
extensions.add(NativeRistrettoPointContext::new());
extensions.add(AlgebraContext::new());
extensions.add(NativeAggregatorContext::new(
txn_hash,
unique_session_hash,
resolver,
move_vm.vm_config().delayed_field_optimization_enabled,
resolver,
));
extensions.add(RandomnessContext::new());
extensions.add(NativeTransactionContext::new(
txn_hash.to_vec(),
unique_session_hash.to_vec(),
session_id.into_script_hash(),
chain_id.id(),
maybe_user_transaction_context,
Expand Down
43 changes: 40 additions & 3 deletions aptos-move/aptos-vm/src/transaction_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};

Expand All @@ -31,6 +32,7 @@ pub struct TransactionMetadata {
pub is_keyless: bool,
pub entry_function_payload: Option<EntryFunction>,
pub multisig_payload: Option<Multisig>,
pub raw_transaction_hash: Vec<u8>,
}

impl TransactionMetadata {
Expand Down Expand Up @@ -89,6 +91,36 @@ 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: _,
} => {
// In the case of a fee payer transaction, the hash value is generated using
// AccountAddress::ZERO as the fee payer address.
let raw_txn = RawTransactionWithData::new_fee_payer(
txn.clone().into_raw_transaction(),
secondary_signer_addresses.clone(),
AccountAddress::ZERO,
);
raw_txn.hash().to_vec()
},
_ => txn.raw_transaction_ref().hash().to_vec(),
},
}
}

Expand Down Expand Up @@ -158,6 +190,10 @@ impl TransactionMetadata {
self.multisig_payload.clone()
}

pub fn raw_transaction_hash(&self) -> Vec<u8> {
self.raw_transaction_hash.clone()
}

pub fn as_user_transaction_context(&self) -> UserTransactionContext {
UserTransactionContext::new(
self.sender,
Expand All @@ -170,6 +206,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(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module admin::transaction_context_test {
type_arg_names: vector<String>,
args: vector<vector<u8>>,
multisig_address: address,
raw_transaction_hash: vector<u8>,
}

/// Called when the module is first deployed at address `signer`, which is supposed to be @admin (= 0x1).
Expand All @@ -44,6 +45,7 @@ module admin::transaction_context_test {
args: vector[],
type_arg_names: vector[],
multisig_address: @0x0,
raw_transaction_hash: vector[],
}
);
}
Expand Down Expand Up @@ -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<TransactionContextStore>(@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<TransactionContextStore>(@admin);
store.raw_transaction_hash = transaction_context::raw_transaction_hash();
}
}
139 changes: 138 additions & 1 deletion aptos-move/e2e-move-tests/src/tests/transaction_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
// 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,
on_chain_config::FeatureFlag,
transaction::{EntryFunction, MultisigTransactionPayload, TransactionPayload},
transaction::{
EntryFunction, MultisigTransactionPayload, RawTransactionWithData, TransactionPayload,
},
};
use bcs::to_bytes;
use move_core_types::{
Expand All @@ -31,6 +34,7 @@ struct TransactionContextStore {
type_arg_names: Vec<String>,
args: Vec<Vec<u8>>,
multisig_address: AccountAddress,
raw_transaction_hash: Vec<u8>,
}

fn setup(harness: &mut MoveHarness) -> Account {
Expand Down Expand Up @@ -468,3 +472,136 @@ 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_simple() {
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::<crate::tests::transaction_context::TransactionContextStore>(
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());
}

#[test]
fn test_transaction_context_raw_transaction_hash_multiagent() {
let mut harness = new_move_harness();

let alice = setup(&mut harness);
let bob = harness.new_account_with_balance_and_sequence_number(1000000, 0);

let fun: MemberId = str::parse(
"0x1::transaction_context_test::store_raw_transaction_hash_from_native_txn_context_multi",
)
.unwrap();
let MemberId {
module_id,
member_id: function_id,
} = fun;
let ty_args = vec![];
let args = vec![];
let payload = TransactionPayload::EntryFunction(EntryFunction::new(
module_id,
function_id,
ty_args,
args,
));
let signed_txn = TransactionBuilder::new(alice.clone())
.secondary_signers(vec![bob.clone()])
.payload(payload)
.sequence_number(harness.sequence_number(alice.address()))
.max_gas_amount(1_000_000)
.gas_unit_price(1)
.sign_multi_agent();

let raw_txn_with_data =
RawTransactionWithData::new_multi_agent(signed_txn.clone().into_raw_transaction(), vec![
*bob.address(),
]);
let expected_hash = raw_txn_with_data.hash();

let output = harness.run_raw(signed_txn);
assert_success!(*output.status());

let txn_ctx_store = harness
.read_resource::<crate::tests::transaction_context::TransactionContextStore>(
alice.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());
}

#[test]
fn test_transaction_context_raw_transaction_hash_multiagent_with_fee_payer() {
let mut harness = new_move_harness();

let alice = setup(&mut harness);
let bob = harness.new_account_with_balance_and_sequence_number(1000000, 0);
let fee_payer = harness.new_account_with_balance_and_sequence_number(1000000, 0);

let fun: MemberId = str::parse(
"0x1::transaction_context_test::store_raw_transaction_hash_from_native_txn_context_multi",
)
.unwrap();
let MemberId {
module_id,
member_id: function_id,
} = fun;
let ty_args = vec![];
let args = vec![];
let payload = TransactionPayload::EntryFunction(EntryFunction::new(
module_id,
function_id,
ty_args,
args,
));
let signed_txn = TransactionBuilder::new(alice.clone())
.fee_payer(fee_payer.clone())
.secondary_signers(vec![bob.clone()])
.payload(payload)
.sequence_number(harness.sequence_number(alice.address()))
.max_gas_amount(1_000_000)
.gas_unit_price(1)
.sign_fee_payer();

// In the case of a fee payer transaction, the hash value is generated using
// AccountAddress::ZERO as the fee payer address.
let raw_txn_with_data = RawTransactionWithData::new_fee_payer(
signed_txn.clone().into_raw_transaction(),
vec![*bob.address()],
AccountAddress::ZERO,
);
let expected_hash = raw_txn_with_data.hash();

let output = harness.run_raw(signed_txn);
assert_success!(*output.status());

let txn_ctx_store = harness
.read_resource::<crate::tests::transaction_context::TransactionContextStore>(
alice.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());
}
2 changes: 1 addition & 1 deletion aptos-move/framework/aptos-framework/doc/randomness.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ of the hash function).
<b>let</b> seed = *<a href="../../aptos-stdlib/../move-stdlib/doc/option.md#0x1_option_borrow">option::borrow</a>(&<a href="randomness.md#0x1_randomness">randomness</a>.seed);

<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector_append">vector::append</a>(&<b>mut</b> input, seed);
<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector_append">vector::append</a>(&<b>mut</b> input, <a href="transaction_context.md#0x1_transaction_context_get_transaction_hash">transaction_context::get_transaction_hash</a>());
<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector_append">vector::append</a>(&<b>mut</b> input, <a href="transaction_context.md#0x1_transaction_context_unique_session_hash">transaction_context::unique_session_hash</a>());
<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector_append">vector::append</a>(&<b>mut</b> input, <a href="randomness.md#0x1_randomness_fetch_and_increment_txn_counter">fetch_and_increment_txn_counter</a>());
<a href="../../aptos-stdlib/../move-stdlib/doc/hash.md#0x1_hash_sha3_256">hash::sha3_256</a>(input)
}
Expand Down
Loading

0 comments on commit df35fd7

Please sign in to comment.