Skip to content

Commit

Permalink
wrap fee/nonce validation in one function
Browse files Browse the repository at this point in the history
  • Loading branch information
2501babe committed Nov 4, 2024
1 parent af8ef47 commit 42ef120
Showing 1 changed file with 93 additions and 77 deletions.
170 changes: 93 additions & 77 deletions svm/src/transaction_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use {
},
account_overrides::AccountOverrides,
message_processor::MessageProcessor,
nonce_info::NonceInfo,
program_loader::{get_program_modification_slot, load_program_with_pubkey},
rollback_accounts::RollbackAccounts,
transaction_account_state_info::TransactionAccountStateInfo,
Expand Down Expand Up @@ -313,28 +314,13 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
// in the same batch may modify the same accounts. Transaction order is
// preserved within entries written to the ledger.
for (tx, check_result) in sanitized_txs.iter().zip(check_results) {
let (validate_result, single_validate_fees_us) = measure_us!(check_result
.and_then(|tx_details| {
// If this is a nonce transaction, validate the nonce info.
// This must be done for every transaction to supprt SIMD83 because
// it may have changed due to use, authorization, or deallocation.
// This function is a successful no-op if given a blockhash transaction.
Self::validate_transaction_nonce(
&mut account_loader,
tx,
&tx_details,
&environment.blockhash,
&mut error_metrics,
)?;

Ok(tx_details)
})
.and_then(|tx_details| {
// Now validate the fee-payer for the transaction unconditionally.
Self::validate_transaction_fee_payer(
let (validate_result, single_validate_fees_us) =
measure_us!(check_result.and_then(|tx_details| {
Self::validate_transaction_nonce_and_fee_payer(
&mut account_loader,
tx,
tx_details,
&environment.blockhash,
&environment.feature_set,
environment
.fee_structure
Expand Down Expand Up @@ -433,6 +419,47 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
}
}

fn validate_transaction_nonce_and_fee_payer<CB: TransactionProcessingCallback>(
account_loader: &mut AccountLoader<CB>,
message: &impl SVMMessage,
checked_details: CheckedTransactionDetails,
environment_blockhash: &Hash,
feature_set: &FeatureSet,
fee_structure: &FeeStructure,
rent_collector: &dyn SVMRentCollector,
error_counters: &mut TransactionErrorMetrics,
) -> transaction::Result<ValidatedTransactionDetails> {
// If this is a nonce transaction, validate the nonce info.
// This must be done for every transaction to support SIMD83 because
// it may have changed due to use, authorization, or deallocation.
// This function is a successful no-op if given a blockhash transaction.
if let CheckedTransactionDetails {
nonce: Some(ref nonce_info),
lamports_per_signature: _,
} = checked_details
{
let next_durable_nonce = DurableNonce::from_blockhash(environment_blockhash);
Self::validate_transaction_nonce(
account_loader,
message,
nonce_info,
&next_durable_nonce,
error_counters,
)?;
}

// Now validate the fee-payer for the transaction unconditionally.
Self::validate_transaction_fee_payer(
account_loader,
message,
checked_details,
feature_set,
fee_structure,
rent_collector,
error_counters,
)
}

// Loads transaction fee payer, collects rent if necessary, then calculates
// transaction fees, and deducts them from the fee payer balance. If the
// account is not found or has insufficient funds, an error is returned.
Expand Down Expand Up @@ -515,69 +542,58 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
fn validate_transaction_nonce<CB: TransactionProcessingCallback>(
account_loader: &mut AccountLoader<CB>,
message: &impl SVMMessage,
checked_details: &CheckedTransactionDetails,
environment_blockhash: &Hash,
nonce_info: &NonceInfo,
next_durable_nonce: &DurableNonce,
error_counters: &mut TransactionErrorMetrics,
) -> transaction::Result<()> {
if let CheckedTransactionDetails {
nonce: Some(nonce_info),
lamports_per_signature: _,
} = checked_details
{
let next_durable_nonce = DurableNonce::from_blockhash(environment_blockhash);

// When SIMD83 is enabled, if the nonce has been used in this batch already, we must drop
// the transaction. This is the same as if it was used in different batches in the same slot.
// If the nonce account was closed in the batch, we error as if the blockhash didn't validate.
// We must validate the account in case it was reopened, either as a normal system account,
// or a fake nonce account. We must also check the signer in case the authority was changed.
//
// Note these checks are *not* obviated by fee-only transactions.
let nonce_is_valid = account_loader
.load_account(nonce_info.address(), AccountUsagePattern::Writable)
.and_then(|loaded_nonce| {
let current_nonce_account = &loaded_nonce.account;
system_program::check_id(current_nonce_account.owner()).then_some(())?;
StateMut::<NonceVersions>::state(current_nonce_account).ok()
})
.and_then(
|current_nonce_versions| match current_nonce_versions.state() {
NonceState::Initialized(ref current_nonce_data) => {
let nonce_can_be_advanced =
current_nonce_data.durable_nonce != next_durable_nonce;

let nonce_authority_is_valid = message
.account_keys()
.iter()
.enumerate()
.any(|(i, address)| {
address == &current_nonce_data.authority && message.is_signer(i)
});

if nonce_authority_is_valid {
Some(nonce_can_be_advanced)
} else {
None
}
// When SIMD83 is enabled, if the nonce has been used in this batch already, we must drop
// the transaction. This is the same as if it was used in different batches in the same slot.
// If the nonce account was closed in the batch, we error as if the blockhash didn't validate.
// We must validate the account in case it was reopened, either as a normal system account,
// or a fake nonce account. We must also check the signer in case the authority was changed.
//
// Note these checks are *not* obviated by fee-only transactions.
let nonce_is_valid = account_loader
.load_account(nonce_info.address(), AccountUsagePattern::Writable)
.and_then(|loaded_nonce| {
let current_nonce_account = &loaded_nonce.account;
system_program::check_id(current_nonce_account.owner()).then_some(())?;
StateMut::<NonceVersions>::state(current_nonce_account).ok()
})
.and_then(
|current_nonce_versions| match current_nonce_versions.state() {
NonceState::Initialized(ref current_nonce_data) => {
let nonce_can_be_advanced =
&current_nonce_data.durable_nonce != next_durable_nonce;

let nonce_authority_is_valid = message
.account_keys()
.iter()
.enumerate()
.any(|(i, address)| {
address == &current_nonce_data.authority && message.is_signer(i)
});

if nonce_authority_is_valid {
Some(nonce_can_be_advanced)
} else {
None
}
_ => None,
},
);
}
_ => None,
},
);

match nonce_is_valid {
None => {
error_counters.blockhash_not_found += 1;
Err(TransactionError::BlockhashNotFound)
}
Some(false) => {
error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound)
}
Some(true) => Ok(()),
match nonce_is_valid {
None => {
error_counters.blockhash_not_found += 1;
Err(TransactionError::BlockhashNotFound)
}
} else {
// not a nonce transaction, nothing to do
Ok(())
Some(false) => {
error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound)
}
Some(true) => Ok(()),
}
}

Expand Down

0 comments on commit 42ef120

Please sign in to comment.