Add Electra fork boilerplate (#5122)

* Add Electra fork boilerplate

* Remove electra from spec tests

* Fix tests

* Remove sneaky log file

* Fix more tests

* Fix even more tests and add suggestions

* Remove unrelated lcli addition

* Update more tests

* Merge branch 'unstable' into electra

* Add comment for test-suite lcli override

* Merge branch 'unstable' into electra

* Cleanup

* Merge branch 'unstable' into electra

* Apply suggestions

* Merge branch 'unstable' into electra

* Merge sigp/unstable into electra

* Merge branch 'unstable' into electra
This commit is contained in:
Mac L
2024-04-02 23:35:02 +11:00
committed by GitHub
parent 3058b96f25
commit f8fdb71f50
105 changed files with 2079 additions and 405 deletions

View File

@@ -0,0 +1,3 @@
# Gnosis preset - Electra
ELECTRA_PLACEHOLDER: 0

View File

@@ -0,0 +1,3 @@
# Mainnet preset - Electra
ELECTRA_PLACEHOLDER: 0

View File

@@ -0,0 +1,3 @@
# Minimal preset - Electra
ELECTRA_PLACEHOLDER: 0

View File

@@ -12,7 +12,7 @@ use tree_hash_derive::TreeHash;
/// A block of the `BeaconChain`.
#[superstruct(
variants(Base, Altair, Merge, Capella, Deneb),
variants(Base, Altair, Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Debug,
@@ -69,6 +69,8 @@ pub struct BeaconBlock<T: EthSpec, Payload: AbstractExecPayload<T> = FullPayload
pub body: BeaconBlockBodyCapella<T, Payload>,
#[superstruct(only(Deneb), partial_getter(rename = "body_deneb"))]
pub body: BeaconBlockBodyDeneb<T, Payload>,
#[superstruct(only(Electra), partial_getter(rename = "body_electra"))]
pub body: BeaconBlockBodyElectra<T, Payload>,
}
pub type BlindedBeaconBlock<E> = BeaconBlock<E, BlindedPayload<E>>;
@@ -124,8 +126,9 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlock<T, Payload> {
/// Usually it's better to prefer `from_ssz_bytes` which will decode the correct variant based
/// on the fork slot.
pub fn any_from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
BeaconBlockDeneb::from_ssz_bytes(bytes)
.map(BeaconBlock::Deneb)
BeaconBlockElectra::from_ssz_bytes(bytes)
.map(BeaconBlock::Electra)
.or_else(|_| BeaconBlockDeneb::from_ssz_bytes(bytes).map(BeaconBlock::Deneb))
.or_else(|_| BeaconBlockCapella::from_ssz_bytes(bytes).map(BeaconBlock::Capella))
.or_else(|_| BeaconBlockMerge::from_ssz_bytes(bytes).map(BeaconBlock::Merge))
.or_else(|_| BeaconBlockAltair::from_ssz_bytes(bytes).map(BeaconBlock::Altair))
@@ -221,6 +224,7 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockRef<'a, T, Payl
BeaconBlockRef::Merge { .. } => ForkName::Merge,
BeaconBlockRef::Capella { .. } => ForkName::Capella,
BeaconBlockRef::Deneb { .. } => ForkName::Deneb,
BeaconBlockRef::Electra { .. } => ForkName::Electra,
}
}
@@ -595,6 +599,83 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockDene
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockElectra<T, Payload> {
/// Return a Electra block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
let bls_to_execution_changes = vec![
SignedBlsToExecutionChange {
message: BlsToExecutionChange {
validator_index: 0,
from_bls_pubkey: PublicKeyBytes::empty(),
to_execution_address: Address::zero(),
},
signature: Signature::empty()
};
T::max_bls_to_execution_changes()
]
.into();
let sync_aggregate = SyncAggregate {
sync_committee_signature: AggregateSignature::empty(),
sync_committee_bits: BitVector::default(),
};
BeaconBlockElectra {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyElectra {
proposer_slashings: base_block.body.proposer_slashings,
attester_slashings: base_block.body.attester_slashings,
attestations: base_block.body.attestations,
deposits: base_block.body.deposits,
voluntary_exits: base_block.body.voluntary_exits,
bls_to_execution_changes,
sync_aggregate,
randao_reveal: Signature::empty(),
eth1_data: Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
deposit_count: 0,
},
graffiti: Graffiti::default(),
execution_payload: Payload::Electra::default(),
blob_kzg_commitments: VariableList::empty(),
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockElectra<T, Payload> {
/// Returns an empty Electra block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockElectra {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyElectra {
randao_reveal: Signature::empty(),
eth1_data: Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
deposit_count: 0,
},
graffiti: Graffiti::default(),
proposer_slashings: VariableList::empty(),
attester_slashings: VariableList::empty(),
attestations: VariableList::empty(),
deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(),
sync_aggregate: SyncAggregate::empty(),
execution_payload: Payload::Electra::default(),
bls_to_execution_changes: VariableList::empty(),
blob_kzg_commitments: VariableList::empty(),
},
}
}
}
// We can convert pre-Bellatrix blocks without payloads into blocks "with" payloads.
impl<E: EthSpec> From<BeaconBlockBase<E, BlindedPayload<E>>>
for BeaconBlockBase<E, FullPayload<E>>
@@ -675,6 +756,7 @@ impl_from!(BeaconBlockAltair, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body
impl_from!(BeaconBlockMerge, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyMerge<_, _>| body.into());
impl_from!(BeaconBlockCapella, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyCapella<_, _>| body.into());
impl_from!(BeaconBlockDeneb, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyDeneb<_, _>| body.into());
impl_from!(BeaconBlockElectra, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyElectra<_, _>| body.into());
// We can clone blocks with payloads to blocks without payloads, without cloning the payload.
macro_rules! impl_clone_as_blinded {
@@ -707,6 +789,7 @@ impl_clone_as_blinded!(BeaconBlockAltair, <E, FullPayload<E>>, <E, BlindedPayloa
impl_clone_as_blinded!(BeaconBlockMerge, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockCapella, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockDeneb, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockElectra, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
// A reference to a full beacon block can be cloned into a blinded beacon block, without cloning the
// execution payload.
@@ -822,7 +905,7 @@ mod tests {
}
#[test]
fn roundtrip_4844_block() {
fn roundtrip_deneb_block() {
let rng = &mut XorShiftRng::from_seed([42; 16]);
let spec = &ForkName::Deneb.make_genesis_spec(MainnetEthSpec::default_spec());
@@ -840,6 +923,26 @@ mod tests {
});
}
#[test]
fn roundtrip_electra_block() {
let rng = &mut XorShiftRng::from_seed([42; 16]);
let spec = &ForkName::Electra.make_genesis_spec(MainnetEthSpec::default_spec());
let inner_block = BeaconBlockElectra {
slot: Slot::random_for_test(rng),
proposer_index: u64::random_for_test(rng),
parent_root: Hash256::random_for_test(rng),
state_root: Hash256::random_for_test(rng),
body: BeaconBlockBodyElectra::random_for_test(rng),
};
let block = BeaconBlock::Electra(inner_block.clone());
test_ssz_tree_hash_pair_with(&block, &inner_block, |bytes| {
BeaconBlock::from_ssz_bytes(bytes, spec)
});
}
#[test]
fn decode_base_and_altair() {
type E = MainnetEthSpec;
@@ -857,10 +960,13 @@ mod tests {
let capella_slot = capella_epoch.start_slot(E::slots_per_epoch());
let deneb_epoch = capella_epoch + 1;
let deneb_slot = deneb_epoch.start_slot(E::slots_per_epoch());
let electra_epoch = deneb_epoch + 1;
let electra_slot = electra_epoch.start_slot(E::slots_per_epoch());
spec.altair_fork_epoch = Some(altair_epoch);
spec.capella_fork_epoch = Some(capella_epoch);
spec.deneb_fork_epoch = Some(deneb_epoch);
spec.electra_fork_epoch = Some(electra_epoch);
// BeaconBlockBase
{
@@ -934,7 +1040,7 @@ mod tests {
slot: deneb_slot,
..<_>::random_for_test(rng)
});
// It's invalid to have an Capella block with a epoch lower than the fork epoch.
// It's invalid to have a Deneb block with a epoch lower than the fork epoch.
let bad_block = {
let mut bad = good_block.clone();
*bad.slot_mut() = capella_slot;
@@ -949,5 +1055,28 @@ mod tests {
BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec)
.expect_err("bad deneb block cannot be decoded");
}
// BeaconBlockElectra
{
let good_block = BeaconBlock::Electra(BeaconBlockElectra {
slot: electra_slot,
..<_>::random_for_test(rng)
});
// It's invalid to have an Electra block with a epoch lower than the fork epoch.
let bad_block = {
let mut bad = good_block.clone();
*bad.slot_mut() = deneb_slot;
bad
};
assert_eq!(
BeaconBlock::from_ssz_bytes(&good_block.as_ssz_bytes(), &spec)
.expect("good electra block can be decoded"),
good_block
);
// TODO(electra): once the Electra block is changed from Deneb, update this to match
// the other forks.
assert!(BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec).is_ok());
}
}
}

View File

@@ -29,7 +29,7 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
///
/// This *superstruct* abstracts over the hard-fork.
#[superstruct(
variants(Base, Altair, Merge, Capella, Deneb),
variants(Base, Altair, Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Debug,
@@ -67,7 +67,7 @@ pub struct BeaconBlockBody<T: EthSpec, Payload: AbstractExecPayload<T> = FullPay
pub attestations: VariableList<Attestation<T>, T::MaxAttestations>,
pub deposits: VariableList<Deposit, T::MaxDeposits>,
pub voluntary_exits: VariableList<SignedVoluntaryExit, T::MaxVoluntaryExits>,
#[superstruct(only(Altair, Merge, Capella, Deneb))]
#[superstruct(only(Altair, Merge, Capella, Deneb, Electra))]
pub sync_aggregate: SyncAggregate<T>,
// 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
@@ -81,10 +81,13 @@ pub struct BeaconBlockBody<T: EthSpec, Payload: AbstractExecPayload<T> = FullPay
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
#[serde(flatten)]
pub execution_payload: Payload::Deneb,
#[superstruct(only(Capella, Deneb))]
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
#[serde(flatten)]
pub execution_payload: Payload::Electra,
#[superstruct(only(Capella, Deneb, Electra))]
pub bls_to_execution_changes:
VariableList<SignedBlsToExecutionChange, T::MaxBlsToExecutionChanges>,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
pub blob_kzg_commitments: KzgCommitments<T>,
#[superstruct(only(Base, Altair))]
#[ssz(skip_serializing, skip_deserializing)]
@@ -107,6 +110,7 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBodyRef<'a, T,
Self::Merge(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Capella(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Electra(body) => Ok(Payload::Ref::from(&body.execution_payload)),
}
}
@@ -177,6 +181,67 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBodyRef<'a, T,
// Join the proofs for the subtree and the main tree
proof.append(&mut proof_body);
debug_assert_eq!(proof.len(), T::kzg_proof_inclusion_proof_depth());
Ok(proof.into())
}
// TODO(electra): De-duplicate proof computation.
Self::Electra(body) => {
// We compute the branches by generating 2 merkle trees:
// 1. Merkle tree for the `blob_kzg_commitments` List object
// 2. Merkle tree for the `BeaconBlockBody` container
// We then merge the branches for both the trees all the way up to the root.
// Part1 (Branches for the subtree rooted at `blob_kzg_commitments`)
//
// Branches for `blob_kzg_commitments` without length mix-in
let depth = T::max_blob_commitments_per_block()
.next_power_of_two()
.ilog2();
let leaves: Vec<_> = body
.blob_kzg_commitments
.iter()
.map(|commitment| commitment.tree_hash_root())
.collect();
let tree = MerkleTree::create(&leaves, depth as usize);
let (_, mut proof) = tree
.generate_proof(index, depth as usize)
.map_err(Error::MerkleTreeError)?;
// Add the branch corresponding to the length mix-in.
let length = body.blob_kzg_commitments.len();
let usize_len = std::mem::size_of::<usize>();
let mut length_bytes = [0; BYTES_PER_CHUNK];
length_bytes
.get_mut(0..usize_len)
.ok_or(Error::MerkleTreeError(MerkleTreeError::PleaseNotifyTheDevs))?
.copy_from_slice(&length.to_le_bytes());
let length_root = Hash256::from_slice(length_bytes.as_slice());
proof.push(length_root);
// Part 2
// Branches for `BeaconBlockBody` container
let leaves = [
body.randao_reveal.tree_hash_root(),
body.eth1_data.tree_hash_root(),
body.graffiti.tree_hash_root(),
body.proposer_slashings.tree_hash_root(),
body.attester_slashings.tree_hash_root(),
body.attestations.tree_hash_root(),
body.deposits.tree_hash_root(),
body.voluntary_exits.tree_hash_root(),
body.sync_aggregate.tree_hash_root(),
body.execution_payload.tree_hash_root(),
body.bls_to_execution_changes.tree_hash_root(),
body.blob_kzg_commitments.tree_hash_root(),
];
let beacon_block_body_depth = leaves.len().next_power_of_two().ilog2() as usize;
let tree = MerkleTree::create(&leaves, beacon_block_body_depth);
let (_, mut proof_body) = tree
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
.map_err(Error::MerkleTreeError)?;
// Join the proofs for the subtree and the main tree
proof.append(&mut proof_body);
debug_assert_eq!(proof.len(), T::kzg_proof_inclusion_proof_depth());
Ok(proof.into())
}
@@ -199,6 +264,7 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBodyRef<'a, T,
BeaconBlockBodyRef::Merge { .. } => ForkName::Merge,
BeaconBlockBodyRef::Capella { .. } => ForkName::Capella,
BeaconBlockBodyRef::Deneb { .. } => ForkName::Deneb,
BeaconBlockBodyRef::Electra { .. } => ForkName::Electra,
}
}
}
@@ -467,6 +533,50 @@ impl<E: EthSpec> From<BeaconBlockBodyDeneb<E, FullPayload<E>>>
}
}
impl<E: EthSpec> From<BeaconBlockBodyElectra<E, FullPayload<E>>>
for (
BeaconBlockBodyElectra<E, BlindedPayload<E>>,
Option<ExecutionPayloadElectra<E>>,
)
{
fn from(body: BeaconBlockBodyElectra<E, FullPayload<E>>) -> Self {
let BeaconBlockBodyElectra {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadElectra { execution_payload },
bls_to_execution_changes,
blob_kzg_commitments,
} = body;
(
BeaconBlockBodyElectra {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadElectra {
execution_payload_header: From::from(&execution_payload),
},
bls_to_execution_changes,
blob_kzg_commitments: blob_kzg_commitments.clone(),
},
Some(execution_payload),
)
}
}
// We can clone a full block into a blinded block, without cloning the payload.
impl<E: EthSpec> BeaconBlockBodyBase<E, FullPayload<E>> {
pub fn clone_as_blinded(&self) -> BeaconBlockBodyBase<E, BlindedPayload<E>> {
@@ -584,6 +694,42 @@ impl<E: EthSpec> BeaconBlockBodyDeneb<E, FullPayload<E>> {
}
}
impl<E: EthSpec> BeaconBlockBodyElectra<E, FullPayload<E>> {
pub fn clone_as_blinded(&self) -> BeaconBlockBodyElectra<E, BlindedPayload<E>> {
let BeaconBlockBodyElectra {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadElectra { execution_payload },
bls_to_execution_changes,
blob_kzg_commitments,
} = self;
BeaconBlockBodyElectra {
randao_reveal: randao_reveal.clone(),
eth1_data: eth1_data.clone(),
graffiti: *graffiti,
proposer_slashings: proposer_slashings.clone(),
attester_slashings: attester_slashings.clone(),
attestations: attestations.clone(),
deposits: deposits.clone(),
voluntary_exits: voluntary_exits.clone(),
sync_aggregate: sync_aggregate.clone(),
execution_payload: BlindedPayloadElectra {
execution_payload_header: execution_payload.into(),
},
bls_to_execution_changes: bls_to_execution_changes.clone(),
blob_kzg_commitments: blob_kzg_commitments.clone(),
}
}
}
impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
for (
BeaconBlockBody<E, BlindedPayload<E>>,

View File

@@ -181,7 +181,7 @@ impl From<BeaconStateHash> for Hash256 {
/// The state of the `BeaconChain` at some slot.
#[superstruct(
variants(Base, Altair, Merge, Capella, Deneb),
variants(Base, Altair, Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Derivative,
@@ -261,9 +261,9 @@ where
pub current_epoch_attestations: VariableList<PendingAttestation<T>, T::MaxPendingAttestations>,
// Participation (Altair and later)
#[superstruct(only(Altair, Merge, Capella, Deneb))]
#[superstruct(only(Altair, Merge, Capella, Deneb, Electra))]
pub previous_epoch_participation: VariableList<ParticipationFlags, T::ValidatorRegistryLimit>,
#[superstruct(only(Altair, Merge, Capella, Deneb))]
#[superstruct(only(Altair, Merge, Capella, Deneb, Electra))]
pub current_epoch_participation: VariableList<ParticipationFlags, T::ValidatorRegistryLimit>,
// Finality
@@ -278,13 +278,13 @@ where
// Inactivity
#[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")]
#[superstruct(only(Altair, Merge, Capella, Deneb))]
#[superstruct(only(Altair, Merge, Capella, Deneb, Electra))]
pub inactivity_scores: VariableList<u64, T::ValidatorRegistryLimit>,
// Light-client sync committees
#[superstruct(only(Altair, Merge, Capella, Deneb))]
#[superstruct(only(Altair, Merge, Capella, Deneb, Electra))]
pub current_sync_committee: Arc<SyncCommittee<T>>,
#[superstruct(only(Altair, Merge, Capella, Deneb))]
#[superstruct(only(Altair, Merge, Capella, Deneb, Electra))]
pub next_sync_committee: Arc<SyncCommittee<T>>,
// Execution
@@ -303,16 +303,21 @@ where
partial_getter(rename = "latest_execution_payload_header_deneb")
)]
pub latest_execution_payload_header: ExecutionPayloadHeaderDeneb<T>,
#[superstruct(
only(Electra),
partial_getter(rename = "latest_execution_payload_header_electra")
)]
pub latest_execution_payload_header: ExecutionPayloadHeaderElectra<T>,
// Capella
#[superstruct(only(Capella, Deneb), partial_getter(copy))]
#[superstruct(only(Capella, Deneb, Electra), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub next_withdrawal_index: u64,
#[superstruct(only(Capella, Deneb), partial_getter(copy))]
#[superstruct(only(Capella, Deneb, Electra), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub next_withdrawal_validator_index: u64,
// Deep history valid from Capella onwards.
#[superstruct(only(Capella, Deneb))]
#[superstruct(only(Capella, Deneb, Electra))]
pub historical_summaries: VariableList<HistoricalSummary, T::HistoricalRootsLimit>,
// Caching (not in the spec)
@@ -449,6 +454,7 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Merge { .. } => ForkName::Merge,
BeaconState::Capella { .. } => ForkName::Capella,
BeaconState::Deneb { .. } => ForkName::Deneb,
BeaconState::Electra { .. } => ForkName::Electra,
}
}
@@ -759,6 +765,9 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Deneb(state) => Ok(ExecutionPayloadHeaderRef::Deneb(
&state.latest_execution_payload_header,
)),
BeaconState::Electra(state) => Ok(ExecutionPayloadHeaderRef::Electra(
&state.latest_execution_payload_header,
)),
}
}
@@ -776,6 +785,9 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Deneb(state) => Ok(ExecutionPayloadHeaderRefMut::Deneb(
&mut state.latest_execution_payload_header,
)),
BeaconState::Electra(state) => Ok(ExecutionPayloadHeaderRefMut::Electra(
&mut state.latest_execution_payload_header,
)),
}
}
@@ -1227,6 +1239,11 @@ impl<T: EthSpec> BeaconState<T> {
&mut state.balances,
&mut state.progressive_balances_cache,
),
BeaconState::Electra(state) => (
&mut state.validators,
&mut state.balances,
&mut state.progressive_balances_cache,
),
}
}
@@ -1348,7 +1365,7 @@ impl<T: EthSpec> BeaconState<T> {
| BeaconState::Altair(_)
| BeaconState::Merge(_)
| BeaconState::Capella(_) => self.get_churn_limit(spec)?,
BeaconState::Deneb(_) => std::cmp::min(
BeaconState::Deneb(_) | BeaconState::Electra(_) => std::cmp::min(
spec.max_per_epoch_activation_churn_limit,
self.get_churn_limit(spec)?,
),
@@ -1443,6 +1460,7 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Merge(state) => Ok(&mut state.current_epoch_participation),
BeaconState::Capella(state) => Ok(&mut state.current_epoch_participation),
BeaconState::Deneb(state) => Ok(&mut state.current_epoch_participation),
BeaconState::Electra(state) => Ok(&mut state.current_epoch_participation),
}
} else if epoch == self.previous_epoch() {
match self {
@@ -1451,6 +1469,7 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Merge(state) => Ok(&mut state.previous_epoch_participation),
BeaconState::Capella(state) => Ok(&mut state.previous_epoch_participation),
BeaconState::Deneb(state) => Ok(&mut state.previous_epoch_participation),
BeaconState::Electra(state) => Ok(&mut state.previous_epoch_participation),
}
} else {
Err(BeaconStateError::EpochOutOfBounds)
@@ -1763,6 +1782,7 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Merge(inner) => BeaconState::Merge(inner.clone()),
BeaconState::Capella(inner) => BeaconState::Capella(inner.clone()),
BeaconState::Deneb(inner) => BeaconState::Deneb(inner.clone()),
BeaconState::Electra(inner) => BeaconState::Electra(inner.clone()),
};
if config.committee_caches {
res.committee_caches_mut()
@@ -1943,6 +1963,7 @@ impl<T: EthSpec> CompareFields for BeaconState<T> {
(BeaconState::Merge(x), BeaconState::Merge(y)) => x.compare_fields(y),
(BeaconState::Capella(x), BeaconState::Capella(y)) => x.compare_fields(y),
(BeaconState::Deneb(x), BeaconState::Deneb(y)) => x.compare_fields(y),
(BeaconState::Electra(x), BeaconState::Electra(y)) => x.compare_fields(y),
_ => panic!("compare_fields: mismatched state variants",),
}
}

