diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 1e901987a..277194105 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -4759,30 +4759,41 @@ impl BeaconChain { .kzg .as_ref() .ok_or(BlockProductionError::TrustedSetupNotInitialized)?; - let kzg_aggregated_proof = - kzg_utils::compute_aggregate_kzg_proof::(kzg, &blobs) - .map_err(BlockProductionError::KzgError)?; let beacon_block_root = block.canonical_root(); let expected_kzg_commitments = block.body().blob_kzg_commitments().map_err(|_| { BlockProductionError::InvalidBlockVariant( "EIP4844 block does not contain kzg commitments".to_string(), ) })?; - let blobs_sidecar = BlobsSidecar { - beacon_block_slot: slot, - beacon_block_root, - blobs, - kzg_aggregated_proof, - }; - kzg_utils::validate_blobs_sidecar( - kzg, - slot, - beacon_block_root, - expected_kzg_commitments, - &blobs_sidecar, - ) - .map_err(BlockProductionError::KzgError)?; - self.blob_cache.put(beacon_block_root, blobs_sidecar); + + let blob_sidecars = VariableList::from( + blobs + .into_iter() + .enumerate() + .map(|(blob_index, blob)| { + BlobSidecar { + block_root: beacon_block_root, + index: blob_index as u64, + slot, + block_parent_root: block.parent_root(), + proposer_index, + blob, + kzg_commitment: expected_kzg_commitments[blob_index].clone(), + kzg_proof: Default::default(), // TODO: compute KZG proof + } + }) + .collect::>>(), + ); + + // TODO: validate blobs + // kzg_utils::validate_blobs_sidecar( + // kzg, + // slot, + // beacon_block_root, + // expected_kzg_commitments, + // &blobs_sidecar, + // ).map_err(BlockProductionError::KzgError)?; + self.blob_cache.put(beacon_block_root, blob_sidecars); } metrics::inc_counter(&metrics::BLOCK_PRODUCTION_SUCCESSES); diff --git a/beacon_node/beacon_chain/src/blob_cache.rs b/beacon_node/beacon_chain/src/blob_cache.rs index d03e62ab6..e3b0a06b6 100644 --- a/beacon_node/beacon_chain/src/blob_cache.rs +++ b/beacon_node/beacon_chain/src/blob_cache.rs @@ -1,12 +1,12 @@ use lru::LruCache; use parking_lot::Mutex; -use types::{BlobsSidecar, EthSpec, Hash256}; +use types::{BlobSidecars, EthSpec, Hash256}; pub const DEFAULT_BLOB_CACHE_SIZE: usize = 10; /// A cache blobs by beacon block root. pub struct BlobCache { - blobs: Mutex>>, + blobs: Mutex>>, } #[derive(Hash, PartialEq, Eq)] @@ -21,11 +21,11 @@ impl Default for BlobCache { } impl BlobCache { - pub fn put(&self, beacon_block: Hash256, blobs: BlobsSidecar) -> Option> { + pub fn put(&self, beacon_block: Hash256, blobs: BlobSidecars) -> Option> { self.blobs.lock().put(BlobCacheId(beacon_block), blobs) } - pub fn pop(&self, root: &Hash256) -> Option> { + pub fn pop(&self, root: &Hash256) -> Option> { self.blobs.lock().pop(&BlobCacheId(*root)) } } diff --git a/beacon_node/beacon_chain/src/errors.rs b/beacon_node/beacon_chain/src/errors.rs index 3b4c46249..8f9aa0293 100644 --- a/beacon_node/beacon_chain/src/errors.rs +++ b/beacon_node/beacon_chain/src/errors.rs @@ -266,6 +266,7 @@ pub enum BlockProductionError { blob_block_hash: ExecutionBlockHash, payload_block_hash: ExecutionBlockHash, }, + NoBlobsCached, FailedToReadFinalizedBlock(store::Error), MissingFinalizedBlock(Hash256), BlockTooLarge(usize), diff --git a/beacon_node/http_api/src/build_block_contents.rs b/beacon_node/http_api/src/build_block_contents.rs new file mode 100644 index 000000000..d456c320a --- /dev/null +++ b/beacon_node/http_api/src/build_block_contents.rs @@ -0,0 +1,34 @@ +use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProductionError}; +use std::sync::Arc; +use types::{ + AbstractExecPayload, BeaconBlock, BeaconBlockAndBlobSidecars, BlockContents, ForkName, +}; + +type Error = warp::reject::Rejection; + +pub fn build_block_contents>( + fork_name: ForkName, + chain: Arc>, + block: BeaconBlock, +) -> Result, Error> { + match fork_name { + ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + Ok(BlockContents::Block(block)) + } + ForkName::Eip4844 => { + let block_root = &block.canonical_root(); + if let Some(blob_sidecars) = chain.blob_cache.pop(block_root) { + let block_and_blobs = BeaconBlockAndBlobSidecars { + block, + blob_sidecars, + }; + + Ok(BlockContents::BlockAndBlobSidecars(block_and_blobs)) + } else { + return Err(warp_utils::reject::block_production_error( + BlockProductionError::NoBlobsCached, + )); + } + } + } +} diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index a089b6e97..ea3985906 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -11,6 +11,7 @@ mod attester_duties; mod block_id; mod block_packing_efficiency; mod block_rewards; +mod build_block_contents; mod database; mod metrics; mod proposer_duties; @@ -2421,7 +2422,10 @@ pub fn serve( .fork_name(&chain.spec) .map_err(inconsistent_fork_rejection)?; - fork_versioned_response(endpoint_version, fork_name, block) + let block_contents = + build_block_contents::build_block_contents(fork_name, chain, block); + + fork_versioned_response(endpoint_version, fork_name, block_contents?) .map(|response| warp::reply::json(&response)) }, ); diff --git a/beacon_node/http_api/src/publish_blocks.rs b/beacon_node/http_api/src/publish_blocks.rs index 324943673..1e785cd1e 100644 --- a/beacon_node/http_api/src/publish_blocks.rs +++ b/beacon_node/http_api/src/publish_blocks.rs @@ -12,7 +12,7 @@ use tokio::sync::mpsc::UnboundedSender; use tree_hash::TreeHash; use types::{ AbstractExecPayload, BlindedPayload, EthSpec, ExecPayload, ExecutionBlockHash, FullPayload, - Hash256, SignedBeaconBlock, SignedBeaconBlockAndBlobsSidecar, + Hash256, SignedBeaconBlock, }; use warp::Rejection; @@ -40,10 +40,11 @@ pub async fn publish_block( let wrapped_block: BlockWrapper = if matches!(block.as_ref(), &SignedBeaconBlock::Eip4844(_)) { if let Some(sidecar) = chain.blob_cache.pop(&block_root) { - let block_and_blobs = SignedBeaconBlockAndBlobsSidecar { - beacon_block: block, - blobs_sidecar: Arc::new(sidecar), - }; + // TODO: Needs to be adjusted + // let block_and_blobs = SignedBeaconBlockAndBlobsSidecar { + // beacon_block: block, + // blobs_sidecar: Arc::new(sidecar), + // }; unimplemented!("Needs to be adjusted") } else { //FIXME(sean): This should probably return a specific no-blob-cached error code, beacon API coordination required diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 4535546a9..5a55f21e3 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -1388,7 +1388,7 @@ impl BeaconNodeHttpClient { slot: Slot, randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, - ) -> Result>, Error> { + ) -> Result>, Error> { self.get_validator_blocks_modular(slot, randao_reveal, graffiti, SkipRandaoVerification::No) .await } @@ -1400,7 +1400,7 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, - ) -> Result>, Error> { + ) -> Result>, Error> { let mut path = self.eth_path(V2)?; path.path_segments_mut() diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index 0f26cd0e5..a92926582 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -731,6 +731,17 @@ impl From>> } } +impl> From> + for BeaconBlock +{ + fn from(block_contents: BlockContents) -> Self { + match block_contents { + BlockContents::BlockAndBlobSidecars(block_and_sidecars) => block_and_sidecars.block, + BlockContents::Block(block) => block, + } + } +} + impl> ForkVersionDeserialize for BeaconBlock { diff --git a/consensus/types/src/beacon_block_and_blob_sidecars.rs b/consensus/types/src/beacon_block_and_blob_sidecars.rs new file mode 100644 index 000000000..c518f765b --- /dev/null +++ b/consensus/types/src/beacon_block_and_blob_sidecars.rs @@ -0,0 +1,37 @@ +use crate::{ + AbstractExecPayload, BeaconBlock, BlobSidecars, EthSpec, ForkName, ForkVersionDeserialize, +}; +use derivative::Derivative; +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::Encode; +use tree_hash_derive::TreeHash; + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)] +#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] +#[serde(bound = "T: EthSpec, Payload: AbstractExecPayload")] +pub struct BeaconBlockAndBlobSidecars> { + pub block: BeaconBlock, + pub blob_sidecars: BlobSidecars, +} + +impl> ForkVersionDeserialize + for BeaconBlockAndBlobSidecars +{ + fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>( + value: serde_json::value::Value, + fork_name: ForkName, + ) -> Result { + #[derive(Deserialize)] + #[serde(bound = "T: EthSpec")] + struct Helper { + block: serde_json::Value, + blob_sidecars: BlobSidecars, + } + let helper: Helper = serde_json::from_value(value).map_err(serde::de::Error::custom)?; + + Ok(Self { + block: BeaconBlock::deserialize_by_fork::<'de, D>(helper.block, fork_name)?, + blob_sidecars: helper.blob_sidecars, + }) + } +} diff --git a/consensus/types/src/blob_sidecar.rs b/consensus/types/src/blob_sidecar.rs index 12a6633de..251bef034 100644 --- a/consensus/types/src/blob_sidecar.rs +++ b/consensus/types/src/blob_sidecar.rs @@ -5,6 +5,7 @@ use kzg::{KzgCommitment, KzgProof}; use serde_derive::{Deserialize, Serialize}; use ssz::Encode; use ssz_derive::{Decode, Encode}; +use ssz_types::VariableList; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; @@ -34,15 +35,20 @@ pub struct BlobIdentifier { pub struct BlobSidecar { pub block_root: Hash256, // TODO: fix the type, should fit in u8 as well + #[serde(with = "eth2_serde_utils::quoted_u64")] pub index: u64, pub slot: Slot, pub block_parent_root: Hash256, + #[serde(with = "eth2_serde_utils::quoted_u64")] pub proposer_index: u64, + #[serde(with = "ssz_types::serde_utils::hex_fixed_vec")] pub blob: Blob, pub kzg_commitment: KzgCommitment, pub kzg_proof: KzgProof, } +pub type BlobSidecars = VariableList, ::MaxBlobsPerBlock>; + impl SignedRoot for BlobSidecar {} impl BlobSidecar { diff --git a/consensus/types/src/block_contents.rs b/consensus/types/src/block_contents.rs new file mode 100644 index 000000000..d5a500280 --- /dev/null +++ b/consensus/types/src/block_contents.rs @@ -0,0 +1,56 @@ +use crate::{ + AbstractExecPayload, BeaconBlock, BeaconBlockAndBlobSidecars, BlobSidecars, EthSpec, ForkName, + ForkVersionDeserialize, +}; +use derivative::Derivative; +use serde_derive::{Deserialize, Serialize}; + +/// A wrapper over a [`BeaconBlock`] or a [`BeaconBlockAndBlobSidecars`]. +#[derive(Clone, Debug, Derivative, Serialize, Deserialize)] +#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] +#[serde(untagged)] +#[serde(bound = "T: EthSpec")] +pub enum BlockContents> { + BlockAndBlobSidecars(BeaconBlockAndBlobSidecars), + Block(BeaconBlock), +} + +impl> BlockContents { + pub fn block(&self) -> &BeaconBlock { + match self { + BlockContents::BlockAndBlobSidecars(block_and_sidecars) => &block_and_sidecars.block, + BlockContents::Block(block) => block, + } + } + + pub fn deconstruct(self) -> (BeaconBlock, Option>) { + match self { + BlockContents::BlockAndBlobSidecars(block_and_sidecars) => ( + block_and_sidecars.block, + Some(block_and_sidecars.blob_sidecars), + ), + BlockContents::Block(block) => (block, None), + } + } +} + +impl> ForkVersionDeserialize + for BlockContents +{ + fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>( + value: serde_json::value::Value, + fork_name: ForkName, + ) -> Result { + match fork_name { + ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + Ok(BlockContents::Block(BeaconBlock::deserialize_by_fork::< + 'de, + D, + >(value, fork_name)?)) + } + ForkName::Eip4844 => Ok(BlockContents::BlockAndBlobSidecars( + BeaconBlockAndBlobSidecars::deserialize_by_fork::<'de, D>(value, fork_name)?, + )), + } + } +} diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index ce4837853..50b9547c9 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -99,8 +99,10 @@ pub mod slot_data; #[cfg(feature = "sqlite")] pub mod sqlite; +pub mod beacon_block_and_blob_sidecars; pub mod blob_sidecar; pub mod blobs_sidecar; +pub mod block_contents; pub mod signed_blob; pub mod signed_block_and_blobs; pub mod transaction; @@ -116,6 +118,7 @@ pub use crate::beacon_block::{ BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockEip4844, BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, EmptyBlock, }; +pub use crate::beacon_block_and_blob_sidecars::BeaconBlockAndBlobSidecars; pub use crate::beacon_block_body::{ BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyCapella, BeaconBlockBodyEip4844, BeaconBlockBodyMerge, BeaconBlockBodyRef, BeaconBlockBodyRefMut, @@ -123,8 +126,9 @@ pub use crate::beacon_block_body::{ pub use crate::beacon_block_header::BeaconBlockHeader; pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee}; pub use crate::beacon_state::{BeaconTreeHashCache, Error as BeaconStateError, *}; -pub use crate::blob_sidecar::BlobSidecar; +pub use crate::blob_sidecar::{BlobSidecar, BlobSidecars}; pub use crate::blobs_sidecar::{Blobs, BlobsSidecar}; +pub use crate::block_contents::BlockContents; pub use crate::bls_to_execution_change::BlsToExecutionChange; pub use crate::chain_spec::{ChainSpec, Config, Domain}; pub use crate::checkpoint::Checkpoint; diff --git a/validator_client/src/block_service.rs b/validator_client/src/block_service.rs index 3b3749237..22064cb10 100644 --- a/validator_client/src/block_service.rs +++ b/validator_client/src/block_service.rs @@ -15,8 +15,8 @@ use std::time::Duration; use tokio::sync::mpsc; use tokio::time::sleep; use types::{ - AbstractExecPayload, BlindedPayload, BlockType, EthSpec, FullPayload, Graffiti, PublicKeyBytes, - Slot, + AbstractExecPayload, BeaconBlock, BlindedPayload, BlockType, EthSpec, FullPayload, Graffiti, + PublicKeyBytes, Slot, }; #[derive(Debug)] @@ -347,7 +347,7 @@ impl BlockService { RequireSynced::No, OfflineOnFailure::Yes, |beacon_node| async move { - let block = match Payload::block_type() { + let block: BeaconBlock = match Payload::block_type() { BlockType::Full => { let _get_timer = metrics::start_timer_vec( &metrics::BLOCK_SERVICE_TIMES, @@ -367,6 +367,7 @@ impl BlockService { )) })? .data + .into() } BlockType::Blinded => { let _get_timer = metrics::start_timer_vec( @@ -387,6 +388,7 @@ impl BlockService { )) })? .data + .into() } };