Skip to content

Commit b61a7ee

Browse files
committed
feat: add logic for creating header proof for pectra types
1 parent 4dffcd5 commit b61a7ee

File tree

6 files changed

+190
-6
lines changed

6 files changed

+190
-6
lines changed

bin/e2hs-writer/src/reader.rs

+45-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use ethportal_api::{
1414
block_body::BlockBody,
1515
header_with_proof::{
1616
build_capella_historical_summaries_proof, build_deneb_historical_summaries_proof,
17-
build_historical_roots_proof, BlockHeaderProof,
18-
BlockProofHistoricalHashesAccumulator, HeaderWithProof,
17+
build_electra_historical_summaries_proof, build_historical_roots_proof,
18+
BlockHeaderProof, BlockProofHistoricalHashesAccumulator, HeaderWithProof,
1919
},
2020
},
2121
network_spec::network_spec,
@@ -33,6 +33,7 @@ use crate::{
3333
utils::{
3434
bellatrix_execution_payload_to_header, capella_execution_payload_to_header,
3535
lookup_epoch_acc, post_deneb_execution_payload_to_header,
36+
post_electra_execution_payload_to_header,
3637
},
3738
};
3839

@@ -241,6 +242,48 @@ impl EpochReader {
241242
})
242243
}
243244

