Merge branch 'unstable' into validator-manager

This commit is contained in:
Paul Hauner
2023-02-24 09:25:45 +11:00
286 changed files with 13296 additions and 2554 deletions

View File

@@ -11,9 +11,20 @@ use tree_hash_derive::TreeHash;
/// A Validators aggregate attestation and selection proof.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct AggregateAndProof<T: EthSpec> {
/// The index of the validator that created the attestation.
#[serde(with = "eth2_serde_utils::quoted_u64")]

View File

@@ -23,12 +23,21 @@ pub enum Error {
/// Details an attestation that can be slashable.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Derivative,
arbitrary::Arbitrary,
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct Attestation<T: EthSpec> {
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
pub data: AttestationData,

View File

@@ -10,8 +10,8 @@ use tree_hash_derive::TreeHash;
/// The data upon which an attestation is based.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,

View File

@@ -1,8 +1,7 @@
use crate::*;
use serde_derive::{Deserialize, Serialize};
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize)]
#[derive(arbitrary::Arbitrary, Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize)]
pub struct AttestationDuty {
/// The slot during which the attester must attest.
pub slot: Slot,

View File

@@ -9,12 +9,21 @@ use tree_hash_derive::TreeHash;
/// Two conflicting attestations.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Derivative, Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
Derivative,
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Eq, Hash(bound = "T: EthSpec"))]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct AttesterSlashing<T: EthSpec> {
pub attestation_1: IndexedAttestation<T>,
pub attestation_2: IndexedAttestation<T>,

View File

@@ -1,6 +1,6 @@
use crate::beacon_block_body::{
BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyMerge, BeaconBlockBodyRef,
BeaconBlockBodyRefMut,
BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyEip4844, BeaconBlockBodyMerge,
BeaconBlockBodyRef, BeaconBlockBodyRefMut,
};
use crate::test_utils::TestRandom;
use crate::*;
@@ -17,7 +17,7 @@ use tree_hash_derive::TreeHash;
/// A block of the `BeaconChain`.
#[superstruct(
variants(Base, Altair, Merge),
variants(Base, Altair, Merge, Capella, Eip4844),
variant_attributes(
derive(
Debug,
@@ -29,10 +29,14 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
),
derivative(PartialEq, Hash(bound = "T: EthSpec, Payload: ExecPayload<T>")),
serde(bound = "T: EthSpec, Payload: ExecPayload<T>", deny_unknown_fields),
cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)),
derivative(PartialEq, Hash(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")),
serde(
bound = "T: EthSpec, Payload: AbstractExecPayload<T>",
deny_unknown_fields
),
arbitrary(bound = "T: EthSpec, Payload: AbstractExecPayload<T>"),
),
ref_attributes(
derive(Debug, PartialEq, TreeHash),
@@ -41,14 +45,16 @@ use tree_hash_derive::TreeHash;
map_ref_into(BeaconBlockBodyRef, BeaconBlock),
map_ref_mut_into(BeaconBlockBodyRefMut)
)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)]
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative, arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(untagged)]
#[serde(bound = "T: EthSpec, Payload: ExecPayload<T>")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[serde(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
#[arbitrary(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
pub struct BeaconBlock<T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>> {
pub struct BeaconBlock<T: EthSpec, Payload: AbstractExecPayload<T> = FullPayload<T>> {
#[superstruct(getter(copy))]
pub slot: Slot,
#[superstruct(getter(copy))]
@@ -64,23 +70,34 @@ pub struct BeaconBlock<T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>> {
pub body: BeaconBlockBodyAltair<T, Payload>,
#[superstruct(only(Merge), partial_getter(rename = "body_merge"))]
pub body: BeaconBlockBodyMerge<T, Payload>,
#[superstruct(only(Capella), partial_getter(rename = "body_capella"))]
pub body: BeaconBlockBodyCapella<T, Payload>,
#[superstruct(only(Eip4844), partial_getter(rename = "body_eip4844"))]
pub body: BeaconBlockBodyEip4844<T, Payload>,
}
pub type BlindedBeaconBlock<E> = BeaconBlock<E, BlindedPayload<E>>;
impl<T: EthSpec, Payload: ExecPayload<T>> SignedRoot for BeaconBlock<T, Payload> {}
impl<'a, T: EthSpec, Payload: ExecPayload<T>> SignedRoot for BeaconBlockRef<'a, T, Payload> {}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedRoot for BeaconBlock<T, Payload> {}
impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> SignedRoot
for BeaconBlockRef<'a, T, Payload>
{
}
impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlock<T, Payload> {
/// Empty block trait for each block variant to implement.
pub trait EmptyBlock {
/// Returns an empty block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self;
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlock<T, Payload> {
/// Returns an empty block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
if spec.bellatrix_fork_epoch == Some(T::genesis_epoch()) {
Self::Merge(BeaconBlockMerge::empty(spec))
} else if spec.altair_fork_epoch == Some(T::genesis_epoch()) {
Self::Altair(BeaconBlockAltair::empty(spec))
} else {
Self::Base(BeaconBlockBase::empty(spec))
}
map_fork_name!(
spec.fork_name_at_epoch(T::genesis_epoch()),
Self,
EmptyBlock::empty(spec)
)
}
/// Custom SSZ decoder that takes a `ChainSpec` as context.
@@ -109,13 +126,12 @@ impl<T: EthSpec, Payload: ExecPayload<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> {
BeaconBlockMerge::from_ssz_bytes(bytes)
.map(BeaconBlock::Merge)
.or_else(|_| {
BeaconBlockAltair::from_ssz_bytes(bytes)
.map(BeaconBlock::Altair)
.or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base))
})
BeaconBlockEip4844::from_ssz_bytes(bytes)
.map(BeaconBlock::Eip4844)
.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))
.or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base))
}
/// Convenience accessor for the `body` as a `BeaconBlockBodyRef`.
@@ -178,7 +194,7 @@ impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlock<T, Payload> {
}
}
impl<'a, T: EthSpec, Payload: ExecPayload<T>> BeaconBlockRef<'a, T, Payload> {
impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockRef<'a, T, Payload> {
/// Returns the name of the fork pertaining to `self`.
///
/// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork
@@ -189,6 +205,8 @@ impl<'a, T: EthSpec, Payload: ExecPayload<T>> BeaconBlockRef<'a, T, Payload> {
BeaconBlockRef::Base { .. } => ForkName::Base,
BeaconBlockRef::Altair { .. } => ForkName::Altair,
BeaconBlockRef::Merge { .. } => ForkName::Merge,
BeaconBlockRef::Capella { .. } => ForkName::Capella,
BeaconBlockRef::Eip4844 { .. } => ForkName::Eip4844,
};
if fork_at_slot == object_fork {
@@ -242,12 +260,12 @@ impl<'a, T: EthSpec, Payload: ExecPayload<T>> BeaconBlockRef<'a, T, Payload> {
/// Extracts a reference to an execution payload from a block, returning an error if the block
/// is pre-merge.
pub fn execution_payload(&self) -> Result<&Payload, Error> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'a>, Error> {
self.body().execution_payload()
}
}
impl<'a, T: EthSpec, Payload: ExecPayload<T>> BeaconBlockRefMut<'a, T, Payload> {
impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockRefMut<'a, T, Payload> {
/// Convert a mutable reference to a beacon block to a mutable ref to its body.
pub fn body_mut(self) -> BeaconBlockBodyRefMut<'a, T, Payload> {
map_beacon_block_ref_mut_into_beacon_block_body_ref_mut!(&'a _, self, |block, cons| cons(
@@ -256,9 +274,8 @@ impl<'a, T: EthSpec, Payload: ExecPayload<T>> BeaconBlockRefMut<'a, T, Payload>
}
}
impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockBase<T, Payload> {
/// Returns an empty block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockBase<T, Payload> {
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockBase {
slot: spec.genesis_slot,
proposer_index: 0,
@@ -281,7 +298,9 @@ impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockBase<T, Payload> {
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBase<T, Payload> {
/// Return a block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let header = BeaconBlockHeader {
@@ -377,9 +396,9 @@ impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockBase<T, Payload> {
}
}
impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockAltair<T, Payload> {
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockAltair<T, Payload> {
/// Returns an empty Altair block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockAltair {
slot: spec.genesis_slot,
proposer_index: 0,
@@ -403,7 +422,9 @@ impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockAltair<T, Payload> {
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockAltair<T, Payload> {
/// Return an Altair block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
@@ -436,9 +457,9 @@ impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockAltair<T, Payload> {
}
}
impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockMerge<T, Payload> {
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockMerge<T, Payload> {
/// Returns an empty Merge block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockMerge {
slot: spec.genesis_slot,
proposer_index: 0,
@@ -458,7 +479,112 @@ impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockMerge<T, Payload> {
deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(),
sync_aggregate: SyncAggregate::empty(),
execution_payload: Payload::default(),
execution_payload: Payload::Merge::default(),
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockCapella<T, Payload> {
/// Return a Capella 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(),
};
BeaconBlockCapella {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyCapella {
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::Capella::default(),
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockCapella<T, Payload> {
/// Returns an empty Capella block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockCapella {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyCapella {
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::Capella::default(),
bls_to_execution_changes: VariableList::empty(),
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockEip4844<T, Payload> {
/// Returns an empty Eip4844 block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockEip4844 {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyEip4844 {
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::Eip4844::default(),
bls_to_execution_changes: VariableList::empty(),
blob_kzg_commitments: VariableList::empty(),
},
}
}
@@ -533,7 +659,7 @@ macro_rules! impl_from {
parent_root,
state_root,
body,
}, payload)
}, payload.map(Into::into))
}
}
}
@@ -542,6 +668,8 @@ macro_rules! impl_from {
impl_from!(BeaconBlockBase, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyBase<_, _>| body.into());
impl_from!(BeaconBlockAltair, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyAltair<_, _>| body.into());
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!(BeaconBlockEip4844, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyEip4844<_, _>| body.into());
// We can clone blocks with payloads to blocks without payloads, without cloning the payload.
macro_rules! impl_clone_as_blinded {
@@ -572,6 +700,8 @@ macro_rules! impl_clone_as_blinded {
impl_clone_as_blinded!(BeaconBlockBase, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockAltair, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
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!(BeaconBlockEip4844, <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.
@@ -601,6 +731,24 @@ impl<E: EthSpec> From<BeaconBlock<E, FullPayload<E>>>
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BeaconBlock<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
Ok(map_fork_name!(
fork_name,
Self,
serde_json::from_value(value).map_err(|e| serde::de::Error::custom(format!(
"BeaconBlock failed to deserialize: {:?}",
e
)))?
))
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -650,19 +798,65 @@ mod tests {
});
}
#[test]
fn roundtrip_capella_block() {
let rng = &mut XorShiftRng::from_seed([42; 16]);
let spec = &ForkName::Capella.make_genesis_spec(MainnetEthSpec::default_spec());
let inner_block = BeaconBlockCapella {
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: BeaconBlockBodyCapella::random_for_test(rng),
};
let block = BeaconBlock::Capella(inner_block.clone());
test_ssz_tree_hash_pair_with(&block, &inner_block, |bytes| {
BeaconBlock::from_ssz_bytes(bytes, spec)
});
}
#[test]
fn roundtrip_4844_block() {
let rng = &mut XorShiftRng::from_seed([42; 16]);
let spec = &ForkName::Eip4844.make_genesis_spec(MainnetEthSpec::default_spec());
let inner_block = BeaconBlockEip4844 {
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: BeaconBlockBodyEip4844::random_for_test(rng),
};
let block = BeaconBlock::Eip4844(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;
let spec = E::default_spec();
let mut spec = E::default_spec();
let rng = &mut XorShiftRng::from_seed([42; 16]);
let fork_epoch = spec.altair_fork_epoch.unwrap();
let altair_fork_epoch = spec.altair_fork_epoch.unwrap();
let base_epoch = fork_epoch.saturating_sub(1_u64);
let base_epoch = altair_fork_epoch.saturating_sub(1_u64);
let base_slot = base_epoch.end_slot(E::slots_per_epoch());
let altair_epoch = fork_epoch;
let altair_epoch = altair_fork_epoch;
let altair_slot = altair_epoch.start_slot(E::slots_per_epoch());
let capella_epoch = altair_fork_epoch + 1;
let capella_slot = capella_epoch.start_slot(E::slots_per_epoch());
let eip4844_epoch = capella_epoch + 1;
let eip4844_slot = eip4844_epoch.start_slot(E::slots_per_epoch());
spec.altair_fork_epoch = Some(altair_epoch);
spec.capella_fork_epoch = Some(capella_epoch);
spec.eip4844_fork_epoch = Some(eip4844_epoch);
// BeaconBlockBase
{
@@ -707,5 +901,49 @@ mod tests {
BeaconBlock::from_ssz_bytes(&bad_altair_block.as_ssz_bytes(), &spec)
.expect_err("bad altair block cannot be decoded");
}
// BeaconBlockCapella
{
let good_block = BeaconBlock::Capella(BeaconBlockCapella {
slot: capella_slot,
..<_>::random_for_test(rng)
});
// It's invalid to have an Capella block with a epoch lower than the fork epoch.
let bad_block = {
let mut bad = good_block.clone();
*bad.slot_mut() = altair_slot;
bad
};
assert_eq!(
BeaconBlock::from_ssz_bytes(&good_block.as_ssz_bytes(), &spec)
.expect("good capella block can be decoded"),
good_block
);
BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec)
.expect_err("bad capella block cannot be decoded");
}
// BeaconBlockEip4844
{
let good_block = BeaconBlock::Eip4844(BeaconBlockEip4844 {
slot: eip4844_slot,
..<_>::random_for_test(rng)
});
// It's invalid to have an Capella block with a epoch lower than the fork epoch.
let bad_block = {
let mut bad = good_block.clone();
*bad.slot_mut() = capella_slot;
bad
};
assert_eq!(
BeaconBlock::from_ssz_bytes(&good_block.as_ssz_bytes(), &spec)
.expect("good eip4844 block can be decoded"),
good_block
);
BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec)
.expect_err("bad eip4844 block cannot be decoded");
}
}
}

View File

@@ -1,3 +1,4 @@
use crate::kzg_commitment::KzgCommitment;
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
@@ -13,7 +14,7 @@ use tree_hash_derive::TreeHash;
///
/// This *superstruct* abstracts over the hard-fork.
#[superstruct(
variants(Base, Altair, Merge),
variants(Base, Altair, Merge, Capella, Eip4844),
variant_attributes(
derive(
Debug,
@@ -25,20 +26,24 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
),
derivative(PartialEq, Hash(bound = "T: EthSpec, Payload: ExecPayload<T>")),
serde(bound = "T: EthSpec, Payload: ExecPayload<T>", deny_unknown_fields),
cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))
derivative(PartialEq, Hash(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")),
serde(
bound = "T: EthSpec, Payload: AbstractExecPayload<T>",
deny_unknown_fields
),
arbitrary(bound = "T: EthSpec, Payload: AbstractExecPayload<T>"),
),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
)]
#[derive(Debug, Clone, Serialize, Deserialize, Derivative)]
#[derive(Debug, Clone, Serialize, Deserialize, Derivative, arbitrary::Arbitrary)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(untagged)]
#[serde(bound = "T: EthSpec, Payload: ExecPayload<T>")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub struct BeaconBlockBody<T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>> {
#[serde(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
#[arbitrary(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
pub struct BeaconBlockBody<T: EthSpec, Payload: AbstractExecPayload<T> = FullPayload<T>> {
pub randao_reveal: Signature,
pub eth1_data: Eth1Data,
pub graffiti: Graffiti,
@@ -47,21 +52,50 @@ pub struct BeaconBlockBody<T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>>
pub attestations: VariableList<Attestation<T>, T::MaxAttestations>,
pub deposits: VariableList<Deposit, T::MaxDeposits>,
pub voluntary_exits: VariableList<SignedVoluntaryExit, T::MaxVoluntaryExits>,
#[superstruct(only(Altair, Merge))]
#[superstruct(only(Altair, Merge, Capella, Eip4844))]
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
// payloads.
#[superstruct(only(Merge))]
#[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))]
#[serde(flatten)]
pub execution_payload: Payload,
pub execution_payload: Payload::Merge,
#[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))]
#[serde(flatten)]
pub execution_payload: Payload::Capella,
#[superstruct(only(Eip4844), partial_getter(rename = "execution_payload_eip4844"))]
#[serde(flatten)]
pub execution_payload: Payload::Eip4844,
#[superstruct(only(Capella, Eip4844))]
pub bls_to_execution_changes:
VariableList<SignedBlsToExecutionChange, T::MaxBlsToExecutionChanges>,
#[superstruct(only(Eip4844))]
pub blob_kzg_commitments: VariableList<KzgCommitment, T::MaxBlobsPerBlock>,
#[superstruct(only(Base, Altair))]
#[ssz(skip_serializing, skip_deserializing)]
#[tree_hash(skip_hashing)]
#[serde(skip)]
#[arbitrary(default)]
pub _phantom: PhantomData<Payload>,
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBody<T, Payload> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'_>, Error> {
self.to_ref().execution_payload()
}
}
impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBodyRef<'a, T, Payload> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'a>, Error> {
match self {
Self::Base(_) | Self::Altair(_) => Err(Error::IncorrectStateVariant),
Self::Merge(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Capella(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Eip4844(body) => Ok(Payload::Ref::from(&body.execution_payload)),
}
}
}
impl<'a, T: EthSpec> BeaconBlockBodyRef<'a, T> {
/// Get the fork_name of this object
pub fn fork_name(self) -> ForkName {
@@ -69,6 +103,8 @@ impl<'a, T: EthSpec> BeaconBlockBodyRef<'a, T> {
BeaconBlockBodyRef::Base { .. } => ForkName::Base,
BeaconBlockBodyRef::Altair { .. } => ForkName::Altair,
BeaconBlockBodyRef::Merge { .. } => ForkName::Merge,
BeaconBlockBodyRef::Capella { .. } => ForkName::Capella,
BeaconBlockBodyRef::Eip4844 { .. } => ForkName::Eip4844,
}
}
}
@@ -214,7 +250,7 @@ impl<E: EthSpec> From<BeaconBlockBodyAltair<E, FullPayload<E>>>
impl<E: EthSpec> From<BeaconBlockBodyMerge<E, FullPayload<E>>>
for (
BeaconBlockBodyMerge<E, BlindedPayload<E>>,
Option<ExecutionPayload<E>>,
Option<ExecutionPayloadMerge<E>>,
)
{
fn from(body: BeaconBlockBodyMerge<E, FullPayload<E>>) -> Self {
@@ -228,7 +264,7 @@ impl<E: EthSpec> From<BeaconBlockBodyMerge<E, FullPayload<E>>>
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayload { execution_payload },
execution_payload: FullPayloadMerge { execution_payload },
} = body;
(
@@ -242,7 +278,7 @@ impl<E: EthSpec> From<BeaconBlockBodyMerge<E, FullPayload<E>>>
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayload {
execution_payload: BlindedPayloadMerge {
execution_payload_header: From::from(&execution_payload),
},
},
@@ -251,6 +287,92 @@ impl<E: EthSpec> From<BeaconBlockBodyMerge<E, FullPayload<E>>>
}
}
impl<E: EthSpec> From<BeaconBlockBodyCapella<E, FullPayload<E>>>
for (
BeaconBlockBodyCapella<E, BlindedPayload<E>>,
Option<ExecutionPayloadCapella<E>>,
)
{
fn from(body: BeaconBlockBodyCapella<E, FullPayload<E>>) -> Self {
let BeaconBlockBodyCapella {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadCapella { execution_payload },
bls_to_execution_changes,
} = body;
(
BeaconBlockBodyCapella {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadCapella {
execution_payload_header: From::from(&execution_payload),
},
bls_to_execution_changes,
},
Some(execution_payload),
)
}
}
impl<E: EthSpec> From<BeaconBlockBodyEip4844<E, FullPayload<E>>>
for (
BeaconBlockBodyEip4844<E, BlindedPayload<E>>,
Option<ExecutionPayloadEip4844<E>>,
)
{
fn from(body: BeaconBlockBodyEip4844<E, FullPayload<E>>) -> Self {
let BeaconBlockBodyEip4844 {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadEip4844 { execution_payload },
bls_to_execution_changes,
blob_kzg_commitments,
} = body;
(
BeaconBlockBodyEip4844 {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadEip4844 {
execution_payload_header: From::from(&execution_payload),
},
bls_to_execution_changes,
blob_kzg_commitments,
},
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>> {
@@ -278,7 +400,7 @@ impl<E: EthSpec> BeaconBlockBodyMerge<E, FullPayload<E>> {
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayload { execution_payload },
execution_payload: FullPayloadMerge { execution_payload },
} = self;
BeaconBlockBodyMerge {
@@ -291,13 +413,83 @@ impl<E: EthSpec> BeaconBlockBodyMerge<E, FullPayload<E>> {
deposits: deposits.clone(),
voluntary_exits: voluntary_exits.clone(),
sync_aggregate: sync_aggregate.clone(),
execution_payload: BlindedPayload {
execution_payload_header: From::from(execution_payload),
execution_payload: BlindedPayloadMerge {
execution_payload_header: execution_payload.into(),
},
}
}
}
impl<E: EthSpec> BeaconBlockBodyCapella<E, FullPayload<E>> {
pub fn clone_as_blinded(&self) -> BeaconBlockBodyCapella<E, BlindedPayload<E>> {
let BeaconBlockBodyCapella {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadCapella { execution_payload },
bls_to_execution_changes,
} = self;
BeaconBlockBodyCapella {
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: BlindedPayloadCapella {
execution_payload_header: execution_payload.into(),
},
bls_to_execution_changes: bls_to_execution_changes.clone(),
}
}
}
impl<E: EthSpec> BeaconBlockBodyEip4844<E, FullPayload<E>> {
pub fn clone_as_blinded(&self) -> BeaconBlockBodyEip4844<E, BlindedPayload<E>> {
let BeaconBlockBodyEip4844 {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadEip4844 { execution_payload },
bls_to_execution_changes,
blob_kzg_commitments,
} = self;
BeaconBlockBodyEip4844 {
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: BlindedPayloadEip4844 {
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>>,
@@ -307,7 +499,7 @@ impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
fn from(body: BeaconBlockBody<E, FullPayload<E>>) -> Self {
map_beacon_block_body!(body, |inner, cons| {
let (block, payload) = inner.into();
(cons(block), payload)
(cons(block), payload.map(Into::into))
})
}
}

View File

@@ -10,9 +10,19 @@ use tree_hash_derive::TreeHash;
/// A header of a `BeaconBlock`.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct BeaconBlockHeader {
pub slot: Slot,

View File

@@ -17,8 +17,7 @@ impl<'a> BeaconCommittee<'a> {
}
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Default, Clone, Debug, PartialEq)]
#[derive(arbitrary::Arbitrary, Default, Clone, Debug, PartialEq)]
pub struct OwnedBeaconCommittee {
pub slot: Slot,
pub index: CommitteeIndex,

View File

@@ -14,6 +14,7 @@ use ssz::{ssz_encode, Decode, DecodeError, Encode};
use ssz_derive::{Decode, Encode};
use ssz_types::{typenum::Unsigned, BitVector, FixedVector};
use std::convert::TryInto;
use std::hash::Hash;
use std::{fmt, mem, sync::Arc};
use superstruct::superstruct;
use swap_or_not_shuffle::compute_shuffled_index;
@@ -25,6 +26,7 @@ pub use self::committee_cache::{
compute_committee_index_in_epoch, compute_committee_range_in_epoch, epoch_committee_count,
CommitteeCache,
};
use crate::historical_summary::HistoricalSummary;
pub use clone_config::CloneConfig;
pub use eth_spec::*;
pub use iter::BlockRootsIter;
@@ -120,6 +122,7 @@ pub enum Error {
ArithError(ArithError),
MissingBeaconBlock(SignedBeaconBlockHash),
MissingBeaconState(BeaconStateHash),
PayloadConversionLogicFlaw,
SyncCommitteeNotKnown {
current_epoch: Epoch,
epoch: Epoch,
@@ -144,8 +147,7 @@ impl AllowNextEpoch {
}
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
#[derive(PartialEq, Eq, Hash, Clone, Copy, arbitrary::Arbitrary)]
pub struct BeaconStateHash(Hash256);
impl fmt::Debug for BeaconStateHash {
@@ -174,7 +176,7 @@ impl From<BeaconStateHash> for Hash256 {
/// The state of the `BeaconChain` at some slot.
#[superstruct(
variants(Base, Altair, Merge),
variants(Base, Altair, Merge, Capella, Eip4844),
variant_attributes(
derive(
Derivative,
@@ -187,18 +189,19 @@ impl From<BeaconStateHash> for Hash256 {
TreeHash,
TestRandom,
CompareFields,
arbitrary::Arbitrary
),
serde(bound = "T: EthSpec", deny_unknown_fields),
arbitrary(bound = "T: EthSpec"),
derivative(Clone),
cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))
),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
)]
#[derive(Debug, PartialEq, Serialize, Deserialize, Encode, TreeHash)]
#[derive(Debug, PartialEq, Serialize, Deserialize, Encode, TreeHash, arbitrary::Arbitrary)]
#[serde(untagged)]
#[serde(bound = "T: EthSpec")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[arbitrary(bound = "T: EthSpec")]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
pub struct BeaconState<T>
@@ -222,6 +225,7 @@ where
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
#[compare_fields(as_slice)]
pub state_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
// Frozen in Capella, replaced by historical_summaries
pub historical_roots: VariableList<Hash256, T::HistoricalRootsLimit>,
// Ethereum 1.0 chain data
@@ -252,9 +256,9 @@ where
pub current_epoch_attestations: VariableList<PendingAttestation<T>, T::MaxPendingAttestations>,
// Participation (Altair and later)
#[superstruct(only(Altair, Merge))]
#[superstruct(only(Altair, Merge, Capella, Eip4844))]
pub previous_epoch_participation: VariableList<ParticipationFlags, T::ValidatorRegistryLimit>,
#[superstruct(only(Altair, Merge))]
#[superstruct(only(Altair, Merge, Capella, Eip4844))]
pub current_epoch_participation: VariableList<ParticipationFlags, T::ValidatorRegistryLimit>,
// Finality
@@ -269,18 +273,42 @@ where
// Inactivity
#[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")]
#[superstruct(only(Altair, Merge))]
#[superstruct(only(Altair, Merge, Capella, Eip4844))]
pub inactivity_scores: VariableList<u64, T::ValidatorRegistryLimit>,
// Light-client sync committees
#[superstruct(only(Altair, Merge))]
#[superstruct(only(Altair, Merge, Capella, Eip4844))]
pub current_sync_committee: Arc<SyncCommittee<T>>,
#[superstruct(only(Altair, Merge))]
#[superstruct(only(Altair, Merge, Capella, Eip4844))]
pub next_sync_committee: Arc<SyncCommittee<T>>,
// Execution
#[superstruct(only(Merge))]
pub latest_execution_payload_header: ExecutionPayloadHeader<T>,
#[superstruct(
only(Merge),
partial_getter(rename = "latest_execution_payload_header_merge")
)]
pub latest_execution_payload_header: ExecutionPayloadHeaderMerge<T>,
#[superstruct(
only(Capella),
partial_getter(rename = "latest_execution_payload_header_capella")
)]
pub latest_execution_payload_header: ExecutionPayloadHeaderCapella<T>,
#[superstruct(
only(Eip4844),
partial_getter(rename = "latest_execution_payload_header_eip4844")
)]
pub latest_execution_payload_header: ExecutionPayloadHeaderEip4844<T>,
// Capella
#[superstruct(only(Capella, Eip4844), partial_getter(copy))]
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub next_withdrawal_index: u64,
#[superstruct(only(Capella, Eip4844), partial_getter(copy))]
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub next_withdrawal_validator_index: u64,
// Deep history valid from Capella onwards.
#[superstruct(only(Capella, Eip4844))]
pub historical_summaries: VariableList<HistoricalSummary, T::HistoricalRootsLimit>,
// Caching (not in the spec)
#[serde(skip_serializing, skip_deserializing)]
@@ -391,6 +419,8 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Base { .. } => ForkName::Base,
BeaconState::Altair { .. } => ForkName::Altair,
BeaconState::Merge { .. } => ForkName::Merge,
BeaconState::Capella { .. } => ForkName::Capella,
BeaconState::Eip4844 { .. } => ForkName::Eip4844,
};
if fork_at_slot == object_fork {
@@ -680,6 +710,39 @@ impl<T: EthSpec> BeaconState<T> {
.ok_or(Error::ShuffleIndexOutOfBounds(index))
}
/// Convenience accessor for the `execution_payload_header` as an `ExecutionPayloadHeaderRef`.
pub fn latest_execution_payload_header(&self) -> Result<ExecutionPayloadHeaderRef<T>, Error> {
match self {
BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant),
BeaconState::Merge(state) => Ok(ExecutionPayloadHeaderRef::Merge(
&state.latest_execution_payload_header,
)),
BeaconState::Capella(state) => Ok(ExecutionPayloadHeaderRef::Capella(
&state.latest_execution_payload_header,
)),
BeaconState::Eip4844(state) => Ok(ExecutionPayloadHeaderRef::Eip4844(
&state.latest_execution_payload_header,
)),
}
}
pub fn latest_execution_payload_header_mut(
&mut self,
) -> Result<ExecutionPayloadHeaderRefMut<T>, Error> {
match self {
BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant),
BeaconState::Merge(state) => Ok(ExecutionPayloadHeaderRefMut::Merge(
&mut state.latest_execution_payload_header,
)),
BeaconState::Capella(state) => Ok(ExecutionPayloadHeaderRefMut::Capella(
&mut state.latest_execution_payload_header,
)),
BeaconState::Eip4844(state) => Ok(ExecutionPayloadHeaderRefMut::Eip4844(
&mut state.latest_execution_payload_header,
)),
}
}
/// Return `true` if the validator who produced `slot_signature` is eligible to aggregate.
///
/// Spec v0.12.1
@@ -1104,6 +1167,8 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Base(state) => (&mut state.validators, &mut state.balances),
BeaconState::Altair(state) => (&mut state.validators, &mut state.balances),
BeaconState::Merge(state) => (&mut state.validators, &mut state.balances),
BeaconState::Capella(state) => (&mut state.validators, &mut state.balances),
BeaconState::Eip4844(state) => (&mut state.validators, &mut state.balances),
}
}
@@ -1300,12 +1365,16 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant),
BeaconState::Altair(state) => Ok(&mut state.current_epoch_participation),
BeaconState::Merge(state) => Ok(&mut state.current_epoch_participation),
BeaconState::Capella(state) => Ok(&mut state.current_epoch_participation),
BeaconState::Eip4844(state) => Ok(&mut state.current_epoch_participation),
}
} else if epoch == self.previous_epoch() {
match self {
BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant),
BeaconState::Altair(state) => Ok(&mut state.previous_epoch_participation),
BeaconState::Merge(state) => Ok(&mut state.previous_epoch_participation),
BeaconState::Capella(state) => Ok(&mut state.previous_epoch_participation),
BeaconState::Eip4844(state) => Ok(&mut state.previous_epoch_participation),
}
} else {
Err(BeaconStateError::EpochOutOfBounds)
@@ -1610,6 +1679,8 @@ impl<T: EthSpec> BeaconState<T> {
BeaconState::Base(inner) => BeaconState::Base(inner.clone()),
BeaconState::Altair(inner) => BeaconState::Altair(inner.clone()),
BeaconState::Merge(inner) => BeaconState::Merge(inner.clone()),
BeaconState::Capella(inner) => BeaconState::Capella(inner.clone()),
BeaconState::Eip4844(inner) => BeaconState::Eip4844(inner.clone()),
};
if config.committee_caches {
*res.committee_caches_mut() = self.committee_caches().clone();
@@ -1777,7 +1848,25 @@ impl<T: EthSpec> CompareFields for BeaconState<T> {
(BeaconState::Base(x), BeaconState::Base(y)) => x.compare_fields(y),
(BeaconState::Altair(x), BeaconState::Altair(y)) => x.compare_fields(y),
(BeaconState::Merge(x), BeaconState::Merge(y)) => x.compare_fields(y),
(BeaconState::Capella(x), BeaconState::Capella(y)) => x.compare_fields(y),
(BeaconState::Eip4844(x), BeaconState::Eip4844(y)) => x.compare_fields(y),
_ => panic!("compare_fields: mismatched state variants",),
}
}
}
impl<T: EthSpec> ForkVersionDeserialize for BeaconState<T> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
Ok(map_fork_name!(
fork_name,
Self,
serde_json::from_value(value).map_err(|e| serde::de::Error::custom(format!(
"BeaconState failed to deserialize: {:?}",
e
)))?
))
}
}

