Skip to content

Commit

Permalink
refactor: move bank check transaction logic to new module (#2328)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry authored Aug 5, 2024
1 parent c986303 commit 5e0956e
Show file tree
Hide file tree
Showing 3 changed files with 364 additions and 328 deletions.
182 changes: 8 additions & 174 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ use {
solana_cost_model::cost_tracker::CostTracker,
solana_loader_v4_program::create_program_runtime_environment_v2,
solana_measure::{measure::Measure, measure_time, measure_us},
solana_perf::perf_libs,
solana_program_runtime::{
invoke_context::BuiltinFunctionWithContext, loaded_programs::ProgramCacheEntry,
},
Expand All @@ -103,9 +102,9 @@ use {
clock::{
BankId, Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_HASHES_PER_TICK,
DEFAULT_TICKS_PER_SECOND, INITIAL_RENT_EPOCH, MAX_PROCESSING_AGE,
MAX_TRANSACTION_FORWARDING_DELAY, MAX_TRANSACTION_FORWARDING_DELAY_GPU,
SECONDS_PER_DAY, UPDATED_HASHES_PER_TICK2, UPDATED_HASHES_PER_TICK3,
UPDATED_HASHES_PER_TICK4, UPDATED_HASHES_PER_TICK5, UPDATED_HASHES_PER_TICK6,
MAX_TRANSACTION_FORWARDING_DELAY, SECONDS_PER_DAY, UPDATED_HASHES_PER_TICK2,
UPDATED_HASHES_PER_TICK3, UPDATED_HASHES_PER_TICK4, UPDATED_HASHES_PER_TICK5,
UPDATED_HASHES_PER_TICK6,
},
epoch_info::EpochInfo,
epoch_schedule::EpochSchedule,
Expand All @@ -124,8 +123,7 @@ use {
message::{AccountKeys, SanitizedMessage},
native_loader,
native_token::LAMPORTS_PER_SOL,
nonce::{self, state::DurableNonce, NONCED_TX_MARKER_IX_INDEX},
nonce_account,
nonce::state::DurableNonce,
packet::PACKET_DATA_SIZE,
precompiles::get_precompiles,
pubkey::Pubkey,
Expand All @@ -141,7 +139,7 @@ use {
sysvar::{self, last_restart_slot::LastRestartSlot, Sysvar, SysvarId},
timing::years_as_slots,
transaction::{
self, MessageHash, Result, SanitizedTransaction, Transaction, TransactionError,
MessageHash, Result, SanitizedTransaction, Transaction, TransactionError,
TransactionVerificationMode, VersionedTransaction, MAX_TX_ACCOUNT_LOCKS,
},
transaction_context::{TransactionAccount, TransactionReturnData},
Expand All @@ -151,12 +149,9 @@ use {
stake_state::StakeStateV2,
},
solana_svm::{
account_loader::{
collect_rent_from_account, CheckedTransactionDetails, TransactionCheckResult,
},
account_loader::collect_rent_from_account,
account_overrides::AccountOverrides,
account_saver::collect_accounts_to_store,
nonce_info::NonceInfo,
transaction_commit_result::{CommittedTransaction, TransactionCommitResult},
transaction_error_metrics::TransactionErrorMetrics,
transaction_execution_result::{
Expand Down Expand Up @@ -199,6 +194,7 @@ use {
ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
},
solana_program_runtime::{loaded_programs::ProgramCacheForTxBatch, sysvar_cache::SysvarCache},
solana_sdk::nonce,
solana_svm::program_loader::load_program_with_pubkey,
solana_system_program::{get_system_account_kind, SystemAccountKind},
};
Expand All @@ -216,6 +212,7 @@ mod address_lookup_table;
pub mod bank_hash_details;
mod builtin_programs;
pub mod builtins;
mod check_transactions;
pub mod epoch_accounts_hash_utils;
mod fee_distribution;
mod metrics;
Expand Down Expand Up @@ -3426,96 +3423,6 @@ impl Bank {
self.rc.accounts.accounts_db.remove_unrooted_slots(slots)
}

fn check_age(
&self,
sanitized_txs: &[impl core::borrow::Borrow<SanitizedTransaction>],
lock_results: &[Result<()>],
max_age: usize,
error_counters: &mut TransactionErrorMetrics,
) -> Vec<TransactionCheckResult> {
let hash_queue = self.blockhash_queue.read().unwrap();
let last_blockhash = hash_queue.last_hash();
let next_durable_nonce = DurableNonce::from_blockhash(&last_blockhash);

sanitized_txs
.iter()
.zip(lock_results)
.map(|(tx, lock_res)| match lock_res {
Ok(()) => self.check_transaction_age(
tx.borrow(),
max_age,
&next_durable_nonce,
&hash_queue,
error_counters,
),
Err(e) => Err(e.clone()),
})
.collect()
}

fn check_transaction_age(
&self,
tx: &SanitizedTransaction,
max_age: usize,
next_durable_nonce: &DurableNonce,
hash_queue: &BlockhashQueue,
error_counters: &mut TransactionErrorMetrics,
) -> TransactionCheckResult {
let recent_blockhash = tx.message().recent_blockhash();
if let Some(hash_info) = hash_queue.get_hash_info_if_valid(recent_blockhash, max_age) {
Ok(CheckedTransactionDetails {
nonce: None,
lamports_per_signature: hash_info.lamports_per_signature(),
})
} else if let Some((nonce, nonce_data)) =
self.check_and_load_message_nonce_account(tx.message(), next_durable_nonce)
{
Ok(CheckedTransactionDetails {
nonce: Some(nonce),
lamports_per_signature: nonce_data.get_lamports_per_signature(),
})
} else {
error_counters.blockhash_not_found += 1;
Err(TransactionError::BlockhashNotFound)
}
}

fn is_transaction_already_processed(
&self,
sanitized_tx: &SanitizedTransaction,
status_cache: &BankStatusCache,
) -> bool {
let key = sanitized_tx.message_hash();
let transaction_blockhash = sanitized_tx.message().recent_blockhash();
status_cache
.get_status(key, transaction_blockhash, &self.ancestors)
.is_some()
}

fn check_status_cache(
&self,
sanitized_txs: &[impl core::borrow::Borrow<SanitizedTransaction>],
lock_results: Vec<TransactionCheckResult>,
error_counters: &mut TransactionErrorMetrics,
) -> Vec<TransactionCheckResult> {
let rcache = self.status_cache.read().unwrap();
sanitized_txs
.iter()
.zip(lock_results)
.map(|(sanitized_tx, lock_result)| {
let sanitized_tx = sanitized_tx.borrow();
if lock_result.is_ok()
&& self.is_transaction_already_processed(sanitized_tx, &rcache)
{
error_counters.already_processed += 1;
return Err(TransactionError::AlreadyProcessed);
}

lock_result
})
.collect()
}

pub fn get_hash_age(&self, hash: &Hash) -> Option<u64> {
self.blockhash_queue.read().unwrap().get_hash_age(hash)
}
Expand All @@ -3527,49 +3434,6 @@ impl Bank {
.is_hash_valid_for_age(hash, max_age)
}

fn load_message_nonce_account(
&self,
message: &SanitizedMessage,
) -> Option<(NonceInfo, nonce::state::Data)> {
let nonce_address = message.get_durable_nonce()?;
let nonce_account = self.get_account_with_fixed_root(nonce_address)?;
let nonce_data =
nonce_account::verify_nonce_account(&nonce_account, message.recent_blockhash())?;

let nonce_is_authorized = message
.get_ix_signers(NONCED_TX_MARKER_IX_INDEX as usize)
.any(|signer| signer == &nonce_data.authority);
if !nonce_is_authorized {
return None;
}

Some((NonceInfo::new(*nonce_address, nonce_account), nonce_data))
}

fn check_and_load_message_nonce_account(
&self,
message: &SanitizedMessage,
next_durable_nonce: &DurableNonce,
) -> Option<(NonceInfo, nonce::state::Data)> {
let nonce_is_advanceable = message.recent_blockhash() != next_durable_nonce.as_hash();
if nonce_is_advanceable {
self.load_message_nonce_account(message)
} else {
None
}
}

pub fn check_transactions(
&self,
sanitized_txs: &[impl core::borrow::Borrow<SanitizedTransaction>],
lock_results: &[Result<()>],
max_age: usize,
error_counters: &mut TransactionErrorMetrics,
) -> Vec<TransactionCheckResult> {
let lock_results = self.check_age(sanitized_txs, lock_results, max_age, error_counters);
self.check_status_cache(sanitized_txs, lock_results, error_counters)
}

pub fn collect_balances(&self, batch: &TransactionBatch) -> TransactionBalances {
let mut balances: TransactionBalances = vec![];
for transaction in batch.sanitized_transactions() {
Expand Down Expand Up @@ -6545,36 +6409,6 @@ impl Bank {
.try_get_epoch_accounts_hash()
}

/// Checks a batch of sanitized transactions again bank for age and status
pub fn check_transactions_with_forwarding_delay(
&self,
transactions: &[SanitizedTransaction],
filter: &[transaction::Result<()>],
forward_transactions_to_leader_at_slot_offset: u64,
) -> Vec<TransactionCheckResult> {
let mut error_counters = TransactionErrorMetrics::default();
// The following code also checks if the blockhash for a transaction is too old
// The check accounts for
// 1. Transaction forwarding delay
// 2. The slot at which the next leader will actually process the transaction
// Drop the transaction if it will expire by the time the next node receives and processes it
let api = perf_libs::api();
let max_tx_fwd_delay = if api.is_none() {
MAX_TRANSACTION_FORWARDING_DELAY
} else {
MAX_TRANSACTION_FORWARDING_DELAY_GPU
};

self.check_transactions(
transactions,
filter,
(MAX_PROCESSING_AGE)
.saturating_sub(max_tx_fwd_delay)
.saturating_sub(forward_transactions_to_leader_at_slot_offset as usize),
&mut error_counters,
)
}

pub fn is_in_slot_hashes_history(&self, slot: &Slot) -> bool {
if slot < &self.slot {
if let Ok(slot_hashes) = self.transaction_processor.sysvar_cache().get_slot_hashes() {
Expand Down
Loading

0 comments on commit 5e0956e

Please sign in to comment.