245+
#[allow(dead_code)]
246+
fn get_electra_block_data(&mut self, block_number: u64) -> anyhow::Result<AllBlockData> {
247+
let (block, era) = self.era_provider.get_post_merge(block_number)?;
248+
let block = block
249+
.block
250+
.message_electra()
251+
.map_err(|e| anyhow!("Unable to decode ekectra block: {e:?}"))?;
252+
let payload = &block.body.execution_payload;
253+
let transactions = decode_transactions(&payload.transactions)?;
254+
let withdrawals: Vec<Withdrawal> =
255+
payload.withdrawals.iter().map(Withdrawal::from).collect();
256+
257+
let header_with_proof = HeaderWithProof {
258+
header: post_electra_execution_payload_to_header(
259+
payload,
260+
block.parent_root,
261+
&transactions,
262+
&withdrawals,
263+
&block.body.execution_requests,
264+
)?,
265+
proof: BlockHeaderProof::HistoricalSummariesDeneb(
266+
build_electra_historical_summaries_proof(
267+
block.slot,
268+
&era.historical_batch.block_roots,
269+
block,
270+
),
271+
),
272+
};
273+
let body = BlockBody(AlloyBlockBody {
274+
transactions,
275+
ommers: vec![],
276+
withdrawals: Some(Withdrawals::new(withdrawals)),
277+
});
278+
let receipts = self.get_receipts(block_number, header_with_proof.header.receipts_root)?;
279+
Ok(AllBlockData {
280+
block_number,
281+
header_with_proof,
282+
body,
283+
receipts,
284+
})
285+
}
286+
244287
pub fn iter_blocks(mut self) -> impl Iterator<Item = anyhow::Result<AllBlockData>> {
245288
(self.starting_block..self.ending_block).map(move |current_block| {
246289
if current_block

bin/e2hs-writer/src/utils.rs

+44-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use alloy::{
1010
};
1111
use anyhow::{anyhow, ensure};
1212
use ethportal_api::{
13-
consensus::execution_payload::ExecutionPayloadDeneb,
13+
consensus::{
14+
execution_payload::{ExecutionPayloadDeneb, ExecutionPayloadElectra},
15+
execution_requests::ExecutionRequests,
16+
},
1417
types::{
1518
consensus::execution_payload::{ExecutionPayloadBellatrix, ExecutionPayloadCapella},
1619
execution::accumulator::EpochAccumulator,
@@ -134,6 +137,46 @@ pub fn post_deneb_execution_payload_to_header(
134137
Ok(header)
135138
}
136139

140+
pub fn post_electra_execution_payload_to_header(
141+
payload: &ExecutionPayloadElectra,
142+
parent_beacon_block_root: B256,
143+
transactions: &[TxEnvelope],
144+
withdrawals: &[Withdrawal],
145+
_requests: &ExecutionRequests,
146+
) -> anyhow::Result<Header> {
147+
let transactions_root = calculate_transaction_root(transactions);
148+
let withdrawals_root = calculate_withdrawals_root(withdrawals);
149+
let header = Header {
150+
parent_hash: payload.parent_hash,
151+
ommers_hash: EMPTY_UNCLE_ROOT_HASH,
152+
beneficiary: payload.fee_recipient,
153+
state_root: payload.state_root,
154+
transactions_root,
155+
receipts_root: payload.receipts_root,
156+
logs_bloom: Bloom::from_slice(payload.logs_bloom.to_vec().as_slice()),
157+
difficulty: U256::ZERO,
158+
number: payload.block_number,
159+
gas_limit: payload.gas_limit,
160+
gas_used: payload.gas_used,
161+
timestamp: payload.timestamp,
162+
extra_data: payload.extra_data.to_vec().into(),
163+
mix_hash: payload.prev_randao,
164+
nonce: B64::ZERO,
165+
base_fee_per_gas: Some(payload.base_fee_per_gas.to()),
166+
withdrawals_root: Some(withdrawals_root),
167+
blob_gas_used: Some(payload.blob_gas_used),
168+
excess_blob_gas: Some(payload.excess_blob_gas),
169+
parent_beacon_block_root: Some(parent_beacon_block_root),
170+
requests_hash: None, // TODO
171+
};
172+
173+
ensure!(
174+
payload.block_hash == header.hash_slow(),
175+
"Block hash mismatch"
176+
);
177+
Ok(header)
178+
}
179+
137180
/// Lookup the epoch accumulator & epoch hash for the given epoch index.
138181
pub async fn lookup_epoch_acc(
139182
epoch_index: u64,

crates/ethportal-api/src/types/consensus/beacon_block.rs

+23
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,29 @@ impl BeaconBlockDeneb {
144144
}
145145
}
146146

147+
impl BeaconBlockElectra {
148+
pub fn build_body_root_proof(&self) -> Vec<B256> {
149+
let leaves = [
150+
self.slot.tree_hash_root(),
151+
self.proposer_index.tree_hash_root(),
152+
self.parent_root.tree_hash_root(),
153+
self.state_root.tree_hash_root(),
154+
self.body.tree_hash_root(),
155+
];
156+
// We want to prove the body root, which is the 5th leaf
157+
build_merkle_proof_for_index(leaves, 4)
158+
}
159+
160+
pub fn build_execution_block_hash_proof(&self) -> Vec<B256> {
161+
[
162+
self.body.execution_payload.build_block_hash_proof(),
163+
self.body.build_execution_payload_proof(),
164+
self.build_body_root_proof(),
165+
]
166+
.concat()
167+
}
168+
}
169+
147170
/// A `BeaconBlock` and a signature from its proposer.
148171
#[superstruct(
149172
variants(Bellatrix, Capella, Deneb, Electra),

crates/ethportal-api/src/types/consensus/body.rs

+22
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,28 @@ impl BeaconBlockBodyDeneb {
168168
}
169169
}
170170

171+
impl BeaconBlockBodyElectra {
172+
pub fn build_execution_payload_proof(&self) -> Vec<B256> {
173+
let leaves = [
174+
self.randao_reveal.tree_hash_root(),
175+
self.eth1_data.tree_hash_root(),
176+
self.graffiti.tree_hash_root(),
177+
self.proposer_slashings.tree_hash_root(),
178+
self.attester_slashings.tree_hash_root(),
179+
self.attestations.tree_hash_root(),
180+
self.deposits.tree_hash_root(),
181+
self.voluntary_exits.tree_hash_root(),
182+
self.sync_aggregate.tree_hash_root(),
183+
self.execution_payload.tree_hash_root(),
184+
self.bls_to_execution_changes.tree_hash_root(),
185+
self.blob_kzg_commitments.tree_hash_root(),
186+
self.execution_requests.tree_hash_root(),
187+
];
188+
// We want to prove the 10th leaf
189+
build_merkle_proof_for_index(leaves, 9)
190+
}
191+
}
192+
171193
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Decode, Encode, TreeHash)]
172194
pub struct SyncAggregate {
173195
pub sync_committee_bits: BitVector<typenum::U512>,

crates/ethportal-api/src/types/consensus/execution_payload.rs

+25
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,31 @@ impl ExecutionPayloadDeneb {
163163
}
164164
}
165165

166+
impl ExecutionPayloadElectra {
167+
pub fn build_block_hash_proof(&self) -> Vec<B256> {
168+
let leaves = [
169+
self.parent_hash.tree_hash_root(),
170+
self.fee_recipient.tree_hash_root(),
171+
self.state_root.tree_hash_root(),
172+
self.receipts_root.tree_hash_root(),
173+
self.logs_bloom.tree_hash_root(),
174+
self.prev_randao.tree_hash_root(),
175+
self.block_number.tree_hash_root(),
176+
self.gas_limit.tree_hash_root(),
177+
self.gas_used.tree_hash_root(),
178+
self.timestamp.tree_hash_root(),
179+
self.extra_data.tree_hash_root(),
180+
self.base_fee_per_gas.tree_hash_root(),
181+
self.block_hash.tree_hash_root(),
182+
self.transactions.tree_hash_root(),
183+
self.withdrawals.tree_hash_root(),
184+
self.blob_gas_used.tree_hash_root(),
185+
self.excess_blob_gas.tree_hash_root(),
186+
];
187+
build_merkle_proof_for_index(leaves, 12)
188+
}
189+
}
190+
166191
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)]
167192
pub struct Withdrawal {
168193
#[serde(deserialize_with = "as_u64")]

crates/ethportal-api/src/types/execution/header_with_proof.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use tree_hash::TreeHash;
99

1010
use crate::{
1111
consensus::{
12-
beacon_block::BeaconBlockDeneb, beacon_state::RootsPerHistoricalRoot,
12+
beacon_block::{BeaconBlockDeneb, BeaconBlockElectra},
13+
beacon_state::RootsPerHistoricalRoot,
1314
constants::SLOTS_PER_HISTORICAL_ROOT,
1415
},
1516
types::{
@@ -200,7 +201,7 @@ pub fn build_historical_roots_proof(
200201
}
201202
}
202203

203-
/// Builds `BlockProofHistoricalSummariesCapella` for a given slot.
204+
/// Builds `BlockProofHistoricalSummariesCapella` for a given slot, from Capella `BeaconBlock`.
204205
///
205206
/// The `block_roots` represents the `block_roots` fields from `BeaconState`.
206207
pub fn build_capella_historical_summaries_proof(
@@ -227,7 +228,7 @@ pub fn build_capella_historical_summaries_proof(
227228
}
228229
}
229230

230-
/// Builds `BlockProofHistoricalSummariesDeneb` for a given slot.
231+
/// Builds `BlockProofHistoricalSummariesDeneb` for a given slot, from Deneb `BeaconBlock`.
231232
///
232233
/// The `block_roots` represents the `block_roots` fields from `BeaconState`.
233234
pub fn build_deneb_historical_summaries_proof(
@@ -254,6 +255,33 @@ pub fn build_deneb_historical_summaries_proof(
254255
}
255256
}
256257

258+
/// Builds `BlockProofHistoricalSummariesDeneb` for a given slot, from Electra `BeaconBlock`.
259+
///
260+
/// The `block_roots` represents the `block_roots` fields from `BeaconState`.
261+
pub fn build_electra_historical_summaries_proof(
262+
slot: u64,
263+
block_roots: &RootsPerHistoricalRoot,
264+
beacon_block: &BeaconBlockElectra,
265+
) -> BlockProofHistoricalSummariesDeneb {
266+
let beacon_block_proof = build_merkle_proof_for_index(
267+
block_roots.clone(),
268+
slot as usize % SLOTS_PER_HISTORICAL_ROOT,
269+
);
270+
let beacon_block_proof = BeaconBlockProofHistoricalSummaries::new(beacon_block_proof)
271+
.expect("error creating BeaconBlockProofHistoricalSummaries");
272+
273+
let execution_block_proof =
274+
ExecutionBlockProofDeneb::new(beacon_block.build_execution_block_hash_proof())
275+
.expect("error creating ExecutionBlockProofDeneb");
276+
277+
BlockProofHistoricalSummariesDeneb {
278+
beacon_block_proof,
279+
beacon_block_root: beacon_block.tree_hash_root(),
280+
execution_block_proof,
281+
slot,
282+
}
283+
}
284+
257285
#[cfg(test)]
258286
#[allow(clippy::unwrap_used)]
259287
mod tests {

0 commit comments

Comments
 (0)