View File

@@ -182,6 +182,7 @@ pub fn is_progressive_balances_enabled<E: EthSpec>(state: &BeaconState<E>) -> bo
BeaconState::Altair(_)
| BeaconState::Merge(_)
| BeaconState::Capella(_)
| BeaconState::Deneb(_) => true,
| BeaconState::Deneb(_)
| BeaconState::Electra(_) => true,
}
}

View File

@@ -1,8 +1,8 @@
use crate::beacon_block_body::KzgCommitments;
use crate::{
ChainSpec, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, ForkName,
ForkVersionDeserialize, SignedRoot, Uint256,
ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef,
ExecutionPayloadHeaderRefMut, ForkName, ForkVersionDeserialize, SignedRoot, Uint256,
};
use bls::PublicKeyBytes;
use bls::Signature;
@@ -11,7 +11,7 @@ use superstruct::superstruct;
use tree_hash_derive::TreeHash;
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(
derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone),
serde(bound = "E: EthSpec", deny_unknown_fields)
@@ -29,7 +29,9 @@ pub struct BuilderBid<E: EthSpec> {
pub header: ExecutionPayloadHeaderCapella<E>,
#[superstruct(only(Deneb), partial_getter(rename = "header_deneb"))]
pub header: ExecutionPayloadHeaderDeneb<E>,
#[superstruct(only(Deneb))]
#[superstruct(only(Electra), partial_getter(rename = "header_electra"))]
pub header: ExecutionPayloadHeaderElectra<E>,
#[superstruct(only(Deneb, Electra))]
pub blob_kzg_commitments: KzgCommitments<E>,
#[serde(with = "serde_utils::quoted_u256")]
pub value: Uint256,
@@ -80,6 +82,7 @@ impl<T: EthSpec> ForkVersionDeserialize for BuilderBid<T> {
ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"BuilderBid failed to deserialize: unsupported fork '{}'",

View File

@@ -169,6 +169,13 @@ pub struct ChainSpec {
pub deneb_fork_version: [u8; 4],
pub deneb_fork_epoch: Option<Epoch>,
/*
* Electra hard fork params
*/
pub electra_fork_version: [u8; 4],
/// The Electra fork epoch is optional, with `None` representing "Electra never happens".
pub electra_fork_epoch: Option<Epoch>,
/*
* Networking
*/
@@ -282,15 +289,18 @@ 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.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::Merge,
_ => match self.altair_fork_epoch {
Some(fork_epoch) if epoch >= fork_epoch => ForkName::Altair,
_ => ForkName::Base,
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::Merge,
_ => match self.altair_fork_epoch {
Some(fork_epoch) if epoch >= fork_epoch => ForkName::Altair,
_ => ForkName::Base,
},
},
},
},
@@ -305,6 +315,7 @@ impl ChainSpec {
ForkName::Merge => self.bellatrix_fork_version,
ForkName::Capella => self.capella_fork_version,
ForkName::Deneb => self.deneb_fork_version,
ForkName::Electra => self.electra_fork_version,
}
}
@@ -316,6 +327,7 @@ impl ChainSpec {
ForkName::Merge => self.bellatrix_fork_epoch,
ForkName::Capella => self.capella_fork_epoch,
ForkName::Deneb => self.deneb_fork_epoch,
ForkName::Electra => self.electra_fork_epoch,
}
}
@@ -327,6 +339,7 @@ impl ChainSpec {
BeaconState::Merge(_) => self.inactivity_penalty_quotient_bellatrix,
BeaconState::Capella(_) => self.inactivity_penalty_quotient_bellatrix,
BeaconState::Deneb(_) => self.inactivity_penalty_quotient_bellatrix,
BeaconState::Electra(_) => self.inactivity_penalty_quotient_bellatrix,
}
}
@@ -341,6 +354,7 @@ impl ChainSpec {
BeaconState::Merge(_) => self.proportional_slashing_multiplier_bellatrix,
BeaconState::Capella(_) => self.proportional_slashing_multiplier_bellatrix,
BeaconState::Deneb(_) => self.proportional_slashing_multiplier_bellatrix,
BeaconState::Electra(_) => self.proportional_slashing_multiplier_bellatrix,
}
}
@@ -355,6 +369,7 @@ impl ChainSpec {
BeaconState::Merge(_) => self.min_slashing_penalty_quotient_bellatrix,
BeaconState::Capella(_) => self.min_slashing_penalty_quotient_bellatrix,
BeaconState::Deneb(_) => self.min_slashing_penalty_quotient_bellatrix,
BeaconState::Electra(_) => self.min_slashing_penalty_quotient_bellatrix,
}
}
@@ -513,7 +528,7 @@ impl ChainSpec {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
self.max_blocks_by_root_request
}
ForkName::Deneb => self.max_blocks_by_root_request_deneb,
ForkName::Deneb | ForkName::Electra => self.max_blocks_by_root_request_deneb,
}
}
@@ -522,7 +537,7 @@ impl ChainSpec {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
self.max_request_blocks
}
ForkName::Deneb => self.max_request_blocks_deneb,
ForkName::Deneb | ForkName::Electra => self.max_request_blocks_deneb,
};
max_request_blocks as usize
}
@@ -683,6 +698,12 @@ impl ChainSpec {
deneb_fork_version: [0x04, 0x00, 0x00, 0x00],
deneb_fork_epoch: Some(Epoch::new(269568)),
/*
* Electra hard fork params
*/
electra_fork_version: [0x05, 00, 00, 00],
electra_fork_epoch: None,
/*
* Network specific
*/
@@ -779,6 +800,9 @@ impl ChainSpec {
// Deneb
deneb_fork_version: [0x04, 0x00, 0x00, 0x01],
deneb_fork_epoch: None,
// Electra
electra_fork_version: [0x05, 0x00, 0x00, 0x01],
electra_fork_epoch: None,
// Other
network_id: 2, // lighthouse testnet network id
deposit_chain_id: 5,
@@ -946,6 +970,12 @@ impl ChainSpec {
deneb_fork_version: [0x04, 0x00, 0x00, 0x64],
deneb_fork_epoch: Some(Epoch::new(889856)),
/*
* Electra hard fork params
*/
electra_fork_version: [0x05, 0x00, 0x00, 0x64],
electra_fork_epoch: None,
/*
* Network specific
*/
@@ -1069,6 +1099,14 @@ pub struct Config {
#[serde(deserialize_with = "deserialize_fork_epoch")]
pub deneb_fork_epoch: Option<MaybeQuoted<Epoch>>,
#[serde(default = "default_electra_fork_version")]
#[serde(with = "serde_utils::bytes_4_hex")]
electra_fork_version: [u8; 4],
#[serde(default)]
#[serde(serialize_with = "serialize_fork_epoch")]
#[serde(deserialize_with = "deserialize_fork_epoch")]
pub electra_fork_epoch: Option<MaybeQuoted<Epoch>>,
#[serde(with = "serde_utils::quoted_u64")]
seconds_per_slot: u64,
#[serde(with = "serde_utils::quoted_u64")]
@@ -1177,6 +1215,11 @@ fn default_deneb_fork_version() -> [u8; 4] {
[0xff, 0xff, 0xff, 0xff]
}
fn default_electra_fork_version() -> [u8; 4] {
// This value shouldn't be used.
[0xff, 0xff, 0xff, 0xff]
}
/// Placeholder value: 2^256-2^10 (115792089237316195423570985008687907853269984665640564039457584007913129638912).
///
/// Taken from https://github.com/ethereum/consensus-specs/blob/d5e4828aecafaf1c57ef67a5f23c4ae7b08c5137/configs/mainnet.yaml#L15-L16
@@ -1386,19 +1429,27 @@ impl Config {
altair_fork_epoch: spec
.altair_fork_epoch
.map(|epoch| MaybeQuoted { value: epoch }),
bellatrix_fork_version: spec.bellatrix_fork_version,
bellatrix_fork_epoch: spec
.bellatrix_fork_epoch
.map(|epoch| MaybeQuoted { value: epoch }),
capella_fork_version: spec.capella_fork_version,
capella_fork_epoch: spec
.capella_fork_epoch
.map(|epoch| MaybeQuoted { value: epoch }),
deneb_fork_version: spec.deneb_fork_version,
deneb_fork_epoch: spec
.deneb_fork_epoch
.map(|epoch| MaybeQuoted { value: epoch }),
electra_fork_version: spec.electra_fork_version,
electra_fork_epoch: spec
.electra_fork_epoch
.map(|epoch| MaybeQuoted { value: epoch }),
seconds_per_slot: spec.seconds_per_slot,
seconds_per_eth1_block: spec.seconds_per_eth1_block,
min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay,
@@ -1468,6 +1519,8 @@ impl Config {
capella_fork_version,
deneb_fork_epoch,
deneb_fork_version,
electra_fork_epoch,
electra_fork_version,
seconds_per_slot,
seconds_per_eth1_block,
min_validator_withdrawability_delay,
@@ -1522,6 +1575,8 @@ impl Config {
capella_fork_version,
deneb_fork_epoch: deneb_fork_epoch.map(|q| q.value),
deneb_fork_version,
electra_fork_epoch: electra_fork_epoch.map(|q| q.value),
electra_fork_version,
seconds_per_slot,
seconds_per_eth1_block,
min_validator_withdrawability_delay,

View File

@@ -1,6 +1,6 @@
use crate::{
consts::altair, AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, ChainSpec, Config,
DenebPreset, EthSpec, ForkName,
DenebPreset, ElectraPreset, EthSpec, ForkName,
};
use maplit::hashmap;
use serde::{Deserialize, Serialize};
@@ -12,7 +12,7 @@ use superstruct::superstruct;
///
/// Mostly useful for the API.
#[superstruct(
variants(Capella, Deneb),
variants(Capella, Deneb, Electra),
variant_attributes(derive(Serialize, Deserialize, Debug, PartialEq, Clone))
)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
@@ -29,9 +29,12 @@ pub struct ConfigAndPreset {
pub bellatrix_preset: BellatrixPreset,
#[serde(flatten)]
pub capella_preset: CapellaPreset,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
#[serde(flatten)]
pub deneb_preset: DenebPreset,
#[superstruct(only(Electra))]
#[serde(flatten)]
pub electra_preset: ElectraPreset,
/// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks.
#[serde(flatten)]
pub extra_fields: HashMap<String, Value>,
@@ -46,11 +49,29 @@ impl ConfigAndPreset {
let capella_preset = CapellaPreset::from_chain_spec::<T>(spec);
let extra_fields = get_extra_fields(spec);
if spec.deneb_fork_epoch.is_some()
if spec.electra_fork_epoch.is_some()
|| fork_name.is_none()
|| fork_name == Some(ForkName::Electra)
{
let deneb_preset = DenebPreset::from_chain_spec::<T>(spec);
let electra_preset = ElectraPreset::from_chain_spec::<T>(spec);
ConfigAndPreset::Electra(ConfigAndPresetElectra {
config,
base_preset,
altair_preset,
bellatrix_preset,
capella_preset,
deneb_preset,
electra_preset,
extra_fields,
})
} else if spec.deneb_fork_epoch.is_some()
|| fork_name.is_none()
|| fork_name == Some(ForkName::Deneb)
{
let deneb_preset = DenebPreset::from_chain_spec::<T>(spec);
ConfigAndPreset::Deneb(ConfigAndPresetDeneb {
config,
base_preset,
@@ -136,8 +157,8 @@ mod test {
.write(false)
.open(tmp_file.as_ref())
.expect("error while opening the file");
let from: ConfigAndPresetDeneb =
let from: ConfigAndPresetElectra =
serde_yaml::from_reader(reader).expect("error while deserializing");
assert_eq!(ConfigAndPreset::Deneb(from), yamlconfig);
assert_eq!(ConfigAndPreset::Electra(from), yamlconfig);
}
}

View File

@@ -134,6 +134,11 @@ pub trait EthSpec:
/// Must be set to `BytesPerFieldElement * FieldElementsPerBlob`.
type BytesPerBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* New in Electra
*/
type ElectraPlaceholder: Unsigned + Clone + Sync + Send + Debug + PartialEq;
fn default_spec() -> ChainSpec;
fn spec_name() -> EthSpecId;
@@ -273,10 +278,15 @@ pub trait EthSpec:
fn bytes_per_blob() -> usize {
Self::BytesPerBlob::to_usize()
}
/// Returns the `KZG_COMMITMENT_INCLUSION_PROOF_DEPTH` preset for this specification.
fn kzg_proof_inclusion_proof_depth() -> usize {
Self::KzgCommitmentInclusionProofDepth::to_usize()
}
fn electra_placeholder() -> usize {
Self::ElectraPlaceholder::to_usize()
}
}
/// Macro to inherit some type values from another EthSpec.
@@ -327,6 +337,7 @@ impl EthSpec for MainnetEthSpec {
type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch
type MaxBlsToExecutionChanges = U16;
type MaxWithdrawalsPerPayload = U16;
type ElectraPlaceholder = U16;
fn default_spec() -> ChainSpec {
ChainSpec::mainnet()
@@ -378,7 +389,8 @@ impl EthSpec for MinimalEthSpec {
MaxExtraDataBytes,
MaxBlsToExecutionChanges,
MaxBlobsPerBlock,
BytesPerFieldElement
BytesPerFieldElement,
ElectraPlaceholder
});
fn default_spec() -> ChainSpec {
@@ -430,6 +442,7 @@ impl EthSpec for GnosisEthSpec {
type BytesPerFieldElement = U32;
type BytesPerBlob = U131072;
type KzgCommitmentInclusionProofDepth = U17;
type ElectraPlaceholder = U16;
fn default_spec() -> ChainSpec {
ChainSpec::gnosis()

View File

@@ -15,7 +15,7 @@ pub type Transactions<T> = VariableList<
pub type Withdrawals<T> = VariableList<Withdrawal, <T as EthSpec>::MaxWithdrawalsPerPayload>;
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Default,
@@ -81,12 +81,12 @@ pub struct ExecutionPayload<T: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<T>,
#[superstruct(only(Capella, Deneb))]
#[superstruct(only(Capella, Deneb, Electra))]
pub withdrawals: Withdrawals<T>,
#[superstruct(only(Deneb), partial_getter(copy))]
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub blob_gas_used: u64,
#[superstruct(only(Deneb), partial_getter(copy))]
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub excess_blob_gas: u64,
}
@@ -110,6 +110,7 @@ impl<T: EthSpec> ExecutionPayload<T> {
ForkName::Merge => ExecutionPayloadMerge::from_ssz_bytes(bytes).map(Self::Merge),
ForkName::Capella => ExecutionPayloadCapella::from_ssz_bytes(bytes).map(Self::Capella),
ForkName::Deneb => ExecutionPayloadDeneb::from_ssz_bytes(bytes).map(Self::Deneb),
ForkName::Electra => ExecutionPayloadElectra::from_ssz_bytes(bytes).map(Self::Electra),
}
}
@@ -149,6 +150,19 @@ impl<T: EthSpec> ExecutionPayload<T> {
// Max size of variable length `withdrawals` field
+ (T::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
}
#[allow(clippy::arithmetic_side_effects)]
/// Returns the maximum size of an execution payload.
pub fn max_execution_payload_electra_size() -> usize {
// Fixed part
ExecutionPayloadElectra::<T>::default().as_ssz_bytes().len()
// Max size of variable length `extra_data` field
+ (T::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len())
// Max size of variable length `transactions` field
+ (T::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + T::max_bytes_per_transaction()))
// Max size of variable length `withdrawals` field
+ (T::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
}
}
impl<T: EthSpec> ForkVersionDeserialize for ExecutionPayload<T> {
@@ -164,6 +178,7 @@ impl<T: EthSpec> ForkVersionDeserialize for ExecutionPayload<T> {
ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"ExecutionPayload failed to deserialize: unsupported fork '{}'",
@@ -180,6 +195,7 @@ impl<T: EthSpec> ExecutionPayload<T> {
ExecutionPayload::Merge(_) => ForkName::Merge,
ExecutionPayload::Capella(_) => ForkName::Capella,
ExecutionPayload::Deneb(_) => ForkName::Deneb,
ExecutionPayload::Electra(_) => ForkName::Electra,
}
}
}

View File

@@ -8,7 +8,7 @@ use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Default,
@@ -76,14 +76,14 @@ pub struct ExecutionPayloadHeader<T: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[superstruct(getter(copy))]
pub transactions_root: Hash256,
#[superstruct(only(Capella, Deneb))]
#[superstruct(only(Capella, Deneb, Electra))]
#[superstruct(getter(copy))]
pub withdrawals_root: Hash256,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
#[serde(with = "serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub blob_gas_used: u64,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
#[serde(with = "serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub excess_blob_gas: u64,
@@ -104,6 +104,9 @@ impl<T: EthSpec> ExecutionPayloadHeader<T> {
ExecutionPayloadHeaderCapella::from_ssz_bytes(bytes).map(Self::Capella)
}
ForkName::Deneb => ExecutionPayloadHeaderDeneb::from_ssz_bytes(bytes).map(Self::Deneb),
ForkName::Electra => {
ExecutionPayloadHeaderElectra::from_ssz_bytes(bytes).map(Self::Electra)
}
}
}
}
@@ -163,6 +166,30 @@ impl<T: EthSpec> ExecutionPayloadHeaderCapella<T> {
}
}
impl<T: EthSpec> ExecutionPayloadHeaderDeneb<T> {
pub fn upgrade_to_electra(&self) -> ExecutionPayloadHeaderElectra<T> {
ExecutionPayloadHeaderElectra {
parent_hash: self.parent_hash,
fee_recipient: self.fee_recipient,
state_root: self.state_root,
receipts_root: self.receipts_root,
logs_bloom: self.logs_bloom.clone(),
prev_randao: self.prev_randao,
block_number: self.block_number,
gas_limit: self.gas_limit,
gas_used: self.gas_used,
timestamp: self.timestamp,
extra_data: self.extra_data.clone(),
base_fee_per_gas: self.base_fee_per_gas,
block_hash: self.block_hash,
transactions_root: self.transactions_root,
withdrawals_root: self.withdrawals_root,
blob_gas_used: self.blob_gas_used,
excess_blob_gas: self.excess_blob_gas,
}
}
}
impl<'a, T: EthSpec> From<&'a ExecutionPayloadMerge<T>> for ExecutionPayloadHeaderMerge<T> {
fn from(payload: &'a ExecutionPayloadMerge<T>) -> Self {
Self {
@@ -183,6 +210,7 @@ impl<'a, T: EthSpec> From<&'a ExecutionPayloadMerge<T>> for ExecutionPayloadHead
}
}
}
impl<'a, T: EthSpec> From<&'a ExecutionPayloadCapella<T>> for ExecutionPayloadHeaderCapella<T> {
fn from(payload: &'a ExecutionPayloadCapella<T>) -> Self {
Self {
@@ -229,6 +257,30 @@ impl<'a, T: EthSpec> From<&'a ExecutionPayloadDeneb<T>> for ExecutionPayloadHead
}
}
impl<'a, T: EthSpec> From<&'a ExecutionPayloadElectra<T>> for ExecutionPayloadHeaderElectra<T> {
fn from(payload: &'a ExecutionPayloadElectra<T>) -> Self {
Self {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom.clone(),
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data.clone(),
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
withdrawals_root: payload.withdrawals.tree_hash_root(),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
}
}
}
// These impls are required to work around an inelegance in `to_execution_payload_header`.
// They only clone headers so they should be relatively cheap.
impl<'a, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderMerge<T> {
@@ -249,6 +301,12 @@ impl<'a, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderDeneb<T> {
}
}
impl<'a, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderElectra<T> {
fn from(payload: &'a Self) -> Self {
payload.clone()
}
}
impl<'a, T: EthSpec> From<ExecutionPayloadRef<'a, T>> for ExecutionPayloadHeader<T> {
fn from(payload: ExecutionPayloadRef<'a, T>) -> Self {
map_execution_payload_ref_into_execution_payload_header!(
@@ -289,6 +347,18 @@ impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for ExecutionPayloadHeaderDe
}
}
impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for ExecutionPayloadHeaderElectra<T> {
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<T>) -> Result<Self, Self::Error> {
match header {
ExecutionPayloadHeader::Electra(execution_payload_header) => {
Ok(execution_payload_header)
}
_ => Err(BeaconStateError::IncorrectStateVariant),
}
}
}
impl<T: EthSpec> ForkVersionDeserialize for ExecutionPayloadHeader<T> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
@@ -305,6 +375,7 @@ impl<T: EthSpec> ForkVersionDeserialize for ExecutionPayloadHeader<T> {
ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"ExecutionPayloadHeader failed to deserialize: unsupported fork '{}'",

View File

@@ -62,6 +62,13 @@ impl ForkContext {
));
}
if spec.electra_fork_epoch.is_some() {
fork_to_digest.push((
ForkName::Electra,
ChainSpec::compute_fork_digest(spec.electra_fork_version, genesis_validators_root),
));
}
let fork_to_digest: HashMap<ForkName, [u8; 4]> = fork_to_digest.into_iter().collect();
let digest_to_fork = fork_to_digest

View File

@@ -14,6 +14,7 @@ pub enum ForkName {
Merge,
Capella,
Deneb,
Electra,
}
impl ForkName {
@@ -24,6 +25,7 @@ impl ForkName {
ForkName::Merge,
ForkName::Capella,
ForkName::Deneb,
ForkName::Electra,
]
}
@@ -42,6 +44,7 @@ impl ForkName {
spec.bellatrix_fork_epoch = None;
spec.capella_fork_epoch = None;
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec
}
ForkName::Altair => {
@@ -49,6 +52,7 @@ impl ForkName {
spec.bellatrix_fork_epoch = None;
spec.capella_fork_epoch = None;
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec
}
ForkName::Merge => {
@@ -56,6 +60,7 @@ impl ForkName {
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = None;
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec
}
ForkName::Capella => {
@@ -63,6 +68,7 @@ impl ForkName {
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec
}
ForkName::Deneb => {
@@ -70,6 +76,15 @@ impl ForkName {
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = None;
spec
}
ForkName::Electra => {
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = Some(Epoch::new(0));
spec
}
}
@@ -85,6 +100,7 @@ impl ForkName {
ForkName::Merge => Some(ForkName::Altair),
ForkName::Capella => Some(ForkName::Merge),
ForkName::Deneb => Some(ForkName::Capella),
ForkName::Electra => Some(ForkName::Deneb),
}
}
@@ -97,7 +113,8 @@ impl ForkName {
ForkName::Altair => Some(ForkName::Merge),
ForkName::Merge => Some(ForkName::Capella),
ForkName::Capella => Some(ForkName::Deneb),
ForkName::Deneb => None,
ForkName::Deneb => Some(ForkName::Electra),
ForkName::Electra => None,
}
}
}
@@ -147,6 +164,10 @@ macro_rules! map_fork_name_with {
let (value, extra_data) = $body;
($t::Deneb(value), extra_data)
}
ForkName::Electra => {
let (value, extra_data) = $body;
($t::Electra(value), extra_data)
}
}
};
}
@@ -161,6 +182,7 @@ impl FromStr for ForkName {
"bellatrix" | "merge" => ForkName::Merge,
"capella" => ForkName::Capella,
"deneb" => ForkName::Deneb,
"electra" => ForkName::Electra,
_ => return Err(format!("unknown fork name: {}", fork_name)),
})
}
@@ -174,6 +196,7 @@ impl Display for ForkName {
ForkName::Merge => "bellatrix".fmt(f),
ForkName::Capella => "capella".fmt(f),
ForkName::Deneb => "deneb".fmt(f),
ForkName::Electra => "electra".fmt(f),
}
}
}

