Add builder SSZ flow (#6859)

This commit is contained in:
Eitan Seri-Levi
2025-02-03 09:07:42 +03:00
committed by GitHub
parent 55d1e754b4
commit 1e2b547b35
14 changed files with 406 additions and 62 deletions

View File

@@ -3,25 +3,37 @@ use crate::{
ChainSpec, EthSpec, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName,
ForkVersionDeserialize, SignedRoot, Uint256,
ForkVersionDecode, ForkVersionDeserialize, SignedRoot, Uint256,
};
use bls::PublicKeyBytes;
use bls::Signature;
use serde::{Deserialize, Deserializer, Serialize};
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use tree_hash_derive::TreeHash;
#[superstruct(
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
variant_attributes(
derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone),
derive(
PartialEq,
Debug,
Encode,
Serialize,
Deserialize,
TreeHash,
Decode,
Clone
),
serde(bound = "E: EthSpec", deny_unknown_fields)
),
map_ref_into(ExecutionPayloadHeaderRef),
map_ref_mut_into(ExecutionPayloadHeaderRefMut)
)]
#[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)]
#[derive(PartialEq, Debug, Encode, Serialize, Deserialize, TreeHash, Clone)]
#[serde(bound = "E: EthSpec", deny_unknown_fields, untagged)]
#[ssz(enum_behaviour = "transparent")]
#[tree_hash(enum_behaviour = "transparent")]
pub struct BuilderBid<E: EthSpec> {
#[superstruct(only(Bellatrix), partial_getter(rename = "header_bellatrix"))]
@@ -65,16 +77,54 @@ impl<'a, E: EthSpec> BuilderBidRefMut<'a, E> {
}
}
impl<E: EthSpec> ForkVersionDecode for BuilderBid<E> {
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let builder_bid = match fork_name {
ForkName::Altair | ForkName::Base => {
return Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadHeader: {fork_name}",
)))
}
ForkName::Bellatrix => {
BuilderBid::Bellatrix(BuilderBidBellatrix::from_ssz_bytes(bytes)?)
}
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella::from_ssz_bytes(bytes)?),
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb::from_ssz_bytes(bytes)?),
ForkName::Electra => BuilderBid::Electra(BuilderBidElectra::from_ssz_bytes(bytes)?),
ForkName::Fulu => BuilderBid::Fulu(BuilderBidFulu::from_ssz_bytes(bytes)?),
};
Ok(builder_bid)
}
}
impl<E: EthSpec> SignedRoot for BuilderBid<E> {}
/// Validator registration, for use in interacting with servers implementing the builder API.
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
#[derive(PartialEq, Debug, Encode, Serialize, Deserialize, Clone)]
#[serde(bound = "E: EthSpec")]
pub struct SignedBuilderBid<E: EthSpec> {
pub message: BuilderBid<E>,
pub signature: Signature,
}
impl<E: EthSpec> ForkVersionDecode for SignedBuilderBid<E> {
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
builder.register_type::<Signature>()?;
let mut decoder = builder.build()?;
let message = decoder
.decode_next_with(|bytes| BuilderBid::from_ssz_bytes_by_fork(bytes, fork_name))?;
let signature = decoder.decode_next()?;
Ok(Self { message, signature })
}
}
impl<E: EthSpec> ForkVersionDeserialize for BuilderBid<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: serde_json::value::Value,

View File

@@ -40,7 +40,7 @@ pub type Withdrawals<E> = VariableList<Withdrawal, <E as EthSpec>::MaxWithdrawal
map_ref_into(ExecutionPayloadHeader)
)]
#[derive(
Debug, Clone, Serialize, Encode, Deserialize, TreeHash, Derivative, arbitrary::Arbitrary,
Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative, arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[serde(bound = "E: EthSpec", untagged)]
@@ -102,8 +102,9 @@ impl<'a, E: EthSpec> ExecutionPayloadRef<'a, E> {
}
}
impl<E: EthSpec> ExecutionPayload<E> {
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
impl<E: EthSpec> ForkVersionDecode for ExecutionPayload<E> {
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayload: {fork_name}",
@@ -117,7 +118,9 @@ impl<E: EthSpec> ExecutionPayload<E> {
ForkName::Fulu => ExecutionPayloadFulu::from_ssz_bytes(bytes).map(Self::Fulu),
}
}
}
impl<E: EthSpec> ExecutionPayload<E> {
#[allow(clippy::arithmetic_side_effects)]
/// Returns the maximum size of an execution payload.
pub fn max_execution_payload_bellatrix_size() -> usize {

View File

@@ -4,6 +4,11 @@ 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,

View File

@@ -178,7 +178,9 @@ 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::{ForkVersionDeserialize, ForkVersionedResponse};
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::indexed_attestation::{

View File

@@ -86,6 +86,17 @@ pub struct SignedBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E> = FullP
pub signature: Signature,
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> ForkVersionDecode
for SignedBeaconBlock<E, Payload>
{
/// SSZ decode with explicit fork variant.
fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
Self::from_ssz_bytes_with(bytes, |bytes| {
BeaconBlock::from_ssz_bytes_for_fork(bytes, fork_name)
})
}
}
pub type SignedBlindedBeaconBlock<E> = SignedBeaconBlock<E, BlindedPayload<E>>;
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload> {
@@ -108,16 +119,6 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
Self::from_ssz_bytes_with(bytes, |bytes| BeaconBlock::from_ssz_bytes(bytes, spec))
}
/// SSZ decode with explicit fork variant.
pub fn from_ssz_bytes_for_fork(
bytes: &[u8],
fork_name: ForkName,
) -> Result<Self, ssz::DecodeError> {
Self::from_ssz_bytes_with(bytes, |bytes| {
BeaconBlock::from_ssz_bytes_for_fork(bytes, fork_name)
})
}
/// SSZ decode which attempts to decode all variants (slow).
pub fn any_from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
Self::from_ssz_bytes_with(bytes, BeaconBlock::any_from_ssz_bytes)