mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-17 18:58:23 +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:
@@ -1433,14 +1433,12 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |value: serde_json::Value,
|
||||
consensus_version: ForkName,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
let request = PublishBlockRequest::<T::EthSpec>::context_deserialize(
|
||||
&value,
|
||||
@@ -1456,7 +1454,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
BroadcastValidation::default(),
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1472,14 +1469,12 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |block_bytes: Bytes,
|
||||
consensus_version: ForkName,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
let block_contents = PublishBlockRequest::<T::EthSpec>::from_ssz_bytes(
|
||||
&block_bytes,
|
||||
@@ -1495,7 +1490,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
BroadcastValidation::default(),
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1512,15 +1506,13 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |validation_level: api_types::BroadcastValidationQuery,
|
||||
value: serde_json::Value,
|
||||
consensus_version: ForkName,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
let request = PublishBlockRequest::<T::EthSpec>::context_deserialize(
|
||||
&value,
|
||||
@@ -1537,7 +1529,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
validation_level.broadcast_validation,
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1554,15 +1545,13 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |validation_level: api_types::BroadcastValidationQuery,
|
||||
block_bytes: Bytes,
|
||||
consensus_version: ForkName,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
let block_contents = PublishBlockRequest::<T::EthSpec>::from_ssz_bytes(
|
||||
&block_bytes,
|
||||
@@ -1578,7 +1567,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
validation_level.broadcast_validation,
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1598,13 +1586,11 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |block_contents: Arc<SignedBlindedBeaconBlock<T::EthSpec>>,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
publish_blocks::publish_blinded_block(
|
||||
block_contents,
|
||||
@@ -1612,7 +1598,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
BroadcastValidation::default(),
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1628,13 +1613,11 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |block_bytes: Bytes,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
let block = SignedBlindedBeaconBlock::<T::EthSpec>::from_ssz_bytes(
|
||||
&block_bytes,
|
||||
@@ -1650,7 +1633,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
BroadcastValidation::default(),
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1666,14 +1648,12 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |validation_level: api_types::BroadcastValidationQuery,
|
||||
blinded_block: Arc<SignedBlindedBeaconBlock<T::EthSpec>>,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
publish_blocks::publish_blinded_block(
|
||||
blinded_block,
|
||||
@@ -1681,7 +1661,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
validation_level.broadcast_validation,
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1697,14 +1676,12 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(network_globals.clone())
|
||||
.then(
|
||||
move |validation_level: api_types::BroadcastValidationQuery,
|
||||
block_bytes: Bytes,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
let block = SignedBlindedBeaconBlock::<T::EthSpec>::from_ssz_bytes(
|
||||
&block_bytes,
|
||||
@@ -1720,7 +1697,6 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
&network_tx,
|
||||
validation_level.broadcast_validation,
|
||||
duplicate_block_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -3839,11 +3815,8 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
if let Some(cgc_change) = chain
|
||||
.data_availability_checker
|
||||
.custody_context()
|
||||
.register_validators::<T::EthSpec>(
|
||||
validators_and_balances,
|
||||
current_slot,
|
||||
&chain.spec,
|
||||
) {
|
||||
.register_validators(validators_and_balances, current_slot, &chain.spec)
|
||||
{
|
||||
chain.update_data_column_custody_info(Some(
|
||||
cgc_change
|
||||
.effective_epoch
|
||||
|
||||
@@ -15,7 +15,7 @@ use eth2::types::{
|
||||
};
|
||||
use execution_layer::{ProvenancedPayload, SubmitBlindedBlockResponse};
|
||||
use futures::TryFutureExt;
|
||||
use lighthouse_network::{NetworkGlobals, PubsubMessage};
|
||||
use lighthouse_network::PubsubMessage;
|
||||
use network::NetworkMessage;
|
||||
use rand::prelude::SliceRandom;
|
||||
use slot_clock::SlotClock;
|
||||
@@ -82,7 +82,6 @@ pub async fn publish_block<T: BeaconChainTypes, B: IntoGossipVerifiedBlock<T>>(
|
||||
network_tx: &UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
validation_level: BroadcastValidation,
|
||||
duplicate_status_code: StatusCode,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>,
|
||||
) -> Result<Response, Rejection> {
|
||||
let seen_timestamp = timestamp_now();
|
||||
let block_publishing_delay_for_testing = chain.config.block_publishing_delay;
|
||||
@@ -223,7 +222,8 @@ pub async fn publish_block<T: BeaconChainTypes, B: IntoGossipVerifiedBlock<T>>(
|
||||
publish_column_sidecars(network_tx, &gossip_verified_columns, &chain).map_err(|_| {
|
||||
warp_utils::reject::custom_server_error("unable to publish data column sidecars".into())
|
||||
})?;
|
||||
let sampling_columns_indices = &network_globals.sampling_columns();
|
||||
let epoch = block.slot().epoch(T::EthSpec::slots_per_epoch());
|
||||
let sampling_columns_indices = chain.sampling_columns_for_epoch(epoch);
|
||||
let sampling_columns = gossip_verified_columns
|
||||
.into_iter()
|
||||
.flatten()
|
||||
@@ -405,7 +405,7 @@ fn build_gossip_verified_data_columns<T: BeaconChainTypes>(
|
||||
let column_index = data_column_sidecar.index;
|
||||
let subnet = DataColumnSubnetId::from_column_index(column_index, &chain.spec);
|
||||
let gossip_verified_column =
|
||||
GossipVerifiedDataColumn::new(data_column_sidecar, subnet.into(), chain);
|
||||
GossipVerifiedDataColumn::new(data_column_sidecar, subnet, chain);
|
||||
|
||||
match gossip_verified_column {
|
||||
Ok(blob) => Ok(Some(blob)),
|
||||
@@ -633,7 +633,6 @@ pub async fn publish_blinded_block<T: BeaconChainTypes>(
|
||||
network_tx: &UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
validation_level: BroadcastValidation,
|
||||
duplicate_status_code: StatusCode,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>,
|
||||
) -> Result<Response, Rejection> {
|
||||
let block_root = blinded_block.canonical_root();
|
||||
let full_block_opt = reconstruct_block(chain.clone(), block_root, blinded_block).await?;
|
||||
@@ -646,7 +645,6 @@ pub async fn publish_blinded_block<T: BeaconChainTypes>(
|
||||
network_tx,
|
||||
validation_level,
|
||||
duplicate_status_code,
|
||||
network_globals,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
|
||||
@@ -372,7 +372,6 @@ pub async fn consensus_partial_pass_only_consensus() {
|
||||
|
||||
/* submit `block_b` which should induce equivocation */
|
||||
let channel = tokio::sync::mpsc::unbounded_channel();
|
||||
let network_globals = tester.ctx.network_globals.clone().unwrap();
|
||||
|
||||
let publication_result = publish_block(
|
||||
None,
|
||||
@@ -381,7 +380,6 @@ pub async fn consensus_partial_pass_only_consensus() {
|
||||
&channel.0,
|
||||
validation_level,
|
||||
StatusCode::ACCEPTED,
|
||||
network_globals,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -663,7 +661,6 @@ pub async fn equivocation_consensus_late_equivocation() {
|
||||
assert!(gossip_block_a.is_err());
|
||||
|
||||
let channel = tokio::sync::mpsc::unbounded_channel();
|
||||
let network_globals = tester.ctx.network_globals.clone().unwrap();
|
||||
|
||||
let publication_result = publish_block(
|
||||
None,
|
||||
@@ -672,7 +669,6 @@ pub async fn equivocation_consensus_late_equivocation() {
|
||||
&channel.0,
|
||||
validation_level,
|
||||
StatusCode::ACCEPTED,
|
||||
network_globals,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1302,7 +1298,6 @@ pub async fn blinded_equivocation_consensus_late_equivocation() {
|
||||
assert!(gossip_block_a.is_err());
|
||||
|
||||
let channel = tokio::sync::mpsc::unbounded_channel();
|
||||
let network_globals = tester.ctx.network_globals.clone().unwrap();
|
||||
|
||||
let publication_result = publish_blinded_block(
|
||||
block_b,
|
||||
@@ -1310,7 +1305,6 @@ pub async fn blinded_equivocation_consensus_late_equivocation() {
|
||||
&channel.0,
|
||||
validation_level,
|
||||
StatusCode::ACCEPTED,
|
||||
network_globals,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1487,7 +1481,7 @@ pub async fn block_seen_on_gossip_with_some_blobs_or_columns() {
|
||||
&block,
|
||||
partial_blobs.iter(),
|
||||
partial_kzg_proofs.iter(),
|
||||
Some(get_custody_columns(&tester)),
|
||||
Some(get_custody_columns(&tester, block.slot())),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1558,7 +1552,7 @@ pub async fn blobs_or_columns_seen_on_gossip_without_block() {
|
||||
&block,
|
||||
blobs.iter(),
|
||||
kzg_proofs.iter(),
|
||||
Some(get_custody_columns(&tester)),
|
||||
Some(get_custody_columns(&tester, block.slot())),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1629,7 +1623,7 @@ async fn blobs_or_columns_seen_on_gossip_without_block_and_no_http_blobs_or_colu
|
||||
&block,
|
||||
blobs.iter(),
|
||||
kzg_proofs.iter(),
|
||||
Some(get_custody_columns(&tester)),
|
||||
Some(get_custody_columns(&tester, block.slot())),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1703,7 +1697,7 @@ async fn slashable_blobs_or_columns_seen_on_gossip_cause_failure() {
|
||||
&block_b,
|
||||
blobs_b.iter(),
|
||||
kzg_proofs_b.iter(),
|
||||
Some(get_custody_columns(&tester)),
|
||||
Some(get_custody_columns(&tester, block_b.slot())),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1802,11 +1796,15 @@ fn assert_server_message_error(error_response: eth2::Error, expected_message: St
|
||||
assert_eq!(err.message, expected_message);
|
||||
}
|
||||
|
||||
fn get_custody_columns(tester: &InteractiveTester<E>) -> HashSet<ColumnIndex> {
|
||||
fn get_custody_columns(tester: &InteractiveTester<E>, slot: Slot) -> HashSet<ColumnIndex> {
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
tester
|
||||
.ctx
|
||||
.network_globals
|
||||
.chain
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.sampling_columns()
|
||||
.sampling_columns_for_epoch(epoch)
|
||||
.iter()
|
||||
.copied()
|
||||
.collect()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user