Adding light_client gossip topics (#3693)
## Issue Addressed Implementing the light_client_gossip topics but I'm not there yet. Which issue # does this PR address? Partially #3651 ## Proposed Changes Add light client gossip topics. Please list or describe the changes introduced by this PR. I'm going to Implement light_client_finality_update and light_client_optimistic_update gossip topics. Currently I've attempted the former and I'm seeking feedback. ## Additional Info I've only implemented the light_client_finality_update topic because I wanted to make sure I was on the correct path. Also checking that the gossiped LightClientFinalityUpdate is the same as the locally constructed one is not implemented because caching the updates will make this much easier. Could someone give me some feedback on this please? Please provide any additional information. For example, future considerations or information useful for reviewers. Co-authored-by: GeemoCandama <104614073+GeemoCandama@users.noreply.github.com>
This commit is contained in:
parent
c973bfc90c
commit
1b28ef8a8d
@ -22,6 +22,12 @@ use crate::execution_payload::{get_execution_payload, NotifyExecutionLayer, Prep
|
||||
use crate::fork_choice_signal::{ForkChoiceSignalRx, ForkChoiceSignalTx, ForkChoiceWaitResult};
|
||||
use crate::head_tracker::HeadTracker;
|
||||
use crate::historical_blocks::HistoricalBlockError;
|
||||
use crate::light_client_finality_update_verification::{
|
||||
Error as LightClientFinalityUpdateError, VerifiedLightClientFinalityUpdate,
|
||||
};
|
||||
use crate::light_client_optimistic_update_verification::{
|
||||
Error as LightClientOptimisticUpdateError, VerifiedLightClientOptimisticUpdate,
|
||||
};
|
||||
use crate::migrate::BackgroundMigrator;
|
||||
use crate::naive_aggregation_pool::{
|
||||
AggregatedAttestationMap, Error as NaiveAggregationError, NaiveAggregationPool,
|
||||
@ -335,6 +341,10 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
/// Maintains a record of which validators we've seen attester slashings for.
|
||||
pub(crate) observed_attester_slashings:
|
||||
Mutex<ObservedOperations<AttesterSlashing<T::EthSpec>, T::EthSpec>>,
|
||||
/// The most recently validated light client finality update received on gossip.
|
||||
pub latest_seen_finality_update: Mutex<Option<LightClientFinalityUpdate<T::EthSpec>>>,
|
||||
/// The most recently validated light client optimistic update received on gossip.
|
||||
pub latest_seen_optimistic_update: Mutex<Option<LightClientOptimisticUpdate<T::EthSpec>>>,
|
||||
/// Provides information from the Ethereum 1 (PoW) chain.
|
||||
pub eth1_chain: Option<Eth1Chain<T::Eth1Chain, T::EthSpec>>,
|
||||
/// Interfaces with the execution client.
|
||||
@ -1779,6 +1789,40 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Accepts some 'LightClientFinalityUpdate' from the network and attempts to verify it
|
||||
pub fn verify_finality_update_for_gossip(
|
||||
self: &Arc<Self>,
|
||||
light_client_finality_update: LightClientFinalityUpdate<T::EthSpec>,
|
||||
seen_timestamp: Duration,
|
||||
) -> Result<VerifiedLightClientFinalityUpdate<T>, LightClientFinalityUpdateError> {
|
||||
VerifiedLightClientFinalityUpdate::verify(
|
||||
light_client_finality_update,
|
||||
self,
|
||||
seen_timestamp,
|
||||
)
|
||||
.map(|v| {
|
||||
metrics::inc_counter(&metrics::FINALITY_UPDATE_PROCESSING_SUCCESSES);
|
||||
v
|
||||
})
|
||||
}
|
||||
|
||||
/// Accepts some 'LightClientOptimisticUpdate' from the network and attempts to verify it
|
||||
pub fn verify_optimistic_update_for_gossip(
|
||||
self: &Arc<Self>,
|
||||
light_client_optimistic_update: LightClientOptimisticUpdate<T::EthSpec>,
|
||||
seen_timestamp: Duration,
|
||||
) -> Result<VerifiedLightClientOptimisticUpdate<T>, LightClientOptimisticUpdateError> {
|
||||
VerifiedLightClientOptimisticUpdate::verify(
|
||||
light_client_optimistic_update,
|
||||
self,
|
||||
seen_timestamp,
|
||||
)
|
||||
.map(|v| {
|
||||
metrics::inc_counter(&metrics::OPTIMISTIC_UPDATE_PROCESSING_SUCCESSES);
|
||||
v
|
||||
})
|
||||
}
|
||||
|
||||
/// Accepts some attestation-type object and attempts to verify it in the context of fork
|
||||
/// choice. If it is valid it is applied to `self.fork_choice`.
|
||||
///
|
||||
|
@ -780,6 +780,8 @@ where
|
||||
observed_voluntary_exits: <_>::default(),
|
||||
observed_proposer_slashings: <_>::default(),
|
||||
observed_attester_slashings: <_>::default(),
|
||||
latest_seen_finality_update: <_>::default(),
|
||||
latest_seen_optimistic_update: <_>::default(),
|
||||
eth1_chain: self.eth1_chain,
|
||||
execution_layer: self.execution_layer,
|
||||
genesis_validators_root,
|
||||
|
@ -21,6 +21,8 @@ pub mod fork_choice_signal;
|
||||
pub mod fork_revert;
|
||||
mod head_tracker;
|
||||
pub mod historical_blocks;
|
||||
pub mod light_client_finality_update_verification;
|
||||
pub mod light_client_optimistic_update_verification;
|
||||
pub mod merge_readiness;
|
||||
pub mod metrics;
|
||||
pub mod migrate;
|
||||
|
@ -0,0 +1,135 @@
|
||||
use crate::{
|
||||
beacon_chain::MAXIMUM_GOSSIP_CLOCK_DISPARITY, BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use slot_clock::SlotClock;
|
||||
use std::time::Duration;
|
||||
use strum::AsRefStr;
|
||||
use types::{
|
||||
light_client_update::Error as LightClientUpdateError, LightClientFinalityUpdate, Slot,
|
||||
};
|
||||
|
||||
/// Returned when a light client finality update was not successfully verified. It might not have been verified for
|
||||
/// two reasons:
|
||||
///
|
||||
/// - The light client finality message is malformed or inappropriate for the context (indicated by all variants
|
||||
/// other than `BeaconChainError`).
|
||||
/// - The application encountered an internal error whilst attempting to determine validity
|
||||
/// (the `BeaconChainError` variant)
|
||||
#[derive(Debug, AsRefStr)]
|
||||
pub enum Error {
|
||||
/// Light client finality update message with a lower or equal finalized_header slot already forwarded.
|
||||
FinalityUpdateAlreadySeen,
|
||||
/// The light client finality message was received is prior to one-third of slot duration passage. (with
|
||||
/// respect to the gossip clock disparity and slot clock duration).
|
||||
///
|
||||
/// ## Peer scoring
|
||||
///
|
||||
/// Assuming the local clock is correct, the peer has sent an invalid message.
|
||||
TooEarly,
|
||||
/// Light client finality update message does not match the locally constructed one.
|
||||
///
|
||||
/// ## Peer Scoring
|
||||
///
|
||||
InvalidLightClientFinalityUpdate,
|
||||
/// Signature slot start time is none.
|
||||
SigSlotStartIsNone,
|
||||
/// Failed to construct a LightClientFinalityUpdate from state.
|
||||
FailedConstructingUpdate,
|
||||
/// Beacon chain error occured.
|
||||
BeaconChainError(BeaconChainError),
|
||||
LightClientUpdateError(LightClientUpdateError),
|
||||
}
|
||||
|
||||
impl From<BeaconChainError> for Error {
|
||||
fn from(e: BeaconChainError) -> Self {
|
||||
Error::BeaconChainError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LightClientUpdateError> for Error {
|
||||
fn from(e: LightClientUpdateError) -> Self {
|
||||
Error::LightClientUpdateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a `LightClientFinalityUpdate` that has been verified for propagation on the gossip network.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = "T: BeaconChainTypes"))]
|
||||
pub struct VerifiedLightClientFinalityUpdate<T: BeaconChainTypes> {
|
||||
light_client_finality_update: LightClientFinalityUpdate<T::EthSpec>,
|
||||
seen_timestamp: Duration,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> VerifiedLightClientFinalityUpdate<T> {
|
||||
/// Returns `Ok(Self)` if the `light_client_finality_update` is valid to be (re)published on the gossip
|
||||
/// network.
|
||||
pub fn verify(
|
||||
light_client_finality_update: LightClientFinalityUpdate<T::EthSpec>,
|
||||
chain: &BeaconChain<T>,
|
||||
seen_timestamp: Duration,
|
||||
) -> Result<Self, Error> {
|
||||
let gossiped_finality_slot = light_client_finality_update.finalized_header.slot;
|
||||
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
|
||||
let signature_slot = light_client_finality_update.signature_slot;
|
||||
let start_time = chain.slot_clock.start_of(signature_slot);
|
||||
let mut latest_seen_finality_update = chain.latest_seen_finality_update.lock();
|
||||
|
||||
let head = chain.canonical_head.cached_head();
|
||||
let head_block = &head.snapshot.beacon_block;
|
||||
let attested_block_root = head_block.message().parent_root();
|
||||
let attested_block = chain
|
||||
.get_blinded_block(&attested_block_root)?
|
||||
.ok_or(Error::FailedConstructingUpdate)?;
|
||||
let mut attested_state = chain
|
||||
.get_state(&attested_block.state_root(), Some(attested_block.slot()))?
|
||||
.ok_or(Error::FailedConstructingUpdate)?;
|
||||
|
||||
let finalized_block_root = attested_state.finalized_checkpoint().root;
|
||||
let finalized_block = chain
|
||||
.get_blinded_block(&finalized_block_root)?
|
||||
.ok_or(Error::FailedConstructingUpdate)?;
|
||||
let latest_seen_finality_update_slot = match latest_seen_finality_update.as_ref() {
|
||||
Some(update) => update.finalized_header.slot,
|
||||
None => Slot::new(0),
|
||||
};
|
||||
|
||||
// verify that no other finality_update with a lower or equal
|
||||
// finalized_header.slot was already forwarded on the network
|
||||
if gossiped_finality_slot <= latest_seen_finality_update_slot {
|
||||
return Err(Error::FinalityUpdateAlreadySeen);
|
||||
}
|
||||
|
||||
// verify that enough time has passed for the block to have been propagated
|
||||
match start_time {
|
||||
Some(time) => {
|
||||
if seen_timestamp + MAXIMUM_GOSSIP_CLOCK_DISPARITY < time + one_third_slot_duration
|
||||
{
|
||||
return Err(Error::TooEarly);
|
||||
}
|
||||
}
|
||||
None => return Err(Error::SigSlotStartIsNone),
|
||||
}
|
||||
|
||||
let head_state = &head.snapshot.beacon_state;
|
||||
let finality_update = LightClientFinalityUpdate::new(
|
||||
&chain.spec,
|
||||
head_state,
|
||||
head_block,
|
||||
&mut attested_state,
|
||||
&finalized_block,
|
||||
)?;
|
||||
|
||||
// verify that the gossiped finality update is the same as the locally constructed one.
|
||||
if finality_update != light_client_finality_update {
|
||||
return Err(Error::InvalidLightClientFinalityUpdate);
|
||||
}
|
||||
|
||||
*latest_seen_finality_update = Some(light_client_finality_update.clone());
|
||||
|
||||
Ok(Self {
|
||||
light_client_finality_update,
|
||||
seen_timestamp,
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
use crate::{
|
||||
beacon_chain::MAXIMUM_GOSSIP_CLOCK_DISPARITY, BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use slot_clock::SlotClock;
|
||||
use std::time::Duration;
|
||||
use strum::AsRefStr;
|
||||
use types::{
|
||||
light_client_update::Error as LightClientUpdateError, LightClientOptimisticUpdate, Slot,
|
||||
};
|
||||
|
||||
/// Returned when a light client optimistic update was not successfully verified. It might not have been verified for
|
||||
/// two reasons:
|
||||
///
|
||||
/// - The light client optimistic message is malformed or inappropriate for the context (indicated by all variants
|
||||
/// other than `BeaconChainError`).
|
||||
/// - The application encountered an internal error whilst attempting to determine validity
|
||||
/// (the `BeaconChainError` variant)
|
||||
#[derive(Debug, AsRefStr)]
|
||||
pub enum Error {
|
||||
/// Light client optimistic update message with a lower or equal optimistic_header slot already forwarded.
|
||||
OptimisticUpdateAlreadySeen,
|
||||
/// The light client optimistic message was received is prior to one-third of slot duration passage. (with
|
||||
/// respect to the gossip clock disparity and slot clock duration).
|
||||
///
|
||||
/// ## Peer scoring
|
||||
///
|
||||
/// Assuming the local clock is correct, the peer has sent an invalid message.
|
||||
TooEarly,
|
||||
/// Light client optimistic update message does not match the locally constructed one.
|
||||
///
|
||||
/// ## Peer Scoring
|
||||
///
|
||||
InvalidLightClientOptimisticUpdate,
|
||||
/// Signature slot start time is none.
|
||||
SigSlotStartIsNone,
|
||||
/// Failed to construct a LightClientOptimisticUpdate from state.
|
||||
FailedConstructingUpdate,
|
||||
/// Beacon chain error occured.
|
||||
BeaconChainError(BeaconChainError),
|
||||
LightClientUpdateError(LightClientUpdateError),
|
||||
}
|
||||
|
||||
impl From<BeaconChainError> for Error {
|
||||
fn from(e: BeaconChainError) -> Self {
|
||||
Error::BeaconChainError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LightClientUpdateError> for Error {
|
||||
fn from(e: LightClientUpdateError) -> Self {
|
||||
Error::LightClientUpdateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a `LightClientOptimisticUpdate` that has been verified for propagation on the gossip network.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = "T: BeaconChainTypes"))]
|
||||
pub struct VerifiedLightClientOptimisticUpdate<T: BeaconChainTypes> {
|
||||
light_client_optimistic_update: LightClientOptimisticUpdate<T::EthSpec>,
|
||||
seen_timestamp: Duration,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
|
||||
/// Returns `Ok(Self)` if the `light_client_optimistic_update` is valid to be (re)published on the gossip
|
||||
/// network.
|
||||
pub fn verify(
|
||||
light_client_optimistic_update: LightClientOptimisticUpdate<T::EthSpec>,
|
||||
chain: &BeaconChain<T>,
|
||||
seen_timestamp: Duration,
|
||||
) -> Result<Self, Error> {
|
||||
let gossiped_optimistic_slot = light_client_optimistic_update.attested_header.slot;
|
||||
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
|
||||
let signature_slot = light_client_optimistic_update.signature_slot;
|
||||
let start_time = chain.slot_clock.start_of(signature_slot);
|
||||
let mut latest_seen_optimistic_update = chain.latest_seen_optimistic_update.lock();
|
||||
|
||||
let head = chain.canonical_head.cached_head();
|
||||
let head_block = &head.snapshot.beacon_block;
|
||||
let attested_block_root = head_block.message().parent_root();
|
||||
let attested_block = chain
|
||||
.get_blinded_block(&attested_block_root)?
|
||||
.ok_or(Error::FailedConstructingUpdate)?;
|
||||
|
||||
let attested_state = chain
|
||||
.get_state(&attested_block.state_root(), Some(attested_block.slot()))?
|
||||
.ok_or(Error::FailedConstructingUpdate)?;
|
||||
let latest_seen_optimistic_update_slot = match latest_seen_optimistic_update.as_ref() {
|
||||
Some(update) => update.attested_header.slot,
|
||||
None => Slot::new(0),
|
||||
};
|
||||
|
||||
// verify that no other optimistic_update with a lower or equal
|
||||
// optimistic_header.slot was already forwarded on the network
|
||||
if gossiped_optimistic_slot <= latest_seen_optimistic_update_slot {
|
||||
return Err(Error::OptimisticUpdateAlreadySeen);
|
||||
}
|
||||
|
||||
// verify that enough time has passed for the block to have been propagated
|
||||
match start_time {
|
||||
Some(time) => {
|
||||
if seen_timestamp + MAXIMUM_GOSSIP_CLOCK_DISPARITY < time + one_third_slot_duration
|
||||
{
|
||||
return Err(Error::TooEarly);
|
||||
}
|
||||
}
|
||||
None => return Err(Error::SigSlotStartIsNone),
|
||||
}
|
||||
|
||||
let optimistic_update =
|
||||
LightClientOptimisticUpdate::new(&chain.spec, head_block, &attested_state)?;
|
||||
|
||||
// verify that the gossiped optimistic update is the same as the locally constructed one.
|
||||
if optimistic_update != light_client_optimistic_update {
|
||||
return Err(Error::InvalidLightClientOptimisticUpdate);
|
||||
}
|
||||
|
||||
*latest_seen_optimistic_update = Some(light_client_optimistic_update.clone());
|
||||
|
||||
Ok(Self {
|
||||
light_client_optimistic_update,
|
||||
seen_timestamp,
|
||||
})
|
||||
}
|
||||
}
|
@ -948,6 +948,24 @@ lazy_static! {
|
||||
);
|
||||
}
|
||||
|
||||
// Fifth lazy-static block is used to account for macro recursion limit.
|
||||
lazy_static! {
|
||||
/*
|
||||
* Light server message verification
|
||||
*/
|
||||
pub static ref FINALITY_UPDATE_PROCESSING_SUCCESSES: Result<IntCounter> = try_create_int_counter(
|
||||
"light_client_finality_update_verification_success_total",
|
||||
"Number of light client finality updates verified for gossip"
|
||||
);
|
||||
/*
|
||||
* Light server message verification
|
||||
*/
|
||||
pub static ref OPTIMISTIC_UPDATE_PROCESSING_SUCCESSES: Result<IntCounter> = try_create_int_counter(
|
||||
"light_client_optimistic_update_verification_success_total",
|
||||
"Number of light client optimistic updates verified for gossip"
|
||||
);
|
||||
}
|
||||
|
||||
/// Scrape the `beacon_chain` for metrics that are not constantly updated (e.g., the present slot,
|
||||
/// head state info, etc) and update the Prometheus `DEFAULT_REGISTRY`.
|
||||
pub fn scrape_for_metrics<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>) {
|
||||
|
@ -34,6 +34,10 @@ pub struct GossipCache {
|
||||
signed_contribution_and_proof: Option<Duration>,
|
||||
/// Timeout for sync committee messages.
|
||||
sync_committee_message: Option<Duration>,
|
||||
/// Timeout for light client finality updates.
|
||||
light_client_finality_update: Option<Duration>,
|
||||
/// Timeout for light client optimistic updates.
|
||||
light_client_optimistic_update: Option<Duration>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -55,6 +59,10 @@ pub struct GossipCacheBuilder {
|
||||
signed_contribution_and_proof: Option<Duration>,
|
||||
/// Timeout for sync committee messages.
|
||||
sync_committee_message: Option<Duration>,
|
||||
/// Timeout for light client finality updates.
|
||||
light_client_finality_update: Option<Duration>,
|
||||
/// Timeout for light client optimistic updates.
|
||||
light_client_optimistic_update: Option<Duration>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -113,6 +121,18 @@ impl GossipCacheBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Timeout for light client finality update messages.
|
||||
pub fn light_client_finality_update_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.light_client_finality_update = Some(timeout);
|
||||
self
|
||||
}
|
||||
|
||||
/// Timeout for light client optimistic update messages.
|
||||
pub fn light_client_optimistic_update_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.light_client_optimistic_update = Some(timeout);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> GossipCache {
|
||||
let GossipCacheBuilder {
|
||||
default_timeout,
|
||||
@ -124,6 +144,8 @@ impl GossipCacheBuilder {
|
||||
attester_slashing,
|
||||
signed_contribution_and_proof,
|
||||
sync_committee_message,
|
||||
light_client_finality_update,
|
||||
light_client_optimistic_update,
|
||||
} = self;
|
||||
GossipCache {
|
||||
expirations: DelayQueue::default(),
|
||||
@ -136,6 +158,8 @@ impl GossipCacheBuilder {
|
||||
attester_slashing: attester_slashing.or(default_timeout),
|
||||
signed_contribution_and_proof: signed_contribution_and_proof.or(default_timeout),
|
||||
sync_committee_message: sync_committee_message.or(default_timeout),
|
||||
light_client_finality_update: light_client_finality_update.or(default_timeout),
|
||||
light_client_optimistic_update: light_client_optimistic_update.or(default_timeout),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -158,6 +182,8 @@ impl GossipCache {
|
||||
GossipKind::AttesterSlashing => self.attester_slashing,
|
||||
GossipKind::SignedContributionAndProof => self.signed_contribution_and_proof,
|
||||
GossipKind::SyncCommitteeMessage(_) => self.sync_committee_message,
|
||||
GossipKind::LightClientFinalityUpdate => self.light_client_finality_update,
|
||||
GossipKind::LightClientOptimisticUpdate => self.light_client_optimistic_update,
|
||||
};
|
||||
let expire_timeout = match expire_timeout {
|
||||
Some(expire_timeout) => expire_timeout,
|
||||
|
@ -253,6 +253,8 @@ pub(crate) fn create_whitelist_filter(
|
||||
add(ProposerSlashing);
|
||||
add(AttesterSlashing);
|
||||
add(SignedContributionAndProof);
|
||||
add(LightClientFinalityUpdate);
|
||||
add(LightClientOptimisticUpdate);
|
||||
for id in 0..attestation_subnet_count {
|
||||
add(Attestation(SubnetId::new(id)));
|
||||
}
|
||||
|
@ -16,4 +16,7 @@ pub use globals::NetworkGlobals;
|
||||
pub use pubsub::{PubsubMessage, SnappyTransform};
|
||||
pub use subnet::{Subnet, SubnetDiscovery};
|
||||
pub use sync_state::{BackFillState, SyncState};
|
||||
pub use topics::{subnet_from_topic_hash, GossipEncoding, GossipKind, GossipTopic, CORE_TOPICS};
|
||||
pub use topics::{
|
||||
subnet_from_topic_hash, GossipEncoding, GossipKind, GossipTopic, CORE_TOPICS,
|
||||
LIGHT_CLIENT_GOSSIP_TOPICS,
|
||||
};
|
||||
|
@ -9,10 +9,10 @@ use std::boxed::Box;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
Attestation, AttesterSlashing, EthSpec, ForkContext, ForkName, ProposerSlashing,
|
||||
SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase,
|
||||
SignedBeaconBlockMerge, SignedContributionAndProof, SignedVoluntaryExit, SubnetId,
|
||||
SyncCommitteeMessage, SyncSubnetId,
|
||||
Attestation, AttesterSlashing, EthSpec, ForkContext, ForkName, LightClientFinalityUpdate,
|
||||
LightClientOptimisticUpdate, ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock,
|
||||
SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockMerge,
|
||||
SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -33,6 +33,10 @@ pub enum PubsubMessage<T: EthSpec> {
|
||||
SignedContributionAndProof(Box<SignedContributionAndProof<T>>),
|
||||
/// Gossipsub message providing notification of unaggregated sync committee signatures with its subnet id.
|
||||
SyncCommitteeMessage(Box<(SyncSubnetId, SyncCommitteeMessage)>),
|
||||
/// Gossipsub message providing notification of a light client finality update.
|
||||
LightClientFinalityUpdate(Box<LightClientFinalityUpdate<T>>),
|
||||
/// Gossipsub message providing notification of a light client optimistic update.
|
||||
LightClientOptimisticUpdate(Box<LightClientOptimisticUpdate<T>>),
|
||||
}
|
||||
|
||||
// Implements the `DataTransform` trait of gossipsub to employ snappy compression
|
||||
@ -115,6 +119,10 @@ impl<T: EthSpec> PubsubMessage<T> {
|
||||
PubsubMessage::AttesterSlashing(_) => GossipKind::AttesterSlashing,
|
||||
PubsubMessage::SignedContributionAndProof(_) => GossipKind::SignedContributionAndProof,
|
||||
PubsubMessage::SyncCommitteeMessage(data) => GossipKind::SyncCommitteeMessage(data.0),
|
||||
PubsubMessage::LightClientFinalityUpdate(_) => GossipKind::LightClientFinalityUpdate,
|
||||
PubsubMessage::LightClientOptimisticUpdate(_) => {
|
||||
GossipKind::LightClientOptimisticUpdate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,6 +214,22 @@ impl<T: EthSpec> PubsubMessage<T> {
|
||||
sync_committee,
|
||||
))))
|
||||
}
|
||||
GossipKind::LightClientFinalityUpdate => {
|
||||
let light_client_finality_update =
|
||||
LightClientFinalityUpdate::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
Ok(PubsubMessage::LightClientFinalityUpdate(Box::new(
|
||||
light_client_finality_update,
|
||||
)))
|
||||
}
|
||||
GossipKind::LightClientOptimisticUpdate => {
|
||||
let light_client_optimistic_update =
|
||||
LightClientOptimisticUpdate::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
Ok(PubsubMessage::LightClientOptimisticUpdate(Box::new(
|
||||
light_client_optimistic_update,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,6 +251,8 @@ impl<T: EthSpec> PubsubMessage<T> {
|
||||
PubsubMessage::Attestation(data) => data.1.as_ssz_bytes(),
|
||||
PubsubMessage::SignedContributionAndProof(data) => data.as_ssz_bytes(),
|
||||
PubsubMessage::SyncCommitteeMessage(data) => data.1.as_ssz_bytes(),
|
||||
PubsubMessage::LightClientFinalityUpdate(data) => data.as_ssz_bytes(),
|
||||
PubsubMessage::LightClientOptimisticUpdate(data) => data.as_ssz_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,6 +287,12 @@ impl<T: EthSpec> std::fmt::Display for PubsubMessage<T> {
|
||||
PubsubMessage::SyncCommitteeMessage(data) => {
|
||||
write!(f, "Sync committee message: subnet_id: {}", *data.0)
|
||||
}
|
||||
PubsubMessage::LightClientFinalityUpdate(_data) => {
|
||||
write!(f, "Light CLient Finality Update")
|
||||
}
|
||||
PubsubMessage::LightClientOptimisticUpdate(_data) => {
|
||||
write!(f, "Light CLient Optimistic Update")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ pub const PROPOSER_SLASHING_TOPIC: &str = "proposer_slashing";
|
||||
pub const ATTESTER_SLASHING_TOPIC: &str = "attester_slashing";
|
||||
pub const SIGNED_CONTRIBUTION_AND_PROOF_TOPIC: &str = "sync_committee_contribution_and_proof";
|
||||
pub const SYNC_COMMITTEE_PREFIX_TOPIC: &str = "sync_committee_";
|
||||
pub const LIGHT_CLIENT_FINALITY_UPDATE: &str = "light_client_finality_update";
|
||||
pub const LIGHT_CLIENT_OPTIMISTIC_UPDATE: &str = "light_client_optimistic_update";
|
||||
|
||||
pub const CORE_TOPICS: [GossipKind; 6] = [
|
||||
GossipKind::BeaconBlock,
|
||||
@ -28,6 +30,11 @@ pub const CORE_TOPICS: [GossipKind; 6] = [
|
||||
GossipKind::SignedContributionAndProof,
|
||||
];
|
||||
|
||||
pub const LIGHT_CLIENT_GOSSIP_TOPICS: [GossipKind; 2] = [
|
||||
GossipKind::LightClientFinalityUpdate,
|
||||
GossipKind::LightClientOptimisticUpdate,
|
||||
];
|
||||
|
||||
/// A gossipsub topic which encapsulates the type of messages that should be sent and received over
|
||||
/// the pubsub protocol and the way the messages should be encoded.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
@ -63,6 +70,10 @@ pub enum GossipKind {
|
||||
/// Topic for publishing unaggregated sync committee signatures on a particular subnet.
|
||||
#[strum(serialize = "sync_committee")]
|
||||
SyncCommitteeMessage(SyncSubnetId),
|
||||
/// Topic for publishing finality updates for light clients.
|
||||
LightClientFinalityUpdate,
|
||||
/// Topic for publishing optimistic updates for light clients.
|
||||
LightClientOptimisticUpdate,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GossipKind {
|
||||
@ -136,6 +147,8 @@ impl GossipTopic {
|
||||
VOLUNTARY_EXIT_TOPIC => GossipKind::VoluntaryExit,
|
||||
PROPOSER_SLASHING_TOPIC => GossipKind::ProposerSlashing,
|
||||
ATTESTER_SLASHING_TOPIC => GossipKind::AttesterSlashing,
|
||||
LIGHT_CLIENT_FINALITY_UPDATE => GossipKind::LightClientFinalityUpdate,
|
||||
LIGHT_CLIENT_OPTIMISTIC_UPDATE => GossipKind::LightClientOptimisticUpdate,
|
||||
topic => match committee_topic_index(topic) {
|
||||
Some(subnet) => match subnet {
|
||||
Subnet::Attestation(s) => GossipKind::Attestation(s),
|
||||
@ -194,6 +207,8 @@ impl std::fmt::Display for GossipTopic {
|
||||
GossipKind::SyncCommitteeMessage(index) => {
|
||||
format!("{}{}", SYNC_COMMITTEE_PREFIX_TOPIC, *index)
|
||||
}
|
||||
GossipKind::LightClientFinalityUpdate => LIGHT_CLIENT_FINALITY_UPDATE.into(),
|
||||
GossipKind::LightClientOptimisticUpdate => LIGHT_CLIENT_OPTIMISTIC_UPDATE.into(),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
|
@ -62,9 +62,9 @@ use std::{cmp, collections::HashSet};
|
||||
use task_executor::TaskExecutor;
|
||||
use tokio::sync::mpsc;
|
||||
use types::{
|
||||
Attestation, AttesterSlashing, Hash256, ProposerSlashing, SignedAggregateAndProof,
|
||||
SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit, SubnetId,
|
||||
SyncCommitteeMessage, SyncSubnetId,
|
||||
Attestation, AttesterSlashing, Hash256, LightClientFinalityUpdate, LightClientOptimisticUpdate,
|
||||
ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof,
|
||||
SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId,
|
||||
};
|
||||
use work_reprocessing_queue::{
|
||||
spawn_reprocess_scheduler, QueuedAggregate, QueuedRpcBlock, QueuedUnaggregate, ReadyWork,
|
||||
@ -129,6 +129,14 @@ const MAX_GOSSIP_PROPOSER_SLASHING_QUEUE_LEN: usize = 4_096;
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_ATTESTER_SLASHING_QUEUE_LEN: usize = 4_096;
|
||||
|
||||
/// The maximum number of queued `LightClientFinalityUpdate` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_FINALITY_UPDATE_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `LightClientOptimisticUpdate` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_OPTIMISTIC_UPDATE_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `SyncCommitteeMessage` objects that will be stored before we start dropping
|
||||
/// them.
|
||||
const MAX_SYNC_MESSAGE_QUEUE_LEN: usize = 2048;
|
||||
@ -195,6 +203,8 @@ pub const GOSSIP_PROPOSER_SLASHING: &str = "gossip_proposer_slashing";
|
||||
pub const GOSSIP_ATTESTER_SLASHING: &str = "gossip_attester_slashing";
|
||||
pub const GOSSIP_SYNC_SIGNATURE: &str = "gossip_sync_signature";
|
||||
pub const GOSSIP_SYNC_CONTRIBUTION: &str = "gossip_sync_contribution";
|
||||
pub const GOSSIP_LIGHT_CLIENT_FINALITY_UPDATE: &str = "light_client_finality_update";
|
||||
pub const GOSSIP_LIGHT_CLIENT_OPTIMISTIC_UPDATE: &str = "light_client_optimistic_update";
|
||||
pub const RPC_BLOCK: &str = "rpc_block";
|
||||
pub const CHAIN_SEGMENT: &str = "chain_segment";
|
||||
pub const STATUS_PROCESSING: &str = "status_processing";
|
||||
@ -476,6 +486,42 @@ impl<T: BeaconChainTypes> WorkEvent<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Work` event for some light client finality update.
|
||||
pub fn gossip_light_client_finality_update(
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_finality_update: Box<LightClientFinalityUpdate<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
) -> Self {
|
||||
Self {
|
||||
drop_during_sync: true,
|
||||
work: Work::GossipLightClientFinalityUpdate {
|
||||
message_id,
|
||||
peer_id,
|
||||
light_client_finality_update,
|
||||
seen_timestamp,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Work` event for some light client optimistic update.
|
||||
pub fn gossip_light_client_optimistic_update(
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_optimistic_update: Box<LightClientOptimisticUpdate<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
) -> Self {
|
||||
Self {
|
||||
drop_during_sync: true,
|
||||
work: Work::GossipLightClientOptimisticUpdate {
|
||||
message_id,
|
||||
peer_id,
|
||||
light_client_optimistic_update,
|
||||
seen_timestamp,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Work` event for some attester slashing.
|
||||
pub fn gossip_attester_slashing(
|
||||
message_id: MessageId,
|
||||
@ -730,6 +776,18 @@ pub enum Work<T: BeaconChainTypes> {
|
||||
sync_contribution: Box<SignedContributionAndProof<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
},
|
||||
GossipLightClientFinalityUpdate {
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_finality_update: Box<LightClientFinalityUpdate<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
},
|
||||
GossipLightClientOptimisticUpdate {
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_optimistic_update: Box<LightClientOptimisticUpdate<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
},
|
||||
RpcBlock {
|
||||
block_root: Hash256,
|
||||
block: Arc<SignedBeaconBlock<T::EthSpec>>,
|
||||
@ -777,6 +835,8 @@ impl<T: BeaconChainTypes> Work<T> {
|
||||
Work::GossipAttesterSlashing { .. } => GOSSIP_ATTESTER_SLASHING,
|
||||
Work::GossipSyncSignature { .. } => GOSSIP_SYNC_SIGNATURE,
|
||||
Work::GossipSyncContribution { .. } => GOSSIP_SYNC_CONTRIBUTION,
|
||||
Work::GossipLightClientFinalityUpdate { .. } => GOSSIP_LIGHT_CLIENT_FINALITY_UPDATE,
|
||||
Work::GossipLightClientOptimisticUpdate { .. } => GOSSIP_LIGHT_CLIENT_OPTIMISTIC_UPDATE,
|
||||
Work::RpcBlock { .. } => RPC_BLOCK,
|
||||
Work::ChainSegment { .. } => CHAIN_SEGMENT,
|
||||
Work::Status { .. } => STATUS_PROCESSING,
|
||||
@ -916,6 +976,10 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
let mut gossip_attester_slashing_queue =
|
||||
FifoQueue::new(MAX_GOSSIP_ATTESTER_SLASHING_QUEUE_LEN);
|
||||
|
||||
// Using a FIFO queue for light client updates to maintain sequence order.
|
||||
let mut finality_update_queue = FifoQueue::new(MAX_GOSSIP_FINALITY_UPDATE_QUEUE_LEN);
|
||||
let mut optimistic_update_queue = FifoQueue::new(MAX_GOSSIP_OPTIMISTIC_UPDATE_QUEUE_LEN);
|
||||
|
||||
// Using a FIFO queue since blocks need to be imported sequentially.
|
||||
let mut rpc_block_queue = FifoQueue::new(MAX_RPC_BLOCK_QUEUE_LEN);
|
||||
let mut chain_segment_queue = FifoQueue::new(MAX_CHAIN_SEGMENT_QUEUE_LEN);
|
||||
@ -1250,6 +1314,12 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
Work::GossipSyncContribution { .. } => {
|
||||
sync_contribution_queue.push(work)
|
||||
}
|
||||
Work::GossipLightClientFinalityUpdate { .. } => {
|
||||
finality_update_queue.push(work, work_id, &self.log)
|
||||
}
|
||||
Work::GossipLightClientOptimisticUpdate { .. } => {
|
||||
optimistic_update_queue.push(work, work_id, &self.log)
|
||||
}
|
||||
Work::RpcBlock { .. } => rpc_block_queue.push(work, work_id, &self.log),
|
||||
Work::ChainSegment { ref process_id, .. } => match process_id {
|
||||
ChainSegmentProcessId::RangeBatchId { .. }
|
||||
@ -1551,7 +1621,7 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
)
|
||||
}),
|
||||
/*
|
||||
* Syn contribution verification.
|
||||
* Sync contribution verification.
|
||||
*/
|
||||
Work::GossipSyncContribution {
|
||||
message_id,
|
||||
@ -1566,6 +1636,38 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
seen_timestamp,
|
||||
)
|
||||
}),
|
||||
/*
|
||||
* Light client finality update verification.
|
||||
*/
|
||||
Work::GossipLightClientFinalityUpdate {
|
||||
message_id,
|
||||
peer_id,
|
||||
light_client_finality_update,
|
||||
seen_timestamp,
|
||||
} => task_spawner.spawn_blocking(move || {
|
||||
worker.process_gossip_finality_update(
|
||||
message_id,
|
||||
peer_id,
|
||||
*light_client_finality_update,
|
||||
seen_timestamp,
|
||||
)
|
||||
}),
|
||||
/*
|
||||
* Light client optimistic update verification.
|
||||
*/
|
||||
Work::GossipLightClientOptimisticUpdate {
|
||||
message_id,
|
||||
peer_id,
|
||||
light_client_optimistic_update,
|
||||
seen_timestamp,
|
||||
} => task_spawner.spawn_blocking(move || {
|
||||
worker.process_gossip_optimistic_update(
|
||||
message_id,
|
||||
peer_id,
|
||||
*light_client_optimistic_update,
|
||||
seen_timestamp,
|
||||
)
|
||||
}),
|
||||
/*
|
||||
* Verification for beacon blocks received during syncing via RPC.
|
||||
*/
|
||||
|
@ -3,6 +3,8 @@ use crate::{metrics, service::NetworkMessage, sync::SyncMessage};
|
||||
use beacon_chain::store::Error;
|
||||
use beacon_chain::{
|
||||
attestation_verification::{self, Error as AttnError, VerifiedAttestation},
|
||||
light_client_finality_update_verification::Error as LightClientFinalityUpdateError,
|
||||
light_client_optimistic_update_verification::Error as LightClientOptimisticUpdateError,
|
||||
observed_operations::ObservationOutcome,
|
||||
sync_committee_verification::{self, Error as SyncCommitteeError},
|
||||
validator_monitor::get_block_delay_ms,
|
||||
@ -18,9 +20,10 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use store::hot_cold_store::HotColdDBError;
|
||||
use tokio::sync::mpsc;
|
||||
use types::{
|
||||
Attestation, AttesterSlashing, EthSpec, Hash256, IndexedAttestation, ProposerSlashing,
|
||||
SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit,
|
||||
Slot, SubnetId, SyncCommitteeMessage, SyncSubnetId,
|
||||
Attestation, AttesterSlashing, EthSpec, Hash256, IndexedAttestation, LightClientFinalityUpdate,
|
||||
LightClientOptimisticUpdate, ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock,
|
||||
SignedContributionAndProof, SignedVoluntaryExit, Slot, SubnetId, SyncCommitteeMessage,
|
||||
SyncSubnetId,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -1303,6 +1306,138 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
metrics::inc_counter(&metrics::BEACON_PROCESSOR_SYNC_CONTRIBUTION_IMPORTED_TOTAL);
|
||||
}
|
||||
|
||||
pub fn process_gossip_finality_update(
|
||||
self,
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_finality_update: LightClientFinalityUpdate<T::EthSpec>,
|
||||
seen_timestamp: Duration,
|
||||
) {
|
||||
match self
|
||||
.chain
|
||||
.verify_finality_update_for_gossip(light_client_finality_update, seen_timestamp)
|
||||
{
|
||||
Ok(_verified_light_client_finality_update) => {
|
||||
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept);
|
||||
}
|
||||
Err(e) => {
|
||||
metrics::register_finality_update_error(&e);
|
||||
match e {
|
||||
LightClientFinalityUpdateError::InvalidLightClientFinalityUpdate => {
|
||||
debug!(
|
||||
self.log,
|
||||
"LC invalid finality update";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
);
|
||||
|
||||
self.gossip_penalize_peer(
|
||||
peer_id,
|
||||
PeerAction::LowToleranceError,
|
||||
"light_client_gossip_error",
|
||||
);
|
||||
}
|
||||
LightClientFinalityUpdateError::TooEarly => {
|
||||
debug!(
|
||||
self.log,
|
||||
"LC finality update too early";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
);
|
||||
|
||||
self.gossip_penalize_peer(
|
||||
peer_id,
|
||||
PeerAction::HighToleranceError,
|
||||
"light_client_gossip_error",
|
||||
);
|
||||
}
|
||||
LightClientFinalityUpdateError::FinalityUpdateAlreadySeen => debug!(
|
||||
self.log,
|
||||
"LC finality update already seen";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
),
|
||||
LightClientFinalityUpdateError::BeaconChainError(_)
|
||||
| LightClientFinalityUpdateError::LightClientUpdateError(_)
|
||||
| LightClientFinalityUpdateError::SigSlotStartIsNone
|
||||
| LightClientFinalityUpdateError::FailedConstructingUpdate => debug!(
|
||||
self.log,
|
||||
"LC error constructing finality update";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
),
|
||||
}
|
||||
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn process_gossip_optimistic_update(
|
||||
self,
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_optimistic_update: LightClientOptimisticUpdate<T::EthSpec>,
|
||||
seen_timestamp: Duration,
|
||||
) {
|
||||
match self
|
||||
.chain
|
||||
.verify_optimistic_update_for_gossip(light_client_optimistic_update, seen_timestamp)
|
||||
{
|
||||
Ok(_verified_light_client_optimistic_update) => {
|
||||
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept);
|
||||
}
|
||||
Err(e) => {
|
||||
metrics::register_optimistic_update_error(&e);
|
||||
match e {
|
||||
LightClientOptimisticUpdateError::InvalidLightClientOptimisticUpdate => {
|
||||
debug!(
|
||||
self.log,
|
||||
"LC invalid optimistic update";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
);
|
||||
|
||||
self.gossip_penalize_peer(
|
||||
peer_id,
|
||||
PeerAction::HighToleranceError,
|
||||
"light_client_gossip_error",
|
||||
)
|
||||
}
|
||||
LightClientOptimisticUpdateError::TooEarly => {
|
||||
debug!(
|
||||
self.log,
|
||||
"LC optimistic update too early";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
);
|
||||
|
||||
self.gossip_penalize_peer(
|
||||
peer_id,
|
||||
PeerAction::HighToleranceError,
|
||||
"light_client_gossip_error",
|
||||
);
|
||||
}
|
||||
LightClientOptimisticUpdateError::OptimisticUpdateAlreadySeen => debug!(
|
||||
self.log,
|
||||
"LC optimistic update already seen";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
),
|
||||
LightClientOptimisticUpdateError::BeaconChainError(_)
|
||||
| LightClientOptimisticUpdateError::LightClientUpdateError(_)
|
||||
| LightClientOptimisticUpdateError::SigSlotStartIsNone
|
||||
| LightClientOptimisticUpdateError::FailedConstructingUpdate => debug!(
|
||||
self.log,
|
||||
"LC error constructing optimistic update";
|
||||
"peer" => %peer_id,
|
||||
"error" => ?e,
|
||||
),
|
||||
}
|
||||
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Handle an error whilst verifying an `Attestation` or `SignedAggregateAndProof` from the
|
||||
/// network.
|
||||
fn handle_attestation_verification_failure(
|
||||
|
@ -1,5 +1,7 @@
|
||||
use beacon_chain::{
|
||||
attestation_verification::Error as AttnError,
|
||||
light_client_finality_update_verification::Error as LightClientFinalityUpdateError,
|
||||
light_client_optimistic_update_verification::Error as LightClientOptimisticUpdateError,
|
||||
sync_committee_verification::Error as SyncCommitteeError,
|
||||
};
|
||||
use fnv::FnvHashMap;
|
||||
@ -252,6 +254,19 @@ lazy_static! {
|
||||
"Gossipsub sync_committee errors per error type",
|
||||
&["type"]
|
||||
);
|
||||
pub static ref GOSSIP_FINALITY_UPDATE_ERRORS_PER_TYPE: Result<IntCounterVec> =
|
||||
try_create_int_counter_vec(
|
||||
"gossipsub_light_client_finality_update_errors_per_type",
|
||||
"Gossipsub light_client_finality_update errors per error type",
|
||||
&["type"]
|
||||
);
|
||||
pub static ref GOSSIP_OPTIMISTIC_UPDATE_ERRORS_PER_TYPE: Result<IntCounterVec> =
|
||||
try_create_int_counter_vec(
|
||||
"gossipsub_light_client_optimistic_update_errors_per_type",
|
||||
"Gossipsub light_client_optimistic_update errors per error type",
|
||||
&["type"]
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Network queue metrics
|
||||
@ -358,6 +373,14 @@ pub fn update_bandwidth_metrics(bandwidth: Arc<BandwidthSinks>) {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register_finality_update_error(error: &LightClientFinalityUpdateError) {
|
||||
inc_counter_vec(&GOSSIP_FINALITY_UPDATE_ERRORS_PER_TYPE, &[error.as_ref()]);
|
||||
}
|
||||
|
||||
pub fn register_optimistic_update_error(error: &LightClientOptimisticUpdateError) {
|
||||
inc_counter_vec(&GOSSIP_OPTIMISTIC_UPDATE_ERRORS_PER_TYPE, &[error.as_ref()]);
|
||||
}
|
||||
|
||||
pub fn register_attestation_error(error: &AttnError) {
|
||||
inc_counter_vec(&GOSSIP_ATTESTATION_ERRORS_PER_TYPE, &[error.as_ref()]);
|
||||
}
|
||||
|
@ -280,6 +280,30 @@ impl<T: BeaconChainTypes> Router<T> {
|
||||
sync_committtee_msg.0,
|
||||
);
|
||||
}
|
||||
PubsubMessage::LightClientFinalityUpdate(light_client_finality_update) => {
|
||||
trace!(
|
||||
self.log,
|
||||
"Received light client finality update";
|
||||
"peer_id" => %peer_id
|
||||
);
|
||||
self.processor.on_light_client_finality_update_gossip(
|
||||
id,
|
||||
peer_id,
|
||||
light_client_finality_update,
|
||||
);
|
||||
}
|
||||
PubsubMessage::LightClientOptimisticUpdate(light_client_optimistic_update) => {
|
||||
trace!(
|
||||
self.log,
|
||||
"Received light client optimistic update";
|
||||
"peer_id" => %peer_id
|
||||
);
|
||||
self.processor.on_light_client_optimistic_update_gossip(
|
||||
id,
|
||||
peer_id,
|
||||
light_client_optimistic_update,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,9 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use store::SyncCommitteeMessage;
|
||||
use tokio::sync::mpsc;
|
||||
use types::{
|
||||
Attestation, AttesterSlashing, EthSpec, ProposerSlashing, SignedAggregateAndProof,
|
||||
SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncSubnetId,
|
||||
Attestation, AttesterSlashing, EthSpec, LightClientFinalityUpdate, LightClientOptimisticUpdate,
|
||||
ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof,
|
||||
SignedVoluntaryExit, SubnetId, SyncSubnetId,
|
||||
};
|
||||
|
||||
/// Processes validated messages from the network. It relays necessary data to the syncing thread
|
||||
@ -368,6 +369,34 @@ impl<T: BeaconChainTypes> Processor<T> {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn on_light_client_finality_update_gossip(
|
||||
&mut self,
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_finality_update: Box<LightClientFinalityUpdate<T::EthSpec>>,
|
||||
) {
|
||||
self.send_beacon_processor_work(BeaconWorkEvent::gossip_light_client_finality_update(
|
||||
message_id,
|
||||
peer_id,
|
||||
light_client_finality_update,
|
||||
timestamp_now(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn on_light_client_optimistic_update_gossip(
|
||||
&mut self,
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
light_client_optimistic_update: Box<LightClientOptimisticUpdate<T::EthSpec>>,
|
||||
) {
|
||||
self.send_beacon_processor_work(BeaconWorkEvent::gossip_light_client_optimistic_update(
|
||||
message_id,
|
||||
peer_id,
|
||||
light_client_optimistic_update,
|
||||
timestamp_now(),
|
||||
))
|
||||
}
|
||||
|
||||
fn send_beacon_processor_work(&mut self, work: BeaconWorkEvent<T>) {
|
||||
self.beacon_processor_send
|
||||
.try_send(work)
|
||||
|
@ -208,6 +208,8 @@ pub struct NetworkService<T: BeaconChainTypes> {
|
||||
metrics_update: tokio::time::Interval,
|
||||
/// gossipsub_parameter_update timer
|
||||
gossipsub_parameter_update: tokio::time::Interval,
|
||||
/// enable_light_client_server indicator
|
||||
enable_light_client_server: bool,
|
||||
/// The logger for the network service.
|
||||
fork_context: Arc<ForkContext>,
|
||||
log: slog::Logger,
|
||||
@ -345,6 +347,7 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
gossipsub_parameter_update,
|
||||
fork_context,
|
||||
log: network_log,
|
||||
enable_light_client_server: config.enable_light_client_server,
|
||||
};
|
||||
|
||||
network_service.spawn_service(executor);
|
||||
@ -679,6 +682,7 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let mut subscribed_topics: Vec<GossipTopic> = vec![];
|
||||
for topic_kind in lighthouse_network::types::CORE_TOPICS.iter() {
|
||||
for fork_digest in self.required_gossip_fork_digests() {
|
||||
@ -695,6 +699,25 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
}
|
||||
}
|
||||
|
||||
if self.enable_light_client_server {
|
||||
for light_client_topic_kind in
|
||||
lighthouse_network::types::LIGHT_CLIENT_GOSSIP_TOPICS.iter()
|
||||
{
|
||||
for fork_digest in self.required_gossip_fork_digests() {
|
||||
let light_client_topic = GossipTopic::new(
|
||||
light_client_topic_kind.clone(),
|
||||
GossipEncoding::default(),
|
||||
fork_digest,
|
||||
);
|
||||
if self.libp2p.subscribe(light_client_topic.clone()) {
|
||||
subscribed_topics.push(light_client_topic);
|
||||
} else {
|
||||
warn!(self.log, "Could not subscribe to topic"; "topic" => %light_client_topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are to subscribe to all subnets we do it here
|
||||
if self.subscribe_all_subnets {
|
||||
for subnet_id in 0..<<T as BeaconChainTypes>::EthSpec as EthSpec>::SubnetBitfieldLength::to_u64() {
|
||||
|
@ -51,6 +51,7 @@ pub mod graffiti;
|
||||
pub mod historical_batch;
|
||||
pub mod indexed_attestation;
|
||||
pub mod light_client_bootstrap;
|
||||
pub mod light_client_finality_update;
|
||||
pub mod light_client_optimistic_update;
|
||||
pub mod light_client_update;
|
||||
pub mod pending_attestation;
|
||||
@ -136,6 +137,8 @@ pub use crate::free_attestation::FreeAttestation;
|
||||
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
|
||||
pub use crate::historical_batch::HistoricalBatch;
|
||||
pub use crate::indexed_attestation::IndexedAttestation;
|
||||
pub use crate::light_client_finality_update::LightClientFinalityUpdate;
|
||||
pub use crate::light_client_optimistic_update::LightClientOptimisticUpdate;
|
||||
pub use crate::participation_flags::ParticipationFlags;
|
||||
pub use crate::participation_list::ParticipationList;
|
||||
pub use crate::payload::{BlindedPayload, BlockType, ExecPayload, FullPayload};
|
||||
|
@ -1,10 +1,10 @@
|
||||
use super::{BeaconBlockHeader, EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee};
|
||||
use crate::{light_client_update::*, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec};
|
||||
use safe_arith::ArithError;
|
||||
use super::{
|
||||
BeaconBlockHeader, EthSpec, FixedVector, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock,
|
||||
Slot, SyncAggregate,
|
||||
};
|
||||
use crate::{light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::typenum::{U5, U6};
|
||||
use std::sync::Arc;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
@ -28,43 +28,38 @@ pub struct LightClientFinalityUpdate<T: EthSpec> {
|
||||
|
||||
impl<T: EthSpec> LightClientFinalityUpdate<T> {
|
||||
pub fn new(
|
||||
chain_spec: ChainSpec,
|
||||
beacon_state: BeaconState<T>,
|
||||
block: BeaconBlock<T>,
|
||||
chain_spec: &ChainSpec,
|
||||
beacon_state: &BeaconState<T>,
|
||||
block: &SignedBeaconBlock<T>,
|
||||
attested_state: &mut BeaconState<T>,
|
||||
finalized_block: BeaconBlock<T>,
|
||||
finalized_block: &SignedBlindedBeaconBlock<T>,
|
||||
) -> Result<Self, Error> {
|
||||
let altair_fork_epoch = chain_spec
|
||||
.altair_fork_epoch
|
||||
.ok_or(Error::AltairForkNotActive)?;
|
||||
if attested_state.slot().epoch(T::slots_per_epoch()) < altair_fork_epoch {
|
||||
if beacon_state.slot().epoch(T::slots_per_epoch()) < altair_fork_epoch {
|
||||
return Err(Error::AltairForkNotActive);
|
||||
}
|
||||
|
||||
let sync_aggregate = block.body().sync_aggregate()?;
|
||||
let sync_aggregate = block.message().body().sync_aggregate()?;
|
||||
if sync_aggregate.num_set_bits() < chain_spec.min_sync_committee_participants as usize {
|
||||
return Err(Error::NotEnoughSyncCommitteeParticipants);
|
||||
}
|
||||
|
||||
// Compute and validate attested header.
|
||||
let mut attested_header = attested_state.latest_block_header().clone();
|
||||
attested_header.state_root = attested_state.tree_hash_root();
|
||||
attested_header.state_root = attested_state.update_tree_hash_cache()?;
|
||||
// Build finalized header from finalized block
|
||||
let finalized_header = BeaconBlockHeader {
|
||||
slot: finalized_block.slot(),
|
||||
proposer_index: finalized_block.proposer_index(),
|
||||
parent_root: finalized_block.parent_root(),
|
||||
state_root: finalized_block.state_root(),
|
||||
body_root: finalized_block.body_root(),
|
||||
};
|
||||
let finalized_header = finalized_block.message().block_header();
|
||||
|
||||
if finalized_header.tree_hash_root() != beacon_state.finalized_checkpoint().root {
|
||||
return Err(Error::InvalidFinalizedBlock);
|
||||
}
|
||||
|
||||
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
|
||||
Ok(Self {
|
||||
attested_header: attested_header,
|
||||
finalized_header: finalized_header,
|
||||
attested_header,
|
||||
finalized_header,
|
||||
finality_branch: FixedVector::new(finality_branch)?,
|
||||
sync_aggregate: sync_aggregate.clone(),
|
||||
signature_slot: block.slot(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{BeaconBlockHeader, EthSpec, Slot, SyncAggregate};
|
||||
use crate::{
|
||||
light_client_update::Error, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec,
|
||||
light_client_update::Error, test_utils::TestRandom, BeaconState, ChainSpec, SignedBeaconBlock,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@ -23,9 +23,9 @@ pub struct LightClientOptimisticUpdate<T: EthSpec> {
|
||||
|
||||
impl<T: EthSpec> LightClientOptimisticUpdate<T> {
|
||||
pub fn new(
|
||||
chain_spec: ChainSpec,
|
||||
block: BeaconBlock<T>,
|
||||
attested_state: BeaconState<T>,
|
||||
chain_spec: &ChainSpec,
|
||||
block: &SignedBeaconBlock<T>,
|
||||
attested_state: &BeaconState<T>,
|
||||
) -> Result<Self, Error> {
|
||||
let altair_fork_epoch = chain_spec
|
||||
.altair_fork_epoch
|
||||
@ -34,7 +34,7 @@ impl<T: EthSpec> LightClientOptimisticUpdate<T> {
|
||||
return Err(Error::AltairForkNotActive);
|
||||
}
|
||||
|
||||
let sync_aggregate = block.body().sync_aggregate()?;
|
||||
let sync_aggregate = block.message().body().sync_aggregate()?;
|
||||
if sync_aggregate.num_set_bits() < chain_spec.min_sync_committee_participants as usize {
|
||||
return Err(Error::NotEnoughSyncCommitteeParticipants);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user