View File

@@ -336,7 +336,6 @@ pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> V
active
}
#[cfg(feature = "arbitrary-fuzz")]
impl arbitrary::Arbitrary<'_> for CommitteeCache {
fn arbitrary(_u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self::default())

View File

@@ -61,7 +61,6 @@ impl ExitCache {
}
}
#[cfg(feature = "arbitrary-fuzz")]
impl arbitrary::Arbitrary<'_> for ExitCache {
fn arbitrary(_u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self::default())

View File

@@ -42,7 +42,6 @@ impl PubkeyCache {
}
}
#[cfg(feature = "arbitrary-fuzz")]
impl arbitrary::Arbitrary<'_> for PubkeyCache {
fn arbitrary(_u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self::default())

View File

@@ -2,7 +2,7 @@
use crate::test_utils::*;
use crate::test_utils::{SeedableRng, XorShiftRng};
use beacon_chain::test_utils::{
interop_genesis_state, test_spec, BeaconChainHarness, EphemeralHarnessType,
interop_genesis_state_with_eth1, test_spec, BeaconChainHarness, EphemeralHarnessType,
DEFAULT_ETH1_BLOCK_HASH,
};
use beacon_chain::types::{
@@ -551,7 +551,7 @@ fn tree_hash_cache_linear_history_long_skip() {
let spec = &test_spec::<MinimalEthSpec>();
// This state has a cache that advances normally each slot.
let mut state: BeaconState<MinimalEthSpec> = interop_genesis_state(
let mut state: BeaconState<MinimalEthSpec> = interop_genesis_state_with_eth1(
&keypairs,
0,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),

View File

@@ -3,6 +3,7 @@
#![allow(clippy::indexing_slicing)]
use super::Error;
use crate::historical_summary::HistoricalSummaryCache;
use crate::{BeaconState, EthSpec, Hash256, ParticipationList, Slot, Unsigned, Validator};
use cached_tree_hash::{int_log, CacheArena, CachedTreeHash, TreeHashCache};
use rayon::prelude::*;
@@ -142,6 +143,7 @@ pub struct BeaconTreeHashCacheInner<T: EthSpec> {
block_roots: TreeHashCache,
state_roots: TreeHashCache,
historical_roots: TreeHashCache,
historical_summaries: OptionalTreeHashCache,
balances: TreeHashCache,
randao_mixes: TreeHashCache,
slashings: TreeHashCache,
@@ -164,6 +166,14 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
let historical_roots = state
.historical_roots()
.new_tree_hash_cache(&mut fixed_arena);
let historical_summaries = OptionalTreeHashCache::new(
state
.historical_summaries()
.ok()
.map(HistoricalSummaryCache::new)
.as_ref(),
);
let randao_mixes = state.randao_mixes().new_tree_hash_cache(&mut fixed_arena);
let validators = ValidatorsListTreeHashCache::new::<T>(state.validators());
@@ -200,6 +210,7 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
block_roots,
state_roots,
historical_roots,
historical_summaries,
balances,
randao_mixes,
slashings,
@@ -249,6 +260,7 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
.slashings()
.recalculate_tree_hash_root(&mut self.slashings_arena, &mut self.slashings)?,
];
// Participation
if let BeaconState::Base(state) = state {
leaves.push(state.previous_epoch_attestations.tree_hash_root());
@@ -291,6 +303,24 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
if let Ok(payload_header) = state.latest_execution_payload_header() {
leaves.push(payload_header.tree_hash_root());
}
// Withdrawal indices (Capella and later).
if let Ok(next_withdrawal_index) = state.next_withdrawal_index() {
leaves.push(next_withdrawal_index.tree_hash_root());
}
if let Ok(next_withdrawal_validator_index) = state.next_withdrawal_validator_index() {
leaves.push(next_withdrawal_validator_index.tree_hash_root());
}
// Historical roots/summaries (Capella and later).
if let Ok(historical_summaries) = state.historical_summaries() {
leaves.push(
self.historical_summaries.recalculate_tree_hash_root(
&HistoricalSummaryCache::new(historical_summaries),
)?,
);
}
Ok(leaves)
}
@@ -570,7 +600,6 @@ impl OptionalTreeHashCacheInner {
}
}
#[cfg(feature = "arbitrary-fuzz")]
impl<T: EthSpec> arbitrary::Arbitrary<'_> for BeaconTreeHashCache<T> {
fn arbitrary(_u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self::default())

View File

@@ -0,0 +1,43 @@
use crate::kzg_proof::KzgProof;
use crate::{Blob, EthSpec, Hash256, SignedRoot, Slot};
use serde_derive::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use ssz_types::VariableList;
use tree_hash_derive::TreeHash;
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
PartialEq,
Default,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct BlobsSidecar<T: EthSpec> {
pub beacon_block_root: Hash256,
pub beacon_block_slot: Slot,
pub blobs: VariableList<Blob<T>, T::MaxBlobsPerBlock>,
pub kzg_aggregate_proof: KzgProof,
}
impl<T: EthSpec> SignedRoot for BlobsSidecar<T> {}
impl<T: EthSpec> BlobsSidecar<T> {
pub fn empty() -> Self {
Self::default()
}
#[allow(clippy::integer_arithmetic)]
pub fn max_size() -> usize {
// Fixed part
Self::empty().as_ssz_bytes().len()
// Max size of variable length `blobs` field
+ (T::max_blobs_per_block() * <Blob<T> as Encode>::ssz_fixed_len())
}
}

View File

@@ -0,0 +1,57 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::PublicKeyBytes;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct BlsToExecutionChange {
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub validator_index: u64,
pub from_bls_pubkey: PublicKeyBytes,
pub to_execution_address: Address,
}
impl SignedRoot for BlsToExecutionChange {}
impl BlsToExecutionChange {
pub fn sign(
self,
secret_key: &SecretKey,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> SignedBlsToExecutionChange {
let domain = spec.compute_domain(
Domain::BlsToExecutionChange,
spec.genesis_fork_version,
genesis_validators_root,
);
let message = self.signing_root(domain);
SignedBlsToExecutionChange {
message: self,
signature: secret_key.sign(message),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(BlsToExecutionChange);
}

View File

@@ -1,4 +1,7 @@
use crate::{ChainSpec, EthSpec, ExecPayload, ExecutionPayloadHeader, SignedRoot, Uint256};
use crate::{
AbstractExecPayload, ChainSpec, EthSpec, ExecPayload, ExecutionPayloadHeader, ForkName,
ForkVersionDeserialize, SignedRoot, Uint256,
};
use bls::PublicKeyBytes;
use bls::Signature;
use serde::{Deserialize as De, Deserializer, Serialize as Ser, Serializer};
@@ -10,7 +13,7 @@ use tree_hash_derive::TreeHash;
#[serde_as]
#[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)]
#[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
pub struct BuilderBid<E: EthSpec, Payload: ExecPayload<E>> {
pub struct BuilderBid<E: EthSpec, Payload: AbstractExecPayload<E>> {
#[serde_as(as = "BlindedPayloadAsHeader<E>")]
pub header: Payload,
#[serde(with = "eth2_serde_utils::quoted_u256")]
@@ -21,16 +24,70 @@ pub struct BuilderBid<E: EthSpec, Payload: ExecPayload<E>> {
_phantom_data: PhantomData<E>,
}
impl<E: EthSpec, Payload: ExecPayload<E>> SignedRoot for BuilderBid<E, Payload> {}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedRoot for BuilderBid<E, Payload> {}
/// Validator registration, for use in interacting with servers implementing the builder API.
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
#[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
pub struct SignedBuilderBid<E: EthSpec, Payload: ExecPayload<E>> {
pub struct SignedBuilderBid<E: EthSpec, Payload: AbstractExecPayload<E>> {
pub message: BuilderBid<E, Payload>,
pub signature: Signature,
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BuilderBid<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
let convert_err = |_| {
serde::de::Error::custom(
"BuilderBid failed to deserialize: unable to convert payload header to payload",
)
};
#[derive(Deserialize)]
struct Helper {
header: serde_json::Value,
#[serde(with = "eth2_serde_utils::quoted_u256")]
value: Uint256,
pubkey: PublicKeyBytes,
}
let helper: Helper = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
let payload_header =
ExecutionPayloadHeader::deserialize_by_fork::<'de, D>(helper.header, fork_name)?;
Ok(Self {
header: Payload::try_from(payload_header).map_err(convert_err)?,
value: helper.value,
pubkey: helper.pubkey,
_phantom_data: Default::default(),
})
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for SignedBuilderBid<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct Helper {
pub message: serde_json::Value,
pub signature: Signature,
}
let helper: Helper = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(Self {
message: BuilderBid::deserialize_by_fork::<'de, D>(helper.message, fork_name)?,
signature: helper.signature,
})
}
}
struct BlindedPayloadAsHeader<E>(PhantomData<E>);
impl<E: EthSpec, Payload: ExecPayload<E>> SerializeAs<Payload> for BlindedPayloadAsHeader<E> {
@@ -42,7 +99,7 @@ impl<E: EthSpec, Payload: ExecPayload<E>> SerializeAs<Payload> for BlindedPayloa
}
}
impl<'de, E: EthSpec, Payload: ExecPayload<E>> DeserializeAs<'de, Payload>
impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> DeserializeAs<'de, Payload>
for BlindedPayloadAsHeader<E>
{
fn deserialize_as<D>(deserializer: D) -> Result<Payload, D::Error>
@@ -55,7 +112,7 @@ impl<'de, E: EthSpec, Payload: ExecPayload<E>> DeserializeAs<'de, Payload>
}
}
impl<E: EthSpec, Payload: ExecPayload<E>> SignedBuilderBid<E, Payload> {
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBuilderBid<E, Payload> {
pub fn verify_signature(&self, spec: &ChainSpec) -> bool {
self.message
.pubkey

View File

@@ -11,8 +11,10 @@ use tree_hash::TreeHash;
/// Each of the BLS signature domains.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Domain {
BlsToExecutionChange,
BeaconProposer,
BeaconAttester,
BlobsSideCar,
Randao,
Deposit,
VoluntaryExit,
@@ -27,8 +29,7 @@ pub enum Domain {
/// Lighthouse's internal configuration struct.
///
/// Contains a mixture of "preset" and "config" values w.r.t to the EF definitions.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Debug, Clone)]
#[derive(arbitrary::Arbitrary, PartialEq, Debug, Clone)]
pub struct ChainSpec {
/*
* Config name
@@ -99,6 +100,7 @@ pub struct ChainSpec {
*/
pub(crate) domain_beacon_proposer: u32,
pub(crate) domain_beacon_attester: u32,
pub(crate) domain_blobs_sidecar: u32,
pub(crate) domain_randao: u32,
pub(crate) domain_deposit: u32,
pub(crate) domain_voluntary_exit: u32,
@@ -151,6 +153,20 @@ pub struct ChainSpec {
pub terminal_block_hash_activation_epoch: Epoch,
pub safe_slots_to_import_optimistically: u64,
/*
* Capella hard fork params
*/
pub capella_fork_version: [u8; 4],
/// The Capella fork epoch is optional, with `None` representing "Capella never happens".
pub capella_fork_epoch: Option<Epoch>,
pub max_validators_per_withdrawals_sweep: u64,
/*
* Eip4844 hard fork params
*/
pub eip4844_fork_version: [u8; 4],
pub eip4844_fork_epoch: Option<Epoch>,
/*
* Networking
*/
@@ -170,6 +186,11 @@ pub struct ChainSpec {
* Application params
*/
pub(crate) domain_application_mask: u32,
/*
* Capella params
*/
pub(crate) domain_bls_to_execution_change: u32,
}
impl ChainSpec {
@@ -234,11 +255,17 @@ 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.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.eip4844_fork_epoch {
Some(fork_epoch) if epoch >= fork_epoch => ForkName::Eip4844,
_ => 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,
},
},
},
}
}
@@ -249,6 +276,8 @@ impl ChainSpec {
ForkName::Base => self.genesis_fork_version,
ForkName::Altair => self.altair_fork_version,
ForkName::Merge => self.bellatrix_fork_version,
ForkName::Capella => self.capella_fork_version,
ForkName::Eip4844 => self.eip4844_fork_version,
}
}
@@ -258,6 +287,8 @@ impl ChainSpec {
ForkName::Base => Some(Epoch::new(0)),
ForkName::Altair => self.altair_fork_epoch,
ForkName::Merge => self.bellatrix_fork_epoch,
ForkName::Capella => self.capella_fork_epoch,
ForkName::Eip4844 => self.eip4844_fork_epoch,
}
}
@@ -267,6 +298,8 @@ impl ChainSpec {
BeaconState::Base(_) => self.inactivity_penalty_quotient,
BeaconState::Altair(_) => self.inactivity_penalty_quotient_altair,
BeaconState::Merge(_) => self.inactivity_penalty_quotient_bellatrix,
BeaconState::Capella(_) => self.inactivity_penalty_quotient_bellatrix,
BeaconState::Eip4844(_) => self.inactivity_penalty_quotient_bellatrix,
}
}
@@ -279,6 +312,8 @@ impl ChainSpec {
BeaconState::Base(_) => self.proportional_slashing_multiplier,
BeaconState::Altair(_) => self.proportional_slashing_multiplier_altair,
BeaconState::Merge(_) => self.proportional_slashing_multiplier_bellatrix,
BeaconState::Capella(_) => self.proportional_slashing_multiplier_bellatrix,
BeaconState::Eip4844(_) => self.proportional_slashing_multiplier_bellatrix,
}
}
@@ -291,6 +326,8 @@ impl ChainSpec {
BeaconState::Base(_) => self.min_slashing_penalty_quotient,
BeaconState::Altair(_) => self.min_slashing_penalty_quotient_altair,
BeaconState::Merge(_) => self.min_slashing_penalty_quotient_bellatrix,
BeaconState::Capella(_) => self.min_slashing_penalty_quotient_bellatrix,
BeaconState::Eip4844(_) => self.min_slashing_penalty_quotient_bellatrix,
}
}
@@ -329,6 +366,7 @@ impl ChainSpec {
match domain {
Domain::BeaconProposer => self.domain_beacon_proposer,
Domain::BeaconAttester => self.domain_beacon_attester,
Domain::BlobsSideCar => self.domain_blobs_sidecar,
Domain::Randao => self.domain_randao,
Domain::Deposit => self.domain_deposit,
Domain::VoluntaryExit => self.domain_voluntary_exit,
@@ -338,6 +376,7 @@ impl ChainSpec {
Domain::ContributionAndProof => self.domain_contribution_and_proof,
Domain::SyncCommitteeSelectionProof => self.domain_sync_committee_selection_proof,
Domain::ApplicationMask(application_domain) => application_domain.get_domain_constant(),
Domain::BlsToExecutionChange => self.domain_bls_to_execution_change,
}
}
@@ -500,8 +539,8 @@ impl ChainSpec {
* Initial Values
*/
genesis_fork_version: [0; 4],
bls_withdrawal_prefix_byte: 0,
eth1_address_withdrawal_prefix_byte: 1,
bls_withdrawal_prefix_byte: 0x00,
eth1_address_withdrawal_prefix_byte: 0x01,
/*
* Time parameters
@@ -535,6 +574,7 @@ impl ChainSpec {
domain_voluntary_exit: 4,
domain_selection_proof: 5,
domain_aggregate_and_proof: 6,
domain_blobs_sidecar: 10, // 0x0a000000
/*
* Fork choice
@@ -589,6 +629,19 @@ impl ChainSpec {
terminal_block_hash_activation_epoch: Epoch::new(u64::MAX),
safe_slots_to_import_optimistically: 128u64,
/*
* Capella hard fork params
*/
capella_fork_version: [0x03, 00, 00, 00],
capella_fork_epoch: None,
max_validators_per_withdrawals_sweep: 16384,
/*
* Eip4844 hard fork params
*/
eip4844_fork_version: [0x04, 0x00, 0x00, 0x00],
eip4844_fork_epoch: None,
/*
* Network specific
*/
@@ -608,6 +661,11 @@ impl ChainSpec {
* Application specific
*/
domain_application_mask: APPLICATION_DOMAIN_BUILDER,
/*
* Capella params
*/
domain_bls_to_execution_change: 10,
}
}
@@ -647,6 +705,13 @@ impl ChainSpec {
// `Uint256::MAX` which is `2*256- 1`.
.checked_add(Uint256::one())
.expect("addition does not overflow"),
// Capella
capella_fork_version: [0x03, 0x00, 0x00, 0x01],
capella_fork_epoch: None,
max_validators_per_withdrawals_sweep: 16,
// Eip4844
eip4844_fork_version: [0x04, 0x00, 0x00, 0x01],
eip4844_fork_epoch: None,
// Other
network_id: 2, // lighthouse testnet network id
deposit_chain_id: 5,
@@ -709,8 +774,8 @@ impl ChainSpec {
* Initial Values
*/
genesis_fork_version: [0x00, 0x00, 0x00, 0x64],
bls_withdrawal_prefix_byte: 0,
eth1_address_withdrawal_prefix_byte: 1,
bls_withdrawal_prefix_byte: 0x00,
eth1_address_withdrawal_prefix_byte: 0x01,
/*
* Time parameters
@@ -744,6 +809,7 @@ impl ChainSpec {
domain_voluntary_exit: 4,
domain_selection_proof: 5,
domain_aggregate_and_proof: 6,
domain_blobs_sidecar: 10,
/*
* Fork choice
@@ -800,6 +866,19 @@ impl ChainSpec {
terminal_block_hash_activation_epoch: Epoch::new(u64::MAX),
safe_slots_to_import_optimistically: 128u64,
/*
* Capella hard fork params
*/
capella_fork_version: [0x03, 0x00, 0x00, 0x64],
capella_fork_epoch: None,
max_validators_per_withdrawals_sweep: 16384,
/*
* Eip4844 hard fork params
*/
eip4844_fork_version: [0x04, 0x00, 0x00, 0x64],
eip4844_fork_epoch: None,
/*
* Network specific
*/
@@ -819,6 +898,11 @@ impl ChainSpec {
* Application specific
*/
domain_application_mask: APPLICATION_DOMAIN_BUILDER,
/*
* Capella params
*/
domain_bls_to_execution_change: 10,
}
}
}
@@ -878,6 +962,22 @@ pub struct Config {
#[serde(deserialize_with = "deserialize_fork_epoch")]
pub bellatrix_fork_epoch: Option<MaybeQuoted<Epoch>>,
#[serde(default = "default_capella_fork_version")]
#[serde(with = "eth2_serde_utils::bytes_4_hex")]
capella_fork_version: [u8; 4],
#[serde(default)]
#[serde(serialize_with = "serialize_fork_epoch")]
#[serde(deserialize_with = "deserialize_fork_epoch")]
pub capella_fork_epoch: Option<MaybeQuoted<Epoch>>,
#[serde(default = "default_eip4844_fork_version")]
#[serde(with = "eth2_serde_utils::bytes_4_hex")]
eip4844_fork_version: [u8; 4],
#[serde(default)]
#[serde(serialize_with = "serialize_fork_epoch")]
#[serde(deserialize_with = "deserialize_fork_epoch")]
pub eip4844_fork_epoch: Option<MaybeQuoted<Epoch>>,
#[serde(with = "eth2_serde_utils::quoted_u64")]
seconds_per_slot: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
@@ -915,6 +1015,16 @@ fn default_bellatrix_fork_version() -> [u8; 4] {
[0xff, 0xff, 0xff, 0xff]
}
fn default_capella_fork_version() -> [u8; 4] {
// TODO: determine if the bellatrix example should be copied like this
[0xff, 0xff, 0xff, 0xff]
}
fn default_eip4844_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
@@ -1011,6 +1121,14 @@ impl Config {
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 }),
eip4844_fork_version: spec.eip4844_fork_version,
eip4844_fork_epoch: spec
.eip4844_fork_epoch
.map(|epoch| MaybeQuoted { value: epoch }),
seconds_per_slot: spec.seconds_per_slot,
seconds_per_eth1_block: spec.seconds_per_eth1_block,
@@ -1056,6 +1174,10 @@ impl Config {
altair_fork_epoch,
bellatrix_fork_epoch,
bellatrix_fork_version,
capella_fork_epoch,
capella_fork_version,
eip4844_fork_epoch,
eip4844_fork_version,
seconds_per_slot,
seconds_per_eth1_block,
min_validator_withdrawability_delay,
@@ -1086,6 +1208,10 @@ impl Config {
altair_fork_epoch: altair_fork_epoch.map(|q| q.value),
bellatrix_fork_epoch: bellatrix_fork_epoch.map(|q| q.value),
bellatrix_fork_version,
capella_fork_epoch: capella_fork_epoch.map(|q| q.value),
capella_fork_version,
eip4844_fork_epoch: eip4844_fork_epoch.map(|q| q.value),
eip4844_fork_version,
seconds_per_slot,
seconds_per_eth1_block,
min_validator_withdrawability_delay,
@@ -1159,6 +1285,7 @@ mod tests {
test_domain(Domain::BeaconProposer, spec.domain_beacon_proposer, &spec);
test_domain(Domain::BeaconAttester, spec.domain_beacon_attester, &spec);
test_domain(Domain::BlobsSideCar, spec.domain_blobs_sidecar, &spec);
test_domain(Domain::Randao, spec.domain_randao, &spec);
test_domain(Domain::Deposit, spec.domain_deposit, &spec);
test_domain(Domain::VoluntaryExit, spec.domain_voluntary_exit, &spec);
@@ -1177,6 +1304,14 @@ mod tests {
apply_bit_mask(builder_domain_pre_mask, &spec),
&spec,
);
test_domain(
Domain::BlsToExecutionChange,
spec.domain_bls_to_execution_change,
&spec,
);
test_domain(Domain::BlobsSideCar, spec.domain_blobs_sidecar, &spec);
}
fn apply_bit_mask(domain_bytes: [u8; 4], spec: &ChainSpec) -> u32 {

View File

@@ -8,8 +8,8 @@ use tree_hash_derive::TreeHash;
/// Casper FFG checkpoint, used in attestations.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
Copy,

View File

@@ -1,5 +1,6 @@
use crate::{
consts::altair, AltairPreset, BasePreset, BellatrixPreset, ChainSpec, Config, EthSpec, ForkName,
consts::altair, AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, ChainSpec, Config,
EthSpec, ForkName,
};
use maplit::hashmap;
use serde_derive::{Deserialize, Serialize};
@@ -11,7 +12,7 @@ use superstruct::superstruct;
///
/// Mostly useful for the API.
#[superstruct(
variants(Altair, Bellatrix),
variants(Bellatrix, Capella),
variant_attributes(derive(Serialize, Deserialize, Debug, PartialEq, Clone))
)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
@@ -24,9 +25,11 @@ pub struct ConfigAndPreset {
pub base_preset: BasePreset,
#[serde(flatten)]
pub altair_preset: AltairPreset,
#[superstruct(only(Bellatrix))]
#[serde(flatten)]
pub bellatrix_preset: BellatrixPreset,
#[superstruct(only(Capella))]
#[serde(flatten)]
pub capella_preset: CapellaPreset,
/// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks.
#[serde(flatten)]
pub extra_fields: HashMap<String, Value>,
@@ -37,14 +40,24 @@ impl ConfigAndPreset {
let config = Config::from_chain_spec::<T>(spec);
let base_preset = BasePreset::from_chain_spec::<T>(spec);
let altair_preset = AltairPreset::from_chain_spec::<T>(spec);
let bellatrix_preset = BellatrixPreset::from_chain_spec::<T>(spec);
let extra_fields = get_extra_fields(spec);
if spec.bellatrix_fork_epoch.is_some()
if spec.capella_fork_epoch.is_some()
|| fork_name.is_none()
|| fork_name == Some(ForkName::Merge)
|| fork_name == Some(ForkName::Capella)
{
let bellatrix_preset = BellatrixPreset::from_chain_spec::<T>(spec);
let capella_preset = CapellaPreset::from_chain_spec::<T>(spec);
ConfigAndPreset::Capella(ConfigAndPresetCapella {
config,
base_preset,
altair_preset,
bellatrix_preset,
capella_preset,
extra_fields,
})
} else {
ConfigAndPreset::Bellatrix(ConfigAndPresetBellatrix {
config,
base_preset,
@@ -52,13 +65,6 @@ impl ConfigAndPreset {
bellatrix_preset,
extra_fields,
})
} else {
ConfigAndPreset::Altair(ConfigAndPresetAltair {
config,
base_preset,
altair_preset,
extra_fields,
})
}
}
}
@@ -72,6 +78,7 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
"bls_withdrawal_prefix".to_uppercase() => u8_hex(spec.bls_withdrawal_prefix_byte),
"domain_beacon_proposer".to_uppercase() => u32_hex(spec.domain_beacon_proposer),
"domain_beacon_attester".to_uppercase() => u32_hex(spec.domain_beacon_attester),
"domain_blobs_sidecar".to_uppercase() => u32_hex(spec.domain_blobs_sidecar),
"domain_randao".to_uppercase()=> u32_hex(spec.domain_randao),
"domain_deposit".to_uppercase()=> u32_hex(spec.domain_deposit),
"domain_voluntary_exit".to_uppercase() => u32_hex(spec.domain_voluntary_exit),
@@ -130,8 +137,8 @@ mod test {
.write(false)
.open(tmp_file.as_ref())
.expect("error while opening the file");
let from: ConfigAndPresetBellatrix =
let from: ConfigAndPresetCapella =
serde_yaml::from_reader(reader).expect("error while deserializing");
assert_eq!(ConfigAndPreset::Bellatrix(from), yamlconfig);
assert_eq!(ConfigAndPreset::Capella(from), yamlconfig);
}
}

View File

@@ -22,3 +22,17 @@ pub mod altair {
pub mod merge {
pub const INTERVALS_PER_SLOT: u64 = 3;
}
pub mod eip4844 {
use crate::Uint256;
use lazy_static::lazy_static;
lazy_static! {
pub static ref BLS_MODULUS: Uint256 = Uint256::from_dec_str(
"52435875175126190479447740508185965837690552500527637822603658699938581184513"
)
.expect("should initialize BLS_MODULUS");
}
pub const BLOB_TX_TYPE: u8 = 5;
pub const VERSIONED_HASH_VERSION_KZG: u8 = 1;
}

View File

@@ -9,9 +9,20 @@ use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
/// A Validators aggregate sync committee contribution and selection proof.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct ContributionAndProof<T: EthSpec> {
/// The index of the validator that created the sync contribution.
#[serde(with = "eth2_serde_utils::quoted_u64")]

View File

@@ -11,9 +11,18 @@ pub const DEPOSIT_TREE_DEPTH: usize = 32;
/// A deposit to potentially become a beacon chain validator.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct Deposit {
pub proof: FixedVector<Hash256, U33>,

View File

@@ -10,9 +10,18 @@ use tree_hash_derive::TreeHash;
/// The data supplied by the user to the deposit contract.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct DepositData {
pub pubkey: PublicKeyBytes,

View File

@@ -10,8 +10,18 @@ use tree_hash_derive::TreeHash;
/// The data supplied by the user to the deposit contract.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct DepositMessage {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,

View File

@@ -10,9 +10,18 @@ use tree_hash_derive::TreeHash;
/// a nodes local ENR.
///
/// Spec v0.11
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Default,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct EnrForkId {
#[serde(with = "eth2_serde_utils::bytes_4_hex")]

View File

@@ -9,8 +9,8 @@ use tree_hash_derive::TreeHash;
/// Contains data obtained from the Eth1 chain.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Clone,

View File

@@ -48,7 +48,9 @@ impl fmt::Display for EthSpecId {
}
}
pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + Eq {
pub trait EthSpec:
'static + Default + Sync + Send + Clone + Debug + PartialEq + Eq + for<'a> arbitrary::Arbitrary<'a>
{
/*
* Constants
*/
@@ -95,6 +97,16 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
type GasLimitDenominator: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MinGasLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxExtraDataBytes: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* New in Capella
*/
type MaxBlsToExecutionChanges: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxWithdrawalsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* New in Eip4844
*/
type MaxBlobsPerBlock: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type FieldElementsPerBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* Derived values (set these CAREFULLY)
*/
@@ -222,6 +234,21 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
fn bytes_per_logs_bloom() -> usize {
Self::BytesPerLogsBloom::to_usize()
}
/// Returns the `MAX_BLS_TO_EXECUTION_CHANGES` constant for this specification.
fn max_bls_to_execution_changes() -> usize {
Self::MaxBlsToExecutionChanges::to_usize()
}
/// Returns the `MAX_WITHDRAWALS_PER_PAYLOAD` constant for this specification.
fn max_withdrawals_per_payload() -> usize {
Self::MaxWithdrawalsPerPayload::to_usize()
}
/// Returns the `MAX_BLOBS_PER_BLOCK` constant for this specification.
fn max_blobs_per_block() -> usize {
Self::MaxBlobsPerBlock::to_usize()
}
}
/// Macro to inherit some type values from another EthSpec.
@@ -233,8 +260,7 @@ macro_rules! params_from_eth_spec {
}
/// Ethereum Foundation specifications.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
pub struct MainnetEthSpec;
impl EthSpec for MainnetEthSpec {
@@ -262,9 +288,13 @@ impl EthSpec for MainnetEthSpec {
type GasLimitDenominator = U1024;
type MinGasLimit = U5000;
type MaxExtraDataBytes = U32;
type MaxBlobsPerBlock = U16; // 2**4 = 16
type FieldElementsPerBlob = U4096;
type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count
type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch
type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch
type MaxBlsToExecutionChanges = U16;
type MaxWithdrawalsPerPayload = U16;
fn default_spec() -> ChainSpec {
ChainSpec::mainnet()
@@ -276,8 +306,7 @@ impl EthSpec for MainnetEthSpec {
}
/// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
pub struct MinimalEthSpec;
impl EthSpec for MinimalEthSpec {
@@ -290,6 +319,7 @@ impl EthSpec for MinimalEthSpec {
type SyncSubcommitteeSize = U8; // 32 committee size / 4 sync committee subnet count
type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch
type SlotsPerEth1VotingPeriod = U32; // 4 epochs * 8 slots per epoch
type MaxWithdrawalsPerPayload = U4;
params_from_eth_spec!(MainnetEthSpec {
JustificationBitsLength,
@@ -309,7 +339,10 @@ impl EthSpec for MinimalEthSpec {
BytesPerLogsBloom,
GasLimitDenominator,
MinGasLimit,
MaxExtraDataBytes
MaxExtraDataBytes,
MaxBlsToExecutionChanges,
MaxBlobsPerBlock,
FieldElementsPerBlob
});
fn default_spec() -> ChainSpec {
@@ -322,8 +355,7 @@ impl EthSpec for MinimalEthSpec {
}
/// Gnosis Beacon Chain specifications.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
pub struct GnosisEthSpec;
impl EthSpec for GnosisEthSpec {
@@ -354,6 +386,10 @@ impl EthSpec for GnosisEthSpec {
type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count
type MaxPendingAttestations = U2048; // 128 max attestations * 16 slots per epoch
type SlotsPerEth1VotingPeriod = U1024; // 64 epochs * 16 slots per epoch
type MaxBlsToExecutionChanges = U16;
type MaxWithdrawalsPerPayload = U16;
type MaxBlobsPerBlock = U16; // 2**4 = 16
type FieldElementsPerBlob = U4096;
fn default_spec() -> ChainSpec {
ChainSpec::gnosis()

View File

@@ -6,8 +6,18 @@ use serde_derive::{Deserialize, Serialize};
use ssz::{Decode, DecodeError, Encode};
use std::fmt;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash, Derivative)]
#[derive(
arbitrary::Arbitrary,
Default,
Clone,
Copy,
Serialize,
Deserialize,
Eq,
PartialEq,
Hash,
Derivative,
)]
#[derivative(Debug = "transparent")]
#[serde(transparent)]
pub struct ExecutionBlockHash(Hash256);

View File

@@ -17,14 +17,16 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
use crate::{Address, EthSpec, ExecutionPayload, Hash256, Hash64, Uint256};
use crate::{Address, EthSpec, ExecutionPayloadRef, Hash256, Hash64, Uint256};
use metastruct::metastruct;
/// Execution block header as used for RLP encoding and Keccak hashing.
///
/// Credit to Reth for the type definition.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[metastruct(mappings(map_execution_block_header_fields()))]
#[metastruct(mappings(map_execution_block_header_fields_except_withdrawals(exclude(
withdrawals_root
))))]
pub struct ExecutionBlockHeader {
pub parent_hash: Hash256,
pub ommers_hash: Hash256,
@@ -42,33 +44,36 @@ pub struct ExecutionBlockHeader {
pub mix_hash: Hash256,
pub nonce: Hash64,
pub base_fee_per_gas: Uint256,
pub withdrawals_root: Option<Hash256>,
}
impl ExecutionBlockHeader {
pub fn from_payload<E: EthSpec>(
payload: &ExecutionPayload<E>,
payload: ExecutionPayloadRef<E>,
rlp_empty_list_root: Hash256,
rlp_transactions_root: Hash256,
rlp_withdrawals_root: Option<Hash256>,
) -> Self {
// Most of these field mappings are defined in EIP-3675 except for `mixHash`, which is
// defined in EIP-4399.
ExecutionBlockHeader {
parent_hash: payload.parent_hash.into_root(),
parent_hash: payload.parent_hash().into_root(),
ommers_hash: rlp_empty_list_root,
beneficiary: payload.fee_recipient,
state_root: payload.state_root,
beneficiary: payload.fee_recipient(),
state_root: payload.state_root(),
transactions_root: rlp_transactions_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom.clone().into(),
receipts_root: payload.receipts_root(),
logs_bloom: payload.logs_bloom().clone().into(),
difficulty: Uint256::zero(),
number: payload.block_number.into(),
gas_limit: payload.gas_limit.into(),
gas_used: payload.gas_used.into(),
timestamp: payload.timestamp,
extra_data: payload.extra_data.clone().into(),
mix_hash: payload.prev_randao,
number: payload.block_number().into(),
gas_limit: payload.gas_limit().into(),
gas_used: payload.gas_used().into(),
timestamp: payload.timestamp(),
extra_data: payload.extra_data().clone().into(),
mix_hash: payload.prev_randao(),
nonce: Hash64::zero(),
base_fee_per_gas: payload.base_fee_per_gas,
base_fee_per_gas: payload.base_fee_per_gas(),
withdrawals_root: rlp_withdrawals_root,
}
}
}

View File

@@ -1,7 +1,7 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize};
use ssz::Encode;
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
@@ -12,50 +12,162 @@ pub type Transactions<T> = VariableList<
<T as EthSpec>::MaxTransactionsPerPayload,
>;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub type Withdrawals<T> = VariableList<Withdrawal, <T as EthSpec>::MaxWithdrawalsPerPayload>;
#[superstruct(
variants(Merge, Capella, Eip4844),
variant_attributes(
derive(
Default,
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
),
derivative(PartialEq, Hash(bound = "T: EthSpec")),
serde(bound = "T: EthSpec", deny_unknown_fields),
arbitrary(bound = "T: EthSpec")
),
cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
map_into(FullPayload, BlindedPayload),
map_ref_into(ExecutionPayloadHeader)
)]
#[derive(
Default, Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Derivative,
Debug, Clone, Serialize, Encode, Deserialize, TreeHash, Derivative, arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(bound = "T: EthSpec")]
#[serde(bound = "T: EthSpec", untagged)]
#[arbitrary(bound = "T: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
#[tree_hash(enum_behaviour = "transparent")]
pub struct ExecutionPayload<T: EthSpec> {
#[superstruct(getter(copy))]
pub parent_hash: ExecutionBlockHash,
#[superstruct(getter(copy))]
pub fee_recipient: Address,
#[superstruct(getter(copy))]
pub state_root: Hash256,
#[superstruct(getter(copy))]
pub receipts_root: Hash256,
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
#[superstruct(getter(copy))]
pub prev_randao: Hash256,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub block_number: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub gas_limit: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub gas_used: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub timestamp: u64,
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
#[serde(with = "eth2_serde_utils::quoted_u256")]
#[superstruct(getter(copy))]
pub base_fee_per_gas: Uint256,
#[superstruct(only(Eip4844))]
#[serde(with = "eth2_serde_utils::quoted_u256")]
#[superstruct(getter(copy))]
pub excess_data_gas: Uint256,
#[superstruct(getter(copy))]
pub block_hash: ExecutionBlockHash,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<T>,
#[superstruct(only(Capella, Eip4844))]
pub withdrawals: Withdrawals<T>,
}
impl<'a, T: EthSpec> ExecutionPayloadRef<'a, T> {
// this emulates clone on a normal reference type
pub fn clone_from_ref(&self) -> ExecutionPayload<T> {
map_execution_payload_ref!(&'a _, self, move |payload, cons| {
cons(payload);
payload.clone().into()
})
}
}
impl<T: EthSpec> ExecutionPayload<T> {
pub fn empty() -> Self {
Self::default()
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayload: {fork_name}",
))),
ForkName::Merge => ExecutionPayloadMerge::from_ssz_bytes(bytes).map(Self::Merge),
ForkName::Capella => ExecutionPayloadCapella::from_ssz_bytes(bytes).map(Self::Capella),
ForkName::Eip4844 => ExecutionPayloadEip4844::from_ssz_bytes(bytes).map(Self::Eip4844),
}
}
#[allow(clippy::integer_arithmetic)]
/// Returns the maximum size of an execution payload.
pub fn max_execution_payload_size() -> usize {
pub fn max_execution_payload_merge_size() -> usize {
// Fixed part
Self::empty().as_ssz_bytes().len()
ExecutionPayloadMerge::<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()))
}
#[allow(clippy::integer_arithmetic)]
/// Returns the maximum size of an execution payload.
pub fn max_execution_payload_capella_size() -> usize {
// Fixed part
ExecutionPayloadCapella::<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())
}
#[allow(clippy::integer_arithmetic)]
/// Returns the maximum size of an execution payload.
pub fn max_execution_payload_eip4844_size() -> usize {
// Fixed part
ExecutionPayloadEip4844::<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> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
let convert_err = |e| {
serde::de::Error::custom(format!("ExecutionPayload failed to deserialize: {:?}", e))
};
Ok(match fork_name {
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::Eip4844 => Self::Eip4844(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 '{}'",
fork_name
)));
}
})
}
}

