use crate::{
config::ChainSpec, serde::Eip2718Wrapper, state::StateDb, BlockHeaderCommit, Commitment,
CommitmentVersion, EvmBlockHeader, EvmEnv, GuestEvmEnv, MerkleTrie,
};
use ::serde::{Deserialize, Serialize};
use alloy_consensus::ReceiptEnvelope;
use alloy_primitives::{map::HashMap, Bytes, Sealed, B256};
#[derive(Clone, Serialize, Deserialize)]
pub struct BlockInput<H> {
header: H,
state_trie: MerkleTrie,
storage_tries: Vec<MerkleTrie>,
contracts: Vec<Bytes>,
ancestors: Vec<H>,
receipts: Option<Vec<Eip2718Wrapper<ReceiptEnvelope>>>,
}
impl<H: EvmBlockHeader> BlockHeaderCommit<H> for () {
fn commit(self, header: &Sealed<H>, config_id: B256) -> Commitment {
Commitment::new(
CommitmentVersion::Block as u16,
header.number(),
header.seal(),
config_id,
)
}
}
impl<H: EvmBlockHeader> BlockInput<H> {
pub fn into_env(self) -> GuestEvmEnv<H> {
let state_root = self.state_trie.hash_slow();
assert_eq!(self.header.state_root(), &state_root, "State root mismatch");
let header = self.header.seal_slow();
let mut block_hashes =
HashMap::with_capacity_and_hasher(self.ancestors.len() + 1, Default::default());
block_hashes.insert(header.number(), header.seal());
let mut previous_header = header.inner();
for ancestor in &self.ancestors {
let ancestor_hash = ancestor.hash_slow();
assert_eq!(
previous_header.parent_hash(),
&ancestor_hash,
"Invalid ancestor chain: block {} is not the parent of block {}",
ancestor.number(),
previous_header.number()
);
block_hashes.insert(ancestor.number(), ancestor_hash);
previous_header = ancestor;
}
#[cfg(not(feature = "unstable-event"))]
let logs = {
assert!(self.receipts.is_none(), "Receipts not supported");
None
};
#[cfg(feature = "unstable-event")]
let logs = self.receipts.map(|receipts| {
let root = alloy_trie::root::ordered_trie_root_with_encoder(&receipts, |r, out| {
alloy_eips::eip2718::Encodable2718::encode_2718(r, out)
});
assert_eq!(header.receipts_root(), &root, "Receipts root mismatch");
receipts
.into_iter()
.flat_map(|wrapper| match wrapper.into_inner() {
ReceiptEnvelope::Legacy(t) => t.receipt.logs,
ReceiptEnvelope::Eip2930(t) => t.receipt.logs,
ReceiptEnvelope::Eip1559(t) => t.receipt.logs,
ReceiptEnvelope::Eip4844(t) => t.receipt.logs,
ReceiptEnvelope::Eip7702(t) => t.receipt.logs,
})
.collect()
});
let db = StateDb::new(
self.state_trie,
self.storage_tries,
self.contracts,
block_hashes,
logs,
);
let commit = Commitment::new(
CommitmentVersion::Block as u16,
header.number(),
header.seal(),
ChainSpec::DEFAULT_DIGEST,
);
EvmEnv::new(db, header, commit)
}
}
#[cfg(feature = "host")]
pub mod host {
use super::BlockInput;
use crate::{
host::db::{ProofDb, ProviderDb},
serde::Eip2718Wrapper,
EvmBlockHeader,
};
use alloy::{network::Network, providers::Provider};
use alloy_primitives::Sealed;
use anyhow::{anyhow, ensure};
use log::debug;
use std::fmt::Display;
impl<H> BlockInput<H> {
pub(crate) async fn from_proof_db<N, P>(
mut db: ProofDb<ProviderDb<N, P>>,
header: Sealed<H>,
) -> anyhow::Result<Self>
where
N: Network,
P: Provider<N>,
H: EvmBlockHeader + TryFrom<<N as Network>::HeaderResponse>,
<H as TryFrom<<N as Network>::HeaderResponse>>::Error: Display,
{
assert_eq!(db.inner().block(), header.seal(), "DB block mismatch");
let (state_trie, storage_tries) = db.state_proof().await?;
ensure!(
header.state_root() == &state_trie.hash_slow(),
"accountProof root does not match header's stateRoot"
);
let contracts: Vec<_> = db.contracts().values().cloned().collect();
let mut ancestors = Vec::new();
for rlp_header in db.ancestor_proof(header.number()).await? {
let header: H = rlp_header
.try_into()
.map_err(|err| anyhow!("header invalid: {}", err))?;
ancestors.push(header);
}
let receipts = db.receipt_proof().await?;
let receipts =
receipts.map(|receipts| receipts.into_iter().map(Eip2718Wrapper::new).collect());
debug!("state size: {}", state_trie.size());
debug!("storage tries: {}", storage_tries.len());
debug!(
"total storage size: {}",
storage_tries.iter().map(|t| t.size()).sum::<usize>()
);
debug!("contracts: {}", contracts.len());
debug!("ancestor blocks: {}", ancestors.len());
debug!("receipts: {:?}", receipts.as_ref().map(Vec::len));
let input = BlockInput {
header: header.into_inner(),
state_trie,
storage_tries,
contracts,
ancestors,
receipts,
};
Ok(input)
}
}
}