mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-01 03:33:47 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
@@ -141,6 +141,8 @@ pub enum Error {
|
||||
},
|
||||
TotalActiveBalanceDiffUninitialized,
|
||||
MissingImmutableValidator(usize),
|
||||
IndexNotSupported(usize),
|
||||
MerkleTreeError(merkle_proof::MerkleTreeError),
|
||||
}
|
||||
|
||||
/// Control whether an epoch-indexed field can be indexed at the next epoch or not.
|
||||
@@ -1660,6 +1662,60 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
};
|
||||
Ok(sync_committee)
|
||||
}
|
||||
|
||||
pub fn compute_merkle_proof(
|
||||
&mut self,
|
||||
generalized_index: usize,
|
||||
) -> Result<Vec<Hash256>, Error> {
|
||||
/* FIXME(sproul): re-enable merkle proofs
|
||||
// 1. Convert generalized index to field index.
|
||||
let field_index = match generalized_index {
|
||||
light_client_update::CURRENT_SYNC_COMMITTEE_INDEX
|
||||
| light_client_update::NEXT_SYNC_COMMITTEE_INDEX => {
|
||||
// Sync committees are top-level fields, subtract off the generalized indices
|
||||
// for the internal nodes. Result should be 22 or 23, the field offset of the committee
|
||||
// in the `BeaconState`:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
|
||||
generalized_index
|
||||
.checked_sub(tree_hash_cache::NUM_BEACON_STATE_HASH_TREE_ROOT_LEAVES)
|
||||
.ok_or(Error::IndexNotSupported(generalized_index))?
|
||||
}
|
||||
light_client_update::FINALIZED_ROOT_INDEX => {
|
||||
// Finalized root is the right child of `finalized_checkpoint`, divide by two to get
|
||||
// the generalized index of `state.finalized_checkpoint`.
|
||||
let finalized_checkpoint_generalized_index = generalized_index / 2;
|
||||
// Subtract off the internal nodes. Result should be 105/2 - 32 = 20 which matches
|
||||
// position of `finalized_checkpoint` in `BeaconState`.
|
||||
finalized_checkpoint_generalized_index
|
||||
.checked_sub(tree_hash_cache::NUM_BEACON_STATE_HASH_TREE_ROOT_LEAVES)
|
||||
.ok_or(Error::IndexNotSupported(generalized_index))?
|
||||
}
|
||||
_ => return Err(Error::IndexNotSupported(generalized_index)),
|
||||
};
|
||||
|
||||
// 2. Get all `BeaconState` leaves.
|
||||
let mut cache = self
|
||||
.tree_hash_cache_mut()
|
||||
.take()
|
||||
.ok_or(Error::TreeHashCacheNotInitialized)?;
|
||||
let leaves = cache.recalculate_tree_hash_leaves(self)?;
|
||||
self.tree_hash_cache_mut().restore(cache);
|
||||
|
||||
// 3. Make deposit tree.
|
||||
// Use the depth of the `BeaconState` fields (i.e. `log2(32) = 5`).
|
||||
let depth = light_client_update::CURRENT_SYNC_COMMITTEE_PROOF_LEN;
|
||||
let tree = merkle_proof::MerkleTree::create(&leaves, depth);
|
||||
let (_, mut proof) = tree.generate_proof(field_index, depth)?;
|
||||
|
||||
// 4. If we're proving the finalized root, patch in the finalized epoch to complete the proof.
|
||||
if generalized_index == light_client_update::FINALIZED_ROOT_INDEX {
|
||||
proof.insert(0, self.finalized_checkpoint().epoch.tree_hash_root());
|
||||
}
|
||||
|
||||
Ok(proof)
|
||||
*/
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec, V: ValidatorTrait> BeaconState<T, V> {
|
||||
@@ -1718,6 +1774,12 @@ impl From<tree_hash::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<merkle_proof::MerkleTreeError> for Error {
|
||||
fn from(e: merkle_proof::MerkleTreeError) -> Error {
|
||||
Error::MerkleTreeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithError> for Error {
|
||||
fn from(e: ArithError) -> Error {
|
||||
Error::ArithError(e)
|
||||
|
||||
@@ -784,7 +784,7 @@ impl ChainSpec {
|
||||
domain_sync_committee_selection_proof: 8,
|
||||
domain_contribution_and_proof: 9,
|
||||
altair_fork_version: [0x01, 0x00, 0x00, 0x64],
|
||||
altair_fork_epoch: Some(Epoch::new(256)),
|
||||
altair_fork_epoch: Some(Epoch::new(512)),
|
||||
|
||||
/*
|
||||
* Merge hard fork params
|
||||
@@ -795,14 +795,11 @@ impl ChainSpec {
|
||||
.expect("pow does not overflow"),
|
||||
proportional_slashing_multiplier_bellatrix: 3,
|
||||
bellatrix_fork_version: [0x02, 0x00, 0x00, 0x64],
|
||||
bellatrix_fork_epoch: None,
|
||||
terminal_total_difficulty: Uint256::MAX
|
||||
.checked_sub(Uint256::from(2u64.pow(10)))
|
||||
.expect("subtraction does not overflow")
|
||||
// Add 1 since the spec declares `2**256 - 2**10` and we use
|
||||
// `Uint256::MAX` which is `2*256- 1`.
|
||||
.checked_add(Uint256::one())
|
||||
.expect("addition does not overflow"),
|
||||
bellatrix_fork_epoch: Some(Epoch::new(385536)),
|
||||
terminal_total_difficulty: Uint256::from_dec_str(
|
||||
"8626000000000000000000058750000000000000000000",
|
||||
)
|
||||
.expect("terminal_total_difficulty is a valid integer"),
|
||||
terminal_block_hash: ExecutionBlockHash::zero(),
|
||||
terminal_block_hash_activation_epoch: Epoch::new(u64::MAX),
|
||||
safe_slots_to_import_optimistically: 128u64,
|
||||
|
||||
@@ -40,7 +40,7 @@ impl ConfigAndPreset {
|
||||
let extra_fields = get_extra_fields(spec);
|
||||
|
||||
if spec.bellatrix_fork_epoch.is_some()
|
||||
|| fork_name == None
|
||||
|| fork_name.is_none()
|
||||
|| fork_name == Some(ForkName::Merge)
|
||||
{
|
||||
let bellatrix_preset = BellatrixPreset::from_chain_spec::<T>(spec);
|
||||
@@ -65,7 +65,7 @@ impl ConfigAndPreset {
|
||||
|
||||
/// Get a hashmap of constants to add to the `PresetAndConfig`
|
||||
pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
|
||||
let hex_string = |value: &[u8]| format!("0x{}", hex::encode(&value)).into();
|
||||
let hex_string = |value: &[u8]| format!("0x{}", hex::encode(value)).into();
|
||||
let u32_hex = |v: u32| hex_string(&v.to_le_bytes());
|
||||
let u8_hex = |v: u8| hex_string(&v.to_le_bytes());
|
||||
hashmap! {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ impl Graffiti {
|
||||
|
||||
impl fmt::Display for Graffiti {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", eth2_serde_utils::hex::encode(&self.0))
|
||||
write!(f, "{}", eth2_serde_utils::hex::encode(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,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;
|
||||
@@ -49,6 +50,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;
|
||||
@@ -116,6 +120,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;
|
||||
|
||||
44
consensus/types/src/light_client_bootstrap.rs
Normal file
44
consensus/types/src/light_client_bootstrap.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use super::{BeaconBlockHeader, BeaconState, EthSpec, Hash256, SyncCommittee};
|
||||
use crate::{light_client_update::*, test_utils::TestRandom};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::FixedVector;
|
||||
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: &mut BeaconState<T>) -> Result<Self, Error> {
|
||||
let mut header = beacon_state.latest_block_header().clone();
|
||||
header.state_root = beacon_state.tree_hash_root();
|
||||
let current_sync_committee_branch =
|
||||
beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?;
|
||||
Ok(LightClientBootstrap {
|
||||
header,
|
||||
current_sync_committee: beacon_state.current_sync_committee()?.clone(),
|
||||
current_sync_committee_branch: FixedVector::new(current_sync_committee_branch)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::MainnetEthSpec;
|
||||
|
||||
ssz_tests!(LightClientBootstrap<MainnetEthSpec>);
|
||||
}
|
||||
81
consensus/types/src/light_client_finality_update.rs
Normal file
81
consensus/types/src/light_client_finality_update.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
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: &mut 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);
|
||||
}
|
||||
|
||||
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
|
||||
Ok(Self {
|
||||
attested_header: attested_header,
|
||||
finalized_header: finalized_header,
|
||||
finality_branch: FixedVector::new(finality_branch)?,
|
||||
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>);
|
||||
}
|
||||
173
consensus/types/src/light_client_update.rs
Normal file
173
consensus/types/src/light_client_update.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
use super::{BeaconBlockHeader, EthSpec, 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},
|
||||
FixedVector,
|
||||
};
|
||||
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: &mut 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);
|
||||
}
|
||||
let next_sync_committee_branch =
|
||||
attested_state.compute_merkle_proof(NEXT_SYNC_COMMITTEE_INDEX)?;
|
||||
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
|
||||
Ok(Self {
|
||||
attested_header,
|
||||
next_sync_committee: attested_state.next_sync_committee()?.clone(),
|
||||
next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?,
|
||||
finalized_header,
|
||||
finality_branch: FixedVector::new(finality_branch)?,
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user