Skip to content

Commit 41a45ce

Browse files
committed
electra devnet-4 support
1 parent ae53884 commit 41a45ce

File tree

9 files changed

+293
-13
lines changed

9 files changed

+293
-13
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ codegen-units = 1
2424
incremental = false
2525

2626
[workspace.dependencies]
27-
ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "cf3c404043230559660810bc0c9d6d5a8498d819" }
28-
beacon-api-client = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "cf3c404043230559660810bc0c9d6d5a8498d819" }
27+
28+
ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "92f3ec3" }
29+
beacon-api-client = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "92f3ec3" }
2930

3031
reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.0" }
3132
reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.0" }

mev-build-rs/src/auctioneer/service.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::{
22
auctioneer::auction_schedule::{AuctionSchedule, Proposals, Proposer, RelayIndex, RelaySet},
33
bidder::Service as Bidder,
4-
compat::{to_blobs_bundle, to_bytes20, to_bytes32, to_execution_payload},
4+
compat::{
5+
to_blobs_bundle, to_bytes20, to_bytes32, to_execution_payload, to_execution_requests,
6+
},
57
payload::attributes::{BuilderPayloadBuilderAttributes, ProposalAttributes},
68
service::ClockMessage,
79
Error,
@@ -61,6 +63,7 @@ fn prepare_submission(
6163
};
6264
let fork = context.fork_for(auction_context.slot);
6365
let execution_payload = to_execution_payload(payload.block(), fork)?;
66+
let execution_requests = to_execution_requests(payload.block(), fork)?;
6467
let signature = sign_builder_message(&message, signing_key, context)?;
6568
let submission = match fork {
6669
Fork::Bellatrix => {
@@ -83,6 +86,15 @@ fn prepare_submission(
8386
blobs_bundle: to_blobs_bundle(payload.sidecars())?,
8487
signature,
8588
}),
89+
Fork::Electra => {
90+
SignedBidSubmission::Electra(block_submission::electra::SignedBidSubmission {
91+
message,
92+
execution_payload,
93+
execution_requests,
94+
blobs_bundle: to_blobs_bundle(payload.sidecars())?,
95+
signature,
96+
})
97+
}
8698
fork => return Err(Error::UnsupportedFork(fork)),
8799
};
88100
Ok(submission)

mev-build-rs/src/compat.rs

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
use crate::Error;
22
use alloy_eips::eip2718::Encodable2718;
33
use ethereum_consensus::{
4-
crypto::{KzgCommitment, KzgProof},
4+
crypto::{self, KzgCommitment, KzgProof},
55
primitives::{Bytes32, ExecutionAddress},
6-
ssz::prelude::{ByteList, ByteVector, SimpleSerializeError, U256},
6+
ssz::prelude::{ByteList, ByteVector, DeserializeError, SimpleSerializeError, U256},
77
Fork,
88
};
99
use mev_rs::types::{BlobsBundle, ExecutionPayload};
1010
use reth::primitives::{
1111
revm_primitives::{alloy_primitives::Bloom, Address, B256},
12-
BlobTransactionSidecar, SealedBlock,
12+
BlobTransactionSidecar, Request, SealedBlock,
1313
};
1414

1515
#[cfg(not(feature = "minimal-preset"))]
16-
use ethereum_consensus::deneb::mainnet as deneb;
16+
use ethereum_consensus::{deneb::mainnet as deneb, electra::mainnet as electra};
1717
#[cfg(feature = "minimal-preset")]
18-
use ethereum_consensus::deneb::minimal as deneb;
18+
use ethereum_consensus::{deneb::minimal as deneb, electra::minimal as electra};
1919

2020
pub fn to_bytes32(value: B256) -> Bytes32 {
2121
Bytes32::try_from(value.as_ref()).unwrap()
@@ -29,13 +29,62 @@ fn to_byte_vector(value: Bloom) -> ByteVector<256> {
2929
ByteVector::<256>::try_from(value.as_ref()).unwrap()
3030
}
3131

32+
pub fn to_execution_requests(
33+
block: &SealedBlock,
34+
fork: Fork,
35+
) -> Result<electra::ExecutionRequests, Error> {
36+
let input = block.body.requests.as_ref();
37+
let mut requests = electra::ExecutionRequests::default();
38+
if let Some(input) = input {
39+
for request in &input.0 {
40+
match request {
41+
Request::DepositRequest(request) => {
42+
requests.deposits.push(electra::DepositRequest {
43+
public_key: TryFrom::try_from(request.pubkey.as_ref())
44+
.map_err(|err: crypto::BlsError| Error::Consensus(err.into()))?,
45+
withdrawal_credentials: TryFrom::try_from(
46+
request.withdrawal_credentials.as_ref(),
47+
)
48+
.map_err(|err: DeserializeError| {
49+
Error::Consensus(SimpleSerializeError::Deserialize(err).into())
50+
})?,
51+
amount: request.amount,
52+
signature: TryFrom::try_from(request.signature.as_ref())
53+
.map_err(|err: crypto::BlsError| Error::Consensus(err.into()))?,
54+
index: request.index,
55+
});
56+
}
57+
Request::WithdrawalRequest(request) => {
58+
requests.withdrawals.push(electra::WithdrawalRequest {
59+
source_address: to_bytes20(request.source_address),
60+
validator_public_key: TryFrom::try_from(request.validator_pubkey.as_ref())
61+
.map_err(|err: crypto::BlsError| Error::Consensus(err.into()))?,
62+
amount: request.amount,
63+
});
64+
}
65+
Request::ConsolidationRequest(request) => {
66+
requests.consolidations.push(electra::ConsolidationRequest {
67+
source_address: to_bytes20(request.source_address),
68+
source_public_key: TryFrom::try_from(request.source_pubkey.as_ref())
69+
.map_err(|err: crypto::BlsError| Error::Consensus(err.into()))?,
70+
target_public_key: TryFrom::try_from(request.target_pubkey.as_ref())
71+
.map_err(|err: crypto::BlsError| Error::Consensus(err.into()))?,
72+
});
73+
}
74+
_ => return Err(Error::UnsupportedFork(fork)),
75+
}
76+
}
77+
}
78+
Ok(requests)
79+
}
80+
3281
pub fn to_execution_payload(value: &SealedBlock, fork: Fork) -> Result<ExecutionPayload, Error> {
3382
let hash = value.hash();
3483
let header = &value.header;
3584
let transactions = &value.body.transactions;
3685
let withdrawals = &value.body.withdrawals;
3786
match fork {
38-
Fork::Deneb => {
87+
Fork::Deneb | Fork::Electra => {
3988
let transactions = transactions
4089
.iter()
4190
.map(|t| deneb::Transaction::try_from(t.encoded_2718().as_ref()).unwrap())

mev-relay-rs/src/auction_context.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn to_header(execution_payload: &ExecutionPayload) -> Result<ExecutionPayloadHea
2323
}
2424
ExecutionPayload::Capella(payload) => ExecutionPayloadHeader::Capella(payload.try_into()?),
2525
ExecutionPayload::Deneb(payload) => ExecutionPayloadHeader::Deneb(payload.try_into()?),
26+
ExecutionPayload::Electra(payload) => ExecutionPayloadHeader::Electra(payload.try_into()?),
2627
};
2728
Ok(header)
2829
}
@@ -89,11 +90,52 @@ pub mod deneb {
8990
}
9091
}
9192

93+
pub mod electra {
94+
use super::*;
95+
96+
#[cfg(not(feature = "minimal-preset"))]
97+
use ethereum_consensus::electra::mainnet as electra;
98+
#[cfg(feature = "minimal-preset")]
99+
use ethereum_consensus::electra::minimal as electra;
100+
101+
#[derive(Debug, PartialEq, Eq)]
102+
pub struct AuctionContext {
103+
pub builder_public_key: BlsPublicKey,
104+
pub bid_trace: BidTrace,
105+
pub receive_duration: Duration,
106+
pub signed_builder_bid: SignedBuilderBid,
107+
pub execution_payload: ExecutionPayload,
108+
pub execution_requests: electra::ExecutionRequests,
109+
pub value: U256,
110+
pub blobs_bundle: BlobsBundle,
111+
}
112+
113+
impl Hash for AuctionContext {
114+
fn hash<H: Hasher>(&self, state: &mut H) {
115+
self.builder_public_key.hash(state);
116+
self.bid_trace.hash(state);
117+
self.receive_duration.hash(state);
118+
self.signed_builder_bid.hash(state);
119+
let payload_root =
120+
self.execution_payload.hash_tree_root().expect("can get hash tree root");
121+
payload_root.hash(state);
122+
let requests_root =
123+
self.execution_requests.hash_tree_root().expect("can get hash tree root");
124+
requests_root.hash(state);
125+
self.value.hash(state);
126+
let blobs_bundle_root =
127+
self.blobs_bundle.hash_tree_root().expect("can get hash tree root");
128+
blobs_bundle_root.hash(state);
129+
}
130+
}
131+
}
132+
92133
#[derive(Debug, PartialEq, Eq, Hash)]
93134
pub enum AuctionContext {
94135
Bellatrix(bellatrix::AuctionContext),
95136
Capella(capella::AuctionContext),
96137
Deneb(deneb::AuctionContext),
138+
Electra(electra::AuctionContext),
97139
}
98140

99141
impl AuctionContext {
@@ -134,6 +176,15 @@ impl AuctionContext {
134176
public_key: relay_public_key,
135177
})
136178
}
179+
SignedBidSubmission::Electra(ref submission) => {
180+
BuilderBid::Electra(builder_bid::electra::BuilderBid {
181+
header: execution_payload_header,
182+
blob_kzg_commitments: submission.blobs_bundle.commitments.clone(),
183+
execution_requests: submission.execution_requests.clone(),
184+
value,
185+
public_key: relay_public_key,
186+
})
187+
}
137188
};
138189