View File

@@ -1,49 +1,167 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize};
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use BeaconStateError;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[superstruct(
variants(Merge, Capella, Eip4844),
variant_attributes(
derive(
Default,
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
),
derivative(PartialEq, Hash(bound = "T: EthSpec")),
serde(bound = "T: EthSpec", deny_unknown_fields),
arbitrary(bound = "T: EthSpec")
),
ref_attributes(derive(PartialEq, TreeHash), tree_hash(enum_behaviour = "transparent")),
cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant")
)]
#[derive(
Default, Debug, Clone, Serialize, Deserialize, Derivative, Encode, Decode, TreeHash, TestRandom,
Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative, arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(bound = "T: EthSpec", untagged)]
#[arbitrary(bound = "T: EthSpec")]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
pub struct ExecutionPayloadHeader<T: EthSpec> {
#[superstruct(getter(copy))]
pub parent_hash: ExecutionBlockHash,
#[superstruct(getter(copy))]
pub fee_recipient: Address,
#[superstruct(getter(copy))]
pub state_root: Hash256,
#[superstruct(getter(copy))]
pub receipts_root: Hash256,
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
#[superstruct(getter(copy))]
pub prev_randao: Hash256,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub block_number: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub gas_limit: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub gas_used: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub timestamp: u64,
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
#[serde(with = "eth2_serde_utils::quoted_u256")]
#[superstruct(getter(copy))]
pub base_fee_per_gas: Uint256,
#[superstruct(only(Eip4844))]
#[serde(with = "eth2_serde_utils::quoted_u256")]
#[superstruct(getter(copy))]
pub excess_data_gas: Uint256,
#[superstruct(getter(copy))]
pub block_hash: ExecutionBlockHash,
#[superstruct(getter(copy))]
pub transactions_root: Hash256,
#[superstruct(only(Capella, Eip4844))]
#[superstruct(getter(copy))]
pub withdrawals_root: Hash256,
}
impl<T: EthSpec> ExecutionPayloadHeader<T> {
pub fn empty() -> Self {
Self::default()
pub fn transactions(&self) -> Option<&Transactions<T>> {
None
}
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadHeader: {fork_name}",
))),
ForkName::Merge => ExecutionPayloadHeaderMerge::from_ssz_bytes(bytes).map(Self::Merge),
ForkName::Capella => {
ExecutionPayloadHeaderCapella::from_ssz_bytes(bytes).map(Self::Capella)
}
ForkName::Eip4844 => {
ExecutionPayloadHeaderEip4844::from_ssz_bytes(bytes).map(Self::Eip4844)
}
}
}
}
impl<'a, T: EthSpec> From<&'a ExecutionPayload<T>> for ExecutionPayloadHeader<T> {
fn from(payload: &'a ExecutionPayload<T>) -> Self {
ExecutionPayloadHeader {
impl<'a, T: EthSpec> ExecutionPayloadHeaderRef<'a, T> {
pub fn is_default_with_zero_roots(self) -> bool {
map_execution_payload_header_ref!(&'a _, self, |inner, cons| {
cons(inner);
*inner == Default::default()
})
}
}
impl<T: EthSpec> ExecutionPayloadHeaderMerge<T> {
pub fn upgrade_to_capella(&self) -> ExecutionPayloadHeaderCapella<T> {
ExecutionPayloadHeaderCapella {
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: Hash256::zero(),
}
}
}
impl<T: EthSpec> ExecutionPayloadHeaderCapella<T> {
pub fn upgrade_to_eip4844(&self) -> ExecutionPayloadHeaderEip4844<T> {
ExecutionPayloadHeaderEip4844 {
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,
// TODO: verify if this is correct
excess_data_gas: Uint256::zero(),
block_hash: self.block_hash,
transactions_root: self.transactions_root,
withdrawals_root: self.withdrawals_root,
}
}
}
impl<'a, T: EthSpec> From<&'a ExecutionPayloadMerge<T>> for ExecutionPayloadHeaderMerge<T> {
fn from(payload: &'a ExecutionPayloadMerge<T>) -> Self {
Self {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
@@ -61,3 +179,135 @@ impl<'a, T: EthSpec> From<&'a ExecutionPayload<T>> for ExecutionPayloadHeader<T>
}
}
}
impl<'a, T: EthSpec> From<&'a ExecutionPayloadCapella<T>> for ExecutionPayloadHeaderCapella<T> {
fn from(payload: &'a ExecutionPayloadCapella<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(),
}
}
}
impl<'a, T: EthSpec> From<&'a ExecutionPayloadEip4844<T>> for ExecutionPayloadHeaderEip4844<T> {
fn from(payload: &'a ExecutionPayloadEip4844<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,
excess_data_gas: payload.excess_data_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
withdrawals_root: payload.withdrawals.tree_hash_root(),
}
}
}
// 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> {
fn from(payload: &'a Self) -> Self {
payload.clone()
}
}
impl<'a, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderCapella<T> {
fn from(payload: &'a Self) -> Self {
payload.clone()
}
}
impl<'a, T: EthSpec> From<&'a Self> for ExecutionPayloadHeaderEip4844<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!(
&'a _,
payload,
|inner, cons| cons(inner.into())
)
}
}
impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for ExecutionPayloadHeaderMerge<T> {
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<T>) -> Result<Self, Self::Error> {
match header {
ExecutionPayloadHeader::Merge(execution_payload_header) => Ok(execution_payload_header),
_ => Err(BeaconStateError::IncorrectStateVariant),
}
}
}
impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for ExecutionPayloadHeaderCapella<T> {
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<T>) -> Result<Self, Self::Error> {
match header {
ExecutionPayloadHeader::Capella(execution_payload_header) => {
Ok(execution_payload_header)
}
_ => Err(BeaconStateError::IncorrectStateVariant),
}
}
}
impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for ExecutionPayloadHeaderEip4844<T> {
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<T>) -> Result<Self, Self::Error> {
match header {
ExecutionPayloadHeader::Eip4844(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,
fork_name: ForkName,
) -> Result<Self, D::Error> {
let convert_err = |e| {
serde::de::Error::custom(format!(
"ExecutionPayloadHeader failed to deserialize: {:?}",
e
))
};
Ok(match fork_name {
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::Eip4844 => Self::Eip4844(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 '{}'",
fork_name
)));
}
})
}
}

