Implement get validator block endpoint for EIP-4844

This commit is contained in:
Jimmy Chen 2023-02-15 16:44:13 +11:00
parent ae3e5f73d6
commit 6ec0ce6070
No known key found for this signature in database
GPG Key ID: 7AAEE02659DCF690
13 changed files with 201 additions and 34 deletions

View File

@ -4759,30 +4759,41 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.kzg
.as_ref()
.ok_or(BlockProductionError::TrustedSetupNotInitialized)?;
let kzg_aggregated_proof =
kzg_utils::compute_aggregate_kzg_proof::<T::EthSpec>(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::<Vec<BlobSidecar<T::EthSpec>>>(),
);
// 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);

View File

@ -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<T: EthSpec> {
blobs: Mutex<LruCache<BlobCacheId, BlobsSidecar<T>>>,
blobs: Mutex<LruCache<BlobCacheId, BlobSidecars<T>>>,
}
#[derive(Hash, PartialEq, Eq)]
@ -21,11 +21,11 @@ impl<T: EthSpec> Default for BlobCache<T> {
}
impl<T: EthSpec> BlobCache<T> {
pub fn put(&self, beacon_block: Hash256, blobs: BlobsSidecar<T>) -> Option<BlobsSidecar<T>> {
pub fn put(&self, beacon_block: Hash256, blobs: BlobSidecars<T>) -> Option<BlobSidecars<T>> {
self.blobs.lock().put(BlobCacheId(beacon_block), blobs)
}
pub fn pop(&self, root: &Hash256) -> Option<BlobsSidecar<T>> {
pub fn pop(&self, root: &Hash256) -> Option<BlobSidecars<T>> {
self.blobs.lock().pop(&BlobCacheId(*root))
}
}

View File

@ -266,6 +266,7 @@ pub enum BlockProductionError {
blob_block_hash: ExecutionBlockHash,
payload_block_hash: ExecutionBlockHash,
},
NoBlobsCached,
FailedToReadFinalizedBlock(store::Error),
MissingFinalizedBlock(Hash256),
BlockTooLarge(usize),

View File

@ -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<T: BeaconChainTypes, Payload: AbstractExecPayload<T::EthSpec>>(
fork_name: ForkName,
chain: Arc<BeaconChain<T>>,
block: BeaconBlock<T::EthSpec, Payload>,
) -> Result<BlockContents<T::EthSpec, Payload>, 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,
));
}
}
}
}

View File

@ -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<T: BeaconChainTypes>(
.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))
},
);

View File

@ -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<T: BeaconChainTypes>(
let wrapped_block: BlockWrapper<T::EthSpec> =
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

View File

@ -1388,7 +1388,7 @@ impl BeaconNodeHttpClient {
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
self.get_validator_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<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()

View File

@ -731,6 +731,17 @@ impl<E: EthSpec> From<BeaconBlock<E, FullPayload<E>>>
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> From<BlockContents<T, Payload>>
for BeaconBlock<T, Payload>
{
fn from(block_contents: BlockContents<T, Payload>) -> Self {
match block_contents {
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => block_and_sidecars.block,
BlockContents::Block(block) => block,
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BeaconBlock<T, Payload>
{

View File

@ -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<T>")]
pub struct BeaconBlockAndBlobSidecars<T: EthSpec, Payload: AbstractExecPayload<T>> {
pub block: BeaconBlock<T, Payload>,
pub blob_sidecars: BlobSidecars<T>,
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BeaconBlockAndBlobSidecars<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")]
struct Helper<T: EthSpec> {
block: serde_json::Value,
blob_sidecars: BlobSidecars<T>,
}
let helper: Helper<T> = 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,
})
}
}

View File

@ -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<T: EthSpec> {
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<T>,
pub kzg_commitment: KzgCommitment,
pub kzg_proof: KzgProof,
}
pub type BlobSidecars<T> = VariableList<BlobSidecar<T>, <T as EthSpec>::MaxBlobsPerBlock>;
impl<T: EthSpec> SignedRoot for BlobSidecar<T> {}
impl<T: EthSpec> BlobSidecar<T> {

View File

@ -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<T: EthSpec, Payload: AbstractExecPayload<T>> {
BlockAndBlobSidecars(BeaconBlockAndBlobSidecars<T, Payload>),
Block(BeaconBlock<T, Payload>),
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockContents<T, Payload> {
pub fn block(&self) -> &BeaconBlock<T, Payload> {
match self {
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => &block_and_sidecars.block,
BlockContents::Block(block) => block,
}
}
pub fn deconstruct(self) -> (BeaconBlock<T, Payload>, Option<BlobSidecars<T>>) {
match self {
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => (
block_and_sidecars.block,
Some(block_and_sidecars.blob_sidecars),
),
BlockContents::Block(block) => (block, None),
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BlockContents<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
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)?,
)),
}
}
}

View File

@ -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;

View File

@ -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<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
RequireSynced::No,
OfflineOnFailure::Yes,
|beacon_node| async move {
let block = match Payload::block_type() {
let block: BeaconBlock<E, Payload> = match Payload::block_type() {
BlockType::Full => {
let _get_timer = metrics::start_timer_vec(
&metrics::BLOCK_SERVICE_TIMES,
@ -367,6 +367,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
))
})?
.data
.into()
}
BlockType::Blinded => {
let _get_timer = metrics::start_timer_vec(
@ -387,6 +388,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
))
})?
.data
.into()
}
};