merge conflicts

This commit is contained in:
Eitan Seri-Levi
2025-05-27 14:56:02 -07:00
358 changed files with 11552 additions and 6768 deletions

View File

@@ -1,8 +1,9 @@
use super::{AttestationBase, AttestationElectra, AttestationRef};
use super::{
ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, SelectionProof, Signature,
SignedRoot,
ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, PublicKey, SecretKey, SelectionProof,
Signature, SignedRoot,
};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::Attestation;
use serde::{Deserialize, Serialize};
@@ -26,6 +27,7 @@ use tree_hash_derive::TreeHash;
TestRandom,
TreeHash,
),
context_deserialize(ForkName),
serde(bound = "E: EthSpec"),
arbitrary(bound = "E: EthSpec"),
),

View File

@@ -1,10 +1,12 @@
use crate::context_deserialize;
use crate::slot_data::SlotData;
use crate::{test_utils::TestRandom, Hash256, Slot};
use crate::{Checkpoint, ForkVersionDeserialize};
use crate::{Checkpoint, ContextDeserialize, ForkName};
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::BitVector;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use superstruct::superstruct;
use test_random_derive::TestRandom;
@@ -15,7 +17,7 @@ use super::{
Signature, SignedRoot,
};
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
SszTypesError(ssz_types::Error),
BitfieldError(ssz::BitfieldError),
@@ -46,6 +48,7 @@ impl From<ssz_types::Error> for Error {
arbitrary::Arbitrary,
TreeHash,
),
context_deserialize(ForkName),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
@@ -210,6 +213,13 @@ impl<E: EthSpec> Attestation<E> {
}
}
pub fn get_committee_indices_map(&self) -> HashSet<u64> {
match self {
Attestation::Base(att) => HashSet::from([att.data.index]),
Attestation::Electra(att) => att.get_committee_indices().into_iter().collect(),
}
}
pub fn is_aggregation_bits_zero(&self) -> bool {
match self {
Attestation::Base(att) => att.aggregation_bits.is_zero(),
@@ -293,7 +303,11 @@ impl<E: EthSpec> AttestationRef<'_, E> {
impl<E: EthSpec> AttestationElectra<E> {
pub fn committee_index(&self) -> Option<u64> {
self.get_committee_indices().first().cloned()
self.committee_bits
.iter()
.enumerate()
.find(|&(_, bit)| bit)
.map(|(index, _)| index as u64)
}
pub fn get_aggregation_bits(&self) -> Vec<u64> {
@@ -520,45 +534,44 @@ impl<'a, E: EthSpec> From<AttestationRefOnDisk<'a, E>> for AttestationRef<'a, E>
}
}
impl<E: EthSpec> ForkVersionDeserialize for Attestation<E> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::Value,
fork_name: crate::ForkName,
) -> Result<Self, D::Error> {
if fork_name.electra_enabled() {
let attestation: AttestationElectra<E> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(Attestation::Electra(attestation))
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for Attestation<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if context.electra_enabled() {
AttestationElectra::<E>::deserialize(deserializer)
.map_err(serde::de::Error::custom)
.map(Attestation::Electra)
} else {
let attestation: AttestationBase<E> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(Attestation::Base(attestation))
AttestationBase::<E>::deserialize(deserializer)
.map_err(serde::de::Error::custom)
.map(Attestation::Base)
}
}
}
impl<E: EthSpec> ForkVersionDeserialize for Vec<Attestation<E>> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::Value,
fork_name: crate::ForkName,
) -> Result<Self, D::Error> {
if fork_name.electra_enabled() {
let attestations: Vec<AttestationElectra<E>> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(attestations
.into_iter()
.map(Attestation::Electra)
.collect::<Vec<_>>())
/*
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for Vec<Attestation<E>> {
fn context_deserialize<D>(
deserializer: D,
context: ForkName,
) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if context.electra_enabled() {
<Vec<AttestationElectra<E>>>::deserialize(deserializer)
.map_err(serde::de::Error::custom)
.map(|vec| vec.into_iter().map(Attestation::Electra).collect::<Vec<_>>())
} else {
let attestations: Vec<AttestationBase<E>> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(attestations
.into_iter()
.map(Attestation::Base)
.collect::<Vec<_>>())
<Vec<AttestationBase<E>>>::deserialize(deserializer)
.map_err(serde::de::Error::custom)
.map(|vec| vec.into_iter().map(Attestation::Base).collect::<Vec<_>>())
}
}
}
*/
#[derive(
Debug,
@@ -573,6 +586,7 @@ impl<E: EthSpec> ForkVersionDeserialize for Vec<Attestation<E>> {
TreeHash,
PartialEq,
)]
#[context_deserialize(ForkName)]
pub struct SingleAttestation {
#[serde(with = "serde_utils::quoted_u64")]
pub committee_index: u64,

View File

@@ -1,12 +1,11 @@
use crate::test_utils::TestRandom;
use crate::{Checkpoint, Hash256, SignedRoot, Slot};
use crate::slot_data::SlotData;
use crate::test_utils::TestRandom;
use crate::{Checkpoint, ForkName, Hash256, SignedRoot, Slot};
use context_deserialize_derive::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
/// The data upon which an attestation is based.
///
/// Spec v0.12.1
@@ -25,6 +24,7 @@ use tree_hash_derive::TreeHash;
TestRandom,
Default,
)]
#[context_deserialize(ForkName)]
pub struct AttestationData {
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]

View File

@@ -1,10 +1,12 @@
use crate::context_deserialize;
use crate::indexed_attestation::{
IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef,
};
use crate::{test_utils::TestRandom, EthSpec};
use crate::{ContextDeserialize, ForkName};
use derivative::Derivative;
use rand::{Rng, RngCore};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use test_random_derive::TestRandom;
@@ -25,6 +27,7 @@ use tree_hash_derive::TreeHash;
TestRandom,
arbitrary::Arbitrary
),
context_deserialize(ForkName),
derivative(PartialEq, Eq, Hash(bound = "E: EthSpec")),
serde(bound = "E: EthSpec"),
arbitrary(bound = "E: EthSpec")
@@ -171,25 +174,27 @@ impl<E: EthSpec> TestRandom for AttesterSlashing<E> {
}
}
impl<E: EthSpec> crate::ForkVersionDeserialize for Vec<AttesterSlashing<E>> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::Value,
fork_name: crate::ForkName,
) -> Result<Self, D::Error> {
if fork_name.electra_enabled() {
let slashings: Vec<AttesterSlashingElectra<E>> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(slashings
.into_iter()
.map(AttesterSlashing::Electra)
.collect::<Vec<_>>())
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for Vec<AttesterSlashing<E>> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if context.electra_enabled() {
<Vec<AttesterSlashingElectra<E>>>::deserialize(deserializer)
.map_err(serde::de::Error::custom)
.map(|vec| {
vec.into_iter()
.map(AttesterSlashing::Electra)
.collect::<Vec<_>>()
})
} else {
let slashings: Vec<AttesterSlashingBase<E>> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(slashings
.into_iter()
.map(AttesterSlashing::Base)
.collect::<Vec<_>>())
<Vec<AttesterSlashingBase<E>>>::deserialize(deserializer)
.map_err(serde::de::Error::custom)
.map(|vec| {
vec.into_iter()
.map(AttesterSlashing::Base)
.collect::<Vec<_>>()
})
}
}
}

View File

@@ -2,7 +2,7 @@ use crate::attestation::AttestationBase;
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{Decode, DecodeError};
use ssz_derive::{Decode, Encode};
use std::fmt;
@@ -802,23 +802,21 @@ impl<E: EthSpec> From<BeaconBlock<E, FullPayload<E>>>
}
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> ForkVersionDeserialize
impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> ContextDeserialize<'de, ForkName>
for BeaconBlock<E, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(map_fork_name!(
fork_name,
context,
Self,
serde_json::from_value(value).map_err(|e| serde::de::Error::custom(format!(
"BeaconBlock failed to deserialize: {:?}",
e
)))?
serde::Deserialize::deserialize(deserializer)?
))
}
}
pub enum BlockImportSource {
Gossip,
Lookup,

View File

@@ -3,7 +3,7 @@ use crate::*;
use derivative::Derivative;
use merkle_proof::{MerkleTree, MerkleTreeError};
use metastruct::metastruct;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::{Decode, Encode};
use std::marker::PhantomData;
use superstruct::superstruct;
@@ -48,6 +48,7 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
deny_unknown_fields
),
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
context_deserialize(ForkName),
),
specific_variant_attributes(
Base(metastruct(mappings(beacon_block_body_base_fields(groups(fields))))),
@@ -62,10 +63,11 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
)]
#[derive(Debug, Clone, Serialize, Deserialize, Derivative, arbitrary::Arbitrary)]
#[derive(Debug, Clone, Serialize, Deserialize, Derivative, TreeHash, arbitrary::Arbitrary)]
#[derivative(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,
@@ -1088,6 +1090,21 @@ impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
}
}
impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> ContextDeserialize<'de, ForkName>
for BeaconBlockBody<E, Payload>
{
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(map_fork_name!(
context,
Self,
serde::Deserialize::deserialize(deserializer)?
))
}
}
/// 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();

