API for LightClientBootstrap, LightClientFinalityUpdate, LightClientOptimisticUpdate and light client events (#3954)

* rebase and add comment

* conditional test

* test

* optimistic chould be working now

* finality should be working now

* try again

* try again

* clippy fix

* add lc bootstrap beacon api

* add lc optimistic/finality update to events

* fmt

* That error isn't occuring on my computer but I think this should fix it

* Add missing test file

* Update light client types to comply with Altair light client spec.

* Fix test compilation

* Support deserializing light client structures for the Bellatrix fork

* Move `get_light_client_bootstrap` logic to `BeaconChain`. `LightClientBootstrap` API to return `ForkVersionedResponse`.

* Misc fixes.
- log cleanup
- move http_api config mutation to `config::get_config` for consistency
- fix light client API responses

* Add light client bootstrap API test and fix existing ones.

* Fix test for `light-client-server` http api config.

* Appease clippy

* Efficiency improvement when retrieving beacon state.

---------

Co-authored-by: Jimmy Chen <jchen.tc@gmail.com>
This commit is contained in:
GeemoCandama
2023-11-28 00:14:29 -06:00
committed by GitHub
parent 44c1817c2b
commit 8a599ec7dc
24 changed files with 632 additions and 111 deletions

View File

@@ -99,6 +99,7 @@ pub mod slot_data;
pub mod sqlite;
pub mod blob_sidecar;
pub mod light_client_header;
pub mod sidecar;
pub mod signed_blob;
@@ -154,8 +155,11 @@ pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedRe
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_bootstrap::LightClientBootstrap;
pub use crate::light_client_finality_update::LightClientFinalityUpdate;
pub use crate::light_client_header::LightClientHeader;
pub use crate::light_client_optimistic_update::LightClientOptimisticUpdate;
pub use crate::light_client_update::{Error as LightClientError, LightClientUpdate};
pub use crate::participation_flags::ParticipationFlags;
pub use crate::participation_list::ParticipationList;
pub use crate::payload::{

View File

@@ -1,10 +1,13 @@
use super::{BeaconBlockHeader, BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee};
use crate::{light_client_update::*, test_utils::TestRandom};
use serde::{Deserialize, Serialize};
use super::{BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee};
use crate::{
light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize,
LightClientHeader,
};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
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.
@@ -22,8 +25,8 @@ use tree_hash::TreeHash;
#[serde(bound = "T: EthSpec")]
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientBootstrap<T: EthSpec> {
/// Requested beacon block header.
pub header: BeaconBlockHeader,
/// The requested beacon block header.
pub header: LightClientHeader,
/// The `SyncCommittee` used in the requested period.
pub current_sync_committee: Arc<SyncCommittee<T>>,
/// Merkle proof for sync committee
@@ -33,17 +36,37 @@ pub struct LightClientBootstrap<T: EthSpec> {
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();
header.state_root = beacon_state.update_tree_hash_cache()?;
let current_sync_committee_branch =
beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?;
Ok(LightClientBootstrap {
header,
header: header.into(),
current_sync_committee: beacon_state.current_sync_committee()?.clone(),
current_sync_committee_branch: FixedVector::new(current_sync_committee_branch)?,
})
}
}
impl<T: EthSpec> ForkVersionDeserialize for LightClientBootstrap<T> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge => {
Ok(serde_json::from_value::<LightClientBootstrap<T>>(value)
.map_err(serde::de::Error::custom))?
}
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
Err(serde::de::Error::custom(format!(
"LightClientBootstrap failed to deserialize: unsupported fork '{}'",
fork_name
)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,9 +1,12 @@
use super::{
BeaconBlockHeader, EthSpec, FixedVector, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock,
Slot, SyncAggregate,
EthSpec, FixedVector, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot, SyncAggregate,
};
use crate::{light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec};
use serde::{Deserialize, Serialize};
use crate::{
light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec, ForkName,
ForkVersionDeserialize, LightClientHeader,
};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
@@ -25,9 +28,9 @@ use tree_hash::TreeHash;
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientFinalityUpdate<T: EthSpec> {
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
pub attested_header: BeaconBlockHeader,
pub attested_header: LightClientHeader,
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
pub finalized_header: BeaconBlockHeader,
pub finalized_header: LightClientHeader,
/// Merkle proof attesting finalized header.
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
/// current sync aggreggate
@@ -68,8 +71,8 @@ impl<T: EthSpec> LightClientFinalityUpdate<T> {
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
Ok(Self {
attested_header,
finalized_header,
attested_header: attested_header.into(),
finalized_header: finalized_header.into(),
finality_branch: FixedVector::new(finality_branch)?,
sync_aggregate: sync_aggregate.clone(),
signature_slot: block.slot(),
@@ -77,6 +80,26 @@ impl<T: EthSpec> LightClientFinalityUpdate<T> {
}
}
impl<T: EthSpec> ForkVersionDeserialize for LightClientFinalityUpdate<T> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge => Ok(serde_json::from_value::<
LightClientFinalityUpdate<T>,
>(value)
.map_err(serde::de::Error::custom))?,
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
Err(serde::de::Error::custom(format!(
"LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'",
fork_name
)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -0,0 +1,26 @@
use crate::test_utils::TestRandom;
use crate::BeaconBlockHeader;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
arbitrary::Arbitrary,
)]
pub struct LightClientHeader {
pub beacon: BeaconBlockHeader,
}
impl From<BeaconBlockHeader> for LightClientHeader {
fn from(beacon: BeaconBlockHeader) -> Self {
LightClientHeader { beacon }
}
}

View File

@@ -1,8 +1,10 @@
use super::{BeaconBlockHeader, EthSpec, Slot, SyncAggregate};
use super::{EthSpec, ForkName, ForkVersionDeserialize, Slot, SyncAggregate};
use crate::light_client_header::LightClientHeader;
use crate::{
light_client_update::Error, test_utils::TestRandom, BeaconState, ChainSpec, SignedBeaconBlock,
};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
@@ -24,7 +26,7 @@ use tree_hash::TreeHash;
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientOptimisticUpdate<T: EthSpec> {
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
pub attested_header: BeaconBlockHeader,
pub attested_header: LightClientHeader,
/// current sync aggreggate
pub sync_aggregate: SyncAggregate<T>,
/// Slot of the sync aggregated singature
@@ -53,13 +55,33 @@ impl<T: EthSpec> LightClientOptimisticUpdate<T> {
let mut attested_header = attested_state.latest_block_header().clone();
attested_header.state_root = attested_state.tree_hash_root();
Ok(Self {
attested_header,
attested_header: attested_header.into(),
sync_aggregate: sync_aggregate.clone(),
signature_slot: block.slot(),
})
}
}
impl<T: EthSpec> ForkVersionDeserialize for LightClientOptimisticUpdate<T> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge => Ok(serde_json::from_value::<
LightClientOptimisticUpdate<T>,
>(value)
.map_err(serde::de::Error::custom))?,
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
Err(serde::de::Error::custom(format!(
"LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'",
fork_name
)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,7 +1,11 @@
use super::{BeaconBlockHeader, EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee};
use crate::{beacon_state, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec};
use crate::{
beacon_state, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec, ForkName,
ForkVersionDeserialize, LightClientHeader,
};
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz_derive::{Decode, Encode};
use ssz_types::typenum::{U5, U6};
use std::sync::Arc;
@@ -67,13 +71,13 @@ impl From<ArithError> for Error {
#[arbitrary(bound = "T: EthSpec")]
pub struct LightClientUpdate<T: EthSpec> {
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
pub attested_header: BeaconBlockHeader,
pub attested_header: LightClientHeader,
/// 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,
pub finalized_header: LightClientHeader,
/// Merkle proof attesting finalized header.
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
/// current sync aggreggate
@@ -128,10 +132,10 @@ impl<T: EthSpec> LightClientUpdate<T> {
attested_state.compute_merkle_proof(NEXT_SYNC_COMMITTEE_INDEX)?;
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
Ok(Self {
attested_header,
attested_header: attested_header.into(),
next_sync_committee: attested_state.next_sync_committee()?.clone(),
next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?,
finalized_header,
finalized_header: finalized_header.into(),
finality_branch: FixedVector::new(finality_branch)?,
sync_aggregate: sync_aggregate.clone(),
signature_slot: block.slot(),
@@ -139,6 +143,26 @@ impl<T: EthSpec> LightClientUpdate<T> {
}
}
impl<T: EthSpec> ForkVersionDeserialize for LightClientUpdate<T> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Altair | ForkName::Merge => {
Ok(serde_json::from_value::<LightClientUpdate<T>>(value)
.map_err(serde::de::Error::custom))?
}
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
Err(serde::de::Error::custom(format!(
"LightClientUpdate failed to deserialize: unsupported fork '{}'",
fork_name
)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,5 +1,4 @@
use crate::test_utils::TestRandom;
use crate::typenum::Unsigned;
use crate::{EthSpec, FixedVector, SyncSubnetId};
use bls::PublicKeyBytes;
use safe_arith::{ArithError, SafeArith};
@@ -46,14 +45,11 @@ pub struct SyncCommittee<T: EthSpec> {
impl<T: EthSpec> SyncCommittee<T> {
/// Create a temporary sync committee that should *never* be included in a legitimate consensus object.
pub fn temporary() -> Result<Self, ssz_types::Error> {
Ok(Self {
pubkeys: FixedVector::new(vec![
PublicKeyBytes::empty();
T::SyncCommitteeSize::to_usize()
])?,
pub fn temporary() -> Self {
Self {
pubkeys: FixedVector::from_elem(PublicKeyBytes::empty()),
aggregate_pubkey: PublicKeyBytes::empty(),
})
}
}
/// Return the pubkeys in this `SyncCommittee` for the given `subcommittee_index`.