use alloy::{
consensus::crypto::RecoveryError,
consensus::{
transaction::{Recovered, SignerRecoverable},
Block as AlloyBlock, BlockHeader, EthereumTxEnvelope, EthereumTypedTransaction, Header,
TxEip4844,
},
primitives::{Address, BlockNumber, Bloom, Bytes, Sealed, B256, B64, U256},
};
pub type SealedHeader = Sealed<Header>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SealedBlock<T = TransactionSigned> {
pub header: SealedHeader,
pub transactions: Vec<T>,
}
impl<T> SealedBlock<T> {
pub const fn new(header: SealedHeader, transactions: Vec<T>) -> Self {
Self { header, transactions }
}
#[doc(hidden)]
pub fn blank_for_testing() -> Self {
Self { header: Sealed::new(Header::default()), transactions: Vec::new() }
}
#[doc(hidden)]
pub fn blank_with_header(header: Header) -> Self {
Self { header: Sealed::new(header), transactions: Vec::new() }
}
pub fn transactions(&self) -> &[T] {
&self.transactions
}
}
impl Default for SealedBlock {
fn default() -> Self {
Self::blank_for_testing()
}
}
impl SealedBlock {
pub fn recover(self) -> Result<RecoveredBlock, RecoveryError> {
let transactions = self
.transactions
.into_iter()
.map(|tx| {
let sender = tx.recover_signer()?;
Ok(Recovered::new_unchecked(tx, sender))
})
.collect::<Result<Vec<_>, RecoveryError>>()?;
Ok(SealedBlock { header: self.header, transactions })
}
pub fn recover_unchecked(self, senders: Vec<Address>) -> RecoveredBlock {
assert_eq!(
self.transactions.len(),
senders.len(),
"senders length mismatch: expected {}, got {}",
self.transactions.len(),
senders.len(),
);
let transactions = self
.transactions
.into_iter()
.zip(senders)
.map(|(tx, sender)| Recovered::new_unchecked(tx, sender))
.collect();
SealedBlock { header: self.header, transactions }
}
}
pub type RecoveredBlock = SealedBlock<Recovered<TransactionSigned>>;
impl Default for RecoveredBlock {
fn default() -> Self {
Self { header: Sealed::new(Header::default()), transactions: Vec::new() }
}
}
impl RecoveredBlock {
pub fn senders(&self) -> impl ExactSizeIterator<Item = Address> + '_ {
self.transactions.iter().map(Recovered::signer)
}
}
impl<T> BlockHeader for SealedBlock<T> {
fn parent_hash(&self) -> B256 {
self.header.parent_hash()
}
fn ommers_hash(&self) -> B256 {
self.header.ommers_hash()
}
fn beneficiary(&self) -> Address {
self.header.beneficiary()
}
fn state_root(&self) -> B256 {
self.header.state_root()
}
fn transactions_root(&self) -> B256 {
self.header.transactions_root()
}
fn receipts_root(&self) -> B256 {
self.header.receipts_root()
}
fn withdrawals_root(&self) -> Option<B256> {
self.header.withdrawals_root()
}
fn logs_bloom(&self) -> Bloom {
self.header.logs_bloom()
}
fn difficulty(&self) -> U256 {
self.header.difficulty()
}
fn number(&self) -> BlockNumber {
self.header.number()
}
fn gas_limit(&self) -> u64 {
self.header.gas_limit()
}
fn gas_used(&self) -> u64 {
self.header.gas_used()
}
fn timestamp(&self) -> u64 {
self.header.timestamp()
}
fn mix_hash(&self) -> Option<B256> {
self.header.mix_hash()
}
fn nonce(&self) -> Option<B64> {
self.header.nonce()
}
fn base_fee_per_gas(&self) -> Option<u64> {
self.header.base_fee_per_gas()
}
fn blob_gas_used(&self) -> Option<u64> {
self.header.blob_gas_used()
}
fn excess_blob_gas(&self) -> Option<u64> {
self.header.excess_blob_gas()
}
fn parent_beacon_block_root(&self) -> Option<B256> {
self.header.parent_beacon_block_root()
}
fn requests_hash(&self) -> Option<B256> {
self.header.requests_hash()
}
fn extra_data(&self) -> &Bytes {
self.header.extra_data()
}
}
pub type Transaction = EthereumTypedTransaction<TxEip4844>;
pub type TransactionSigned = EthereumTxEnvelope<TxEip4844>;
pub type Block = AlloyBlock<TransactionSigned>;