139190
let signature = sign_builder_message(&bid, relay_secret_key, context)?;
@@ -167,6 +218,16 @@ impl AuctionContext {
167218
value,
168219
blobs_bundle: submission.blobs_bundle,
169220
}),
221+
SignedBidSubmission::Electra(submission) => Self::Electra(electra::AuctionContext {
222+
builder_public_key,
223+
bid_trace: submission.message,
224+
receive_duration,
225+
signed_builder_bid,
226+
execution_payload,
227+
execution_requests: submission.execution_requests,
228+
value,
229+
blobs_bundle: submission.blobs_bundle,
230+
}),
170231
};
171232

172233
Ok(auction_context)
@@ -177,6 +238,7 @@ impl AuctionContext {
177238
Self::Bellatrix(context) => &context.builder_public_key,
178239
Self::Capella(context) => &context.builder_public_key,
179240
Self::Deneb(context) => &context.builder_public_key,
241+
Self::Electra(context) => &context.builder_public_key,
180242
}
181243
}
182244

@@ -185,6 +247,7 @@ impl AuctionContext {
185247
Self::Bellatrix(context) => &context.bid_trace,
186248
Self::Capella(context) => &context.bid_trace,
187249
Self::Deneb(context) => &context.bid_trace,
250+
Self::Electra(context) => &context.bid_trace,
188251
}
189252
}
190253

