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