View File

@@ -1,6 +1,7 @@
use crate::test_utils::TestRandom;
use crate::*;
use context_deserialize_derive::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -24,6 +25,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct BeaconBlockHeader {
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]

View File

@@ -0,0 +1,239 @@
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,6 +1,7 @@
use self::committee_cache::get_active_validator_indices;
use crate::historical_summary::HistoricalSummary;
use crate::test_utils::TestRandom;
use crate::ContextDeserialize;
use crate::FixedBytesExtended;
use crate::*;
use compare_fields::CompareFields;
@@ -11,7 +12,7 @@ use int_to_bytes::{int_to_bytes4, int_to_bytes8};
use metastruct::{metastruct, NumFields};
pub use pubkey_cache::PubkeyCache;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{ssz_encode, Decode, DecodeError, Encode};
use ssz_derive::{Decode, Encode};
use std::hash::Hash;
@@ -2896,18 +2897,15 @@ impl<E: EthSpec> CompareFields for BeaconState<E> {
}
}
impl<E: EthSpec> ForkVersionDeserialize for BeaconState<E> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for BeaconState<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(map_fork_name!(
fork_name,
context,
Self,
serde_json::from_value(value).map_err(|e| serde::de::Error::custom(format!(
"BeaconState failed to deserialize: {:?}",
e
)))?
serde::Deserialize::deserialize(deserializer)?
))
}
}

View File

@@ -1,9 +1,10 @@
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,
ForkVersionDeserialize, Hash256, KzgProofs, RuntimeFixedVector, RuntimeVariableList,
SignedBeaconBlock, SignedBeaconBlockHeader, Slot, VariableList,
BeaconStateError, Blob, ChainSpec, Epoch, EthSpec, FixedVector, ForkName, Hash256, KzgProofs,
RuntimeFixedVector, RuntimeVariableList, SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
VariableList,
};
use bls::Signature;
use derivative::Derivative;
@@ -25,6 +26,7 @@ use tree_hash_derive::TreeHash;
#[derive(
Serialize, Deserialize, Encode, Decode, TreeHash, Copy, Clone, Debug, PartialEq, Eq, Hash,
)]
#[context_deserialize(ForkName)]
pub struct BlobIdentifier {
pub block_root: Hash256,
pub index: u64,
@@ -54,6 +56,7 @@ impl Ord for BlobIdentifier {
Derivative,
arbitrary::Arbitrary,
)]
#[context_deserialize(ForkName)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
@@ -296,12 +299,3 @@ pub type BlobSidecarList<E> = RuntimeVariableList<Arc<BlobSidecar<E>>>;
/// Alias for a non length-constrained list of `BlobSidecar`s.
pub type FixedBlobSidecarList<E> = RuntimeFixedVector<Option<Arc<BlobSidecar<E>>>>;
pub type BlobsList<E> = VariableList<Blob<E>, <E as EthSpec>::MaxBlobCommitmentsPerBlock>;
impl<E: EthSpec> ForkVersionDeserialize for BlobSidecarList<E> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
_: ForkName,
) -> Result<Self, D::Error> {
serde_json::from_value::<BlobSidecarList<E>>(value).map_err(serde::de::Error::custom)
}
}

View File