@@ -193,6 +256,7 @@ impl AuctionContext {
193256
Self::Bellatrix(context) => context.receive_duration,
194257
Self::Capella(context) => context.receive_duration,
195258
Self::Deneb(context) => context.receive_duration,
259+
Self::Electra(context) => context.receive_duration,
196260
}
197261
}
198262

@@ -201,6 +265,7 @@ impl AuctionContext {
201265
Self::Bellatrix(context) => &context.signed_builder_bid,
202266
Self::Capella(context) => &context.signed_builder_bid,
203267
Self::Deneb(context) => &context.signed_builder_bid,
268+
Self::Electra(context) => &context.signed_builder_bid,
204269
}
205270
}
206271

@@ -209,6 +274,7 @@ impl AuctionContext {
209274
Self::Bellatrix(context) => &context.execution_payload,
210275
Self::Capella(context) => &context.execution_payload,
211276
Self::Deneb(context) => &context.execution_payload,
277+
Self::Electra(context) => &context.execution_payload,
212278
}
213279
}
214280

@@ -217,6 +283,7 @@ impl AuctionContext {
217283
Self::Bellatrix(_) => None,
218284
Self::Capella(_) => None,
219285
Self::Deneb(context) => Some(&context.blobs_bundle),
286+
Self::Electra(context) => Some(&context.blobs_bundle),
220287
}
221288
}
222289

