Resolve merge conflicts

This commit is contained in:
Eitan Seri-Levi
2026-01-02 08:52:14 -06:00
918 changed files with 49304 additions and 37273 deletions

View File

@@ -1,29 +1,40 @@
[package]
name = "types"
version = "0.2.1"
authors = ["Paul Hauner <paul@paulhauner.com>", "Age Manning <Age@AgeManning.com>"]
authors = [
"Paul Hauner <paul@paulhauner.com>",
"Age Manning <Age@AgeManning.com>",
]
edition = { workspace = true }
[[bench]]
name = "benches"
harness = false
[features]
default = ["sqlite", "legacy-arith"]
# Allow saturating arithmetic on slots and epochs. Enabled by default, but deprecated.
legacy-arith = []
sqlite = ["dep:rusqlite"]
arbitrary = [
"dep:arbitrary",
"bls/arbitrary",
"ethereum_ssz/arbitrary",
"milhouse/arbitrary",
"ssz_types/arbitrary",
"swap_or_not_shuffle/arbitrary",
]
arbitrary-fuzz = ["arbitrary"]
portable = ["bls/supranational-portable"]
[dependencies]
alloy-primitives = { workspace = true }
alloy-rlp = { version = "0.3.4", features = ["derive"] }
# The arbitrary dependency is enabled by default since Capella to avoid complexity introduced by
# `AbstractExecPayload`
arbitrary = { workspace = true, features = ["derive"] }
bls = { workspace = true, features = ["arbitrary"] }
alloy-rlp = { workspace = true, features = ["derive"] }
arbitrary = { workspace = true, features = ["derive"], optional = true }
bls = { workspace = true }
compare_fields = { workspace = true }
compare_fields_derive = { workspace = true }
context_deserialize = { workspace = true }
context_deserialize_derive = { workspace = true }
derivative = { workspace = true }
educe = { workspace = true }
eth2_interop_keypairs = { path = "../../common/eth2_interop_keypairs" }
ethereum_hashing = { workspace = true }
ethereum_serde_utils = { workspace = true }
ethereum_ssz = { workspace = true, features = ["arbitrary"] }
ethereum_ssz = { workspace = true }
ethereum_ssz_derive = { workspace = true }
fixed_bytes = { workspace = true }
hex = { workspace = true }
@@ -36,7 +47,7 @@ metastruct = "0.1.0"
milhouse = { workspace = true }
parking_lot = { workspace = true }
rand = { workspace = true }
rand_xorshift = "0.3.0"
rand_xorshift = "0.4.0"
rayon = { workspace = true }
regex = { workspace = true }
rpds = { workspace = true }
@@ -46,14 +57,15 @@ serde = { workspace = true, features = ["rc"] }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
smallvec = { workspace = true }
ssz_types = { workspace = true, features = ["arbitrary"] }
ssz_types = { workspace = true }
superstruct = { workspace = true }
swap_or_not_shuffle = { workspace = true, features = ["arbitrary"] }
swap_or_not_shuffle = { workspace = true }
tempfile = { workspace = true }
test_random_derive = { path = "../../common/test_random_derive" }
tracing = { workspace = true }
tree_hash = { workspace = true }
tree_hash_derive = { workspace = true }
typenum = { workspace = true }
[dev-dependencies]
beacon_chain = { workspace = true }
@@ -62,12 +74,9 @@ paste = { workspace = true }
state_processing = { workspace = true }
tokio = { workspace = true }
[features]
default = ["sqlite", "legacy-arith"]
# Allow saturating arithmetic on slots and epochs. Enabled by default, but deprecated.
legacy-arith = []
sqlite = ["dep:rusqlite"]
# The `arbitrary-fuzz` feature is a no-op provided for backwards compatibility.
# For simplicity `Arbitrary` is now derived regardless of the feature's presence.
arbitrary-fuzz = []
portable = ["bls/supranational-portable"]
[lints.clippy]
module_inception = "allow"
[[bench]]
name = "benches"
harness = false

View File

@@ -1,11 +1,12 @@
use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
use criterion::{BatchSize, BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use fixed_bytes::FixedBytesExtended;
use milhouse::List;
use rayon::prelude::*;
use ssz::Encode;
use std::sync::Arc;
use types::{
test_utils::generate_deterministic_keypair, BeaconState, Epoch, Eth1Data, EthSpec,
FixedBytesExtended, Hash256, MainnetEthSpec, Validator,
BeaconState, Epoch, Eth1Data, EthSpec, Hash256, MainnetEthSpec, Validator,
test_utils::generate_deterministic_keypair,
};
fn get_state<E: EthSpec>(validator_count: usize) -> BeaconState<E> {

View File

@@ -41,8 +41,7 @@ MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16
# Withdrawals processing
# ---------------------------------------------------------------
# 2**3 ( = 8) pending withdrawals
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 6
# Pending deposits processing
# ---------------------------------------------------------------

View File

@@ -8,3 +8,7 @@ FIELD_ELEMENTS_PER_CELL: 64
FIELD_ELEMENTS_PER_EXT_BLOB: 8192
# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'))
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4
# FIELD_ELEMENTS_PER_EXT_BLOB // FIELD_ELEMENTS_PER_CELL (= 128)
CELLS_PER_EXT_BLOB: 128
# CELLS_PER_EXT_BLOB (= 128)
NUMBER_OF_COLUMNS: 128

View File

@@ -0,0 +1 @@
# Gnosis preset - Gloas

View File

@@ -8,3 +8,7 @@ FIELD_ELEMENTS_PER_CELL: 64
FIELD_ELEMENTS_PER_EXT_BLOB: 8192
# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'))
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4
# FIELD_ELEMENTS_PER_EXT_BLOB // FIELD_ELEMENTS_PER_CELL (= 128)
CELLS_PER_EXT_BLOB: 128
# CELLS_PER_EXT_BLOB (= 128)
NUMBER_OF_COLUMNS: 128

View File

@@ -0,0 +1 @@
# Mainnet preset - Gloas

View File

@@ -4,7 +4,7 @@
# ---------------------------------------------------------------
# `uint64(4096)`
FIELD_ELEMENTS_PER_BLOB: 4096
# [customized]
MAX_BLOB_COMMITMENTS_PER_BLOCK: 32
# [customized] `floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) + 1 + ceillog2(MAX_BLOB_COMMITMENTS_PER_BLOCK)` = 4 + 1 + 5 = 10
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 10
# `uint64(4096)`
MAX_BLOB_COMMITMENTS_PER_BLOCK: 4096
# `floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) + 1 + ceillog2(MAX_BLOB_COMMITMENTS_PER_BLOCK)` = 4 + 1 + 12 = 17
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 17

View File

@@ -32,10 +32,10 @@ MAX_ATTESTATIONS_ELECTRA: 8
# Execution
# ---------------------------------------------------------------
# [customized] 2**2 (= 4) deposit requests
MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: 4
# [customized] 2**1 (= 2) withdrawal requests
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2
# 2**13 (= 8,192) deposit requests
MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: 8192
# 2**4 (= 16) withdrawal requests
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16
# 2**1 (= 2) consolidation requests
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 2

View File

@@ -8,3 +8,7 @@ FIELD_ELEMENTS_PER_CELL: 64
FIELD_ELEMENTS_PER_EXT_BLOB: 8192
# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'))
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4
# FIELD_ELEMENTS_PER_EXT_BLOB // FIELD_ELEMENTS_PER_CELL (= 128)
CELLS_PER_EXT_BLOB: 128
# CELLS_PER_EXT_BLOB (= 128)
NUMBER_OF_COLUMNS: 128

View File

@@ -0,0 +1 @@
# Minimal preset - Gloas

View File

@@ -1,22 +1,24 @@
use super::{AttestationBase, AttestationElectra, AttestationRef};
use super::{
ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, PublicKey, SecretKey, SelectionProof,
Signature, SignedRoot,
};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::Attestation;
use bls::{PublicKey, SecretKey, Signature};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
attestation::{
Attestation, AttestationBase, AttestationElectra, AttestationRef, SelectionProof,
},
core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot},
fork::{Fork, ForkName},
test_utils::TestRandom,
};
#[superstruct(
variants(Base, Electra),
variant_attributes(
derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
@@ -29,23 +31,29 @@ use tree_hash_derive::TreeHash;
),
context_deserialize(ForkName),
serde(bound = "E: EthSpec"),
arbitrary(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
),
ref_attributes(
derive(Debug, PartialEq, TreeHash, Serialize,),
derive(Debug, PartialEq, TreeHash, Serialize),
serde(untagged, bound = "E: EthSpec"),
tree_hash(enum_behaviour = "transparent")
),
map_ref_into(AttestationRef)
)]
#[derive(
arbitrary::Arbitrary, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[arbitrary(bound = "E: EthSpec")]
pub struct AggregateAndProof<E: EthSpec> {
/// The index of the validator that created the attestation.
#[serde(with = "serde_utils::quoted_u64")]

View File

@@ -1,20 +1,26 @@
use crate::context_deserialize;
use crate::slot_data::SlotData;
use crate::{test_utils::TestRandom, Hash256, Slot};
use crate::{Checkpoint, ContextDeserialize, ForkName};
use derivative::Derivative;
use std::{
collections::HashSet,
hash::{Hash, Hasher},
};
use bls::{AggregateSignature, SecretKey, Signature};
use context_deserialize::{ContextDeserialize, context_deserialize};
use educe::Educe;
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::BitVector;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use ssz_types::{BitList, BitVector};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use super::{
AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey,
Signature, SignedRoot,
use crate::{
attestation::{
AttestationData, Checkpoint, IndexedAttestation, IndexedAttestationBase,
IndexedAttestationElectra,
},
core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot, SlotData},
fork::{Fork, ForkName},
test_utils::TestRandom,
};
#[derive(Debug, PartialEq, Clone)]
@@ -44,35 +50,33 @@ impl From<ssz_types::Error> for Error {
Decode,
Encode,
TestRandom,
Derivative,
arbitrary::Arbitrary,
Educe,
TreeHash,
),
context_deserialize(ForkName),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)
),
ref_attributes(derive(TreeHash), tree_hash(enum_behaviour = "transparent")),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
)]
#[derive(
Debug,
Clone,
Serialize,
TreeHash,
Encode,
Derivative,
Deserialize,
arbitrary::Arbitrary,
PartialEq,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, TreeHash, Encode, Educe, Deserialize)]
#[educe(PartialEq)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[arbitrary(bound = "E: EthSpec")]
pub struct Attestation<E: EthSpec> {
#[superstruct(only(Base), partial_getter(rename = "aggregation_bits_base"))]
pub aggregation_bits: BitList<E::MaxValidatorsPerCommittee>,
@@ -246,10 +250,17 @@ impl<E: EthSpec> Attestation<E> {
attester_index: u64,
) -> Result<SingleAttestation, Error> {
match self {
Self::Base(_) => Err(Error::IncorrectStateVariant),
Self::Base(attn) => attn.to_single_attestation_with_attester_index(attester_index),
Self::Electra(attn) => attn.to_single_attestation_with_attester_index(attester_index),
}
}
pub fn get_aggregation_bits(&self) -> Vec<u64> {
match self {
Self::Base(attn) => attn.get_aggregation_bits(),
Self::Electra(attn) => attn.get_aggregation_bits(),
}
}
}
impl<E: EthSpec> AttestationRef<'_, E> {
@@ -461,6 +472,26 @@ impl<E: EthSpec> AttestationBase<E> {
) -> Result<BitList<E::MaxValidatorsPerSlot>, ssz::BitfieldError> {
self.aggregation_bits.resize::<E::MaxValidatorsPerSlot>()
}
pub fn get_aggregation_bits(&self) -> Vec<u64> {
self.aggregation_bits
.iter()
.enumerate()
.filter_map(|(index, bit)| if bit { Some(index as u64) } else { None })
.collect()
}
pub fn to_single_attestation_with_attester_index(
&self,
attester_index: u64,
) -> Result<SingleAttestation, Error> {
Ok(SingleAttestation {
committee_index: self.data.index,
attester_index,
data: self.data.clone(),
signature: self.signature.clone(),
})
}
}
impl<E: EthSpec> SlotData for Attestation<E> {
@@ -483,7 +514,7 @@ pub enum AttestationOnDisk<E: EthSpec> {
}
impl<E: EthSpec> AttestationOnDisk<E> {
pub fn to_ref(&self) -> AttestationRefOnDisk<E> {
pub fn to_ref(&self) -> AttestationRefOnDisk<'_, E> {
match self {
AttestationOnDisk::Base(att) => AttestationRefOnDisk::Base(att),
AttestationOnDisk::Electra(att) => AttestationRefOnDisk::Electra(att),
@@ -573,19 +604,8 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for Vec<Attestation<E>>
}
*/
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
Decode,
Encode,
TestRandom,
Derivative,
arbitrary::Arbitrary,
TreeHash,
PartialEq,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, Serialize, Deserialize, Decode, Encode, TestRandom, TreeHash, PartialEq)]
#[context_deserialize(ForkName)]
pub struct SingleAttestation {
#[serde(with = "serde_utils::quoted_u64")]
@@ -596,6 +616,27 @@ pub struct SingleAttestation {
pub signature: AggregateSignature,
}
impl SingleAttestation {
pub fn to_indexed<E: EthSpec>(
&self,
fork_name: ForkName,
) -> Result<IndexedAttestation<E>, ssz_types::Error> {
if fork_name.electra_enabled() {
Ok(IndexedAttestation::Electra(IndexedAttestationElectra {
attesting_indices: vec![self.attester_index].try_into()?,
data: self.data.clone(),
signature: self.signature.clone(),
}))
} else {
Ok(IndexedAttestation::Base(IndexedAttestationBase {
attesting_indices: vec![self.attester_index].try_into()?,
data: self.data.clone(),
signature: self.signature.clone(),
}))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -615,12 +656,12 @@ mod tests {
let attestation_data = size_of::<AttestationData>();
let signature = size_of::<AggregateSignature>();
assert_eq!(aggregation_bits, 152);
assert_eq!(aggregation_bits, 144);
assert_eq!(attestation_data, 128);
assert_eq!(signature, 288 + 16);
let attestation_expected = aggregation_bits + attestation_data + signature;
assert_eq!(attestation_expected, 584);
assert_eq!(attestation_expected, 576);
assert_eq!(
size_of::<AttestationBase<MainnetEthSpec>>(),
attestation_expected
@@ -638,13 +679,13 @@ mod tests {
size_of::<BitList<<MainnetEthSpec as EthSpec>::MaxCommitteesPerSlot>>();
let signature = size_of::<AggregateSignature>();
assert_eq!(aggregation_bits, 152);
assert_eq!(committee_bits, 152);
assert_eq!(aggregation_bits, 144);
assert_eq!(committee_bits, 144);
assert_eq!(attestation_data, 128);
assert_eq!(signature, 288 + 16);
let attestation_expected = aggregation_bits + committee_bits + attestation_data + signature;
assert_eq!(attestation_expected, 736);
assert_eq!(attestation_expected, 720);
assert_eq!(
size_of::<AttestationElectra<MainnetEthSpec>>(),
attestation_expected

View File

@@ -1,16 +1,21 @@
use crate::slot_data::SlotData;
use crate::test_utils::TestRandom;
use crate::{Checkpoint, ForkName, Hash256, SignedRoot, Slot};
use context_deserialize_derive::context_deserialize;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
attestation::Checkpoint,
core::{Hash256, SignedRoot, Slot, SlotData},
fork::ForkName,
test_utils::TestRandom,
};
/// The data upon which an attestation is based.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,

View File

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

View File

@@ -1,4 +1,4 @@
use crate::*;
use crate::{attestation::CommitteeIndex, core::Slot};
#[derive(Default, Clone, Debug, PartialEq)]
pub struct BeaconCommittee<'a> {
@@ -17,7 +17,8 @@ impl BeaconCommittee<'_> {
}
}
#[derive(arbitrary::Arbitrary, Default, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Default, Clone, Debug, PartialEq)]
pub struct OwnedBeaconCommittee {
pub slot: Slot,
pub index: CommitteeIndex,

View File

@@ -1,16 +1,20 @@
use crate::test_utils::TestRandom;
use crate::{Epoch, ForkName, Hash256};
use context_deserialize_derive::context_deserialize;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{Epoch, Hash256},
fork::ForkName,
test_utils::TestRandom,
};
/// Casper FFG checkpoint, used in attestations.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
Copy,

View File

@@ -1,17 +1,21 @@
use crate::context_deserialize;
use crate::{
test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec, ForkName, VariableList,
use std::{
hash::{Hash, Hasher},
slice::Iter,
};
use core::slice::Iter;
use derivative::Derivative;
use bls::AggregateSignature;
use context_deserialize::context_deserialize;
use educe::Educe;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use std::hash::{Hash, Hasher};
use ssz_types::VariableList;
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{attestation::AttestationData, core::EthSpec, fork::ForkName, test_utils::TestRandom};
/// Details an attestation that can be slashable.
///
/// To be included in an `AttesterSlashing`.
@@ -28,32 +32,30 @@ use tree_hash_derive::TreeHash;
Decode,
Encode,
TestRandom,
Derivative,
arbitrary::Arbitrary,
Educe,
TreeHash,
),
context_deserialize(ForkName),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
)
)]
#[derive(
Debug,
Clone,
Serialize,
TreeHash,
Encode,
Derivative,
Deserialize,
arbitrary::Arbitrary,
PartialEq,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, TreeHash, Encode, Educe, Deserialize)]
#[educe(PartialEq)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[arbitrary(bound = "E: EthSpec")]
pub struct IndexedAttestation<E: EthSpec> {
/// Lists validator registry indices, not committee indices.
#[superstruct(only(Base), partial_getter(rename = "attesting_indices_base"))]
@@ -210,9 +212,10 @@ impl<E: EthSpec> Hash for IndexedAttestation<E> {
#[cfg(test)]
mod tests {
use super::*;
use crate::slot_epoch::Epoch;
use crate::test_utils::{SeedableRng, XorShiftRng};
use crate::MainnetEthSpec;
use crate::{
core::{Epoch, MainnetEthSpec},
test_utils::{SeedableRng, XorShiftRng},
};
#[test]
pub fn test_is_double_vote_true() {

View File

@@ -0,0 +1,36 @@
use crate::test_utils::TestRandom;
use crate::{EthSpec, ForkName, PayloadAttestationData};
use bls::AggregateSignature;
use context_deserialize::context_deserialize;
use core::slice::Iter;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::VariableList;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(TestRandom, TreeHash, Debug, Clone, PartialEq, Encode, Decode, Serialize, Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[cfg_attr(feature = "arbitrary", arbitrary(bound = "E: EthSpec"))]
#[context_deserialize(ForkName)]
pub struct IndexedPayloadAttestation<E: EthSpec> {
#[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")]
pub attesting_indices: VariableList<u64, E::PTCSize>,
pub data: PayloadAttestationData,
pub signature: AggregateSignature,
}
impl<E: EthSpec> IndexedPayloadAttestation<E> {
pub fn attesting_indices_iter(&self) -> Iter<'_, u64> {
self.attesting_indices.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
ssz_and_tree_hash_tests!(IndexedPayloadAttestation<MainnetEthSpec>);
}

View File

@@ -0,0 +1,47 @@
mod aggregate_and_proof;
mod attestation;
mod attestation_data;
mod attestation_duty;
mod beacon_committee;
mod checkpoint;
mod indexed_attestation;
mod indexed_payload_attestation;
mod participation_flags;
mod payload_attestation;
mod payload_attestation_data;
mod payload_attestation_message;
mod pending_attestation;
mod selection_proof;
mod shuffling_id;
mod signed_aggregate_and_proof;
mod subnet_id;
pub use aggregate_and_proof::{
AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef,
};
pub use attestation::{
Attestation, AttestationBase, AttestationElectra, AttestationOnDisk, AttestationRef,
AttestationRefMut, AttestationRefOnDisk, Error as AttestationError, SingleAttestation,
};
pub use attestation_data::AttestationData;
pub use attestation_duty::AttestationDuty;
pub use beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
pub use checkpoint::Checkpoint;
pub use indexed_attestation::{
IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef,
};
pub use indexed_payload_attestation::IndexedPayloadAttestation;
pub use participation_flags::ParticipationFlags;
pub use payload_attestation::PayloadAttestation;
pub use payload_attestation_data::PayloadAttestationData;
pub use payload_attestation_message::PayloadAttestationMessage;
pub use pending_attestation::PendingAttestation;
pub use selection_proof::SelectionProof;
pub use shuffling_id::AttestationShufflingId;
pub use signed_aggregate_and_proof::{
SignedAggregateAndProof, SignedAggregateAndProofBase, SignedAggregateAndProofElectra,
SignedAggregateAndProofRefMut,
};
pub use subnet_id::SubnetId;
pub type CommitteeIndex = u64;

View File

@@ -1,13 +1,17 @@
use crate::{consts::altair::NUM_FLAG_INDICES, test_utils::TestRandom, Hash256};
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use ssz::{Decode, DecodeError, Encode};
use test_random_derive::TestRandom;
use tree_hash::{PackedEncoding, TreeHash, TreeHashType};
use crate::{
core::{Hash256, consts::altair::NUM_FLAG_INDICES},
test_utils::TestRandom,
};
#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize, TestRandom)]
#[serde(transparent)]
#[derive(arbitrary::Arbitrary)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct ParticipationFlags {
#[serde(with = "serde_utils::quoted_u8")]
bits: u8,

View File

@@ -0,0 +1,31 @@
use crate::attestation::payload_attestation_data::PayloadAttestationData;
use crate::test_utils::TestRandom;
use crate::{EthSpec, ForkName};
use bls::AggregateSignature;
use context_deserialize::context_deserialize;
use educe::Educe;
use serde::{Deserialize, Serialize};
use ssz::BitList;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(TestRandom, TreeHash, Debug, Clone, Encode, Decode, Serialize, Deserialize, Educe)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[cfg_attr(feature = "arbitrary", arbitrary(bound = "E: EthSpec"))]
#[educe(PartialEq, Hash)]
#[context_deserialize(ForkName)]
pub struct PayloadAttestation<E: EthSpec> {
pub aggregation_bits: BitList<E::PTCSize>,
pub data: PayloadAttestationData,
pub signature: AggregateSignature,
}
#[cfg(test)]
mod payload_attestation_tests {
use super::*;
use crate::MinimalEthSpec;
ssz_and_tree_hash_tests!(PayloadAttestation<MinimalEthSpec>);
}

View File

@@ -0,0 +1,28 @@
use crate::test_utils::TestRandom;
use crate::{ForkName, Hash256, SignedRoot, Slot};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(
TestRandom, TreeHash, Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize, Hash,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[context_deserialize(ForkName)]
pub struct PayloadAttestationData {
pub beacon_block_root: Hash256,
pub slot: Slot,
pub payload_present: bool,
pub blob_data_available: bool,
}
impl SignedRoot for PayloadAttestationData {}
#[cfg(test)]
mod payload_attestation_data_tests {
use super::*;
ssz_and_tree_hash_tests!(PayloadAttestationData);
}

View File

@@ -0,0 +1,26 @@
use crate::ForkName;
use crate::attestation::payload_attestation_data::PayloadAttestationData;
use crate::test_utils::TestRandom;
use bls::Signature;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(TestRandom, TreeHash, Debug, Clone, PartialEq, Encode, Decode, Serialize, Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[context_deserialize(ForkName)]
pub struct PayloadAttestationMessage {
#[serde(with = "serde_utils::quoted_u64")]
pub validator_index: u64,
pub data: PayloadAttestationData,
pub signature: Signature,
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(PayloadAttestationMessage);
}

View File

@@ -1,27 +1,21 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{AttestationData, BitList, EthSpec, ForkName};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::BitList;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{attestation::AttestationData, core::EthSpec, fork::ForkName, test_utils::TestRandom};
/// 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,
arbitrary::Arbitrary,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[arbitrary(bound = "E: EthSpec")]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[context_deserialize(ForkName)]
pub struct PendingAttestation<E: EthSpec> {
pub aggregation_bits: BitList<E::MaxValidatorsPerCommittee>,

View File

@@ -1,12 +1,19 @@
use crate::{
ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, Signature, SignedRoot, Slot,
};
use ethereum_hashing::hash;
use safe_arith::{ArithError, SafeArith};
use ssz::Encode;
use std::cmp;
#[derive(arbitrary::Arbitrary, PartialEq, Debug, Clone)]
use bls::{PublicKey, SecretKey, Signature};
use ethereum_hashing::hash;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use ssz::Encode;
use crate::{
core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot},
fork::Fork,
};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SelectionProof(Signature);
impl SelectionProof {

View File

@@ -1,7 +1,12 @@
use crate::*;
use std::hash::Hash;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use std::hash::Hash;
use crate::{
core::{Epoch, EthSpec, Hash256, RelativeEpoch},
state::{BeaconState, BeaconStateError},
};
/// Can be used to key (ID) the shuffling in some chain, in some epoch.
///

View File

@@ -1,18 +1,21 @@
use super::{
AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef,
};
use super::{
Attestation, AttestationRef, ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, SecretKey,
SelectionProof, Signature, SignedRoot,
};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use bls::{SecretKey, Signature};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
attestation::{
AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef,
Attestation, AttestationRef, SelectionProof,
},
core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot},
fork::{Fork, ForkName},
test_utils::TestRandom,
};
/// A Validators signed aggregate proof to publish on the `beacon_aggregate_and_proof`
/// gossipsub topic.
///
@@ -21,7 +24,6 @@ use tree_hash_derive::TreeHash;
variants(Base, Electra),
variant_attributes(
derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
@@ -34,19 +36,25 @@ use tree_hash_derive::TreeHash;
),
context_deserialize(ForkName),
serde(bound = "E: EthSpec"),
arbitrary(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
),
map_into(Attestation),
map_ref_into(AggregateAndProofRef)
)]
#[derive(
arbitrary::Arbitrary, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[arbitrary(bound = "E: EthSpec")]
pub struct SignedAggregateAndProof<E: EthSpec> {
/// The `AggregateAndProof` that was signed.
#[superstruct(flatten)]

View File

@@ -1,17 +1,23 @@
//! Identifies each shard by an integer identifier.
use crate::SingleAttestation;
use crate::{AttestationRef, ChainSpec, CommitteeIndex, EthSpec, Slot};
use alloy_primitives::{bytes::Buf, U256};
use std::{
ops::{Deref, DerefMut},
sync::LazyLock,
};
use alloy_primitives::{U256, bytes::Buf};
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use crate::{
attestation::{AttestationRef, CommitteeIndex, SingleAttestation},
core::{ChainSpec, EthSpec, Slot},
};
const MAX_SUBNET_ID: usize = 64;
/// The number of bits in a Discovery `NodeId`. This is used for binary operations on the node-id
/// data.
const NODE_ID_BITS: u64 = 256;
const NODE_ID_BITS: u32 = 256;
static SUBNET_ID_TO_STRING: LazyLock<Vec<String>> = LazyLock::new(|| {
let mut v = Vec::with_capacity(MAX_SUBNET_ID);
@@ -22,7 +28,8 @@ static SUBNET_ID_TO_STRING: LazyLock<Vec<String>> = LazyLock::new(|| {
v
});
#[derive(arbitrary::Arbitrary, Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SubnetId(#[serde(with = "serde_utils::quoted_u64")] u64);
@@ -101,7 +108,7 @@ impl SubnetId {
spec: &ChainSpec,
) -> impl Iterator<Item = SubnetId> {
// The bits of the node-id we are using to define the subnets.
let prefix_bits = spec.attestation_subnet_prefix_bits as u64;
let prefix_bits = spec.attestation_subnet_prefix_bits as u32;
let node_id = U256::from_be_slice(&raw_node_id);
// calculate the prefixes used to compute the subnet and shuffling

View File

@@ -1,239 +0,0 @@
use crate::{ContextDeserialize, ForkName};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::value::Value;
pub trait ForkVersionDecode: Sized {
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError>;
}
/// The metadata of type M should be set to `EmptyMetadata` if you don't care about adding fields other than
/// version. If you *do* care about adding other fields you can mix in any type that implements
/// `Deserialize`.
#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct ForkVersionedResponse<T, M = EmptyMetadata> {
pub version: ForkName,
#[serde(flatten)]
pub metadata: M,
pub data: T,
}
// Used for responses to V1 endpoints that don't have a version field.
/// The metadata of type M should be set to `EmptyMetadata` if you don't care about adding fields other than
/// version. If you *do* care about adding other fields you can mix in any type that implements
/// `Deserialize`.
#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct UnversionedResponse<T, M = EmptyMetadata> {
pub metadata: M,
pub data: T,
}
#[derive(Debug, PartialEq, Clone, Serialize)]
#[serde(untagged)]
pub enum BeaconResponse<T, M = EmptyMetadata> {
ForkVersioned(ForkVersionedResponse<T, M>),
Unversioned(UnversionedResponse<T, M>),
}
impl<T, M> BeaconResponse<T, M> {
pub fn version(&self) -> Option<ForkName> {
match self {
BeaconResponse::ForkVersioned(response) => Some(response.version),
BeaconResponse::Unversioned(_) => None,
}
}
pub fn data(&self) -> &T {
match self {
BeaconResponse::ForkVersioned(response) => &response.data,
BeaconResponse::Unversioned(response) => &response.data,
}
}
pub fn metadata(&self) -> &M {
match self {
BeaconResponse::ForkVersioned(response) => &response.metadata,
BeaconResponse::Unversioned(response) => &response.metadata,
}
}
}
/// Metadata type similar to unit (i.e. `()`) but deserializes from a map (`serde_json::Value`).
///
/// Unfortunately the braces are semantically significant, i.e. `struct EmptyMetadata;` does not
/// work.
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct EmptyMetadata {}
/// Fork versioned response with extra information about finalization & optimistic execution.
pub type ExecutionOptimisticFinalizedBeaconResponse<T> =
BeaconResponse<T, ExecutionOptimisticFinalizedMetadata>;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct ExecutionOptimisticFinalizedMetadata {
pub execution_optimistic: Option<bool>,
pub finalized: Option<bool>,
}
impl<'de, T, M> Deserialize<'de> for ForkVersionedResponse<T, M>
where
T: ContextDeserialize<'de, ForkName>,
M: DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
version: ForkName,
#[serde(flatten)]
metadata: Value,
data: Value,
}
let helper = Helper::deserialize(deserializer)?;
// Deserialize metadata
let metadata = serde_json::from_value(helper.metadata).map_err(serde::de::Error::custom)?;
// Deserialize `data` using ContextDeserialize
let data = T::context_deserialize(helper.data, helper.version)
.map_err(serde::de::Error::custom)?;
Ok(ForkVersionedResponse {
version: helper.version,
metadata,
data,
})
}
}
impl<'de, T, M> Deserialize<'de> for UnversionedResponse<T, M>
where
T: DeserializeOwned,
M: DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper<T, M> {
#[serde(flatten)]
metadata: M,
data: T,
}
let helper = Helper::deserialize(deserializer)?;
Ok(UnversionedResponse {
metadata: helper.metadata,
data: helper.data,
})
}
}
impl<T, M> BeaconResponse<T, M> {
pub fn map_data<U>(self, f: impl FnOnce(T) -> U) -> BeaconResponse<U, M> {
match self {
BeaconResponse::ForkVersioned(response) => {
BeaconResponse::ForkVersioned(response.map_data(f))
}
BeaconResponse::Unversioned(response) => {
BeaconResponse::Unversioned(response.map_data(f))
}
}
}
pub fn into_data(self) -> T {
match self {
BeaconResponse::ForkVersioned(response) => response.data,
BeaconResponse::Unversioned(response) => response.data,
}
}
}
impl<T, M> UnversionedResponse<T, M> {
pub fn map_data<U>(self, f: impl FnOnce(T) -> U) -> UnversionedResponse<U, M> {
let UnversionedResponse { metadata, data } = self;
UnversionedResponse {
metadata,
data: f(data),
}
}
}
impl<T, M> ForkVersionedResponse<T, M> {
/// Apply a function to the inner `data`, potentially changing its type.
pub fn map_data<U>(self, f: impl FnOnce(T) -> U) -> ForkVersionedResponse<U, M> {
let ForkVersionedResponse {
version,
metadata,
data,
} = self;
ForkVersionedResponse {
version,
metadata,
data: f(data),
}
}
}
impl<T, M> From<ForkVersionedResponse<T, M>> for BeaconResponse<T, M> {
fn from(response: ForkVersionedResponse<T, M>) -> Self {
BeaconResponse::ForkVersioned(response)
}
}
impl<T, M> From<UnversionedResponse<T, M>> for BeaconResponse<T, M> {
fn from(response: UnversionedResponse<T, M>) -> Self {
BeaconResponse::Unversioned(response)
}
}
#[cfg(test)]
mod fork_version_response_tests {
use crate::{
ExecutionPayload, ExecutionPayloadBellatrix, 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: ForkName::Bellatrix,
metadata: Default::default(),
data: ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::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: ForkName::Capella,
metadata: Default::default(),
data: ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::default()),
}))
.unwrap();
let result: Result<ForkVersionedResponse<ExecutionPayload<E>>, _> =
serde_json::from_str(&response_json);
assert!(result.is_err());
}
}

