Skip to content

Commit 6ce6ad7

Browse files
committed
feat: update beacon content types to support Pectra
1 parent 01a3ed0 commit 6ce6ad7

File tree

9 files changed

+337
-124
lines changed

9 files changed

+337
-124
lines changed

bin/portal-bridge/src/bridge/beacon.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use alloy::primitives::B256;
88
use anyhow::{bail, ensure};
99
use ethportal_api::{
1010
consensus::historical_summaries::{
11-
HistoricalSummariesStateProof, HistoricalSummariesWithProof,
11+
HistoricalSummariesProofDeneb, HistoricalSummariesWithProofDeneb,
1212
},
1313
light_client::update::LightClientUpdate,
1414
types::{
@@ -478,14 +478,12 @@ impl BeaconBridge {
478478
"Historical summaries proof length is not 5"
479479
);
480480
let historical_summaries = beacon_state.historical_summaries;
481-
let historical_summaries_with_proof = ForkVersionedHistoricalSummariesWithProof {
482-
fork_name: ForkName::Deneb,
483-
historical_summaries_with_proof: HistoricalSummariesWithProof {
481+
let historical_summaries_with_proof =
482+
ForkVersionedHistoricalSummariesWithProof::Deneb(HistoricalSummariesWithProofDeneb {
484483
epoch: state_epoch,
485484
historical_summaries,
486-
proof: HistoricalSummariesStateProof::from(historical_summaries_proof),
487-
},
488-
};
485+
proof: HistoricalSummariesProofDeneb::from(historical_summaries_proof),
486+
});
489487
info!(
490488
epoch = %state_epoch,
491489
"Generated HistoricalSummariesWithProof",

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

+46
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,52 @@ impl BeaconStateDeneb {
240240
}
241241
}
242242

243+
impl BeaconStateElectra {
244+
pub fn build_historical_summaries_proof(&self) -> Vec<B256> {
245+
let leaves = [
246+
self.genesis_time.tree_hash_root(),
247+
self.genesis_validators_root.tree_hash_root(),
248+
self.slot.tree_hash_root(),
249+
self.fork.tree_hash_root(),
250+
self.latest_block_header.tree_hash_root(),
251+
self.block_roots.tree_hash_root(),
252+
self.state_roots.tree_hash_root(),
253+
self.historical_roots.tree_hash_root(),
254+
self.eth1_data.tree_hash_root(),
255+
self.eth1_data_votes.tree_hash_root(),
256+
self.eth1_deposit_index.tree_hash_root(),
257+
self.validators.tree_hash_root(),
258+
self.balances.tree_hash_root(),
259+
self.randao_mixes.tree_hash_root(),
260+
self.slashings.tree_hash_root(),
261+
self.previous_epoch_participation.tree_hash_root(),
262+
self.current_epoch_participation.tree_hash_root(),
263+
self.justification_bits.tree_hash_root(),
264+
self.previous_justified_checkpoint.tree_hash_root(),
265+
self.current_justified_checkpoint.tree_hash_root(),
266+
self.finalized_checkpoint.tree_hash_root(),
267+
self.inactivity_scores.tree_hash_root(),
268+
self.current_sync_committee.tree_hash_root(),
269+
self.next_sync_committee.tree_hash_root(),
270+
self.latest_execution_payload_header.tree_hash_root(),
271+
self.next_withdrawal_index.tree_hash_root(),
272+
self.next_withdrawal_validator_index.tree_hash_root(),
273+
self.historical_summaries.tree_hash_root(),
274+
self.deposit_requests_start_index.tree_hash_root(),
275+
self.deposit_balance_to_consume.tree_hash_root(),
276+
self.exit_balance_to_consume.tree_hash_root(),
277+
self.earliest_exit_epoch.tree_hash_root(),
278+
self.consolidation_balance_to_consume.tree_hash_root(),
279+
self.earliest_consolidation_epoch.tree_hash_root(),
280+
self.pending_deposits.tree_hash_root(),
281+
self.pending_partial_withdrawals.tree_hash_root(),
282+
self.pending_consolidations.tree_hash_root(),
283+
];
284+
285+
build_merkle_proof_for_index(leaves, 27)
286+
}
287+
}
288+
243289
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
244290
///
245291
/// Spec v0.12.1

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

+16-3
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,25 @@ pub struct HistoricalSummary {
1616
}
1717

1818
pub type HistoricalSummaries = VariableList<HistoricalSummary, typenum::U16777216>;
19-
pub type HistoricalSummariesStateProof = FixedVector<B256, typenum::U5>;
19+
20+
pub const HISTORICAL_SUMMARIES_GINDEX_DENEB: usize = 59;
21+
pub const HISTORICAL_SUMMARIES_GINDEX_ELECTRA: usize = 91;
22+
23+
pub type HistoricalSummariesProofDeneb = FixedVector<B256, typenum::U5>;
24+
pub type HistoricalSummariesProofElectra = FixedVector<B256, typenum::U6>;
25+
26+
/// A historical summaries BeaconState field with proof.
27+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode)]
28+
pub struct HistoricalSummariesWithProofDeneb {
29+
pub epoch: u64,
30+
pub historical_summaries: HistoricalSummaries,
31+
pub proof: HistoricalSummariesProofDeneb,
32+
}
2033

