Builder flow for Deneb & Blobs (#4428)

* Add Deneb builder flow types with generics

* Update validator client `get_blinded_blocks` call to support Deneb

* `produceBlindedBlock` endpoint updates:
- Handle new Deneb BuilderBid response from builder endpoint (new BlindedBlobsBundle type)
- Build BlockContents response (containing kzg_commitments, proof and blinded_blob_sidecars)

* Appease Clippy lint

* Partial implementation of submit blinded block & blobs. Refactor existing `BlobSidecar` related types to support blinded blobs.

* Add associated types for BlockProposal

* Rename `AbstractSidecar` to `Sidecar`

* Remove blob cache as it's no longer necessary

* Remove unnecessary enum variant

* Clean up

* Hanlde unblinded blobs and publish full block contents

* Fix tests

* Add local EL blobs caching in blinded flow

* Remove BlockProposal and move associated Sidecar trait to AbstractExecPayload to simplify changes

* add blob roots associated type

* move raw blobs associated type to sidecar trait

* Fix todos and improve error handling

* Consolidate BlobsBundle from `execution_layer` into `consensus/types`

* Rename RawBlobs, Blobs, and BlobRoots

* Use `BlobRoots` type alias

* Update error message.

Co-authored-by: realbigsean <seananderson33@GMAIL.com>

* update builder bid type

# Conflicts:
#	consensus/types/src/builder_bid.rs

* Fix lint

* remove generic from builder bid

---------

Co-authored-by: realbigsean <seananderson33@gmail.com>
This commit is contained in:
Jimmy Chen
2023-08-10 23:32:49 +10:00
committed by GitHub
parent fddd4e4c87
commit 0b7a426946
32 changed files with 1027 additions and 499 deletions

View File

@@ -1,17 +1,109 @@
use crate::test_utils::TestRandom;
use crate::{Blob, ChainSpec, Domain, EthSpec, Fork, Hash256, SignedBlobSidecar, SignedRoot, Slot};
use bls::SecretKey;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::sync::Arc;
use derivative::Derivative;
use kzg::{Kzg, KzgCommitment, KzgPreset, KzgProof};
use rand::Rng;
use serde::de::DeserializeOwned;
use serde_derive::{Deserialize, Serialize};
use ssz::Encode;
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use ssz_types::{FixedVector, VariableList};
use std::sync::Arc;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use bls::SecretKey;
use test_random_derive::TestRandom;
use crate::beacon_block_body::KzgCommitments;
use crate::test_utils::TestRandom;
use crate::{
AbstractExecPayload, BeaconBlock, Blob, ChainSpec, Domain, EthSpec, Fork, Hash256,
SignedBlobSidecar, SignedRoot, Slot,
};
pub trait Sidecar<E: EthSpec>:
serde::Serialize
+ Clone
+ DeserializeOwned
+ Encode
+ Decode
+ Hash
+ TreeHash
+ TestRandom
+ Debug
+ SignedRoot
+ Sync
+ Send
+ for<'a> arbitrary::Arbitrary<'a>
{
type BlobItems: BlobItems<E>;
fn slot(&self) -> Slot;
fn build_sidecar<Payload: AbstractExecPayload<E>>(
blob_items: Self::BlobItems,
block: &BeaconBlock<E, Payload>,
expected_kzg_commitments: &KzgCommitments<E>,
kzg_proofs: Vec<KzgProof>,
) -> Result<SidecarList<E, Self>, String>;
}
pub trait BlobItems<T: EthSpec>: Sync + Send + Sized {
fn try_from_blob_roots(roots: BlobRootsList<T>) -> Result<Self, String>;
fn try_from_blobs(blobs: BlobsList<T>) -> Result<Self, String>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn blobs(&self) -> Option<&BlobsList<T>>;
}
impl<T: EthSpec> BlobItems<T> for BlobsList<T> {
fn try_from_blob_roots(_roots: BlobRootsList<T>) -> Result<Self, String> {
Err("Unexpected conversion from blob roots to blobs".to_string())
}
fn try_from_blobs(blobs: BlobsList<T>) -> Result<Self, String> {
Ok(blobs)
}
fn len(&self) -> usize {
VariableList::len(self)
}
fn is_empty(&self) -> bool {
VariableList::is_empty(self)
}
fn blobs(&self) -> Option<&BlobsList<T>> {
Some(self)
}
}
impl<T: EthSpec> BlobItems<T> for BlobRootsList<T> {
fn try_from_blob_roots(roots: BlobRootsList<T>) -> Result<Self, String> {
Ok(roots)
}
fn try_from_blobs(_blobs: BlobsList<T>) -> Result<Self, String> {
// It is possible to convert from blobs to blob roots, however this should be done using
// `From` or `Into` instead of this generic implementation; this function implementation
// should be unreachable, and attempt to use this indicates a bug somewhere.
Err("Unexpected conversion from blob to blob roots".to_string())
}
fn len(&self) -> usize {
VariableList::len(self)
}
fn is_empty(&self) -> bool {
VariableList::is_empty(self)
}
fn blobs(&self) -> Option<&BlobsList<T>> {
None
}
}
/// Container of the data that identifies an individual blob.
#[derive(
Serialize, Deserialize, Encode, Decode, TreeHash, Copy, Clone, Debug, PartialEq, Eq, Hash,
@@ -63,6 +155,67 @@ pub struct BlobSidecar<T: EthSpec> {
pub kzg_proof: KzgProof,
}
impl<E: EthSpec> Sidecar<E> for BlobSidecar<E> {
type BlobItems = BlobsList<E>;
fn slot(&self) -> Slot {
self.slot
}
fn build_sidecar<Payload: AbstractExecPayload<E>>(
blobs: BlobsList<E>,
block: &BeaconBlock<E, Payload>,
expected_kzg_commitments: &KzgCommitments<E>,
kzg_proofs: Vec<KzgProof>,
) -> Result<SidecarList<E, Self>, String> {
let beacon_block_root = block.canonical_root();
let slot = block.slot();
let blob_sidecars = BlobSidecarList::from(
blobs
.into_iter()
.enumerate()
.map(|(blob_index, blob)| {
let kzg_commitment = expected_kzg_commitments
.get(blob_index)
.ok_or("KZG commitment should exist for blob")?;
let kzg_proof = kzg_proofs
.get(blob_index)
.ok_or("KZG proof should exist for blob")?;
Ok(Arc::new(BlobSidecar {
block_root: beacon_block_root,
index: blob_index as u64,
slot,
block_parent_root: block.parent_root(),
proposer_index: block.proposer_index(),
blob,
kzg_commitment: *kzg_commitment,
kzg_proof: *kzg_proof,
}))
})
.collect::<Result<Vec<_>, String>>()?,
);
Ok(blob_sidecars)
}
}
impl<E: EthSpec> From<Arc<BlobSidecar<E>>> for BlindedBlobSidecar {
fn from(blob_sidecar: Arc<BlobSidecar<E>>) -> Self {
BlindedBlobSidecar {
block_root: blob_sidecar.block_root,
index: blob_sidecar.index,
slot: blob_sidecar.slot,
block_parent_root: blob_sidecar.block_parent_root,
proposer_index: blob_sidecar.proposer_index,
blob_root: blob_sidecar.blob.tree_hash_root(),
kzg_commitment: blob_sidecar.kzg_commitment,
kzg_proof: blob_sidecar.kzg_proof,
}
}
}
impl<T: EthSpec> PartialOrd for BlobSidecar<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.index.partial_cmp(&other.index)
@@ -75,11 +228,6 @@ impl<T: EthSpec> Ord for BlobSidecar<T> {
}
}
pub type BlobSidecarList<T> = VariableList<Arc<BlobSidecar<T>>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type FixedBlobSidecarList<T> =
FixedVector<Option<Arc<BlobSidecar<T>>>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type Blobs<T> = VariableList<Blob<T>, <T as EthSpec>::MaxBlobsPerBlock>;
impl<T: EthSpec> SignedRoot for BlobSidecar<T> {}
impl<T: EthSpec> BlobSidecar<T> {
@@ -153,6 +301,94 @@ impl<T: EthSpec> BlobSidecar<T> {
SignedBlobSidecar {
message: self,
signature,
_phantom: PhantomData,
}
}
}
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
Default,
TestRandom,
Derivative,
arbitrary::Arbitrary,
)]
#[derivative(PartialEq, Eq, Hash)]
pub struct BlindedBlobSidecar {
pub block_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
pub slot: Slot,
pub block_parent_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub proposer_index: u64,
pub blob_root: Hash256,
pub kzg_commitment: KzgCommitment,
pub kzg_proof: KzgProof,
}
impl SignedRoot for BlindedBlobSidecar {}
impl<E: EthSpec> Sidecar<E> for BlindedBlobSidecar {
type BlobItems = BlobRootsList<E>;
fn slot(&self) -> Slot {
self.slot
}
fn build_sidecar<Payload: AbstractExecPayload<E>>(
blob_roots: BlobRootsList<E>,
block: &BeaconBlock<E, Payload>,
expected_kzg_commitments: &KzgCommitments<E>,
kzg_proofs: Vec<KzgProof>,
) -> Result<SidecarList<E, BlindedBlobSidecar>, String> {
let beacon_block_root = block.canonical_root();
let slot = block.slot();
let blob_sidecars = BlindedBlobSidecarList::<E>::from(
blob_roots
.into_iter()
.enumerate()
.map(|(blob_index, blob_root)| {
let kzg_commitment = expected_kzg_commitments
.get(blob_index)
.ok_or("KZG commitment should exist for blob")?;
let kzg_proof = kzg_proofs.get(blob_index).ok_or(format!(
"Missing KZG proof for slot {} blob index: {}",
slot, blob_index
))?;
Ok(Arc::new(BlindedBlobSidecar {
block_root: beacon_block_root,
index: blob_index as u64,
slot,
block_parent_root: block.parent_root(),
proposer_index: block.proposer_index(),
blob_root,
kzg_commitment: *kzg_commitment,
kzg_proof: *kzg_proof,
}))
})
.collect::<Result<Vec<_>, String>>()?,
);
Ok(blob_sidecars)
}
}
pub type SidecarList<T, Sidecar> = VariableList<Arc<Sidecar>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type BlobSidecarList<T> = SidecarList<T, BlobSidecar<T>>;
pub type BlindedBlobSidecarList<T> = SidecarList<T, BlindedBlobSidecar>;
pub type FixedBlobSidecarList<T> =
FixedVector<Option<Arc<BlobSidecar<T>>>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type BlobsList<T> = VariableList<Blob<T>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type BlobRootsList<T> = VariableList<Hash256, <T as EthSpec>::MaxBlobsPerBlock>;

