diff --git a/crates/consensus/src/compute.rs b/crates/consensus/src/compute.rs index 8bbd59a..c748dd1 100644 --- a/crates/consensus/src/compute.rs +++ b/crates/consensus/src/compute.rs @@ -66,7 +66,7 @@ pub fn compute_domain( genesis_validators_root: Option, ) -> Result { let fork_data_root = compute_fork_data_root( - fork_version.unwrap_or(ctx.fork_parameters().genesis_version.clone()), + fork_version.unwrap_or(ctx.fork_parameters().genesis_version().clone()), genesis_validators_root.unwrap_or_default(), )?; let mut domain: [u8; 32] = Default::default(); @@ -96,6 +96,7 @@ mod tests { #[test] fn test_compute_timestamp_at_slot() { let ctx = DefaultChainContext::new_with_config(1729846322.into(), get_minimal_config()); + assert!(ctx.validate().is_ok(), "context is invalid"); assert_eq!(compute_timestamp_at_slot(&ctx, 0.into()), 1729846322.into()); assert_eq!(compute_timestamp_at_slot(&ctx, 1.into()), 1729846328.into()); assert_eq!(compute_timestamp_at_slot(&ctx, 2.into()), 1729846334.into()); diff --git a/crates/consensus/src/config/goerli.rs b/crates/consensus/src/config/goerli.rs index 77a2d5c..4d19bc7 100644 --- a/crates/consensus/src/config/goerli.rs +++ b/crates/consensus/src/config/goerli.rs @@ -13,11 +13,11 @@ pub fn get_config() -> Config { fork_parameters: ForkParameters::new( Version([0, 0, 16, 32]), vec![ - ForkParameter::new(Version([5, 0, 16, 32]), U64(u64::MAX)), - ForkParameter::new(Version([4, 0, 16, 32]), U64(231680)), - ForkParameter::new(Version([3, 0, 16, 32]), U64(162304)), - ForkParameter::new(Version([2, 0, 16, 32]), U64(112260)), ForkParameter::new(Version([1, 0, 16, 32]), U64(36660)), + ForkParameter::new(Version([2, 0, 16, 32]), U64(112260)), + ForkParameter::new(Version([3, 0, 16, 32]), U64(162304)), + ForkParameter::new(Version([4, 0, 16, 32]), U64(231680)), + ForkParameter::new(Version([5, 0, 16, 32]), U64(u64::MAX)), ], ), min_genesis_time: U64(1614588812), diff --git a/crates/consensus/src/config/mainnet.rs b/crates/consensus/src/config/mainnet.rs index fc56c84..7d23d95 100644 --- a/crates/consensus/src/config/mainnet.rs +++ b/crates/consensus/src/config/mainnet.rs @@ -13,11 +13,11 @@ pub fn get_config() -> Config { fork_parameters: ForkParameters::new( Version([0, 0, 0, 0]), vec![ - ForkParameter::new(Version([5, 0, 0, 0]), U64(u64::MAX)), - ForkParameter::new(Version([4, 0, 0, 0]), U64(269568)), - ForkParameter::new(Version([3, 0, 0, 0]), U64(194048)), - ForkParameter::new(Version([2, 0, 0, 0]), U64(144896)), ForkParameter::new(Version([1, 0, 0, 0]), U64(74240)), + ForkParameter::new(Version([2, 0, 0, 0]), U64(144896)), + ForkParameter::new(Version([3, 0, 0, 0]), U64(194048)), + ForkParameter::new(Version([4, 0, 0, 0]), U64(269568)), + ForkParameter::new(Version([5, 0, 0, 0]), U64(u64::MAX)), ], ), min_genesis_time: U64(1606824000), diff --git a/crates/consensus/src/config/minimal.rs b/crates/consensus/src/config/minimal.rs index 6fcc234..054bd84 100644 --- a/crates/consensus/src/config/minimal.rs +++ b/crates/consensus/src/config/minimal.rs @@ -13,14 +13,14 @@ pub fn get_config() -> Config { fork_parameters: ForkParameters::new( Version([0, 0, 0, 1]), vec![ - // deneb - ForkParameter::new(Version([4, 0, 0, 1]), U64(0)), - // capella - ForkParameter::new(Version([3, 0, 0, 1]), U64(0)), - // belatrix - ForkParameter::new(Version([2, 0, 0, 1]), U64(0)), // altair ForkParameter::new(Version([1, 0, 0, 1]), U64(0)), + // belatrix + ForkParameter::new(Version([2, 0, 0, 1]), U64(0)), + // capella + ForkParameter::new(Version([3, 0, 0, 1]), U64(0)), + // deneb + ForkParameter::new(Version([4, 0, 0, 1]), U64(0)), ], ), min_genesis_time: U64(1578009600), diff --git a/crates/consensus/src/config/sepolia.rs b/crates/consensus/src/config/sepolia.rs index 24e3fb0..c419d9f 100644 --- a/crates/consensus/src/config/sepolia.rs +++ b/crates/consensus/src/config/sepolia.rs @@ -13,11 +13,11 @@ pub fn get_config() -> Config { fork_parameters: ForkParameters::new( Version([144, 0, 0, 105]), vec![ - ForkParameter::new(Version([144, 0, 0, 116]), U64(u64::MAX)), - ForkParameter::new(Version([144, 0, 0, 115]), U64(132608)), - ForkParameter::new(Version([144, 0, 0, 114]), U64(56832)), - ForkParameter::new(Version([144, 0, 0, 113]), U64(100)), ForkParameter::new(Version([144, 0, 0, 112]), U64(50)), + ForkParameter::new(Version([144, 0, 0, 113]), U64(100)), + ForkParameter::new(Version([144, 0, 0, 114]), U64(56832)), + ForkParameter::new(Version([144, 0, 0, 115]), U64(132608)), + ForkParameter::new(Version([144, 0, 0, 116]), U64(u64::MAX)), ], ), min_genesis_time: U64(1655647200), diff --git a/crates/consensus/src/context.rs b/crates/consensus/src/context.rs index ad74f94..8439b45 100644 --- a/crates/consensus/src/context.rs +++ b/crates/consensus/src/context.rs @@ -1,6 +1,7 @@ use crate::{ beacon::{Epoch, Slot}, config::Config, + errors::Error, fork::ForkParameters, types::U64, }; @@ -47,6 +48,11 @@ impl DefaultChainContext { config.preset.EPOCHS_PER_SYNC_COMMITTEE_PERIOD, ) } + + pub fn validate(&self) -> Result<(), Error> { + self.fork_parameters.validate()?; + Ok(()) + } } impl ChainContext for DefaultChainContext { diff --git a/crates/consensus/src/errors.rs b/crates/consensus/src/errors.rs index 029432a..542db4d 100644 --- a/crates/consensus/src/errors.rs +++ b/crates/consensus/src/errors.rs @@ -1,4 +1,10 @@ -use crate::{beacon::Root, bls::PublicKey, fork::ForkParameters, internal_prelude::*, types::H256}; +use crate::{ + beacon::{Root, Version}, + bls::PublicKey, + fork::ForkParameters, + internal_prelude::*, + types::{H256, U64}, +}; use displaydoc::Display; #[derive(Debug, Display)] @@ -21,6 +27,10 @@ pub enum Error { InvalidAddressLength(usize, usize), /// invalid fork parameters order: `{0:?}` InvalidForkParamersOrder(ForkParameters), + /// invalid fork version: `epoch={0:?} fork={1:?} index={2}` + UnknownFork(U64, U64, usize), + /// the fork version does not support execution payload: `{0:?}` + NotSupportedExecutionPayload(Version), /// other error: `{description}` Other { description: String }, } diff --git a/crates/consensus/src/fork.rs b/crates/consensus/src/fork.rs index f507b04..94bb183 100644 --- a/crates/consensus/src/fork.rs +++ b/crates/consensus/src/fork.rs @@ -7,10 +7,32 @@ use crate::errors::Error; use crate::internal_prelude::*; use crate::types::U64; +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum Fork { + Genesis(Version), + Altair(ForkParameter), + Bellatrix(ForkParameter), + Capella(ForkParameter), + Deneb(ForkParameter), +} + +impl Fork { + pub fn execution_payload_tree_depth(&self) -> Result { + match self { + Fork::Genesis(v) => Err(Error::NotSupportedExecutionPayload(v.clone())), + Fork::Altair(f) => Err(Error::NotSupportedExecutionPayload(f.version.clone())), + Fork::Bellatrix(_) => Ok(bellatrix::EXECUTION_PAYLOAD_TREE_DEPTH), + Fork::Capella(_) => Ok(capella::EXECUTION_PAYLOAD_TREE_DEPTH), + Fork::Deneb(_) => Ok(deneb::EXECUTION_PAYLOAD_TREE_DEPTH), + } + } +} + #[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ForkParameters { - pub genesis_version: Version, - pub forks: Vec, + genesis_version: Version, + /// Forks in order of increasing epoch + forks: Vec, } impl ForkParameters { @@ -22,7 +44,7 @@ impl ForkParameters { } pub fn validate(&self) -> Result<(), Error> { - if self.forks.windows(2).all(|f| f[0].epoch >= f[1].epoch) { + if self.forks.windows(2).all(|f| f[0].epoch <= f[1].epoch) { Ok(()) } else { Err(Error::InvalidForkParamersOrder(self.clone())) @@ -33,14 +55,34 @@ impl ForkParameters { U64(0) } + pub fn genesis_version(&self) -> &Version { + &self.genesis_version + } + pub fn compute_fork_version(&self, epoch: Epoch) -> Result<&Version, Error> { - for fork in self.forks.iter() { + for fork in self.forks.iter().rev() { if epoch >= fork.epoch { return Ok(&fork.version); } } Ok(&self.genesis_version) } + + pub fn compute_fork(&self, epoch: Epoch) -> Result { + for (i, fork) in self.forks.iter().enumerate().rev() { + if epoch >= fork.epoch { + let fork = fork.clone(); + return Ok(match i { + 0 => Fork::Altair(fork), + 1 => Fork::Bellatrix(fork), + 2 => Fork::Capella(fork), + 3 => Fork::Deneb(fork), + _ => return Err(Error::UnknownFork(epoch, fork.epoch, i)), + }); + } + } + Ok(Fork::Genesis(self.genesis_version.clone())) + } } #[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] diff --git a/crates/consensus/src/fork/bellatrix.rs b/crates/consensus/src/fork/bellatrix.rs index 568fe19..aae7be7 100644 --- a/crates/consensus/src/fork/bellatrix.rs +++ b/crates/consensus/src/fork/bellatrix.rs @@ -495,6 +495,7 @@ mod test { // ensure that signing_root calculation is correct let ctx = DefaultChainContext::new_with_config(0.into(), config::mainnet::get_config()); + assert!(ctx.validate().is_ok(), "context is invalid"); let fork_version = compute_fork_version(&ctx, compute_epoch_at_slot(&ctx, update.signature_slot)).unwrap(); let domain = compute_domain( diff --git a/crates/light-client-cli/src/client.rs b/crates/light-client-cli/src/client.rs index 68e80e6..a940023 100644 --- a/crates/light-client-cli/src/client.rs +++ b/crates/light-client-cli/src/client.rs @@ -13,13 +13,13 @@ use ethereum_consensus::{ BlockNumber, EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, }, - fork::deneb::{self, LightClientUpdate, EXECUTION_PAYLOAD_TREE_DEPTH}, + fork::deneb::{self, LightClientUpdate}, sync_protocol::SyncCommitteePeriod, types::{H256, U64}, }; use ethereum_light_client_verifier::{ consensus::SyncProtocolVerifier, - context::{ConsensusVerificationContext, Fraction, LightClientContext}, + context::{ChainConsensusVerificationContext, Fraction, LightClientContext}, state::should_update_sync_committees, updates::{deneb::ConsensusUpdateInfo, ConsensusUpdate}, }; @@ -45,7 +45,6 @@ pub struct LightClient< chain: Chain, verifier: SyncProtocolVerifier< SYNC_COMMITTEE_SIZE, - EXECUTION_PAYLOAD_TREE_DEPTH, LightClientStore, >, genesis_time: U64, @@ -245,7 +244,7 @@ impl< async fn process_light_client_update( &self, - vctx: &(impl ChainContext + ConsensusVerificationContext), + vctx: &impl ChainConsensusVerificationContext, update: LightClientUpdate, state: &LightClientStore, ) -> Result< @@ -304,7 +303,7 @@ impl< } } - fn build_verification_context(&self) -> impl ChainContext + ConsensusVerificationContext { + fn build_verification_context(&self) -> impl ChainConsensusVerificationContext { LightClientContext::new_with_config( self.ctx.config.clone(), self.genesis_validators_root, diff --git a/crates/light-client-verifier/src/consensus.rs b/crates/light-client-verifier/src/consensus.rs index e65cfda..fd619a2 100644 --- a/crates/light-client-verifier/src/consensus.rs +++ b/crates/light-client-verifier/src/consensus.rs @@ -1,4 +1,4 @@ -use crate::context::ConsensusVerificationContext; +use crate::context::{ChainConsensusVerificationContext, ConsensusVerificationContext}; use crate::errors::Error; use crate::internal_prelude::*; use crate::misbehaviour::Misbehaviour; @@ -15,6 +15,7 @@ use ethereum_consensus::context::ChainContext; use ethereum_consensus::execution::{ EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, }; +use ethereum_consensus::fork::Fork; use ethereum_consensus::merkle::is_valid_merkle_branch; use ethereum_consensus::sync_protocol::{ SyncCommittee, CURRENT_SYNC_COMMITTEE_DEPTH, CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX, @@ -27,15 +28,11 @@ use ethereum_consensus::types::H256; #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct SyncProtocolVerifier< const SYNC_COMMITTEE_SIZE: usize, - const EXECUTION_PAYLOAD_TREE_DEPTH: usize, ST: LightClientStoreReader, >(PhantomData); -impl< - const SYNC_COMMITTEE_SIZE: usize, - const EXECUTION_PAYLOAD_TREE_DEPTH: usize, - ST: LightClientStoreReader, - > SyncProtocolVerifier +impl> + SyncProtocolVerifier { /// validates a LightClientBootstrap pub fn validate_boostrap>( @@ -62,7 +59,7 @@ impl< /// validates consensus update and execution update pub fn validate_updates< - CC: ChainContext + ConsensusVerificationContext, + CC: ChainConsensusVerificationContext, CU: ConsensusUpdate, EU: ExecutionUpdate, >( @@ -78,6 +75,7 @@ impl< self.ensure_relevant_update(ctx, store, consensus_update)?; self.validate_consensus_update(ctx, store, consensus_update)?; self.validate_execution_update( + ctx.compute_fork(consensus_update.finalized_beacon_header().slot)?, consensus_update.finalized_execution_root(), execution_update, )?; @@ -87,7 +85,7 @@ impl< /// validate a consensus update with a committee from the trusted store /// follow the light client protocol in the consensus spec pub fn validate_consensus_update< - CC: ChainContext + ConsensusVerificationContext, + CC: ChainConsensusVerificationContext, CU: ConsensusUpdate, >( &self, @@ -104,13 +102,14 @@ impl< /// validate an execution update with trusted/verified beacon block body pub fn validate_execution_update( &self, + update_fork: Fork, trusted_execution_root: Root, update: &EU, ) -> Result<(), Error> { is_valid_merkle_branch( hash_tree_root(update.state_root()).unwrap().0.into(), &update.state_root_branch(), - EXECUTION_PAYLOAD_TREE_DEPTH as u32, + update_fork.execution_payload_tree_depth()? as u32, EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX as u64, trusted_execution_root, ) @@ -119,7 +118,7 @@ impl< is_valid_merkle_branch( hash_tree_root(update.block_number()).unwrap().0.into(), &update.block_number_branch(), - EXECUTION_PAYLOAD_TREE_DEPTH as u32, + update_fork.execution_payload_tree_depth()? as u32, EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX as u64, trusted_execution_root, ) @@ -131,7 +130,7 @@ impl< /// validates a misbehaviour with the store. /// it returns `Ok` if the misbehaviour is valid pub fn validate_misbehaviour< - CC: ChainContext + ConsensusVerificationContext, + CC: ChainConsensusVerificationContext, CU: ConsensusUpdate, >( &self, @@ -415,7 +414,6 @@ mod tests_bellatrix { beacon::Version, bls::aggreate_public_key, config::{minimal, Config}, - fork::bellatrix::EXECUTION_PAYLOAD_TREE_DEPTH, fork::{ForkParameter, ForkParameters}, preset, types::U64, @@ -428,7 +426,6 @@ mod tests_bellatrix { fn test_bootstrap() { let verifier = SyncProtocolVerifier::< { preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }, - EXECUTION_PAYLOAD_TREE_DEPTH, MockStore<{ preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }>, >::default(); let path = format!("{}/initial_state.json", TEST_DATA_DIR); @@ -467,7 +464,6 @@ mod tests_bellatrix { fn test_verification() { let verifier = SyncProtocolVerifier::< { preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }, - EXECUTION_PAYLOAD_TREE_DEPTH, MockStore<{ preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }>, >::default(); @@ -488,6 +484,7 @@ mod tests_bellatrix { Fraction::new(2, 3), 1729846322.into(), ); + assert!(ctx.validate().is_ok(), "context is invalid"); let updates = [ "light_client_update_period_5.json", @@ -540,8 +537,8 @@ mod tests_bellatrix { fork_parameters: ForkParameters::new( Version([0, 0, 0, 1]), vec![ - ForkParameter::new(Version([2, 0, 0, 1]), U64(0)), ForkParameter::new(Version([1, 0, 0, 1]), U64(0)), + ForkParameter::new(Version([2, 0, 0, 1]), U64(0)), ], ), min_genesis_time: U64(1578009600), diff --git a/crates/light-client-verifier/src/context.rs b/crates/light-client-verifier/src/context.rs index f73c9a5..b8a036e 100644 --- a/crates/light-client-verifier/src/context.rs +++ b/crates/light-client-verifier/src/context.rs @@ -1,9 +1,10 @@ use ethereum_consensus::{ beacon::{Epoch, Root, Slot}, - compute::compute_slot_at_timestamp, + compute::{compute_epoch_at_slot, compute_slot_at_timestamp}, config::Config, context::ChainContext, - fork::ForkParameters, + errors::Error, + fork::{Fork, ForkParameters}, types::U64, }; #[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -36,6 +37,15 @@ pub trait ConsensusVerificationContext { fn signature_threshold(&self) -> Fraction; } +pub trait ChainConsensusVerificationContext: + ChainContext + ConsensusVerificationContext + Sized +{ + fn compute_fork(&self, slot: U64) -> Result { + self.fork_parameters() + .compute_fork(compute_epoch_at_slot(self, slot)) + } +} + pub struct LightClientContext { fork_parameters: ForkParameters, seconds_per_slot: U64, @@ -98,6 +108,11 @@ impl LightClientContext { current_timestamp, ) } + + pub fn validate(&self) -> Result<(), Error> { + self.fork_parameters.validate()?; + Ok(()) + } } impl ConsensusVerificationContext for LightClientContext { @@ -139,3 +154,5 @@ impl ChainContext for LightClientContext { self.epochs_per_sync_committee_period } } + +impl ChainConsensusVerificationContext for LightClientContext {}