2134
/// A historical summaries BeaconState field with proof.
2235
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode)]
23-
pub struct HistoricalSummariesWithProof {
36+
pub struct HistoricalSummariesWithProofElectra {
2437
pub epoch: u64,
2538
pub historical_summaries: HistoricalSummaries,
26-
pub proof: HistoricalSummariesStateProof,
39+
pub proof: HistoricalSummariesProofElectra,
2740
}

crates/ethportal-api/src/types/content_value/beacon.rs

+99-35
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ssz::{Decode, DecodeError, Encode};
55
use ssz_types::{typenum::U128, VariableList};
66

77
use crate::{
8+
consensus::historical_summaries::{HistoricalSummaries, HistoricalSummariesWithProofElectra},
89
light_client::{
910
bootstrap::{LightClientBootstrapDeneb, LightClientBootstrapElectra},
1011
finality_update::{LightClientFinalityUpdateDeneb, LightClientFinalityUpdateElectra},
@@ -14,7 +15,7 @@ use crate::{
1415
types::{
1516
consensus::{
1617
fork::{ForkDigest, ForkName},
17-
historical_summaries::HistoricalSummariesWithProof,
18+
historical_summaries::HistoricalSummariesWithProofDeneb,
1819
light_client::{
1920
bootstrap::{
2021
LightClientBootstrap, LightClientBootstrapBellatrix,
@@ -477,57 +478,116 @@ impl Encode for ForkVersionedLightClientFinalityUpdate {
477478

478479
/// A wrapper type including a `ForkName` and `HistoricalSummariesWithProof`
479480
#[derive(Clone, Debug, PartialEq)]
480-
pub struct ForkVersionedHistoricalSummariesWithProof {
481-
pub fork_name: ForkName,
482-
pub historical_summaries_with_proof: HistoricalSummariesWithProof,
481+
pub enum ForkVersionedHistoricalSummariesWithProof {
482+
Deneb(HistoricalSummariesWithProofDeneb),
483+
Electra(HistoricalSummariesWithProofElectra),
483484
}
484485

485486
impl ForkVersionedHistoricalSummariesWithProof {
486-
pub fn encode(&self) -> Vec<u8> {
487-
let fork_digest = self.fork_name.as_fork_digest();
488-
489-
let mut data = fork_digest.to_vec();
490-
data.extend(self.historical_summaries_with_proof.as_ssz_bytes());
491-
data
487+
pub fn epoch(&self) -> u64 {
488+
match self {
489+
Self::Deneb(historical_summaries_with_proof) => historical_summaries_with_proof.epoch,
490+
Self::Electra(historical_summaries_with_proof) => historical_summaries_with_proof.epoch,
491+
}
492492
}
493493

494-
pub fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
495-
let fork_digest = ForkDigest::try_from(&buf[0..4]).map_err(|err| {
496-
DecodeError::BytesInvalid(format!("Unable to decode fork digest: {err:?}"))
497-
})?;
498-
let fork_name = ForkName::try_from(fork_digest).map_err(|_| {
499-
DecodeError::BytesInvalid(format!("Unable to decode fork name: {fork_digest:?}"))
500-
})?;
501-
let summaries_with_proof = HistoricalSummariesWithProof::from_ssz_bytes(&buf[4..])?;
494+
pub fn fork_name(&self) -> ForkName {
495+
match self {
496+
Self::Deneb(_) => ForkName::Deneb,
497+
Self::Electra(_) => ForkName::Electra,
498+
}
499+
}
500+
}
502501

503-
Ok(Self {
504-
fork_name,
505-
historical_summaries_with_proof: summaries_with_proof,
506-
})
502+
impl From<ForkVersionedHistoricalSummariesWithProof> for HistoricalSummaries {
503+
fn from(historical_summaries_with_proof: ForkVersionedHistoricalSummariesWithProof) -> Self {
504+
match historical_summaries_with_proof {
505+
ForkVersionedHistoricalSummariesWithProof::Deneb(historical_summaries_with_proof) => {
506+
historical_summaries_with_proof.historical_summaries
507+
}
508+
ForkVersionedHistoricalSummariesWithProof::Electra(historical_summaries_with_proof) => {
509+
historical_summaries_with_proof.historical_summaries
510+
}
511+
}
507512
}
508513
}
509514

510-
impl Decode for ForkVersionedHistoricalSummariesWithProof {
515+
impl Encode for ForkVersionedHistoricalSummariesWithProof {
511516
fn is_ssz_fixed_len() -> bool {
512517
false
513518
}
514519

515-
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
516-
Self::decode(bytes)
520+
fn ssz_append(&self, buf: &mut Vec<u8>) {
521+
self.fork_name().as_fork_digest().ssz_append(buf);
522+
match self {
523+
Self::Deneb(historical_summaries_with_proof) => {
524+
historical_summaries_with_proof.ssz_append(buf)
525+
}
526+
Self::Electra(historical_summaries_with_proof) => {
527+
historical_summaries_with_proof.ssz_append(buf)
528+
}
529+
}
530+
}
531+
532+
fn ssz_fixed_len() -> usize {
533+
ssz::BYTES_PER_LENGTH_OFFSET
534+
}
535+
536+
fn ssz_bytes_len(&self) -> usize {
537+
let fork_digest_len = self.fork_name().as_fork_digest().ssz_bytes_len();
538+
let historical_summaries_with_proof_len = match self {
539+
Self::Deneb(historical_summaries_with_proof) => {
540+
historical_summaries_with_proof.ssz_bytes_len()
541+
}
542+
Self::Electra(historical_summaries_with_proof) => {
543+
historical_summaries_with_proof.ssz_bytes_len()
544+
}
545+
};
546+
fork_digest_len + historical_summaries_with_proof_len
517547
}
518548
}
519549

520-
impl Encode for ForkVersionedHistoricalSummariesWithProof {
550+
impl Decode for ForkVersionedHistoricalSummariesWithProof {
521551
fn is_ssz_fixed_len() -> bool {
522552
false
523553
}
524554

525-
fn ssz_append(&self, buf: &mut Vec<u8>) {
526-
buf.extend_from_slice(&self.encode());
555+
fn ssz_fixed_len() -> usize {
556+
ssz::BYTES_PER_LENGTH_OFFSET
527557
}
528558

529-
fn ssz_bytes_len(&self) -> usize {
530-
self.encode().len()
559+
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
560+
let fork_digest_len = <ForkDigest as Decode>::ssz_fixed_len();
561+
let Some((fork_digest_bytes, historical_summaries_with_proof_bytes)) =
562+
bytes.split_at_checked(fork_digest_len)
563+
else {
564+
return Err(DecodeError::InvalidByteLength {
565+
len: bytes.len(),
566+
expected: fork_digest_len,
567+
});
568+
};
569+
570+
let fork_digest = ForkDigest::from_ssz_bytes(fork_digest_bytes)?;
571+
let fork_name = match ForkName::try_from(fork_digest) {
572+
Ok(fork_name) => fork_name,
573+
Err(err) => return Err(DecodeError::BytesInvalid(err.to_string())),
574+
};
575+
576+
match fork_name {
577+
ForkName::Bellatrix | ForkName::Capella => Err(DecodeError::BytesInvalid(format!(
578+
"HistoricalSummariesWithProof not supported for fork digest: {fork_digest:?}"
579+
))),
580+
ForkName::Deneb => Ok(Self::Deneb(
581+
HistoricalSummariesWithProofDeneb::from_ssz_bytes(
582+
historical_summaries_with_proof_bytes,
583+
)?,
584+
)),
585+
ForkName::Electra => Ok(Self::Electra(
586+
HistoricalSummariesWithProofElectra::from_ssz_bytes(
587+
historical_summaries_with_proof_bytes,
588+
)?,
589+
)),
590+
}
531591
}
532592
}
533593

@@ -710,11 +770,15 @@ mod test {
710770

711771
match &beacon_content {
712772
BeaconContentValue::HistoricalSummariesWithProof(content) => {
713-
assert_eq!(
714-
expected_epoch,
715-
content.historical_summaries_with_proof.epoch
716-
);
717-
assert_eq!(ForkName::Deneb, content.fork_name);
773+
assert_eq!(ForkName::Deneb, content.fork_name());
774+
775+
let ForkVersionedHistoricalSummariesWithProof::Deneb(
776+
historical_summaries_with_proof,
777+
) = content
778+
else {
779+
panic!("Expected Deneb historical summaries, actual: {content:?}");
780+
};
781+
assert_eq!(expected_epoch, historical_summaries_with_proof.epoch);
718782
}
719783
_ => panic!("Invalid beacon content type!"),
720784
}

crates/subnetworks/beacon/src/storage.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -717,11 +717,14 @@ fn prune_old_bootstrap_data(
717717
#[allow(clippy::unwrap_used)]
718718
mod test {
719719
use ethportal_api::{
720-
types::content_key::beacon::{
721-
HistoricalSummariesWithProofKey, LightClientFinalityUpdateKey,
722-
LightClientOptimisticUpdateKey,
720+
types::{
721+
content_key::beacon::{
722+
HistoricalSummariesWithProofKey, LightClientFinalityUpdateKey,
723+
LightClientOptimisticUpdateKey,
724+
},
725+
content_value::beacon::ForkVersionedHistoricalSummariesWithProof,
723726
},
724-
LightClientBootstrapKey, LightClientUpdatesByRangeKey,
727+
BeaconContentValue, ContentValue, LightClientBootstrapKey, LightClientUpdatesByRangeKey,
725728
};
726729
use tree_hash::TreeHash;
727730
use trin_storage::test_utils::create_test_portal_storage_config_with_capacity;
@@ -872,14 +875,18 @@ mod test {
872875
fn test_beacon_storage_get_put_historical_summaries() {
873876
let (_temp_dir, config) = create_test_portal_storage_config_with_capacity(10).unwrap();
874877
let mut storage = BeaconStorage::new(config).unwrap();
875-
let (value, _) = test_utils::get_history_summaries_with_proof();
876-
let epoch = value.historical_summaries_with_proof.epoch;
878+
let (historical_summaries_with_proof, _) =
879+
test_utils::get_deneb_historical_summaries_with_proof();
880+
let epoch = historical_summaries_with_proof.epoch;
881+
let value = BeaconContentValue::HistoricalSummariesWithProof(
882+
ForkVersionedHistoricalSummariesWithProof::Deneb(historical_summaries_with_proof),
883+
);
877884
let key = BeaconContentKey::HistoricalSummariesWithProof(HistoricalSummariesWithProofKey {
878885
epoch,
879886
});
880-
storage.put(key.clone(), value.as_ssz_bytes()).unwrap();
887+
storage.put(key.clone(), value.encode()).unwrap();
881888
let result = storage.get(&key).unwrap().unwrap();
882-
assert_eq!(result, value.as_ssz_bytes());
889+
assert_eq!(result, value.encode());
883890

884891
// Test is_key_within_radius_and_unavailable for the same epoch
885892
let should_store_content = storage.is_key_within_radius_and_unavailable(&key).unwrap();
@@ -911,7 +918,7 @@ mod test {
911918
epoch: 0,
912919
});
913920
let result = storage.get(&key).unwrap().unwrap();
914-
assert_eq!(result, value.as_ssz_bytes());
921+
assert_eq!(result, value.encode());
915922
}
916923

917924
#[test]

0 commit comments

Comments
 (0)