1use std::{
2 fmt::{Debug, Display},
3 marker::PhantomData,
4};
5
6use alloy_consensus::{Header, Sealed};
7use alloy_eips::{eip4788::BEACON_ROOTS_ADDRESS, BlockId};
8use alloy_primitives::{B256, U256};
9use alloy_provider::{network::AnyNetwork, Provider};
10use async_trait::async_trait;
11use ethereum_consensus::ssz::prelude::Prove;
12use rsp_mpt::EthereumState;
13use sp1_cc_client_executor::{
14 get_beacon_root_from_state, rebuild_merkle_root, Anchor, BeaconAnchor, BeaconAnchorId,
15 BeaconStateAnchor, BeaconWithHeaderAnchor, ChainedBeaconAnchor, BLOCK_HASH_LEAF_INDEX,
16 HISTORY_BUFFER_LENGTH, STATE_ROOT_LEAF_INDEX,
17};
18use url::Url;
19
20use crate::{
21 beacon::{BeaconClient, SignedBeaconBlock},
22 HostError,
23};
24
25#[async_trait]
27pub trait AnchorBuilder {
28 async fn build<B: Into<BlockId> + Send>(&self, block_id: B) -> Result<Anchor, HostError>;
29}
30
31#[derive(Debug, Clone, Copy)]
37pub enum BeaconBlockField {
38 BlockHash,
39 StateRoot,
40}
41
42impl Display for BeaconBlockField {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 BeaconBlockField::BlockHash => write!(f, "block_hash"),
46 BeaconBlockField::StateRoot => write!(f, "state_root"),
47 }
48 }
49}
50
51impl PartialEq<BeaconBlockField> for usize {
52 fn eq(&self, other: &BeaconBlockField) -> bool {
53 let other = usize::from(other);
54
55 *self == other
56 }
57}
58
59impl From<&BeaconBlockField> for usize {
60 fn from(value: &BeaconBlockField) -> Self {
61 match value {
62 BeaconBlockField::BlockHash => BLOCK_HASH_LEAF_INDEX,
63 BeaconBlockField::StateRoot => STATE_ROOT_LEAF_INDEX,
64 }
65 }
66}
67
68#[async_trait]
70pub trait BeaconAnchorKind: Sized {
71 async fn build_beacon_anchor_from_header<P: Provider<AnyNetwork>>(
72 header: &Sealed<Header>,
73 field: BeaconBlockField,
74 beacon_anchor_builder: &BeaconAnchorBuilder<P, Self>,
75 ) -> Result<(B256, BeaconAnchor), HostError>;
76}
77
78#[derive(Debug)]
80pub struct Eip4788BeaconAnchor;
81
82#[async_trait]
83impl BeaconAnchorKind for Eip4788BeaconAnchor {
84 async fn build_beacon_anchor_from_header<P: Provider<AnyNetwork>>(
85 header: &Sealed<Header>,
86 field: BeaconBlockField,
87 beacon_anchor_builder: &BeaconAnchorBuilder<P, Self>,
88 ) -> Result<(B256, BeaconAnchor), HostError> {
89 let child_header =
90 beacon_anchor_builder.header_anchor_builder.get_header(header.number + 1).await?;
91 assert_eq!(child_header.parent_hash, header.seal());
92
93 let beacon_root = child_header
94 .parent_beacon_block_root
95 .ok_or_else(|| HostError::ParentBeaconBlockRootMissing)?;
96
97 let anchor = beacon_anchor_builder
98 .build_beacon_anchor(
99 beacon_root,
100 BeaconAnchorId::Timestamp(child_header.timestamp),
101 field,
102 )
103 .await?;
104
105 Ok((beacon_root, anchor))
106 }
107}
108
109#[derive(Debug)]
111pub struct ConsensusBeaconAnchor;
112
113#[async_trait]
114impl BeaconAnchorKind for ConsensusBeaconAnchor {
115 async fn build_beacon_anchor_from_header<P: Provider<AnyNetwork>>(
116 header: &Sealed<Header>,
117 field: BeaconBlockField,
118 beacon_anchor_builder: &BeaconAnchorBuilder<P, Self>,
119 ) -> Result<(B256, BeaconAnchor), HostError> {
120 let parent_root = header
121 .parent_beacon_block_root
122 .ok_or_else(|| HostError::ParentBeaconBlockRootMissing)?;
123
124 let (beacon_root, beacon_header) = beacon_anchor_builder
125 .client
126 .get_header_from_parent_root(parent_root.to_string())
127 .await?;
128
129 let anchor = beacon_anchor_builder
130 .build_beacon_anchor(
131 beacon_root,
132 BeaconAnchorId::Slot(beacon_header.message.slot),
133 field,
134 )
135 .await?;
136
137 Ok((beacon_root, anchor))
138 }
139}
140
141#[derive(Debug)]
145pub struct HeaderAnchorBuilder<P> {
146 provider: P,
147}
148
149impl<P> HeaderAnchorBuilder<P> {
150 pub fn new(provider: P) -> Self {
151 Self { provider }
152 }
153}
154
155impl<P: Provider<AnyNetwork>> HeaderAnchorBuilder<P> {
156 pub async fn get_header<B: Into<BlockId>>(
157 &self,
158 block_id: B,
159 ) -> Result<Sealed<Header>, HostError> {
160 let block_id = block_id.into();
161 let block = self
162 .provider
163 .get_block(block_id)
164 .await?
165 .ok_or_else(|| HostError::BlockNotFoundError(block_id))?;
166
167 let header = block
168 .header
169 .inner
170 .clone()
171 .try_into_header()
172 .map_err(|_| HostError::HeaderConversionError(block.inner.header.number))?;
173
174 Ok(Sealed::new(header))
175 }
176}
177
178#[async_trait]
179impl<P: Provider<AnyNetwork>> AnchorBuilder for HeaderAnchorBuilder<P> {
180 async fn build<B: Into<BlockId> + Send>(&self, block_id: B) -> Result<Anchor, HostError> {
181 let header = self.get_header(block_id).await?;
182
183 Ok(header.into_inner().into())
184 }
185}
186
187pub struct BeaconAnchorBuilder<P, K> {
189 header_anchor_builder: HeaderAnchorBuilder<P>,
190 client: BeaconClient,
191 phantom: PhantomData<K>,
192}
193
194impl<P> BeaconAnchorBuilder<P, Eip4788BeaconAnchor> {
195 pub fn new(header_anchor_builder: HeaderAnchorBuilder<P>, cl_rpc_url: Url) -> Self {
197 Self { header_anchor_builder, client: BeaconClient::new(cl_rpc_url), phantom: PhantomData }
198 }
199
200 pub fn into_consensus(self) -> BeaconAnchorBuilder<P, ConsensusBeaconAnchor> {
202 BeaconAnchorBuilder {
203 header_anchor_builder: self.header_anchor_builder,
204 client: self.client,
205 phantom: PhantomData,
206 }
207 }
208}
209
210impl<P: Provider<AnyNetwork>, K: BeaconAnchorKind> BeaconAnchorBuilder<P, K> {
211 pub async fn build_beacon_anchor_with_header(
213 &self,
214 header: &Sealed<Header>,
215 field: BeaconBlockField,
216 ) -> Result<BeaconWithHeaderAnchor, HostError> {
217 let (beacon_root, anchor) = K::build_beacon_anchor_from_header(header, field, self).await?;
218
219 if matches!(field, BeaconBlockField::BlockHash) {
220 assert!(
221 verify_merkle_root(header.seal(), anchor.proof(), usize::from(&field), beacon_root),
222 "the proof verification fail, field: {field}",
223 );
224 }
225
226 Ok(BeaconWithHeaderAnchor::new(header.clone_inner(), anchor))
227 }
228
229 pub async fn build_beacon_anchor(
231 &self,
232 beacon_root: B256,
233 id: BeaconAnchorId,
234 field: BeaconBlockField,
235 ) -> Result<BeaconAnchor, HostError> {
236 let signed_beacon_block = self.client.get_block(beacon_root.to_string()).await?;
237
238 let (proof, _) = match signed_beacon_block {
239 SignedBeaconBlock::Deneb(signed_beacon_block) => {
240 signed_beacon_block.message.prove(&[
241 "body".into(),
242 "execution_payload".into(),
243 field.to_string().as_str().into(),
244 ])?
245 }
246 SignedBeaconBlock::Electra(signed_beacon_block) => {
247 signed_beacon_block.message.prove(&[
248 "body".into(),
249 "execution_payload".into(),
250 field.to_string().as_str().into(),
251 ])?
252 }
253 _ => unimplemented!(),
254 };
255
256 assert!(proof.index == field, "the field leaf index is incorrect");
257
258 let proof = proof.branch.iter().map(|n| n.0.into()).collect::<Vec<_>>();
259
260 let anchor = BeaconAnchor::new(proof, id);
261
262 Ok(anchor)
263 }
264}
265
266#[async_trait]
267impl<P: Provider<AnyNetwork>> AnchorBuilder for BeaconAnchorBuilder<P, Eip4788BeaconAnchor> {
268 async fn build<B: Into<BlockId> + Send>(&self, block_id: B) -> Result<Anchor, HostError> {
269 let header = self.header_anchor_builder.get_header(block_id).await?;
270 let anchor =
271 self.build_beacon_anchor_with_header(&header, BeaconBlockField::BlockHash).await?;
272
273 Ok(Anchor::Eip4788(anchor))
274 }
275}
276
277#[async_trait]
278impl<P: Provider<AnyNetwork>> AnchorBuilder for BeaconAnchorBuilder<P, ConsensusBeaconAnchor> {
279 async fn build<B: Into<BlockId> + Send>(&self, block_id: B) -> Result<Anchor, HostError> {
280 let header = self.header_anchor_builder.get_header(block_id).await?;
281 let anchor =
282 self.build_beacon_anchor_with_header(&header, BeaconBlockField::BlockHash).await?;
283
284 Ok(Anchor::Consensus(anchor))
285 }
286}
287
288impl<P: Debug, K: Debug> Debug for BeaconAnchorBuilder<P, K> {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 f.debug_struct("BeaconAnchorBuilder")
291 .field("header_anchor_builder", &self.header_anchor_builder)
292 .finish()
293 }
294}
295
296#[derive(Debug)]
298pub struct ChainedBeaconAnchorBuilder<P> {
299 beacon_anchor_builder: BeaconAnchorBuilder<P, Eip4788BeaconAnchor>,
300 reference: BlockId,
302}
303
304impl<P> ChainedBeaconAnchorBuilder<P> {
305 pub fn new(
306 beacon_anchor_builder: BeaconAnchorBuilder<P, Eip4788BeaconAnchor>,
307 reference: BlockId,
308 ) -> Self {
309 Self { beacon_anchor_builder, reference }
310 }
311}
312
313impl<P: Provider<AnyNetwork>> ChainedBeaconAnchorBuilder<P> {
314 async fn get_eip_4788_timestamp(
317 &self,
318 timestamp: U256,
319 block_hash: B256,
320 ) -> Result<U256, HostError> {
321 let timestamp_idx = timestamp % HISTORY_BUFFER_LENGTH;
322 let result = self
323 .beacon_anchor_builder
324 .header_anchor_builder
325 .provider
326 .get_storage_at(BEACON_ROOTS_ADDRESS, timestamp_idx)
327 .block_id(BlockId::Hash(block_hash.into()))
328 .await?;
329
330 Ok(result)
331 }
332
333 async fn retrieve_state(
336 &self,
337 timestamp: U256,
338 block_hash: B256,
339 ) -> Result<EthereumState, HostError> {
340 let timestamp_idx = timestamp % HISTORY_BUFFER_LENGTH;
342 let root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH;
343
344 let provider = &self.beacon_anchor_builder.header_anchor_builder.provider;
345
346 let proof = provider
347 .get_proof(BEACON_ROOTS_ADDRESS, vec![timestamp_idx.into(), root_idx.into()])
348 .block_id(BlockId::Hash(block_hash.into()))
349 .await?;
350
351 let state = EthereumState::from_account_proof(proof)?;
352
353 Ok(state)
354 }
355}
356
357#[async_trait]
358impl<P: Provider<AnyNetwork>> AnchorBuilder for ChainedBeaconAnchorBuilder<P> {
359 async fn build<B: Into<BlockId> + Send>(&self, block_id: B) -> Result<Anchor, HostError> {
361 let execution_header =
362 self.beacon_anchor_builder.header_anchor_builder.get_header(block_id).await?;
363 let reference_header =
364 self.beacon_anchor_builder.header_anchor_builder.get_header(self.reference).await?;
365 assert!(
366 execution_header.number < reference_header.number,
367 "The execution block must be an ancestor of the reference block"
368 );
369
370 let execution_anchor = self
372 .beacon_anchor_builder
373 .build_beacon_anchor_with_header(&execution_header, BeaconBlockField::BlockHash)
374 .await?;
375 let mut current_anchor = Some(
377 self.beacon_anchor_builder
378 .build_beacon_anchor_with_header(&reference_header, BeaconBlockField::StateRoot)
379 .await?
380 .into(),
381 );
382 let mut current_state_block_hash = reference_header.seal();
383 let mut state_anchors: Vec<BeaconStateAnchor> = vec![];
384
385 loop {
387 let timestamp = self
388 .get_eip_4788_timestamp(
389 U256::from(execution_anchor.id().as_timestamp().unwrap()),
390 current_state_block_hash,
391 )
392 .await?;
393 let state = self.retrieve_state(timestamp, current_state_block_hash).await?;
395 let parent_beacon_root = get_beacon_root_from_state(&state, timestamp);
396
397 state_anchors.insert(0, BeaconStateAnchor::new(state, current_anchor.take().unwrap()));
398
399 if timestamp == U256::from(execution_anchor.id().as_timestamp().unwrap()) {
401 assert!(
402 parent_beacon_root == execution_anchor.beacon_root(),
403 "failed to validate final beacon anchor"
404 );
405 break;
406 }
407
408 current_state_block_hash = self
409 .beacon_anchor_builder
410 .client
411 .get_execution_payload_block_hash(parent_beacon_root.to_string())
412 .await?;
413
414 let _ = current_anchor.replace(
416 self.beacon_anchor_builder
417 .build_beacon_anchor(
418 parent_beacon_root,
419 BeaconAnchorId::Timestamp(timestamp.to()),
420 BeaconBlockField::StateRoot,
421 )
422 .await?,
423 );
424 }
425
426 Ok(Anchor::ChainedEip4788(ChainedBeaconAnchor::new(execution_anchor, state_anchors)))
427 }
428}
429
430fn verify_merkle_root(
432 block_hash: B256,
433 proof: &[B256],
434 generalized_index: usize,
435 beacon_root: B256,
436) -> bool {
437 rebuild_merkle_root(block_hash, generalized_index, proof) == beacon_root
438}