* Refactor: Improve type safety and readability of transaction execution (#22215) * resolve conflicts Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
@@ -804,16 +804,17 @@ impl BankingStage {
|
|||||||
fn record_transactions(
|
fn record_transactions(
|
||||||
bank_slot: Slot,
|
bank_slot: Slot,
|
||||||
txs: &[SanitizedTransaction],
|
txs: &[SanitizedTransaction],
|
||||||
results: &[TransactionExecutionResult],
|
execution_results: &[TransactionExecutionResult],
|
||||||
recorder: &TransactionRecorder,
|
recorder: &TransactionRecorder,
|
||||||
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
||||||
let mut processed_generation = Measure::start("record::process_generation");
|
let mut processed_generation = Measure::start("record::process_generation");
|
||||||
let (processed_transactions, processed_transactions_indexes): (Vec<_>, Vec<_>) = results
|
let (processed_transactions, processed_transactions_indexes): (Vec<_>, Vec<_>) =
|
||||||
|
execution_results
|
||||||
.iter()
|
.iter()
|
||||||
.zip(txs)
|
.zip(txs)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, ((r, _n), tx))| {
|
.filter_map(|(i, (execution_result, tx))| {
|
||||||
if Bank::can_commit(r) {
|
if execution_result.was_executed() {
|
||||||
Some((tx.to_versioned_transaction(), i))
|
Some((tx.to_versioned_transaction(), i))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -885,15 +886,8 @@ impl BankingStage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut execute_timings = ExecuteTimings::default();
|
let mut execute_timings = ExecuteTimings::default();
|
||||||
let (
|
let (mut loaded_accounts, execution_results, mut retryable_txs, tx_count, signature_count) =
|
||||||
mut loaded_accounts,
|
bank.load_and_execute_transactions(
|
||||||
results,
|
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
mut retryable_txs,
|
|
||||||
tx_count,
|
|
||||||
signature_count,
|
|
||||||
) = bank.load_and_execute_transactions(
|
|
||||||
batch,
|
batch,
|
||||||
MAX_PROCESSING_AGE,
|
MAX_PROCESSING_AGE,
|
||||||
transaction_status_sender.is_some(),
|
transaction_status_sender.is_some(),
|
||||||
@@ -905,8 +899,12 @@ impl BankingStage {
|
|||||||
let freeze_lock = bank.freeze_lock();
|
let freeze_lock = bank.freeze_lock();
|
||||||
|
|
||||||
let mut record_time = Measure::start("record_time");
|
let mut record_time = Measure::start("record_time");
|
||||||
let (num_to_commit, retryable_record_txs) =
|
let (num_to_commit, retryable_record_txs) = Self::record_transactions(
|
||||||
Self::record_transactions(bank.slot(), batch.sanitized_transactions(), &results, poh);
|
bank.slot(),
|
||||||
|
batch.sanitized_transactions(),
|
||||||
|
&execution_results,
|
||||||
|
poh,
|
||||||
|
);
|
||||||
inc_new_counter_info!(
|
inc_new_counter_info!(
|
||||||
"banking_stage-record_transactions_num_to_commit",
|
"banking_stage-record_transactions_num_to_commit",
|
||||||
*num_to_commit.as_ref().unwrap_or(&0)
|
*num_to_commit.as_ref().unwrap_or(&0)
|
||||||
@@ -928,7 +926,7 @@ impl BankingStage {
|
|||||||
let tx_results = bank.commit_transactions(
|
let tx_results = bank.commit_transactions(
|
||||||
sanitized_txs,
|
sanitized_txs,
|
||||||
&mut loaded_accounts,
|
&mut loaded_accounts,
|
||||||
&results,
|
execution_results,
|
||||||
tx_count,
|
tx_count,
|
||||||
signature_count,
|
signature_count,
|
||||||
&mut execute_timings,
|
&mut execute_timings,
|
||||||
@@ -945,8 +943,6 @@ impl BankingStage {
|
|||||||
tx_results.execution_results,
|
tx_results.execution_results,
|
||||||
TransactionBalancesSet::new(pre_balances, post_balances),
|
TransactionBalancesSet::new(pre_balances, post_balances),
|
||||||
TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances),
|
TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances),
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
tx_results.rent_debits,
|
tx_results.rent_debits,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1609,6 +1605,7 @@ mod tests {
|
|||||||
poh_service::PohService,
|
poh_service::PohService,
|
||||||
},
|
},
|
||||||
solana_rpc::transaction_status_service::TransactionStatusService,
|
solana_rpc::transaction_status_service::TransactionStatusService,
|
||||||
|
solana_runtime::bank::TransactionExecutionDetails,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
@@ -1640,6 +1637,15 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_execution_result(status: Result<(), TransactionError>) -> TransactionExecutionResult {
|
||||||
|
TransactionExecutionResult::Executed(TransactionExecutionDetails {
|
||||||
|
status,
|
||||||
|
log_messages: None,
|
||||||
|
inner_instructions: None,
|
||||||
|
durable_nonce_fee: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_banking_stage_shutdown1() {
|
fn test_banking_stage_shutdown1() {
|
||||||
let genesis_config = create_genesis_config(2).genesis_config;
|
let genesis_config = create_genesis_config(2).genesis_config;
|
||||||
@@ -2027,19 +2033,16 @@ mod tests {
|
|||||||
system_transaction::transfer(&keypair2, &pubkey2, 1, genesis_config.hash()),
|
system_transaction::transfer(&keypair2, &pubkey2, 1, genesis_config.hash()),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let mut results = vec![(Ok(()), None), (Ok(()), None)];
|
let mut results = vec![new_execution_result(Ok(())); 2];
|
||||||
let _ = BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
|
let _ = BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
|
||||||
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
|
||||||
assert_eq!(entry.transactions.len(), txs.len());
|
assert_eq!(entry.transactions.len(), txs.len());
|
||||||
|
|
||||||
// InstructionErrors should still be recorded
|
// InstructionErrors should still be recorded
|
||||||
results[0] = (
|
results[0] = new_execution_result(Err(TransactionError::InstructionError(
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
1,
|
1,
|
||||||
SystemError::ResultWithNegativeLamports.into(),
|
SystemError::ResultWithNegativeLamports.into(),
|
||||||
)),
|
)));
|
||||||
None,
|
|
||||||
);
|
|
||||||
let (res, retryable) =
|
let (res, retryable) =
|
||||||
BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
|
BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
|
||||||
res.unwrap();
|
res.unwrap();
|
||||||
@@ -2048,7 +2051,7 @@ mod tests {
|
|||||||
assert_eq!(entry.transactions.len(), txs.len());
|
assert_eq!(entry.transactions.len(), txs.len());
|
||||||
|
|
||||||
// Other TransactionErrors should not be recorded
|
// Other TransactionErrors should not be recorded
|
||||||
results[0] = (Err(TransactionError::AccountNotFound), None);
|
results[0] = TransactionExecutionResult::NotExecuted(TransactionError::AccountNotFound);
|
||||||
let (res, retryable) =
|
let (res, retryable) =
|
||||||
BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
|
BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
|
||||||
res.unwrap();
|
res.unwrap();
|
||||||
|
@@ -20,8 +20,8 @@ use {
|
|||||||
accounts_index::AccountSecondaryIndexes,
|
accounts_index::AccountSecondaryIndexes,
|
||||||
accounts_update_notifier_interface::AccountsUpdateNotifier,
|
accounts_update_notifier_interface::AccountsUpdateNotifier,
|
||||||
bank::{
|
bank::{
|
||||||
Bank, ExecuteTimings, InnerInstructionsList, RentDebits, TransactionBalancesSet,
|
Bank, ExecuteTimings, RentDebits, TransactionBalancesSet, TransactionExecutionResult,
|
||||||
TransactionExecutionResult, TransactionLogMessages, TransactionResults,
|
TransactionResults,
|
||||||
},
|
},
|
||||||
bank_forks::BankForks,
|
bank_forks::BankForks,
|
||||||
bank_utils,
|
bank_utils,
|
||||||
@@ -175,8 +175,7 @@ fn execute_batch(
|
|||||||
|
|
||||||
let pre_process_units: u64 = aggregate_total_execution_units(timings);
|
let pre_process_units: u64 = aggregate_total_execution_units(timings);
|
||||||
|
|
||||||
let (tx_results, balances, inner_instructions, transaction_logs) =
|
let (tx_results, balances) = batch.bank().load_execute_and_commit_transactions(
|
||||||
batch.bank().load_execute_and_commit_transactions(
|
|
||||||
batch,
|
batch,
|
||||||
MAX_PROCESSING_AGE,
|
MAX_PROCESSING_AGE,
|
||||||
transaction_status_sender.is_some(),
|
transaction_status_sender.is_some(),
|
||||||
@@ -238,8 +237,6 @@ fn execute_batch(
|
|||||||
execution_results,
|
execution_results,
|
||||||
balances,
|
balances,
|
||||||
token_balances,
|
token_balances,
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
rent_debits,
|
rent_debits,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1399,11 +1396,9 @@ pub enum TransactionStatusMessage {
|
|||||||
pub struct TransactionStatusBatch {
|
pub struct TransactionStatusBatch {
|
||||||
pub bank: Arc<Bank>,
|
pub bank: Arc<Bank>,
|
||||||
pub transactions: Vec<SanitizedTransaction>,
|
pub transactions: Vec<SanitizedTransaction>,
|
||||||
pub statuses: Vec<TransactionExecutionResult>,
|
pub execution_results: Vec<TransactionExecutionResult>,
|
||||||
pub balances: TransactionBalancesSet,
|
pub balances: TransactionBalancesSet,
|
||||||
pub token_balances: TransactionTokenBalancesSet,
|
pub token_balances: TransactionTokenBalancesSet,
|
||||||
pub inner_instructions: Option<Vec<Option<InnerInstructionsList>>>,
|
|
||||||
pub transaction_logs: Option<Vec<Option<TransactionLogMessages>>>,
|
|
||||||
pub rent_debits: Vec<RentDebits>,
|
pub rent_debits: Vec<RentDebits>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1418,29 +1413,28 @@ impl TransactionStatusSender {
|
|||||||
&self,
|
&self,
|
||||||
bank: Arc<Bank>,
|
bank: Arc<Bank>,
|
||||||
transactions: Vec<SanitizedTransaction>,
|
transactions: Vec<SanitizedTransaction>,
|
||||||
statuses: Vec<TransactionExecutionResult>,
|
mut execution_results: Vec<TransactionExecutionResult>,
|
||||||
balances: TransactionBalancesSet,
|
balances: TransactionBalancesSet,
|
||||||
token_balances: TransactionTokenBalancesSet,
|
token_balances: TransactionTokenBalancesSet,
|
||||||
inner_instructions: Vec<Option<InnerInstructionsList>>,
|
|
||||||
transaction_logs: Vec<Option<TransactionLogMessages>>,
|
|
||||||
rent_debits: Vec<RentDebits>,
|
rent_debits: Vec<RentDebits>,
|
||||||
) {
|
) {
|
||||||
let slot = bank.slot();
|
let slot = bank.slot();
|
||||||
let (inner_instructions, transaction_logs) = if !self.enable_cpi_and_log_storage {
|
if !self.enable_cpi_and_log_storage {
|
||||||
(None, None)
|
execution_results.iter_mut().for_each(|execution_result| {
|
||||||
} else {
|
if let TransactionExecutionResult::Executed(details) = execution_result {
|
||||||
(Some(inner_instructions), Some(transaction_logs))
|
details.log_messages.take();
|
||||||
};
|
details.inner_instructions.take();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
.sender
|
.sender
|
||||||
.send(TransactionStatusMessage::Batch(TransactionStatusBatch {
|
.send(TransactionStatusMessage::Batch(TransactionStatusBatch {
|
||||||
bank,
|
bank,
|
||||||
transactions,
|
transactions,
|
||||||
statuses,
|
execution_results,
|
||||||
balances,
|
balances,
|
||||||
token_balances,
|
token_balances,
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
rent_debits,
|
rent_debits,
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
@@ -3483,8 +3477,6 @@ pub mod tests {
|
|||||||
..
|
..
|
||||||
},
|
},
|
||||||
_balances,
|
_balances,
|
||||||
_inner_instructions,
|
|
||||||
_log_messages,
|
|
||||||
) = batch.bank().load_execute_and_commit_transactions(
|
) = batch.bank().load_execute_and_commit_transactions(
|
||||||
&batch,
|
&batch,
|
||||||
MAX_PROCESSING_AGE,
|
MAX_PROCESSING_AGE,
|
||||||
|
@@ -416,7 +416,7 @@ fn setup_fees(bank: Bank) -> Bank {
|
|||||||
bank.commit_transactions(
|
bank.commit_transactions(
|
||||||
&[], // transactions
|
&[], // transactions
|
||||||
&mut [], // loaded accounts
|
&mut [], // loaded accounts
|
||||||
&[], // transaction execution results
|
vec![], // transaction execution results
|
||||||
0, // tx count
|
0, // tx count
|
||||||
1, // signature count
|
1, // signature count
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
|
@@ -17,7 +17,6 @@ use solana_bpf_loader_program::{
|
|||||||
use solana_bpf_rust_invoke::instructions::*;
|
use solana_bpf_rust_invoke::instructions::*;
|
||||||
use solana_bpf_rust_realloc::instructions::*;
|
use solana_bpf_rust_realloc::instructions::*;
|
||||||
use solana_bpf_rust_realloc_invoke::instructions::*;
|
use solana_bpf_rust_realloc_invoke::instructions::*;
|
||||||
use solana_cli_output::display::println_transaction;
|
|
||||||
use solana_program_runtime::invoke_context::with_mock_invoke_context;
|
use solana_program_runtime::invoke_context::with_mock_invoke_context;
|
||||||
use solana_rbpf::{
|
use solana_rbpf::{
|
||||||
elf::Executable,
|
elf::Executable,
|
||||||
@@ -25,7 +24,10 @@ use solana_rbpf::{
|
|||||||
vm::{Config, Tracer},
|
vm::{Config, Tracer},
|
||||||
};
|
};
|
||||||
use solana_runtime::{
|
use solana_runtime::{
|
||||||
bank::{Bank, ExecuteTimings, NonceInfo, TransactionBalancesSet, TransactionResults},
|
bank::{
|
||||||
|
Bank, DurableNonceFee, ExecuteTimings, TransactionBalancesSet, TransactionExecutionDetails,
|
||||||
|
TransactionExecutionResult, TransactionResults,
|
||||||
|
},
|
||||||
bank_client::BankClient,
|
bank_client::BankClient,
|
||||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
loader_utils::{
|
loader_utils::{
|
||||||
@@ -53,12 +55,13 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use solana_transaction_status::{
|
use solana_transaction_status::{
|
||||||
token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions,
|
token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions,
|
||||||
TransactionStatusMeta, TransactionWithStatusMeta, UiTransactionEncoding,
|
TransactionStatusMeta, TransactionWithStatusMeta,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap, convert::TryFrom, env, fs::File, io::Read, path::PathBuf, str::FromStr,
|
collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
use std::{collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, sync::Arc};
|
||||||
|
|
||||||
/// BPF program file extension
|
/// BPF program file extension
|
||||||
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
||||||
@@ -299,7 +302,7 @@ fn process_transaction_and_record_inner(
|
|||||||
let signature = tx.signatures.get(0).unwrap().clone();
|
let signature = tx.signatures.get(0).unwrap().clone();
|
||||||
let txs = vec![tx];
|
let txs = vec![tx];
|
||||||
let tx_batch = bank.prepare_batch_for_tests(txs);
|
let tx_batch = bank.prepare_batch_for_tests(txs);
|
||||||
let (mut results, _, mut inner_instructions, _transaction_logs) = bank
|
let mut results = bank
|
||||||
.load_execute_and_commit_transactions(
|
.load_execute_and_commit_transactions(
|
||||||
&tx_batch,
|
&tx_batch,
|
||||||
MAX_PROCESSING_AGE,
|
MAX_PROCESSING_AGE,
|
||||||
@@ -307,20 +310,27 @@ fn process_transaction_and_record_inner(
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
);
|
)
|
||||||
|
.0;
|
||||||
let result = results
|
let result = results
|
||||||
.fee_collection_results
|
.fee_collection_results
|
||||||
.swap_remove(0)
|
.swap_remove(0)
|
||||||
.and_then(|_| bank.get_signature_status(&signature).unwrap());
|
.and_then(|_| bank.get_signature_status(&signature).unwrap());
|
||||||
(
|
let inner_instructions = results
|
||||||
result,
|
.execution_results
|
||||||
inner_instructions
|
|
||||||
.swap_remove(0)
|
.swap_remove(0)
|
||||||
.expect("cpi recording should be enabled"),
|
.details()
|
||||||
)
|
.expect("tx should be executed")
|
||||||
|
.clone()
|
||||||
|
.inner_instructions
|
||||||
|
.expect("cpi recording should be enabled");
|
||||||
|
(result, inner_instructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_transactions(bank: &Bank, txs: Vec<Transaction>) -> Vec<ConfirmedTransaction> {
|
fn execute_transactions(
|
||||||
|
bank: &Bank,
|
||||||
|
txs: Vec<Transaction>,
|
||||||
|
) -> Vec<Result<ConfirmedTransaction, TransactionError>> {
|
||||||
let batch = bank.prepare_batch_for_tests(txs.clone());
|
let batch = bank.prepare_batch_for_tests(txs.clone());
|
||||||
let mut timings = ExecuteTimings::default();
|
let mut timings = ExecuteTimings::default();
|
||||||
let mut mint_decimals = HashMap::new();
|
let mut mint_decimals = HashMap::new();
|
||||||
@@ -334,8 +344,6 @@ fn execute_transactions(bank: &Bank, txs: Vec<Transaction>) -> Vec<ConfirmedTran
|
|||||||
post_balances,
|
post_balances,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
) = bank.load_execute_and_commit_transactions(
|
) = bank.load_execute_and_commit_transactions(
|
||||||
&batch,
|
&batch,
|
||||||
std::usize::MAX,
|
std::usize::MAX,
|
||||||
@@ -349,30 +357,39 @@ fn execute_transactions(bank: &Bank, txs: Vec<Transaction>) -> Vec<ConfirmedTran
|
|||||||
izip!(
|
izip!(
|
||||||
txs.iter(),
|
txs.iter(),
|
||||||
execution_results.into_iter(),
|
execution_results.into_iter(),
|
||||||
inner_instructions.into_iter(),
|
|
||||||
pre_balances.into_iter(),
|
pre_balances.into_iter(),
|
||||||
post_balances.into_iter(),
|
post_balances.into_iter(),
|
||||||
tx_pre_token_balances.into_iter(),
|
tx_pre_token_balances.into_iter(),
|
||||||
tx_post_token_balances.into_iter(),
|
tx_post_token_balances.into_iter(),
|
||||||
transaction_logs.into_iter(),
|
|
||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
|(
|
|(
|
||||||
tx,
|
tx,
|
||||||
(execute_result, nonce),
|
execution_result,
|
||||||
inner_instructions,
|
|
||||||
pre_balances,
|
pre_balances,
|
||||||
post_balances,
|
post_balances,
|
||||||
pre_token_balances,
|
pre_token_balances,
|
||||||
post_token_balances,
|
post_token_balances,
|
||||||
log_messages,
|
|
||||||
)| {
|
)| {
|
||||||
let lamports_per_signature = nonce
|
match execution_result {
|
||||||
.map(|nonce| nonce.lamports_per_signature())
|
TransactionExecutionResult::Executed(details) => {
|
||||||
.unwrap_or_else(|| {
|
let TransactionExecutionDetails {
|
||||||
bank.get_lamports_per_signature_for_blockhash(&tx.message().recent_blockhash)
|
status,
|
||||||
})
|
log_messages,
|
||||||
.expect("lamports_per_signature must exist");
|
inner_instructions,
|
||||||
|
durable_nonce_fee,
|
||||||
|
} = details;
|
||||||
|
|
||||||
|
let lamports_per_signature = match durable_nonce_fee {
|
||||||
|
Some(DurableNonceFee::Valid(lamports_per_signature)) => {
|
||||||
|
Some(lamports_per_signature)
|
||||||
|
}
|
||||||
|
Some(DurableNonceFee::Invalid) => None,
|
||||||
|
None => bank.get_lamports_per_signature_for_blockhash(
|
||||||
|
&tx.message().recent_blockhash,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.expect("lamports_per_signature must be available");
|
||||||
let fee = Bank::get_fee_for_message_with_lamports_per_signature(
|
let fee = Bank::get_fee_for_message_with_lamports_per_signature(
|
||||||
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
@@ -391,7 +408,7 @@ fn execute_transactions(bank: &Bank, txs: Vec<Transaction>) -> Vec<ConfirmedTran
|
|||||||
});
|
});
|
||||||
|
|
||||||
let tx_status_meta = TransactionStatusMeta {
|
let tx_status_meta = TransactionStatusMeta {
|
||||||
status: execute_result,
|
status,
|
||||||
fee,
|
fee,
|
||||||
pre_balances,
|
pre_balances,
|
||||||
post_balances,
|
post_balances,
|
||||||
@@ -402,27 +419,22 @@ fn execute_transactions(bank: &Bank, txs: Vec<Transaction>) -> Vec<ConfirmedTran
|
|||||||
rewards: None,
|
rewards: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfirmedTransaction {
|
Ok(ConfirmedTransaction {
|
||||||
slot: bank.slot(),
|
slot: bank.slot(),
|
||||||
transaction: TransactionWithStatusMeta {
|
transaction: TransactionWithStatusMeta {
|
||||||
transaction: tx.clone(),
|
transaction: tx.clone(),
|
||||||
meta: Some(tx_status_meta),
|
meta: Some(tx_status_meta),
|
||||||
},
|
},
|
||||||
block_time: None,
|
block_time: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TransactionExecutionResult::NotExecuted(err) => Err(err.clone()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_confirmed_tx(name: &str, confirmed_tx: ConfirmedTransaction) {
|
|
||||||
let block_time = confirmed_tx.block_time;
|
|
||||||
let tx = confirmed_tx.transaction.transaction.clone();
|
|
||||||
let encoded = confirmed_tx.encode(UiTransactionEncoding::JsonParsed);
|
|
||||||
println!("EXECUTE {} (slot {})", name, encoded.slot);
|
|
||||||
println_transaction(&tx, &encoded.transaction.meta, " ", None, block_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
||||||
fn test_program_bpf_sanity() {
|
fn test_program_bpf_sanity() {
|
||||||
@@ -2450,43 +2462,35 @@ fn test_program_upgradeable_locks() {
|
|||||||
execute_transactions(&bank, vec![invoke_tx, upgrade_tx])
|
execute_transactions(&bank, vec![invoke_tx, upgrade_tx])
|
||||||
};
|
};
|
||||||
|
|
||||||
if false {
|
assert!(matches!(
|
||||||
println!("upgrade and invoke");
|
results1[0],
|
||||||
for result in &results1 {
|
Ok(ConfirmedTransactionWithStatusMeta {
|
||||||
print_confirmed_tx("result", result.clone());
|
transaction: TransactionWithStatusMeta {
|
||||||
}
|
meta: Some(TransactionStatusMeta { status: Ok(()), .. }),
|
||||||
println!("invoke and upgrade");
|
..
|
||||||
for result in &results2 {
|
},
|
||||||
print_confirmed_tx("result", result.clone());
|
..
|
||||||
}
|
})
|
||||||
}
|
));
|
||||||
|
assert_eq!(results1[1], Err(TransactionError::AccountInUse));
|
||||||
|
|
||||||
if let Some(ref meta) = results1[0].transaction.meta {
|
assert!(matches!(
|
||||||
assert_eq!(meta.status, Ok(()));
|
results2[0],
|
||||||
} else {
|
Ok(ConfirmedTransactionWithStatusMeta {
|
||||||
panic!("no meta");
|
transaction: TransactionWithStatusMeta {
|
||||||
}
|
meta: Some(TransactionStatusMeta {
|
||||||
if let Some(ref meta) = results1[1].transaction.meta {
|
status: Err(TransactionError::InstructionError(
|
||||||
assert_eq!(meta.status, Err(TransactionError::AccountInUse));
|
|
||||||
} else {
|
|
||||||
panic!("no meta");
|
|
||||||
}
|
|
||||||
if let Some(ref meta) = results2[0].transaction.meta {
|
|
||||||
assert_eq!(
|
|
||||||
meta.status,
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
0,
|
0,
|
||||||
InstructionError::ProgramFailedToComplete
|
InstructionError::ProgramFailedToComplete
|
||||||
))
|
)),
|
||||||
);
|
..
|
||||||
} else {
|
}),
|
||||||
panic!("no meta");
|
..
|
||||||
}
|
},
|
||||||
if let Some(ref meta) = results2[1].transaction.meta {
|
..
|
||||||
assert_eq!(meta.status, Err(TransactionError::AccountInUse));
|
})
|
||||||
} else {
|
));
|
||||||
panic!("no meta");
|
assert_eq!(results2[1], Err(TransactionError::AccountInUse));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bpf_rust")]
|
#[cfg(feature = "bpf_rust")]
|
||||||
|
@@ -6,7 +6,9 @@ use {
|
|||||||
blockstore::Blockstore,
|
blockstore::Blockstore,
|
||||||
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
|
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
|
||||||
},
|
},
|
||||||
solana_runtime::bank::{Bank, InnerInstructionsList, NonceInfo, TransactionLogMessages},
|
solana_runtime::bank::{
|
||||||
|
Bank, DurableNonceFee, TransactionExecutionDetails, TransactionExecutionResult,
|
||||||
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
extract_and_fmt_memos, InnerInstructions, Reward, TransactionStatusMeta,
|
extract_and_fmt_memos, InnerInstructions, Reward, TransactionStatusMeta,
|
||||||
},
|
},
|
||||||
@@ -67,57 +69,45 @@ impl TransactionStatusService {
|
|||||||
TransactionStatusMessage::Batch(TransactionStatusBatch {
|
TransactionStatusMessage::Batch(TransactionStatusBatch {
|
||||||
bank,
|
bank,
|
||||||
transactions,
|
transactions,
|
||||||
statuses,
|
execution_results,
|
||||||
balances,
|
balances,
|
||||||
token_balances,
|
token_balances,
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
rent_debits,
|
rent_debits,
|
||||||
}) => {
|
}) => {
|
||||||
let slot = bank.slot();
|
let slot = bank.slot();
|
||||||
let inner_instructions_iter: Box<
|
|
||||||
dyn Iterator<Item = Option<InnerInstructionsList>>,
|
|
||||||
> = if let Some(inner_instructions) = inner_instructions {
|
|
||||||
Box::new(inner_instructions.into_iter())
|
|
||||||
} else {
|
|
||||||
Box::new(std::iter::repeat_with(|| None))
|
|
||||||
};
|
|
||||||
let transaction_logs_iter: Box<
|
|
||||||
dyn Iterator<Item = Option<TransactionLogMessages>>,
|
|
||||||
> = if let Some(transaction_logs) = transaction_logs {
|
|
||||||
Box::new(transaction_logs.into_iter())
|
|
||||||
} else {
|
|
||||||
Box::new(std::iter::repeat_with(|| None))
|
|
||||||
};
|
|
||||||
for (
|
for (
|
||||||
transaction,
|
transaction,
|
||||||
(status, nonce),
|
execution_result,
|
||||||
pre_balances,
|
pre_balances,
|
||||||
post_balances,
|
post_balances,
|
||||||
pre_token_balances,
|
pre_token_balances,
|
||||||
post_token_balances,
|
post_token_balances,
|
||||||
inner_instructions,
|
|
||||||
log_messages,
|
|
||||||
rent_debits,
|
rent_debits,
|
||||||
) in izip!(
|
) in izip!(
|
||||||
transactions,
|
transactions,
|
||||||
statuses,
|
execution_results,
|
||||||
balances.pre_balances,
|
balances.pre_balances,
|
||||||
balances.post_balances,
|
balances.post_balances,
|
||||||
token_balances.pre_token_balances,
|
token_balances.pre_token_balances,
|
||||||
token_balances.post_token_balances,
|
token_balances.post_token_balances,
|
||||||
inner_instructions_iter,
|
|
||||||
transaction_logs_iter,
|
|
||||||
rent_debits,
|
rent_debits,
|
||||||
) {
|
) {
|
||||||
if Bank::can_commit(&status) {
|
if let TransactionExecutionResult::Executed(details) = execution_result {
|
||||||
let lamports_per_signature = nonce
|
let TransactionExecutionDetails {
|
||||||
.map(|nonce| nonce.lamports_per_signature())
|
status,
|
||||||
.unwrap_or_else(|| {
|
log_messages,
|
||||||
bank.get_lamports_per_signature_for_blockhash(
|
inner_instructions,
|
||||||
|
durable_nonce_fee,
|
||||||
|
} = details;
|
||||||
|
let lamports_per_signature = match durable_nonce_fee {
|
||||||
|
Some(DurableNonceFee::Valid(lamports_per_signature)) => {
|
||||||
|
Some(lamports_per_signature)
|
||||||
|
}
|
||||||
|
Some(DurableNonceFee::Invalid) => None,
|
||||||
|
None => bank.get_lamports_per_signature_for_blockhash(
|
||||||
transaction.message().recent_blockhash(),
|
transaction.message().recent_blockhash(),
|
||||||
)
|
),
|
||||||
})
|
}
|
||||||
.expect("lamports_per_signature must be available");
|
.expect("lamports_per_signature must be available");
|
||||||
let fee = Bank::get_fee_for_message_with_lamports_per_signature(
|
let fee = Bank::get_fee_for_message_with_lamports_per_signature(
|
||||||
transaction.message(),
|
transaction.message(),
|
||||||
@@ -331,18 +321,21 @@ pub(crate) mod tests {
|
|||||||
let mut rent_debits = RentDebits::default();
|
let mut rent_debits = RentDebits::default();
|
||||||
rent_debits.insert(&pubkey, 123, 456);
|
rent_debits.insert(&pubkey, 123, 456);
|
||||||
|
|
||||||
let transaction_result = (
|
let transaction_result =
|
||||||
Ok(()),
|
TransactionExecutionResult::Executed(TransactionExecutionDetails {
|
||||||
Some(
|
status: Ok(()),
|
||||||
NonceFull::from_partial(
|
log_messages: None,
|
||||||
|
inner_instructions: None,
|
||||||
|
durable_nonce_fee: Some(DurableNonceFee::from(
|
||||||
|
&NonceFull::from_partial(
|
||||||
rollback_partial,
|
rollback_partial,
|
||||||
&SanitizedMessage::Legacy(message),
|
&SanitizedMessage::Legacy(message),
|
||||||
&[(pubkey, nonce_account)],
|
&[(pubkey, nonce_account)],
|
||||||
&rent_debits,
|
&rent_debits,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
)),
|
||||||
);
|
});
|
||||||
|
|
||||||
let balances = TransactionBalancesSet {
|
let balances = TransactionBalancesSet {
|
||||||
pre_balances: vec![vec![123456]],
|
pre_balances: vec![vec![123456]],
|
||||||
@@ -374,11 +367,9 @@ pub(crate) mod tests {
|
|||||||
let transaction_status_batch = TransactionStatusBatch {
|
let transaction_status_batch = TransactionStatusBatch {
|
||||||
bank,
|
bank,
|
||||||
transactions: vec![transaction],
|
transactions: vec![transaction],
|
||||||
statuses: vec![transaction_result],
|
execution_results: vec![transaction_result],
|
||||||
balances,
|
balances,
|
||||||
token_balances,
|
token_balances,
|
||||||
inner_instructions: None,
|
|
||||||
transaction_logs: None,
|
|
||||||
rent_debits: vec![rent_debits],
|
rent_debits: vec![rent_debits],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1076,26 +1076,35 @@ impl Accounts {
|
|||||||
leave_nonce_on_success: bool,
|
leave_nonce_on_success: bool,
|
||||||
) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
|
) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
|
||||||
let mut accounts = Vec::with_capacity(load_results.len());
|
let mut accounts = Vec::with_capacity(load_results.len());
|
||||||
for (i, ((tx_load_result, _), tx)) in load_results.iter_mut().zip(txs).enumerate() {
|
for (i, ((tx_load_result, nonce), tx)) in load_results.iter_mut().zip(txs).enumerate() {
|
||||||
if tx_load_result.is_err() {
|
if tx_load_result.is_err() {
|
||||||
// Don't store any accounts if tx failed to load
|
// Don't store any accounts if tx failed to load
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (execution_result, nonce) = &execution_results[i];
|
let execution_status = match &execution_results[i] {
|
||||||
let maybe_nonce = match (execution_result, nonce) {
|
TransactionExecutionResult::Executed(details) => &details.status,
|
||||||
(Ok(_), Some(nonce)) => {
|
// Don't store any accounts if tx wasn't executed
|
||||||
|
TransactionExecutionResult::NotExecuted(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let maybe_nonce = match (execution_status, &*nonce) {
|
||||||
|
(Ok(()), Some(nonce)) => {
|
||||||
if leave_nonce_on_success {
|
if leave_nonce_on_success {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((nonce, false /* rollback */))
|
Some((nonce, false /* rollback */))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Err(TransactionError::InstructionError(_, _)), Some(nonce)) => {
|
(Err(_), Some(nonce)) => {
|
||||||
Some((nonce, true /* rollback */))
|
Some((nonce, true /* rollback */))
|
||||||
}
|
}
|
||||||
(Ok(_), _) => None, // Success, don't do any additional nonce processing
|
(Ok(_), None) => None, // Success, don't do any additional nonce processing
|
||||||
(Err(_), _) => continue, // Not nonce, don't store any accounts
|
(Err(_), None) => {
|
||||||
|
// Fees for failed transactions which don't use durable nonces are
|
||||||
|
// deducted in Bank::filter_program_errors_and_collect_fee
|
||||||
|
continue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let message = tx.message();
|
let message = tx.message();
|
||||||
@@ -1113,14 +1122,14 @@ impl Accounts {
|
|||||||
let is_nonce_account = prepare_if_nonce_account(
|
let is_nonce_account = prepare_if_nonce_account(
|
||||||
address,
|
address,
|
||||||
account,
|
account,
|
||||||
execution_result,
|
execution_status,
|
||||||
is_fee_payer,
|
is_fee_payer,
|
||||||
maybe_nonce,
|
maybe_nonce,
|
||||||
blockhash,
|
blockhash,
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
);
|
);
|
||||||
|
|
||||||
if execution_result.is_ok() || is_nonce_account || is_fee_payer {
|
if execution_status.is_ok() || is_nonce_account || is_fee_payer {
|
||||||
if account.rent_epoch() == INITIAL_RENT_EPOCH {
|
if account.rent_epoch() == INITIAL_RENT_EPOCH {
|
||||||
let rent = rent_collector.collect_from_created_account(
|
let rent = rent_collector.collect_from_created_account(
|
||||||
address,
|
address,
|
||||||
@@ -1229,7 +1238,10 @@ pub fn update_accounts_bench(accounts: &Accounts, pubkeys: &[Pubkey], slot: u64)
|
|||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
crate::rent_collector::RentCollector,
|
crate::{
|
||||||
|
bank::{DurableNonceFee, TransactionExecutionDetails},
|
||||||
|
rent_collector::RentCollector,
|
||||||
|
},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{AccountSharedData, WritableAccount},
|
account::{AccountSharedData, WritableAccount},
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
@@ -1262,6 +1274,18 @@ mod tests {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_execution_result(
|
||||||
|
status: Result<()>,
|
||||||
|
nonce: Option<&NonceFull>,
|
||||||
|
) -> TransactionExecutionResult {
|
||||||
|
TransactionExecutionResult::Executed(TransactionExecutionDetails {
|
||||||
|
status,
|
||||||
|
log_messages: None,
|
||||||
|
inner_instructions: None,
|
||||||
|
durable_nonce_fee: nonce.map(DurableNonceFee::from),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn load_accounts_with_fee_and_rent(
|
fn load_accounts_with_fee_and_rent(
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
ka: &[(Pubkey, AccountSharedData)],
|
ka: &[(Pubkey, AccountSharedData)],
|
||||||
@@ -2668,10 +2692,10 @@ mod tests {
|
|||||||
.insert_new_readonly(&pubkey);
|
.insert_new_readonly(&pubkey);
|
||||||
}
|
}
|
||||||
let txs = vec![tx0, tx1];
|
let txs = vec![tx0, tx1];
|
||||||
let programs = vec![(Ok(()), None), (Ok(()), None)];
|
let execution_results = vec![new_execution_result(Ok(()), None); 2];
|
||||||
let collected_accounts = accounts.collect_accounts_to_store(
|
let collected_accounts = accounts.collect_accounts_to_store(
|
||||||
&txs,
|
&txs,
|
||||||
&programs,
|
&execution_results,
|
||||||
loaded.as_mut_slice(),
|
loaded.as_mut_slice(),
|
||||||
&rent_collector,
|
&rent_collector,
|
||||||
&Hash::default(),
|
&Hash::default(),
|
||||||
@@ -3091,16 +3115,16 @@ mod tests {
|
|||||||
AccountShrinkThreshold::default(),
|
AccountShrinkThreshold::default(),
|
||||||
);
|
);
|
||||||
let txs = vec![tx];
|
let txs = vec![tx];
|
||||||
let programs = vec![(
|
let execution_results = vec![new_execution_result(
|
||||||
Err(TransactionError::InstructionError(
|
Err(TransactionError::InstructionError(
|
||||||
1,
|
1,
|
||||||
InstructionError::InvalidArgument,
|
InstructionError::InvalidArgument,
|
||||||
)),
|
)),
|
||||||
nonce,
|
nonce.as_ref(),
|
||||||
)];
|
)];
|
||||||
let collected_accounts = accounts.collect_accounts_to_store(
|
let collected_accounts = accounts.collect_accounts_to_store(
|
||||||
&txs,
|
&txs,
|
||||||
&programs,
|
&execution_results,
|
||||||
loaded.as_mut_slice(),
|
loaded.as_mut_slice(),
|
||||||
&rent_collector,
|
&rent_collector,
|
||||||
&next_blockhash,
|
&next_blockhash,
|
||||||
@@ -3201,16 +3225,16 @@ mod tests {
|
|||||||
AccountShrinkThreshold::default(),
|
AccountShrinkThreshold::default(),
|
||||||
);
|
);
|
||||||
let txs = vec![tx];
|
let txs = vec![tx];
|
||||||
let programs = vec![(
|
let execution_results = vec![new_execution_result(
|
||||||
Err(TransactionError::InstructionError(
|
Err(TransactionError::InstructionError(
|
||||||
1,
|
1,
|
||||||
InstructionError::InvalidArgument,
|
InstructionError::InvalidArgument,
|
||||||
)),
|
)),
|
||||||
nonce,
|
nonce.as_ref(),
|
||||||
)];
|
)];
|
||||||
let collected_accounts = accounts.collect_accounts_to_store(
|
let collected_accounts = accounts.collect_accounts_to_store(
|
||||||
&txs,
|
&txs,
|
||||||
&programs,
|
&execution_results,
|
||||||
loaded.as_mut_slice(),
|
loaded.as_mut_slice(),
|
||||||
&rent_collector,
|
&rent_collector,
|
||||||
&next_blockhash,
|
&next_blockhash,
|
||||||
|
@@ -37,7 +37,10 @@
|
|||||||
use solana_sdk::recent_blockhashes_account;
|
use solana_sdk::recent_blockhashes_account;
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
accounts::{AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult},
|
accounts::{
|
||||||
|
AccountAddressFilter, Accounts, LoadedTransaction, TransactionAccounts,
|
||||||
|
TransactionLoadResult,
|
||||||
|
},
|
||||||
accounts_db::{
|
accounts_db::{
|
||||||
AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages,
|
AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages,
|
||||||
ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
|
ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
|
||||||
@@ -506,12 +509,91 @@ impl StatusCacheRc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type TransactionCheckResult = (Result<()>, Option<NoncePartial>);
|
pub type TransactionCheckResult = (Result<()>, Option<NoncePartial>);
|
||||||
pub type TransactionExecutionResult = (Result<()>, Option<NonceFull>);
|
|
||||||
pub struct TransactionResults {
|
pub struct TransactionResults {
|
||||||
pub fee_collection_results: Vec<Result<()>>,
|
pub fee_collection_results: Vec<Result<()>>,
|
||||||
pub execution_results: Vec<TransactionExecutionResult>,
|
pub execution_results: Vec<TransactionExecutionResult>,
|
||||||
pub rent_debits: Vec<RentDebits>,
|
pub rent_debits: Vec<RentDebits>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TransactionExecutionDetails {
|
||||||
|
pub status: Result<()>,
|
||||||
|
pub log_messages: Option<Vec<String>>,
|
||||||
|
pub inner_instructions: Option<Vec<Vec<CompiledInstruction>>>,
|
||||||
|
pub durable_nonce_fee: Option<DurableNonceFee>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type safe representation of a transaction execution attempt which
|
||||||
|
/// differentiates between a transaction that was executed (will be
|
||||||
|
/// committed to the ledger) and a transaction which wasn't executed
|
||||||
|
/// and will be dropped.
|
||||||
|
///
|
||||||
|
/// Note: `Result<TransactionExecutionDetails, TransactionError>` is not
|
||||||
|
/// used because it's easy to forget that the inner `details.status` field
|
||||||
|
/// is what should be checked to detect a successful transaction. This
|
||||||
|
/// enum provides a convenience method `Self::was_executed_successfully` to
|
||||||
|
/// make such checks hard to do incorrectly.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TransactionExecutionResult {
|
||||||
|
Executed(TransactionExecutionDetails),
|
||||||
|
NotExecuted(TransactionError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionExecutionResult {
|
||||||
|
pub fn was_executed_successfully(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Executed(details) => details.status.is_ok(),
|
||||||
|
Self::NotExecuted { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn was_executed(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Executed(_) => true,
|
||||||
|
Self::NotExecuted(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn details(&self) -> Option<&TransactionExecutionDetails> {
|
||||||
|
match self {
|
||||||
|
Self::Executed(details) => Some(details),
|
||||||
|
Self::NotExecuted(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flattened_result(&self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Executed(details) => details.status.clone(),
|
||||||
|
Self::NotExecuted(err) => Err(err.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum DurableNonceFee {
|
||||||
|
Valid(u64),
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&NonceFull> for DurableNonceFee {
|
||||||
|
fn from(nonce: &NonceFull) -> Self {
|
||||||
|
match nonce.lamports_per_signature() {
|
||||||
|
Some(lamports_per_signature) => Self::Valid(lamports_per_signature),
|
||||||
|
None => Self::Invalid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DurableNonceFee {
|
||||||
|
pub fn lamports_per_signature(&self) -> Option<u64> {
|
||||||
|
match self {
|
||||||
|
Self::Valid(lamports_per_signature) => Some(*lamports_per_signature),
|
||||||
|
Self::Invalid => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TransactionSimulationResult {
|
pub struct TransactionSimulationResult {
|
||||||
pub result: Result<()>,
|
pub result: Result<()>,
|
||||||
pub logs: TransactionLogMessages,
|
pub logs: TransactionLogMessages,
|
||||||
@@ -2995,30 +3077,22 @@ impl Bank {
|
|||||||
.clear_slot_entries(slot);
|
.clear_slot_entries(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_commit(result: &Result<()>) -> bool {
|
|
||||||
match result {
|
|
||||||
Ok(_) => true,
|
|
||||||
Err(TransactionError::InstructionError(_, _)) => true,
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_transaction_statuses(
|
fn update_transaction_statuses(
|
||||||
&self,
|
&self,
|
||||||
sanitized_txs: &[SanitizedTransaction],
|
sanitized_txs: &[SanitizedTransaction],
|
||||||
res: &[TransactionExecutionResult],
|
execution_results: &[TransactionExecutionResult],
|
||||||
) {
|
) {
|
||||||
let mut status_cache = self.src.status_cache.write().unwrap();
|
let mut status_cache = self.src.status_cache.write().unwrap();
|
||||||
assert_eq!(sanitized_txs.len(), res.len());
|
assert_eq!(sanitized_txs.len(), execution_results.len());
|
||||||
for (tx, (res, _nonce)) in sanitized_txs.iter().zip(res) {
|
for (tx, execution_result) in sanitized_txs.iter().zip(execution_results) {
|
||||||
if Self::can_commit(res) {
|
if let TransactionExecutionResult::Executed(details) = execution_result {
|
||||||
// Add the message hash to the status cache to ensure that this message
|
// Add the message hash to the status cache to ensure that this message
|
||||||
// won't be processed again with a different signature.
|
// won't be processed again with a different signature.
|
||||||
status_cache.insert(
|
status_cache.insert(
|
||||||
tx.message().recent_blockhash(),
|
tx.message().recent_blockhash(),
|
||||||
tx.message_hash(),
|
tx.message_hash(),
|
||||||
self.slot(),
|
self.slot(),
|
||||||
res.clone(),
|
details.status.clone(),
|
||||||
);
|
);
|
||||||
// Add the transaction signature to the status cache so that transaction status
|
// Add the transaction signature to the status cache so that transaction status
|
||||||
// can be queried by transaction signature over RPC. In the future, this should
|
// can be queried by transaction signature over RPC. In the future, this should
|
||||||
@@ -3027,7 +3101,7 @@ impl Bank {
|
|||||||
tx.message().recent_blockhash(),
|
tx.message().recent_blockhash(),
|
||||||
tx.signature(),
|
tx.signature(),
|
||||||
self.slot(),
|
self.slot(),
|
||||||
res.clone(),
|
details.status.clone(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3163,9 +3237,7 @@ impl Bank {
|
|||||||
|
|
||||||
let (
|
let (
|
||||||
loaded_transactions,
|
loaded_transactions,
|
||||||
executed,
|
mut execution_results,
|
||||||
_inner_instructions,
|
|
||||||
logs,
|
|
||||||
_retryable_transactions,
|
_retryable_transactions,
|
||||||
_transaction_count,
|
_transaction_count,
|
||||||
_signature_count,
|
_signature_count,
|
||||||
@@ -3180,8 +3252,6 @@ impl Bank {
|
|||||||
&mut timings,
|
&mut timings,
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = executed[0].0.clone().map(|_| ());
|
|
||||||
let logs = logs.get(0).cloned().flatten().unwrap_or_default();
|
|
||||||
let post_simulation_accounts = loaded_transactions
|
let post_simulation_accounts = loaded_transactions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
@@ -3207,8 +3277,16 @@ impl Bank {
|
|||||||
|
|
||||||
debug!("simulate_transaction: {:?}", timings);
|
debug!("simulate_transaction: {:?}", timings);
|
||||||
|
|
||||||
|
let execution_result = execution_results.pop().unwrap();
|
||||||
|
let flattened_result = execution_result.flattened_result();
|
||||||
|
let logs = match execution_result {
|
||||||
|
TransactionExecutionResult::Executed(details) => details.log_messages,
|
||||||
|
TransactionExecutionResult::NotExecuted(_) => None,
|
||||||
|
}
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
TransactionSimulationResult {
|
TransactionSimulationResult {
|
||||||
result,
|
result: flattened_result,
|
||||||
logs,
|
logs,
|
||||||
post_simulation_accounts,
|
post_simulation_accounts,
|
||||||
units_consumed,
|
units_consumed,
|
||||||
@@ -3506,6 +3584,110 @@ impl Bank {
|
|||||||
cache.remove(pubkey);
|
cache.remove(pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute a transaction using the provided loaded accounts and update
|
||||||
|
/// the executors cache if the transaction was successful.
|
||||||
|
fn execute_loaded_transaction(
|
||||||
|
&self,
|
||||||
|
tx: &SanitizedTransaction,
|
||||||
|
loaded_transaction: &mut LoadedTransaction,
|
||||||
|
compute_budget: ComputeBudget,
|
||||||
|
durable_nonce_fee: Option<DurableNonceFee>,
|
||||||
|
enable_cpi_recording: bool,
|
||||||
|
enable_log_recording: bool,
|
||||||
|
execute_details_timings: &mut ExecuteDetailsTimings,
|
||||||
|
error_counters: &mut ErrorCounters,
|
||||||
|
) -> TransactionExecutionResult {
|
||||||
|
let legacy_message = match tx.message().legacy_message() {
|
||||||
|
Some(message) => message,
|
||||||
|
None => {
|
||||||
|
// TODO: support versioned messages
|
||||||
|
return TransactionExecutionResult::NotExecuted(
|
||||||
|
TransactionError::UnsupportedVersion,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let executors = self.get_executors(
|
||||||
|
tx.message(),
|
||||||
|
&loaded_transaction.accounts,
|
||||||
|
&loaded_transaction.program_indices,
|
||||||
|
);
|
||||||
|
|
||||||
|
let account_refcells = Self::accounts_to_refcells(&mut loaded_transaction.accounts);
|
||||||
|
|
||||||
|
let instruction_recorders = if enable_cpi_recording {
|
||||||
|
let ix_count = tx.message().instructions().len();
|
||||||
|
let mut recorders = Vec::with_capacity(ix_count);
|
||||||
|
recorders.resize_with(ix_count, InstructionRecorder::default);
|
||||||
|
Some(recorders)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let log_collector = if enable_log_recording {
|
||||||
|
Some(LogCollector::new_ref())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature();
|
||||||
|
let process_result = MessageProcessor::process_message(
|
||||||
|
&self.builtin_programs.vec,
|
||||||
|
legacy_message,
|
||||||
|
&loaded_transaction.program_indices,
|
||||||
|
&account_refcells,
|
||||||
|
self.rent_collector.rent,
|
||||||
|
log_collector.clone(),
|
||||||
|
executors.clone(),
|
||||||
|
instruction_recorders.as_deref(),
|
||||||
|
self.feature_set.clone(),
|
||||||
|
compute_budget,
|
||||||
|
execute_details_timings,
|
||||||
|
&*self.sysvar_cache.read().unwrap(),
|
||||||
|
blockhash,
|
||||||
|
lamports_per_signature,
|
||||||
|
);
|
||||||
|
|
||||||
|
let log_messages: Option<TransactionLogMessages> =
|
||||||
|
log_collector.and_then(|log_collector| {
|
||||||
|
Rc::try_unwrap(log_collector)
|
||||||
|
.map(|log_collector| log_collector.into_inner().into())
|
||||||
|
.ok()
|
||||||
|
});
|
||||||
|
|
||||||
|
let inner_instructions: Option<InnerInstructionsList> =
|
||||||
|
instruction_recorders.and_then(|instruction_recorders| {
|
||||||
|
instruction_recorders
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| r.compile_instructions(tx.message()))
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) =
|
||||||
|
Self::refcells_to_accounts(&mut loaded_transaction.accounts, account_refcells)
|
||||||
|
{
|
||||||
|
warn!("Account lifetime mismanagement");
|
||||||
|
return TransactionExecutionResult::NotExecuted(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = process_result
|
||||||
|
.map(|info| {
|
||||||
|
self.update_accounts_data_len(info.accounts_data_len_delta);
|
||||||
|
self.update_executors(executors);
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
error_counters.instruction_error += 1;
|
||||||
|
err
|
||||||
|
});
|
||||||
|
|
||||||
|
TransactionExecutionResult::Executed(TransactionExecutionDetails {
|
||||||
|
status,
|
||||||
|
log_messages,
|
||||||
|
inner_instructions,
|
||||||
|
durable_nonce_fee,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn load_and_execute_transactions(
|
pub fn load_and_execute_transactions(
|
||||||
&self,
|
&self,
|
||||||
@@ -3517,8 +3699,6 @@ impl Bank {
|
|||||||
) -> (
|
) -> (
|
||||||
Vec<TransactionLoadResult>,
|
Vec<TransactionLoadResult>,
|
||||||
Vec<TransactionExecutionResult>,
|
Vec<TransactionExecutionResult>,
|
||||||
Vec<Option<InnerInstructionsList>>,
|
|
||||||
Vec<Option<TransactionLogMessages>>,
|
|
||||||
Vec<usize>,
|
Vec<usize>,
|
||||||
u64,
|
u64,
|
||||||
u64,
|
u64,
|
||||||
@@ -3567,129 +3747,36 @@ impl Bank {
|
|||||||
|
|
||||||
let mut execution_time = Measure::start("execution_time");
|
let mut execution_time = Measure::start("execution_time");
|
||||||
let mut signature_count: u64 = 0;
|
let mut signature_count: u64 = 0;
|
||||||
let mut inner_instructions: Vec<Option<InnerInstructionsList>> =
|
|
||||||
Vec::with_capacity(sanitized_txs.len());
|
|
||||||
let mut transaction_log_messages: Vec<Option<Vec<String>>> =
|
|
||||||
Vec::with_capacity(sanitized_txs.len());
|
|
||||||
|
|
||||||
let executed: Vec<TransactionExecutionResult> = loaded_txs
|
let execute_details_timings = &mut timings.details;
|
||||||
|
let execution_results: Vec<TransactionExecutionResult> = loaded_txs
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(sanitized_txs.iter())
|
.zip(sanitized_txs.iter())
|
||||||
.map(|(accs, tx)| match accs {
|
.map(|(accs, tx)| match accs {
|
||||||
(Err(e), _nonce) => {
|
(Err(e), _nonce) => TransactionExecutionResult::NotExecuted(e.clone()),
|
||||||
transaction_log_messages.push(None);
|
|
||||||
inner_instructions.push(None);
|
|
||||||
(Err(e.clone()), None)
|
|
||||||
}
|
|
||||||
(Ok(loaded_transaction), nonce) => {
|
(Ok(loaded_transaction), nonce) => {
|
||||||
let feature_set = self.feature_set.clone();
|
let feature_set = self.feature_set.clone();
|
||||||
signature_count += u64::from(tx.message().header().num_required_signatures);
|
signature_count += u64::from(tx.message().header().num_required_signatures);
|
||||||
|
|
||||||
let mut compute_budget = self.compute_budget.unwrap_or_else(ComputeBudget::new);
|
let mut compute_budget = self.compute_budget.unwrap_or_else(ComputeBudget::new);
|
||||||
|
if feature_set.is_active(&tx_wide_compute_cap::id()) {
|
||||||
|
if let Err(err) = compute_budget.process_transaction(tx, feature_set) {
|
||||||
|
return TransactionExecutionResult::NotExecuted(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut process_result = if feature_set.is_active(&tx_wide_compute_cap::id()) {
|
let durable_nonce_fee = nonce.as_ref().map(DurableNonceFee::from);
|
||||||
compute_budget.process_transaction(tx, feature_set.clone())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
if process_result.is_ok() {
|
self.execute_loaded_transaction(
|
||||||
let executors = self.get_executors(
|
tx,
|
||||||
tx.message(),
|
loaded_transaction,
|
||||||
&loaded_transaction.accounts,
|
|
||||||
&loaded_transaction.program_indices,
|
|
||||||
);
|
|
||||||
|
|
||||||
let account_refcells =
|
|
||||||
Self::accounts_to_refcells(&mut loaded_transaction.accounts);
|
|
||||||
|
|
||||||
let instruction_recorders = if enable_cpi_recording {
|
|
||||||
let ix_count = tx.message().instructions().len();
|
|
||||||
let mut recorders = Vec::with_capacity(ix_count);
|
|
||||||
recorders.resize_with(ix_count, InstructionRecorder::default);
|
|
||||||
Some(recorders)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let log_collector = if enable_log_recording {
|
|
||||||
Some(LogCollector::new_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let (blockhash, lamports_per_signature) =
|
|
||||||
self.last_blockhash_and_lamports_per_signature();
|
|
||||||
|
|
||||||
if let Some(legacy_message) = tx.message().legacy_message() {
|
|
||||||
process_result = MessageProcessor::process_message(
|
|
||||||
&self.builtin_programs.vec,
|
|
||||||
legacy_message,
|
|
||||||
&loaded_transaction.program_indices,
|
|
||||||
&account_refcells,
|
|
||||||
self.rent_collector.rent,
|
|
||||||
log_collector.clone(),
|
|
||||||
executors.clone(),
|
|
||||||
instruction_recorders.as_deref(),
|
|
||||||
feature_set,
|
|
||||||
compute_budget,
|
compute_budget,
|
||||||
&mut timings.details,
|
durable_nonce_fee,
|
||||||
&*self.sysvar_cache.read().unwrap(),
|
enable_cpi_recording,
|
||||||
blockhash,
|
enable_log_recording,
|
||||||
lamports_per_signature,
|
execute_details_timings,
|
||||||
|
&mut error_counters,
|
||||||
)
|
)
|
||||||
.map(|process_result| {
|
|
||||||
self.update_accounts_data_len(
|
|
||||||
process_result.accounts_data_len_delta,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// TODO: support versioned messages
|
|
||||||
process_result = Err(TransactionError::UnsupportedVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
let log_messages: Option<TransactionLogMessages> =
|
|
||||||
log_collector.and_then(|log_collector| {
|
|
||||||
Rc::try_unwrap(log_collector)
|
|
||||||
.map(|log_collector| log_collector.into_inner().into())
|
|
||||||
.ok()
|
|
||||||
});
|
|
||||||
transaction_log_messages.push(log_messages);
|
|
||||||
let inner_instruction_list: Option<InnerInstructionsList> =
|
|
||||||
instruction_recorders.and_then(|instruction_recorders| {
|
|
||||||
instruction_recorders
|
|
||||||
.into_iter()
|
|
||||||
.map(|r| r.compile_instructions(tx.message()))
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
inner_instructions.push(inner_instruction_list);
|
|
||||||
|
|
||||||
if let Err(e) = Self::refcells_to_accounts(
|
|
||||||
&mut loaded_transaction.accounts,
|
|
||||||
account_refcells,
|
|
||||||
) {
|
|
||||||
warn!("Account lifetime mismanagement");
|
|
||||||
process_result = Err(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if process_result.is_ok() {
|
|
||||||
self.update_executors(executors);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
transaction_log_messages.push(None);
|
|
||||||
inner_instructions.push(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let nonce = match &process_result {
|
|
||||||
Ok(_) => nonce.clone(), // May need to calculate the fee based on the nonce
|
|
||||||
Err(TransactionError::InstructionError(_, _)) => {
|
|
||||||
error_counters.instruction_error += 1;
|
|
||||||
nonce.clone() // May need to advance the nonce
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
(process_result, nonce)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -3712,17 +3799,18 @@ impl Bank {
|
|||||||
let transaction_log_collector_config =
|
let transaction_log_collector_config =
|
||||||
self.transaction_log_collector_config.read().unwrap();
|
self.transaction_log_collector_config.read().unwrap();
|
||||||
|
|
||||||
for (i, ((r, _nonce), tx)) in executed.iter().zip(sanitized_txs).enumerate() {
|
for (execution_result, tx) in execution_results.iter().zip(sanitized_txs) {
|
||||||
if let Some(debug_keys) = &self.transaction_debug_keys {
|
if let Some(debug_keys) = &self.transaction_debug_keys {
|
||||||
for key in tx.message().account_keys_iter() {
|
for key in tx.message().account_keys_iter() {
|
||||||
if debug_keys.contains(key) {
|
if debug_keys.contains(key) {
|
||||||
info!("slot: {} result: {:?} tx: {:?}", self.slot, r, tx);
|
let result = execution_result.flattened_result();
|
||||||
|
info!("slot: {} result: {:?} tx: {:?}", self.slot, result, tx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if Self::can_commit(r) // Skip log collection for unprocessed transactions
|
if execution_result.was_executed() // Skip log collection for unprocessed transactions
|
||||||
&& transaction_log_collector_config.filter != TransactionLogCollectorFilter::None
|
&& transaction_log_collector_config.filter != TransactionLogCollectorFilter::None
|
||||||
{
|
{
|
||||||
let mut filtered_mentioned_addresses = Vec::new();
|
let mut filtered_mentioned_addresses = Vec::new();
|
||||||
@@ -3753,16 +3841,21 @@ impl Bank {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if store {
|
if store {
|
||||||
if let Some(log_messages) = transaction_log_messages.get(i).cloned().flatten() {
|
if let TransactionExecutionResult::Executed(TransactionExecutionDetails {
|
||||||
|
status,
|
||||||
|
log_messages: Some(log_messages),
|
||||||
|
..
|
||||||
|
}) = execution_result
|
||||||
|
{
|
||||||
let mut transaction_log_collector =
|
let mut transaction_log_collector =
|
||||||
self.transaction_log_collector.write().unwrap();
|
self.transaction_log_collector.write().unwrap();
|
||||||
let transaction_log_index = transaction_log_collector.logs.len();
|
let transaction_log_index = transaction_log_collector.logs.len();
|
||||||
|
|
||||||
transaction_log_collector.logs.push(TransactionLogInfo {
|
transaction_log_collector.logs.push(TransactionLogInfo {
|
||||||
signature: *tx.signature(),
|
signature: *tx.signature(),
|
||||||
result: r.clone(),
|
result: status.clone(),
|
||||||
is_vote,
|
is_vote,
|
||||||
log_messages,
|
log_messages: log_messages.clone(),
|
||||||
});
|
});
|
||||||
for key in filtered_mentioned_addresses.into_iter() {
|
for key in filtered_mentioned_addresses.into_iter() {
|
||||||
transaction_log_collector
|
transaction_log_collector
|
||||||
@@ -3775,15 +3868,18 @@ impl Bank {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.is_ok() {
|
match execution_result.flattened_result() {
|
||||||
|
Ok(()) => {
|
||||||
tx_count += 1;
|
tx_count += 1;
|
||||||
} else {
|
}
|
||||||
|
Err(err) => {
|
||||||
if *err_count == 0 {
|
if *err_count == 0 {
|
||||||
debug!("tx error: {:?} {:?}", r, tx);
|
debug!("tx error: {:?} {:?}", err, tx);
|
||||||
}
|
}
|
||||||
*err_count += 1;
|
*err_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if *err_count > 0 {
|
if *err_count > 0 {
|
||||||
debug!(
|
debug!(
|
||||||
"{} errors of {} txs",
|
"{} errors of {} txs",
|
||||||
@@ -3794,9 +3890,7 @@ impl Bank {
|
|||||||
Self::update_error_counters(&error_counters);
|
Self::update_error_counters(&error_counters);
|
||||||
(
|
(
|
||||||
loaded_txs,
|
loaded_txs,
|
||||||
executed,
|
execution_results,
|
||||||
inner_instructions,
|
|
||||||
transaction_log_messages,
|
|
||||||
retryable_txs,
|
retryable_txs,
|
||||||
tx_count,
|
tx_count,
|
||||||
signature_count,
|
signature_count,
|
||||||
@@ -3840,10 +3934,16 @@ impl Bank {
|
|||||||
let results = txs
|
let results = txs
|
||||||
.iter()
|
.iter()
|
||||||
.zip(execution_results)
|
.zip(execution_results)
|
||||||
.map(|(tx, (execution_result, nonce))| {
|
.map(|(tx, execution_result)| {
|
||||||
let (lamports_per_signature, is_nonce) = nonce
|
let (execution_status, durable_nonce_fee) = match &execution_result {
|
||||||
.as_ref()
|
TransactionExecutionResult::Executed(details) => {
|
||||||
.map(|nonce| nonce.lamports_per_signature())
|
Ok((&details.status, details.durable_nonce_fee.as_ref()))
|
||||||
|
}
|
||||||
|
TransactionExecutionResult::NotExecuted(err) => Err(err.clone()),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let (lamports_per_signature, is_nonce) = durable_nonce_fee
|
||||||
|
.map(|durable_nonce_fee| durable_nonce_fee.lamports_per_signature())
|
||||||
.map(|maybe_lamports_per_signature| (maybe_lamports_per_signature, true))
|
.map(|maybe_lamports_per_signature| (maybe_lamports_per_signature, true))
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
(
|
(
|
||||||
@@ -3856,8 +3956,6 @@ impl Bank {
|
|||||||
lamports_per_signature.ok_or(TransactionError::BlockhashNotFound)?;
|
lamports_per_signature.ok_or(TransactionError::BlockhashNotFound)?;
|
||||||
let fee = Self::calculate_fee(tx.message(), lamports_per_signature);
|
let fee = Self::calculate_fee(tx.message(), lamports_per_signature);
|
||||||
|
|
||||||
match *execution_result {
|
|
||||||
Err(TransactionError::InstructionError(_, _)) => {
|
|
||||||
// In case of instruction error, even though no accounts
|
// In case of instruction error, even though no accounts
|
||||||
// were stored we still need to charge the payer the
|
// were stored we still need to charge the payer the
|
||||||
// fee.
|
// fee.
|
||||||
@@ -3865,18 +3963,12 @@ impl Bank {
|
|||||||
//...except nonce accounts, which already have their
|
//...except nonce accounts, which already have their
|
||||||
// post-load, fee deducted, pre-execute account state
|
// post-load, fee deducted, pre-execute account state
|
||||||
// stored
|
// stored
|
||||||
if !is_nonce {
|
if execution_status.is_err() && !is_nonce {
|
||||||
self.withdraw(tx.message().fee_payer(), fee)?;
|
self.withdraw(tx.message().fee_payer(), fee)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fees += fee;
|
fees += fee;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
Ok(()) => {
|
|
||||||
fees += fee;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => execution_result.clone(),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -3888,7 +3980,7 @@ impl Bank {
|
|||||||
&self,
|
&self,
|
||||||
sanitized_txs: &[SanitizedTransaction],
|
sanitized_txs: &[SanitizedTransaction],
|
||||||
loaded_txs: &mut [TransactionLoadResult],
|
loaded_txs: &mut [TransactionLoadResult],
|
||||||
executed_results: &[TransactionExecutionResult],
|
execution_results: Vec<TransactionExecutionResult>,
|
||||||
tx_count: u64,
|
tx_count: u64,
|
||||||
signature_count: u64,
|
signature_count: u64,
|
||||||
timings: &mut ExecuteTimings,
|
timings: &mut ExecuteTimings,
|
||||||
@@ -3914,10 +4006,7 @@ impl Bank {
|
|||||||
.fetch_max(processed_tx_count, Relaxed);
|
.fetch_max(processed_tx_count, Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if executed_results
|
if execution_results.iter().any(|result| result.was_executed()) {
|
||||||
.iter()
|
|
||||||
.any(|(res, _)| Self::can_commit(res))
|
|
||||||
{
|
|
||||||
self.is_delta.store(true, Relaxed);
|
self.is_delta.store(true, Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3926,7 +4015,7 @@ impl Bank {
|
|||||||
self.rc.accounts.store_cached(
|
self.rc.accounts.store_cached(
|
||||||
self.slot(),
|
self.slot(),
|
||||||
sanitized_txs,
|
sanitized_txs,
|
||||||
executed_results,
|
&execution_results,
|
||||||
loaded_txs,
|
loaded_txs,
|
||||||
&self.rent_collector,
|
&self.rent_collector,
|
||||||
&blockhash,
|
&blockhash,
|
||||||
@@ -3934,10 +4023,10 @@ impl Bank {
|
|||||||
self.rent_for_sysvars(),
|
self.rent_for_sysvars(),
|
||||||
self.leave_nonce_on_success(),
|
self.leave_nonce_on_success(),
|
||||||
);
|
);
|
||||||
let rent_debits = self.collect_rent(executed_results, loaded_txs);
|
let rent_debits = self.collect_rent(&execution_results, loaded_txs);
|
||||||
|
|
||||||
let mut update_stakes_cache_time = Measure::start("update_stakes_cache_time");
|
let mut update_stakes_cache_time = Measure::start("update_stakes_cache_time");
|
||||||
self.update_stakes_cache(sanitized_txs, executed_results, loaded_txs);
|
self.update_stakes_cache(sanitized_txs, &execution_results, loaded_txs);
|
||||||
update_stakes_cache_time.stop();
|
update_stakes_cache_time.stop();
|
||||||
|
|
||||||
// once committed there is no way to unroll
|
// once committed there is no way to unroll
|
||||||
@@ -3951,13 +4040,13 @@ impl Bank {
|
|||||||
timings.update_stakes_cache_us = timings
|
timings.update_stakes_cache_us = timings
|
||||||
.update_stakes_cache_us
|
.update_stakes_cache_us
|
||||||
.saturating_add(update_stakes_cache_time.as_us());
|
.saturating_add(update_stakes_cache_time.as_us());
|
||||||
self.update_transaction_statuses(sanitized_txs, executed_results);
|
self.update_transaction_statuses(sanitized_txs, &execution_results);
|
||||||
let fee_collection_results =
|
let fee_collection_results =
|
||||||
self.filter_program_errors_and_collect_fee(sanitized_txs, executed_results);
|
self.filter_program_errors_and_collect_fee(sanitized_txs, &execution_results);
|
||||||
|
|
||||||
TransactionResults {
|
TransactionResults {
|
||||||
fee_collection_results,
|
fee_collection_results,
|
||||||
execution_results: executed_results.to_vec(),
|
execution_results,
|
||||||
rent_debits,
|
rent_debits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4121,24 +4210,24 @@ impl Bank {
|
|||||||
|
|
||||||
fn collect_rent(
|
fn collect_rent(
|
||||||
&self,
|
&self,
|
||||||
res: &[TransactionExecutionResult],
|
execution_results: &[TransactionExecutionResult],
|
||||||
loaded_txs: &mut [TransactionLoadResult],
|
loaded_txs: &mut [TransactionLoadResult],
|
||||||
) -> Vec<RentDebits> {
|
) -> Vec<RentDebits> {
|
||||||
let mut collected_rent: u64 = 0;
|
let mut collected_rent: u64 = 0;
|
||||||
let mut rent_debits: Vec<RentDebits> = Vec::with_capacity(loaded_txs.len());
|
let rent_debits: Vec<_> = loaded_txs
|
||||||
for (i, (raccs, _nonce)) in loaded_txs.iter_mut().enumerate() {
|
.iter_mut()
|
||||||
let (res, _nonce) = &res[i];
|
.zip(execution_results)
|
||||||
if res.is_err() || raccs.is_err() {
|
.map(|((load_result, _nonce), execution_result)| {
|
||||||
rent_debits.push(RentDebits::default());
|
if let (Ok(loaded_transaction), true) =
|
||||||
continue;
|
(load_result, execution_result.was_executed_successfully())
|
||||||
}
|
{
|
||||||
|
|
||||||
let loaded_transaction = raccs.as_mut().unwrap();
|
|
||||||
|
|
||||||
collected_rent += loaded_transaction.rent;
|
collected_rent += loaded_transaction.rent;
|
||||||
rent_debits.push(mem::take(&mut loaded_transaction.rent_debits));
|
mem::take(&mut loaded_transaction.rent_debits)
|
||||||
|
} else {
|
||||||
|
RentDebits::default()
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
self.collected_rent.fetch_add(collected_rent, Relaxed);
|
self.collected_rent.fetch_add(collected_rent, Relaxed);
|
||||||
rent_debits
|
rent_debits
|
||||||
}
|
}
|
||||||
@@ -4630,27 +4719,15 @@ impl Bank {
|
|||||||
enable_cpi_recording: bool,
|
enable_cpi_recording: bool,
|
||||||
enable_log_recording: bool,
|
enable_log_recording: bool,
|
||||||
timings: &mut ExecuteTimings,
|
timings: &mut ExecuteTimings,
|
||||||
) -> (
|
) -> (TransactionResults, TransactionBalancesSet) {
|
||||||
TransactionResults,
|
|
||||||
TransactionBalancesSet,
|
|
||||||
Vec<Option<InnerInstructionsList>>,
|
|
||||||
Vec<Option<TransactionLogMessages>>,
|
|
||||||
) {
|
|
||||||
let pre_balances = if collect_balances {
|
let pre_balances = if collect_balances {
|
||||||
self.collect_balances(batch)
|
self.collect_balances(batch)
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
let (
|
let (mut loaded_txs, execution_results, _, tx_count, signature_count) = self
|
||||||
mut loaded_txs,
|
.load_and_execute_transactions(
|
||||||
executed,
|
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
_,
|
|
||||||
tx_count,
|
|
||||||
signature_count,
|
|
||||||
) = self.load_and_execute_transactions(
|
|
||||||
batch,
|
batch,
|
||||||
max_age,
|
max_age,
|
||||||
enable_cpi_recording,
|
enable_cpi_recording,
|
||||||
@@ -4661,7 +4738,7 @@ impl Bank {
|
|||||||
let results = self.commit_transactions(
|
let results = self.commit_transactions(
|
||||||
batch.sanitized_transactions(),
|
batch.sanitized_transactions(),
|
||||||
&mut loaded_txs,
|
&mut loaded_txs,
|
||||||
&executed,
|
execution_results,
|
||||||
tx_count,
|
tx_count,
|
||||||
signature_count,
|
signature_count,
|
||||||
timings,
|
timings,
|
||||||
@@ -4674,8 +4751,6 @@ impl Bank {
|
|||||||
(
|
(
|
||||||
results,
|
results,
|
||||||
TransactionBalancesSet::new(pre_balances, post_balances),
|
TransactionBalancesSet::new(pre_balances, post_balances),
|
||||||
inner_instructions,
|
|
||||||
transaction_logs,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5522,18 +5597,15 @@ impl Bank {
|
|||||||
fn update_stakes_cache(
|
fn update_stakes_cache(
|
||||||
&self,
|
&self,
|
||||||
txs: &[SanitizedTransaction],
|
txs: &[SanitizedTransaction],
|
||||||
res: &[TransactionExecutionResult],
|
execution_results: &[TransactionExecutionResult],
|
||||||
loaded_txs: &[TransactionLoadResult],
|
loaded_txs: &[TransactionLoadResult],
|
||||||
) {
|
) {
|
||||||
for (i, ((raccs, _load_nonce), tx)) in loaded_txs.iter().zip(txs).enumerate() {
|
for (i, ((load_result, _load_nonce), tx)) in loaded_txs.iter().zip(txs).enumerate() {
|
||||||
let (res, _res_nonce) = &res[i];
|
if let (Ok(loaded_transaction), true) = (
|
||||||
if res.is_err() || raccs.is_err() {
|
load_result,
|
||||||
continue;
|
execution_results[i].was_executed_successfully(),
|
||||||
}
|
) {
|
||||||
|
|
||||||
let message = tx.message();
|
let message = tx.message();
|
||||||
let loaded_transaction = raccs.as_ref().unwrap();
|
|
||||||
|
|
||||||
for (_i, (pubkey, account)) in
|
for (_i, (pubkey, account)) in
|
||||||
(0..message.account_keys_len()).zip(loaded_transaction.accounts.iter())
|
(0..message.account_keys_len()).zip(loaded_transaction.accounts.iter())
|
||||||
{
|
{
|
||||||
@@ -5545,6 +5617,7 @@ impl Bank {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
|
pub fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
|
||||||
self.stakes_cache.stakes().staked_nodes()
|
self.stakes_cache.stakes().staked_nodes()
|
||||||
@@ -6303,6 +6376,18 @@ pub(crate) mod tests {
|
|||||||
Message::new(instructions, payer).try_into().unwrap()
|
Message::new(instructions, payer).try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_execution_result(
|
||||||
|
status: Result<()>,
|
||||||
|
nonce: Option<&NonceFull>,
|
||||||
|
) -> TransactionExecutionResult {
|
||||||
|
TransactionExecutionResult::Executed(TransactionExecutionDetails {
|
||||||
|
status,
|
||||||
|
log_messages: None,
|
||||||
|
inner_instructions: None,
|
||||||
|
durable_nonce_fee: nonce.map(DurableNonceFee::from),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nonce_info() {
|
fn test_nonce_info() {
|
||||||
let lamports_per_signature = 42;
|
let lamports_per_signature = 42;
|
||||||
@@ -9042,8 +9127,8 @@ pub(crate) mod tests {
|
|||||||
));
|
));
|
||||||
|
|
||||||
let results = vec![
|
let results = vec![
|
||||||
(Ok(()), None),
|
new_execution_result(Ok(()), None),
|
||||||
(
|
new_execution_result(
|
||||||
Err(TransactionError::InstructionError(
|
Err(TransactionError::InstructionError(
|
||||||
1,
|
1,
|
||||||
SystemError::ResultWithNegativeLamports.into(),
|
SystemError::ResultWithNegativeLamports.into(),
|
||||||
@@ -11411,8 +11496,8 @@ pub(crate) mod tests {
|
|||||||
let txs = vec![tx0, tx1, tx2];
|
let txs = vec![tx0, tx1, tx2];
|
||||||
|
|
||||||
let lock_result = bank0.prepare_batch_for_tests(txs);
|
let lock_result = bank0.prepare_batch_for_tests(txs);
|
||||||
let (transaction_results, transaction_balances_set, inner_instructions, transaction_logs) =
|
let (transaction_results, transaction_balances_set) = bank0
|
||||||
bank0.load_execute_and_commit_transactions(
|
.load_execute_and_commit_transactions(
|
||||||
&lock_result,
|
&lock_result,
|
||||||
MAX_PROCESSING_AGE,
|
MAX_PROCESSING_AGE,
|
||||||
true,
|
true,
|
||||||
@@ -11421,27 +11506,34 @@ pub(crate) mod tests {
|
|||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(inner_instructions.iter().all(Option::is_none));
|
|
||||||
assert!(transaction_logs.iter().all(Option::is_none));
|
|
||||||
|
|
||||||
assert_eq!(inner_instructions.len(), 3);
|
|
||||||
assert_eq!(transaction_logs.len(), 3);
|
|
||||||
assert_eq!(transaction_balances_set.pre_balances.len(), 3);
|
assert_eq!(transaction_balances_set.pre_balances.len(), 3);
|
||||||
assert_eq!(transaction_balances_set.post_balances.len(), 3);
|
assert_eq!(transaction_balances_set.post_balances.len(), 3);
|
||||||
|
|
||||||
assert!(transaction_results.execution_results[0].0.is_ok());
|
assert!(transaction_results.execution_results[0].was_executed_successfully());
|
||||||
assert_eq!(transaction_balances_set.pre_balances[0], vec![8, 11, 1]);
|
assert_eq!(transaction_balances_set.pre_balances[0], vec![8, 11, 1]);
|
||||||
assert_eq!(transaction_balances_set.post_balances[0], vec![5, 13, 1]);
|
assert_eq!(transaction_balances_set.post_balances[0], vec![5, 13, 1]);
|
||||||
|
|
||||||
// Failed transactions still produce balance sets
|
// Failed transactions still produce balance sets
|
||||||
// This is a TransactionError - not possible to charge fees
|
// This is a TransactionError - not possible to charge fees
|
||||||
assert!(transaction_results.execution_results[1].0.is_err());
|
assert!(matches!(
|
||||||
|
transaction_results.execution_results[1],
|
||||||
|
TransactionExecutionResult::NotExecuted(TransactionError::AccountNotFound),
|
||||||
|
));
|
||||||
assert_eq!(transaction_balances_set.pre_balances[1], vec![0, 0, 1]);
|
assert_eq!(transaction_balances_set.pre_balances[1], vec![0, 0, 1]);
|
||||||
assert_eq!(transaction_balances_set.post_balances[1], vec![0, 0, 1]);
|
assert_eq!(transaction_balances_set.post_balances[1], vec![0, 0, 1]);
|
||||||
|
|
||||||
// Failed transactions still produce balance sets
|
// Failed transactions still produce balance sets
|
||||||
// This is an InstructionError - fees charged
|
// This is an InstructionError - fees charged
|
||||||
assert!(transaction_results.execution_results[2].0.is_err());
|
assert!(matches!(
|
||||||
|
transaction_results.execution_results[2],
|
||||||
|
TransactionExecutionResult::Executed(TransactionExecutionDetails {
|
||||||
|
status: Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::Custom(1),
|
||||||
|
)),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
));
|
||||||
assert_eq!(transaction_balances_set.pre_balances[2], vec![9, 0, 1]);
|
assert_eq!(transaction_balances_set.pre_balances[2], vec![9, 0, 1]);
|
||||||
assert_eq!(transaction_balances_set.post_balances[2], vec![8, 0, 1]);
|
assert_eq!(transaction_balances_set.post_balances[2], vec![8, 0, 1]);
|
||||||
}
|
}
|
||||||
@@ -14546,7 +14638,7 @@ pub(crate) mod tests {
|
|||||||
let txs = vec![tx0, tx1, tx2];
|
let txs = vec![tx0, tx1, tx2];
|
||||||
let batch = bank.prepare_batch_for_tests(txs);
|
let batch = bank.prepare_batch_for_tests(txs);
|
||||||
|
|
||||||
let log_results = bank
|
let execution_results = bank
|
||||||
.load_execute_and_commit_transactions(
|
.load_execute_and_commit_transactions(
|
||||||
&batch,
|
&batch,
|
||||||
MAX_PROCESSING_AGE,
|
MAX_PROCESSING_AGE,
|
||||||
@@ -14555,11 +14647,28 @@ pub(crate) mod tests {
|
|||||||
true,
|
true,
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
)
|
)
|
||||||
.3;
|
.0
|
||||||
assert_eq!(log_results.len(), 3);
|
.execution_results;
|
||||||
assert!(log_results[0].as_ref().unwrap()[1].contains(&"success".to_string()));
|
|
||||||
assert!(log_results[1].as_ref().unwrap()[2].contains(&"failed".to_string()));
|
assert_eq!(execution_results.len(), 3);
|
||||||
assert!(log_results[2].as_ref().is_none());
|
|
||||||
|
assert!(execution_results[0].details().is_some());
|
||||||
|
assert!(execution_results[0]
|
||||||
|
.details()
|
||||||
|
.unwrap()
|
||||||
|
.log_messages
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()[1]
|
||||||
|
.contains(&"success".to_string()));
|
||||||
|
assert!(execution_results[1].details().is_some());
|
||||||
|
assert!(execution_results[1]
|
||||||
|
.details()
|
||||||
|
.unwrap()
|
||||||
|
.log_messages
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()[2]
|
||||||
|
.contains(&"failed".to_string()));
|
||||||
|
assert!(!execution_results[2].was_executed());
|
||||||
|
|
||||||
let stored_logs = &bank.transaction_log_collector.read().unwrap().logs;
|
let stored_logs = &bank.transaction_log_collector.read().unwrap().logs;
|
||||||
let success_log_info = stored_logs
|
let success_log_info = stored_logs
|
||||||
|
@@ -43,8 +43,8 @@ pub fn find_and_send_votes(
|
|||||||
sanitized_txs
|
sanitized_txs
|
||||||
.iter()
|
.iter()
|
||||||
.zip(execution_results.iter())
|
.zip(execution_results.iter())
|
||||||
.for_each(|(tx, (result, _nonce))| {
|
.for_each(|(tx, result)| {
|
||||||
if tx.is_simple_vote_transaction() && result.is_ok() {
|
if tx.is_simple_vote_transaction() && result.was_executed_successfully() {
|
||||||
if let Some(parsed_vote) =
|
if let Some(parsed_vote) =
|
||||||
vote_transaction::parse_sanitized_vote_transaction(tx)
|
vote_transaction::parse_sanitized_vote_transaction(tx)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user