View File

@@ -9,8 +9,8 @@ use tree_hash_derive::TreeHash;
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
Copy,

View File

@@ -47,6 +47,20 @@ impl ForkContext {
));
}
if spec.capella_fork_epoch.is_some() {
fork_to_digest.push((
ForkName::Capella,
ChainSpec::compute_fork_digest(spec.capella_fork_version, genesis_validators_root),
));
}
if spec.eip4844_fork_epoch.is_some() {
fork_to_digest.push((
ForkName::Eip4844,
ChainSpec::compute_fork_digest(spec.eip4844_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

@@ -9,9 +9,18 @@ use tree_hash_derive::TreeHash;
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Default,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct ForkData {
#[serde(with = "eth2_serde_utils::bytes_4_hex")]

View File

@@ -11,11 +11,19 @@ pub enum ForkName {
Base,
Altair,
Merge,
Capella,
Eip4844,
}
impl ForkName {
pub fn list_all() -> Vec<ForkName> {
vec![ForkName::Base, ForkName::Altair, ForkName::Merge]
vec![
ForkName::Base,
ForkName::Altair,
ForkName::Merge,
ForkName::Capella,
ForkName::Eip4844,
]
}
/// Set the activation slots in the given `ChainSpec` so that the fork named by `self`
@@ -26,16 +34,36 @@ impl ForkName {
ForkName::Base => {
spec.altair_fork_epoch = None;
spec.bellatrix_fork_epoch = None;
spec.capella_fork_epoch = None;
spec.eip4844_fork_epoch = None;
spec
}
ForkName::Altair => {
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = None;
spec.capella_fork_epoch = None;
spec.eip4844_fork_epoch = None;
spec
}
ForkName::Merge => {
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = None;
spec.eip4844_fork_epoch = None;
spec
}
ForkName::Capella => {
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.eip4844_fork_epoch = None;
spec
}
ForkName::Eip4844 => {
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.eip4844_fork_epoch = Some(Epoch::new(0));
spec
}
}
@@ -49,6 +77,8 @@ impl ForkName {
ForkName::Base => None,
ForkName::Altair => Some(ForkName::Base),
ForkName::Merge => Some(ForkName::Altair),
ForkName::Capella => Some(ForkName::Merge),
ForkName::Eip4844 => Some(ForkName::Capella),
}
}
@@ -59,7 +89,9 @@ impl ForkName {
match self {
ForkName::Base => Some(ForkName::Altair),
ForkName::Altair => Some(ForkName::Merge),
ForkName::Merge => None,
ForkName::Merge => Some(ForkName::Capella),
ForkName::Capella => Some(ForkName::Eip4844),
ForkName::Eip4844 => None,
}
}
}
@@ -101,6 +133,14 @@ macro_rules! map_fork_name_with {
let (value, extra_data) = $body;
($t::Merge(value), extra_data)
}
ForkName::Capella => {
let (value, extra_data) = $body;
($t::Capella(value), extra_data)
}
ForkName::Eip4844 => {
let (value, extra_data) = $body;
($t::Eip4844(value), extra_data)
}
}
};
}
@@ -113,6 +153,8 @@ impl FromStr for ForkName {
"phase0" | "base" => ForkName::Base,
"altair" => ForkName::Altair,
"bellatrix" | "merge" => ForkName::Merge,
"capella" => ForkName::Capella,
"eip4844" => ForkName::Eip4844,
_ => return Err(format!("unknown fork name: {}", fork_name)),
})
}
@@ -124,6 +166,8 @@ impl Display for ForkName {
ForkName::Base => "phase0".fmt(f),
ForkName::Altair => "altair".fmt(f),
ForkName::Merge => "bellatrix".fmt(f),
ForkName::Capella => "capella".fmt(f),
ForkName::Eip4844 => "eip4844".fmt(f),
}
}
}
@@ -155,7 +199,7 @@ mod test {
#[test]
fn previous_and_next_fork_consistent() {
assert_eq!(ForkName::Merge.next_fork(), None);
assert_eq!(ForkName::Eip4844.next_fork(), None);
assert_eq!(ForkName::Base.previous_fork(), None);
for (prev_fork, fork) in ForkName::list_all().into_iter().tuple_windows() {

View File

@@ -0,0 +1,138 @@
use crate::ForkName;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::value::Value;
use std::sync::Arc;
// Deserialize is only implemented for types that implement ForkVersionDeserialize
#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct ExecutionOptimisticForkVersionedResponse<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<ForkName>,
pub execution_optimistic: Option<bool>,
pub data: T,
}
impl<'de, F> serde::Deserialize<'de> for ExecutionOptimisticForkVersionedResponse<F>
where
F: ForkVersionDeserialize,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
version: Option<ForkName>,
execution_optimistic: Option<bool>,
data: serde_json::Value,
}
let helper = Helper::deserialize(deserializer)?;
let data = match helper.version {
Some(fork_name) => F::deserialize_by_fork::<'de, D>(helper.data, fork_name)?,
None => serde_json::from_value(helper.data).map_err(serde::de::Error::custom)?,
};
Ok(ExecutionOptimisticForkVersionedResponse {
version: helper.version,
execution_optimistic: helper.execution_optimistic,
data,
})
}
}
pub trait ForkVersionDeserialize: Sized + DeserializeOwned {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error>;
}
// Deserialize is only implemented for types that implement ForkVersionDeserialize
#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct ForkVersionedResponse<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<ForkName>,
pub data: T,
}
impl<'de, F> serde::Deserialize<'de> for ForkVersionedResponse<F>
where
F: ForkVersionDeserialize,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
version: Option<ForkName>,
data: serde_json::Value,
}
let helper = Helper::deserialize(deserializer)?;
let data = match helper.version {
Some(fork_name) => F::deserialize_by_fork::<'de, D>(helper.data, fork_name)?,
None => serde_json::from_value(helper.data).map_err(serde::de::Error::custom)?,
};
Ok(ForkVersionedResponse {
version: helper.version,
data,
})
}
}
impl<F: ForkVersionDeserialize> ForkVersionDeserialize for Arc<F> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
Ok(Arc::new(F::deserialize_by_fork::<'de, D>(
value, fork_name,
)?))
}
}
#[cfg(test)]
mod fork_version_response_tests {
use crate::{
ExecutionPayload, ExecutionPayloadMerge, ForkName, ForkVersionedResponse, MainnetEthSpec,
};
use serde_json::json;
#[test]
fn fork_versioned_response_deserialize_correct_fork() {
type E = MainnetEthSpec;
let response_json =
serde_json::to_string(&json!(ForkVersionedResponse::<ExecutionPayload<E>> {
version: Some(ForkName::Merge),
data: ExecutionPayload::Merge(ExecutionPayloadMerge::default()),
}))
.unwrap();
let result: Result<ForkVersionedResponse<ExecutionPayload<E>>, _> =
serde_json::from_str(&response_json);
assert!(result.is_ok());
}
#[test]
fn fork_versioned_response_deserialize_incorrect_fork() {
type E = MainnetEthSpec;
let response_json =
serde_json::to_string(&json!(ForkVersionedResponse::<ExecutionPayload<E>> {
version: Some(ForkName::Capella),
data: ExecutionPayload::Merge(ExecutionPayloadMerge::default()),
}))
.unwrap();
let result: Result<ForkVersionedResponse<ExecutionPayload<E>>, _> =
serde_json::from_str(&response_json);
assert!(result.is_err());
}
}

