From 6ce216e5efb6b3ca2e7e6e5b2f9dd0497ef8db66 Mon Sep 17 00:00:00 2001 From: Satya Vusirikala Date: Tue, 29 Oct 2024 17:30:30 -0700 Subject: [PATCH 01/17] Build works --- Cargo.lock | 1 + api/src/context.rs | 95 +- api/src/tests/mod.rs | 4 +- api/src/transactions.rs | 249 ++++- api/types/src/convert.rs | 378 ++++---- api/types/src/lib.rs | 7 +- api/types/src/transaction.rs | 150 ++- .../aptos-debugger/src/aptos_debugger.rs | 39 +- .../src/components/feature_flags.rs | 3 + .../aptos-validator-interface/src/lib.rs | 1 + .../src/rest_interface.rs | 5 +- .../src/storage_interface.rs | 4 +- aptos-move/aptos-vm/src/aptos_vm.rs | 345 ++++--- aptos-move/aptos-vm/src/errors.rs | 5 +- .../src/move_vm_ext/session/session_id.rs | 92 +- .../aptos-vm/src/transaction_metadata.rs | 37 +- .../aptos-vm/src/transaction_validation.rs | 260 +++++- aptos-move/e2e-tests/src/executor.rs | 4 + .../framework/aptos-framework/doc/genesis.md | 3 +- .../aptos-framework/doc/nonce_validation.md | 309 ++++++ .../framework/aptos-framework/doc/overview.md | 1 + .../doc/transaction_validation.md | 883 ++++++++---------- .../aptos-framework/sources/genesis.move | 3 +- .../sources/nonce_validation.move | 145 +++ .../sources/transaction_validation.move | 235 ++++- .../sources/transaction_validation.spec.move | 826 ++++++++-------- .../cached-packages/src/aptos_stdlib.rs | 69 +- .../src/config/internal_indexer_db_config.rs | 9 +- consensus/consensus-types/src/common.rs | 27 +- consensus/src/block_preparer.rs | 4 + .../src/quorum_store/batch_coordinator.rs | 6 + consensus/src/quorum_store/batch_generator.rs | 2 +- .../src/quorum_store/batch_proof_queue.rs | 2 +- .../tests/batch_proof_queue_test.rs | 58 +- consensus/src/quorum_store/types.rs | 6 +- consensus/src/txn_notifier.rs | 2 +- crates/aptos-rest-client/src/lib.rs | 75 +- .../src/emitter/mod.rs | 196 ++-- .../src/emitter/submission_worker.rs | 422 ++++++--- .../src/emitter/transaction_executor.rs | 2 +- crates/transaction-generator-lib/src/args.rs | 421 ++++++--- .../src/call_custom_modules.rs | 15 +- .../src/entry_points.rs | 44 +- crates/transaction-generator-lib/src/lib.rs | 46 +- .../src/p2p_transaction_generator.rs | 75 +- .../src/workflow_delegator.rs | 5 +- .../indexer-grpc-data-service/src/service.rs | 2 + .../indexer-grpc-fullnode/src/convert.rs | 81 +- .../src/internal_indexer_db_service.rs | 3 +- execution/executor/src/metrics.rs | 40 +- execution/executor/src/tests/mock_vm/mod.rs | 67 +- .../executor/tests/internal_indexer_test.rs | 2 +- mempool/src/core_mempool/index.rs | 337 ++++--- mempool/src/core_mempool/mempool.rs | 196 ++-- mempool/src/core_mempool/mod.rs | 4 +- mempool/src/core_mempool/transaction.rs | 37 +- mempool/src/core_mempool/transaction_store.rs | 466 +++++---- mempool/src/logging.rs | 41 +- mempool/src/shared_mempool/tasks.rs | 83 +- mempool/src/shared_mempool/types.rs | 11 +- .../src/shared_mempool/use_case_history.rs | 4 +- mempool/src/tests/common.rs | 63 +- mempool/src/tests/core_mempool_test.rs | 551 ++++++++--- mempool/src/tests/integration_tests.rs | 3 +- mempool/src/tests/mocks.rs | 12 +- mempool/src/tests/multi_node_test.rs | 13 +- mempool/src/tests/node.rs | 9 +- mempool/src/tests/shared_mempool_test.rs | 26 +- mempool/src/tests/test_framework.rs | 32 +- peer-monitoring-service/server/src/tests.rs | 14 +- protos/rust/src/pb/aptos.transaction.v1.rs | 27 +- .../rust/src/pb/aptos.transaction.v1.serde.rs | 127 +++ sdk/src/transaction_builder.rs | 29 +- sdk/src/types.rs | 46 +- .../mempool-notifications/src/lib.rs | 10 +- .../state-sync-driver/src/tests/mocks.rs | 33 +- .../state-sync-driver/src/tests/utils.rs | 6 +- .../storage-service/server/src/tests/mock.rs | 31 +- storage/aptosdb/src/db/fake_aptosdb.rs | 26 +- .../aptosdb/src/db/include/aptosdb_reader.rs | 38 +- storage/aptosdb/src/db/test_helper.rs | 6 +- storage/aptosdb/src/db_debugger/validation.rs | 4 +- storage/aptosdb/src/db_options.rs | 9 +- .../aptosdb/src/ledger_db/transaction_db.rs | 25 +- .../aptosdb/src/pruner/ledger_pruner/test.rs | 6 +- storage/aptosdb/src/schema/mod.rs | 4 +- storage/aptosdb/src/transaction_store/mod.rs | 113 ++- storage/aptosdb/src/transaction_store/test.rs | 6 +- storage/indexer/Cargo.toml | 1 + storage/indexer/src/db_indexer.rs | 133 ++- storage/indexer/src/indexer_reader.rs | 32 +- storage/indexer_schemas/src/schema/mod.rs | 12 +- .../mod.rs | 10 +- .../test.rs | 4 +- .../orderless_transaction_by_account/mod.rs | 67 ++ .../orderless_transaction_by_account/test.rs | 20 + .../transaction_summaries_by_account/mod.rs | 64 ++ .../transaction_summaries_by_account/test.rs | 20 + storage/indexer_schemas/src/utils.rs | 127 ++- storage/schemadb/src/iterator.rs | 1 + storage/storage-interface/src/lib.rs | 24 +- .../src/suites/realistic_workloads.rs | 4 +- testsuite/forge/src/interface/aptos.rs | 3 +- .../consensus/quorum_store_fault_tolerance.rs | 6 +- testsuite/smoke-test/src/rest_api.rs | 7 +- testsuite/smoke-test/src/rosetta.rs | 4 +- testsuite/smoke-test/src/txn_emitter.rs | 6 +- .../move/move-core/types/src/vm_status.rs | 4 +- .../DPN/sources/DiemAccount.move | 2 + types/src/indexer/indexer_db_reader.rs | 25 +- types/src/on_chain_config/aptos_features.rs | 6 + types/src/proptest_types.rs | 230 +++-- types/src/transaction/mod.rs | 175 ++++ types/src/transaction/multisig.rs | 11 + types/src/transaction/use_case.rs | 15 +- vm-validator/src/vm_validator.rs | 1 + 116 files changed, 6495 insertions(+), 2732 deletions(-) create mode 100644 aptos-move/framework/aptos-framework/doc/nonce_validation.md create mode 100644 aptos-move/framework/aptos-framework/sources/nonce_validation.move rename storage/indexer_schemas/src/schema/{transaction_by_account => ordered_transaction_by_account}/mod.rs (85%) rename storage/indexer_schemas/src/schema/{transaction_by_account => ordered_transaction_by_account}/test.rs (74%) create mode 100644 storage/indexer_schemas/src/schema/orderless_transaction_by_account/mod.rs create mode 100644 storage/indexer_schemas/src/schema/orderless_transaction_by_account/test.rs create mode 100644 storage/indexer_schemas/src/schema/transaction_summaries_by_account/mod.rs create mode 100644 storage/indexer_schemas/src/schema/transaction_summaries_by_account/test.rs diff --git a/Cargo.lock b/Cargo.lock index 15f52e4b304bb..b9370d2e420b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1222,6 +1222,7 @@ version = "0.1.0" dependencies = [ "anyhow", "aptos-config", + "aptos-crypto", "aptos-db-indexer-schemas", "aptos-logger", "aptos-metrics-core", diff --git a/api/src/context.rs b/api/src/context.rs index bdc96fa74e0a1..099b567562ed2 100644 --- a/api/src/context.rs +++ b/api/src/context.rs @@ -13,8 +13,7 @@ use crate::{ }; use anyhow::{anyhow, bail, ensure, format_err, Context as AnyhowContext, Result}; use aptos_api_types::{ - AptosErrorCode, AsConverter, BcsBlock, GasEstimation, LedgerInfo, ResourceGroup, - TransactionOnChainData, + transaction::ReplayProtector, AptosErrorCode, AsConverter, BcsBlock, GasEstimation, LedgerInfo, ResourceGroup, TransactionOnChainData, TransactionSummary }; use aptos_config::config::{GasEstimationConfig, NodeConfig, RoleType}; use aptos_crypto::HashValue; @@ -34,7 +33,7 @@ use aptos_types::{ chain_id::ChainId, contract_event::{ContractEvent, ContractEventV1, EventWithVersion}, event::EventKey, - indexer::indexer_db_reader::IndexerReader, + indexer::indexer_db_reader::{IndexedTransactionSummary, IndexerReader}, ledger_info::LedgerInfoWithSignatures, on_chain_config::{GasSchedule, GasScheduleV2, OnChainConfig, OnChainExecutionConfig}, state_store::{ @@ -786,6 +785,41 @@ impl Context { Ok(txns) } + pub fn render_transaction_summaries( + &self, + ledger_info: &LedgerInfo, + data: Vec, + ) -> Result, E> { + if data.is_empty() { + return Ok(vec![]); + } + + // Question: Is it worth adding timestamp based on the version here? Or should we rather just store the timestamp in IndexedTransactionSummary. + let txn_summaries: Vec = data + .into_iter() + .map(|t| { + // let timestamp = self.db.get_block_timestamp(t.version)?; + Ok( + TransactionSummary { + sender: t.sender.into(), + version: t.version.into(), + transaction_hash: t.transaction_hash.into(), + replay_protector: match t.replay_protector { + aptos_types::transaction::ReplayProtector::Nonce(nonce) => ReplayProtector::Nonce(nonce.into()), + aptos_types::transaction::ReplayProtector::SequenceNumber(seq_num) => ReplayProtector::SequenceNumber(seq_num.into()) + }, + // timestamp: timestamp.into(), + } + ) + }) + .collect::>() + .context("Failed to convert transaction summary data from storage") + .map_err(|err| { + E::internal_with_code(err, AptosErrorCode::InternalError, ledger_info) + })?; + Ok(txn_summaries) + } + pub fn get_transactions( &self, start_version: u64, @@ -833,7 +867,7 @@ impl Context { .collect() } - pub fn get_account_transactions( + pub fn get_ordered_account_transactions( &self, address: AccountAddress, start_seq_number: Option, @@ -854,7 +888,7 @@ impl Context { }; let txns_res = if !db_sharding_enabled(&self.node_config) { - self.db.get_account_transactions( + self.db.get_ordered_account_transactions( address, start_seq_number, limit as u64, @@ -868,7 +902,7 @@ impl Context { .map_err(|err| { E::internal_with_code(err, AptosErrorCode::InternalError, ledger_info) })? - .get_account_transactions( + .get_ordered_account_transactions( address, start_seq_number, limit as u64, @@ -893,6 +927,55 @@ impl Context { .map_err(|err| E::internal_with_code(err, AptosErrorCode::InternalError, ledger_info)) } + pub fn get_account_all_transaction_summaries( + &self, + address: AccountAddress, + start_version: Option, + end_version: Option, + limit: u16, + ledger_version: u64, + ledger_info: &LedgerInfo, + ) -> Result, E> { + let txns_res = if !db_sharding_enabled(&self.node_config) { + self.db.get_account_all_transaction_summaries( + address, + start_version, + end_version, + limit as u64, + ledger_version, + ) + } else { + self.indexer_reader + .as_ref() + .ok_or_else(|| anyhow!("Indexer reader is None")) + .map_err(|err| { + E::internal_with_code(err, AptosErrorCode::InternalError, ledger_info) + })? + .get_account_all_transaction_summaries( + address, + start_version, + end_version, + limit as u64, + ledger_version, + ) + .map_err(|e| AptosDbError::Other(e.to_string())) + }; + txns_res + .context("Failed to retrieve account transaction summaries") + .map_err(|err| { + E::internal_with_code(err, AptosErrorCode::InternalError, ledger_info) + }) + // .map(|txns| txns.iter().map(|t| TransactionSummary { + // sender: t.sender.into(), + // version: t.version.into(), + // transaction_hash: t.transaction_hash.into(), + // replay_protector: match t.replay_protector { + // aptos_types::transaction::ReplayProtector::Nonce(nonce) => ReplayProtector::Nonce(nonce.into()), + // aptos_types::transaction::ReplayProtector::SequenceNumber(seq_num) => ReplayProtector::SequenceNumber(seq_num.into()) + // }, + // }).collect()) + } + pub fn get_transaction_by_hash( &self, hash: HashValue, diff --git a/api/src/tests/mod.rs b/api/src/tests/mod.rs index 43468220043b8..72ebfbab8284f 100644 --- a/api/src/tests/mod.rs +++ b/api/src/tests/mod.rs @@ -37,7 +37,7 @@ fn new_test_context_with_config(test_name: String, node_config: NodeConfig) -> T fn new_test_context_with_db_sharding_and_internal_indexer(test_name: String) -> TestContext { let mut node_config = NodeConfig::default(); node_config.storage.rocksdb_configs.enable_storage_sharding = true; - node_config.indexer_db_config = InternalIndexerDBConfig::new(true, true, true, 0, true, 10); + node_config.indexer_db_config = InternalIndexerDBConfig::new(true, true, true, true, 0, true, 10); let test_context = super_new_test_context(test_name, node_config, false, None); let _ = test_context .get_indexer_reader() @@ -52,6 +52,6 @@ fn new_test_context_with_sharding_and_delayed_internal_indexer( ) -> TestContext { let mut node_config = NodeConfig::default(); node_config.storage.rocksdb_configs.enable_storage_sharding = true; - node_config.indexer_db_config = InternalIndexerDBConfig::new(true, true, true, 0, true, 1); + node_config.indexer_db_config = InternalIndexerDBConfig::new(true, true, true, true, 0, true, 1); super_new_test_context(test_name, node_config, false, end_version) } diff --git a/api/src/transactions.rs b/api/src/transactions.rs index f5cf952c967b5..ce017e9f463bf 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -21,22 +21,15 @@ use crate::{ }; use anyhow::Context as AnyhowContext; use aptos_api_types::{ - verify_function_identifier, verify_module_identifier, Address, AptosError, AptosErrorCode, - AsConverter, EncodeSubmissionRequest, GasEstimation, GasEstimationBcs, HashValue, - HexEncodedBytes, LedgerInfo, MoveType, PendingTransaction, SubmitTransactionRequest, - Transaction, TransactionData, TransactionOnChainData, TransactionsBatchSingleSubmissionFailure, - TransactionsBatchSubmissionResult, UserTransaction, VerifyInput, VerifyInputWithRecursion, U64, + transaction::TransactionSummary, verify_function_identifier, verify_module_identifier, Address, AptosError, AptosErrorCode, AsConverter, EncodeSubmissionRequest, GasEstimation, GasEstimationBcs, HashValue, HexEncodedBytes, LedgerInfo, MoveType, PendingTransaction, SubmitTransactionRequest, Transaction, TransactionData, TransactionOnChainData, TransactionsBatchSingleSubmissionFailure, TransactionsBatchSubmissionResult, UserTransaction, VerifyInput, VerifyInputWithRecursion, MAX_RECURSIVE_TYPES_ALLOWED, U64 }; use aptos_crypto::{hash::CryptoHash, signing_message}; use aptos_types::{ - account_address::AccountAddress, - mempool_status::MempoolStatusCode, - transaction::{ + account_address::AccountAddress, mempool_status::MempoolStatusCode, transaction::{ EntryFunction, ExecutionStatus, MultisigTransactionPayload, RawTransaction, - RawTransactionWithData, SignedTransaction, TransactionPayload, - }, - vm_status::StatusCode, - AptosCoinType, CoinType, + RawTransactionWithData, Script, SignedTransaction, TransactionExecutable, + TransactionPayload, TransactionPayloadV2, + }, vm_status::StatusCode, AptosCoinType, CoinType }; use aptos_vm::{AptosSimulationVM, AptosVM}; use move_core_types::{ident_str, language_storage::ModuleId, vm_status::VMStatus}; @@ -45,7 +38,8 @@ use poem_openapi::{ payload::Json, ApiRequest, OpenApi, }; -use std::{sync::Arc, time::Duration}; +use aptos_logger::{error, info}; +use std::{cmp::min, sync::Arc, time::Duration}; generate_success_response!(SubmitTransactionResponse, (202, Accepted)); @@ -305,12 +299,15 @@ impl TransactionsApi { /// Get account transactions /// - /// Retrieves on-chain committed transactions from an account. If the start - /// version is too far in the past, a 410 will be returned. + /// Retrieves on-chain committed sequence-number based transactions from an account. + /// Does not retrieve orderless transactions sent from the account. + /// If the start version is too far in the past, a 410 will be returned. /// /// If no start version is given, it will start at version 0. /// /// To retrieve a pending transaction, use /transactions/by_hash. + + // Question: Can this operation id and function name be changed to "get_ordered_account_transactions" #[oai( path = "/accounts/:address/transactions", method = "get", @@ -340,9 +337,65 @@ impl TransactionsApi { self.context.max_transactions_page_size(), ); let api = self.clone(); - api_spawn_blocking(move || api.list_by_account(&accept_type, page, address.0)).await + api_spawn_blocking(move || api.list_ordered_txns_by_account(&accept_type, page, address.0)).await + } + + /// Get account transaction summaries + /// + /// Retrieves on-chain committed transactions (both sequence number based + /// and orderless transactions) from an account. + /// If no start version is given, it will start at version 0. + /// + /// To retrieve a pending transaction, use /transactions/by_hash. + #[oai( + path = "/accounts/:address/transaction_summaries", + method = "get", + operation_id = "get_account_transaction_summaries", + tag = "ApiTags::Transactions" + )] + // Question: Do you prefer "/accounts/:address/transactions_v2" instead? + // Question: Should the API return the entire transaction or just a summary? + // Question: Should the API return only user transactions, or all transaction types? + // Question: It might be hard for a user to enter start_time and end_time in milliseconds since UNIX_EPOCH? + async fn get_accounts_transaction_summaries( + &self, + accept_type: AcceptType, + /// Address of account with or without a `0x` prefix + address: Path
, + /// Transaction version to start list of transactions + /// + /// If not provided, defaults to showing the latest transactions + start_version: Query>, + /// Transaction version to end list of transactions + /// + /// If not provided, defaults to showing the latest transactions + end_version: Query>, + /// Block timestamp in milliseconds since UNIX_EPOCH to start list of transactions + /// + /// If not provided, defaults to showing the latest transactions + start_time: Query>, + /// Block timestamp in milliseconds since UNIX_EPOCHto end list of transactions + /// + /// If not provided, defaults to showing the latest transactions + end_time: Query>, + /// Max number of transactions to retrieve. + /// + /// If not provided, defaults to default page size + limit: Query>, + ) -> BasicResultWith404> { + fail_point_poem("endpoint_get_accounts_transaction_summaries")?; + self.context + .check_api_output_enabled("Get account transaction summaries", &accept_type)?; + let limit = if let Some(limit) = limit.0 { + min(limit, self.context.max_transactions_page_size()) + } else { + self.context.max_transactions_page_size() + }; + let api = self.clone(); + api_spawn_blocking(move || api.list_all_txn_summaries_by_account(&accept_type, address.0, start_version.0, end_version.0, limit)).await } + /// Submit transaction /// /// This endpoint accepts transaction submissions in two formats. @@ -1001,8 +1054,8 @@ impl TransactionsApi { ) } - /// List all transactions for an account - fn list_by_account( + /// List sequence number based transactions for an account + fn list_ordered_txns_by_account( &self, accept_type: &AcceptType, page: Page, @@ -1014,7 +1067,7 @@ impl TransactionsApi { let latest_ledger_info = account.latest_ledger_info; // TODO: Return more specific errors from within this function. - let data = self.context.get_account_transactions( + let data = self.context.get_ordered_account_transactions( address.into(), page.start_option(), page.limit(&latest_ledger_info)?, @@ -1034,18 +1087,90 @@ impl TransactionsApi { } } + + /// List all transactions for an account + fn list_all_txn_summaries_by_account( + &self, + accept_type: &AcceptType, + address: Address, + start_version: Option, + end_version: Option, + limit: u16, + ) -> BasicResultWith404> { + let (latest_ledger_info, ledger_version) = self.context + .get_latest_ledger_info_and_verify_lookup_version(None)?; + + // TODO: Return more specific errors from within this function. + match self.context.get_account_all_transaction_summaries( + address.into(), + start_version.map(|v| v.into()), + end_version.map(|v| v.into()), + limit, + ledger_version, + &latest_ledger_info, + ) { + Ok(data) => { + for txn_summary in &data { + info!("list_all_txn_summaries_by_account address: {:?}, replay_protector: {:?}", txn_summary.sender, txn_summary.replay_protector); + } + match accept_type { + AcceptType::Json => BasicResponse::try_from_json(( + self.context + .render_transaction_summaries(&latest_ledger_info, data)?, + &latest_ledger_info, + BasicResponseStatus::Ok, + )), + AcceptType::Bcs => { + BasicResponse::try_from_bcs((data, &latest_ledger_info, BasicResponseStatus::Ok)) + }, + } + }, + Err(e) => { + error!("list_all_txn_summaries_by_account error: {:?}", e); + Err(e) + } + } + } + /// Parses a single signed transaction fn get_signed_transaction( &self, ledger_info: &LedgerInfo, data: SubmitTransactionPost, ) -> Result { - pub const MAX_SIGNED_TRANSACTION_DEPTH: usize = 16; + let validate_script = |script: &Script| -> Result<(), SubmitTransactionError> { + if script.code().is_empty() { + return Err(SubmitTransactionError::bad_request_with_code( + "Script payload bytecode must not be empty", + AptosErrorCode::InvalidInput, + ledger_info, + )); + } + + for arg in script.ty_args() { + let arg = MoveType::from(arg); + arg.verify(0) + .context("Transaction script function type arg invalid") + .map_err(|err| { + SubmitTransactionError::bad_request_with_code( + err, + AptosErrorCode::InvalidInput, + ledger_info, + ) + })?; + } + Ok(()) + }; + + let validate_entry_function = + |entry_function: &EntryFunction| -> Result<(), SubmitTransactionError> { + TransactionsApi::validate_entry_function_payload_format(ledger_info, entry_function) + }; match data { SubmitTransactionPost::Bcs(data) => { let signed_transaction: SignedTransaction = - bcs::from_bytes_with_limit(&data.0, MAX_SIGNED_TRANSACTION_DEPTH) + bcs::from_bytes_with_limit(&data.0, MAX_RECURSIVE_TYPES_ALLOWED as usize) .context("Failed to deserialize input into SignedTransaction") .map_err(|err| { SubmitTransactionError::bad_request_with_code( @@ -1057,41 +1182,16 @@ impl TransactionsApi { // Verify the signed transaction match signed_transaction.payload() { TransactionPayload::EntryFunction(entry_function) => { - TransactionsApi::validate_entry_function_payload_format( - ledger_info, - entry_function, - )?; + validate_entry_function(entry_function)?; }, TransactionPayload::Script(script) => { - if script.code().is_empty() { - return Err(SubmitTransactionError::bad_request_with_code( - "Script payload bytecode must not be empty", - AptosErrorCode::InvalidInput, - ledger_info, - )); - } - - for arg in script.ty_args() { - let arg = MoveType::from(arg); - arg.verify(0) - .context("Transaction script function type arg invalid") - .map_err(|err| { - SubmitTransactionError::bad_request_with_code( - err, - AptosErrorCode::InvalidInput, - ledger_info, - ) - })?; - } + validate_script(script)?; }, TransactionPayload::Multisig(multisig) => { if let Some(payload) = &multisig.transaction_payload { match payload { MultisigTransactionPayload::EntryFunction(entry_function) => { - TransactionsApi::validate_entry_function_payload_format( - ledger_info, - entry_function, - )?; + validate_entry_function(entry_function)?; }, } } @@ -1106,6 +1206,33 @@ impl TransactionsApi { ledger_info, )) }, + TransactionPayload::V2(TransactionPayloadV2::V1 { + executable, + extra_config, + }) => match executable { + TransactionExecutable::Script(script) => { + validate_script(script)?; + if extra_config.is_multisig() { + return Err(SubmitTransactionError::bad_request_with_code( + "Script transaction payload must not be a multisig transaction", + AptosErrorCode::InvalidInput, + ledger_info, + )); + } + }, + TransactionExecutable::EntryFunction(entry_function) => { + validate_entry_function(entry_function)?; + }, + TransactionExecutable::Empty => { + if !extra_config.is_multisig() { + return Err(SubmitTransactionError::bad_request_with_code( + "Empty transaction payload must be a multisig transaction", + AptosErrorCode::InvalidInput, + ledger_info, + )); + } + }, + }, } // TODO: Verify script args? @@ -1423,6 +1550,30 @@ impl TransactionsApi { "Multisig::unknown".to_string() } }, + TransactionPayload::V2(TransactionPayloadV2::V1 { + executable, + extra_config, + }) => { + let mut stats_key: String = "V2::".to_string(); + if extra_config.is_multisig() { + stats_key += "Multisig::"; + }; + if extra_config.is_orderless() { + stats_key += "Orderless::"; + } + if let TransactionExecutable::Script(_) = executable { + stats_key += format!("Script::{}", txn.committed_hash()).as_str(); + } else if let TransactionExecutable::EntryFunction(entry_function) = executable { + stats_key += FunctionStats::function_to_key( + entry_function.module(), + &entry_function.function().into(), + ) + .as_str(); + } else if let TransactionExecutable::Empty = executable { + stats_key += "unknown"; + }; + stats_key + }, }; self.context .simulate_txn_stats() diff --git a/api/types/src/convert.rs b/api/types/src/convert.rs index 7a9d699c20e12..31520d75568b5 100644 --- a/api/types/src/convert.rs +++ b/api/types/src/convert.rs @@ -4,18 +4,14 @@ use crate::{ transaction::{ - BlockEpilogueTransaction, BlockMetadataTransaction, DecodedTableData, DeleteModule, - DeleteResource, DeleteTableItem, DeletedTableData, MultisigPayload, - MultisigTransactionPayload, StateCheckpointTransaction, UserTransactionRequestInner, - WriteModule, WriteResource, WriteTableItem, + BlockEpilogueTransaction, BlockMetadataTransaction, DecodedTableData, DeleteModule, DeleteResource, DeleteTableItem, DeletedTableData, Empty, MultisigPayload, MultisigTransactionPayload, StateCheckpointTransaction, TransactionExecutable, TransactionExtraConfig, TransactionPayloadV2, TransactionPayloadV2V1, UserTransactionRequestInner, WriteModule, WriteResource, WriteTableItem }, view::{ViewFunction, ViewRequest}, Address, Bytecode, DirectWriteSet, EntryFunctionId, EntryFunctionPayload, Event, HexEncodedBytes, MoveFunction, MoveModuleBytecode, MoveResource, MoveScriptBytecode, MoveType, MoveValue, PendingTransaction, ResourceGroup, ScriptPayload, ScriptWriteSet, SubmitTransactionRequest, Transaction, TransactionInfo, TransactionOnChainData, - TransactionPayload, UserTransactionRequest, VersionedEvent, WriteSet, WriteSetChange, - WriteSetPayload, + TransactionPayload, VersionedEvent, WriteSet, WriteSetChange, WriteSetPayload, }; use anyhow::{bail, ensure, format_err, Context as AnyhowContext, Result}; use aptos_crypto::{hash::CryptoHash, HashValue}; @@ -279,53 +275,61 @@ impl<'a, S: StateView> MoveConverter<'a, S> { &self, payload: aptos_types::transaction::TransactionPayload, ) -> Result { - use aptos_types::transaction::TransactionPayload::*; - let ret = match payload { - Script(s) => { - let (code, ty_args, args) = s.into_inner(); - let script_args = self.inner.view_script_arguments(&code, &args, &ty_args); + use aptos_types::transaction::{EntryFunction, Script, TransactionPayload::*}; - let json_args = match script_args { - Ok(values) => values - .into_iter() - .map(|v| MoveValue::try_from(v)?.json()) - .collect::>()?, - Err(_e) => convert_txn_args(&args) - .into_iter() - .map(|arg| HexEncodedBytes::from(arg).json()) - .collect::>()?, - }; - TransactionPayload::ScriptPayload(ScriptPayload { - code: MoveScriptBytecode::new(code).try_parse_abi(), - type_arguments: ty_args.into_iter().map(|arg| arg.into()).collect(), - arguments: json_args, - }) - }, - EntryFunction(fun) => { - let (module, function, ty_args, args) = fun.into_inner(); - let func_args = self - .inner - .view_function_arguments(&module, &function, &ty_args, &args); + let try_into_script_payload = |s: Script| -> Result { + let (code, ty_args, args) = s.into_inner(); + let script_args = self.inner.view_script_arguments(&code, &args, &ty_args); - let json_args = match func_args { - Ok(values) => values - .into_iter() - .map(|v| MoveValue::try_from(v)?.json()) - .collect::>()?, - Err(_e) => args - .into_iter() - .map(|arg| HexEncodedBytes::from(arg).json()) - .collect::>()?, - }; + let json_args = match script_args { + Ok(values) => values + .into_iter() + .map(|v| MoveValue::try_from(v)?.json()) + .collect::>()?, + Err(_e) => convert_txn_args(&args) + .into_iter() + .map(|arg| HexEncodedBytes::from(arg).json()) + .collect::>()?, + }; - TransactionPayload::EntryFunctionPayload(EntryFunctionPayload { - arguments: json_args, - function: EntryFunctionId { - module: module.into(), - name: function.into(), - }, - type_arguments: ty_args.into_iter().map(|arg| arg.into()).collect(), - }) + Ok(ScriptPayload { + code: MoveScriptBytecode::new(code).try_parse_abi(), + type_arguments: ty_args.into_iter().map(|arg| arg.into()).collect(), + arguments: json_args, + }) + }; + + let try_into_entry_function_payload = |fun: EntryFunction| -> Result { + let (module, function, ty_args, args) = fun.into_inner(); + let func_args = self + .inner + .view_function_arguments(&module, &function, &ty_args, &args); + + let json_args = match func_args { + Ok(values) => values + .into_iter() + .map(|v| MoveValue::try_from(v)?.json()) + .collect::>()?, + Err(_e) => args + .into_iter() + .map(|arg| HexEncodedBytes::from(arg).json()) + .collect::>()?, + }; + + Ok(EntryFunctionPayload { + arguments: json_args, + function: EntryFunctionId { + module: module.into(), + name: function.into(), + }, + type_arguments: ty_args.into_iter().map(|arg| arg.into()).collect(), + }) + }; + + let ret = match payload { + Script(s) => TransactionPayload::ScriptPayload(try_into_script_payload(s)?), + EntryFunction(fun) => { + TransactionPayload::EntryFunctionPayload(try_into_entry_function_payload(fun)?) }, Multisig(multisig) => { let transaction_payload = if let Some(payload) = multisig.transaction_payload { @@ -333,33 +337,10 @@ impl<'a, S: StateView> MoveConverter<'a, S> { aptos_types::transaction::MultisigTransactionPayload::EntryFunction( entry_function, ) => { - let (module, function, ty_args, args) = entry_function.into_inner(); - let func_args = self - .inner - .view_function_arguments(&module, &function, &ty_args, &args); - let json_args = match func_args { - Ok(values) => values - .into_iter() - .map(|v| MoveValue::try_from(v)?.json()) - .collect::>()?, - Err(_e) => args - .into_iter() - .map(|arg| HexEncodedBytes::from(arg).json()) - .collect::>()?, - }; - + let entry_function_payload = + try_into_entry_function_payload(entry_function)?; Some(MultisigTransactionPayload::EntryFunctionPayload( - EntryFunctionPayload { - arguments: json_args, - function: EntryFunctionId { - module: module.into(), - name: function.into(), - }, - type_arguments: ty_args - .into_iter() - .map(|arg| arg.into()) - .collect(), - }, + entry_function_payload, )) }, } @@ -371,7 +352,40 @@ impl<'a, S: StateView> MoveConverter<'a, S> { transaction_payload, }) }, - + V2(aptos_types::transaction::TransactionPayloadV2::V1 { + executable, + extra_config, + }) => { + let executable = match executable { + aptos_types::transaction::TransactionExecutable::EntryFunction( + entry_function, + ) => TransactionExecutable::EntryFunctionPayload( + try_into_entry_function_payload(entry_function)?, + ), + aptos_types::transaction::TransactionExecutable::Script(script) => { + TransactionExecutable::ScriptPayload(try_into_script_payload(script)?) + }, + aptos_types::transaction::TransactionExecutable::Empty => { + TransactionExecutable::Empty(Empty) + }, + }; + let extra_config = match extra_config { + aptos_types::transaction::TransactionExtraConfig::V1 { + multisig_address, + replay_protection_nonce, + } => TransactionExtraConfig::V1(crate::transaction::TransactionExtraConfigV1 { + multisig_address: multisig_address + .map(|multisig_address| multisig_address.into()), + replay_protection_nonce, + }), + }; + TransactionPayload::V2(crate::transaction::TransactionPayloadV2::V1( + TransactionPayloadV2V1 { + executable, + extra_config, + }, + )) + }, // Deprecated. ModuleBundle(_) => bail!("Module bundle payload has been removed"), }; @@ -592,20 +606,21 @@ impl<'a, S: StateView> MoveConverter<'a, S> { Ok(ret) } - pub fn try_into_signed_transaction( - &self, - txn: UserTransactionRequest, - chain_id: ChainId, - ) -> Result { - let signature = txn - .signature - .clone() - .ok_or_else(|| format_err!("missing signature"))?; - Ok(SignedTransaction::new_signed_transaction( - self.try_into_raw_transaction(txn, chain_id)?, - signature.try_into()?, - )) - } + // (We are not using this function anywhere) + // pub fn try_into_signed_transaction( + // &self, + // txn: UserTransactionRequest, + // chain_id: ChainId, + // ) -> Result { + // let signature = txn + // .signature + // .clone() + // .ok_or_else(|| format_err!("missing signature"))?; + // Ok(SignedTransaction::new_signed_transaction( + // self.try_into_raw_transaction(txn, chain_id)?, + // signature.try_into()?, + // )) + // } pub fn try_into_signed_transaction_poem( &self, @@ -621,30 +636,31 @@ impl<'a, S: StateView> MoveConverter<'a, S> { )) } - pub fn try_into_raw_transaction( - &self, - txn: UserTransactionRequest, - chain_id: ChainId, - ) -> Result { - let UserTransactionRequest { - sender, - sequence_number, - max_gas_amount, - gas_unit_price, - expiration_timestamp_secs, - payload, - signature: _, - } = txn; - Ok(RawTransaction::new( - sender.into(), - sequence_number.into(), - self.try_into_aptos_core_transaction_payload(payload)?, - max_gas_amount.into(), - gas_unit_price.into(), - expiration_timestamp_secs.into(), - chain_id, - )) - } + // (We are not using this function anywhere) + // pub fn try_into_raw_transaction( + // &self, + // txn: UserTransactionRequest, + // chain_id: ChainId, + // ) -> Result { + // let UserTransactionRequest { + // sender, + // sequence_number, + // max_gas_amount, + // gas_unit_price, + // expiration_timestamp_secs, + // payload, + // signature: _, + // } = txn; + // Ok(RawTransaction::new( + // sender.into(), + // sequence_number.into(), + // self.try_into_aptos_core_transaction_payload(payload)?, + // max_gas_amount.into(), + // gas_unit_price.into(), + // expiration_timestamp_secs.into(), + // chain_id, + // )) + // } pub fn try_into_raw_transaction_poem( &self, @@ -675,10 +691,13 @@ impl<'a, S: StateView> MoveConverter<'a, S> { &self, payload: TransactionPayload, ) -> Result { - use aptos_types::transaction::TransactionPayload as Target; + use aptos_types::transaction::{ + TransactionExecutable as Executable, TransactionExtraConfig as ExtraConfig, + TransactionPayload as Target, TransactionPayloadV2 as TargetV2, + }; - let ret = match payload { - TransactionPayload::EntryFunctionPayload(entry_func_payload) => { + let try_into_entry_function = + |entry_func_payload: EntryFunctionPayload| -> Result { let EntryFunctionPayload { function, type_arguments, @@ -704,7 +723,7 @@ impl<'a, S: StateView> MoveConverter<'a, S> { .map(bcs::to_bytes) .collect::>()?; - Target::EntryFunction(EntryFunction::new( + Ok(EntryFunction::new( module.into(), function.name.into(), type_arguments @@ -713,74 +732,49 @@ impl<'a, S: StateView> MoveConverter<'a, S> { .collect::>()?, args, )) + }; + + let try_into_script_payload = |script: ScriptPayload| -> Result