Verify KZG in Bulk During Block Sync (#4903)
This commit is contained in:
parent
a380f6ef1f
commit
7818100777
@ -583,29 +583,33 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
|
|||||||
&chain.spec,
|
&chain.spec,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// unzip chain segment and verify kzg in bulk
|
||||||
|
let (roots, blocks): (Vec<_>, Vec<_>) = chain_segment.into_iter().unzip();
|
||||||
|
let maybe_available_blocks = chain
|
||||||
|
.data_availability_checker
|
||||||
|
.verify_kzg_for_rpc_blocks(blocks)?;
|
||||||
|
// zip it back up
|
||||||
|
let mut signature_verified_blocks = roots
|
||||||
|
.into_iter()
|
||||||
|
.zip(maybe_available_blocks)
|
||||||
|
.map(|(block_root, maybe_available_block)| {
|
||||||
|
let consensus_context = ConsensusContext::new(maybe_available_block.slot())
|
||||||
|
.set_current_block_root(block_root);
|
||||||
|
SignatureVerifiedBlock {
|
||||||
|
block: maybe_available_block,
|
||||||
|
block_root,
|
||||||
|
parent: None,
|
||||||
|
consensus_context,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// verify signatures
|
||||||
let pubkey_cache = get_validator_pubkey_cache(chain)?;
|
let pubkey_cache = get_validator_pubkey_cache(chain)?;
|
||||||
let mut signature_verifier = get_signature_verifier(&state, &pubkey_cache, &chain.spec);
|
let mut signature_verifier = get_signature_verifier(&state, &pubkey_cache, &chain.spec);
|
||||||
|
for svb in &mut signature_verified_blocks {
|
||||||
let mut signature_verified_blocks = Vec::with_capacity(chain_segment.len());
|
signature_verifier
|
||||||
|
.include_all_signatures(svb.block.as_block(), &mut svb.consensus_context)?;
|
||||||
for (block_root, block) in &chain_segment {
|
|
||||||
let mut consensus_context =
|
|
||||||
ConsensusContext::new(block.slot()).set_current_block_root(*block_root);
|
|
||||||
|
|
||||||
signature_verifier.include_all_signatures(block.as_block(), &mut consensus_context)?;
|
|
||||||
|
|
||||||
let maybe_available_block = chain
|
|
||||||
.data_availability_checker
|
|
||||||
.check_rpc_block_availability(block.clone())?;
|
|
||||||
|
|
||||||
// Save the block and its consensus context. The context will have had its proposer index
|
|
||||||
// and attesting indices filled in, which can be used to accelerate later block processing.
|
|
||||||
signature_verified_blocks.push(SignatureVerifiedBlock {
|
|
||||||
block: maybe_available_block,
|
|
||||||
block_root: *block_root,
|
|
||||||
parent: None,
|
|
||||||
consensus_context,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if signature_verifier.verify().is_err() {
|
if signature_verifier.verify().is_err() {
|
||||||
@ -1159,10 +1163,7 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for Arc<SignedBeaconBlock
|
|||||||
.map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?;
|
.map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?;
|
||||||
let maybe_available = chain
|
let maybe_available = chain
|
||||||
.data_availability_checker
|
.data_availability_checker
|
||||||
.check_rpc_block_availability(RpcBlock::new_without_blobs(
|
.verify_kzg_for_rpc_block(RpcBlock::new_without_blobs(Some(block_root), self.clone()))
|
||||||
Some(block_root),
|
|
||||||
self.clone(),
|
|
||||||
))
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
BlockSlashInfo::SignatureNotChecked(
|
BlockSlashInfo::SignatureNotChecked(
|
||||||
self.signed_block_header(),
|
self.signed_block_header(),
|
||||||
@ -1192,7 +1193,7 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for RpcBlock<T::EthSpec>
|
|||||||
.map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?;
|
.map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?;
|
||||||
let maybe_available = chain
|
let maybe_available = chain
|
||||||
.data_availability_checker
|
.data_availability_checker
|
||||||
.check_rpc_block_availability(self.clone())
|
.verify_kzg_for_rpc_block(self.clone())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
BlockSlashInfo::SignatureNotChecked(
|
BlockSlashInfo::SignatureNotChecked(
|
||||||
self.signed_block_header(),
|
self.signed_block_header(),
|
||||||
|
@ -45,6 +45,13 @@ impl<E: EthSpec> RpcBlock<E> {
|
|||||||
RpcBlockInner::BlockAndBlobs(block, _) => block,
|
RpcBlockInner::BlockAndBlobs(block, _) => block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn blobs(&self) -> Option<&BlobSidecarList<E>> {
|
||||||
|
match &self.block {
|
||||||
|
RpcBlockInner::Block(_) => None,
|
||||||
|
RpcBlockInner::BlockAndBlobs(_, blobs) => Some(blobs),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Note: This variant is intentionally private because we want to safely construct the
|
/// Note: This variant is intentionally private because we want to safely construct the
|
||||||
|
@ -240,9 +240,12 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
|||||||
.put_pending_executed_block(executed_block)
|
.put_pending_executed_block(executed_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a block is available, returns a `MaybeAvailableBlock` that may include the fully
|
/// Verifies kzg commitments for an RpcBlock, returns a `MaybeAvailableBlock` that may
|
||||||
/// available block.
|
/// include the fully available block.
|
||||||
pub fn check_rpc_block_availability(
|
///
|
||||||
|
/// WARNING: This function assumes all required blobs are already present, it does NOT
|
||||||
|
/// check if there are any missing blobs.
|
||||||
|
pub fn verify_kzg_for_rpc_block(
|
||||||
&self,
|
&self,
|
||||||
block: RpcBlock<T::EthSpec>,
|
block: RpcBlock<T::EthSpec>,
|
||||||
) -> Result<MaybeAvailableBlock<T::EthSpec>, AvailabilityCheckError> {
|
) -> Result<MaybeAvailableBlock<T::EthSpec>, AvailabilityCheckError> {
|
||||||
@ -279,6 +282,68 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a vector of blocks are available. Returns a vector of `MaybeAvailableBlock`
|
||||||
|
/// This is more efficient than calling `verify_kzg_for_rpc_block` in a loop as it does
|
||||||
|
/// all kzg verification at once
|
||||||
|
///
|
||||||
|
/// WARNING: This function assumes all required blobs are already present, it does NOT
|
||||||
|
/// check if there are any missing blobs.
|
||||||
|
pub fn verify_kzg_for_rpc_blocks(
|
||||||
|
&self,
|
||||||
|
blocks: Vec<RpcBlock<T::EthSpec>>,
|
||||||
|
) -> Result<Vec<MaybeAvailableBlock<T::EthSpec>>, AvailabilityCheckError> {
|
||||||
|
let mut results = Vec::with_capacity(blocks.len());
|
||||||
|
let all_blobs: BlobSidecarList<T::EthSpec> = blocks
|
||||||
|
.iter()
|
||||||
|
.filter(|block| self.blobs_required_for_block(block.as_block()))
|
||||||
|
// this clone is cheap as it's cloning an Arc
|
||||||
|
.filter_map(|block| block.blobs().cloned())
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
// verify kzg for all blobs at once
|
||||||
|
if !all_blobs.is_empty() {
|
||||||
|
let kzg = self
|
||||||
|
.kzg
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(AvailabilityCheckError::KzgNotInitialized)?;
|
||||||
|
verify_kzg_for_blob_list(&all_blobs, kzg)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for block in blocks {
|
||||||
|
let (block_root, block, blobs) = block.deconstruct();
|
||||||
|
match blobs {
|
||||||
|
None => {
|
||||||
|
if self.blobs_required_for_block(&block) {
|
||||||
|
results.push(MaybeAvailableBlock::AvailabilityPending { block_root, block })
|
||||||
|
} else {
|
||||||
|
results.push(MaybeAvailableBlock::Available(AvailableBlock {
|
||||||
|
block_root,
|
||||||
|
block,
|
||||||
|
blobs: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(blob_list) => {
|
||||||
|
let verified_blobs = if self.blobs_required_for_block(&block) {
|
||||||
|
Some(blob_list)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
// already verified kzg for all blobs
|
||||||
|
results.push(MaybeAvailableBlock::Available(AvailableBlock {
|
||||||
|
block_root,
|
||||||
|
block,
|
||||||
|
blobs: verified_blobs,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines the blob requirements for a block. If the block is pre-deneb, no blobs are required.
|
/// Determines the blob requirements for a block. If the block is pre-deneb, no blobs are required.
|
||||||
/// If the block's epoch is from prior to the data availability boundary, no blobs are required.
|
/// If the block's epoch is from prior to the data availability boundary, no blobs are required.
|
||||||
fn blobs_required_for_block(&self, block: &SignedBeaconBlock<T::EthSpec>) -> bool {
|
fn blobs_required_for_block(&self, block: &SignedBeaconBlock<T::EthSpec>) -> bool {
|
||||||
|
@ -140,7 +140,7 @@ async fn produces_attestations() {
|
|||||||
available_block,
|
available_block,
|
||||||
) = chain
|
) = chain
|
||||||
.data_availability_checker
|
.data_availability_checker
|
||||||
.check_rpc_block_availability(rpc_block)
|
.verify_kzg_for_rpc_block(rpc_block)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
else {
|
else {
|
||||||
panic!("block should be available")
|
panic!("block should be available")
|
||||||
@ -218,7 +218,7 @@ async fn early_attester_cache_old_request() {
|
|||||||
harness
|
harness
|
||||||
.chain
|
.chain
|
||||||
.data_availability_checker
|
.data_availability_checker
|
||||||
.check_rpc_block_availability(rpc_block)
|
.verify_kzg_for_rpc_block(rpc_block)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
else {
|
else {
|
||||||
panic!("block should be available")
|
panic!("block should be available")
|
||||||
|
@ -2464,10 +2464,10 @@ async fn weak_subjectivity_sync_test(slots: Vec<Slot>, checkpoint_slot: Slot) {
|
|||||||
if let MaybeAvailableBlock::Available(block) = harness
|
if let MaybeAvailableBlock::Available(block) = harness
|
||||||
.chain
|
.chain
|
||||||
.data_availability_checker
|
.data_availability_checker
|
||||||
.check_rpc_block_availability(
|
.verify_kzg_for_rpc_block(
|
||||||
RpcBlock::new(Some(block_root), Arc::new(full_block), Some(blobs)).unwrap(),
|
RpcBlock::new(Some(block_root), Arc::new(full_block), Some(blobs)).unwrap(),
|
||||||
)
|
)
|
||||||
.expect("should check availability")
|
.expect("should verify kzg")
|
||||||
{
|
{
|
||||||
available_blocks.push(block);
|
available_blocks.push(block);
|
||||||
}
|
}
|
||||||
|
@ -560,14 +560,10 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
|||||||
downloaded_blocks: Vec<RpcBlock<T::EthSpec>>,
|
downloaded_blocks: Vec<RpcBlock<T::EthSpec>>,
|
||||||
) -> (usize, Result<(), ChainSegmentFailed>) {
|
) -> (usize, Result<(), ChainSegmentFailed>) {
|
||||||
let total_blocks = downloaded_blocks.len();
|
let total_blocks = downloaded_blocks.len();
|
||||||
let available_blocks = match downloaded_blocks
|
let available_blocks = match self
|
||||||
.into_iter()
|
.chain
|
||||||
.map(|block| {
|
.data_availability_checker
|
||||||
self.chain
|
.verify_kzg_for_rpc_blocks(downloaded_blocks)
|
||||||
.data_availability_checker
|
|
||||||
.check_rpc_block_availability(block)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
{
|
{
|
||||||
Ok(blocks) => blocks
|
Ok(blocks) => blocks
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
Loading…
Reference in New Issue
Block a user