View File

@@ -112,11 +112,13 @@ pub use crate::attestation_duty::AttestationDuty;
pub use crate::attester_slashing::AttesterSlashing;
pub use crate::beacon_block::{
BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockDeneb,
BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, EmptyBlock,
BeaconBlockElectra, BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock,
EmptyBlock,
};
pub use crate::beacon_block_body::{
BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyCapella,
BeaconBlockBodyDeneb, BeaconBlockBodyMerge, BeaconBlockBodyRef, BeaconBlockBodyRefMut,
BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyMerge, BeaconBlockBodyRef,
BeaconBlockBodyRefMut,
};
pub use crate::beacon_block_header::BeaconBlockHeader;
pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
@@ -125,7 +127,9 @@ pub use crate::blob_sidecar::{BlobSidecar, BlobSidecarList, BlobsList};
pub use crate::bls_to_execution_change::BlsToExecutionChange;
pub use crate::chain_spec::{ChainSpec, Config, Domain};
pub use crate::checkpoint::Checkpoint;
pub use crate::config_and_preset::{ConfigAndPreset, ConfigAndPresetCapella, ConfigAndPresetDeneb};
pub use crate::config_and_preset::{
ConfigAndPreset, ConfigAndPresetCapella, ConfigAndPresetDeneb, ConfigAndPresetElectra,
};
pub use crate::contribution_and_proof::ContributionAndProof;
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
pub use crate::deposit_data::DepositData;
@@ -137,12 +141,13 @@ pub use crate::eth_spec::EthSpecId;
pub use crate::execution_block_hash::ExecutionBlockHash;
pub use crate::execution_block_header::ExecutionBlockHeader;
pub use crate::execution_payload::{
ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge,
ExecutionPayloadRef, Transaction, Transactions, Withdrawals,
ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra,
ExecutionPayloadMerge, ExecutionPayloadRef, Transaction, Transactions, Withdrawals,
};
pub use crate::execution_payload_header::{
ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef,
ExecutionPayloadHeaderRefMut,
};
pub use crate::fork::Fork;
pub use crate::fork_context::ForkContext;
@@ -175,11 +180,14 @@ pub use crate::participation_flags::ParticipationFlags;
pub use crate::participation_list::ParticipationList;
pub use crate::payload::{
AbstractExecPayload, BlindedPayload, BlindedPayloadCapella, BlindedPayloadDeneb,
BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload, FullPayload,
FullPayloadCapella, FullPayloadDeneb, FullPayloadMerge, FullPayloadRef, OwnedExecPayload,
BlindedPayloadElectra, BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload,
FullPayload, FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadMerge,
FullPayloadRef, OwnedExecPayload,
};
pub use crate::pending_attestation::PendingAttestation;
pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, DenebPreset};
pub use crate::preset::{
AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, DenebPreset, ElectraPreset,
};
pub use crate::proposer_preparation_data::ProposerPreparationData;
pub use crate::proposer_slashing::ProposerSlashing;
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
@@ -190,8 +198,8 @@ pub use crate::signed_aggregate_and_proof::SignedAggregateAndProof;
pub use crate::signed_beacon_block::{
ssz_tagged_signed_beacon_block, ssz_tagged_signed_beacon_block_arc, SignedBeaconBlock,
SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockCapella,
SignedBeaconBlockDeneb, SignedBeaconBlockHash, SignedBeaconBlockMerge,
SignedBlindedBeaconBlock,
SignedBeaconBlockDeneb, SignedBeaconBlockElectra, SignedBeaconBlockHash,
SignedBeaconBlockMerge, SignedBlindedBeaconBlock,
};
pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader;
pub use crate::signed_bls_to_execution_change::SignedBlsToExecutionChange;