@@ -19,6 +19,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct BlsToExecutionChange {
#[serde(with = "serde_utils::quoted_u64")]
pub validator_index: u64,

View File

@@ -1,9 +1,10 @@
use crate::beacon_block_body::KzgCommitments;
use crate::{
ChainSpec, EthSpec, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderEip7805, ExecutionPayloadHeaderElectra,
ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionRequests, ForkName, ForkVersionDecode, ForkVersionDeserialize, SignedRoot, Uint256,
ChainSpec, ContextDeserialize, EthSpec, ExecutionPayloadHeaderBellatrix,
ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderEip7805,
ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderRef,
ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionDecode,
SignedRoot, Uint256,
};
use bls::PublicKeyBytes;
use bls::Signature;
@@ -11,6 +12,8 @@ use serde::{Deserialize, Deserializer, Serialize};
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use crate::test_utils::TestRandom;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
#[superstruct(
@@ -24,7 +27,8 @@ use tree_hash_derive::TreeHash;
Deserialize,
TreeHash,
Decode,
Clone
Clone,
TestRandom
),
serde(bound = "E: EthSpec", deny_unknown_fields)
),
@@ -128,47 +132,61 @@ impl<E: EthSpec> ForkVersionDecode for SignedBuilderBid<E> {
}
}
impl<E: EthSpec> ForkVersionDeserialize for BuilderBid<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for BuilderBid<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err =
|e| serde::de::Error::custom(format!("BuilderBid failed to deserialize: {:?}", e));
Ok(match fork_name {
Ok(match context {
ForkName::Bellatrix => {
Self::Bellatrix(serde_json::from_value(value).map_err(convert_err)?)
Self::Bellatrix(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Eip7805 => {
Self::Eip7805(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Eip7805 => Self::Eip7805(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Fulu => Self::Fulu(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"BuilderBid failed to deserialize: unsupported fork '{}'",
fork_name
context
)));
}
})
}
}
impl<E: EthSpec> ForkVersionDeserialize for SignedBuilderBid<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for SignedBuilderBid<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
pub message: serde_json::Value,
pub signature: Signature,
message: serde_json::Value,
signature: Signature,
}
let helper: Helper = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(Self {
message: BuilderBid::deserialize_by_fork::<'de, D>(helper.message, fork_name)?,
let helper = Helper::deserialize(deserializer)?;
// Deserialize `data` using ContextDeserialize
let message = BuilderBid::<E>::context_deserialize(helper.message, context)
.map_err(serde::de::Error::custom)?;
Ok(SignedBuilderBid {
message,
signature: helper.signature,
})
}

View File

@@ -1,6 +1,6 @@
use crate::application_domain::{ApplicationDomain, APPLICATION_DOMAIN_BUILDER};
use crate::blob_sidecar::BlobIdentifier;
use crate::data_column_sidecar::DataColumnIdentifier;
use crate::data_column_sidecar::DataColumnsByRootIdentifier;
use crate::*;
use int_to_bytes::int_to_bytes4;
use safe_arith::{ArithError, SafeArith};
@@ -219,10 +219,9 @@ pub struct ChainSpec {
pub boot_nodes: Vec<String>,
pub network_id: u8,
pub target_aggregators_per_committee: u64,
pub gossip_max_size: u64,
pub max_payload_size: u64,
max_request_blocks: u64,
pub min_epochs_for_block_requests: u64,
pub max_chunk_size: u64,
pub ttfb_timeout: u64,
pub resp_timeout: u64,
pub attestation_propagation_slot_range: u64,
@@ -250,6 +249,11 @@ pub struct ChainSpec {
blob_sidecar_subnet_count_electra: u64,
max_request_blob_sidecars_electra: u64,
/*
* Networking Fulu
*/
max_blobs_per_block_fulu: u64,
/*
* Networking Derived
*
@@ -684,7 +688,9 @@ impl ChainSpec {
/// Return the value of `MAX_BLOBS_PER_BLOCK` appropriate for `fork`.
pub fn max_blobs_per_block_by_fork(&self, fork_name: ForkName) -> u64 {
if fork_name.electra_enabled() {
if fork_name.fulu_enabled() {
self.max_blobs_per_block_fulu
} else if fork_name.electra_enabled() {
self.max_blobs_per_block_electra
} else {
self.max_blobs_per_block
@@ -744,6 +750,35 @@ impl ChainSpec {
(0..self.data_column_sidecar_subnet_count).map(DataColumnSubnetId::new)
}
/// Worst-case compressed length for a given payload of size n when using snappy.
///
/// https://github.com/google/snappy/blob/32ded457c0b1fe78ceb8397632c416568d6714a0/snappy.cc#L218C1-L218C47
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md#max_compressed_len
fn max_compressed_len_snappy(n: usize) -> Option<usize> {
32_usize.checked_add(n)?.checked_add(n / 6)
}
/// Max compressed length of a message that we receive over gossip.
pub fn max_compressed_len(&self) -> usize {
Self::max_compressed_len_snappy(self.max_payload_size as usize)
.expect("should not overflow")
}
/// Max allowed size of a raw, compressed message received over the network.
///
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md#max_compressed_len
pub fn max_message_size(&self) -> usize {
std::cmp::max(
// 1024 to account for framing + encoding overhead
Self::max_compressed_len_snappy(self.max_payload_size as usize)
.expect("should not overflow")
.safe_add(1024)
.expect("should not overflow"),
//1MB
1024 * 1024,
)
}
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
pub fn mainnet() -> Self {
Self {
@@ -911,7 +946,7 @@ impl ChainSpec {
* Electra hard fork params
*/
electra_fork_version: [0x05, 00, 00, 00],
electra_fork_epoch: None,
electra_fork_epoch: Some(Epoch::new(364032)),
unset_deposit_requests_start_index: u64::MAX,
full_exit_request_amount: 0,
min_activation_balance: option_wrapper(|| {
@@ -966,9 +1001,8 @@ impl ChainSpec {
subnets_per_node: 2,
maximum_gossip_clock_disparity_millis: default_maximum_gossip_clock_disparity_millis(),
target_aggregators_per_committee: 16,
gossip_max_size: default_gossip_max_size(),
max_payload_size: default_max_payload_size(),
min_epochs_for_block_requests: default_min_epochs_for_block_requests(),
max_chunk_size: default_max_chunk_size(),
ttfb_timeout: default_ttfb_timeout(),
resp_timeout: default_resp_timeout(),
message_domain_invalid_snappy: default_message_domain_invalid_snappy(),
@@ -1001,6 +1035,11 @@ impl ChainSpec {
blob_sidecar_subnet_count_electra: default_blob_sidecar_subnet_count_electra(),
max_request_blob_sidecars_electra: default_max_request_blob_sidecars_electra(),
/*
* Networking Fulu specific
*/
max_blobs_per_block_fulu: default_max_blobs_per_block_fulu(),
/*
* Application specific
*/
@@ -1252,7 +1291,7 @@ impl ChainSpec {
* Electra hard fork params
*/
electra_fork_version: [0x05, 0x00, 0x00, 0x64],
electra_fork_epoch: None,
electra_fork_epoch: Some(Epoch::new(1337856)),
unset_deposit_requests_start_index: u64::MAX,
full_exit_request_amount: 0,
min_activation_balance: option_wrapper(|| {
@@ -1274,7 +1313,7 @@ impl ChainSpec {
})
.expect("calculation does not overflow"),
max_per_epoch_activation_exit_churn_limit: option_wrapper(|| {
u64::checked_pow(2, 8)?.checked_mul(u64::checked_pow(10, 9)?)
u64::checked_pow(2, 6)?.checked_mul(u64::checked_pow(10, 9)?)
})
.expect("calculation does not overflow"),
@@ -1307,9 +1346,8 @@ impl ChainSpec {
subnets_per_node: 4, // Make this larger than usual to avoid network damage
maximum_gossip_clock_disparity_millis: default_maximum_gossip_clock_disparity_millis(),
target_aggregators_per_committee: 16,
gossip_max_size: default_gossip_max_size(),
max_payload_size: default_max_payload_size(),
min_epochs_for_block_requests: 33024,
max_chunk_size: default_max_chunk_size(),
ttfb_timeout: default_ttfb_timeout(),
resp_timeout: default_resp_timeout(),
message_domain_invalid_snappy: default_message_domain_invalid_snappy(),
@@ -1325,7 +1363,7 @@ impl ChainSpec {
max_request_data_column_sidecars: default_max_request_data_column_sidecars(),
min_epochs_for_blob_sidecars_requests: 16384,
blob_sidecar_subnet_count: default_blob_sidecar_subnet_count(),
max_blobs_per_block: default_max_blobs_per_block(),
max_blobs_per_block: 2,
/*
* Derived Deneb Specific
@@ -1338,9 +1376,14 @@ impl ChainSpec {
/*
* Networking Electra specific
*/
max_blobs_per_block_electra: default_max_blobs_per_block_electra(),
blob_sidecar_subnet_count_electra: default_blob_sidecar_subnet_count_electra(),
max_request_blob_sidecars_electra: default_max_request_blob_sidecars_electra(),
max_blobs_per_block_electra: 2,
blob_sidecar_subnet_count_electra: 2,
max_request_blob_sidecars_electra: 256,
/*
* Networking Fulu specific
*/
max_blobs_per_block_fulu: default_max_blobs_per_block_fulu(),
/*
* Application specific
@@ -1489,18 +1532,15 @@ pub struct Config {
#[serde(with = "serde_utils::quoted_u64")]
gas_limit_adjustment_factor: u64,
#[serde(default = "default_gossip_max_size")]
#[serde(default = "default_max_payload_size")]
#[serde(with = "serde_utils::quoted_u64")]
gossip_max_size: u64,
max_payload_size: u64,
#[serde(default = "default_max_request_blocks")]
#[serde(with = "serde_utils::quoted_u64")]
max_request_blocks: u64,
#[serde(default = "default_min_epochs_for_block_requests")]
#[serde(with = "serde_utils::quoted_u64")]
min_epochs_for_block_requests: u64,
#[serde(default = "default_max_chunk_size")]
#[serde(with = "serde_utils::quoted_u64")]
max_chunk_size: u64,
#[serde(default = "default_ttfb_timeout")]
#[serde(with = "serde_utils::quoted_u64")]
ttfb_timeout: u64,
@@ -1572,6 +1612,9 @@ pub struct Config {
#[serde(default = "default_custody_requirement")]
#[serde(with = "serde_utils::quoted_u64")]
custody_requirement: u64,
#[serde(default = "default_max_blobs_per_block_fulu")]
#[serde(with = "serde_utils::quoted_u64")]
max_blobs_per_block_fulu: u64,
}
fn default_bellatrix_fork_version() -> [u8; 4] {
@@ -1640,7 +1683,7 @@ const fn default_gas_limit_adjustment_factor() -> u64 {
1024
}
const fn default_gossip_max_size() -> u64 {
const fn default_max_payload_size() -> u64 {
10485760
}
@@ -1648,10 +1691,6 @@ const fn default_min_epochs_for_block_requests() -> u64 {
33024
}
const fn default_max_chunk_size() -> u64 {
10485760
}
const fn default_ttfb_timeout() -> u64 {
5
}
@@ -1718,6 +1757,10 @@ const fn default_max_blobs_per_block_electra() -> u64 {
9
}
const fn default_max_blobs_per_block_fulu() -> u64 {
12
}
const fn default_attestation_propagation_slot_range() -> u64 {
32
}
@@ -1771,15 +1814,21 @@ fn max_blobs_by_root_request_common(max_request_blob_sidecars: u64) -> usize {
.len()
}
fn max_data_columns_by_root_request_common(max_request_data_column_sidecars: u64) -> usize {
let max_request_data_column_sidecars = max_request_data_column_sidecars as usize;
let empty_data_column_id = DataColumnIdentifier {
fn max_data_columns_by_root_request_common(
max_request_blocks: u64,
number_of_columns: u64,
) -> usize {
let max_request_blocks = max_request_blocks as usize;
let number_of_columns = number_of_columns as usize;
let empty_data_columns_by_root_id = DataColumnsByRootIdentifier {
block_root: Hash256::zero(),
index: 0,
columns: RuntimeVariableList::from_vec(vec![0; number_of_columns], number_of_columns),
};
RuntimeVariableList::from_vec(
vec![empty_data_column_id; max_request_data_column_sidecars],
max_request_data_column_sidecars,
RuntimeVariableList::<DataColumnsByRootIdentifier>::from_vec(
vec![empty_data_columns_by_root_id; max_request_blocks],
max_request_blocks,
)
.as_ssz_bytes()
.len()
@@ -1798,7 +1847,10 @@ fn default_max_blobs_by_root_request() -> usize {
}
fn default_data_columns_by_root_request() -> usize {
max_data_columns_by_root_request_common(default_max_request_data_column_sidecars())
max_data_columns_by_root_request_common(
default_max_request_blocks_deneb(),
default_number_of_columns(),
)
}
impl Default for Config {
@@ -1922,10 +1974,9 @@ impl Config {
gas_limit_adjustment_factor: spec.gas_limit_adjustment_factor,
gossip_max_size: spec.gossip_max_size,
max_payload_size: spec.max_payload_size,
max_request_blocks: spec.max_request_blocks,
min_epochs_for_block_requests: spec.min_epochs_for_block_requests,
max_chunk_size: spec.max_chunk_size,
ttfb_timeout: spec.ttfb_timeout,
resp_timeout: spec.resp_timeout,
attestation_propagation_slot_range: spec.attestation_propagation_slot_range,
@@ -1951,6 +2002,7 @@ impl Config {
data_column_sidecar_subnet_count: spec.data_column_sidecar_subnet_count,
samples_per_slot: spec.samples_per_slot,
custody_requirement: spec.custody_requirement,
max_blobs_per_block_fulu: spec.max_blobs_per_block_fulu,
}
}
@@ -2005,9 +2057,8 @@ impl Config {
deposit_network_id,
deposit_contract_address,
gas_limit_adjustment_factor,
gossip_max_size,
max_payload_size,
min_epochs_for_block_requests,
max_chunk_size,
ttfb_timeout,
resp_timeout,
message_domain_invalid_snappy,
@@ -2032,6 +2083,7 @@ impl Config {
data_column_sidecar_subnet_count,
samples_per_slot,
custody_requirement,
max_blobs_per_block_fulu,
} = self;
if preset_base != E::spec_name().to_string().as_str() {
@@ -2078,9 +2130,8 @@ impl Config {
terminal_total_difficulty,
terminal_block_hash,
terminal_block_hash_activation_epoch,
gossip_max_size,
max_payload_size,
min_epochs_for_block_requests,
max_chunk_size,
ttfb_timeout,
resp_timeout,
message_domain_invalid_snappy,
@@ -2109,7 +2160,8 @@ impl Config {
),
max_blobs_by_root_request: max_blobs_by_root_request_common(max_request_blob_sidecars),
max_data_columns_by_root_request: max_data_columns_by_root_request_common(
max_request_data_column_sidecars,
max_request_blocks_deneb,
number_of_columns,
),
number_of_columns,
@@ -2117,6 +2169,7 @@ impl Config {
data_column_sidecar_subnet_count,
samples_per_slot,
custody_requirement,
max_blobs_per_block_fulu,
..chain_spec.clone()
})
@@ -2386,9 +2439,8 @@ mod yaml_tests {
check_default!(terminal_block_hash);
check_default!(terminal_block_hash_activation_epoch);
check_default!(bellatrix_fork_version);
check_default!(gossip_max_size);
check_default!(max_payload_size);
check_default!(min_epochs_for_block_requests);
check_default!(max_chunk_size);
check_default!(ttfb_timeout);
check_default!(resp_timeout);
check_default!(message_domain_invalid_snappy);
@@ -2414,4 +2466,17 @@ mod yaml_tests {
[0, 0, 0, 1]
);
}
#[test]
fn test_max_network_limits_overflow() {
let mut spec = MainnetEthSpec::default_spec();
// Should not overflow
let _ = spec.max_message_size();
let _ = spec.max_compressed_len();
spec.max_payload_size *= 10;
// Should not overflow even with a 10x increase in max
let _ = spec.max_message_size();
let _ = spec.max_compressed_len();
}
}

View File

@@ -1,5 +1,6 @@
use crate::test_utils::TestRandom;
use crate::{Epoch, Hash256};
use crate::{Epoch, ForkName, Hash256};
use context_deserialize_derive::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -24,6 +25,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct Checkpoint {
pub epoch: Epoch,
pub root: Hash256,

View File

@@ -1,4 +1,5 @@
use crate::{test_utils::TestRandom, Address, PublicKeyBytes, SignedRoot};
use crate::context_deserialize;
use crate::{test_utils::TestRandom, Address, ForkName, PublicKeyBytes, SignedRoot};
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
@@ -19,6 +20,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct ConsolidationRequest {
pub source_address: Address,
pub source_pubkey: PublicKeyBytes,

View File

@@ -1,7 +1,8 @@
use super::{
ChainSpec, EthSpec, Fork, Hash256, SecretKey, Signature, SignedRoot, SyncCommitteeContribution,
SyncSelectionProof,
ChainSpec, EthSpec, Fork, ForkName, Hash256, SecretKey, Signature, SignedRoot,
SyncCommitteeContribution, SyncSelectionProof,
};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -23,6 +24,7 @@ use tree_hash_derive::TreeHash;
)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
pub struct ContributionAndProof<E: EthSpec> {
/// The index of the validator that created the sync contribution.
#[serde(with = "serde_utils::quoted_u64")]

View File

@@ -1,7 +1,10 @@
use crate::beacon_block_body::{KzgCommitments, BLOB_KZG_COMMITMENTS_INDEX};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::BeaconStateError;
use crate::{BeaconBlockHeader, Epoch, EthSpec, Hash256, KzgProofs, SignedBeaconBlockHeader, Slot};
use crate::{
BeaconBlockHeader, BeaconStateError, Epoch, EthSpec, ForkName, Hash256, RuntimeVariableList,
SignedBeaconBlockHeader, Slot,
};
use bls::Signature;
use derivative::Derivative;
use kzg::Error as KzgError;
@@ -9,11 +12,10 @@ use kzg::{KzgCommitment, KzgProof};
use merkle_proof::verify_merkle_proof;
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz::{DecodeError, Encode};
use ssz_derive::{Decode, Encode};
use ssz_types::Error as SszError;
use ssz_types::{FixedVector, VariableList};
use std::hash::Hash;
use std::sync::Arc;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
@@ -23,13 +25,47 @@ 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>;
/// Container of the data that identifies an individual data column.
#[derive(
Serialize, Deserialize, Encode, Decode, TreeHash, Copy, Clone, Debug, PartialEq, Eq, Hash,
)]
pub struct DataColumnIdentifier {
/// Identifies a set of data columns associated with a specific beacon block.
#[derive(Encode, Clone, Debug, PartialEq)]
pub struct DataColumnsByRootIdentifier {
pub block_root: Hash256,
pub index: ColumnIndex,
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 type DataColumnSidecarList<E> = Vec<Arc<DataColumnSidecar<E>>>;
@@ -49,6 +85,7 @@ pub type DataColumnSidecarList<E> = Vec<Arc<DataColumnSidecar<E>>>;
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
#[context_deserialize(ForkName)]
pub struct DataColumnSidecar<E: EthSpec> {
#[serde(with = "serde_utils::quoted_u64")]
pub index: ColumnIndex,
@@ -56,7 +93,7 @@ pub struct DataColumnSidecar<E: EthSpec> {
pub column: DataColumn<E>,
/// All the KZG commitments and proofs associated with the block, used for verifying sample cells.
pub kzg_commitments: KzgCommitments<E>,
pub kzg_proofs: KzgProofs<E>,
pub kzg_proofs: VariableList<KzgProof, E::MaxBlobCommitmentsPerBlock>,
pub signed_block_header: SignedBeaconBlockHeader,
/// An inclusion proof, proving the inclusion of `blob_kzg_commitments` in `BeaconBlockBody`.
pub kzg_commitments_inclusion_proof: FixedVector<Hash256, E::KzgCommitmentsInclusionProofDepth>,
@@ -132,13 +169,6 @@ impl<E: EthSpec> DataColumnSidecar<E> {
.as_ssz_bytes()
.len()
}
pub fn id(&self) -> DataColumnIdentifier {
DataColumnIdentifier {
block_root: self.block_root(),
index: self.index,
}
}
}
#[derive(Debug)]
@@ -178,3 +208,45 @@ 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,3 +1,4 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::*;
use serde::{Deserialize, Serialize};
@@ -24,6 +25,7 @@ pub const DEPOSIT_TREE_DEPTH: usize = 32;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct Deposit {
pub proof: FixedVector<Hash256, U33>,
pub data: DepositData,

View File

@@ -1,6 +1,5 @@
use crate::test_utils::TestRandom;
use crate::*;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -22,6 +21,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct DepositData {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,

View File

@@ -21,6 +21,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct DepositMessage {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{Hash256, PublicKeyBytes};
use crate::{ForkName, Hash256, PublicKeyBytes};
use bls::SignatureBytes;
use serde::{Deserialize, Serialize};
use ssz::Encode;
@@ -20,6 +21,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct DepositRequest {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,

View File

@@ -1,6 +1,7 @@
use super::Hash256;
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::ForkName;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -24,6 +25,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct Eth1Data {
pub deposit_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]

View File

@@ -4,8 +4,8 @@ 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, U4, U4096, U512, U625, U64,
U65536, U8, U8192,
U134217728, U16, U16777216, U17, U2, U2048, U256, U262144, U32, U33554432, U4, U4096, U512,
U625, U64, U65536, U8, U8192,
};
use std::fmt::{self, Debug};
use std::str::FromStr;
@@ -146,6 +146,11 @@ pub trait EthSpec:
/// Must be set to `BytesPerFieldElement * FieldElementsPerCell`.
type BytesPerCell: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/// The maximum number of cell commitments per block
///
/// FieldElementsPerExtBlob * MaxBlobCommitmentsPerBlock
type MaxCellsPerBlock: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* New in Electra
*/
@@ -437,6 +442,7 @@ impl EthSpec for MainnetEthSpec {
type FieldElementsPerExtBlob = U8192;
type BytesPerBlob = U131072;
type BytesPerCell = U2048;
type MaxCellsPerBlock = U33554432;
type KzgCommitmentInclusionProofDepth = U17;
type KzgCommitmentsInclusionProofDepth = U4; // inclusion of the whole list of commitments
type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count
@@ -492,6 +498,7 @@ impl EthSpec for MinimalEthSpec {
type MaxWithdrawalRequestsPerPayload = U2;
type FieldElementsPerCell = U64;
type FieldElementsPerExtBlob = U8192;
type MaxCellsPerBlock = U33554432;
type BytesPerCell = U2048;
type KzgCommitmentsInclusionProofDepth = U4;
@@ -586,6 +593,7 @@ impl EthSpec for GnosisEthSpec {
type MaxPendingDepositsPerEpoch = U16;
type FieldElementsPerCell = U64;
type FieldElementsPerExtBlob = U8192;
type MaxCellsPerBlock = U33554432;
type BytesPerCell = U2048;
type KzgCommitmentsInclusionProofDepth = U4;
type InclusionListCommitteeSize = U16;

View File

@@ -112,3 +112,22 @@ impl fmt::Display for ExecutionBlockHash {
write!(f, "{}", self.0)
}
}
impl From<Hash256> for ExecutionBlockHash {
fn from(hash: Hash256) -> Self {
Self(hash)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_hash256() {
let hash = Hash256::random();
let ex_hash = ExecutionBlockHash::from(hash);
assert_eq!(ExecutionBlockHash(hash), ex_hash);
}
}

View File

@@ -1,6 +1,6 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -30,6 +30,7 @@ pub type Withdrawals<E> = VariableList<Withdrawal, <E as EthSpec>::MaxWithdrawal
Derivative,
arbitrary::Arbitrary
),
context_deserialize(ForkName),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec")
@@ -134,29 +135,38 @@ impl<E: EthSpec> ExecutionPayload<E> {
}
}
impl<E: EthSpec> ForkVersionDeserialize for ExecutionPayload<E> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayload<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!("ExecutionPayload failed to deserialize: {:?}", e))
};
Ok(match fork_name {
ForkName::Bellatrix => {
Self::Bellatrix(serde_json::from_value(value).map_err(convert_err)?)
}
ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Eip7805 => Self::Eip7805(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Fulu => Self::Fulu(serde_json::from_value(value).map_err(convert_err)?),
Ok(match context {
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"ExecutionPayload failed to deserialize: unsupported fork '{}'",
fork_name
)));
context
)))
}
ForkName::Bellatrix => {
Self::Bellatrix(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Eip7805 => {
Self::Eip7805(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}

View File

@@ -1,6 +1,6 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -25,7 +25,8 @@ use tree_hash_derive::TreeHash;
),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec")
arbitrary(bound = "E: EthSpec"),
context_deserialize(ForkName),
),
ref_attributes(
derive(PartialEq, TreeHash, Debug),
@@ -545,32 +546,41 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadHeader<E>> for ExecutionPayloadHeaderFu
}
}
impl<E: EthSpec> ForkVersionDeserialize for ExecutionPayloadHeader<E> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for ExecutionPayloadHeader<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!(
"ExecutionPayloadHeader failed to deserialize: {:?}",
e
))
};
Ok(match fork_name {
ForkName::Bellatrix => {
Self::Bellatrix(serde_json::from_value(value).map_err(convert_err)?)
}
ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Eip7805 => Self::Eip7805(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Fulu => Self::Fulu(serde_json::from_value(value).map_err(convert_err)?),
Ok(match context {
ForkName::Base | ForkName::Altair => {
return Err(serde::de::Error::custom(format!(
"ExecutionPayloadHeader failed to deserialize: unsupported fork '{}'",
fork_name
)));
context
)))
}
ForkName::Bellatrix => {
Self::Bellatrix(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Eip7805 => {
Self::Eip7805(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{ConsolidationRequest, DepositRequest, EthSpec, Hash256, WithdrawalRequest};
use crate::{ConsolidationRequest, DepositRequest, EthSpec, ForkName, Hash256, WithdrawalRequest};
use alloy_primitives::Bytes;
use derivative::Derivative;
use ethereum_hashing::{DynamicContext, Sha256Context};
@@ -33,6 +34,7 @@ pub type ConsolidationRequests<E> =
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[derivative(PartialEq, Eq, Hash(bound = "E: EthSpec"))]
#[context_deserialize(ForkName)]
pub struct ExecutionRequests<E: EthSpec> {
pub deposits: DepositRequests<E>,
pub withdrawals: WithdrawalRequests<E>,

View File

@@ -1,5 +1,6 @@
use crate::test_utils::TestRandom;
use crate::Epoch;
use crate::{Epoch, ForkName};
use context_deserialize_derive::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -23,6 +24,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct Fork {
#[serde(with = "serde_utils::bytes_4_hex")]
pub previous_version: [u8; 4],

View File

@@ -1,5 +1,6 @@
use crate::test_utils::TestRandom;
use crate::{Hash256, SignedRoot};
use crate::{ForkName, Hash256, SignedRoot};
use context_deserialize_derive::context_deserialize;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -22,6 +23,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct ForkData {
#[serde(with = "serde_utils::bytes_4_hex")]
pub current_version: [u8; 4],

View File

@@ -1,152 +0,0 @@
use crate::ForkName;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::value::Value;
use std::sync::Arc;
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>;
}
pub trait ForkVersionDeserialize: Sized + DeserializeOwned {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error>;
}
/// Deserialize is only implemented for types that implement ForkVersionDeserialize.
///
/// 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> {
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<ForkName>,
#[serde(flatten)]
pub metadata: M,
pub data: T,
}
/// 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 ExecutionOptimisticFinalizedForkVersionedResponse<T> =
ForkVersionedResponse<T, ExecutionOptimisticFinalizedMetadata>;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct ExecutionOptimisticFinalizedMetadata {
pub execution_optimistic: Option<bool>,
pub finalized: Option<bool>,
}
impl<'de, F, M> serde::Deserialize<'de> for ForkVersionedResponse<F, M>
where
F: ForkVersionDeserialize,
M: DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
version: Option<ForkName>,
#[serde(flatten)]
metadata: serde_json::Value,
data: serde_json::Value,
}
let helper = Helper::deserialize(deserializer)?;
let data = match helper.version {
Some(fork_name) => F::deserialize_by_fork::<'de, D>(helper.data, fork_name)?,
None => serde_json::from_value(helper.data).map_err(serde::de::Error::custom)?,
};
let metadata = serde_json::from_value(helper.metadata).map_err(serde::de::Error::custom)?;
Ok(ForkVersionedResponse {
version: helper.version,
metadata,
data,
})
}
}
impl<F: ForkVersionDeserialize> ForkVersionDeserialize for Arc<F> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
Ok(Arc::new(F::deserialize_by_fork::<'de, D>(
value, fork_name,
)?))
}
}
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),
}
}
}
#[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: Some(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: Some(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

@@ -22,6 +22,7 @@ use tree_hash_derive::TreeHash;
arbitrary::Arbitrary,
)]
#[arbitrary(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
pub struct HistoricalBatch<E: EthSpec> {
#[test_random(default)]
pub block_roots: Vector<Hash256, E::SlotsPerHistoricalRoot>,

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{BeaconState, EthSpec, Hash256};
use crate::{BeaconState, EthSpec, ForkName, Hash256};
use compare_fields_derive::CompareFields;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -28,6 +29,7 @@ use tree_hash_derive::TreeHash;
Default,
arbitrary::Arbitrary,
)]
#[context_deserialize(ForkName)]
pub struct HistoricalSummary {
block_summary_root: Hash256,
state_summary_root: Hash256,

View File

@@ -1,4 +1,7 @@
use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec, VariableList};
use crate::context_deserialize;
use crate::{
test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec, ForkName, VariableList,
};
use core::slice::Iter;
use derivative::Derivative;
use serde::{Deserialize, Serialize};
@@ -29,6 +32,7 @@ use tree_hash_derive::TreeHash;
arbitrary::Arbitrary,
TreeHash,
),
context_deserialize(ForkName),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),

