reth_node_ethereum/
engine_ssz_witness.rs1use crate::engine_ssz_containers::ExecutionWitnessV1;
4use alloy_primitives::B256;
5use alloy_rpc_types_engine::ExecutionData;
6use reth_ethereum_primitives::{EthPrimitives, TransactionSigned};
7use reth_evm::{execute::Executor, ConfigureEvm};
8use reth_primitives_traits::{AlloyBlockHeader, Block};
9use reth_provider::{HeaderProvider, StateProviderFactory};
10use reth_revm::{database::StateProviderDatabase, witness::ExecutionWitnessRecord};
11use reth_tasks::Runtime;
12use reth_trie_common::ExecutionWitnessMode;
13use std::{future::Future, pin::Pin};
14
15#[derive(Clone, Debug)]
22pub struct EngineSszWitnessGenerator<Provider, Evm> {
23 provider: Provider,
24 evm_config: Evm,
25 task_spawner: Runtime,
26}
27
28impl<Provider, Evm> EngineSszWitnessGenerator<Provider, Evm> {
29 pub const fn new(provider: Provider, evm_config: Evm, task_spawner: Runtime) -> Self {
31 Self { provider, evm_config, task_spawner }
32 }
33}
34
35impl<Provider, Evm> EngineSszWitness for EngineSszWitnessGenerator<Provider, Evm>
36where
37 Provider: HeaderProvider + StateProviderFactory + Clone + Send + Sync + 'static,
38 Provider::Header: alloy_rlp::Encodable,
39 Evm: ConfigureEvm<Primitives = EthPrimitives> + 'static,
40{
41 fn generate_witness(
42 &self,
43 payload: ExecutionData,
44 ) -> Pin<
45 Box<
46 dyn Future<Output = Result<ExecutionWitnessV1, EngineSszWitnessError>> + Send + 'static,
47 >,
48 > {
49 let provider = self.provider.clone();
50 let evm_config = self.evm_config.clone();
51 let task_spawner = self.task_spawner.clone();
52
53 Box::pin(async move {
54 task_spawner
55 .spawn_blocking(move || {
56 let block = payload
58 .payload
59 .try_into_block_with_sidecar::<TransactionSigned>(&payload.sidecar)
60 .map_err(eyre::Report::new)?
61 .try_into_recovered()
62 .map_err(eyre::Report::new)?;
63
64 let block_number = block.header().number;
65 let parent_hash = block.header().parent_hash;
66 let state_provider =
67 provider.state_by_block_hash(parent_hash).map_err(|source| {
68 EngineSszWitnessError::ParentStateUnavailable {
69 parent: parent_hash,
70 source: eyre::Report::new(source),
71 }
72 })?;
73 let block_executor =
74 evm_config.executor(StateProviderDatabase::new(state_provider));
75 let mut witness = None;
76 let mut first_header = block_number.saturating_sub(1);
77 block_executor
78 .execute_with_state_closure(&block, |statedb: &reth_revm::State<_>| {
79 if let Some((number, _)) = statedb.block_hashes.lowest() {
80 first_header = number;
81 }
82 witness = Some(
83 ExecutionWitnessRecord::new(statedb)
84 .into_execution_witness_without_headers(
85 &statedb.database.0,
86 ExecutionWitnessMode::Canonical,
87 ),
88 );
89 })
90 .map_err(eyre::Report::new)?;
91
92 let witness = witness
93 .expect("state closure is called after successful execution")
94 .map_err(eyre::Report::new)?;
95
96 let mut headers = Vec::new();
98 let mut hash = parent_hash;
99 for _ in first_header..block_number {
100 let header = provider
101 .header(hash)
102 .map_err(eyre::Report::new)?
103 .ok_or_else(|| eyre::eyre!("ancestor {hash} not found for witness"))?;
104 hash = header.parent_hash();
105 headers.push(alloy_rlp::encode(&header).into());
106 }
107 headers.reverse();
108
109 Ok(ExecutionWitnessV1 { state: witness.state, codes: witness.codes, headers })
110 })
111 .await
112 .map_err(eyre::Report::new)?
113 })
114 }
115}
116
117pub trait EngineSszWitness: Send + Sync + 'static {
119 fn generate_witness(
121 &self,
122 payload: ExecutionData,
123 ) -> Pin<
124 Box<
125 dyn Future<Output = Result<ExecutionWitnessV1, EngineSszWitnessError>> + Send + 'static,
126 >,
127 >;
128}
129
130#[derive(Debug)]
132pub enum EngineSszWitnessError {
133 ParentStateUnavailable {
135 parent: B256,
137 source: eyre::Report,
139 },
140 Internal(eyre::Report),
142}
143
144impl From<eyre::Report> for EngineSszWitnessError {
145 fn from(error: eyre::Report) -> Self {
146 Self::Internal(error)
147 }
148}
149
150impl std::fmt::Display for EngineSszWitnessError {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 match self {
153 Self::ParentStateUnavailable { parent, source } => {
154 write!(f, "parent state {parent} is unavailable through the provider: {source}")
155 }
156 Self::Internal(error) => std::fmt::Display::fmt(error, f),
157 }
158 }
159}
160
161impl std::error::Error for EngineSszWitnessError {
162 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
163 match self {
164 Self::ParentStateUnavailable { source, .. } | Self::Internal(source) => {
165 Some(source.as_ref())
166 }
167 }
168 }
169}