Skip to content

Commit 474c1b4

Browse files
authored
Verify inclusion proof should not be fallible (#5787)
* Verify inclusion proof should not be fallible * Add blob sidecar inclusion test (#33) * Add blob sidecar inclusion test. * Fix lint
1 parent 21f3a19 commit 474c1b4

File tree

7 files changed

+153
-31
lines changed

7 files changed

+153
-31
lines changed

beacon_node/beacon_chain/src/blob_verification.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use crate::block_verification::{
1010
use crate::kzg_utils::{validate_blob, validate_blobs};
1111
use crate::{metrics, BeaconChainError};
1212
use kzg::{Error as KzgError, Kzg, KzgCommitment};
13-
use merkle_proof::MerkleTreeError;
1413
use slog::debug;
1514
use ssz_derive::{Decode, Encode};
1615
use ssz_types::VariableList;
@@ -128,13 +127,6 @@ pub enum GossipBlobError<E: EthSpec> {
128127
/// The blob sidecar is invalid and the peer is faulty.
129128
KzgError(kzg::Error),
130129

131-
/// The kzg commitment inclusion proof failed.
132-
///
133-
/// ## Peer scoring
134-
///
135-
/// The blob sidecar is invalid
136-
InclusionProof(MerkleTreeError),
137-
138130
/// The pubkey cache timed out.
139131
///
140132
/// ## Peer scoring
@@ -459,10 +451,7 @@ pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
459451

460452
// Verify the inclusion proof in the sidecar
461453
let _timer = metrics::start_timer(&metrics::BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION);
462-
if !blob_sidecar
463-
.verify_blob_sidecar_inclusion_proof()
464-
.map_err(GossipBlobError::InclusionProof)?
465-
{
454+
if !blob_sidecar.verify_blob_sidecar_inclusion_proof() {
466455
return Err(GossipBlobError::InvalidInclusionProof);
467456
}
468457
drop(_timer);

beacon_node/network/src/network_beacon_processor/gossip_methods.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,6 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
690690
| GossipBlobError::InvalidSubnet { .. }
691691
| GossipBlobError::InvalidInclusionProof
692692
| GossipBlobError::KzgError(_)
693-
| GossipBlobError::InclusionProof(_)
694693
| GossipBlobError::NotFinalizedDescendant { .. } => {
695694
warn!(
696695
self.log,

beacon_node/network/src/sync/network_context/requests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl<E: EthSpec> ActiveBlobsByRootRequest<E> {
120120
if self.request.block_root != block_root {
121121
return Err(LookupVerifyError::UnrequestedBlockRoot(block_root));
122122
}
123-
if !blob.verify_blob_sidecar_inclusion_proof().unwrap_or(false) {
123+
if !blob.verify_blob_sidecar_inclusion_proof() {
124124
return Err(LookupVerifyError::InvalidInclusionProof);
125125
}
126126
if !self.request.indices.contains(&blob.index) {

consensus/types/src/blob_sidecar.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use kzg::{
1212
};
1313
use merkle_proof::{merkle_root_from_branch, verify_merkle_proof, MerkleTreeError};
1414
use rand::Rng;
15-
use safe_arith::{ArithError, SafeArith};
15+
use safe_arith::ArithError;
1616
use serde::{Deserialize, Serialize};
1717
use ssz::Encode;
1818
use ssz_derive::{Decode, Encode};
@@ -190,33 +190,30 @@ impl<E: EthSpec> BlobSidecar<E> {
190190
}
191191

192192
/// Verifies the kzg commitment inclusion merkle proof.
193-
pub fn verify_blob_sidecar_inclusion_proof(&self) -> Result<bool, MerkleTreeError> {
194-
// Depth of the subtree rooted at `blob_kzg_commitments` in the `BeaconBlockBody`
195-
// is equal to depth of the ssz List max size + 1 for the length mixin
196-
let kzg_commitments_tree_depth = (E::max_blob_commitments_per_block()
197-
.next_power_of_two()
198-
.ilog2()
199-
.safe_add(1))? as usize;
193+
pub fn verify_blob_sidecar_inclusion_proof(&self) -> bool {
194+
let kzg_commitments_tree_depth = E::kzg_commitments_tree_depth();
195+
196+
// EthSpec asserts that kzg_commitments_tree_depth is less than KzgCommitmentInclusionProofDepth
197+
let (kzg_commitment_subtree_proof, kzg_commitments_proof) = self
198+
.kzg_commitment_inclusion_proof
199+
.split_at(kzg_commitments_tree_depth);
200+
200201
// Compute the `tree_hash_root` of the `blob_kzg_commitments` subtree using the
201202
// inclusion proof branches
202203
let blob_kzg_commitments_root = merkle_root_from_branch(
203204
self.kzg_commitment.tree_hash_root(),
204-
self.kzg_commitment_inclusion_proof
205-
.get(0..kzg_commitments_tree_depth)
206-
.ok_or(MerkleTreeError::PleaseNotifyTheDevs)?,
205+
kzg_commitment_subtree_proof,
207206
kzg_commitments_tree_depth,
208207
self.index as usize,
209208
);
210209
// The remaining inclusion proof branches are for the top level `BeaconBlockBody` tree
211-
Ok(verify_merkle_proof(
210+
verify_merkle_proof(
212211
blob_kzg_commitments_root,
213-
self.kzg_commitment_inclusion_proof
214-
.get(kzg_commitments_tree_depth..E::kzg_proof_inclusion_proof_depth())
215-
.ok_or(MerkleTreeError::PleaseNotifyTheDevs)?,
216-
E::kzg_proof_inclusion_proof_depth().safe_sub(kzg_commitments_tree_depth)?,
212+
kzg_commitments_proof,
213+
E::block_body_tree_depth(),
217214
BLOB_KZG_COMMITMENTS_INDEX,
218215
self.signed_block_header.message.body_root,
219-
))
216+
)
220217
}
221218

222219
pub fn random_valid<R: Rng>(rng: &mut R, kzg: &Kzg) -> Result<Self, String> {

consensus/types/src/eth_spec.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,22 @@ pub trait EthSpec:
292292
Self::KzgCommitmentInclusionProofDepth::to_usize()
293293
}
294294

295+
fn kzg_commitments_tree_depth() -> usize {
296+
// Depth of the subtree rooted at `blob_kzg_commitments` in the `BeaconBlockBody`
297+
// is equal to depth of the ssz List max size + 1 for the length mixin
298+
Self::max_blob_commitments_per_block()
299+
.next_power_of_two()
300+
.ilog2()
301+
.safe_add(1)
302+
.expect("The log of max_blob_commitments_per_block can not overflow") as usize
303+
}
304+
305+
fn block_body_tree_depth() -> usize {
306+
Self::kzg_proof_inclusion_proof_depth()
307+
.safe_sub(Self::kzg_commitments_tree_depth())
308+
.expect("Preset values are not configurable and never result in non-positive block body depth")
309+
}
310+
295311
/// Returns the `PENDING_BALANCE_DEPOSITS_LIMIT` constant for this specification.
296312
fn pending_balance_deposits_limit() -> usize {
297313
Self::PendingBalanceDepositsLimit::to_usize()
@@ -517,3 +533,26 @@ impl EthSpec for GnosisEthSpec {
517533
EthSpecId::Gnosis
518534
}
519535
}
536+
537+
#[cfg(test)]
538+
mod test {
539+
use crate::{EthSpec, GnosisEthSpec, MainnetEthSpec, MinimalEthSpec};
540+
541+
fn assert_valid_spec<E: EthSpec>() {
542+
E::kzg_commitments_tree_depth();
543+
E::block_body_tree_depth();
544+
}
545+
546+
#[test]
547+
fn mainnet_spec() {
548+
assert_valid_spec::<MainnetEthSpec>();
549+
}
550+
#[test]
551+
fn minimal_spec() {
552+
assert_valid_spec::<MinimalEthSpec>();
553+
}
554+
#[test]
555+
fn gnosis_spec() {
556+
assert_valid_spec::<GnosisEthSpec>();
557+
}
558+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use rand::Rng;
2+
3+
use kzg::{KzgCommitment, KzgProof};
4+
5+
use crate::beacon_block_body::KzgCommitments;
6+
use crate::*;
7+
8+
use super::*;
9+
10+
type BlobsBundle<E> = (KzgCommitments<E>, KzgProofs<E>, BlobsList<E>);
11+
12+
pub fn generate_rand_block_and_blobs<E: EthSpec>(
13+
fork_name: ForkName,
14+
num_blobs: usize,
15+
rng: &mut impl Rng,
16+
) -> (SignedBeaconBlock<E, FullPayload<E>>, Vec<BlobSidecar<E>>) {
17+
let inner = map_fork_name!(fork_name, BeaconBlock, <_>::random_for_test(rng));
18+
let mut block = SignedBeaconBlock::from_block(inner, Signature::random_for_test(rng));
19+
let mut blob_sidecars = vec![];
20+
21+
if block.fork_name_unchecked() < ForkName::Deneb {
22+
return (block, blob_sidecars);
23+
}
24+
25+
let (commitments, proofs, blobs) = generate_blobs::<E>(num_blobs).unwrap();
26+
*block
27+
.message_mut()
28+
.body_mut()
29+
.blob_kzg_commitments_mut()
30+
.expect("kzg commitment expected from Deneb") = commitments.clone();
31+
32+
for (index, ((blob, kzg_commitment), kzg_proof)) in blobs
33+
.into_iter()
34+
.zip(commitments.into_iter())
35+
.zip(proofs.into_iter())
36+
.enumerate()
37+
{
38+
blob_sidecars.push(BlobSidecar {
39+
index: index as u64,
40+
blob: blob.clone(),
41+
kzg_commitment,
42+
kzg_proof,
43+
signed_block_header: block.signed_block_header(),
44+
kzg_commitment_inclusion_proof: block
45+
.message()
46+
.body()
47+
.kzg_commitment_merkle_proof(index)
48+
.unwrap(),
49+
});
50+
}
51+
(block, blob_sidecars)
52+
}
53+
54+
pub fn generate_blobs<E: EthSpec>(n_blobs: usize) -> Result<BlobsBundle<E>, String> {
55+
let (mut commitments, mut proofs, mut blobs) = BlobsBundle::<E>::default();
56+
57+
for blob_index in 0..n_blobs {
58+
blobs
59+
.push(Blob::<E>::default())
60+
.map_err(|_| format!("blobs are full, blob index: {:?}", blob_index))?;
61+
commitments
62+
.push(KzgCommitment::empty_for_testing())
63+
.map_err(|_| format!("blobs are full, blob index: {:?}", blob_index))?;
64+
proofs
65+
.push(KzgProof::empty())
66+
.map_err(|_| format!("blobs are full, blob index: {:?}", blob_index))?;
67+
}
68+
69+
Ok((commitments, proofs, blobs))
70+
}
71+
72+
#[cfg(test)]
73+
mod test {
74+
use super::*;
75+
use rand::thread_rng;
76+
77+
#[test]
78+
fn test_verify_blob_inclusion_proof() {
79+
let (_block, blobs) =
80+
generate_rand_block_and_blobs::<MainnetEthSpec>(ForkName::Deneb, 6, &mut thread_rng());
81+
for blob in blobs {
82+
assert!(blob.verify_blob_sidecar_inclusion_proof());
83+
}
84+
}
85+
86+
#[test]
87+
fn test_verify_blob_inclusion_proof_invalid() {
88+
let (_block, blobs) =
89+
generate_rand_block_and_blobs::<MainnetEthSpec>(ForkName::Deneb, 6, &mut thread_rng());
90+
91+
for mut blob in blobs {
92+
blob.kzg_commitment_inclusion_proof = FixedVector::random_for_test(&mut thread_rng());
93+
assert!(!blob.verify_blob_sidecar_inclusion_proof());
94+
}
95+
}
96+
}

consensus/types/src/test_utils/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use tree_hash::TreeHash;
1515
#[macro_use]
1616
mod macros;
1717
mod generate_deterministic_keypairs;
18+
#[cfg(test)]
19+
mod generate_random_block_and_blobs;
1820
mod test_random;
1921

2022
pub fn test_ssz_tree_hash_pair<T, U>(v1: &T, v2: &U)

0 commit comments

Comments
 (0)