1use std::{collections::BTreeSet, marker::PhantomData};
2
3use alloy_consensus::ReceiptEnvelope;
4use alloy_eips::{eip2718::Eip2718Error, Decodable2718, Encodable2718};
5use alloy_primitives::{Address, Bytes, B256, U256};
6use alloy_provider::{network::AnyNetwork, Provider};
7use alloy_rpc_types::{AnyReceiptEnvelope, Filter, Log as RpcLog};
8use alloy_sol_types::SolCall;
9use eyre::eyre;
10use reth_primitives::EthPrimitives;
11use revm::{context::result::ExecutionResult, database::CacheDB};
12use rsp_mpt::EthereumState;
13use rsp_primitives::{account_proof::eip1186_proof_to_account_proof, genesis::Genesis};
14use rsp_rpc_db::RpcDb;
15use sp1_cc_client_executor::{
16 io::{EvmSketchInput, Primitives},
17 Anchor, ContractInput,
18};
19
20use crate::{EvmSketchBuilder, HostError};
21
22#[derive(Debug)]
25pub struct EvmSketch<P, PT> {
26 pub genesis: Genesis,
28 pub anchor: Anchor,
30 pub rpc_db: RpcDb<P, AnyNetwork>,
32 pub receipts: Option<Vec<ReceiptEnvelope>>,
34 pub provider: P,
36
37 pub phantom: PhantomData<PT>,
38}
39
40impl EvmSketch<(), EthPrimitives> {
41 pub fn builder() -> EvmSketchBuilder<(), EthPrimitives, ()> {
42 EvmSketchBuilder::default()
43 }
44}
45
46impl<P, PT> EvmSketch<P, PT>
47where
48 P: Provider<AnyNetwork> + Clone,
49 PT: Primitives,
50{
51 pub async fn call<C: SolCall>(
56 &self,
57 contract_address: Address,
58 caller_address: Address,
59 calldata: C,
60 ) -> eyre::Result<C::Return> {
61 let cache_db = CacheDB::new(&self.rpc_db);
62 let chain_spec = PT::build_spec(&self.genesis)?;
63 let input = ContractInput::new_call(contract_address, caller_address, calldata);
64 let output = PT::transact(&input, cache_db, self.anchor.header(), U256::ZERO, chain_spec)
65 .map_err(|err| eyre!(err))?;
66
67 let output_bytes = match output.result {
68 ExecutionResult::Success { output, .. } => Ok(output.data().clone()),
69 ExecutionResult::Revert { output, .. } => Ok(output),
70 ExecutionResult::Halt { reason, .. } => Err(eyre!("Execution halted: {reason:?}")),
71 }?;
72
73 Ok(C::abi_decode_returns(&output_bytes)?)
74 }
75
76 pub async fn call_raw(&self, input: &ContractInput) -> eyre::Result<Bytes> {
78 let cache_db = CacheDB::new(&self.rpc_db);
79 let chain_spec = PT::build_spec(&self.genesis)?;
80 let output = PT::transact(input, cache_db, self.anchor.header(), U256::ZERO, chain_spec)
81 .map_err(|err| eyre!(err))?;
82
83 let output_bytes = match output.result {
84 ExecutionResult::Success { output, .. } => Ok(output.data().clone()),
85 ExecutionResult::Revert { output, .. } => Ok(output),
86 ExecutionResult::Halt { reason, .. } => Err(eyre!("Execution halted: {reason:?}")),
87 }?;
88
89 Ok(output_bytes)
90 }
91
92 pub async fn create(&self, caller_address: Address, calldata: Bytes) -> eyre::Result<Bytes> {
94 let cache_db = CacheDB::new(&self.rpc_db);
95 let chain_spec = PT::build_spec(&self.genesis)?;
96 let input = ContractInput::new_create(caller_address, calldata);
97 let output = PT::transact(&input, cache_db, self.anchor.header(), U256::ZERO, chain_spec)
98 .map_err(|err| eyre!(err))?;
99
100 let output_bytes = match output.result {
101 ExecutionResult::Success { output, .. } => Ok(output.data().clone()),
102 ExecutionResult::Revert { output, .. } => Ok(output),
103 ExecutionResult::Halt { reason, .. } => Err(eyre!("Execution halted: {reason:?}")),
104 }?;
105
106 Ok(output_bytes.clone())
107 }
108
109 pub async fn get_logs(&mut self, filter: &Filter) -> Result<Vec<RpcLog>, HostError> {
114 let logs = self.provider.get_logs(filter).await?;
115
116 if !logs.is_empty() && self.receipts.is_none() {
117 let receipts = self
118 .provider
119 .get_block_receipts(self.anchor.header().number.into())
120 .await?
121 .unwrap_or_default()
122 .into_iter()
123 .map(|r| convert_receipt_envelope(r.inner.inner))
124 .collect::<Result<_, _>>()?;
125
126 self.receipts = Some(receipts);
127 }
128
129 Ok(logs)
130 }
131
132 pub async fn finalize(self) -> Result<EvmSketchInput, HostError> {
134 let block_number = self.anchor.header().number;
135
136 let state_requests = self.rpc_db.get_state_requests();
138 tracing::info!("fetching storage proofs");
139 let mut storage_proofs = Vec::new();
140
141 for (address, used_keys) in state_requests.iter() {
142 let keys = used_keys
143 .iter()
144 .map(|key| B256::from(*key))
145 .collect::<BTreeSet<_>>()
146 .into_iter()
147 .collect::<Vec<_>>();
148
149 let storage_proof =
150 self.provider.get_proof(*address, keys).block_id(block_number.into()).await?;
151 storage_proofs.push(eip1186_proof_to_account_proof(storage_proof));
152 }
153
154 let storage_proofs_by_address =
155 storage_proofs.iter().map(|item| (item.address, item.clone())).collect();
156 let state = EthereumState::from_proofs(
157 self.anchor.header().state_root,
158 &storage_proofs_by_address,
159 )?;
160
161 let oldest_ancestor = *self.rpc_db.oldest_ancestor.read().unwrap();
163 let mut ancestor_headers = vec![];
164 tracing::info!("fetching {} ancestor headers", block_number - oldest_ancestor);
165 for height in (oldest_ancestor..=(block_number - 1)).rev() {
166 let block = self.provider.get_block_by_number(height.into()).full().await?.unwrap();
167 ancestor_headers.push(
168 block
169 .inner
170 .header
171 .inner
172 .clone()
173 .try_into_header()
174 .map_err(|h| HostError::HeaderConversionError(h.number))?,
175 );
176 }
177
178 Ok(EvmSketchInput {
179 anchor: self.anchor,
180 genesis: self.genesis,
181 ancestor_headers,
182 state,
183 state_requests,
184 bytecodes: self.rpc_db.get_bytecodes(),
185 receipts: self.receipts,
186 })
187 }
188}
189
190fn convert_receipt_envelope(
192 any_receipt_envelope: AnyReceiptEnvelope<RpcLog>,
193) -> Result<ReceiptEnvelope, Eip2718Error> {
194 let any_receipt_envelope = AnyReceiptEnvelope {
195 inner: any_receipt_envelope.inner.map_logs(|l| l.inner),
196 r#type: any_receipt_envelope.r#type,
197 };
198
199 let mut buf = vec![];
200
201 any_receipt_envelope.encode_2718(&mut buf);
202
203 ReceiptEnvelope::decode_2718(&mut buf.as_slice())
204}
205
206#[cfg(test)]
207mod tests {
208 use reth_primitives::EthPrimitives;
209 use sp1_cc_client_executor::io::EvmSketchInput;
210
211 use crate::EvmSketch;
212
213 fn assert_sync<T: Sync>() {}
215
216 #[test]
217 fn test_sync() {
218 assert_sync::<EvmSketch<(), EthPrimitives>>();
219 assert_sync::<EvmSketchInput>();
220 }
221}