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:
parent
fddd4e4c87
commit
0b7a426946
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2151,6 +2151,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slashing_protection",
|
||||
"ssz_types",
|
||||
"store",
|
||||
"tokio",
|
||||
"types",
|
||||
@ -8500,7 +8501,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"serde_yaml",
|
||||
"slog",
|
||||
"smallvec 1.11.0",
|
||||
|
@ -7,7 +7,6 @@ use crate::attester_cache::{AttesterCache, AttesterCacheKey};
|
||||
use crate::beacon_block_streamer::{BeaconBlockStreamer, CheckEarlyAttesterCache};
|
||||
use crate::beacon_proposer_cache::compute_proposer_duties_from_head;
|
||||
use crate::beacon_proposer_cache::BeaconProposerCache;
|
||||
use crate::blob_cache::BlobCache;
|
||||
use crate::blob_verification::{self, GossipBlobError, GossipVerifiedBlob};
|
||||
use crate::block_times_cache::BlockTimesCache;
|
||||
use crate::block_verification::POS_PANDA_BANNER;
|
||||
@ -67,8 +66,10 @@ use crate::validator_monitor::{
|
||||
HISTORIC_EPOCHS as VALIDATOR_MONITOR_HISTORIC_EPOCHS,
|
||||
};
|
||||
use crate::validator_pubkey_cache::ValidatorPubkeyCache;
|
||||
use crate::{kzg_utils, AvailabilityPendingExecutedBlock};
|
||||
use crate::{metrics, BeaconChainError, BeaconForkChoiceStore, BeaconSnapshot, CachedHead};
|
||||
use crate::{
|
||||
kzg_utils, metrics, AvailabilityPendingExecutedBlock, BeaconChainError, BeaconForkChoiceStore,
|
||||
BeaconSnapshot, CachedHead,
|
||||
};
|
||||
use eth2::types::{EventKind, SseBlock, SseExtendedPayloadAttributes, SyncDuty};
|
||||
use execution_layer::{
|
||||
BlockProposalContents, BuilderParams, ChainHealth, ExecutionLayer, FailedCondition,
|
||||
@ -118,7 +119,7 @@ use task_executor::{ShutdownReason, TaskExecutor};
|
||||
use tokio_stream::Stream;
|
||||
use tree_hash::TreeHash;
|
||||
use types::beacon_state::CloneConfig;
|
||||
use types::blob_sidecar::{BlobSidecarList, FixedBlobSidecarList};
|
||||
use types::blob_sidecar::{BlobItems, BlobSidecarList, FixedBlobSidecarList};
|
||||
use types::consts::deneb::MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS;
|
||||
use types::*;
|
||||
|
||||
@ -473,12 +474,15 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
pub validator_monitor: RwLock<ValidatorMonitor<T::EthSpec>>,
|
||||
/// The slot at which blocks are downloaded back to.
|
||||
pub genesis_backfill_slot: Slot,
|
||||
pub proposal_blob_cache: BlobCache<T::EthSpec>,
|
||||
pub data_availability_checker: Arc<DataAvailabilityChecker<T>>,
|
||||
pub kzg: Option<Arc<Kzg<<T::EthSpec as EthSpec>::Kzg>>>,
|
||||
}
|
||||
|
||||
type BeaconBlockAndState<T, Payload> = (BeaconBlock<T, Payload>, BeaconState<T>);
|
||||
type BeaconBlockAndState<T, Payload> = (
|
||||
BeaconBlock<T, Payload>,
|
||||
BeaconState<T>,
|
||||
Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
|
||||
);
|
||||
|
||||
impl FinalizationAndCanonicity {
|
||||
pub fn is_finalized(self) -> bool {
|
||||
@ -4978,67 +4982,52 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
let blobs_verification_timer =
|
||||
metrics::start_timer(&metrics::BLOCK_PRODUCTION_BLOBS_VERIFICATION_TIMES);
|
||||
if let (Some(blobs), Some(proofs)) = (blobs_opt, proofs_opt) {
|
||||
let kzg = self
|
||||
.kzg
|
||||
.as_ref()
|
||||
.ok_or(BlockProductionError::TrustedSetupNotInitialized)?;
|
||||
let beacon_block_root = block.canonical_root();
|
||||
let expected_kzg_commitments = block.body().blob_kzg_commitments().map_err(|_| {
|
||||
BlockProductionError::InvalidBlockVariant(
|
||||
"DENEB block does not contain kzg commitments".to_string(),
|
||||
let maybe_sidecar_list = match (blobs_opt, proofs_opt) {
|
||||
(Some(blobs_or_blobs_roots), Some(proofs)) => {
|
||||
let expected_kzg_commitments =
|
||||
block.body().blob_kzg_commitments().map_err(|_| {
|
||||
BlockProductionError::InvalidBlockVariant(
|
||||
"deneb block does not contain kzg commitments".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if expected_kzg_commitments.len() != blobs_or_blobs_roots.len() {
|
||||
return Err(BlockProductionError::MissingKzgCommitment(format!(
|
||||
"Missing KZG commitment for slot {}. Expected {}, got: {}",
|
||||
block.slot(),
|
||||
blobs_or_blobs_roots.len(),
|
||||
expected_kzg_commitments.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let kzg_proofs = Vec::from(proofs);
|
||||
|
||||
if let Some(blobs) = blobs_or_blobs_roots.blobs() {
|
||||
let kzg = self
|
||||
.kzg
|
||||
.as_ref()
|
||||
.ok_or(BlockProductionError::TrustedSetupNotInitialized)?;
|
||||
kzg_utils::validate_blobs::<T::EthSpec>(
|
||||
kzg,
|
||||
expected_kzg_commitments,
|
||||
blobs,
|
||||
&kzg_proofs,
|
||||
)
|
||||
.map_err(BlockProductionError::KzgError)?;
|
||||
}
|
||||
|
||||
Some(
|
||||
Sidecar::build_sidecar(
|
||||
blobs_or_blobs_roots,
|
||||
&block,
|
||||
expected_kzg_commitments,
|
||||
kzg_proofs,
|
||||
)
|
||||
.map_err(BlockProductionError::FailedToBuildBlobSidecars)?,
|
||||
)
|
||||
})?;
|
||||
|
||||
if expected_kzg_commitments.len() != blobs.len() {
|
||||
return Err(BlockProductionError::MissingKzgCommitment(format!(
|
||||
"Missing KZG commitment for slot {}. Expected {}, got: {}",
|
||||
slot,
|
||||
blobs.len(),
|
||||
expected_kzg_commitments.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let kzg_proofs = Vec::from(proofs);
|
||||
|
||||
kzg_utils::validate_blobs::<T::EthSpec>(
|
||||
kzg.as_ref(),
|
||||
expected_kzg_commitments,
|
||||
&blobs,
|
||||
&kzg_proofs,
|
||||
)
|
||||
.map_err(BlockProductionError::KzgError)?;
|
||||
|
||||
let blob_sidecars = BlobSidecarList::from(
|
||||
blobs
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(blob_index, blob)| {
|
||||
let kzg_commitment = expected_kzg_commitments
|
||||
.get(blob_index)
|
||||
.expect("KZG commitment should exist for blob");
|
||||
|
||||
let kzg_proof = kzg_proofs
|
||||
.get(blob_index)
|
||||
.expect("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,
|
||||
blob,
|
||||
kzg_commitment: *kzg_commitment,
|
||||
kzg_proof: *kzg_proof,
|
||||
}))
|
||||
})
|
||||
.collect::<Result<Vec<_>, BlockProductionError>>()?,
|
||||
);
|
||||
|
||||
self.proposal_blob_cache
|
||||
.put(beacon_block_root, blob_sidecars);
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
drop(blobs_verification_timer);
|
||||
|
||||
@ -5052,7 +5041,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
"slot" => block.slot()
|
||||
);
|
||||
|
||||
Ok((block, state))
|
||||
Ok((block, state, maybe_sidecar_list))
|
||||
}
|
||||
|
||||
/// This method must be called whenever an execution engine indicates that a payload is
|
||||
|
@ -1,35 +0,0 @@
|
||||
use lru::LruCache;
|
||||
use parking_lot::Mutex;
|
||||
use types::{BlobSidecarList, EthSpec, Hash256};
|
||||
|
||||
pub const DEFAULT_BLOB_CACHE_SIZE: usize = 10;
|
||||
|
||||
/// A cache blobs by beacon block root.
|
||||
pub struct BlobCache<T: EthSpec> {
|
||||
blobs: Mutex<LruCache<BlobCacheId, BlobSidecarList<T>>>,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct BlobCacheId(Hash256);
|
||||
|
||||
impl<T: EthSpec> Default for BlobCache<T> {
|
||||
fn default() -> Self {
|
||||
BlobCache {
|
||||
blobs: Mutex::new(LruCache::new(DEFAULT_BLOB_CACHE_SIZE)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> BlobCache<T> {
|
||||
pub fn put(
|
||||
&self,
|
||||
beacon_block: Hash256,
|
||||
blobs: BlobSidecarList<T>,
|
||||
) -> Option<BlobSidecarList<T>> {
|
||||
self.blobs.lock().put(BlobCacheId(beacon_block), blobs)
|
||||
}
|
||||
|
||||
pub fn pop(&self, root: &Hash256) -> Option<BlobSidecarList<T>> {
|
||||
self.blobs.lock().pop(&BlobCacheId(*root))
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
use crate::beacon_chain::{CanonicalHead, BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, OP_POOL_DB_KEY};
|
||||
use crate::blob_cache::BlobCache;
|
||||
use crate::data_availability_checker::DataAvailabilityChecker;
|
||||
use crate::eth1_chain::{CachingEth1Backend, SszEth1};
|
||||
use crate::eth1_finalization_cache::Eth1FinalizationCache;
|
||||
@ -889,7 +888,6 @@ where
|
||||
DataAvailabilityChecker::new(slot_clock, kzg.clone(), store, self.spec)
|
||||
.map_err(|e| format!("Error initializing DataAvailabiltyChecker: {:?}", e))?,
|
||||
),
|
||||
proposal_blob_cache: BlobCache::default(),
|
||||
kzg,
|
||||
};
|
||||
|
||||
|
@ -275,20 +275,22 @@ pub enum BlockProductionError {
|
||||
blob_block_hash: ExecutionBlockHash,
|
||||
payload_block_hash: ExecutionBlockHash,
|
||||
},
|
||||
NoBlobsCached,
|
||||
FailedToReadFinalizedBlock(store::Error),
|
||||
MissingFinalizedBlock(Hash256),
|
||||
BlockTooLarge(usize),
|
||||
ShuttingDown,
|
||||
MissingBlobs,
|
||||
MissingSyncAggregate,
|
||||
MissingExecutionPayload,
|
||||
MissingKzgCommitment(String),
|
||||
MissingKzgProof(String),
|
||||
TokioJoin(tokio::task::JoinError),
|
||||
BeaconChain(BeaconChainError),
|
||||
InvalidPayloadFork,
|
||||
TrustedSetupNotInitialized,
|
||||
InvalidBlockVariant(String),
|
||||
KzgError(kzg::Error),
|
||||
FailedToBuildBlobSidecars(String),
|
||||
}
|
||||
|
||||
easy_from_to!(BlockProcessingError, BlockProductionError);
|
||||
|
@ -7,7 +7,6 @@ mod beacon_chain;
|
||||
mod beacon_fork_choice_store;
|
||||
pub mod beacon_proposer_cache;
|
||||
mod beacon_snapshot;
|
||||
pub mod blob_cache;
|
||||
pub mod blob_verification;
|
||||
pub mod block_reward;
|
||||
mod block_times_cache;
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
StateSkipConfig,
|
||||
};
|
||||
use bls::get_withdrawal_credentials;
|
||||
use eth2::types::BlockContentsTuple;
|
||||
use eth2::types::SignedBlockContentsTuple;
|
||||
use execution_layer::test_utils::generate_genesis_header;
|
||||
use execution_layer::{
|
||||
auth::JwtKey,
|
||||
@ -50,6 +50,7 @@ use state_processing::{
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@ -817,9 +818,28 @@ where
|
||||
&self,
|
||||
state: BeaconState<E>,
|
||||
slot: Slot,
|
||||
) -> (BlockContentsTuple<E, BlindedPayload<E>>, BeaconState<E>) {
|
||||
) -> (
|
||||
SignedBlockContentsTuple<E, BlindedPayload<E>>,
|
||||
BeaconState<E>,
|
||||
) {
|
||||
let (unblinded, new_state) = self.make_block(state, slot).await;
|
||||
((unblinded.0.into(), unblinded.1), new_state)
|
||||
let maybe_blinded_blob_sidecars = unblinded.1.map(|blob_sidecar_list| {
|
||||
VariableList::new(
|
||||
blob_sidecar_list
|
||||
.into_iter()
|
||||
.map(|blob_sidecar| {
|
||||
let blinded_sidecar: BlindedBlobSidecar = blob_sidecar.message.into();
|
||||
SignedSidecar {
|
||||
message: Arc::new(blinded_sidecar),
|
||||
signature: blob_sidecar.signature,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
((unblinded.0.into(), maybe_blinded_blob_sidecars), new_state)
|
||||
}
|
||||
|
||||
/// Returns a newly created block, signed by the proposer for the given slot.
|
||||
@ -827,7 +847,7 @@ where
|
||||
&self,
|
||||
mut state: BeaconState<E>,
|
||||
slot: Slot,
|
||||
) -> (BlockContentsTuple<E, FullPayload<E>>, BeaconState<E>) {
|
||||
) -> (SignedBlockContentsTuple<E, FullPayload<E>>, BeaconState<E>) {
|
||||
assert_ne!(slot, 0, "can't produce a block at slot 0");
|
||||
assert!(slot >= state.slot());
|
||||
|
||||
@ -845,7 +865,7 @@ where
|
||||
|
||||
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
|
||||
|
||||
let (block, state) = self
|
||||
let (block, state, maybe_blob_sidecars) = self
|
||||
.chain
|
||||
.produce_block_on_state(
|
||||
state,
|
||||
@ -865,18 +885,14 @@ where
|
||||
&self.spec,
|
||||
);
|
||||
|
||||
let block_contents: BlockContentsTuple<E, FullPayload<E>> = match &signed_block {
|
||||
let block_contents: SignedBlockContentsTuple<E, FullPayload<E>> = match &signed_block {
|
||||
SignedBeaconBlock::Base(_)
|
||||
| SignedBeaconBlock::Altair(_)
|
||||
| SignedBeaconBlock::Merge(_)
|
||||
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
||||
SignedBeaconBlock::Deneb(_) => {
|
||||
if let Some(blobs) = self
|
||||
.chain
|
||||
.proposal_blob_cache
|
||||
.pop(&signed_block.canonical_root())
|
||||
{
|
||||
let signed_blobs: SignedBlobSidecarList<E> = Vec::from(blobs)
|
||||
if let Some(blobs) = maybe_blob_sidecars {
|
||||
let signed_blobs: SignedSidecarList<E, BlobSidecar<E>> = Vec::from(blobs)
|
||||
.into_iter()
|
||||
.map(|blob| {
|
||||
blob.sign(
|
||||
@ -911,7 +927,7 @@ where
|
||||
&self,
|
||||
mut state: BeaconState<E>,
|
||||
slot: Slot,
|
||||
) -> (BlockContentsTuple<E, FullPayload<E>>, BeaconState<E>) {
|
||||
) -> (SignedBlockContentsTuple<E, FullPayload<E>>, BeaconState<E>) {
|
||||
assert_ne!(slot, 0, "can't produce a block at slot 0");
|
||||
assert!(slot >= state.slot());
|
||||
|
||||
@ -931,7 +947,7 @@ where
|
||||
|
||||
let pre_state = state.clone();
|
||||
|
||||
let (block, state) = self
|
||||
let (block, state, maybe_blob_sidecars) = self
|
||||
.chain
|
||||
.produce_block_on_state(
|
||||
state,
|
||||
@ -951,18 +967,14 @@ where
|
||||
&self.spec,
|
||||
);
|
||||
|
||||
let block_contents: BlockContentsTuple<E, FullPayload<E>> = match &signed_block {
|
||||
let block_contents: SignedBlockContentsTuple<E, FullPayload<E>> = match &signed_block {
|
||||
SignedBeaconBlock::Base(_)
|
||||
| SignedBeaconBlock::Altair(_)
|
||||
| SignedBeaconBlock::Merge(_)
|
||||
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
||||
SignedBeaconBlock::Deneb(_) => {
|
||||
if let Some(blobs) = self
|
||||
.chain
|
||||
.proposal_blob_cache
|
||||
.pop(&signed_block.canonical_root())
|
||||
{
|
||||
let signed_blobs: SignedBlobSidecarList<E> = Vec::from(blobs)
|
||||
if let Some(blobs) = maybe_blob_sidecars {
|
||||
let signed_blobs: SignedSidecarList<E, BlobSidecar<E>> = Vec::from(blobs)
|
||||
.into_iter()
|
||||
.map(|blob| {
|
||||
blob.sign(
|
||||
@ -1778,7 +1790,7 @@ where
|
||||
state: BeaconState<E>,
|
||||
slot: Slot,
|
||||
block_modifier: impl FnOnce(&mut BeaconBlock<E>),
|
||||
) -> (BlockContentsTuple<E, FullPayload<E>>, BeaconState<E>) {
|
||||
) -> (SignedBlockContentsTuple<E, FullPayload<E>>, BeaconState<E>) {
|
||||
assert_ne!(slot, 0, "can't produce a block at slot 0");
|
||||
assert!(slot >= state.slot());
|
||||
|
||||
@ -1876,7 +1888,7 @@ where
|
||||
&self,
|
||||
slot: Slot,
|
||||
block_root: Hash256,
|
||||
block_contents: BlockContentsTuple<E, FullPayload<E>>,
|
||||
block_contents: SignedBlockContentsTuple<E, FullPayload<E>>,
|
||||
) -> Result<SignedBeaconBlockHash, BlockError<E>> {
|
||||
self.set_current_slot(slot);
|
||||
let (block, blobs) = block_contents;
|
||||
@ -1906,7 +1918,7 @@ where
|
||||
|
||||
pub async fn process_block_result(
|
||||
&self,
|
||||
block_contents: BlockContentsTuple<E, FullPayload<E>>,
|
||||
block_contents: SignedBlockContentsTuple<E, FullPayload<E>>,
|
||||
) -> Result<SignedBeaconBlockHash, BlockError<E>> {
|
||||
let (block, blobs) = block_contents;
|
||||
// Note: we are just dropping signatures here and skipping signature verification.
|
||||
@ -1991,7 +2003,7 @@ where
|
||||
) -> Result<
|
||||
(
|
||||
SignedBeaconBlockHash,
|
||||
BlockContentsTuple<E, FullPayload<E>>,
|
||||
SignedBlockContentsTuple<E, FullPayload<E>>,
|
||||
BeaconState<E>,
|
||||
),
|
||||
BlockError<E>,
|
||||
|
@ -126,6 +126,7 @@ async fn get_chain_segment_with_signed_blobs() -> (
|
||||
.get(&BlobSignatureKey::new(block_root, blob_index))
|
||||
.unwrap()
|
||||
.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -1,8 +1,8 @@
|
||||
use eth2::types::builder_bid::SignedBuilderBid;
|
||||
use eth2::types::payload::FullPayloadContents;
|
||||
use eth2::types::{
|
||||
AbstractExecPayload, BlindedPayload, EthSpec, ExecutionBlockHash, ExecutionPayload,
|
||||
ForkVersionedResponse, PublicKeyBytes, SignedBlockContents, SignedValidatorRegistrationData,
|
||||
Slot,
|
||||
BlindedPayload, EthSpec, ExecutionBlockHash, ForkVersionedResponse, PublicKeyBytes,
|
||||
SignedBlockContents, SignedValidatorRegistrationData, Slot,
|
||||
};
|
||||
pub use eth2::Error;
|
||||
use eth2::{ok_or_error, StatusCode};
|
||||
@ -141,7 +141,7 @@ impl BuilderHttpClient {
|
||||
pub async fn post_builder_blinded_blocks<E: EthSpec>(
|
||||
&self,
|
||||
blinded_block: &SignedBlockContents<E, BlindedPayload<E>>,
|
||||
) -> Result<ForkVersionedResponse<ExecutionPayload<E>>, Error> {
|
||||
) -> Result<ForkVersionedResponse<FullPayloadContents<E>>, Error> {
|
||||
let mut path = self.server.full.clone();
|
||||
|
||||
path.path_segments_mut()
|
||||
@ -163,12 +163,12 @@ impl BuilderHttpClient {
|
||||
}
|
||||
|
||||
/// `GET /eth/v1/builder/header`
|
||||
pub async fn get_builder_header<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
pub async fn get_builder_header<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
pubkey: &PublicKeyBytes,
|
||||
) -> Result<Option<ForkVersionedResponse<SignedBuilderBid<E, Payload>>>, Error> {
|
||||
) -> Result<Option<ForkVersionedResponse<SignedBuilderBid<E>>>, Error> {
|
||||
let mut path = self.server.full.clone();
|
||||
|
||||
path.path_segments_mut()
|
||||
|
@ -20,16 +20,14 @@ use state_processing::per_block_processing::deneb::deneb::kzg_commitment_to_vers
|
||||
use std::convert::TryFrom;
|
||||
use strum::IntoStaticStr;
|
||||
use superstruct::superstruct;
|
||||
use types::beacon_block_body::KzgCommitments;
|
||||
use types::blob_sidecar::Blobs;
|
||||
pub use types::{
|
||||
Address, BeaconBlockRef, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader,
|
||||
ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, Uint256, VariableList,
|
||||
Withdrawal, Withdrawals,
|
||||
};
|
||||
use types::{
|
||||
BeaconStateError, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge,
|
||||
KzgProofs, VersionedHash,
|
||||
BeaconStateError, BlobsBundle, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadMerge, KzgProofs, VersionedHash,
|
||||
};
|
||||
|
||||
pub mod auth;
|
||||
@ -64,7 +62,6 @@ pub enum Error {
|
||||
IncorrectStateVariant,
|
||||
RequiredMethodUnsupported(&'static str),
|
||||
UnsupportedForkVariant(String),
|
||||
BadConversion(String),
|
||||
RlpDecoderError(rlp::DecoderError),
|
||||
BlobTxConversionError(BlobTxConversionError),
|
||||
}
|
||||
@ -416,7 +413,7 @@ pub struct GetPayloadResponse<T: EthSpec> {
|
||||
pub execution_payload: ExecutionPayloadDeneb<T>,
|
||||
pub block_value: Uint256,
|
||||
#[superstruct(only(Deneb))]
|
||||
pub blobs_bundle: BlobsBundleV1<T>,
|
||||
pub blobs_bundle: BlobsBundle<T>,
|
||||
#[superstruct(only(Deneb), partial_getter(copy))]
|
||||
pub should_override_builder: bool,
|
||||
}
|
||||
@ -452,7 +449,7 @@ impl<T: EthSpec> From<GetPayloadResponse<T>> for ExecutionPayload<T> {
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<GetPayloadResponse<T>>
|
||||
for (ExecutionPayload<T>, Uint256, Option<BlobsBundleV1<T>>)
|
||||
for (ExecutionPayload<T>, Uint256, Option<BlobsBundle<T>>)
|
||||
{
|
||||
fn from(response: GetPayloadResponse<T>) -> Self {
|
||||
match response {
|
||||
@ -575,13 +572,6 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq)]
|
||||
pub struct BlobsBundleV1<E: EthSpec> {
|
||||
pub commitments: KzgCommitments<E>,
|
||||
pub proofs: KzgProofs<E>,
|
||||
pub blobs: Blobs<E>,
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(Merge, Capella, Deneb),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
|
@ -3,10 +3,11 @@ use serde::{Deserialize, Serialize};
|
||||
use strum::EnumString;
|
||||
use superstruct::superstruct;
|
||||
use types::beacon_block_body::KzgCommitments;
|
||||
use types::blob_sidecar::Blobs;
|
||||
use types::blob_sidecar::BlobsList;
|
||||
use types::{
|
||||
EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadMerge, FixedVector, Transactions, Unsigned, VariableList, Withdrawal,
|
||||
BlobsBundle, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella,
|
||||
ExecutionPayloadDeneb, ExecutionPayloadMerge, FixedVector, Transactions, Unsigned,
|
||||
VariableList, Withdrawal,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -441,11 +442,11 @@ pub struct JsonBlobsBundleV1<E: EthSpec> {
|
||||
pub commitments: KzgCommitments<E>,
|
||||
pub proofs: KzgProofs<E>,
|
||||
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
|
||||
pub blobs: Blobs<E>,
|
||||
pub blobs: BlobsList<E>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<BlobsBundleV1<E>> for JsonBlobsBundleV1<E> {
|
||||
fn from(blobs_bundle: BlobsBundleV1<E>) -> Self {
|
||||
impl<E: EthSpec> From<BlobsBundle<E>> for JsonBlobsBundleV1<E> {
|
||||
fn from(blobs_bundle: BlobsBundle<E>) -> Self {
|
||||
Self {
|
||||
commitments: blobs_bundle.commitments,
|
||||
proofs: blobs_bundle.proofs,
|
||||
@ -453,7 +454,7 @@ impl<E: EthSpec> From<BlobsBundleV1<E>> for JsonBlobsBundleV1<E> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<E: EthSpec> From<JsonBlobsBundleV1<E>> for BlobsBundleV1<E> {
|
||||
impl<E: EthSpec> From<JsonBlobsBundleV1<E>> for BlobsBundle<E> {
|
||||
fn from(json_blobs_bundle: JsonBlobsBundleV1<E>) -> Self {
|
||||
Self {
|
||||
commitments: json_blobs_bundle.commitments,
|
||||
|
@ -13,8 +13,8 @@ pub use engine_api::*;
|
||||
pub use engine_api::{http, http::deposit_methods, http::HttpJsonRpc};
|
||||
use engines::{Engine, EngineError};
|
||||
pub use engines::{EngineState, ForkchoiceState};
|
||||
use eth2::types::SignedBlockContents;
|
||||
use eth2::types::{builder_bid::SignedBuilderBid, ForkVersionedResponse};
|
||||
use eth2::types::{builder_bid::SignedBuilderBid, BlobsBundle, ForkVersionedResponse};
|
||||
use eth2::types::{FullPayloadContents, SignedBlockContents};
|
||||
use ethers_core::abi::ethereum_types::FromStrRadixErr;
|
||||
use ethers_core::types::Transaction as EthersTransaction;
|
||||
use fork_choice::ForkchoiceUpdateParameters;
|
||||
@ -41,12 +41,13 @@ use tokio::{
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tree_hash::TreeHash;
|
||||
use types::beacon_block_body::KzgCommitments;
|
||||
use types::blob_sidecar::Blobs;
|
||||
use types::KzgProofs;
|
||||
use types::blob_sidecar::BlobItems;
|
||||
use types::builder_bid::BuilderBid;
|
||||
use types::{
|
||||
AbstractExecPayload, BeaconStateError, BlindedPayload, BlockType, ChainSpec, Epoch,
|
||||
ExecPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge,
|
||||
};
|
||||
use types::{KzgProofs, Sidecar};
|
||||
use types::{ProposerPreparationData, PublicKeyBytes, Signature, Slot, Transaction};
|
||||
|
||||
mod block_hash;
|
||||
@ -86,6 +87,40 @@ pub enum ProvenancedPayload<P> {
|
||||
Builder(P),
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<BuilderBid<E>>
|
||||
for ProvenancedPayload<BlockProposalContents<E, Payload>>
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: BuilderBid<E>) -> Result<Self, Error> {
|
||||
let block_proposal_contents = match value {
|
||||
BuilderBid::Merge(builder_bid) => BlockProposalContents::Payload {
|
||||
payload: ExecutionPayloadHeader::Merge(builder_bid.header)
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidPayloadConversion)?,
|
||||
block_value: builder_bid.value,
|
||||
},
|
||||
BuilderBid::Capella(builder_bid) => BlockProposalContents::Payload {
|
||||
payload: ExecutionPayloadHeader::Capella(builder_bid.header)
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidPayloadConversion)?,
|
||||
block_value: builder_bid.value,
|
||||
},
|
||||
BuilderBid::Deneb(builder_bid) => BlockProposalContents::PayloadAndBlobs {
|
||||
payload: ExecutionPayloadHeader::Deneb(builder_bid.header)
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidPayloadConversion)?,
|
||||
block_value: builder_bid.value,
|
||||
kzg_commitments: builder_bid.blinded_blobs_bundle.commitments,
|
||||
blobs: BlobItems::try_from_blob_roots(builder_bid.blinded_blobs_bundle.blob_roots)
|
||||
.map_err(Error::InvalidBlobConversion)?,
|
||||
proofs: builder_bid.blinded_blobs_bundle.proofs,
|
||||
},
|
||||
};
|
||||
Ok(ProvenancedPayload::Builder(block_proposal_contents))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
NoEngine,
|
||||
@ -107,6 +142,8 @@ pub enum Error {
|
||||
InvalidJWTSecret(String),
|
||||
InvalidForkForPayload,
|
||||
InvalidPayloadBody(String),
|
||||
InvalidPayloadConversion,
|
||||
InvalidBlobConversion(String),
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
@ -131,28 +168,31 @@ pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
payload: Payload,
|
||||
block_value: Uint256,
|
||||
kzg_commitments: KzgCommitments<T>,
|
||||
blobs: Blobs<T>,
|
||||
blobs: <Payload::Sidecar as Sidecar<T>>::BlobItems,
|
||||
proofs: KzgProofs<T>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> From<GetPayloadResponse<E>>
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<GetPayloadResponse<E>>
|
||||
for BlockProposalContents<E, Payload>
|
||||
{
|
||||
fn from(response: GetPayloadResponse<E>) -> Self {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(response: GetPayloadResponse<E>) -> Result<Self, Error> {
|
||||
let (execution_payload, block_value, maybe_bundle) = response.into();
|
||||
match maybe_bundle {
|
||||
Some(bundle) => Self::PayloadAndBlobs {
|
||||
Some(bundle) => Ok(Self::PayloadAndBlobs {
|
||||
payload: execution_payload.into(),
|
||||
block_value,
|
||||
kzg_commitments: bundle.commitments,
|
||||
blobs: bundle.blobs,
|
||||
blobs: BlobItems::try_from_blobs(bundle.blobs)
|
||||
.map_err(Error::InvalidBlobConversion)?,
|
||||
proofs: bundle.proofs,
|
||||
},
|
||||
None => Self::Payload {
|
||||
}),
|
||||
None => Ok(Self::Payload {
|
||||
payload: execution_payload.into(),
|
||||
block_value,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,7 +204,7 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Paylo
|
||||
) -> (
|
||||
Payload,
|
||||
Option<KzgCommitments<T>>,
|
||||
Option<Blobs<T>>,
|
||||
Option<<Payload::Sidecar as Sidecar<T>>::BlobItems>,
|
||||
Option<KzgProofs<T>>,
|
||||
) {
|
||||
match self {
|
||||
@ -184,47 +224,20 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Paylo
|
||||
|
||||
pub fn payload(&self) -> &Payload {
|
||||
match self {
|
||||
Self::Payload {
|
||||
payload,
|
||||
block_value: _,
|
||||
} => payload,
|
||||
Self::PayloadAndBlobs {
|
||||
payload,
|
||||
block_value: _,
|
||||
kzg_commitments: _,
|
||||
blobs: _,
|
||||
proofs: _,
|
||||
} => payload,
|
||||
Self::Payload { payload, .. } => payload,
|
||||
Self::PayloadAndBlobs { payload, .. } => payload,
|
||||
}
|
||||
}
|
||||
pub fn to_payload(self) -> Payload {
|
||||
match self {
|
||||
Self::Payload {
|
||||
payload,
|
||||
block_value: _,
|
||||
} => payload,
|
||||
Self::PayloadAndBlobs {
|
||||
payload,
|
||||
block_value: _,
|
||||
kzg_commitments: _,
|
||||
blobs: _,
|
||||
proofs: _,
|
||||
} => payload,
|
||||
Self::Payload { payload, .. } => payload,
|
||||
Self::PayloadAndBlobs { payload, .. } => payload,
|
||||
}
|
||||
}
|
||||
pub fn block_value(&self) -> &Uint256 {
|
||||
match self {
|
||||
Self::Payload {
|
||||
payload: _,
|
||||
block_value,
|
||||
} => block_value,
|
||||
Self::PayloadAndBlobs {
|
||||
payload: _,
|
||||
block_value,
|
||||
kzg_commitments: _,
|
||||
blobs: _,
|
||||
proofs: _,
|
||||
} => block_value,
|
||||
Self::Payload { block_value, .. } => block_value,
|
||||
Self::PayloadAndBlobs { block_value, .. } => block_value,
|
||||
}
|
||||
}
|
||||
pub fn default_at_fork(fork_name: ForkName) -> Result<Self, BeaconStateError> {
|
||||
@ -238,7 +251,7 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Paylo
|
||||
ForkName::Deneb => BlockProposalContents::PayloadAndBlobs {
|
||||
payload: Payload::default_at_fork(fork_name)?,
|
||||
block_value: Uint256::zero(),
|
||||
blobs: VariableList::default(),
|
||||
blobs: Payload::default_blobs_at_fork(fork_name)?,
|
||||
kzg_commitments: VariableList::default(),
|
||||
proofs: VariableList::default(),
|
||||
},
|
||||
@ -285,6 +298,8 @@ pub enum FailedCondition {
|
||||
EpochsSinceFinalization,
|
||||
}
|
||||
|
||||
type PayloadContentsRefTuple<'a, T> = (ExecutionPayloadRef<'a, T>, Option<&'a BlobsBundle<T>>);
|
||||
|
||||
struct Inner<E: EthSpec> {
|
||||
engine: Arc<Engine>,
|
||||
builder: Option<BuilderHttpClient>,
|
||||
@ -488,12 +503,28 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
}
|
||||
|
||||
/// Cache a full payload, keyed on the `tree_hash_root` of the payload
|
||||
fn cache_payload(&self, payload: ExecutionPayloadRef<T>) -> Option<ExecutionPayload<T>> {
|
||||
self.inner.payload_cache.put(payload.clone_from_ref())
|
||||
fn cache_payload(
|
||||
&self,
|
||||
payload_and_blobs: PayloadContentsRefTuple<T>,
|
||||
) -> Option<FullPayloadContents<T>> {
|
||||
let (payload_ref, maybe_json_blobs_bundle) = payload_and_blobs;
|
||||
|
||||
let payload = payload_ref.clone_from_ref();
|
||||
let maybe_blobs_bundle = maybe_json_blobs_bundle
|
||||
.cloned()
|
||||
.map(|blobs_bundle| BlobsBundle {
|
||||
commitments: blobs_bundle.commitments,
|
||||
proofs: blobs_bundle.proofs,
|
||||
blobs: blobs_bundle.blobs,
|
||||
});
|
||||
|
||||
self.inner
|
||||
.payload_cache
|
||||
.put(FullPayloadContents::new(payload, maybe_blobs_bundle))
|
||||
}
|
||||
|
||||
/// Attempt to retrieve a full payload from the payload cache by the payload root
|
||||
pub fn get_payload_by_root(&self, root: &Hash256) -> Option<ExecutionPayload<T>> {
|
||||
pub fn get_payload_by_root(&self, root: &Hash256) -> Option<FullPayloadContents<T>> {
|
||||
self.inner.payload_cache.get(root)
|
||||
}
|
||||
|
||||
@ -791,7 +822,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
current_fork,
|
||||
)
|
||||
.await
|
||||
.map(|get_payload_response| ProvenancedPayload::Local(get_payload_response.into()))
|
||||
.and_then(GetPayloadResponse::try_into)
|
||||
.map(ProvenancedPayload::Local)
|
||||
}
|
||||
};
|
||||
|
||||
@ -856,7 +888,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
let ((relay_result, relay_duration), (local_result, local_duration)) = tokio::join!(
|
||||
timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
|
||||
builder
|
||||
.get_builder_header::<T, Payload>(slot, parent_hash, &pubkey)
|
||||
.get_builder_header::<T>(slot, parent_hash, &pubkey)
|
||||
.await
|
||||
}),
|
||||
timed_future(metrics::GET_BLINDED_PAYLOAD_LOCAL, async {
|
||||
@ -874,7 +906,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
self.log(),
|
||||
"Requested blinded execution payload";
|
||||
"relay_fee_recipient" => match &relay_result {
|
||||
Ok(Some(r)) => format!("{:?}", r.data.message.header.fee_recipient()),
|
||||
Ok(Some(r)) => format!("{:?}", r.data.message.header().fee_recipient()),
|
||||
Ok(None) => "empty response".to_string(),
|
||||
Err(_) => "request failed".to_string(),
|
||||
},
|
||||
@ -897,7 +929,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"local_block_hash" => ?local.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.into()))
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
}
|
||||
(Ok(None), Ok(local)) => {
|
||||
info!(
|
||||
@ -907,10 +939,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"local_block_hash" => ?local.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.into()))
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
}
|
||||
(Ok(Some(relay)), Ok(local)) => {
|
||||
let header = &relay.data.message.header;
|
||||
let header = &relay.data.message.header();
|
||||
|
||||
info!(
|
||||
self.log(),
|
||||
@ -920,21 +952,21 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
|
||||
let relay_value = relay.data.message.value;
|
||||
let relay_value = relay.data.message.value();
|
||||
let local_value = *local.block_value();
|
||||
|
||||
if !self.inner.always_prefer_builder_payload {
|
||||
if local_value >= relay_value {
|
||||
if local_value >= *relay_value {
|
||||
info!(
|
||||
self.log(),
|
||||
"Local block is more profitable than relay block";
|
||||
"local_block_value" => %local_value,
|
||||
"relay_value" => %relay_value
|
||||
);
|
||||
return Ok(ProvenancedPayload::Local(local.into()));
|
||||
return Ok(ProvenancedPayload::Local(local.try_into()?));
|
||||
} else if local.should_override_builder().unwrap_or(false) {
|
||||
let percentage_difference =
|
||||
percentage_difference_u256(local_value, relay_value);
|
||||
percentage_difference_u256(local_value, *relay_value);
|
||||
if percentage_difference.map_or(false, |percentage| {
|
||||
percentage
|
||||
< self
|
||||
@ -947,7 +979,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"local_block_value" => %local_value,
|
||||
"relay_value" => %relay_value
|
||||
);
|
||||
return Ok(ProvenancedPayload::Local(local.into()));
|
||||
return Ok(ProvenancedPayload::Local(local.try_into()?));
|
||||
}
|
||||
} else {
|
||||
info!(
|
||||
@ -968,12 +1000,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
current_fork,
|
||||
spec,
|
||||
) {
|
||||
Ok(()) => Ok(ProvenancedPayload::Builder(
|
||||
BlockProposalContents::Payload {
|
||||
payload: relay.data.message.header,
|
||||
block_value: relay.data.message.value,
|
||||
},
|
||||
)),
|
||||
Ok(()) => Ok(ProvenancedPayload::try_from(relay.data.message)?),
|
||||
Err(reason) if !reason.payload_invalid() => {
|
||||
info!(
|
||||
self.log(),
|
||||
@ -983,7 +1010,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"relay_block_hash" => ?header.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.into()))
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
}
|
||||
Err(reason) => {
|
||||
metrics::inc_counter_vec(
|
||||
@ -998,12 +1025,12 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"relay_block_hash" => ?header.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.into()))
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
(Ok(Some(relay)), Err(local_error)) => {
|
||||
let header = &relay.data.message.header;
|
||||
let header = &relay.data.message.header();
|
||||
|
||||
info!(
|
||||
self.log(),
|
||||
@ -1022,20 +1049,12 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
current_fork,
|
||||
spec,
|
||||
) {
|
||||
Ok(()) => Ok(ProvenancedPayload::Builder(
|
||||
BlockProposalContents::Payload {
|
||||
payload: relay.data.message.header,
|
||||
block_value: relay.data.message.value,
|
||||
},
|
||||
)),
|
||||
Ok(()) => Ok(ProvenancedPayload::try_from(relay.data.message)?),
|
||||
// If the payload is valid then use it. The local EE failed
|
||||
// to produce a payload so we have no alternative.
|
||||
Err(e) if !e.payload_invalid() => Ok(ProvenancedPayload::Builder(
|
||||
BlockProposalContents::Payload {
|
||||
payload: relay.data.message.header,
|
||||
block_value: relay.data.message.value,
|
||||
},
|
||||
)),
|
||||
Err(e) if !e.payload_invalid() => {
|
||||
Ok(ProvenancedPayload::try_from(relay.data.message)?)
|
||||
}
|
||||
Err(reason) => {
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_BUILDER_REJECTIONS,
|
||||
@ -1103,7 +1122,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
current_fork,
|
||||
)
|
||||
.await
|
||||
.map(|get_payload_response| ProvenancedPayload::Local(get_payload_response.into()))
|
||||
.and_then(GetPayloadResponse::try_into)
|
||||
.map(ProvenancedPayload::Local)
|
||||
}
|
||||
|
||||
/// Get a full payload without caching its result in the execution layer's payload cache.
|
||||
@ -1148,7 +1168,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
payload_attributes: &PayloadAttributes,
|
||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||
current_fork: ForkName,
|
||||
f: fn(&ExecutionLayer<T>, ExecutionPayloadRef<T>) -> Option<ExecutionPayload<T>>,
|
||||
cache_fn: fn(
|
||||
&ExecutionLayer<T>,
|
||||
PayloadContentsRefTuple<T>,
|
||||
) -> Option<FullPayloadContents<T>>,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
self.engine()
|
||||
.request(move |engine| async move {
|
||||
@ -1227,7 +1250,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"suggested_fee_recipient" => ?payload_attributes.suggested_fee_recipient(),
|
||||
);
|
||||
}
|
||||
if f(self, payload_response.execution_payload_ref()).is_some() {
|
||||
if cache_fn(self, (payload_response.execution_payload_ref(), payload_response.blobs_bundle().ok())).is_some() {
|
||||
warn!(
|
||||
self.log(),
|
||||
"Duplicate payload cached, this might indicate redundant proposal \
|
||||
@ -1859,7 +1882,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&self,
|
||||
block_root: Hash256,
|
||||
block: &SignedBlockContents<T, BlindedPayload<T>>,
|
||||
) -> Result<ExecutionPayload<T>, Error> {
|
||||
) -> Result<FullPayloadContents<T>, Error> {
|
||||
debug!(
|
||||
self.log(),
|
||||
"Sending block to builder";
|
||||
@ -1878,11 +1901,12 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
.await;
|
||||
|
||||
match &payload_result {
|
||||
Ok(payload) => {
|
||||
Ok(unblinded_response) => {
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_BUILDER_REVEAL_PAYLOAD_OUTCOME,
|
||||
&[metrics::SUCCESS],
|
||||
);
|
||||
let payload = unblinded_response.payload_ref();
|
||||
info!(
|
||||
self.log(),
|
||||
"Builder successfully revealed payload";
|
||||
@ -2025,8 +2049,8 @@ impl fmt::Display for InvalidBuilderPayload {
|
||||
}
|
||||
|
||||
/// Perform some cursory, non-exhaustive validation of the bid returned from the builder.
|
||||
fn verify_builder_bid<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
bid: &ForkVersionedResponse<SignedBuilderBid<T, Payload>>,
|
||||
fn verify_builder_bid<T: EthSpec>(
|
||||
bid: &ForkVersionedResponse<SignedBuilderBid<T>>,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
payload_attributes: &PayloadAttributes,
|
||||
block_number: Option<u64>,
|
||||
@ -2035,11 +2059,11 @@ fn verify_builder_bid<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Box<InvalidBuilderPayload>> {
|
||||
let is_signature_valid = bid.data.verify_signature(spec);
|
||||
let header = &bid.data.message.header;
|
||||
let payload_value = bid.data.message.value;
|
||||
let header = &bid.data.message.header();
|
||||
let payload_value = bid.data.message.value();
|
||||
|
||||
// Avoid logging values that we can't represent with our Prometheus library.
|
||||
let payload_value_gwei = bid.data.message.value / 1_000_000_000;
|
||||
let payload_value_gwei = bid.data.message.value() / 1_000_000_000;
|
||||
if payload_value_gwei <= Uint256::from(i64::max_value()) {
|
||||
metrics::set_gauge_vec(
|
||||
&metrics::EXECUTION_LAYER_PAYLOAD_BIDS,
|
||||
@ -2053,12 +2077,12 @@ fn verify_builder_bid<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
.ok()
|
||||
.cloned()
|
||||
.map(|withdrawals| Withdrawals::<T>::from(withdrawals).tree_hash_root());
|
||||
let payload_withdrawals_root = header.withdrawals_root().ok();
|
||||
let payload_withdrawals_root = header.withdrawals_root().ok().copied();
|
||||
|
||||
if payload_value < profit_threshold {
|
||||
if *payload_value < profit_threshold {
|
||||
Err(Box::new(InvalidBuilderPayload::LowValue {
|
||||
profit_threshold,
|
||||
payload_value,
|
||||
payload_value: *payload_value,
|
||||
}))
|
||||
} else if header.parent_hash() != parent_hash {
|
||||
Err(Box::new(InvalidBuilderPayload::ParentHash {
|
||||
@ -2088,7 +2112,7 @@ fn verify_builder_bid<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
} else if !is_signature_valid {
|
||||
Err(Box::new(InvalidBuilderPayload::Signature {
|
||||
signature: bid.data.signature.clone(),
|
||||
pubkey: bid.data.message.pubkey,
|
||||
pubkey: *bid.data.message.pubkey(),
|
||||
}))
|
||||
} else if payload_withdrawals_root != expected_withdrawals_root {
|
||||
Err(Box::new(InvalidBuilderPayload::WithdrawalsRoot {
|
||||
@ -2197,8 +2221,8 @@ fn ethers_tx_to_ssz<T: EthSpec>(
|
||||
|
||||
fn noop<T: EthSpec>(
|
||||
_: &ExecutionLayer<T>,
|
||||
_: ExecutionPayloadRef<T>,
|
||||
) -> Option<ExecutionPayload<T>> {
|
||||
_: PayloadContentsRefTuple<T>,
|
||||
) -> Option<FullPayloadContents<T>> {
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
use eth2::types::FullPayloadContents;
|
||||
use lru::LruCache;
|
||||
use parking_lot::Mutex;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{EthSpec, ExecutionPayload, Hash256};
|
||||
use types::{EthSpec, Hash256};
|
||||
|
||||
pub const DEFAULT_PAYLOAD_CACHE_SIZE: usize = 10;
|
||||
|
||||
/// A cache mapping execution payloads by tree hash roots.
|
||||
pub struct PayloadCache<T: EthSpec> {
|
||||
payloads: Mutex<LruCache<PayloadCacheId, ExecutionPayload<T>>>,
|
||||
payloads: Mutex<LruCache<PayloadCacheId, FullPayloadContents<T>>>,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
@ -22,16 +23,16 @@ impl<T: EthSpec> Default for PayloadCache<T> {
|
||||
}
|
||||
|
||||
impl<T: EthSpec> PayloadCache<T> {
|
||||
pub fn put(&self, payload: ExecutionPayload<T>) -> Option<ExecutionPayload<T>> {
|
||||
let root = payload.tree_hash_root();
|
||||
pub fn put(&self, payload: FullPayloadContents<T>) -> Option<FullPayloadContents<T>> {
|
||||
let root = payload.payload_ref().tree_hash_root();
|
||||
self.payloads.lock().put(PayloadCacheId(root), payload)
|
||||
}
|
||||
|
||||
pub fn pop(&self, root: &Hash256) -> Option<ExecutionPayload<T>> {
|
||||
pub fn pop(&self, root: &Hash256) -> Option<FullPayloadContents<T>> {
|
||||
self.payloads.lock().pop(&PayloadCacheId(*root))
|
||||
}
|
||||
|
||||
pub fn get(&self, hash: &Hash256) -> Option<ExecutionPayload<T>> {
|
||||
pub fn get(&self, hash: &Hash256) -> Option<FullPayloadContents<T>> {
|
||||
self.payloads.lock().get(&PayloadCacheId(*hash)).cloned()
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
},
|
||||
ExecutionBlock, PayloadAttributes, PayloadId, PayloadStatusV1, PayloadStatusV1Status,
|
||||
},
|
||||
random_valid_tx, BlobsBundleV1, ExecutionBlockWithTransactions,
|
||||
random_valid_tx, ExecutionBlockWithTransactions,
|
||||
};
|
||||
use kzg::Kzg;
|
||||
use rand::thread_rng;
|
||||
@ -16,9 +16,9 @@ use std::sync::Arc;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::TreeHash;
|
||||
use types::{
|
||||
BlobSidecar, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella,
|
||||
ExecutionPayloadDeneb, ExecutionPayloadHeader, ExecutionPayloadMerge, ForkName, Hash256,
|
||||
Transactions, Uint256,
|
||||
BlobSidecar, BlobsBundle, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload,
|
||||
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadHeader, ExecutionPayloadMerge,
|
||||
ForkName, Hash256, Transactions, Uint256,
|
||||
};
|
||||
|
||||
use super::DEFAULT_TERMINAL_BLOCK;
|
||||
@ -128,7 +128,7 @@ pub struct ExecutionBlockGenerator<T: EthSpec> {
|
||||
/*
|
||||
* deneb stuff
|
||||
*/
|
||||
pub blobs_bundles: HashMap<PayloadId, BlobsBundleV1<T>>,
|
||||
pub blobs_bundles: HashMap<PayloadId, BlobsBundle<T>>,
|
||||
pub kzg: Option<Arc<Kzg<T::Kzg>>>,
|
||||
}
|
||||
|
||||
@ -406,7 +406,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
self.payload_ids.get(id).cloned()
|
||||
}
|
||||
|
||||
pub fn get_blobs_bundle(&mut self, id: &PayloadId) -> Option<BlobsBundleV1<T>> {
|
||||
pub fn get_blobs_bundle(&mut self, id: &PayloadId) -> Option<BlobsBundle<T>> {
|
||||
self.blobs_bundles.get(id).cloned()
|
||||
}
|
||||
|
||||
@ -630,8 +630,8 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
pub fn generate_random_blobs<T: EthSpec>(
|
||||
n_blobs: usize,
|
||||
kzg: &Kzg<T::Kzg>,
|
||||
) -> Result<(BlobsBundleV1<T>, Transactions<T>), String> {
|
||||
let mut bundle = BlobsBundleV1::<T>::default();
|
||||
) -> Result<(BlobsBundle<T>, Transactions<T>), String> {
|
||||
let mut bundle = BlobsBundle::<T>::default();
|
||||
let mut transactions = vec![];
|
||||
for blob_index in 0..n_blobs {
|
||||
let random_valid_sidecar = BlobSidecar::<T>::random_valid(&mut thread_rng(), kzg)?;
|
||||
|
@ -202,7 +202,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
assert_eq!(
|
||||
self.el
|
||||
.get_payload_by_root(&payload_header.tree_hash_root()),
|
||||
Some(payload.clone())
|
||||
Some(FullPayloadContents::Payload(payload.clone()))
|
||||
);
|
||||
|
||||
// TODO: again consider forks
|
||||
|
@ -1,22 +1,25 @@
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProductionError};
|
||||
use eth2::types::{BeaconBlockAndBlobSidecars, BlockContents};
|
||||
use std::sync::Arc;
|
||||
use types::{AbstractExecPayload, BeaconBlock, ForkName};
|
||||
use beacon_chain::BlockProductionError;
|
||||
use eth2::types::{BeaconBlockAndBlobSidecars, BlindedBeaconBlockAndBlobSidecars, BlockContents};
|
||||
use types::{
|
||||
BeaconBlock, BlindedBlobSidecarList, BlindedPayload, BlobSidecarList, EthSpec, ForkName,
|
||||
FullPayload,
|
||||
};
|
||||
|
||||
type Error = warp::reject::Rejection;
|
||||
type FullBlockContents<E> = BlockContents<E, FullPayload<E>>;
|
||||
type BlindedBlockContents<E> = BlockContents<E, BlindedPayload<E>>;
|
||||
|
||||
pub fn build_block_contents<T: BeaconChainTypes, Payload: AbstractExecPayload<T::EthSpec>>(
|
||||
pub fn build_block_contents<E: EthSpec>(
|
||||
fork_name: ForkName,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
block: BeaconBlock<T::EthSpec, Payload>,
|
||||
) -> Result<BlockContents<T::EthSpec, Payload>, Error> {
|
||||
block: BeaconBlock<E, FullPayload<E>>,
|
||||
maybe_blobs: Option<BlobSidecarList<E>>,
|
||||
) -> Result<FullBlockContents<E>, Error> {
|
||||
match fork_name {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
||||
Ok(BlockContents::Block(block))
|
||||
}
|
||||
ForkName::Deneb => {
|
||||
let block_root = &block.canonical_root();
|
||||
if let Some(blob_sidecars) = chain.proposal_blob_cache.pop(block_root) {
|
||||
if let Some(blob_sidecars) = maybe_blobs {
|
||||
let block_and_blobs = BeaconBlockAndBlobSidecars {
|
||||
block,
|
||||
blob_sidecars,
|
||||
@ -25,7 +28,33 @@ pub fn build_block_contents<T: BeaconChainTypes, Payload: AbstractExecPayload<T:
|
||||
Ok(BlockContents::BlockAndBlobSidecars(block_and_blobs))
|
||||
} else {
|
||||
Err(warp_utils::reject::block_production_error(
|
||||
BlockProductionError::NoBlobsCached,
|
||||
BlockProductionError::MissingBlobs,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_blinded_block_contents<E: EthSpec>(
|
||||
fork_name: ForkName,
|
||||
block: BeaconBlock<E, BlindedPayload<E>>,
|
||||
maybe_blobs: Option<BlindedBlobSidecarList<E>>,
|
||||
) -> Result<BlindedBlockContents<E>, Error> {
|
||||
match fork_name {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
||||
Ok(BlockContents::Block(block))
|
||||
}
|
||||
ForkName::Deneb => {
|
||||
if let Some(blinded_blob_sidecars) = maybe_blobs {
|
||||
let block_and_blobs = BlindedBeaconBlockAndBlobSidecars {
|
||||
blinded_block: block,
|
||||
blinded_blob_sidecars,
|
||||
};
|
||||
|
||||
Ok(BlockContents::BlindedBlockAndBlobSidecars(block_and_blobs))
|
||||
} else {
|
||||
Err(warp_utils::reject::block_production_error(
|
||||
BlockProductionError::MissingBlobs,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -1440,14 +1440,14 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(network_tx_filter.clone())
|
||||
.and(log_filter.clone())
|
||||
.and_then(
|
||||
|block: SignedBlockContents<T::EthSpec, BlindedPayload<_>>,
|
||||
|block_contents: SignedBlockContents<T::EthSpec, BlindedPayload<_>>,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
log: Logger| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
publish_blocks::publish_blinded_block(
|
||||
block,
|
||||
block_contents,
|
||||
chain,
|
||||
&network_tx,
|
||||
log,
|
||||
@ -3065,7 +3065,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
ProduceBlockVerification::VerifyRandao
|
||||
};
|
||||
|
||||
let (block, _) = chain
|
||||
let (block, _, maybe_blobs) = chain
|
||||
.produce_block_with_verification::<FullPayload<T::EthSpec>>(
|
||||
randao_reveal,
|
||||
slot,
|
||||
@ -3080,9 +3080,9 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.map_err(inconsistent_fork_rejection)?;
|
||||
|
||||
let block_contents =
|
||||
build_block_contents::build_block_contents(fork_name, chain, block);
|
||||
build_block_contents::build_block_contents(fork_name, block, maybe_blobs)?;
|
||||
|
||||
fork_versioned_response(endpoint_version, fork_name, block_contents?)
|
||||
fork_versioned_response(endpoint_version, fork_name, block_contents)
|
||||
.map(|response| warp::reply::json(&response).into_response())
|
||||
.map(|res| add_consensus_version_header(res, fork_name))
|
||||
})
|
||||
@ -3129,7 +3129,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
ProduceBlockVerification::VerifyRandao
|
||||
};
|
||||
|
||||
let (block, _) = chain
|
||||
let (block, _, maybe_blobs) = chain
|
||||
.produce_block_with_verification::<BlindedPayload<T::EthSpec>>(
|
||||
randao_reveal,
|
||||
slot,
|
||||
@ -3143,8 +3143,14 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.fork_name(&chain.spec)
|
||||
.map_err(inconsistent_fork_rejection)?;
|
||||
|
||||
let block_contents = build_block_contents::build_blinded_block_contents(
|
||||
fork_name,
|
||||
block,
|
||||
maybe_blobs,
|
||||
)?;
|
||||
|
||||
// Pose as a V2 endpoint so we return the fork `version`.
|
||||
fork_versioned_response(V2, fork_name, block)
|
||||
fork_versioned_response(V2, fork_name, block_contents)
|
||||
.map(|response| warp::reply::json(&response).into_response())
|
||||
.map(|res| add_consensus_version_header(res, fork_name))
|
||||
})
|
||||
|
@ -7,7 +7,7 @@ use beacon_chain::{
|
||||
IntoGossipVerifiedBlockContents, NotifyExecutionLayer,
|
||||
};
|
||||
use eth2::types::BroadcastValidation;
|
||||
use eth2::types::SignedBlockContents;
|
||||
use eth2::types::{FullPayloadContents, SignedBlockContents};
|
||||
use execution_layer::ProvenancedPayload;
|
||||
use lighthouse_network::PubsubMessage;
|
||||
use network::NetworkMessage;
|
||||
@ -267,15 +267,15 @@ pub async fn publish_block<T: BeaconChainTypes, B: IntoGossipVerifiedBlockConten
|
||||
/// Handles a request from the HTTP API for blinded blocks. This converts blinded blocks into full
|
||||
/// blocks before publishing.
|
||||
pub async fn publish_blinded_block<T: BeaconChainTypes>(
|
||||
block: SignedBlockContents<T::EthSpec, BlindedPayload<T::EthSpec>>,
|
||||
block_contents: SignedBlockContents<T::EthSpec, BlindedPayload<T::EthSpec>>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: &UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
log: Logger,
|
||||
validation_level: BroadcastValidation,
|
||||
) -> Result<(), Rejection> {
|
||||
let block_root = block.signed_block().canonical_root();
|
||||
let block_root = block_contents.signed_block().canonical_root();
|
||||
let full_block: ProvenancedBlock<T, SignedBlockContents<T::EthSpec>> =
|
||||
reconstruct_block(chain.clone(), block_root, block, log.clone()).await?;
|
||||
reconstruct_block(chain.clone(), block_root, block_contents, log.clone()).await?;
|
||||
publish_block::<T, _>(
|
||||
Some(block_root),
|
||||
full_block,
|
||||
@ -293,25 +293,21 @@ pub async fn publish_blinded_block<T: BeaconChainTypes>(
|
||||
pub async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
block_root: Hash256,
|
||||
block: SignedBlockContents<T::EthSpec, BlindedPayload<T::EthSpec>>,
|
||||
block_contents: SignedBlockContents<T::EthSpec, BlindedPayload<T::EthSpec>>,
|
||||
log: Logger,
|
||||
) -> Result<ProvenancedBlock<T, SignedBlockContents<T::EthSpec>>, Rejection> {
|
||||
let full_payload_opt = if let Ok(payload_header) =
|
||||
block.signed_block().message().body().execution_payload()
|
||||
{
|
||||
let block = block_contents.signed_block();
|
||||
let full_payload_opt = if let Ok(payload_header) = block.message().body().execution_payload() {
|
||||
let el = chain.execution_layer.as_ref().ok_or_else(|| {
|
||||
warp_utils::reject::custom_server_error("Missing execution layer".to_string())
|
||||
})?;
|
||||
|
||||
// If the execution block hash is zero, use an empty payload.
|
||||
let full_payload = if payload_header.block_hash() == ExecutionBlockHash::zero() {
|
||||
let full_payload_contents = if payload_header.block_hash() == ExecutionBlockHash::zero() {
|
||||
let payload = FullPayload::default_at_fork(
|
||||
chain.spec.fork_name_at_epoch(
|
||||
block
|
||||
.signed_block()
|
||||
.slot()
|
||||
.epoch(T::EthSpec::slots_per_epoch()),
|
||||
),
|
||||
chain
|
||||
.spec
|
||||
.fork_name_at_epoch(block.slot().epoch(T::EthSpec::slots_per_epoch())),
|
||||
)
|
||||
.map_err(|e| {
|
||||
warp_utils::reject::custom_server_error(format!(
|
||||
@ -319,7 +315,7 @@ pub async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
))
|
||||
})?
|
||||
.into();
|
||||
ProvenancedPayload::Local(payload)
|
||||
ProvenancedPayload::Local(FullPayloadContents::Payload(payload))
|
||||
// If we already have an execution payload with this transactions root cached, use it.
|
||||
} else if let Some(cached_payload) =
|
||||
el.get_payload_by_root(&payload_header.tree_hash_root())
|
||||
@ -336,14 +332,14 @@ pub async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
late_block_logging(
|
||||
&chain,
|
||||
timestamp_now(),
|
||||
block.signed_block().message(),
|
||||
block.message(),
|
||||
block_root,
|
||||
"builder",
|
||||
&log,
|
||||
);
|
||||
|
||||
let full_payload = el
|
||||
.propose_blinded_beacon_block(block_root, &block)
|
||||
.propose_blinded_beacon_block(block_root, &block_contents)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warp_utils::reject::custom_server_error(format!(
|
||||
@ -355,7 +351,7 @@ pub async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
ProvenancedPayload::Builder(full_payload)
|
||||
};
|
||||
|
||||
Some(full_payload)
|
||||
Some(full_payload_contents)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -363,23 +359,14 @@ pub async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
match full_payload_opt {
|
||||
// A block without a payload is pre-merge and we consider it locally
|
||||
// built.
|
||||
None => block
|
||||
.deconstruct()
|
||||
.0
|
||||
.try_into_full_block(None)
|
||||
.map(SignedBlockContents::Block)
|
||||
None => block_contents
|
||||
.try_into_full_block_and_blobs(None)
|
||||
.map(ProvenancedBlock::local),
|
||||
Some(ProvenancedPayload::Local(full_payload)) => block
|
||||
.deconstruct()
|
||||
.0
|
||||
.try_into_full_block(Some(full_payload))
|
||||
.map(SignedBlockContents::Block)
|
||||
Some(ProvenancedPayload::Local(full_payload_contents)) => block_contents
|
||||
.try_into_full_block_and_blobs(Some(full_payload_contents))
|
||||
.map(ProvenancedBlock::local),
|
||||
Some(ProvenancedPayload::Builder(full_payload)) => block
|
||||
.deconstruct()
|
||||
.0
|
||||
.try_into_full_block(Some(full_payload))
|
||||
.map(SignedBlockContents::Block)
|
||||
Some(ProvenancedPayload::Builder(full_payload_contents)) => block_contents
|
||||
.try_into_full_block_and_blobs(Some(full_payload_contents))
|
||||
.map(ProvenancedBlock::builder),
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
|
@ -2576,12 +2576,16 @@ impl ApiTester {
|
||||
.get_validator_blinded_blocks::<E, Payload>(slot, &randao_reveal, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
.data
|
||||
.deconstruct()
|
||||
.0;
|
||||
|
||||
let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
|
||||
let signed_block_contents =
|
||||
SignedBlockContents::<E, Payload>::Block(signed_block.clone());
|
||||
|
||||
self.client
|
||||
.post_beacon_blinded_blocks(&signed_block)
|
||||
.post_beacon_blinded_blocks(&signed_block_contents)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -2636,7 +2640,9 @@ impl ApiTester {
|
||||
.get_validator_blinded_blocks::<E, Payload>(slot, &randao_reveal, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
.data
|
||||
.deconstruct()
|
||||
.0;
|
||||
|
||||
let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
|
||||
|
||||
@ -2659,7 +2665,7 @@ impl ApiTester {
|
||||
for _ in 0..E::slots_per_epoch() {
|
||||
let slot = self.chain.slot().unwrap();
|
||||
|
||||
let block = self
|
||||
let block_contents = self
|
||||
.client
|
||||
.get_validator_blinded_blocks_modular::<E, Payload>(
|
||||
slot,
|
||||
@ -2670,7 +2676,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
assert_eq!(block.slot(), slot);
|
||||
assert_eq!(block_contents.block().slot(), slot);
|
||||
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
|
||||
}
|
||||
|
||||
@ -3206,6 +3212,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3246,6 +3253,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3289,6 +3297,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3338,6 +3347,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3386,6 +3396,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3433,6 +3444,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3479,6 +3491,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3515,6 +3528,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3552,6 +3566,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3595,6 +3610,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3624,6 +3640,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3673,6 +3690,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3712,6 +3730,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3755,6 +3774,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3796,6 +3816,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3833,6 +3854,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3870,6 +3892,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3907,6 +3930,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3957,6 +3981,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
@ -3999,6 +4024,7 @@ impl ApiTester {
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.block()
|
||||
.body()
|
||||
.execution_payload()
|
||||
.unwrap()
|
||||
|
@ -12,7 +12,6 @@ use beacon_chain::builder::Witness;
|
||||
use beacon_chain::eth1_chain::CachingEth1Backend;
|
||||
use beacon_chain::test_utils::{build_log, BeaconChainHarness, EphemeralHarnessType};
|
||||
use beacon_processor::WorkEvent;
|
||||
use execution_layer::BlobsBundleV1;
|
||||
use lighthouse_network::rpc::RPCResponseErrorCode;
|
||||
use lighthouse_network::{NetworkGlobals, Request};
|
||||
use slot_clock::{ManualSlotClock, SlotClock, TestingSlotClock};
|
||||
@ -21,8 +20,8 @@ use tokio::sync::mpsc;
|
||||
use types::{
|
||||
map_fork_name, map_fork_name_with,
|
||||
test_utils::{SeedableRng, TestRandom, XorShiftRng},
|
||||
BeaconBlock, BlobSidecar, EthSpec, ForkName, FullPayloadDeneb, MinimalEthSpec as E,
|
||||
SignedBeaconBlock,
|
||||
BeaconBlock, BlobSidecar, BlobsBundle, EthSpec, ForkName, FullPayloadDeneb,
|
||||
MinimalEthSpec as E, SignedBeaconBlock,
|
||||
};
|
||||
|
||||
type T = Witness<ManualSlotClock, CachingEth1Backend<E>, E, MemoryStore<E>, MemoryStore<E>>;
|
||||
@ -126,7 +125,7 @@ impl TestRig {
|
||||
}
|
||||
message.body.blob_kzg_commitments = bundle.commitments.clone();
|
||||
|
||||
let BlobsBundleV1 {
|
||||
let BlobsBundle {
|
||||
commitments,
|
||||
proofs,
|
||||
blobs,
|
||||
|
@ -9,6 +9,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
serde_json = "1.0.58"
|
||||
ssz_types = "0.5.4"
|
||||
types = { path = "../../consensus/types" }
|
||||
reqwest = { version = "0.11.0", features = ["json", "stream"] }
|
||||
lighthouse_network = { path = "../../beacon_node/lighthouse_network" }
|
||||
|
@ -727,7 +727,7 @@ impl BeaconNodeHttpClient {
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn post_beacon_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
block: &SignedBeaconBlock<T, Payload>,
|
||||
block: &SignedBlockContents<T, Payload>,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path(V1)?;
|
||||
|
||||
@ -1648,7 +1648,7 @@ impl BeaconNodeHttpClient {
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
|
||||
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
|
||||
self.get_validator_blinded_blocks_modular(
|
||||
slot,
|
||||
randao_reveal,
|
||||
@ -1668,7 +1668,7 @@ impl BeaconNodeHttpClient {
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
|
||||
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
|
||||
let mut path = self.eth_path(V1)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
|
@ -1361,28 +1361,41 @@ mod tests {
|
||||
}
|
||||
|
||||
/// A wrapper over a [`BeaconBlock`] or a [`BeaconBlockAndBlobSidecars`].
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub enum BlockContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
BlockAndBlobSidecars(BeaconBlockAndBlobSidecars<T, Payload>),
|
||||
BlindedBlockAndBlobSidecars(BlindedBeaconBlockAndBlobSidecars<T, Payload>),
|
||||
Block(BeaconBlock<T, Payload>),
|
||||
}
|
||||
|
||||
pub type BlockContentsTuple<T, Payload> = (
|
||||
BeaconBlock<T, Payload>,
|
||||
Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
|
||||
);
|
||||
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockContents<T, Payload> {
|
||||
pub fn block(&self) -> &BeaconBlock<T, Payload> {
|
||||
match self {
|
||||
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => &block_and_sidecars.block,
|
||||
BlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
|
||||
&block_and_sidecars.blinded_block
|
||||
}
|
||||
BlockContents::Block(block) => block,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deconstruct(self) -> (BeaconBlock<T, Payload>, Option<BlobSidecarList<T>>) {
|
||||
pub fn deconstruct(self) -> BlockContentsTuple<T, Payload> {
|
||||
match self {
|
||||
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => (
|
||||
block_and_sidecars.block,
|
||||
Some(block_and_sidecars.blob_sidecars),
|
||||
),
|
||||
BlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => (
|
||||
block_and_sidecars.blinded_block,
|
||||
Some(block_and_sidecars.blinded_blob_sidecars),
|
||||
),
|
||||
BlockContents::Block(block) => (block, None),
|
||||
}
|
||||
}
|
||||
@ -1415,14 +1428,17 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> Into<BeaconBlock<T, Payload>>
|
||||
fn into(self) -> BeaconBlock<T, Payload> {
|
||||
match self {
|
||||
Self::BlockAndBlobSidecars(block_and_sidecars) => block_and_sidecars.block,
|
||||
Self::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
|
||||
block_and_sidecars.blinded_block
|
||||
}
|
||||
Self::Block(block) => block,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type BlockContentsTuple<T, Payload> = (
|
||||
pub type SignedBlockContentsTuple<T, Payload> = (
|
||||
SignedBeaconBlock<T, Payload>,
|
||||
Option<SignedBlobSidecarList<T>>,
|
||||
Option<SignedSidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
|
||||
);
|
||||
|
||||
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBeaconBlockAndBlobSidecars`].
|
||||
@ -1432,21 +1448,29 @@ pub type BlockContentsTuple<T, Payload> = (
|
||||
#[ssz(enum_behaviour = "transparent")]
|
||||
pub enum SignedBlockContents<T: EthSpec, Payload: AbstractExecPayload<T> = FullPayload<T>> {
|
||||
BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars<T, Payload>),
|
||||
BlindedBlockAndBlobSidecars(SignedBlindedBeaconBlockAndBlobSidecars<T, Payload>),
|
||||
Block(SignedBeaconBlock<T, Payload>),
|
||||
}
|
||||
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedBlockContents<T, Payload> {
|
||||
pub fn new(
|
||||
block: SignedBeaconBlock<T, Payload>,
|
||||
blobs: Option<SignedBlobSidecarList<T>>,
|
||||
blobs: Option<SignedSidecarList<T, Payload::Sidecar>>,
|
||||
) -> Self {
|
||||
if let Some(blobs) = blobs {
|
||||
Self::BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars {
|
||||
signed_block: block,
|
||||
signed_blob_sidecars: blobs,
|
||||
})
|
||||
} else {
|
||||
Self::Block(block)
|
||||
match (Payload::block_type(), blobs) {
|
||||
(BlockType::Blinded, Some(blobs)) => {
|
||||
Self::BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars {
|
||||
signed_block: block,
|
||||
signed_blob_sidecars: blobs,
|
||||
})
|
||||
}
|
||||
(BlockType::Full, Some(blobs)) => {
|
||||
Self::BlindedBlockAndBlobSidecars(SignedBlindedBeaconBlockAndBlobSidecars {
|
||||
signed_blinded_block: block,
|
||||
signed_blinded_blob_sidecars: blobs,
|
||||
})
|
||||
}
|
||||
(_, None) => Self::Block(block),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1462,30 +1486,88 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedBlockContents<T, Payload
|
||||
SignedBlockContents::BlockAndBlobSidecars(block_and_sidecars) => {
|
||||
&block_and_sidecars.signed_block
|
||||
}
|
||||
SignedBlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
|
||||
&block_and_sidecars.signed_blinded_block
|
||||
}
|
||||
SignedBlockContents::Block(block) => block,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blobs_cloned(&self) -> Option<SignedBlobSidecarList<T>> {
|
||||
pub fn blobs_cloned(&self) -> Option<SignedSidecarList<T, Payload::Sidecar>> {
|
||||
match self {
|
||||
SignedBlockContents::BlockAndBlobSidecars(block_and_sidecars) => {
|
||||
Some(block_and_sidecars.signed_blob_sidecars.clone())
|
||||
}
|
||||
SignedBlockContents::Block(_block) => None,
|
||||
SignedBlockContents::BlindedBlockAndBlobSidecars(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deconstruct(self) -> BlockContentsTuple<T, Payload> {
|
||||
pub fn deconstruct(self) -> SignedBlockContentsTuple<T, Payload> {
|
||||
match self {
|
||||
SignedBlockContents::BlockAndBlobSidecars(block_and_sidecars) => (
|
||||
block_and_sidecars.signed_block,
|
||||
Some(block_and_sidecars.signed_blob_sidecars),
|
||||
),
|
||||
SignedBlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => (
|
||||
block_and_sidecars.signed_blinded_block,
|
||||
Some(block_and_sidecars.signed_blinded_blob_sidecars),
|
||||
),
|
||||
SignedBlockContents::Block(block) => (block, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> SignedBlockContents<T, BlindedPayload<T>> {
|
||||
pub fn try_into_full_block_and_blobs(
|
||||
self,
|
||||
maybe_full_payload_contents: Option<FullPayloadContents<T>>,
|
||||
) -> Option<SignedBlockContents<T, FullPayload<T>>> {
|
||||
match self {
|
||||
SignedBlockContents::BlindedBlockAndBlobSidecars(blinded_block_and_blob_sidecars) => {
|
||||
maybe_full_payload_contents.and_then(|full_payload_contents| {
|
||||
match full_payload_contents.deconstruct() {
|
||||
(full_payload, Some(blobs_bundle)) => {
|
||||
let maybe_full_block = blinded_block_and_blob_sidecars
|
||||
.signed_blinded_block
|
||||
.try_into_full_block(Some(full_payload));
|
||||
let full_blob_sidecars: Vec<_> = blinded_block_and_blob_sidecars
|
||||
.signed_blinded_blob_sidecars
|
||||
.into_iter()
|
||||
.zip(blobs_bundle.blobs)
|
||||
.map(|(blinded_blob_sidecar, blob)| {
|
||||
blinded_blob_sidecar.into_full_blob_sidecars(blob)
|
||||
})
|
||||
.collect();
|
||||
|
||||
maybe_full_block.map(|signed_block| {
|
||||
SignedBlockContents::BlockAndBlobSidecars(
|
||||
SignedBeaconBlockAndBlobSidecars {
|
||||
signed_block,
|
||||
signed_blob_sidecars: VariableList::from(
|
||||
full_blob_sidecars,
|
||||
),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
// Can't build full block contents without full blobs
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
SignedBlockContents::Block(blinded_block) => {
|
||||
let full_payload_opt = maybe_full_payload_contents.map(|o| o.deconstruct().0);
|
||||
blinded_block
|
||||
.try_into_full_block(full_payload_opt)
|
||||
.map(SignedBlockContents::Block)
|
||||
}
|
||||
// Unexpected scenario for blinded block proposal
|
||||
SignedBlockContents::BlockAndBlobSidecars(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> TryFrom<SignedBeaconBlock<T, Payload>>
|
||||
for SignedBlockContents<T, Payload>
|
||||
{
|
||||
@ -1503,18 +1585,26 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> TryFrom<SignedBeaconBlock<T, P
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> From<BlockContentsTuple<T, Payload>>
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> From<SignedBlockContentsTuple<T, Payload>>
|
||||
for SignedBlockContents<T, Payload>
|
||||
{
|
||||
fn from(block_contents_tuple: BlockContentsTuple<T, Payload>) -> Self {
|
||||
fn from(block_contents_tuple: SignedBlockContentsTuple<T, Payload>) -> Self {
|
||||
match block_contents_tuple {
|
||||
(signed_block, None) => SignedBlockContents::Block(signed_block),
|
||||
(signed_block, Some(signed_blob_sidecars)) => {
|
||||
SignedBlockContents::BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars {
|
||||
signed_block,
|
||||
signed_blob_sidecars,
|
||||
})
|
||||
}
|
||||
(signed_block, Some(signed_blob_sidecars)) => match Payload::block_type() {
|
||||
BlockType::Blinded => SignedBlockContents::BlindedBlockAndBlobSidecars(
|
||||
SignedBlindedBeaconBlockAndBlobSidecars {
|
||||
signed_blinded_block: signed_block,
|
||||
signed_blinded_blob_sidecars: signed_blob_sidecars,
|
||||
},
|
||||
),
|
||||
BlockType::Full => {
|
||||
SignedBlockContents::BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars {
|
||||
signed_block,
|
||||
signed_blob_sidecars,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1523,14 +1613,14 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> From<BlockContentsTuple<T, Pay
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct SignedBeaconBlockAndBlobSidecars<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
pub signed_block: SignedBeaconBlock<T, Payload>,
|
||||
pub signed_blob_sidecars: SignedBlobSidecarList<T>,
|
||||
pub signed_blob_sidecars: SignedSidecarList<T, Payload::Sidecar>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
|
||||
#[serde(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
|
||||
pub struct BeaconBlockAndBlobSidecars<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
pub block: BeaconBlock<T, Payload>,
|
||||
pub blob_sidecars: BlobSidecarList<T>,
|
||||
pub blob_sidecars: SidecarList<T, Payload::Sidecar>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
|
||||
@ -1541,12 +1631,13 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
|
||||
fork_name: ForkName,
|
||||
) -> Result<Self, D::Error> {
|
||||
#[derive(Deserialize)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
struct Helper<T: EthSpec> {
|
||||
#[serde(bound = "T: EthSpec, S: Sidecar<T>")]
|
||||
struct Helper<T: EthSpec, S: Sidecar<T>> {
|
||||
block: serde_json::Value,
|
||||
blob_sidecars: BlobSidecarList<T>,
|
||||
blob_sidecars: SidecarList<T, S>,
|
||||
}
|
||||
let helper: Helper<T> = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
|
||||
let helper: Helper<T, Payload::Sidecar> =
|
||||
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
|
||||
|
||||
Ok(Self {
|
||||
block: BeaconBlock::deserialize_by_fork::<'de, D>(helper.block, fork_name)?,
|
||||
@ -1554,3 +1645,49 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct SignedBlindedBeaconBlockAndBlobSidecars<
|
||||
T: EthSpec,
|
||||
Payload: AbstractExecPayload<T> = BlindedPayload<T>,
|
||||
> {
|
||||
pub signed_blinded_block: SignedBeaconBlock<T, Payload>,
|
||||
pub signed_blinded_blob_sidecars: SignedSidecarList<T, Payload::Sidecar>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
|
||||
#[serde(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
|
||||
pub struct BlindedBeaconBlockAndBlobSidecars<
|
||||
T: EthSpec,
|
||||
Payload: AbstractExecPayload<T> = BlindedPayload<T>,
|
||||
> {
|
||||
pub blinded_block: BeaconBlock<T, Payload>,
|
||||
pub blinded_blob_sidecars: SidecarList<T, Payload::Sidecar>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
|
||||
for BlindedBeaconBlockAndBlobSidecars<T, Payload>
|
||||
{
|
||||
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
|
||||
value: serde_json::value::Value,
|
||||
fork_name: ForkName,
|
||||
) -> Result<Self, D::Error> {
|
||||
#[derive(Deserialize)]
|
||||
#[serde(bound = "T: EthSpec, S: Sidecar<T>")]
|
||||
struct Helper<T: EthSpec, S: Sidecar<T>> {
|
||||
blinded_block: serde_json::Value,
|
||||
blinded_blob_sidecars: SidecarList<T, S>,
|
||||
}
|
||||
let helper: Helper<T, Payload::Sidecar> =
|
||||
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
|
||||
|
||||
Ok(Self {
|
||||
blinded_block: BeaconBlock::deserialize_by_fork::<'de, D>(
|
||||
helper.blinded_block,
|
||||
fork_name,
|
||||
)?,
|
||||
blinded_blob_sidecars: helper.blinded_blob_sidecars,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ superstruct = "0.6.0"
|
||||
metastruct = "0.1.0"
|
||||
serde_json = "1.0.74"
|
||||
smallvec = "1.8.0"
|
||||
serde_with = "1.13.0"
|
||||
maplit = "1.0.2"
|
||||
strum = { version = "0.24.0", features = ["derive"] }
|
||||
|
||||
|
@ -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`.
|
||||
|
@ -525,7 +525,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
|
||||
Some(blob_sidecars) => {
|
||||
match self_ref
|
||||
.validator_store
|
||||
.sign_blobs(*validator_pubkey_ref, blob_sidecars)
|
||||
.sign_blobs::<Payload>(*validator_pubkey_ref, blob_sidecars)
|
||||
.await
|
||||
{
|
||||
Ok(signed_blobs) => Some(signed_blobs),
|
||||
@ -620,16 +620,15 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
|
||||
&metrics::BLOCK_SERVICE_TIMES,
|
||||
&[metrics::BLINDED_BEACON_BLOCK_HTTP_POST],
|
||||
);
|
||||
todo!("need to be adjusted for blobs");
|
||||
// beacon_node
|
||||
// .post_beacon_blinded_blocks(signed_block_contents.signed_block())
|
||||
// .await
|
||||
// .map_err(|e| {
|
||||
// BlockError::Irrecoverable(format!(
|
||||
// "Error from beacon node when publishing block: {:?}",
|
||||
// e
|
||||
// ))
|
||||
// })?
|
||||
beacon_node
|
||||
.post_beacon_blinded_blocks(signed_block_contents)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
BlockError::Irrecoverable(format!(
|
||||
"Error from beacon node when publishing block: {:?}",
|
||||
e
|
||||
))
|
||||
})?
|
||||
}
|
||||
}
|
||||
Ok::<_, BlockError>(())
|
||||
@ -665,21 +664,20 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
|
||||
&metrics::BLOCK_SERVICE_TIMES,
|
||||
&[metrics::BLINDED_BEACON_BLOCK_HTTP_GET],
|
||||
);
|
||||
todo!("implement blinded flow for blobs");
|
||||
// beacon_node
|
||||
// .get_validator_blinded_blocks::<E, Payload>(
|
||||
// slot,
|
||||
// randao_reveal_ref,
|
||||
// graffiti.as_ref(),
|
||||
// )
|
||||
// .await
|
||||
// .map_err(|e| {
|
||||
// BlockError::Recoverable(format!(
|
||||
// "Error from beacon node when producing block: {:?}",
|
||||
// e
|
||||
// ))
|
||||
// })?
|
||||
// .data
|
||||
beacon_node
|
||||
.get_validator_blinded_blocks::<E, Payload>(
|
||||
slot,
|
||||
randao_reveal_ref,
|
||||
graffiti.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
BlockError::Recoverable(format!(
|
||||
"Error from beacon node when producing block: {:?}",
|
||||
e
|
||||
))
|
||||
})?
|
||||
.data
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -37,7 +37,7 @@ pub enum Error {
|
||||
pub enum SignableMessage<'a, T: EthSpec, Payload: AbstractExecPayload<T> = FullPayload<T>> {
|
||||
RandaoReveal(Epoch),
|
||||
BeaconBlock(&'a BeaconBlock<T, Payload>),
|
||||
BlobSidecar(&'a BlobSidecar<T>),
|
||||
BlobSidecar(&'a Payload::Sidecar),
|
||||
AttestationData(&'a AttestationData),
|
||||
SignedAggregateAndProof(&'a AggregateAndProof<T>),
|
||||
SelectionProof(Slot),
|
||||
|
@ -20,10 +20,10 @@ use std::sync::Arc;
|
||||
use task_executor::TaskExecutor;
|
||||
use types::{
|
||||
attestation::Error as AttestationError, graffiti::GraffitiString, AbstractExecPayload, Address,
|
||||
AggregateAndProof, Attestation, BeaconBlock, BlindedPayload, BlobSidecarList, ChainSpec,
|
||||
ContributionAndProof, Domain, Epoch, EthSpec, Fork, ForkName, Graffiti, Hash256, Keypair,
|
||||
PublicKeyBytes, SelectionProof, Signature, SignedAggregateAndProof, SignedBeaconBlock,
|
||||
SignedBlobSidecar, SignedBlobSidecarList, SignedContributionAndProof, SignedRoot,
|
||||
AggregateAndProof, Attestation, BeaconBlock, BlindedPayload, ChainSpec, ContributionAndProof,
|
||||
Domain, Epoch, EthSpec, Fork, ForkName, Graffiti, Hash256, Keypair, PublicKeyBytes,
|
||||
SelectionProof, Sidecar, SidecarList, Signature, SignedAggregateAndProof, SignedBeaconBlock,
|
||||
SignedContributionAndProof, SignedRoot, SignedSidecar, SignedSidecarList,
|
||||
SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncAggregatorSelectionData,
|
||||
SyncCommitteeContribution, SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId,
|
||||
ValidatorRegistrationData, VoluntaryExit,
|
||||
@ -566,22 +566,21 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sign_blobs(
|
||||
pub async fn sign_blobs<Payload: AbstractExecPayload<E>>(
|
||||
&self,
|
||||
validator_pubkey: PublicKeyBytes,
|
||||
blob_sidecars: BlobSidecarList<E>,
|
||||
) -> Result<SignedBlobSidecarList<E>, Error> {
|
||||
blob_sidecars: SidecarList<E, Payload::Sidecar>,
|
||||
) -> Result<SignedSidecarList<E, Payload::Sidecar>, Error> {
|
||||
let mut signed_blob_sidecars = Vec::new();
|
||||
|
||||
for blob_sidecar in blob_sidecars.into_iter() {
|
||||
let slot = blob_sidecar.slot;
|
||||
let slot = blob_sidecar.slot();
|
||||
let signing_epoch = slot.epoch(E::slots_per_epoch());
|
||||
let signing_context = self.signing_context(Domain::BlobSidecar, signing_epoch);
|
||||
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
|
||||
|
||||
let signature = signing_method
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::BlobSidecar(&blob_sidecar),
|
||||
.get_signature::<E, Payload>(
|
||||
SignableMessage::BlobSidecar(blob_sidecar.as_ref()),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
&self.task_executor,
|
||||
@ -590,9 +589,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
|
||||
metrics::inc_counter_vec(&metrics::SIGNED_BLOBS_TOTAL, &[metrics::SUCCESS]);
|
||||
|
||||
signed_blob_sidecars.push(SignedBlobSidecar {
|
||||
signed_blob_sidecars.push(SignedSidecar {
|
||||
message: blob_sidecar,
|
||||
signature,
|
||||
_phantom: PhantomData,
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user