1use crate::{
16 config::ChainSpec, serde::Eip2718Wrapper, state::StateDb, BlockHeaderCommit, Commitment,
17 CommitmentVersion, EvmBlockHeader, EvmEnv, EvmFactory, GuestEvmEnv, MerkleTrie,
18};
19use ::serde::{Deserialize, Serialize};
20use alloy_consensus::ReceiptEnvelope;
21use alloy_primitives::{map::HashMap, Bytes, Sealable, Sealed, B256};
22use std::marker::PhantomData;
23
24#[derive(Clone, Serialize, Deserialize)]
26pub struct BlockInput<F: EvmFactory> {
27 header: F::Header,
28 state_trie: MerkleTrie,
29 storage_tries: Vec<MerkleTrie>,
30 contracts: Vec<Bytes>,
31 ancestors: Vec<F::Header>,
32 receipts: Option<Vec<Eip2718Wrapper<ReceiptEnvelope>>>,
33 #[serde(skip)]
34 phantom: PhantomData<F>,
35}
36
37impl<H: EvmBlockHeader> BlockHeaderCommit<H> for () {
41 fn commit(self, header: &Sealed<H>, config_id: B256) -> Commitment {
42 Commitment::new(
43 CommitmentVersion::Block as u16,
44 header.number(),
45 header.seal(),
46 config_id,
47 )
48 }
49}
50
51impl<F: EvmFactory> BlockInput<F> {
52 pub fn into_env(self, chain_spec: &ChainSpec<F::Spec>) -> GuestEvmEnv<F> {
54 let state_root = self.state_trie.hash_slow();
56 assert_eq!(self.header.state_root(), &state_root, "State root mismatch");
57
58 let header = self.header.seal_slow();
60
61 let mut block_hashes =
63 HashMap::with_capacity_and_hasher(self.ancestors.len() + 1, Default::default());
64 block_hashes.insert(header.number(), header.seal());
65
66 let mut previous_header = header.inner();
67 for ancestor in &self.ancestors {
68 let ancestor_hash = ancestor.hash_slow();
69 assert_eq!(
70 previous_header.parent_hash(),
71 &ancestor_hash,
72 "Invalid ancestor chain: block {} is not the parent of block {}",
73 ancestor.number(),
74 previous_header.number()
75 );
76 block_hashes.insert(ancestor.number(), ancestor_hash);
77 previous_header = ancestor;
78 }
79
80 let logs = self.receipts.map(|receipts| {
82 let root = alloy_trie::root::ordered_trie_root_with_encoder(&receipts, |r, out| {
83 alloy_eips::eip2718::Encodable2718::encode_2718(r, out)
84 });
85 assert_eq!(header.receipts_root(), &root, "Receipts root mismatch");
86
87 receipts
88 .into_iter()
89 .flat_map(|wrapper| match wrapper.into_inner() {
90 ReceiptEnvelope::Legacy(t) => t.receipt.logs,
91 ReceiptEnvelope::Eip2930(t) => t.receipt.logs,
92 ReceiptEnvelope::Eip1559(t) => t.receipt.logs,
93 ReceiptEnvelope::Eip4844(t) => t.receipt.logs,
94 ReceiptEnvelope::Eip7702(t) => t.receipt.logs,
95 })
96 .collect()
97 });
98
99 let db = StateDb::new(
100 self.state_trie,
101 self.storage_tries,
102 self.contracts,
103 block_hashes,
104 logs,
105 );
106 let commit = Commitment::new(
107 CommitmentVersion::Block as u16,
108 header.number(),
109 header.seal(),
110 chain_spec.digest(),
111 );
112
113 EvmEnv::new(db, chain_spec, header, commit)
114 }
115}
116
117#[cfg(feature = "host")]
118pub mod host {
119 use super::BlockInput;
120 use crate::{
121 host::db::{ProofDb, ProviderDb},
122 serde::Eip2718Wrapper,
123 EvmBlockHeader, EvmFactory,
124 };
125 use alloy::{network::Network, providers::Provider};
126 use alloy_primitives::Sealed;
127 use anyhow::{anyhow, ensure};
128 use log::debug;
129 use std::{fmt::Display, marker::PhantomData};
130
131 impl<F: EvmFactory> BlockInput<F> {
132 pub(crate) async fn from_proof_db<N, P>(
135 mut db: ProofDb<ProviderDb<N, P>>,
136 header: Sealed<F::Header>,
137 ) -> anyhow::Result<Self>
138 where
139 N: Network,
140 P: Provider<N>,
141 F::Header: TryFrom<<N as Network>::HeaderResponse>,
142 <F::Header as TryFrom<<N as Network>::HeaderResponse>>::Error: Display,
143 {
144 assert_eq!(db.inner().block(), header.seal(), "DB block mismatch");
145
146 let (state_trie, storage_tries) = db.state_proof().await?;
147 ensure!(
148 header.state_root() == &state_trie.hash_slow(),
149 "accountProof root does not match header's stateRoot"
150 );
151
152 let contracts: Vec<_> = db.contracts().values().cloned().collect();
154
155 let mut ancestors = Vec::new();
157 for rlp_header in db.ancestor_proof(header.number()).await? {
158 let header: F::Header = rlp_header
159 .try_into()
160 .map_err(|err| anyhow!("header invalid: {}", err))?;
161 ancestors.push(header);
162 }
163
164 let receipts = db.receipt_proof().await?;
165 let receipts =
167 receipts.map(|receipts| receipts.into_iter().map(Eip2718Wrapper::new).collect());
168
169 debug!("state size: {}", state_trie.size());
170 debug!("storage tries: {}", storage_tries.len());
171 debug!(
172 "total storage size: {}",
173 storage_tries.iter().map(|t| t.size()).sum::<usize>()
174 );
175 debug!("contracts: {}", contracts.len());
176 debug!("ancestor blocks: {}", ancestors.len());
177 debug!("receipts: {:?}", receipts.as_ref().map(Vec::len));
178
179 let input = BlockInput {
180 header: header.into_inner(),
181 state_trie,
182 storage_tries,
183 contracts,
184 ancestors,
185 receipts,
186 phantom: PhantomData,
187 };
188
189 Ok(input)
190 }
191 }
192}