mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 20:57:10 +00:00
add initial IL gossip verification
This commit is contained in:
104
beacon_node/beacon_chain/src/inclusion_list_verification.rs
Normal file
104
beacon_node/beacon_chain/src/inclusion_list_verification.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||
|
||||
use slot_clock::SlotClock;
|
||||
use strum::AsRefStr;
|
||||
use types::{Domain, EthSpec, SignedInclusionList, SignedRoot, Slot};
|
||||
|
||||
#[derive(Debug, AsRefStr)]
|
||||
pub enum GossipInclusionListError {
|
||||
FutureSlot {
|
||||
message_slot: Slot,
|
||||
latest_permissible_slot: Slot,
|
||||
},
|
||||
PastSlot {
|
||||
message_slot: Slot,
|
||||
earliest_permissible_slot: Slot,
|
||||
},
|
||||
InvalidCommitteeRoot,
|
||||
ValidatorNotInCommittee,
|
||||
TooManyTransactions,
|
||||
InvalidSignature,
|
||||
BeaconChainError(BeaconChainError),
|
||||
// TODO: equivocation e.g. PriorInclusionListKnown
|
||||
}
|
||||
|
||||
impl From<BeaconChainError> for GossipInclusionListError {
|
||||
fn from(value: BeaconChainError) -> Self {
|
||||
Self::BeaconChainError(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GossipVerifiedInclusionList<T: BeaconChainTypes> {
|
||||
signed_il: SignedInclusionList<T::EthSpec>,
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> GossipVerifiedInclusionList<T> {
|
||||
pub fn verify(
|
||||
signed_il: &SignedInclusionList<T::EthSpec>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, GossipInclusionListError> {
|
||||
// the slot is equal to the previous slot or the current slot
|
||||
let message_slot = signed_il.message.slot;
|
||||
let earliest_permissible_slot = chain
|
||||
.slot_clock
|
||||
.now_with_past_tolerance(chain.spec.maximum_gossip_clock_disparity())
|
||||
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
||||
if message_slot < earliest_permissible_slot {
|
||||
return Err(GossipInclusionListError::PastSlot {
|
||||
message_slot,
|
||||
earliest_permissible_slot,
|
||||
});
|
||||
}
|
||||
let latest_permissible_slot = chain
|
||||
.slot_clock
|
||||
.now_with_future_tolerance(chain.spec.maximum_gossip_clock_disparity())
|
||||
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
||||
if message_slot > latest_permissible_slot {
|
||||
return Err(GossipInclusionListError::FutureSlot {
|
||||
message_slot,
|
||||
latest_permissible_slot,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: the slot is equal to the current slot or the previous slot and the current time is
|
||||
// not past the attestation deadline
|
||||
|
||||
// TODO: the IL committee root is equal to the hash tree root of the expected committee
|
||||
|
||||
// TODO: the validator index is contained in the committee corresponding to the committee
|
||||
// root
|
||||
|
||||
// the transaction length is less than or equal to the specified maximum
|
||||
if signed_il.message.transactions.len() > T::EthSpec::max_transactions_per_inclusion_list()
|
||||
{
|
||||
return Err(GossipInclusionListError::TooManyTransactions);
|
||||
}
|
||||
|
||||
// TODO: the message is the first or second valid message received from the validator
|
||||
// corresponding to the validator index
|
||||
|
||||
// the signature is valid w.r.t. the validator index
|
||||
let epoch = chain.epoch()?;
|
||||
let fork = chain.spec.fork_at_epoch(epoch);
|
||||
let genesis_validators_root = chain.genesis_validators_root;
|
||||
let domain = chain.spec.get_domain(
|
||||
epoch,
|
||||
Domain::InclusionListCommittee,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = signed_il.message.signing_root(domain);
|
||||
let validator_index = signed_il.message.validator_index as usize;
|
||||
let pubkey = chain.validator_pubkey(validator_index)?;
|
||||
let Some(pubkey) = pubkey else {
|
||||
return Err(GossipInclusionListError::BeaconChainError(
|
||||
BeaconChainError::ValidatorIndexUnknown(validator_index),
|
||||
));
|
||||
};
|
||||
signed_il.signature.verify(&pubkey, message);
|
||||
|
||||
Ok(Self {
|
||||
signed_il: signed_il.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ pub mod fork_revert;
|
||||
pub mod graffiti_calculator;
|
||||
mod head_tracker;
|
||||
pub mod historical_blocks;
|
||||
pub mod inclusion_list_verification;
|
||||
pub mod kzg_utils;
|
||||
pub mod light_client_finality_update_verification;
|
||||
pub mod light_client_optimistic_update_verification;
|
||||
|
||||
@@ -7,6 +7,9 @@ use crate::{
|
||||
use beacon_chain::blob_verification::{GossipBlobError, GossipVerifiedBlob};
|
||||
use beacon_chain::block_verification_types::AsBlock;
|
||||
use beacon_chain::data_column_verification::{GossipDataColumnError, GossipVerifiedDataColumn};
|
||||
use beacon_chain::inclusion_list_verification::{
|
||||
GossipInclusionListError, GossipVerifiedInclusionList,
|
||||
};
|
||||
use beacon_chain::store::Error;
|
||||
use beacon_chain::{
|
||||
attestation_verification::{self, Error as AttnError, VerifiedAttestation},
|
||||
@@ -36,8 +39,8 @@ use types::{
|
||||
DataColumnSidecar, DataColumnSubnetId, EthSpec, Hash256, IndexedAttestation,
|
||||
LightClientFinalityUpdate, LightClientOptimisticUpdate, ProposerSlashing,
|
||||
SignedAggregateAndProof, SignedBeaconBlock, SignedBlsToExecutionChange,
|
||||
SignedContributionAndProof, SignedVoluntaryExit, Slot, SubnetId, SyncCommitteeMessage,
|
||||
SyncSubnetId,
|
||||
SignedContributionAndProof, SignedInclusionList, SignedVoluntaryExit, Slot, SubnetId,
|
||||
SyncCommitteeMessage, SyncSubnetId,
|
||||
};
|
||||
|
||||
use beacon_processor::{
|
||||
@@ -2139,6 +2142,35 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn process_gossip_inclusion_list(
|
||||
self: &Arc<Self>,
|
||||
message_id: MessageId,
|
||||
peer_id: PeerId,
|
||||
il: SignedInclusionList<T::EthSpec>,
|
||||
seen_timestamp: Duration,
|
||||
) {
|
||||
match GossipVerifiedInclusionList::verify(&il, &self.chain) {
|
||||
Ok(gossip_verified_il) => {
|
||||
debug!(self.log, "Successfully verified gossip inclusion list");
|
||||
}
|
||||
Err(err) => match err {
|
||||
GossipInclusionListError::FutureSlot { .. }
|
||||
| GossipInclusionListError::PastSlot { .. }
|
||||
| GossipInclusionListError::ValidatorNotInCommittee
|
||||
| GossipInclusionListError::TooManyTransactions
|
||||
| GossipInclusionListError::InvalidSignature => {
|
||||
debug!(self.log, "Could not verify inclusion list for gossip. Rejecting the inclusion list"; "error" => ?err);
|
||||
}
|
||||
GossipInclusionListError::InvalidCommitteeRoot => {
|
||||
debug!(self.log, "Could not verify inclusion list for gossip. Ignoring the inclusion list"; "error" => ?err);
|
||||
}
|
||||
GossipInclusionListError::BeaconChainError(_) => {
|
||||
crit!(self.log, "Internal error when verifying inclusion list"; "error" => ?err);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle an error whilst verifying an `Attestation` or `SignedAggregateAndProof` from the
|
||||
/// network.
|
||||
fn handle_attestation_verification_failure(
|
||||
|
||||
@@ -27,6 +27,7 @@ pub enum Domain {
|
||||
ContributionAndProof,
|
||||
SyncCommitteeSelectionProof,
|
||||
ApplicationMask(ApplicationDomain),
|
||||
InclusionListCommittee,
|
||||
}
|
||||
|
||||
/// Lighthouse's internal configuration struct.
|
||||
@@ -187,6 +188,11 @@ pub struct ChainSpec {
|
||||
pub min_per_epoch_churn_limit_electra: u64,
|
||||
pub max_per_epoch_activation_exit_churn_limit: u64,
|
||||
|
||||
/*
|
||||
* FOCIL params
|
||||
*/
|
||||
pub domain_inclusion_list_committee: u32,
|
||||
|
||||
/*
|
||||
* DAS params
|
||||
*/
|
||||
@@ -475,6 +481,7 @@ impl ChainSpec {
|
||||
Domain::SyncCommitteeSelectionProof => self.domain_sync_committee_selection_proof,
|
||||
Domain::ApplicationMask(application_domain) => application_domain.get_domain_constant(),
|
||||
Domain::BlsToExecutionChange => self.domain_bls_to_execution_change,
|
||||
Domain::InclusionListCommittee => self.domain_inclusion_list_committee,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,6 +804,11 @@ impl ChainSpec {
|
||||
})
|
||||
.expect("calculation does not overflow"),
|
||||
|
||||
/*
|
||||
* FOCIL params
|
||||
*/
|
||||
domain_inclusion_list_committee: 13,
|
||||
|
||||
/*
|
||||
* DAS params
|
||||
*/
|
||||
@@ -1115,6 +1127,11 @@ impl ChainSpec {
|
||||
})
|
||||
.expect("calculation does not overflow"),
|
||||
|
||||
/*
|
||||
* FOCIL params
|
||||
*/
|
||||
domain_inclusion_list_committee: 13,
|
||||
|
||||
/*
|
||||
* DAS params
|
||||
*/
|
||||
@@ -1951,6 +1968,12 @@ mod tests {
|
||||
spec.domain_bls_to_execution_change,
|
||||
&spec,
|
||||
);
|
||||
|
||||
test_domain(
|
||||
Domain::InclusionListCommittee,
|
||||
spec.domain_inclusion_list_committee,
|
||||
&spec,
|
||||
);
|
||||
}
|
||||
|
||||
fn apply_bit_mask(domain_bytes: [u8; 4], spec: &ChainSpec) -> u32 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{EthSpec, Hash256, Signature, Slot, Transaction};
|
||||
use crate::{EthSpec, Hash256, Signature, SignedRoot, Slot, Transaction};
|
||||
|
||||
use derivative::Derivative;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -33,6 +33,8 @@ pub struct InclusionList<E: EthSpec> {
|
||||
VariableList<Transaction<E::MaxBytesPerTransaction>, E::MaxTransactionsPerInclusionList>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> SignedRoot for InclusionList<E> {}
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, Derivative, arbitrary::Arbitrary,
|
||||
)]
|
||||
|
||||
Reference in New Issue
Block a user