sp1_cc_client_executor/anchor.rs
1use std::{collections::HashMap, fmt::Display};
2
3use alloy_consensus::Header;
4use alloy_eips::eip4788::BEACON_ROOTS_ADDRESS;
5use alloy_primitives::{uint, B256, U256};
6use revm::DatabaseRef;
7use rsp_client_executor::io::TrieDB;
8use rsp_mpt::EthereumState;
9use serde::{Deserialize, Serialize};
10use serde_with::serde_as;
11use sha2::{Digest, Sha256};
12
13use crate::AnchorType;
14
15// https://eips.ethereum.org/EIPS/eip-4788
16pub const HISTORY_BUFFER_LENGTH: U256 = uint!(8191_U256);
17/// The generalized Merkle tree index of the `block_hash` field in the `BeaconBlock`.
18const BLOCK_HASH_LEAF_INDEX: usize = 6444;
19/// The generalized Merkle tree index of the `state_root` field in the `BeaconBlock`.
20const STATE_ROOT_LEAF_INDEX: usize = 6434;
21
22/// Ethereum anchoring system for verifying block execution against beacon chain roots.
23///
24/// This module provides structures and functionality for creating cryptographic anchors
25/// that link Ethereum execution blocks to their corresponding beacon chain state. These
26/// anchors enable verification of block validity through Merkle proofs and beacon root
27/// commitments stored via EIP-4788.
28///
29/// # Anchor Types
30///
31/// - **Header Anchor**: Direct reference to an execution block header
32/// - **EIP-4788 Anchor**: Links execution block to beacon chain via EIP-4788 beacon roots
33/// - **Chained EIP-4788 Anchor**: Multi-hop verification through state transitions
34/// - **Consensus Anchor**: Direct beacon chain consensus verification
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
36pub enum Anchor {
37 Header(HeaderAnchor),
38 Eip4788(BeaconWithHeaderAnchor),
39 ChainedEip4788(ChainedBeaconAnchor),
40 Consensus(BeaconWithHeaderAnchor),
41}
42
43impl Anchor {
44 /// Returns the execution block header for this anchor.
45 pub fn header(&self) -> &Header {
46 match self {
47 Anchor::Header(header_anchor) => &header_anchor.header,
48 Anchor::Eip4788(beacon_anchor) | Anchor::Consensus(beacon_anchor) => {
49 &beacon_anchor.inner.header
50 }
51 Anchor::ChainedEip4788(chained_anchor) => &chained_anchor.inner.inner.header,
52 }
53 }
54
55 /// Returns the resolved anchor containing the final identifier and hash after verification.
56 pub fn resolve(&self) -> ResolvedAnchor {
57 match self {
58 Anchor::Header(header_anchor) => ResolvedAnchor {
59 id: U256::from(header_anchor.header.number),
60 hash: header_anchor.header.hash_slow(),
61 },
62 Anchor::Eip4788(beacon_anchor) | Anchor::Consensus(beacon_anchor) => {
63 let block_hash = beacon_anchor.inner.header.hash_slow();
64 let hash = beacon_anchor.anchor.beacon_root(block_hash, BLOCK_HASH_LEAF_INDEX);
65
66 ResolvedAnchor { id: beacon_anchor.id().into(), hash }
67 }
68 Anchor::ChainedEip4788(chained_anchor) => {
69 // Retrieve the execution block beacon root and timestamp
70 let mut beacon_root = chained_anchor.inner.beacon_root();
71 let mut timestamp = U256::from(chained_anchor.inner.id().as_timestamp().unwrap());
72
73 // Iterate over all the state anchors stating from the execution block
74 // to the reference block
75 for state_anchor in &chained_anchor.state_anchors {
76 let state_root = state_anchor.state.state_root();
77 let current_beacon_root =
78 get_beacon_root_from_state(&state_anchor.state, timestamp);
79
80 // Verify that the previous anchor is valid wrt the current state
81 assert_eq!(current_beacon_root, beacon_root, "Beacon root should match");
82
83 // Retrieve the beacon root and timestamp of the current state
84 beacon_root =
85 state_anchor.anchor.beacon_root(state_root, STATE_ROOT_LEAF_INDEX);
86 timestamp = U256::from(state_anchor.anchor.id().as_timestamp().unwrap());
87 }
88
89 // If the full chain is valid, return the resolved anchor containing
90 // the reference block beacon root and timestamp
91 ResolvedAnchor { id: timestamp, hash: beacon_root }
92 }
93 }
94 }
95
96 /// Returns the anchor type for this anchor.
97 pub fn ty(&self) -> AnchorType {
98 match self {
99 Anchor::Header(_) => AnchorType::BlockHash,
100 Anchor::Eip4788(_) | Anchor::ChainedEip4788(_) => AnchorType::Eip4788,
101 Anchor::Consensus(_) => AnchorType::Consensus,
102 }
103 }
104}
105
106impl From<Header> for Anchor {
107 fn from(header: Header) -> Self {
108 Self::Header(HeaderAnchor { header })
109 }
110}
111
112/// A resolved anchor containing the final computed identifier and hash.
113///
114/// This structure represents the result of processing an anchor through its
115/// verification chain, yielding a canonical identifier and cryptographic hash
116/// that can be used for block validation.
117#[derive(Debug, Clone, Copy)]
118pub struct ResolvedAnchor {
119 pub id: U256,
120 pub hash: B256,
121}
122
123/// A simple anchor that directly references an Ethereum execution block header.
124///
125/// This is the most basic form of anchor that provides direct access to a block header
126/// without any additional cryptographic proofs or beacon chain integration. It's typically
127/// used when you have direct trust in the header data or when working with local/trusted
128/// block sources.
129#[serde_as]
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
131pub struct HeaderAnchor {
132 #[serde_as(as = "alloy_consensus::serde_bincode_compat::Header")]
133 header: Header,
134}
135
136/// An anchor that combines an execution block header with beacon chain verification.
137///
138/// This structure links an Ethereum execution block to the beacon chain through
139/// cryptographic proofs, enabling verification that the execution block is part
140/// of the canonical beacon chain. It's used for EIP-4788 based verification
141/// where beacon roots are stored in the execution layer.
142///
143/// The anchor contains:
144/// - An execution block header that can be verified
145/// - A beacon anchor with Merkle proofs linking the block to beacon chain state
146///
147/// This enables trustless verification of execution blocks by checking their
148/// inclusion in the beacon chain consensus without requiring direct access
149/// to beacon chain data.
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
151pub struct BeaconWithHeaderAnchor {
152 inner: HeaderAnchor,
153 anchor: BeaconAnchor,
154}
155
156impl BeaconWithHeaderAnchor {
157 /// Creates a new beacon anchor with header and beacon proof.
158 pub fn new(header: Header, anchor: BeaconAnchor) -> Self {
159 Self { inner: HeaderAnchor { header }, anchor }
160 }
161
162 /// Returns the Merkle proof for beacon chain verification.
163 pub fn proof(&self) -> &[B256] {
164 self.anchor.proof()
165 }
166
167 /// Returns the block identifier used for verification.
168 pub fn id(&self) -> &BeaconAnchorId {
169 self.anchor.id()
170 }
171
172 /// Returns the beacon root for this anchor computed from the execution block hash.
173 pub fn beacon_root(&self) -> B256 {
174 self.anchor.beacon_root(self.inner.header.hash_slow(), BLOCK_HASH_LEAF_INDEX)
175 }
176}
177
178impl From<BeaconWithHeaderAnchor> for BeaconAnchor {
179 fn from(value: BeaconWithHeaderAnchor) -> Self {
180 value.anchor
181 }
182}
183
184/// A beacon chain anchor that provides cryptographic proof linking data to beacon chain state.
185///
186/// This structure contains a Merkle proof and identifier that can be used to verify
187/// that a specific piece of data (like a block hash or state root) is correctly
188/// included in the beacon chain. The proof enables trustless verification without
189/// requiring access to the full beacon chain state.
190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
191pub struct BeaconAnchor {
192 proof: Vec<B256>,
193 id: BeaconAnchorId,
194}
195
196impl BeaconAnchor {
197 /// Creates a new beacon anchor with the given proof and identifier.
198 pub fn new(proof: Vec<B256>, id: BeaconAnchorId) -> Self {
199 Self { proof, id }
200 }
201 /// Creates a new beacon anchor with the given proof and identifier.
202 pub fn proof(&self) -> &[B256] {
203 &self.proof
204 }
205
206 /// Returns the block identifier used for verification.
207 pub fn id(&self) -> &BeaconAnchorId {
208 &self.id
209 }
210
211 /// Reconstructs the beacon chain Merkle root from a leaf value and proof.
212 pub fn beacon_root(&self, leaf: B256, generalized_index: usize) -> B256 {
213 rebuild_merkle_root(leaf, generalized_index, &self.proof)
214 }
215}
216
217/// Identifier for a beacon chain anchor, specifying how to locate the anchor in beacon chain history.
218///
219/// The beacon chain stores historical roots that can be accessed either by timestamp
220/// (for EIP-4788 verification) or by slot number (for direct beacon chain verification).
221/// This enum allows anchors to specify which indexing method should be used.
222///
223/// # Variants
224///
225/// - **Timestamp**: References a beacon root by its timestamp, used with EIP-4788
226/// where beacon roots are stored in the execution layer indexed by timestamp
227/// - **Slot**: References a beacon root by its slot number, used for direct beacon
228/// chain verification where data is indexed by consensus slots
229#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
230pub enum BeaconAnchorId {
231 Timestamp(u64),
232 Slot(u64),
233}
234
235impl BeaconAnchorId {
236 /// Returns timestamp if this is a Timestamp variant, None otherwise.
237 pub fn as_timestamp(&self) -> Option<u64> {
238 match self {
239 BeaconAnchorId::Timestamp(t) => Some(*t),
240 BeaconAnchorId::Slot(_) => None,
241 }
242 }
243}
244
245impl From<&BeaconAnchorId> for U256 {
246 fn from(value: &BeaconAnchorId) -> Self {
247 match value {
248 BeaconAnchorId::Timestamp(t) => U256::from(*t),
249 BeaconAnchorId::Slot(s) => U256::from(*s),
250 }
251 }
252}
253
254/// A chained anchor that enables verification through multiple beacon chain state transitions.
255///
256/// This structure extends the basic beacon anchor concept by allowing verification through
257/// a chain of state transitions. It's useful when you need to verify an execution block
258/// against a beacon chain state that's not directly accessible via EIP-4788, but can be
259/// reached through a series of intermediate states.
260///
261/// The chained anchor works by:
262/// 1. Starting with an execution block and its beacon root (via EIP-4788)
263/// 2. Following a chain of beacon chain state transitions through intermediate states
264/// 3. Ending at a reference beacon chain state that can be independently verified
265///
266/// Each step in the chain verifies:
267/// - The previous beacon root matches the current state's stored beacon root
268/// - The current state root is properly included in the next beacon chain state
269///
270/// This enables verification of execution blocks against historical beacon chain states
271/// that may not be directly accessible, creating a cryptographic chain of trust through
272/// intermediate consensus states.
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
274pub struct ChainedBeaconAnchor {
275 inner: BeaconWithHeaderAnchor,
276 state_anchors: Vec<BeaconStateAnchor>,
277}
278
279impl ChainedBeaconAnchor {
280 /// Creates a new chained beacon anchor linking an execution block through multiple state transitions.
281 pub fn new(inner: BeaconWithHeaderAnchor, state_anchors: Vec<BeaconStateAnchor>) -> Self {
282 Self { inner, state_anchors }
283 }
284}
285
286/// An anchor that combines beacon chain state with cryptographic proof for state transition verification.
287///
288/// This structure represents a single link in a chained verification process, containing
289/// both a beacon chain state and the cryptographic proof needed to verify that state's
290/// inclusion in the beacon chain. It's used as a building block for `ChainedBeaconAnchor`
291/// to enable verification through multiple state transitions.
292///
293/// The anchor contains:
294/// - A complete Ethereum beacon chain state snapshot
295/// - A beacon anchor with Merkle proofs linking the state root to beacon chain consensus
296///
297/// This enables verification that a specific beacon chain state is legitimate and can
298/// be used as a trusted intermediate step in a chain of cryptographic proofs. Each
299/// `BeaconStateAnchor` in a chain verifies that its state root is properly included
300/// in the next beacon chain state, creating a verifiable path from an execution block
301/// to a reference beacon chain state.
302#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
303pub struct BeaconStateAnchor {
304 state: EthereumState,
305 anchor: BeaconAnchor,
306}
307
308impl BeaconStateAnchor {
309 pub fn new(state: EthereumState, anchor: BeaconAnchor) -> Self {
310 Self { state, anchor }
311 }
312}
313
314/// A field identifier for beacon block components that can be verified via Merkle proofs.
315///
316/// This enum specifies which field of a beacon block should be used as the leaf value
317/// in Merkle proof verification. Different anchor types require verification of different
318/// beacon block fields to establish the cryptographic link between execution and consensus layers.
319#[derive(Debug, Clone, Copy)]
320pub enum BeaconBlockField {
321 BlockHash,
322 StateRoot,
323}
324
325impl Display for BeaconBlockField {
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327 match self {
328 BeaconBlockField::BlockHash => write!(f, "block_hash"),
329 BeaconBlockField::StateRoot => write!(f, "state_root"),
330 }
331 }
332}
333
334impl PartialEq<BeaconBlockField> for usize {
335 fn eq(&self, other: &BeaconBlockField) -> bool {
336 let other = usize::from(other);
337
338 *self == other
339 }
340}
341
342impl From<&BeaconBlockField> for usize {
343 fn from(value: &BeaconBlockField) -> Self {
344 match value {
345 BeaconBlockField::BlockHash => BLOCK_HASH_LEAF_INDEX,
346 BeaconBlockField::StateRoot => STATE_ROOT_LEAF_INDEX,
347 }
348 }
349}
350
351/// Rebuilds a Merkle tree root from a leaf value and its branch proof.
352///
353/// Given a leaf value, its generalized index in the tree, and the sibling hashes
354/// along the path to the root, this function reconstructs the Merkle root by
355/// iteratively hashing the current node with its sibling at each level.
356///
357/// # Arguments
358///
359/// * `leaf` - The leaf value to start reconstruction from
360/// * `generalized_index` - The generalized index of the leaf in the Merkle tree
361/// * `branch` - Slice of sibling hashes along the path from leaf to root
362///
363/// # Returns
364///
365/// The reconstructed Merkle root hash
366pub fn rebuild_merkle_root(leaf: B256, generalized_index: usize, branch: &[B256]) -> B256 {
367 let mut current_hash = leaf;
368 let depth = generalized_index.ilog2();
369 let mut index = generalized_index - (1 << depth);
370 let mut hasher = Sha256::new();
371
372 for sibling in branch {
373 // Determine if the current node is a left or right child
374 let is_left = index % 2 == 0;
375
376 // Combine the current hash with the sibling hash
377 if is_left {
378 // If current node is left child, hash(current + sibling)
379 hasher.update(current_hash);
380 hasher.update(sibling);
381 } else {
382 // If current node is right child, hash(sibling + current)
383 hasher.update(sibling);
384 hasher.update(current_hash);
385 }
386 current_hash.copy_from_slice(&hasher.finalize_reset());
387
388 // Move up to the parent level
389 index /= 2;
390 }
391
392 current_hash
393}
394
395/// Retrieves a beacon root from Ethereum state using EIP-4788 storage.
396///
397/// This function looks up a beacon root stored in the EIP-4788 beacon roots contract
398/// at the `BEACON_ROOTS_ADDRESS`. The beacon root is indexed by timestamp and retrieved
399/// from the circular buffer using modular arithmetic.
400///
401/// # Arguments
402///
403/// * `state` - The Ethereum state to query
404/// * `timestamp` - The timestamp to look up the beacon root for
405///
406/// # Returns
407///
408/// The beacon root hash stored at the given timestamp
409pub fn get_beacon_root_from_state(state: &EthereumState, timestamp: U256) -> B256 {
410 let db = TrieDB::new(state, HashMap::default(), HashMap::default());
411 let timestamp_idx = timestamp % HISTORY_BUFFER_LENGTH;
412 let root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH;
413
414 let root = db.storage_ref(BEACON_ROOTS_ADDRESS, root_idx).unwrap();
415
416 root.into()
417}