Skip to content

Commit 3c41e58

Browse files
committed
feat: add electra support
1 parent 9ec2e77 commit 3c41e58

File tree

11 files changed

+287
-18
lines changed

11 files changed

+287
-18
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,7 @@ sha3-v0-10-8 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", packa
136136
crypto-bigint = { git = "https://github.com/sp1-patches/RustCrypto-bigint", tag = "patch-0.5.5-sp1-4.0.0" }
137137
tiny-keccak = { git = "https://github.com/sp1-patches/tiny-keccak", tag = "patch-2.0.2-sp1-4.0.0" }
138138
secp256k1 = { git = "https://github.com/sp1-patches/rust-secp256k1", tag = "patch-0.30.0-sp1-4.1.0" }
139+
140+
# TODO: Remove once https://github.com/ralexstokes/ethereum-consensus/pull/419 is merged.
141+
[patch."https://github.com/ralexstokes/ethereum-consensus"]
142+
ethereum-consensus = { git = "https://github.com/leruaa/ethereum-consensus", branch = "electra-fixes" }

crates/client-executor/src/anchor.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ impl Anchor {
6161
beacon_root =
6262
state_anchor.anchor.beacon_root(state_root, STATE_ROOT_LEAF_INDEX);
6363
timestamp = state_anchor.anchor.timestamp();
64-
65-
eprintln!("{}: {beacon_root}", timestamp.to::<u64>());
6664
}
6765

6866
ResolvedAnchor { id: timestamp, hash: beacon_root }

crates/host-executor/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ async-trait.workspace = true
1313
eyre.workspace = true
1414
reqwest.workspace = true
1515
serde.workspace = true
16+
serde_json.workspace = true
1617
url.workspace = true
1718
tokio.workspace = true
1819
tracing.workspace = true

crates/host-executor/src/anchor_builder.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ use alloy_eips::{eip4788::BEACON_ROOTS_ADDRESS, BlockId};
55
use alloy_primitives::{B256, U256};
66
use alloy_provider::{network::AnyNetwork, Provider};
77
use async_trait::async_trait;
8-
use ethereum_consensus::{ssz::prelude::Prove, types::SignedBeaconBlock};
8+
use ethereum_consensus::ssz::prelude::Prove;
99
use rsp_mpt::EthereumState;
1010
use sp1_cc_client_executor::{
1111
get_beacon_root_from_state, rebuild_merkle_root, Anchor, BeaconAnchor, BeaconBlockField,
1212
BeaconStateAnchor, BeaconWithHeaderAnchor, ChainedBeaconAnchor, HISTORY_BUFFER_LENGTH,
1313
};
1414
use url::Url;
1515

16-
use crate::{beacon_client::BeaconClient, HostError};
16+
use crate::{
17+
beacon::{BeaconClient, SignedBeaconBlock},
18+
HostError,
19+
};
1720

1821
/// Abstracts [`Anchor`] creation.
1922
#[async_trait]
@@ -120,7 +123,14 @@ impl<P: Provider<AnyNetwork>> BeaconAnchorBuilder<P> {
120123
field.to_string().as_str().into(),
121124
])?
122125
}
123-
_ => todo!(),
126+
SignedBeaconBlock::Electra(signed_beacon_block) => {
127+
signed_beacon_block.message.prove(&[
128+
"body".into(),
129+
"execution_payload".into(),
130+
field.to_string().as_str().into(),
131+
])?
132+
}
133+
_ => unimplemented!(),
124134
};
125135

126136
assert!(proof.index == field, "the field leaf index is incorrect");
Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use alloy_primitives::B256;
2-
use ethereum_consensus::types::mainnet::SignedBeaconBlock;
2+
use ethereum_consensus::Fork;
33
use reqwest::Client as ReqwestClient;
4-
use serde::Deserialize;
4+
use serde::{Deserialize, Serialize};
5+
use serde_json::value::RawValue;
56
use url::Url;
67

78
use crate::BeaconError;
89

