Resolve merge conflicts

This commit is contained in:
Eitan Seri-Levi
2026-01-02 08:52:14 -06:00
918 changed files with 49304 additions and 37273 deletions

View File

@@ -3,21 +3,25 @@
use beacon_chain::block_verification_types::{AsBlock, ExecutedBlock, RpcBlock};
use beacon_chain::data_column_verification::CustodyDataColumn;
use beacon_chain::{
test_utils::{
test_spec, AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType,
},
AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes, ExecutionPendingBlock,
custody_context::NodeCustodyType,
test_utils::{
AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType, test_spec,
},
};
use beacon_chain::{
BeaconSnapshot, BlockError, ChainConfig, ChainSegmentResult, IntoExecutionPendingBlock,
InvalidSignature, NotifyExecutionLayer,
};
use bls::{AggregateSignature, Keypair, Signature};
use fixed_bytes::FixedBytesExtended;
use logging::create_test_tracing_subscriber;
use slasher::{Config as SlasherConfig, Slasher};
use state_processing::{
BlockProcessingError, ConsensusContext, VerifyBlockRoot,
common::{attesting_indices_base, attesting_indices_electra},
per_block_processing::{per_block_processing, BlockSignatureStrategy},
per_slot_processing, BlockProcessingError, ConsensusContext, VerifyBlockRoot,
per_block_processing::{BlockSignatureStrategy, per_block_processing},
per_slot_processing,
};
use std::marker::PhantomData;
use std::sync::{Arc, LazyLock};
@@ -30,8 +34,6 @@ type E = MainnetEthSpec;
const VALIDATOR_COUNT: usize = 24;
const CHAIN_SEGMENT_LENGTH: usize = 64 * 5;
const BLOCK_INDICES: &[usize] = &[0, 1, 32, 64, 68 + 1, 129, CHAIN_SEGMENT_LENGTH - 1];
// Default custody group count for tests
const CGC: usize = 8;
/// A cached set of keys.
static KEYPAIRS: LazyLock<Vec<Keypair>> =
@@ -43,7 +45,10 @@ enum DataSidecars<E: EthSpec> {
}
async fn get_chain_segment() -> (Vec<BeaconSnapshot<E>>, Vec<Option<DataSidecars<E>>>) {
let harness = get_harness(VALIDATOR_COUNT);
// The assumption that you can re-import a block based on what you have in your DB
// is no longer true, as fullnodes stores less than what they sample.
// We use a supernode here to build a chain segment.
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Supernode);
harness
.extend_chain(
@@ -102,7 +107,10 @@ async fn get_chain_segment() -> (Vec<BeaconSnapshot<E>>, Vec<Option<DataSidecars
(segment, segment_sidecars)
}
fn get_harness(validator_count: usize) -> BeaconChainHarness<EphemeralHarnessType<E>> {
fn get_harness(
validator_count: usize,
node_custody_type: NodeCustodyType,
) -> BeaconChainHarness<EphemeralHarnessType<E>> {
let harness = BeaconChainHarness::builder(MainnetEthSpec)
.default_spec()
.chain_config(ChainConfig {
@@ -110,6 +118,7 @@ fn get_harness(validator_count: usize) -> BeaconChainHarness<EphemeralHarnessTyp
..ChainConfig::default()
})
.keypairs(KEYPAIRS[0..validator_count].to_vec())
.node_custody_type(node_custody_type)
.fresh_ephemeral_store()
.mock_execution_layer()
.build();
@@ -122,14 +131,13 @@ fn get_harness(validator_count: usize) -> BeaconChainHarness<EphemeralHarnessTyp
fn chain_segment_blocks(
chain_segment: &[BeaconSnapshot<E>],
chain_segment_sidecars: &[Option<DataSidecars<E>>],
spec: &ChainSpec,
) -> Vec<RpcBlock<E>> {
chain_segment
.iter()
.zip(chain_segment_sidecars.iter())
.map(|(snapshot, data_sidecars)| {
let block = snapshot.beacon_block.clone();
build_rpc_block(block, data_sidecars, spec)
build_rpc_block(block, data_sidecars)
})
.collect()
}
@@ -137,17 +145,15 @@ fn chain_segment_blocks(
fn build_rpc_block(
block: Arc<SignedBeaconBlock<E>>,
data_sidecars: &Option<DataSidecars<E>>,
spec: &ChainSpec,
) -> RpcBlock<E> {
match data_sidecars {
Some(DataSidecars::Blobs(blobs)) => {
RpcBlock::new(None, block, Some(blobs.clone())).unwrap()
}
Some(DataSidecars::DataColumns(columns)) => {
RpcBlock::new_with_custody_columns(None, block, columns.clone(), columns.len(), spec)
.unwrap()
RpcBlock::new_with_custody_columns(None, block, columns.clone()).unwrap()
}
None => RpcBlock::new_without_blobs(None, block, 0),
None => RpcBlock::new_without_blobs(None, block),
}
}
@@ -256,12 +262,11 @@ fn update_data_column_signed_header<E: EthSpec>(
#[tokio::test]
async fn chain_segment_full_segment() {
let harness = get_harness(VALIDATOR_COUNT);
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
let blocks: Vec<RpcBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec)
.into_iter()
.collect();
let blocks: Vec<RpcBlock<E>> = chain_segment_blocks(&chain_segment, &chain_segment_blobs)
.into_iter()
.collect();
harness
.chain
@@ -294,20 +299,20 @@ async fn chain_segment_full_segment() {
#[tokio::test]
async fn chain_segment_varying_chunk_size() {
for chunk_size in &[1, 2, 3, 5, 31, 32, 33, 42] {
let harness = get_harness(VALIDATOR_COUNT);
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
let blocks: Vec<RpcBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec)
.into_iter()
.collect();
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
let blocks: Vec<RpcBlock<E>> = chain_segment_blocks(&chain_segment, &chain_segment_blobs)
.into_iter()
.collect();
for chunk_size in &[1, 2, 31, 32, 33] {
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
harness
.chain
.slot_clock
.set_slot(blocks.last().unwrap().slot().as_u64());
for chunk in blocks.chunks(*chunk_size) {
for chunk in blocks.clone().chunks(*chunk_size) {
harness
.chain
.process_chain_segment(chunk.to_vec(), NotifyExecutionLayer::Yes)
@@ -328,7 +333,7 @@ async fn chain_segment_varying_chunk_size() {
#[tokio::test]
async fn chain_segment_non_linear_parent_roots() {
let harness = get_harness(VALIDATOR_COUNT);
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
harness
@@ -339,10 +344,9 @@ async fn chain_segment_non_linear_parent_roots() {
/*
* Test with a block removed.
*/
let mut blocks: Vec<RpcBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec)
.into_iter()
.collect();
let mut blocks: Vec<RpcBlock<E>> = chain_segment_blocks(&chain_segment, &chain_segment_blobs)
.into_iter()
.collect();
blocks.remove(2);
assert!(
@@ -360,17 +364,15 @@ async fn chain_segment_non_linear_parent_roots() {
/*
* Test with a modified parent root.
*/
let mut blocks: Vec<RpcBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec)
.into_iter()
.collect();
let mut blocks: Vec<RpcBlock<E>> = chain_segment_blocks(&chain_segment, &chain_segment_blobs)
.into_iter()
.collect();
let (mut block, signature) = blocks[3].as_block().clone().deconstruct();
*block.parent_root_mut() = Hash256::zero();
blocks[3] = RpcBlock::new_without_blobs(
None,
Arc::new(SignedBeaconBlock::from_block(block, signature)),
harness.sampling_column_count,
);
assert!(
@@ -388,7 +390,7 @@ async fn chain_segment_non_linear_parent_roots() {
#[tokio::test]
async fn chain_segment_non_linear_slots() {
let harness = get_harness(VALIDATOR_COUNT);
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
harness
.chain
@@ -399,16 +401,14 @@ async fn chain_segment_non_linear_slots() {
* Test where a child is lower than the parent.
*/
let mut blocks: Vec<RpcBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec)
.into_iter()
.collect();
let mut blocks: Vec<RpcBlock<E>> = chain_segment_blocks(&chain_segment, &chain_segment_blobs)
.into_iter()
.collect();
let (mut block, signature) = blocks[3].as_block().clone().deconstruct();
*block.slot_mut() = Slot::new(0);
blocks[3] = RpcBlock::new_without_blobs(
None,
Arc::new(SignedBeaconBlock::from_block(block, signature)),
harness.sampling_column_count,
);
assert!(
@@ -427,16 +427,14 @@ async fn chain_segment_non_linear_slots() {
* Test where a child is equal to the parent.
*/
let mut blocks: Vec<RpcBlock<E>> =
chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec)
.into_iter()
.collect();
let mut blocks: Vec<RpcBlock<E>> = chain_segment_blocks(&chain_segment, &chain_segment_blobs)
.into_iter()
.collect();
let (mut block, signature) = blocks[3].as_block().clone().deconstruct();
*block.slot_mut() = blocks[2].slot();
blocks[3] = RpcBlock::new_without_blobs(
None,
Arc::new(SignedBeaconBlock::from_block(block, signature)),
harness.sampling_column_count,
);
assert!(
@@ -463,9 +461,7 @@ async fn assert_invalid_signature(
let blocks: Vec<RpcBlock<E>> = snapshots
.iter()
.zip(chain_segment_blobs.iter())
.map(|(snapshot, blobs)| {
build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec)
})
.map(|(snapshot, blobs)| build_rpc_block(snapshot.beacon_block.clone(), blobs))
.collect();
// Ensure the block will be rejected if imported in a chain segment.
@@ -490,9 +486,7 @@ async fn assert_invalid_signature(
.iter()
.take(block_index)
.zip(chain_segment_blobs.iter())
.map(|(snapshot, blobs)| {
build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec)
})
.map(|(snapshot, blobs)| build_rpc_block(snapshot.beacon_block.clone(), blobs))
.collect();
// We don't care if this fails, we just call this to ensure that all prior blocks have been
// imported prior to this test.
@@ -509,7 +503,6 @@ async fn assert_invalid_signature(
build_rpc_block(
snapshots[block_index].beacon_block.clone(),
&chain_segment_blobs[block_index],
&harness.spec,
),
NotifyExecutionLayer::Yes,
BlockImportSource::Lookup,
@@ -539,7 +532,7 @@ async fn assert_invalid_signature(
async fn get_invalid_sigs_harness(
chain_segment: &[BeaconSnapshot<E>],
) -> BeaconChainHarness<EphemeralHarnessType<E>> {
let harness = get_harness(VALIDATOR_COUNT);
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
harness
.chain
.slot_clock
@@ -567,9 +560,7 @@ async fn invalid_signature_gossip_block() {
.iter()
.take(block_index)
.zip(chain_segment_blobs.iter())
.map(|(snapshot, blobs)| {
build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec)
})
.map(|(snapshot, blobs)| build_rpc_block(snapshot.beacon_block.clone(), blobs))
.collect();
harness
.chain
@@ -578,11 +569,7 @@ async fn invalid_signature_gossip_block() {
.into_block_error()
.expect("should import all blocks prior to the one being tested");
let signed_block = SignedBeaconBlock::from_block(block, junk_signature());
let rpc_block = RpcBlock::new_without_blobs(
None,
Arc::new(signed_block),
harness.sampling_column_count,
);
let rpc_block = RpcBlock::new_without_blobs(None, Arc::new(signed_block));
let process_res = harness
.chain
.process_block(
@@ -624,9 +611,7 @@ async fn invalid_signature_block_proposal() {
let blocks: Vec<RpcBlock<E>> = snapshots
.iter()
.zip(chain_segment_blobs.iter())
.map(|(snapshot, blobs)| {
build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec)
})
.map(|(snapshot, blobs)| build_rpc_block(snapshot.beacon_block.clone(), blobs))
.collect::<Vec<_>>();
// Ensure the block will be rejected if imported in a chain segment.
let process_res = harness
@@ -725,7 +710,7 @@ async fn invalid_signature_attester_slashing() {
let attester_slashing = if fork_name.electra_enabled() {
let indexed_attestation = IndexedAttestationElectra {
attesting_indices: vec![0].into(),
attesting_indices: vec![0].try_into().unwrap(),
data: AttestationData {
slot: Slot::new(0),
index: 0,
@@ -749,7 +734,7 @@ async fn invalid_signature_attester_slashing() {
AttesterSlashing::Electra(attester_slashing)
} else {
let indexed_attestation = IndexedAttestationBase {
attesting_indices: vec![0].into(),
attesting_indices: vec![0].try_into().unwrap(),
data: AttestationData {
slot: Slot::new(0),
index: 0,
@@ -779,42 +764,47 @@ async fn invalid_signature_attester_slashing() {
.clone()
.deconstruct();
match &mut block.body_mut() {
BeaconBlockBodyRefMut::Base(ref mut blk) => {
BeaconBlockBodyRefMut::Base(blk) => {
blk.attester_slashings
.push(attester_slashing.as_base().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Altair(ref mut blk) => {
BeaconBlockBodyRefMut::Altair(blk) => {
blk.attester_slashings
.push(attester_slashing.as_base().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Bellatrix(ref mut blk) => {
BeaconBlockBodyRefMut::Bellatrix(blk) => {
blk.attester_slashings
.push(attester_slashing.as_base().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Capella(ref mut blk) => {
BeaconBlockBodyRefMut::Capella(blk) => {
blk.attester_slashings
.push(attester_slashing.as_base().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Deneb(ref mut blk) => {
BeaconBlockBodyRefMut::Deneb(blk) => {
blk.attester_slashings
.push(attester_slashing.as_base().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Electra(ref mut blk) => {
BeaconBlockBodyRefMut::Electra(blk) => {
blk.attester_slashings
.push(attester_slashing.as_electra().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Eip7805(ref mut blk) => {
BeaconBlockBodyRefMut::Fulu(blk) => {
blk.attester_slashings
.push(attester_slashing.as_electra().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Fulu(ref mut blk) => {
BeaconBlockBodyRefMut::Eip7805(blk) => {
blk.attester_slashings
.push(attester_slashing.as_electra().unwrap().clone())
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Gloas(blk) => {
blk.attester_slashings
.push(attester_slashing.as_electra().unwrap().clone())
.expect("should update attester slashing");
@@ -850,35 +840,39 @@ async fn invalid_signature_attestation() {
.clone()
.deconstruct();
match &mut block.body_mut() {
BeaconBlockBodyRefMut::Base(ref mut blk) => blk
BeaconBlockBodyRefMut::Base(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Altair(ref mut blk) => blk
BeaconBlockBodyRefMut::Altair(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Bellatrix(ref mut blk) => blk
BeaconBlockBodyRefMut::Bellatrix(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Capella(ref mut blk) => blk
BeaconBlockBodyRefMut::Capella(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Deneb(ref mut blk) => blk
BeaconBlockBodyRefMut::Deneb(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Electra(ref mut blk) => blk
BeaconBlockBodyRefMut::Electra(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Eip7805(ref mut blk) => blk
BeaconBlockBodyRefMut::Fulu(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Fulu(ref mut blk) => blk
BeaconBlockBodyRefMut::Eip7805(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Gloas(blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
@@ -916,7 +910,9 @@ async fn invalid_signature_deposit() {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let mut snapshots = chain_segment.clone();
let deposit = Deposit {
proof: vec![Hash256::zero(); DEPOSIT_TREE_DEPTH + 1].into(),
proof: vec![Hash256::zero(); DEPOSIT_TREE_DEPTH + 1]
.try_into()
.unwrap(),
data: DepositData {
pubkey: Keypair::random().pk.into(),
withdrawal_credentials: Hash256::zero(),
@@ -941,9 +937,7 @@ async fn invalid_signature_deposit() {
let blocks: Vec<RpcBlock<E>> = snapshots
.iter()
.zip(chain_segment_blobs.iter())
.map(|(snapshot, blobs)| {
build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec)
})
.map(|(snapshot, blobs)| build_rpc_block(snapshot.beacon_block.clone(), blobs))
.collect();
assert!(
!matches!(
@@ -1007,11 +1001,10 @@ fn unwrap_err<T, U>(result: Result<T, U>) -> U {
#[tokio::test]
async fn block_gossip_verification() {
let harness = get_harness(VALIDATOR_COUNT);
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let (chain_segment, chain_segment_blobs) = get_chain_segment().await;
let block_index = CHAIN_SEGMENT_LENGTH - 2;
let cgc = harness.chain.spec.custody_requirement as usize;
harness
.chain
@@ -1025,7 +1018,7 @@ async fn block_gossip_verification() {
{
let gossip_verified = harness
.chain
.verify_block_for_gossip(snapshot.beacon_block.clone(), get_cgc(&blobs_opt))
.verify_block_for_gossip(snapshot.beacon_block.clone())
.await
.expect("should obtain gossip verified block");
@@ -1067,7 +1060,7 @@ async fn block_gossip_verification() {
*block.slot_mut() = expected_block_slot;
assert!(
matches!(
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature)), cgc).await),
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature))).await),
BlockError::FutureSlot {
present_slot,
block_slot,
@@ -1101,7 +1094,7 @@ async fn block_gossip_verification() {
*block.slot_mut() = expected_finalized_slot;
assert!(
matches!(
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature)), cgc).await),
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature))).await),
BlockError::WouldRevertFinalizedSlot {
block_slot,
finalized_slot,
@@ -1131,10 +1124,10 @@ async fn block_gossip_verification() {
unwrap_err(
harness
.chain
.verify_block_for_gossip(
Arc::new(SignedBeaconBlock::from_block(block, junk_signature())),
cgc
)
.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(
block,
junk_signature()
)),)
.await
),
BlockError::InvalidSignature(InvalidSignature::ProposerSignature)
@@ -1159,7 +1152,7 @@ async fn block_gossip_verification() {
*block.parent_root_mut() = parent_root;
assert!(
matches!(
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature)), cgc).await),
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature))).await),
BlockError::ParentUnknown {parent_root: p}
if p == parent_root
),
@@ -1185,7 +1178,7 @@ async fn block_gossip_verification() {
*block.parent_root_mut() = parent_root;
assert!(
matches!(
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature)), cgc).await),
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature))).await),
BlockError::NotFinalizedDescendant { block_parent_root }
if block_parent_root == parent_root
),
@@ -1222,7 +1215,7 @@ async fn block_gossip_verification() {
);
assert!(
matches!(
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(block.clone()), cgc).await),
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(block.clone())).await),
BlockError::IncorrectBlockProposer {
block,
local_shuffling,
@@ -1234,7 +1227,12 @@ async fn block_gossip_verification() {
// Check to ensure that we registered this is a valid block from this proposer.
assert!(
matches!(
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(block.clone()), cgc).await),
unwrap_err(
harness
.chain
.verify_block_for_gossip(Arc::new(block.clone()))
.await
),
BlockError::DuplicateImportStatusUnknown(_),
),
"should register any valid signature against the proposer, even if the block failed later verification"
@@ -1242,11 +1240,7 @@ async fn block_gossip_verification() {
let block = chain_segment[block_index].beacon_block.clone();
assert!(
harness
.chain
.verify_block_for_gossip(block, cgc)
.await
.is_ok(),
harness.chain.verify_block_for_gossip(block).await.is_ok(),
"the valid block should be processed"
);
@@ -1264,13 +1258,47 @@ async fn block_gossip_verification() {
matches!(
harness
.chain
.verify_block_for_gossip(block.clone(), cgc)
.verify_block_for_gossip(block.clone())
.await
.expect_err("should error when processing known block"),
BlockError::DuplicateImportStatusUnknown(_)
),
"the second proposal by this validator should be rejected"
);
/*
* This test ensures that:
*
* We do not accept blocks with blob_kzg_commitments length larger than the max_blobs for that epoch.
*/
let (mut block, signature) = chain_segment[block_index]
.beacon_block
.as_ref()
.clone()
.deconstruct();
let kzg_commitments_len = harness
.chain
.spec
.max_blobs_per_block(block.slot().epoch(E::slots_per_epoch()))
as usize;
if let Ok(kzg_commitments) = block.body_mut().blob_kzg_commitments_mut() {
*kzg_commitments = vec![KzgCommitment::empty_for_testing(); kzg_commitments_len + 1]
.try_into()
.unwrap();
assert!(
matches!(
unwrap_err(harness.chain.verify_block_for_gossip(Arc::new(SignedBeaconBlock::from_block(block, signature))).await),
BlockError::InvalidBlobCount {
max_blobs_at_epoch,
block,
}
if max_blobs_at_epoch == kzg_commitments_len && block == kzg_commitments_len + 1
),
"should not import a block with higher blob_kzg_commitment length than the max_blobs at epoch"
);
}
}
async fn verify_and_process_gossip_data_sidecars(
@@ -1303,7 +1331,7 @@ async fn verify_and_process_gossip_data_sidecars(
);
harness.chain.verify_data_column_sidecar_for_gossip(
column_sidecar.into_inner(),
*subnet_id,
subnet_id,
)
})
.collect::<Result<Vec<_>, _>>()
@@ -1340,17 +1368,8 @@ async fn verify_block_for_gossip_slashing_detection() {
let state = harness.get_current_state();
let ((block1, blobs1), _) = harness.make_block(state.clone(), Slot::new(1)).await;
let ((block2, _blobs2), _) = harness.make_block(state, Slot::new(1)).await;
let cgc = if block1.fork_name_unchecked().fulu_enabled() {
harness.get_sampling_column_count()
} else {
0
};
let verified_block = harness
.chain
.verify_block_for_gossip(block1, cgc)
.await
.unwrap();
let verified_block = harness.chain.verify_block_for_gossip(block1).await.unwrap();
if let Some((kzg_proofs, blobs)) = blobs1 {
harness
@@ -1373,7 +1392,7 @@ async fn verify_block_for_gossip_slashing_detection() {
)
.await
.unwrap();
unwrap_err(harness.chain.verify_block_for_gossip(block2, CGC).await);
unwrap_err(harness.chain.verify_block_for_gossip(block2).await);
// Slasher should have been handed the two conflicting blocks and crafted a slashing.
slasher.process_queued(Epoch::new(0)).unwrap();
@@ -1387,7 +1406,7 @@ async fn verify_block_for_gossip_slashing_detection() {
#[tokio::test]
async fn verify_block_for_gossip_doppelganger_detection() {
let harness = get_harness(VALIDATOR_COUNT);
let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode);
let state = harness.get_current_state();
let ((block, _), _) = harness.make_block(state.clone(), Slot::new(1)).await;
@@ -1397,11 +1416,7 @@ async fn verify_block_for_gossip_doppelganger_detection() {
.attestations()
.map(|att| att.clone_as_attestation())
.collect::<Vec<_>>();
let verified_block = harness
.chain
.verify_block_for_gossip(block, CGC)
.await
.unwrap();
let verified_block = harness.chain.verify_block_for_gossip(block).await.unwrap();
harness
.chain
.process_block(
@@ -1437,24 +1452,30 @@ async fn verify_block_for_gossip_doppelganger_detection() {
assert!(harness.chain.validator_seen_at_epoch(index, epoch));
// Check the correct beacon cache is populated
assert!(harness
.chain
.observed_block_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if block attester was observed"));
assert!(!harness
.chain
.observed_gossip_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip attester was observed"));
assert!(!harness
.chain
.observed_aggregators
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip aggregator was observed"));
assert!(
harness
.chain
.observed_block_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if block attester was observed")
);
assert!(
!harness
.chain
.observed_gossip_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip attester was observed")
);
assert!(
!harness
.chain
.observed_aggregators
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip aggregator was observed")
);
}
}
}
@@ -1548,7 +1569,7 @@ async fn add_base_block_to_altair_chain() {
assert!(matches!(
harness
.chain
.verify_block_for_gossip(Arc::new(base_block.clone()), CGC)
.verify_block_for_gossip(Arc::new(base_block.clone()))
.await
.expect_err("should error when processing base block"),
BlockError::InconsistentFork(InconsistentFork {
@@ -1558,7 +1579,7 @@ async fn add_base_block_to_altair_chain() {
));
// Ensure that it would be impossible to import via `BeaconChain::process_block`.
let base_rpc_block = RpcBlock::new_without_blobs(None, Arc::new(base_block.clone()), 0);
let base_rpc_block = RpcBlock::new_without_blobs(None, Arc::new(base_block.clone()));
assert!(matches!(
harness
.chain
@@ -1582,7 +1603,7 @@ async fn add_base_block_to_altair_chain() {
harness
.chain
.process_chain_segment(
vec![RpcBlock::new_without_blobs(None, Arc::new(base_block), 0)],
vec![RpcBlock::new_without_blobs(None, Arc::new(base_block))],
NotifyExecutionLayer::Yes,
)
.await,
@@ -1685,7 +1706,7 @@ async fn add_altair_block_to_base_chain() {
assert!(matches!(
harness
.chain
.verify_block_for_gossip(Arc::new(altair_block.clone()), CGC)
.verify_block_for_gossip(Arc::new(altair_block.clone()))
.await
.expect_err("should error when processing altair block"),
BlockError::InconsistentFork(InconsistentFork {
@@ -1695,7 +1716,7 @@ async fn add_altair_block_to_base_chain() {
));
// Ensure that it would be impossible to import via `BeaconChain::process_block`.
let altair_rpc_block = RpcBlock::new_without_blobs(None, Arc::new(altair_block.clone()), 0);
let altair_rpc_block = RpcBlock::new_without_blobs(None, Arc::new(altair_block.clone()));
assert!(matches!(
harness
.chain
@@ -1719,7 +1740,7 @@ async fn add_altair_block_to_base_chain() {
harness
.chain
.process_chain_segment(
vec![RpcBlock::new_without_blobs(None, Arc::new(altair_block), 0)],
vec![RpcBlock::new_without_blobs(None, Arc::new(altair_block))],
NotifyExecutionLayer::Yes
)
.await,
@@ -1733,6 +1754,8 @@ async fn add_altair_block_to_base_chain() {
));
}
// This is a regression test for this bug:
// https://github.com/sigp/lighthouse/issues/4332#issuecomment-1565092279
#[tokio::test]
async fn import_duplicate_block_unrealized_justification() {
let spec = MainnetEthSpec::default_spec();
@@ -1780,11 +1803,7 @@ async fn import_duplicate_block_unrealized_justification() {
// Create two verified variants of the block, representing the same block being processed in
// parallel.
let notify_execution_layer = NotifyExecutionLayer::Yes;
let rpc_block = RpcBlock::new_without_blobs(
Some(block_root),
block.clone(),
harness.sampling_column_count,
);
let rpc_block = RpcBlock::new_without_blobs(Some(block_root), block.clone());
let verified_block1 = rpc_block
.clone()
.into_execution_pending_block(block_root, chain, notify_execution_layer)
@@ -1798,7 +1817,7 @@ async fn import_duplicate_block_unrealized_justification() {
.await
.unwrap();
// Unrealized justification should NOT have updated.
// The store's global unrealized justification should update immediately and match the block.
let unrealized_justification = {
let fc = chain.canonical_head.fork_choice_read_lock();
assert_eq!(fc.justified_checkpoint().epoch, 0);
@@ -1815,9 +1834,12 @@ async fn import_duplicate_block_unrealized_justification() {
};
// Import the second verified block, simulating a block processed via RPC.
import_execution_pending_block(chain.clone(), verified_block2)
.await
.unwrap();
assert_eq!(
import_execution_pending_block(chain.clone(), verified_block2)
.await
.unwrap_err(),
format!("DuplicateFullyImported({block_root})")
);
// Unrealized justification should still be updated.
let fc3 = chain.canonical_head.fork_choice_read_lock();
@@ -1855,14 +1877,3 @@ async fn import_execution_pending_block<T: BeaconChainTypes>(
}
}
}
fn get_cgc<E: EthSpec>(blobs_opt: &Option<DataSidecars<E>>) -> usize {
if let Some(data_sidecars) = blobs_opt.as_ref() {
match data_sidecars {
DataSidecars::Blobs(_) => 0,
DataSidecars::DataColumns(d) => d.len(),
}
} else {
0
}
}