View File

@@ -1,22 +1,45 @@
use crate::attestation::AttestationBase;
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
use std::{fmt, marker::PhantomData};
use bls::{AggregateSignature, PublicKeyBytes, SecretKey, Signature, SignatureBytes};
use context_deserialize::ContextDeserialize;
use educe::Educe;
use fixed_bytes::FixedBytesExtended;
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{Decode, DecodeError};
use ssz_derive::{Decode, Encode};
use std::fmt;
use std::marker::PhantomData;
use ssz_types::{BitList, BitVector, FixedVector, VariableList};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use typenum::Unsigned;
use self::indexed_attestation::IndexedAttestationBase;
use crate::{
SignedExecutionPayloadBid,
attestation::{AttestationBase, AttestationData, IndexedAttestationBase},
block::{
BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyBellatrix,
BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyEip7805,
BeaconBlockBodyElectra, BeaconBlockBodyFulu, BeaconBlockBodyGloas, BeaconBlockBodyRef,
BeaconBlockBodyRefMut, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader,
},
core::{ChainSpec, Domain, Epoch, EthSpec, Graffiti, Hash256, SignedRoot, Slot},
deposit::{Deposit, DepositData},
execution::{
AbstractExecPayload, BlindedPayload, Eth1Data, ExecutionPayload, ExecutionRequests,
FullPayload,
},
exit::{SignedVoluntaryExit, VoluntaryExit},
fork::{Fork, ForkName, InconsistentFork, map_fork_name},
slashing::{AttesterSlashingBase, ProposerSlashing},
state::BeaconStateError,
sync_committee::SyncAggregate,
test_utils::TestRandom,
};
/// A block of the `BeaconChain`.
#[superstruct(
variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Eip7805, Fulu),
variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Eip7805, Gloas),
variant_attributes(
derive(
Debug,
@@ -27,15 +50,18 @@ use self::indexed_attestation::IndexedAttestationBase;
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
Educe,
),
derivative(PartialEq, Hash(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")),
educe(PartialEq, Hash(bound(E: EthSpec, Payload: AbstractExecPayload<E>))),
serde(
bound = "E: EthSpec, Payload: AbstractExecPayload<E>",
deny_unknown_fields
),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")
)
),
ref_attributes(
derive(Debug, PartialEq, TreeHash),
@@ -44,13 +70,15 @@ use self::indexed_attestation::IndexedAttestationBase;
map_ref_into(BeaconBlockBodyRef, BeaconBlock),
map_ref_mut_into(BeaconBlockBodyRefMut)
)]
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative, arbitrary::Arbitrary,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")
)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(untagged)]
#[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 BeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E> = FullPayload<E>> {
@@ -75,10 +103,12 @@ pub struct BeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E> = FullPayload
pub body: BeaconBlockBodyDeneb<E, Payload>,
#[superstruct(only(Electra), partial_getter(rename = "body_electra"))]
pub body: BeaconBlockBodyElectra<E, Payload>,
#[superstruct(only(Eip7805), partial_getter(rename = "body_eip7805"))]
pub body: BeaconBlockBodyEip7805<E, Payload>,
#[superstruct(only(Fulu), partial_getter(rename = "body_fulu"))]
pub body: BeaconBlockBodyFulu<E, Payload>,
#[superstruct(only(Eip7805), partial_getter(rename = "body_eip7805"))]
pub body: BeaconBlockBodyEip7805<E, Payload>,
#[superstruct(only(Gloas), partial_getter(rename = "body_gloas"))]
pub body: BeaconBlockBodyGloas<E, Payload>,
}
pub type BlindedBeaconBlock<E> = BeaconBlock<E, BlindedPayload<E>>;
@@ -131,9 +161,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlock<E, 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> {
BeaconBlockFulu::from_ssz_bytes(bytes)
.map(BeaconBlock::Fulu)
BeaconBlockGloas::from_ssz_bytes(bytes)
.map(BeaconBlock::Gloas)
.or_else(|_| BeaconBlockEip7805::from_ssz_bytes(bytes).map(BeaconBlock::Eip7805))
.or_else(|_| BeaconBlockFulu::from_ssz_bytes(bytes).map(BeaconBlock::Fulu))
.or_else(|_| BeaconBlockElectra::from_ssz_bytes(bytes).map(BeaconBlock::Electra))
.or_else(|_| BeaconBlockDeneb::from_ssz_bytes(bytes).map(BeaconBlock::Deneb))
.or_else(|_| BeaconBlockCapella::from_ssz_bytes(bytes).map(BeaconBlock::Capella))
@@ -232,8 +263,9 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockRef<'a, E, Payl
BeaconBlockRef::Capella { .. } => ForkName::Capella,
BeaconBlockRef::Deneb { .. } => ForkName::Deneb,
BeaconBlockRef::Electra { .. } => ForkName::Electra,
BeaconBlockRef::Eip7805 { .. } => ForkName::Eip7805,
BeaconBlockRef::Fulu { .. } => ForkName::Fulu,
BeaconBlockRef::Eip7805 { .. } => ForkName::Eip7805,
BeaconBlockRef::Gloas { .. } => ForkName::Gloas,
}
}
@@ -278,7 +310,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockRef<'a, E, Payl
/// 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::Ref<'a>, Error> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'a>, BeaconStateError> {
self.body().execution_payload()
}
}
@@ -418,7 +450,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockAlta
/// Returns an empty Altair block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockAltair {
slot: spec.genesis_slot,
slot: spec
.altair_fork_epoch
.expect("altair enabled")
.start_slot(E::slots_per_epoch()),
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
@@ -451,7 +486,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockAltair<E, Payload>
sync_committee_bits: BitVector::default(),
};
BeaconBlockAltair {
slot: spec.genesis_slot,
slot: spec
.altair_fork_epoch
.expect("altair enabled")
.start_slot(E::slots_per_epoch()),
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
@@ -479,7 +517,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockBell
/// Returns an empty Bellatrix block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockBellatrix {
slot: spec.genesis_slot,
slot: spec
.bellatrix_fork_epoch
.expect("bellatrix enabled")
.start_slot(E::slots_per_epoch()),
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
@@ -507,7 +548,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockCape
/// Returns an empty Capella block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockCapella {
slot: spec.genesis_slot,
slot: spec
.capella_fork_epoch
.expect("capella enabled")
.start_slot(E::slots_per_epoch()),
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
@@ -536,7 +580,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockDene
/// Returns an empty Deneb block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockDeneb {
slot: spec.genesis_slot,
slot: spec
.deneb_fork_epoch
.expect("deneb enabled")
.start_slot(E::slots_per_epoch()),
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
@@ -566,7 +613,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockElec
/// Returns an empty Electra block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockElectra {
slot: spec.genesis_slot,
slot: spec
.electra_fork_epoch
.expect("electra enabled")
.start_slot(E::slots_per_epoch()),
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
@@ -628,7 +678,10 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockFulu
/// Returns an empty Fulu block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockFulu {
slot: spec.genesis_slot,
slot: spec
.fulu_fork_epoch
.expect("fulu enabled")
.start_slot(E::slots_per_epoch()),
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
@@ -655,6 +708,63 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockFulu
}
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockGloas<E, Payload> {
/// Returns an empty Gloas block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockGloas {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyGloas {
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(),
bls_to_execution_changes: VariableList::empty(),
signed_execution_payload_bid: SignedExecutionPayloadBid::empty(),
payload_attestations: VariableList::empty(),
_phantom: PhantomData,
},
}
}
}
// TODO(EIP-7732) Mark's branch had the following implementation but not sure if it's needed so will just add header below for reference
// impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockEIP7732<E, Payload> {
// TODO(EIP-7732) Look into whether we can remove this in the future since no blinded blocks post-gloas
impl<E: EthSpec> From<BeaconBlockGloas<E, BlindedPayload<E>>>
for BeaconBlockGloas<E, FullPayload<E>>
{
fn from(block: BeaconBlockGloas<E, BlindedPayload<E>>) -> Self {
let BeaconBlockGloas {
slot,
proposer_index,
parent_root,
state_root,
body,
} = block;
BeaconBlockGloas {
slot,
proposer_index,
parent_root,
state_root,
body: body.into(),
}
}
}
// We can convert pre-Bellatrix blocks without payloads into blocks "with" payloads.
impl<E: EthSpec> From<BeaconBlockBase<E, BlindedPayload<E>>>
for BeaconBlockBase<E, FullPayload<E>>
@@ -738,6 +848,7 @@ impl_from!(BeaconBlockDeneb, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body:
impl_from!(BeaconBlockElectra, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyElectra<_, _>| body.into());
impl_from!(BeaconBlockEip7805, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyEip7805<_, _>| body.into());
impl_from!(BeaconBlockFulu, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyFulu<_, _>| body.into());
impl_from!(BeaconBlockGloas, <E, FullPayload<E>>, <E, BlindedPayload<E>>, |body: BeaconBlockBodyGloas<_, _>| body.into());
// We can clone blocks with payloads to blocks without payloads, without cloning the payload.
macro_rules! impl_clone_as_blinded {
@@ -771,8 +882,9 @@ impl_clone_as_blinded!(BeaconBlockBellatrix, <E, FullPayload<E>>, <E, BlindedPay
impl_clone_as_blinded!(BeaconBlockCapella, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockDeneb, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockElectra, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockEip7805, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockFulu, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockEip7805, <E, FullPayload<E>>, <E, BlindedPayload<E>>);
impl_clone_as_blinded!(BeaconBlockGloas, <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.
@@ -817,6 +929,7 @@ impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> ContextDeserialize<'de, F
}
}
#[derive(Clone, Copy)]
pub enum BlockImportSource {
Gossip,
Lookup,
@@ -838,7 +951,10 @@ impl fmt::Display for BlockImportSource {
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{test_ssz_tree_hash_pair_with, SeedableRng, XorShiftRng};
use crate::{
core::MainnetEthSpec,
test_utils::{SeedableRng, XorShiftRng, test_ssz_tree_hash_pair_with},
};
use ssz::Encode;
type BeaconBlock = super::BeaconBlock<MainnetEthSpec>;
@@ -981,6 +1097,26 @@ mod tests {
});
}
#[test]
fn roundtrip_gloas_block() {
let rng = &mut XorShiftRng::from_seed([42; 16]);
let spec = &ForkName::Gloas.make_genesis_spec(MainnetEthSpec::default_spec());
let inner_block = BeaconBlockGloas {
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: BeaconBlockBodyGloas::random_for_test(rng),
};
let block = BeaconBlock::Gloas(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;
@@ -1004,6 +1140,8 @@ mod tests {
let eip7805_slot = eip7805_epoch.start_slot(E::slots_per_epoch());
let fulu_epoch = eip7805_epoch + 1;
let fulu_slot = fulu_epoch.start_slot(E::slots_per_epoch());
let gloas_epoch = fulu_epoch + 1;
let gloas_slot = gloas_epoch.start_slot(E::slots_per_epoch());
spec.altair_fork_epoch = Some(altair_epoch);
spec.capella_fork_epoch = Some(capella_epoch);
@@ -1011,6 +1149,7 @@ mod tests {
spec.electra_fork_epoch = Some(electra_epoch);
spec.eip7805_fork_epoch = Some(eip7805_epoch);
spec.fulu_fork_epoch = Some(fulu_epoch);
spec.gloas_fork_epoch = Some(gloas_epoch);
// BeaconBlockBase
{
@@ -1150,22 +1289,37 @@ mod tests {
slot: fulu_slot,
..<_>::random_for_test(rng)
});
// It's invalid to have a Fulu block with a epoch lower than the fork epoch.
let _bad_block = {
let mut bad = good_block.clone();
*bad.slot_mut() = electra_slot;
bad
};
assert_eq!(
BeaconBlock::from_ssz_bytes(&good_block.as_ssz_bytes(), &spec)
.expect("good fulu block can be decoded"),
good_block
);
// TODO(fulu): Uncomment once Fulu has features since without features
// and with an Electra slot it decodes successfully to Electra.
}
// BeaconBlockGloas
{
let good_block = BeaconBlock::Gloas(BeaconBlockGloas {
slot: gloas_slot,
..<_>::random_for_test(rng)
});
// It's invalid to have a Fulu block with a epoch lower than the fork epoch.
let _bad_block = {
let mut bad = good_block.clone();
*bad.slot_mut() = fulu_slot;
bad
};
assert_eq!(
BeaconBlock::from_ssz_bytes(&good_block.as_ssz_bytes(), &spec)
.expect("good gloas block can be decoded"),
good_block
);
// TODO(gloas): Uncomment once Gloas has features since without features
// and with a Fulu slot it decodes successfully to Fulu.
//BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec)
// .expect_err("bad fulu block cannot be decoded");
// .expect_err("bad gloas block cannot be decoded");
}
}
}

View File

@@ -1,18 +1,44 @@
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
use std::marker::PhantomData;
use bls::Signature;
use context_deserialize::{ContextDeserialize, context_deserialize};
use educe::Educe;
use merkle_proof::{MerkleTree, MerkleTreeError};
use metastruct::metastruct;
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::{Decode, Encode};
use std::marker::PhantomData;
use ssz_types::{FixedVector, VariableList};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash::{TreeHash, BYTES_PER_CHUNK};
use tree_hash::{BYTES_PER_CHUNK, TreeHash};
use tree_hash_derive::TreeHash;
pub type KzgCommitments<E> =
VariableList<KzgCommitment, <E as EthSpec>::MaxBlobCommitmentsPerBlock>;
use crate::payload_attestation::PayloadAttestation;
use crate::{
SignedExecutionPayloadBid,
attestation::{AttestationBase, AttestationElectra, AttestationRef, AttestationRefMut},
core::{EthSpec, Graffiti, Hash256},
deposit::Deposit,
execution::{
AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella,
BlindedPayloadDeneb, BlindedPayloadEip7805, BlindedPayloadElectra, BlindedPayloadFulu,
Eth1Data, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadEip7805, ExecutionPayloadElectra,
ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionRequests, FullPayload,
FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb, FullPayloadEip7805,
FullPayloadElectra, FullPayloadFulu, SignedBlsToExecutionChange,
},
exit::SignedVoluntaryExit,
fork::{ForkName, map_fork_name},
kzg_ext::KzgCommitments,
light_client::consts::{EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_PROOF_LEN},
slashing::{
AttesterSlashingBase, AttesterSlashingElectra, AttesterSlashingRef, ProposerSlashing,
},
state::BeaconStateError,
sync_committee::SyncAggregate,
test_utils::TestRandom,
};
/// The number of leaves (including padding) on the `BeaconBlockBody` Merkle tree.
///
@@ -28,7 +54,7 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
///
/// This *superstruct* abstracts over the hard-fork.
#[superstruct(
variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Eip7805, Fulu),
variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Eip7805, Gloas),
variant_attributes(
derive(
Debug,
@@ -39,15 +65,18 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
Educe,
),
derivative(PartialEq, Hash(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")),
educe(PartialEq, Hash(bound(E: EthSpec, Payload: AbstractExecPayload<E>))),
serde(
bound = "E: EthSpec, Payload: AbstractExecPayload<E>",
deny_unknown_fields
),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
),
context_deserialize(ForkName),
),
specific_variant_attributes(
@@ -57,18 +86,29 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
Capella(metastruct(mappings(beacon_block_body_capella_fields(groups(fields))))),
Deneb(metastruct(mappings(beacon_block_body_deneb_fields(groups(fields))))),
Electra(metastruct(mappings(beacon_block_body_electra_fields(groups(fields))))),
Fulu(metastruct(mappings(beacon_block_body_fulu_fields(groups(fields))))),
Eip7805(metastruct(mappings(beacon_block_body_eip7805_fields(groups(fields))))),
Fulu(metastruct(mappings(beacon_block_body_fulu_fields(groups(fields)))))
Gloas(metastruct(mappings(beacon_block_body_gloas_fields(groups(fields))))),
),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
cast_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
partial_getter_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
)
)]
#[derive(Debug, Clone, Serialize, Deserialize, Derivative, TreeHash, arbitrary::Arbitrary)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")
)]
#[derive(Debug, Clone, Serialize, Deserialize, Educe, TreeHash)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(untagged)]
#[serde(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")]
#[tree_hash(enum_behaviour = "transparent")]
#[arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")]
pub struct BeaconBlockBody<E: EthSpec, Payload: AbstractExecPayload<E> = FullPayload<E>> {
pub randao_reveal: Signature,
pub eth1_data: Eth1Data,
@@ -80,7 +120,7 @@ pub struct BeaconBlockBody<E: EthSpec, Payload: AbstractExecPayload<E> = FullPay
)]
pub attester_slashings: VariableList<AttesterSlashingBase<E>, E::MaxAttesterSlashings>,
#[superstruct(
only(Electra, Eip7805, Fulu),
only(Electra, Fulu, Eip7805, Gloas),
partial_getter(rename = "attester_slashings_electra")
)]
pub attester_slashings:
@@ -91,13 +131,13 @@ pub struct BeaconBlockBody<E: EthSpec, Payload: AbstractExecPayload<E> = FullPay
)]
pub attestations: VariableList<AttestationBase<E>, E::MaxAttestations>,
#[superstruct(
only(Electra, Eip7805, Fulu),
only(Electra, Fulu, Eip7805, Gloas),
partial_getter(rename = "attestations_electra")
)]
pub attestations: VariableList<AttestationElectra<E>, E::MaxAttestationsElectra>,
pub deposits: VariableList<Deposit, E::MaxDeposits>,
pub voluntary_exits: VariableList<SignedVoluntaryExit, E::MaxVoluntaryExits>,
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Eip7805, Fulu))]
#[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Eip7805, Gloas))]
pub sync_aggregate: SyncAggregate<E>,
// We flatten the execution payload so that serde can use the name of the inner type,
// either `execution_payload` for full payloads, or `execution_payload_header` for blinded
@@ -123,24 +163,28 @@ pub struct BeaconBlockBody<E: EthSpec, Payload: AbstractExecPayload<E> = FullPay
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
#[serde(flatten)]
pub execution_payload: Payload::Fulu,
#[superstruct(only(Capella, Deneb, Electra, Eip7805, Fulu))]
#[superstruct(only(Capella, Deneb, Electra, Fulu, Eip7805, Gloas))]
pub bls_to_execution_changes:
VariableList<SignedBlsToExecutionChange, E::MaxBlsToExecutionChanges>,
#[superstruct(only(Deneb, Electra, Eip7805, Fulu))]
pub blob_kzg_commitments: KzgCommitments<E>,
#[superstruct(only(Electra, Eip7805, Fulu))]
pub execution_requests: ExecutionRequests<E>,
#[superstruct(only(Base, Altair))]
#[superstruct(only(Gloas))]
pub signed_execution_payload_bid: SignedExecutionPayloadBid,
#[superstruct(only(Gloas))]
pub payload_attestations: VariableList<PayloadAttestation<E>, E::MaxPayloadAttestations>,
#[superstruct(only(Base, Altair, Gloas))]
#[metastruct(exclude_from(fields))]
#[ssz(skip_serializing, skip_deserializing)]
#[tree_hash(skip_hashing)]
#[serde(skip)]
#[arbitrary(default)]
#[cfg_attr(feature = "arbitrary", arbitrary(default))]
pub _phantom: PhantomData<Payload>,
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBody<E, Payload> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'_>, Error> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'_>, BeaconStateError> {
self.to_ref().execution_payload()
}
@@ -151,19 +195,20 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBody<E, Payload> {
}
impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E, Payload> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'a>, Error> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'a>, BeaconStateError> {
match self {
Self::Base(_) | Self::Altair(_) => Err(Error::IncorrectStateVariant),
Self::Base(_) | Self::Altair(_) => Err(BeaconStateError::IncorrectStateVariant),
Self::Bellatrix(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Capella(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Electra(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Eip7805(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Fulu(body) => Ok(Payload::Ref::from(&body.execution_payload)),
Self::Gloas(_) => Err(BeaconStateError::IncorrectStateVariant),
}
}
pub(crate) fn body_merkle_leaves(&self) -> Vec<Hash256> {
pub fn body_merkle_leaves(&self) -> Vec<Hash256> {
let mut leaves = vec![];
match self {
Self::Base(body) => {
@@ -198,6 +243,10 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
beacon_block_body_fulu_fields!(body, |_, field| leaves
.push(field.tree_hash_root()));
}
Self::Gloas(body) => {
beacon_block_body_gloas_fields!(body, |_, field| leaves
.push(field.tree_hash_root()));
}
}
leaves
}
@@ -209,7 +258,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
pub fn kzg_commitment_merkle_proof(
&self,
index: usize,
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, Error> {
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, BeaconStateError> {
let kzg_commitments_proof = self.kzg_commitments_merkle_proof()?;
let proof = self.complete_kzg_commitment_merkle_proof(index, &kzg_commitments_proof)?;
Ok(proof)
@@ -217,16 +266,19 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
/// Produces the proof of inclusion for a `KzgCommitment` in `self.blob_kzg_commitments`
/// at `index` using an existing proof for the `blob_kzg_commitments` field.
/// TODO(EIP7732) Investigate calling functions since this will no longer work for glas since no block_kzg_commitments in the body anymore
pub fn complete_kzg_commitment_merkle_proof(
&self,
index: usize,
kzg_commitments_proof: &[Hash256],
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, Error> {
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, BeaconStateError> {
match self {
Self::Base(_) | Self::Altair(_) | Self::Bellatrix(_) | Self::Capella(_) => {
Err(Error::IncorrectStateVariant)
}
Self::Deneb(_) | Self::Electra(_) | Self::Eip7805(_) | Self::Fulu(_) => {
Self::Base(_)
| Self::Altair(_)
| Self::Bellatrix(_)
| Self::Capella(_)
| Self::Gloas(_) => Err(BeaconStateError::IncorrectStateVariant),
Self::Deneb(_) | Self::Electra(_) | Self::Fulu(_) | Self::Eip7805(_) => {
// We compute the branches by generating 2 merkle trees:
// 1. Merkle tree for the `blob_kzg_commitments` List object
// 2. Merkle tree for the `BeaconBlockBody` container
@@ -246,7 +298,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
let tree = MerkleTree::create(&blob_leaves, depth as usize);
let (_, mut proof) = tree
.generate_proof(index, depth as usize)
.map_err(Error::MerkleTreeError)?;
.map_err(BeaconStateError::MerkleTreeError)?;
// Add the branch corresponding to the length mix-in.
let length = blob_leaves.len();
@@ -254,7 +306,9 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
let mut length_bytes = [0; BYTES_PER_CHUNK];
length_bytes
.get_mut(0..usize_len)
.ok_or(Error::MerkleTreeError(MerkleTreeError::PleaseNotifyTheDevs))?
.ok_or(BeaconStateError::MerkleTreeError(
MerkleTreeError::PleaseNotifyTheDevs,
))?
.copy_from_slice(&length.to_le_bytes());
let length_root = Hash256::from_slice(length_bytes.as_slice());
proof.push(length_root);
@@ -272,32 +326,41 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
/// Produces the proof of inclusion for `self.blob_kzg_commitments`.
pub fn kzg_commitments_merkle_proof(
&self,
) -> Result<FixedVector<Hash256, E::KzgCommitmentsInclusionProofDepth>, Error> {
) -> Result<FixedVector<Hash256, E::KzgCommitmentsInclusionProofDepth>, BeaconStateError> {
let body_leaves = self.body_merkle_leaves();
let beacon_block_body_depth = body_leaves.len().next_power_of_two().ilog2() as usize;
let tree = MerkleTree::create(&body_leaves, beacon_block_body_depth);
let (_, proof) = tree
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
.map_err(Error::MerkleTreeError)?;
.map_err(BeaconStateError::MerkleTreeError)?;
Ok(FixedVector::new(proof)?)
}
pub fn block_body_merkle_proof(&self, generalized_index: usize) -> Result<Vec<Hash256>, Error> {
pub fn block_body_merkle_proof(
&self,
generalized_index: usize,
) -> Result<Vec<Hash256>, BeaconStateError> {
let field_index = match generalized_index {
light_client_update::EXECUTION_PAYLOAD_INDEX => {
EXECUTION_PAYLOAD_INDEX => {
// Execution payload is a top-level field, subtract off the generalized indices
// for the internal nodes. Result should be 9, the field offset of the execution
// payload in the `BeaconBlockBody`:
// https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#beaconblockbody
generalized_index
.checked_sub(NUM_BEACON_BLOCK_BODY_HASH_TREE_ROOT_LEAVES)
.ok_or(Error::GeneralizedIndexNotSupported(generalized_index))?
.ok_or(BeaconStateError::GeneralizedIndexNotSupported(
generalized_index,
))?
}
_ => {
return Err(BeaconStateError::GeneralizedIndexNotSupported(
generalized_index,
));
}
_ => return Err(Error::GeneralizedIndexNotSupported(generalized_index)),
};
let leaves = self.body_merkle_leaves();
let depth = light_client_update::EXECUTION_PAYLOAD_PROOF_LEN;
let depth = EXECUTION_PAYLOAD_PROOF_LEN;
let tree = merkle_proof::MerkleTree::create(&leaves, depth);
let (_, proof) = tree.generate_proof(field_index, depth)?;
@@ -311,29 +374,17 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
}
pub fn attestations_len(&self) -> usize {
match self {
Self::Base(body) => body.attestations.len(),
Self::Altair(body) => body.attestations.len(),
Self::Bellatrix(body) => body.attestations.len(),
Self::Capella(body) => body.attestations.len(),
Self::Deneb(body) => body.attestations.len(),
Self::Electra(body) => body.attestations.len(),
Self::Eip7805(body) => body.attestations.len(),
Self::Fulu(body) => body.attestations.len(),
}
map_beacon_block_body_ref!(&'a _, self, |inner, cons| {
cons(inner);
inner.attestations.len()
})
}
pub fn attester_slashings_len(&self) -> usize {
match self {
Self::Base(body) => body.attester_slashings.len(),
Self::Altair(body) => body.attester_slashings.len(),
Self::Bellatrix(body) => body.attester_slashings.len(),
Self::Capella(body) => body.attester_slashings.len(),
Self::Deneb(body) => body.attester_slashings.len(),
Self::Electra(body) => body.attester_slashings.len(),
Self::Eip7805(body) => body.attester_slashings.len(),
Self::Fulu(body) => body.attester_slashings.len(),
}
map_beacon_block_body_ref!(&'a _, self, |inner, cons| {
cons(inner);
inner.attester_slashings.len()
})
}
pub fn attestations(&self) -> Box<dyn Iterator<Item = AttestationRef<'a, E>> + 'a> {
@@ -346,6 +397,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
Self::Electra(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)),
Self::Eip7805(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)),
Self::Fulu(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)),
Self::Gloas(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)),
}
}
@@ -391,6 +443,11 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
.iter()
.map(AttesterSlashingRef::Electra),
),
Self::Gloas(body) => Box::new(
body.attester_slashings
.iter()
.map(AttesterSlashingRef::Electra),
),
}
}
}
@@ -422,6 +479,9 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRefMut<'a,
Self::Fulu(body) => {
Box::new(body.attestations.iter_mut().map(AttestationRefMut::Electra))
}
Self::Gloas(body) => {
Box::new(body.attestations.iter_mut().map(AttestationRefMut::Electra))
}
}
}
}
@@ -438,6 +498,7 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'_, E, Payl
BeaconBlockBodyRef::Electra { .. } => ForkName::Electra,
BeaconBlockBodyRef::Eip7805 { .. } => ForkName::Eip7805,
BeaconBlockBodyRef::Fulu { .. } => ForkName::Fulu,
BeaconBlockBodyRef::Gloas { .. } => ForkName::Gloas,
}
}
}
@@ -505,6 +566,46 @@ impl<E: EthSpec> From<BeaconBlockBodyAltair<E, BlindedPayload<E>>>
}
}
// Post-Fulu block bodies without payloads can be converted into block bodies with payloads
// TODO(EIP-7732) Look into whether we can remove this in the future since no blinded blocks post-gloas
impl<E: EthSpec> From<BeaconBlockBodyGloas<E, BlindedPayload<E>>>
for BeaconBlockBodyGloas<E, FullPayload<E>>
{
fn from(body: BeaconBlockBodyGloas<E, BlindedPayload<E>>) -> Self {
let BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom,
} = body;
BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom: PhantomData,
}
}
}
// Likewise bodies with payloads can be transformed into bodies without.
impl<E: EthSpec> From<BeaconBlockBodyBase<E, FullPayload<E>>>
for (
@@ -844,6 +945,50 @@ impl<E: EthSpec> From<BeaconBlockBodyFulu<E, FullPayload<E>>>
}
}
impl<E: EthSpec> From<BeaconBlockBodyGloas<E, FullPayload<E>>>
for (
BeaconBlockBodyGloas<E, BlindedPayload<E>>,
Option<ExecutionPayloadGloas<E>>,
)
{
fn from(body: BeaconBlockBodyGloas<E, FullPayload<E>>) -> Self {
let BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom,
} = body;
(
BeaconBlockBodyGloas {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings,
attester_slashings,
attestations,
deposits,
voluntary_exits,
sync_aggregate,
bls_to_execution_changes,
signed_execution_payload_bid,
payload_attestations,
_phantom: PhantomData,
},
None,
)
}
}
// 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>> {
@@ -1075,6 +1220,13 @@ impl<E: EthSpec> BeaconBlockBodyFulu<E, FullPayload<E>> {
}
}
impl<E: EthSpec> BeaconBlockBodyGloas<E, FullPayload<E>> {
pub fn clone_as_blinded(&self) -> BeaconBlockBodyGloas<E, BlindedPayload<E>> {
let (block_body, _payload) = self.clone().into();
block_body
}
}
impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
for (
BeaconBlockBody<E, BlindedPayload<E>>,
@@ -1105,22 +1257,16 @@ impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> ContextDeserialize<'de, F
}
}
/// Util method helpful for logging.
pub fn format_kzg_commitments(commitments: &[KzgCommitment]) -> String {
let commitment_strings: Vec<String> = commitments.iter().map(|x| x.to_string()).collect();
let commitments_joined = commitment_strings.join(", ");
let surrounded_commitments = format!("[{}]", commitments_joined);
surrounded_commitments
}
#[cfg(test)]
mod tests {
mod base {
use super::super::*;
use crate::core::MainnetEthSpec;
ssz_and_tree_hash_tests!(BeaconBlockBodyBase<MainnetEthSpec>);
}
mod altair {
use super::super::*;
use crate::core::MainnetEthSpec;
ssz_and_tree_hash_tests!(BeaconBlockBodyAltair<MainnetEthSpec>);
}
}

