From fcab95a71e6629e174b37a991930eac80d1c8ff2 Mon Sep 17 00:00:00 2001 From: Jun Kimura Date: Sun, 27 Oct 2024 18:04:48 +0900 Subject: [PATCH 1/2] remove type parameter `EXECUTION_PAYLOAD_TREE_DEPTH` from `SyncProtocolVerifier` Signed-off-by: Jun Kimura --- crates/consensus/src/compute.rs | 3 +- crates/consensus/src/config/goerli.rs | 19 +++++-- crates/consensus/src/config/mainnet.rs | 19 +++++-- crates/consensus/src/config/minimal.rs | 23 ++++++--- crates/consensus/src/config/sepolia.rs | 19 +++++-- crates/consensus/src/context.rs | 6 +++ crates/consensus/src/errors.rs | 12 ++++- crates/consensus/src/fork.rs | 50 +++++++++++++++++-- crates/consensus/src/fork/bellatrix.rs | 3 +- crates/consensus/src/fork/capella.rs | 2 +- crates/consensus/src/fork/deneb.rs | 2 +- crates/light-client-cli/src/client.rs | 9 ++-- crates/light-client-verifier/src/consensus.rs | 29 +++++------ crates/light-client-verifier/src/context.rs | 21 +++++++- 14 files changed, 167 insertions(+), 50 deletions(-) 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..20df6cd 100644 --- a/crates/consensus/src/config/goerli.rs +++ b/crates/consensus/src/config/goerli.rs @@ -13,13 +13,24 @@ 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), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_config_validation() { + let config = get_config(); + assert!(config.fork_parameters.validate().is_ok()); + } +} diff --git a/crates/consensus/src/config/mainnet.rs b/crates/consensus/src/config/mainnet.rs index fc56c84..454b8a6 100644 --- a/crates/consensus/src/config/mainnet.rs +++ b/crates/consensus/src/config/mainnet.rs @@ -13,13 +13,24 @@ 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), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_config_validation() { + let config = get_config(); + assert!(config.fork_parameters.validate().is_ok()); + } +} diff --git a/crates/consensus/src/config/minimal.rs b/crates/consensus/src/config/minimal.rs index 6fcc234..d1a686a 100644 --- a/crates/consensus/src/config/minimal.rs +++ b/crates/consensus/src/config/minimal.rs @@ -13,16 +13,27 @@ 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), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_config_validation() { + let config = get_config(); + assert!(config.fork_parameters.validate().is_ok()); + } +} diff --git a/crates/consensus/src/config/sepolia.rs b/crates/consensus/src/config/sepolia.rs index 24e3fb0..f485bc6 100644 --- a/crates/consensus/src/config/sepolia.rs +++ b/crates/consensus/src/config/sepolia.rs @@ -13,13 +13,24 @@ 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), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_config_validation() { + let config = get_config(); + assert!(config.fork_parameters.validate().is_ok()); + } +} 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..6a354db 100644 --- a/crates/consensus/src/fork/bellatrix.rs +++ b/crates/consensus/src/fork/bellatrix.rs @@ -366,7 +366,7 @@ pub fn gen_execution_payload_field_proof< } #[cfg(test)] -mod test { +mod tests { use super::{ gen_execution_payload_field_proof, gen_execution_payload_proof, BeaconBlockHeader, }; @@ -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/consensus/src/fork/capella.rs b/crates/consensus/src/fork/capella.rs index cd4a927..6067de2 100644 --- a/crates/consensus/src/fork/capella.rs +++ b/crates/consensus/src/fork/capella.rs @@ -409,7 +409,7 @@ pub fn gen_execution_payload_field_proof< } #[cfg(test)] -mod test { +mod tests { use super::*; use crate::beacon::BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX; use crate::merkle::is_valid_merkle_branch; diff --git a/crates/consensus/src/fork/deneb.rs b/crates/consensus/src/fork/deneb.rs index 61b4bde..78f85a3 100644 --- a/crates/consensus/src/fork/deneb.rs +++ b/crates/consensus/src/fork/deneb.rs @@ -440,7 +440,7 @@ pub fn gen_execution_payload_field_proof< } #[cfg(test)] -mod test { +mod tests { use super::*; use crate::beacon::BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX; use crate::merkle::is_valid_merkle_branch; 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 {} From c027e7d8839347ce3e24c282f6aefbb9e96e2212 Mon Sep 17 00:00:00 2001 From: Jun Kimura Date: Sun, 27 Oct 2024 18:26:13 +0900 Subject: [PATCH 2/2] fix to validate fork parameters in `ForkParameters::new()` Signed-off-by: Jun Kimura --- crates/consensus/src/compute.rs | 3 +- crates/consensus/src/config/goerli.rs | 6 +- crates/consensus/src/config/mainnet.rs | 6 +- crates/consensus/src/config/minimal.rs | 6 +- crates/consensus/src/config/sepolia.rs | 6 +- crates/consensus/src/context.rs | 6 - crates/consensus/src/fork.rs | 121 +++++++++++++++++- crates/consensus/src/fork/bellatrix.rs | 1 - crates/light-client-verifier/src/consensus.rs | 4 +- crates/light-client-verifier/src/context.rs | 5 - 10 files changed, 131 insertions(+), 33 deletions(-) diff --git a/crates/consensus/src/compute.rs b/crates/consensus/src/compute.rs index c748dd1..85b199e 100644 --- a/crates/consensus/src/compute.rs +++ b/crates/consensus/src/compute.rs @@ -96,7 +96,6 @@ 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()); @@ -109,7 +108,7 @@ mod tests { fn get_minimal_config() -> Config { Config { preset: preset::minimal::PRESET, - fork_parameters: ForkParameters::new(Version([0, 0, 0, 1]), vec![]), + fork_parameters: ForkParameters::new(Version([0, 0, 0, 1]), vec![]).unwrap(), min_genesis_time: U64(1578009600), } } diff --git a/crates/consensus/src/config/goerli.rs b/crates/consensus/src/config/goerli.rs index 20df6cd..47e18d4 100644 --- a/crates/consensus/src/config/goerli.rs +++ b/crates/consensus/src/config/goerli.rs @@ -19,7 +19,8 @@ pub fn get_config() -> Config { ForkParameter::new(Version([4, 0, 16, 32]), U64(231680)), ForkParameter::new(Version([5, 0, 16, 32]), U64(u64::MAX)), ], - ), + ) + .unwrap(), min_genesis_time: U64(1614588812), } } @@ -30,7 +31,6 @@ mod tests { #[test] fn test_config_validation() { - let config = get_config(); - assert!(config.fork_parameters.validate().is_ok()); + let _ = get_config(); } } diff --git a/crates/consensus/src/config/mainnet.rs b/crates/consensus/src/config/mainnet.rs index 454b8a6..e487d1c 100644 --- a/crates/consensus/src/config/mainnet.rs +++ b/crates/consensus/src/config/mainnet.rs @@ -19,7 +19,8 @@ pub fn get_config() -> Config { ForkParameter::new(Version([4, 0, 0, 0]), U64(269568)), ForkParameter::new(Version([5, 0, 0, 0]), U64(u64::MAX)), ], - ), + ) + .unwrap(), min_genesis_time: U64(1606824000), } } @@ -30,7 +31,6 @@ mod tests { #[test] fn test_config_validation() { - let config = get_config(); - assert!(config.fork_parameters.validate().is_ok()); + let _ = get_config(); } } diff --git a/crates/consensus/src/config/minimal.rs b/crates/consensus/src/config/minimal.rs index d1a686a..900b2db 100644 --- a/crates/consensus/src/config/minimal.rs +++ b/crates/consensus/src/config/minimal.rs @@ -22,7 +22,8 @@ pub fn get_config() -> Config { // deneb ForkParameter::new(Version([4, 0, 0, 1]), U64(0)), ], - ), + ) + .unwrap(), min_genesis_time: U64(1578009600), } } @@ -33,7 +34,6 @@ mod tests { #[test] fn test_config_validation() { - let config = get_config(); - assert!(config.fork_parameters.validate().is_ok()); + let _ = get_config(); } } diff --git a/crates/consensus/src/config/sepolia.rs b/crates/consensus/src/config/sepolia.rs index f485bc6..8ead79f 100644 --- a/crates/consensus/src/config/sepolia.rs +++ b/crates/consensus/src/config/sepolia.rs @@ -19,7 +19,8 @@ pub fn get_config() -> Config { ForkParameter::new(Version([144, 0, 0, 115]), U64(132608)), ForkParameter::new(Version([144, 0, 0, 116]), U64(u64::MAX)), ], - ), + ) + .unwrap(), min_genesis_time: U64(1655647200), } } @@ -30,7 +31,6 @@ mod tests { #[test] fn test_config_validation() { - let config = get_config(); - assert!(config.fork_parameters.validate().is_ok()); + let _ = get_config(); } } diff --git a/crates/consensus/src/context.rs b/crates/consensus/src/context.rs index 8439b45..ad74f94 100644 --- a/crates/consensus/src/context.rs +++ b/crates/consensus/src/context.rs @@ -1,7 +1,6 @@ use crate::{ beacon::{Epoch, Slot}, config::Config, - errors::Error, fork::ForkParameters, types::U64, }; @@ -48,11 +47,6 @@ 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/fork.rs b/crates/consensus/src/fork.rs index 94bb183..95304df 100644 --- a/crates/consensus/src/fork.rs +++ b/crates/consensus/src/fork.rs @@ -28,22 +28,27 @@ impl Fork { } } +/// Fork parameters for the beacon chain #[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ForkParameters { genesis_version: Version, - /// Forks in order of increasing epoch + /// Forks in order of ascending epoch + /// The first element is the first fork after genesis + /// i.e., [Altair, Bellatrix, Capella, Deneb, ...] forks: Vec, } impl ForkParameters { - pub const fn new(genesis_version: Version, forks: Vec) -> Self { - Self { + pub fn new(genesis_version: Version, forks: Vec) -> Result { + let this = Self { genesis_version, forks, - } + }; + this.validate()?; + Ok(this) } - pub fn validate(&self) -> Result<(), Error> { + fn validate(&self) -> Result<(), Error> { if self.forks.windows(2).all(|f| f[0].epoch <= f[1].epoch) { Ok(()) } else { @@ -59,6 +64,11 @@ impl ForkParameters { &self.genesis_version } + pub fn forks(&self) -> &[ForkParameter] { + &self.forks + } + + /// Compute the fork version for the given epoch pub fn compute_fork_version(&self, epoch: Epoch) -> Result<&Version, Error> { for fork in self.forks.iter().rev() { if epoch >= fork.epoch { @@ -68,6 +78,9 @@ impl ForkParameters { Ok(&self.genesis_version) } + /// Compute the fork for the given epoch + /// + /// If `forks` does not contain a fork for the given epoch, it returns an error. pub fn compute_fork(&self, epoch: Epoch) -> Result { for (i, fork) in self.forks.iter().enumerate().rev() { if epoch >= fork.epoch { @@ -85,6 +98,8 @@ impl ForkParameters { } } +/// Fork parameters for each fork +/// In the mainnet, you can find the parameters here: https://github.com/ethereum/consensus-specs/blob/9849fb39e75e6228ebd610ef0ad22f5b41543cd5/configs/mainnet.yaml#L35 #[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ForkParameter { pub version: Version, @@ -96,3 +111,99 @@ impl ForkParameter { Self { version, epoch } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_fork_parameters() { + let res = ForkParameters::new( + Version([0, 0, 0, 1]), + vec![ + ForkParameter::new(Version([1, 0, 0, 1]), U64(0)), + ForkParameter::new(Version([2, 0, 0, 1]), U64(0)), + ForkParameter::new(Version([3, 0, 0, 1]), U64(0)), + ForkParameter::new(Version([4, 0, 0, 1]), U64(0)), + ], + ); + assert!(res.is_ok()); + let params = res.unwrap(); + let res = params.compute_fork(0.into()); + assert!(res.is_ok()); + assert_eq!( + res.unwrap(), + Fork::Deneb(ForkParameter::new(Version([4, 0, 0, 1]), U64(0))) + ); + + let res = ForkParameters::new(Version([0, 0, 0, 1]), vec![]); + assert!(res.is_ok()); + let params = res.unwrap(); + let res = params.compute_fork(0.into()); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Fork::Genesis(Version([0, 0, 0, 1]))); + + let res = ForkParameters::new( + Version([0, 0, 0, 1]), + vec![ForkParameter::new(Version([1, 0, 0, 1]), U64(0))], + ); + let params = res.unwrap(); + let res = params.compute_fork(0.into()); + assert!(res.is_ok()); + assert_eq!( + res.unwrap(), + Fork::Altair(ForkParameter::new(Version([1, 0, 0, 1]), U64(0))) + ); + + let res = ForkParameters::new( + Version([0, 0, 0, 1]), + vec![ + ForkParameter::new(Version([1, 0, 0, 1]), U64(0)), + ForkParameter::new(Version([2, 0, 0, 1]), U64(1)), + ForkParameter::new(Version([3, 0, 0, 1]), U64(2)), + ForkParameter::new(Version([4, 0, 0, 1]), U64(3)), + ], + ); + assert!(res.is_ok()); + let params = res.unwrap(); + let res = params.compute_fork(0.into()); + assert!(res.is_ok()); + assert_eq!( + res.unwrap(), + Fork::Altair(ForkParameter::new(Version([1, 0, 0, 1]), U64(0))) + ); + let res = params.compute_fork(1.into()); + assert!(res.is_ok()); + assert_eq!( + res.unwrap(), + Fork::Bellatrix(ForkParameter::new(Version([2, 0, 0, 1]), U64(1))) + ); + let res = params.compute_fork(2.into()); + assert!(res.is_ok()); + assert_eq!( + res.unwrap(), + Fork::Capella(ForkParameter::new(Version([3, 0, 0, 1]), U64(2))) + ); + let res = params.compute_fork(3.into()); + assert!(res.is_ok()); + assert_eq!( + res.unwrap(), + Fork::Deneb(ForkParameter::new(Version([4, 0, 0, 1]), U64(3))) + ); + let res = params.compute_fork(4.into()); + assert!(res.is_ok()); + assert_eq!( + res.unwrap(), + Fork::Deneb(ForkParameter::new(Version([4, 0, 0, 1]), U64(3))) + ); + + let res = ForkParameters::new( + Version([0, 0, 0, 1]), + vec![ + ForkParameter::new(Version([2, 0, 0, 1]), U64(1)), + ForkParameter::new(Version([1, 0, 0, 1]), U64(0)), + ], + ); + assert!(res.is_err()); + } +} diff --git a/crates/consensus/src/fork/bellatrix.rs b/crates/consensus/src/fork/bellatrix.rs index 6a354db..685eef2 100644 --- a/crates/consensus/src/fork/bellatrix.rs +++ b/crates/consensus/src/fork/bellatrix.rs @@ -495,7 +495,6 @@ mod tests { // 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-verifier/src/consensus.rs b/crates/light-client-verifier/src/consensus.rs index fd619a2..49d77ed 100644 --- a/crates/light-client-verifier/src/consensus.rs +++ b/crates/light-client-verifier/src/consensus.rs @@ -484,7 +484,6 @@ 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,7 +539,8 @@ mod tests_bellatrix { ForkParameter::new(Version([1, 0, 0, 1]), U64(0)), ForkParameter::new(Version([2, 0, 0, 1]), U64(0)), ], - ), + ) + .unwrap(), min_genesis_time: U64(1578009600), } } diff --git a/crates/light-client-verifier/src/context.rs b/crates/light-client-verifier/src/context.rs index b8a036e..e38ad3a 100644 --- a/crates/light-client-verifier/src/context.rs +++ b/crates/light-client-verifier/src/context.rs @@ -108,11 +108,6 @@ impl LightClientContext { current_timestamp, ) } - - pub fn validate(&self) -> Result<(), Error> { - self.fork_parameters.validate()?; - Ok(()) - } } impl ConsensusVerificationContext for LightClientContext {