Compare commits

...

11 Commits

Author SHA1 Message Date
Mac L
5a4be377d4 Add even more 2024-05-02 18:07:56 +10:00
Mac L
d9a0c3d6c2 Add more 2024-05-02 16:06:28 +10:00
Mac L
689cdaef61 Rename to has_feature 2024-05-02 11:05:03 +10:00
Mac L
b68d08c847 Start replacing boilerplate 2024-05-01 18:12:38 +10:00
Mac L
4e3839ec98 Fix errors 2024-05-01 15:52:08 +10:00
Mac L
9a49efc854 Merge branch 'unstable' into superstruct-features 2024-05-01 15:40:28 +10:00
Mac L
2bac4b8a19 Add syntax to more types 2024-05-01 13:09:42 +10:00
Mac L
5c80d92b22 Use 'super-features' for past forks 2024-04-23 10:44:02 +10:00
Michael Sproul
3b92e0bd9a Reimplement fork_name_at_epoch 2024-04-22 16:49:04 +10:00
Michael Sproul
e862e5613c Implement PartialOrd for ForkName 2024-04-22 15:50:59 +10:00
Michael Sproul
7b8a7c0497 Start prototyping superstruct features 2024-04-19 19:03:25 +10:00
51 changed files with 825 additions and 701 deletions

5
Cargo.lock generated
View File

@@ -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",
]

View File

@@ -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"

View File

@@ -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<T: BeaconChainTypes> BeaconChain<T> {
pub fn compute_attestation_rewards(
@@ -51,13 +51,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.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)
}
}

View File

@@ -733,6 +733,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.map(|slot| slot.epoch(T::EthSpec::slots_per_epoch()))
}
/// Returns the latest fork for the current slot.
pub fn current_fork(&self) -> Result<ForkName, Error> {
Ok(self.spec.fork_name_at_slot::<T::EthSpec>(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<T: BeaconChainTypes> BeaconChain<T> {
bls_to_execution_change: SignedBlsToExecutionChange,
) -> Result<ObservationOutcome<SignedBlsToExecutionChange, T::EthSpec>, 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<T: BeaconChainTypes> BeaconChain<T> {
})
}
/// Check if the current slot is greater than or equal to the Capella fork epoch.
pub fn current_slot_is_post_capella(&self) -> Result<bool, Error> {
let current_fork = self.spec.fork_name_at_slot::<T::EthSpec>(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<T: BeaconChainTypes> BeaconChain<T> {
// 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<T: BeaconChainTypes> BeaconChain<T> {
payload_attributes
} else {
let prepare_slot_fork = self.spec.fork_name_at_slot::<T::EthSpec>(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<T: BeaconChainTypes> BeaconChain<T> {
.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)
}
}
}

View File

@@ -411,19 +411,16 @@ pub fn get_execution_payload<T: BeaconChainTypes>(
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

View File

@@ -877,15 +877,12 @@ where
&self.spec,
));
let block_contents: SignedBlockContentsTuple<E> = match *signed_block {
SignedBeaconBlock::Base(_)
| SignedBeaconBlock::Altair(_)
| SignedBeaconBlock::Bellatrix(_)
| SignedBeaconBlock::Capella(_) => (signed_block, None),
SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => {
let block_contents: SignedBlockContentsTuple<E> =
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<E> = match *signed_block {
SignedBeaconBlock::Base(_)
| SignedBeaconBlock::Altair(_)
| SignedBeaconBlock::Bellatrix(_)
| SignedBeaconBlock::Capella(_) => (signed_block, None),
SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => {
let block_contents: SignedBlockContentsTuple<E> =
if signed_block.has_feature(FeatureName::Deneb) {
(signed_block, block_response.blob_items)
}
};
} else {
(signed_block, None)
};
(block_contents, pre_state)
}

View File

@@ -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()

View File

@@ -0,0 +1,2 @@
// Dummy build.rs file to enable OUT_DIR usage by superstruct
fn main() {}

View File

@@ -45,8 +45,8 @@ pub fn calculate_execution_block_hash<E: EthSpec>(
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,
);

View File

@@ -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<E: EthSpec> {
#[serde(rename = "hash")]
pub block_hash: ExecutionBlockHash,
pub transactions: Vec<Transaction>,
#[superstruct(only(Capella, Deneb, Electra))]
#[superstruct(feature(Capella))]
pub withdrawals: Vec<JsonWithdrawal>,
#[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<E: EthSpec> {
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload: ExecutionPayloadElectra<E>,
pub block_value: Uint256,
#[superstruct(only(Deneb, Electra))]
#[superstruct(feature(Deneb))]
pub blobs_bundle: BlobsBundle<E>,
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
#[superstruct(feature(Deneb), partial_getter(copy))]
pub should_override_builder: bool,
}

View File

@@ -884,30 +884,34 @@ impl HttpJsonRpc {
) -> Result<GetPayloadResponse<E>, Error> {
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
match fork_name {
ForkName::Bellatrix => {
let response: JsonGetPayloadResponseV1<E> = 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<E> = 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<E> = 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<E> = 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<GetPayloadResponse<E>, Error> {
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
match fork_name {
ForkName::Deneb => {
let response: JsonGetPayloadResponseV3<E> = 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<E> = 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<E> = 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<E> = 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<GetPayloadResponse<E>, 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
))),
)))
}
}

