mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 10:22:38 +00:00
Fix wrong columns getting processed on a CGC change (#7792)
This PR fixes a bug where wrong columns could get processed immediately after a CGC increase.
Scenario:
- The node's CGC increased due to additional validators attached to it (lets say from 10 to 11)
- The new CGC is advertised and new subnets are subscribed immediately, however the change won't be effective in the data availability check until the next epoch (See [this](ab0e8870b4/beacon_node/beacon_chain/src/validator_custody.rs (L93-L99))). Data availability checker still only require 10 columns for the current epoch.
- During this time, data columns for the additional custody column (lets say column 11) may arrive via gossip as we're already subscribed to the topic, and it may be incorrectly used to satisfy the existing data availability requirement (10 columns), and result in this additional column (instead of a required one) getting persisted, resulting in database inconsistency.
This commit is contained in:
@@ -607,7 +607,6 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
self: &Arc<Self>,
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
_peer_client: Client,
|
||||
subnet_id: DataColumnSubnetId,
|
||||
column_sidecar: Arc<DataColumnSidecar<T::EthSpec>>,
|
||||
seen_duration: Duration,
|
||||
@@ -623,7 +622,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
);
|
||||
match self
|
||||
.chain
|
||||
.verify_data_column_sidecar_for_gossip(column_sidecar.clone(), *subnet_id)
|
||||
.verify_data_column_sidecar_for_gossip(column_sidecar.clone(), subnet_id)
|
||||
{
|
||||
Ok(gossip_verified_data_column) => {
|
||||
metrics::inc_counter(
|
||||
@@ -650,6 +649,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
duration,
|
||||
);
|
||||
}
|
||||
|
||||
self.process_gossip_verified_data_column(
|
||||
peer_id,
|
||||
gossip_verified_data_column,
|
||||
|
||||
@@ -227,7 +227,6 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
self: &Arc<Self>,
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
peer_client: Client,
|
||||
subnet_id: DataColumnSubnetId,
|
||||
column_sidecar: Arc<DataColumnSidecar<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
@@ -238,7 +237,6 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
.process_gossip_data_column_sidecar(
|
||||
message_id,
|
||||
peer_id,
|
||||
peer_client,
|
||||
subnet_id,
|
||||
column_sidecar,
|
||||
seen_timestamp,
|
||||
@@ -753,7 +751,8 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
block_root: Hash256,
|
||||
publish_blobs: bool,
|
||||
) {
|
||||
let custody_columns = self.network_globals.sampling_columns();
|
||||
let epoch = block.slot().epoch(T::EthSpec::slots_per_epoch());
|
||||
let custody_columns = self.chain.sampling_columns_for_epoch(epoch);
|
||||
let self_cloned = self.clone();
|
||||
let publish_fn = move |blobs_or_data_column| {
|
||||
if publish_blobs {
|
||||
|
||||
@@ -37,8 +37,9 @@ use tokio::sync::mpsc;
|
||||
use types::blob_sidecar::FixedBlobSidecarList;
|
||||
use types::{
|
||||
AttesterSlashing, BlobSidecar, BlobSidecarList, ChainSpec, DataColumnSidecarList,
|
||||
DataColumnSubnetId, Epoch, Hash256, MainnetEthSpec, ProposerSlashing, SignedAggregateAndProof,
|
||||
SignedBeaconBlock, SignedVoluntaryExit, SingleAttestation, Slot, SubnetId,
|
||||
DataColumnSubnetId, Epoch, EthSpec, Hash256, MainnetEthSpec, ProposerSlashing,
|
||||
SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, SingleAttestation, Slot,
|
||||
SubnetId,
|
||||
};
|
||||
|
||||
type E = MainnetEthSpec;
|
||||
@@ -271,6 +272,8 @@ impl TestRig {
|
||||
let (blob_sidecars, data_columns) = if let Some((kzg_proofs, blobs)) = next_block_tuple.1 {
|
||||
if chain.spec.is_peer_das_enabled_for_epoch(block.epoch()) {
|
||||
let kzg = get_kzg(&chain.spec);
|
||||
let epoch = block.slot().epoch(E::slots_per_epoch());
|
||||
let sampling_indices = chain.sampling_columns_for_epoch(epoch);
|
||||
let custody_columns: DataColumnSidecarList<E> = blobs_to_data_column_sidecars(
|
||||
&blobs.iter().collect_vec(),
|
||||
kzg_proofs.clone().into_iter().collect_vec(),
|
||||
@@ -280,7 +283,7 @@ impl TestRig {
|
||||
)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter(|c| network_globals.sampling_columns().contains(&c.index))
|
||||
.filter(|c| sampling_indices.contains(&c.index))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(None, Some(custody_columns))
|
||||
@@ -357,7 +360,6 @@ impl TestRig {
|
||||
.send_gossip_data_column_sidecar(
|
||||
junk_message_id(),
|
||||
junk_peer_id(),
|
||||
Client::default(),
|
||||
DataColumnSubnetId::from_column_index(data_column.index, &self.chain.spec),
|
||||
data_column.clone(),
|
||||
Duration::from_secs(0),
|
||||
@@ -807,7 +809,8 @@ async fn accept_processed_gossip_data_columns_without_import() {
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|data_column| {
|
||||
let subnet_id = data_column.index;
|
||||
let subnet_id =
|
||||
DataColumnSubnetId::from_column_index(data_column.index, &rig.chain.spec);
|
||||
validate_data_column_sidecar_for_gossip::<_, DoNotObserve>(
|
||||
data_column,
|
||||
subnet_id,
|
||||
@@ -820,7 +823,7 @@ async fn accept_processed_gossip_data_columns_without_import() {
|
||||
let block_root = rig.next_block.canonical_root();
|
||||
rig.chain
|
||||
.data_availability_checker
|
||||
.put_gossip_verified_data_columns(block_root, verified_data_columns)
|
||||
.put_gossip_verified_data_columns(block_root, rig.next_block.slot(), verified_data_columns)
|
||||
.expect("should put data columns into availability cache");
|
||||
|
||||
// WHEN an already processed but unobserved data column is received via gossip
|
||||
|
||||
@@ -379,7 +379,6 @@ impl<T: BeaconChainTypes> Router<T> {
|
||||
.send_gossip_data_column_sidecar(
|
||||
message_id,
|
||||
peer_id,
|
||||
self.network_globals.client(&peer_id),
|
||||
subnet_id,
|
||||
column_sidecar,
|
||||
timestamp_now(),
|
||||
|
||||
@@ -551,7 +551,13 @@ impl<T: BeaconChainTypes> SyncNetworkContext<T> {
|
||||
// Attempt to find all required custody peers before sending any request or creating an ID
|
||||
let columns_by_range_peers_to_request =
|
||||
if matches!(batch_type, ByRangeRequestType::BlocksAndColumns) {
|
||||
let column_indexes = self.network_globals().sampling_columns();
|
||||
let epoch = Slot::new(*request.start_slot()).epoch(T::EthSpec::slots_per_epoch());
|
||||
let column_indexes = self
|
||||
.chain
|
||||
.sampling_columns_for_epoch(epoch)
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
Some(self.select_columns_by_range_peers_to_request(
|
||||
&column_indexes,
|
||||
peers,
|
||||
@@ -602,18 +608,14 @@ impl<T: BeaconChainTypes> SyncNetworkContext<T> {
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let epoch = Slot::new(*request.start_slot()).epoch(T::EthSpec::slots_per_epoch());
|
||||
let info = RangeBlockComponentsRequest::new(
|
||||
blocks_req_id,
|
||||
blobs_req_id,
|
||||
data_column_requests.map(|data_column_requests| {
|
||||
(
|
||||
data_column_requests,
|
||||
self.network_globals()
|
||||
.sampling_columns()
|
||||
.clone()
|
||||
.iter()
|
||||
.copied()
|
||||
.collect(),
|
||||
self.chain.sampling_columns_for_epoch(epoch).to_vec(),
|
||||
)
|
||||
}),
|
||||
);
|
||||
@@ -1015,11 +1017,16 @@ impl<T: BeaconChainTypes> SyncNetworkContext<T> {
|
||||
.cached_data_column_indexes(&block_root)
|
||||
.unwrap_or_default();
|
||||
|
||||
let current_epoch = self.chain.epoch().map_err(|e| {
|
||||
RpcRequestSendError::InternalError(format!("Unable to read slot clock {:?}", e))
|
||||
})?;
|
||||
|
||||
// Include only the blob indexes not yet imported (received through gossip)
|
||||
let custody_indexes_to_fetch = self
|
||||
.network_globals()
|
||||
.sampling_columns()
|
||||
.into_iter()
|
||||
.chain
|
||||
.sampling_columns_for_epoch(current_epoch)
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|index| !custody_indexes_imported.contains(index))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
@@ -367,11 +367,11 @@ impl<E: EthSpec, B: BatchConfig> BatchInfo<E, B> {
|
||||
|
||||
pub fn processing_completed(
|
||||
&mut self,
|
||||
procesing_result: BatchProcessingResult,
|
||||
processing_result: BatchProcessingResult,
|
||||
) -> Result<BatchOperationOutcome, WrongState> {
|
||||
match self.state.poison() {
|
||||
BatchState::Processing(attempt) => {
|
||||
self.state = match procesing_result {
|
||||
self.state = match processing_result {
|
||||
BatchProcessingResult::Success => BatchState::AwaitingValidation(attempt),
|
||||
BatchProcessingResult::FaultyFailure => {
|
||||
// register the failed attempt
|
||||
|
||||
Reference in New Issue
Block a user