diff --git a/core/src/banking_stage/transaction_scheduler/receive_and_buffer.rs b/core/src/banking_stage/transaction_scheduler/receive_and_buffer.rs deleted file mode 100644 index 21afe448d151d3..00000000000000 --- a/core/src/banking_stage/transaction_scheduler/receive_and_buffer.rs +++ /dev/null @@ -1,376 +0,0 @@ -use { - super::{ - scheduler_metrics::{SchedulerCountMetrics, SchedulerTimingMetrics}, - transaction_state_container::StateContainer, - }, - crate::banking_stage::{ - decision_maker::BufferedPacketsDecision, - immutable_deserialized_packet::ImmutableDeserializedPacket, - packet_deserializer::PacketDeserializer, scheduler_messages::MaxAge, - transaction_scheduler::transaction_state::SanitizedTransactionTTL, - TransactionStateContainer, - }, - arrayvec::ArrayVec, - core::time::Duration, - crossbeam_channel::RecvTimeoutError, - solana_accounts_db::account_locks::validate_account_locks, - solana_cost_model::cost_model::CostModel, - solana_measure::measure_us, - solana_runtime::{bank::Bank, bank_forks::BankForks}, - solana_runtime_transaction::{ - runtime_transaction::RuntimeTransaction, transaction_meta::StaticMeta, - transaction_with_meta::TransactionWithMeta, - }, - solana_sdk::{ - address_lookup_table::state::estimate_last_valid_slot, - clock::{Epoch, Slot, MAX_PROCESSING_AGE}, - fee::FeeBudgetLimits, - saturating_add_assign, - transaction::SanitizedTransaction, - }, - solana_svm::transaction_error_metrics::TransactionErrorMetrics, - std::sync::{Arc, RwLock}, -}; - -pub(crate) trait ReceiveAndBuffer { - type Transaction: TransactionWithMeta; - type Container: StateContainer; - - /// Returns whether the packet receiver is still connected. - fn receive_and_buffer_packets( - &mut self, - container: &mut Self::Container, - timing_metrics: &mut SchedulerTimingMetrics, - count_metrics: &mut SchedulerCountMetrics, - decision: &BufferedPacketsDecision, - ) -> bool; -} - -pub(crate) struct SanitizedTransactionReceiveAndBuffer { - /// Packet/Transaction ingress. - packet_receiver: PacketDeserializer, - bank_forks: Arc>, - - forwarding_enabled: bool, -} - -impl ReceiveAndBuffer for SanitizedTransactionReceiveAndBuffer { - type Transaction = RuntimeTransaction; - type Container = TransactionStateContainer; - - /// Returns whether the packet receiver is still connected. - fn receive_and_buffer_packets( - &mut self, - container: &mut Self::Container, - timing_metrics: &mut SchedulerTimingMetrics, - count_metrics: &mut SchedulerCountMetrics, - decision: &BufferedPacketsDecision, - ) -> bool { - let remaining_queue_capacity = container.remaining_capacity(); - - const MAX_PACKET_RECEIVE_TIME: Duration = Duration::from_millis(10); - let (recv_timeout, should_buffer) = match decision { - BufferedPacketsDecision::Consume(_) => ( - if container.is_empty() { - MAX_PACKET_RECEIVE_TIME - } else { - Duration::ZERO - }, - true, - ), - BufferedPacketsDecision::Forward => (MAX_PACKET_RECEIVE_TIME, self.forwarding_enabled), - BufferedPacketsDecision::ForwardAndHold | BufferedPacketsDecision::Hold => { - (MAX_PACKET_RECEIVE_TIME, true) - } - }; - - let (received_packet_results, receive_time_us) = measure_us!(self - .packet_receiver - .receive_packets(recv_timeout, remaining_queue_capacity, |packet| { - packet.check_excessive_precompiles()?; - Ok(packet) - })); - - timing_metrics.update(|timing_metrics| { - saturating_add_assign!(timing_metrics.receive_time_us, receive_time_us); - }); - - match received_packet_results { - Ok(receive_packet_results) => { - let num_received_packets = receive_packet_results.deserialized_packets.len(); - - count_metrics.update(|count_metrics| { - saturating_add_assign!(count_metrics.num_received, num_received_packets); - }); - - if should_buffer { - let (_, buffer_time_us) = measure_us!(self.buffer_packets( - container, - timing_metrics, - count_metrics, - receive_packet_results.deserialized_packets - )); - timing_metrics.update(|timing_metrics| { - saturating_add_assign!(timing_metrics.buffer_time_us, buffer_time_us); - }); - } else { - count_metrics.update(|count_metrics| { - saturating_add_assign!( - count_metrics.num_dropped_on_receive, - num_received_packets - ); - }); - } - } - Err(RecvTimeoutError::Timeout) => {} - Err(RecvTimeoutError::Disconnected) => return false, - } - - true - } -} - -impl SanitizedTransactionReceiveAndBuffer { - pub fn new( - packet_receiver: PacketDeserializer, - bank_forks: Arc>, - forwarding_enabled: bool, - ) -> Self { - Self { - packet_receiver, - bank_forks, - forwarding_enabled, - } - } - - fn buffer_packets( - &mut self, - container: &mut TransactionStateContainer>, - _timing_metrics: &mut SchedulerTimingMetrics, - count_metrics: &mut SchedulerCountMetrics, - packets: Vec, - ) { - // Convert to Arcs - let packets: Vec<_> = packets.into_iter().map(Arc::new).collect(); - // Sanitize packets, generate IDs, and insert into the container. - let (root_bank, working_bank) = { - let bank_forks = self.bank_forks.read().unwrap(); - let root_bank = bank_forks.root_bank(); - let working_bank = bank_forks.working_bank(); - (root_bank, working_bank) - }; - let alt_resolved_slot = root_bank.slot(); - let sanitized_epoch = root_bank.epoch(); - let transaction_account_lock_limit = working_bank.get_transaction_account_lock_limit(); - let vote_only = working_bank.vote_only_bank(); - - const CHUNK_SIZE: usize = 128; - let lock_results: [_; CHUNK_SIZE] = core::array::from_fn(|_| Ok(())); - - let mut arc_packets = ArrayVec::<_, CHUNK_SIZE>::new(); - let mut transactions = ArrayVec::<_, CHUNK_SIZE>::new(); - let mut max_ages = ArrayVec::<_, CHUNK_SIZE>::new(); - let mut fee_budget_limits_vec = ArrayVec::<_, CHUNK_SIZE>::new(); - - let mut error_counts = TransactionErrorMetrics::default(); - for chunk in packets.chunks(CHUNK_SIZE) { - let mut post_sanitization_count: usize = 0; - chunk - .iter() - .filter_map(|packet| { - packet - .build_sanitized_transaction( - vote_only, - root_bank.as_ref(), - root_bank.get_reserved_account_keys(), - ) - .map(|(tx, deactivation_slot)| (packet.clone(), tx, deactivation_slot)) - }) - .inspect(|_| saturating_add_assign!(post_sanitization_count, 1)) - .filter(|(_packet, tx, _deactivation_slot)| { - validate_account_locks( - tx.message().account_keys(), - transaction_account_lock_limit, - ) - .is_ok() - }) - .filter_map(|(packet, tx, deactivation_slot)| { - tx.compute_budget_instruction_details() - .sanitize_and_convert_to_compute_budget_limits(&working_bank.feature_set) - .map(|compute_budget| { - (packet, tx, deactivation_slot, compute_budget.into()) - }) - .ok() - }) - .for_each(|(packet, tx, deactivation_slot, fee_budget_limits)| { - arc_packets.push(packet); - transactions.push(tx); - max_ages.push(calculate_max_age( - sanitized_epoch, - deactivation_slot, - alt_resolved_slot, - )); - fee_budget_limits_vec.push(fee_budget_limits); - }); - - let check_results = working_bank.check_transactions( - &transactions, - &lock_results[..transactions.len()], - MAX_PROCESSING_AGE, - &mut error_counts, - ); - let post_lock_validation_count = transactions.len(); - - let mut post_transaction_check_count: usize = 0; - let mut num_dropped_on_capacity: usize = 0; - let mut num_buffered: usize = 0; - for ((((packet, transaction), max_age), fee_budget_limits), _check_result) in - arc_packets - .drain(..) - .zip(transactions.drain(..)) - .zip(max_ages.drain(..)) - .zip(fee_budget_limits_vec.drain(..)) - .zip(check_results) - .filter(|(_, check_result)| check_result.is_ok()) - { - saturating_add_assign!(post_transaction_check_count, 1); - - let (priority, cost) = - calculate_priority_and_cost(&transaction, &fee_budget_limits, &working_bank); - let transaction_ttl = SanitizedTransactionTTL { - transaction, - max_age, - }; - - if container.insert_new_transaction(transaction_ttl, packet, priority, cost) { - saturating_add_assign!(num_dropped_on_capacity, 1); - } - saturating_add_assign!(num_buffered, 1); - } - - // Update metrics for transactions that were dropped. - let num_dropped_on_sanitization = chunk.len().saturating_sub(post_sanitization_count); - let num_dropped_on_lock_validation = - post_sanitization_count.saturating_sub(post_lock_validation_count); - let num_dropped_on_transaction_checks = - post_lock_validation_count.saturating_sub(post_transaction_check_count); - - count_metrics.update(|count_metrics| { - saturating_add_assign!( - count_metrics.num_dropped_on_capacity, - num_dropped_on_capacity - ); - saturating_add_assign!(count_metrics.num_buffered, num_buffered); - saturating_add_assign!( - count_metrics.num_dropped_on_sanitization, - num_dropped_on_sanitization - ); - saturating_add_assign!( - count_metrics.num_dropped_on_validate_locks, - num_dropped_on_lock_validation - ); - saturating_add_assign!( - count_metrics.num_dropped_on_receive_transaction_checks, - num_dropped_on_transaction_checks - ); - }); - } - } -} - -/// Calculate priority and cost for a transaction: -/// -/// Cost is calculated through the `CostModel`, -/// and priority is calculated through a formula here that attempts to sell -/// blockspace to the highest bidder. -/// -/// The priority is calculated as: -/// P = R / (1 + C) -/// where P is the priority, R is the reward, -/// and C is the cost towards block-limits. -/// -/// Current minimum costs are on the order of several hundred, -/// so the denominator is effectively C, and the +1 is simply -/// to avoid any division by zero due to a bug - these costs -/// are calculated by the cost-model and are not direct -/// from user input. They should never be zero. -/// Any difference in the prioritization is negligible for -/// the current transaction costs. -fn calculate_priority_and_cost( - transaction: &RuntimeTransaction, - fee_budget_limits: &FeeBudgetLimits, - bank: &Bank, -) -> (u64, u64) { - let cost = CostModel::calculate_cost(transaction, &bank.feature_set).sum(); - let reward = bank.calculate_reward_for_transaction(transaction, fee_budget_limits); - - // We need a multiplier here to avoid rounding down too aggressively. - // For many transactions, the cost will be greater than the fees in terms of raw lamports. - // For the purposes of calculating prioritization, we multiply the fees by a large number so that - // the cost is a small fraction. - // An offset of 1 is used in the denominator to explicitly avoid division by zero. - const MULTIPLIER: u64 = 1_000_000; - ( - reward - .saturating_mul(MULTIPLIER) - .saturating_div(cost.saturating_add(1)), - cost, - ) -} - -/// Given the epoch, the minimum deactivation slot, and the current slot, -/// return the `MaxAge` that should be used for the transaction. This is used -/// to determine the maximum slot that a transaction will be considered valid -/// for, without re-resolving addresses or resanitizing. -/// -/// This function considers the deactivation period of Address Table -/// accounts. If the deactivation period runs past the end of the epoch, -/// then the transaction is considered valid until the end of the epoch. -/// Otherwise, the transaction is considered valid until the deactivation -/// period. -/// -/// Since the deactivation period technically uses blocks rather than -/// slots, the value used here is the lower-bound on the deactivation -/// period, i.e. the transaction's address lookups are valid until -/// AT LEAST this slot. -fn calculate_max_age( - sanitized_epoch: Epoch, - deactivation_slot: Slot, - current_slot: Slot, -) -> MaxAge { - let alt_min_expire_slot = estimate_last_valid_slot(deactivation_slot.min(current_slot)); - MaxAge { - sanitized_epoch, - alt_invalidation_slot: alt_min_expire_slot, - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_calculate_max_age() { - let current_slot = 100; - let sanitized_epoch = 10; - - // ALT deactivation slot is delayed - assert_eq!( - calculate_max_age(sanitized_epoch, current_slot - 1, current_slot), - MaxAge { - sanitized_epoch, - alt_invalidation_slot: current_slot - 1 - + solana_sdk::slot_hashes::get_entries() as u64, - } - ); - - // no deactivation slot - assert_eq!( - calculate_max_age(sanitized_epoch, u64::MAX, current_slot), - MaxAge { - sanitized_epoch, - alt_invalidation_slot: current_slot + solana_sdk::slot_hashes::get_entries() as u64, - } - ); - } -} diff --git a/programs/compute-budget-bench/benches/compute_budget.rs b/programs/compute-budget-bench/benches/compute_budget.rs deleted file mode 100644 index 7c758d45c7ed05..00000000000000 --- a/programs/compute-budget-bench/benches/compute_budget.rs +++ /dev/null @@ -1,155 +0,0 @@ -use { - criterion::{black_box, criterion_group, criterion_main, Criterion}, - solana_compute_budget::compute_budget_limits::ComputeBudgetLimits, - solana_runtime_transaction::instructions_processor::process_compute_budget_instructions, - solana_sdk::{ - compute_budget::ComputeBudgetInstruction, feature_set::FeatureSet, - instruction::CompiledInstruction, - }, - solana_svm_transaction::instruction::SVMInstruction, - std::num::NonZero, -}; - -const ONE_PAGE: u32 = 32 * 1024; -const SIXTY_FOUR_MB: u32 = 64 * 1024 * 1024; - -fn bench_request_heap_frame(c: &mut Criterion) { - let instruction = [( - solana_sdk::compute_budget::id(), - CompiledInstruction::new_from_raw_parts( - 0, - ComputeBudgetInstruction::request_heap_frame(ONE_PAGE).data, - vec![], - ), - )]; - let feature_set = FeatureSet::default(); - - c.bench_function("request_heap_limit", |bencher| { - bencher.iter(|| { - assert_eq!( - process_compute_budget_instructions( - black_box( - instruction - .iter() - .map(|(id, ix)| (id, SVMInstruction::from(ix))) - ), - black_box(&feature_set) - ), - Ok(ComputeBudgetLimits { - updated_heap_bytes: ONE_PAGE, - compute_unit_limit: 0, - compute_unit_price: 0, - loaded_accounts_bytes: NonZero::new(SIXTY_FOUR_MB).unwrap() - }) - ) - }) - }); -} - -fn bench_set_compute_unit_limit(c: &mut Criterion) { - let instruction = [( - solana_sdk::compute_budget::id(), - CompiledInstruction::new_from_raw_parts( - 0, - ComputeBudgetInstruction::set_compute_unit_limit(1024).data, - vec![], - ), - )]; - let feature_set = FeatureSet::default(); - - c.bench_function("set_compute_unit_limit", |bencher| { - bencher.iter(|| { - assert_eq!( - process_compute_budget_instructions( - black_box( - instruction - .iter() - .map(|(id, ix)| (id, SVMInstruction::from(ix))) - ), - black_box(&feature_set) - ), - Ok(ComputeBudgetLimits { - updated_heap_bytes: ONE_PAGE, - compute_unit_limit: 1024, - compute_unit_price: 0, - loaded_accounts_bytes: NonZero::new(SIXTY_FOUR_MB).unwrap() - }) - ) - }) - }); -} - -fn bench_set_compute_unit_price(c: &mut Criterion) { - let instruction = [( - solana_sdk::compute_budget::id(), - CompiledInstruction::new_from_raw_parts( - 0, - ComputeBudgetInstruction::set_compute_unit_price(1).data, - vec![], - ), - )]; - let feature_set = FeatureSet::default(); - - c.bench_function("set_compute_unit_price", |bencher| { - bencher.iter(|| { - assert_eq!( - process_compute_budget_instructions( - black_box( - instruction - .iter() - .map(|(id, ix)| (id, SVMInstruction::from(ix))) - ), - black_box(&feature_set) - ), - Ok(ComputeBudgetLimits { - updated_heap_bytes: ONE_PAGE, - compute_unit_limit: 0, - compute_unit_price: 1, - loaded_accounts_bytes: NonZero::new(SIXTY_FOUR_MB).unwrap() - }) - ) - }) - }); -} - -fn bench_set_loaded_accounts_data_size_limit(c: &mut Criterion) { - let instruction = [( - solana_sdk::compute_budget::id(), - CompiledInstruction::new_from_raw_parts( - 0, - ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(1).data, - vec![], - ), - )]; - let feature_set = FeatureSet::default(); - - c.bench_function("set_loaded_accounts_data_size_limit", |bencher| { - bencher.iter(|| { - assert_eq!( - process_compute_budget_instructions( - black_box( - instruction - .iter() - .map(|(id, ix)| (id, SVMInstruction::from(ix))) - ), - black_box(&feature_set) - ), - Ok(ComputeBudgetLimits { - updated_heap_bytes: ONE_PAGE, - compute_unit_limit: 0, - compute_unit_price: 0, - loaded_accounts_bytes: NonZero::new(1).unwrap() - }) - ) - }) - }); -} - -criterion_group!( - benches, - bench_request_heap_frame, - bench_set_compute_unit_limit, - bench_set_compute_unit_price, - bench_set_loaded_accounts_data_size_limit, -); -criterion_main!(benches); diff --git a/runtime-transaction/src/runtime_transaction/sdk_transactions.rs b/runtime-transaction/src/runtime_transaction/sdk_transactions.rs deleted file mode 100644 index a82e9c1217fff6..00000000000000 --- a/runtime-transaction/src/runtime_transaction/sdk_transactions.rs +++ /dev/null @@ -1,346 +0,0 @@ -use { - super::{ComputeBudgetInstructionDetails, RuntimeTransaction}, - crate::{ - signature_details::get_precompile_signature_details, - transaction_meta::{StaticMeta, TransactionMeta}, - transaction_with_meta::TransactionWithMeta, - }, - solana_pubkey::Pubkey, - solana_sdk::{ - message::{AddressLoader, TransactionSignatureDetails}, - simple_vote_transaction_checker::is_simple_vote_transaction, - transaction::{ - MessageHash, Result, SanitizedTransaction, SanitizedVersionedTransaction, - VersionedTransaction, - }, - }, - solana_svm_transaction::instruction::SVMInstruction, - std::{borrow::Cow, collections::HashSet}, -}; - -impl RuntimeTransaction { - pub fn try_from( - sanitized_versioned_tx: SanitizedVersionedTransaction, - 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 precompile_signature_details = get_precompile_signature_details( - sanitized_versioned_tx - .get_message() - .program_instructions_iter() - .map(|(program_id, ix)| (program_id, SVMInstruction::from(ix))), - ); - let signature_details = TransactionSignatureDetails::new( - u64::from( - sanitized_versioned_tx - .get_message() - .message - .header() - .num_required_signatures, - ), - precompile_signature_details.num_secp256k1_instruction_signatures, - precompile_signature_details.num_ed25519_instruction_signatures, - precompile_signature_details.num_secp256r1_instruction_signatures, - ); - let compute_budget_instruction_details = ComputeBudgetInstructionDetails::try_from( - sanitized_versioned_tx - .get_message() - .program_instructions_iter() - .map(|(program_id, ix)| (program_id, SVMInstruction::from(ix))), - )?; - - Ok(Self { - transaction: sanitized_versioned_tx, - meta: TransactionMeta { - message_hash, - is_simple_vote_transaction: is_simple_vote_tx, - signature_details, - compute_budget_instruction_details, - }, - }) - } -} - -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, - reserved_account_keys: &HashSet, - ) -> Result { - let hash = *statically_loaded_runtime_tx.message_hash(); - let is_simple_vote_tx = statically_loaded_runtime_tx.is_simple_vote_transaction(); - let sanitized_transaction = SanitizedTransaction::try_new( - statically_loaded_runtime_tx.transaction, - hash, - is_simple_vote_tx, - address_loader, - reserved_account_keys, - )?; - - let mut tx = Self { - transaction: sanitized_transaction, - meta: statically_loaded_runtime_tx.meta, - }; - tx.load_dynamic_metadata()?; - - Ok(tx) - } - - fn load_dynamic_metadata(&mut self) -> Result<()> { - Ok(()) - } -} - -impl TransactionWithMeta for RuntimeTransaction { - #[inline] - fn as_sanitized_transaction(&self) -> Cow { - Cow::Borrowed(self) - } - - #[inline] - fn to_versioned_transaction(&self) -> VersionedTransaction { - self.transaction.to_versioned_transaction() - } -} - -#[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(test)] -mod tests { - use { - super::*, - solana_program::{ - system_instruction, - vote::{self, state::Vote}, - }, - solana_sdk::{ - compute_budget::ComputeBudgetInstruction, - feature_set::FeatureSet, - hash::Hash, - instruction::Instruction, - message::Message, - reserved_account_keys::ReservedAccountKeys, - signer::{keypair::Keypair, Signer}, - transaction::{SimpleAddressLoader, Transaction, VersionedTransaction}, - }, - }; - - fn vote_sanitized_versioned_transaction() -> SanitizedVersionedTransaction { - let bank_hash = Hash::new_unique(); - let block_hash = Hash::new_unique(); - let vote_keypair = Keypair::new(); - let node_keypair = Keypair::new(); - let auth_keypair = Keypair::new(); - let votes = Vote::new(vec![1, 2, 3], bank_hash); - let vote_ix = - vote::instruction::vote(&vote_keypair.pubkey(), &auth_keypair.pubkey(), votes); - let mut vote_tx = Transaction::new_with_payer(&[vote_ix], Some(&node_keypair.pubkey())); - vote_tx.partial_sign(&[&node_keypair], block_hash); - vote_tx.partial_sign(&[&auth_keypair], block_hash); - - SanitizedVersionedTransaction::try_from(VersionedTransaction::from(vote_tx)).unwrap() - } - - fn non_vote_sanitized_versioned_transaction() -> SanitizedVersionedTransaction { - TestTransaction::new().to_sanitized_versioned_transaction() - } - - // Simple transfer transaction for testing, it does not support vote instruction - // because simple vote transaction will not request limits - struct TestTransaction { - from_keypair: Keypair, - hash: Hash, - instructions: Vec, - } - - impl TestTransaction { - fn new() -> Self { - let from_keypair = Keypair::new(); - let instructions = vec![system_instruction::transfer( - &from_keypair.pubkey(), - &solana_sdk::pubkey::new_rand(), - 1, - )]; - TestTransaction { - from_keypair, - hash: Hash::new_unique(), - instructions, - } - } - - fn add_compute_unit_limit(&mut self, val: u32) -> &mut TestTransaction { - self.instructions - .push(ComputeBudgetInstruction::set_compute_unit_limit(val)); - self - } - - fn add_compute_unit_price(&mut self, val: u64) -> &mut TestTransaction { - self.instructions - .push(ComputeBudgetInstruction::set_compute_unit_price(val)); - self - } - - fn add_loaded_accounts_bytes(&mut self, val: u32) -> &mut TestTransaction { - self.instructions - .push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(val)); - self - } - - fn to_sanitized_versioned_transaction(&self) -> SanitizedVersionedTransaction { - let message = Message::new(&self.instructions, Some(&self.from_keypair.pubkey())); - let tx = Transaction::new(&[&self.from_keypair], message, self.hash); - SanitizedVersionedTransaction::try_from(VersionedTransaction::from(tx)).unwrap() - } - } - - #[test] - fn test_runtime_transaction_is_vote_meta() { - fn get_is_simple_vote( - svt: SanitizedVersionedTransaction, - is_simple_vote: Option, - ) -> bool { - RuntimeTransaction::::try_from( - svt, - MessageHash::Compute, - is_simple_vote, - ) - .unwrap() - .meta - .is_simple_vote_transaction - } - - assert!(!get_is_simple_vote( - non_vote_sanitized_versioned_transaction(), - None - )); - - assert!(get_is_simple_vote( - non_vote_sanitized_versioned_transaction(), - Some(true), // override - )); - - assert!(get_is_simple_vote( - vote_sanitized_versioned_transaction(), - None - )); - - assert!(!get_is_simple_vote( - vote_sanitized_versioned_transaction(), - Some(false), // override - )); - } - - #[test] - fn test_advancing_transaction_type() { - let hash = Hash::new_unique(); - - let statically_loaded_transaction = - RuntimeTransaction::::try_from( - non_vote_sanitized_versioned_transaction(), - MessageHash::Precomputed(hash), - None, - ) - .unwrap(); - - assert_eq!(hash, *statically_loaded_transaction.message_hash()); - assert!(!statically_loaded_transaction.is_simple_vote_transaction()); - - let dynamically_loaded_transaction = RuntimeTransaction::::try_from( - statically_loaded_transaction, - SimpleAddressLoader::Disabled, - &ReservedAccountKeys::empty_key_set(), - ); - let dynamically_loaded_transaction = - dynamically_loaded_transaction.expect("created from statically loaded tx"); - - assert_eq!(hash, *dynamically_loaded_transaction.message_hash()); - assert!(!dynamically_loaded_transaction.is_simple_vote_transaction()); - } - - #[test] - fn test_runtime_transaction_static_meta() { - let hash = Hash::new_unique(); - let compute_unit_limit = 250_000; - let compute_unit_price = 1_000; - let loaded_accounts_bytes = 1_024; - let mut test_transaction = TestTransaction::new(); - - let runtime_transaction_static = - RuntimeTransaction::::try_from( - test_transaction - .add_compute_unit_limit(compute_unit_limit) - .add_compute_unit_price(compute_unit_price) - .add_loaded_accounts_bytes(loaded_accounts_bytes) - .to_sanitized_versioned_transaction(), - MessageHash::Precomputed(hash), - None, - ) - .unwrap(); - - assert_eq!(&hash, runtime_transaction_static.message_hash()); - assert!(!runtime_transaction_static.is_simple_vote_transaction()); - - let signature_details = &runtime_transaction_static.meta.signature_details; - assert_eq!(1, signature_details.num_transaction_signatures()); - assert_eq!(0, signature_details.num_secp256k1_instruction_signatures()); - assert_eq!(0, signature_details.num_ed25519_instruction_signatures()); - - for feature_set in [FeatureSet::default(), FeatureSet::all_enabled()] { - let compute_budget_limits = runtime_transaction_static - .compute_budget_instruction_details() - .sanitize_and_convert_to_compute_budget_limits(&feature_set) - .unwrap(); - assert_eq!(compute_unit_limit, compute_budget_limits.compute_unit_limit); - assert_eq!(compute_unit_price, compute_budget_limits.compute_unit_price); - assert_eq!( - loaded_accounts_bytes, - compute_budget_limits.loaded_accounts_bytes.get() - ); - } - } -}