diff --git a/core/src/forward_packet_batches_by_accounts.rs b/core/src/forward_packet_batches_by_accounts.rs index 889747ece..14fcfe486 100644 --- a/core/src/forward_packet_batches_by_accounts.rs +++ b/core/src/forward_packet_batches_by_accounts.rs @@ -187,7 +187,10 @@ impl ForwardPacketBatchesByAccounts { mod tests { use { super::*, - crate::unprocessed_packet_batches::{DeserializedPacket, TransactionPriorityDetails}, + crate::{ + transaction_priority_details::TransactionPriorityDetails, + unprocessed_packet_batches::DeserializedPacket, + }, solana_runtime::{ bank::Bank, bank_forks::BankForks, diff --git a/core/src/lib.rs b/core/src/lib.rs index 53d580a03..9be6ab5b9 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -68,6 +68,7 @@ mod tower1_7_14; pub mod tower_storage; pub mod tpu; pub mod tracer_packet_stats; +pub mod transaction_priority_details; pub mod tree_diff; pub mod tvu; pub mod unfrozen_gossip_verified_vote_hashes; diff --git a/core/src/transaction_priority_details.rs b/core/src/transaction_priority_details.rs new file mode 100644 index 000000000..7f816cad2 --- /dev/null +++ b/core/src/transaction_priority_details.rs @@ -0,0 +1,177 @@ +use { + solana_program_runtime::compute_budget::ComputeBudget, + solana_sdk::{ + instruction::CompiledInstruction, + pubkey::Pubkey, + transaction::{SanitizedTransaction, SanitizedVersionedTransaction}, + }, +}; + +#[derive(Debug, PartialEq, Eq)] +pub struct TransactionPriorityDetails { + pub priority: u64, + pub compute_unit_limit: u64, +} + +pub trait GetTransactionPriorityDetails { + fn get_transaction_priority_details(&self) -> Option; + + fn process_compute_budget_instruction<'a>( + instructions: impl Iterator, + ) -> Option { + let mut compute_budget = ComputeBudget::default(); + let prioritization_fee_details = compute_budget + .process_instructions( + instructions, + true, // use default units per instruction + true, // don't reject txs that use set compute unit price ix + ) + .ok()?; + Some(TransactionPriorityDetails { + priority: prioritization_fee_details.get_priority(), + compute_unit_limit: compute_budget.compute_unit_limit, + }) + } +} + +impl GetTransactionPriorityDetails for SanitizedVersionedTransaction { + fn get_transaction_priority_details(&self) -> Option { + Self::process_compute_budget_instruction(self.get_message().program_instructions_iter()) + } +} + +impl GetTransactionPriorityDetails for SanitizedTransaction { + fn get_transaction_priority_details(&self) -> Option { + Self::process_compute_budget_instruction(self.message().program_instructions_iter()) + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + message::Message, + pubkey::Pubkey, + signature::{Keypair, Signer}, + system_instruction, + transaction::{Transaction, VersionedTransaction}, + }, + }; + + #[test] + fn test_get_priority_with_valid_request_heap_frame_tx() { + let keypair = Keypair::new(); + let transaction = Transaction::new_unsigned(Message::new( + &[ + system_instruction::transfer(&keypair.pubkey(), &Pubkey::new_unique(), 1), + ComputeBudgetInstruction::request_heap_frame(32 * 1024), + ], + Some(&keypair.pubkey()), + )); + + // assert for SanitizedVersionedTransaction + let versioned_transaction = VersionedTransaction::from(transaction.clone()); + let sanitized_versioned_transaction = + SanitizedVersionedTransaction::try_new(versioned_transaction).unwrap(); + assert_eq!( + sanitized_versioned_transaction.get_transaction_priority_details(), + Some(TransactionPriorityDetails { + priority: 0, + compute_unit_limit: + solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT + as u64 + }) + ); + + // assert for SanitizedTransaction + let sanitized_transaction = + SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap(); + assert_eq!( + sanitized_transaction.get_transaction_priority_details(), + Some(TransactionPriorityDetails { + priority: 0, + compute_unit_limit: + solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT + as u64 + }) + ); + } + + #[test] + fn test_get_priority_with_valid_set_compute_units_limit() { + let requested_cu = 101u32; + let keypair = Keypair::new(); + let transaction = Transaction::new_unsigned(Message::new( + &[ + system_instruction::transfer(&keypair.pubkey(), &Pubkey::new_unique(), 1), + ComputeBudgetInstruction::set_compute_unit_limit(requested_cu), + ], + Some(&keypair.pubkey()), + )); + + // assert for SanitizedVersionedTransaction + let versioned_transaction = VersionedTransaction::from(transaction.clone()); + let sanitized_versioned_transaction = + SanitizedVersionedTransaction::try_new(versioned_transaction).unwrap(); + assert_eq!( + sanitized_versioned_transaction.get_transaction_priority_details(), + Some(TransactionPriorityDetails { + priority: 0, + compute_unit_limit: requested_cu as u64, + }) + ); + + // assert for SanitizedTransaction + let sanitized_transaction = + SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap(); + assert_eq!( + sanitized_transaction.get_transaction_priority_details(), + Some(TransactionPriorityDetails { + priority: 0, + compute_unit_limit: requested_cu as u64, + }) + ); + } + + #[test] + fn test_get_priority_with_valid_set_compute_unit_price() { + let requested_price = 1_000; + let keypair = Keypair::new(); + let transaction = Transaction::new_unsigned(Message::new( + &[ + system_instruction::transfer(&keypair.pubkey(), &Pubkey::new_unique(), 1), + ComputeBudgetInstruction::set_compute_unit_price(requested_price), + ], + Some(&keypair.pubkey()), + )); + + // assert for SanitizedVersionedTransaction + let versioned_transaction = VersionedTransaction::from(transaction.clone()); + let sanitized_versioned_transaction = + SanitizedVersionedTransaction::try_new(versioned_transaction).unwrap(); + assert_eq!( + sanitized_versioned_transaction.get_transaction_priority_details(), + Some(TransactionPriorityDetails { + priority: requested_price, + compute_unit_limit: + solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT + as u64 + }) + ); + + // assert for SanitizedTransaction + let sanitized_transaction = + SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap(); + assert_eq!( + sanitized_transaction.get_transaction_priority_details(), + Some(TransactionPriorityDetails { + priority: requested_price, + compute_unit_limit: + solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT + as u64 + }) + ); + } +} diff --git a/core/src/unprocessed_packet_batches.rs b/core/src/unprocessed_packet_batches.rs index 6c8e62462..3305e264b 100644 --- a/core/src/unprocessed_packet_batches.rs +++ b/core/src/unprocessed_packet_batches.rs @@ -1,11 +1,13 @@ use { + crate::transaction_priority_details::{ + GetTransactionPriorityDetails, TransactionPriorityDetails, + }, min_max_heap::MinMaxHeap, solana_perf::packet::{Packet, PacketBatch}, - solana_program_runtime::compute_budget::ComputeBudget, solana_sdk::{ feature_set, hash::Hash, - message::{Message, SanitizedVersionedMessage}, + message::Message, sanitize::SanitizeError, short_vec::decode_shortu16_len, signature::Signature, @@ -39,12 +41,6 @@ pub enum DeserializedPacketError { PrioritizationFailure, } -#[derive(Debug, PartialEq, Eq)] -pub struct TransactionPriorityDetails { - pub priority: u64, - pub compute_unit_limit: u64, -} - #[derive(Debug, PartialEq, Eq)] pub struct ImmutableDeserializedPacket { original_packet: Packet, @@ -113,7 +109,7 @@ impl DeserializedPacket { // drop transaction if prioritization fails. let priority_details = priority_details - .or_else(|| get_priority_details(sanitized_transaction.get_message())) + .or_else(|| sanitized_transaction.get_transaction_priority_details()) .ok_or(DeserializedPacketError::PrioritizationFailure)?; Ok(Self { @@ -406,21 +402,6 @@ pub fn packet_message(packet: &Packet) -> Result<&[u8], DeserializedPacketError> .ok_or(DeserializedPacketError::SignatureOverflowed(sig_size)) } -fn get_priority_details(message: &SanitizedVersionedMessage) -> Option { - let mut compute_budget = ComputeBudget::default(); - let prioritization_fee_details = compute_budget - .process_instructions( - message.program_instructions_iter(), - true, // use default units per instruction - true, // don't reject txs that use set compute unit price ix - ) - .ok()?; - Some(TransactionPriorityDetails { - priority: prioritization_fee_details.get_priority(), - compute_unit_limit: compute_budget.compute_unit_limit, - }) -} - pub fn transactions_to_deserialized_packets( transactions: &[Transaction], ) -> Result, DeserializedPacketError> { @@ -464,11 +445,8 @@ mod tests { super::*, solana_perf::packet::PacketFlags, solana_sdk::{ - compute_budget::ComputeBudgetInstruction, - message::VersionedMessage, - pubkey::Pubkey, signature::{Keypair, Signer}, - system_instruction, system_transaction, + system_transaction, transaction::{SimpleAddressLoader, Transaction}, }, solana_vote_program::vote_transaction, @@ -600,72 +578,6 @@ mod tests { assert!(unprocessed_packet_batches.pop_max_n(0).is_none()); } - #[test] - fn test_get_priority_with_valid_request_heap_frame_tx() { - let payer = Pubkey::new_unique(); - let message = SanitizedVersionedMessage::try_from(VersionedMessage::Legacy(Message::new( - &[ - system_instruction::transfer(&Pubkey::new_unique(), &Pubkey::new_unique(), 1), - ComputeBudgetInstruction::request_heap_frame(32 * 1024), - ], - Some(&payer), - ))) - .unwrap(); - assert_eq!( - get_priority_details(&message), - Some(TransactionPriorityDetails { - priority: 0, - compute_unit_limit: - solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT - as u64 - }) - ); - } - - #[test] - fn test_get_priority_with_valid_set_compute_units_limit() { - let requested_cu = 101u32; - let payer = Pubkey::new_unique(); - let message = SanitizedVersionedMessage::try_from(VersionedMessage::Legacy(Message::new( - &[ - system_instruction::transfer(&Pubkey::new_unique(), &Pubkey::new_unique(), 1), - ComputeBudgetInstruction::set_compute_unit_limit(requested_cu), - ], - Some(&payer), - ))) - .unwrap(); - assert_eq!( - get_priority_details(&message), - Some(TransactionPriorityDetails { - priority: 0, - compute_unit_limit: requested_cu as u64, - }) - ); - } - - #[test] - fn test_get_priority_with_valid_set_compute_unit_price() { - let requested_price = 1_000; - let payer = Pubkey::new_unique(); - let message = SanitizedVersionedMessage::try_from(VersionedMessage::Legacy(Message::new( - &[ - system_instruction::transfer(&Pubkey::new_unique(), &Pubkey::new_unique(), 1), - ComputeBudgetInstruction::set_compute_unit_price(requested_price), - ], - Some(&payer), - ))) - .unwrap(); - assert_eq!( - get_priority_details(&message), - Some(TransactionPriorityDetails { - priority: requested_price, - compute_unit_limit: - solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT - as u64 - }) - ); - } - #[cfg(test)] fn make_test_packets( transactions: Vec,