diff --git a/crates/consensus/src/fork.rs b/crates/consensus/src/fork.rs index b8dae27..68a4486 100644 --- a/crates/consensus/src/fork.rs +++ b/crates/consensus/src/fork.rs @@ -17,6 +17,11 @@ pub const GENESIS_SPEC: ForkSpec = ForkSpec { execution_payload_block_number_gindex: 0, }; +pub const ALTAIR_INDEX: usize = 0; +pub const BELLATRIX_INDEX: usize = 1; +pub const CAPELLA_INDEX: usize = 2; +pub const DENEB_INDEX: usize = 3; + /// Fork parameters for the beacon chain #[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ForkParameters { @@ -63,19 +68,32 @@ impl ForkParameters { /// Compute the fork version for the given epoch pub fn compute_fork_version(&self, epoch: Epoch) -> Version { self.compute_fork(epoch) - .map(|f| f.version) + .map(|(_, f)| f.version.clone()) .unwrap_or(self.genesis_version.clone()) } /// Compute the fork spec for the given epoch pub fn compute_fork_spec(&self, epoch: Epoch) -> ForkSpec { self.compute_fork(epoch) - .map(|f| f.spec) + .map(|(_, f)| f.spec.clone()) .unwrap_or(GENESIS_SPEC) } - fn compute_fork(&self, epoch: Epoch) -> Option { - self.forks.iter().rev().find(|f| epoch >= f.epoch).cloned() + /// Returns a boolean indicating whether the given epoch is after the fork + pub fn is_fork(&self, epoch: Epoch, fork_index: usize) -> bool { + if let Some((current, _)) = self.compute_fork(epoch) { + current >= fork_index + } else { + false + } + } + + fn compute_fork(&self, epoch: Epoch) -> Option<(usize, &ForkParameter)> { + self.forks + .iter() + .enumerate() + .rev() + .find(|(_, f)| epoch >= f.epoch) } } @@ -169,6 +187,13 @@ mod tests { assert_eq!(params.compute_fork_version(2.into()), Version([3, 0, 0, 1])); assert_eq!(params.compute_fork_version(3.into()), Version([4, 0, 0, 1])); assert_eq!(params.compute_fork_version(4.into()), Version([4, 0, 0, 1])); + assert!(params.is_fork(0.into(), ALTAIR_INDEX)); + assert!(!params.is_fork(0.into(), BELLATRIX_INDEX)); + assert!(params.is_fork(1.into(), ALTAIR_INDEX)); + assert!(params.is_fork(1.into(), BELLATRIX_INDEX)); + assert!(!params.is_fork(1.into(), CAPELLA_INDEX)); + assert!(params.is_fork(3.into(), DENEB_INDEX)); + assert!(params.is_fork(4.into(), DENEB_INDEX)); let res = ForkParameters::new( Version([0, 0, 0, 1]), diff --git a/crates/light-client-verifier/src/consensus.rs b/crates/light-client-verifier/src/consensus.rs index 55f1204..6e1c342 100644 --- a/crates/light-client-verifier/src/consensus.rs +++ b/crates/light-client-verifier/src/consensus.rs @@ -12,7 +12,7 @@ use ethereum_consensus::compute::{ compute_sync_committee_period_at_slot, hash_tree_root, }; use ethereum_consensus::context::ChainContext; -use ethereum_consensus::fork::ForkSpec; +use ethereum_consensus::fork::{ForkSpec, BELLATRIX_INDEX}; use ethereum_consensus::merkle::is_valid_normalized_merkle_branch; use ethereum_consensus::sync_protocol::SyncCommittee; use ethereum_consensus::types::H256; @@ -249,6 +249,14 @@ pub fn validate_light_client_update< consensus_update: &CU, ) -> Result<(), Error> { consensus_update.validate_basic(ctx)?; + let finalized_epoch = + compute_epoch_at_slot(ctx, consensus_update.finalized_beacon_header().slot); + if !ctx + .fork_parameters() + .is_fork(finalized_epoch, BELLATRIX_INDEX) + { + return Err(Error::ForkNotSupported(finalized_epoch)); + } let current_period = store.current_period(ctx); let signature_period = diff --git a/crates/light-client-verifier/src/errors.rs b/crates/light-client-verifier/src/errors.rs index 219f387..7fa9a6e 100644 --- a/crates/light-client-verifier/src/errors.rs +++ b/crates/light-client-verifier/src/errors.rs @@ -1,7 +1,7 @@ use crate::internal_prelude::*; use displaydoc::Display; use ethereum_consensus::{ - beacon::{BeaconBlockHeader, Root, Slot}, + beacon::{BeaconBlockHeader, Epoch, Root, Slot}, bls::PublicKey, errors::MerkleError, sync_protocol::SyncCommitteePeriod, @@ -13,6 +13,8 @@ type BoxedTrieError = Box>; #[derive(Debug, Display)] pub enum Error { + /// the light client does not support the fork corresponding to the finalized epoch: `epoch={0}` + ForkNotSupported(Epoch), /// unexpected signature period: `store={0} signature={1} reason={2}` UnexpectedSingaturePeriod(SyncCommitteePeriod, SyncCommitteePeriod, String), /// unexpected attested period: `store={0} attested={1} reason={2}`