View File

@@ -1,14 +0,0 @@
/// Note: this object does not actually exist in the spec.
///
/// We use it for managing attestations that have not been aggregated.
use super::{AttestationData, Signature};
use serde_derive::Serialize;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct FreeAttestation {
pub data: AttestationData,
pub signature: Signature,
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub validator_index: u64,
}

View File

@@ -14,7 +14,7 @@ pub const GRAFFITI_BYTES_LEN: usize = 32;
/// The 32-byte `graffiti` field on a beacon block.
#[derive(Default, Debug, PartialEq, Hash, Clone, Copy, Serialize, Deserialize)]
#[serde(transparent)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(arbitrary::Arbitrary)]
pub struct Graffiti(#[serde(with = "serde_graffiti")] pub [u8; GRAFFITI_BYTES_LEN]);
impl Graffiti {

View File

@@ -10,8 +10,19 @@ use tree_hash_derive::TreeHash;
/// Historical block and state roots.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
arbitrary::Arbitrary,
)]
#[arbitrary(bound = "T: EthSpec")]
pub struct HistoricalBatch<T: EthSpec> {
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
pub state_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,

View File

@@ -0,0 +1,89 @@
use crate::test_utils::TestRandom;
use crate::Unsigned;
use crate::{BeaconState, EthSpec, Hash256};
use cached_tree_hash::Error;
use cached_tree_hash::{int_log, CacheArena, CachedTreeHash, TreeHashCache};
use compare_fields_derive::CompareFields;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::VariableList;
use test_random_derive::TestRandom;
use tree_hash::{mix_in_length, TreeHash, BYTES_PER_CHUNK};
use tree_hash_derive::TreeHash;
/// `HistoricalSummary` matches the components of the phase0 `HistoricalBatch`
/// making the two hash_tree_root-compatible. This struct is introduced into the beacon state
/// in the Capella hard fork.
///
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#historicalsummary
#[derive(
Debug,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
CompareFields,
Clone,
Copy,
Default,
arbitrary::Arbitrary,
)]
pub struct HistoricalSummary {
block_summary_root: Hash256,
state_summary_root: Hash256,
}
impl HistoricalSummary {
pub fn new<T: EthSpec>(state: &BeaconState<T>) -> Self {
Self {
block_summary_root: state.block_roots().tree_hash_root(),
state_summary_root: state.state_roots().tree_hash_root(),
}
}
}
/// Wrapper type allowing the implementation of `CachedTreeHash`.
#[derive(Debug)]
pub struct HistoricalSummaryCache<'a, N: Unsigned> {
pub inner: &'a VariableList<HistoricalSummary, N>,
}
impl<'a, N: Unsigned> HistoricalSummaryCache<'a, N> {
pub fn new(inner: &'a VariableList<HistoricalSummary, N>) -> Self {
Self { inner }
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.inner.len()
}
}
impl<'a, N: Unsigned> CachedTreeHash<TreeHashCache> for HistoricalSummaryCache<'a, N> {
fn new_tree_hash_cache(&self, arena: &mut CacheArena) -> TreeHashCache {
TreeHashCache::new(arena, int_log(N::to_usize()), self.len())
}
fn recalculate_tree_hash_root(
&self,
arena: &mut CacheArena,
cache: &mut TreeHashCache,
) -> Result<Hash256, Error> {
Ok(mix_in_length(
&cache.recalculate_merkle_root(arena, leaf_iter(self.inner))?,
self.len(),
))
}
}
pub fn leaf_iter(
values: &[HistoricalSummary],
) -> impl Iterator<Item = [u8; BYTES_PER_CHUNK]> + ExactSizeIterator + '_ {
values
.iter()
.map(|value| value.tree_hash_root())
.map(Hash256::to_fixed_bytes)
}

