use crate::block::pb::TransactionStatus::TransactionstatusExecuted;
use crate::pb;
impl pb::Block {
pub fn transaction_traces(&self) -> impl Iterator<Item = &pb::TransactionTrace> {
let traces = if self.filtering_applied {
self.filtered_transaction_traces.iter()
} else {
self.unfiltered_transaction_traces.iter()
};
traces.filter(|trx| trx.receipt.is_some() && trx.receipt.as_ref().unwrap().status == TransactionstatusExecuted as i32)
}
pub fn into_transaction_traces(self) -> impl Iterator<Item = pb::TransactionTrace> {
let traces = if self.filtering_applied {
self.filtered_transaction_traces.into_iter()
} else {
self.unfiltered_transaction_traces.into_iter()
};
traces.filter(|trx| trx.receipt.is_some() && trx.receipt.as_ref().unwrap().status == TransactionstatusExecuted as i32)
}
pub fn action_traces(&self) -> impl Iterator<Item = (&pb::ActionTrace, &pb::TransactionTrace)> {
self.transaction_traces().flat_map(|trx| trx.action_traces.iter().map(move |trace| (trace, trx)))
}
pub fn into_action_traces(self) -> impl Iterator<Item = pb::ActionTrace> {
self.into_transaction_traces().flat_map(|trx| trx.action_traces)
}
pub fn all_actions<'a, A: crate::action::Action>(&'a self, accounts: &'a [&str]) -> impl Iterator<Item = (A, &pb::ActionTrace, &pb::TransactionTrace)> + 'a {
self.action_traces().filter_map(|(trace, trx)| {
let contract = trace.action.as_ref().unwrap().account.as_str();
if !accounts.is_empty() && !accounts.contains(&contract) {
return None;
}
A::match_and_decode(trace).map(|action| (action, trace, trx))
})
}
pub fn actions<'a, A: crate::action::Action>(&'a self, accounts: &'a [&str]) -> impl Iterator<Item = (A, &pb::ActionTrace, &pb::TransactionTrace)> + 'a {
self.all_actions(accounts)
.filter(|(_, trace, _)| trace.receiver.as_str() == trace.action.as_ref().unwrap().account.as_str())
}
pub fn notifications<'a, A: crate::action::Action>(&'a self, accounts: &'a [&str]) -> impl Iterator<Item = (A, &pb::ActionTrace, &pb::TransactionTrace)> + 'a {
self.all_actions(accounts)
.filter(|(_, trace, _)| trace.receiver.as_str() != trace.action.as_ref().unwrap().account.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::block::pb::TransactionStatus::{TransactionstatusExecuted, TransactionstatusSoftfail};
fn create_test_block(
filtering_applied: bool,
unfiltered_transaction_traces: Vec<pb::TransactionTrace>,
filtered_transaction_traces: Vec<pb::TransactionTrace>,
unfiltered_transaction_count: u32,
filtered_transaction_count: u32,
unfiltered_executed_input_action_count: u32,
filtered_executed_input_action_count: u32,
unfiltered_executed_total_action_count: u32,
filtered_executed_total_action_count: u32,
) -> pb::Block {
pb::Block {
filtering_applied,
unfiltered_transaction_traces,
filtered_transaction_traces,
unfiltered_transaction_count,
filtered_transaction_count,
unfiltered_executed_input_action_count,
filtered_executed_input_action_count,
unfiltered_executed_total_action_count,
filtered_executed_total_action_count,
..Default::default()
}
}
#[test]
fn test_all_transaction_traces() {
let unfiltered_traces = vec![
pb::TransactionTrace {
id: String::from("trx1"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusExecuted as i32,
..Default::default()
}),
..Default::default()
},
pb::TransactionTrace {
id: String::from("trx2"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusExecuted as i32,
..Default::default()
}),
..Default::default()
},
];
let block = create_test_block(false, unfiltered_traces.clone(), vec![], 2, 0, 5, 0, 7, 0);
let all_traces: Vec<_> = block.into_transaction_traces().collect();
assert_eq!(all_traces, unfiltered_traces);
}
#[test]
fn test_all_action_traces() {
let unfiltered_traces = vec![
pb::TransactionTrace {
id: String::from("trx1"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusExecuted as i32,
..Default::default()
}),
action_traces: vec![pb::ActionTrace { ..Default::default() }],
..Default::default()
},
pb::TransactionTrace {
id: String::from("trx2"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusExecuted as i32,
..Default::default()
}),
action_traces: vec![pb::ActionTrace { ..Default::default() }, pb::ActionTrace { ..Default::default() }],
..Default::default()
},
];
let block = create_test_block(false, unfiltered_traces.clone(), vec![], 2, 0, 5, 0, 7, 0);
let all_traces: Vec<_> = block.action_traces().collect();
assert_eq!(all_traces.len(), 3);
}
#[test]
fn test_executed_transaction_traces() {
let executed_traces = vec![
pb::TransactionTrace {
id: String::from("trx1"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusExecuted as i32,
..Default::default()
}),
..Default::default()
},
pb::TransactionTrace {
id: String::from("trx2"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusExecuted as i32,
..Default::default()
}),
..Default::default()
},
];
let block = create_test_block(false, executed_traces.clone(), vec![], 2, 0, 5, 0, 7, 0);
let produced_traces: Vec<_> = block.transaction_traces().cloned().collect();
assert_eq!(executed_traces, produced_traces);
}
#[test]
fn test_executed_transaction_traces_no_receipt() {
let executed_traces = vec![
pb::TransactionTrace {
id: String::from("trx1"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusExecuted as i32,
..Default::default()
}),
..Default::default()
},
pb::TransactionTrace {
id: String::from("trx2"),
receipt: Some(pb::TransactionReceiptHeader {
status: TransactionstatusSoftfail as i32,
..Default::default()
}),
..Default::default()
},
pb::TransactionTrace {
id: String::from("trx3"),
receipt: None,
..Default::default()
},
];
let block = create_test_block(false, executed_traces.clone(), vec![], 2, 0, 5, 0, 7, 0);
let produced_traces: Vec<_> = block.transaction_traces().cloned().collect();
assert_eq!(executed_traces[0], produced_traces[0]);
assert_eq!(produced_traces.len(), 1);
}
}