mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-21 13:54:44 +00:00
merge with unstable
This commit is contained in:
@@ -178,6 +178,9 @@ pub struct ChainSpec {
|
||||
pub attestation_subnet_count: u64,
|
||||
pub random_subnets_per_validator: u64,
|
||||
pub epochs_per_random_subnet_subscription: u64,
|
||||
pub subnets_per_node: u8,
|
||||
pub epochs_per_subnet_subscription: u64,
|
||||
attestation_subnet_extra_bits: u8,
|
||||
|
||||
/*
|
||||
* Application params
|
||||
@@ -467,6 +470,22 @@ impl ChainSpec {
|
||||
Hash256::from(domain)
|
||||
}
|
||||
|
||||
#[allow(clippy::integer_arithmetic)]
|
||||
pub const fn attestation_subnet_prefix_bits(&self) -> u32 {
|
||||
// maybe use log2 when stable https://github.com/rust-lang/rust/issues/70887
|
||||
|
||||
// NOTE: this line is here simply to guarantee that if self.attestation_subnet_count type
|
||||
// is changed, a compiler warning will be raised. This code depends on the type being u64.
|
||||
let attestation_subnet_count: u64 = self.attestation_subnet_count;
|
||||
let attestation_subnet_count_bits = if attestation_subnet_count == 0 {
|
||||
0
|
||||
} else {
|
||||
63 - attestation_subnet_count.leading_zeros()
|
||||
};
|
||||
|
||||
self.attestation_subnet_extra_bits as u32 + attestation_subnet_count_bits
|
||||
}
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
pub fn mainnet() -> Self {
|
||||
Self {
|
||||
@@ -630,9 +649,12 @@ impl ChainSpec {
|
||||
attestation_propagation_slot_range: 32,
|
||||
attestation_subnet_count: 64,
|
||||
random_subnets_per_validator: 1,
|
||||
subnets_per_node: 1,
|
||||
maximum_gossip_clock_disparity_millis: 500,
|
||||
target_aggregators_per_committee: 16,
|
||||
epochs_per_random_subnet_subscription: 256,
|
||||
epochs_per_subnet_subscription: 256,
|
||||
attestation_subnet_extra_bits: 6,
|
||||
|
||||
/*
|
||||
* Application specific
|
||||
@@ -865,9 +887,12 @@ impl ChainSpec {
|
||||
attestation_propagation_slot_range: 32,
|
||||
attestation_subnet_count: 64,
|
||||
random_subnets_per_validator: 1,
|
||||
subnets_per_node: 1,
|
||||
maximum_gossip_clock_disparity_millis: 500,
|
||||
target_aggregators_per_committee: 16,
|
||||
epochs_per_random_subnet_subscription: 256,
|
||||
epochs_per_subnet_subscription: 256,
|
||||
attestation_subnet_extra_bits: 6,
|
||||
|
||||
/*
|
||||
* Application specific
|
||||
|
||||
83
consensus/types/src/deposit_tree_snapshot.rs
Normal file
83
consensus/types/src/deposit_tree_snapshot.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use crate::*;
|
||||
use eth2_hashing::{hash32_concat, ZERO_HASHES};
|
||||
use int_to_bytes::int_to_bytes32;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use test_utils::TestRandom;
|
||||
use DEPOSIT_TREE_DEPTH;
|
||||
|
||||
#[derive(Encode, Decode, Deserialize, Serialize, Clone, Debug, PartialEq, TestRandom)]
|
||||
pub struct FinalizedExecutionBlock {
|
||||
pub deposit_root: Hash256,
|
||||
pub deposit_count: u64,
|
||||
pub block_hash: Hash256,
|
||||
pub block_height: u64,
|
||||
}
|
||||
|
||||
impl From<&DepositTreeSnapshot> for FinalizedExecutionBlock {
|
||||
fn from(snapshot: &DepositTreeSnapshot) -> Self {
|
||||
Self {
|
||||
deposit_root: snapshot.deposit_root,
|
||||
deposit_count: snapshot.deposit_count,
|
||||
block_hash: snapshot.execution_block_hash,
|
||||
block_height: snapshot.execution_block_height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Deserialize, Serialize, Clone, Debug, PartialEq, TestRandom)]
|
||||
pub struct DepositTreeSnapshot {
|
||||
pub finalized: Vec<Hash256>,
|
||||
pub deposit_root: Hash256,
|
||||
pub deposit_count: u64,
|
||||
pub execution_block_hash: Hash256,
|
||||
pub execution_block_height: u64,
|
||||
}
|
||||
|
||||
impl Default for DepositTreeSnapshot {
|
||||
fn default() -> Self {
|
||||
let mut result = Self {
|
||||
finalized: vec![],
|
||||
deposit_root: Hash256::default(),
|
||||
deposit_count: 0,
|
||||
execution_block_hash: Hash256::zero(),
|
||||
execution_block_height: 0,
|
||||
};
|
||||
// properly set the empty deposit root
|
||||
result.deposit_root = result.calculate_root().unwrap();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl DepositTreeSnapshot {
|
||||
// Calculates the deposit tree root from the hashes in the snapshot
|
||||
pub fn calculate_root(&self) -> Option<Hash256> {
|
||||
let mut size = self.deposit_count;
|
||||
let mut index = self.finalized.len();
|
||||
let mut deposit_root = [0; 32];
|
||||
for height in 0..DEPOSIT_TREE_DEPTH {
|
||||
deposit_root = if (size & 1) == 1 {
|
||||
index = index.checked_sub(1)?;
|
||||
hash32_concat(self.finalized.get(index)?.as_bytes(), &deposit_root)
|
||||
} else {
|
||||
hash32_concat(&deposit_root, ZERO_HASHES.get(height)?)
|
||||
};
|
||||
size /= 2;
|
||||
}
|
||||
// add mix-in-length
|
||||
deposit_root = hash32_concat(&deposit_root, &int_to_bytes32(self.deposit_count));
|
||||
|
||||
Some(Hash256::from_slice(&deposit_root))
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.calculate_root()
|
||||
.map_or(false, |calculated| self.deposit_root == calculated)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
ssz_tests!(DepositTreeSnapshot);
|
||||
}
|
||||
@@ -35,6 +35,7 @@ pub mod contribution_and_proof;
|
||||
pub mod deposit;
|
||||
pub mod deposit_data;
|
||||
pub mod deposit_message;
|
||||
pub mod deposit_tree_snapshot;
|
||||
pub mod enr_fork_id;
|
||||
pub mod eth1_data;
|
||||
pub mod eth_spec;
|
||||
@@ -48,6 +49,9 @@ pub mod free_attestation;
|
||||
pub mod graffiti;
|
||||
pub mod historical_batch;
|
||||
pub mod indexed_attestation;
|
||||
pub mod light_client_bootstrap;
|
||||
pub mod light_client_optimistic_update;
|
||||
pub mod light_client_update;
|
||||
pub mod pending_attestation;
|
||||
pub mod proposer_preparation_data;
|
||||
pub mod proposer_slashing;
|
||||
@@ -122,6 +126,7 @@ pub use crate::contribution_and_proof::ContributionAndProof;
|
||||
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
|
||||
pub use crate::deposit_data::DepositData;
|
||||
pub use crate::deposit_message::DepositMessage;
|
||||
pub use crate::deposit_tree_snapshot::{DepositTreeSnapshot, FinalizedExecutionBlock};
|
||||
pub use crate::enr_fork_id::EnrForkId;
|
||||
pub use crate::eth1_data::Eth1Data;
|
||||
pub use crate::eth_spec::EthSpecId;
|
||||
|
||||
45
consensus/types/src/light_client_bootstrap.rs
Normal file
45
consensus/types/src/light_client_bootstrap.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use super::{BeaconBlockHeader, BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee};
|
||||
use crate::{light_client_update::*, test_utils::TestRandom};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::sync::Arc;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
/// A LightClientBootstrap is the initializer we send over to lightclient nodes
|
||||
/// that are trying to generate their basic storage when booting up.
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct LightClientBootstrap<T: EthSpec> {
|
||||
/// Requested beacon block header.
|
||||
pub header: BeaconBlockHeader,
|
||||
/// The `SyncCommittee` used in the requested period.
|
||||
pub current_sync_committee: Arc<SyncCommittee<T>>,
|
||||
/// Merkle proof for sync committee
|
||||
pub current_sync_committee_branch: FixedVector<Hash256, CurrentSyncCommitteeProofLen>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> LightClientBootstrap<T> {
|
||||
pub fn from_beacon_state(beacon_state: BeaconState<T>) -> Result<Self, Error> {
|
||||
let mut header = beacon_state.latest_block_header().clone();
|
||||
header.state_root = beacon_state.tree_hash_root();
|
||||
Ok(LightClientBootstrap {
|
||||
header,
|
||||
current_sync_committee: beacon_state.current_sync_committee()?.clone(),
|
||||
/// TODO(Giulio2002): Generate Merkle Proof, this is just empty hashes
|
||||
current_sync_committee_branch: FixedVector::new(vec![
|
||||
Hash256::zero();
|
||||
CURRENT_SYNC_COMMITTEE_PROOF_LEN
|
||||
])?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::MainnetEthSpec;
|
||||
|
||||
ssz_tests!(LightClientBootstrap<MainnetEthSpec>);
|
||||
}
|
||||
80
consensus/types/src/light_client_finality_update.rs
Normal file
80
consensus/types/src/light_client_finality_update.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
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 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;
|
||||
|
||||
/// A LightClientFinalityUpdate is the update lightclient request or received by a gossip that
|
||||
/// signal a new finalized beacon block header for the light client sync protocol.
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct LightClientFinalityUpdate<T: EthSpec> {
|
||||
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
||||
pub attested_header: BeaconBlockHeader,
|
||||
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
|
||||
pub finalized_header: BeaconBlockHeader,
|
||||
/// Merkle proof attesting finalized header.
|
||||
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
|
||||
/// current sync aggreggate
|
||||
pub sync_aggregate: SyncAggregate<T>,
|
||||
/// Slot of the sync aggregated singature
|
||||
pub signature_slot: Slot,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> LightClientFinalityUpdate<T> {
|
||||
pub fn new(
|
||||
chain_spec: ChainSpec,
|
||||
beacon_state: BeaconState<T>,
|
||||
block: BeaconBlock<T>,
|
||||
attested_state: BeaconState<T>,
|
||||
finalized_block: BeaconBlock<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 {
|
||||
return Err(Error::AltairForkNotActive);
|
||||
}
|
||||
|
||||
let sync_aggregate = block.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();
|
||||
// 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(),
|
||||
};
|
||||
if finalized_header.tree_hash_root() != beacon_state.finalized_checkpoint().root {
|
||||
return Err(Error::InvalidFinalizedBlock);
|
||||
}
|
||||
// TODO(Giulio2002): compute proper merkle proofs.
|
||||
Ok(Self {
|
||||
attested_header: attested_header,
|
||||
finalized_header: finalized_header,
|
||||
finality_branch: FixedVector::new(vec![Hash256::zero(); FINALIZED_ROOT_PROOF_LEN])?,
|
||||
sync_aggregate: sync_aggregate.clone(),
|
||||
signature_slot: block.slot(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::MainnetEthSpec;
|
||||
|
||||
ssz_tests!(LightClientFinalityUpdate<MainnetEthSpec>);
|
||||
}
|
||||
59
consensus/types/src/light_client_optimistic_update.rs
Normal file
59
consensus/types/src/light_client_optimistic_update.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use super::{BeaconBlockHeader, EthSpec, Slot, SyncAggregate};
|
||||
use crate::{
|
||||
light_client_update::Error, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
/// A LightClientOptimisticUpdate is the update we send on each slot,
|
||||
/// it is based off the current unfinalized epoch is verified only against BLS signature.
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct LightClientOptimisticUpdate<T: EthSpec> {
|
||||
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
||||
pub attested_header: BeaconBlockHeader,
|
||||
/// current sync aggreggate
|
||||
pub sync_aggregate: SyncAggregate<T>,
|
||||
/// Slot of the sync aggregated singature
|
||||
pub signature_slot: Slot,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> LightClientOptimisticUpdate<T> {
|
||||
pub fn new(
|
||||
chain_spec: ChainSpec,
|
||||
block: BeaconBlock<T>,
|
||||
attested_state: BeaconState<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 {
|
||||
return Err(Error::AltairForkNotActive);
|
||||
}
|
||||
|
||||
let sync_aggregate = block.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();
|
||||
Ok(Self {
|
||||
attested_header,
|
||||
sync_aggregate: sync_aggregate.clone(),
|
||||
signature_slot: block.slot(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::MainnetEthSpec;
|
||||
|
||||
ssz_tests!(LightClientOptimisticUpdate<MainnetEthSpec>);
|
||||
}
|
||||
171
consensus/types/src/light_client_update.rs
Normal file
171
consensus/types/src/light_client_update.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use super::{BeaconBlockHeader, EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee};
|
||||
use crate::{beacon_state, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec};
|
||||
use safe_arith::ArithError;
|
||||
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;
|
||||
|
||||
pub const FINALIZED_ROOT_INDEX: usize = 105;
|
||||
pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54;
|
||||
pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55;
|
||||
|
||||
pub type FinalizedRootProofLen = U6;
|
||||
pub type CurrentSyncCommitteeProofLen = U5;
|
||||
pub type NextSyncCommitteeProofLen = U5;
|
||||
|
||||
pub const FINALIZED_ROOT_PROOF_LEN: usize = 6;
|
||||
pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
|
||||
pub const NEXT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Error {
|
||||
SszTypesError(ssz_types::Error),
|
||||
BeaconStateError(beacon_state::Error),
|
||||
ArithError(ArithError),
|
||||
AltairForkNotActive,
|
||||
NotEnoughSyncCommitteeParticipants,
|
||||
MismatchingPeriods,
|
||||
InvalidFinalizedBlock,
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for Error {
|
||||
fn from(e: ssz_types::Error) -> Error {
|
||||
Error::SszTypesError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<beacon_state::Error> for Error {
|
||||
fn from(e: beacon_state::Error) -> Error {
|
||||
Error::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithError> for Error {
|
||||
fn from(e: ArithError) -> Error {
|
||||
Error::ArithError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// A LightClientUpdate is the update we request solely to either complete the bootstraping process,
|
||||
/// or to sync up to the last committee period, we need to have one ready for each ALTAIR period
|
||||
/// we go over, note: there is no need to keep all of the updates from [ALTAIR_PERIOD, CURRENT_PERIOD].
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct LightClientUpdate<T: EthSpec> {
|
||||
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
||||
pub attested_header: BeaconBlockHeader,
|
||||
/// The `SyncCommittee` used in the next period.
|
||||
pub next_sync_committee: Arc<SyncCommittee<T>>,
|
||||
/// Merkle proof for next sync committee
|
||||
pub next_sync_committee_branch: FixedVector<Hash256, NextSyncCommitteeProofLen>,
|
||||
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
|
||||
pub finalized_header: BeaconBlockHeader,
|
||||
/// Merkle proof attesting finalized header.
|
||||
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
|
||||
/// current sync aggreggate
|
||||
pub sync_aggregate: SyncAggregate<T>,
|
||||
/// Slot of the sync aggregated singature
|
||||
pub signature_slot: Slot,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> LightClientUpdate<T> {
|
||||
pub fn new(
|
||||
chain_spec: ChainSpec,
|
||||
beacon_state: BeaconState<T>,
|
||||
block: BeaconBlock<T>,
|
||||
attested_state: BeaconState<T>,
|
||||
finalized_block: BeaconBlock<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 {
|
||||
return Err(Error::AltairForkNotActive);
|
||||
}
|
||||
|
||||
let sync_aggregate = block.body().sync_aggregate()?;
|
||||
if sync_aggregate.num_set_bits() < chain_spec.min_sync_committee_participants as usize {
|
||||
return Err(Error::NotEnoughSyncCommitteeParticipants);
|
||||
}
|
||||
|
||||
let signature_period = block.epoch().sync_committee_period(&chain_spec)?;
|
||||
// Compute and validate attested header.
|
||||
let mut attested_header = attested_state.latest_block_header().clone();
|
||||
attested_header.state_root = attested_state.tree_hash_root();
|
||||
let attested_period = attested_header
|
||||
.slot
|
||||
.epoch(T::slots_per_epoch())
|
||||
.sync_committee_period(&chain_spec)?;
|
||||
if attested_period != signature_period {
|
||||
return Err(Error::MismatchingPeriods);
|
||||
}
|
||||
// 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(),
|
||||
};
|
||||
if finalized_header.tree_hash_root() != beacon_state.finalized_checkpoint().root {
|
||||
return Err(Error::InvalidFinalizedBlock);
|
||||
}
|
||||
// TODO(Giulio2002): compute proper merkle proofs.
|
||||
Ok(Self {
|
||||
attested_header,
|
||||
next_sync_committee: attested_state.next_sync_committee()?.clone(),
|
||||
next_sync_committee_branch: FixedVector::new(vec![
|
||||
Hash256::zero();
|
||||
NEXT_SYNC_COMMITTEE_PROOF_LEN
|
||||
])?,
|
||||
finalized_header,
|
||||
finality_branch: FixedVector::new(vec![Hash256::zero(); FINALIZED_ROOT_PROOF_LEN])?,
|
||||
sync_aggregate: sync_aggregate.clone(),
|
||||
signature_slot: block.slot(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::MainnetEthSpec;
|
||||
use ssz_types::typenum::Unsigned;
|
||||
|
||||
ssz_tests!(LightClientUpdate<MainnetEthSpec>);
|
||||
|
||||
#[test]
|
||||
fn finalized_root_params() {
|
||||
assert!(2usize.pow(FINALIZED_ROOT_PROOF_LEN as u32) <= FINALIZED_ROOT_INDEX);
|
||||
assert!(2usize.pow(FINALIZED_ROOT_PROOF_LEN as u32 + 1) > FINALIZED_ROOT_INDEX);
|
||||
assert_eq!(FinalizedRootProofLen::to_usize(), FINALIZED_ROOT_PROOF_LEN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_sync_committee_params() {
|
||||
assert!(
|
||||
2usize.pow(CURRENT_SYNC_COMMITTEE_PROOF_LEN as u32) <= CURRENT_SYNC_COMMITTEE_INDEX
|
||||
);
|
||||
assert!(
|
||||
2usize.pow(CURRENT_SYNC_COMMITTEE_PROOF_LEN as u32 + 1) > CURRENT_SYNC_COMMITTEE_INDEX
|
||||
);
|
||||
assert_eq!(
|
||||
CurrentSyncCommitteeProofLen::to_usize(),
|
||||
CURRENT_SYNC_COMMITTEE_PROOF_LEN
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_sync_committee_params() {
|
||||
assert!(2usize.pow(NEXT_SYNC_COMMITTEE_PROOF_LEN as u32) <= NEXT_SYNC_COMMITTEE_INDEX);
|
||||
assert!(2usize.pow(NEXT_SYNC_COMMITTEE_PROOF_LEN as u32 + 1) > NEXT_SYNC_COMMITTEE_INDEX);
|
||||
assert_eq!(
|
||||
NextSyncCommitteeProofLen::to_usize(),
|
||||
NEXT_SYNC_COMMITTEE_PROOF_LEN
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
//! Identifies each shard by an integer identifier.
|
||||
use crate::{AttestationData, ChainSpec, CommitteeIndex, EthSpec, Slot};
|
||||
use crate::{AttestationData, ChainSpec, CommitteeIndex, Epoch, EthSpec, Slot};
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use swap_or_not_shuffle::compute_shuffled_index;
|
||||
|
||||
const MAX_SUBNET_ID: usize = 64;
|
||||
|
||||
@@ -71,6 +72,45 @@ impl SubnetId {
|
||||
.safe_rem(spec.attestation_subnet_count)?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[allow(clippy::integer_arithmetic)]
|
||||
/// Computes the set of subnets the node should be subscribed to during the current epoch,
|
||||
/// along with the first epoch in which these subscriptions are no longer valid.
|
||||
pub fn compute_subnets_for_epoch<T: EthSpec>(
|
||||
node_id: ethereum_types::U256,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(impl Iterator<Item = SubnetId>, Epoch), &'static str> {
|
||||
let node_id_prefix =
|
||||
(node_id >> (256 - spec.attestation_subnet_prefix_bits() as usize)).as_usize();
|
||||
|
||||
let subscription_event_idx = epoch.as_u64() / spec.epochs_per_subnet_subscription;
|
||||
let permutation_seed =
|
||||
eth2_hashing::hash(&int_to_bytes::int_to_bytes8(subscription_event_idx));
|
||||
|
||||
let num_subnets = 1 << spec.attestation_subnet_prefix_bits();
|
||||
|
||||
let permutated_prefix = compute_shuffled_index(
|
||||
node_id_prefix,
|
||||
num_subnets,
|
||||
&permutation_seed,
|
||||
spec.shuffle_round_count,
|
||||
)
|
||||
.ok_or("Unable to shuffle")? as u64;
|
||||
|
||||
// Get the constants we need to avoid holding a reference to the spec
|
||||
let &ChainSpec {
|
||||
subnets_per_node,
|
||||
attestation_subnet_count,
|
||||
..
|
||||
} = spec;
|
||||
|
||||
let subnet_set_generator = (0..subnets_per_node).map(move |idx| {
|
||||
SubnetId::new((permutated_prefix + idx as u64) % attestation_subnet_count)
|
||||
});
|
||||
let valid_until_epoch = (subscription_event_idx + 1) * spec.epochs_per_subnet_subscription;
|
||||
Ok((subnet_set_generator, valid_until_epoch.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SubnetId {
|
||||
|
||||
Reference in New Issue
Block a user