View File

@@ -12,12 +12,21 @@ use tree_hash_derive::TreeHash;
/// To be included in an `AttesterSlashing`.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Derivative, Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
Derivative,
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Eq)] // to satisfy Clippy's lint about `Hash`
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct IndexedAttestation<T: EthSpec> {
/// Lists validator registry indices, not committee indices.
#[serde(with = "quoted_variable_list_u64")]

View File

@@ -0,0 +1,45 @@
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use std::fmt;
use std::fmt::{Display, Formatter};
use tree_hash::{PackedEncoding, TreeHash};
#[derive(
Derivative, Debug, Clone, Encode, Decode, Serialize, Deserialize, arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Eq, Hash)]
#[ssz(struct_behaviour = "transparent")]
pub struct KzgCommitment(#[serde(with = "BigArray")] pub [u8; 48]);
impl Display for KzgCommitment {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", eth2_serde_utils::hex::encode(self.0))
}
}
impl TreeHash for KzgCommitment {
fn tree_hash_type() -> tree_hash::TreeHashType {
<[u8; 48] as TreeHash>::tree_hash_type()
}
fn tree_hash_packed_encoding(&self) -> PackedEncoding {
self.0.tree_hash_packed_encoding()
}
fn tree_hash_packing_factor() -> usize {
<[u8; 48] as TreeHash>::tree_hash_packing_factor()
}
fn tree_hash_root(&self) -> tree_hash::Hash256 {
self.0.tree_hash_root()
}
}
impl TestRandom for KzgCommitment {
fn random_for_test(rng: &mut impl rand::RngCore) -> Self {
KzgCommitment(<[u8; 48] as TestRandom>::random_for_test(rng))
}
}

