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

@@ -1,50 +1,57 @@
use crate::blob_verification::GossipVerifiedBlob;
use crate::block_verification_types::{AsBlock, RpcBlock};
use crate::custody_context::NodeCustodyType;
use crate::data_column_verification::CustodyDataColumn;
use crate::graffiti_calculator::GraffitiSettings;
use crate::kzg_utils::build_data_column_sidecars;
use crate::observed_operations::ObservationOutcome;
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
use crate::{BeaconBlockResponseWrapper, get_block_root};
use crate::{
BeaconChain, BeaconChainTypes, BlockError, ChainConfig, ServerSentEventHandler,
StateSkipConfig,
builder::{BeaconChainBuilder, Witness},
};
pub use crate::{
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
BeaconChainError, NotifyExecutionLayer, ProduceBlockVerification,
beacon_chain::{BEACON_CHAIN_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
migrate::MigratorConfig,
single_attestation::single_attestation_to_attestation,
sync_committee_verification::Error as SyncCommitteeError,
validator_monitor::{ValidatorMonitor, ValidatorMonitorConfig},
BeaconChainError, NotifyExecutionLayer, ProduceBlockVerification,
};
use crate::{
builder::{BeaconChainBuilder, Witness},
eth1_chain::CachingEth1Backend,
BeaconChain, BeaconChainTypes, BlockError, ChainConfig, ServerSentEventHandler,
StateSkipConfig,
};
use crate::{get_block_root, BeaconBlockResponseWrapper};
use bls::get_withdrawal_credentials;
use eth2::types::SignedBlockContentsTuple;
use bls::{
AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey, Signature, SignatureBytes,
};
use eth2::types::{GraffitiPolicy, SignedBlockContentsTuple};
use execution_layer::test_utils::generate_genesis_header;
use execution_layer::{
ExecutionLayer,
auth::JwtKey,
test_utils::{
ExecutionBlockGenerator, MockBuilder, MockExecutionLayer, DEFAULT_JWT_SECRET,
DEFAULT_TERMINAL_BLOCK,
DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK, ExecutionBlockGenerator, MockBuilder,
MockExecutionLayer,
},
ExecutionLayer,
};
use fixed_bytes::FixedBytesExtended;
use futures::channel::mpsc::Receiver;
pub use genesis::{InteropGenesisBuilder, DEFAULT_ETH1_BLOCK_HASH};
pub use genesis::{DEFAULT_ETH1_BLOCK_HASH, InteropGenesisBuilder};
use int_to_bytes::int_to_bytes32;
use kzg::Kzg;
use kzg::trusted_setup::get_trusted_setup;
use kzg::{Kzg, TrustedSetup};
use logging::create_test_tracing_subscriber;
use merkle_proof::MerkleTree;
use operation_pool::ReceivedPreCapella;
use parking_lot::{Mutex, RwLockWriteGuard};
use rand::rngs::StdRng;
use rand::Rng;
use rand::SeedableRng;
use rand::rngs::StdRng;
use rand::seq::SliceRandom;
use rayon::prelude::*;
use sensitive_url::SensitiveUrl;
use slot_clock::{SlotClock, TestingSlotClock};
use ssz_types::{RuntimeVariableList, VariableList};
use state_processing::per_block_processing::compute_timestamp_at_slot;
use state_processing::state_advance::complete_state_advance;
use std::borrow::Cow;
@@ -55,26 +62,26 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, LazyLock};
use std::time::Duration;
use store::database::interface::BeaconNodeBackend;
use store::{config::StoreConfig, HotColdDB, ItemStore, MemoryStore};
use store::{HotColdDB, ItemStore, MemoryStore, config::StoreConfig};
use task_executor::TaskExecutor;
use task_executor::{test_utils::TestRuntime, ShutdownReason};
use task_executor::{ShutdownReason, test_utils::TestRuntime};
use tree_hash::TreeHash;
use typenum::U4294967296;
use types::data_column_custody_group::CustodyIndex;
use types::indexed_attestation::IndexedAttestationBase;
use types::payload::BlockProductionVersion;
pub use types::test_utils::generate_deterministic_keypairs;
use types::test_utils::TestRandom;
use types::{typenum::U4294967296, *};
pub use types::test_utils::generate_deterministic_keypairs;
use types::*;
// 4th September 2019
pub const HARNESS_GENESIS_TIME: u64 = 1_567_552_690;
// Environment variable to read if `fork_from_env` feature is enabled.
pub const FORK_NAME_ENV_VAR: &str = "FORK_NAME";
// Environment variable to read if `ci_logger` feature is enabled.
pub const CI_LOGGER_DIR_ENV_VAR: &str = "CI_LOGGER_DIR";
// Pre-computed data column sidecar using a single static blob from:
// `beacon_node/execution_layer/src/test_utils/fixtures/mainnet/test_blobs_bundle.ssz`
const TEST_DATA_COLUMN_SIDECARS_SSZ: &[u8] =
pub const TEST_DATA_COLUMN_SIDECARS_SSZ: &[u8] =
include_bytes!("test_utils/fixtures/test_data_column_sidecars.ssz");
// Default target aggregators to set during testing, this ensures an aggregator at each slot.
@@ -83,34 +90,23 @@ const TEST_DATA_COLUMN_SIDECARS_SSZ: &[u8] =
// a different value.
pub const DEFAULT_TARGET_AGGREGATORS: u64 = u64::MAX;
static KZG: LazyLock<Arc<Kzg>> = LazyLock::new(|| {
let trusted_setup: TrustedSetup = serde_json::from_reader(get_trusted_setup().as_slice())
.map_err(|e| format!("Unable to read trusted setup file: {}", e))
.expect("should have trusted setup");
let kzg = Kzg::new_from_trusted_setup(trusted_setup).expect("should create kzg");
Arc::new(kzg)
});
// Minimum and maximum number of blobs to generate in each slot when using the `NumBlobs::Random` option (default).
const DEFAULT_MIN_BLOBS: usize = 1;
const DEFAULT_MAX_BLOBS: usize = 2;
static KZG_PEERDAS: LazyLock<Arc<Kzg>> = LazyLock::new(|| {
let trusted_setup: TrustedSetup = serde_json::from_reader(get_trusted_setup().as_slice())
.map_err(|e| format!("Unable to read trusted setup file: {}", e))
.expect("should have trusted setup");
let kzg = Kzg::new_from_trusted_setup_das_enabled(trusted_setup).expect("should create kzg");
static KZG: LazyLock<Arc<Kzg>> = LazyLock::new(|| {
let kzg = Kzg::new_from_trusted_setup(&get_trusted_setup()).expect("should create kzg");
Arc::new(kzg)
});
static KZG_NO_PRECOMP: LazyLock<Arc<Kzg>> = LazyLock::new(|| {
let trusted_setup: TrustedSetup = serde_json::from_reader(get_trusted_setup().as_slice())
.map_err(|e| format!("Unable to read trusted setup file: {}", e))
.expect("should have trusted setup");
let kzg = Kzg::new_from_trusted_setup_no_precomp(trusted_setup).expect("should create kzg");
let kzg =
Kzg::new_from_trusted_setup_no_precomp(&get_trusted_setup()).expect("should create kzg");
Arc::new(kzg)
});
pub fn get_kzg(spec: &ChainSpec) -> Arc<Kzg> {
if spec.fulu_fork_epoch.is_some() {
KZG_PEERDAS.clone()
} else if spec.deneb_fork_epoch.is_some() {
KZG.clone()
} else {
KZG_NO_PRECOMP.clone()
@@ -118,7 +114,7 @@ pub fn get_kzg(spec: &ChainSpec) -> Arc<Kzg> {
}
pub type BaseHarnessType<E, THotStore, TColdStore> =
Witness<TestingSlotClock, CachingEth1Backend<E>, E, THotStore, TColdStore>;
Witness<TestingSlotClock, E, THotStore, TColdStore>;
pub type DiskHarnessType<E> = BaseHarnessType<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>;
pub type EphemeralHarnessType<E> = BaseHarnessType<E, MemoryStore<E>, MemoryStore<E>>;
@@ -189,23 +185,28 @@ fn make_rng() -> Mutex<StdRng> {
Mutex::new(StdRng::seed_from_u64(0x0DDB1A5E5BAD5EEDu64))
}
/// Return a `ChainSpec` suitable for test usage.
///
/// If the `fork_from_env` feature is enabled, read the fork to use from the FORK_NAME environment
/// variable. Otherwise use the default spec.
pub fn test_spec<E: EthSpec>() -> ChainSpec {
let mut spec = if cfg!(feature = "fork_from_env") {
pub fn fork_name_from_env() -> Option<ForkName> {
if cfg!(feature = "fork_from_env") {
let fork_name = std::env::var(FORK_NAME_ENV_VAR).unwrap_or_else(|e| {
panic!(
"{} env var must be defined when using fork_from_env: {:?}",
FORK_NAME_ENV_VAR, e
)
});
let fork = ForkName::from_str(fork_name.as_str()).unwrap();
fork.make_genesis_spec(E::default_spec())
Some(ForkName::from_str(fork_name.as_str()).unwrap())
} else {
E::default_spec()
};
None
}
}
/// Return a `ChainSpec` suitable for test usage.
///
/// If the `fork_from_env` feature is enabled, read the fork to use from the FORK_NAME environment
/// variable. Otherwise use the default spec.
pub fn test_spec<E: EthSpec>() -> ChainSpec {
let mut spec = fork_name_from_env()
.map(|fork| fork.make_genesis_spec(E::default_spec()))
.unwrap_or_else(|| E::default_spec());
// Set target aggregators to a high value by default.
spec.target_aggregators_per_committee = DEFAULT_TARGET_AGGREGATORS;
@@ -228,7 +229,7 @@ pub struct Builder<T: BeaconChainTypes> {
testing_slot_clock: Option<TestingSlotClock>,
validator_monitor_config: Option<ValidatorMonitorConfig>,
genesis_state_builder: Option<InteropGenesisBuilder<T::EthSpec>>,
import_all_data_columns: bool,
node_custody_type: NodeCustodyType,
runtime: TestRuntime,
}
@@ -374,7 +375,7 @@ where
testing_slot_clock: None,
validator_monitor_config: None,
genesis_state_builder: None,
import_all_data_columns: false,
node_custody_type: NodeCustodyType::Fullnode,
runtime,
}
}
@@ -460,8 +461,8 @@ where
self
}
pub fn import_all_data_columns(mut self, import_all_data_columns: bool) -> Self {
self.import_all_data_columns = import_all_data_columns;
pub fn node_custody_type(mut self, node_custody_type: NodeCustodyType) -> Self {
self.node_custody_type = node_custody_type;
self
}
@@ -518,15 +519,15 @@ where
mock.server.execution_block_generator().osaka_time = spec.fulu_fork_epoch.map(|epoch| {
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
});
mock.server.execution_block_generator().amsterdam_time =
spec.gloas_fork_epoch.map(|epoch| {
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
});
self
}
pub fn mock_execution_layer(self) -> Self {
self.mock_execution_layer_with_config()
}
pub fn mock_execution_layer_with_config(mut self) -> Self {
pub fn mock_execution_layer(mut self) -> Self {
let mock = mock_execution_layer_from_parts::<E>(
self.spec.clone().expect("cannot build without spec"),
self.runtime.task_executor.clone(),
@@ -585,11 +586,10 @@ where
)
.task_executor(self.runtime.task_executor.clone())
.execution_layer(self.execution_layer)
.dummy_eth1_backend()
.expect("should build dummy backend")
.shutdown_sender(shutdown_tx)
.chain_config(chain_config)
.import_all_data_columns(self.import_all_data_columns)
.node_custody_type(self.node_custody_type)
.ordered_custody_column_indices(generate_data_column_indices_rand_order::<E>())
.event_handler(Some(ServerSentEventHandler::new_with_capacity(5)))
.validator_monitor_config(validator_monitor_config)
.rng(Box::new(StdRng::seed_from_u64(42)));
@@ -619,12 +619,6 @@ where
let chain = builder.build().expect("should build");
let sampling_column_count = if self.import_all_data_columns {
chain.spec.number_of_custody_groups as usize
} else {
chain.spec.custody_requirement as usize
};
BeaconChainHarness {
spec: chain.spec.clone(),
chain: Arc::new(chain),
@@ -635,7 +629,6 @@ where
mock_execution_layer: self.mock_execution_layer,
mock_builder: None,
rng: make_rng(),
sampling_column_count,
}
}
}
@@ -659,6 +652,9 @@ pub fn mock_execution_layer_from_parts<E: EthSpec>(
let osaka_time = spec.fulu_fork_epoch.map(|epoch| {
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
});
let amsterdam_time = spec.gloas_fork_epoch.map(|epoch| {
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
});
let kzg = get_kzg(&spec);
@@ -670,6 +666,7 @@ pub fn mock_execution_layer_from_parts<E: EthSpec>(
prague_time,
eip7805_time,
osaka_time,
amsterdam_time,
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec,
Some(kzg),
@@ -696,7 +693,6 @@ pub struct BeaconChainHarness<T: BeaconChainTypes> {
pub mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>,
pub mock_builder: Option<Arc<MockBuilder<T::EthSpec>>>,
pub sampling_column_count: usize,
pub rng: Mutex<StdRng>,
}
@@ -738,7 +734,10 @@ where
pub fn set_mock_builder(
&mut self,
beacon_url: SensitiveUrl,
) -> impl futures::Future<Output = ()> {
strict_registrations: bool,
apply_operations: bool,
broadcast_to_bn: bool,
) -> impl futures::Future<Output = ()> + use<E, Hot, Cold> {
let mock_el = self
.mock_execution_layer
.as_ref()
@@ -750,6 +749,9 @@ where
let (mock_builder, (addr, mock_builder_server)) = MockBuilder::new_for_testing(
mock_el_url,
beacon_url,
strict_registrations,
apply_operations,
broadcast_to_bn,
self.spec.clone(),
self.runtime.task_executor.clone(),
);
@@ -798,10 +800,6 @@ where
(0..self.validator_keypairs.len()).collect()
}
pub fn get_sampling_column_count(&self) -> usize {
self.sampling_column_count
}
pub fn slots_per_epoch(&self) -> u64 {
E::slots_per_epoch()
}
@@ -914,7 +912,9 @@ where
let fork_choice = self.chain.canonical_head.fork_choice_read_lock();
if heads.is_empty() {
let nodes = &fork_choice.proto_array().core_proto_array().nodes;
panic!("Expected to know head block root {head_block_root:?}, but heads is empty. Nodes: {nodes:#?}");
panic!(
"Expected to know head block root {head_block_root:?}, but heads is empty. Nodes: {nodes:#?}"
);
} else {
panic!(
"Expected to know head block root {head_block_root:?}, known heads {heads:#?}"
@@ -928,8 +928,67 @@ where
state: BeaconState<E>,
slot: Slot,
) -> (SignedBlindedBeaconBlock<E>, BeaconState<E>) {
let (unblinded, new_state) = self.make_block(state, slot).await;
((*unblinded.0).clone().into(), new_state)
self.make_blinded_block_with_modifier(state, slot, |_| {})
.await
}
pub async fn make_blinded_block_with_modifier(
&self,
mut state: BeaconState<E>,
slot: Slot,
block_modifier: impl FnOnce(&mut BlindedBeaconBlock<E>),
) -> (SignedBlindedBeaconBlock<E>, BeaconState<E>) {
assert_ne!(slot, 0, "can't produce a block at slot 0");
assert!(slot >= state.slot());
complete_state_advance(&mut state, None, slot, &self.spec)
.expect("should be able to advance state to slot");
state.build_caches(&self.spec).expect("should build caches");
let proposer_index = state.get_beacon_proposer_index(slot, &self.spec).unwrap();
// If we produce two blocks for the same slot, they hash up to the same value and
// BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce
// different blocks each time.
let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>());
let graffiti_settings =
GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti));
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
// Always use the builder, so that we produce a "real" blinded payload.
let builder_boost_factor = Some(u64::MAX);
let BeaconBlockResponseWrapper::Blinded(block_response) = self
.chain
.produce_block_on_state(
state,
None,
slot,
randao_reveal,
graffiti_settings,
ProduceBlockVerification::VerifyRandao,
builder_boost_factor,
BlockProductionVersion::V3,
)
.await
.unwrap()
else {
panic!("Should always be a blinded payload response");
};
let mut block = block_response.block;
block_modifier(&mut block);
let signed_block = block.sign(
&self.validator_keypairs[proposer_index].sk,
&block_response.state.fork(),
block_response.state.genesis_validators_root(),
&self.spec,
);
(signed_block, block_response.state)
}
/// Returns a newly created block, signed by the proposer for the given slot.
@@ -951,7 +1010,9 @@ where
// If we produce two blocks for the same slot, they hash up to the same value and
// BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce
// different blocks each time.
let graffiti = Graffiti::from(self.rng.lock().gen::<[u8; 32]>());
let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>());
let graffiti_settings =
GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti));
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
@@ -962,7 +1023,7 @@ where
None,
slot,
randao_reveal,
Some(graffiti),
graffiti_settings,
ProduceBlockVerification::VerifyRandao,
None,
BlockProductionVersion::FullV2,
@@ -1010,7 +1071,9 @@ where
// If we produce two blocks for the same slot, they hash up to the same value and
// BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce
// different blocks each time.
let graffiti = Graffiti::from(self.rng.lock().gen::<[u8; 32]>());
let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>());
let graffiti_settings =
GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti));
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
@@ -1023,7 +1086,7 @@ where
None,
slot,
randao_reveal,
Some(graffiti),
graffiti_settings,
ProduceBlockVerification::VerifyRandao,
None,
BlockProductionVersion::FullV2,
@@ -1137,9 +1200,14 @@ where
attn.aggregation_bits
.set(aggregation_bit_index, true)
.unwrap();
attn
Attestation::Electra(attn)
}
Attestation::Base(mut attn) => {
attn.aggregation_bits
.set(aggregation_bit_index, true)
.unwrap();
Attestation::Base(attn)
}
Attestation::Base(_) => panic!("Must be an Electra attestation"),
};
let aggregation_bits = attestation.get_aggregation_bits();
@@ -1167,8 +1235,10 @@ where
let single_attestation =
attestation.to_single_attestation_with_attester_index(attester_index as u64)?;
let fork_name = self.spec.fork_name_at_slot::<E>(attestation.data().slot);
let attestation: Attestation<E> =
single_attestation_to_attestation(&single_attestation, committee.committee).unwrap();
single_attestation_to_attestation(&single_attestation, committee.committee, fork_name)
.unwrap();
assert_eq!(
single_attestation.committee_index,
@@ -2344,7 +2414,7 @@ where
.collect::<Vec<_>>();
// Building a VarList from leaves
let deposit_data_list = VariableList::<_, U4294967296>::from(leaves.clone());
let deposit_data_list = VariableList::<_, U4294967296>::try_from(leaves.clone()).unwrap();
// Setting the deposit_root to be the tree_hash_root of the VarList
state.eth1_data_mut().deposit_root = deposit_data_list.tree_hash_root();
@@ -2368,7 +2438,7 @@ where
let deposits = datas
.into_par_iter()
.zip(proofs.into_par_iter())
.map(|(data, proof)| (data, proof.into()))
.map(|(data, proof)| (data, proof.try_into().unwrap()))
.map(|(data, proof)| Deposit { proof, data })
.collect::<Vec<_>>();
@@ -2440,7 +2510,7 @@ where
.blob_kzg_commitments()
.is_ok_and(|c| !c.is_empty());
if !has_blobs {
return RpcBlock::new_without_blobs(Some(block_root), block, 0);
return RpcBlock::new_without_blobs(Some(block_root), block);
}
// Blobs are stored as data columns from Fulu (PeerDAS)
@@ -2450,14 +2520,7 @@ where
.into_iter()
.map(CustodyDataColumn::from_asserted_custody)
.collect::<Vec<_>>();
RpcBlock::new_with_custody_columns(
Some(block_root),
block,
custody_columns,
self.get_sampling_column_count(),
&self.spec,
)
.unwrap()
RpcBlock::new_with_custody_columns(Some(block_root), block, custody_columns).unwrap()
} else {
let blobs = self.chain.get_blobs(&block_root).unwrap().blobs();
RpcBlock::new(Some(block_root), block, blobs).unwrap()
@@ -2465,14 +2528,15 @@ where
}
/// Builds an `RpcBlock` from a `SignedBeaconBlock` and `BlobsList`.
fn build_rpc_block_from_blobs(
pub fn build_rpc_block_from_blobs(
&self,
block_root: Hash256,
block: Arc<SignedBeaconBlock<E, FullPayload<E>>>,
blob_items: Option<(KzgProofs<E>, BlobsList<E>)>,
) -> Result<RpcBlock<E>, BlockError> {
Ok(if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) {
let sampling_column_count = self.get_sampling_column_count();
let epoch = block.slot().epoch(E::slots_per_epoch());
let sampling_columns = self.chain.sampling_columns_for_epoch(epoch);
if blob_items.is_some_and(|(_, blobs)| !blobs.is_empty()) {
// Note: this method ignores the actual custody columns and just take the first
@@ -2480,18 +2544,12 @@ where
// currently have any knowledge of the columns being custodied.
let columns = generate_data_column_sidecars_from_block(&block, &self.spec)
.into_iter()
.take(sampling_column_count)
.filter(|d| sampling_columns.contains(&d.index))
.map(CustodyDataColumn::from_asserted_custody)
.collect::<Vec<_>>();
RpcBlock::new_with_custody_columns(
Some(block_root),
block,
columns,
sampling_column_count,
&self.spec,
)?
RpcBlock::new_with_custody_columns(Some(block_root), block, columns)?
} else {
RpcBlock::new_without_blobs(Some(block_root), block, 0)
RpcBlock::new_without_blobs(Some(block_root), block)
}
} else {
let blobs = blob_items
@@ -2504,7 +2562,11 @@ where
})
}
pub fn process_attestations(&self, attestations: HarnessAttestations<E>) {
pub fn process_attestations(
&self,
attestations: HarnessAttestations<E>,
state: &BeaconState<E>,
) {
let num_validators = self.validator_keypairs.len();
let mut unaggregated = Vec::with_capacity(num_validators);
// This is an over-allocation, but it should be fine. It won't be *that* memory hungry and
@@ -2513,7 +2575,35 @@ where
for (unaggregated_attestations, maybe_signed_aggregate) in attestations.iter() {
for (attn, subnet) in unaggregated_attestations {
unaggregated.push((attn, Some(*subnet)));
let aggregation_bits = attn.get_aggregation_bits();
if aggregation_bits.len() != 1 {
panic!("Must be an unaggregated attestation")
}
let aggregation_bit = *aggregation_bits.first().unwrap();
let committee = state
.get_beacon_committee(attn.data().slot, attn.committee_index().unwrap())
.unwrap();
let attester_index = committee
.committee
.iter()
.enumerate()
.find_map(|(i, &index)| {
if aggregation_bit as usize == i {
return Some(index);
}
None
})
.unwrap();
let single_attestation = attn
.to_single_attestation_with_attester_index(attester_index as u64)
.unwrap();
unaggregated.push((single_attestation, Some(*subnet)));
}
if let Some(a) = maybe_signed_aggregate {
@@ -2523,7 +2613,9 @@ where
for result in self
.chain
.batch_verify_unaggregated_attestations_for_gossip(unaggregated.into_iter())
.batch_verify_unaggregated_attestations_for_gossip(
unaggregated.iter().map(|(attn, subnet)| (attn, *subnet)),
)
.unwrap()
{
let verified = result.unwrap();
@@ -2590,7 +2682,7 @@ where
) {
let attestations =
self.make_attestations(validators, state, state_root, block_hash, block.slot());
self.process_attestations(attestations);
self.process_attestations(attestations, state);
}
pub fn sync_committee_sign_block(
@@ -2745,10 +2837,7 @@ where
mut latest_block_hash: Option<SignedBeaconBlockHash>,
sync_committee_strategy: SyncCommitteeStrategy,
) -> AddBlocksResult<E> {
assert!(
slots.windows(2).all(|w| w[0] <= w[1]),
"Slots have to be sorted"
); // slice.is_sorted() isn't stabilized at the moment of writing this
assert!(slots.is_sorted(), "Slots have to be in ascending order");
let mut block_hash_from_slot: HashMap<Slot, SignedBeaconBlockHash> = HashMap::new();
let mut state_hash_from_slot: HashMap<Slot, BeaconStateHash> = HashMap::new();
for slot in slots {
@@ -2788,10 +2877,7 @@ where
mut latest_block_hash: Option<SignedBeaconBlockHash>,
sync_committee_strategy: SyncCommitteeStrategy,
) -> AddBlocksResult<E> {
assert!(
slots.windows(2).all(|w| w[0] <= w[1]),
"Slots have to be sorted"
); // slice.is_sorted() isn't stabilized at the moment of writing this
assert!(slots.is_sorted(), "Slots have to be in ascending order");
let mut block_hash_from_slot: HashMap<Slot, SignedBeaconBlockHash> = HashMap::new();
let mut state_hash_from_slot: HashMap<Slot, BeaconStateHash> = HashMap::new();
for slot in slots {
@@ -2918,7 +3004,6 @@ where
let chain_dump = self.chain.chain_dump().unwrap();
chain_dump
.iter()
.cloned()
.map(|checkpoint| checkpoint.beacon_state.finalized_checkpoint().root)
.filter(|block_hash| *block_hash != Hash256::zero())
.map(|hash| hash.into())
@@ -3190,17 +3275,22 @@ where
let is_peerdas_enabled = self.chain.spec.is_peer_das_enabled_for_epoch(block.epoch());
if is_peerdas_enabled {
let custody_columns = custody_columns_opt.unwrap_or_else(|| {
let sampling_column_count = self.get_sampling_column_count() as u64;
(0..sampling_column_count).collect()
let epoch = block.slot().epoch(E::slots_per_epoch());
self.chain
.sampling_columns_for_epoch(epoch)
.iter()
.copied()
.collect()
});
let verified_columns = generate_data_column_sidecars_from_block(block, &self.spec)
.into_iter()
.filter(|c| custody_columns.contains(&c.index))
.map(|sidecar| {
let column_index = sidecar.index;
let subnet_id =
DataColumnSubnetId::from_column_index(sidecar.index, &self.spec);
self.chain
.verify_data_column_sidecar_for_gossip(sidecar, column_index)
.verify_data_column_sidecar_for_gossip(sidecar, subnet_id)
})
.collect::<Result<Vec<_>, _>>()
.unwrap();
@@ -3246,96 +3336,50 @@ pub enum NumBlobs {
None,
}
macro_rules! add_blob_transactions {
($message:expr, $payload_type:ty, $num_blobs:expr, $rng:expr, $fork_name:expr) => {{
let num_blobs = match $num_blobs {
NumBlobs::Random => $rng.random_range(DEFAULT_MIN_BLOBS..=DEFAULT_MAX_BLOBS),
NumBlobs::Number(n) => n,
NumBlobs::None => 0,
};
let (bundle, transactions) =
execution_layer::test_utils::generate_blobs::<E>(num_blobs, $fork_name).unwrap();
let payload: &mut $payload_type = &mut $message.body.execution_payload;
payload.execution_payload.transactions = <_>::default();
for tx in Vec::from(transactions) {
payload.execution_payload.transactions.push(tx).unwrap();
}
$message.body.blob_kzg_commitments = bundle.commitments.clone();
bundle
}};
}
pub fn generate_rand_block_and_blobs<E: EthSpec>(
fork_name: ForkName,
num_blobs: NumBlobs,
rng: &mut impl Rng,
spec: &ChainSpec,
) -> (SignedBeaconBlock<E, FullPayload<E>>, Vec<BlobSidecar<E>>) {
let inner = map_fork_name!(fork_name, BeaconBlock, <_>::random_for_test(rng));
let mut block = SignedBeaconBlock::from_block(inner, types::Signature::random_for_test(rng));
let max_blobs = spec.max_blobs_per_block(block.epoch()) as usize;
let mut block = SignedBeaconBlock::from_block(inner, Signature::random_for_test(rng));
let mut blob_sidecars = vec![];
let bundle = match block {
SignedBeaconBlock::Deneb(SignedBeaconBlockDeneb {
ref mut message, ..
}) => {
// Get either zero blobs or a random number of blobs between 1 and Max Blobs.
let payload: &mut FullPayloadDeneb<E> = &mut message.body.execution_payload;
let num_blobs = match num_blobs {
NumBlobs::Random => rng.gen_range(1..=max_blobs),
NumBlobs::Number(n) => n,
NumBlobs::None => 0,
};
let (bundle, transactions) =
execution_layer::test_utils::generate_blobs::<E>(num_blobs, fork_name).unwrap();
payload.execution_payload.transactions = <_>::default();
for tx in Vec::from(transactions) {
payload.execution_payload.transactions.push(tx).unwrap();
}
message.body.blob_kzg_commitments = bundle.commitments.clone();
bundle
}
}) => add_blob_transactions!(message, FullPayloadDeneb<E>, num_blobs, rng, fork_name),
SignedBeaconBlock::Electra(SignedBeaconBlockElectra {
ref mut message, ..
}) => {
// Get either zero blobs or a random number of blobs between 1 and Max Blobs.
let payload: &mut FullPayloadElectra<E> = &mut message.body.execution_payload;
let num_blobs = match num_blobs {
NumBlobs::Random => rng.gen_range(1..=max_blobs),
NumBlobs::Number(n) => n,
NumBlobs::None => 0,
};
let (bundle, transactions) =
execution_layer::test_utils::generate_blobs::<E>(num_blobs, fork_name).unwrap();
payload.execution_payload.transactions = <_>::default();
for tx in Vec::from(transactions) {
payload.execution_payload.transactions.push(tx).unwrap();
}
message.body.blob_kzg_commitments = bundle.commitments.clone();
bundle
}
SignedBeaconBlock::Eip7805(SignedBeaconBlockEip7805 {
ref mut message, ..
}) => {
// Get either zero blobs or a random number of blobs between 1 and Max Blobs.
let payload: &mut FullPayloadEip7805<E> = &mut message.body.execution_payload;
let num_blobs = match num_blobs {
NumBlobs::Random => rng.gen_range(1..=max_blobs),
NumBlobs::Number(n) => n,
NumBlobs::None => 0,
};
let (bundle, transactions) =
execution_layer::test_utils::generate_blobs::<E>(num_blobs, fork_name).unwrap();
payload.execution_payload.transactions = <_>::default();
for tx in Vec::from(transactions) {
payload.execution_payload.transactions.push(tx).unwrap();
}
message.body.blob_kzg_commitments = bundle.commitments.clone();
bundle
}
}) => add_blob_transactions!(message, FullPayloadElectra<E>, num_blobs, rng, fork_name),
SignedBeaconBlock::Fulu(SignedBeaconBlockFulu {
ref mut message, ..
}) => {
// Get either zero blobs or a random number of blobs between 1 and Max Blobs.
let payload: &mut FullPayloadFulu<E> = &mut message.body.execution_payload;
let num_blobs = match num_blobs {
NumBlobs::Random => rng.gen_range(1..=max_blobs),
NumBlobs::Number(n) => n,
NumBlobs::None => 0,
};
let (bundle, transactions) =
execution_layer::test_utils::generate_blobs::<E>(num_blobs, fork_name).unwrap();
payload.execution_payload.transactions = <_>::default();
for tx in Vec::from(transactions) {
payload.execution_payload.transactions.push(tx).unwrap();
}
message.body.blob_kzg_commitments = bundle.commitments.clone();
bundle
}
}) => add_blob_transactions!(message, FullPayloadFulu<E>, num_blobs, rng, fork_name),
SignedBeaconBlock::Eip7805(SignedBeaconBlockEip7805 {
ref mut message, ..
}) => add_blob_transactions!(message, FullPayloadEip7805<E>, num_blobs, rng, fork_name),
// TODO(EIP-7732) Add `SignedBeaconBlock::Gloas` variant
_ => return (block, blob_sidecars),
};
@@ -3376,13 +3420,13 @@ pub fn generate_rand_block_and_data_columns<E: EthSpec>(
SignedBeaconBlock<E, FullPayload<E>>,
DataColumnSidecarList<E>,
) {
let (block, _blobs) = generate_rand_block_and_blobs(fork_name, num_blobs, rng, spec);
let (block, _blobs) = generate_rand_block_and_blobs(fork_name, num_blobs, rng);
let data_columns = generate_data_column_sidecars_from_block(&block, spec);
(block, data_columns)
}
/// Generate data column sidecars from pre-computed cells and proofs.
fn generate_data_column_sidecars_from_block<E: EthSpec>(
pub fn generate_data_column_sidecars_from_block<E: EthSpec>(
block: &SignedBeaconBlock<E>,
spec: &ChainSpec,
) -> DataColumnSidecarList<E> {
@@ -3401,7 +3445,7 @@ fn generate_data_column_sidecars_from_block<E: EthSpec>(
// load the precomputed column sidecar to avoid computing them for every block in the tests.
let template_data_columns = RuntimeVariableList::<DataColumnSidecar<E>>::from_ssz_bytes(
TEST_DATA_COLUMN_SIDECARS_SSZ,
spec.number_of_columns as usize,
E::number_of_columns(),
)
.unwrap();
@@ -3432,3 +3476,9 @@ fn generate_data_column_sidecars_from_block<E: EthSpec>(
)
.unwrap()
}
pub fn generate_data_column_indices_rand_order<E: EthSpec>() -> Vec<CustodyIndex> {
let mut indices = (0..E::number_of_columns() as u64).collect::<Vec<_>>();
indices.shuffle(&mut StdRng::seed_from_u64(42));
indices
}