use alloy::{
consensus::{ReceiptEnvelope, TxReceipt},
primitives::{Address, Bloom, Bytes, Log},
};
use std::sync::OnceLock;
#[derive(Debug, Clone)]
pub struct BlockOutput<T: TxReceipt = ReceiptEnvelope> {
receipts: Vec<T>,
senders: Vec<Address>,
bloom: OnceLock<Bloom>,
}
impl Default for BlockOutput {
fn default() -> Self {
Self::with_capacity(0)
}
}
impl<T: TxReceipt<Log = alloy::primitives::Log>> BlockOutput<T> {
pub fn with_capacity(capacity: usize) -> Self {
Self {
receipts: Vec::with_capacity(capacity),
senders: Vec::with_capacity(capacity),
bloom: Default::default(),
}
}
fn seal(&self) {
self.bloom.get_or_init(|| {
let mut bloom = Bloom::default();
for log in self.logs() {
bloom.accrue_log(log);
}
bloom
});
}
fn unseal(&mut self) {
self.bloom.take();
}
pub fn reserve(&mut self, capacity: usize) {
self.receipts.reserve(capacity);
self.senders.reserve(capacity);
}
#[allow(clippy::missing_const_for_fn)] pub fn receipts(&self) -> &[T] {
&self.receipts
}
pub fn logs(&self) -> impl Iterator<Item = &Log> {
self.receipts.iter().flat_map(|r| r.logs())
}
pub fn logs_bloom(&self) -> Bloom {
self.seal();
self.bloom.get().cloned().unwrap()
}
#[allow(clippy::missing_const_for_fn)] pub fn senders(&self) -> &[Address] {
&self.senders
}
pub fn cumulative_gas_used(&self) -> u64 {
self.receipts().last().map(TxReceipt::cumulative_gas_used).unwrap_or_default()
}
pub fn push_result(&mut self, receipt: T, sender: Address) {
self.unseal();
self.push_receipt(receipt);
self.push_sender(sender);
}
fn push_receipt(&mut self, receipt: T) {
self.receipts.push(receipt);
}
fn push_sender(&mut self, sender: Address) {
self.senders.push(sender);
}
pub fn find_deposit_requests(&self) -> impl Iterator<Item = Bytes> + use<'_, T> {
crate::system::eip6110::check_logs_for_deposits(
self.receipts().iter().flat_map(TxReceipt::logs),
)
}
pub fn into_parts(self) -> (Vec<T>, Vec<Address>, Bloom) {
let bloom = self.logs_bloom();
(self.receipts, self.senders, bloom)
}
}
impl<T: TxReceipt + PartialEq> PartialEq for BlockOutput<T> {
fn eq(&self, other: &Self) -> bool {
self.receipts == other.receipts && self.senders == other.senders
}
}
impl<T: TxReceipt + Eq> Eq for BlockOutput<T> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn block_output_eq_with_one_populated_bloom() {
let output_a = BlockOutput::default();
let output_b = BlockOutput::default();
output_a.logs_bloom();
assert!(output_a.bloom.get().is_some());
assert!(output_b.bloom.get().is_none());
assert_eq!(output_a, output_b);
}
}