View File

@@ -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<E>,
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload: &'block ExecutionPayloadElectra<E>,
#[superstruct(only(Deneb, Electra))]
#[superstruct(feature(Deneb))]
pub versioned_hashes: Vec<VersionedHash>,
#[superstruct(only(Deneb, Electra))]
#[superstruct(feature(Deneb))]
pub parent_beacon_block_root: Hash256,
}

View File

@@ -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<E: EthSpec>(
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 {

View File

@@ -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<E: EthSpec>(
@@ -11,11 +11,8 @@ pub fn build_block_contents<E: EthSpec>(
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<E: EthSpec>(
blobs,
}),
))
} else {
Ok(ProduceBlockV3Response::Full(FullBlockContents::Block(
block.block,
)))
}
},
}
}
}

View File

@@ -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<T: BeaconChainTypes>(
.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

View File

@@ -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<T: BeaconChainTypes, B: IntoGossipVerifiedBlockConten
info!(log, "Signed block published to network via HTTP API"; "slot" => 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(())
};

View File

@@ -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)
}

View File

@@ -0,0 +1,2 @@
// Dummy build.rs file to enable OUT_DIR usage by superstruct
fn main() {}

View File

@@ -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<PendingAttestation<E>, E::MaxPendingAttestations>,
// Participation (Altair and later)
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
pub previous_epoch_participation: List<ParticipationFlags, E::ValidatorRegistryLimit>,
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
pub current_epoch_participation: List<ParticipationFlags, E::ValidatorRegistryLimit>,
// 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<u64, E::ValidatorRegistryLimit>,
// Light-client sync committees
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
pub current_sync_committee: Arc<SyncCommittee<E>>,
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
pub next_sync_committee: Arc<SyncCommittee<E>>,
// Execution
@@ -110,36 +117,36 @@ where
pub latest_execution_payload_header: ExecutionPayloadHeaderElectra<E>,
// 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<List<HistoricalSummary, E::HistoricalRootsLimit>>,
// 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<PendingBalanceDeposit, E::PendingBalanceDepositsLimit>,
#[superstruct(only(Electra))]
#[superstruct(feature(Electra))]
pub pending_partial_withdrawals:
List<PendingPartialWithdrawal, E::PendingPartialWithdrawalsLimit>,
#[superstruct(only(Electra))]
#[superstruct(feature(Electra))]
pub pending_consolidations: List<PendingConsolidation, E::PendingConsolidationsLimit>,
}

View File

@@ -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,
)?
};
(

View File

@@ -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<E: EthSpec>(
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);
}

View File

@@ -55,15 +55,12 @@ pub fn slash_validator<E: EthSpec>(
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.

View File

@@ -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<E: EthSpec>(
state: &mut BeaconState<E>,
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<E: EthSpec>(
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<E: EthSpec>(
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<E: EthSpec>(
state: &mut BeaconState<E>,
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)?;

View File

@@ -188,7 +188,7 @@ pub fn per_block_processing<E: EthSpec, Payload: AbstractExecPayload<E>>(
)?;
}
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<E: EthSpec, Payload: AbstractExecPayload<E>>(
/// 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<E: EthSpec>(state: &BeaconState<E>) -> 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<E: EthSpec, Payload: AbstractExecPayload<E>>(
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 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,
});
}
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 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;
}
}
// 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(())
}
// these shouldn't even be encountered but they're here for completeness
BeaconState::Base(_) | BeaconState::Altair(_) => 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;
}
Ok(())
} else {
Ok(())
}
}