View File

@@ -0,0 +1,74 @@
use crate::test_utils::{RngCore, TestRandom};
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use ssz_derive::{Decode, Encode};
use std::fmt;
use tree_hash::{PackedEncoding, TreeHash};
const KZG_PROOF_BYTES_LEN: usize = 48;
#[derive(
Debug,
PartialEq,
Hash,
Clone,
Copy,
Encode,
Decode,
Serialize,
Deserialize,
arbitrary::Arbitrary,
)]
#[serde(transparent)]
#[ssz(struct_behaviour = "transparent")]
pub struct KzgProof(#[serde(with = "BigArray")] pub [u8; KZG_PROOF_BYTES_LEN]);
impl fmt::Display for KzgProof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", eth2_serde_utils::hex::encode(self.0))
}
}
impl Default for KzgProof {
fn default() -> Self {
KzgProof([0; 48])
}
}
impl From<[u8; KZG_PROOF_BYTES_LEN]> for KzgProof {
fn from(bytes: [u8; KZG_PROOF_BYTES_LEN]) -> Self {
Self(bytes)
}
}
impl Into<[u8; KZG_PROOF_BYTES_LEN]> for KzgProof {
fn into(self) -> [u8; KZG_PROOF_BYTES_LEN] {
self.0
}
}
impl TreeHash for KzgProof {
fn tree_hash_type() -> tree_hash::TreeHashType {
<[u8; KZG_PROOF_BYTES_LEN]>::tree_hash_type()
}
fn tree_hash_packed_encoding(&self) -> PackedEncoding {
self.0.tree_hash_packed_encoding()
}
fn tree_hash_packing_factor() -> usize {
<[u8; KZG_PROOF_BYTES_LEN]>::tree_hash_packing_factor()
}
fn tree_hash_root(&self) -> tree_hash::Hash256 {
self.0.tree_hash_root()
}
}
impl TestRandom for KzgProof {
fn random_for_test(rng: &mut impl RngCore) -> Self {
let mut bytes = [0; KZG_PROOF_BYTES_LEN];
rng.fill_bytes(&mut bytes);
Self(bytes)
}
}

View File

