From 9b56f30a60682a7ba46a151d1c751675cf01ca35 Mon Sep 17 00:00:00 2001 From: Andrew Fitzgerald Date: Mon, 4 Nov 2024 12:22:40 -0600 Subject: [PATCH] TransactionBatch - hold RuntimeTransaction (#3041) --- Cargo.lock | 7 ++ banks-server/Cargo.toml | 1 + banks-server/src/banks_server.rs | 3 +- core/benches/consumer.rs | 5 +- core/src/banking_stage.rs | 7 +- core/src/banking_stage/committer.rs | 11 +- core/src/banking_stage/consume_worker.rs | 7 +- core/src/banking_stage/consumer.rs | 22 ++-- .../forward_packet_batches_by_accounts.rs | 26 ++-- core/src/banking_stage/forwarder.rs | 6 +- .../immutable_deserialized_packet.rs | 34 +++-- core/src/banking_stage/qos_service.rs | 43 +++---- core/src/banking_stage/scheduler_messages.rs | 3 +- .../prio_graph_scheduler.rs | 38 +++--- .../scheduler_controller.rs | 9 +- .../transaction_state.rs | 5 +- .../transaction_state_container.rs | 15 +-- .../unprocessed_transaction_storage.rs | 34 ++--- core/tests/unified_scheduler.rs | 5 +- cost-model/Cargo.toml | 3 + cost-model/benches/cost_model.rs | 5 +- cost-model/benches/cost_tracker.rs | 10 +- cost-model/src/cost_model.rs | 116 +++++++++--------- cost-model/src/cost_tracker.rs | 41 ++++--- cost-model/src/transaction_cost.rs | 32 +++-- entry/Cargo.toml | 1 + entry/benches/entry_sigverify.rs | 17 +-- entry/src/entry.rs | 41 +++++-- ledger-tool/Cargo.toml | 1 + ledger-tool/src/main.rs | 5 +- ledger/Cargo.toml | 1 + ledger/benches/blockstore_processor.rs | 5 +- ledger/src/blockstore_processor.rs | 36 +++--- programs/sbf/Cargo.lock | 6 + programs/sbf/Cargo.toml | 4 +- programs/sbf/tests/programs.rs | 9 +- programs/sbf/tests/simulation.rs | 5 +- programs/sbf/tests/syscall_get_epoch_stake.rs | 5 +- programs/sbf/tests/sysvar.rs | 5 +- rpc/Cargo.toml | 4 + rpc/src/rpc.rs | 7 +- runtime-transaction/Cargo.toml | 3 + .../src/compute_budget_instruction_details.rs | 1 + runtime-transaction/src/lib.rs | 1 + .../src/runtime_transaction.rs | 87 +++++++++++-- .../src/svm_transaction_adapter.rs | 25 ++++ runtime-transaction/src/transaction_meta.rs | 1 + runtime/Cargo.toml | 8 +- runtime/benches/prioritization_fee_cache.rs | 5 +- runtime/src/bank.rs | 69 ++++++----- runtime/src/bank/check_transactions.rs | 9 +- runtime/src/bank/tests.rs | 16 +-- runtime/src/bank_utils.rs | 3 +- runtime/src/installed_scheduler_pool.rs | 14 ++- runtime/src/prioritization_fee_cache.rs | 29 +++-- runtime/src/transaction_batch.rs | 19 +-- sdk/program/src/message/sanitized.rs | 2 +- unified-scheduler-logic/Cargo.toml | 6 + unified-scheduler-logic/src/lib.rs | 42 ++++--- unified-scheduler-pool/Cargo.toml | 1 + unified-scheduler-pool/src/lib.rs | 99 ++++++++------- 61 files changed, 663 insertions(+), 417 deletions(-) create mode 100644 runtime-transaction/src/svm_transaction_adapter.rs diff --git a/Cargo.lock b/Cargo.lock index c7a658e078c7fa..69d39465670cf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,6 +195,7 @@ dependencies = [ "solana-program-runtime", "solana-rpc", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-stake-program", "solana-storage-bigtable", @@ -5928,6 +5929,7 @@ dependencies = [ "solana-client", "solana-feature-set", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-send-transaction-service", "solana-svm", @@ -6681,6 +6683,7 @@ dependencies = [ "solana-metrics", "solana-perf", "solana-rayon-threadlimit", + "solana-runtime-transaction", "solana-sdk", ] @@ -7079,6 +7082,7 @@ dependencies = [ "solana-program-runtime", "solana-rayon-threadlimit", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-stake-program", "solana-storage-bigtable", @@ -7805,6 +7809,7 @@ dependencies = [ "solana-rayon-threadlimit", "solana-rpc-client-api", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-send-transaction-service", "solana-stake-program", @@ -8852,6 +8857,7 @@ name = "solana-unified-scheduler-logic" version = "2.2.0" dependencies = [ "assert_matches", + "solana-runtime-transaction", "solana-sdk", "static_assertions", ] @@ -8871,6 +8877,7 @@ dependencies = [ "solana-ledger", "solana-logger", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-timings", "solana-unified-scheduler-logic", diff --git a/banks-server/Cargo.toml b/banks-server/Cargo.toml index a2fe94c781cc02..bb36365c345cc1 100644 --- a/banks-server/Cargo.toml +++ b/banks-server/Cargo.toml @@ -17,6 +17,7 @@ solana-banks-interface = { workspace = true } solana-client = { workspace = true } solana-feature-set = { workspace = true } solana-runtime = { workspace = true } +solana-runtime-transaction = { workspace = true } solana-sdk = { workspace = true } solana-send-transaction-service = { workspace = true } solana-svm = { workspace = true } diff --git a/banks-server/src/banks_server.rs b/banks-server/src/banks_server.rs index f2d2d10da85abb..6744154d5e4398 100644 --- a/banks-server/src/banks_server.rs +++ b/banks-server/src/banks_server.rs @@ -15,6 +15,7 @@ use { commitment::BlockCommitmentCache, verify_precompiles::verify_precompiles, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ account::Account, clock::Slot, @@ -178,7 +179,7 @@ fn simulate_transaction( bank: &Bank, transaction: VersionedTransaction, ) -> BanksTransactionResultWithSimulation { - let sanitized_transaction = match SanitizedTransaction::try_create( + let sanitized_transaction = match RuntimeTransaction::try_create( transaction, MessageHash::Compute, Some(false), // is_simple_vote_tx diff --git a/core/benches/consumer.rs b/core/benches/consumer.rs index ec615016f29a92..3a89cdfd39d0dd 100644 --- a/core/benches/consumer.rs +++ b/core/benches/consumer.rs @@ -20,6 +20,7 @@ use { poh_service::PohService, }, solana_runtime::{bank::Bank, bank_forks::BankForks}, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ account::{Account, ReadableAccount}, signature::Keypair, @@ -66,7 +67,7 @@ fn create_funded_accounts(bank: &Bank, num: usize) -> Vec { accounts } -fn create_transactions(bank: &Bank, num: usize) -> Vec { +fn create_transactions(bank: &Bank, num: usize) -> Vec> { let funded_accounts = create_funded_accounts(bank, 2 * num); funded_accounts .into_par_iter() @@ -76,7 +77,7 @@ fn create_transactions(bank: &Bank, num: usize) -> Vec { let to = &chunk[1]; system_transaction::transfer(from, &to.pubkey(), 1, bank.last_blockhash()) }) - .map(SanitizedTransaction::from_transaction_for_tests) + .map(RuntimeTransaction::from_transaction_for_tests) .collect() } diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index 4e717bcf13e50a..94b8f1aeaa68df 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -841,6 +841,7 @@ mod tests { poh_service::PohService, }, solana_runtime::{bank::Bank, genesis_utils::bootstrap_validator_stake_lamports}, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ hash::Hash, poh_config::PohConfig, @@ -867,9 +868,11 @@ mod tests { (node, cluster_info) } - pub(crate) fn sanitize_transactions(txs: Vec) -> Vec { + pub(crate) fn sanitize_transactions( + txs: Vec, + ) -> Vec> { txs.into_iter() - .map(SanitizedTransaction::from_transaction_for_tests) + .map(RuntimeTransaction::from_transaction_for_tests) .collect() } diff --git a/core/src/banking_stage/committer.rs b/core/src/banking_stage/committer.rs index ed718bbdee95fd..92d830e730a72d 100644 --- a/core/src/banking_stage/committer.rs +++ b/core/src/banking_stage/committer.rs @@ -12,6 +12,7 @@ use { transaction_batch::TransactionBatch, vote_sender_types::ReplayVoteSender, }, + solana_runtime_transaction::svm_transaction_adapter::SVMTransactionAdapter, solana_sdk::{pubkey::Pubkey, saturating_add_assign, transaction::SanitizedTransaction}, solana_svm::{ transaction_commit_result::{TransactionCommitResult, TransactionCommitResultExtensions}, @@ -22,7 +23,7 @@ use { solana_transaction_status::{ token_balances::TransactionTokenBalancesSet, TransactionTokenBalance, }, - std::{collections::HashMap, sync::Arc}, + std::{borrow::Borrow, collections::HashMap, sync::Arc}, }; #[derive(Clone, Debug, PartialEq, Eq)] @@ -134,7 +135,13 @@ impl Committer { starting_transaction_index: Option, ) { if let Some(transaction_status_sender) = &self.transaction_status_sender { - let txs = batch.sanitized_transactions().to_vec(); + // Clone `SanitizedTransaction` out of `RuntimeTransaction`, this is + // done to send over the status sender. + let txs = batch + .sanitized_transactions() + .iter() + .map(|tx| tx.as_sanitized_transaction().borrow().clone()) + .collect_vec(); let post_balances = bank.collect_balances(batch); let post_token_balances = collect_token_balances(bank, batch, &mut pre_balance_info.mint_decimals); diff --git a/core/src/banking_stage/consume_worker.rs b/core/src/banking_stage/consume_worker.rs index 349f5b8a9d8e6d..e1533dfd4b1e98 100644 --- a/core/src/banking_stage/consume_worker.rs +++ b/core/src/banking_stage/consume_worker.rs @@ -733,6 +733,7 @@ mod tests { bank_forks::BankForks, prioritization_fee_cache::PrioritizationFeeCache, vote_sender_types::ReplayVoteReceiver, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ address_lookup_table::AddressLookupTableAccount, clock::{Slot, MAX_PROCESSING_AGE}, @@ -746,9 +747,7 @@ mod tests { signature::Keypair, signer::Signer, system_instruction, system_transaction, - transaction::{ - MessageHash, SanitizedTransaction, TransactionError, VersionedTransaction, - }, + transaction::{MessageHash, TransactionError, VersionedTransaction}, }, solana_svm_transaction::svm_message::SVMMessage, std::{ @@ -1101,7 +1100,7 @@ mod tests { readonly: vec![], }; let loader = SimpleAddressLoader::Enabled(loaded_addresses); - SanitizedTransaction::try_create( + RuntimeTransaction::try_create( VersionedTransaction::try_new( VersionedMessage::V0( v0::Message::try_compile( diff --git a/core/src/banking_stage/consumer.rs b/core/src/banking_stage/consumer.rs index f2ae4dc905841c..1191cab110651f 100644 --- a/core/src/banking_stage/consumer.rs +++ b/core/src/banking_stage/consumer.rs @@ -24,7 +24,10 @@ use { transaction_batch::TransactionBatch, verify_precompiles::verify_precompiles, }, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, + solana_runtime_transaction::{ + instructions_processor::process_compute_budget_instructions, + runtime_transaction::RuntimeTransaction, + }, solana_sdk::{ clock::{FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET, MAX_PROCESSING_AGE}, fee::FeeBudgetLimits, @@ -228,7 +231,7 @@ impl Consumer { &self, bank: &Arc, bank_creation_time: &Instant, - sanitized_transactions: &[SanitizedTransaction], + sanitized_transactions: &[RuntimeTransaction], banking_stage_stats: &BankingStageStats, slot_metrics_tracker: &mut LeaderSlotMetricsTracker, ) -> ProcessTransactionsSummary { @@ -284,7 +287,7 @@ impl Consumer { &self, bank: &Arc, bank_creation_time: &Instant, - transactions: &[SanitizedTransaction], + transactions: &[RuntimeTransaction], ) -> ProcessTransactionsSummary { let mut chunk_start = 0; let mut all_retryable_tx_indexes = vec![]; @@ -386,7 +389,7 @@ impl Consumer { pub fn process_and_record_transactions( &self, bank: &Arc, - txs: &[SanitizedTransaction], + txs: &[RuntimeTransaction], chunk_offset: usize, ) -> ProcessTransactionBatchOutput { let mut error_counters = TransactionErrorMetrics::default(); @@ -429,7 +432,7 @@ impl Consumer { pub fn process_and_record_aged_transactions( &self, bank: &Arc, - txs: &[SanitizedTransaction], + txs: &[RuntimeTransaction], max_ages: &[MaxAge], ) -> ProcessTransactionBatchOutput { let move_precompile_verification_to_svm = bank @@ -473,7 +476,7 @@ impl Consumer { fn process_and_record_transactions_with_pre_results( &self, bank: &Arc, - txs: &[SanitizedTransaction], + txs: &[RuntimeTransaction], chunk_offset: usize, pre_results: impl Iterator>, ) -> ProcessTransactionBatchOutput { @@ -804,7 +807,7 @@ impl Consumer { /// * `pending_indexes` - identifies which indexes in the `transactions` list are still pending fn filter_pending_packets_from_pending_txs( bank: &Bank, - transactions: &[SanitizedTransaction], + transactions: &[RuntimeTransaction], pending_indexes: &[usize], ) -> Vec { let filter = @@ -888,7 +891,7 @@ mod tests { signature::Keypair, signer::Signer, system_instruction, system_program, system_transaction, - transaction::{MessageHash, Transaction, VersionedTransaction}, + transaction::{Transaction, VersionedTransaction}, }, solana_svm::account_loader::CheckedTransactionDetails, solana_timings::ProgramTiming, @@ -903,6 +906,7 @@ mod tests { thread::{Builder, JoinHandle}, time::Duration, }, + transaction::MessageHash, }; fn execute_transactions_with_dummy_poh_service( @@ -2060,7 +2064,7 @@ mod tests { }); let tx = VersionedTransaction::try_new(message, &[&keypair]).unwrap(); - let sanitized_tx = SanitizedTransaction::try_create( + let sanitized_tx = RuntimeTransaction::try_create( tx.clone(), MessageHash::Compute, Some(false), diff --git a/core/src/banking_stage/forward_packet_batches_by_accounts.rs b/core/src/banking_stage/forward_packet_batches_by_accounts.rs index 67b323c2876a18..4e9967ee118a57 100644 --- a/core/src/banking_stage/forward_packet_batches_by_accounts.rs +++ b/core/src/banking_stage/forward_packet_batches_by_accounts.rs @@ -8,7 +8,7 @@ use { }, solana_feature_set::FeatureSet, solana_perf::packet::Packet, - solana_sdk::transaction::SanitizedTransaction, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_svm_transaction::svm_message::SVMMessage, std::sync::Arc, }; @@ -106,7 +106,7 @@ impl ForwardPacketBatchesByAccounts { pub fn try_add_packet( &mut self, - sanitized_transaction: &SanitizedTransaction, + sanitized_transaction: &RuntimeTransaction, immutable_packet: Arc, feature_set: &FeatureSet, ) -> bool { @@ -171,14 +171,15 @@ mod tests { use { super::*, crate::banking_stage::unprocessed_packet_batches::DeserializedPacket, + lazy_static::lazy_static, solana_cost_model::transaction_cost::{UsageCostDetails, WritableKeysTransaction}, solana_feature_set::FeatureSet, solana_sdk::{ compute_budget::ComputeBudgetInstruction, - message::{Message, TransactionSignatureDetails}, + message::Message, pubkey::Pubkey, system_instruction, - transaction::Transaction, + transaction::{SanitizedTransaction, Transaction}, }, }; @@ -187,7 +188,11 @@ mod tests { fn build_test_transaction_and_packet( priority: u64, write_to_account: &Pubkey, - ) -> (SanitizedTransaction, DeserializedPacket, u32) { + ) -> ( + RuntimeTransaction, + DeserializedPacket, + u32, + ) { let from_account = solana_sdk::pubkey::new_rand(); let transaction = Transaction::new_unsigned(Message::new( @@ -198,7 +203,7 @@ mod tests { Some(&from_account), )); let sanitized_transaction = - SanitizedTransaction::from_transaction_for_tests(transaction.clone()); + RuntimeTransaction::from_transaction_for_tests(transaction.clone()); let tx_cost = CostModel::calculate_cost(&sanitized_transaction, &FeatureSet::all_enabled()); let cost = tx_cost.sum(); let deserialized_packet = @@ -211,7 +216,10 @@ mod tests { } fn zero_transaction_cost() -> TransactionCost<'static, WritableKeysTransaction> { - static DUMMY_TRANSACTION: WritableKeysTransaction = WritableKeysTransaction(vec![]); + lazy_static! { + static ref DUMMY_TRANSACTION: RuntimeTransaction = + RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![])); + }; TransactionCost::Transaction(UsageCostDetails { transaction: &DUMMY_TRANSACTION, @@ -221,7 +229,6 @@ mod tests { programs_execution_cost: 0, loaded_accounts_data_size_cost: 0, allocated_accounts_data_size: 0, - signature_details: TransactionSignatureDetails::new(0, 0, 0), }) } @@ -370,7 +377,8 @@ mod tests { ForwardPacketBatchesByAccounts::new_with_default_batch_limits(); forward_packet_batches_by_accounts.batch_vote_limit = test_cost + 1; - let dummy_transaction = WritableKeysTransaction(vec![]); + let dummy_transaction = + RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![])); let transaction_cost = TransactionCost::SimpleVote { transaction: &dummy_transaction, }; diff --git a/core/src/banking_stage/forwarder.rs b/core/src/banking_stage/forwarder.rs index d48c1556fa206e..a6933893cac4c0 100644 --- a/core/src/banking_stage/forwarder.rs +++ b/core/src/banking_stage/forwarder.rs @@ -19,8 +19,10 @@ use { solana_perf::{data_budget::DataBudget, packet::Packet}, solana_poh::poh_recorder::PohRecorder, solana_runtime::bank_forks::BankForks, - solana_sdk::{pubkey::Pubkey, transaction::SanitizedTransaction, transport::TransportError}, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, + solana_sdk::{pubkey::Pubkey, transport::TransportError}, solana_streamer::sendmmsg::batch_send, + solana_svm_transaction::svm_message::SVMMessage, std::{ iter::repeat, net::{SocketAddr, UdpSocket}, @@ -64,7 +66,7 @@ impl Forwarder { pub fn try_add_packet( &mut self, - sanitized_transaction: &SanitizedTransaction, + sanitized_transaction: &RuntimeTransaction, immutable_packet: Arc, feature_set: &FeatureSet, ) -> bool { diff --git a/core/src/banking_stage/immutable_deserialized_packet.rs b/core/src/banking_stage/immutable_deserialized_packet.rs index 978e4f9b935c7e..afd5d34dc4fd16 100644 --- a/core/src/banking_stage/immutable_deserialized_packet.rs +++ b/core/src/banking_stage/immutable_deserialized_packet.rs @@ -3,7 +3,10 @@ use { solana_compute_budget::compute_budget_limits::ComputeBudgetLimits, solana_perf::packet::Packet, solana_runtime::bank::Bank, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, + solana_runtime_transaction::{ + instructions_processor::process_compute_budget_instructions, + runtime_transaction::RuntimeTransaction, + }, solana_sanitize::SanitizeError, solana_sdk::{ clock::Slot, @@ -11,7 +14,9 @@ use { message::{v0::LoadedAddresses, AddressLoaderError, Message, SimpleAddressLoader}, pubkey::Pubkey, signature::Signature, - transaction::{SanitizedTransaction, SanitizedVersionedTransaction, VersionedTransaction}, + transaction::{ + MessageHash, SanitizedTransaction, SanitizedVersionedTransaction, VersionedTransaction, + }, }, solana_short_vec::decode_shortu16_len, solana_svm_transaction::{ @@ -40,7 +45,7 @@ pub enum DeserializedPacketError { FailedFilter(#[from] PacketFilterFailure), } -#[derive(Debug, Eq)] +#[derive(Debug)] pub struct ImmutableDeserializedPacket { original_packet: Packet, transaction: SanitizedVersionedTransaction, @@ -118,7 +123,7 @@ impl ImmutableDeserializedPacket { votes_only: bool, bank: &Bank, reserved_account_keys: &HashSet, - ) -> Option<(SanitizedTransaction, Slot)> { + ) -> Option<(RuntimeTransaction, Slot)> { if votes_only && !self.is_simple_vote() { return None; } @@ -127,14 +132,18 @@ impl ImmutableDeserializedPacket { let (loaded_addresses, deactivation_slot) = Self::resolve_addresses_with_deactivation(self.transaction(), bank).ok()?; let address_loader = SimpleAddressLoader::Enabled(loaded_addresses); - - let tx = SanitizedTransaction::try_new( - self.transaction().clone(), - *self.message_hash(), - self.is_simple_vote(), - address_loader, - reserved_account_keys, + let tx = RuntimeTransaction::::try_from( + self.transaction.clone(), + MessageHash::Precomputed(self.message_hash), + Some(self.is_simple_vote), ) + .and_then(|tx| { + RuntimeTransaction::::try_from( + tx, + address_loader, + reserved_account_keys, + ) + }) .ok()?; Some((tx, deactivation_slot)) } @@ -156,7 +165,8 @@ impl ImmutableDeserializedPacket { } } -// PartialEq MUST be consistent with PartialOrd and Ord +// Eq and PartialEq MUST be consistent with PartialOrd and Ord +impl Eq for ImmutableDeserializedPacket {} impl PartialEq for ImmutableDeserializedPacket { fn eq(&self, other: &Self) -> bool { self.compute_unit_price() == other.compute_unit_price() diff --git a/core/src/banking_stage/qos_service.rs b/core/src/banking_stage/qos_service.rs index 0d9b2f02a32ee9..e0ce523bbfdb37 100644 --- a/core/src/banking_stage/qos_service.rs +++ b/core/src/banking_stage/qos_service.rs @@ -11,6 +11,7 @@ use { solana_feature_set::FeatureSet, solana_measure::measure::Measure, solana_runtime::bank::Bank, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ clock::Slot, saturating_add_assign, @@ -43,7 +44,7 @@ impl QosService { pub fn select_and_accumulate_transaction_costs<'a>( &self, bank: &Bank, - transactions: &'a [SanitizedTransaction], + transactions: &'a [RuntimeTransaction], pre_results: impl Iterator>, ) -> ( Vec>>, @@ -73,7 +74,7 @@ impl QosService { fn compute_transaction_costs<'a>( &self, feature_set: &FeatureSet, - transactions: impl Iterator, + transactions: impl Iterator>, pre_results: impl Iterator>, ) -> Vec>> { let mut compute_cost_time = Measure::start("compute_cost_time"); @@ -98,7 +99,7 @@ impl QosService { /// and a count of the number of transactions that would fit in the block fn select_transactions_per_cost<'a>( &self, - transactions: impl Iterator, + transactions: impl Iterator>, transactions_costs: impl Iterator< Item = transaction::Result>, >, @@ -628,7 +629,6 @@ mod tests { solana_runtime::genesis_utils::{create_genesis_config, GenesisConfigInfo}, solana_sdk::{ hash::Hash, - message::TransactionSignatureDetails, signature::{Keypair, Signer}, system_transaction, }, @@ -642,10 +642,10 @@ mod tests { // make a vec of txs let keypair = Keypair::new(); - let transfer_tx = SanitizedTransaction::from_transaction_for_tests( + let transfer_tx = RuntimeTransaction::from_transaction_for_tests( system_transaction::transfer(&keypair, &keypair.pubkey(), 1, Hash::default()), ); - let vote_tx = SanitizedTransaction::from_transaction_for_tests( + let vote_tx = RuntimeTransaction::from_transaction_for_tests( vote_transaction::new_tower_sync_transaction( TowerSync::from(vec![(42, 1)]), Hash::default(), @@ -685,10 +685,10 @@ mod tests { let bank = Arc::new(Bank::new_for_tests(&genesis_config)); let keypair = Keypair::new(); - let transfer_tx = SanitizedTransaction::from_transaction_for_tests( + let transfer_tx = RuntimeTransaction::from_transaction_for_tests( system_transaction::transfer(&keypair, &keypair.pubkey(), 1, Hash::default()), ); - let vote_tx = SanitizedTransaction::from_transaction_for_tests( + let vote_tx = RuntimeTransaction::from_transaction_for_tests( vote_transaction::new_tower_sync_transaction( TowerSync::from(vec![(42, 1)]), Hash::default(), @@ -701,7 +701,6 @@ mod tests { let transfer_tx_cost = CostModel::calculate_cost(&transfer_tx, &FeatureSet::all_enabled()).sum(); let vote_tx_cost = CostModel::calculate_cost(&vote_tx, &FeatureSet::all_enabled()).sum(); - // make a vec of txs let txs = vec![transfer_tx.clone(), vote_tx.clone(), transfer_tx, vote_tx]; @@ -747,8 +746,8 @@ mod tests { ], Some(&keypair.pubkey()), )); - let transfer_tx = SanitizedTransaction::from_transaction_for_tests(transaction.clone()); - let txs: Vec = (0..transaction_count) + let transfer_tx = RuntimeTransaction::from_transaction_for_tests(transaction.clone()); + let txs: Vec<_> = (0..transaction_count) .map(|_| transfer_tx.clone()) .collect(); let execute_units_adjustment: u64 = 10; @@ -817,11 +816,15 @@ mod tests { // calculate their costs, apply to cost_tracker let transaction_count = 5; let keypair = Keypair::new(); - let transfer_tx = SanitizedTransaction::from_transaction_for_tests( - system_transaction::transfer(&keypair, &keypair.pubkey(), 1, Hash::default()), - ); - let txs: Vec = (0..transaction_count) - .map(|_| transfer_tx.clone()) + let txs: Vec<_> = (0..transaction_count) + .map(|_| { + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( + &keypair, + &keypair.pubkey(), + 1, + Hash::default(), + )) + }) .collect(); // assert all tx_costs should be removed from cost_tracker if all execution_results are all Not Committed @@ -867,9 +870,8 @@ mod tests { ], Some(&keypair.pubkey()), )); - let transfer_tx = SanitizedTransaction::from_transaction_for_tests(transaction.clone()); - let txs: Vec = (0..transaction_count) - .map(|_| transfer_tx.clone()) + let txs: Vec<_> = (0..transaction_count) + .map(|_| RuntimeTransaction::from_transaction_for_tests(transaction.clone())) .collect(); let execute_units_adjustment: u64 = 10; let loaded_accounts_data_size_adjustment: u32 = 32000; @@ -951,7 +953,7 @@ mod tests { let programs_execution_cost = 10; let num_txs = 4; - let dummy_transaction = WritableKeysTransaction(vec![]); + let dummy_transaction = RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![])); let tx_cost_results: Vec<_> = (0..num_txs) .map(|n| { if n % 2 == 0 { @@ -963,7 +965,6 @@ mod tests { programs_execution_cost, loaded_accounts_data_size_cost: 0, allocated_accounts_data_size: 0, - signature_details: TransactionSignatureDetails::new(0, 0, 0), })) } else { Err(TransactionError::WouldExceedMaxBlockCostLimit) diff --git a/core/src/banking_stage/scheduler_messages.rs b/core/src/banking_stage/scheduler_messages.rs index 29e9b99f50588a..0340db477975af 100644 --- a/core/src/banking_stage/scheduler_messages.rs +++ b/core/src/banking_stage/scheduler_messages.rs @@ -1,4 +1,5 @@ use { + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{clock::Slot, transaction::SanitizedTransaction}, std::fmt::Display, }; @@ -46,7 +47,7 @@ pub struct MaxAge { pub struct ConsumeWork { pub batch_id: TransactionBatchId, pub ids: Vec, - pub transactions: Vec, + pub transactions: Vec>, pub max_ages: Vec, } diff --git a/core/src/banking_stage/transaction_scheduler/prio_graph_scheduler.rs b/core/src/banking_stage/transaction_scheduler/prio_graph_scheduler.rs index 2687c04d271991..47819b298a4ea9 100644 --- a/core/src/banking_stage/transaction_scheduler/prio_graph_scheduler.rs +++ b/core/src/banking_stage/transaction_scheduler/prio_graph_scheduler.rs @@ -21,6 +21,7 @@ use { prio_graph::{AccessKind, GraphNode, PrioGraph}, solana_cost_model::block_cost_limits::MAX_BLOCK_UNITS, solana_measure::measure_us, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{pubkey::Pubkey, saturating_add_assign, transaction::SanitizedTransaction}, }; @@ -83,8 +84,8 @@ impl PrioGraphScheduler { pub(crate) fn schedule( &mut self, container: &mut TransactionStateContainer, - pre_graph_filter: impl Fn(&[&SanitizedTransaction], &mut [bool]), - pre_lock_filter: impl Fn(&SanitizedTransaction) -> bool, + pre_graph_filter: impl Fn(&[&RuntimeTransaction], &mut [bool]), + pre_lock_filter: impl Fn(&RuntimeTransaction) -> bool, ) -> Result { let num_threads = self.consume_work_senders.len(); let max_cu_per_thread = MAX_BLOCK_UNITS / num_threads as u64; @@ -373,7 +374,7 @@ impl PrioGraphScheduler { fn complete_batch( &mut self, batch_id: TransactionBatchId, - transactions: &[SanitizedTransaction], + transactions: &[RuntimeTransaction], ) { let thread_id = self.in_flight_tracker.complete_batch(batch_id); for transaction in transactions { @@ -444,7 +445,7 @@ impl PrioGraphScheduler { thread_set: ThreadSet, batch_cus_per_thread: &[u64], in_flight_cus_per_thread: &[u64], - batches_per_thread: &[Vec], + batches_per_thread: &[Vec>], in_flight_per_thread: &[usize], ) -> ThreadId { thread_set @@ -495,7 +496,7 @@ pub(crate) struct SchedulingSummary { struct Batches { ids: Vec>, - transactions: Vec>, + transactions: Vec>>, max_ages: Vec>, total_cus: Vec, } @@ -504,7 +505,10 @@ impl Batches { fn new(num_threads: usize) -> Self { Self { ids: vec![Vec::with_capacity(TARGET_NUM_TRANSACTIONS_PER_BATCH); num_threads], - transactions: vec![Vec::with_capacity(TARGET_NUM_TRANSACTIONS_PER_BATCH); num_threads], + + transactions: (0..num_threads) + .map(|_| Vec::with_capacity(TARGET_NUM_TRANSACTIONS_PER_BATCH)) + .collect(), max_ages: vec![Vec::with_capacity(TARGET_NUM_TRANSACTIONS_PER_BATCH); num_threads], total_cus: vec![0; num_threads], } @@ -515,7 +519,7 @@ impl Batches { thread_id: ThreadId, ) -> ( Vec, - Vec, + Vec>, Vec, u64, ) { @@ -540,7 +544,7 @@ impl Batches { /// A transaction has been scheduled to a thread. struct TransactionSchedulingInfo { thread_id: ThreadId, - transaction: SanitizedTransaction, + transaction: RuntimeTransaction, max_age: MaxAge, cost: u64, } @@ -556,7 +560,7 @@ enum TransactionSchedulingError { fn try_schedule_transaction( transaction_state: &mut TransactionState, - pre_lock_filter: impl Fn(&SanitizedTransaction) -> bool, + pre_lock_filter: impl Fn(&RuntimeTransaction) -> bool, blocking_locks: &mut ReadWriteAccountSet, account_locks: &mut ThreadAwareAccountLocks, num_threads: usize, @@ -661,7 +665,7 @@ mod tests { to_pubkeys: impl IntoIterator>, lamports: u64, priority: u64, - ) -> SanitizedTransaction { + ) -> RuntimeTransaction { let to_pubkeys_lamports = to_pubkeys .into_iter() .map(|pubkey| *pubkey.borrow()) @@ -673,7 +677,7 @@ mod tests { ixs.push(prioritization); let message = Message::new(&ixs, Some(&from_keypair.pubkey())); let tx = Transaction::new(&[from_keypair], message, Hash::default()); - SanitizedTransaction::from_transaction_for_tests(tx) + RuntimeTransaction::from_transaction_for_tests(tx) } fn create_container( @@ -735,11 +739,14 @@ mod tests { .unzip() } - fn test_pre_graph_filter(_txs: &[&SanitizedTransaction], results: &mut [bool]) { + fn test_pre_graph_filter( + _txs: &[&RuntimeTransaction], + results: &mut [bool], + ) { results.fill(true); } - fn test_pre_lock_filter(_tx: &SanitizedTransaction) -> bool { + fn test_pre_lock_filter(_tx: &RuntimeTransaction) -> bool { true } @@ -914,8 +921,9 @@ mod tests { ]); // 2nd transaction should be filtered out and dropped before locking. - let pre_lock_filter = - |tx: &SanitizedTransaction| tx.message().fee_payer() != &keypair.pubkey(); + let pre_lock_filter = |tx: &RuntimeTransaction| { + tx.message().fee_payer() != &keypair.pubkey() + }; let scheduling_summary = scheduler .schedule(&mut container, test_pre_graph_filter, pre_lock_filter) .unwrap(); diff --git a/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs b/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs index 0ca1ee562dc535..b2d67b628a13f0 100644 --- a/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs +++ b/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs @@ -28,7 +28,10 @@ use { solana_cost_model::cost_model::CostModel, solana_measure::measure_us, solana_runtime::{bank::Bank, bank_forks::BankForks}, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, + solana_runtime_transaction::{ + instructions_processor::process_compute_budget_instructions, + runtime_transaction::RuntimeTransaction, + }, solana_sdk::{ self, address_lookup_table::state::estimate_last_valid_slot, @@ -223,7 +226,7 @@ impl SchedulerController { } fn pre_graph_filter( - transactions: &[&SanitizedTransaction], + transactions: &[&RuntimeTransaction], results: &mut [bool], bank: &Bank, max_age: usize, @@ -661,7 +664,7 @@ impl SchedulerController { /// Any difference in the prioritization is negligible for /// the current transaction costs. fn calculate_priority_and_cost( - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, fee_budget_limits: &FeeBudgetLimits, bank: &Bank, ) -> (u64, u64) { diff --git a/core/src/banking_stage/transaction_scheduler/transaction_state.rs b/core/src/banking_stage/transaction_scheduler/transaction_state.rs index efb59be1b8b5b5..0ab329531dcfa3 100644 --- a/core/src/banking_stage/transaction_scheduler/transaction_state.rs +++ b/core/src/banking_stage/transaction_scheduler/transaction_state.rs @@ -2,13 +2,14 @@ use { crate::banking_stage::{ immutable_deserialized_packet::ImmutableDeserializedPacket, scheduler_messages::MaxAge, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::transaction::SanitizedTransaction, std::sync::Arc, }; /// Simple wrapper type to tie a sanitized transaction to max age slot. pub(crate) struct SanitizedTransactionTTL { - pub(crate) transaction: SanitizedTransaction, + pub(crate) transaction: RuntimeTransaction, pub(crate) max_age: MaxAge, } @@ -232,7 +233,7 @@ mod tests { ImmutableDeserializedPacket::new(Packet::from_data(None, tx.clone()).unwrap()).unwrap(), ); let transaction_ttl = SanitizedTransactionTTL { - transaction: SanitizedTransaction::from_transaction_for_tests(tx), + transaction: RuntimeTransaction::from_transaction_for_tests(tx), max_age: MaxAge { epoch_invalidation_slot: Slot::MAX, alt_invalidation_slot: Slot::MAX, diff --git a/core/src/banking_stage/transaction_scheduler/transaction_state_container.rs b/core/src/banking_stage/transaction_scheduler/transaction_state_container.rs index 7d40c66ec1b673..156099b749726e 100644 --- a/core/src/banking_stage/transaction_scheduler/transaction_state_container.rs +++ b/core/src/banking_stage/transaction_scheduler/transaction_state_container.rs @@ -154,16 +154,11 @@ mod tests { use { super::*, crate::banking_stage::scheduler_messages::MaxAge, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ - compute_budget::ComputeBudgetInstruction, - hash::Hash, - message::Message, - packet::Packet, - signature::Keypair, - signer::Signer, - slot_history::Slot, - system_instruction, - transaction::{SanitizedTransaction, Transaction}, + compute_budget::ComputeBudgetInstruction, hash::Hash, message::Message, packet::Packet, + signature::Keypair, signer::Signer, slot_history::Slot, system_instruction, + transaction::Transaction, }, }; @@ -186,7 +181,7 @@ mod tests { ComputeBudgetInstruction::set_compute_unit_price(priority), ]; let message = Message::new(&ixs, Some(&from_keypair.pubkey())); - let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new( + let tx = RuntimeTransaction::from_transaction_for_tests(Transaction::new( &[&from_keypair], message, Hash::default(), diff --git a/core/src/banking_stage/unprocessed_transaction_storage.rs b/core/src/banking_stage/unprocessed_transaction_storage.rs index 56e814acea9219..e86780002ea694 100644 --- a/core/src/banking_stage/unprocessed_transaction_storage.rs +++ b/core/src/banking_stage/unprocessed_transaction_storage.rs @@ -21,6 +21,7 @@ use { solana_feature_set::FeatureSet, solana_measure::measure_us, solana_runtime::bank::Bank, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ clock::FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET, hash::Hash, saturating_add_assign, transaction::SanitizedTransaction, @@ -137,7 +138,7 @@ fn filter_processed_packets<'a, F>( pub struct ConsumeScannerPayload<'a> { pub reached_end_of_slot: bool, pub account_locks: ReadWriteAccountSet, - pub sanitized_transactions: Vec, + pub sanitized_transactions: Vec>, pub slot_metrics_tracker: &'a mut LeaderSlotMetricsTracker, pub message_hash_to_transaction: &'a mut HashMap, pub error_counters: TransactionErrorMetrics, @@ -788,22 +789,21 @@ impl ThreadLocalUnprocessedPackets { packets_to_process: &[Arc], bank: &Bank, total_dropped_packets: &mut usize, - ) -> (Vec, Vec) { + ) -> (Vec>, Vec) { // Get ref of ImmutableDeserializedPacket let deserialized_packets = packets_to_process.iter().map(|p| &**p); - let (transactions, transaction_to_packet_indexes): (Vec, Vec) = - deserialized_packets - .enumerate() - .filter_map(|(packet_index, deserialized_packet)| { - deserialized_packet - .build_sanitized_transaction( - bank.vote_only_bank(), - bank, - bank.get_reserved_account_keys(), - ) - .map(|(transaction, _deactivation_slot)| (transaction, packet_index)) - }) - .unzip(); + let (transactions, transaction_to_packet_indexes): (Vec<_>, Vec<_>) = deserialized_packets + .enumerate() + .filter_map(|(packet_index, deserialized_packet)| { + deserialized_packet + .build_sanitized_transaction( + bank.vote_only_bank(), + bank, + bank.get_reserved_account_keys(), + ) + .map(|(transaction, _deactivation_slot)| (transaction, packet_index)) + }) + .unzip(); let filtered_count = packets_to_process.len().saturating_sub(transactions.len()); saturating_add_assign!(*total_dropped_packets, filtered_count); @@ -813,7 +813,7 @@ impl ThreadLocalUnprocessedPackets { /// Checks sanitized transactions against bank, returns valid transaction indexes fn filter_invalid_transactions( - transactions: &[SanitizedTransaction], + transactions: &[RuntimeTransaction], bank: &Bank, total_dropped_packets: &mut usize, ) -> Vec { @@ -849,7 +849,7 @@ impl ThreadLocalUnprocessedPackets { fn add_filtered_packets_to_forward_buffer( forward_buffer: &mut ForwardPacketBatchesByAccounts, packets_to_process: &[Arc], - transactions: &[SanitizedTransaction], + transactions: &[RuntimeTransaction], transaction_to_packet_indexes: &[usize], forwardable_transaction_indexes: &[usize], total_dropped_packets: &mut usize, diff --git a/core/tests/unified_scheduler.rs b/core/tests/unified_scheduler.rs index a458e776fbc42c..75795f2f6c01ee 100644 --- a/core/tests/unified_scheduler.rs +++ b/core/tests/unified_scheduler.rs @@ -19,6 +19,7 @@ use { accounts_background_service::AbsRequestSender, bank::Bank, bank_forks::BankForks, genesis_utils::GenesisConfigInfo, prioritization_fee_cache::PrioritizationFeeCache, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ hash::Hash, pubkey::Pubkey, @@ -48,7 +49,7 @@ fn test_scheduler_waited_by_drop_bank_service() { result: &mut Result<()>, timings: &mut ExecuteTimings, bank: &Arc, - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, index: usize, handler_context: &HandlerContext, ) { @@ -97,7 +98,7 @@ fn test_scheduler_waited_by_drop_bank_service() { let root_hash = root_bank.hash(); bank_forks.write().unwrap().insert(root_bank); - let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, diff --git a/cost-model/Cargo.toml b/cost-model/Cargo.toml index 56948b20462a27..09bed5dcdd755f 100644 --- a/cost-model/Cargo.toml +++ b/cost-model/Cargo.toml @@ -38,6 +38,9 @@ rand = "0.8.5" # See order-crates-for-publishing.py for using this unusual `path = "."` solana-cost-model = { path = ".", features = ["dev-context-only-utils"] } solana-logger = { workspace = true } +solana-runtime-transaction = { workspace = true, features = [ + "dev-context-only-utils", +] } solana-sdk = { workspace = true, features = ["dev-context-only-utils"] } solana-system-program = { workspace = true } static_assertions = { workspace = true } diff --git a/cost-model/benches/cost_model.rs b/cost-model/benches/cost_model.rs index c92ddd7f9b0b1a..50882835f3e6f0 100644 --- a/cost-model/benches/cost_model.rs +++ b/cost-model/benches/cost_model.rs @@ -2,6 +2,7 @@ extern crate test; use { solana_cost_model::cost_model::CostModel, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ feature_set::FeatureSet, hash::Hash, @@ -16,7 +17,7 @@ use { }; struct BenchSetup { - transactions: Vec, + transactions: Vec>, feature_set: FeatureSet, } @@ -32,7 +33,7 @@ fn setup(num_transactions: usize) -> BenchSetup { let ixs = system_instruction::transfer_many(&from_keypair.pubkey(), &to_lamports); let message = Message::new(&ixs, Some(&from_keypair.pubkey())); let transaction = Transaction::new(&[from_keypair], message, Hash::default()); - SanitizedTransaction::from_transaction_for_tests(transaction) + RuntimeTransaction::from_transaction_for_tests(transaction) }) .collect(); diff --git a/cost-model/benches/cost_tracker.rs b/cost-model/benches/cost_tracker.rs index a7b8b107d09a8d..9119a7937da201 100644 --- a/cost-model/benches/cost_tracker.rs +++ b/cost-model/benches/cost_tracker.rs @@ -6,13 +6,14 @@ use { cost_tracker::CostTracker, transaction_cost::{TransactionCost, UsageCostDetails, WritableKeysTransaction}, }, - solana_sdk::{message::TransactionSignatureDetails, pubkey::Pubkey}, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, + solana_sdk::pubkey::Pubkey, test::Bencher, }; struct BenchSetup { cost_tracker: CostTracker, - transactions: Vec, + transactions: Vec>, } fn setup(num_transactions: usize, contentious_transactions: bool) -> BenchSetup { @@ -33,7 +34,7 @@ fn setup(num_transactions: usize, contentious_transactions: bool) -> BenchSetup }; writable_accounts.push(writable_account_key) }); - WritableKeysTransaction(writable_accounts) + RuntimeTransaction::new_for_tests(WritableKeysTransaction(writable_accounts)) }) .collect_vec(); @@ -44,7 +45,7 @@ fn setup(num_transactions: usize, contentious_transactions: bool) -> BenchSetup } fn get_costs( - transactions: &[WritableKeysTransaction], + transactions: &[RuntimeTransaction], ) -> Vec> { transactions .iter() @@ -57,7 +58,6 @@ fn get_costs( programs_execution_cost: 9999, loaded_accounts_data_size_cost: 0, allocated_accounts_data_size: 0, - signature_details: TransactionSignatureDetails::new(0, 0, 0), }) }) .collect_vec() diff --git a/cost-model/src/cost_model.rs b/cost-model/src/cost_model.rs index 6fb9eee2d86158..3dbfb02f2b5697 100644 --- a/cost-model/src/cost_model.rs +++ b/cost-model/src/cost_model.rs @@ -12,13 +12,13 @@ use { DEFAULT_HEAP_COST, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT, }, solana_feature_set::{self as feature_set, FeatureSet}, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, + solana_runtime_transaction::{ + runtime_transaction::RuntimeTransaction, transaction_meta::StaticMeta, + }, solana_sdk::{ borsh1::try_from_slice_unchecked, compute_budget::{self, ComputeBudgetInstruction}, fee::FeeStructure, - instruction::CompiledInstruction, - message::TransactionSignatureDetails, program_utils::limited_deserialize, pubkey::Pubkey, saturating_add_assign, @@ -27,9 +27,8 @@ use { MAX_PERMITTED_DATA_LENGTH, }, system_program, - transaction::SanitizedTransaction, }, - solana_svm_transaction::svm_message::SVMMessage, + solana_svm_transaction::{instruction::SVMInstruction, svm_message::SVMMessage}, }; pub struct CostModel; @@ -42,15 +41,14 @@ enum SystemProgramAccountAllocation { } impl CostModel { - pub fn calculate_cost<'a>( - transaction: &'a SanitizedTransaction, + pub fn calculate_cost<'a, Tx: SVMMessage>( + transaction: &'a RuntimeTransaction, feature_set: &FeatureSet, - ) -> TransactionCost<'a, SanitizedTransaction> { + ) -> TransactionCost<'a, Tx> { if transaction.is_simple_vote_transaction() { TransactionCost::SimpleVote { transaction } } else { - let (signatures_count_detail, signature_cost) = - Self::get_signature_cost(transaction, feature_set); + let signature_cost = Self::get_signature_cost(transaction, feature_set); let write_lock_cost = Self::get_write_lock_cost(transaction, feature_set); let (programs_execution_cost, loaded_accounts_data_size_cost, data_bytes_cost) = Self::get_transaction_cost(transaction, feature_set); @@ -65,7 +63,6 @@ impl CostModel { programs_execution_cost, loaded_accounts_data_size_cost, allocated_accounts_data_size, - signature_details: signatures_count_detail, }; TransactionCost::Transaction(usage_cost_details) @@ -74,17 +71,16 @@ impl CostModel { // Calculate executed transaction CU cost, with actual execution and loaded accounts size // costs. - pub fn calculate_cost_for_executed_transaction<'a>( - transaction: &'a SanitizedTransaction, + pub fn calculate_cost_for_executed_transaction<'a, Tx: SVMMessage>( + transaction: &'a RuntimeTransaction, actual_programs_execution_cost: u64, actual_loaded_accounts_data_size_bytes: u32, feature_set: &FeatureSet, - ) -> TransactionCost<'a, SanitizedTransaction> { + ) -> TransactionCost<'a, Tx> { if transaction.is_simple_vote_transaction() { TransactionCost::SimpleVote { transaction } } else { - let (signatures_count_detail, signature_cost) = - Self::get_signature_cost(transaction, feature_set); + let signature_cost = Self::get_signature_cost(transaction, feature_set); let write_lock_cost = Self::get_write_lock_cost(transaction, feature_set); let instructions_data_cost = Self::get_instructions_data_cost(transaction); @@ -105,7 +101,6 @@ impl CostModel { programs_execution_cost, loaded_accounts_data_size_cost, allocated_accounts_data_size, - signature_details: signatures_count_detail, }; TransactionCost::Transaction(usage_cost_details) @@ -114,10 +109,10 @@ impl CostModel { /// Returns signature details and the total signature cost fn get_signature_cost( - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, feature_set: &FeatureSet, - ) -> (TransactionSignatureDetails, u64) { - let signatures_count_detail = transaction.message().get_signature_details(); + ) -> u64 { + let signatures_count_detail = transaction.signature_details(); let ed25519_verify_cost = if feature_set.is_active(&feature_set::ed25519_precompile_verify_strict::id()) { @@ -126,7 +121,7 @@ impl CostModel { ED25519_VERIFY_COST }; - let signature_cost = signatures_count_detail + signatures_count_detail .num_transaction_signatures() .saturating_mul(SIGNATURE_COST) .saturating_add( @@ -138,9 +133,7 @@ impl CostModel { signatures_count_detail .num_ed25519_instruction_signatures() .saturating_mul(ed25519_verify_cost), - ); - - (signatures_count_detail, signature_cost) + ) } fn get_writable_accounts(message: &impl SVMMessage) -> impl Iterator { @@ -164,7 +157,7 @@ impl CostModel { /// Return (programs_execution_cost, loaded_accounts_data_size_cost, data_bytes_cost) fn get_transaction_cost( - transaction: &impl SVMMessage, + transaction: &RuntimeTransaction, feature_set: &FeatureSet, ) -> (u64, u64, u64) { let mut programs_execution_costs = 0u64; @@ -198,15 +191,20 @@ impl CostModel { } } - // if failed to process compute_budget instructions, the transaction will not be executed - // by `bank`, therefore it should be considered as no execution cost by cost model. - match process_compute_budget_instructions(transaction.program_instructions_iter()) { + // if failed to process compute budget instructions, the transaction + // will not be executed by `bank`, therefore it should be considered + // as no execution cost by cost model. + match transaction.compute_budget_limits(feature_set) { Ok(compute_budget_limits) => { - // if tx contained user-space instructions and a more accurate estimate available correct it, - // where "user-space instructions" must be specifically checked by - // 'compute_unit_limit_is_set' flag, because compute_budget does not distinguish - // builtin and bpf instructions when calculating default compute-unit-limit. (see - // compute_budget.rs test `test_process_mixed_instructions_without_compute_budget`) + // if tx contained user-space instructions and a more accurate + // estimate available correct it, where + // "user-space instructions" must be specifically checked by + // 'compute_unit_limit_is_set' flag, because compute_budget + // does not distinguish builtin and bpf instructions when + // calculating default compute-unit-limit. + // + // (see compute_budget.rs test + // `test_process_mixed_instructions_without_compute_budget`) if has_user_space_instructions && compute_unit_limit_is_set { programs_execution_costs = u64::from(compute_budget_limits.compute_unit_limit); } @@ -229,11 +227,9 @@ impl CostModel { } /// Return the instruction data bytes cost. - fn get_instructions_data_cost(transaction: &SanitizedTransaction) -> u64 { + fn get_instructions_data_cost(transaction: &impl SVMMessage) -> u64 { let ix_data_bytes_len_total: u64 = transaction - .message() - .instructions() - .iter() + .instructions_iter() .map(|instruction| instruction.data.len() as u64) .sum(); @@ -267,10 +263,10 @@ impl CostModel { fn calculate_account_data_size_on_instruction( program_id: &Pubkey, - instruction: &CompiledInstruction, + instruction: SVMInstruction, ) -> SystemProgramAccountAllocation { if program_id == &system_program::id() { - if let Ok(instruction) = limited_deserialize(&instruction.data) { + if let Ok(instruction) = limited_deserialize(instruction.data) { Self::calculate_account_data_size_on_deserialized_system_instruction(instruction) } else { SystemProgramAccountAllocation::Failed @@ -282,9 +278,9 @@ impl CostModel { /// eventually, potentially determine account data size of all writable accounts /// at the moment, calculate account data size of account creation - fn calculate_allocated_accounts_data_size(transaction: &SanitizedTransaction) -> u64 { + fn calculate_allocated_accounts_data_size(transaction: &impl SVMMessage) -> u64 { let mut tx_attempted_allocation_size: u64 = 0; - for (program_id, instruction) in transaction.message().program_instructions_iter() { + for (program_id, instruction) in transaction.program_instructions_iter() { match Self::calculate_account_data_size_on_instruction(program_id, instruction) { SystemProgramAccountAllocation::Failed => { // If any system program instructions can be statically @@ -351,7 +347,7 @@ mod tests { )], Some(&Pubkey::new_unique()), )); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); assert_eq!( CostModel::calculate_allocated_accounts_data_size(&sanitized_tx), @@ -376,7 +372,7 @@ mod tests { ], Some(&Pubkey::new_unique()), )); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); assert_eq!( CostModel::calculate_allocated_accounts_data_size(&sanitized_tx), @@ -417,7 +413,7 @@ mod tests { ], Some(&Pubkey::new_unique()), )); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); assert_eq!( CostModel::calculate_allocated_accounts_data_size(&sanitized_tx), @@ -440,7 +436,7 @@ mod tests { ], Some(&Pubkey::new_unique()), )); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); assert_eq!( 0, // SystemProgramAccountAllocation::Failed, @@ -457,7 +453,7 @@ mod tests { ], Some(&Pubkey::new_unique()), )); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); assert_eq!( 0, // SystemProgramAccountAllocation::Failed, @@ -517,7 +513,7 @@ mod tests { let (mint_keypair, start_hash) = test_setup(); let keypair = Keypair::new(); - let simple_transaction = SanitizedTransaction::from_transaction_for_tests( + let simple_transaction = RuntimeTransaction::from_transaction_for_tests( system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 2, start_hash), ); debug!( @@ -552,7 +548,7 @@ mod tests { vec![Pubkey::new_unique()], instructions, ); - let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx); + let token_transaction = RuntimeTransaction::from_transaction_for_tests(tx); debug!("token_transaction {:?}", token_transaction); let (program_execution_cost, _loaded_accounts_data_size_cost, data_bytes_cost) = @@ -570,7 +566,7 @@ mod tests { // Cannot write-lock the system program, it will be demoted when taking locks. // However, the cost should be calculated as if it were taken. - let simple_transaction = SanitizedTransaction::from_transaction_for_tests( + let simple_transaction = RuntimeTransaction::from_transaction_for_tests( system_transaction::transfer(&mint_keypair, &system_program::id(), 2, start_hash), ); @@ -614,7 +610,7 @@ mod tests { vec![Pubkey::new_unique(), compute_budget::id()], instructions, ); - let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx); + let token_transaction = RuntimeTransaction::from_transaction_for_tests(tx); let (program_execution_cost, _loaded_accounts_data_size_cost, data_bytes_cost) = CostModel::get_transaction_cost(&token_transaction, &FeatureSet::all_enabled()); @@ -637,10 +633,10 @@ mod tests { .unwrap(), vec![], ), - // to trigger `duplicate_instruction_error` error + // to trigger failure in `sanitize_and_convert_to_compute_budget_limits` CompiledInstruction::new_from_raw_parts( 4, - ComputeBudgetInstruction::SetComputeUnitLimit(1_000) + ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit(0) .pack() .unwrap(), vec![], @@ -656,7 +652,7 @@ mod tests { vec![Pubkey::new_unique(), compute_budget::id()], instructions, ); - let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx); + let token_transaction = RuntimeTransaction::from_transaction_for_tests(tx); let (program_execution_cost, _loaded_accounts_data_size_cost, _data_bytes_cost) = CostModel::get_transaction_cost(&token_transaction, &FeatureSet::all_enabled()); @@ -672,7 +668,7 @@ mod tests { let instructions = system_instruction::transfer_many(&mint_keypair.pubkey(), &[(key1, 1), (key2, 1)]); let message = Message::new(&instructions, Some(&mint_keypair.pubkey())); - let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new( + let tx = RuntimeTransaction::from_transaction_for_tests(Transaction::new( &[&mint_keypair], message, start_hash, @@ -704,7 +700,7 @@ mod tests { CompiledInstruction::new(3, &(), vec![0, 1]), CompiledInstruction::new(4, &(), vec![0, 2]), ]; - let tx = SanitizedTransaction::from_transaction_for_tests( + let tx = RuntimeTransaction::from_transaction_for_tests( Transaction::new_with_compiled_instructions( &[&mint_keypair], &[key1, key2], @@ -735,7 +731,7 @@ mod tests { CompiledInstruction::new(4, &(), vec![0, 2]), CompiledInstruction::new(5, &(), vec![1, 3]), ]; - let tx = SanitizedTransaction::from_transaction_for_tests( + let tx = RuntimeTransaction::from_transaction_for_tests( Transaction::new_with_compiled_instructions( &[&signer1, &signer2], &[key1, key2], @@ -757,7 +753,7 @@ mod tests { #[test] fn test_cost_model_calculate_cost_all_default() { let (mint_keypair, start_hash) = test_setup(); - let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &Keypair::new().pubkey(), 2, @@ -791,7 +787,7 @@ mod tests { let to_keypair = Keypair::new(); let data_limit = 32 * 1024u32; let tx = - SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + RuntimeTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( &[ system_instruction::transfer(&mint_keypair.pubkey(), &to_keypair.pubkey(), 2), ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_limit), @@ -826,7 +822,7 @@ mod tests { let (mint_keypair, start_hash) = test_setup(); let transaction = - SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + RuntimeTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( &[ Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 2), @@ -855,7 +851,7 @@ mod tests { let (mint_keypair, start_hash) = test_setup(); let transaction = - SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + RuntimeTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( &[ system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 2), ComputeBudgetInstruction::set_compute_unit_limit(12_345), diff --git a/cost-model/src/cost_tracker.rs b/cost-model/src/cost_tracker.rs index 3b9382bba0a901..467f7e0d5761d5 100644 --- a/cost-model/src/cost_tracker.rs +++ b/cost-model/src/cost_tracker.rs @@ -412,10 +412,8 @@ mod tests { use { super::*, crate::transaction_cost::{WritableKeysTransaction, *}, - solana_sdk::{ - message::TransactionSignatureDetails, - signature::{Keypair, Signer}, - }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, + solana_sdk::signature::{Keypair, Signer}, std::cmp, }; @@ -437,14 +435,16 @@ mod tests { Keypair::new() } - fn build_simple_transaction(mint_keypair: &Keypair) -> WritableKeysTransaction { - WritableKeysTransaction(vec![mint_keypair.pubkey()]) + fn build_simple_transaction( + mint_keypair: &Keypair, + ) -> RuntimeTransaction { + RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![mint_keypair.pubkey()])) } fn simple_usage_cost_details( - transaction: &WritableKeysTransaction, + transaction: &RuntimeTransaction, programs_execution_cost: u64, - ) -> UsageCostDetails { + ) -> UsageCostDetails> { UsageCostDetails { transaction, signature_cost: 0, @@ -453,12 +453,11 @@ mod tests { programs_execution_cost, loaded_accounts_data_size_cost: 0, allocated_accounts_data_size: 0, - signature_details: TransactionSignatureDetails::new(0, 0, 0), } } fn simple_transaction_cost( - transaction: &WritableKeysTransaction, + transaction: &RuntimeTransaction, programs_execution_cost: u64, ) -> TransactionCost { TransactionCost::Transaction(simple_usage_cost_details( @@ -468,7 +467,7 @@ mod tests { } fn simple_vote_transaction_cost( - transaction: &WritableKeysTransaction, + transaction: &RuntimeTransaction, ) -> TransactionCost { TransactionCost::SimpleVote { transaction } } @@ -755,7 +754,9 @@ mod tests { // | acct3 | $cost | // and block_cost = $cost { - let transaction = WritableKeysTransaction(vec![acct1, acct2, acct3]); + let transaction = RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![ + acct1, acct2, acct3, + ])); let tx_cost = simple_transaction_cost(&transaction, cost); assert!(testee.try_add(&tx_cost).is_ok()); let (_costliest_account, costliest_account_cost) = testee.find_costliest_account(); @@ -770,7 +771,8 @@ mod tests { // | acct3 | $cost | // and block_cost = $cost * 2 { - let transaction = WritableKeysTransaction(vec![acct2]); + let transaction = + RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![acct2])); let tx_cost = simple_transaction_cost(&transaction, cost); assert!(testee.try_add(&tx_cost).is_ok()); let (costliest_account, costliest_account_cost) = testee.find_costliest_account(); @@ -787,7 +789,8 @@ mod tests { // | acct3 | $cost | // and block_cost = $cost * 2 { - let transaction = WritableKeysTransaction(vec![acct1, acct2]); + let transaction = + RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![acct1, acct2])); let tx_cost = simple_transaction_cost(&transaction, cost); assert!(testee.try_add(&tx_cost).is_err()); let (costliest_account, costliest_account_cost) = testee.find_costliest_account(); @@ -808,7 +811,8 @@ mod tests { let block_max = account_max * 3; // for three accts let mut testee = CostTracker::new(account_max, block_max, block_max); - let transaction = WritableKeysTransaction(vec![acct1, acct2, acct3]); + let transaction = + RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![acct1, acct2, acct3])); let tx_cost = simple_transaction_cost(&transaction, cost); let mut expected_block_cost = tx_cost.sum(); let expected_tx_count = 1; @@ -890,11 +894,11 @@ mod tests { let estimated_programs_execution_cost = 100; let estimated_loaded_accounts_data_size_cost = 200; let number_writeble_accounts = 3; - let transaction = WritableKeysTransaction( + let transaction = RuntimeTransaction::new_for_tests(WritableKeysTransaction( std::iter::repeat_with(Pubkey::new_unique) .take(number_writeble_accounts) .collect(), - ); + )); let mut usage_cost = simple_usage_cost_details(&transaction, estimated_programs_execution_cost); @@ -957,7 +961,8 @@ mod tests { let mut cost_tracker = CostTracker::default(); let cost = 100u64; - let transaction = WritableKeysTransaction(vec![Pubkey::new_unique()]); + let transaction = + RuntimeTransaction::new_for_tests(WritableKeysTransaction(vec![Pubkey::new_unique()])); let tx_cost = simple_transaction_cost(&transaction, cost); cost_tracker.add_transaction_cost(&tx_cost); // assert cost_tracker is reverted to default diff --git a/cost-model/src/transaction_cost.rs b/cost-model/src/transaction_cost.rs index fcfdfdda195aa1..712e11378f75a7 100644 --- a/cost-model/src/transaction_cost.rs +++ b/cost-model/src/transaction_cost.rs @@ -1,6 +1,9 @@ use { crate::block_cost_limits, - solana_sdk::{message::TransactionSignatureDetails, pubkey::Pubkey}, + solana_runtime_transaction::{ + runtime_transaction::RuntimeTransaction, transaction_meta::StaticMeta, + }, + solana_sdk::pubkey::Pubkey, solana_svm_transaction::svm_message::SVMMessage, }; @@ -16,8 +19,10 @@ const SIMPLE_VOTE_USAGE_COST: u64 = 3428; #[derive(Debug)] pub enum TransactionCost<'a, Tx: SVMMessage> { - SimpleVote { transaction: &'a Tx }, - Transaction(UsageCostDetails<'a, Tx>), + SimpleVote { + transaction: &'a RuntimeTransaction, + }, + Transaction(UsageCostDetails<'a, RuntimeTransaction>), } impl<'a, Tx: SVMMessage> TransactionCost<'a, Tx> { @@ -104,9 +109,10 @@ impl<'a, Tx: SVMMessage> TransactionCost<'a, Tx> { pub fn num_transaction_signatures(&self) -> u64 { match self { Self::SimpleVote { .. } => 1, - Self::Transaction(usage_cost) => { - usage_cost.signature_details.num_transaction_signatures() - } + Self::Transaction(usage_cost) => usage_cost + .transaction + .signature_details() + .num_transaction_signatures(), } } @@ -114,7 +120,8 @@ impl<'a, Tx: SVMMessage> TransactionCost<'a, Tx> { match self { Self::SimpleVote { .. } => 0, Self::Transaction(usage_cost) => usage_cost - .signature_details + .transaction + .signature_details() .num_secp256k1_instruction_signatures(), } } @@ -123,7 +130,8 @@ impl<'a, Tx: SVMMessage> TransactionCost<'a, Tx> { match self { Self::SimpleVote { .. } => 0, Self::Transaction(usage_cost) => usage_cost - .signature_details + .transaction + .signature_details() .num_ed25519_instruction_signatures(), } } @@ -139,7 +147,6 @@ pub struct UsageCostDetails<'a, Tx: SVMMessage> { pub programs_execution_cost: u64, pub loaded_accounts_data_size_cost: u64, pub allocated_accounts_data_size: u64, - pub signature_details: TransactionSignatureDetails, } impl<'a, Tx: SVMMessage> UsageCostDetails<'a, Tx> { @@ -225,12 +232,13 @@ mod tests { super::*, crate::cost_model::CostModel, solana_feature_set::FeatureSet, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ hash::Hash, message::SimpleAddressLoader, reserved_account_keys::ReservedAccountKeys, signer::keypair::Keypair, - transaction::{MessageHash, SanitizedTransaction, VersionedTransaction}, + transaction::{MessageHash, VersionedTransaction}, }, solana_vote_program::{vote_state::TowerSync, vote_transaction}, }; @@ -251,7 +259,7 @@ mod tests { ); // create a sanitized vote transaction - let vote_transaction = SanitizedTransaction::try_create( + let vote_transaction = RuntimeTransaction::try_create( VersionedTransaction::from(transaction.clone()), MessageHash::Compute, Some(true), @@ -261,7 +269,7 @@ mod tests { .unwrap(); // create a identical sanitized transaction, but identified as non-vote - let none_vote_transaction = SanitizedTransaction::try_create( + let none_vote_transaction = RuntimeTransaction::try_create( VersionedTransaction::from(transaction), MessageHash::Compute, Some(false), diff --git a/entry/Cargo.toml b/entry/Cargo.toml index d8b45e01160c79..75c429d35f8e80 100644 --- a/entry/Cargo.toml +++ b/entry/Cargo.toml @@ -23,6 +23,7 @@ solana-merkle-tree = { workspace = true } solana-metrics = { workspace = true } solana-perf = { workspace = true } solana-rayon-threadlimit = { workspace = true } +solana-runtime-transaction = { workspace = true } solana-sdk = { workspace = true } [dev-dependencies] diff --git a/entry/benches/entry_sigverify.rs b/entry/benches/entry_sigverify.rs index d9b97b2dbfd91e..7df8eb017ff8b5 100644 --- a/entry/benches/entry_sigverify.rs +++ b/entry/benches/entry_sigverify.rs @@ -3,12 +3,13 @@ extern crate test; use { solana_entry::entry::{self, VerifyRecyclers}, solana_perf::test_tx::test_tx, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ hash::Hash, reserved_account_keys::ReservedAccountKeys, transaction::{ - Result, SanitizedTransaction, SimpleAddressLoader, TransactionVerificationMode, - VersionedTransaction, + MessageHash, Result, SanitizedTransaction, SimpleAddressLoader, + TransactionVerificationMode, VersionedTransaction, }, }, std::sync::Arc, @@ -28,7 +29,7 @@ fn bench_gpusigverify(bencher: &mut Bencher) { let verify_transaction = { move |versioned_tx: VersionedTransaction, verification_mode: TransactionVerificationMode| - -> Result { + -> Result> { let sanitized_tx = { let message_hash = if verification_mode == TransactionVerificationMode::FullVerification { @@ -37,9 +38,9 @@ fn bench_gpusigverify(bencher: &mut Bencher) { versioned_tx.message.hash() }; - SanitizedTransaction::try_create( + RuntimeTransaction::try_create( versioned_tx, - message_hash, + MessageHash::Precomputed(message_hash), None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), @@ -78,12 +79,12 @@ fn bench_cpusigverify(bencher: &mut Bencher) { .collect::>(); let verify_transaction = { - move |versioned_tx: VersionedTransaction| -> Result { + move |versioned_tx: VersionedTransaction| -> Result> { let sanitized_tx = { let message_hash = versioned_tx.verify_and_hash_message()?; - SanitizedTransaction::try_create( + RuntimeTransaction::try_create( versioned_tx, - message_hash, + MessageHash::Precomputed(message_hash), None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), diff --git a/entry/src/entry.rs b/entry/src/entry.rs index ec945a122f0142..5b3d1bfd3eddd4 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -21,6 +21,7 @@ use { sigverify, }, solana_rayon_threadlimit::get_max_thread_count, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ hash::Hash, packet::Meta, @@ -151,7 +152,7 @@ impl From<&Entry> for EntrySummary { /// Typed entry to distinguish between transaction and tick entries pub enum EntryType { - Transactions(Vec), + Transactions(Vec>), Tick(Hash), } @@ -394,7 +395,11 @@ impl EntryVerificationState { pub fn verify_transactions( entries: Vec, thread_pool: &ThreadPool, - verify: Arc Result + Send + Sync>, + verify: Arc< + dyn Fn(VersionedTransaction) -> Result> + + Send + + Sync, + >, ) -> Result> { thread_pool.install(|| { entries @@ -422,7 +427,10 @@ pub fn start_verify_transactions( thread_pool: &ThreadPool, verify_recyclers: VerifyRecyclers, verify: Arc< - dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result + dyn Fn( + VersionedTransaction, + TransactionVerificationMode, + ) -> Result> + Send + Sync, >, @@ -460,7 +468,10 @@ fn start_verify_transactions_cpu( skip_verification: bool, thread_pool: &ThreadPool, verify: Arc< - dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result + dyn Fn( + VersionedTransaction, + TransactionVerificationMode, + ) -> Result> + Send + Sync, >, @@ -490,13 +501,16 @@ fn start_verify_transactions_gpu( verify_recyclers: VerifyRecyclers, thread_pool: &ThreadPool, verify: Arc< - dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result + dyn Fn( + VersionedTransaction, + TransactionVerificationMode, + ) -> Result> + Send + Sync, >, ) -> Result { let verify_func = { - move |versioned_tx: VersionedTransaction| -> Result { + move |versioned_tx: VersionedTransaction| -> Result> { verify( versioned_tx, TransactionVerificationMode::HashAndVerifyPrecompiles, @@ -506,7 +520,7 @@ fn start_verify_transactions_gpu( let entries = verify_transactions(entries, thread_pool, Arc::new(verify_func))?; - let transactions: Vec<&SanitizedTransaction> = entries + let transactions = entries .iter() .filter_map(|entry_type| match entry_type { EntryType::Tick(_) => None, @@ -987,7 +1001,8 @@ mod tests { signature::{Keypair, Signer}, system_transaction, transaction::{ - Result, SanitizedTransaction, SimpleAddressLoader, VersionedTransaction, + MessageHash, Result, SanitizedTransaction, SimpleAddressLoader, + VersionedTransaction, }, }, }; @@ -1011,7 +1026,7 @@ mod tests { dyn Fn( VersionedTransaction, TransactionVerificationMode, - ) -> Result + ) -> Result> + Send + Sync, >, @@ -1023,7 +1038,7 @@ mod tests { } else { TransactionVerificationMode::FullVerification }; - move |versioned_tx: VersionedTransaction| -> Result { + move |versioned_tx: VersionedTransaction| -> Result> { verify(versioned_tx, verification_mode) } }; @@ -1072,7 +1087,7 @@ mod tests { let verify_transaction = { move |versioned_tx: VersionedTransaction, verification_mode: TransactionVerificationMode| - -> Result { + -> Result> { let sanitized_tx = { let message_hash = if verification_mode == TransactionVerificationMode::FullVerification { @@ -1081,9 +1096,9 @@ mod tests { versioned_tx.message.hash() }; - SanitizedTransaction::try_create( + RuntimeTransaction::try_create( versioned_tx, - message_hash, + MessageHash::Precomputed(message_hash), None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), diff --git a/ledger-tool/Cargo.toml b/ledger-tool/Cargo.toml index abfa07ade49d38..9e9fc9838df959 100644 --- a/ledger-tool/Cargo.toml +++ b/ledger-tool/Cargo.toml @@ -45,6 +45,7 @@ solana-measure = { workspace = true } solana-program-runtime = { workspace = true } solana-rpc = { workspace = true } solana-runtime = { workspace = true, features = ["dev-context-only-utils"] } +solana-runtime-transaction = { workspace = true } solana-sdk = { workspace = true } solana-stake-program = { workspace = true } solana-storage-bigtable = { workspace = true } diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 6756806ad4ecb3..1c5b1e28acdf01 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -59,6 +59,7 @@ use { SUPPORTED_ARCHIVE_COMPRESSION, }, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ account::{AccountSharedData, ReadableAccount, WritableAccount}, account_utils::StateMut, @@ -73,7 +74,7 @@ use { shred_version::compute_shred_version, stake::{self, state::StakeStateV2}, system_program, - transaction::{MessageHash, SanitizedTransaction, SimpleAddressLoader}, + transaction::{MessageHash, SimpleAddressLoader}, }, solana_stake_program::{points::PointValue, stake_state}, solana_transaction_status::parse_ui_instruction, @@ -472,7 +473,7 @@ fn compute_slot_cost( .transactions .into_iter() .filter_map(|transaction| { - SanitizedTransaction::try_create( + RuntimeTransaction::try_create( transaction, MessageHash::Compute, None, diff --git a/ledger/Cargo.toml b/ledger/Cargo.toml index 57bb0d1a0a8c73..f4b1a560a52002 100644 --- a/ledger/Cargo.toml +++ b/ledger/Cargo.toml @@ -59,6 +59,7 @@ solana-perf = { workspace = true } solana-program-runtime = { workspace = true } solana-rayon-threadlimit = { workspace = true } solana-runtime = { workspace = true } +solana-runtime-transaction = { workspace = true } solana-sdk = { workspace = true } solana-stake-program = { workspace = true } solana-storage-bigtable = { workspace = true } diff --git a/ledger/benches/blockstore_processor.rs b/ledger/benches/blockstore_processor.rs index 1fd9b4f937ce6c..44f65db1d54fd4 100644 --- a/ledger/benches/blockstore_processor.rs +++ b/ledger/benches/blockstore_processor.rs @@ -17,6 +17,7 @@ use { prioritization_fee_cache::PrioritizationFeeCache, transaction_batch::{OwnedOrBorrowed, TransactionBatch}, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ account::{Account, ReadableAccount}, signature::Keypair, @@ -60,7 +61,7 @@ fn create_funded_accounts(bank: &Bank, num: usize) -> Vec { accounts } -fn create_transactions(bank: &Bank, num: usize) -> Vec { +fn create_transactions(bank: &Bank, num: usize) -> Vec> { let funded_accounts = create_funded_accounts(bank, 2 * num); funded_accounts .into_par_iter() @@ -70,7 +71,7 @@ fn create_transactions(bank: &Bank, num: usize) -> Vec { let to = &chunk[1]; system_transaction::transfer(from, &to.pubkey(), 1, bank.last_blockhash()) }) - .map(SanitizedTransaction::from_transaction_for_tests) + .map(RuntimeTransaction::from_transaction_for_tests) .collect() } diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index b8186e7a9a3ee8..e89eb54e73eaff 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -38,6 +38,9 @@ use { transaction_batch::{OwnedOrBorrowed, TransactionBatch}, vote_sender_types::ReplayVoteSender, }, + solana_runtime_transaction::{ + runtime_transaction::RuntimeTransaction, svm_transaction_adapter::SVMTransactionAdapter, + }, solana_sdk::{ clock::{Slot, MAX_PROCESSING_AGE}, genesis_config::GenesisConfig, @@ -59,6 +62,7 @@ use { solana_transaction_status::token_balances::TransactionTokenBalancesSet, solana_vote::vote_account::VoteAccountsHashMap, std::{ + borrow::Borrow, collections::{HashMap, HashSet}, ops::{Index, Range}, path::PathBuf, @@ -85,7 +89,7 @@ pub struct TransactionBatchWithIndexes<'a, 'b, Tx: SVMMessage> { // us from nicely unwinding these with manual unlocking. pub struct LockedTransactionsWithIndexes { lock_results: Vec>, - transactions: Vec, + transactions: Vec>, starting_index: usize, } @@ -201,7 +205,11 @@ pub fn execute_batch( let first_err = get_first_error(batch, &commit_results); if let Some(transaction_status_sender) = transaction_status_sender { - let transactions = batch.sanitized_transactions().to_vec(); + let transactions: Vec = batch + .sanitized_transactions() + .iter() + .map(|tx| tx.as_sanitized_transaction().borrow().clone()) + .collect(); let post_token_balances = if record_token_balances { collect_token_balances(bank, batch, &mut mint_decimals) } else { @@ -232,7 +240,7 @@ pub fn execute_batch( fn check_block_cost_limits( bank: &Bank, commit_results: &[TransactionCommitResult], - sanitized_transactions: &[SanitizedTransaction], + sanitized_transactions: &[RuntimeTransaction], ) -> Result<()> { assert_eq!(sanitized_transactions.len(), commit_results.len()); @@ -447,13 +455,13 @@ fn schedule_batches_for_execution( first_err } -fn rebatch_transactions<'a>( +fn rebatch_transactions<'a, Tx: SVMMessage>( lock_results: &'a [Result<()>], bank: &'a Arc, - sanitized_txs: &'a [SanitizedTransaction], + sanitized_txs: &'a [RuntimeTransaction], range: Range, transaction_indexes: &'a [usize], -) -> TransactionBatchWithIndexes<'a, 'a, SanitizedTransaction> { +) -> TransactionBatchWithIndexes<'a, 'a, Tx> { let txs = &sanitized_txs[range.clone()]; let results = &lock_results[range.clone()]; let mut tx_batch = @@ -592,7 +600,7 @@ pub fn process_entries_for_tests( let replay_tx_thread_pool = create_thread_pool(1); let verify_transaction = { let bank = bank.clone_with_scheduler(); - move |versioned_tx: VersionedTransaction| -> Result { + move |versioned_tx: VersionedTransaction| -> Result> { bank.verify_transaction(versioned_tx, TransactionVerificationMode::FullVerification) } }; @@ -721,7 +729,7 @@ fn process_entries( fn queue_batches_with_lock_retry( bank: &Bank, starting_index: usize, - transactions: Vec, + transactions: Vec>, batches: &mut Vec>, mut process_batches: impl FnMut( Drain>, @@ -1648,7 +1656,7 @@ fn confirm_slot_entries( let bank = bank.clone_with_scheduler(); move |versioned_tx: VersionedTransaction, verification_mode: TransactionVerificationMode| - -> Result { + -> Result> { bank.verify_transaction(versioned_tx, verification_mode) } }; @@ -4744,7 +4752,7 @@ pub mod tests { fn create_test_transactions( mint_keypair: &Keypair, genesis_hash: &Hash, - ) -> Vec { + ) -> Vec> { let pubkey = solana_sdk::pubkey::new_rand(); let keypair2 = Keypair::new(); let pubkey2 = solana_sdk::pubkey::new_rand(); @@ -4752,19 +4760,19 @@ pub mod tests { let pubkey3 = solana_sdk::pubkey::new_rand(); vec![ - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( mint_keypair, &pubkey, 1, *genesis_hash, )), - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &keypair2, &pubkey2, 1, *genesis_hash, )), - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &keypair3, &pubkey3, 1, @@ -5138,7 +5146,7 @@ pub mod tests { } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100); let bank = Bank::new_for_tests(&genesis_config); - let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &Pubkey::new_unique(), 1, diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 241acf5e8a3e83..e51a1070e49038 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -4883,6 +4883,7 @@ dependencies = [ "solana-client", "solana-feature-set", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-send-transaction-service", "solana-svm", @@ -5320,6 +5321,7 @@ dependencies = [ "solana-metrics", "solana-perf", "solana-rayon-threadlimit", + "solana-runtime-transaction", "solana-sdk", ] @@ -5606,6 +5608,7 @@ dependencies = [ "solana-program-runtime", "solana-rayon-threadlimit", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-stake-program", "solana-storage-bigtable", @@ -6132,6 +6135,7 @@ dependencies = [ "solana-rayon-threadlimit", "solana-rpc-client-api", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-send-transaction-service", "solana-stake-program", @@ -7346,6 +7350,7 @@ name = "solana-unified-scheduler-logic" version = "2.2.0" dependencies = [ "assert_matches", + "solana-runtime-transaction", "solana-sdk", "static_assertions", ] @@ -7363,6 +7368,7 @@ dependencies = [ "scopeguard", "solana-ledger", "solana-runtime", + "solana-runtime-transaction", "solana-sdk", "solana-timings", "solana-unified-scheduler-logic", diff --git a/programs/sbf/Cargo.toml b/programs/sbf/Cargo.toml index 259dd8886c7e96..0153d9f28eb114 100644 --- a/programs/sbf/Cargo.toml +++ b/programs/sbf/Cargo.toml @@ -120,7 +120,9 @@ solana-measure = { workspace = true } solana-program = { workspace = true } solana-program-runtime = { workspace = true } solana-runtime = { workspace = true, features = ["dev-context-only-utils"] } -solana-runtime-transaction = { workspace = true } +solana-runtime-transaction = { workspace = true, features = [ + "dev-context-only-utils", +] } solana-sbf-rust-invoke-dep = { workspace = true } solana-sbf-rust-realloc-dep = { workspace = true } solana-sbf-rust-realloc-invoke-dep = { workspace = true } diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index e176d7cac37fcc..84f9dbc96c57a3 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -33,7 +33,10 @@ use { load_upgradeable_program_wrapper, set_upgrade_authority, upgrade_program, }, }, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, + solana_runtime_transaction::{ + instructions_processor::process_compute_budget_instructions, + runtime_transaction::RuntimeTransaction, + }, solana_sbf_rust_invoke_dep::*, solana_sbf_rust_realloc_dep::*, solana_sbf_rust_realloc_invoke_dep::*, @@ -59,7 +62,7 @@ use { system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, system_program, sysvar::{self, clock}, - transaction::{SanitizedTransaction, Transaction, TransactionError, VersionedTransaction}, + transaction::{Transaction, TransactionError, VersionedTransaction}, }, solana_svm::{ transaction_commit_result::{CommittedTransaction, TransactionCommitResult}, @@ -652,7 +655,7 @@ fn test_return_data_and_log_data_syscall() { let blockhash = bank.last_blockhash(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); let transaction = Transaction::new(&[&mint_keypair], message, blockhash); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); let result = bank.simulate_transaction(&sanitized_tx, false); diff --git a/programs/sbf/tests/simulation.rs b/programs/sbf/tests/simulation.rs index a1a9f6a6ce8ab0..62732531065915 100644 --- a/programs/sbf/tests/simulation.rs +++ b/programs/sbf/tests/simulation.rs @@ -8,13 +8,14 @@ use { genesis_utils::{create_genesis_config, GenesisConfigInfo}, loader_utils::load_upgradeable_program_and_advance_slot, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ instruction::{AccountMeta, Instruction}, message::Message, pubkey::Pubkey, signature::{Keypair, Signer}, sysvar::{clock, slot_history}, - transaction::{SanitizedTransaction, Transaction}, + transaction::Transaction, }, }; @@ -50,7 +51,7 @@ fn test_no_panic_banks_client() { let blockhash = bank.last_blockhash(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); let transaction = Transaction::new(&[&mint_keypair], message, blockhash); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); let result = bank.simulate_transaction(&sanitized_tx, false); assert!(result.result.is_ok()); } diff --git a/programs/sbf/tests/syscall_get_epoch_stake.rs b/programs/sbf/tests/syscall_get_epoch_stake.rs index 2de6d36bbf7664..2cd7e20a1f3590 100644 --- a/programs/sbf/tests/syscall_get_epoch_stake.rs +++ b/programs/sbf/tests/syscall_get_epoch_stake.rs @@ -10,11 +10,12 @@ use { }, loader_utils::load_upgradeable_program_and_advance_slot, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ instruction::{AccountMeta, Instruction}, message::Message, signature::{Keypair, Signer}, - transaction::{SanitizedTransaction, Transaction}, + transaction::Transaction, }, solana_vote::vote_account::VoteAccount, solana_vote_program::vote_state::create_account_with_authorized, @@ -90,7 +91,7 @@ fn test_syscall_get_epoch_stake() { let blockhash = bank.last_blockhash(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); let transaction = Transaction::new(&[&mint_keypair], message, blockhash); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); let result = bank.simulate_transaction(&sanitized_tx, false); diff --git a/programs/sbf/tests/sysvar.rs b/programs/sbf/tests/sysvar.rs index 499a43ec7503f3..41a3d7016daffa 100644 --- a/programs/sbf/tests/sysvar.rs +++ b/programs/sbf/tests/sysvar.rs @@ -8,6 +8,7 @@ use { genesis_utils::{create_genesis_config, GenesisConfigInfo}, loader_utils::load_upgradeable_program_and_advance_slot, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ instruction::{AccountMeta, Instruction}, message::Message, @@ -18,7 +19,7 @@ use { clock, epoch_rewards, epoch_schedule, instructions, recent_blockhashes, rent, slot_hashes, slot_history, stake_history, }, - transaction::{SanitizedTransaction, Transaction}, + transaction::Transaction, }, }; @@ -92,7 +93,7 @@ fn test_sysvar_syscalls() { let blockhash = bank.last_blockhash(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); let transaction = Transaction::new(&[&mint_keypair], message, blockhash); - let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction); let result = bank.simulate_transaction(&sanitized_tx, false); assert!(result.result.is_ok()); } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index f7bc16bd6f9bd9..bbda760bac0493 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -45,6 +45,7 @@ solana-poh = { workspace = true } solana-rayon-threadlimit = { workspace = true } solana-rpc-client-api = { workspace = true } solana-runtime = { workspace = true } +solana-runtime-transaction = { workspace = true } solana-sdk = { workspace = true } solana-send-transaction-service = { workspace = true } solana-stake-program = { workspace = true } @@ -67,6 +68,9 @@ tokio-util = { workspace = true, features = ["codec", "compat"] } serial_test = { workspace = true } solana-net-utils = { workspace = true } solana-runtime = { workspace = true, features = ["dev-context-only-utils"] } +solana-runtime-transaction = { workspace = true, features = [ + "dev-context-only-utils", +] } solana-stake-program = { workspace = true } spl-pod = { workspace = true } symlink = { workspace = true } diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 40bd47f71c0e16..524661b4614e08 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -62,6 +62,7 @@ use { snapshot_utils, verify_precompiles::verify_precompiles, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, clock::{Slot, UnixTimestamp, MAX_PROCESSING_AGE}, @@ -4212,8 +4213,8 @@ fn sanitize_transaction( transaction: VersionedTransaction, address_loader: impl AddressLoader, reserved_account_keys: &HashSet, -) -> Result { - SanitizedTransaction::try_create( +) -> Result> { + RuntimeTransaction::try_create( transaction, MessageHash::Compute, None, @@ -4737,7 +4738,7 @@ pub mod tests { let prioritization_fee_cache = &self.meta.prioritization_fee_cache; let transactions: Vec<_> = transactions .into_iter() - .map(SanitizedTransaction::from_transaction_for_tests) + .map(RuntimeTransaction::from_transaction_for_tests) .collect(); prioritization_fee_cache.update(&bank, transactions.iter()); } diff --git a/runtime-transaction/Cargo.toml b/runtime-transaction/Cargo.toml index 9ccb325593da89..489ff1d3f78285 100644 --- a/runtime-transaction/Cargo.toml +++ b/runtime-transaction/Cargo.toml @@ -32,6 +32,9 @@ solana-program = { workspace = true } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +[features] +dev-context-only-utils = [] + [[bench]] name = "process_compute_budget_instructions" harness = false diff --git a/runtime-transaction/src/compute_budget_instruction_details.rs b/runtime-transaction/src/compute_budget_instruction_details.rs index ab148ef14ae152..7934c6643e1f51 100644 --- a/runtime-transaction/src/compute_budget_instruction_details.rs +++ b/runtime-transaction/src/compute_budget_instruction_details.rs @@ -14,6 +14,7 @@ use { }; #[cfg_attr(test, derive(Eq, PartialEq))] +#[cfg_attr(feature = "dev-context-only-utils", derive(Clone))] #[derive(Default, Debug)] pub(crate) struct ComputeBudgetInstructionDetails { // compute-budget instruction details: diff --git a/runtime-transaction/src/lib.rs b/runtime-transaction/src/lib.rs index 40c31d4b4d653a..7d7b7c825794fd 100644 --- a/runtime-transaction/src/lib.rs +++ b/runtime-transaction/src/lib.rs @@ -6,4 +6,5 @@ mod compute_budget_program_id_filter; pub mod instructions_processor; pub mod runtime_transaction; pub mod signature_details; +pub mod svm_transaction_adapter; pub mod transaction_meta; diff --git a/runtime-transaction/src/runtime_transaction.rs b/runtime-transaction/src/runtime_transaction.rs index 1dbd44bed70bf6..b44223b3bcf7cb 100644 --- a/runtime-transaction/src/runtime_transaction.rs +++ b/runtime-transaction/src/runtime_transaction.rs @@ -24,7 +24,10 @@ use { pubkey::Pubkey, signature::Signature, simple_vote_transaction_checker::is_simple_vote_transaction, - transaction::{Result, SanitizedTransaction, SanitizedVersionedTransaction}, + transaction::{ + MessageHash, Result, SanitizedTransaction, SanitizedVersionedTransaction, + VersionedTransaction, + }, }, solana_svm_transaction::{ instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup, @@ -33,6 +36,7 @@ use { std::collections::HashSet, }; +#[cfg_attr(feature = "dev-context-only-utils", derive(Clone))] #[derive(Debug)] pub struct RuntimeTransaction { transaction: T, @@ -71,13 +75,15 @@ impl Deref for RuntimeTransaction { impl RuntimeTransaction { pub fn try_from( sanitized_versioned_tx: SanitizedVersionedTransaction, - message_hash: Option, + message_hash: MessageHash, is_simple_vote_tx: Option, ) -> Result { + let message_hash = match message_hash { + MessageHash::Precomputed(hash) => hash, + MessageHash::Compute => sanitized_versioned_tx.get_message().message.hash(), + }; let is_simple_vote_tx = is_simple_vote_tx .unwrap_or_else(|| is_simple_vote_transaction(&sanitized_versioned_tx)); - let message_hash = - message_hash.unwrap_or_else(|| sanitized_versioned_tx.get_message().message.hash()); let precompile_signature_details = get_precompile_signature_details( sanitized_versioned_tx @@ -116,6 +122,31 @@ impl RuntimeTransaction { } impl RuntimeTransaction { + /// Create a new `RuntimeTransaction` from an + /// unsanitized `VersionedTransaction`. + pub fn try_create( + tx: VersionedTransaction, + message_hash: MessageHash, + is_simple_vote_tx: Option, + address_loader: impl AddressLoader, + reserved_account_keys: &HashSet, + ) -> Result { + let statically_loaded_runtime_tx = + RuntimeTransaction::::try_from( + SanitizedVersionedTransaction::try_from(tx)?, + message_hash, + is_simple_vote_tx, + )?; + Self::try_from( + statically_loaded_runtime_tx, + address_loader, + reserved_account_keys, + ) + } + + /// Create a new `RuntimeTransaction` from a + /// `RuntimeTransaction` that already has + /// static metadata loaded. pub fn try_from( statically_loaded_runtime_tx: RuntimeTransaction, address_loader: impl AddressLoader, @@ -145,6 +176,38 @@ impl RuntimeTransaction { } } +#[cfg(feature = "dev-context-only-utils")] +impl RuntimeTransaction { + pub fn from_transaction_for_tests(transaction: solana_sdk::transaction::Transaction) -> Self { + let versioned_transaction = VersionedTransaction::from(transaction); + Self::try_create( + versioned_transaction, + MessageHash::Compute, + None, + solana_sdk::message::SimpleAddressLoader::Disabled, + &HashSet::new(), + ) + .expect("failed to create RuntimeTransaction from Transaction") + } +} + +#[cfg(feature = "dev-context-only-utils")] +impl RuntimeTransaction { + /// Create simply wrapped transaction with a `TransactionMeta` for testing. + /// The `TransactionMeta` is default initialized. + pub fn new_for_tests(transaction: Tx) -> Self { + Self { + transaction, + meta: TransactionMeta { + message_hash: Hash::default(), + is_simple_vote_transaction: false, + signature_details: TransactionSignatureDetails::new(0, 0, 0), + compute_budget_instruction_details: ComputeBudgetInstructionDetails::default(), + }, + } + } +} + impl SVMMessage for RuntimeTransaction { // override to access from the cached meta instead of re-calculating fn num_total_signatures(&self) -> u64 { @@ -302,10 +365,14 @@ mod tests { svt: SanitizedVersionedTransaction, is_simple_vote: Option, ) -> bool { - RuntimeTransaction::::try_from(svt, None, is_simple_vote) - .unwrap() - .meta - .is_simple_vote_transaction + RuntimeTransaction::::try_from( + svt, + MessageHash::Compute, + is_simple_vote, + ) + .unwrap() + .meta + .is_simple_vote_transaction } assert!(!get_is_simple_vote( @@ -336,7 +403,7 @@ mod tests { let statically_loaded_transaction = RuntimeTransaction::::try_from( non_vote_sanitized_versioned_transaction(), - Some(hash), + MessageHash::Precomputed(hash), None, ) .unwrap(); @@ -371,7 +438,7 @@ mod tests { .add_compute_unit_price(compute_unit_price) .add_loaded_accounts_bytes(loaded_accounts_bytes) .to_sanitized_versioned_transaction(), - Some(hash), + MessageHash::Precomputed(hash), None, ) .unwrap(); diff --git a/runtime-transaction/src/svm_transaction_adapter.rs b/runtime-transaction/src/svm_transaction_adapter.rs new file mode 100644 index 00000000000000..639c58182f78fd --- /dev/null +++ b/runtime-transaction/src/svm_transaction_adapter.rs @@ -0,0 +1,25 @@ +use { + core::borrow::Borrow, + solana_sdk::transaction::{SanitizedTransaction, VersionedTransaction}, + solana_svm_transaction::svm_transaction::SVMTransaction, +}; + +/// Adapter trait to allow for conversion to legacy transaction types +/// that are required by some outside interfaces. +/// Eventually this trait should go away, but for now it is necessary. +pub trait SVMTransactionAdapter: SVMTransaction { + fn as_sanitized_transaction(&self) -> impl Borrow; + fn to_versioned_transaction(&self) -> VersionedTransaction; +} + +impl SVMTransactionAdapter for SanitizedTransaction { + #[inline] + fn as_sanitized_transaction(&self) -> impl Borrow { + self + } + + #[inline] + fn to_versioned_transaction(&self) -> VersionedTransaction { + self.to_versioned_transaction() + } +} diff --git a/runtime-transaction/src/transaction_meta.rs b/runtime-transaction/src/transaction_meta.rs index 6e10d233636674..0329ad9e94e01a 100644 --- a/runtime-transaction/src/transaction_meta.rs +++ b/runtime-transaction/src/transaction_meta.rs @@ -36,6 +36,7 @@ pub trait StaticMeta { /// on-chain ALT, examples are: transaction usage costs, nonce account. pub trait DynamicMeta: StaticMeta {} +#[cfg_attr(feature = "dev-context-only-utils", derive(Clone))] #[derive(Debug)] pub struct TransactionMeta { pub(crate) message_hash: Hash, diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 8062d3de3a4384..b7eefcea5a76b2 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -114,6 +114,9 @@ solana-accounts-db = { workspace = true, features = ["dev-context-only-utils"] } solana-logger = { workspace = true } # See order-crates-for-publishing.py for using this unusual `path = "."` solana-runtime = { path = ".", features = ["dev-context-only-utils"] } +solana-runtime-transaction = { workspace = true, features = [ + "dev-context-only-utils", +] } solana-sdk = { workspace = true, features = ["dev-context-only-utils"] } solana-svm = { workspace = true, features = ["dev-context-only-utils"] } static_assertions = { workspace = true } @@ -123,7 +126,10 @@ test-case = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [features] -dev-context-only-utils = ["solana-svm/dev-context-only-utils"] +dev-context-only-utils = [ + "solana-svm/dev-context-only-utils", + "solana-runtime-transaction/dev-context-only-utils", +] frozen-abi = [ "dep:solana-frozen-abi", "dep:solana-frozen-abi-macro", diff --git a/runtime/benches/prioritization_fee_cache.rs b/runtime/benches/prioritization_fee_cache.rs index 8c6bf1fe0a7d68..f44b67b0a5dd28 100644 --- a/runtime/benches/prioritization_fee_cache.rs +++ b/runtime/benches/prioritization_fee_cache.rs @@ -9,6 +9,7 @@ use { genesis_utils::{create_genesis_config, GenesisConfigInfo}, prioritization_fee_cache::*, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ compute_budget::ComputeBudgetInstruction, message::Message, @@ -25,7 +26,7 @@ fn build_sanitized_transaction( compute_unit_price: u64, signer_account: &Pubkey, write_account: &Pubkey, -) -> SanitizedTransaction { +) -> RuntimeTransaction { let transfer_lamports = 1; let transaction = Transaction::new_unsigned(Message::new( &[ @@ -36,7 +37,7 @@ fn build_sanitized_transaction( Some(signer_account), )); - SanitizedTransaction::from_transaction_for_tests(transaction) + RuntimeTransaction::from_transaction_for_tests(transaction) } #[bench] diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 47323ada22d7bb..7517c3c219166a 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -105,7 +105,11 @@ use { solana_program_runtime::{ invoke_context::BuiltinFunctionWithContext, loaded_programs::ProgramCacheEntry, }, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, + solana_runtime_transaction::{ + instructions_processor::process_compute_budget_instructions, + runtime_transaction::RuntimeTransaction, svm_transaction_adapter::SVMTransactionAdapter, + transaction_meta::StaticMeta, + }, solana_sdk::{ account::{ create_account_shared_data_with_fields as create_account, from_account, Account, @@ -175,7 +179,7 @@ use { TransactionProcessingConfig, TransactionProcessingEnvironment, }, }, - solana_svm_transaction::svm_message::SVMMessage, + solana_svm_transaction::{svm_message::SVMMessage, svm_transaction::SVMTransaction}, solana_timings::{ExecuteTimingType, ExecuteTimings}, solana_vote::vote_account::{VoteAccount, VoteAccountsHashMap}, solana_vote_program::vote_state::VoteState, @@ -3290,7 +3294,7 @@ impl Bank { fn update_transaction_statuses( &self, - sanitized_txs: &[SanitizedTransaction], + sanitized_txs: &[RuntimeTransaction], processing_results: &[TransactionProcessingResult], ) { let mut status_cache = self.status_cache.write().unwrap(); @@ -3300,7 +3304,7 @@ impl Bank { // Add the message hash to the status cache to ensure that this message // won't be processed again with a different signature. status_cache.insert( - tx.message().recent_blockhash(), + tx.recent_blockhash(), tx.message_hash(), self.slot(), processed_tx.status(), @@ -3309,7 +3313,7 @@ impl Bank { // can be queried by transaction signature over RPC. In the future, this should // only be added for API nodes because voting validators don't need to do this. status_cache.insert( - tx.message().recent_blockhash(), + tx.recent_blockhash(), tx.signature(), self.slot(), processed_tx.status(), @@ -3452,7 +3456,7 @@ impl Bank { let sanitized_txs = txs .into_iter() .map(|tx| { - SanitizedTransaction::try_create( + RuntimeTransaction::try_create( tx, MessageHash::Compute, None, @@ -3474,7 +3478,7 @@ impl Bank { } /// Attempt to take locks on the accounts in a transaction batch - pub fn try_lock_accounts(&self, txs: &[SanitizedTransaction]) -> Vec> { + pub fn try_lock_accounts(&self, txs: &[impl SVMMessage]) -> Vec> { let tx_account_lock_limit = self.get_transaction_account_lock_limit(); self.rc .accounts @@ -3482,10 +3486,10 @@ impl Bank { } /// Prepare a locked transaction batch from a list of sanitized transactions. - pub fn prepare_sanitized_batch<'a, 'b>( + pub fn prepare_sanitized_batch<'a, 'b, Tx: SVMMessage>( &'a self, - txs: &'b [SanitizedTransaction], - ) -> TransactionBatch<'a, 'b, SanitizedTransaction> { + txs: &'b [RuntimeTransaction], + ) -> TransactionBatch<'a, 'b, Tx> { TransactionBatch::new( self.try_lock_accounts(txs), self, @@ -3495,11 +3499,11 @@ impl Bank { /// Prepare a locked transaction batch from a list of sanitized transactions, and their cost /// limited packing status - pub fn prepare_sanitized_batch_with_results<'a, 'b>( + pub fn prepare_sanitized_batch_with_results<'a, 'b, Tx: SVMMessage>( &'a self, - transactions: &'b [SanitizedTransaction], + transactions: &'b [RuntimeTransaction], transaction_results: impl Iterator>, - ) -> TransactionBatch<'a, 'b, SanitizedTransaction> { + ) -> TransactionBatch<'a, 'b, Tx> { // this lock_results could be: Ok, AccountInUse, WouldExceedBlockMaxLimit or WouldExceedAccountMaxLimit let tx_account_lock_limit = self.get_transaction_account_lock_limit(); let lock_results = self.rc.accounts.lock_accounts_with_results( @@ -3511,13 +3515,12 @@ impl Bank { } /// Prepare a transaction batch from a single transaction without locking accounts - pub fn prepare_unlocked_batch_from_single_tx<'a>( + pub fn prepare_unlocked_batch_from_single_tx<'a, Tx: SVMMessage>( &'a self, - transaction: &'a SanitizedTransaction, - ) -> TransactionBatch<'_, '_, SanitizedTransaction> { + transaction: &'a RuntimeTransaction, + ) -> TransactionBatch<'_, '_, Tx> { let tx_account_lock_limit = self.get_transaction_account_lock_limit(); - let lock_result = - validate_account_locks(transaction.message().account_keys(), tx_account_lock_limit); + let lock_result = validate_account_locks(transaction.account_keys(), tx_account_lock_limit); let mut batch = TransactionBatch::new( vec![lock_result], self, @@ -3530,7 +3533,7 @@ impl Bank { /// Run transactions against a frozen bank without committing the results pub fn simulate_transaction( &self, - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, enable_cpi_recording: bool, ) -> TransactionSimulationResult { assert!(self.is_frozen(), "simulation bank must be frozen"); @@ -3542,7 +3545,7 @@ impl Bank { /// is frozen, enabling use in single-Bank test frameworks pub fn simulate_transaction_unchecked( &self, - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, enable_cpi_recording: bool, ) -> TransactionSimulationResult { let account_keys = transaction.message().account_keys(); @@ -3790,7 +3793,7 @@ impl Bank { fn collect_logs( &self, - transactions: &[SanitizedTransaction], + transactions: &[RuntimeTransaction], processing_results: &[TransactionProcessingResult], ) { let transaction_log_collector_config = @@ -3982,7 +3985,7 @@ impl Bank { pub fn commit_transactions( &self, - sanitized_txs: &[SanitizedTransaction], + sanitized_txs: &[RuntimeTransaction], processing_results: Vec, processed_counts: &ProcessedTransactionCounts, timings: &mut ExecuteTimings, @@ -4025,7 +4028,12 @@ impl Bank { .accounts() .accounts_db .has_accounts_update_notifier() - .then(|| sanitized_txs.iter().collect::>()); + .then(|| { + sanitized_txs + .iter() + .map(|tx| tx.as_sanitized_transaction()) + .collect::>() + }); let (accounts_to_store, transactions) = collect_accounts_to_store( sanitized_txs, @@ -5894,7 +5902,7 @@ impl Bank { &self, tx: VersionedTransaction, verification_mode: TransactionVerificationMode, - ) -> Result { + ) -> Result> { let sanitized_tx = { let size = bincode::serialized_size(&tx).map_err(|_| TransactionError::SanitizeFailure)?; @@ -5908,9 +5916,9 @@ impl Bank { tx.message.hash() }; - SanitizedTransaction::try_create( + RuntimeTransaction::try_create( tx, - message_hash, + MessageHash::Precomputed(message_hash), None, self, self.get_reserved_account_keys(), @@ -5933,7 +5941,7 @@ impl Bank { pub fn fully_verify_transaction( &self, tx: VersionedTransaction, - ) -> Result { + ) -> Result> { self.verify_transaction(tx, TransactionVerificationMode::FullVerification) } @@ -6287,7 +6295,7 @@ impl Bank { /// a bank-level cache of vote accounts and stake delegation info fn update_stakes_cache( &self, - txs: &[SanitizedTransaction], + txs: &[RuntimeTransaction], processing_results: &[TransactionProcessingResult], ) { debug_assert_eq!(txs.len(), processing_results.len()); @@ -6306,7 +6314,7 @@ impl Bank { }) .filter(|(_, executed_tx)| executed_tx.was_successful()) .flat_map(|(tx, executed_tx)| { - let num_account_keys = tx.message().account_keys().len(); + let num_account_keys = tx.account_keys().len(); let loaded_tx = &executed_tx.loaded_transaction; loaded_tx.accounts.iter().take(num_account_keys) }) @@ -7119,6 +7127,7 @@ impl Bank { } /// Prepare a transaction batch from a list of legacy transactions. Used for tests only. + #[cfg(feature = "dev-context-only-utils")] pub fn prepare_batch_for_tests( &self, txs: Vec, @@ -7126,7 +7135,7 @@ impl Bank { let transaction_account_lock_limit = self.get_transaction_account_lock_limit(); let sanitized_txs = txs .into_iter() - .map(SanitizedTransaction::from_transaction_for_tests) + .map(RuntimeTransaction::from_transaction_for_tests) .collect::>(); let lock_results = self .rc diff --git a/runtime/src/bank/check_transactions.rs b/runtime/src/bank/check_transactions.rs index 103aa70bb9f4c1..3bba991fcdd604 100644 --- a/runtime/src/bank/check_transactions.rs +++ b/runtime/src/bank/check_transactions.rs @@ -2,6 +2,7 @@ use { super::{Bank, BankStatusCache}, solana_accounts_db::blockhash_queue::BlockhashQueue, solana_perf::perf_libs, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ account::AccountSharedData, account_utils::StateMut, @@ -31,7 +32,7 @@ impl Bank { /// Checks a batch of sanitized transactions again bank for age and status pub fn check_transactions_with_forwarding_delay( &self, - transactions: &[SanitizedTransaction], + transactions: &[RuntimeTransaction], filter: &[TransactionResult<()>], forward_transactions_to_leader_at_slot_offset: u64, ) -> Vec { @@ -60,7 +61,7 @@ impl Bank { pub fn check_transactions( &self, - sanitized_txs: &[impl core::borrow::Borrow], + sanitized_txs: &[impl core::borrow::Borrow>], lock_results: &[TransactionResult<()>], max_age: usize, error_counters: &mut TransactionErrorMetrics, @@ -71,7 +72,7 @@ impl Bank { fn check_age( &self, - sanitized_txs: &[impl core::borrow::Borrow], + sanitized_txs: &[impl core::borrow::Borrow>], lock_results: &[TransactionResult<()>], max_age: usize, error_counters: &mut TransactionErrorMetrics, @@ -184,7 +185,7 @@ impl Bank { fn check_status_cache( &self, - sanitized_txs: &[impl core::borrow::Borrow], + sanitized_txs: &[impl core::borrow::Borrow>], lock_results: Vec, error_counters: &mut TransactionErrorMetrics, ) -> Vec { diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 9903b3c3d66f1d..5a3cec8193c219 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -10040,17 +10040,17 @@ fn test_verify_and_hash_transaction_sig_len() { // Too few signatures: Sanitization failure { let tx = make_transaction(TestCase::RemoveSignature); - assert_eq!( + assert_matches!( bank.verify_transaction(tx.into(), TransactionVerificationMode::FullVerification), - Err(TransactionError::SanitizeFailure), + Err(TransactionError::SanitizeFailure) ); } // Too many signatures: Sanitization failure { let tx = make_transaction(TestCase::AddSignature); - assert_eq!( + assert_matches!( bank.verify_transaction(tx.into(), TransactionVerificationMode::FullVerification), - Err(TransactionError::SanitizeFailure), + Err(TransactionError::SanitizeFailure) ); } } @@ -10085,9 +10085,9 @@ fn test_verify_transactions_packet_data_size() { { let tx = make_transaction(25); assert!(bincode::serialized_size(&tx).unwrap() > PACKET_DATA_SIZE as u64); - assert_eq!( + assert_matches!( bank.verify_transaction(tx.into(), TransactionVerificationMode::FullVerification), - Err(TransactionError::SanitizeFailure), + Err(TransactionError::SanitizeFailure) ); } // Assert that verify fails as soon as serialized @@ -12997,7 +12997,7 @@ fn test_failed_simulation_compute_units() { let transaction = Transaction::new(&[&mint_keypair], message, bank.last_blockhash()); bank.freeze(); - let sanitized = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized = RuntimeTransaction::from_transaction_for_tests(transaction); let simulation = bank.simulate_transaction(&sanitized, false); assert_eq!(expected_consumed_units, simulation.units_consumed); } @@ -13020,7 +13020,7 @@ fn test_failed_simulation_load_error() { let transaction = Transaction::new(&[&mint_keypair], message, bank.last_blockhash()); bank.freeze(); - let sanitized = SanitizedTransaction::from_transaction_for_tests(transaction); + let sanitized = RuntimeTransaction::from_transaction_for_tests(transaction); let simulation = bank.simulate_transaction(&sanitized, false); assert_eq!( simulation, diff --git a/runtime/src/bank_utils.rs b/runtime/src/bank_utils.rs index 0395511008d87a..82a8f783442ba2 100644 --- a/runtime/src/bank_utils.rs +++ b/runtime/src/bank_utils.rs @@ -1,5 +1,6 @@ use { crate::vote_sender_types::ReplayVoteSender, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::transaction::SanitizedTransaction, solana_svm::transaction_commit_result::{ TransactionCommitResult, TransactionCommitResultExtensions, @@ -40,7 +41,7 @@ pub fn setup_bank_and_vote_pubkeys_for_tests( } pub fn find_and_send_votes( - sanitized_txs: &[SanitizedTransaction], + sanitized_txs: &[RuntimeTransaction], commit_results: &[TransactionCommitResult], vote_sender: Option<&ReplayVoteSender>, ) { diff --git a/runtime/src/installed_scheduler_pool.rs b/runtime/src/installed_scheduler_pool.rs index db332228f03175..9aa4a20e09c558 100644 --- a/runtime/src/installed_scheduler_pool.rs +++ b/runtime/src/installed_scheduler_pool.rs @@ -23,6 +23,7 @@ use { crate::bank::Bank, log::*, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ clock::Slot, hash::Hash, @@ -161,8 +162,11 @@ pub trait InstalledScheduler: Send + Sync + Debug + 'static { /// optimize the fast code-path of normal transaction scheduling to be multi-threaded at the /// cost of far slower error code-path while giving implementors increased flexibility by /// having &mut. - fn schedule_execution(&self, transaction: SanitizedTransaction, index: usize) - -> ScheduleResult; + fn schedule_execution( + &self, + transaction: RuntimeTransaction, + index: usize, + ) -> ScheduleResult; /// Return the error which caused the scheduler to abort. /// @@ -440,7 +444,9 @@ impl BankWithScheduler { /// wait_for_termination()-ed or the unified scheduler is disabled in the first place). pub fn schedule_transaction_executions( &self, - transactions_with_indexes: impl ExactSizeIterator, + transactions_with_indexes: impl ExactSizeIterator< + Item = (RuntimeTransaction, usize), + >, ) -> Result<()> { trace!( "schedule_transaction_executions(): {} txs", @@ -834,7 +840,7 @@ mod tests { mint_keypair, .. } = create_genesis_config(10_000); - let tx0 = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx0 = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, diff --git a/runtime/src/prioritization_fee_cache.rs b/runtime/src/prioritization_fee_cache.rs index e1392e6f66b7f3..53058fab3533f5 100644 --- a/runtime/src/prioritization_fee_cache.rs +++ b/runtime/src/prioritization_fee_cache.rs @@ -4,11 +4,12 @@ use { log::*, solana_accounts_db::account_locks::validate_account_locks, solana_measure::measure_us, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, + solana_runtime_transaction::{ + runtime_transaction::RuntimeTransaction, transaction_meta::StaticMeta, + }, solana_sdk::{ clock::{BankId, Slot}, pubkey::Pubkey, - transaction::SanitizedTransaction, }, solana_svm_transaction::svm_message::SVMMessage, std::{ @@ -195,7 +196,11 @@ impl PrioritizationFeeCache { /// Update with a list of non-vote transactions' compute_budget_details and account_locks; Only /// transactions have both valid compute_budget_details and account_locks will be used to update /// fee_cache asynchronously. - pub fn update<'a>(&self, bank: &Bank, txs: impl Iterator) { + pub fn update<'a, Tx: SVMMessage + 'a>( + &self, + bank: &Bank, + txs: impl Iterator>, + ) { let (_, send_updates_us) = measure_us!({ for sanitized_transaction in txs { // Vote transactions are not prioritized, therefore they are excluded from @@ -204,13 +209,11 @@ impl PrioritizationFeeCache { continue; } - let compute_budget_limits = process_compute_budget_instructions( - SVMMessage::program_instructions_iter(sanitized_transaction), - ); + let compute_budget_limits = + sanitized_transaction.compute_budget_limits(&bank.feature_set); - let message = sanitized_transaction.message(); let lock_result = validate_account_locks( - message.account_keys(), + sanitized_transaction.account_keys(), bank.get_transaction_account_lock_limit(), ); @@ -225,11 +228,11 @@ impl PrioritizationFeeCache { continue; } - let writable_accounts = message + let writable_accounts = sanitized_transaction .account_keys() .iter() .enumerate() - .filter(|(index, _)| message.is_writable(*index)) + .filter(|(index, _)| sanitized_transaction.is_writable(*index)) .map(|(_, key)| *key) .collect(); @@ -440,7 +443,7 @@ mod tests { compute_unit_price: u64, signer_account: &Pubkey, write_account: &Pubkey, - ) -> SanitizedTransaction { + ) -> RuntimeTransaction { let transaction = Transaction::new_unsigned(Message::new( &[ system_instruction::transfer(signer_account, write_account, 1), @@ -449,14 +452,14 @@ mod tests { Some(signer_account), )); - SanitizedTransaction::from_transaction_for_tests(transaction) + RuntimeTransaction::from_transaction_for_tests(transaction) } // update fee cache is asynchronous, this test helper blocks until update is completed. fn sync_update<'a>( prioritization_fee_cache: &PrioritizationFeeCache, bank: Arc, - txs: impl ExactSizeIterator, + txs: impl ExactSizeIterator>, ) { let expected_update_count = prioritization_fee_cache .metrics diff --git a/runtime/src/transaction_batch.rs b/runtime/src/transaction_batch.rs index 4fbb23523910fd..30535b3ce9899c 100644 --- a/runtime/src/transaction_batch.rs +++ b/runtime/src/transaction_batch.rs @@ -1,6 +1,7 @@ use { - crate::bank::Bank, core::ops::Deref, solana_sdk::transaction::Result, - solana_svm_transaction::svm_message::SVMMessage, + crate::bank::Bank, core::ops::Deref, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, + solana_sdk::transaction::Result, solana_svm_transaction::svm_message::SVMMessage, }; pub enum OwnedOrBorrowed<'a, T> { @@ -23,7 +24,7 @@ impl Deref for OwnedOrBorrowed<'_, T> { pub struct TransactionBatch<'a, 'b, Tx: SVMMessage> { lock_results: Vec>, bank: &'a Bank, - sanitized_txs: OwnedOrBorrowed<'b, Tx>, + sanitized_txs: OwnedOrBorrowed<'b, RuntimeTransaction>, needs_unlock: bool, } @@ -31,7 +32,7 @@ impl<'a, 'b, Tx: SVMMessage> TransactionBatch<'a, 'b, Tx> { pub fn new( lock_results: Vec>, bank: &'a Bank, - sanitized_txs: OwnedOrBorrowed<'b, Tx>, + sanitized_txs: OwnedOrBorrowed<'b, RuntimeTransaction>, ) -> Self { assert_eq!(lock_results.len(), sanitized_txs.len()); Self { @@ -46,7 +47,7 @@ impl<'a, 'b, Tx: SVMMessage> TransactionBatch<'a, 'b, Tx> { &self.lock_results } - pub fn sanitized_transactions(&self) -> &[Tx] { + pub fn sanitized_transactions(&self) -> &[RuntimeTransaction] { &self.sanitized_txs } @@ -191,7 +192,7 @@ mod tests { ); } - fn setup(insert_conflicting_tx: bool) -> (Bank, Vec) { + fn setup(insert_conflicting_tx: bool) -> (Bank, Vec>) { let dummy_leader_pubkey = solana_sdk::pubkey::new_rand(); let GenesisConfigInfo { genesis_config, @@ -204,15 +205,15 @@ mod tests { let keypair2 = Keypair::new(); let pubkey2 = solana_sdk::pubkey::new_rand(); - let mut txs = vec![SanitizedTransaction::from_transaction_for_tests( + let mut txs = vec![RuntimeTransaction::from_transaction_for_tests( system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()), )]; if insert_conflicting_tx { - txs.push(SanitizedTransaction::from_transaction_for_tests( + txs.push(RuntimeTransaction::from_transaction_for_tests( system_transaction::transfer(&mint_keypair, &pubkey2, 1, genesis_config.hash()), )); } - txs.push(SanitizedTransaction::from_transaction_for_tests( + txs.push(RuntimeTransaction::from_transaction_for_tests( system_transaction::transfer(&keypair2, &pubkey2, 1, genesis_config.hash()), )); diff --git a/sdk/program/src/message/sanitized.rs b/sdk/program/src/message/sanitized.rs index 792dd1c39104ed..6d71f8c1265b85 100644 --- a/sdk/program/src/message/sanitized.rs +++ b/sdk/program/src/message/sanitized.rs @@ -406,7 +406,7 @@ impl SanitizedMessage { /// Transaction signature details including the number of transaction signatures /// and precompile signatures. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct TransactionSignatureDetails { num_transaction_signatures: u64, num_secp256k1_instruction_signatures: u64, diff --git a/unified-scheduler-logic/Cargo.toml b/unified-scheduler-logic/Cargo.toml index b05cec41a7c862..b48fed86f1162b 100644 --- a/unified-scheduler-logic/Cargo.toml +++ b/unified-scheduler-logic/Cargo.toml @@ -11,5 +11,11 @@ edition = { workspace = true } [dependencies] assert_matches = { workspace = true } +solana-runtime-transaction = { workspace = true } solana-sdk = { workspace = true } static_assertions = { workspace = true } + +[dev-dependencies] +solana-runtime-transaction = { workspace = true, features = [ + "dev-context-only-utils", +] } diff --git a/unified-scheduler-logic/src/lib.rs b/unified-scheduler-logic/src/lib.rs index da748ef401d1fe..2e8caca3b85b8b 100644 --- a/unified-scheduler-logic/src/lib.rs +++ b/unified-scheduler-logic/src/lib.rs @@ -98,6 +98,7 @@ use { crate::utils::{ShortCounter, Token, TokenCell}, assert_matches::assert_matches, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{pubkey::Pubkey, transaction::SanitizedTransaction}, static_assertions::const_assert_eq, std::{collections::VecDeque, mem, sync::Arc}, @@ -413,7 +414,7 @@ const_assert_eq!(mem::size_of::(), 0); /// Internal scheduling data about a particular task. #[derive(Debug)] pub struct TaskInner { - transaction: SanitizedTransaction, + transaction: RuntimeTransaction, /// The index of a transaction in ledger entries; not used by SchedulingStateMachine by itself. /// Carrying this along with the transaction is needed to properly record the execution result /// of it. @@ -427,7 +428,7 @@ impl TaskInner { self.index } - pub fn transaction(&self) -> &SanitizedTransaction { + pub fn transaction(&self) -> &RuntimeTransaction { &self.transaction } @@ -754,8 +755,8 @@ impl SchedulingStateMachine { } } - /// Creates a new task with [`SanitizedTransaction`] with all of its corresponding - /// [`UsageQueue`]s preloaded. + /// Creates a new task with [`RuntimeTransaction`] with all of + /// its corresponding [`UsageQueue`]s preloaded. /// /// Closure (`usage_queue_loader`) is used to delegate the (possibly multi-threaded) /// implementation of [`UsageQueue`] look-up by [`pubkey`](Pubkey) to callers. It's the @@ -768,7 +769,7 @@ impl SchedulingStateMachine { /// after created, if `has_no_active_task()` is `true`. Also note that this is desired for /// separation of concern. pub fn create_task( - transaction: SanitizedTransaction, + transaction: RuntimeTransaction, index: usize, usage_queue_loader: &mut impl FnMut(Pubkey) -> UsageQueue, ) -> Task { @@ -887,21 +888,20 @@ mod tests { instruction::{AccountMeta, Instruction}, message::Message, pubkey::Pubkey, - signature::Signer, - signer::keypair::Keypair, transaction::{SanitizedTransaction, Transaction}, }, std::{cell::RefCell, collections::HashMap, rc::Rc}, }; - fn simplest_transaction() -> SanitizedTransaction { - let payer = Keypair::new(); - let message = Message::new(&[], Some(&payer.pubkey())); + fn simplest_transaction() -> RuntimeTransaction { + let message = Message::new(&[], Some(&Pubkey::new_unique())); let unsigned = Transaction::new_unsigned(message); - SanitizedTransaction::from_transaction_for_tests(unsigned) + RuntimeTransaction::from_transaction_for_tests(unsigned) } - fn transaction_with_readonly_address(address: Pubkey) -> SanitizedTransaction { + fn transaction_with_readonly_address( + address: Pubkey, + ) -> RuntimeTransaction { let instruction = Instruction { program_id: Pubkey::default(), accounts: vec![AccountMeta::new_readonly(address, false)], @@ -909,10 +909,12 @@ mod tests { }; let message = Message::new(&[instruction], Some(&Pubkey::new_unique())); let unsigned = Transaction::new_unsigned(message); - SanitizedTransaction::from_transaction_for_tests(unsigned) + RuntimeTransaction::from_transaction_for_tests(unsigned) } - fn transaction_with_writable_address(address: Pubkey) -> SanitizedTransaction { + fn transaction_with_writable_address( + address: Pubkey, + ) -> RuntimeTransaction { let instruction = Instruction { program_id: Pubkey::default(), accounts: vec![AccountMeta::new(address, false)], @@ -920,7 +922,7 @@ mod tests { }; let message = Message::new(&[instruction], Some(&Pubkey::new_unique())); let unsigned = Transaction::new_unsigned(message); - SanitizedTransaction::from_transaction_for_tests(unsigned) + RuntimeTransaction::from_transaction_for_tests(unsigned) } fn create_address_loader( @@ -972,18 +974,18 @@ mod tests { #[test] fn test_create_task() { let sanitized = simplest_transaction(); - let task = SchedulingStateMachine::create_task(sanitized.clone(), 3, &mut |_| { - UsageQueue::default() - }); + let signature = *sanitized.signature(); + let task = + SchedulingStateMachine::create_task(sanitized, 3, &mut |_| UsageQueue::default()); assert_eq!(task.task_index(), 3); - assert_eq!(task.transaction(), &sanitized); + assert_eq!(task.transaction().signature(), &signature); } #[test] fn test_non_conflicting_task_related_counts() { let sanitized = simplest_transaction(); let address_loader = &mut create_address_loader(None); - let task = SchedulingStateMachine::create_task(sanitized.clone(), 3, address_loader); + let task = SchedulingStateMachine::create_task(sanitized, 3, address_loader); let mut state_machine = unsafe { SchedulingStateMachine::exclusively_initialize_current_thread_for_scheduling() diff --git a/unified-scheduler-pool/Cargo.toml b/unified-scheduler-pool/Cargo.toml index e4dd6015ed44cd..2ceda195039ea1 100644 --- a/unified-scheduler-pool/Cargo.toml +++ b/unified-scheduler-pool/Cargo.toml @@ -19,6 +19,7 @@ qualifier_attr = { workspace = true } scopeguard = { workspace = true } solana-ledger = { workspace = true } solana-runtime = { workspace = true } +solana-runtime-transaction = { workspace = true } solana-sdk = { workspace = true } solana-timings = { workspace = true } solana-unified-scheduler-logic = { workspace = true } diff --git a/unified-scheduler-pool/src/lib.rs b/unified-scheduler-pool/src/lib.rs index 105e0d93326d3e..4787ba0709f4e1 100644 --- a/unified-scheduler-pool/src/lib.rs +++ b/unified-scheduler-pool/src/lib.rs @@ -31,6 +31,7 @@ use { prioritization_fee_cache::PrioritizationFeeCache, vote_sender_types::ReplayVoteSender, }, + solana_runtime_transaction::runtime_transaction::RuntimeTransaction, solana_sdk::{ pubkey::Pubkey, transaction::{Result, SanitizedTransaction, TransactionError}, @@ -411,7 +412,7 @@ pub trait TaskHandler: Send + Sync + Debug + Sized + 'static { result: &mut Result<()>, timings: &mut ExecuteTimings, bank: &Arc, - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, index: usize, handler_context: &HandlerContext, ); @@ -425,7 +426,7 @@ impl TaskHandler for DefaultTaskHandler { result: &mut Result<()>, timings: &mut ExecuteTimings, bank: &Arc, - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, index: usize, handler_context: &HandlerContext, ) { @@ -1411,7 +1412,7 @@ impl InstalledScheduler for PooledScheduler { fn schedule_execution( &self, - transaction: SanitizedTransaction, + transaction: RuntimeTransaction, index: usize, ) -> ScheduleResult { let task = SchedulingStateMachine::create_task(transaction, index, &mut |pubkey| { @@ -1753,7 +1754,7 @@ mod tests { _result: &mut Result<()>, timings: &mut ExecuteTimings, _bank: &Arc, - _transaction: &SanitizedTransaction, + _transaction: &RuntimeTransaction, _index: usize, _handler_context: &HandlerContext, ) { @@ -1777,7 +1778,7 @@ mod tests { pool.register_timeout_listener(bank.create_timeout_listener()); let tx_before_stale = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -1789,7 +1790,7 @@ mod tests { sleepless_testing::at(TestCheckPoint::AfterTimeoutListenerTriggered); let tx_after_stale = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -1897,7 +1898,7 @@ mod tests { pool.register_timeout_listener(bank.create_timeout_listener()); let tx_before_stale = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -1910,7 +1911,7 @@ mod tests { sleepless_testing::at(TestCheckPoint::AfterTimeoutListenerTriggered); let tx_after_stale = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -1936,7 +1937,7 @@ mod tests { result: &mut Result<()>, _timings: &mut ExecuteTimings, _bank: &Arc, - _transaction: &SanitizedTransaction, + _transaction: &RuntimeTransaction, _index: usize, _handler_context: &HandlerContext, ) { @@ -1961,7 +1962,7 @@ mod tests { .. } = create_genesis_config(10_000); - let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -2047,7 +2048,7 @@ mod tests { _result: &mut Result<()>, _timings: &mut ExecuteTimings, _bank: &Arc, - _transaction: &SanitizedTransaction, + _transaction: &RuntimeTransaction, _index: usize, _handler_context: &HandlerContext, ) { @@ -2082,13 +2083,12 @@ mod tests { const MAX_TASK_COUNT: usize = 100; for i in 0..MAX_TASK_COUNT { - let tx = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( - &mint_keypair, - &solana_sdk::pubkey::new_rand(), - 2, - genesis_config.hash(), - )); + let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( + &mint_keypair, + &solana_sdk::pubkey::new_rand(), + 2, + genesis_config.hash(), + )); scheduler.schedule_execution(tx, i).unwrap(); } @@ -2234,7 +2234,7 @@ mod tests { mint_keypair, .. } = create_genesis_config(10_000); - let tx0 = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx0 = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -2294,20 +2294,19 @@ mod tests { let scheduler = pool.take_scheduler(context); let unfunded_keypair = Keypair::new(); - let bad_tx = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( - &unfunded_keypair, - &solana_sdk::pubkey::new_rand(), - 2, - genesis_config.hash(), - )); + let bad_tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( + &unfunded_keypair, + &solana_sdk::pubkey::new_rand(), + 2, + genesis_config.hash(), + )); assert_eq!(bank.transaction_count(), 0); scheduler.schedule_execution(bad_tx, 0).unwrap(); sleepless_testing::at(TestCheckPoint::AfterTaskHandled); assert_eq!(bank.transaction_count(), 0); let good_tx_after_bad_tx = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 3, @@ -2386,7 +2385,7 @@ mod tests { _result: &mut Result<()>, _timings: &mut ExecuteTimings, _bank: &Arc, - _transaction: &SanitizedTransaction, + _transaction: &RuntimeTransaction, index: usize, _handler_context: &HandlerContext, ) { @@ -2425,13 +2424,12 @@ mod tests { for index in 0..TX_COUNT { // Use 2 non-conflicting txes to exercise the channel disconnected case as well. - let tx = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( - &Keypair::new(), - &solana_sdk::pubkey::new_rand(), - 1, - genesis_config.hash(), - )); + let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( + &Keypair::new(), + &solana_sdk::pubkey::new_rand(), + 1, + genesis_config.hash(), + )); scheduler.schedule_execution(tx, index).unwrap(); } // finally unblock the scheduler thread; otherwise the above schedule_execution could @@ -2467,7 +2465,7 @@ mod tests { result: &mut Result<()>, _timings: &mut ExecuteTimings, _bank: &Arc, - _transaction: &SanitizedTransaction, + _transaction: &RuntimeTransaction, index: usize, _handler_context: &HandlerContext, ) { @@ -2499,13 +2497,12 @@ mod tests { let scheduler = pool.do_take_scheduler(context); for i in 0..10 { - let tx = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( - &mint_keypair, - &solana_sdk::pubkey::new_rand(), - 2, - genesis_config.hash(), - )); + let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( + &mint_keypair, + &solana_sdk::pubkey::new_rand(), + 2, + genesis_config.hash(), + )); scheduler.schedule_execution(tx, i).unwrap(); } // finally unblock the scheduler thread; otherwise the above schedule_execution could @@ -2537,7 +2534,7 @@ mod tests { result: &mut Result<()>, timings: &mut ExecuteTimings, bank: &Arc, - transaction: &SanitizedTransaction, + transaction: &RuntimeTransaction, index: usize, handler_context: &HandlerContext, ) { @@ -2564,13 +2561,13 @@ mod tests { } = create_genesis_config(10_000); // tx0 and tx1 is definitely conflicting to write-lock the mint address - let tx0 = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx0 = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, genesis_config.hash(), )); - let tx1 = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + let tx1 = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -2622,7 +2619,7 @@ mod tests { _result: &mut Result<()>, _timings: &mut ExecuteTimings, bank: &Arc, - _transaction: &SanitizedTransaction, + _transaction: &RuntimeTransaction, index: usize, _handler_context: &HandlerContext, ) { @@ -2657,7 +2654,7 @@ mod tests { // Create a dummy tx and two contexts let dummy_tx = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -2717,7 +2714,7 @@ mod tests { fn schedule_execution( &self, - transaction: SanitizedTransaction, + transaction: RuntimeTransaction, index: usize, ) -> ScheduleResult { let transaction_and_index = (transaction, index); @@ -2823,7 +2820,7 @@ mod tests { .. } = create_genesis_config(10_000); let very_old_valid_tx = - SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer( &mint_keypair, &solana_sdk::pubkey::new_rand(), 2, @@ -2921,7 +2918,7 @@ mod tests { ); // mangle the transfer tx to try to lock fee_payer (= mint_keypair) address twice! tx.message.account_keys.push(tx.message.account_keys[0]); - let tx = SanitizedTransaction::from_transaction_for_tests(tx); + let tx = RuntimeTransaction::from_transaction_for_tests(tx); // this internally should call SanitizedTransaction::get_account_locks(). let result = &mut Ok(());