View File

@@ -262,29 +262,22 @@ pub fn process_attestations<E: EthSpec, Payload: AbstractExecPayload<E>>(
ctxt: &mut ConsensusContext<E>,
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(())
}

View File

@@ -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(
let domain = if state.has_feature(FeatureName::Deneb) {
// EIP-7044
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(),
),
// EIP-7044
BeaconState::Deneb(_) | BeaconState::Electra(_) => spec.compute_domain(
Domain::VoluntaryExit,
spec.capella_fork_version,
state.genesis_validators_root(),
),
)
};
let message = exit.signing_root(domain);

View File

@@ -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)

View File

@@ -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<E: EthSpec>(
.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)
}
}

2
consensus/types/build.rs Normal file
View File

@@ -0,0 +1,2 @@
// Dummy build.rs file to enable OUT_DIR usage by superstruct
fn main() {}

View File

@@ -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<E>> 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(

View File

@@ -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<E: EthSpec, Payload: AbstractExecPayload<E> = FullPay
pub attestations: VariableList<Attestation<E>, E::MaxAttestations>,
pub deposits: VariableList<Deposit, E::MaxDeposits>,
pub voluntary_exits: VariableList<SignedVoluntaryExit, E::MaxVoluntaryExits>,
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
pub sync_aggregate: SyncAggregate<E>,
// 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<E: EthSpec, Payload: AbstractExecPayload<E> = 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<SignedBlsToExecutionChange, E::MaxBlsToExecutionChanges>,
#[superstruct(only(Deneb, Electra))]
#[superstruct(feature(Deneb))]
pub blob_kzg_commitments: KzgCommitments<E>,
#[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<E>> 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<E>> BeaconBlockBodyRef<'a, E, Payload> {

View File

@@ -206,7 +206,14 @@ impl From<BeaconStateHash> 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<u64, E::EpochsPerSlashingsVector>,
// Attestations (genesis fork only)
#[superstruct(only(Base))]
#[superstruct(feature(not(Altair)))]
#[test_random(default)]
pub previous_epoch_attestations: List<PendingAttestation<E>, E::MaxPendingAttestations>,
#[superstruct(only(Base))]
#[superstruct(feature(not(Altair)))]
#[test_random(default)]
pub current_epoch_attestations: List<PendingAttestation<E>, E::MaxPendingAttestations>,
// Participation (Altair and later)
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
#[test_random(default)]
pub previous_epoch_participation: List<ParticipationFlags, E::ValidatorRegistryLimit>,
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
#[test_random(default)]
pub current_epoch_participation: List<ParticipationFlags, E::ValidatorRegistryLimit>,
@@ -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<u64, E::ValidatorRegistryLimit>,
// Light-client sync committees
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
#[metastruct(exclude_from(tree_lists))]
pub current_sync_committee: Arc<SyncCommittee<E>>,
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))]
#[superstruct(feature(Altair))]
#[metastruct(exclude_from(tree_lists))]
pub next_sync_committee: Arc<SyncCommittee<E>>,
@@ -454,51 +461,51 @@ where
pub latest_execution_payload_header: ExecutionPayloadHeaderElectra<E>,
// 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<HistoricalSummary, E::HistoricalRootsLimit>,
// 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<PendingBalanceDeposit, E::PendingBalanceDepositsLimit>,
#[test_random(default)]
#[superstruct(only(Electra))]
#[superstruct(feature(Electra))]
pub pending_partial_withdrawals:
List<PendingPartialWithdrawal, E::PendingPartialWithdrawalsLimit>,
#[test_random(default)]
#[superstruct(only(Electra))]
#[superstruct(feature(Electra))]
pub pending_consolidations: List<PendingConsolidation, E::PendingConsolidationsLimit>,
// Caching (not in the spec)
@@ -628,20 +635,6 @@ impl<E: EthSpec> BeaconState<E> {
}
}
/// 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<E: EthSpec> BeaconState<E> {
///
/// Uses the current epoch committee cache, and will error if it isn't initialized.
pub fn get_activation_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
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

View File

@@ -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<E: EthSpec>(state: &BeaconState<E>) -> bool {
match state {
BeaconState::Base(_) => false,
BeaconState::Altair(_)
| BeaconState::Bellatrix(_)
| BeaconState::Capella(_)
| BeaconState::Deneb(_)
| BeaconState::Electra(_) => true,
}
}

View File