View File

@@ -1,29 +1,24 @@
use crate::test_utils::TestRandom;
use crate::*;
use context_deserialize_derive::context_deserialize;
use bls::SecretKey;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use crate::{
block::SignedBeaconBlockHeader,
core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot},
fork::{Fork, ForkName},
test_utils::TestRandom,
};
/// A header of a `BeaconBlock`.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct BeaconBlockHeader {

View File

@@ -0,0 +1,26 @@
mod beacon_block;
mod beacon_block_body;
mod beacon_block_header;
mod signed_beacon_block;
mod signed_beacon_block_header;
pub use beacon_block::{
BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockCapella,
BeaconBlockDeneb, BeaconBlockEip7805, BeaconBlockElectra, BeaconBlockFulu, BeaconBlockGloas,
BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, BlockImportSource, EmptyBlock,
};
pub use beacon_block_body::{
BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase,
BeaconBlockBodyBellatrix, BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyEip7805,
BeaconBlockBodyElectra, BeaconBlockBodyFulu, BeaconBlockBodyGloas, BeaconBlockBodyRef,
BeaconBlockBodyRefMut, NUM_BEACON_BLOCK_BODY_HASH_TREE_ROOT_LEAVES,
};
pub use beacon_block_header::BeaconBlockHeader;
pub use signed_beacon_block::{
SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix,
SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockEip7805,
SignedBeaconBlockElectra, SignedBeaconBlockFulu, SignedBeaconBlockGloas, SignedBeaconBlockHash,
SignedBlindedBeaconBlock, ssz_tagged_signed_beacon_block, ssz_tagged_signed_beacon_block_arc,
};
pub use signed_beacon_block_header::SignedBeaconBlockHeader;

View File