10+
use super::SignedBeaconBlock;
11+
912
/// A client used for connecting and querying a beacon node.
1013
#[derive(Debug, Clone)]
1114
pub struct BeaconClient {
@@ -14,13 +17,33 @@ pub struct BeaconClient {
1417
}
1518

1619
/// The data format returned by official Eth Beacon Node APIs.
17-
#[derive(Debug, Deserialize)]
18-
struct BeaconData<T> {
19-
#[allow(unused)]
20+
#[derive(Debug, Serialize, Deserialize)]
21+
struct BeaconResponse<'a> {
22+
pub version: Fork,
2023
pub execution_optimistic: bool,
21-
#[allow(unused)]
2224
pub finalized: bool,
23-
pub data: T,
25+
#[serde(borrow)]
26+
data: &'a RawValue,
27+
}
28+
29+
impl<'de> serde::Deserialize<'de> for SignedBeaconBlock {
30+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
31+
where
32+
D: serde::Deserializer<'de>,
33+
{
34+
let BeaconResponse { version, data, .. } = BeaconResponse::deserialize(deserializer)?;
35+
let data = match version {
36+
Fork::Phase0 => serde_json::from_str(data.get()).map(SignedBeaconBlock::Phase0),
37+
Fork::Altair => serde_json::from_str(data.get()).map(SignedBeaconBlock::Altair),
38+
Fork::Bellatrix => serde_json::from_str(data.get()).map(SignedBeaconBlock::Bellatrix),
39+
Fork::Capella => serde_json::from_str(data.get()).map(SignedBeaconBlock::Capella),
40+
Fork::Deneb => serde_json::from_str(data.get()).map(SignedBeaconBlock::Deneb),
41+
Fork::Electra => serde_json::from_str(data.get()).map(SignedBeaconBlock::Electra),
42+
}
43+
.map_err(serde::de::Error::custom)?;
44+
45+
Ok(data)
46+
}
2447
}
2548

2649
impl BeaconClient {
@@ -33,9 +56,9 @@ impl BeaconClient {
3356
let endpoint = format!("{}eth/v2/beacon/blocks/{}", self.rpc_url, beacon_id);
3457

3558
let response = self.client.get(&endpoint).send().await?;
36-
let parsed = response.error_for_status()?.json::<BeaconData<SignedBeaconBlock>>().await?;
59+
let block = response.error_for_status()?.json::<SignedBeaconBlock>().await?;
3760

38-
Ok(parsed.data)
61+
Ok(block)
3962
}
4063

4164
/// Retrieves the execution bock hash at the given `beacon_id`.
@@ -50,6 +73,7 @@ impl BeaconClient {
5073
SignedBeaconBlock::Bellatrix(b) => Some(b.message.body.execution_payload.block_hash),
5174
SignedBeaconBlock::Capella(b) => Some(b.message.body.execution_payload.block_hash),
5275
SignedBeaconBlock::Deneb(b) => Some(b.message.body.execution_payload.block_hash),
76+
SignedBeaconBlock::Electra(b) => Some(b.message.body.execution_payload.block_hash),
5377
};
5478

5579
block_hash.ok_or_else(|| BeaconError::ExecutionPayloadMissing).map(|h| B256::from_slice(&h))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod signed_beacon_block;
2+
pub(crate) use signed_beacon_block::mainnet::SignedBeaconBlock;
3+
4+
mod client;
5+
pub use client::BeaconClient;
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
use ethereum_consensus::{altair, bellatrix, capella, deneb, electra, phase0, ssz::prelude::*};
2+
3+
#[derive(Debug, Clone, PartialEq, Eq, Serializable, HashTreeRoot, serde::Serialize)]
4+
#[ssz(transparent)]
5+
#[serde(untagged)]
6+
pub enum SignedBeaconBlock<
7+
const MAX_PROPOSER_SLASHINGS: usize,
8+
const MAX_VALIDATORS_PER_COMMITTEE: usize,
9+
const MAX_ATTESTER_SLASHINGS: usize,
10+
const MAX_ATTESTATIONS: usize,
11+
const MAX_DEPOSITS: usize,
12+
const MAX_VOLUNTARY_EXITS: usize,
13+
const SYNC_COMMITTEE_SIZE: usize,
14+
const BYTES_PER_LOGS_BLOOM: usize,
15+
const MAX_EXTRA_DATA_BYTES: usize,
16+
const MAX_BYTES_PER_TRANSACTION: usize,
17+
const MAX_TRANSACTIONS_PER_PAYLOAD: usize,
18+
const MAX_WITHDRAWALS_PER_PAYLOAD: usize,
19+
const MAX_BLS_TO_EXECUTION_CHANGES: usize,
20+
const MAX_BLOB_COMMITMENTS_PER_BLOCK: usize,
21+
const MAX_VALIDATORS_PER_SLOT: usize,
22+
const MAX_COMMITTEES_PER_SLOT: usize,
23+
const MAX_ATTESTER_SLASHINGS_ELECTRA: usize,
24+
const MAX_ATTESTATIONS_ELECTRA: usize,
25+
const MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: usize,
26+
const MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: usize,
27+
const MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: usize,
28+
> {
29+
Phase0(
30+
phase0::SignedBeaconBlock<
31+
MAX_PROPOSER_SLASHINGS,
32+
MAX_VALIDATORS_PER_COMMITTEE,
33+
MAX_ATTESTER_SLASHINGS,
34+
MAX_ATTESTATIONS,
35+
MAX_DEPOSITS,
36+
MAX_VOLUNTARY_EXITS,
37+
>,
38+
),
39+
Altair(
40+
altair::SignedBeaconBlock<
41+
MAX_PROPOSER_SLASHINGS,
42+
MAX_VALIDATORS_PER_COMMITTEE,
43+
MAX_ATTESTER_SLASHINGS,
44+
MAX_ATTESTATIONS,
45+
MAX_DEPOSITS,
46+
MAX_VOLUNTARY_EXITS,
47+
SYNC_COMMITTEE_SIZE,
48+
>,
49+
),
50+
Bellatrix(
51+
bellatrix::SignedBeaconBlock<
52+
MAX_PROPOSER_SLASHINGS,
53+
MAX_VALIDATORS_PER_COMMITTEE,
54+
MAX_ATTESTER_SLASHINGS,
55+
MAX_ATTESTATIONS,
56+
MAX_DEPOSITS,
57+
MAX_VOLUNTARY_EXITS,
58+
SYNC_COMMITTEE_SIZE,
59+
BYTES_PER_LOGS_BLOOM,
60+
MAX_EXTRA_DATA_BYTES,
61+
MAX_BYTES_PER_TRANSACTION,
62+
MAX_TRANSACTIONS_PER_PAYLOAD,
63+
>,
64+
),
65+
Capella(
66+
capella::SignedBeaconBlock<
67+
MAX_PROPOSER_SLASHINGS,
68+
MAX_VALIDATORS_PER_COMMITTEE,
69+
MAX_ATTESTER_SLASHINGS,
70+
MAX_ATTESTATIONS,
71+
MAX_DEPOSITS,
72+
MAX_VOLUNTARY_EXITS,
73+
SYNC_COMMITTEE_SIZE,
74+
BYTES_PER_LOGS_BLOOM,
75+
MAX_EXTRA_DATA_BYTES,
76+
MAX_BYTES_PER_TRANSACTION,
77+
MAX_TRANSACTIONS_PER_PAYLOAD,
78+
MAX_WITHDRAWALS_PER_PAYLOAD,
79+
MAX_BLS_TO_EXECUTION_CHANGES,
80+
>,
81+
),
82+
Deneb(
83+
deneb::SignedBeaconBlock<
84+
MAX_PROPOSER_SLASHINGS,
85+
MAX_VALIDATORS_PER_COMMITTEE,
86+
MAX_ATTESTER_SLASHINGS,
87+
MAX_ATTESTATIONS,
88+
MAX_DEPOSITS,
89+
MAX_VOLUNTARY_EXITS,
90+
SYNC_COMMITTEE_SIZE,
91+
BYTES_PER_LOGS_BLOOM,
92+
MAX_EXTRA_DATA_BYTES,
93+
MAX_BYTES_PER_TRANSACTION,
94+
MAX_TRANSACTIONS_PER_PAYLOAD,
95+
MAX_WITHDRAWALS_PER_PAYLOAD,
96+
MAX_BLS_TO_EXECUTION_CHANGES,
97+
MAX_BLOB_COMMITMENTS_PER_BLOCK,
98+
>,
99+
),
100+
Electra(
101+
electra::SignedBeaconBlock<
102+
MAX_PROPOSER_SLASHINGS,
103+
MAX_VALIDATORS_PER_SLOT,
104+
MAX_COMMITTEES_PER_SLOT,
105+
MAX_ATTESTER_SLASHINGS_ELECTRA,
106+
MAX_ATTESTATIONS_ELECTRA,
107+
MAX_DEPOSITS,
108+
MAX_VOLUNTARY_EXITS,
109+
SYNC_COMMITTEE_SIZE,
110+
BYTES_PER_LOGS_BLOOM,
111+
MAX_EXTRA_DATA_BYTES,
112+
MAX_BYTES_PER_TRANSACTION,
113+
MAX_TRANSACTIONS_PER_PAYLOAD,
114+
MAX_WITHDRAWALS_PER_PAYLOAD,
115+
MAX_BLS_TO_EXECUTION_CHANGES,
116+
MAX_BLOB_COMMITMENTS_PER_BLOCK,
117+
MAX_DEPOSIT_REQUESTS_PER_PAYLOAD,
118+
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
119+
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
120+
>,
121+
),
122+
}
123+
124+
pub(crate) mod mainnet {
125+
use ethereum_consensus::{
126+
altair::mainnet::SYNC_COMMITTEE_SIZE,
127+
bellatrix::mainnet::{
128+
BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES,
129+
MAX_TRANSACTIONS_PER_PAYLOAD,
130+
},
131+
capella::mainnet::{MAX_BLS_TO_EXECUTION_CHANGES, MAX_WITHDRAWALS_PER_PAYLOAD},
132+
deneb::mainnet::MAX_BLOB_COMMITMENTS_PER_BLOCK,
133+
phase0::mainnet::{
134+
MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_COMMITTEES_PER_SLOT, MAX_DEPOSITS,
135+
MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS,
136+
},
137+
};
138+
139+
const MAX_ATTESTER_SLASHINGS_ELECTRA: usize = 1;
140+
const MAX_ATTESTATIONS_ELECTRA: usize = 8;
141+
142+
const MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: usize = 8192;
143+
const MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: usize = 16;
144+
const MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: usize = 2;
145+
const MAX_COMMITTEES_PER_SLOT_USIZE: usize = MAX_COMMITTEES_PER_SLOT as usize;
146+
147+
const MAX_VALIDATORS_PER_SLOT: usize =
148+
MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT_USIZE;
149+
150+
pub(crate) type SignedBeaconBlock = super::SignedBeaconBlock<
151+
MAX_PROPOSER_SLASHINGS,
152+
MAX_VALIDATORS_PER_COMMITTEE,
153+
MAX_ATTESTER_SLASHINGS,
154+
MAX_ATTESTATIONS,
155+
MAX_DEPOSITS,
156+
MAX_VOLUNTARY_EXITS,
157+
SYNC_COMMITTEE_SIZE,
158+
BYTES_PER_LOGS_BLOOM,
159+
MAX_EXTRA_DATA_BYTES,
160+
MAX_BYTES_PER_TRANSACTION,
161+
MAX_TRANSACTIONS_PER_PAYLOAD,
162+
MAX_WITHDRAWALS_PER_PAYLOAD,
163+
MAX_BLS_TO_EXECUTION_CHANGES,
164+
MAX_BLOB_COMMITMENTS_PER_BLOCK,
165+
MAX_VALIDATORS_PER_SLOT,
166+
MAX_COMMITTEES_PER_SLOT_USIZE,
167+
MAX_ATTESTER_SLASHINGS_ELECTRA,
168+
MAX_ATTESTATIONS_ELECTRA,
169+
MAX_DEPOSIT_REQUESTS_PER_PAYLOAD,
170+
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
171+
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
172+
>;
173+
}

crates/host-executor/src/errors.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ pub enum HostError {
2626
#[derive(Error, Debug)]
2727
pub enum BeaconError {
2828
#[error("Reqwest error: {0}")]
29-
ReqwestError(#[from] reqwest::Error),
29+
Reqwest(#[from] reqwest::Error),
30+
#[error("Serde error: {0}")]
31+
Serde(#[from] serde_json::Error),
3032
#[error("Execution payload missing")]
3133
ExecutionPayloadMissing,
3234
}

crates/host-executor/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ pub use anchor_builder::{
55
AnchorBuilder, BeaconAnchorBuilder, ChainedBeaconAnchorBuilder, HeaderAnchorBuilder,
66
};
77

8-
mod beacon_client;
9-
pub use beacon_client::BeaconClient;
8+
mod beacon;
9+
pub use beacon::BeaconClient;
1010

1111
mod errors;
1212
pub use errors::{BeaconError, HostError};

0 commit comments

Comments
 (0)