@@ -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<E: EthSpec> {
pub header: ExecutionPayloadHeaderDeneb<E>,
#[superstruct(only(Electra), partial_getter(rename = "header_electra"))]
pub header: ExecutionPayloadHeaderElectra<E>,
#[superstruct(only(Deneb, Electra))]
#[superstruct(feature(Deneb))]
pub blob_kzg_commitments: KzgCommitments<E>,
#[serde(with = "serde_utils::quoted_u256")]
pub value: Uint256,

View File

@@ -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<E>,
) -> 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<E>,
) -> 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

View File

@@ -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.

View File

@@ -15,7 +15,15 @@ pub type Transactions<E> = VariableList<
pub type Withdrawals<E> = VariableList<Withdrawal, <E as EthSpec>::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<E: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<E>,
#[superstruct(only(Capella, Deneb, Electra))]
#[superstruct(feature(Capella))]
pub withdrawals: Withdrawals<E>,
#[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<E: EthSpec> ForkVersionDeserialize for ExecutionPayload<E> {
})
}
}
impl<E: EthSpec> ExecutionPayload<E> {
pub fn fork_name(&self) -> ForkName {
match self {
ExecutionPayload::Bellatrix(_) => ForkName::Bellatrix,
ExecutionPayload::Capella(_) => ForkName::Capella,
ExecutionPayload::Deneb(_) => ForkName::Deneb,
ExecutionPayload::Electra(_) => ForkName::Electra,
}
}
}

View File

@@ -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<E: EthSpec> {
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,

View File

@@ -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,
}

View File

@@ -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<Ordering> {
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<ForkName> {
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<FeatureName> {
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
]
);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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<E: EthSpec> {
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_header_deneb"))]
pub execution: ExecutionPayloadHeaderDeneb<E>,
#[superstruct(only(Capella, Deneb))]
#[superstruct(feature(Capella))]
pub execution_branch: FixedVector<Hash256, ExecutionPayloadProofLen>,
#[ssz(skip_serializing, skip_deserializing)]

View File

@@ -110,7 +110,15 @@ pub trait AbstractExecPayload<E: EthSpec>:
}
#[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<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for FullPayload<E> {
}
#[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,

View File

@@ -37,7 +37,14 @@ impl From<SignedBeaconBlockHash> 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,

View File

@@ -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);

1
feature_deps.bak Normal file
View File

@@ -0,0 +1 @@
[["Altair",[]],["Bellatrix",["Altair"]],["Capella",["Bellatrix"]],["Deneb",["Capella"]],["Electra",["Deneb"]]]

1
fork_order.bak Normal file
View File

@@ -0,0 +1 @@
[["Base"],[]],["Altair",["Altair"]],["Bellatrix",["Bellatrix"]],["Capella",["Capella"]],["Deneb",["Deneb"]],["Electra",["Electra"]]]

View File

@@ -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<E: EthSpec> EpochTransition<E> for JustificationAndFinalization {
fn run(state: &mut BeaconState<E>, 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<E: EthSpec> EpochTransition<E> for RewardsAndPenalties {
fn run(state: &mut BeaconState<E>, 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<E: EthSpec> EpochTransition<E> for RegistryUpdates {
impl<E: EthSpec> EpochTransition<E> for Slashings {
fn run(state: &mut BeaconState<E>, 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<E: EthSpec> EpochTransition<E> for RandaoMixesReset {
impl<E: EthSpec> EpochTransition<E> for HistoricalRootsUpdate {
fn run(state: &mut BeaconState<E>, _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<E: EthSpec> EpochTransition<E> for HistoricalSummariesUpdate {
fn run(state: &mut BeaconState<E>, _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<E: EthSpec> EpochTransition<E> for ParticipationRecordUpdates {
impl<E: EthSpec> EpochTransition<E> for SyncCommitteeUpdates {
fn run(state: &mut BeaconState<E>, 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<E: EthSpec> EpochTransition<E> for InactivityUpdates {
fn run(state: &mut BeaconState<E>, 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<E: EthSpec> EpochTransition<E> for ParticipationFlagUpdates {
fn run(state: &mut BeaconState<E>, _: &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<E: EthSpec, T: EpochTransition<E>> Case for EpochProcessing<E, T> {
}
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"
}
}

View File

@@ -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<E: EthSpec> Operation<E> for Attestation<E> {
) -> 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,
)
}
)
}
}
}

View File

@@ -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<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
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<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
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 {