View File

@@ -1,76 +1,97 @@
use crate::beacon_block_body::KzgCommitments;
use crate::{
AbstractExecPayload, ChainSpec, EthSpec, ExecPayload, ExecutionPayloadHeader, ForkName,
ForkVersionDeserialize, SignedRoot, Uint256,
BlobRootsList, ChainSpec, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ForkName, ForkVersionDeserialize,
KzgProofs, SignedRoot, Uint256,
};
use bls::PublicKeyBytes;
use bls::Signature;
use serde::{Deserialize as De, Deserializer, Serialize as Ser, Serializer};
use serde::Deserializer;
use serde_derive::{Deserialize, Serialize};
use serde_with::{serde_as, DeserializeAs, SerializeAs};
use std::marker::PhantomData;
use superstruct::superstruct;
use tree_hash_derive::TreeHash;
#[serde_as]
#[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)]
#[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
pub struct BuilderBid<E: EthSpec, Payload: AbstractExecPayload<E>> {
#[serde_as(as = "BlindedPayloadAsHeader<E>")]
pub header: Payload,
#[serde(bound = "E: EthSpec")]
pub struct BlindedBlobsBundle<E: EthSpec> {
pub commitments: KzgCommitments<E>,
pub proofs: KzgProofs<E>,
pub blob_roots: BlobRootsList<E>,
}
#[superstruct(
variants(Merge, Capella, Deneb),
variant_attributes(
derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone),
serde(bound = "E: EthSpec", deny_unknown_fields)
),
map_ref_into(ExecutionPayloadHeaderRef)
)]
#[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)]
#[serde(bound = "E: EthSpec", deny_unknown_fields, untagged)]
#[tree_hash(enum_behaviour = "transparent")]
pub struct BuilderBid<E: EthSpec> {
#[superstruct(only(Merge), partial_getter(rename = "header_merge"))]
pub header: ExecutionPayloadHeaderMerge<E>,
#[superstruct(only(Capella), partial_getter(rename = "header_capella"))]
pub header: ExecutionPayloadHeaderCapella<E>,
#[superstruct(only(Deneb), partial_getter(rename = "header_deneb"))]
pub header: ExecutionPayloadHeaderDeneb<E>,
#[superstruct(only(Deneb))]
pub blinded_blobs_bundle: BlindedBlobsBundle<E>,
#[serde(with = "serde_utils::quoted_u256")]
pub value: Uint256,
pub pubkey: PublicKeyBytes,
#[serde(skip)]
#[tree_hash(skip_hashing)]
_phantom_data: PhantomData<E>,
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedRoot for BuilderBid<E, Payload> {}
impl<E: EthSpec> BuilderBid<E> {
pub fn header(&self) -> ExecutionPayloadHeaderRef<'_, E> {
self.to_ref().header()
}
}
impl<'a, E: EthSpec> BuilderBidRef<'a, E> {
pub fn header(&self) -> ExecutionPayloadHeaderRef<'a, E> {
map_builder_bid_ref_into_execution_payload_header_ref!(&'a _, self, |bid, cons| cons(
&bid.header
))
}
}
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)]
#[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
pub struct SignedBuilderBid<E: EthSpec, Payload: AbstractExecPayload<E>> {
pub message: BuilderBid<E, Payload>,
#[serde(bound = "E: EthSpec")]
pub struct SignedBuilderBid<E: EthSpec> {
pub message: BuilderBid<E>,
pub signature: Signature,
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BuilderBid<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
impl<T: EthSpec> ForkVersionDeserialize for BuilderBid<T> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
let convert_err = |_| {
serde::de::Error::custom(
"BuilderBid failed to deserialize: unable to convert payload header to payload",
)
};
let convert_err =
|e| serde::de::Error::custom(format!("BuilderBid failed to deserialize: {:?}", e));
#[derive(Deserialize)]
struct Helper {
header: serde_json::Value,
#[serde(with = "serde_utils::quoted_u256")]
value: Uint256,
pubkey: PublicKeyBytes,
}
let helper: Helper = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
let payload_header =
ExecutionPayloadHeader::deserialize_by_fork::<'de, D>(helper.header, fork_name)?;
Ok(Self {
header: Payload::try_from(payload_header).map_err(convert_err)?,
value: helper.value,
pubkey: helper.pubkey,
_phantom_data: Default::default(),
Ok(match fork_name {
ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?),
ForkName::Deneb => Self::Deneb(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
)));
}
})
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for SignedBuilderBid<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
impl<T: EthSpec> ForkVersionDeserialize for SignedBuilderBid<T> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
@@ -88,34 +109,10 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
}
}
struct BlindedPayloadAsHeader<E>(PhantomData<E>);
impl<E: EthSpec, Payload: ExecPayload<E>> SerializeAs<Payload> for BlindedPayloadAsHeader<E> {
fn serialize_as<S>(source: &Payload, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
source.to_execution_payload_header().serialize(serializer)
}
}
impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> DeserializeAs<'de, Payload>
for BlindedPayloadAsHeader<E>
{
fn deserialize_as<D>(deserializer: D) -> Result<Payload, D::Error>
where
D: Deserializer<'de>,
{
let payload_header = ExecutionPayloadHeader::deserialize(deserializer)?;
Payload::try_from(payload_header)
.map_err(|_| serde::de::Error::custom("unable to convert payload header to payload"))
}
}
impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBuilderBid<E, Payload> {
impl<E: EthSpec> SignedBuilderBid<E> {
pub fn verify_signature(&self, spec: &ChainSpec) -> bool {
self.message
.pubkey
.pubkey()
.decompress()
.map(|pubkey| {
let domain = spec.get_builder_domain();

View File

@@ -119,7 +119,10 @@ pub use crate::beacon_block_body::{
pub use crate::beacon_block_header::BeaconBlockHeader;
pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
pub use crate::beacon_state::{BeaconTreeHashCache, Error as BeaconStateError, *};
pub use crate::blob_sidecar::{BlobSidecar, BlobSidecarList};
pub use crate::blob_sidecar::{
BlindedBlobSidecar, BlindedBlobSidecarList, BlobRootsList, BlobSidecar, BlobSidecarList,
BlobsList, Sidecar, SidecarList,
};
pub use crate::bls_to_execution_change::BlsToExecutionChange;
pub use crate::chain_spec::{ChainSpec, Config, Domain};
pub use crate::checkpoint::Checkpoint;
@@ -158,8 +161,9 @@ pub use crate::participation_flags::ParticipationFlags;
pub use crate::participation_list::ParticipationList;
pub use crate::payload::{
AbstractExecPayload, BlindedPayload, BlindedPayloadCapella, BlindedPayloadDeneb,
BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload, FullPayload,
FullPayloadCapella, FullPayloadDeneb, FullPayloadMerge, FullPayloadRef, OwnedExecPayload,
BlindedPayloadMerge, BlindedPayloadRef, BlobsBundle, BlockType, ExecPayload,
ExecutionPayloadAndBlobs, FullPayload, FullPayloadCapella, FullPayloadContents,
FullPayloadDeneb, FullPayloadMerge, FullPayloadRef, OwnedExecPayload,
};
pub use crate::pending_attestation::PendingAttestation;
pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset, CapellaPreset};

View File

@@ -1,7 +1,9 @@
use crate::beacon_block_body::KzgCommitments;
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use std::borrow::Cow;
@@ -83,6 +85,8 @@ pub trait AbstractExecPayload<T: EthSpec>:
+ TryInto<Self::Capella>
+ TryInto<Self::Deneb>
{
type Sidecar: Sidecar<T>;
type Ref<'a>: ExecPayload<T>
+ Copy
+ From<&'a Self::Merge>
@@ -103,6 +107,9 @@ pub trait AbstractExecPayload<T: EthSpec>:
+ TryFrom<ExecutionPayloadHeaderDeneb<T>>;
fn default_at_fork(fork_name: ForkName) -> Result<Self, Error>;
fn default_blobs_at_fork(
fork_name: ForkName,
) -> Result<<Self::Sidecar as Sidecar<T>>::BlobItems, Error>;
}
#[superstruct(
@@ -379,6 +386,7 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
}
impl<T: EthSpec> AbstractExecPayload<T> for FullPayload<T> {
type Sidecar = BlobSidecar<T>;
type Ref<'a> = FullPayloadRef<'a, T>;
type Merge = FullPayloadMerge<T>;
type Capella = FullPayloadCapella<T>;
@@ -392,6 +400,9 @@ impl<T: EthSpec> AbstractExecPayload<T> for FullPayload<T> {
ForkName::Deneb => Ok(FullPayloadDeneb::default().into()),
}
}
fn default_blobs_at_fork(_fork_name: ForkName) -> Result<BlobsList<T>, Error> {
Ok(VariableList::default())
}
}
impl<T: EthSpec> From<ExecutionPayload<T>> for FullPayload<T> {
@@ -897,6 +908,8 @@ impl<T: EthSpec> AbstractExecPayload<T> for BlindedPayload<T> {
type Capella = BlindedPayloadCapella<T>;
type Deneb = BlindedPayloadDeneb<T>;
type Sidecar = BlindedBlobSidecar;
fn default_at_fork(fork_name: ForkName) -> Result<Self, Error> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(Error::IncorrectStateVariant),
@@ -905,6 +918,9 @@ impl<T: EthSpec> AbstractExecPayload<T> for BlindedPayload<T> {
ForkName::Deneb => Ok(BlindedPayloadDeneb::default().into()),
}
}
fn default_blobs_at_fork(_fork_name: ForkName) -> Result<BlobRootsList<T>, Error> {
Ok(VariableList::default())
}
}
impl<T: EthSpec> From<ExecutionPayload<T>> for BlindedPayload<T> {
@@ -955,3 +971,84 @@ impl<T: EthSpec> From<BlindedPayload<T>> for ExecutionPayloadHeader<T> {
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[serde(bound = "E: EthSpec")]
pub enum FullPayloadContents<E: EthSpec> {
Payload(ExecutionPayload<E>),
PayloadAndBlobs(ExecutionPayloadAndBlobs<E>),
}
impl<E: EthSpec> FullPayloadContents<E> {
pub fn new(
execution_payload: ExecutionPayload<E>,
maybe_blobs: Option<BlobsBundle<E>>,
) -> Self {
match maybe_blobs {
None => Self::Payload(execution_payload),
Some(blobs_bundle) => Self::PayloadAndBlobs(ExecutionPayloadAndBlobs {
execution_payload,
blobs_bundle,
}),
}
}
pub fn payload_ref(&self) -> &ExecutionPayload<E> {
match self {
FullPayloadContents::Payload(payload) => payload,
FullPayloadContents::PayloadAndBlobs(payload_and_blobs) => {
&payload_and_blobs.execution_payload
}
}
}
pub fn block_hash(&self) -> ExecutionBlockHash {
self.payload_ref().block_hash()
}
pub fn deconstruct(self) -> (ExecutionPayload<E>, Option<BlobsBundle<E>>) {
match self {
FullPayloadContents::Payload(payload) => (payload, None),
FullPayloadContents::PayloadAndBlobs(payload_and_blobs) => (
payload_and_blobs.execution_payload,
Some(payload_and_blobs.blobs_bundle),
),
}
}
}
impl<E: EthSpec> ForkVersionDeserialize for FullPayloadContents<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Merge | ForkName::Capella => serde_json::from_value(value)
.map(Self::Payload)
.map_err(serde::de::Error::custom),
ForkName::Deneb => serde_json::from_value(value)
.map(Self::PayloadAndBlobs)
.map_err(serde::de::Error::custom),
ForkName::Base | ForkName::Altair => Err(serde::de::Error::custom(format!(
"FullPayloadContents deserialization for {fork_name} not implemented"
))),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct ExecutionPayloadAndBlobs<E: EthSpec> {
pub execution_payload: ExecutionPayload<E>,
pub blobs_bundle: BlobsBundle<E>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct BlobsBundle<E: EthSpec> {
pub commitments: KzgCommitments<E>,
pub proofs: KzgProofs<E>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
pub blobs: BlobsList<E>,
}

View File

@@ -1,12 +1,13 @@
use crate::{
test_utils::TestRandom, BlobSidecar, ChainSpec, Domain, EthSpec, Fork, Hash256, Signature,
SignedRoot, SigningData,
test_utils::TestRandom, BlindedBlobSidecar, Blob, BlobSidecar, ChainSpec, Domain, EthSpec,
Fork, Hash256, Sidecar, Signature, SignedRoot, SigningData,
};
use bls::PublicKey;
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::VariableList;
use std::marker::PhantomData;
use std::sync::Arc;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
@@ -25,16 +26,45 @@ use tree_hash_derive::TreeHash;
Derivative,
arbitrary::Arbitrary,
)]
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
#[derivative(Hash(bound = "T: EthSpec"))]
pub struct SignedBlobSidecar<T: EthSpec> {
pub message: Arc<BlobSidecar<T>>,
#[serde(bound = "T: EthSpec, S: Sidecar<T>")]
#[arbitrary(bound = "T: EthSpec, S: Sidecar<T>")]
#[derivative(Hash(bound = "T: EthSpec, S: Sidecar<T>"))]
pub struct SignedSidecar<T: EthSpec, S: Sidecar<T>> {
pub message: Arc<S>,
pub signature: Signature,
#[ssz(skip_serializing, skip_deserializing)]
#[tree_hash(skip_hashing)]
#[serde(skip)]
#[arbitrary(default)]
pub _phantom: PhantomData<T>,
}
pub type SignedBlobSidecarList<T> =
VariableList<SignedBlobSidecar<T>, <T as EthSpec>::MaxBlobsPerBlock>;
impl<T: EthSpec> SignedSidecar<T, BlindedBlobSidecar> {
pub fn into_full_blob_sidecars(self, blob: Blob<T>) -> SignedSidecar<T, BlobSidecar<T>> {
let blinded_sidecar = self.message;
SignedSidecar {
message: Arc::new(BlobSidecar {
block_root: blinded_sidecar.block_root,
index: blinded_sidecar.index,
slot: blinded_sidecar.slot,
block_parent_root: blinded_sidecar.block_parent_root,
proposer_index: blinded_sidecar.proposer_index,
blob,
kzg_commitment: blinded_sidecar.kzg_commitment,
kzg_proof: blinded_sidecar.kzg_proof,
}),
signature: self.signature,
_phantom: PhantomData,
}
}
}
/// List of Signed Sidecars that implements `Sidecar`.
pub type SignedSidecarList<T, Sidecar> =
VariableList<SignedSidecar<T, Sidecar>, <T as EthSpec>::MaxBlobsPerBlock>;
pub type SignedBlobSidecarList<T> = SignedSidecarList<T, BlobSidecar<T>>;
pub type SignedBlobSidecar<T> = SignedSidecar<T, BlobSidecar<T>>;
impl<T: EthSpec> SignedBlobSidecar<T> {
/// Verify `self.signature`.