View File

@@ -22,6 +22,7 @@ pub mod beacon_block;
pub mod beacon_block_body;
pub mod beacon_block_header;
pub mod beacon_committee;
pub mod beacon_response;
pub mod beacon_state;
pub mod bls_to_execution_change;
pub mod builder_bid;
@@ -44,7 +45,6 @@ pub mod execution_payload_header;
pub mod fork;
pub mod fork_data;
pub mod fork_name;
pub mod fork_versioned_response;
pub mod graffiti;
pub mod historical_batch;
pub mod historical_summary;
@@ -141,6 +141,9 @@ pub use crate::beacon_block_body::{
};
pub use crate::beacon_block_header::BeaconBlockHeader;
pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
pub use crate::beacon_response::{
BeaconResponse, ForkVersionDecode, ForkVersionedResponse, UnversionedResponse,
};
pub use crate::beacon_state::{Error as BeaconStateError, *};
pub use crate::blob_sidecar::{BlobIdentifier, BlobSidecar, BlobSidecarList, BlobsList};
pub use crate::bls_to_execution_change::BlsToExecutionChange;
@@ -152,7 +155,7 @@ pub use crate::config_and_preset::{
pub use crate::consolidation_request::ConsolidationRequest;
pub use crate::contribution_and_proof::ContributionAndProof;
pub use crate::data_column_sidecar::{
ColumnIndex, DataColumnIdentifier, DataColumnSidecar, DataColumnSidecarList,
ColumnIndex, DataColumnSidecar, DataColumnSidecarList, DataColumnsByRootIdentifier,
};
pub use crate::data_column_subnet_id::DataColumnSubnetId;
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
@@ -181,9 +184,6 @@ pub use crate::fork::Fork;
pub use crate::fork_context::ForkContext;
pub use crate::fork_data::ForkData;
pub use crate::fork_name::{ForkName, InconsistentFork};
pub use crate::fork_versioned_response::{
ForkVersionDecode, ForkVersionDeserialize, ForkVersionedResponse,
};
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
pub use crate::historical_batch::HistoricalBatch;
pub use crate::inclusion_list::{InclusionList, InclusionListTransactions, SignedInclusionList};
@@ -280,7 +280,14 @@ pub type Address = fixed_bytes::Address;
pub type ForkVersion = [u8; 4];
pub type BLSFieldElement = Uint256;
pub type Blob<E> = FixedVector<u8, <E as EthSpec>::BytesPerBlob>;
pub type KzgProofs<E> = VariableList<KzgProof, <E as EthSpec>::MaxBlobCommitmentsPerBlock>;
// Note on List limit:
// - Deneb to Electra: `MaxBlobCommitmentsPerBlock`
// - Fulu: `MaxCellsPerBlock`
// We choose to use a single type (with the larger value from Fulu as `N`) instead of having to
// introduce a new type for Fulu. This is to avoid messy conversions and having to add extra types
// with no gains - as `N` does not impact serialisation at all, and only affects merkleization,
// which we don't current do on `KzgProofs` anyway.
pub type KzgProofs<E> = VariableList<KzgProof, <E as EthSpec>::MaxCellsPerBlock>;
pub type VersionedHash = Hash256;
pub type Hash64 = alloy_primitives::B64;
@@ -288,6 +295,8 @@ pub use bls::{
AggregatePublicKey, AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey,
Signature, SignatureBytes,
};
pub use context_deserialize::ContextDeserialize;
pub use context_deserialize_derive::context_deserialize;
pub use kzg::{KzgCommitment, KzgProof, VERSIONED_HASH_VERSION_KZG};
pub use milhouse::{self, List, Vector};
pub use ssz_types::{typenum, typenum::Unsigned, BitList, BitVector, FixedVector, VariableList};

View File

@@ -1,12 +1,12 @@
use crate::context_deserialize;
use crate::{
light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec, EthSpec, FixedVector,
ForkName, ForkVersionDeserialize, Hash256, LightClientHeader, LightClientHeaderAltair,
light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec, ContextDeserialize,
EthSpec, FixedVector, ForkName, Hash256, LightClientHeader, LightClientHeaderAltair,
LightClientHeaderCapella, LightClientHeaderDeneb, LightClientHeaderElectra,
LightClientHeaderFulu, SignedBlindedBeaconBlock, Slot, SyncCommittee,
};
use derivative::Derivative;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use std::sync::Arc;
@@ -34,6 +34,7 @@ use tree_hash_derive::TreeHash;
),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
context_deserialize(ForkName),
)
)]
#[derive(
@@ -217,20 +218,40 @@ impl<E: EthSpec> LightClientBootstrap<E> {
}
}
impl<E: EthSpec> ForkVersionDeserialize for LightClientBootstrap<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
if fork_name.altair_enabled() {
Ok(serde_json::from_value::<LightClientBootstrap<E>>(value)
.map_err(serde::de::Error::custom))?
} else {
Err(serde::de::Error::custom(format!(
"LightClientBootstrap failed to deserialize: unsupported fork '{}'",
fork_name
)))
}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientBootstrap<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!(
"LightClientBootstrap failed to deserialize: {:?}",
e
))
};
Ok(match context {
ForkName::Base => {
return Err(serde::de::Error::custom(format!(
"LightClientBootstrap failed to deserialize: unsupported fork '{}'",
context
)))
}
ForkName::Altair | ForkName::Bellatrix => {
Self::Altair(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra | ForkName::Eip7805 => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}
}

View File

@@ -1,13 +1,13 @@
use super::{EthSpec, FixedVector, Hash256, LightClientHeader, Slot, SyncAggregate};
use crate::context_deserialize;
use crate::ChainSpec;
use crate::{
light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize,
light_client_update::*, test_utils::TestRandom, ContextDeserialize, ForkName,
LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb,
LightClientHeaderElectra, LightClientHeaderFulu, SignedBlindedBeaconBlock,
};
use derivative::Derivative;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz::{Decode, Encode};
use ssz_derive::Decode;
use ssz_derive::Encode;
@@ -33,11 +33,10 @@ use tree_hash_derive::TreeHash;
),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
context_deserialize(ForkName),
)
)]
#[derive(
Debug, Clone, Serialize, Encode, TreeHash, Deserialize, arbitrary::Arbitrary, PartialEq,
)]
#[derive(Debug, Clone, Serialize, Encode, TreeHash, arbitrary::Arbitrary, PartialEq)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
@@ -237,20 +236,40 @@ impl<E: EthSpec> LightClientFinalityUpdate<E> {
}
}
impl<E: EthSpec> ForkVersionDeserialize for LightClientFinalityUpdate<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
if fork_name.altair_enabled() {
serde_json::from_value::<LightClientFinalityUpdate<E>>(value)
.map_err(serde::de::Error::custom)
} else {
Err(serde::de::Error::custom(format!(
"LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'",
fork_name
)))
}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientFinalityUpdate<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!(
"LightClientFinalityUpdate failed to deserialize: {:?}",
e
))
};
Ok(match context {
ForkName::Base => {
return Err(serde::de::Error::custom(format!(
"LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'",
context
)))
}
ForkName::Altair | ForkName::Bellatrix => {
Self::Altair(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra | ForkName::Eip7805 => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}
}

View File

@@ -1,6 +1,5 @@
use crate::context_deserialize;
use crate::ChainSpec;
use crate::ForkName;
use crate::ForkVersionDeserialize;
use crate::{light_client_update::*, BeaconBlockBody};
use crate::{
test_utils::TestRandom, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
@@ -8,8 +7,9 @@ use crate::{
SignedBlindedBeaconBlock,
};
use crate::{BeaconBlockHeader, ExecutionPayloadHeader};
use crate::{ContextDeserialize, ForkName};
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use std::marker::PhantomData;
@@ -35,11 +35,10 @@ use tree_hash_derive::TreeHash;
),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
context_deserialize(ForkName),
)
)]
#[derive(
Debug, Clone, Serialize, TreeHash, Encode, Deserialize, arbitrary::Arbitrary, PartialEq,
)]
#[derive(Debug, Clone, Serialize, TreeHash, Encode, arbitrary::Arbitrary, PartialEq)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
@@ -334,31 +333,40 @@ impl<E: EthSpec> Default for LightClientHeaderFulu<E> {
}
}
impl<E: EthSpec> ForkVersionDeserialize for LightClientHeader<E> {
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Bellatrix => serde_json::from_value(value)
.map(|light_client_header| Self::Altair(light_client_header))
.map_err(serde::de::Error::custom),
ForkName::Capella => serde_json::from_value(value)
.map(|light_client_header| Self::Capella(light_client_header))
.map_err(serde::de::Error::custom),
ForkName::Deneb => serde_json::from_value(value)
.map(|light_client_header| Self::Deneb(light_client_header))
.map_err(serde::de::Error::custom),
ForkName::Electra | ForkName::Eip7805 => serde_json::from_value(value)
.map(|light_client_header| Self::Electra(light_client_header))
.map_err(serde::de::Error::custom),
ForkName::Fulu => serde_json::from_value(value)
.map(|light_client_header| Self::Fulu(light_client_header))
.map_err(serde::de::Error::custom),
ForkName::Base => Err(serde::de::Error::custom(format!(
"LightClientHeader deserialization for {fork_name} not implemented"
))),
}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientHeader<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!(
"LightClientFinalityUpdate failed to deserialize: {:?}",
e
))
};
Ok(match context {
ForkName::Base => {
return Err(serde::de::Error::custom(format!(
"LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'",
context
)))
}
ForkName::Altair | ForkName::Bellatrix => {
Self::Altair(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra | ForkName::Eip7805 => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}
}

View File

@@ -1,4 +1,5 @@
use super::{EthSpec, ForkName, ForkVersionDeserialize, LightClientHeader, Slot, SyncAggregate};
use super::{ContextDeserialize, EthSpec, ForkName, LightClientHeader, Slot, SyncAggregate};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{
light_client_update::*, ChainSpec, LightClientHeaderAltair, LightClientHeaderCapella,
@@ -7,7 +8,6 @@ use crate::{
};
use derivative::Derivative;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz::{Decode, Encode};
use ssz_derive::Decode;
use ssz_derive::Encode;
@@ -36,11 +36,10 @@ use tree_hash_derive::TreeHash;
),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
context_deserialize(ForkName),
)
)]
#[derive(
Debug, Clone, Serialize, Encode, TreeHash, Deserialize, arbitrary::Arbitrary, PartialEq,
)]
#[derive(Debug, Clone, Serialize, Encode, TreeHash, arbitrary::Arbitrary, PartialEq)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
@@ -210,22 +209,40 @@ impl<E: EthSpec> LightClientOptimisticUpdate<E> {
}
}
impl<E: EthSpec> ForkVersionDeserialize for LightClientOptimisticUpdate<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
if fork_name.altair_enabled() {
Ok(
serde_json::from_value::<LightClientOptimisticUpdate<E>>(value)
.map_err(serde::de::Error::custom),
)?
} else {
Err(serde::de::Error::custom(format!(
"LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'",
fork_name
)))
}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientOptimisticUpdate<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!(
"LightClientOptimisticUpdate failed to deserialize: {:?}",
e
))
};
Ok(match context {
ForkName::Base => {
return Err(serde::de::Error::custom(format!(
"LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'",
context
)))
}
ForkName::Altair | ForkName::Bellatrix => {
Self::Altair(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra | ForkName::Eip7805 => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}
}

View File

@@ -1,8 +1,9 @@
use super::{EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee};
use crate::context_deserialize;
use crate::light_client_header::LightClientHeaderElectra;
use crate::LightClientHeader;
use crate::{
beacon_state, test_utils::TestRandom, ChainSpec, Epoch, ForkName, ForkVersionDeserialize,
beacon_state, test_utils::TestRandom, ChainSpec, ContextDeserialize, Epoch, ForkName,
LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb,
LightClientHeaderFulu, SignedBlindedBeaconBlock,
};
@@ -10,7 +11,6 @@ use derivative::Derivative;
use safe_arith::ArithError;
use safe_arith::SafeArith;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz::{Decode, Encode};
use ssz_derive::Decode;
use ssz_derive::Encode;
@@ -117,11 +117,10 @@ impl From<milhouse::Error> for Error {
),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
context_deserialize(ForkName),
)
)]
#[derive(
Debug, Clone, Serialize, Encode, TreeHash, Deserialize, arbitrary::Arbitrary, PartialEq,
)]
#[derive(Debug, Clone, Serialize, Encode, TreeHash, arbitrary::Arbitrary, PartialEq)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
@@ -180,19 +179,37 @@ pub struct LightClientUpdate<E: EthSpec> {
pub signature_slot: Slot,
}
impl<E: EthSpec> ForkVersionDeserialize for LightClientUpdate<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Base => Err(serde::de::Error::custom(format!(
"LightClientUpdate failed to deserialize: unsupported fork '{}'",
fork_name
))),
_ => Ok(serde_json::from_value::<LightClientUpdate<E>>(value)
.map_err(serde::de::Error::custom))?,
}
impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for LightClientUpdate<E> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let convert_err = |e| {
serde::de::Error::custom(format!("LightClientUpdate failed to deserialize: {:?}", e))
};
Ok(match context {
ForkName::Base => {
return Err(serde::de::Error::custom(format!(
"LightClientUpdate failed to deserialize: unsupported fork '{}'",
context
)))
}
ForkName::Altair | ForkName::Bellatrix => {
Self::Altair(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Capella => {
Self::Capella(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Deneb => {
Self::Deneb(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Electra | ForkName::Eip7805 => {
Self::Electra(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
ForkName::Fulu => {
Self::Fulu(Deserialize::deserialize(deserializer).map_err(convert_err)?)
}
})
}
}

View File

@@ -86,6 +86,7 @@ pub trait AbstractExecPayload<E: EthSpec>:
+ TryInto<Self::Electra>
+ TryInto<Self::Eip7805>
+ TryInto<Self::Fulu>
+ Sync
{
type Ref<'a>: ExecPayload<E>
+ Copy
@@ -99,27 +100,33 @@ pub trait AbstractExecPayload<E: EthSpec>:
type Bellatrix: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadBellatrix<E>>>
+ TryFrom<ExecutionPayloadHeaderBellatrix<E>>;
+ TryFrom<ExecutionPayloadHeaderBellatrix<E>>
+ Sync;
type Capella: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadCapella<E>>>
+ TryFrom<ExecutionPayloadHeaderCapella<E>>;
+ TryFrom<ExecutionPayloadHeaderCapella<E>>
+ Sync;
type Deneb: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadDeneb<E>>>
+ TryFrom<ExecutionPayloadHeaderDeneb<E>>;
+ TryFrom<ExecutionPayloadHeaderDeneb<E>>
+ Sync;
type Electra: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadElectra<E>>>
+ TryFrom<ExecutionPayloadHeaderElectra<E>>;
+ TryFrom<ExecutionPayloadHeaderElectra<E>>
+ Sync;
type Eip7805: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadEip7805<E>>>
+ TryFrom<ExecutionPayloadHeaderEip7805<E>>;
+ TryFrom<ExecutionPayloadHeaderEip7805<E>>
+ Sync;
type Fulu: OwnedExecPayload<E>
+ Into<Self>
+ for<'a> From<Cow<'a, ExecutionPayloadFulu<E>>>
+ TryFrom<ExecutionPayloadHeaderFulu<E>>;
+ TryFrom<ExecutionPayloadHeaderFulu<E>>
+ Sync;
}
#[superstruct(

View File

@@ -1,6 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{AttestationData, BitList, EthSpec};
use crate::{AttestationData, BitList, EthSpec, ForkName};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -22,6 +22,7 @@ use tree_hash_derive::TreeHash;
arbitrary::Arbitrary,
)]
#[arbitrary(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
pub struct PendingAttestation<E: EthSpec> {
pub aggregation_bits: BitList<E::MaxValidatorsPerCommittee>,
pub data: AttestationData,

View File

@@ -1,4 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::ForkName;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -18,6 +20,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct PendingConsolidation {
#[serde(with = "serde_utils::quoted_u64")]
pub source_index: u64,

View File

@@ -18,6 +18,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct PendingDeposit {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::Epoch;
use crate::{Epoch, ForkName};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -19,6 +20,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct PendingPartialWithdrawal {
#[serde(with = "serde_utils::quoted_u64")]
pub validator_index: u64,

View File

@@ -227,28 +227,36 @@ pub struct ElectraPreset {
pub min_activation_balance: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_effective_balance_electra: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub min_slashing_penalty_quotient_electra: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub whistleblower_reward_quotient_electra: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_pending_partials_per_withdrawals_sweep: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub pending_deposits_limit: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub pending_partial_withdrawals_limit: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub pending_consolidations_limit: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_consolidation_requests_per_payload: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_deposit_requests_per_payload: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_attester_slashings_electra: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_attestations_electra: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_deposit_requests_per_payload: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_withdrawal_requests_per_payload: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_consolidation_requests_per_payload: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_pending_partials_per_withdrawals_sweep: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub max_pending_deposits_per_epoch: u64,
}
impl ElectraPreset {
@@ -256,19 +264,26 @@ impl ElectraPreset {
Self {
min_activation_balance: spec.min_activation_balance,
max_effective_balance_electra: spec.max_effective_balance_electra,
min_slashing_penalty_quotient_electra: spec.min_slashing_penalty_quotient_electra,
whistleblower_reward_quotient_electra: spec.whistleblower_reward_quotient_electra,
max_pending_partials_per_withdrawals_sweep: spec
.max_pending_partials_per_withdrawals_sweep,
pending_deposits_limit: E::pending_deposits_limit() as u64,
pending_partial_withdrawals_limit: E::pending_partial_withdrawals_limit() as u64,
pending_consolidations_limit: E::pending_consolidations_limit() as u64,
max_consolidation_requests_per_payload: E::max_consolidation_requests_per_payload()
as u64,
max_deposit_requests_per_payload: E::max_deposit_requests_per_payload() as u64,
max_attester_slashings_electra: E::max_attester_slashings_electra() as u64,
max_attestations_electra: E::max_attestations_electra() as u64,
max_deposit_requests_per_payload: E::max_deposit_requests_per_payload() as u64,
max_withdrawal_requests_per_payload: E::max_withdrawal_requests_per_payload() as u64,
max_consolidation_requests_per_payload: E::max_consolidation_requests_per_payload()
as u64,
max_pending_partials_per_withdrawals_sweep: spec
.max_pending_partials_per_withdrawals_sweep,
max_pending_deposits_per_epoch: E::max_pending_deposits_per_epoch() as u64,
}
}
}

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::SignedBeaconBlockHeader;
use crate::{ForkName, SignedBeaconBlockHeader};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -23,6 +24,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct ProposerSlashing {
pub signed_header_1: SignedBeaconBlockHeader,
pub signed_header_2: SignedBeaconBlockHeader,

View File

@@ -1,5 +1,7 @@
use crate::ContextDeserialize;
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde::de::Error as DeError;
use serde::{Deserialize, Deserializer, Serialize};
use ssz::Decode;
use ssz_types::Error;
use std::ops::{Deref, Index, IndexMut};
@@ -217,6 +219,28 @@ where
}
}
impl<'de, C, T> ContextDeserialize<'de, (C, usize)> for RuntimeVariableList<T>
where
T: ContextDeserialize<'de, C>,
C: Clone,
{
fn context_deserialize<D>(deserializer: D, context: (C, usize)) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// first parse out a Vec<C> using the Vec<C> impl you already have
let vec: Vec<T> = Vec::context_deserialize(deserializer, context.0)?;
if vec.len() > context.1 {
return Err(DeError::custom(format!(
"RuntimeVariableList lengh {} exceeds max_len {}",
vec.len(),
context.1
)));
}
Ok(RuntimeVariableList::from_vec(vec, context.1))
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@@ -2,11 +2,11 @@ use super::{
AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef,
};
use super::{
AttestationRef, ChainSpec, Domain, EthSpec, Fork, Hash256, SecretKey, SelectionProof,
Signature, SignedRoot,
Attestation, AttestationRef, ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, SecretKey,
SelectionProof, Signature, SignedRoot,
};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::Attestation;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
@@ -32,6 +32,7 @@ use tree_hash_derive::TreeHash;
TestRandom,
TreeHash,
),
context_deserialize(ForkName),
serde(bound = "E: EthSpec"),
arbitrary(bound = "E: EthSpec"),
),

View File

@@ -1,11 +1,13 @@
use crate::beacon_block_body::{format_kzg_commitments, BLOB_KZG_COMMITMENTS_INDEX};
use crate::test_utils::TestRandom;
use crate::*;
use derivative::Derivative;
use merkle_proof::MerkleTree;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use ssz_derive::{Decode, Encode};
use std::fmt;
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
@@ -49,7 +51,8 @@ impl From<SignedBeaconBlockHash> for Hash256 {
Decode,
TreeHash,
Derivative,
arbitrary::Arbitrary
arbitrary::Arbitrary,
TestRandom
),
derivative(PartialEq, Hash(bound = "E: EthSpec")),
serde(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
@@ -767,20 +770,17 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
}
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> ForkVersionDeserialize
impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> ContextDeserialize<'de, ForkName>
for SignedBeaconBlock<E, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
fn context_deserialize<D>(deserializer: D, context: ForkName) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(map_fork_name!(
fork_name,
context,
Self,
serde_json::from_value(value).map_err(|e| serde::de::Error::custom(format!(
"SignedBeaconBlock failed to deserialize: {:?}",
e
)))?
serde::Deserialize::deserialize(deserializer)?
))
}
}

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::{
test_utils::TestRandom, BeaconBlockHeader, ChainSpec, Domain, EthSpec, Fork, Hash256,
test_utils::TestRandom, BeaconBlockHeader, ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256,
PublicKey, Signature, SignedRoot,
};
use serde::{Deserialize, Serialize};
@@ -24,6 +25,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SignedBeaconBlockHeader {
pub message: BeaconBlockHeader,
pub signature: Signature,

View File

@@ -19,6 +19,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SignedBlsToExecutionChange {
pub message: BlsToExecutionChange,
pub signature: Signature,

View File

@@ -1,7 +1,8 @@
use super::{
ChainSpec, ContributionAndProof, Domain, EthSpec, Fork, Hash256, SecretKey, Signature,
SignedRoot, SyncCommitteeContribution, SyncSelectionProof,
ChainSpec, ContributionAndProof, Domain, EthSpec, Fork, ForkName, Hash256, SecretKey,
Signature, SignedRoot, SyncCommitteeContribution, SyncSelectionProof,
};
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -24,6 +25,7 @@ use tree_hash_derive::TreeHash;
)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
pub struct SignedContributionAndProof<E: EthSpec> {
/// The `ContributionAndProof` that was signed.
pub message: ContributionAndProof<E>,

View File

@@ -1,4 +1,5 @@
use crate::{test_utils::TestRandom, VoluntaryExit};
use crate::context_deserialize;
use crate::{test_utils::TestRandom, ForkName, VoluntaryExit};
use bls::Signature;
use serde::{Deserialize, Serialize};
@@ -22,6 +23,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SignedVoluntaryExit {
pub message: VoluntaryExit,
pub signature: Signature,

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::Hash256;
use crate::{ForkName, Hash256};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -19,6 +20,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SigningData {
pub object_root: Hash256,
pub domain: Hash256,

View File

@@ -1,6 +1,7 @@
use crate::consts::altair::SYNC_COMMITTEE_SUBNET_COUNT;
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{AggregateSignature, BitVector, EthSpec, SyncCommitteeContribution};
use crate::{AggregateSignature, BitVector, EthSpec, ForkName, SyncCommitteeContribution};
use derivative::Derivative;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
@@ -36,6 +37,7 @@ impl From<ArithError> for Error {
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
pub struct SyncAggregate<E: EthSpec> {
pub sync_committee_bits: BitVector<E::SyncCommitteeSize>,
pub sync_committee_signature: AggregateSignature,

View File

@@ -1,6 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{SignedRoot, Slot};
use crate::{ForkName, SignedRoot, Slot};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -19,6 +19,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SyncAggregatorSelectionData {
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{EthSpec, FixedVector, SyncSubnetId};
use crate::{EthSpec, FixedVector, ForkName, SyncSubnetId};
use bls::PublicKeyBytes;
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
@@ -38,6 +39,7 @@ impl From<ArithError> for Error {
)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
pub struct SyncCommittee<E: EthSpec> {
pub pubkeys: FixedVector<PublicKeyBytes, E::SyncCommitteeSize>,
pub aggregate_pubkey: PublicKeyBytes,

View File

@@ -1,4 +1,5 @@
use super::{AggregateSignature, EthSpec, SignedRoot};
use super::{AggregateSignature, EthSpec, ForkName, SignedRoot};
use crate::context_deserialize;
use crate::slot_data::SlotData;
use crate::{test_utils::TestRandom, BitVector, Hash256, Slot, SyncCommitteeMessage};
use serde::{Deserialize, Serialize};
@@ -28,6 +29,7 @@ pub enum Error {
)]
#[serde(bound = "E: EthSpec")]
#[arbitrary(bound = "E: EthSpec")]
#[context_deserialize(ForkName)]
pub struct SyncCommitteeContribution<E: EthSpec> {
pub slot: Slot,
pub beacon_block_root: Hash256,

View File

@@ -1,7 +1,9 @@
use crate::test_utils::TestRandom;
use crate::{ChainSpec, Domain, EthSpec, Fork, Hash256, SecretKey, Signature, SignedRoot, Slot};
use crate::context_deserialize;
use crate::slot_data::SlotData;
use crate::test_utils::TestRandom;
use crate::{
ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, SecretKey, Signature, SignedRoot, Slot,
};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -20,6 +22,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct SyncCommitteeMessage {
pub slot: Slot,
pub beacon_block_root: Hash256,

View File

@@ -1,3 +1,4 @@
use crate::context_deserialize;
use crate::{
test_utils::TestRandom, Address, BeaconState, ChainSpec, Checkpoint, Epoch, EthSpec,
FixedBytesExtended, ForkName, Hash256, PublicKeyBytes,
@@ -23,6 +24,7 @@ use tree_hash_derive::TreeHash;
TestRandom,
TreeHash,
)]
#[context_deserialize(ForkName)]
pub struct Validator {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,
@@ -249,7 +251,6 @@ impl Validator {
}
}
/// TODO(electra): refactor these functions and make it simpler.. this is a mess
/// Returns `true` if the validator is partially withdrawable.
fn is_partially_withdrawable_validator_capella(&self, balance: u64, spec: &ChainSpec) -> bool {
self.has_eth1_withdrawal_credential(spec)

View File

@@ -1,3 +1,4 @@
use crate::context_deserialize;
use crate::{
test_utils::TestRandom, ChainSpec, Domain, Epoch, ForkName, Hash256, SecretKey, SignedRoot,
SignedVoluntaryExit,
@@ -24,6 +25,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct VoluntaryExit {
/// Earliest epoch when voluntary exit can be processed.
pub epoch: Epoch,
@@ -40,6 +42,16 @@ impl VoluntaryExit {
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> SignedVoluntaryExit {
let domain = self.get_domain(genesis_validators_root, spec);
let message = self.signing_root(domain);
SignedVoluntaryExit {
message: self,
signature: secret_key.sign(message),
}
}
pub fn get_domain(&self, genesis_validators_root: Hash256, spec: &ChainSpec) -> Hash256 {
let fork_name = spec.fork_name_at_epoch(self.epoch);
let fork_version = if fork_name.deneb_enabled() {
// EIP-7044
@@ -47,14 +59,7 @@ impl VoluntaryExit {
} else {
spec.fork_version_for_name(fork_name)
};
let domain =
spec.compute_domain(Domain::VoluntaryExit, fork_version, genesis_validators_root);
let message = self.signing_root(domain);
SignedVoluntaryExit {
message: self,
signature: secret_key.sign(message),
}
spec.compute_domain(Domain::VoluntaryExit, fork_version, genesis_validators_root)
}
}

View File

@@ -19,6 +19,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct Withdrawal {
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,

View File

@@ -1,5 +1,6 @@
use crate::context_deserialize;
use crate::test_utils::TestRandom;
use crate::{Address, PublicKeyBytes};
use crate::{Address, ForkName, PublicKeyBytes};
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
@@ -20,6 +21,7 @@ use tree_hash_derive::TreeHash;
TreeHash,
TestRandom,
)]
#[context_deserialize(ForkName)]
pub struct WithdrawalRequest {
#[serde(with = "serde_utils::address_hex")]
pub source_address: Address,