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}