@@ -225,6 +292,7 @@ impl AuctionContext {
225292
Self::Bellatrix(context) => context.value,
226293
Self::Capella(context) => context.value,
227294
Self::Deneb(context) => context.value,
295+
Self::Electra(context) => context.value,
228296
}
229297
}
230298

@@ -240,6 +308,12 @@ impl AuctionContext {
240308
blobs_bundle: context.blobs_bundle.clone(),
241309
})
242310
}
311+
Self::Electra(context) => {
312+
AuctionContents::Electra(auction_contents::deneb::AuctionContents {
313+
execution_payload: context.execution_payload.clone(),
314+
blobs_bundle: context.blobs_bundle.clone(),
315+
})
316+
}
243317
}
244318
}
245319
}

mev-relay-rs/src/relay.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ use ethereum_consensus::{
4040
bellatrix::mainnet as bellatrix,
4141
capella::mainnet as capella,
4242
deneb::mainnet as deneb,
43+
electra::mainnet as electra,
4344
types::mainnet::{ExecutionPayloadHeaderRef, SignedBeaconBlock},
4445
};
4546
#[cfg(feature = "minimal-preset")]
4647
use ethereum_consensus::{
4748
bellatrix::minimal as bellatrix,
4849
capella::minimal as capella,
4950
deneb::minimal as deneb,
51+
electra::minimal as electra,
5052
types::minimal::{ExecutionPayloadHeaderRef, SignedBeaconBlock},
5153
};
5254

@@ -80,6 +82,13 @@ fn validate_header_equality(
8082
return Err(RelayError::InvalidExecutionPayloadInBlock);
8183
}
8284
}
85+
ExecutionPayloadHeader::Electra(local_header) => {
86+
let provided_header =
87+
provided_header.electra().ok_or(RelayError::InvalidExecutionPayloadInBlock)?;
88+
if local_header != provided_header {
89+
return Err(RelayError::InvalidExecutionPayloadInBlock);
90+
}
91+
}
8392
}
8493
Ok(())
8594
}
@@ -188,6 +197,41 @@ fn unblind_block(
188197
};
189198
Ok(SignedBeaconBlock::Deneb(inner))
190199
}
200+
SignedBlindedBeaconBlock::Electra(blinded_block) => {
201+
let signature = blinded_block.signature.clone();
202+
let block = &blinded_block.message;
203+
let body = &block.body;
204+
let execution_payload = execution_payload.electra().ok_or(Error::InvalidFork {
205+
expected: Fork::Electra,
206+
provided: execution_payload.version(),
207+
})?;
208+
209+
let inner = electra::SignedBeaconBlock {
210+
message: electra::BeaconBlock {
211+
slot: block.slot,
212+
proposer_index: block.proposer_index,
213+
parent_root: block.parent_root,
214+
state_root: block.state_root,
215+
body: electra::BeaconBlockBody {
216+
randao_reveal: body.randao_reveal.clone(),
217+
eth1_data: body.eth1_data.clone(),
218+
graffiti: body.graffiti.clone(),
219+
proposer_slashings: body.proposer_slashings.clone(),
220+
attester_slashings: body.attester_slashings.clone(),
221+
attestations: body.attestations.clone(),
222+
deposits: body.deposits.clone(),
223+
voluntary_exits: body.voluntary_exits.clone(),
224+
sync_aggregate: body.sync_aggregate.clone(),
225+
execution_payload: execution_payload.clone(),
226+
bls_to_execution_changes: body.bls_to_execution_changes.clone(),
227+
blob_kzg_commitments: body.blob_kzg_commitments.clone(),
228+
execution_requests: body.execution_requests.clone(),
229+
},
230+
},
231+
signature,
232+
};
233+
Ok(SignedBeaconBlock::Electra(inner))
234+
}
191235
}
192236
}
193237

0 commit comments

Comments
 (0)