Skip to content

Commit

Permalink
CODE: split nonce validation into its own function
Browse files Browse the repository at this point in the history
  • Loading branch information
2501babe committed Oct 22, 2024
1 parent 581787a commit 8287561
Showing 1 changed file with 47 additions and 31 deletions.
78 changes: 47 additions & 31 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 @@ -494,38 +495,13 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
fee_details.total_fee(),
)?;

// 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
if let Some(ref advanced_nonce_info) = advanced_nonce {
let nonces_are_equal = account_loader
.load_account(advanced_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) => {
Some(&current_nonce_data.durable_nonce == durable_nonce)
}
_ => None,
},
);

match nonces_are_equal {
Some(false) => (),
Some(true) => {
error_counters.account_not_found += 1;
return Err(TransactionError::AccountNotFound);
}
None => {
error_counters.blockhash_not_found += 1;
return Err(TransactionError::BlockhashNotFound);
}
}
self.validate_transaction_nonce(
account_loader,
advanced_nonce_info,
durable_nonce,
error_counters,
)?;
}

// Capture fee-subtracted fee payer account and next nonce account state
Expand All @@ -546,6 +522,46 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
})
}

fn validate_transaction_nonce<CB: TransactionProcessingCallback>(
&self,
account_loader: &mut AccountLoader<CB>,
advanced_nonce_info: &NonceInfo,
durable_nonce: &DurableNonce,
error_counters: &mut TransactionErrorMetrics,
) -> transaction::Result<()> {
// 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
let nonces_are_equal = account_loader
.load_account(advanced_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) => {
Some(&current_nonce_data.durable_nonce == durable_nonce)
}
_ => None,
},
);

match nonces_are_equal {
None => {
error_counters.blockhash_not_found += 1;
Err(TransactionError::BlockhashNotFound)
}
Some(true) => {
error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound)
}
Some(false) => Ok(()),
}
}

/// Returns a map from executable program accounts (all accounts owned by any loader)
/// to their usage counters, for the transactions with a valid blockhash or nonce.
fn filter_executable_program_accounts<CB: TransactionProcessingCallback>(
Expand Down

0 comments on commit 8287561

Please sign in to comment.