risc0_steel/beacon/mod.rs
1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Types related to commitments to the beacon block root.
16use crate::{merkle, BlockHeaderCommit, Commitment, CommitmentVersion, ComposeInput};
17use alloy_primitives::{Sealed, B256};
18use serde::{Deserialize, Serialize};
19use std::fmt;
20
21#[cfg(feature = "host")]
22pub(crate) mod host;
23
24/// The generalized Merkle tree index of the `state_root` field in the `BeaconBlock`.
25pub const STATE_ROOT_LEAF_INDEX: usize = 6434;
26
27/// The generalized Merkle tree index of the `block_hash` field in the `BeaconBlock`.
28pub const BLOCK_HASH_LEAF_INDEX: usize = 6444;
29
30/// Input committing to the corresponding Beacon Chain block root.
31pub type BeaconInput<F> = ComposeInput<F, BeaconCommit>;
32
33/// A commitment that an execution block hash is included in a specific beacon block on the Ethereum
34/// blockchain.
35///
36/// This type represents a commitment that proves the inclusion of an execution block's hash within
37/// a particular beacon block on the Ethereum beacon chain. It relies on a Merkle proof to establish
38/// this link, ensuring the integrity and verifiability of the connection between the execution
39/// block and the beacon chain.
40///
41/// **Important:** This type currently relies on an underlying implementation that only supports the
42/// Deneb fork of the beacon chain. If the beacon chain undergoes a future upgrade, this type's
43/// functionality may be affected, potentially requiring updates to handle new block structures or
44/// proof generation mechanisms.
45///
46/// Users should monitor for beacon chain upgrades and ensure they are using a compatible version of
47/// this library.
48pub type BeaconCommit = GeneralizedBeaconCommit<BLOCK_HASH_LEAF_INDEX>;
49
50/// A beacon block identifier.
51#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
52pub enum BeaconBlockId {
53 /// Timestamp of the child execution block, to query the beacon block root using the EIP-4788
54 /// beacon roots contract.
55 Eip4788(u64),
56 /// Slot of the beacon block.
57 Slot(u64),
58}
59
60impl fmt::Display for BeaconBlockId {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match self {
63 BeaconBlockId::Eip4788(timestamp) => {
64 write!(f, "eip4788-timestamp: {timestamp}")
65 }
66 BeaconBlockId::Slot(slot) => write!(f, "slot: {slot}"),
67 }
68 }
69}
70
71impl BeaconBlockId {
72 pub const fn as_version(&self) -> u16 {
73 match self {
74 BeaconBlockId::Eip4788(_) => CommitmentVersion::Beacon as u16,
75 BeaconBlockId::Slot(_) => CommitmentVersion::Consensus as u16,
76 }
77 }
78 pub const fn as_id(&self) -> u64 {
79 match self {
80 BeaconBlockId::Eip4788(ts) => *ts,
81 BeaconBlockId::Slot(slot) => *slot,
82 }
83 }
84}
85
86/// A commitment to a field of the beacon block at a specific index in a Merkle tree, along with a
87/// timestamp.
88///
89/// The constant generic parameter `LEAF_INDEX` specifies the generalized Merkle tree index of the
90/// leaf node in the Merkle tree corresponding to the field.
91#[derive(Clone, Serialize, Deserialize)]
92pub struct GeneralizedBeaconCommit<const LEAF_INDEX: usize> {
93 proof: Vec<B256>,
94 block_id: BeaconBlockId,
95}
96
97impl<const LEAF_INDEX: usize> GeneralizedBeaconCommit<LEAF_INDEX> {
98 /// Creates a new `GeneralizedBeaconCommit`.
99 ///
100 /// It panics if `LEAF_INDEX` is zero, because a Merkle tree cannot have a leaf at index 0.
101 #[must_use]
102 #[inline]
103 pub const fn new(proof: Vec<B256>, block_id: BeaconBlockId) -> Self {
104 assert!(LEAF_INDEX > 0);
105 Self { proof, block_id }
106 }
107
108 /// Disassembles this `GeneralizedBeaconCommit`, returning the underlying Merkle proof and
109 /// beacon block identifier.
110 #[inline]
111 pub fn into_parts(self) -> (Vec<B256>, BeaconBlockId) {
112 (self.proof, self.block_id)
113 }
114
115 /// Calculates the root of the Merkle tree containing the given `leaf` hash at `LEAF_INDEX`,
116 /// using the provided Merkle proof.
117 #[inline]
118 pub fn process_proof(&self, leaf: B256) -> Result<B256, merkle::InvalidProofError> {
119 merkle::process_proof(leaf, &self.proof, LEAF_INDEX)
120 }
121
122 /// Verifies that the given `leaf` hash is present at the `LEAF_INDEX` in the Merkle tree
123 /// represented by the `root` hash.
124 #[inline]
125 pub fn verify(&self, leaf: B256, root: B256) -> Result<(), merkle::InvalidProofError> {
126 merkle::verify(leaf, &self.proof, LEAF_INDEX, root)
127 }
128
129 /// Returns the beacon block identifier (slot or timestamp).
130 pub(crate) fn block_id(&self) -> BeaconBlockId {
131 self.block_id
132 }
133
134 pub(crate) fn into_commit(self, leaf: B256) -> (BeaconBlockId, B256) {
135 let beacon_root = self
136 .process_proof(leaf)
137 .expect("Invalid beacon inclusion proof");
138 (self.block_id(), beacon_root)
139 }
140}
141
142impl<H, const LEAF_INDEX: usize> BlockHeaderCommit<H> for GeneralizedBeaconCommit<LEAF_INDEX> {
143 #[inline]
144 fn commit(self, header: &Sealed<H>, config_id: B256) -> Commitment {
145 let (block_id, beacon_root) = self.into_commit(header.seal());
146 Commitment::new(
147 block_id.as_version(),
148 block_id.as_id(),
149 beacon_root,
150 config_id,
151 )
152 }
153}