@@ -1,17 +1,44 @@
use crate::beacon_block_body::{format_kzg_commitments, BLOB_KZG_COMMITMENTS_INDEX};
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
use std::fmt;
use bls::{PublicKey, Signature};
use context_deserialize::ContextDeserialize;
use educe::Educe;
use merkle_proof::MerkleTree;
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::{Decode, Encode};
use std::fmt;
use ssz_types::FixedVector;
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tracing::instrument;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[derive(arbitrary::Arbitrary, PartialEq, Eq, Hash, Clone, Copy)]
use crate::{
block::{
BLOB_KZG_COMMITMENTS_INDEX, BeaconBlock, BeaconBlockAltair, BeaconBlockBase,
BeaconBlockBellatrix, BeaconBlockBodyBellatrix, BeaconBlockBodyCapella,
BeaconBlockBodyDeneb, BeaconBlockBodyEip7805, BeaconBlockBodyElectra, BeaconBlockBodyFulu,
BeaconBlockCapella, BeaconBlockDeneb, BeaconBlockEip7805, BeaconBlockElectra,
BeaconBlockFulu, BeaconBlockGloas, BeaconBlockHeader, BeaconBlockRef, BeaconBlockRefMut,
SignedBeaconBlockHeader,
},
core::{ChainSpec, Domain, Epoch, EthSpec, Hash256, SignedRoot, SigningData, Slot},
execution::{
AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella,
BlindedPayloadDeneb, BlindedPayloadEip7805, BlindedPayloadElectra, BlindedPayloadFulu,
ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadEip7805, ExecutionPayloadElectra,
ExecutionPayloadFulu, FullPayload, FullPayloadBellatrix, FullPayloadCapella,
FullPayloadDeneb, FullPayloadEip7805, FullPayloadElectra, FullPayloadFulu,
},
fork::{Fork, ForkName, ForkVersionDecode, InconsistentFork, map_fork_name},
kzg_ext::format_kzg_commitments,
state::BeaconStateError,
test_utils::TestRandom,
};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct SignedBeaconBlockHash(Hash256);
impl fmt::Debug for SignedBeaconBlockHash {
@@ -40,7 +67,7 @@ impl From<SignedBeaconBlockHash> for Hash256 {
/// A `BeaconBlock` and a signature from its proposer.
#[superstruct(
variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Eip7805, Fulu),
variants(Base, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Eip7805, Gloas),
variant_attributes(
derive(
Debug,
@@ -50,25 +77,30 @@ impl From<SignedBeaconBlockHash> for Hash256 {
Encode,
Decode,
TreeHash,
Derivative,
arbitrary::Arbitrary,
Educe,
TestRandom
),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
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, arbitrary::Arbitrary,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>")
)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(untagged)]
#[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: AbstractExecPayload<E> = FullPayload<E>> {
@@ -88,6 +120,8 @@ pub struct SignedBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E> = FullP
pub message: BeaconBlockEip7805<E, Payload>,
#[superstruct(only(Fulu), partial_getter(rename = "message_fulu"))]
pub message: BeaconBlockFulu<E, Payload>,
#[superstruct(only(Gloas), partial_getter(rename = "message_gloas"))]
pub message: BeaconBlockGloas<E, Payload>,
pub signature: Signature,
}
@@ -177,6 +211,9 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
BeaconBlock::Fulu(message) => {
SignedBeaconBlock::Fulu(SignedBeaconBlockFulu { message, signature })
}
BeaconBlock::Gloas(message) => {
SignedBeaconBlock::Gloas(SignedBeaconBlockGloas { message, signature })
}
}
}
@@ -247,6 +284,7 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
}
/// Produce a signed beacon block header corresponding to this block.
#[instrument(level = "debug", skip_all)]
pub fn signed_block_header(&self) -> SignedBeaconBlockHeader {
SignedBeaconBlockHeader {
message: self.message().block_header(),
@@ -264,7 +302,7 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
SignedBeaconBlockHeader,
FixedVector<Hash256, E::KzgCommitmentsInclusionProofDepth>,
),
Error,
BeaconStateError,
> {
// Create the block body merkle tree
let body_leaves = self.message().body().body_merkle_leaves();
@@ -274,7 +312,7 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
// Compute the KZG commitments inclusion proof
let (_, proof) = body_merkle_tree
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
.map_err(Error::MerkleTreeError)?;
.map_err(BeaconStateError::MerkleTreeError)?;
let kzg_commitments_inclusion_proof = FixedVector::new(proof)?;
let block_header = BeaconBlockHeader {
@@ -700,6 +738,20 @@ impl<E: EthSpec> SignedBeaconBlockFulu<E, BlindedPayload<E>> {
}
}
// We can convert gloas blocks without payloads into blocks "with" payloads.
// TODO(EIP-7732) Look into whether we can remove this in the future since no blinded blocks post-gloas
impl<E: EthSpec> From<SignedBeaconBlockGloas<E, BlindedPayload<E>>>
for SignedBeaconBlockGloas<E, FullPayload<E>>
{
fn from(signed_block: SignedBeaconBlockGloas<E, BlindedPayload<E>>) -> Self {
let SignedBeaconBlockGloas { message, signature } = signed_block;
SignedBeaconBlockGloas {
message: message.into(),
signature,
}
}
}
impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
pub fn try_into_full_block(
self,
@@ -726,6 +778,7 @@ impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
(SignedBeaconBlock::Fulu(block), Some(ExecutionPayload::Fulu(payload))) => {
SignedBeaconBlock::Fulu(block.into_full_block(payload))
}
(SignedBeaconBlock::Gloas(block), _) => SignedBeaconBlock::Gloas(block.into()),
// avoid wildcard matching forks so that compiler will
// direct us here when a new fork has been added
(SignedBeaconBlock::Bellatrix(_), _) => return None,
@@ -734,6 +787,7 @@ impl<E: EthSpec> SignedBeaconBlock<E, BlindedPayload<E>> {
(SignedBeaconBlock::Electra(_), _) => return None,
(SignedBeaconBlock::Eip7805(_), _) => return None,
(SignedBeaconBlock::Fulu(_), _) => return None,
// TODO(EIP-7732) Determine if need a match arm for gloas here
};
Some(full_block)
}
@@ -882,6 +936,9 @@ pub mod ssz_tagged_signed_beacon_block {
ForkName::Fulu => Ok(SignedBeaconBlock::Fulu(
SignedBeaconBlockFulu::from_ssz_bytes(body)?,
)),
ForkName::Gloas => Ok(SignedBeaconBlock::Gloas(
SignedBeaconBlockGloas::from_ssz_bytes(body)?,
)),
}
}
}
@@ -911,6 +968,7 @@ pub mod ssz_tagged_signed_beacon_block_arc {
#[cfg(test)]
mod test {
use super::*;
use crate::{block::EmptyBlock, core::MainnetEthSpec};
#[test]
fn add_remove_payload_roundtrip() {
@@ -953,11 +1011,26 @@ mod test {
}
}
fn spec_with_all_forks_enabled<E: EthSpec>() -> ChainSpec {
let mut chain_spec = E::default_spec();
chain_spec.altair_fork_epoch = Some(Epoch::new(1));
chain_spec.bellatrix_fork_epoch = Some(Epoch::new(2));
chain_spec.capella_fork_epoch = Some(Epoch::new(3));
chain_spec.deneb_fork_epoch = Some(Epoch::new(4));
chain_spec.electra_fork_epoch = Some(Epoch::new(5));
chain_spec.fulu_fork_epoch = Some(Epoch::new(6));
chain_spec.gloas_fork_epoch = Some(Epoch::new(7));
// check that we have all forks covered
assert!(chain_spec.fork_epoch(ForkName::latest()).is_some());
chain_spec
}
#[test]
fn test_ssz_tagged_signed_beacon_block() {
type E = MainnetEthSpec;
let spec = &E::default_spec();
let spec = &spec_with_all_forks_enabled::<E>();
let sig = Signature::empty();
let blocks = vec![
SignedBeaconBlock::<E>::from_block(
@@ -984,11 +1057,15 @@ mod test {
BeaconBlock::Electra(BeaconBlockElectra::empty(spec)),
sig.clone(),
),
SignedBeaconBlock::from_block(
BeaconBlock::Fulu(BeaconBlockFulu::empty(spec)),
sig.clone(),
),
SignedBeaconBlock::from_block(
BeaconBlock::Eip7805(BeaconBlockEip7805::empty(spec)),
sig.clone(),
),
SignedBeaconBlock::from_block(BeaconBlock::Fulu(BeaconBlockFulu::empty(spec)), sig),
SignedBeaconBlock::from_block(BeaconBlock::Gloas(BeaconBlockGloas::empty(spec)), sig),
];
for block in blocks {

View File

@@ -1,29 +1,23 @@
use crate::context_deserialize;
use crate::{
test_utils::TestRandom, BeaconBlockHeader, ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256,
PublicKey, Signature, SignedRoot,
};
use bls::{PublicKey, Signature};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
block::BeaconBlockHeader,
core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot},
fork::{Fork, ForkName},
test_utils::TestRandom,
};
/// A signed header of a `BeaconBlock`.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SignedBeaconBlockHeader {

View File

@@ -1,14 +1,6 @@
use crate::beacon_block_body::KzgCommitments;
use crate::test_utils::TestRandom;
use crate::{
ChainSpec, ContextDeserialize, EthSpec, ExecutionPayloadHeaderBellatrix,
ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderEip7805,
ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderRef,
ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionDecode, SignedRoot,
Uint256,
};
use bls::PublicKeyBytes;
use bls::Signature;
use context_deserialize::ContextDeserialize;
use serde::{Deserialize, Deserializer, Serialize};
use ssz::Decode;
use ssz_derive::{Decode, Encode};
@@ -16,6 +8,19 @@ use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{ChainSpec, EthSpec, SignedRoot, Uint256},
execution::{
ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderEip7805, ExecutionPayloadHeaderElectra,
ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionRequests,
},
fork::{ForkName, ForkVersionDecode},
kzg_ext::KzgCommitments,
test_utils::TestRandom,
};
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Eip7805, Fulu),
variant_attributes(
@@ -87,10 +92,10 @@ impl<E: EthSpec> ForkVersionDecode for BuilderBid<E> {
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let builder_bid = match fork_name {
ForkName::Altair | ForkName::Base => {
ForkName::Altair | ForkName::Base | ForkName::Gloas => {
return Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadHeader: {fork_name}",
)))
)));
}
ForkName::Bellatrix => {
BuilderBid::Bellatrix(BuilderBidBellatrix::from_ssz_bytes(bytes)?)
@@ -158,7 +163,7 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for BuilderBid<E> {
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Base | ForkName::Altair => {
ForkName::Base | ForkName::Altair | ForkName::Gloas => {
return Err(serde::de::Error::custom(format!(
"BuilderBid failed to deserialize: unsupported fork '{}'",
context

View File

@@ -0,0 +1,36 @@
use crate::test_utils::TestRandom;
use crate::{BuilderPendingWithdrawal, ForkName};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(
Debug,
PartialEq,
Eq,
Hash,
Clone,
Default,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[context_deserialize(ForkName)]
pub struct BuilderPendingPayment {
#[serde(with = "serde_utils::quoted_u64")]
pub weight: u64,
pub withdrawal: BuilderPendingWithdrawal,
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(BuilderPendingPayment);
}

View File

@@ -1,17 +1,18 @@
use crate::test_utils::TestRandom;
use crate::*;
use crate::{Address, Epoch, ForkName};
use context_deserialize::context_deserialize;
use serde::{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,
Default,
Serialize,
Deserialize,
Encode,
@@ -19,21 +20,21 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[context_deserialize(ForkName)]
pub struct Withdrawal {
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub validator_index: u64,
pub struct BuilderPendingWithdrawal {
#[serde(with = "serde_utils::address_hex")]
pub address: Address,
pub fee_recipient: Address,
#[serde(with = "serde_utils::quoted_u64")]
pub amount: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub builder_index: u64,
pub withdrawable_epoch: Epoch,
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(Withdrawal);
ssz_and_tree_hash_tests!(BuilderPendingWithdrawal);
}

View File

@@ -0,0 +1,10 @@
mod builder_bid;
mod builder_pending_payment;
mod builder_pending_withdrawal;
pub use builder_bid::{
BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidEip7805,
BuilderBidElectra, BuilderBidFulu, SignedBuilderBid,
};
pub use builder_pending_payment::BuilderPendingPayment;
pub use builder_pending_withdrawal::BuilderPendingWithdrawal;

View File

@@ -1,24 +1,20 @@
use crate::context_deserialize;
use crate::{test_utils::TestRandom, Address, ForkName, PublicKeyBytes, SignedRoot};
use bls::PublicKeyBytes;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{Address, SignedRoot},
fork::ForkName,
test_utils::TestRandom,
};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct ConsolidationRequest {

View File

@@ -0,0 +1,5 @@
mod consolidation_request;
mod pending_consolidation;
pub use consolidation_request::ConsolidationRequest;
pub use pending_consolidation::PendingConsolidation;

View File

@@ -1,24 +1,14 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::ForkName;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{fork::ForkName, test_utils::TestRandom};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct PendingConsolidation {

View File

@@ -1,18 +1,19 @@
use crate::{
consts::altair, consts::deneb, AltairPreset, BasePreset, BellatrixPreset, CapellaPreset,
ChainSpec, Config, DenebPreset, Eip7805Preset, ElectraPreset, EthSpec, ForkName, FuluPreset,
};
use maplit::hashmap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use superstruct::superstruct;
use crate::core::{
AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, ChainSpec, Config, DenebPreset,
Eip7805Preset, ElectraPreset, EthSpec, FuluPreset, GloasPreset, consts,
};
/// Fusion of a runtime-config with the compile-time preset values.
///
/// Mostly useful for the API.
#[superstruct(
variants(Deneb, Electra, Eip7805, Fulu),
variants(Deneb, Electra, Fulu, Eip7805, Gloas),
variant_attributes(derive(Serialize, Deserialize, Debug, PartialEq, Clone))
)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
@@ -31,24 +32,26 @@ pub struct ConfigAndPreset {
pub capella_preset: CapellaPreset,
#[serde(flatten)]
pub deneb_preset: DenebPreset,
#[superstruct(only(Electra, Eip7805, Fulu))]
#[superstruct(only(Electra, Fulu, Eip7805, Gloas))]
#[serde(flatten)]
pub electra_preset: ElectraPreset,
#[superstruct(only(Eip7805, Fulu))]
#[serde(flatten)]
pub eip7805_preset: Eip7805Preset,
#[superstruct(only(Fulu))]
#[superstruct(only(Fulu, Eip7805, Gloas))]
#[serde(flatten)]
pub fulu_preset: FuluPreset,
#[superstruct(only(Eip7805, Gloas))]
#[serde(flatten)]
pub eip7805_preset: Eip7805Preset,
#[superstruct(only(Gloas))]
#[serde(flatten)]
pub gloas_preset: GloasPreset,
/// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks.
#[serde(flatten)]
pub extra_fields: HashMap<String, Value>,
}
impl ConfigAndPreset {
// DEPRECATED: the `fork_name` argument is never used, we should remove it.
pub fn from_chain_spec<E: EthSpec>(spec: &ChainSpec, fork_name: Option<ForkName>) -> Self {
let config = Config::from_chain_spec::<E>(spec);
pub fn from_chain_spec<E: EthSpec>(spec: &ChainSpec) -> Self {
let mut config = Config::from_chain_spec::<E>(spec);
let base_preset = BasePreset::from_chain_spec::<E>(spec);
let altair_preset = AltairPreset::from_chain_spec::<E>(spec);
let bellatrix_preset = BellatrixPreset::from_chain_spec::<E>(spec);
@@ -56,12 +59,44 @@ impl ConfigAndPreset {
let deneb_preset = DenebPreset::from_chain_spec::<E>(spec);
let extra_fields = get_extra_fields(spec);
if spec.fulu_fork_epoch.is_some()
|| fork_name.is_none()
|| fork_name == Some(ForkName::Fulu)
{
if spec.is_gloas_scheduled() {
let electra_preset = ElectraPreset::from_chain_spec::<E>(spec);
let fulu_preset = FuluPreset::from_chain_spec::<E>(spec);
let eip7805_preset = Eip7805Preset::from_chain_spec(spec);
let gloas_preset = GloasPreset::from_chain_spec::<E>(spec);
ConfigAndPreset::Gloas(ConfigAndPresetGloas {
config,
base_preset,
altair_preset,
bellatrix_preset,
capella_preset,
deneb_preset,
electra_preset,
fulu_preset,
eip7805_preset,
gloas_preset,
extra_fields,
})
} else if spec.is_focil_scheduled() {
let electra_preset = ElectraPreset::from_chain_spec::<E>(spec);
let fulu_preset = FuluPreset::from_chain_spec::<E>(spec);
let eip7805_preset = Eip7805Preset::from_chain_spec(spec);
ConfigAndPreset::Eip7805(ConfigAndPresetEip7805 {
config,
base_preset,
altair_preset,
bellatrix_preset,
capella_preset,
deneb_preset,
electra_preset,
fulu_preset,
eip7805_preset,
extra_fields,
})
} else if spec.is_fulu_scheduled() {
let electra_preset = ElectraPreset::from_chain_spec::<E>(spec);
let eip7805_preset = Eip7805Preset::from_chain_spec::<E>(spec);
let fulu_preset = FuluPreset::from_chain_spec::<E>(spec);
ConfigAndPreset::Fulu(ConfigAndPresetFulu {
@@ -72,32 +107,13 @@ impl ConfigAndPreset {
capella_preset,
deneb_preset,
electra_preset,
eip7805_preset,
fulu_preset,
extra_fields,
})
} else if spec.electra_fork_epoch.is_some()
|| fork_name.is_none()
|| fork_name == Some(ForkName::Eip7805)
{
let electra_preset = ElectraPreset::from_chain_spec::<E>(spec);
let eip7805_preset = Eip7805Preset::from_chain_spec::<E>(spec);
} else {
// Remove blob schedule for backwards-compatibility.
config.blob_schedule.set_skip_serializing();
ConfigAndPreset::Eip7805(ConfigAndPresetEip7805 {
config,
base_preset,
altair_preset,
bellatrix_preset,
capella_preset,
deneb_preset,
electra_preset,
eip7805_preset,
extra_fields,
})
} else if spec.electra_fork_epoch.is_some()
|| fork_name.is_none()
|| fork_name == Some(ForkName::Electra)
{
let electra_preset = ElectraPreset::from_chain_spec::<E>(spec);
ConfigAndPreset::Electra(ConfigAndPresetElectra {
@@ -110,16 +126,6 @@ impl ConfigAndPreset {
electra_preset,
extra_fields,
})
} else {
ConfigAndPreset::Deneb(ConfigAndPresetDeneb {
config,
base_preset,
altair_preset,
bellatrix_preset,
capella_preset,
deneb_preset,
extra_fields,
})
}
}
}
@@ -148,11 +154,11 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
"domain_sync_committee_selection_proof".to_uppercase() =>
u32_hex(spec.domain_sync_committee_selection_proof),
"sync_committee_subnet_count".to_uppercase() =>
altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(),
consts::altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(),
"target_aggregators_per_sync_subcommittee".to_uppercase() =>
altair::TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE.to_string().into(),
consts::altair::TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE.to_string().into(),
// Deneb
"versioned_hash_version_kzg".to_uppercase() => deneb::VERSIONED_HASH_VERSION_KZG.to_string().into(),
"versioned_hash_version_kzg".to_uppercase() => consts::deneb::VERSIONED_HASH_VERSION_KZG.to_string().into(),
// Electra
"compounding_withdrawal_prefix".to_uppercase() => u8_hex(spec.compounding_withdrawal_prefix_byte),
"unset_deposit_requests_start_index".to_uppercase() => spec.unset_deposit_requests_start_index.to_string().into(),
@@ -163,7 +169,7 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
#[cfg(test)]
mod test {
use super::*;
use crate::MainnetEthSpec;
use crate::{Epoch, MainnetEthSpec};
use std::fs::File;
use tempfile::NamedTempFile;
@@ -175,9 +181,10 @@ mod test {
.write(true)
.open(tmp_file.as_ref())
.expect("error opening file");
let mainnet_spec = ChainSpec::mainnet();
let mut yamlconfig =
ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec, None);
let mut mainnet_spec = ChainSpec::mainnet();
// setting gloas_fork_epoch because we are roundtripping a gloas config
mainnet_spec.gloas_fork_epoch = Some(Epoch::new(42));
let mut yamlconfig = ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec);
let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789");
let (k2, v2) = ("SAMPLE_HARDFORK_KEY2", "987654321");
let (k3, v3) = ("SAMPLE_HARDFORK_KEY3", 32);
@@ -194,8 +201,8 @@ mod test {
.write(false)
.open(tmp_file.as_ref())
.expect("error while opening the file");
let from: ConfigAndPresetFulu =
let from: ConfigAndPresetGloas =
serde_yaml::from_reader(reader).expect("error while deserializing");
assert_eq!(ConfigAndPreset::Fulu(from), yamlconfig);
assert_eq!(ConfigAndPreset::Gloas(from), yamlconfig);
}
}

View File

@@ -23,5 +23,5 @@ pub mod bellatrix {
pub const INTERVALS_PER_SLOT: u64 = 3;
}
pub mod deneb {
pub use crate::VERSIONED_HASH_VERSION_KZG;
pub use kzg::VERSIONED_HASH_VERSION_KZG;
}

View File

@@ -1,33 +1,27 @@
use crate::test_utils::TestRandom;
use crate::Epoch;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{core::Epoch, test_utils::TestRandom};
/// Specifies a fork which allows nodes to identify each other on the network. This fork is used in
/// a nodes local ENR.
///
/// Spec v0.11
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Default,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
pub struct EnrForkId {
/// Fork digest of the current fork computed from [`ChainSpec::compute_fork_digest`].
#[serde(with = "serde_utils::bytes_4_hex")]
pub fork_digest: [u8; 4],
/// `next_fork_version` is the fork version corresponding to the next planned fork at a future
/// epoch. The fork version will only change for regular forks, not BPO forks.
#[serde(with = "serde_utils::bytes_4_hex")]
pub next_fork_version: [u8; 4],
/// `next_fork_epoch` is the epoch at which the next fork (whether a regular fork or a BPO fork) is planned
pub next_fork_epoch: Epoch,
}

View File

@@ -1,16 +1,22 @@
use crate::*;
use std::{
fmt::{self, Debug},
str::FromStr,
};
use safe_arith::SafeArith;
use serde::{Deserialize, Serialize};
use ssz_types::typenum::{
bit::B0, UInt, U0, U1, U10, U1024, U1048576, U1073741824, U1099511627776, U128, U131072,
U134217728, U16, U16777216, U17, U2, U2048, U256, U262144, U32, U33554432, U4, U4096, U512,
U625, U64, U65536, U8, U8192,
use typenum::{
U0, U1, U2, U4, U8, U16, U17, U32, U64, U128, U256, U512, U625, U1024, U2048, U4096, U8192,
U65536, U131072, U262144, U1048576, U16777216, U33554432, U134217728, U1073741824,
U1099511627776, UInt, Unsigned, bit::B0,
};
use std::fmt::{self, Debug};
use std::str::FromStr;
pub type U5000 = UInt<UInt<UInt<U625, B0>, B0>, B0>; // 625 * 8 = 5000
use crate::{
core::{ChainSpec, Epoch},
state::BeaconStateError,
};
type U5000 = UInt<UInt<UInt<U625, B0>, B0>, B0>; // 625 * 8 = 5000
const MAINNET: &str = "mainnet";
const MINIMAL: &str = "minimal";
@@ -49,9 +55,7 @@ impl fmt::Display for EthSpecId {
}
}
pub trait EthSpec:
'static + Default + Sync + Send + Clone + Debug + PartialEq + Eq + for<'a> arbitrary::Arbitrary<'a>
{
pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + Eq {
/*
* Constants
*/
@@ -113,11 +117,14 @@ pub trait EthSpec:
type BytesPerFieldElement: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type KzgCommitmentInclusionProofDepth: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* New in PeerDAS
* New in Fulu
*/
type FieldElementsPerCell: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type FieldElementsPerExtBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type KzgCommitmentsInclusionProofDepth: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type CellsPerExtBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type NumberOfColumns: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type ProposerLookaheadSlots: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* Derived values (set these CAREFULLY)
*/
@@ -165,7 +172,15 @@ pub trait EthSpec:
type MaxPendingDepositsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* FOCIL
* New in Gloas
*/
type PTCSize: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxPayloadAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type BuilderPendingPaymentsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type BuilderPendingWithdrawalsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* New in Eip7805
*/
type InclusionListCommitteeSize: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxTransactionsPerInclusionList: Unsigned + Clone + Sync + Send + Debug + PartialEq;
@@ -187,7 +202,7 @@ pub trait EthSpec:
fn get_committee_count_per_slot(
active_validator_count: usize,
spec: &ChainSpec,
) -> Result<usize, Error> {
) -> Result<usize, BeaconStateError> {
Self::get_committee_count_per_slot_with(
active_validator_count,
spec.max_committees_per_slot,
@@ -199,7 +214,7 @@ pub trait EthSpec:
active_validator_count: usize,
max_committees_per_slot: usize,
target_committee_size: usize,
) -> Result<usize, Error> {
) -> Result<usize, BeaconStateError> {
let slots_per_epoch = Self::SlotsPerEpoch::to_usize();
Ok(std::cmp::max(
@@ -315,6 +330,11 @@ pub trait EthSpec:
Self::BytesPerBlob::to_usize()
}
/// Returns the `BYTES_PER_CELL` constant for this specification.
fn bytes_per_cell() -> usize {
Self::BytesPerCell::to_usize()
}
/// Returns the `KZG_COMMITMENT_INCLUSION_PROOF_DEPTH` preset for this specification.
fn kzg_proof_inclusion_proof_depth() -> usize {
Self::KzgCommitmentInclusionProofDepth::to_usize()
@@ -351,6 +371,16 @@ pub trait EthSpec:
Self::PendingConsolidationsLimit::to_usize()
}
/// Returns the `BUILDER_PENDING_PAYMENTS_LIMIT` constant for this specification.
fn builder_pending_payments_limit() -> usize {
Self::BuilderPendingPaymentsLimit::to_usize()
}
/// Returns the `BUILDER_PENDING_WITHDRAWALS_LIMIT` constant for this specification.
fn builder_pending_withdrawals_limit() -> usize {
Self::BuilderPendingWithdrawalsLimit::to_usize()
}
/// Returns the `MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD` constant for this specification.
fn max_consolidation_requests_per_payload() -> usize {
Self::MaxConsolidationRequestsPerPayload::to_usize()
@@ -394,6 +424,28 @@ pub trait EthSpec:
fn max_transactions_per_inclusion_list() -> usize {
Self::MaxTransactionsPerInclusionList::to_usize()
}
fn cells_per_ext_blob() -> usize {
Self::CellsPerExtBlob::to_usize()
}
fn number_of_columns() -> usize {
Self::NumberOfColumns::to_usize()
}
fn proposer_lookahead_slots() -> usize {
Self::ProposerLookaheadSlots::to_usize()
}
/// Returns the `PTCSize` constant for this specification.
fn ptc_size() -> usize {
Self::PTCSize::to_usize()
}
/// Returns the `MaxPayloadAttestations` constant for this specification.
fn max_payload_attestations() -> usize {
Self::MaxPayloadAttestations::to_usize()
}
}
/// Macro to inherit some type values from another EthSpec.
@@ -405,7 +457,8 @@ macro_rules! params_from_eth_spec {
}
/// Ethereum Foundation specifications.
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
pub struct MainnetEthSpec;
impl EthSpec for MainnetEthSpec {
@@ -422,6 +475,8 @@ impl EthSpec for MainnetEthSpec {
type EpochsPerSlashingsVector = U8192;
type HistoricalRootsLimit = U16777216;
type ValidatorRegistryLimit = U1099511627776;
type BuilderPendingPaymentsLimit = U64; // 2 * SLOTS_PER_EPOCH = 2 * 32 = 64
type BuilderPendingWithdrawalsLimit = U1048576;
type MaxProposerSlashings = U16;
type MaxAttesterSlashings = U2;
type MaxAttestations = U128;
@@ -445,6 +500,9 @@ impl EthSpec for MainnetEthSpec {
type MaxCellsPerBlock = U33554432;
type KzgCommitmentInclusionProofDepth = U17;
type KzgCommitmentsInclusionProofDepth = U4; // inclusion of the whole list of commitments
type CellsPerExtBlob = U128;
type NumberOfColumns = U128;
type ProposerLookaheadSlots = U64; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
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
@@ -460,6 +518,8 @@ impl EthSpec for MainnetEthSpec {
type MaxWithdrawalRequestsPerPayload = U16;
type MaxTransactionsPerInclusionList = U16;
type MaxPendingDepositsPerEpoch = U16;
type PTCSize = U512;
type MaxPayloadAttestations = U4;
type InclusionListCommitteeSize = U16;
fn default_spec() -> ChainSpec {
@@ -472,7 +532,8 @@ impl EthSpec for MainnetEthSpec {
}
/// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo.
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
pub struct MinimalEthSpec;
impl EthSpec for MinimalEthSpec {
@@ -490,17 +551,19 @@ impl EthSpec for MinimalEthSpec {
type MaxWithdrawalsPerPayload = U4;
type FieldElementsPerBlob = U4096;
type BytesPerBlob = U131072;
type MaxBlobCommitmentsPerBlock = U32;
type KzgCommitmentInclusionProofDepth = U10;
type MaxBlobCommitmentsPerBlock = U4096;
type KzgCommitmentInclusionProofDepth = U17;
type PendingPartialWithdrawalsLimit = U64;
type PendingConsolidationsLimit = U64;
type MaxDepositRequestsPerPayload = U4;
type MaxWithdrawalRequestsPerPayload = U2;
type FieldElementsPerCell = U64;
type FieldElementsPerExtBlob = U8192;
type MaxCellsPerBlock = U33554432;
type BytesPerCell = U2048;
type KzgCommitmentsInclusionProofDepth = U4;
type CellsPerExtBlob = U128;
type NumberOfColumns = U128;
type ProposerLookaheadSlots = U16; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
type BuilderPendingPaymentsLimit = U16; // 2 * SLOTS_PER_EPOCH = 2 * 8 = 16
params_from_eth_spec!(MainnetEthSpec {
JustificationBitsLength,
@@ -510,6 +573,7 @@ impl EthSpec for MinimalEthSpec {
GenesisEpoch,
HistoricalRootsLimit,
ValidatorRegistryLimit,
BuilderPendingWithdrawalsLimit,
MaxProposerSlashings,
MaxAttesterSlashings,
MaxAttestations,
@@ -528,6 +592,10 @@ impl EthSpec for MinimalEthSpec {
MaxConsolidationRequestsPerPayload,
MaxAttesterSlashingsElectra,
MaxAttestationsElectra,
MaxDepositRequestsPerPayload,
MaxWithdrawalRequestsPerPayload,
PTCSize,
MaxPayloadAttestations,
InclusionListCommitteeSize,
MaxTransactionsPerInclusionList
});
@@ -542,7 +610,8 @@ impl EthSpec for MinimalEthSpec {
}
/// Gnosis Beacon Chain specifications.
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
pub struct GnosisEthSpec;
impl EthSpec for GnosisEthSpec {
@@ -559,6 +628,8 @@ impl EthSpec for GnosisEthSpec {
type EpochsPerSlashingsVector = U8192;
type HistoricalRootsLimit = U16777216;
type ValidatorRegistryLimit = U1099511627776;
type BuilderPendingPaymentsLimit = U32; // 2 * SLOTS_PER_EPOCH = 2 * 16 = 32
type BuilderPendingWithdrawalsLimit = U1048576;
type MaxProposerSlashings = U16;
type MaxAttesterSlashings = U2;
type MaxAttestations = U128;
@@ -596,6 +667,11 @@ impl EthSpec for GnosisEthSpec {
type MaxCellsPerBlock = U33554432;
type BytesPerCell = U2048;
type KzgCommitmentsInclusionProofDepth = U4;
type CellsPerExtBlob = U128;
type NumberOfColumns = U128;
type ProposerLookaheadSlots = U32; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
type PTCSize = U512;
type MaxPayloadAttestations = U2;
type InclusionListCommitteeSize = U16;
type MaxTransactionsPerInclusionList = U16;
@@ -611,12 +687,17 @@ impl EthSpec for GnosisEthSpec {
#[cfg(test)]
mod test {
use crate::{EthSpec, GnosisEthSpec, MainnetEthSpec, MinimalEthSpec};
use ssz_types::typenum::Unsigned;
use typenum::Unsigned;
fn assert_valid_spec<E: EthSpec>() {
let spec = E::default_spec();
E::kzg_commitments_tree_depth();
E::block_body_tree_depth();
assert!(E::MaxValidatorsPerSlot::to_i32() >= E::MaxValidatorsPerCommittee::to_i32());
assert_eq!(
E::proposer_lookahead_slots(),
(spec.min_seed_lookahead.as_usize() + 1) * E::slots_per_epoch() as usize
);
}
#[test]

View File

@@ -1,20 +1,19 @@
use crate::{
test_utils::{RngCore, TestRandom},
Hash256,
};
use std::{fmt, str::FromStr};
use rand::RngCore;
use regex::bytes::Regex;
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
use ssz::{Decode, DecodeError, Encode};
use std::fmt;
use std::str::FromStr;
use tree_hash::{PackedEncoding, TreeHash};
use crate::{core::Hash256, test_utils::TestRandom};
pub const GRAFFITI_BYTES_LEN: usize = 32;
/// The 32-byte `graffiti` field on a beacon block.
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Default, Debug, PartialEq, Hash, Clone, Copy, Serialize, Deserialize)]
#[serde(transparent)]
#[derive(arbitrary::Arbitrary)]
pub struct Graffiti(#[serde(with = "serde_graffiti")] pub [u8; GRAFFITI_BYTES_LEN]);
impl Graffiti {

View File

@@ -0,0 +1,44 @@
pub mod consts;
mod application_domain;
mod chain_spec;
mod config_and_preset;
mod enr_fork_id;
mod eth_spec;
mod graffiti;
mod non_zero_usize;
mod preset;
mod relative_epoch;
mod signing_data;
mod slot_data;
#[macro_use]
mod slot_epoch_macros;
mod slot_epoch;
#[cfg(feature = "sqlite")]
mod sqlite;
pub use application_domain::{APPLICATION_DOMAIN_BUILDER, ApplicationDomain};
pub use chain_spec::{BlobParameters, BlobSchedule, ChainSpec, Config, Domain};
pub use config_and_preset::{
ConfigAndPreset, ConfigAndPresetDeneb, ConfigAndPresetElectra, ConfigAndPresetFulu,
ConfigAndPresetGloas, get_extra_fields,
};
pub use enr_fork_id::EnrForkId;
pub use eth_spec::{EthSpec, EthSpecId, GNOSIS, GnosisEthSpec, MainnetEthSpec, MinimalEthSpec};
pub use graffiti::{GRAFFITI_BYTES_LEN, Graffiti, GraffitiString};
pub use non_zero_usize::new_non_zero_usize;
pub use preset::{
AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, DenebPreset, Eip7805Preset,
ElectraPreset, FuluPreset, GloasPreset,
};
pub use relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
pub use signing_data::{SignedRoot, SigningData};
pub use slot_data::SlotData;
pub use slot_epoch::{Epoch, Slot};
pub type Hash256 = alloy_primitives::B256;
pub type Uint256 = alloy_primitives::U256;
pub type Hash64 = alloy_primitives::B64;
pub type Address = alloy_primitives::Address;
pub type VersionedHash = Hash256;
pub type MerkleProof = Vec<Hash256>;

View File

@@ -1,5 +1,7 @@
use crate::{ChainSpec, Epoch, EthSpec, Unsigned};
use serde::{Deserialize, Serialize};
use typenum::Unsigned;
use crate::core::{ChainSpec, Epoch, EthSpec};
/// Value-level representation of an Ethereum consensus "preset".
///
@@ -208,6 +210,8 @@ pub struct DenebPreset {
#[serde(with = "serde_utils::quoted_u64")]
pub max_blob_commitments_per_block: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub kzg_commitment_inclusion_proof_depth: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub field_elements_per_blob: u64,
}
@@ -215,6 +219,7 @@ impl DenebPreset {
pub fn from_chain_spec<E: EthSpec>(_spec: &ChainSpec) -> Self {
Self {
max_blob_commitments_per_block: E::max_blob_commitments_per_block() as u64,
kzg_commitment_inclusion_proof_depth: E::KzgCommitmentInclusionProofDepth::to_u64(),
field_elements_per_blob: E::field_elements_per_blob() as u64,
}
}
@@ -298,7 +303,7 @@ pub struct Eip7805Preset {
}
impl Eip7805Preset {
pub fn from_chain_spec<E: EthSpec>(spec: &ChainSpec) -> Self {
pub fn from_chain_spec(spec: &ChainSpec) -> Self {
Self {
domain_inclusion_list_committee: spec.domain_inclusion_list_committee,
inclusion_list_committee_size: spec.inclusion_list_committee_size,
@@ -315,6 +320,10 @@ pub struct FuluPreset {
pub field_elements_per_ext_blob: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub kzg_commitments_inclusion_proof_depth: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub cells_per_ext_blob: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub number_of_columns: u64,
}
impl FuluPreset {
@@ -324,10 +333,22 @@ impl FuluPreset {
field_elements_per_ext_blob: E::field_elements_per_ext_blob() as u64,
kzg_commitments_inclusion_proof_depth: E::kzg_commitments_inclusion_proof_depth()
as u64,
cells_per_ext_blob: E::cells_per_ext_blob() as u64,
number_of_columns: E::number_of_columns() as u64,
}
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct GloasPreset {}
impl GloasPreset {
pub fn from_chain_spec<E: EthSpec>(_spec: &ChainSpec) -> Self {
Self {}
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -374,10 +395,13 @@ mod test {
assert_eq!(electra, ElectraPreset::from_chain_spec::<E>(&spec));
let eip7805: Eip7805Preset = preset_from_file(&preset_name, "eip7805.yaml");
assert_eq!(eip7805, Eip7805Preset::from_chain_spec::<E>(&spec));
assert_eq!(eip7805, Eip7805Preset::from_chain_spec(&spec));
let fulu: FuluPreset = preset_from_file(&preset_name, "fulu.yaml");
assert_eq!(fulu, FuluPreset::from_chain_spec::<E>(&spec));
let gloas: GloasPreset = preset_from_file(&preset_name, "gloas.yaml");
assert_eq!(gloas, GloasPreset::from_chain_spec::<E>(&spec));
}
#[test]

View File

@@ -1,6 +1,7 @@
use crate::*;
use safe_arith::{ArithError, SafeArith};
use crate::core::{Epoch, Slot};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Error {
EpochTooLow { base: Epoch, other: Epoch },
@@ -18,7 +19,8 @@ impl From<ArithError> for Error {
/// to and following some epoch.
///
/// Spec v0.12.1
#[derive(Debug, PartialEq, Clone, Copy, arbitrary::Arbitrary)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RelativeEpoch {
/// The prior epoch.
Previous,

View File

@@ -1,25 +1,14 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{ForkName, Hash256};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
use crate::{core::Hash256, fork::ForkName, test_utils::TestRandom};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[context_deserialize(ForkName)]
pub struct SigningData {
pub object_root: Hash256,

View File

@@ -1,4 +1,4 @@
use crate::Slot;
use crate::core::Slot;
/// A trait providing a `Slot` getter for messages that are related to a single slot. Useful in
/// making parts of attestation and sync committee processing generic.

View File

@@ -10,51 +10,38 @@
//! implement `Into<u64>`, however this would allow operations between `Slots` and `Epochs` which
//! may lead to programming errors which are not detected by the compiler.
use crate::test_utils::TestRandom;
use crate::{ChainSpec, SignedRoot};
use std::{fmt, hash::Hash};
use rand::RngCore;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use ssz::{Decode, DecodeError, Encode};
use std::fmt;
use std::hash::Hash;
use crate::{
core::{ChainSpec, SignedRoot},
test_utils::TestRandom,
};
#[cfg(feature = "legacy-arith")]
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
#[derive(
arbitrary::Arbitrary,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Slot(#[serde(with = "serde_utils::quoted_u64")] u64);
#[derive(
arbitrary::Arbitrary,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Epoch(#[serde(with = "serde_utils::quoted_u64")] u64);
impl Epoch {
/// Returns an iterator `(end..=start)`
pub fn range_inclusive_rev(start: Self, end: Self) -> impl Iterator<Item = Self> {
(end.0..=start.0).rev().map(Epoch)
}
}
impl_common!(Slot);
impl_common!(Epoch);
@@ -118,7 +105,7 @@ impl Epoch {
.as_u64())
}
pub fn slot_iter(&self, slots_per_epoch: u64) -> SlotIter {
pub fn slot_iter(&self, slots_per_epoch: u64) -> SlotIter<'_> {
SlotIter {
current_iteration: 0,
epoch: self,

View File

@@ -1,14 +1,15 @@
//! Implementations of SQLite compatibility traits.
use crate::{Epoch, Slot};
use rusqlite::{
types::{FromSql, FromSqlError, ToSql, ToSqlOutput, ValueRef},
Error,
types::{FromSql, FromSqlError, ToSql, ToSqlOutput, ValueRef},
};
use crate::core::{Epoch, Slot};
macro_rules! impl_to_from_sql {
($type:ty) => {
impl ToSql for $type {
fn to_sql(&self) -> Result<ToSqlOutput, Error> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>, Error> {
let val_i64 = i64::try_from(self.as_u64())
.map_err(|e| Error::ToSqlConversionFailure(Box::new(e)))?;
Ok(ToSqlOutput::from(val_i64))

View File

@@ -1,27 +1,33 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{
beacon_block_body::BLOB_KZG_COMMITMENTS_INDEX, AbstractExecPayload, BeaconBlockHeader,
BeaconStateError, Blob, ChainSpec, Epoch, EthSpec, FixedVector, ForkName, Hash256, KzgProofs,
RuntimeFixedVector, RuntimeVariableList, SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
VariableList,
};
use std::{fmt::Debug, hash::Hash, sync::Arc};
use bls::Signature;
use derivative::Derivative;
use kzg::{Blob as KzgBlob, Kzg, KzgCommitment, KzgProof, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT};
use merkle_proof::{merkle_root_from_branch, verify_merkle_proof, MerkleTreeError};
use context_deserialize::context_deserialize;
use educe::Educe;
use kzg::{BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT, Blob as KzgBlob, Kzg, KzgCommitment, KzgProof};
use merkle_proof::{MerkleTreeError, merkle_root_from_branch, verify_merkle_proof};
use rand::Rng;
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use std::fmt::Debug;
use std::hash::Hash;
use std::sync::Arc;
use ssz_types::{FixedVector, RuntimeFixedVector, RuntimeVariableList, VariableList};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use crate::{
block::{
BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader,
},
core::{ChainSpec, Epoch, EthSpec, Hash256, Slot},
data::Blob,
execution::AbstractExecPayload,
fork::ForkName,
kzg_ext::KzgProofs,
state::BeaconStateError,
test_utils::TestRandom,
};
/// Container of the data that identifies an individual blob.
#[derive(
Serialize, Deserialize, Encode, Decode, TreeHash, Copy, Clone, Debug, PartialEq, Eq, Hash,
@@ -44,22 +50,15 @@ impl Ord for BlobIdentifier {
}
}
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Educe)]
#[context_deserialize(ForkName)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
#[educe(PartialEq, Eq, Hash(bound(E: EthSpec)))]
pub struct BlobSidecar<E: EthSpec> {
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
@@ -89,6 +88,7 @@ pub enum BlobSidecarError {
MissingKzgCommitment,
BeaconState(BeaconStateError),
MerkleTree(MerkleTreeError),
SszTypes(ssz_types::Error),
ArithError(ArithError),
}
@@ -288,10 +288,11 @@ impl<E: EthSpec> BlobSidecar<E> {
let blob_sidecar = BlobSidecar::new(i, blob, block, *kzg_proof)?;
blob_sidecars.push(Arc::new(blob_sidecar));
}
Ok(RuntimeVariableList::from_vec(
RuntimeVariableList::new(
blob_sidecars,
spec.max_blobs_per_block(block.epoch()) as usize,
))
)
.map_err(BlobSidecarError::SszTypes)
}
}

View File

@@ -1,9 +1,14 @@
use crate::{ChainSpec, ColumnIndex, DataColumnSubnetId};
use std::collections::HashSet;
use alloy_primitives::U256;
use itertools::Itertools;
use maplit::hashset;
use safe_arith::{ArithError, SafeArith};
use std::collections::HashSet;
use crate::{
EthSpec,
core::ChainSpec,
data::{ColumnIndex, DataColumnSubnetId},
};
pub type CustodyIndex = u64;
@@ -25,13 +30,36 @@ pub fn get_custody_groups(
custody_group_count: u64,
spec: &ChainSpec,
) -> Result<HashSet<CustodyIndex>, DataColumnCustodyGroupError> {
if custody_group_count == spec.number_of_custody_groups {
Ok(HashSet::from_iter(0..spec.number_of_custody_groups))
} else {
get_custody_groups_ordered(raw_node_id, custody_group_count, spec)
.map(|custody_groups| custody_groups.into_iter().collect())
}
}
/// Returns a deterministically ordered list of custody groups assigned to a node,
/// preserving the order in which they were computed during iteration.
///
/// # Arguments
/// * `raw_node_id` - 32-byte node identifier
/// * `custody_group_count` - Number of custody groups to generate
/// * `spec` - Chain specification containing custody group parameters
///
/// # Returns
/// Vector of custody group indices in computation order or error if parameters are invalid
fn get_custody_groups_ordered(
raw_node_id: [u8; 32],
custody_group_count: u64,
spec: &ChainSpec,
) -> Result<Vec<CustodyIndex>, DataColumnCustodyGroupError> {
if custody_group_count > spec.number_of_custody_groups {
return Err(DataColumnCustodyGroupError::InvalidCustodyGroupCount(
custody_group_count,
));
}
let mut custody_groups: HashSet<u64> = hashset![];
let mut custody_groups = vec![];
let mut current_id = U256::from_be_slice(&raw_node_id);
while custody_groups.len() < custody_group_count as usize {
let mut node_id_bytes = [0u8; 32];
@@ -44,7 +72,9 @@ pub fn get_custody_groups(
let custody_group = hash_prefix_u64
.safe_rem(spec.number_of_custody_groups)
.expect("spec.number_of_custody_groups must not be zero");
custody_groups.insert(custody_group);
if !custody_groups.contains(&custody_group) {
custody_groups.push(custody_group);
}
current_id = current_id.wrapping_add(U256::from(1u64));
}
@@ -52,10 +82,31 @@ pub fn get_custody_groups(
Ok(custody_groups)
}
/// Returns a deterministically ordered list of custody columns assigned to a node,
/// preserving the order in which they were computed during iteration.
///
/// # Arguments
/// * `raw_node_id` - 32-byte node identifier
/// * `spec` - Chain specification containing custody parameters
pub fn compute_ordered_custody_column_indices<E: EthSpec>(
raw_node_id: [u8; 32],
spec: &ChainSpec,
) -> Result<Vec<ColumnIndex>, DataColumnCustodyGroupError> {
let all_custody_groups_ordered =
get_custody_groups_ordered(raw_node_id, spec.number_of_custody_groups, spec)?;
let mut ordered_custody_columns = vec![];
for custody_index in all_custody_groups_ordered {
let columns = compute_columns_for_custody_group::<E>(custody_index, spec)?;
ordered_custody_columns.extend(columns);
}
Ok(ordered_custody_columns)
}
/// Returns the columns that are associated with a given custody group.
///
/// spec: https://github.com/ethereum/consensus-specs/blob/8e0d0d48e81d6c7c5a8253ab61340f5ea5bac66a/specs/fulu/das-core.md#compute_columns_for_custody_group
pub fn compute_columns_for_custody_group(
pub fn compute_columns_for_custody_group<E: EthSpec>(
custody_group: CustodyIndex,
spec: &ChainSpec,
) -> Result<impl Iterator<Item = ColumnIndex>, DataColumnCustodyGroupError> {
@@ -67,7 +118,7 @@ pub fn compute_columns_for_custody_group(
}
let mut columns = Vec::new();
for i in 0..spec.data_columns_per_group() {
for i in 0..spec.data_columns_per_group::<E>() {
let column = number_of_custody_groups
.safe_mul(i)
.and_then(|v| v.safe_add(custody_group))
@@ -78,7 +129,7 @@ pub fn compute_columns_for_custody_group(
Ok(columns.into_iter())
}
pub fn compute_subnets_for_node(
pub fn compute_subnets_for_node<E: EthSpec>(
raw_node_id: [u8; 32],
custody_group_count: u64,
spec: &ChainSpec,
@@ -87,7 +138,7 @@ pub fn compute_subnets_for_node(
let mut subnets = HashSet::new();
for custody_group in custody_groups {
let custody_group_subnets = compute_subnets_from_custody_group(custody_group, spec)?;
let custody_group_subnets = compute_subnets_from_custody_group::<E>(custody_group, spec)?;
subnets.extend(custody_group_subnets);
}
@@ -95,11 +146,11 @@ pub fn compute_subnets_for_node(
}
/// Returns the subnets that are associated with a given custody group.
pub fn compute_subnets_from_custody_group(
pub fn compute_subnets_from_custody_group<E: EthSpec>(
custody_group: CustodyIndex,
spec: &ChainSpec,
) -> Result<impl Iterator<Item = DataColumnSubnetId> + '_, DataColumnCustodyGroupError> {
let result = compute_columns_for_custody_group(custody_group, spec)?
let result = compute_columns_for_custody_group::<E>(custody_group, spec)?
.map(|column_index| DataColumnSubnetId::from_column_index(column_index, spec))
.unique();
Ok(result)
@@ -108,19 +159,23 @@ pub fn compute_subnets_from_custody_group(
#[cfg(test)]
mod test {
use super::*;
use crate::MainnetEthSpec;
type E = MainnetEthSpec;
#[test]
fn test_compute_columns_for_custody_group() {
let mut spec = ChainSpec::mainnet();
spec.number_of_custody_groups = 64;
spec.number_of_columns = 128;
let columns_per_custody_group = spec.number_of_columns / spec.number_of_custody_groups;
let columns_per_custody_group =
E::number_of_columns() / (spec.number_of_custody_groups as usize);
for custody_group in 0..spec.number_of_custody_groups {
let columns = compute_columns_for_custody_group(custody_group, &spec)
let columns = compute_columns_for_custody_group::<E>(custody_group, &spec)
.unwrap()
.collect::<Vec<_>>();
assert_eq!(columns.len(), columns_per_custody_group as usize);
assert_eq!(columns.len(), columns_per_custody_group);
}
}
@@ -128,14 +183,13 @@ mod test {
fn test_compute_subnets_from_custody_group() {
let mut spec = ChainSpec::mainnet();
spec.number_of_custody_groups = 64;
spec.number_of_columns = 256;
spec.data_column_sidecar_subnet_count = 128;
let subnets_per_custody_group =
spec.data_column_sidecar_subnet_count / spec.number_of_custody_groups;
for custody_group in 0..spec.number_of_custody_groups {
let subnets = compute_subnets_from_custody_group(custody_group, &spec)
let subnets = compute_subnets_from_custody_group::<E>(custody_group, &spec)
.unwrap()
.collect::<Vec<_>>();
assert_eq!(subnets.len(), subnets_per_custody_group as usize);

View File

@@ -1,90 +1,51 @@
use crate::beacon_block_body::{KzgCommitments, BLOB_KZG_COMMITMENTS_INDEX};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{
BeaconBlockHeader, BeaconStateError, Epoch, EthSpec, ForkName, Hash256, RuntimeVariableList,
SignedBeaconBlockHeader, Slot,
};
use std::sync::Arc;
use bls::Signature;
use derivative::Derivative;
use kzg::Error as KzgError;
use context_deserialize::context_deserialize;
use educe::Educe;
use kzg::{KzgCommitment, KzgProof};
use merkle_proof::verify_merkle_proof;
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz::{DecodeError, Encode};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use ssz_types::Error as SszError;
use ssz_types::{FixedVector, VariableList};
use std::sync::Arc;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use crate::{
block::{BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockHeader, SignedBeaconBlockHeader},
core::{Epoch, EthSpec, Hash256, Slot},
fork::ForkName,
kzg_ext::{KzgCommitments, KzgError},
state::BeaconStateError,
test_utils::TestRandom,
};
pub type ColumnIndex = u64;
pub type Cell<E> = FixedVector<u8, <E as EthSpec>::BytesPerCell>;
pub type DataColumn<E> = VariableList<Cell<E>, <E as EthSpec>::MaxBlobCommitmentsPerBlock>;
/// Identifies a set of data columns associated with a specific beacon block.
#[derive(Encode, Clone, Debug, PartialEq)]
pub struct DataColumnsByRootIdentifier {
#[derive(Encode, Decode, Clone, Debug, PartialEq, TreeHash, Deserialize)]
#[context_deserialize(ForkName)]
pub struct DataColumnsByRootIdentifier<E: EthSpec> {
pub block_root: Hash256,
pub columns: RuntimeVariableList<ColumnIndex>,
}
impl RuntimeVariableList<DataColumnsByRootIdentifier> {
pub fn from_ssz_bytes_with_nested(
bytes: &[u8],
max_len: usize,
num_columns: usize,
) -> Result<Self, DecodeError> {
if bytes.is_empty() {
return Ok(RuntimeVariableList::empty(max_len));
}
let vec = ssz::decode_list_of_variable_length_items::<Vec<u8>, Vec<Vec<u8>>>(
bytes,
Some(max_len),
)?
.into_iter()
.map(|bytes| {
let mut builder = ssz::SszDecoderBuilder::new(&bytes);
builder.register_type::<Hash256>()?;
builder.register_anonymous_variable_length_item()?;
let mut decoder = builder.build()?;
let block_root = decoder.decode_next()?;
let columns = decoder.decode_next_with(|bytes| {
RuntimeVariableList::from_ssz_bytes(bytes, num_columns)
})?;
Ok(DataColumnsByRootIdentifier {
block_root,
columns,
})
})
.collect::<Result<Vec<_>, _>>()?;
Ok(RuntimeVariableList::from_vec(vec, max_len))
}
pub columns: VariableList<ColumnIndex, E::NumberOfColumns>,
}
pub type DataColumnSidecarList<E> = Vec<Arc<DataColumnSidecar<E>>>;
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Educe)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
#[educe(PartialEq, Eq, Hash(bound(E: EthSpec)))]
#[context_deserialize(ForkName)]
pub struct DataColumnSidecar<E: EthSpec> {
#[serde(with = "serde_utils::quoted_u64")]
@@ -183,6 +144,7 @@ pub enum DataColumnSidecarError {
PreDeneb,
SszError(SszError),
BuildSidecarFailed(String),
InvalidCellProofLength { expected: usize, actual: usize },
}
impl From<ArithError> for DataColumnSidecarError {
@@ -208,45 +170,3 @@ impl From<SszError> for DataColumnSidecarError {
Self::SszError(e)
}
}
#[cfg(test)]
mod test {
use super::*;
use bls::FixedBytesExtended;
#[test]
fn round_trip_dcbroot_list() {
let max_outer = 5;
let max_inner = 10;
let data = vec![
DataColumnsByRootIdentifier {
block_root: Hash256::from_low_u64_be(10),
columns: RuntimeVariableList::<ColumnIndex>::from_vec(vec![1u64, 2, 3], max_inner),
},
DataColumnsByRootIdentifier {
block_root: Hash256::from_low_u64_be(20),
columns: RuntimeVariableList::<ColumnIndex>::from_vec(vec![4u64, 5], max_inner),
},
];
let list = RuntimeVariableList::from_vec(data.clone(), max_outer);
let ssz_bytes = list.as_ssz_bytes();
let decoded =
RuntimeVariableList::<DataColumnsByRootIdentifier>::from_ssz_bytes_with_nested(
&ssz_bytes, max_outer, max_inner,
)
.expect("should decode list of DataColumnsByRootIdentifier");
assert_eq!(decoded.len(), data.len());
for (original, decoded) in data.iter().zip(decoded.iter()) {
assert_eq!(decoded.block_root, original.block_root);
assert_eq!(
decoded.columns.iter().copied().collect::<Vec<_>>(),
original.columns.iter().copied().collect::<Vec<_>>()
);
}
}
}

View File

@@ -1,15 +1,25 @@
//! Identifies each data column subnet by an integer identifier.
use crate::data_column_sidecar::ColumnIndex;
use crate::ChainSpec;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
use std::ops::{Deref, DerefMut};
use std::{
fmt::{self, Display},
ops::{Deref, DerefMut},
};
#[derive(arbitrary::Arbitrary, Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
use safe_arith::SafeArith;
use serde::{Deserialize, Serialize};
use crate::{core::ChainSpec, data::ColumnIndex};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DataColumnSubnetId(#[serde(with = "serde_utils::quoted_u64")] u64);
impl fmt::Debug for DataColumnSubnetId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
impl DataColumnSubnetId {
pub fn new(id: u64) -> Self {
id.into()
@@ -62,15 +72,3 @@ impl From<&DataColumnSubnetId> for u64 {
val.0
}
}
#[derive(Debug)]
pub enum Error {
ArithError(ArithError),
InvalidCustodySubnetCount(u64),
}
impl From<ArithError> for Error {
fn from(e: ArithError) -> Self {
Error::ArithError(e)
}
}

View File

@@ -0,0 +1,23 @@
mod blob_sidecar;
mod data_column_custody_group;
mod data_column_sidecar;
mod data_column_subnet_id;
pub use blob_sidecar::{
BlobIdentifier, BlobSidecar, BlobSidecarError, BlobSidecarList, BlobsList, FixedBlobSidecarList,
};
pub use data_column_custody_group::{
CustodyIndex, DataColumnCustodyGroupError, compute_columns_for_custody_group,
compute_ordered_custody_column_indices, compute_subnets_for_node,
compute_subnets_from_custody_group, get_custody_groups,
};
pub use data_column_sidecar::{
Cell, ColumnIndex, DataColumn, DataColumnSidecar, DataColumnSidecarError,
DataColumnSidecarList, DataColumnsByRootIdentifier,
};
pub use data_column_subnet_id::DataColumnSubnetId;
use crate::core::EthSpec;
use ssz_types::FixedVector;
pub type Blob<E> = FixedVector<u8, <E as EthSpec>::BytesPerBlob>;

View File

@@ -1,29 +1,21 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::*;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::typenum::U33;
use ssz_types::FixedVector;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use typenum::U33;
use crate::{core::Hash256, deposit::DepositData, fork::ForkName, test_utils::TestRandom};
pub const DEPOSIT_TREE_DEPTH: usize = 32;
/// A deposit to potentially become a beacon chain validator.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct Deposit {

View File

@@ -1,25 +1,23 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::{PublicKeyBytes, SecretKey, SignatureBytes};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{ChainSpec, Hash256, SignedRoot},
deposit::DepositMessage,
fork::ForkName,
test_utils::TestRandom,
};
/// The data supplied by the user to the deposit contract.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct DepositData {

View File

@@ -1,26 +1,21 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::PublicKeyBytes;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{Hash256, SignedRoot},
fork::ForkName,
test_utils::TestRandom,
};
/// The data supplied by the user to the deposit contract.
///
/// Spec v0.12.1
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[context_deserialize(ForkName)]
pub struct DepositMessage {
pub pubkey: PublicKeyBytes,

View File

@@ -1,25 +1,16 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{ForkName, Hash256, PublicKeyBytes};
use bls::SignatureBytes;
use bls::{PublicKeyBytes, SignatureBytes};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{core::Hash256, fork::ForkName, test_utils::TestRandom};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct DepositRequest {

View File

@@ -1,10 +1,11 @@
use crate::*;
use ethereum_hashing::{hash32_concat, ZERO_HASHES};
use ethereum_hashing::{ZERO_HASHES, hash32_concat};
use fixed_bytes::FixedBytesExtended;
use int_to_bytes::int_to_bytes32;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use test_utils::TestRandom;
use crate::{core::Hash256, deposit::DEPOSIT_TREE_DEPTH, test_utils::TestRandom};
#[derive(Encode, Decode, Deserialize, Serialize, Clone, Debug, PartialEq, TestRandom)]
pub struct FinalizedExecutionBlock {

View File

@@ -0,0 +1,13 @@
mod deposit;
mod deposit_data;
mod deposit_message;
mod deposit_request;
mod deposit_tree_snapshot;
mod pending_deposit;
pub use deposit::{DEPOSIT_TREE_DEPTH, Deposit};
pub use deposit_data::DepositData;
pub use deposit_message::DepositMessage;
pub use deposit_request::DepositRequest;
pub use deposit_tree_snapshot::{DepositTreeSnapshot, FinalizedExecutionBlock};
pub use pending_deposit::PendingDeposit;

View File

@@ -1,22 +1,19 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::{PublicKeyBytes, SignatureBytes};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{Hash256, Slot},
fork::ForkName,
test_utils::TestRandom,
};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct PendingDeposit {

View File

@@ -1,23 +1,20 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::{PublicKeyBytes, SecretKey};
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{Address, ChainSpec, Domain, Hash256, SignedRoot},
execution::SignedBlsToExecutionChange,
fork::ForkName,
test_utils::TestRandom,
};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct BlsToExecutionChange {

View File

@@ -0,0 +1,123 @@
// These would usually be created by superstuct but now there's no longer a 1:1 mapping between
// the variants for ExecutionPayload and the variants for
// - ExecutionPayloadHeader
// - FullPayload
// - BlindedPayload
// TODO(EIP-7732): get rid of this whole file and panics once the engine_api is refactored for ePBS
#[macro_export]
macro_rules! map_execution_payload_into_full_payload {
($value:expr, $f:expr) => {
match $value {
ExecutionPayload::Bellatrix(inner) => {
let f: fn(ExecutionPayloadBellatrix<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Bellatrix)
}
ExecutionPayload::Capella(inner) => {
let f: fn(ExecutionPayloadCapella<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Capella)
}
ExecutionPayload::Deneb(inner) => {
let f: fn(ExecutionPayloadDeneb<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Deneb)
}
ExecutionPayload::Electra(inner) => {
let f: fn(ExecutionPayloadElectra<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Electra)
}
ExecutionPayload::Fulu(inner) => {
let f: fn(ExecutionPayloadFulu<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Fulu)
}
ExecutionPayload::Eip7805(inner) => {
let f: fn(ExecutionPayloadEip7805<_>, fn(_) -> _) -> _ = $f;
f(inner, FullPayload::Eip7805)
}
ExecutionPayload::Gloas(_) => panic!("FullPayload::Gloas does not exist!"),
}
};
}
#[macro_export]
macro_rules! map_execution_payload_into_blinded_payload {
($value:expr, $f:expr) => {
match $value {
ExecutionPayload::Bellatrix(inner) => {
let f: fn(ExecutionPayloadBellatrix<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Bellatrix)
}
ExecutionPayload::Capella(inner) => {
let f: fn(ExecutionPayloadCapella<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Capella)
}
ExecutionPayload::Deneb(inner) => {
let f: fn(ExecutionPayloadDeneb<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Deneb)
}
ExecutionPayload::Electra(inner) => {
let f: fn(ExecutionPayloadElectra<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Electra)
}
ExecutionPayload::Fulu(inner) => {
let f: fn(ExecutionPayloadFulu<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Fulu)
}
ExecutionPayload::Eip7805(inner) => {
let f: fn(ExecutionPayloadEip7805<_>, fn(_) -> _) -> _ = $f;
f(inner, BlindedPayload::Eip7805)
}
ExecutionPayload::Gloas(_) => panic!("BlindedPayload::Gloas does not exist!"),
}
};
}
#[macro_export]
macro_rules! map_execution_payload_ref_into_execution_payload_header {
(&$lifetime:tt _, $value:expr, $f:expr) => {
match $value {
ExecutionPayloadRef::Bellatrix(inner) => {
let f: fn(
&$lifetime ExecutionPayloadBellatrix<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Bellatrix)
}
ExecutionPayloadRef::Capella(inner) => {
let f: fn(
&$lifetime ExecutionPayloadCapella<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Capella)
}
ExecutionPayloadRef::Deneb(inner) => {
let f: fn(
&$lifetime ExecutionPayloadDeneb<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Deneb)
}
ExecutionPayloadRef::Electra(inner) => {
let f: fn(
&$lifetime ExecutionPayloadElectra<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Electra)
}
ExecutionPayloadRef::Fulu(inner) => {
let f: fn(
&$lifetime ExecutionPayloadFulu<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Fulu)
}
ExecutionPayloadRef::Eip7805(inner) => {
let f: fn(
&$lifetime ExecutionPayloadEip7805<_>,
fn(_) -> _,
) -> _ = $f;
f(inner, ExecutionPayloadHeader::Eip7805)
}
ExecutionPayloadRef::Gloas(_) => panic!("ExecutionPayloadHeader::Gloas does not exist!"),
}
}
}

View File

@@ -1,17 +1,16 @@
use super::Hash256;
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::ForkName;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{core::Hash256, fork::ForkName, test_utils::TestRandom};
/// Contains data obtained from the Eth1 chain.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Clone,

View File

@@ -1,28 +1,23 @@
use crate::test_utils::TestRandom;
use crate::FixedBytesExtended;
use crate::Hash256;
use derivative::Derivative;
use std::fmt;
use fixed_bytes::FixedBytesExtended;
use rand::RngCore;
use serde::{Deserialize, Serialize};
use ssz::{Decode, DecodeError, Encode};
use std::fmt;
#[derive(
arbitrary::Arbitrary,
Default,
Clone,
Copy,
Serialize,
Deserialize,
Eq,
PartialEq,
Hash,
Derivative,
)]
#[derivative(Debug = "transparent")]
use crate::{core::Hash256, test_utils::TestRandom};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[serde(transparent)]
pub struct ExecutionBlockHash(#[serde(with = "serde_utils::b256_hex")] pub Hash256);
impl fmt::Debug for ExecutionBlockHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
impl ExecutionBlockHash {
pub fn zero() -> Self {
Self(Hash256::zero())

View File

@@ -17,10 +17,15 @@
// 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, ExecutionPayloadRef, Hash256, Hash64, Uint256};
use alloy_rlp::RlpEncodable;
use fixed_bytes::Uint256;
use metastruct::metastruct;
use crate::{
core::{Address, EthSpec, Hash64, Hash256},
execution::ExecutionPayloadRef,
};
/// Execution block header as used for RLP encoding and Keccak hashing.
///
/// Credit to Reth for the type definition.

View File

@@ -1,21 +1,31 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use context_deserialize::{ContextDeserialize, context_deserialize};
use educe::Educe;
use fixed_bytes::Uint256;
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use ssz_types::{FixedVector, VariableList};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{Address, EthSpec, Hash256},
execution::ExecutionBlockHash,
fork::{ForkName, ForkVersionDecode},
state::BeaconStateError,
test_utils::TestRandom,
withdrawal::Withdrawals,
};
pub type Transaction<N> = VariableList<u8, N>;
pub type Transactions<E> = VariableList<
Transaction<<E as EthSpec>::MaxBytesPerTransaction>,
<E as EthSpec>::MaxTransactionsPerPayload,
>;
pub type Withdrawals<E> = VariableList<Withdrawal, <E as EthSpec>::MaxWithdrawalsPerPayload>;
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Eip7805, Fulu),
variants(Bellatrix, Capella, Deneb, Electra, Fulu, Eip7805, Gloas),
variant_attributes(
derive(
Default,
@@ -27,25 +37,34 @@ pub type Withdrawals<E> = VariableList<Withdrawal, <E as EthSpec>::MaxWithdrawal
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
Educe,
),
context_deserialize(ForkName),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec")
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
),
cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
map_into(FullPayload, BlindedPayload),
map_ref_into(ExecutionPayloadHeader)
cast_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
partial_getter_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
)
)]
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative, arbitrary::Arbitrary,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(bound = "E: EthSpec", untagged)]
#[arbitrary(bound = "E: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
#[tree_hash(enum_behaviour = "transparent")]
pub struct ExecutionPayload<E: EthSpec> {
@@ -83,12 +102,12 @@ pub struct ExecutionPayload<E: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<E>,
#[superstruct(only(Capella, Deneb, Electra, Eip7805, Fulu))]
#[superstruct(only(Capella, Deneb, Electra, Fulu, Eip7805, Gloas))]
pub withdrawals: Withdrawals<E>,
#[superstruct(only(Deneb, Electra, Eip7805, Fulu), partial_getter(copy))]
#[superstruct(only(Deneb, Electra, Fulu, Eip7805, Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub blob_gas_used: u64,
#[superstruct(only(Deneb, Electra, Eip7805, Fulu), partial_getter(copy))]
#[superstruct(only(Deneb, Electra, Fulu, Eip7805, Gloas), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
pub excess_blob_gas: u64,
}
@@ -118,6 +137,7 @@ impl<E: EthSpec> ForkVersionDecode for ExecutionPayload<E> {
ForkName::Electra => ExecutionPayloadElectra::from_ssz_bytes(bytes).map(Self::Electra),
ForkName::Eip7805 => ExecutionPayloadEip7805::from_ssz_bytes(bytes).map(Self::Eip7805),
ForkName::Fulu => ExecutionPayloadFulu::from_ssz_bytes(bytes).map(Self::Fulu),
ForkName::Gloas => ExecutionPayloadGloas::from_ssz_bytes(bytes).map(Self::Gloas),
}
}
}
@@ -125,6 +145,7 @@ impl<E: EthSpec> ForkVersionDecode for ExecutionPayload<E> {
impl<E: EthSpec> ExecutionPayload<E> {
#[allow(clippy::arithmetic_side_effects)]
/// Returns the maximum size of an execution payload.
/// TODO(EIP-7732): this seems to only exist for the Bellatrix fork, but Mark's branch has it for all the forks, i.e. max_execution_payload_eip7732_size
pub fn max_execution_payload_bellatrix_size() -> usize {
// Fixed part
ExecutionPayloadBellatrix::<E>::default().as_ssz_bytes().len()
@@ -148,7 +169,7 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayload<E>
return Err(serde::de::Error::custom(format!(
"ExecutionPayload failed to deserialize: unsupported fork '{}'",
context
)))
)));
}
ForkName::Bellatrix => {
Self::Bellatrix(Deserialize::deserialize(deserializer).map_err(convert_err)?)
@@ -168,6 +189,9 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayload<E>
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Gloas => {
Self::Gloas(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}
}
@@ -181,6 +205,7 @@ impl<E: EthSpec> ExecutionPayload<E> {
ExecutionPayload::Electra(_) => ForkName::Electra,
ExecutionPayload::Eip7805(_) => ForkName::Eip7805,
ExecutionPayload::Fulu(_) => ForkName::Fulu,
ExecutionPayload::Gloas(_) => ForkName::Gloas,
}
}
}

View File

@@ -0,0 +1,40 @@
use crate::test_utils::TestRandom;
use crate::{Address, ExecutionBlockHash, ForkName, Hash256, SignedRoot, Slot};
use context_deserialize::context_deserialize;
use educe::Educe;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(
Default, Debug, Clone, Serialize, Encode, Decode, Deserialize, TreeHash, Educe, TestRandom,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[educe(PartialEq, Hash)]
#[context_deserialize(ForkName)]
// https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#executionpayloadbid
pub struct ExecutionPayloadBid {
pub parent_block_hash: ExecutionBlockHash,
pub parent_block_root: Hash256,
pub block_hash: ExecutionBlockHash,
#[serde(with = "serde_utils::address_hex")]
pub fee_recipient: Address,
#[serde(with = "serde_utils::quoted_u64")]
pub gas_limit: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub builder_index: u64,
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]
pub value: u64,
pub blob_kzg_commitments_root: Hash256,
}
impl SignedRoot for ExecutionPayloadBid {}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(ExecutionPayloadBid);
}

View File

@@ -0,0 +1,36 @@
use crate::test_utils::TestRandom;
use crate::{
EthSpec, ExecutionPayloadGloas, ExecutionRequests, ForkName, Hash256, KzgCommitments,
SignedRoot, Slot,
};
use context_deserialize::context_deserialize;
use educe::Educe;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(Debug, Clone, Serialize, Encode, Decode, Deserialize, TestRandom, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[context_deserialize(ForkName)]
#[serde(bound = "E: EthSpec")]
pub struct ExecutionPayloadEnvelope<E: EthSpec> {
pub payload: ExecutionPayloadGloas<E>,
pub execution_requests: ExecutionRequests<E>,
#[serde(with = "serde_utils::quoted_u64")]
pub builder_index: u64,
pub beacon_block_root: Hash256,
pub slot: Slot,
pub blob_kzg_commitments: KzgCommitments<E>,
pub state_root: Hash256,
}
impl<E: EthSpec> SignedRoot for ExecutionPayloadEnvelope<E> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
ssz_and_tree_hash_tests!(ExecutionPayloadEnvelope<MainnetEthSpec>);
}

View File

@@ -1,12 +1,28 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use context_deserialize::{ContextDeserialize, context_deserialize};
use educe::Educe;
use fixed_bytes::FixedBytesExtended;
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use ssz_types::{FixedVector, VariableList};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use crate::{
core::{Address, EthSpec, Hash256, Uint256},
execution::{
ExecutionBlockHash, ExecutionPayloadBellatrix, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadEip7805, ExecutionPayloadElectra,
ExecutionPayloadFulu, ExecutionPayloadRef, Transactions,
},
fork::ForkName,
map_execution_payload_ref_into_execution_payload_header,
state::BeaconStateError,
test_utils::TestRandom,
};
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Eip7805, Fulu),
variant_attributes(
@@ -20,28 +36,39 @@ use tree_hash_derive::TreeHash;
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary
Educe,
),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
context_deserialize(ForkName),
),
ref_attributes(
derive(PartialEq, TreeHash, Debug),
tree_hash(enum_behaviour = "transparent")
),
cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
cast_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
partial_getter_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
map_ref_into(ExecutionPayloadHeader)
)]
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative, arbitrary::Arbitrary,
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(bound = "E: EthSpec", untagged)]
#[arbitrary(bound = "E: EthSpec")]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
pub struct ExecutionPayloadHeader<E: EthSpec> {
@@ -113,13 +140,19 @@ impl<E: EthSpec> ExecutionPayloadHeader<E> {
ExecutionPayloadHeaderEip7805::from_ssz_bytes(bytes).map(Self::Eip7805)
}
ForkName::Fulu => ExecutionPayloadHeaderFulu::from_ssz_bytes(bytes).map(Self::Fulu),
ForkName::Gloas => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadHeader: {fork_name}",
))),
}
}
#[allow(clippy::arithmetic_side_effects)]
pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize {
// TODO(newfork): Add a new case here if there are new variable fields
if fork_name.bellatrix_enabled() {
if fork_name.gloas_enabled() {
// TODO(EIP7732): check this
0
} else if fork_name.bellatrix_enabled() {
// Max size of variable length `extra_data` field
E::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len()
} else {
@@ -218,7 +251,7 @@ impl<E: EthSpec> ExecutionPayloadHeaderDeneb<E> {
}
}
impl<E: EthSpec> ExecutionPayloadHeaderElectra<E> {
impl<E: EthSpec> ExecutionPayloadHeaderFulu<E> {
pub fn upgrade_to_eip7805(&self) -> ExecutionPayloadHeaderEip7805<E> {
ExecutionPayloadHeaderEip7805 {
parent_hash: self.parent_hash,
@@ -242,7 +275,7 @@ impl<E: EthSpec> ExecutionPayloadHeaderElectra<E> {
}
}
impl<E: EthSpec> ExecutionPayloadHeaderEip7805<E> {
impl<E: EthSpec> ExecutionPayloadHeaderElectra<E> {
pub fn upgrade_to_fulu(&self) -> ExecutionPayloadHeaderFulu<E> {
ExecutionPayloadHeaderFulu {
parent_hash: self.parent_hash,
@@ -558,12 +591,6 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadHead
))
};
Ok(match context {
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"ExecutionPayloadHeader failed to deserialize: unsupported fork '{}'",
context
)))
}
ForkName::Bellatrix => {
Self::Bellatrix(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
@@ -582,6 +609,13 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadHead
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Base | ForkName::Altair | ForkName::Gloas => {
return Err(serde::de::Error::custom(format!(
"ExecutionPayloadHeader failed to deserialize: unsupported fork '{}'",
context
)));
}
})
}
}

View File

@@ -1,8 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{ConsolidationRequest, DepositRequest, EthSpec, ForkName, Hash256, WithdrawalRequest};
use alloy_primitives::Bytes;
use derivative::Derivative;
use context_deserialize::context_deserialize;
use educe::Educe;
use ethereum_hashing::{DynamicContext, Sha256Context};
use serde::{Deserialize, Serialize};
use ssz::Encode;
@@ -11,6 +9,15 @@ use ssz_types::VariableList;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
consolidation::ConsolidationRequest,
core::{EthSpec, Hash256},
deposit::DepositRequest,
fork::ForkName,
test_utils::TestRandom,
withdrawal::WithdrawalRequest,
};
pub type DepositRequests<E> =
VariableList<DepositRequest, <E as EthSpec>::MaxDepositRequestsPerPayload>;
pub type WithdrawalRequests<E> =
@@ -18,22 +25,16 @@ pub type WithdrawalRequests<E> =
pub type ConsolidationRequests<E> =
VariableList<ConsolidationRequest, <E as EthSpec>::MaxConsolidationRequestsPerPayload>;
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(
arbitrary::Arbitrary,
Debug,
Derivative,
Default,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, Educe, Default, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
#[educe(PartialEq, Eq, Hash(bound(E: EthSpec)))]
#[context_deserialize(ForkName)]
pub struct ExecutionRequests<E: EthSpec> {
pub deposits: DepositRequests<E>,

View File

@@ -0,0 +1,54 @@
use crate::test_utils::TestRandom;
use crate::{EthSpec, Hash256, SignedRoot, Slot, Transactions};
use bls::{PublicKeyBytes, Signature};
use educe::Educe;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::FixedVector;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
pub type InclusionListCommittee<E> = FixedVector<u64, <E as EthSpec>::InclusionListCommitteeSize>;
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Educe, Decode, TreeHash, TestRandom)]
#[serde(bound = "E: EthSpec")]
#[educe(PartialEq, Eq, Hash(bound(E: EthSpec)))]
pub struct InclusionList<E: EthSpec> {
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]
pub validator_index: u64,
pub inclusion_list_committee_root: Hash256,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<E>,
}
impl<E: EthSpec> SignedRoot for InclusionList<E> {}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, Educe)]
#[serde(bound = "E: EthSpec")]
#[educe(PartialEq, Eq, Hash(bound(E: EthSpec)))]
pub struct SignedInclusionList<E: EthSpec> {
pub message: InclusionList<E>,
pub signature: Signature,
}
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct InclusionListDuty {
/// The slot during which the validator must produce an inclusion list.
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]
/// The index of the validator.
pub validator_index: u64,
/// The hash tree root of the inclusion list committee.
pub committee_root: Hash256,
/// The pubkey of the validator.
pub pubkey: PublicKeyBytes,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
ssz_and_tree_hash_tests!(InclusionList<MainnetEthSpec>);
}

View File

@@ -0,0 +1,49 @@
mod eth1_data;
mod execution_block_hash;
mod execution_block_header;
#[macro_use]
mod execution_payload;
mod bls_to_execution_change;
mod dumb_macros;
mod execution_payload_bid;
mod execution_payload_envelope;
mod execution_payload_header;
mod execution_requests;
mod inclusion_list;
mod payload;
mod signed_bls_to_execution_change;
mod signed_execution_payload_bid;
mod signed_execution_payload_envelope;
pub use bls_to_execution_change::BlsToExecutionChange;
pub use eth1_data::Eth1Data;
pub use execution_block_hash::ExecutionBlockHash;
pub use execution_block_header::{EncodableExecutionBlockHeader, ExecutionBlockHeader};
pub use execution_payload::{
ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
ExecutionPayloadEip7805, ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas,
ExecutionPayloadRef, Transaction, Transactions,
};
pub use execution_payload_bid::ExecutionPayloadBid;
pub use execution_payload_envelope::ExecutionPayloadEnvelope;
pub use execution_payload_header::{
ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderEip7805, ExecutionPayloadHeaderElectra,
ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
};
pub use execution_requests::{
ConsolidationRequests, DepositRequests, ExecutionRequests, RequestType, WithdrawalRequests,
};
pub use inclusion_list::{
InclusionList, InclusionListCommittee, InclusionListDuty, SignedInclusionList,
};
pub use payload::{
AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella,
BlindedPayloadDeneb, BlindedPayloadEip7805, BlindedPayloadElectra, BlindedPayloadFulu,
BlindedPayloadRef, BlockProductionVersion, BlockType, ExecPayload, FullPayload,
FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb, FullPayloadEip7805,
FullPayloadElectra, FullPayloadFulu, FullPayloadRef, OwnedExecPayload,
};
pub use signed_bls_to_execution_change::SignedBlsToExecutionChange;
pub use signed_execution_payload_bid::SignedExecutionPayloadBid;
pub use signed_execution_payload_envelope::SignedExecutionPayloadEnvelope;

View File

@@ -1,16 +1,31 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use educe::Educe;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use std::borrow::Cow;
use std::fmt::Debug;
use std::hash::Hash;
use ssz_types::VariableList;
use std::{borrow::Cow, fmt::Debug, hash::Hash};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use crate::{
core::{Address, EthSpec, Hash256},
execution::{
ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadEip7805, ExecutionPayloadElectra,
ExecutionPayloadFulu, ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix,
ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderEip7805,
ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, ExecutionPayloadRef,
Transactions,
},
fork::ForkName,
map_execution_payload_into_blinded_payload, map_execution_payload_into_full_payload,
state::BeaconStateError,
test_utils::TestRandom,
};
#[derive(Debug, PartialEq)]
pub enum BlockType {
Blinded,
@@ -38,8 +53,8 @@ pub trait ExecPayload<E: EthSpec>: Debug + Clone + PartialEq + Hash + TreeHash +
fn gas_limit(&self) -> u64;
fn transactions(&self) -> Option<&Transactions<E>>;
/// fork-specific fields
fn withdrawals_root(&self) -> Result<Hash256, Error>;
fn blob_gas_used(&self) -> Result<u64, Error>;
fn withdrawals_root(&self) -> Result<Hash256, BeaconStateError>;
fn blob_gas_used(&self) -> Result<u64, BeaconStateError>;
/// Is this a default payload with 0x0 roots for transactions and withdrawals?
fn is_default_with_zero_roots(&self) -> bool;
@@ -49,6 +64,7 @@ pub trait ExecPayload<E: EthSpec>: Debug + Clone + PartialEq + Hash + TreeHash +
}
/// `ExecPayload` functionality the requires ownership.
#[cfg(feature = "arbitrary")]
pub trait OwnedExecPayload<E: EthSpec>:
ExecPayload<E>
+ Default
@@ -61,7 +77,7 @@ pub trait OwnedExecPayload<E: EthSpec>:
+ 'static
{
}
#[cfg(feature = "arbitrary")]
impl<E: EthSpec, P> OwnedExecPayload<E> for P where
P: ExecPayload<E>
+ Default
@@ -75,6 +91,25 @@ impl<E: EthSpec, P> OwnedExecPayload<E> for P where
{
}
/// `ExecPayload` functionality the requires ownership.
#[cfg(not(feature = "arbitrary"))]
pub trait OwnedExecPayload<E: EthSpec>:
ExecPayload<E> + Default + Serialize + DeserializeOwned + Encode + Decode + TestRandom + 'static
{
}
#[cfg(not(feature = "arbitrary"))]
impl<E: EthSpec, P> OwnedExecPayload<E> for P where
P: ExecPayload<E>
+ Default
+ Serialize
+ DeserializeOwned
+ Encode
+ Decode
+ TestRandom
+ 'static
{
}
pub trait AbstractExecPayload<E: EthSpec>:
ExecPayload<E>
+ Sized
@@ -84,8 +119,8 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ TryInto<Self::Capella>
+ TryInto<Self::Deneb>
+ TryInto<Self::Electra>
+ TryInto<Self::Eip7805>
+ TryInto<Self::Fulu>
+ TryInto<Self::Eip7805>
+ Sync
{
type Ref<'a>: ExecPayload<E>
@@ -94,8 +129,8 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ From<&'a Self::Capella>
+ From<&'a Self::Deneb>
+ From<&'a Self::Electra>
+ From<&'a Self::Eip7805>
+ From<&'a Self::Fulu>;
+ From<&'a Self::Fulu>
+ From<&'a Self::Eip7805>;
type Bellatrix: OwnedExecPayload<E>
+ Into<Self>
@@ -117,16 +152,16 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ for<'a> From<Cow<'a, ExecutionPayloadElectra<E>>>
+ TryFrom<ExecutionPayloadHeaderElectra<E>>
+ Sync;
type Eip7805: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadEip7805<E>>>
+ TryFrom<ExecutionPayloadHeaderEip7805<E>>
+ Sync;
type Fulu: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadFulu<E>>>
+ TryFrom<ExecutionPayloadHeaderFulu<E>>
+ Sync;
type Eip7805: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadEip7805<E>>>
+ TryFrom<ExecutionPayloadHeaderEip7805<E>>
+ Sync;
}
#[superstruct(
@@ -141,28 +176,41 @@ pub trait AbstractExecPayload<E: EthSpec>:
Decode,
TestRandom,
TreeHash,
Derivative,
arbitrary::Arbitrary,
Educe,
),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
ssz(struct_behaviour = "transparent"),
),
ref_attributes(
derive(Debug, Derivative, TreeHash),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
derive(Debug, Educe, TreeHash),
educe(PartialEq, Hash(bound(E: EthSpec))),
tree_hash(enum_behaviour = "transparent"),
),
map_into(ExecutionPayload),
map_ref_into(ExecutionPayloadRef),
cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant")
cast_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
partial_getter_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
)
)]
#[derive(Debug, Clone, Serialize, Deserialize, TreeHash, Derivative, arbitrary::Arbitrary)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, Deserialize, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[tree_hash(enum_behaviour = "transparent")]
pub struct FullPayload<E: EthSpec> {
#[superstruct(
@@ -284,36 +332,26 @@ impl<E: EthSpec> ExecPayload<E> for FullPayload<E> {
})
}
fn withdrawals_root(&self) -> Result<Hash256, Error> {
fn withdrawals_root(&self) -> Result<Hash256, BeaconStateError> {
match self {
FullPayload::Bellatrix(_) => Err(Error::IncorrectStateVariant),
FullPayload::Capella(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayload::Deneb(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayload::Electra(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayload::Eip7805(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayload::Fulu(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayload::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant),
FullPayload::Capella(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Deneb(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Electra(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Fulu(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
FullPayload::Eip7805(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()),
}
}
fn blob_gas_used(&self) -> Result<u64, Error> {
fn blob_gas_used(&self) -> Result<u64, BeaconStateError> {
match self {
FullPayload::Bellatrix(_) | FullPayload::Capella(_) => {
Err(Error::IncorrectStateVariant)
Err(BeaconStateError::IncorrectStateVariant)
}
FullPayload::Deneb(ref inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Electra(ref inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Eip7805(ref inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Fulu(ref inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Electra(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Fulu(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayload::Eip7805(inner) => Ok(inner.execution_payload.blob_gas_used),
}
}
@@ -337,15 +375,16 @@ impl<E: EthSpec> FullPayload<E> {
})
}
pub fn default_at_fork(fork_name: ForkName) -> Result<Self, Error> {
pub fn default_at_fork(fork_name: ForkName) -> Result<Self, BeaconStateError> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(Error::IncorrectStateVariant),
ForkName::Base | ForkName::Altair => Err(BeaconStateError::IncorrectStateVariant),
ForkName::Bellatrix => Ok(FullPayloadBellatrix::default().into()),
ForkName::Capella => Ok(FullPayloadCapella::default().into()),
ForkName::Deneb => Ok(FullPayloadDeneb::default().into()),
ForkName::Electra => Ok(FullPayloadElectra::default().into()),
ForkName::Eip7805 => Ok(FullPayloadEip7805::default().into()),
ForkName::Fulu => Ok(FullPayloadFulu::default().into()),
ForkName::Gloas => Err(BeaconStateError::IncorrectStateVariant),
}
}
}
@@ -433,9 +472,9 @@ impl<E: EthSpec> ExecPayload<E> for FullPayloadRef<'_, E> {
})
}
fn withdrawals_root(&self) -> Result<Hash256, Error> {
fn withdrawals_root(&self) -> Result<Hash256, BeaconStateError> {
match self {
FullPayloadRef::Bellatrix(_) => Err(Error::IncorrectStateVariant),
FullPayloadRef::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant),
FullPayloadRef::Capella(inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
@@ -452,10 +491,10 @@ impl<E: EthSpec> ExecPayload<E> for FullPayloadRef<'_, E> {
}
}
fn blob_gas_used(&self) -> Result<u64, Error> {
fn blob_gas_used(&self) -> Result<u64, BeaconStateError> {
match self {
FullPayloadRef::Bellatrix(_) | FullPayloadRef::Capella(_) => {
Err(Error::IncorrectStateVariant)
Err(BeaconStateError::IncorrectStateVariant)
}
FullPayloadRef::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used),
FullPayloadRef::Electra(inner) => Ok(inner.execution_payload.blob_gas_used),
@@ -514,27 +553,40 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for FullPayload<E> {
Decode,
TestRandom,
TreeHash,
Derivative,
arbitrary::Arbitrary
Educe,
),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec"),
),
ssz(struct_behaviour = "transparent"),
),
ref_attributes(
derive(Debug, Derivative, TreeHash),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
derive(Debug, Educe, TreeHash),
educe(PartialEq, Hash(bound(E: EthSpec))),
tree_hash(enum_behaviour = "transparent"),
),
map_into(ExecutionPayloadHeader),
cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant")
cast_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
),
partial_getter_error(
ty = "BeaconStateError",
expr = "BeaconStateError::IncorrectStateVariant"
)
)]
#[derive(Debug, Clone, Serialize, Deserialize, TreeHash, Derivative, arbitrary::Arbitrary)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, Deserialize, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[tree_hash(enum_behaviour = "transparent")]
pub struct BlindedPayload<E: EthSpec> {
#[superstruct(
@@ -634,32 +686,26 @@ impl<E: EthSpec> ExecPayload<E> for BlindedPayload<E> {
None
}
fn withdrawals_root(&self) -> Result<Hash256, Error> {
fn withdrawals_root(&self) -> Result<Hash256, BeaconStateError> {
match self {
BlindedPayload::Bellatrix(_) => Err(Error::IncorrectStateVariant),
BlindedPayload::Capella(ref inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayload::Deneb(ref inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Electra(ref inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayload::Eip7805(ref inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayload::Fulu(ref inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant),
BlindedPayload::Capella(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.withdrawals_root),
BlindedPayload::Eip7805(inner) => Ok(inner.execution_payload_header.withdrawals_root),
}
}
fn blob_gas_used(&self) -> Result<u64, Error> {
fn blob_gas_used(&self) -> Result<u64, BeaconStateError> {
match self {
BlindedPayload::Bellatrix(_) | BlindedPayload::Capella(_) => {
Err(Error::IncorrectStateVariant)
Err(BeaconStateError::IncorrectStateVariant)
}
BlindedPayload::Deneb(ref inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Electra(ref inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Eip7805(ref inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Fulu(ref inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Fulu(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayload::Eip7805(inner) => Ok(inner.execution_payload_header.blob_gas_used),
}
}
@@ -748,9 +794,9 @@ impl<'b, E: EthSpec> ExecPayload<E> for BlindedPayloadRef<'b, E> {
None
}
fn withdrawals_root(&self) -> Result<Hash256, Error> {
fn withdrawals_root(&self) -> Result<Hash256, BeaconStateError> {
match self {
BlindedPayloadRef::Bellatrix(_) => Err(Error::IncorrectStateVariant),
BlindedPayloadRef::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant),
BlindedPayloadRef::Capella(inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
@@ -765,10 +811,10 @@ impl<'b, E: EthSpec> ExecPayload<E> for BlindedPayloadRef<'b, E> {
}
}
fn blob_gas_used(&self) -> Result<u64, Error> {
fn blob_gas_used(&self) -> Result<u64, BeaconStateError> {
match self {
BlindedPayloadRef::Bellatrix(_) | BlindedPayloadRef::Capella(_) => {
Err(Error::IncorrectStateVariant)
Err(BeaconStateError::IncorrectStateVariant)
}
BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used),
BlindedPayloadRef::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used),
@@ -861,12 +907,12 @@ macro_rules! impl_exec_payload_common {
f(self)
}
fn withdrawals_root(&self) -> Result<Hash256, Error> {
fn withdrawals_root(&self) -> Result<Hash256, BeaconStateError> {
let g = $g;
g(self)
}
fn blob_gas_used(&self) -> Result<u64, Error> {
fn blob_gas_used(&self) -> Result<u64, BeaconStateError> {
let h = $h;
h(self)
}
@@ -901,15 +947,16 @@ macro_rules! impl_exec_payload_for_fork {
},
{ |_| { None } },
{
let c: for<'a> fn(&'a $wrapper_type_header<E>) -> Result<Hash256, Error> =
|payload: &$wrapper_type_header<E>| {
let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload);
wrapper_ref_type.withdrawals_root()
};
let c: for<'a> fn(
&'a $wrapper_type_header<E>,
) -> Result<Hash256, BeaconStateError> = |payload: &$wrapper_type_header<E>| {
let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload);
wrapper_ref_type.withdrawals_root()
};
c
},
{
let c: for<'a> fn(&'a $wrapper_type_header<E>) -> Result<u64, Error> =
let c: for<'a> fn(&'a $wrapper_type_header<E>) -> Result<u64, BeaconStateError> =
|payload: &$wrapper_type_header<E>| {
let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload);
wrapper_ref_type.blob_gas_used()
@@ -919,12 +966,12 @@ macro_rules! impl_exec_payload_for_fork {
);
impl<E: EthSpec> TryInto<$wrapper_type_header<E>> for BlindedPayload<E> {
type Error = Error;
type Error = BeaconStateError;
fn try_into(self) -> Result<$wrapper_type_header<E>, Self::Error> {
match self {
BlindedPayload::$fork_variant(payload) => Ok(payload),
_ => Err(Error::IncorrectStateVariant),
_ => Err(BeaconStateError::IncorrectStateVariant),
}
}
}
@@ -947,13 +994,13 @@ macro_rules! impl_exec_payload_for_fork {
}
impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for $wrapper_type_header<E> {
type Error = Error;
type Error = BeaconStateError;
fn try_from(header: ExecutionPayloadHeader<E>) -> Result<Self, Self::Error> {
match header {
ExecutionPayloadHeader::$fork_variant(execution_payload_header) => {
Ok(execution_payload_header.into())
}
_ => Err(Error::PayloadConversionLogicFlaw),
_ => Err(BeaconStateError::PayloadConversionLogicFlaw),
}
}
}
@@ -988,7 +1035,7 @@ macro_rules! impl_exec_payload_for_fork {
c
},
{
let c: for<'a> fn(&'a $wrapper_type_full<E>) -> Result<Hash256, Error> =
let c: for<'a> fn(&'a $wrapper_type_full<E>) -> Result<Hash256, BeaconStateError> =
|payload: &$wrapper_type_full<E>| {
let wrapper_ref_type = FullPayloadRef::$fork_variant(&payload);
wrapper_ref_type.withdrawals_root()
@@ -996,7 +1043,7 @@ macro_rules! impl_exec_payload_for_fork {
c
},
{
let c: for<'a> fn(&'a $wrapper_type_full<E>) -> Result<u64, Error> =
let c: for<'a> fn(&'a $wrapper_type_full<E>) -> Result<u64, BeaconStateError> =
|payload: &$wrapper_type_full<E>| {
let wrapper_ref_type = FullPayloadRef::$fork_variant(&payload);
wrapper_ref_type.blob_gas_used()
@@ -1023,26 +1070,26 @@ macro_rules! impl_exec_payload_for_fork {
}
impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for $wrapper_type_full<E> {
type Error = Error;
type Error = BeaconStateError;
fn try_from(_: ExecutionPayloadHeader<E>) -> Result<Self, Self::Error> {
Err(Error::PayloadConversionLogicFlaw)
Err(BeaconStateError::PayloadConversionLogicFlaw)
}
}
impl<E: EthSpec> TryFrom<$wrapped_type_header<E>> for $wrapper_type_full<E> {
type Error = Error;
type Error = BeaconStateError;
fn try_from(_: $wrapped_type_header<E>) -> Result<Self, Self::Error> {
Err(Error::PayloadConversionLogicFlaw)
Err(BeaconStateError::PayloadConversionLogicFlaw)
}
}
impl<E: EthSpec> TryInto<$wrapper_type_full<E>> for FullPayload<E> {
type Error = Error;
type Error = BeaconStateError;
fn try_into(self) -> Result<$wrapper_type_full<E>, Self::Error> {
match self {
FullPayload::$fork_variant(payload) => Ok(payload),
_ => Err(Error::PayloadConversionLogicFlaw),
_ => Err(BeaconStateError::PayloadConversionLogicFlaw),
}
}
}

View File

@@ -1,23 +1,15 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::Signature;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{execution::BlsToExecutionChange, fork::ForkName, test_utils::TestRandom};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SignedBlsToExecutionChange {

View File

@@ -0,0 +1,35 @@
use crate::test_utils::TestRandom;
use crate::{ExecutionPayloadBid, ForkName};
use bls::Signature;
use context_deserialize::context_deserialize;
use educe::Educe;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(TestRandom, TreeHash, Debug, Clone, Encode, Decode, Serialize, Deserialize, Educe)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[educe(PartialEq, Hash)]
#[context_deserialize(ForkName)]
// https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid
pub struct SignedExecutionPayloadBid {
pub message: ExecutionPayloadBid,
pub signature: Signature,
}
impl SignedExecutionPayloadBid {
pub fn empty() -> Self {
Self {
message: ExecutionPayloadBid::default(),
signature: Signature::empty(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(SignedExecutionPayloadBid);
}

View File

@@ -0,0 +1,24 @@
use crate::test_utils::TestRandom;
use crate::{EthSpec, ExecutionPayloadEnvelope};
use bls::Signature;
use educe::Educe;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(Debug, Clone, Serialize, Encode, Decode, Deserialize, TestRandom, TreeHash, Educe)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(bound = "E: EthSpec")]
pub struct SignedExecutionPayloadEnvelope<E: EthSpec> {
pub message: ExecutionPayloadEnvelope<E>,
pub signature: Signature,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MainnetEthSpec;
ssz_and_tree_hash_tests!(SignedExecutionPayloadEnvelope<MainnetEthSpec>);
}

View File

@@ -0,0 +1,5 @@
mod signed_voluntary_exit;
mod voluntary_exit;
pub use signed_voluntary_exit::SignedVoluntaryExit;
pub use voluntary_exit::VoluntaryExit;

View File

@@ -1,27 +1,18 @@
use crate::context_deserialize;
use crate::{test_utils::TestRandom, ForkName, VoluntaryExit};
use bls::Signature;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{exit::VoluntaryExit, fork::ForkName, test_utils::TestRandom};
/// An exit voluntarily submitted a validator who wishes to withdraw.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SignedVoluntaryExit {

View File

@@ -1,29 +1,23 @@
use crate::context_deserialize;
use crate::{
test_utils::TestRandom, ChainSpec, Domain, Epoch, ForkName, Hash256, SecretKey, SignedRoot,
SignedVoluntaryExit,
};
use bls::SecretKey;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{ChainSpec, Domain, Epoch, Hash256, SignedRoot},
exit::SignedVoluntaryExit,
fork::ForkName,
test_utils::TestRandom,
};
/// An exit voluntarily submitted a validator who wishes to withdraw.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
PartialEq,
Hash,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct VoluntaryExit {

View File

@@ -1,17 +1,16 @@
use crate::test_utils::TestRandom;
use crate::{Epoch, ForkName};
use context_deserialize_derive::context_deserialize;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{core::Epoch, fork::ForkName, test_utils::TestRandom};
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
Copy,

View File

@@ -0,0 +1,285 @@
use std::collections::BTreeMap;
use parking_lot::RwLock;
use crate::{
core::{ChainSpec, Epoch, EthSpec, Hash256, Slot},
fork::ForkName,
};
/// Represents a hard fork in the consensus protocol.
///
/// A hard fork can be one of two types:
/// * A named fork (represented by `ForkName`) which introduces protocol changes.
/// * A blob-parameter-only (BPO) fork which only modifies blob parameters.
///
/// For BPO forks, the `fork_name` remains unchanged from the previous fork,
/// but the `fork_epoch` and `fork_digest` will be different to reflect the
/// new blob parameter changes.
#[derive(Debug, Clone)]
pub struct HardFork {
fork_name: ForkName,
fork_epoch: Epoch,
fork_digest: [u8; 4],
}
impl HardFork {
pub fn new(fork_name: ForkName, fork_digest: [u8; 4], fork_epoch: Epoch) -> HardFork {
HardFork {
fork_name,
fork_epoch,
fork_digest,
}
}
}
/// Provides fork specific info like the current fork name and the fork digests corresponding to every valid fork.
#[derive(Debug)]
pub struct ForkContext {
current_fork: RwLock<HardFork>,
epoch_to_forks: BTreeMap<Epoch, HardFork>,
pub spec: ChainSpec,
}
impl ForkContext {
/// Creates a new `ForkContext` object by enumerating all enabled forks and computing their
/// fork digest.
///
/// A fork is disabled in the `ChainSpec` if the activation slot corresponding to that fork is `None`.
pub fn new<E: EthSpec>(
current_slot: Slot,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> Self {
let epoch_to_forks: BTreeMap<_, _> = spec
.all_digest_epochs()
.map(|epoch| {
let fork_name = spec.fork_name_at_epoch(epoch);
let fork_digest = spec.compute_fork_digest(genesis_validators_root, epoch);
(epoch, HardFork::new(fork_name, fork_digest, epoch))
})
.collect();
let current_epoch = current_slot.epoch(E::slots_per_epoch());
let current_fork = epoch_to_forks
.values()
.rfind(|&fork| fork.fork_epoch <= current_epoch)
.cloned()
.expect("should match at least genesis epoch");
Self {
current_fork: RwLock::new(current_fork),
epoch_to_forks,
spec: spec.clone(),
}
}
/// Returns `true` if the provided `fork_name` exists in the `ForkContext` object.
pub fn fork_exists(&self, fork_name: ForkName) -> bool {
self.spec.fork_epoch(fork_name).is_some()
}
/// Returns the current fork name.
pub fn current_fork_name(&self) -> ForkName {
self.current_fork.read().fork_name
}
/// Returns the current fork epoch.
pub fn current_fork_epoch(&self) -> Epoch {
self.current_fork.read().fork_epoch
}
/// Returns the current fork digest.
pub fn current_fork_digest(&self) -> [u8; 4] {
self.current_fork.read().fork_digest
}
/// Returns the next fork digest. If there's no future fork, returns the current fork digest.
pub fn next_fork_digest(&self) -> Option<[u8; 4]> {
let current_fork_epoch = self.current_fork_epoch();
self.epoch_to_forks
.range(current_fork_epoch..)
.nth(1)
.map(|(_, fork)| fork.fork_digest)
}
/// Updates the `digest_epoch` field to a new digest epoch.
pub fn update_current_fork(
&self,
new_fork_name: ForkName,
new_fork_digest: [u8; 4],
new_fork_epoch: Epoch,
) {
debug_assert!(self.epoch_to_forks.contains_key(&new_fork_epoch));
*self.current_fork.write() = HardFork::new(new_fork_name, new_fork_digest, new_fork_epoch);
}
/// Returns the context bytes/fork_digest corresponding to the genesis fork version.
pub fn genesis_context_bytes(&self) -> [u8; 4] {
self.epoch_to_forks
.first_key_value()
.expect("must contain genesis epoch")
.1
.fork_digest
}
/// Returns the fork type given the context bytes/fork_digest.
/// Returns `None` if context bytes doesn't correspond to any valid `ForkName`.
pub fn get_fork_from_context_bytes(&self, context: [u8; 4]) -> Option<&ForkName> {
self.epoch_to_forks
.values()
.find(|fork| fork.fork_digest == context)
.map(|fork| &fork.fork_name)
}
/// Returns the context bytes/fork_digest corresponding to an epoch.
/// See [`ChainSpec::compute_fork_digest`]
pub fn context_bytes(&self, epoch: Epoch) -> [u8; 4] {
self.epoch_to_forks
.range(..=epoch)
.next_back()
.expect("should match at least genesis epoch")
.1
.fork_digest
}
/// Returns all `fork_digest`s that are currently in the `ForkContext` object.
pub fn all_fork_digests(&self) -> Vec<[u8; 4]> {
self.epoch_to_forks
.values()
.map(|fork| fork.fork_digest)
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::{BlobParameters, BlobSchedule, MainnetEthSpec};
type E = MainnetEthSpec;
fn make_chain_spec() -> ChainSpec {
let blob_parameters = vec![
BlobParameters {
epoch: Epoch::new(6),
max_blobs_per_block: 12,
},
BlobParameters {
epoch: Epoch::new(50),
max_blobs_per_block: 24,
},
BlobParameters {
epoch: Epoch::new(100),
max_blobs_per_block: 48,
},
];
let mut spec = E::default_spec();
spec.altair_fork_epoch = Some(Epoch::new(1));
spec.bellatrix_fork_epoch = Some(Epoch::new(2));
spec.capella_fork_epoch = Some(Epoch::new(3));
spec.deneb_fork_epoch = Some(Epoch::new(4));
spec.electra_fork_epoch = Some(Epoch::new(5));
spec.fulu_fork_epoch = Some(Epoch::new(6));
spec.gloas_fork_epoch = Some(Epoch::new(7));
spec.blob_schedule = BlobSchedule::new(blob_parameters);
spec
}
#[test]
fn test_fork_exists() {
let spec = make_chain_spec();
let genesis_root = Hash256::ZERO;
let current_slot = Slot::new(7);
let context = ForkContext::new::<E>(current_slot, genesis_root, &spec);
assert!(context.fork_exists(ForkName::Electra));
assert!(context.fork_exists(ForkName::Fulu));
assert!(context.fork_exists(ForkName::Gloas));
}
#[test]
fn test_current_fork_name_and_epoch() {
let spec = make_chain_spec();
let electra_epoch = spec.electra_fork_epoch.unwrap();
let electra_slot = electra_epoch.end_slot(E::slots_per_epoch());
let genesis_root = Hash256::ZERO;
let context = ForkContext::new::<E>(electra_slot, genesis_root, &spec);
assert_eq!(context.current_fork_name(), ForkName::Electra);
assert_eq!(context.current_fork_epoch(), electra_epoch);
}
#[test]
fn test_next_fork_digest() {
let spec = make_chain_spec();
let electra_epoch = spec.electra_fork_epoch.unwrap();
let electra_slot = electra_epoch.end_slot(E::slots_per_epoch());
let genesis_root = Hash256::ZERO;
let context = ForkContext::new::<E>(electra_slot, genesis_root, &spec);
let next_digest = context.next_fork_digest().unwrap();
let expected_digest = spec.compute_fork_digest(genesis_root, spec.fulu_fork_epoch.unwrap());
assert_eq!(next_digest, expected_digest);
}
#[test]
fn test_get_fork_from_context_bytes() {
let spec = make_chain_spec();
let genesis_root = Hash256::ZERO;
let current_slot = Slot::new(0);
let context = ForkContext::new::<E>(current_slot, genesis_root, &spec);
let electra_digest = spec.compute_fork_digest(genesis_root, Epoch::new(5));
assert_eq!(
context.get_fork_from_context_bytes(electra_digest),
Some(&ForkName::Electra)
);
let invalid_digest = [9, 9, 9, 9];
assert!(
context
.get_fork_from_context_bytes(invalid_digest)
.is_none()
);
}
#[test]
fn test_context_bytes() {
let spec = make_chain_spec();
let genesis_root = Hash256::ZERO;
let current_slot = Slot::new(0);
let context = ForkContext::new::<E>(current_slot, genesis_root, &spec);
assert_eq!(
context.context_bytes(Epoch::new(0)),
spec.compute_fork_digest(genesis_root, Epoch::new(0))
);
assert_eq!(
context.context_bytes(Epoch::new(12)),
spec.compute_fork_digest(genesis_root, Epoch::new(10))
);
}
#[test]
fn test_all_fork_digests() {
let spec = make_chain_spec();
let genesis_root = Hash256::ZERO;
let current_slot = Slot::new(20);
let context = ForkContext::new::<MainnetEthSpec>(current_slot, genesis_root, &spec);
// Get all enabled fork digests
let fork_digests = context.all_fork_digests();
let expected_digest_count = spec.all_digest_epochs().count();
assert_eq!(fork_digests.len(), expected_digest_count);
}
}

View File

@@ -1,27 +1,21 @@
use crate::test_utils::TestRandom;
use crate::{ForkName, Hash256, SignedRoot};
use context_deserialize_derive::context_deserialize;
use context_deserialize::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
use crate::{
core::{Hash256, SignedRoot},
fork::ForkName,
test_utils::TestRandom,
};
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Default,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct ForkData {

View File

@@ -0,0 +1,64 @@
/// Map a fork name into a fork-versioned superstruct type like `BeaconBlock`.
///
/// The `$body` expression is where the magic happens. The macro allows us to achieve polymorphism
/// in the return type, which is not usually possible in Rust without trait objects.
///
/// E.g. you could call `map_fork_name!(fork, BeaconBlock, serde_json::from_str(s))` to decode
/// different `BeaconBlock` variants depending on the value of `fork`. Note how the type of the body
/// will change between `BeaconBlockBase` and `BeaconBlockAltair` depending on which branch is
/// taken, the important thing is that they are re-unified by injecting them back into the
/// `BeaconBlock` parent enum.
///
/// If you would also like to extract additional data alongside the superstruct type, use
/// the more flexible `map_fork_name_with` macro.
#[macro_export]
macro_rules! map_fork_name {
($fork_name:expr, $t:tt, $body:expr) => {
$crate::map_fork_name_with!($fork_name, $t, { ($body, ()) }).0
};
}
/// Map a fork name into a tuple of `(t, extra)` where `t` is a superstruct type.
#[macro_export]
macro_rules! map_fork_name_with {
($fork_name:expr, $t:tt, $body:block) => {
match $fork_name {
$crate::fork::ForkName::Base => {
let (value, extra_data) = $body;
($t::Base(value), extra_data)
}
$crate::fork::ForkName::Altair => {
let (value, extra_data) = $body;
($t::Altair(value), extra_data)
}
$crate::fork::ForkName::Bellatrix => {
let (value, extra_data) = $body;
($t::Bellatrix(value), extra_data)
}
$crate::fork::ForkName::Capella => {
let (value, extra_data) = $body;
($t::Capella(value), extra_data)
}
$crate::fork::ForkName::Deneb => {
let (value, extra_data) = $body;
($t::Deneb(value), extra_data)
}
$crate::fork::ForkName::Electra => {
let (value, extra_data) = $body;
($t::Electra(value), extra_data)
}
$crate::fork::ForkName::Fulu => {
let (value, extra_data) = $body;
($t::Fulu(value), extra_data)
}
$crate::fork::ForkName::Eip7805 => {
let (value, extra_data) = $body;
($t::Eip7805(value), extra_data)
}
$crate::fork::ForkName::Gloas => {
let (value, extra_data) = $body;
($t::Gloas(value), extra_data)
}
}
};
}

View File

@@ -1,8 +1,12 @@
use crate::{ChainSpec, Epoch};
use std::{
fmt::{self, Display, Formatter},
str::FromStr,
};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use crate::core::{ChainSpec, Epoch};
#[derive(
Debug, Clone, Copy, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
@@ -17,8 +21,9 @@ pub enum ForkName {
Capella,
Deneb,
Electra,
Eip7805,
Fulu,
Eip7805,
Gloas,
}
impl ForkName {
@@ -30,16 +35,15 @@ impl ForkName {
ForkName::Capella,
ForkName::Deneb,
ForkName::Electra,
ForkName::Eip7805,
ForkName::Fulu,
ForkName::Eip7805,
ForkName::Gloas,
]
}
pub fn list_all_fork_epochs(spec: &ChainSpec) -> Vec<(ForkName, Option<Epoch>)> {
ForkName::list_all()
.into_iter()
// Skip Base
.skip(1)
.map(|fork| (fork, spec.fork_epoch(fork)))
.collect()
}
@@ -53,7 +57,7 @@ impl ForkName {
/// This fork serves as the baseline for many tests, and the goal
/// is to ensure features are passing on this fork.
pub fn latest_stable() -> ForkName {
ForkName::Electra
ForkName::Fulu
}
/// Set the activation slots in the given `ChainSpec` so that the fork named by `self`
@@ -67,8 +71,9 @@ impl ForkName {
spec.capella_fork_epoch = None;
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.fulu_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.gloas_fork_epoch = None;
spec
}
ForkName::Altair => {
@@ -77,8 +82,9 @@ impl ForkName {
spec.capella_fork_epoch = None;
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.fulu_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.gloas_fork_epoch = None;
spec
}
ForkName::Bellatrix => {
@@ -87,8 +93,9 @@ impl ForkName {
spec.capella_fork_epoch = None;
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.fulu_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.gloas_fork_epoch = None;
spec
}
ForkName::Capella => {
@@ -97,8 +104,9 @@ impl ForkName {
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = None;
spec.electra_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.fulu_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.gloas_fork_epoch = None;
spec
}
ForkName::Deneb => {
@@ -107,8 +115,9 @@ impl ForkName {
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.fulu_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.gloas_fork_epoch = None;
spec
}
ForkName::Electra => {
@@ -117,18 +126,8 @@ impl ForkName {
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = Some(Epoch::new(0));
spec.fulu_fork_epoch = None;
spec.eip7805_fork_epoch = None;
spec.fulu_fork_epoch = None;
spec
}
ForkName::Eip7805 => {
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = Some(Epoch::new(0));
spec.eip7805_fork_epoch = Some(Epoch::new(0));
spec.fulu_fork_epoch = None;
spec
}
ForkName::Fulu => {
@@ -137,8 +136,30 @@ impl ForkName {
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = Some(Epoch::new(0));
spec.eip7805_fork_epoch = Some(Epoch::new(0));
spec.fulu_fork_epoch = Some(Epoch::new(0));
spec.eip7805_fork_epoch = None;
spec.gloas_fork_epoch = None;
spec
}
ForkName::Eip7805 => {
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = Some(Epoch::new(0));
spec.fulu_fork_epoch = Some(Epoch::new(0));
spec.gloas_fork_epoch = None;
spec
}
ForkName::Gloas => {
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = Some(Epoch::new(0));
spec.fulu_fork_epoch = Some(Epoch::new(0));
spec.eip7805_fork_epoch = Some(Epoch::new(0));
spec.gloas_fork_epoch = Some(Epoch::new(0));
spec
}
}
@@ -146,7 +167,7 @@ impl ForkName {
/// Return the name of the fork immediately prior to the current one.
///
/// If `self` is `ForkName::Base` then `Base` is returned.
/// If `self` is `ForkName::Base` then `None` is returned.
pub fn previous_fork(self) -> Option<ForkName> {
match self {
ForkName::Base => None,
@@ -155,8 +176,9 @@ impl ForkName {
ForkName::Capella => Some(ForkName::Bellatrix),
ForkName::Deneb => Some(ForkName::Capella),
ForkName::Electra => Some(ForkName::Deneb),
ForkName::Eip7805 => Some(ForkName::Electra),
ForkName::Fulu => Some(ForkName::Eip7805),
ForkName::Fulu => Some(ForkName::Electra),
ForkName::Eip7805 => Some(ForkName::Fulu),
ForkName::Gloas => Some(ForkName::Eip7805),
}
}
@@ -170,9 +192,10 @@ impl ForkName {
ForkName::Bellatrix => Some(ForkName::Capella),
ForkName::Capella => Some(ForkName::Deneb),
ForkName::Deneb => Some(ForkName::Electra),
ForkName::Electra => Some(ForkName::Eip7805),
ForkName::Eip7805 => Some(ForkName::Fulu),
ForkName::Fulu => None,
ForkName::Electra => Some(ForkName::Fulu),
ForkName::Fulu => Some(ForkName::Eip7805),
ForkName::Eip7805 => Some(ForkName::Gloas),
ForkName::Gloas => None,
}
}
@@ -196,74 +219,57 @@ impl ForkName {
self >= ForkName::Electra
}
pub fn fulu_enabled(self) -> bool {
self >= ForkName::Fulu
}
pub fn eip7805_enabled(self) -> bool {
self >= ForkName::Eip7805
}
pub fn fulu_enabled(self) -> bool {
self >= ForkName::Fulu
pub fn gloas_enabled(self) -> bool {
self >= ForkName::Gloas
}
}
/// Map a fork name into a fork-versioned superstruct type like `BeaconBlock`.
///
/// The `$body` expression is where the magic happens. The macro allows us to achieve polymorphism
/// in the return type, which is not usually possible in Rust without trait objects.
///
/// E.g. you could call `map_fork_name!(fork, BeaconBlock, serde_json::from_str(s))` to decode
/// different `BeaconBlock` variants depending on the value of `fork`. Note how the type of the body
/// will change between `BeaconBlockBase` and `BeaconBlockAltair` depending on which branch is
/// taken, the important thing is that they are re-unified by injecting them back into the
/// `BeaconBlock` parent enum.
///
/// If you would also like to extract additional data alongside the superstruct type, use
/// the more flexible `map_fork_name_with` macro.
#[macro_export]
macro_rules! map_fork_name {
($fork_name:expr, $t:tt, $body:expr) => {
map_fork_name_with!($fork_name, $t, { ($body, ()) }).0
};
}
/// Map a fork name into a tuple of `(t, extra)` where `t` is a superstruct type.
#[macro_export]
macro_rules! map_fork_name_with {
($fork_name:expr, $t:tt, $body:block) => {
match $fork_name {
ForkName::Base => {
let (value, extra_data) = $body;
($t::Base(value), extra_data)
}
ForkName::Altair => {
let (value, extra_data) = $body;
($t::Altair(value), extra_data)
}
ForkName::Bellatrix => {
let (value, extra_data) = $body;
($t::Bellatrix(value), extra_data)
}
ForkName::Capella => {
let (value, extra_data) = $body;
($t::Capella(value), extra_data)
}
ForkName::Deneb => {
let (value, extra_data) = $body;
($t::Deneb(value), extra_data)
}
ForkName::Electra => {
let (value, extra_data) = $body;
($t::Electra(value), extra_data)
}
ForkName::Eip7805 => {
let (value, extra_data) = $body;
($t::Eip7805(value), extra_data)
}
ForkName::Fulu => {
let (value, extra_data) = $body;
($t::Fulu(value), extra_data)
}
pub fn fork_ascii(self) {
if self == ForkName::Fulu {
println!(
r#"
TO FULU, MOAR BLOBS TO ETHEREUM
III DECEMBER MMXXV
=============================================================================
|||| ||||
|---------------------------------------------------------------------------|
|___-----___-----___-----___-----___-----___-----___-----___-----___-----___|
/ _ \===/ _ \ / _ \===/ _ \ / _ \===/ _ \ / _ \===/ _ \
( (.\ oOo /.) ) ( (.\ oOo /.) ) ( (.\ oOo /.) ) ( (.\ oOo /.) )
\__/=====\__/ \__/=====\__/ \__/=====\__/ \__/=====\__/
||||||| ||||||| ||||||| |||||||
||||||| ||||||| \\/), ||||||| |||||||
||||||| ||||||| ,'.' /, ||||||| |||||||
||||||| ||||||| (_)- / /, ||||||| |||||||
||||||| ||||||| /\_/ |__..--, * ||||||| |||||||
||||||| ||||||| (\___/\ \ \ / ).' ||||||| |||||||
||||||| ||||||| \____/ / (_ // ||||||| |||||||
||||||| ||||||| \\_ ,'--'\_( ||||||| |||||||
(oOoOo) (oOoOo) )_)_/ )_/ )_) (oOoOo) (oOoOo)
J%%%%%L J%%%%%L (_(_.'(_.'(_.' J%%%%%L J%%%%%L
ZZZZZZZZZ ZZZZZZZZZ ZZZZZZZZZ ZZZZZZZZZ
===========================================================================
|_________________________________________________________________________|
|___________________________________________________________________________|
|_____________________________________________________________________________|
|_______________________________________________________________________________|
"#
);
}
};
}
}
impl FromStr for ForkName {
@@ -279,6 +285,7 @@ impl FromStr for ForkName {
"electra" => ForkName::Electra,
"eip7805" => ForkName::Eip7805,
"fulu" => ForkName::Fulu,
"gloas" => ForkName::Gloas,
_ => return Err(format!("unknown fork name: {}", fork_name)),
})
}
@@ -295,6 +302,7 @@ impl Display for ForkName {
ForkName::Electra => "electra".fmt(f),
ForkName::Eip7805 => "eip7805".fmt(f),
ForkName::Fulu => "fulu".fmt(f),
ForkName::Gloas => "gloas".fmt(f),
}
}
}

View File

@@ -0,0 +1,6 @@
use crate::fork::ForkName;
pub trait ForkVersionDecode: Sized {
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError>;
}

View File

@@ -0,0 +1,15 @@
mod fork;
mod fork_context;
mod fork_data;
mod fork_macros;
mod fork_name;
mod fork_version_decode;
pub use crate::{map_fork_name, map_fork_name_with};
pub use fork::Fork;
pub use fork_context::{ForkContext, HardFork};
pub use fork_data::ForkData;
pub use fork_name::{ForkName, InconsistentFork};
pub use fork_version_decode::ForkVersionDecode;
pub type ForkVersion = [u8; 4];

View File

@@ -1,95 +0,0 @@
use parking_lot::RwLock;
use crate::{ChainSpec, EthSpec, ForkName, Hash256, Slot};
use std::collections::HashMap;
/// Provides fork specific info like the current fork name and the fork digests corresponding to every valid fork.
#[derive(Debug)]
pub struct ForkContext {
current_fork: RwLock<ForkName>,
fork_to_digest: HashMap<ForkName, [u8; 4]>,
digest_to_fork: HashMap<[u8; 4], ForkName>,
pub spec: ChainSpec,
}
impl ForkContext {
/// Creates a new `ForkContext` object by enumerating all enabled forks and computing their
/// fork digest.
///
/// A fork is disabled in the `ChainSpec` if the activation slot corresponding to that fork is `None`.
pub fn new<E: EthSpec>(
current_slot: Slot,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> Self {
let fork_to_digest: HashMap<ForkName, [u8; 4]> = ForkName::list_all()
.into_iter()
.filter_map(|fork| {
if spec.fork_epoch(fork).is_some() {
Some((
fork,
ChainSpec::compute_fork_digest(
spec.fork_version_for_name(fork),
genesis_validators_root,
),
))
} else {
None
}
})
.collect();
let digest_to_fork = fork_to_digest
.clone()
.into_iter()
.map(|(k, v)| (v, k))
.collect();
Self {
current_fork: RwLock::new(spec.fork_name_at_slot::<E>(current_slot)),
fork_to_digest,
digest_to_fork,
spec: spec.clone(),
}
}
/// Returns `true` if the provided `fork_name` exists in the `ForkContext` object.
pub fn fork_exists(&self, fork_name: ForkName) -> bool {
self.fork_to_digest.contains_key(&fork_name)
}
/// Returns the `current_fork`.
pub fn current_fork(&self) -> ForkName {
*self.current_fork.read()
}
/// Updates the `current_fork` field to a new fork.
pub fn update_current_fork(&self, new_fork: ForkName) {
*self.current_fork.write() = new_fork;
}
/// Returns the context bytes/fork_digest corresponding to the genesis fork version.
pub fn genesis_context_bytes(&self) -> [u8; 4] {
*self
.fork_to_digest
.get(&ForkName::Base)
.expect("ForkContext must contain genesis context bytes")
}
/// Returns the fork type given the context bytes/fork_digest.
/// Returns `None` if context bytes doesn't correspond to any valid `ForkName`.
pub fn from_context_bytes(&self, context: [u8; 4]) -> Option<&ForkName> {
self.digest_to_fork.get(&context)
}
/// Returns the context bytes/fork_digest corresponding to a fork name.
/// Returns `None` if the `ForkName` has not been initialized.
pub fn to_context_bytes(&self, fork_name: ForkName) -> Option<[u8; 4]> {
self.fork_to_digest.get(&fork_name).cloned()
}
/// Returns all `fork_digest`s that are currently in the `ForkContext` object.
pub fn all_fork_digests(&self) -> Vec<[u8; 4]> {
self.digest_to_fork.keys().cloned().collect()
}
}

View File

@@ -1,53 +0,0 @@
use crate::test_utils::TestRandom;
use crate::{EthSpec, Hash256, Signature, SignedRoot, Slot, Transactions};
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
Derivative,
arbitrary::Arbitrary,
)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
pub struct InclusionList<E: EthSpec> {
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]
pub validator_index: u64,
pub inclusion_list_committee_root: Hash256,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<E>,
}
impl<E: EthSpec> SignedRoot for InclusionList<E> {}
#[derive(
Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, Derivative, arbitrary::Arbitrary,
)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
pub struct SignedInclusionList<E: EthSpec> {
pub message: InclusionList<E>,
pub signature: Signature,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
ssz_and_tree_hash_tests!(InclusionList<MainnetEthSpec>);
}

View File

@@ -1,3 +0,0 @@
use crate::*;
pub type InclusionListCommittee<E> = FixedVector<u64, <E as EthSpec>::InclusionListCommitteeSize>;

Some files were not shown because too many files have changed in this diff Show More