Refactor: Improve type safety and readability of transaction execution (backport #22215) (#22289)

* 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:
mergify[bot]
2022-01-05 23:01:15 +08:00
committed by GitHub
parent 87f4a1f4b6
commit 8578429c4d
8 changed files with 626 additions and 503 deletions

View File

@ -17,7 +17,6 @@ use solana_bpf_loader_program::{
use solana_bpf_rust_invoke::instructions::*;
use solana_bpf_rust_realloc::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_rbpf::{
elf::Executable,
@ -25,7 +24,10 @@ use solana_rbpf::{
vm::{Config, Tracer},
};
use solana_runtime::{
bank::{Bank, ExecuteTimings, NonceInfo, TransactionBalancesSet, TransactionResults},
bank::{
Bank, DurableNonceFee, ExecuteTimings, TransactionBalancesSet, TransactionExecutionDetails,
TransactionExecutionResult, TransactionResults,
},
bank_client::BankClient,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
loader_utils::{
@ -53,12 +55,13 @@ use solana_sdk::{
};
use solana_transaction_status::{
token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions,
TransactionStatusMeta, TransactionWithStatusMeta, UiTransactionEncoding,
TransactionStatusMeta, TransactionWithStatusMeta,
};
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,
};
use std::{collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, sync::Arc};
/// BPF program file extension
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 txs = vec![tx];
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(
&tx_batch,
MAX_PROCESSING_AGE,
@ -307,20 +310,27 @@ fn process_transaction_and_record_inner(
true,
false,
&mut ExecuteTimings::default(),
);
)
.0;
let result = results
.fee_collection_results
.swap_remove(0)
.and_then(|_| bank.get_signature_status(&signature).unwrap());
(
result,
inner_instructions
.swap_remove(0)
.expect("cpi recording should be enabled"),
)
let inner_instructions = results
.execution_results
.swap_remove(0)
.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 mut timings = ExecuteTimings::default();
let mut mint_decimals = HashMap::new();
@ -334,8 +344,6 @@ fn execute_transactions(bank: &Bank, txs: Vec<Transaction>) -> Vec<ConfirmedTran
post_balances,
..
},
inner_instructions,
transaction_logs,
) = bank.load_execute_and_commit_transactions(
&batch,
std::usize::MAX,
@ -349,80 +357,84 @@ fn execute_transactions(bank: &Bank, txs: Vec<Transaction>) -> Vec<ConfirmedTran
izip!(
txs.iter(),
execution_results.into_iter(),
inner_instructions.into_iter(),
pre_balances.into_iter(),
post_balances.into_iter(),
tx_pre_token_balances.into_iter(),
tx_post_token_balances.into_iter(),
transaction_logs.into_iter(),
)
.map(
|(
tx,
(execute_result, nonce),
inner_instructions,
execution_result,
pre_balances,
post_balances,
pre_token_balances,
post_token_balances,
log_messages,
)| {
let lamports_per_signature = nonce
.map(|nonce| nonce.lamports_per_signature())
.unwrap_or_else(|| {
bank.get_lamports_per_signature_for_blockhash(&tx.message().recent_blockhash)
})
.expect("lamports_per_signature must exist");
let fee = Bank::get_fee_for_message_with_lamports_per_signature(
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
lamports_per_signature,
);
match execution_result {
TransactionExecutionResult::Executed(details) => {
let TransactionExecutionDetails {
status,
log_messages,
inner_instructions,
durable_nonce_fee,
} = details;
let inner_instructions = inner_instructions.map(|inner_instructions| {
inner_instructions
.into_iter()
.enumerate()
.map(|(index, instructions)| InnerInstructions {
index: index as u8,
instructions,
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(
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
lamports_per_signature,
);
let inner_instructions = inner_instructions.map(|inner_instructions| {
inner_instructions
.into_iter()
.enumerate()
.map(|(index, instructions)| InnerInstructions {
index: index as u8,
instructions,
})
.filter(|i| !i.instructions.is_empty())
.collect()
});
let tx_status_meta = TransactionStatusMeta {
status,
fee,
pre_balances,
post_balances,
pre_token_balances: Some(pre_token_balances),
post_token_balances: Some(post_token_balances),
inner_instructions,
log_messages,
rewards: None,
};
Ok(ConfirmedTransaction {
slot: bank.slot(),
transaction: TransactionWithStatusMeta {
transaction: tx.clone(),
meta: Some(tx_status_meta),
},
block_time: None,
})
.filter(|i| !i.instructions.is_empty())
.collect()
});
let tx_status_meta = TransactionStatusMeta {
status: execute_result,
fee,
pre_balances,
post_balances,
pre_token_balances: Some(pre_token_balances),
post_token_balances: Some(post_token_balances),
inner_instructions,
log_messages,
rewards: None,
};
ConfirmedTransaction {
slot: bank.slot(),
transaction: TransactionWithStatusMeta {
transaction: tx.clone(),
meta: Some(tx_status_meta),
},
block_time: None,
}
TransactionExecutionResult::NotExecuted(err) => Err(err.clone()),
}
},
)
.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]
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
fn test_program_bpf_sanity() {
@ -2450,43 +2462,35 @@ fn test_program_upgradeable_locks() {
execute_transactions(&bank, vec![invoke_tx, upgrade_tx])
};
if false {
println!("upgrade and invoke");
for result in &results1 {
print_confirmed_tx("result", result.clone());
}
println!("invoke and upgrade");
for result in &results2 {
print_confirmed_tx("result", result.clone());
}
}
assert!(matches!(
results1[0],
Ok(ConfirmedTransactionWithStatusMeta {
transaction: TransactionWithStatusMeta {
meta: Some(TransactionStatusMeta { status: Ok(()), .. }),
..
},
..
})
));
assert_eq!(results1[1], Err(TransactionError::AccountInUse));
if let Some(ref meta) = results1[0].transaction.meta {
assert_eq!(meta.status, Ok(()));
} else {
panic!("no meta");
}
if let Some(ref meta) = results1[1].transaction.meta {
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,
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!(matches!(
results2[0],
Ok(ConfirmedTransactionWithStatusMeta {
transaction: TransactionWithStatusMeta {
meta: Some(TransactionStatusMeta {
status: Err(TransactionError::InstructionError(
0,
InstructionError::ProgramFailedToComplete
)),
..
}),
..
},
..
})
));
assert_eq!(results2[1], Err(TransactionError::AccountInUse));
}
#[cfg(feature = "bpf_rust")]