diff --git a/Cargo.lock b/Cargo.lock index 85c89528d0c..35220238912 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8022,13 +8022,14 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "superstruct" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4e1f478a7728f8855d7e620e9a152cf8932c6614f86564c886f9b8141f3201" +source = "git+https://github.com/sigp/superstruct?branch=features#822403d4d415c181aa7f4765e118606c6772dc58" dependencies = [ "darling", "itertools", "proc-macro2", "quote", + "serde", + "serde_json", "smallvec", "syn 1.0.109", ] diff --git a/Cargo.toml b/Cargo.toml index be2011ba286..31ef0ca8018 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -162,7 +162,7 @@ smallvec = "1.11.2" snap = "1" ssz_types = "0.6" strum = { version = "0.24", features = ["derive"] } -superstruct = "0.7" +superstruct = { git = "https://github.com/sigp/superstruct", branch = "features" } syn = "1" sysinfo = "0.26" tempfile = "3" diff --git a/beacon_node/beacon_chain/src/attestation_rewards.rs b/beacon_node/beacon_chain/src/attestation_rewards.rs index d48a83130e6..ce8530c2ce1 100644 --- a/beacon_node/beacon_chain/src/attestation_rewards.rs +++ b/beacon_node/beacon_chain/src/attestation_rewards.rs @@ -30,7 +30,7 @@ use store::consts::altair::{ TIMELY_TARGET_FLAG_INDEX, }; use types::consts::altair::WEIGHT_DENOMINATOR; -use types::{BeaconState, Epoch, EthSpec, RelativeEpoch}; +use types::{BeaconState, Epoch, EthSpec, FeatureName, RelativeEpoch}; impl BeaconChain { pub fn compute_attestation_rewards( @@ -51,13 +51,10 @@ impl BeaconChain { .get_state(&state_root, Some(state_slot))? .ok_or(BeaconChainError::MissingBeaconState(state_root))?; - match state { - BeaconState::Base(_) => self.compute_attestation_rewards_base(state, validators), - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => self.compute_attestation_rewards_altair(state, validators), + if state.has_feature(FeatureName::Altair) { + self.compute_attestation_rewards_altair(state, validators) + } else { + self.compute_attestation_rewards_base(state, validators) } } diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 8eeb75fd7d7..14609ccb66a 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -733,6 +733,21 @@ impl BeaconChain { .map(|slot| slot.epoch(T::EthSpec::slots_per_epoch())) } + /// Returns the latest fork for the current slot. + pub fn current_fork(&self) -> Result { + Ok(self.spec.fork_name_at_slot::(self.slot()?)) + } + + /// Checks if a feature is enabled on the current fork. + pub fn has_feature(&self, feature: FeatureName) -> bool { + if let Ok(current_fork) = self.current_fork() { + current_fork.has_feature(feature) + } else { + // TODO(superstruct_features): Is this safe? + false + } + } + /// Iterates across all `(block_root, slot)` pairs from `start_slot` /// to the head of the chain (inclusive). /// @@ -2523,7 +2538,7 @@ impl BeaconChain { bls_to_execution_change: SignedBlsToExecutionChange, ) -> Result, Error> { // Ignore BLS to execution changes on gossip prior to Capella. - if !self.current_slot_is_post_capella()? { + if !self.has_feature(FeatureName::Capella) { return Err(Error::BlsToExecutionPriorToCapella); } self.verify_bls_to_execution_change_for_http_api(bls_to_execution_change) @@ -2536,16 +2551,6 @@ impl BeaconChain { }) } - /// Check if the current slot is greater than or equal to the Capella fork epoch. - pub fn current_slot_is_post_capella(&self) -> Result { - let current_fork = self.spec.fork_name_at_slot::(self.slot()?); - if let ForkName::Base | ForkName::Altair | ForkName::Bellatrix = current_fork { - Ok(false) - } else { - Ok(true) - } - } - /// Import a BLS to execution change to the op pool. /// /// Return `true` if the change was added to the pool. @@ -4833,23 +4838,19 @@ impl BeaconChain { // If required, start the process of loading an execution payload from the EL early. This // allows it to run concurrently with things like attestation packing. - let prepare_payload_handle = match &state { - BeaconState::Base(_) | BeaconState::Altair(_) => None, - BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => { - let prepare_payload_handle = get_execution_payload( - self.clone(), - &state, - parent_root, - proposer_index, - builder_params, - builder_boost_factor, - block_production_version, - )?; - Some(prepare_payload_handle) - } + let prepare_payload_handle = if state.has_feature(FeatureName::Bellatrix) { + let prepare_payload_handle = get_execution_payload( + self.clone(), + &state, + parent_root, + proposer_index, + builder_params, + builder_boost_factor, + block_production_version, + )?; + Some(prepare_payload_handle) + } else { + None }; let (mut proposer_slashings, mut attester_slashings, mut voluntary_exits) = @@ -5552,26 +5553,22 @@ impl BeaconChain { payload_attributes } else { let prepare_slot_fork = self.spec.fork_name_at_slot::(prepare_slot); - let withdrawals = match prepare_slot_fork { - ForkName::Base | ForkName::Altair | ForkName::Bellatrix => None, - ForkName::Capella | ForkName::Deneb | ForkName::Electra => { - let chain = self.clone(); - self.spawn_blocking_handle( - move || { - chain.get_expected_withdrawals(&forkchoice_update_params, prepare_slot) - }, - "prepare_beacon_proposer_withdrawals", - ) - .await? - .map(Some)? - } + let withdrawals = if prepare_slot_fork.has_feature(FeatureName::Capella) { + let chain = self.clone(); + self.spawn_blocking_handle( + move || chain.get_expected_withdrawals(&forkchoice_update_params, prepare_slot), + "prepare_beacon_proposer_withdrawals", + ) + .await? + .map(Some)? + } else { + None }; - let parent_beacon_block_root = match prepare_slot_fork { - ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => None, - ForkName::Deneb | ForkName::Electra => { - Some(pre_payload_attributes.parent_beacon_block_root) - } + let parent_beacon_block_root = if prepare_slot_fork.has_feature(FeatureName::Deneb) { + Some(pre_payload_attributes.parent_beacon_block_root) + } else { + None }; let payload_attributes = PayloadAttributes::new( @@ -6609,17 +6606,12 @@ impl BeaconChain { .fork_name(&self.spec) .map_err(Error::InconsistentFork)?; - match fork_name { - ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { - LightClientBootstrap::from_beacon_state(&mut state, &block, &self.spec) - .map(|bootstrap| Some((bootstrap, fork_name))) - .map_err(Error::LightClientError) - } - ForkName::Base => Err(Error::UnsupportedFork), + if fork_name.has_feature(FeatureName::Altair) { + LightClientBootstrap::from_beacon_state(&mut state, &block, &self.spec) + .map(|bootstrap| Some((bootstrap, fork_name))) + .map_err(Error::LightClientError) + } else { + Err(Error::UnsupportedFork) } } } diff --git a/beacon_node/beacon_chain/src/execution_payload.rs b/beacon_node/beacon_chain/src/execution_payload.rs index cbffe363422..61e5e21090d 100644 --- a/beacon_node/beacon_chain/src/execution_payload.rs +++ b/beacon_node/beacon_chain/src/execution_payload.rs @@ -411,19 +411,16 @@ pub fn get_execution_payload( let random = *state.get_randao_mix(current_epoch)?; let latest_execution_payload_header_block_hash = state.latest_execution_payload_header()?.block_hash(); - let withdrawals = match state { - &BeaconState::Capella(_) | &BeaconState::Deneb(_) | &BeaconState::Electra(_) => { - Some(get_expected_withdrawals(state, spec)?.into()) - } - &BeaconState::Bellatrix(_) => None, - // These shouldn't happen but they're here to make the pattern irrefutable - &BeaconState::Base(_) | &BeaconState::Altair(_) => None, + let withdrawals = if state.has_feature(FeatureName::Capella) { + Some(get_expected_withdrawals(state, spec)?.into()) + } else { + None }; - let parent_beacon_block_root = match state { - BeaconState::Deneb(_) | BeaconState::Electra(_) => Some(parent_block_root), - BeaconState::Bellatrix(_) | BeaconState::Capella(_) => None, - // These shouldn't happen but they're here to make the pattern irrefutable - BeaconState::Base(_) | BeaconState::Altair(_) => None, + + let parent_beacon_block_root = if state.has_feature(FeatureName::Deneb) { + Some(parent_block_root) + } else { + None }; // Spawn a task to obtain the execution payload from the EL via a series of async calls. The diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 8fbd5d575f9..ba5b3bda33e 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -877,15 +877,12 @@ where &self.spec, )); - let block_contents: SignedBlockContentsTuple = match *signed_block { - SignedBeaconBlock::Base(_) - | SignedBeaconBlock::Altair(_) - | SignedBeaconBlock::Bellatrix(_) - | SignedBeaconBlock::Capella(_) => (signed_block, None), - SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => { + let block_contents: SignedBlockContentsTuple = + if signed_block.has_feature(FeatureName::Deneb) { (signed_block, block_response.blob_items) - } - }; + } else { + (signed_block, None) + }; (block_contents, block_response.state) } @@ -941,15 +938,13 @@ where &self.spec, )); - let block_contents: SignedBlockContentsTuple = match *signed_block { - SignedBeaconBlock::Base(_) - | SignedBeaconBlock::Altair(_) - | SignedBeaconBlock::Bellatrix(_) - | SignedBeaconBlock::Capella(_) => (signed_block, None), - SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => { + let block_contents: SignedBlockContentsTuple = + if signed_block.has_feature(FeatureName::Deneb) { (signed_block, block_response.blob_items) - } - }; + } else { + (signed_block, None) + }; + (block_contents, pre_state) } diff --git a/beacon_node/beacon_chain/tests/attestation_verification.rs b/beacon_node/beacon_chain/tests/attestation_verification.rs index 1463d1c5c15..692d7d9ad2b 100644 --- a/beacon_node/beacon_chain/tests/attestation_verification.rs +++ b/beacon_node/beacon_chain/tests/attestation_verification.rs @@ -20,7 +20,7 @@ use state_processing::{ use tree_hash::TreeHash; use types::{ test_utils::generate_deterministic_keypair, Address, AggregateSignature, Attestation, - BeaconStateError, BitList, ChainSpec, Epoch, EthSpec, ForkName, Hash256, Keypair, + BeaconStateError, BitList, ChainSpec, Epoch, EthSpec, FeatureName, ForkName, Hash256, Keypair, MainnetEthSpec, SecretKey, SelectionProof, SignedAggregateAndProof, Slot, SubnetId, Unsigned, }; @@ -335,22 +335,22 @@ impl GossipTester { } pub fn earliest_valid_attestation_slot(&self) -> Slot { - let offset = match self.harness.spec.fork_name_at_epoch(self.epoch()) { - ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { - // Subtract an additional slot since the harness will be exactly on the start of the - // slot and the propagation tolerance will allow an extra slot. - E::slots_per_epoch() + 1 - } - // EIP-7045 - ForkName::Deneb | ForkName::Electra => { - let epoch_slot_offset = (self.slot() % E::slots_per_epoch()).as_u64(); - if epoch_slot_offset != 0 { - E::slots_per_epoch() + epoch_slot_offset - } else { - // Here the propagation tolerance will cause the cutoff to be an entire epoch earlier - 2 * E::slots_per_epoch() - } + let offset = if self + .harness + .spec + .fork_name_at_epoch(self.epoch()) + .has_feature(FeatureName::Deneb) + { + //EIP-7045 + let epoch_slot_offset = (self.slot() % E::slots_per_epoch()).as_u64(); + if epoch_slot_offset != 0 { + E::slots_per_epoch() + epoch_slot_offset + } else { + // Here the propagation tolerance will cause the cutoff to be an entire epoch earlier + 2 * E::slots_per_epoch() } + } else { + E::slots_per_epoch() + 1 }; self.slot() diff --git a/beacon_node/execution_layer/build.rs b/beacon_node/execution_layer/build.rs new file mode 100644 index 00000000000..76a1c67d1c8 --- /dev/null +++ b/beacon_node/execution_layer/build.rs @@ -0,0 +1,2 @@ +// Dummy build.rs file to enable OUT_DIR usage by superstruct +fn main() {} diff --git a/beacon_node/execution_layer/src/block_hash.rs b/beacon_node/execution_layer/src/block_hash.rs index 0d0cfaf352c..f9fe38f4281 100644 --- a/beacon_node/execution_layer/src/block_hash.rs +++ b/beacon_node/execution_layer/src/block_hash.rs @@ -45,8 +45,8 @@ pub fn calculate_execution_block_hash( KECCAK_EMPTY_LIST_RLP.as_fixed_bytes().into(), rlp_transactions_root, rlp_withdrawals_root, - rlp_blob_gas_used, - rlp_excess_blob_gas, + rlp_blob_gas_used.copied(), + rlp_excess_blob_gas.copied(), parent_beacon_block_root, ); diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index ce1e0fec5dd..60fc73b66a7 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -26,7 +26,7 @@ pub use types::{ }; use types::{ ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadElectra, KzgProofs, + ExecutionPayloadElectra, FeatureName, KzgProofs, }; use types::{Graffiti, GRAFFITI_BYTES_LEN}; @@ -155,7 +155,15 @@ pub struct ExecutionBlock { /// Representation of an execution block with enough detail to reconstruct a payload. #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive(Clone, Debug, PartialEq, Serialize, Deserialize,), serde(bound = "E: EthSpec", rename_all = "camelCase"), @@ -189,12 +197,12 @@ pub struct ExecutionBlockWithTransactions { #[serde(rename = "hash")] pub block_hash: ExecutionBlockHash, pub transactions: Vec, - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] pub withdrawals: Vec, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] #[serde(with = "serde_utils::u64_hex_be")] pub blob_gas_used: u64, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] #[serde(with = "serde_utils::u64_hex_be")] pub excess_blob_gas: u64, } @@ -425,7 +433,15 @@ pub struct ProposeBlindedBlockResponse { } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes(derive(Clone, Debug, PartialEq),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), @@ -446,9 +462,9 @@ pub struct GetPayloadResponse { #[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))] pub execution_payload: ExecutionPayloadElectra, pub block_value: Uint256, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] pub blobs_bundle: BlobsBundle, - #[superstruct(only(Deneb, Electra), partial_getter(copy))] + #[superstruct(feature(Deneb), partial_getter(copy))] pub should_override_builder: bool, } diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index 93705a16925..482f67f6e40 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -884,30 +884,34 @@ impl HttpJsonRpc { ) -> Result, Error> { let params = json!([JsonPayloadIdRequest::from(payload_id)]); - match fork_name { - ForkName::Bellatrix => { - let response: JsonGetPayloadResponseV1 = self - .rpc_request( - ENGINE_GET_PAYLOAD_V2, - params, - ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, - ) - .await?; - Ok(JsonGetPayloadResponse::V1(response).into()) - } - ForkName::Capella => { - let response: JsonGetPayloadResponseV2 = self - .rpc_request( - ENGINE_GET_PAYLOAD_V2, - params, - ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, - ) - .await?; - Ok(JsonGetPayloadResponse::V2(response).into()) - } - ForkName::Base | ForkName::Altair | ForkName::Deneb | ForkName::Electra => Err( - Error::UnsupportedForkVariant(format!("called get_payload_v2 with {}", fork_name)), - ), + if fork_name.has_feature(FeatureName::Deneb) { + Err(Error::UnsupportedForkVariant(format!( + "called get_payload_v2 with {}", + fork_name + ))) + } else if fork_name.has_feature(FeatureName::Capella) { + let response: JsonGetPayloadResponseV2 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V2, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V2(response).into()) + } else if fork_name.has_feature(FeatureName::Bellatrix) { + let response: JsonGetPayloadResponseV1 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V2, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V1(response).into()) + } else { + Err(Error::UnsupportedForkVariant(format!( + "called get_payload_v2 with {}", + fork_name + ))) } } @@ -918,30 +922,29 @@ impl HttpJsonRpc { ) -> Result, Error> { let params = json!([JsonPayloadIdRequest::from(payload_id)]); - match fork_name { - ForkName::Deneb => { - let response: JsonGetPayloadResponseV3 = self - .rpc_request( - ENGINE_GET_PAYLOAD_V3, - params, - ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, - ) - .await?; - Ok(JsonGetPayloadResponse::V3(response).into()) - } - ForkName::Electra => { - let response: JsonGetPayloadResponseV4 = self - .rpc_request( - ENGINE_GET_PAYLOAD_V3, - params, - ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, - ) - .await?; - Ok(JsonGetPayloadResponse::V4(response).into()) - } - ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => Err( - Error::UnsupportedForkVariant(format!("called get_payload_v3 with {}", fork_name)), - ), + if fork_name.has_feature(FeatureName::Electra) { + let response: JsonGetPayloadResponseV4 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V3, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V4(response).into()) + } else if fork_name.has_feature(FeatureName::Deneb) { + let response: JsonGetPayloadResponseV3 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V3, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + Ok(JsonGetPayloadResponse::V3(response).into()) + } else { + Err(Error::UnsupportedForkVariant(format!( + "called get_payload_v3 with {}", + fork_name + ))) } } @@ -1217,27 +1220,25 @@ impl HttpJsonRpc { payload_id: PayloadId, ) -> Result, Error> { let engine_capabilities = self.get_engine_capabilities(None).await?; - match fork_name { - ForkName::Bellatrix | ForkName::Capella => { - if engine_capabilities.get_payload_v2 { - self.get_payload_v2(fork_name, payload_id).await - } else if engine_capabilities.new_payload_v1 { - self.get_payload_v1(payload_id).await - } else { - Err(Error::RequiredMethodUnsupported("engine_getPayload")) - } + if fork_name.has_feature(FeatureName::Deneb) { + if engine_capabilities.get_payload_v3 { + self.get_payload_v3(fork_name, payload_id).await + } else { + Err(Error::RequiredMethodUnsupported("engine_getPayloadV3")) } - ForkName::Deneb | ForkName::Electra => { - if engine_capabilities.get_payload_v3 { - self.get_payload_v3(fork_name, payload_id).await - } else { - Err(Error::RequiredMethodUnsupported("engine_getPayloadV3")) - } + } else if fork_name.has_feature(FeatureName::Bellatrix) { + if engine_capabilities.get_payload_v2 { + self.get_payload_v2(fork_name, payload_id).await + } else if engine_capabilities.new_payload_v1 { + self.get_payload_v1(payload_id).await + } else { + Err(Error::RequiredMethodUnsupported("engine_getPayload")) } - ForkName::Base | ForkName::Altair => Err(Error::UnsupportedForkVariant(format!( + } else { + Err(Error::UnsupportedForkVariant(format!( "called get_payload with {}", fork_name - ))), + ))) } } diff --git a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs index 8d2e3d5ad06..552bb659dcd 100644 --- a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs +++ b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs @@ -5,7 +5,7 @@ use state_processing::per_block_processing::deneb::kzg_commitment_to_versioned_h use superstruct::superstruct; use types::{ BeaconBlockRef, BeaconStateError, EthSpec, ExecutionBlockHash, ExecutionPayload, - ExecutionPayloadRef, Hash256, VersionedHash, + ExecutionPayloadRef, FeatureName, ForkName, Hash256, VersionedHash, }; use types::{ ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, @@ -13,7 +13,15 @@ use types::{ }; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes(derive(Clone, Debug, PartialEq),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), @@ -39,9 +47,9 @@ pub struct NewPayloadRequest<'block, E: EthSpec> { pub execution_payload: &'block ExecutionPayloadDeneb, #[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))] pub execution_payload: &'block ExecutionPayloadElectra, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] pub versioned_hashes: Vec, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] pub parent_beacon_block_root: Hash256, } diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index c9ae1e60cdc..d385769cfbc 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -20,9 +20,9 @@ use types::builder_bid::{ }; use types::{ Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload, - ExecutionPayloadHeaderRefMut, ForkName, ForkVersionedResponse, Hash256, PublicKeyBytes, - Signature, SignedBlindedBeaconBlock, SignedRoot, SignedValidatorRegistrationData, Slot, - Uint256, + ExecutionPayloadHeaderRefMut, FeatureName, ForkName, ForkVersionedResponse, Hash256, + PublicKeyBytes, Signature, SignedBlindedBeaconBlock, SignedRoot, + SignedValidatorRegistrationData, Slot, Uint256, }; use types::{ExecutionBlockHash, SecretKey}; use warp::{Filter, Rejection}; @@ -479,16 +479,17 @@ pub fn serve( let prev_randao = head_state .get_randao_mix(head_state.current_epoch()) .map_err(|_| reject("couldn't get prev randao"))?; - let expected_withdrawals = match fork { - ForkName::Base | ForkName::Altair | ForkName::Bellatrix => None, - ForkName::Capella | ForkName::Deneb | ForkName::Electra => Some( + let expected_withdrawals = if fork.has_feature(FeatureName::Capella) { + Some( builder .beacon_client .get_expected_withdrawals(&StateId::Head) .await .unwrap() .data, - ), + ) + } else { + None }; let payload_attributes = match fork { diff --git a/beacon_node/http_api/src/build_block_contents.rs b/beacon_node/http_api/src/build_block_contents.rs index 05a6735b327..be7e4439552 100644 --- a/beacon_node/http_api/src/build_block_contents.rs +++ b/beacon_node/http_api/src/build_block_contents.rs @@ -1,6 +1,6 @@ use beacon_chain::{BeaconBlockResponse, BeaconBlockResponseWrapper, BlockProductionError}; use eth2::types::{BlockContents, FullBlockContents, ProduceBlockV3Response}; -use types::{EthSpec, ForkName}; +use types::{EthSpec, FeatureName, ForkName}; type Error = warp::reject::Rejection; pub fn build_block_contents( @@ -11,11 +11,8 @@ pub fn build_block_contents( BeaconBlockResponseWrapper::Blinded(block) => { Ok(ProduceBlockV3Response::Blinded(block.block)) } - BeaconBlockResponseWrapper::Full(block) => match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => Ok( - ProduceBlockV3Response::Full(FullBlockContents::Block(block.block)), - ), - ForkName::Deneb | ForkName::Electra => { + BeaconBlockResponseWrapper::Full(block) => { + if fork_name.has_feature(FeatureName::Deneb) { let BeaconBlockResponse { block, state: _, @@ -37,7 +34,11 @@ pub fn build_block_contents( blobs, }), )) + } else { + Ok(ProduceBlockV3Response::Full(FullBlockContents::Block( + block.block, + ))) } - }, + } } } diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 024e268e2a5..f7957fd6ad3 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -79,11 +79,11 @@ use tokio_stream::{ }; use types::{ fork_versioned_response::EmptyMetadata, Attestation, AttestationData, AttestationShufflingId, - AttesterSlashing, BeaconStateError, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, - ForkVersionedResponse, Hash256, ProposerPreparationData, ProposerSlashing, RelativeEpoch, - SignedAggregateAndProof, SignedBlindedBeaconBlock, SignedBlsToExecutionChange, - SignedContributionAndProof, SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, - SyncCommitteeMessage, SyncContributionData, + AttesterSlashing, BeaconStateError, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, + FeatureName, ForkName, ForkVersionedResponse, Hash256, ProposerPreparationData, + ProposerSlashing, RelativeEpoch, SignedAggregateAndProof, SignedBlindedBeaconBlock, + SignedBlsToExecutionChange, SignedContributionAndProof, SignedValidatorRegistrationData, + SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData, }; use validator::pubkey_to_validator_index; use version::{ @@ -2046,7 +2046,7 @@ pub fn serve( .to_execution_address; // New to P2P *and* op pool, gossip immediately if post-Capella. - let received_pre_capella = if chain.current_slot_is_post_capella().unwrap_or(false) { + let received_pre_capella = if chain.has_feature(FeatureName::Capella) { ReceivedPreCapella::No } else { ReceivedPreCapella::Yes diff --git a/beacon_node/http_api/src/publish_blocks.rs b/beacon_node/http_api/src/publish_blocks.rs index 0d176e6a53a..2eaab0ef448 100644 --- a/beacon_node/http_api/src/publish_blocks.rs +++ b/beacon_node/http_api/src/publish_blocks.rs @@ -20,7 +20,7 @@ use tokio::sync::mpsc::UnboundedSender; use tree_hash::TreeHash; use types::{ AbstractExecPayload, BeaconBlockRef, BlobSidecarList, EthSpec, ExecPayload, ExecutionBlockHash, - ForkName, FullPayload, FullPayloadBellatrix, Hash256, SignedBeaconBlock, + FeatureName, ForkName, FullPayload, FullPayloadBellatrix, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, VariableList, }; use warp::http::StatusCode; @@ -77,28 +77,22 @@ pub async fn publish_block block.slot(), "publish_delay" => ?publish_delay); - match block.as_ref() { - SignedBeaconBlock::Base(_) - | SignedBeaconBlock::Altair(_) - | SignedBeaconBlock::Bellatrix(_) - | SignedBeaconBlock::Capella(_) => { - crate::publish_pubsub_message(&sender, PubsubMessage::BeaconBlock(block)) - .map_err(|_| BlockError::BeaconChainError(BeaconChainError::UnableToPublish))?; - } - SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => { - let mut pubsub_messages = vec![PubsubMessage::BeaconBlock(block)]; - if let Some(blob_sidecars) = blobs_opt { - for (blob_index, blob) in blob_sidecars.into_iter().enumerate() { - pubsub_messages.push(PubsubMessage::BlobSidecar(Box::new(( - blob_index as u64, - blob, - )))); - } + if block.has_feature(FeatureName::Deneb) { + let mut pubsub_messages = vec![PubsubMessage::BeaconBlock(block)]; + if let Some(blob_sidecars) = blobs_opt { + for (blob_index, blob) in blob_sidecars.into_iter().enumerate() { + pubsub_messages.push(PubsubMessage::BlobSidecar(Box::new(( + blob_index as u64, + blob, + )))); } - crate::publish_pubsub_messages(&sender, pubsub_messages) - .map_err(|_| BlockError::BeaconChainError(BeaconChainError::UnableToPublish))?; } - }; + crate::publish_pubsub_messages(&sender, pubsub_messages) + .map_err(|_| BlockError::BeaconChainError(BeaconChainError::UnableToPublish))?; + } else { + crate::publish_pubsub_message(&sender, PubsubMessage::BeaconBlock(block)) + .map_err(|_| BlockError::BeaconChainError(BeaconChainError::UnableToPublish))?; + } Ok(()) }; diff --git a/beacon_node/network/src/sync/range_sync/range.rs b/beacon_node/network/src/sync/range_sync/range.rs index c8e82666840..ff8582c6940 100644 --- a/beacon_node/network/src/sync/range_sync/range.rs +++ b/beacon_node/network/src/sync/range_sync/range.rs @@ -397,7 +397,7 @@ mod tests { use std::collections::HashSet; use store::MemoryStore; use tokio::sync::mpsc; - use types::{ForkName, MinimalEthSpec as E}; + use types::{FeatureName, ForkName, MinimalEthSpec as E}; #[derive(Debug)] struct FakeStorage { @@ -529,21 +529,20 @@ mod tests { } else { panic!("Should have sent a batch request to the peer") }; - let blob_req_id = match fork_name { - ForkName::Deneb | ForkName::Electra => { - if let Ok(NetworkMessage::SendRequest { - peer_id, - request: _, - request_id, - }) = self.network_rx.try_recv() - { - assert_eq!(&peer_id, expected_peer); - Some(request_id) - } else { - panic!("Should have sent a batch request to the peer") - } + let blob_req_id = if fork_name.has_feature(FeatureName::Deneb) { + if let Ok(NetworkMessage::SendRequest { + peer_id, + request: _, + request_id, + }) = self.network_rx.try_recv() + { + assert_eq!(&peer_id, expected_peer); + Some(request_id) + } else { + panic!("Should have sent a batch request to the peer") } - _ => None, + } else { + None }; (block_req_id, blob_req_id) } diff --git a/beacon_node/store/build.rs b/beacon_node/store/build.rs new file mode 100644 index 00000000000..76a1c67d1c8 --- /dev/null +++ b/beacon_node/store/build.rs @@ -0,0 +1,2 @@ +// Dummy build.rs file to enable OUT_DIR usage by superstruct +fn main() {} diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index e56d0580ac2..b3059b5beb3 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -14,7 +14,14 @@ use types::*; /// /// Utilises lazy-loading from separate storage for its vector fields. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes(derive(Debug, PartialEq, Clone, Encode, Decode)) )] #[derive(Debug, PartialEq, Clone, Encode)] @@ -66,9 +73,9 @@ where pub current_epoch_attestations: List, E::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] pub previous_epoch_participation: List, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] pub current_epoch_participation: List, // Finality @@ -78,13 +85,13 @@ where pub finalized_checkpoint: Checkpoint, // Inactivity - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] pub inactivity_scores: List, // Light-client sync committees - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] pub next_sync_committee: Arc>, // Execution @@ -110,36 +117,36 @@ where pub latest_execution_payload_header: ExecutionPayloadHeaderElectra, // Capella - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] pub next_withdrawal_index: u64, - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] pub next_withdrawal_validator_index: u64, #[ssz(skip_serializing, skip_deserializing)] - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] pub historical_summaries: Option>, // Electra - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub deposit_receipts_start_index: u64, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub deposit_balance_to_consume: u64, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub exit_balance_to_consume: u64, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub earliest_exit_epoch: Epoch, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub consolidation_balance_to_consume: u64, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub earliest_consolidation_epoch: Epoch, // TODO(electra) should these be optional? - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub pending_balance_deposits: List, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub pending_partial_withdrawals: List, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub pending_consolidations: List, } diff --git a/consensus/fork_choice/src/fork_choice.rs b/consensus/fork_choice/src/fork_choice.rs index 2846a0112cd..65fbeb4a408 100644 --- a/consensus/fork_choice/src/fork_choice.rs +++ b/consensus/fork_choice/src/fork_choice.rs @@ -15,8 +15,8 @@ use std::time::Duration; use types::{ consts::bellatrix::INTERVALS_PER_SLOT, AbstractExecPayload, AttestationShufflingId, AttesterSlashing, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, Checkpoint, Epoch, - EthSpec, ExecPayload, ExecutionBlockHash, Hash256, IndexedAttestation, RelativeEpoch, - SignedBeaconBlock, Slot, + EthSpec, ExecPayload, ExecutionBlockHash, FeatureName, Hash256, IndexedAttestation, + RelativeEpoch, SignedBeaconBlock, Slot, }; #[derive(Debug)] @@ -747,32 +747,26 @@ where if let Some((parent_justified, parent_finalized)) = parent_checkpoints { (parent_justified, parent_finalized) } else { - let justification_and_finalization_state = match block { - BeaconBlockRef::Electra(_) - | BeaconBlockRef::Deneb(_) - | BeaconBlockRef::Capella(_) - | BeaconBlockRef::Bellatrix(_) - | BeaconBlockRef::Altair(_) => { - // NOTE: Processing justification & finalization requires the progressive - // balances cache, but we cannot initialize it here as we only have an - // immutable reference. The state *should* have come straight from block - // processing, which initialises the cache, but if we add other `on_block` - // calls in future it could be worth passing a mutable reference. - per_epoch_processing::altair::process_justification_and_finalization(state)? - } - BeaconBlockRef::Base(_) => { - let mut validator_statuses = - per_epoch_processing::base::ValidatorStatuses::new(state, spec) - .map_err(Error::ValidatorStatuses)?; - validator_statuses - .process_attestations(state) + let justification_and_finalization_state = if block.has_feature(FeatureName::Altair) + { + // NOTE: Processing justification & finalization requires the progressive + // balances cache, but we cannot initialize it here as we only have an + // immutable reference. The state *should* have come straight from block + // processing, which initialises the cache, but if we add other `on_block` + // calls in future it could be worth passing a mutable reference. + per_epoch_processing::altair::process_justification_and_finalization(state)? + } else { + let mut validator_statuses = + per_epoch_processing::base::ValidatorStatuses::new(state, spec) .map_err(Error::ValidatorStatuses)?; - per_epoch_processing::base::process_justification_and_finalization( - state, - &validator_statuses.total_balances, - spec, - )? - } + validator_statuses + .process_attestations(state) + .map_err(Error::ValidatorStatuses)?; + per_epoch_processing::base::process_justification_and_finalization( + state, + &validator_statuses.total_balances, + spec, + )? }; ( diff --git a/consensus/state_processing/src/common/get_attestation_participation.rs b/consensus/state_processing/src/common/get_attestation_participation.rs index fc09dad1f4e..12493c4a0ef 100644 --- a/consensus/state_processing/src/common/get_attestation_participation.rs +++ b/consensus/state_processing/src/common/get_attestation_participation.rs @@ -7,7 +7,7 @@ use types::{ }, BeaconStateError as Error, }; -use types::{AttestationData, BeaconState, ChainSpec, EthSpec}; +use types::{AttestationData, BeaconState, ChainSpec, EthSpec, FeatureName}; /// Get the participation flags for a valid attestation. /// @@ -44,22 +44,14 @@ pub fn get_attestation_participation_flag_indices( if is_matching_source && inclusion_delay <= E::slots_per_epoch().integer_sqrt() { participation_flag_indices.push(TIMELY_SOURCE_FLAG_INDEX); } - match state { - &BeaconState::Base(_) - | &BeaconState::Altair(_) - | &BeaconState::Bellatrix(_) - | &BeaconState::Capella(_) => { - if is_matching_target && inclusion_delay <= E::slots_per_epoch() { - participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX); - } - } - &BeaconState::Deneb(_) | &BeaconState::Electra(_) => { - if is_matching_target { - // [Modified in Deneb:EIP7045] - participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX); - } - } + + if state.has_feature(FeatureName::Deneb) && is_matching_target { + // [Modified in Deneb:EIP7045] + participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX); + } else if is_matching_target && inclusion_delay <= E::slots_per_epoch() { + participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX); } + if is_matching_head && inclusion_delay == spec.min_attestation_inclusion_delay { participation_flag_indices.push(TIMELY_HEAD_FLAG_INDEX); } diff --git a/consensus/state_processing/src/common/slash_validator.rs b/consensus/state_processing/src/common/slash_validator.rs index 520b58a8af3..c2d10bbf189 100644 --- a/consensus/state_processing/src/common/slash_validator.rs +++ b/consensus/state_processing/src/common/slash_validator.rs @@ -55,15 +55,12 @@ pub fn slash_validator( let whistleblower_index = opt_whistleblower_index.unwrap_or(proposer_index); let whistleblower_reward = validator_effective_balance.safe_div(spec.whistleblower_reward_quotient)?; - let proposer_reward = match state { - BeaconState::Base(_) => whistleblower_reward.safe_div(spec.proposer_reward_quotient)?, - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => whistleblower_reward + let proposer_reward = if state.has_feature(FeatureName::Altair) { + whistleblower_reward .safe_mul(PROPOSER_WEIGHT)? - .safe_div(WEIGHT_DENOMINATOR)?, + .safe_div(WEIGHT_DENOMINATOR)? + } else { + whistleblower_reward.safe_div(spec.proposer_reward_quotient)? }; // Ensure the whistleblower index is in the validator registry. diff --git a/consensus/state_processing/src/common/update_progressive_balances_cache.rs b/consensus/state_processing/src/common/update_progressive_balances_cache.rs index af843b3acbc..b6ce5e7755e 100644 --- a/consensus/state_processing/src/common/update_progressive_balances_cache.rs +++ b/consensus/state_processing/src/common/update_progressive_balances_cache.rs @@ -6,8 +6,8 @@ use crate::metrics::{ use crate::{BlockProcessingError, EpochProcessingError}; use lighthouse_metrics::set_gauge; use types::{ - is_progressive_balances_enabled, BeaconState, BeaconStateError, ChainSpec, Epoch, - EpochTotalBalances, EthSpec, ParticipationFlags, ProgressiveBalancesCache, Validator, + BeaconState, BeaconStateError, ChainSpec, Epoch, EpochTotalBalances, EthSpec, FeatureName, + ParticipationFlags, ProgressiveBalancesCache, Validator, }; /// Initializes the `ProgressiveBalancesCache` if it is unbuilt. @@ -15,7 +15,7 @@ pub fn initialize_progressive_balances_cache( state: &mut BeaconState, spec: &ChainSpec, ) -> Result<(), BeaconStateError> { - if !is_progressive_balances_enabled(state) + if !state.has_feature(FeatureName::Altair) || state.progressive_balances_cache().is_initialized() { return Ok(()); @@ -92,7 +92,7 @@ pub fn update_progressive_balances_on_attestation( validator_effective_balance: u64, validator_slashed: bool, ) -> Result<(), BlockProcessingError> { - if is_progressive_balances_enabled(state) { + if state.has_feature(FeatureName::Altair) { state.progressive_balances_cache_mut().on_new_attestation( epoch, validator_slashed, @@ -109,7 +109,7 @@ pub fn update_progressive_balances_on_slashing( validator_index: usize, validator_effective_balance: u64, ) -> Result<(), BlockProcessingError> { - if is_progressive_balances_enabled(state) { + if state.has_feature(FeatureName::Altair) { let previous_epoch_participation = *state .previous_epoch_participation()? .get(validator_index) @@ -135,7 +135,7 @@ pub fn update_progressive_balances_on_epoch_transition( state: &mut BeaconState, spec: &ChainSpec, ) -> Result<(), EpochProcessingError> { - if is_progressive_balances_enabled(state) { + if state.has_feature(FeatureName::Altair) { state .progressive_balances_cache_mut() .on_epoch_transition(spec)?; diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index 2efa1218829..058e0ff20dc 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -188,7 +188,7 @@ pub fn per_block_processing>( )?; } - if is_progressive_balances_enabled(state) { + if state.has_feature(FeatureName::Altair) { update_progressive_balances_metrics(state.progressive_balances_cache())?; } @@ -453,15 +453,17 @@ pub fn process_execution_payload>( /// repeatedly write code to treat these errors as false. /// https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#is_merge_transition_complete pub fn is_merge_transition_complete(state: &BeaconState) -> bool { - match state { + if state.has_feature(FeatureName::Capella) { + true + } else if state.has_feature(FeatureName::Bellatrix) { // We must check defaultness against the payload header with 0x0 roots, as that's what's meant // by `ExecutionPayloadHeader()` in the spec. - BeaconState::Bellatrix(_) => state + state .latest_execution_payload_header() .map(|header| !header.is_default_with_zero_roots()) - .unwrap_or(false), - BeaconState::Electra(_) | BeaconState::Deneb(_) | BeaconState::Capella(_) => true, - BeaconState::Base(_) | BeaconState::Altair(_) => false, + .unwrap_or(false) + } else { + false } } /// https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#is_merge_transition_block @@ -556,55 +558,52 @@ pub fn process_withdrawals>( payload: Payload::Ref<'_>, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { - match state { - BeaconState::Bellatrix(_) => Ok(()), - BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => { - let expected_withdrawals = get_expected_withdrawals(state, spec)?; - let expected_root = expected_withdrawals.tree_hash_root(); - let withdrawals_root = payload.withdrawals_root()?; - - if expected_root != withdrawals_root { - return Err(BlockProcessingError::WithdrawalsRootMismatch { - expected: expected_root, - found: withdrawals_root, - }); - } + if state.has_feature(FeatureName::Capella) { + let expected_withdrawals = get_expected_withdrawals(state, spec)?; + let expected_root = expected_withdrawals.tree_hash_root(); + let withdrawals_root = payload.withdrawals_root()?; + + if expected_root != withdrawals_root { + return Err(BlockProcessingError::WithdrawalsRootMismatch { + expected: expected_root, + found: withdrawals_root, + }); + } - for withdrawal in expected_withdrawals.iter() { - decrease_balance( - state, - withdrawal.validator_index as usize, - withdrawal.amount, - )?; - } + for withdrawal in expected_withdrawals.iter() { + decrease_balance( + state, + withdrawal.validator_index as usize, + withdrawal.amount, + )?; + } - // Update the next withdrawal index if this block contained withdrawals - if let Some(latest_withdrawal) = expected_withdrawals.last() { - *state.next_withdrawal_index_mut()? = latest_withdrawal.index.safe_add(1)?; - - // Update the next validator index to start the next withdrawal sweep - if expected_withdrawals.len() == E::max_withdrawals_per_payload() { - // Next sweep starts after the latest withdrawal's validator index - let next_validator_index = latest_withdrawal - .validator_index - .safe_add(1)? - .safe_rem(state.validators().len() as u64)?; - *state.next_withdrawal_validator_index_mut()? = next_validator_index; - } - } + // Update the next withdrawal index if this block contained withdrawals + if let Some(latest_withdrawal) = expected_withdrawals.last() { + *state.next_withdrawal_index_mut()? = latest_withdrawal.index.safe_add(1)?; - // Advance sweep by the max length of the sweep if there was not a full set of withdrawals - if expected_withdrawals.len() != E::max_withdrawals_per_payload() { - let next_validator_index = state - .next_withdrawal_validator_index()? - .safe_add(spec.max_validators_per_withdrawals_sweep)? + // Update the next validator index to start the next withdrawal sweep + if expected_withdrawals.len() == E::max_withdrawals_per_payload() { + // Next sweep starts after the latest withdrawal's validator index + let next_validator_index = latest_withdrawal + .validator_index + .safe_add(1)? .safe_rem(state.validators().len() as u64)?; *state.next_withdrawal_validator_index_mut()? = next_validator_index; } + } - Ok(()) + // Advance sweep by the max length of the sweep if there was not a full set of withdrawals + if expected_withdrawals.len() != E::max_withdrawals_per_payload() { + let next_validator_index = state + .next_withdrawal_validator_index()? + .safe_add(spec.max_validators_per_withdrawals_sweep)? + .safe_rem(state.validators().len() as u64)?; + *state.next_withdrawal_validator_index_mut()? = next_validator_index; } - // these shouldn't even be encountered but they're here for completeness - BeaconState::Base(_) | BeaconState::Altair(_) => Ok(()), + + Ok(()) + } else { + Ok(()) } } diff --git a/consensus/state_processing/src/per_block_processing/process_operations.rs b/consensus/state_processing/src/per_block_processing/process_operations.rs index 3aefcf8a9c5..d608ea7ae28 100644 --- a/consensus/state_processing/src/per_block_processing/process_operations.rs +++ b/consensus/state_processing/src/per_block_processing/process_operations.rs @@ -262,29 +262,22 @@ pub fn process_attestations>( ctxt: &mut ConsensusContext, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { - match block_body { - BeaconBlockBodyRef::Base(_) => { - base::process_attestations( - state, - block_body.attestations(), - verify_signatures, - ctxt, - spec, - )?; - } - BeaconBlockBodyRef::Altair(_) - | BeaconBlockBodyRef::Bellatrix(_) - | BeaconBlockBodyRef::Capella(_) - | BeaconBlockBodyRef::Deneb(_) - | BeaconBlockBodyRef::Electra(_) => { - altair_deneb::process_attestations( - state, - block_body.attestations(), - verify_signatures, - ctxt, - spec, - )?; - } + if block_body.has_feature(FeatureName::Altair) { + altair_deneb::process_attestations( + state, + block_body.attestations(), + verify_signatures, + ctxt, + spec, + )?; + } else { + base::process_attestations( + state, + block_body.attestations(), + verify_signatures, + ctxt, + spec, + )?; } Ok(()) } diff --git a/consensus/state_processing/src/per_block_processing/signature_sets.rs b/consensus/state_processing/src/per_block_processing/signature_sets.rs index 9468893f762..9a25809fb93 100644 --- a/consensus/state_processing/src/per_block_processing/signature_sets.rs +++ b/consensus/state_processing/src/per_block_processing/signature_sets.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use tree_hash::TreeHash; use types::{ AbstractExecPayload, AggregateSignature, AttesterSlashing, BeaconBlockRef, BeaconState, - BeaconStateError, ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, + BeaconStateError, ChainSpec, DepositData, Domain, Epoch, EthSpec, FeatureName, Fork, Hash256, InconsistentFork, IndexedAttestation, ProposerSlashing, PublicKey, PublicKeyBytes, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedBlsToExecutionChange, SignedContributionAndProof, SignedRoot, SignedVoluntaryExit, @@ -387,22 +387,20 @@ where let exit = &signed_exit.message; let proposer_index = exit.validator_index as usize; - let domain = match state { - BeaconState::Base(_) - | BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) => spec.get_domain( - exit.epoch, - Domain::VoluntaryExit, - &state.fork(), - state.genesis_validators_root(), - ), + let domain = if state.has_feature(FeatureName::Deneb) { // EIP-7044 - BeaconState::Deneb(_) | BeaconState::Electra(_) => spec.compute_domain( + spec.compute_domain( Domain::VoluntaryExit, spec.capella_fork_version, state.genesis_validators_root(), - ), + ) + } else { + spec.get_domain( + exit.epoch, + Domain::VoluntaryExit, + &state.fork(), + state.genesis_validators_root(), + ) }; let message = exit.signing_root(domain); diff --git a/consensus/state_processing/src/per_block_processing/verify_attestation.rs b/consensus/state_processing/src/per_block_processing/verify_attestation.rs index c904ba55f0a..a4f2ba1050b 100644 --- a/consensus/state_processing/src/per_block_processing/verify_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/verify_attestation.rs @@ -32,21 +32,17 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, E: EthSpec>( attestation: data.slot, } ); - match state { - BeaconState::Base(_) - | BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) => { - verify!( - state.slot() <= data.slot.safe_add(E::slots_per_epoch())?, - Invalid::IncludedTooLate { - state: state.slot(), - attestation: data.slot, - } - ); - } + if state.has_feature(FeatureName::Deneb) { // [Modified in Deneb:EIP7045] - BeaconState::Deneb(_) | BeaconState::Electra(_) => {} + {} + } else { + verify!( + state.slot() <= data.slot.safe_add(E::slots_per_epoch())?, + Invalid::IncludedTooLate { + state: state.slot(), + attestation: data.slot, + } + ); } verify_attestation_for_state(state, attestation, ctxt, verify_signatures, spec) diff --git a/consensus/state_processing/src/per_epoch_processing.rs b/consensus/state_processing/src/per_epoch_processing.rs index 55e8853f3f8..32ab54a5321 100644 --- a/consensus/state_processing/src/per_epoch_processing.rs +++ b/consensus/state_processing/src/per_epoch_processing.rs @@ -5,7 +5,7 @@ pub use epoch_processing_summary::{EpochProcessingSummary, ParticipationEpochSum use errors::EpochProcessingError as Error; pub use justification_and_finalization_state::JustificationAndFinalizationState; use safe_arith::SafeArith; -use types::{BeaconState, ChainSpec, EthSpec}; +use types::{BeaconState, ChainSpec, EthSpec, FeatureName}; pub use registry_updates::{process_registry_updates, process_registry_updates_slow}; pub use slashings::{process_slashings, process_slashings_slow}; @@ -41,13 +41,10 @@ pub fn process_epoch( .fork_name(spec) .map_err(Error::InconsistentStateFork)?; - match state { - BeaconState::Base(_) => base::process_epoch(state, spec), - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => altair::process_epoch(state, spec), + if state.has_feature(FeatureName::Altair) { + altair::process_epoch(state, spec) + } else { + base::process_epoch(state, spec) } } diff --git a/consensus/types/build.rs b/consensus/types/build.rs new file mode 100644 index 00000000000..76a1c67d1c8 --- /dev/null +++ b/consensus/types/build.rs @@ -0,0 +1,2 @@ +// Dummy build.rs file to enable OUT_DIR usage by superstruct +fn main() {} diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index 81491d65056..fd5aac75a04 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -12,7 +12,14 @@ use tree_hash_derive::TreeHash; /// A block of the `BeaconChain`. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name_unchecked"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Debug, @@ -228,6 +235,10 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockRef<'a, E, Payl } } + pub fn has_feature(self, feature: FeatureName) -> bool { + self.fork_name_unchecked().has_feature(feature) + } + /// Convenience accessor for the `body` as a `BeaconBlockBodyRef`. pub fn body(&self) -> BeaconBlockBodyRef<'a, E, Payload> { map_beacon_block_ref_into_beacon_block_body_ref!(&'a _, *self, |block, cons| cons( diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/beacon_block_body.rs index c3077c4ab68..3cdb22d6dd7 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/beacon_block_body.rs @@ -29,7 +29,14 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11; /// /// This *superstruct* abstracts over the hard-fork. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Debug, @@ -67,7 +74,7 @@ pub struct BeaconBlockBody = FullPay pub attestations: VariableList, E::MaxAttestations>, pub deposits: VariableList, pub voluntary_exits: VariableList, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] pub sync_aggregate: SyncAggregate, // We flatten the execution payload so that serde can use the name of the inner type, // either `execution_payload` for full payloads, or `execution_payload_header` for blinded @@ -87,12 +94,12 @@ pub struct BeaconBlockBody = FullPay #[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))] #[serde(flatten)] pub execution_payload: Payload::Electra, - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] pub bls_to_execution_changes: VariableList, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] pub blob_kzg_commitments: KzgCommitments, - #[superstruct(only(Base, Altair))] + #[superstruct(feature(not(Bellatrix)))] #[ssz(skip_serializing, skip_deserializing)] #[tree_hash(skip_hashing)] #[serde(skip)] @@ -256,6 +263,10 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, self.blob_kzg_commitments() .map_or(false, |blobs| !blobs.is_empty()) } + + pub fn has_feature(self, feature: FeatureName) -> bool { + self.fork_name().has_feature(feature) + } } impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, Payload> { diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index 577f282a556..90820f7d8d2 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -206,7 +206,14 @@ impl From for Hash256 { /// The state of the `BeaconChain` at some slot. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name_unchecked"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Derivative, @@ -384,18 +391,18 @@ where pub slashings: Vector, // Attestations (genesis fork only) - #[superstruct(only(Base))] + #[superstruct(feature(not(Altair)))] #[test_random(default)] pub previous_epoch_attestations: List, E::MaxPendingAttestations>, - #[superstruct(only(Base))] + #[superstruct(feature(not(Altair)))] #[test_random(default)] pub current_epoch_attestations: List, E::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] #[test_random(default)] pub previous_epoch_participation: List, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] #[test_random(default)] pub current_epoch_participation: List, @@ -415,15 +422,15 @@ where // Inactivity #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] #[test_random(default)] pub inactivity_scores: List, // Light-client sync committees - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] #[metastruct(exclude_from(tree_lists))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] + #[superstruct(feature(Altair))] #[metastruct(exclude_from(tree_lists))] pub next_sync_committee: Arc>, @@ -454,51 +461,51 @@ where pub latest_execution_payload_header: ExecutionPayloadHeaderElectra, // Capella - #[superstruct(only(Capella, Deneb, Electra), partial_getter(copy))] + #[superstruct(feature(Capella), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] #[metastruct(exclude_from(tree_lists))] pub next_withdrawal_index: u64, - #[superstruct(only(Capella, Deneb, Electra), partial_getter(copy))] + #[superstruct(feature(Capella), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] #[metastruct(exclude_from(tree_lists))] pub next_withdrawal_validator_index: u64, // Deep history valid from Capella onwards. - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] #[test_random(default)] pub historical_summaries: List, // Electra - #[superstruct(only(Electra), partial_getter(copy))] + #[superstruct(feature(Electra), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub deposit_receipts_start_index: u64, - #[superstruct(only(Electra), partial_getter(copy))] + #[superstruct(feature(Electra), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub deposit_balance_to_consume: u64, - #[superstruct(only(Electra), partial_getter(copy))] + #[superstruct(feature(Electra), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub exit_balance_to_consume: u64, - #[superstruct(only(Electra), partial_getter(copy))] + #[superstruct(feature(Electra), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] pub earliest_exit_epoch: Epoch, - #[superstruct(only(Electra), partial_getter(copy))] + #[superstruct(feature(Electra), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] #[serde(with = "serde_utils::quoted_u64")] pub consolidation_balance_to_consume: u64, - #[superstruct(only(Electra), partial_getter(copy))] + #[superstruct(feature(Electra), partial_getter(copy))] #[metastruct(exclude_from(tree_lists))] pub earliest_consolidation_epoch: Epoch, #[test_random(default)] - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub pending_balance_deposits: List, #[test_random(default)] - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub pending_partial_withdrawals: List, #[test_random(default)] - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] pub pending_consolidations: List, // Caching (not in the spec) @@ -628,20 +635,6 @@ impl BeaconState { } } - /// Returns the name of the fork pertaining to `self`. - /// - /// Does not check if `self` is consistent with the fork dictated by `self.slot()`. - pub fn fork_name_unchecked(&self) -> ForkName { - match self { - BeaconState::Base { .. } => ForkName::Base, - BeaconState::Altair { .. } => ForkName::Altair, - BeaconState::Bellatrix { .. } => ForkName::Bellatrix, - BeaconState::Capella { .. } => ForkName::Capella, - BeaconState::Deneb { .. } => ForkName::Deneb, - BeaconState::Electra { .. } => ForkName::Electra, - } - } - /// Returns the `tree_hash_root` of the state. /// /// Spec v0.12.1 @@ -1586,16 +1579,14 @@ impl BeaconState { /// /// Uses the current epoch committee cache, and will error if it isn't initialized. pub fn get_activation_churn_limit(&self, spec: &ChainSpec) -> Result { - Ok(match self { - BeaconState::Base(_) - | BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) => self.get_validator_churn_limit(spec)?, - BeaconState::Deneb(_) | BeaconState::Electra(_) => std::cmp::min( + if self.has_feature(FeatureName::Deneb) { + Ok(std::cmp::min( spec.max_per_epoch_activation_churn_limit, self.get_validator_churn_limit(spec)?, - ), - }) + )) + } else { + Ok(self.get_validator_churn_limit(spec)?) + } } /// Returns the `slot`, `index`, `committee_position` and `committee_len` for which a validator must produce an diff --git a/consensus/types/src/beacon_state/progressive_balances_cache.rs b/consensus/types/src/beacon_state/progressive_balances_cache.rs index fd5e51313f7..c54b933ac38 100644 --- a/consensus/types/src/beacon_state/progressive_balances_cache.rs +++ b/consensus/types/src/beacon_state/progressive_balances_cache.rs @@ -4,7 +4,7 @@ use crate::{ NUM_FLAG_INDICES, TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX, TIMELY_TARGET_FLAG_INDEX, }, - BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, ParticipationFlags, + BeaconStateError, ChainSpec, Epoch, ParticipationFlags, }; use arbitrary::Arbitrary; use safe_arith::SafeArith; @@ -282,15 +282,3 @@ impl ProgressiveBalancesCache { .ok_or(BeaconStateError::ProgressiveBalancesCacheNotInitialized) } } - -/// `ProgressiveBalancesCache` is only enabled from `Altair` as it uses Altair-specific logic. -pub fn is_progressive_balances_enabled(state: &BeaconState) -> bool { - match state { - BeaconState::Base(_) => false, - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => true, - } -} diff --git a/consensus/types/src/builder_bid.rs b/consensus/types/src/builder_bid.rs index 9885f78474f..3ea5e3273fb 100644 --- a/consensus/types/src/builder_bid.rs +++ b/consensus/types/src/builder_bid.rs @@ -2,7 +2,8 @@ use crate::beacon_block_body::KzgCommitments; use crate::{ ChainSpec, EthSpec, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderRef, - ExecutionPayloadHeaderRefMut, ForkName, ForkVersionDeserialize, SignedRoot, Uint256, + ExecutionPayloadHeaderRefMut, FeatureName, ForkName, ForkVersionDeserialize, SignedRoot, + Uint256, }; use bls::PublicKeyBytes; use bls::Signature; @@ -11,7 +12,15 @@ use superstruct::superstruct; use tree_hash_derive::TreeHash; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone), serde(bound = "E: EthSpec", deny_unknown_fields) @@ -31,7 +40,7 @@ pub struct BuilderBid { pub header: ExecutionPayloadHeaderDeneb, #[superstruct(only(Electra), partial_getter(rename = "header_electra"))] pub header: ExecutionPayloadHeaderElectra, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] pub blob_kzg_commitments: KzgCommitments, #[serde(with = "serde_utils::quoted_u256")] pub value: Uint256, diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index b0346a14ef8..23e25ba2fbb 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -303,22 +303,13 @@ impl ChainSpec { /// Returns the name of the fork which is active at `epoch`. pub fn fork_name_at_epoch(&self, epoch: Epoch) -> ForkName { - match self.electra_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Electra, - _ => match self.deneb_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Deneb, - _ => match self.capella_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Capella, - _ => match self.bellatrix_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Bellatrix, - _ => match self.altair_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Altair, - _ => ForkName::Base, - }, - }, - }, - }, + for (fork_name, _) in FORK_ORDER.iter().copied().rev() { + match self.fork_epoch(fork_name) { + Some(fork_epoch) if epoch >= fork_epoch => return fork_name, + _ => continue, + } } + ForkName::Base } /// Returns the fork version for a named fork. @@ -346,9 +337,10 @@ impl ChainSpec { } pub fn inactivity_penalty_quotient_for_fork(&self, fork_name: ForkName) -> u64 { - if fork_name >= ForkName::Bellatrix { + // TODO(superstruct_features) Is this a better pattern? + if fork_name.has_feature(FeatureName::Bellatrix) { self.inactivity_penalty_quotient_bellatrix - } else if fork_name >= ForkName::Altair { + } else if fork_name.has_feature(FeatureName::Altair) { self.inactivity_penalty_quotient_altair } else { self.inactivity_penalty_quotient @@ -361,9 +353,9 @@ impl ChainSpec { state: &BeaconState, ) -> u64 { let fork_name = state.fork_name_unchecked(); - if fork_name >= ForkName::Bellatrix { + if fork_name.has_feature(FeatureName::Bellatrix) { self.proportional_slashing_multiplier_bellatrix - } else if fork_name >= ForkName::Altair { + } else if fork_name.has_feature(FeatureName::Altair) { self.proportional_slashing_multiplier_altair } else { self.proportional_slashing_multiplier @@ -376,11 +368,11 @@ impl ChainSpec { state: &BeaconState, ) -> u64 { let fork_name = state.fork_name_unchecked(); - if fork_name >= ForkName::Electra { + if fork_name.has_feature(FeatureName::Electra) { self.min_slashing_penalty_quotient_electra - } else if fork_name >= ForkName::Bellatrix { + } else if fork_name.has_feature(FeatureName::Bellatrix) { self.min_slashing_penalty_quotient_bellatrix - } else if fork_name >= ForkName::Altair { + } else if fork_name.has_feature(FeatureName::Altair) { self.min_slashing_penalty_quotient_altair } else { self.min_slashing_penalty_quotient @@ -546,7 +538,7 @@ impl ChainSpec { } pub fn max_blocks_by_root_request(&self, fork_name: ForkName) -> usize { - if fork_name >= ForkName::Deneb { + if fork_name.has_feature(FeatureName::Deneb) { self.max_blocks_by_root_request_deneb } else { self.max_blocks_by_root_request @@ -554,7 +546,7 @@ impl ChainSpec { } pub fn max_request_blocks(&self, fork_name: ForkName) -> usize { - if fork_name >= ForkName::Deneb { + if fork_name.has_feature(FeatureName::Deneb) { self.max_request_blocks_deneb as usize } else { self.max_request_blocks as usize diff --git a/consensus/types/src/config_and_preset.rs b/consensus/types/src/config_and_preset.rs index 6fc6e0642ea..3d1ea426466 100644 --- a/consensus/types/src/config_and_preset.rs +++ b/consensus/types/src/config_and_preset.rs @@ -1,6 +1,6 @@ use crate::{ consts::altair, consts::deneb, AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, - ChainSpec, Config, DenebPreset, ElectraPreset, EthSpec, ForkName, + ChainSpec, Config, DenebPreset, ElectraPreset, EthSpec, FeatureName, ForkName, }; use maplit::hashmap; use serde::{Deserialize, Serialize}; @@ -12,7 +12,15 @@ use superstruct::superstruct; /// /// Mostly useful for the API. #[superstruct( - variants(Capella, Deneb, Electra), + feature(Capella), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes(derive(Serialize, Deserialize, Debug, PartialEq, Clone)) )] #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] @@ -29,10 +37,10 @@ pub struct ConfigAndPreset { pub bellatrix_preset: BellatrixPreset, #[serde(flatten)] pub capella_preset: CapellaPreset, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] #[serde(flatten)] pub deneb_preset: DenebPreset, - #[superstruct(only(Electra))] + #[superstruct(feature(Electra))] #[serde(flatten)] pub electra_preset: ElectraPreset, /// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks. diff --git a/consensus/types/src/execution_payload.rs b/consensus/types/src/execution_payload.rs index 0946b9ecffa..d7a6b314ce7 100644 --- a/consensus/types/src/execution_payload.rs +++ b/consensus/types/src/execution_payload.rs @@ -15,7 +15,15 @@ pub type Transactions = VariableList< pub type Withdrawals = VariableList::MaxWithdrawalsPerPayload>; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Default, @@ -81,12 +89,12 @@ pub struct ExecutionPayload { pub block_hash: ExecutionBlockHash, #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] pub transactions: Transactions, - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] pub withdrawals: Withdrawals, - #[superstruct(only(Deneb, Electra), partial_getter(copy))] + #[superstruct(feature(Deneb))] #[serde(with = "serde_utils::quoted_u64")] pub blob_gas_used: u64, - #[superstruct(only(Deneb, Electra), partial_getter(copy))] + #[superstruct(feature(Deneb))] #[serde(with = "serde_utils::quoted_u64")] pub excess_blob_gas: u64, #[superstruct(only(Electra))] @@ -197,14 +205,3 @@ impl ForkVersionDeserialize for ExecutionPayload { }) } } - -impl ExecutionPayload { - pub fn fork_name(&self) -> ForkName { - match self { - ExecutionPayload::Bellatrix(_) => ForkName::Bellatrix, - ExecutionPayload::Capella(_) => ForkName::Capella, - ExecutionPayload::Deneb(_) => ForkName::Deneb, - ExecutionPayload::Electra(_) => ForkName::Electra, - } - } -} diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index 324d7b97472..c9837a83cf8 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -8,7 +8,15 @@ use tree_hash::TreeHash; use tree_hash_derive::TreeHash; #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Default, @@ -77,14 +85,14 @@ pub struct ExecutionPayloadHeader { pub block_hash: ExecutionBlockHash, #[superstruct(getter(copy))] pub transactions_root: Hash256, - #[superstruct(only(Capella, Deneb, Electra))] + #[superstruct(feature(Capella))] #[superstruct(getter(copy))] pub withdrawals_root: Hash256, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] #[serde(with = "serde_utils::quoted_u64")] #[superstruct(getter(copy))] pub blob_gas_used: u64, - #[superstruct(only(Deneb, Electra))] + #[superstruct(feature(Deneb))] #[serde(with = "serde_utils::quoted_u64")] #[superstruct(getter(copy))] pub excess_blob_gas: u64, diff --git a/consensus/types/src/feature_name.rs b/consensus/types/src/feature_name.rs new file mode 100644 index 00000000000..6ceabff3d60 --- /dev/null +++ b/consensus/types/src/feature_name.rs @@ -0,0 +1,18 @@ +// A list of all individual features that are available. +// We can gate parts of the code behind checks that ensure a feature is active. +// +// For now, older Forks have a single "super-feature" which contains all features associated with +// that Fork. It may be worth splitting these up at a later time. +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum FeatureName { + // Altair. + Altair, + // Bellatrix. + Bellatrix, + // Capella. + Capella, + // Deneb. + Deneb, + // Electra. + Electra, +} diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork_name.rs index 5cc66214733..2a12805941a 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork_name.rs @@ -1,12 +1,12 @@ -use crate::{ChainSpec, Epoch}; +use crate::fork_order::FORK_ORDER; +use crate::{ChainSpec, Epoch, FeatureName}; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use std::cmp::{Ord, Ordering}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; -#[derive( - Debug, Clone, Copy, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, -)] +#[derive(Debug, Clone, Copy, Decode, Encode, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(try_from = "String")] #[serde(into = "String")] #[ssz(enum_behaviour = "tag")] @@ -19,21 +19,48 @@ pub enum ForkName { Electra, } +impl PartialOrd for ForkName { + fn partial_cmp(&self, other: &ForkName) -> Option { + let self_idx = FORK_ORDER + .iter() + .position(|(fork_name, _)| fork_name == self); + let other_idx = FORK_ORDER + .iter() + .position(|(fork_name, _)| fork_name == other); + + // Forks that are not enabled will come back as `None`. Treat them as greater than all + // enabled forks (and equal to each other). + match (self_idx, other_idx) { + (None, None) => None, // incomparable + (None, Some(_)) => Some(Ordering::Greater), + (Some(_), None) => Some(Ordering::Less), + (Some(i), Some(j)) => Some(i.cmp(&j)), + } + } +} + impl ForkName { pub fn list_all() -> Vec { - vec![ - ForkName::Base, - ForkName::Altair, - ForkName::Bellatrix, - ForkName::Capella, - ForkName::Deneb, - ForkName::Electra, - ] + FORK_ORDER.iter().map(|(fork, _)| *fork).collect() } - pub fn latest() -> ForkName { - // This unwrap is safe as long as we have 1+ forks. It is tested below. - *ForkName::list_all().last().unwrap() + pub const fn latest() -> ForkName { + #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] + FORK_ORDER[FORK_ORDER.len() - 1].0 + } + + pub fn list_all_enabled_features(self) -> Vec { + let mut all_features = vec![]; + for (fork, features) in FORK_ORDER { + if *fork <= self { + all_features.extend(features.iter()); + } + } + all_features + } + + pub fn has_feature(self, feature: FeatureName) -> bool { + self.list_all_enabled_features().contains(&feature) } /// Set the activation slots in the given `ChainSpec` so that the fork named by `self` @@ -283,4 +310,51 @@ mod test { assert!(prev_fork < fork); } } + + #[test] + fn check_fork_name_enabled_features() { + let base = ForkName::Base; + let altair = ForkName::Altair; + let bellatrix = ForkName::Bellatrix; + let capella = ForkName::Capella; + let deneb = ForkName::Deneb; + let electra = ForkName::Electra; + + assert_eq!(base.list_all_enabled_features(), vec![]); + assert_eq!( + altair.list_all_enabled_features(), + vec![FeatureName::Altair] + ); + assert_eq!( + bellatrix.list_all_enabled_features(), + vec![FeatureName::Altair, FeatureName::Bellatrix] + ); + assert_eq!( + capella.list_all_enabled_features(), + vec![ + FeatureName::Altair, + FeatureName::Bellatrix, + FeatureName::Capella + ] + ); + assert_eq!( + deneb.list_all_enabled_features(), + vec![ + FeatureName::Altair, + FeatureName::Bellatrix, + FeatureName::Capella, + FeatureName::Deneb + ] + ); + assert_eq!( + electra.list_all_enabled_features(), + vec![ + FeatureName::Altair, + FeatureName::Bellatrix, + FeatureName::Capella, + FeatureName::Deneb, + FeatureName::Electra + ] + ); + } } diff --git a/consensus/types/src/fork_order.rs b/consensus/types/src/fork_order.rs new file mode 100644 index 00000000000..08db89336ad --- /dev/null +++ b/consensus/types/src/fork_order.rs @@ -0,0 +1,36 @@ +use crate::{FeatureName, ForkName}; +use superstruct::superstruct; + +#[superstruct(variants_and_features_decl = "FORK_ORDER")] +pub const FORK_ORDER: &[(ForkName, &[FeatureName])] = &[ + (ForkName::Base, &[]), + (ForkName::Altair, &[FeatureName::Altair]), + (ForkName::Bellatrix, &[FeatureName::Bellatrix]), + (ForkName::Capella, &[FeatureName::Capella]), + (ForkName::Deneb, &[FeatureName::Deneb]), + (ForkName::Electra, &[FeatureName::Electra]), +]; + +#[superstruct(feature_dependencies_decl = "FEATURE_DEPENDENCIES")] +pub const FEATURE_DEPENDENCIES: &[(FeatureName, &[FeatureName])] = &[ + (FeatureName::Altair, &[]), + (FeatureName::Bellatrix, &[FeatureName::Altair]), + (FeatureName::Capella, &[FeatureName::Bellatrix]), + (FeatureName::Deneb, &[FeatureName::Capella]), + (FeatureName::Electra, &[FeatureName::Deneb]), +]; + +#[cfg(test)] +mod test { + use super::*; + use itertools::Itertools; + + #[test] + fn partial_ord_sanity_check() { + for (fork_a, fork_b) in FORK_ORDER.iter().map(|(fork, _)| fork).tuple_windows() { + assert!(fork_a < fork_b, "{fork_a} < {fork_b}"); + assert_eq!(fork_a, fork_a); + assert_eq!(fork_b, fork_b); + } + } +} diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 5c521d98af9..f754716863e 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -44,9 +44,11 @@ pub mod execution_block_hash; pub mod execution_layer_withdrawal_request; pub mod execution_payload; pub mod execution_payload_header; +pub mod feature_name; pub mod fork; pub mod fork_data; pub mod fork_name; +pub mod fork_order; pub mod fork_versioned_response; pub mod graffiti; pub mod historical_batch; @@ -162,10 +164,12 @@ pub use crate::execution_payload_header::{ ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, }; +pub use crate::feature_name::FeatureName; pub use crate::fork::Fork; pub use crate::fork_context::ForkContext; pub use crate::fork_data::ForkData; pub use crate::fork_name::{ForkName, InconsistentFork}; +pub use crate::fork_order::FORK_ORDER; pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedResponse}; pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN}; pub use crate::historical_batch::HistoricalBatch; diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client_header.rs index 213ec90f955..45a309e381a 100644 --- a/consensus/types/src/light_client_header.rs +++ b/consensus/types/src/light_client_header.rs @@ -1,5 +1,4 @@ use crate::ChainSpec; -use crate::ForkName; use crate::ForkVersionDeserialize; use crate::{light_client_update::*, BeaconBlockBody}; use crate::{ @@ -7,6 +6,7 @@ use crate::{ FixedVector, Hash256, SignedBeaconBlock, }; use crate::{BeaconBlockHeader, ExecutionPayloadHeader}; +use crate::{FeatureName, ForkName}; use derivative::Derivative; use serde::{Deserialize, Serialize}; use ssz::Decode; @@ -17,7 +17,15 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Altair, Capella, Deneb), + feature(Altair), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Debug, @@ -55,7 +63,7 @@ pub struct LightClientHeader { #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_header_deneb"))] pub execution: ExecutionPayloadHeaderDeneb, - #[superstruct(only(Capella, Deneb))] + #[superstruct(feature(Capella))] pub execution_branch: FixedVector, #[ssz(skip_serializing, skip_deserializing)] diff --git a/consensus/types/src/payload.rs b/consensus/types/src/payload.rs index 80a70c171f5..85a3437caec 100644 --- a/consensus/types/src/payload.rs +++ b/consensus/types/src/payload.rs @@ -110,7 +110,15 @@ pub trait AbstractExecPayload: } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Debug, @@ -447,7 +455,15 @@ impl TryFrom> for FullPayload { } #[superstruct( - variants(Bellatrix, Capella, Deneb, Electra), + feature(Bellatrix), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Debug, diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/signed_beacon_block.rs index 4d3279a7f77..2a3b6f871fe 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/signed_beacon_block.rs @@ -37,7 +37,14 @@ impl From for Hash256 { /// A `BeaconBlock` and a signature from its proposer. #[superstruct( - variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), + variants_and_features_from = "FORK_ORDER", + feature_dependencies = "FEATURE_DEPENDENCIES", + variant_type(name = "ForkName", getter = "fork_name_outer"), + feature_type( + name = "FeatureName", + list = "list_all_features", + check = "has_feature" + ), variant_attributes( derive( Debug, diff --git a/consensus/types/src/voluntary_exit.rs b/consensus/types/src/voluntary_exit.rs index 4c7c16757ed..ce7be8a9003 100644 --- a/consensus/types/src/voluntary_exit.rs +++ b/consensus/types/src/voluntary_exit.rs @@ -1,6 +1,6 @@ use crate::{ - test_utils::TestRandom, ChainSpec, Domain, Epoch, ForkName, Hash256, SecretKey, SignedRoot, - SignedVoluntaryExit, + test_utils::TestRandom, ChainSpec, Domain, Epoch, FeatureName, ForkName, Hash256, SecretKey, + SignedRoot, SignedVoluntaryExit, }; use serde::{Deserialize, Serialize}; @@ -41,12 +41,11 @@ impl VoluntaryExit { spec: &ChainSpec, ) -> SignedVoluntaryExit { let fork_name = spec.fork_name_at_epoch(self.epoch); - let fork_version = match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { - spec.fork_version_for_name(fork_name) - } - // EIP-7044 - ForkName::Deneb | ForkName::Electra => spec.fork_version_for_name(ForkName::Capella), + // EIP-7044 + let fork_version = if fork_name.has_feature(FeatureName::Deneb) { + spec.fork_version_for_name(ForkName::Capella) + } else { + spec.fork_version_for_name(fork_name) }; let domain = spec.compute_domain(Domain::VoluntaryExit, fork_version, genesis_validators_root); diff --git a/feature_deps.bak b/feature_deps.bak new file mode 100644 index 00000000000..52bc42a7364 --- /dev/null +++ b/feature_deps.bak @@ -0,0 +1 @@ +[["Altair",[]],["Bellatrix",["Altair"]],["Capella",["Bellatrix"]],["Deneb",["Capella"]],["Electra",["Deneb"]]] \ No newline at end of file diff --git a/fork_order.bak b/fork_order.bak new file mode 100644 index 00000000000..eaaf24a0a11 --- /dev/null +++ b/fork_order.bak @@ -0,0 +1 @@ +[["Base"],[]],["Altair",["Altair"]],["Bellatrix",["Bellatrix"]],["Capella",["Capella"]],["Deneb",["Deneb"]],["Electra",["Electra"]]] diff --git a/testing/ef_tests/src/cases/epoch_processing.rs b/testing/ef_tests/src/cases/epoch_processing.rs index c4c592e4cf2..d5cd690ce7e 100644 --- a/testing/ef_tests/src/cases/epoch_processing.rs +++ b/testing/ef_tests/src/cases/epoch_processing.rs @@ -19,7 +19,7 @@ use state_processing::per_epoch_processing::{ }; use state_processing::EpochProcessingError; use std::marker::PhantomData; -use types::BeaconState; +use types::{BeaconState, FeatureName}; #[derive(Debug, Clone, Default, Deserialize)] pub struct Metadata { @@ -91,47 +91,35 @@ type_name!(ParticipationFlagUpdates, "participation_flag_updates"); impl EpochTransition for JustificationAndFinalization { fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Base(_) => { - let mut validator_statuses = base::ValidatorStatuses::new(state, spec)?; - validator_statuses.process_attestations(state)?; - let justification_and_finalization_state = - base::process_justification_and_finalization( - state, - &validator_statuses.total_balances, - spec, - )?; - justification_and_finalization_state.apply_changes_to_state(state); - Ok(()) - } - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => { - initialize_progressive_balances_cache(state, spec)?; - let justification_and_finalization_state = - altair::process_justification_and_finalization(state)?; - justification_and_finalization_state.apply_changes_to_state(state); - Ok(()) - } + if state.has_feature(FeatureName::Altair) { + initialize_progressive_balances_cache(state, spec)?; + let justification_and_finalization_state = + altair::process_justification_and_finalization(state)?; + justification_and_finalization_state.apply_changes_to_state(state); + Ok(()) + } else { + let mut validator_statuses = base::ValidatorStatuses::new(state, spec)?; + validator_statuses.process_attestations(state)?; + let justification_and_finalization_state = + base::process_justification_and_finalization( + state, + &validator_statuses.total_balances, + spec, + )?; + justification_and_finalization_state.apply_changes_to_state(state); + Ok(()) } } } impl EpochTransition for RewardsAndPenalties { fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Base(_) => { - let mut validator_statuses = base::ValidatorStatuses::new(state, spec)?; - validator_statuses.process_attestations(state)?; - base::process_rewards_and_penalties(state, &validator_statuses, spec) - } - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => altair::process_rewards_and_penalties_slow(state, spec), + if state.has_feature(FeatureName::Altair) { + altair::process_rewards_and_penalties_slow(state, spec) + } else { + let mut validator_statuses = base::ValidatorStatuses::new(state, spec)?; + validator_statuses.process_attestations(state)?; + base::process_rewards_and_penalties(state, &validator_statuses, spec) } } } @@ -150,24 +138,18 @@ impl EpochTransition for RegistryUpdates { impl EpochTransition for Slashings { fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Base(_) => { - let mut validator_statuses = base::ValidatorStatuses::new(state, spec)?; - validator_statuses.process_attestations(state)?; - process_slashings( - state, - validator_statuses.total_balances.current_epoch(), - spec, - )?; - } - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => { - process_slashings_slow(state, spec)?; - } - }; + if state.has_feature(FeatureName::Altair) { + process_slashings_slow(state, spec)?; + } else { + let mut validator_statuses = base::ValidatorStatuses::new(state, spec)?; + validator_statuses.process_attestations(state)?; + process_slashings( + state, + validator_statuses.total_balances.current_epoch(), + spec, + )?; + } + Ok(()) } } @@ -202,22 +184,20 @@ impl EpochTransition for RandaoMixesReset { impl EpochTransition for HistoricalRootsUpdate { fn run(state: &mut BeaconState, _spec: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Base(_) | BeaconState::Altair(_) | BeaconState::Bellatrix(_) => { - process_historical_roots_update(state) - } - _ => Ok(()), + if state.has_feature(FeatureName::Capella) { + Ok(()) + } else { + process_historical_roots_update(state) } } } impl EpochTransition for HistoricalSummariesUpdate { fn run(state: &mut BeaconState, _spec: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => { - process_historical_summaries_update(state) - } - _ => Ok(()), + if state.has_feature(FeatureName::Capella) { + process_historical_summaries_update(state) + } else { + Ok(()) } } } @@ -234,39 +214,30 @@ impl EpochTransition for ParticipationRecordUpdates { impl EpochTransition for SyncCommitteeUpdates { fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Base(_) => Ok(()), - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => altair::process_sync_committee_updates(state, spec), + if state.has_feature(FeatureName::Altair) { + altair::process_sync_committee_updates(state, spec) + } else { + Ok(()) } } } impl EpochTransition for InactivityUpdates { fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Base(_) => Ok(()), - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => altair::process_inactivity_updates_slow(state, spec), + if state.has_feature(FeatureName::Altair) { + altair::process_inactivity_updates_slow(state, spec) + } else { + Ok(()) } } } impl EpochTransition for ParticipationFlagUpdates { fn run(state: &mut BeaconState, _: &ChainSpec) -> Result<(), EpochProcessingError> { - match state { - BeaconState::Base(_) => Ok(()), - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => altair::process_participation_flag_updates(state), + if state.has_feature(FeatureName::Altair) { + altair::process_participation_flag_updates(state) + } else { + Ok(()) } } } @@ -304,23 +275,18 @@ impl> Case for EpochProcessing { } fn is_enabled_for_fork(fork_name: ForkName) -> bool { - match fork_name { + // No phase0 tests for Altair and later. + if fork_name.has_feature(FeatureName::Capella) { + T::name() != "participation_record_updates" && T::name() != "historical_roots_update" + } else if fork_name.has_feature(FeatureName::Altair) { + T::name() != "participation_record_updates" + && T::name() != "historical_summaries_update" + } else { // No Altair tests for genesis fork. - ForkName::Base => { - T::name() != "sync_committee_updates" - && T::name() != "inactivity_updates" - && T::name() != "participation_flag_updates" - && T::name() != "historical_summaries_update" - } - // No phase0 tests for Altair and later. - ForkName::Altair | ForkName::Bellatrix => { - T::name() != "participation_record_updates" - && T::name() != "historical_summaries_update" - } - ForkName::Capella | ForkName::Deneb | ForkName::Electra => { - T::name() != "participation_record_updates" - && T::name() != "historical_roots_update" - } + T::name() != "sync_committee_updates" + && T::name() != "inactivity_updates" + && T::name() != "participation_flag_updates" + && T::name() != "historical_summaries_update" } } diff --git a/testing/ef_tests/src/cases/operations.rs b/testing/ef_tests/src/cases/operations.rs index 158f2334dc3..8a782d973ae 100644 --- a/testing/ef_tests/src/cases/operations.rs +++ b/testing/ef_tests/src/cases/operations.rs @@ -22,7 +22,7 @@ use std::fmt::Debug; use types::{ Attestation, AttesterSlashing, BeaconBlock, BeaconBlockBody, BeaconBlockBodyBellatrix, BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconState, BlindedPayload, Deposit, - ExecutionPayload, FullPayload, ProposerSlashing, SignedBlsToExecutionChange, + ExecutionPayload, FeatureName, FullPayload, ProposerSlashing, SignedBlsToExecutionChange, SignedVoluntaryExit, SyncAggregate, }; @@ -90,29 +90,24 @@ impl Operation for Attestation { ) -> Result<(), BlockProcessingError> { initialize_epoch_cache(state, spec)?; let mut ctxt = ConsensusContext::new(state.slot()); - match state { - BeaconState::Base(_) => base::process_attestations( + if state.has_feature(FeatureName::Altair) { + initialize_progressive_balances_cache(state, spec)?; + altair_deneb::process_attestation( + state, + self, + 0, + &mut ctxt, + VerifySignatures::True, + spec, + ) + } else { + base::process_attestations( state, &[self.clone()], VerifySignatures::True, &mut ctxt, spec, - ), - BeaconState::Altair(_) - | BeaconState::Bellatrix(_) - | BeaconState::Capella(_) - | BeaconState::Deneb(_) - | BeaconState::Electra(_) => { - initialize_progressive_balances_cache(state, spec)?; - altair_deneb::process_attestation( - state, - self, - 0, - &mut ctxt, - VerifySignatures::True, - spec, - ) - } + ) } } } diff --git a/validator_client/src/validator_store.rs b/validator_client/src/validator_store.rs index f3bdc2c0f69..3552ac5d8f8 100644 --- a/validator_client/src/validator_store.rs +++ b/validator_client/src/validator_store.rs @@ -19,7 +19,7 @@ use task_executor::TaskExecutor; use types::{ attestation::Error as AttestationError, graffiti::GraffitiString, AbstractExecPayload, Address, AggregateAndProof, Attestation, BeaconBlock, BlindedPayload, ChainSpec, ContributionAndProof, - Domain, Epoch, EthSpec, Fork, ForkName, Graffiti, Hash256, PublicKeyBytes, SelectionProof, + Domain, Epoch, EthSpec, FeatureName, Fork, Graffiti, Hash256, PublicKeyBytes, SelectionProof, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, SignedRoot, SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncAggregatorSelectionData, SyncCommitteeContribution, SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId, @@ -359,17 +359,13 @@ impl ValidatorStore { fn signing_context(&self, domain: Domain, signing_epoch: Epoch) -> SigningContext { if domain == Domain::VoluntaryExit { - match self.spec.fork_name_at_epoch(signing_epoch) { - ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { - SigningContext { - domain, - epoch: signing_epoch, - fork: self.fork(signing_epoch), - genesis_validators_root: self.genesis_validators_root, - } - } - // EIP-7044 - ForkName::Deneb | ForkName::Electra => SigningContext { + // EIP-7044 + if self + .spec + .fork_name_at_epoch(signing_epoch) + .has_feature(FeatureName::Deneb) + { + SigningContext { domain, epoch: signing_epoch, fork: Fork { @@ -378,7 +374,14 @@ impl ValidatorStore { epoch: signing_epoch, }, genesis_validators_root: self.genesis_validators_root, - }, + } + } else { + SigningContext { + domain, + epoch: signing_epoch, + fork: self.fork(signing_epoch), + genesis_validators_root: self.genesis_validators_root, + } } } else { SigningContext {