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