mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-17 21:08:32 +00:00
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:
@@ -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>;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user