sp1_cc_client_executor/
io.rs1use std::{fmt::Debug, iter::once, sync::Arc};
12
13use alloy_consensus::ReceiptEnvelope;
14use alloy_evm::{Database, Evm};
15use reth_chainspec::{ChainSpec, EthChainSpec};
16use reth_consensus::{ConsensusError, HeaderValidator};
17use reth_ethereum_consensus::EthBeaconConsensus;
18use reth_evm::{ConfigureEvm, EthEvm, EvmEnv};
19use reth_evm_ethereum::EthEvmConfig;
20use reth_primitives::{EthPrimitives, Header, NodePrimitives, SealedHeader};
21use revm::{
22 context::result::{HaltReason, ResultAndState},
23 inspector::NoOpInspector,
24 state::Bytecode,
25 Context, MainBuilder, MainContext,
26};
27use revm_primitives::{B256, U256};
28use rsp_client_executor::{error::ClientError, io::WitnessInput};
29use rsp_mpt::EthereumState;
30use rsp_primitives::genesis::Genesis;
31use serde::{Deserialize, Serialize};
32use serde_with::serde_as;
33
34use crate::{Anchor, ContractInput};
35
36#[serde_as]
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43pub struct EvmSketchInput {
44 pub anchor: Anchor,
46 pub genesis: Genesis,
48 #[serde_as(as = "Vec<alloy_consensus::serde_bincode_compat::Header>")]
51 pub ancestor_headers: Vec<Header>,
52 pub state: EthereumState,
54 pub bytecodes: Vec<Bytecode>,
56 #[serde_as(as = "Option<Vec<alloy_consensus::serde_bincode_compat::ReceiptEnvelope>>")]
58 pub receipts: Option<Vec<ReceiptEnvelope>>,
59}
60
61impl WitnessInput for EvmSketchInput {
62 #[inline(always)]
63 fn state(&self) -> &EthereumState {
64 &self.state
65 }
66
67 #[inline(always)]
68 fn state_anchor(&self) -> B256 {
69 self.anchor.header().state_root
70 }
71
72 #[inline(always)]
73 fn bytecodes(&self) -> impl Iterator<Item = &Bytecode> {
74 self.bytecodes.iter()
75 }
76
77 #[inline(always)]
78 fn sealed_headers(&self) -> impl Iterator<Item = SealedHeader> {
79 once(SealedHeader::seal_slow(self.anchor.header().clone()))
80 .chain(self.ancestor_headers.iter().map(|h| SealedHeader::seal_slow(h.clone())))
81 }
82}
83
84pub trait Primitives: NodePrimitives {
85 type ChainSpec: EthChainSpec + Debug;
86 type HaltReason: Debug;
87
88 fn build_spec(genesis: &Genesis) -> Result<Arc<Self::ChainSpec>, ClientError>;
89
90 fn validate_header(
91 header: &SealedHeader,
92 chain_spec: Arc<Self::ChainSpec>,
93 ) -> Result<(), ConsensusError>;
94
95 fn transact<DB>(
96 input: &ContractInput,
97 db: DB,
98 header: &Header,
99 difficulty: U256,
100 chain_spec: Arc<Self::ChainSpec>,
101 ) -> Result<ResultAndState<Self::HaltReason>, String>
102 where
103 DB: Database;
104
105 fn active_fork_name(chain_spec: &Self::ChainSpec, header: &Header) -> String;
106}
107
108impl Primitives for EthPrimitives {
109 type ChainSpec = ChainSpec;
110 type HaltReason = HaltReason;
111
112 fn build_spec(genesis: &Genesis) -> Result<Arc<Self::ChainSpec>, ClientError> {
113 Ok(Arc::new(ChainSpec::try_from(genesis).unwrap()))
114 }
115
116 fn validate_header(
117 header: &SealedHeader,
118 chain_spec: Arc<Self::ChainSpec>,
119 ) -> Result<(), ConsensusError> {
120 let validator = EthBeaconConsensus::new(chain_spec);
121 validator.validate_header(header)
122 }
123
124 fn transact<DB: Database>(
125 input: &ContractInput,
126 db: DB,
127 header: &Header,
128 difficulty: U256,
129 chain_spec: Arc<Self::ChainSpec>,
130 ) -> Result<ResultAndState<Self::HaltReason>, String> {
131 let EvmEnv { mut cfg_env, mut block_env, .. } =
132 EthEvmConfig::new(chain_spec).evm_env(header);
133
134 block_env.basefee = 0;
136 block_env.difficulty = difficulty;
137 cfg_env.disable_nonce_check = true;
138 cfg_env.disable_balance_check = true;
139
140 let evm = Context::mainnet()
141 .with_db(db)
142 .with_cfg(cfg_env)
143 .with_block(block_env)
144 .modify_tx_chained(|tx_env| {
145 tx_env.gas_limit = header.gas_limit;
146 })
147 .build_mainnet_with_inspector(NoOpInspector {});
148
149 let mut evm = EthEvm::new(evm, false);
150
151 evm.transact(input).map_err(|err| err.to_string())
152 }
153
154 fn active_fork_name(chain_spec: &Self::ChainSpec, header: &Header) -> String {
155 let spec = reth_evm_ethereum::revm_spec(chain_spec, header);
156
157 spec.to_string()
158 }
159}
160
161#[cfg(feature = "optimism")]
162impl Primitives for reth_optimism_primitives::OpPrimitives {
163 type ChainSpec = reth_optimism_chainspec::OpChainSpec;
164 type HaltReason = op_revm::OpHaltReason;
165
166 fn build_spec(genesis: &Genesis) -> Result<Arc<Self::ChainSpec>, ClientError> {
167 Ok(Arc::new(reth_optimism_chainspec::OpChainSpec::try_from(genesis).unwrap()))
168 }
169
170 fn validate_header(
171 header: &SealedHeader,
172 chain_spec: Arc<Self::ChainSpec>,
173 ) -> Result<(), ConsensusError> {
174 let validator = reth_optimism_consensus::OpBeaconConsensus::new(chain_spec);
175 validator.validate_header(header)
176 }
177
178 fn transact<DB: Database>(
179 input: &ContractInput,
180 db: DB,
181 header: &Header,
182 difficulty: U256,
183 chain_spec: Arc<Self::ChainSpec>,
184 ) -> Result<ResultAndState<Self::HaltReason>, String> {
185 use op_revm::{DefaultOp, OpBuilder};
186
187 let EvmEnv { mut cfg_env, mut block_env, .. } =
188 reth_optimism_evm::OpEvmConfig::optimism(chain_spec).evm_env(header);
189
190 block_env.basefee = 0;
192 block_env.difficulty = difficulty;
193 cfg_env.disable_nonce_check = true;
194 cfg_env.disable_balance_check = true;
195
196 let evm = op_revm::OpContext::op()
197 .with_db(db)
198 .with_cfg(cfg_env)
199 .with_block(block_env)
200 .modify_tx_chained(|tx_env| {
201 tx_env.base.gas_limit = header.gas_limit;
202 })
203 .build_op_with_inspector(NoOpInspector {});
204
205 let mut evm = alloy_op_evm::OpEvm::new(evm, false);
206
207 evm.transact(input).map_err(|err| err.to_string())
208 }
209
210 fn active_fork_name(chain_spec: &Self::ChainSpec, header: &Header) -> String {
211 let spec = reth_optimism_evm::revm_spec(chain_spec, header);
212 let spec: &'static str = spec.into();
213
214 spec.to_string()
215 }
216}