View File

@@ -69,16 +69,11 @@ impl<E: EthSpec> LightClientBootstrap<E> {
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let bootstrap = match fork_name {
ForkName::Altair | ForkName::Merge => {
let header = LightClientBootstrapAltair::from_ssz_bytes(bytes)?;
Self::Altair(header)
Self::Altair(LightClientBootstrapAltair::from_ssz_bytes(bytes)?)
}
ForkName::Capella => {
let header = LightClientBootstrapCapella::from_ssz_bytes(bytes)?;
Self::Capella(header)
}
ForkName::Deneb => {
let header = LightClientBootstrapDeneb::from_ssz_bytes(bytes)?;
Self::Deneb(header)
ForkName::Capella => Self::Capella(LightClientBootstrapCapella::from_ssz_bytes(bytes)?),
ForkName::Deneb | ForkName::Electra => {
Self::Deneb(LightClientBootstrapDeneb::from_ssz_bytes(bytes)?)
}
ForkName::Base => {
return Err(ssz::DecodeError::BytesInvalid(format!(
@@ -117,7 +112,7 @@ impl<E: EthSpec> LightClientBootstrap<E> {
current_sync_committee,
current_sync_committee_branch,
}),
ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb {
ForkName::Deneb | ForkName::Electra => Self::Deneb(LightClientBootstrapDeneb {
header: LightClientHeaderDeneb::block_to_light_client_header(block)?,
current_sync_committee,
current_sync_committee_branch,
@@ -134,14 +129,12 @@ impl<E: EthSpec> ForkVersionDeserialize for LightClientBootstrap<E> {
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => {
Ok(serde_json::from_value::<LightClientBootstrap<E>>(value)
.map_err(serde::de::Error::custom))?
}
ForkName::Base => Err(serde::de::Error::custom(format!(
"LightClientBootstrap failed to deserialize: unsupported fork '{}'",
fork_name
))),
_ => Ok(serde_json::from_value::<LightClientBootstrap<E>>(value)
.map_err(serde::de::Error::custom))?,
}
}
}

View File

@@ -106,7 +106,7 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
};
Self::Capella(finality_update)
}
ForkName::Deneb => {
ForkName::Deneb | ForkName::Electra => {
let finality_update = LightClientFinalityUpdateDeneb {
attested_header: LightClientHeaderDeneb::block_to_light_client_header(
attested_block,
@@ -136,16 +136,13 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let finality_update = match fork_name {
ForkName::Altair | ForkName::Merge => {
let finality_update = LightClientFinalityUpdateAltair::from_ssz_bytes(bytes)?;
Self::Altair(finality_update)
Self::Altair(LightClientFinalityUpdateAltair::from_ssz_bytes(bytes)?)
}
ForkName::Capella => {
let finality_update = LightClientFinalityUpdateCapella::from_ssz_bytes(bytes)?;
Self::Capella(finality_update)
Self::Capella(LightClientFinalityUpdateCapella::from_ssz_bytes(bytes)?)
}
ForkName::Deneb => {
let finality_update = LightClientFinalityUpdateDeneb::from_ssz_bytes(bytes)?;
Self::Deneb(finality_update)
ForkName::Deneb | ForkName::Electra => {
Self::Deneb(LightClientFinalityUpdateDeneb::from_ssz_bytes(bytes)?)
}
ForkName::Base => {
return Err(ssz::DecodeError::BytesInvalid(format!(
@@ -164,14 +161,14 @@ impl<E: EthSpec> ForkVersionDeserialize for LightClientFinalityUpdate<E> {
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => Ok(
serde_json::from_value::<LightClientFinalityUpdate<E>>(value)
.map_err(serde::de::Error::custom),
)?,
ForkName::Base => Err(serde::de::Error::custom(format!(
"LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'",
fork_name
))),
_ => Ok(
serde_json::from_value::<LightClientFinalityUpdate<E>>(value)
.map_err(serde::de::Error::custom),
)?,
}
}
}

View File

@@ -81,7 +81,7 @@ impl<E: EthSpec> LightClientHeader<E> {
ForkName::Capella => LightClientHeader::Capella(
LightClientHeaderCapella::block_to_light_client_header(block)?,
),
ForkName::Deneb => LightClientHeader::Deneb(
ForkName::Deneb | ForkName::Electra => LightClientHeader::Deneb(
LightClientHeaderDeneb::block_to_light_client_header(block)?,
),
};
@@ -91,16 +91,13 @@ impl<E: EthSpec> LightClientHeader<E> {
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let header = match fork_name {
ForkName::Altair | ForkName::Merge => {
let header = LightClientHeaderAltair::from_ssz_bytes(bytes)?;
LightClientHeader::Altair(header)
LightClientHeader::Altair(LightClientHeaderAltair::from_ssz_bytes(bytes)?)
}
ForkName::Capella => {
let header = LightClientHeaderCapella::from_ssz_bytes(bytes)?;
LightClientHeader::Capella(header)
LightClientHeader::Capella(LightClientHeaderCapella::from_ssz_bytes(bytes)?)
}
ForkName::Deneb => {
let header = LightClientHeaderDeneb::from_ssz_bytes(bytes)?;
LightClientHeader::Deneb(header)
ForkName::Deneb | ForkName::Electra => {
LightClientHeader::Deneb(LightClientHeaderDeneb::from_ssz_bytes(bytes)?)
}
ForkName::Base => {
return Err(ssz::DecodeError::BytesInvalid(format!(
@@ -198,7 +195,7 @@ impl<T: EthSpec> ForkVersionDeserialize for LightClientHeader<T> {
ForkName::Capella => serde_json::from_value(value)
.map(|light_client_header| Self::Capella(light_client_header))
.map_err(serde::de::Error::custom),
ForkName::Deneb => serde_json::from_value(value)
ForkName::Deneb | ForkName::Electra => serde_json::from_value(value)
.map(|light_client_header| Self::Deneb(light_client_header))
.map_err(serde::de::Error::custom),
ForkName::Base => Err(serde::de::Error::custom(format!(

View File

@@ -70,36 +70,27 @@ impl<E: EthSpec> LightClientOptimisticUpdate<E> {
.fork_name(chain_spec)
.map_err(|_| Error::InconsistentFork)?
{
ForkName::Altair | ForkName::Merge => {
let optimistic_update = LightClientOptimisticUpdateAltair {
attested_header: LightClientHeaderAltair::block_to_light_client_header(
attested_block,
)?,
sync_aggregate,
signature_slot,
};
Self::Altair(optimistic_update)
}
ForkName::Capella => {
let optimistic_update = LightClientOptimisticUpdateCapella {
attested_header: LightClientHeaderCapella::block_to_light_client_header(
attested_block,
)?,
sync_aggregate,
signature_slot,
};
Self::Capella(optimistic_update)
}
ForkName::Deneb => {
let optimistic_update = LightClientOptimisticUpdateDeneb {
attested_header: LightClientHeaderDeneb::block_to_light_client_header(
attested_block,
)?,
sync_aggregate,
signature_slot,
};
Self::Deneb(optimistic_update)
}
ForkName::Altair | ForkName::Merge => Self::Altair(LightClientOptimisticUpdateAltair {
attested_header: LightClientHeaderAltair::block_to_light_client_header(
attested_block,
)?,
sync_aggregate,
signature_slot,
}),
ForkName::Capella => Self::Capella(LightClientOptimisticUpdateCapella {
attested_header: LightClientHeaderCapella::block_to_light_client_header(
attested_block,
)?,
sync_aggregate,
signature_slot,
}),
ForkName::Deneb | ForkName::Electra => Self::Deneb(LightClientOptimisticUpdateDeneb {
attested_header: LightClientHeaderDeneb::block_to_light_client_header(
attested_block,
)?,
sync_aggregate,
signature_slot,
}),
ForkName::Base => return Err(Error::AltairForkNotActive),
};
@@ -130,16 +121,13 @@ impl<E: EthSpec> LightClientOptimisticUpdate<E> {
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let optimistic_update = match fork_name {
ForkName::Altair | ForkName::Merge => {
let optimistic_update = LightClientOptimisticUpdateAltair::from_ssz_bytes(bytes)?;
Self::Altair(optimistic_update)
Self::Altair(LightClientOptimisticUpdateAltair::from_ssz_bytes(bytes)?)
}
ForkName::Capella => {
let optimistic_update = LightClientOptimisticUpdateCapella::from_ssz_bytes(bytes)?;
Self::Capella(optimistic_update)
Self::Capella(LightClientOptimisticUpdateCapella::from_ssz_bytes(bytes)?)
}
ForkName::Deneb => {
let optimistic_update = LightClientOptimisticUpdateDeneb::from_ssz_bytes(bytes)?;
Self::Deneb(optimistic_update)
ForkName::Deneb | ForkName::Electra => {
Self::Deneb(LightClientOptimisticUpdateDeneb::from_ssz_bytes(bytes)?)
}
ForkName::Base => {
return Err(ssz::DecodeError::BytesInvalid(format!(
@@ -158,14 +146,14 @@ impl<T: EthSpec> ForkVersionDeserialize for LightClientOptimisticUpdate<T> {
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => Ok(
serde_json::from_value::<LightClientOptimisticUpdate<T>>(value)
.map_err(serde::de::Error::custom),
)?,
ForkName::Base => Err(serde::de::Error::custom(format!(
"LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'",
fork_name
))),
_ => Ok(
serde_json::from_value::<LightClientOptimisticUpdate<T>>(value)
.map_err(serde::de::Error::custom),
)?,
}
}
}

View File

@@ -129,14 +129,12 @@ impl<E: EthSpec> ForkVersionDeserialize for LightClientUpdate<E> {
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => {
Ok(serde_json::from_value::<LightClientUpdate<E>>(value)
.map_err(serde::de::Error::custom))?
}
ForkName::Base => Err(serde::de::Error::custom(format!(
"LightClientUpdate failed to deserialize: unsupported fork '{}'",
fork_name
))),
_ => Ok(serde_json::from_value::<LightClientUpdate<E>>(value)
.map_err(serde::de::Error::custom))?,
}
}
}
@@ -216,7 +214,7 @@ impl<E: EthSpec> LightClientUpdate<E> {
signature_slot: block.slot(),
})
}
ForkName::Deneb => {
ForkName::Deneb | ForkName::Electra => {
let attested_header =
LightClientHeaderDeneb::block_to_light_client_header(attested_block)?;
let finalized_header =
@@ -239,16 +237,11 @@ impl<E: EthSpec> LightClientUpdate<E> {
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let update = match fork_name {
ForkName::Altair | ForkName::Merge => {
let update = LightClientUpdateAltair::from_ssz_bytes(bytes)?;
Self::Altair(update)
Self::Altair(LightClientUpdateAltair::from_ssz_bytes(bytes)?)
}
ForkName::Capella => {
let update = LightClientUpdateCapella::from_ssz_bytes(bytes)?;
Self::Capella(update)
}
ForkName::Deneb => {
let update = LightClientUpdateDeneb::from_ssz_bytes(bytes)?;
Self::Deneb(update)
ForkName::Capella => Self::Capella(LightClientUpdateCapella::from_ssz_bytes(bytes)?),
ForkName::Deneb | ForkName::Electra => {
Self::Deneb(LightClientUpdateDeneb::from_ssz_bytes(bytes)?)
}
ForkName::Base => {
return Err(ssz::DecodeError::BytesInvalid(format!(

View File

@@ -82,12 +82,14 @@ pub trait AbstractExecPayload<T: EthSpec>:
+ TryInto<Self::Merge>
+ TryInto<Self::Capella>
+ TryInto<Self::Deneb>
+ TryInto<Self::Electra>
{
type Ref<'a>: ExecPayload<T>
+ Copy
+ From<&'a Self::Merge>
+ From<&'a Self::Capella>
+ From<&'a Self::Deneb>;
+ From<&'a Self::Deneb>
+ From<&'a Self::Electra>;
type Merge: OwnedExecPayload<T>
+ Into<Self>
@@ -101,10 +103,14 @@ pub trait AbstractExecPayload<T: EthSpec>:
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadDeneb<T>>>
+ TryFrom<ExecutionPayloadHeaderDeneb<T>>;
type Electra: OwnedExecPayload<T>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadElectra<T>>>
+ TryFrom<ExecutionPayloadHeaderElectra<T>>;
}
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Debug,
@@ -145,6 +151,8 @@ pub struct FullPayload<T: EthSpec> {
pub execution_payload: ExecutionPayloadCapella<T>,
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
pub execution_payload: ExecutionPayloadDeneb<T>,
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload: ExecutionPayloadElectra<T>,
}
impl<T: EthSpec> From<FullPayload<T>> for ExecutionPayload<T> {
@@ -251,6 +259,9 @@ impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
FullPayload::Deneb(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayload::Electra(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
}
}
@@ -258,6 +269,7 @@ impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
match self {
FullPayload::Merge(_) | FullPayload::Capella(_) => Err(Error::IncorrectStateVariant),
FullPayload::Deneb(ref inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Electra(ref inner) => Ok(inner.execution_payload.blob_gas_used),
}
}
@@ -287,6 +299,7 @@ impl<T: EthSpec> FullPayload<T> {
ForkName::Merge => Ok(FullPayloadMerge::default().into()),
ForkName::Capella => Ok(FullPayloadCapella::default().into()),
ForkName::Deneb => Ok(FullPayloadDeneb::default().into()),
ForkName::Electra => Ok(FullPayloadElectra::default().into()),
}
}
}
@@ -376,6 +389,9 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
FullPayloadRef::Deneb(inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayloadRef::Electra(inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
}
}
@@ -385,6 +401,7 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
Err(Error::IncorrectStateVariant)
}
FullPayloadRef::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Electra(inner) => Ok(inner.execution_payload.blob_gas_used),
}
}
@@ -406,6 +423,7 @@ impl<T: EthSpec> AbstractExecPayload<T> for FullPayload<T> {
type Merge = FullPayloadMerge<T>;
type Capella = FullPayloadCapella<T>;
type Deneb = FullPayloadDeneb<T>;
type Electra = FullPayloadElectra<T>;
}
impl<T: EthSpec> From<ExecutionPayload<T>> for FullPayload<T> {
@@ -424,7 +442,7 @@ impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for FullPayload<T> {
}
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Debug,
@@ -464,6 +482,8 @@ pub struct BlindedPayload<T: EthSpec> {
pub execution_payload_header: ExecutionPayloadHeaderCapella<T>,
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
pub execution_payload_header: ExecutionPayloadHeaderDeneb<T>,
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload_header: ExecutionPayloadHeaderElectra<T>,
}
impl<'a, T: EthSpec> From<BlindedPayloadRef<'a, T>> for BlindedPayload<T> {
@@ -546,6 +566,9 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayload::Deneb(ref inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Electra(ref inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
}
}
@@ -555,6 +578,7 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
Err(Error::IncorrectStateVariant)
}
BlindedPayload::Deneb(ref inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Electra(ref inner) => Ok(inner.execution_payload_header.blob_gas_used),
}
}
@@ -643,6 +667,9 @@ impl<'b, T: EthSpec> ExecPayload<T> for BlindedPayloadRef<'b, T> {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayloadRef::Electra(inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
}
}
@@ -652,6 +679,7 @@ impl<'b, T: EthSpec> ExecPayload<T> for BlindedPayloadRef<'b, T> {
Err(Error::IncorrectStateVariant)
}
BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used),
}
}
@@ -944,12 +972,20 @@ impl_exec_payload_for_fork!(
ExecutionPayloadDeneb,
Deneb
);
impl_exec_payload_for_fork!(
BlindedPayloadElectra,
FullPayloadElectra,
ExecutionPayloadHeaderElectra,
ExecutionPayloadElectra,
Electra
);
impl<T: EthSpec> AbstractExecPayload<T> for BlindedPayload<T> {
type Ref<'a> = BlindedPayloadRef<'a, T>;
type Merge = BlindedPayloadMerge<T>;
type Capella = BlindedPayloadCapella<T>;
type Deneb = BlindedPayloadDeneb<T>;
type Electra = BlindedPayloadElectra<T>;
}
impl<T: EthSpec> From<ExecutionPayload<T>> for BlindedPayload<T> {
@@ -981,6 +1017,11 @@ impl<T: EthSpec> From<ExecutionPayloadHeader<T>> for BlindedPayload<T> {
execution_payload_header,
})
}
ExecutionPayloadHeader::Electra(execution_payload_header) => {
Self::Electra(BlindedPayloadElectra {
execution_payload_header,
})
}
}
}
}
@@ -997,6 +1038,9 @@ impl<T: EthSpec> From<BlindedPayload<T>> for ExecutionPayloadHeader<T> {
BlindedPayload::Deneb(blinded_payload) => {
ExecutionPayloadHeader::Deneb(blinded_payload.execution_payload_header)
}
BlindedPayload::Electra(blinded_payload) => {
ExecutionPayloadHeader::Electra(blinded_payload.execution_payload_header)
}
}
}
}

View File

@@ -226,6 +226,21 @@ impl DenebPreset {
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct ElectraPreset {
#[serde(with = "serde_utils::quoted_u64")]
pub electra_placeholder: u64,
}
impl ElectraPreset {
pub fn from_chain_spec<T: EthSpec>(_spec: &ChainSpec) -> Self {
Self {
electra_placeholder: 0,
}
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -267,6 +282,9 @@ mod test {
let deneb: DenebPreset = preset_from_file(&preset_name, "deneb.yaml");
assert_eq!(deneb, DenebPreset::from_chain_spec::<E>(&spec));
let electra: ElectraPreset = preset_from_file(&preset_name, "electra.yaml");
assert_eq!(electra, ElectraPreset::from_chain_spec::<E>(&spec));
}
#[test]

View File

@@ -37,7 +37,7 @@ impl From<SignedBeaconBlockHash> for Hash256 {
/// A `BeaconBlock` and a signature from its proposer.
#[superstruct(
variants(Base, Altair, Merge, Capella, Deneb),
variants(Base, Altair, Merge, Capella, Deneb, Electra),
variant_attributes(
derive(
Debug,
@@ -78,6 +78,8 @@ pub struct SignedBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E> = FullP
pub message: BeaconBlockCapella<E, Payload>,
#[superstruct(only(Deneb), partial_getter(rename = "message_deneb"))]
pub message: BeaconBlockDeneb<E, Payload>,
#[superstruct(only(Electra), partial_getter(rename = "message_electra"))]
pub message: BeaconBlockElectra<E, Payload>,
pub signature: Signature,
}
@@ -157,6 +159,9 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
BeaconBlock::Deneb(message) => {
SignedBeaconBlock::Deneb(SignedBeaconBlockDeneb { message, signature })
}
BeaconBlock::Electra(message) => {
SignedBeaconBlock::Electra(SignedBeaconBlockElectra { message, signature })
}
}
}
@@ -467,6 +472,62 @@ impl<E: EthSpec> SignedBeaconBlockDeneb<E, BlindedPayload<E>> {
}
}
impl<E: EthSpec> SignedBeaconBlockElectra<E, BlindedPayload<E>> {
pub fn into_full_block(
self,
execution_payload: ExecutionPayloadElectra<E>,
) -> SignedBeaconBlockElectra<E, FullPayload<E>> {
let SignedBeaconBlockElectra {
message:
BeaconBlockElectra {
slot,
proposer_index,
parent_root,
state_root,
body:
BeaconBlockBodyElectra {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadElectra { .. },
bls_to_execution_changes,
blob_kzg_commitments,
},
},
signature,
} = self;
SignedBeaconBlockElectra {
message: BeaconBlockElectra {
slot,
proposer_index,
parent_root,
state_root,
body: BeaconBlockBodyElectra {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadElectra { execution_payload },
bls_to_execution_changes,
blob_kzg_commitments,
},
},
signature,
}
}
}
impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
pub fn try_into_full_block(
self,
@@ -484,11 +545,15 @@ impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
(SignedBeaconBlock::Deneb(block), Some(ExecutionPayload::Deneb(payload))) => {
SignedBeaconBlock::Deneb(block.into_full_block(payload))
}
(SignedBeaconBlock::Electra(block), Some(ExecutionPayload::Electra(payload))) => {
SignedBeaconBlock::Electra(block.into_full_block(payload))
}
// avoid wildcard matching forks so that compiler will
// direct us here when a new fork has been added
(SignedBeaconBlock::Merge(_), _) => return None,
(SignedBeaconBlock::Capella(_), _) => return None,
(SignedBeaconBlock::Deneb(_), _) => return None,
(SignedBeaconBlock::Electra(_), _) => return None,
};
Some(full_block)
}
@@ -631,6 +696,9 @@ pub mod ssz_tagged_signed_beacon_block {
ForkName::Deneb => Ok(SignedBeaconBlock::Deneb(
SignedBeaconBlockDeneb::from_ssz_bytes(body)?,
)),
ForkName::Electra => Ok(SignedBeaconBlock::Electra(
SignedBeaconBlockElectra::from_ssz_bytes(body)?,
)),
}
}
}
@@ -722,7 +790,14 @@ mod test {
BeaconBlock::Capella(BeaconBlockCapella::empty(spec)),
sig.clone(),
),
SignedBeaconBlock::from_block(BeaconBlock::Deneb(BeaconBlockDeneb::empty(spec)), sig),
SignedBeaconBlock::from_block(
BeaconBlock::Deneb(BeaconBlockDeneb::empty(spec)),
sig.clone(),
),
SignedBeaconBlock::from_block(
BeaconBlock::Electra(BeaconBlockElectra::empty(spec)),
sig,
),
];
for block in blocks {

View File

@@ -46,7 +46,7 @@ impl VoluntaryExit {
spec.fork_version_for_name(fork_name)
}
// EIP-7044
ForkName::Deneb => spec.fork_version_for_name(ForkName::Capella),
ForkName::Deneb | ForkName::Electra => spec.fork_version_for_name(ForkName::Capella),
};
let domain =
spec.compute_domain(Domain::VoluntaryExit, fork_version, genesis_validators_root);