Add solana logs
command
This commit is contained in:
@ -404,6 +404,45 @@ pub type InnerInstructionsList = Vec<InnerInstructions>;
|
||||
/// A list of log messages emitted during a transaction
|
||||
pub type TransactionLogMessages = Vec<String>;
|
||||
|
||||
#[derive(Serialize, Deserialize, AbiExample, AbiEnumVisitor, Debug, PartialEq)]
|
||||
pub enum TransactionLogCollectorFilter {
|
||||
All,
|
||||
AllWithVotes,
|
||||
None,
|
||||
OnlyMentionedAddresses,
|
||||
}
|
||||
|
||||
impl Default for TransactionLogCollectorFilter {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AbiExample, Debug, Default)]
|
||||
pub struct TransactionLogCollectorConfig {
|
||||
pub mentioned_addresses: HashSet<Pubkey>,
|
||||
pub filter: TransactionLogCollectorFilter,
|
||||
}
|
||||
|
||||
#[derive(AbiExample, Clone, Debug)]
|
||||
pub struct TransactionLogInfo {
|
||||
pub signature: Signature,
|
||||
pub result: Result<()>,
|
||||
pub is_vote: bool,
|
||||
pub log_messages: TransactionLogMessages,
|
||||
}
|
||||
|
||||
#[derive(AbiExample, Default, Debug)]
|
||||
pub struct TransactionLogCollector {
|
||||
// All the logs collected for from this Bank. Exact contents depend on the
|
||||
// active `TransactionLogCollectorFilter`
|
||||
pub logs: Vec<TransactionLogInfo>,
|
||||
|
||||
// For each `mentioned_addresses`, maintain a list of indicies into `logs` to easily
|
||||
// locate the logs from transactions that included the mentioned addresses.
|
||||
pub mentioned_address_map: HashMap<Pubkey, Vec<usize>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum HashAgeKind {
|
||||
Extant,
|
||||
@ -724,6 +763,13 @@ pub struct Bank {
|
||||
|
||||
transaction_debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
|
||||
// Global configuration for how transaction logs should be collected across all banks
|
||||
pub transaction_log_collector_config: Arc<RwLock<TransactionLogCollectorConfig>>,
|
||||
|
||||
// Logs from transactions that this Bank executed collected according to the criteria in
|
||||
// `transaction_log_collector_config`
|
||||
pub transaction_log_collector: Arc<RwLock<TransactionLogCollector>>,
|
||||
|
||||
pub feature_set: Arc<FeatureSet>,
|
||||
}
|
||||
|
||||
@ -868,6 +914,8 @@ impl Bank {
|
||||
rewards_pool_pubkeys: parent.rewards_pool_pubkeys.clone(),
|
||||
cached_executors: RwLock::new((*parent.cached_executors.read().unwrap()).clone()),
|
||||
transaction_debug_keys: parent.transaction_debug_keys.clone(),
|
||||
transaction_log_collector_config: parent.transaction_log_collector_config.clone(),
|
||||
transaction_log_collector: Arc::new(RwLock::new(TransactionLogCollector::default())),
|
||||
feature_set: parent.feature_set.clone(),
|
||||
};
|
||||
|
||||
@ -984,6 +1032,8 @@ impl Bank {
|
||||
CachedExecutors::new(MAX_CACHED_EXECUTORS),
|
||||
)))),
|
||||
transaction_debug_keys: debug_keys,
|
||||
transaction_log_collector_config: new(),
|
||||
transaction_log_collector: new(),
|
||||
feature_set: new(),
|
||||
};
|
||||
bank.finish_init(genesis_config, additional_builtins);
|
||||
@ -2117,7 +2167,10 @@ impl Bank {
|
||||
}
|
||||
|
||||
/// Run transactions against a frozen bank without committing the results
|
||||
pub fn simulate_transaction(&self, transaction: Transaction) -> (Result<()>, Vec<String>) {
|
||||
pub fn simulate_transaction(
|
||||
&self,
|
||||
transaction: Transaction,
|
||||
) -> (Result<()>, TransactionLogMessages) {
|
||||
assert!(self.is_frozen(), "simulation bank must be frozen");
|
||||
|
||||
let txs = &[transaction];
|
||||
@ -2127,7 +2180,7 @@ impl Bank {
|
||||
_loaded_accounts,
|
||||
executed,
|
||||
_inner_instructions,
|
||||
transaction_logs,
|
||||
log_messages,
|
||||
_retryable_transactions,
|
||||
_transaction_count,
|
||||
_signature_count,
|
||||
@ -2142,7 +2195,7 @@ impl Bank {
|
||||
);
|
||||
|
||||
let transaction_result = executed[0].0.clone().map(|_| ());
|
||||
let log_messages = transaction_logs
|
||||
let log_messages = log_messages
|
||||
.get(0)
|
||||
.map_or(vec![], |messages| messages.to_vec());
|
||||
|
||||
@ -2182,6 +2235,7 @@ impl Bank {
|
||||
&self.feature_set,
|
||||
)
|
||||
}
|
||||
|
||||
fn check_age(
|
||||
&self,
|
||||
txs: &[Transaction],
|
||||
@ -2213,6 +2267,7 @@ impl Bank {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn check_signatures(
|
||||
&self,
|
||||
txs: &[Transaction],
|
||||
@ -2249,6 +2304,7 @@ impl Bank {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn filter_by_vote_transactions(
|
||||
&self,
|
||||
txs: &[Transaction],
|
||||
@ -2260,23 +2316,8 @@ impl Bank {
|
||||
.zip(lock_results.into_iter())
|
||||
.map(|((_, tx), lock_res)| {
|
||||
if lock_res.0.is_ok() {
|
||||
if tx.message.instructions.len() == 1 {
|
||||
let instruction = &tx.message.instructions[0];
|
||||
let program_pubkey =
|
||||
tx.message.account_keys[instruction.program_id_index as usize];
|
||||
if program_pubkey == solana_vote_program::id() {
|
||||
if let Ok(vote_instruction) =
|
||||
limited_deserialize::<VoteInstruction>(&instruction.data)
|
||||
{
|
||||
match vote_instruction {
|
||||
VoteInstruction::Vote(_)
|
||||
| VoteInstruction::VoteSwitch(_, _) => {
|
||||
return lock_res;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_simple_vote_transaction(tx) {
|
||||
return lock_res;
|
||||
}
|
||||
|
||||
error_counters.not_allowed_during_cluster_maintenance += 1;
|
||||
@ -2598,7 +2639,7 @@ impl Bank {
|
||||
let mut signature_count: u64 = 0;
|
||||
let mut inner_instructions: Vec<Option<InnerInstructionsList>> =
|
||||
Vec::with_capacity(txs.len());
|
||||
let mut transaction_logs: Vec<TransactionLogMessages> = Vec::with_capacity(txs.len());
|
||||
let mut transaction_log_messages = Vec::with_capacity(txs.len());
|
||||
let bpf_compute_budget = self
|
||||
.bpf_compute_budget
|
||||
.unwrap_or_else(|| BpfComputeBudget::new(&self.feature_set));
|
||||
@ -2649,7 +2690,7 @@ impl Bank {
|
||||
.unwrap_or_default()
|
||||
.into();
|
||||
|
||||
transaction_logs.push(log_messages);
|
||||
transaction_log_messages.push(log_messages);
|
||||
}
|
||||
|
||||
Self::compile_recorded_instructions(
|
||||
@ -2686,7 +2727,10 @@ impl Bank {
|
||||
|
||||
let mut tx_count: u64 = 0;
|
||||
let err_count = &mut error_counters.total;
|
||||
for ((r, _hash_age_kind), tx) in executed.iter().zip(txs.iter()) {
|
||||
let transaction_log_collector_config =
|
||||
self.transaction_log_collector_config.read().unwrap();
|
||||
|
||||
for (i, ((r, _hash_age_kind), tx)) in executed.iter().zip(txs.iter()).enumerate() {
|
||||
if let Some(debug_keys) = &self.transaction_debug_keys {
|
||||
for key in &tx.message.account_keys {
|
||||
if debug_keys.contains(key) {
|
||||
@ -2695,6 +2739,50 @@ impl Bank {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if transaction_log_collector_config.filter != TransactionLogCollectorFilter::None {
|
||||
let mut transaction_log_collector = self.transaction_log_collector.write().unwrap();
|
||||
let transaction_log_index = transaction_log_collector.logs.len();
|
||||
|
||||
let mut mentioned_address = false;
|
||||
if !transaction_log_collector_config
|
||||
.mentioned_addresses
|
||||
.is_empty()
|
||||
{
|
||||
for key in &tx.message.account_keys {
|
||||
if transaction_log_collector_config
|
||||
.mentioned_addresses
|
||||
.contains(key)
|
||||
{
|
||||
transaction_log_collector
|
||||
.mentioned_address_map
|
||||
.entry(*key)
|
||||
.or_default()
|
||||
.push(transaction_log_index);
|
||||
mentioned_address = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let is_vote = is_simple_vote_transaction(tx);
|
||||
|
||||
let store = match transaction_log_collector_config.filter {
|
||||
TransactionLogCollectorFilter::All => !is_vote || mentioned_address,
|
||||
TransactionLogCollectorFilter::AllWithVotes => true,
|
||||
TransactionLogCollectorFilter::None => false,
|
||||
TransactionLogCollectorFilter::OnlyMentionedAddresses => mentioned_address,
|
||||
};
|
||||
|
||||
if store {
|
||||
transaction_log_collector.logs.push(TransactionLogInfo {
|
||||
signature: tx.signatures[0],
|
||||
result: r.clone(),
|
||||
is_vote,
|
||||
log_messages: transaction_log_messages.get(i).cloned().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if r.is_ok() {
|
||||
tx_count += 1;
|
||||
} else {
|
||||
@ -2716,7 +2804,7 @@ impl Bank {
|
||||
loaded_accounts,
|
||||
executed,
|
||||
inner_instructions,
|
||||
transaction_logs,
|
||||
transaction_log_messages,
|
||||
retryable_txs,
|
||||
tx_count,
|
||||
signature_count,
|
||||
@ -3673,6 +3761,26 @@ impl Bank {
|
||||
.load_by_program_slot(self.slot(), Some(program_id))
|
||||
}
|
||||
|
||||
pub fn get_transaction_logs(
|
||||
&self,
|
||||
address: Option<&Pubkey>,
|
||||
) -> Option<Vec<TransactionLogInfo>> {
|
||||
let transaction_log_collector = self.transaction_log_collector.read().unwrap();
|
||||
|
||||
match address {
|
||||
None => Some(transaction_log_collector.logs.clone()),
|
||||
Some(address) => transaction_log_collector
|
||||
.mentioned_address_map
|
||||
.get(address)
|
||||
.map(|log_indices| {
|
||||
log_indices
|
||||
.iter()
|
||||
.map(|i| transaction_log_collector.logs[*i].clone())
|
||||
.collect()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_accounts_modified_since_parent(&self) -> Vec<(Pubkey, Account)> {
|
||||
self.rc.accounts.load_by_program_slot(self.slot(), None)
|
||||
}
|
||||
@ -4377,6 +4485,21 @@ pub fn goto_end_of_slot(bank: &mut Bank) {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_simple_vote_transaction(transaction: &Transaction) -> bool {
|
||||
if transaction.message.instructions.len() == 1 {
|
||||
let instruction = &transaction.message.instructions[0];
|
||||
let program_pubkey =
|
||||
transaction.message.account_keys[instruction.program_id_index as usize];
|
||||
if program_pubkey == solana_vote_program::id() {
|
||||
if let Ok(vote_instruction) = limited_deserialize::<VoteInstruction>(&instruction.data)
|
||||
{
|
||||
return matches!(vote_instruction, VoteInstruction::Vote(_) | VoteInstruction::VoteSwitch(_, _));
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
Reference in New Issue
Block a user