@@ -1,5 +1,4 @@
//! Ethereum 2.0 types
// Required for big type-level numbers
#![recursion_limit = "128"]
// Clippy lint set up
@@ -28,6 +27,7 @@ pub mod beacon_block_body;
pub mod beacon_block_header;
pub mod beacon_committee;
pub mod beacon_state;
pub mod bls_to_execution_change;
pub mod builder_bid;
pub mod chain_spec;
pub mod checkpoint;
@@ -46,9 +46,10 @@ pub mod execution_payload_header;
pub mod fork;
pub mod fork_data;
pub mod fork_name;
pub mod free_attestation;
pub mod fork_versioned_response;
pub mod graffiti;
pub mod historical_batch;
pub mod historical_summary;
pub mod indexed_attestation;
pub mod light_client_bootstrap;
pub mod light_client_finality_update;
@@ -63,6 +64,7 @@ pub mod shuffling_id;
pub mod signed_aggregate_and_proof;
pub mod signed_beacon_block;
pub mod signed_beacon_block_header;
pub mod signed_bls_to_execution_change;
pub mod signed_contribution_and_proof;
pub mod signed_voluntary_exit;
pub mod signing_data;
@@ -92,11 +94,16 @@ pub mod sync_selection_proof;
pub mod sync_subnet_id;
mod tree_hash_impls;
pub mod validator_registration_data;
pub mod withdrawal;
pub mod slot_data;
#[cfg(feature = "sqlite")]
pub mod sqlite;
pub mod blobs_sidecar;
pub mod kzg_commitment;
pub mod kzg_proof;
use ethereum_types::{H160, H256};
pub use crate::aggregate_and_proof::AggregateAndProof;
@@ -105,20 +112,22 @@ pub use crate::attestation_data::AttestationData;
pub use crate::attestation_duty::AttestationDuty;
pub use crate::attester_slashing::AttesterSlashing;
pub use crate::beacon_block::{
BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockMerge, BeaconBlockRef,
BeaconBlockRefMut, BlindedBeaconBlock,
BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockEip4844,
BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, EmptyBlock,
};
pub use crate::beacon_block_body::{
BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyMerge,
BeaconBlockBodyRef, BeaconBlockBodyRefMut,
BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyCapella,
BeaconBlockBodyEip4844, BeaconBlockBodyMerge, BeaconBlockBodyRef, BeaconBlockBodyRefMut,
};
pub use crate::beacon_block_header::BeaconBlockHeader;
pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
pub use crate::beacon_state::{BeaconTreeHashCache, Error as BeaconStateError, *};
pub use crate::blobs_sidecar::BlobsSidecar;
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, ConfigAndPresetAltair, ConfigAndPresetBellatrix,
ConfigAndPreset, ConfigAndPresetBellatrix, ConfigAndPresetCapella,
};
pub use crate::contribution_and_proof::ContributionAndProof;
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
@@ -130,23 +139,37 @@ pub use crate::eth1_data::Eth1Data;
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, Transaction, Transactions};
pub use crate::execution_payload_header::ExecutionPayloadHeader;
pub use crate::execution_payload::{
ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadEip4844, ExecutionPayloadMerge,
ExecutionPayloadRef, Transaction, Transactions, Withdrawals,
};
pub use crate::execution_payload_header::{
ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderEip4844,
ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
};
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::free_attestation::FreeAttestation;
pub use crate::fork_versioned_response::{
ExecutionOptimisticForkVersionedResponse, ForkVersionDeserialize, ForkVersionedResponse,
};
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
pub use crate::historical_batch::HistoricalBatch;
pub use crate::indexed_attestation::IndexedAttestation;
pub use crate::kzg_commitment::KzgCommitment;
pub use crate::kzg_proof::KzgProof;
pub use crate::light_client_finality_update::LightClientFinalityUpdate;
pub use crate::light_client_optimistic_update::LightClientOptimisticUpdate;
pub use crate::participation_flags::ParticipationFlags;
pub use crate::participation_list::ParticipationList;
pub use crate::payload::{BlindedPayload, BlockType, ExecPayload, FullPayload};
pub use crate::payload::{
AbstractExecPayload, BlindedPayload, BlindedPayloadCapella, BlindedPayloadEip4844,
BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload, FullPayload,
FullPayloadCapella, FullPayloadEip4844, FullPayloadMerge, FullPayloadRef, OwnedExecPayload,
};
pub use crate::pending_attestation::PendingAttestation;
pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset};
pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset, CapellaPreset};
pub use crate::proposer_preparation_data::ProposerPreparationData;
pub use crate::proposer_slashing::ProposerSlashing;
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
@@ -154,10 +177,12 @@ pub use crate::selection_proof::SelectionProof;
pub use crate::shuffling_id::AttestationShufflingId;
pub use crate::signed_aggregate_and_proof::SignedAggregateAndProof;
pub use crate::signed_beacon_block::{
SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockHash,
SignedBeaconBlockMerge, SignedBlindedBeaconBlock,
SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockCapella,
SignedBeaconBlockEip4844, SignedBeaconBlockHash, SignedBeaconBlockMerge,
SignedBlindedBeaconBlock,
};
pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader;
pub use crate::signed_bls_to_execution_change::SignedBlsToExecutionChange;
pub use crate::signed_contribution_and_proof::SignedContributionAndProof;
pub use crate::signed_voluntary_exit::SignedVoluntaryExit;
pub use crate::signing_data::{SignedRoot, SigningData};
@@ -176,13 +201,18 @@ pub use crate::validator::Validator;
pub use crate::validator_registration_data::*;
pub use crate::validator_subscription::ValidatorSubscription;
pub use crate::voluntary_exit::VoluntaryExit;
pub use crate::withdrawal::Withdrawal;
pub use crate::withdrawal_credentials::WithdrawalCredentials;
use serde_big_array::BigArray;
pub type CommitteeIndex = u64;
pub type Hash256 = H256;
pub type Uint256 = ethereum_types::U256;
pub type Address = H160;
pub type ForkVersion = [u8; 4];
pub type BLSFieldElement = Uint256;
pub type Blob<T> = FixedVector<BLSFieldElement, <T as EthSpec>::FieldElementsPerBlob>;
pub type VersionedHash = Hash256;
pub type Hash64 = ethereum_types::H64;
pub use bls::{

View File

@@ -8,9 +8,19 @@ use tree_hash::TreeHash;
/// A LightClientBootstrap is the initializer we send over to lightclient nodes
/// that are trying to generate their basic storage when booting up.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientBootstrap<T: EthSpec> {
/// Requested beacon block header.
pub header: BeaconBlockHeader,

View File

@@ -10,9 +10,19 @@ use tree_hash::TreeHash;
/// A LightClientFinalityUpdate is the update lightclient request or received by a gossip that
/// signal a new finalized beacon block header for the light client sync protocol.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientFinalityUpdate<T: EthSpec> {
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
pub attested_header: BeaconBlockHeader,

View File

@@ -9,9 +9,19 @@ use tree_hash::TreeHash;
/// A LightClientOptimisticUpdate is the update we send on each slot,
/// it is based off the current unfinalized epoch is verified only against BLS signature.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientOptimisticUpdate<T: EthSpec> {
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
pub attested_header: BeaconBlockHeader,

View File

@@ -52,9 +52,19 @@ impl From<ArithError> for Error {
/// A LightClientUpdate is the update we request solely to either complete the bootstraping process,
/// or to sync up to the last committee period, we need to have one ready for each ALTAIR period
/// we go over, note: there is no need to keep all of the updates from [ALTAIR_PERIOD, CURRENT_PERIOD].
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientUpdate<T: EthSpec> {
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
pub attested_header: BeaconBlockHeader,

View File

@@ -7,7 +7,7 @@ use tree_hash::{PackedEncoding, TreeHash, TreeHashType};
#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize, TestRandom)]
#[serde(transparent)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(arbitrary::Arbitrary)]
pub struct ParticipationFlags {
#[serde(with = "eth2_serde_utils::quoted_u8")]
bits: u8,

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,19 @@ use tree_hash_derive::TreeHash;
/// An attestation that has been included in the state but not yet fully processed.
///
/// Spec v0.12.1
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
arbitrary::Arbitrary,
)]
#[arbitrary(bound = "T: EthSpec")]
pub struct PendingAttestation<T: EthSpec> {
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
pub data: AttestationData,
@@ -19,18 +31,6 @@ pub struct PendingAttestation<T: EthSpec> {
pub proposer_index: u64,
}
#[cfg(feature = "arbitrary-fuzz")]
impl<T: EthSpec> arbitrary::Arbitrary<'_> for PendingAttestation<T> {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self {
aggregation_bits: <BitList<T::MaxValidatorsPerCommittee>>::arbitrary(u)?,
data: AttestationData::arbitrary(u)?,
inclusion_delay: u64::arbitrary(u)?,
proposer_index: u64::arbitrary(u)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -184,6 +184,27 @@ impl BellatrixPreset {
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct CapellaPreset {
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub max_bls_to_execution_changes: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub max_withdrawals_per_payload: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub max_validators_per_withdrawals_sweep: u64,
}
impl CapellaPreset {
pub fn from_chain_spec<T: EthSpec>(spec: &ChainSpec) -> Self {
Self {
max_bls_to_execution_changes: T::max_bls_to_execution_changes() as u64,
max_withdrawals_per_payload: T::max_withdrawals_per_payload() as u64,
max_validators_per_withdrawals_sweep: spec.max_validators_per_withdrawals_sweep,
}
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -219,6 +240,9 @@ mod test {
let bellatrix: BellatrixPreset = preset_from_file(&preset_name, "bellatrix.yaml");
assert_eq!(bellatrix, BellatrixPreset::from_chain_spec::<E>(&spec));
let capella: CapellaPreset = preset_from_file(&preset_name, "capella.yaml");
assert_eq!(capella, CapellaPreset::from_chain_spec::<E>(&spec));
}
#[test]

View File

@@ -9,9 +9,19 @@ use tree_hash_derive::TreeHash;
/// Two conflicting proposals from the same proposer (validator).
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct ProposerSlashing {
pub signed_header_1: SignedBeaconBlockHeader,

View File

@@ -14,15 +14,11 @@ impl From<ArithError> for Error {
}
}
#[cfg(feature = "arbitrary-fuzz")]
use arbitrary::Arbitrary;
/// Defines the epochs relative to some epoch. Most useful when referring to the committees prior
/// to and following some epoch.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(Arbitrary))]
#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, PartialEq, Clone, Copy, arbitrary::Arbitrary)]
pub enum RelativeEpoch {
/// The prior epoch.
Previous,

View File

@@ -7,8 +7,7 @@ use ssz::Encode;
use std::cmp;
use std::convert::TryInto;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Debug, Clone)]
#[derive(arbitrary::Arbitrary, PartialEq, Debug, Clone)]
pub struct SelectionProof(Signature);
impl SelectionProof {

View File

@@ -12,9 +12,20 @@ use tree_hash_derive::TreeHash;
/// gossipsub topic.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct SignedAggregateAndProof<T: EthSpec> {
/// The `AggregateAndProof` that was signed.
pub message: AggregateAndProof<T>,

View File

@@ -8,8 +8,7 @@ use superstruct::superstruct;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
#[derive(arbitrary::Arbitrary, PartialEq, Eq, Hash, Clone, Copy)]
pub struct SignedBeaconBlockHash(Hash256);
impl fmt::Debug for SignedBeaconBlockHash {
@@ -38,7 +37,7 @@ impl From<SignedBeaconBlockHash> for Hash256 {
/// A `BeaconBlock` and a signature from its proposer.
#[superstruct(
variants(Base, Altair, Merge),
variants(Base, Altair, Merge, Capella, Eip4844),
variant_attributes(
derive(
Debug,
@@ -49,35 +48,42 @@ impl From<SignedBeaconBlockHash> for Hash256 {
Decode,
TreeHash,
Derivative,
arbitrary::Arbitrary
),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)),
serde(bound = "E: EthSpec, Payload: ExecPayload<E>"),
serde(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
),
map_into(BeaconBlock),
map_ref_into(BeaconBlockRef),
map_ref_mut_into(BeaconBlockRefMut)
)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)]
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative, arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[serde(untagged)]
#[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[serde(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")]
#[arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
pub struct SignedBeaconBlock<E: EthSpec, Payload: ExecPayload<E> = FullPayload<E>> {
pub struct SignedBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E> = FullPayload<E>> {
#[superstruct(only(Base), partial_getter(rename = "message_base"))]
pub message: BeaconBlockBase<E, Payload>,
#[superstruct(only(Altair), partial_getter(rename = "message_altair"))]
pub message: BeaconBlockAltair<E, Payload>,
#[superstruct(only(Merge), partial_getter(rename = "message_merge"))]
pub message: BeaconBlockMerge<E, Payload>,
#[superstruct(only(Capella), partial_getter(rename = "message_capella"))]
pub message: BeaconBlockCapella<E, Payload>,
#[superstruct(only(Eip4844), partial_getter(rename = "message_eip4844"))]
pub message: BeaconBlockEip4844<E, Payload>,
pub signature: Signature,
}
pub type SignedBlindedBeaconBlock<E> = SignedBeaconBlock<E, BlindedPayload<E>>;
impl<E: EthSpec, Payload: ExecPayload<E>> SignedBeaconBlock<E, Payload> {
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload> {
/// Returns the name of the fork pertaining to `self`.
///
/// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork
@@ -129,6 +135,12 @@ impl<E: EthSpec, Payload: ExecPayload<E>> SignedBeaconBlock<E, Payload> {
BeaconBlock::Merge(message) => {
SignedBeaconBlock::Merge(SignedBeaconBlockMerge { message, signature })
}
BeaconBlock::Capella(message) => {
SignedBeaconBlock::Capella(SignedBeaconBlockCapella { message, signature })
}
BeaconBlock::Eip4844(message) => {
SignedBeaconBlock::Eip4844(SignedBeaconBlockEip4844 { message, signature })
}
}
}
@@ -258,7 +270,7 @@ impl<E: EthSpec> From<SignedBeaconBlockAltair<E, BlindedPayload<E>>>
impl<E: EthSpec> SignedBeaconBlockMerge<E, BlindedPayload<E>> {
pub fn into_full_block(
self,
execution_payload: ExecutionPayload<E>,
execution_payload: ExecutionPayloadMerge<E>,
) -> SignedBeaconBlockMerge<E, FullPayload<E>> {
let SignedBeaconBlockMerge {
message:
@@ -278,7 +290,7 @@ impl<E: EthSpec> SignedBeaconBlockMerge<E, BlindedPayload<E>> {
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayload { .. },
execution_payload: BlindedPayloadMerge { .. },
},
},
signature,
@@ -299,7 +311,117 @@ impl<E: EthSpec> SignedBeaconBlockMerge<E, BlindedPayload<E>> {
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayload { execution_payload },
execution_payload: FullPayloadMerge { execution_payload },
},
},
signature,
}
}
}
impl<E: EthSpec> SignedBeaconBlockCapella<E, BlindedPayload<E>> {
pub fn into_full_block(
self,
execution_payload: ExecutionPayloadCapella<E>,
) -> SignedBeaconBlockCapella<E, FullPayload<E>> {
let SignedBeaconBlockCapella {
message:
BeaconBlockCapella {
slot,
proposer_index,
parent_root,
state_root,
body:
BeaconBlockBodyCapella {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadCapella { .. },
bls_to_execution_changes,
},
},
signature,
} = self;
SignedBeaconBlockCapella {
message: BeaconBlockCapella {
slot,
proposer_index,
parent_root,
state_root,
body: BeaconBlockBodyCapella {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadCapella { execution_payload },
bls_to_execution_changes,
},
},
signature,
}
}
}
impl<E: EthSpec> SignedBeaconBlockEip4844<E, BlindedPayload<E>> {
pub fn into_full_block(
self,
execution_payload: ExecutionPayloadEip4844<E>,
) -> SignedBeaconBlockEip4844<E, FullPayload<E>> {
let SignedBeaconBlockEip4844 {
message:
BeaconBlockEip4844 {
slot,
proposer_index,
parent_root,
state_root,
body:
BeaconBlockBodyEip4844 {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadEip4844 { .. },
bls_to_execution_changes,
blob_kzg_commitments,
},
},
signature,
} = self;
SignedBeaconBlockEip4844 {
message: BeaconBlockEip4844 {
slot,
proposer_index,
parent_root,
state_root,
body: BeaconBlockBodyEip4844 {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadEip4844 { execution_payload },
bls_to_execution_changes,
blob_kzg_commitments,
},
},
signature,
@@ -312,12 +434,23 @@ impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
self,
execution_payload: Option<ExecutionPayload<E>>,
) -> Option<SignedBeaconBlock<E, FullPayload<E>>> {
let full_block = match self {
SignedBeaconBlock::Base(block) => SignedBeaconBlock::Base(block.into()),
SignedBeaconBlock::Altair(block) => SignedBeaconBlock::Altair(block.into()),
SignedBeaconBlock::Merge(block) => {
SignedBeaconBlock::Merge(block.into_full_block(execution_payload?))
let full_block = match (self, execution_payload) {
(SignedBeaconBlock::Base(block), _) => SignedBeaconBlock::Base(block.into()),
(SignedBeaconBlock::Altair(block), _) => SignedBeaconBlock::Altair(block.into()),
(SignedBeaconBlock::Merge(block), Some(ExecutionPayload::Merge(payload))) => {
SignedBeaconBlock::Merge(block.into_full_block(payload))
}
(SignedBeaconBlock::Capella(block), Some(ExecutionPayload::Capella(payload))) => {
SignedBeaconBlock::Capella(block.into_full_block(payload))
}
(SignedBeaconBlock::Eip4844(block), Some(ExecutionPayload::Eip4844(payload))) => {
SignedBeaconBlock::Eip4844(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::Eip4844(_), _) => return None,
};
Some(full_block)
}
@@ -354,6 +487,24 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
}
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> ForkVersionDeserialize
for SignedBeaconBlock<E, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
Ok(map_fork_name!(
fork_name,
Self,
serde_json::from_value(value).map_err(|e| serde::de::Error::custom(format!(
"SignedBeaconBlock failed to deserialize: {:?}",
e
)))?
))
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@@ -10,9 +10,19 @@ use tree_hash_derive::TreeHash;
/// A signed header of a `BeaconBlock`.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct SignedBeaconBlockHeader {
pub message: BeaconBlockHeader,

View File

@@ -0,0 +1,33 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::Signature;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct SignedBlsToExecutionChange {
pub message: BlsToExecutionChange,
pub signature: Signature,
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(SignedBlsToExecutionChange);
}

View File

@@ -10,9 +10,20 @@ use tree_hash_derive::TreeHash;
/// A Validators signed contribution proof to publish on the `sync_committee_contribution_and_proof`
/// gossipsub topic.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct SignedContributionAndProof<T: EthSpec> {
/// The `ContributionAndProof` that was signed.
pub message: ContributionAndProof<T>,

View File

@@ -9,9 +9,18 @@ use tree_hash_derive::TreeHash;
/// An exit voluntarily submitted a validator who wishes to withdraw.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct SignedVoluntaryExit {
pub message: VoluntaryExit,

View File

@@ -7,8 +7,18 @@ use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct SigningData {
pub object_root: Hash256,
pub domain: Hash256,

View File

@@ -24,13 +24,35 @@ use std::iter::Iterator;
#[cfg(feature = "legacy-arith")]
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(
arbitrary::Arbitrary,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[serde(transparent)]
pub struct Slot(#[serde(with = "eth2_serde_utils::quoted_u64")] u64);
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(
arbitrary::Arbitrary,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[serde(transparent)]
pub struct Epoch(#[serde(with = "eth2_serde_utils::quoted_u64")] u64);

View File

@@ -18,8 +18,7 @@ lazy_static! {
};
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(arbitrary::Arbitrary, Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SubnetId(#[serde(with = "eth2_serde_utils::quoted_u64")] u64);

View File

@@ -20,12 +20,21 @@ impl From<ArithError> for Error {
}
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Derivative,
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct SyncAggregate<T: EthSpec> {
pub sync_committee_bits: BitVector<T::SyncCommitteeSize>,
pub sync_committee_signature: AggregateSignature,

View File

@@ -6,9 +6,18 @@ use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Hash,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct SyncAggregatorSelectionData {
pub slot: Slot,

View File

@@ -25,9 +25,20 @@ impl From<ArithError> for Error {
}
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct SyncCommittee<T: EthSpec> {
pub pubkeys: FixedVector<PublicKeyBytes, T::SyncCommitteeSize>,
pub aggregate_pubkey: PublicKeyBytes,

View File

@@ -15,9 +15,20 @@ pub enum Error {
}
/// An aggregation of `SyncCommitteeMessage`s, used in creating a `SignedContributionAndProof`.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct SyncCommitteeContribution<T: EthSpec> {
pub slot: Slot,
pub beacon_block_root: Hash256,

View File

@@ -8,8 +8,18 @@ use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
/// The data upon which a `SyncCommitteeContribution` is based.
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct SyncCommitteeMessage {
pub slot: Slot,
pub beacon_block_root: Hash256,

View File

@@ -12,8 +12,7 @@ use ssz_types::typenum::Unsigned;
use std::cmp;
use std::convert::TryInto;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Debug, Clone)]
#[derive(arbitrary::Arbitrary, PartialEq, Debug, Clone)]
pub struct SyncSelectionProof(Signature);
impl SyncSelectionProof {

View File

@@ -19,8 +19,7 @@ lazy_static! {
};
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(arbitrary::Arbitrary, Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SyncSubnetId(#[serde(with = "eth2_serde_utils::quoted_u64")] u64);

View File

@@ -17,7 +17,7 @@ impl CachedTreeHash<TreeHashCache> for Validator {
/// Efficiently tree hash a `Validator`, assuming it was updated by a valid state transition.
///
/// Specifically, we assume that the `pubkey` and `withdrawal_credentials` fields are constant.
/// Specifically, we assume that the `pubkey` field is constant.
fn recalculate_tree_hash_root(
&self,
arena: &mut CacheArena,
@@ -29,8 +29,8 @@ impl CachedTreeHash<TreeHashCache> for Validator {
.iter_mut(arena)?
.enumerate()
.flat_map(|(i, leaf)| {
// Fields pubkey and withdrawal_credentials are constant
if (i == 0 || i == 1) && cache.initialized {
// Pubkey field (index 0) is constant.
if i == 0 && cache.initialized {
None
} else if process_field_by_index(self, i, leaf, !cache.initialized) {
Some(i)

View File

@@ -1,5 +1,6 @@
use crate::{
test_utils::TestRandom, BeaconState, ChainSpec, Epoch, EthSpec, Hash256, PublicKeyBytes,
test_utils::TestRandom, Address, BeaconState, ChainSpec, Epoch, EthSpec, Hash256,
PublicKeyBytes,
};
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -9,8 +10,18 @@ use tree_hash_derive::TreeHash;
/// Information about a `BeaconChain` validator.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
)]
pub struct Validator {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,
@@ -65,6 +76,49 @@ impl Validator {
// Has not yet been activated
&& self.activation_epoch == spec.far_future_epoch
}
/// Returns `true` if the validator has eth1 withdrawal credential.
pub fn has_eth1_withdrawal_credential(&self, spec: &ChainSpec) -> bool {
self.withdrawal_credentials
.as_bytes()
.first()
.map(|byte| *byte == spec.eth1_address_withdrawal_prefix_byte)
.unwrap_or(false)
}
/// Get the eth1 withdrawal address if this validator has one initialized.
pub fn get_eth1_withdrawal_address(&self, spec: &ChainSpec) -> Option<Address> {
self.has_eth1_withdrawal_credential(spec)
.then(|| {
self.withdrawal_credentials
.as_bytes()
.get(12..)
.map(Address::from_slice)
})
.flatten()
}
/// Changes withdrawal credentials to the provided eth1 execution address.
///
/// WARNING: this function does NO VALIDATION - it just does it!
pub fn change_withdrawal_credentials(&mut self, execution_address: &Address, spec: &ChainSpec) {
let mut bytes = [0u8; 32];
bytes[0] = spec.eth1_address_withdrawal_prefix_byte;
bytes[12..].copy_from_slice(execution_address.as_bytes());
self.withdrawal_credentials = Hash256::from(bytes);
}
/// Returns `true` if the validator is fully withdrawable at some epoch.
pub fn is_fully_withdrawable_at(&self, balance: u64, epoch: Epoch, spec: &ChainSpec) -> bool {
self.has_eth1_withdrawal_credential(spec) && self.withdrawable_epoch <= epoch && balance > 0
}
/// Returns `true` if the validator is partially withdrawable.
pub fn is_partially_withdrawable_validator(&self, balance: u64, spec: &ChainSpec) -> bool {
self.has_eth1_withdrawal_credential(spec)
&& self.effective_balance == spec.max_effective_balance
&& balance > spec.max_effective_balance
}
}
impl Default for Validator {

View File

@@ -11,9 +11,18 @@ use tree_hash_derive::TreeHash;
/// An exit voluntarily submitted a validator who wishes to withdraw.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct VoluntaryExit {
/// Earliest epoch when voluntary exit can be processed.

View File

@@ -0,0 +1,37 @@
use crate::test_utils::TestRandom;
use crate::*;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
pub struct Withdrawal {
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub index: u64,
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub validator_index: u64,
pub address: Address,
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub amount: u64,
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(Withdrawal);
}