Add Capella & Deneb light client support (#4946)
* 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 * Merge branch 'unstable' into light_client_beacon_api_1 # Conflicts: # beacon_node/beacon_chain/src/events.rs # beacon_node/http_api/src/lib.rs # beacon_node/http_api/src/test_utils.rs # beacon_node/http_api/tests/main.rs # beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs # beacon_node/lighthouse_network/src/rpc/methods.rs # beacon_node/lighthouse_network/src/service/api_types.rs # beacon_node/network/src/beacon_processor/worker/rpc_methods.rs # beacon_node/tests/test.rs # common/eth2/src/types.rs # lighthouse/src/main.rs * Add missing test file * Update light client types to comply with Altair light client spec. * Fix test compilation * Merge branch 'unstable' into light_client_beacon_api_1 * 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. * Merge branch 'unstable' into light_client_beacon_api_1 * Fix test for `light-client-server` http api config. * Appease clippy * Add Altair light client SSZ tests * Merge branch 'unstable' of https://github.com/sigp/lighthouse into light_client_beacon_api_1 * updates to light client header * light client header from signed beacon block * using options * implement helper functions * placeholder conversion from vec hash256 to exec branch * add deneb * using fixed vector * remove unwraps * by epoch * compute merkle proof * merkle proof * update comments * resolve merge conflicts * linting * Merge branch 'unstable' into light-client-ssz-tests # Conflicts: # beacon_node/beacon_chain/src/beacon_chain.rs # consensus/types/src/light_client_bootstrap.rs # consensus/types/src/light_client_header.rs * superstruct attempt * superstruct changes * lint * altair * update * update * changes to light_client_optimistic_ and finality * merge unstable * refactor * resolved merge conflicts * Merge branch 'unstable' of https://github.com/sigp/lighthouse into capella_deneb_light_client_types * block_to_light_client_header fork aware * fmt * comment fix * comment fix * include merge fork, update deserialize_by_fork, refactor * fmt * pass by ref to prevent clone * rename merkle proof fn * add FIXME * LightClientHeader TestRandom * fix comments * fork version deserialize * merge unstable * move fn arguments, fork name calc * use task executor * remove unneeded fns * remove dead code * add manual ssz decoding/encoding and add ssz_tests_by_fork macro * merge deneb types with tests * merge ssz tests, revert code deletion, cleanup * move chainspec * update ssz tests * fmt * light client ssz tests * change to superstruct * changes from feedback * linting * Merge branch 'unstable' of https://github.com/sigp/lighthouse into capella_deneb_light_client_types * test fix * cleanup * Remove unused `derive`. * Merge branch 'unstable' of https://github.com/sigp/lighthouse into capella_deneb_light_client_types * beta compiler fix * merge
This commit is contained in:
parent
19b0db2cdf
commit
e4d4e439cb
@ -1347,11 +1347,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
(parent_root, slot, sync_aggregate): LightClientProducerEvent<T::EthSpec>,
|
(parent_root, slot, sync_aggregate): LightClientProducerEvent<T::EthSpec>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.light_client_server_cache.recompute_and_cache_updates(
|
self.light_client_server_cache.recompute_and_cache_updates(
|
||||||
&self.log,
|
|
||||||
self.store.clone(),
|
self.store.clone(),
|
||||||
&parent_root,
|
&parent_root,
|
||||||
slot,
|
slot,
|
||||||
&sync_aggregate,
|
&sync_aggregate,
|
||||||
|
&self.log,
|
||||||
|
&self.spec,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6635,13 +6636,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
&self,
|
&self,
|
||||||
block_root: &Hash256,
|
block_root: &Hash256,
|
||||||
) -> Result<Option<(LightClientBootstrap<T::EthSpec>, ForkName)>, Error> {
|
) -> Result<Option<(LightClientBootstrap<T::EthSpec>, ForkName)>, Error> {
|
||||||
let Some((state_root, slot)) = self
|
let handle = self
|
||||||
.get_blinded_block(block_root)?
|
.task_executor
|
||||||
.map(|block| (block.state_root(), block.slot()))
|
.handle()
|
||||||
else {
|
.ok_or(BeaconChainError::RuntimeShutdown)?;
|
||||||
|
|
||||||
|
let Some(block) = handle.block_on(async { self.get_block(block_root).await })? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (state_root, slot) = (block.state_root(), block.slot());
|
||||||
|
|
||||||
let Some(mut state) = self.get_state(&state_root, Some(slot))? else {
|
let Some(mut state) = self.get_state(&state_root, Some(slot))? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
@ -6651,12 +6656,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
.map_err(Error::InconsistentFork)?;
|
.map_err(Error::InconsistentFork)?;
|
||||||
|
|
||||||
match fork_name {
|
match fork_name {
|
||||||
ForkName::Altair | ForkName::Merge => {
|
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => {
|
||||||
LightClientBootstrap::from_beacon_state(&mut state)
|
LightClientBootstrap::from_beacon_state(&mut state, &block, &self.spec)
|
||||||
.map(|bootstrap| Some((bootstrap, fork_name)))
|
.map(|bootstrap| Some((bootstrap, fork_name)))
|
||||||
.map_err(Error::LightClientError)
|
.map_err(Error::LightClientError)
|
||||||
}
|
}
|
||||||
ForkName::Base | ForkName::Capella | ForkName::Deneb => Err(Error::UnsupportedFork),
|
ForkName::Base => Err(Error::UnsupportedFork),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,6 +250,7 @@ easy_from_to!(StateAdvanceError, BeaconChainError);
|
|||||||
easy_from_to!(BlockReplayError, BeaconChainError);
|
easy_from_to!(BlockReplayError, BeaconChainError);
|
||||||
easy_from_to!(InconsistentFork, BeaconChainError);
|
easy_from_to!(InconsistentFork, BeaconChainError);
|
||||||
easy_from_to!(AvailabilityCheckError, BeaconChainError);
|
easy_from_to!(AvailabilityCheckError, BeaconChainError);
|
||||||
|
easy_from_to!(LightClientError, BeaconChainError);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum BlockProductionError {
|
pub enum BlockProductionError {
|
||||||
|
@ -48,7 +48,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientFinalityUpdate<T> {
|
|||||||
// verify that enough time has passed for the block to have been propagated
|
// verify that enough time has passed for the block to have been propagated
|
||||||
let start_time = chain
|
let start_time = chain
|
||||||
.slot_clock
|
.slot_clock
|
||||||
.start_of(rcv_finality_update.signature_slot)
|
.start_of(*rcv_finality_update.signature_slot())
|
||||||
.ok_or(Error::SigSlotStartIsNone)?;
|
.ok_or(Error::SigSlotStartIsNone)?;
|
||||||
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
|
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
|
||||||
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
|
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
|
||||||
|
@ -52,7 +52,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
|
|||||||
// verify that enough time has passed for the block to have been propagated
|
// verify that enough time has passed for the block to have been propagated
|
||||||
let start_time = chain
|
let start_time = chain
|
||||||
.slot_clock
|
.slot_clock
|
||||||
.start_of(rcv_optimistic_update.signature_slot)
|
.start_of(*rcv_optimistic_update.signature_slot())
|
||||||
.ok_or(Error::SigSlotStartIsNone)?;
|
.ok_or(Error::SigSlotStartIsNone)?;
|
||||||
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
|
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
|
||||||
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
|
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
|
||||||
@ -65,10 +65,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
|
|||||||
let head_block = &head.snapshot.beacon_block;
|
let head_block = &head.snapshot.beacon_block;
|
||||||
// check if we can process the optimistic update immediately
|
// check if we can process the optimistic update immediately
|
||||||
// otherwise queue
|
// otherwise queue
|
||||||
let canonical_root = rcv_optimistic_update
|
let canonical_root = rcv_optimistic_update.get_canonical_root();
|
||||||
.attested_header
|
|
||||||
.beacon
|
|
||||||
.canonical_root();
|
|
||||||
|
|
||||||
if canonical_root != head_block.message().parent_root() {
|
if canonical_root != head_block.message().parent_root() {
|
||||||
return Err(Error::UnknownBlockParentRoot(canonical_root));
|
return Err(Error::UnknownBlockParentRoot(canonical_root));
|
||||||
@ -84,7 +81,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
|
|||||||
return Err(Error::InvalidLightClientOptimisticUpdate);
|
return Err(Error::InvalidLightClientOptimisticUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent_root = rcv_optimistic_update.attested_header.beacon.parent_root;
|
let parent_root = rcv_optimistic_update.get_parent_root();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
light_client_optimistic_update: rcv_optimistic_update,
|
light_client_optimistic_update: rcv_optimistic_update,
|
||||||
parent_root,
|
parent_root,
|
||||||
|
@ -8,7 +8,7 @@ use types::light_client_update::{FinalizedRootProofLen, FINALIZED_ROOT_INDEX};
|
|||||||
use types::non_zero_usize::new_non_zero_usize;
|
use types::non_zero_usize::new_non_zero_usize;
|
||||||
use types::{
|
use types::{
|
||||||
BeaconBlockRef, BeaconState, ChainSpec, EthSpec, ForkName, Hash256, LightClientFinalityUpdate,
|
BeaconBlockRef, BeaconState, ChainSpec, EthSpec, ForkName, Hash256, LightClientFinalityUpdate,
|
||||||
LightClientHeader, LightClientOptimisticUpdate, Slot, SyncAggregate,
|
LightClientOptimisticUpdate, Slot, SyncAggregate,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A prev block cache miss requires to re-generate the state of the post-parent block. Items in the
|
/// A prev block cache miss requires to re-generate the state of the post-parent block. Items in the
|
||||||
@ -71,11 +71,12 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
|
|||||||
/// results are cached either on disk or memory to be served via p2p and rest API
|
/// results are cached either on disk or memory to be served via p2p and rest API
|
||||||
pub fn recompute_and_cache_updates(
|
pub fn recompute_and_cache_updates(
|
||||||
&self,
|
&self,
|
||||||
log: &Logger,
|
|
||||||
store: BeaconStore<T>,
|
store: BeaconStore<T>,
|
||||||
block_parent_root: &Hash256,
|
block_parent_root: &Hash256,
|
||||||
block_slot: Slot,
|
block_slot: Slot,
|
||||||
sync_aggregate: &SyncAggregate<T::EthSpec>,
|
sync_aggregate: &SyncAggregate<T::EthSpec>,
|
||||||
|
log: &Logger,
|
||||||
|
chain_spec: &ChainSpec,
|
||||||
) -> Result<(), BeaconChainError> {
|
) -> Result<(), BeaconChainError> {
|
||||||
let _timer =
|
let _timer =
|
||||||
metrics::start_timer(&metrics::LIGHT_CLIENT_SERVER_CACHE_RECOMPUTE_UPDATES_TIMES);
|
metrics::start_timer(&metrics::LIGHT_CLIENT_SERVER_CACHE_RECOMPUTE_UPDATES_TIMES);
|
||||||
@ -83,12 +84,13 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
|
|||||||
let signature_slot = block_slot;
|
let signature_slot = block_slot;
|
||||||
let attested_block_root = block_parent_root;
|
let attested_block_root = block_parent_root;
|
||||||
|
|
||||||
let attested_block = store.get_blinded_block(attested_block_root)?.ok_or(
|
let attested_block =
|
||||||
BeaconChainError::DBInconsistent(format!(
|
store
|
||||||
|
.get_full_block(attested_block_root)?
|
||||||
|
.ok_or(BeaconChainError::DBInconsistent(format!(
|
||||||
"Block not available {:?}",
|
"Block not available {:?}",
|
||||||
attested_block_root
|
attested_block_root
|
||||||
)),
|
)))?;
|
||||||
)?;
|
|
||||||
|
|
||||||
let cached_parts = self.get_or_compute_prev_block_cache(
|
let cached_parts = self.get_or_compute_prev_block_cache(
|
||||||
store.clone(),
|
store.clone(),
|
||||||
@ -109,11 +111,12 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
|
|||||||
};
|
};
|
||||||
if is_latest_optimistic {
|
if is_latest_optimistic {
|
||||||
// can create an optimistic update, that is more recent
|
// can create an optimistic update, that is more recent
|
||||||
*self.latest_optimistic_update.write() = Some(LightClientOptimisticUpdate {
|
*self.latest_optimistic_update.write() = Some(LightClientOptimisticUpdate::new(
|
||||||
attested_header: block_to_light_client_header(attested_block.message()),
|
&attested_block,
|
||||||
sync_aggregate: sync_aggregate.clone(),
|
sync_aggregate.clone(),
|
||||||
signature_slot,
|
signature_slot,
|
||||||
});
|
chain_spec,
|
||||||
|
)?);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Spec: Full nodes SHOULD provide the LightClientFinalityUpdate with the highest
|
// Spec: Full nodes SHOULD provide the LightClientFinalityUpdate with the highest
|
||||||
@ -127,17 +130,16 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
|
|||||||
if is_latest_finality & !cached_parts.finalized_block_root.is_zero() {
|
if is_latest_finality & !cached_parts.finalized_block_root.is_zero() {
|
||||||
// Immediately after checkpoint sync the finalized block may not be available yet.
|
// Immediately after checkpoint sync the finalized block may not be available yet.
|
||||||
if let Some(finalized_block) =
|
if let Some(finalized_block) =
|
||||||
store.get_blinded_block(&cached_parts.finalized_block_root)?
|
store.get_full_block(&cached_parts.finalized_block_root)?
|
||||||
{
|
{
|
||||||
*self.latest_finality_update.write() = Some(LightClientFinalityUpdate {
|
*self.latest_finality_update.write() = Some(LightClientFinalityUpdate::new(
|
||||||
// TODO: may want to cache this result from latest_optimistic_update if producing a
|
&attested_block,
|
||||||
// light_client header becomes expensive
|
&finalized_block,
|
||||||
attested_header: block_to_light_client_header(attested_block.message()),
|
cached_parts.finality_branch.clone(),
|
||||||
finalized_header: block_to_light_client_header(finalized_block.message()),
|
sync_aggregate.clone(),
|
||||||
finality_branch: cached_parts.finality_branch.clone(),
|
|
||||||
sync_aggregate: sync_aggregate.clone(),
|
|
||||||
signature_slot,
|
signature_slot,
|
||||||
});
|
chain_spec,
|
||||||
|
)?);
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
log,
|
log,
|
||||||
@ -214,7 +216,7 @@ impl LightClientCachedData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements spec priorization rules:
|
// Implements spec prioritization rules:
|
||||||
// > Full nodes SHOULD provide the LightClientFinalityUpdate with the highest attested_header.beacon.slot (if multiple, highest signature_slot)
|
// > Full nodes SHOULD provide the LightClientFinalityUpdate with the highest attested_header.beacon.slot (if multiple, highest signature_slot)
|
||||||
//
|
//
|
||||||
// ref: https://github.com/ethereum/consensus-specs/blob/113c58f9bf9c08867f6f5f633c4d98e0364d612a/specs/altair/light-client/full-node.md#create_light_client_finality_update
|
// ref: https://github.com/ethereum/consensus-specs/blob/113c58f9bf9c08867f6f5f633c4d98e0364d612a/specs/altair/light-client/full-node.md#create_light_client_finality_update
|
||||||
@ -223,14 +225,15 @@ fn is_latest_finality_update<T: EthSpec>(
|
|||||||
attested_slot: Slot,
|
attested_slot: Slot,
|
||||||
signature_slot: Slot,
|
signature_slot: Slot,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if attested_slot > prev.attested_header.beacon.slot {
|
let prev_slot = prev.get_attested_header_slot();
|
||||||
|
if attested_slot > prev_slot {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
attested_slot == prev.attested_header.beacon.slot && signature_slot > prev.signature_slot
|
attested_slot == prev_slot && signature_slot > *prev.signature_slot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements spec priorization rules:
|
// Implements spec prioritization rules:
|
||||||
// > Full nodes SHOULD provide the LightClientOptimisticUpdate with the highest attested_header.beacon.slot (if multiple, highest signature_slot)
|
// > Full nodes SHOULD provide the LightClientOptimisticUpdate with the highest attested_header.beacon.slot (if multiple, highest signature_slot)
|
||||||
//
|
//
|
||||||
// ref: https://github.com/ethereum/consensus-specs/blob/113c58f9bf9c08867f6f5f633c4d98e0364d612a/specs/altair/light-client/full-node.md#create_light_client_optimistic_update
|
// ref: https://github.com/ethereum/consensus-specs/blob/113c58f9bf9c08867f6f5f633c4d98e0364d612a/specs/altair/light-client/full-node.md#create_light_client_optimistic_update
|
||||||
@ -239,18 +242,10 @@ fn is_latest_optimistic_update<T: EthSpec>(
|
|||||||
attested_slot: Slot,
|
attested_slot: Slot,
|
||||||
signature_slot: Slot,
|
signature_slot: Slot,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if attested_slot > prev.attested_header.beacon.slot {
|
let prev_slot = prev.get_slot();
|
||||||
|
if attested_slot > prev_slot {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
attested_slot == prev.attested_header.beacon.slot && signature_slot > prev.signature_slot
|
attested_slot == prev_slot && signature_slot > *prev.signature_slot()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_to_light_client_header<T: EthSpec>(
|
|
||||||
block: BeaconBlockRef<T, types::BlindedPayload<T>>,
|
|
||||||
) -> LightClientHeader {
|
|
||||||
// TODO: make fork aware
|
|
||||||
LightClientHeader {
|
|
||||||
beacon: block.block_header(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2337,7 +2337,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
|
|
||||||
let fork_name = chain
|
let fork_name = chain
|
||||||
.spec
|
.spec
|
||||||
.fork_name_at_slot::<T::EthSpec>(update.signature_slot);
|
.fork_name_at_slot::<T::EthSpec>(*update.signature_slot());
|
||||||
match accept_header {
|
match accept_header {
|
||||||
Some(api_types::Accept::Ssz) => Response::builder()
|
Some(api_types::Accept::Ssz) => Response::builder()
|
||||||
.status(200)
|
.status(200)
|
||||||
@ -2384,7 +2384,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
|
|
||||||
let fork_name = chain
|
let fork_name = chain
|
||||||
.spec
|
.spec
|
||||||
.fork_name_at_slot::<T::EthSpec>(update.signature_slot);
|
.fork_name_at_slot::<T::EthSpec>(*update.signature_slot());
|
||||||
match accept_header {
|
match accept_header {
|
||||||
Some(api_types::Accept::Ssz) => Response::builder()
|
Some(api_types::Accept::Ssz) => Response::builder()
|
||||||
.status(200)
|
.status(200)
|
||||||
|
@ -1717,7 +1717,7 @@ impl ApiTester {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let expected = block.slot();
|
let expected = block.slot();
|
||||||
assert_eq!(result.header.beacon.slot, expected);
|
assert_eq!(result.get_slot(), expected);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -590,9 +590,18 @@ fn handle_rpc_response<T: EthSpec>(
|
|||||||
SupportedProtocol::MetaDataV1 => Ok(Some(RPCResponse::MetaData(MetaData::V1(
|
SupportedProtocol::MetaDataV1 => Ok(Some(RPCResponse::MetaData(MetaData::V1(
|
||||||
MetaDataV1::from_ssz_bytes(decoded_buffer)?,
|
MetaDataV1::from_ssz_bytes(decoded_buffer)?,
|
||||||
)))),
|
)))),
|
||||||
SupportedProtocol::LightClientBootstrapV1 => Ok(Some(RPCResponse::LightClientBootstrap(
|
SupportedProtocol::LightClientBootstrapV1 => match fork_name {
|
||||||
LightClientBootstrap::from_ssz_bytes(decoded_buffer)?,
|
Some(fork_name) => Ok(Some(RPCResponse::LightClientBootstrap(Arc::new(
|
||||||
))),
|
LightClientBootstrap::from_ssz_bytes(decoded_buffer, fork_name)?,
|
||||||
|
)))),
|
||||||
|
None => Err(RPCError::ErrorResponse(
|
||||||
|
RPCResponseErrorCode::InvalidRequest,
|
||||||
|
format!(
|
||||||
|
"No context bytes provided for {:?} response",
|
||||||
|
versioned_protocol
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
},
|
||||||
// MetaData V2 responses have no context bytes, so behave similarly to V1 responses
|
// MetaData V2 responses have no context bytes, so behave similarly to V1 responses
|
||||||
SupportedProtocol::MetaDataV2 => Ok(Some(RPCResponse::MetaData(MetaData::V2(
|
SupportedProtocol::MetaDataV2 => Ok(Some(RPCResponse::MetaData(MetaData::V2(
|
||||||
MetaDataV2::from_ssz_bytes(decoded_buffer)?,
|
MetaDataV2::from_ssz_bytes(decoded_buffer)?,
|
||||||
|
@ -388,7 +388,7 @@ pub enum RPCResponse<T: EthSpec> {
|
|||||||
BlobsByRange(Arc<BlobSidecar<T>>),
|
BlobsByRange(Arc<BlobSidecar<T>>),
|
||||||
|
|
||||||
/// A response to a get LIGHT_CLIENT_BOOTSTRAP request.
|
/// A response to a get LIGHT_CLIENT_BOOTSTRAP request.
|
||||||
LightClientBootstrap(LightClientBootstrap<T>),
|
LightClientBootstrap(Arc<LightClientBootstrap<T>>),
|
||||||
|
|
||||||
/// A response to a get BLOBS_BY_ROOT request.
|
/// A response to a get BLOBS_BY_ROOT request.
|
||||||
BlobsByRoot(Arc<BlobSidecar<T>>),
|
BlobsByRoot(Arc<BlobSidecar<T>>),
|
||||||
@ -569,11 +569,7 @@ impl<T: EthSpec> std::fmt::Display for RPCResponse<T> {
|
|||||||
RPCResponse::Pong(ping) => write!(f, "Pong: {}", ping.data),
|
RPCResponse::Pong(ping) => write!(f, "Pong: {}", ping.data),
|
||||||
RPCResponse::MetaData(metadata) => write!(f, "Metadata: {}", metadata.seq_number()),
|
RPCResponse::MetaData(metadata) => write!(f, "Metadata: {}", metadata.seq_number()),
|
||||||
RPCResponse::LightClientBootstrap(bootstrap) => {
|
RPCResponse::LightClientBootstrap(bootstrap) => {
|
||||||
write!(
|
write!(f, "LightClientBootstrap Slot: {}", bootstrap.get_slot())
|
||||||
f,
|
|
||||||
"LightClientBootstrap Slot: {}",
|
|
||||||
bootstrap.header.beacon.slot
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ pub enum Response<TSpec: EthSpec> {
|
|||||||
/// A response to a get BLOBS_BY_ROOT request.
|
/// A response to a get BLOBS_BY_ROOT request.
|
||||||
BlobsByRoot(Option<Arc<BlobSidecar<TSpec>>>),
|
BlobsByRoot(Option<Arc<BlobSidecar<TSpec>>>),
|
||||||
/// A response to a LightClientUpdate request.
|
/// A response to a LightClientUpdate request.
|
||||||
LightClientBootstrap(LightClientBootstrap<TSpec>),
|
LightClientBootstrap(Arc<LightClientBootstrap<TSpec>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TSpec: EthSpec> std::convert::From<Response<TSpec>> for RPCCodedResponse<TSpec> {
|
impl<TSpec: EthSpec> std::convert::From<Response<TSpec>> for RPCCodedResponse<TSpec> {
|
||||||
|
@ -264,17 +264,31 @@ impl<T: EthSpec> PubsubMessage<T> {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
GossipKind::LightClientFinalityUpdate => {
|
GossipKind::LightClientFinalityUpdate => {
|
||||||
let light_client_finality_update =
|
let light_client_finality_update = match fork_context.from_context_bytes(gossip_topic.fork_digest) {
|
||||||
LightClientFinalityUpdate::from_ssz_bytes(data)
|
Some(&fork_name) => {
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
LightClientFinalityUpdate::from_ssz_bytes(data, fork_name)
|
||||||
|
.map_err(|e| format!("{:?}", e))?
|
||||||
|
},
|
||||||
|
None => return Err(format!(
|
||||||
|
"light_client_finality_update topic invalid for given fork digest {:?}",
|
||||||
|
gossip_topic.fork_digest
|
||||||
|
)),
|
||||||
|
};
|
||||||
Ok(PubsubMessage::LightClientFinalityUpdate(Box::new(
|
Ok(PubsubMessage::LightClientFinalityUpdate(Box::new(
|
||||||
light_client_finality_update,
|
light_client_finality_update,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
GossipKind::LightClientOptimisticUpdate => {
|
GossipKind::LightClientOptimisticUpdate => {
|
||||||
let light_client_optimistic_update =
|
let light_client_optimistic_update = match fork_context.from_context_bytes(gossip_topic.fork_digest) {
|
||||||
LightClientOptimisticUpdate::from_ssz_bytes(data)
|
Some(&fork_name) => {
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
LightClientOptimisticUpdate::from_ssz_bytes(data, fork_name)
|
||||||
|
.map_err(|e| format!("{:?}", e))?
|
||||||
|
},
|
||||||
|
None => return Err(format!(
|
||||||
|
"light_client_optimistic_update topic invalid for given fork digest {:?}",
|
||||||
|
gossip_topic.fork_digest
|
||||||
|
)),
|
||||||
|
};
|
||||||
Ok(PubsubMessage::LightClientOptimisticUpdate(Box::new(
|
Ok(PubsubMessage::LightClientOptimisticUpdate(Box::new(
|
||||||
light_client_optimistic_update,
|
light_client_optimistic_update,
|
||||||
)))
|
)))
|
||||||
|
@ -304,7 +304,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
|||||||
match self.chain.get_light_client_bootstrap(&block_root) {
|
match self.chain.get_light_client_bootstrap(&block_root) {
|
||||||
Ok(Some((bootstrap, _))) => self.send_response(
|
Ok(Some((bootstrap, _))) => self.send_response(
|
||||||
peer_id,
|
peer_id,
|
||||||
Response::LightClientBootstrap(bootstrap),
|
Response::LightClientBootstrap(Arc::new(bootstrap)),
|
||||||
request_id,
|
request_id,
|
||||||
),
|
),
|
||||||
Ok(None) => self.send_error_response(
|
Ok(None) => self.send_error_response(
|
||||||
|
@ -15,6 +15,13 @@ pub type KzgCommitments<T> =
|
|||||||
pub type KzgCommitmentOpts<T> =
|
pub type KzgCommitmentOpts<T> =
|
||||||
FixedVector<Option<KzgCommitment>, <T as EthSpec>::MaxBlobsPerBlock>;
|
FixedVector<Option<KzgCommitment>, <T as EthSpec>::MaxBlobsPerBlock>;
|
||||||
|
|
||||||
|
/// The number of leaves (including padding) on the `BeaconBlockBody` Merkle tree.
|
||||||
|
///
|
||||||
|
/// ## Note
|
||||||
|
///
|
||||||
|
/// This constant is set with the assumption that there are `> 8` and `<= 16` fields on the
|
||||||
|
/// `BeaconBlockBody`. **Tree hashing will fail if this value is set incorrectly.**
|
||||||
|
pub const NUM_BEACON_BLOCK_BODY_HASH_TREE_ROOT_LEAVES: usize = 16;
|
||||||
/// Index of the `blob_kzg_commitments` leaf in the `BeaconBlockBody` tree post-deneb.
|
/// Index of the `blob_kzg_commitments` leaf in the `BeaconBlockBody` tree post-deneb.
|
||||||
pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
|
pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
|
||||||
|
|
||||||
@ -591,6 +598,56 @@ impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> BeaconBlockBody<T> {
|
||||||
|
pub fn block_body_merkle_proof(&self, generalized_index: usize) -> Result<Vec<Hash256>, Error> {
|
||||||
|
let field_index = match generalized_index {
|
||||||
|
light_client_update::EXECUTION_PAYLOAD_INDEX => {
|
||||||
|
// Execution payload is a top-level field, subtract off the generalized indices
|
||||||
|
// for the internal nodes. Result should be 9, the field offset of the execution
|
||||||
|
// payload in the `BeaconBlockBody`:
|
||||||
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#beaconblockbody
|
||||||
|
generalized_index
|
||||||
|
.checked_sub(NUM_BEACON_BLOCK_BODY_HASH_TREE_ROOT_LEAVES)
|
||||||
|
.ok_or(Error::IndexNotSupported(generalized_index))?
|
||||||
|
}
|
||||||
|
_ => return Err(Error::IndexNotSupported(generalized_index)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut leaves = vec![
|
||||||
|
self.randao_reveal().tree_hash_root(),
|
||||||
|
self.eth1_data().tree_hash_root(),
|
||||||
|
self.graffiti().tree_hash_root(),
|
||||||
|
self.proposer_slashings().tree_hash_root(),
|
||||||
|
self.attester_slashings().tree_hash_root(),
|
||||||
|
self.attestations().tree_hash_root(),
|
||||||
|
self.deposits().tree_hash_root(),
|
||||||
|
self.voluntary_exits().tree_hash_root(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if let Ok(sync_aggregate) = self.sync_aggregate() {
|
||||||
|
leaves.push(sync_aggregate.tree_hash_root())
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(execution_payload) = self.execution_payload() {
|
||||||
|
leaves.push(execution_payload.tree_hash_root())
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(bls_to_execution_changes) = self.bls_to_execution_changes() {
|
||||||
|
leaves.push(bls_to_execution_changes.tree_hash_root())
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(blob_kzg_commitments) = self.blob_kzg_commitments() {
|
||||||
|
leaves.push(blob_kzg_commitments.tree_hash_root())
|
||||||
|
}
|
||||||
|
|
||||||
|
let depth = light_client_update::EXECUTION_PAYLOAD_PROOF_LEN;
|
||||||
|
let tree = merkle_proof::MerkleTree::create(&leaves, depth);
|
||||||
|
let (_, proof) = tree.generate_proof(field_index, depth)?;
|
||||||
|
|
||||||
|
Ok(proof)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Util method helpful for logging.
|
/// Util method helpful for logging.
|
||||||
pub fn format_kzg_commitments(commitments: &[KzgCommitment]) -> String {
|
pub fn format_kzg_commitments(commitments: &[KzgCommitment]) -> String {
|
||||||
let commitment_strings: Vec<String> = commitments.iter().map(|x| x.to_string()).collect();
|
let commitment_strings: Vec<String> = commitments.iter().map(|x| x.to_string()).collect();
|
||||||
|
@ -152,11 +152,25 @@ pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedRe
|
|||||||
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
|
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
|
||||||
pub use crate::historical_batch::HistoricalBatch;
|
pub use crate::historical_batch::HistoricalBatch;
|
||||||
pub use crate::indexed_attestation::IndexedAttestation;
|
pub use crate::indexed_attestation::IndexedAttestation;
|
||||||
pub use crate::light_client_bootstrap::LightClientBootstrap;
|
pub use crate::light_client_bootstrap::{
|
||||||
pub use crate::light_client_finality_update::LightClientFinalityUpdate;
|
LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella,
|
||||||
pub use crate::light_client_header::LightClientHeader;
|
LightClientBootstrapDeneb,
|
||||||
pub use crate::light_client_optimistic_update::LightClientOptimisticUpdate;
|
};
|
||||||
pub use crate::light_client_update::{Error as LightClientError, LightClientUpdate};
|
pub use crate::light_client_finality_update::{
|
||||||
|
LightClientFinalityUpdate, LightClientFinalityUpdateAltair, LightClientFinalityUpdateCapella,
|
||||||
|
LightClientFinalityUpdateDeneb,
|
||||||
|
};
|
||||||
|
pub use crate::light_client_header::{
|
||||||
|
LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb,
|
||||||
|
};
|
||||||
|
pub use crate::light_client_optimistic_update::{
|
||||||
|
LightClientOptimisticUpdate, LightClientOptimisticUpdateAltair,
|
||||||
|
LightClientOptimisticUpdateCapella, LightClientOptimisticUpdateDeneb,
|
||||||
|
};
|
||||||
|
pub use crate::light_client_update::{
|
||||||
|
Error as LightClientError, LightClientUpdate, LightClientUpdateAltair,
|
||||||
|
LightClientUpdateCapella, LightClientUpdateDeneb,
|
||||||
|
};
|
||||||
pub use crate::participation_flags::ParticipationFlags;
|
pub use crate::participation_flags::ParticipationFlags;
|
||||||
pub use crate::participation_list::ParticipationList;
|
pub use crate::participation_list::ParticipationList;
|
||||||
pub use crate::payload::{
|
pub use crate::payload::{
|
||||||
|
@ -1,68 +1,147 @@
|
|||||||
use super::{BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee};
|
use super::{BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee};
|
||||||
use crate::{
|
use crate::{
|
||||||
light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize,
|
light_client_update::*, test_utils::TestRandom, ChainSpec, ForkName, ForkVersionDeserialize,
|
||||||
LightClientHeader,
|
LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, SignedBeaconBlock,
|
||||||
|
Slot,
|
||||||
};
|
};
|
||||||
|
use derivative::Derivative;
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use ssz::Decode;
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use superstruct::superstruct;
|
||||||
use test_random_derive::TestRandom;
|
use test_random_derive::TestRandom;
|
||||||
|
use tree_hash_derive::TreeHash;
|
||||||
|
|
||||||
/// A LightClientBootstrap is the initializer we send over to light_client nodes
|
/// A LightClientBootstrap is the initializer we send over to light_client nodes
|
||||||
/// that are trying to generate their basic storage when booting up.
|
/// that are trying to generate their basic storage when booting up.
|
||||||
#[derive(
|
#[superstruct(
|
||||||
|
variants(Altair, Capella, Deneb),
|
||||||
|
variant_attributes(
|
||||||
|
derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
Serialize,
|
Serialize,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
Encode,
|
Derivative,
|
||||||
Decode,
|
Decode,
|
||||||
|
Encode,
|
||||||
TestRandom,
|
TestRandom,
|
||||||
arbitrary::Arbitrary,
|
arbitrary::Arbitrary,
|
||||||
|
TreeHash,
|
||||||
|
),
|
||||||
|
serde(bound = "E: EthSpec", deny_unknown_fields),
|
||||||
|
arbitrary(bound = "E: EthSpec"),
|
||||||
|
)
|
||||||
)]
|
)]
|
||||||
#[serde(bound = "T: EthSpec")]
|
#[derive(
|
||||||
#[arbitrary(bound = "T: EthSpec")]
|
Debug, Clone, Serialize, TreeHash, Encode, Deserialize, arbitrary::Arbitrary, PartialEq,
|
||||||
pub struct LightClientBootstrap<T: EthSpec> {
|
)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[tree_hash(enum_behaviour = "transparent")]
|
||||||
|
#[ssz(enum_behaviour = "transparent")]
|
||||||
|
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
|
||||||
|
#[arbitrary(bound = "E: EthSpec")]
|
||||||
|
pub struct LightClientBootstrap<E: EthSpec> {
|
||||||
/// The requested beacon block header.
|
/// The requested beacon block header.
|
||||||
pub header: LightClientHeader,
|
#[superstruct(only(Altair), partial_getter(rename = "header_altair"))]
|
||||||
|
pub header: LightClientHeaderAltair<E>,
|
||||||
|
#[superstruct(only(Capella), partial_getter(rename = "header_capella"))]
|
||||||
|
pub header: LightClientHeaderCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "header_deneb"))]
|
||||||
|
pub header: LightClientHeaderDeneb<E>,
|
||||||
/// The `SyncCommittee` used in the requested period.
|
/// The `SyncCommittee` used in the requested period.
|
||||||
pub current_sync_committee: Arc<SyncCommittee<T>>,
|
pub current_sync_committee: Arc<SyncCommittee<E>>,
|
||||||
/// Merkle proof for sync committee
|
/// Merkle proof for sync committee
|
||||||
pub current_sync_committee_branch: FixedVector<Hash256, CurrentSyncCommitteeProofLen>,
|
pub current_sync_committee_branch: FixedVector<Hash256, CurrentSyncCommitteeProofLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> LightClientBootstrap<T> {
|
impl<E: EthSpec> LightClientBootstrap<E> {
|
||||||
pub fn from_beacon_state(beacon_state: &mut BeaconState<T>) -> Result<Self, Error> {
|
pub fn get_slot<'a>(&'a self) -> Slot {
|
||||||
|
map_light_client_bootstrap_ref!(&'a _, self.to_ref(), |inner, cons| {
|
||||||
|
cons(inner);
|
||||||
|
inner.header.beacon.slot
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
|
||||||
|
let bootstrap = match fork_name {
|
||||||
|
ForkName::Altair | ForkName::Merge => {
|
||||||
|
let header = LightClientBootstrapAltair::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Altair(header)
|
||||||
|
}
|
||||||
|
ForkName::Capella => {
|
||||||
|
let header = LightClientBootstrapCapella::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Capella(header)
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let header = LightClientBootstrapDeneb::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Deneb(header)
|
||||||
|
}
|
||||||
|
ForkName::Base => {
|
||||||
|
return Err(ssz::DecodeError::BytesInvalid(format!(
|
||||||
|
"LightClientBootstrap decoding for {fork_name} not implemented"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(bootstrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_beacon_state(
|
||||||
|
beacon_state: &mut BeaconState<E>,
|
||||||
|
block: &SignedBeaconBlock<E>,
|
||||||
|
chain_spec: &ChainSpec,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
let mut header = beacon_state.latest_block_header().clone();
|
let mut header = beacon_state.latest_block_header().clone();
|
||||||
header.state_root = beacon_state.update_tree_hash_cache()?;
|
header.state_root = beacon_state.update_tree_hash_cache()?;
|
||||||
let current_sync_committee_branch =
|
let current_sync_committee_branch =
|
||||||
beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?;
|
FixedVector::new(beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?)?;
|
||||||
Ok(LightClientBootstrap {
|
|
||||||
header: header.into(),
|
let current_sync_committee = beacon_state.current_sync_committee()?.clone();
|
||||||
current_sync_committee: beacon_state.current_sync_committee()?.clone(),
|
|
||||||
current_sync_committee_branch: FixedVector::new(current_sync_committee_branch)?,
|
let light_client_bootstrap = match block
|
||||||
})
|
.fork_name(chain_spec)
|
||||||
|
.map_err(|_| Error::InconsistentFork)?
|
||||||
|
{
|
||||||
|
ForkName::Base => return Err(Error::AltairForkNotActive),
|
||||||
|
ForkName::Altair | ForkName::Merge => Self::Altair(LightClientBootstrapAltair {
|
||||||
|
header: LightClientHeaderAltair::block_to_light_client_header(block)?,
|
||||||
|
current_sync_committee,
|
||||||
|
current_sync_committee_branch,
|
||||||
|
}),
|
||||||
|
ForkName::Capella => Self::Capella(LightClientBootstrapCapella {
|
||||||
|
header: LightClientHeaderCapella::block_to_light_client_header(block)?,
|
||||||
|
current_sync_committee,
|
||||||
|
current_sync_committee_branch,
|
||||||
|
}),
|
||||||
|
ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb {
|
||||||
|
header: LightClientHeaderDeneb::block_to_light_client_header(block)?,
|
||||||
|
current_sync_committee,
|
||||||
|
current_sync_committee_branch,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(light_client_bootstrap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> ForkVersionDeserialize for LightClientBootstrap<T> {
|
impl<E: EthSpec> ForkVersionDeserialize for LightClientBootstrap<E> {
|
||||||
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
|
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
|
||||||
value: Value,
|
value: Value,
|
||||||
fork_name: ForkName,
|
fork_name: ForkName,
|
||||||
) -> Result<Self, D::Error> {
|
) -> Result<Self, D::Error> {
|
||||||
match fork_name {
|
match fork_name {
|
||||||
ForkName::Altair | ForkName::Merge => {
|
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => {
|
||||||
Ok(serde_json::from_value::<LightClientBootstrap<T>>(value)
|
Ok(serde_json::from_value::<LightClientBootstrap<E>>(value)
|
||||||
.map_err(serde::de::Error::custom))?
|
.map_err(serde::de::Error::custom))?
|
||||||
}
|
}
|
||||||
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
|
ForkName::Base => Err(serde::de::Error::custom(format!(
|
||||||
Err(serde::de::Error::custom(format!(
|
|
||||||
"LightClientBootstrap failed to deserialize: unsupported fork '{}'",
|
"LightClientBootstrap failed to deserialize: unsupported fork '{}'",
|
||||||
fork_name
|
fork_name
|
||||||
)))
|
))),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,5 +151,5 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::MainnetEthSpec;
|
use crate::MainnetEthSpec;
|
||||||
|
|
||||||
ssz_tests!(LightClientBootstrap<MainnetEthSpec>);
|
ssz_tests!(LightClientBootstrapDeneb<MainnetEthSpec>);
|
||||||
}
|
}
|
||||||
|
@ -1,101 +1,177 @@
|
|||||||
use super::{
|
use super::{EthSpec, FixedVector, Hash256, Slot, SyncAggregate};
|
||||||
EthSpec, FixedVector, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot, SyncAggregate,
|
use crate::ChainSpec;
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec, ForkName,
|
light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize,
|
||||||
ForkVersionDeserialize, LightClientHeader,
|
LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, SignedBeaconBlock,
|
||||||
};
|
};
|
||||||
|
use derivative::Derivative;
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz::Decode;
|
||||||
|
use ssz_derive::Decode;
|
||||||
|
use ssz_derive::Encode;
|
||||||
|
use superstruct::superstruct;
|
||||||
use test_random_derive::TestRandom;
|
use test_random_derive::TestRandom;
|
||||||
use tree_hash::TreeHash;
|
use tree_hash_derive::TreeHash;
|
||||||
|
|
||||||
/// A LightClientFinalityUpdate is the update light_client request or received by a gossip that
|
#[superstruct(
|
||||||
/// signal a new finalized beacon block header for the light client sync protocol.
|
variants(Altair, Capella, Deneb),
|
||||||
#[derive(
|
variant_attributes(
|
||||||
|
derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
Serialize,
|
Serialize,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
Encode,
|
Derivative,
|
||||||
Decode,
|
Decode,
|
||||||
|
Encode,
|
||||||
TestRandom,
|
TestRandom,
|
||||||
arbitrary::Arbitrary,
|
arbitrary::Arbitrary,
|
||||||
|
TreeHash,
|
||||||
|
),
|
||||||
|
serde(bound = "E: EthSpec", deny_unknown_fields),
|
||||||
|
arbitrary(bound = "E: EthSpec"),
|
||||||
|
)
|
||||||
)]
|
)]
|
||||||
#[serde(bound = "T: EthSpec")]
|
#[derive(
|
||||||
#[arbitrary(bound = "T: EthSpec")]
|
Debug, Clone, Serialize, Encode, TreeHash, Deserialize, arbitrary::Arbitrary, PartialEq,
|
||||||
pub struct LightClientFinalityUpdate<T: EthSpec> {
|
)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[tree_hash(enum_behaviour = "transparent")]
|
||||||
|
#[ssz(enum_behaviour = "transparent")]
|
||||||
|
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
|
||||||
|
#[arbitrary(bound = "E: EthSpec")]
|
||||||
|
pub struct LightClientFinalityUpdate<E: EthSpec> {
|
||||||
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
||||||
pub attested_header: LightClientHeader,
|
#[superstruct(only(Altair), partial_getter(rename = "attested_header_altair"))]
|
||||||
|
pub attested_header: LightClientHeaderAltair<E>,
|
||||||
|
#[superstruct(only(Capella), partial_getter(rename = "attested_header_capella"))]
|
||||||
|
pub attested_header: LightClientHeaderCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))]
|
||||||
|
pub attested_header: LightClientHeaderDeneb<E>,
|
||||||
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
|
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
|
||||||
pub finalized_header: LightClientHeader,
|
#[superstruct(only(Altair), partial_getter(rename = "finalized_header_altair"))]
|
||||||
|
pub finalized_header: LightClientHeaderAltair<E>,
|
||||||
|
#[superstruct(only(Capella), partial_getter(rename = "finalized_header_capella"))]
|
||||||
|
pub finalized_header: LightClientHeaderCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "finalized_header_deneb"))]
|
||||||
|
pub finalized_header: LightClientHeaderDeneb<E>,
|
||||||
/// Merkle proof attesting finalized header.
|
/// Merkle proof attesting finalized header.
|
||||||
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
|
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
|
||||||
/// current sync aggreggate
|
/// current sync aggreggate
|
||||||
pub sync_aggregate: SyncAggregate<T>,
|
pub sync_aggregate: SyncAggregate<E>,
|
||||||
/// Slot of the sync aggregated singature
|
/// Slot of the sync aggregated singature
|
||||||
pub signature_slot: Slot,
|
pub signature_slot: Slot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> LightClientFinalityUpdate<T> {
|
impl<E: EthSpec> LightClientFinalityUpdate<E> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
attested_block: &SignedBeaconBlock<E>,
|
||||||
|
finalized_block: &SignedBeaconBlock<E>,
|
||||||
|
finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
|
||||||
|
sync_aggregate: SyncAggregate<E>,
|
||||||
|
signature_slot: Slot,
|
||||||
chain_spec: &ChainSpec,
|
chain_spec: &ChainSpec,
|
||||||
beacon_state: &BeaconState<T>,
|
|
||||||
block: &SignedBeaconBlock<T>,
|
|
||||||
attested_state: &mut BeaconState<T>,
|
|
||||||
finalized_block: &SignedBlindedBeaconBlock<T>,
|
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let altair_fork_epoch = chain_spec
|
let finality_update = match attested_block
|
||||||
.altair_fork_epoch
|
.fork_name(chain_spec)
|
||||||
.ok_or(Error::AltairForkNotActive)?;
|
.map_err(|_| Error::InconsistentFork)?
|
||||||
if beacon_state.slot().epoch(T::slots_per_epoch()) < altair_fork_epoch {
|
{
|
||||||
return Err(Error::AltairForkNotActive);
|
ForkName::Altair | ForkName::Merge => {
|
||||||
|
let finality_update = LightClientFinalityUpdateAltair {
|
||||||
|
attested_header: LightClientHeaderAltair::block_to_light_client_header(
|
||||||
|
attested_block,
|
||||||
|
)?,
|
||||||
|
finalized_header: LightClientHeaderAltair::block_to_light_client_header(
|
||||||
|
finalized_block,
|
||||||
|
)?,
|
||||||
|
finality_branch,
|
||||||
|
sync_aggregate,
|
||||||
|
signature_slot,
|
||||||
|
};
|
||||||
|
Self::Altair(finality_update)
|
||||||
|
}
|
||||||
|
ForkName::Capella => {
|
||||||
|
let finality_update = LightClientFinalityUpdateCapella {
|
||||||
|
attested_header: LightClientHeaderCapella::block_to_light_client_header(
|
||||||
|
attested_block,
|
||||||
|
)?,
|
||||||
|
finalized_header: LightClientHeaderCapella::block_to_light_client_header(
|
||||||
|
finalized_block,
|
||||||
|
)?,
|
||||||
|
finality_branch,
|
||||||
|
sync_aggregate,
|
||||||
|
signature_slot,
|
||||||
|
};
|
||||||
|
Self::Capella(finality_update)
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let finality_update = LightClientFinalityUpdateDeneb {
|
||||||
|
attested_header: LightClientHeaderDeneb::block_to_light_client_header(
|
||||||
|
attested_block,
|
||||||
|
)?,
|
||||||
|
finalized_header: LightClientHeaderDeneb::block_to_light_client_header(
|
||||||
|
finalized_block,
|
||||||
|
)?,
|
||||||
|
finality_branch,
|
||||||
|
sync_aggregate,
|
||||||
|
signature_slot,
|
||||||
|
};
|
||||||
|
Self::Deneb(finality_update)
|
||||||
|
}
|
||||||
|
ForkName::Base => return Err(Error::AltairForkNotActive),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(finality_update)
|
||||||
}
|
}
|
||||||
|
|
||||||
let sync_aggregate = block.message().body().sync_aggregate()?;
|
pub fn get_attested_header_slot<'a>(&'a self) -> Slot {
|
||||||
if sync_aggregate.num_set_bits() < chain_spec.min_sync_committee_participants as usize {
|
map_light_client_finality_update_ref!(&'a _, self.to_ref(), |inner, cons| {
|
||||||
return Err(Error::NotEnoughSyncCommitteeParticipants);
|
cons(inner);
|
||||||
}
|
inner.attested_header.beacon.slot
|
||||||
|
|
||||||
// Compute and validate attested header.
|
|
||||||
let mut attested_header = attested_state.latest_block_header().clone();
|
|
||||||
attested_header.state_root = attested_state.update_tree_hash_cache()?;
|
|
||||||
// Build finalized header from finalized block
|
|
||||||
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.into(),
|
|
||||||
finalized_header: finalized_header.into(),
|
|
||||||
finality_branch: FixedVector::new(finality_branch)?,
|
|
||||||
sync_aggregate: sync_aggregate.clone(),
|
|
||||||
signature_slot: block.slot(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
|
||||||
|
let finality_update = match fork_name {
|
||||||
|
ForkName::Altair | ForkName::Merge => {
|
||||||
|
let finality_update = LightClientFinalityUpdateAltair::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Altair(finality_update)
|
||||||
|
}
|
||||||
|
ForkName::Capella => {
|
||||||
|
let finality_update = LightClientFinalityUpdateCapella::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Capella(finality_update)
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let finality_update = LightClientFinalityUpdateDeneb::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Deneb(finality_update)
|
||||||
|
}
|
||||||
|
ForkName::Base => {
|
||||||
|
return Err(ssz::DecodeError::BytesInvalid(format!(
|
||||||
|
"LightClientFinalityUpdate decoding for {fork_name} not implemented"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(finality_update)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> ForkVersionDeserialize for LightClientFinalityUpdate<T> {
|
impl<E: EthSpec> ForkVersionDeserialize for LightClientFinalityUpdate<E> {
|
||||||
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
|
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
|
||||||
value: Value,
|
value: Value,
|
||||||
fork_name: ForkName,
|
fork_name: ForkName,
|
||||||
) -> Result<Self, D::Error> {
|
) -> Result<Self, D::Error> {
|
||||||
match fork_name {
|
match fork_name {
|
||||||
ForkName::Altair | ForkName::Merge => Ok(serde_json::from_value::<
|
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => Ok(
|
||||||
LightClientFinalityUpdate<T>,
|
serde_json::from_value::<LightClientFinalityUpdate<E>>(value)
|
||||||
>(value)
|
.map_err(serde::de::Error::custom),
|
||||||
.map_err(serde::de::Error::custom))?,
|
)?,
|
||||||
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
|
ForkName::Base => Err(serde::de::Error::custom(format!(
|
||||||
Err(serde::de::Error::custom(format!(
|
|
||||||
"LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'",
|
"LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'",
|
||||||
fork_name
|
fork_name
|
||||||
)))
|
))),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,5 +181,5 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::MainnetEthSpec;
|
use crate::MainnetEthSpec;
|
||||||
|
|
||||||
ssz_tests!(LightClientFinalityUpdate<MainnetEthSpec>);
|
ssz_tests!(LightClientFinalityUpdateDeneb<MainnetEthSpec>);
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,209 @@
|
|||||||
use crate::test_utils::TestRandom;
|
|
||||||
use crate::BeaconBlockHeader;
|
use crate::BeaconBlockHeader;
|
||||||
|
use crate::ChainSpec;
|
||||||
|
use crate::ForkName;
|
||||||
|
use crate::ForkVersionDeserialize;
|
||||||
|
use crate::{light_client_update::*, BeaconBlockBody};
|
||||||
|
use crate::{
|
||||||
|
test_utils::TestRandom, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
|
||||||
|
FixedVector, Hash256, SignedBeaconBlock,
|
||||||
|
};
|
||||||
|
use derivative::Derivative;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ssz::Decode;
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use superstruct::superstruct;
|
||||||
use test_random_derive::TestRandom;
|
use test_random_derive::TestRandom;
|
||||||
|
use tree_hash_derive::TreeHash;
|
||||||
|
|
||||||
#[derive(
|
#[superstruct(
|
||||||
|
variants(Altair, Capella, Deneb),
|
||||||
|
variant_attributes(
|
||||||
|
derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
Serialize,
|
Serialize,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
Encode,
|
Derivative,
|
||||||
Decode,
|
Decode,
|
||||||
|
Encode,
|
||||||
TestRandom,
|
TestRandom,
|
||||||
arbitrary::Arbitrary,
|
arbitrary::Arbitrary,
|
||||||
|
TreeHash,
|
||||||
|
),
|
||||||
|
serde(bound = "E: EthSpec", deny_unknown_fields),
|
||||||
|
arbitrary(bound = "E: EthSpec"),
|
||||||
|
)
|
||||||
)]
|
)]
|
||||||
pub struct LightClientHeader {
|
#[derive(
|
||||||
|
Debug, Clone, Serialize, TreeHash, Encode, Deserialize, arbitrary::Arbitrary, PartialEq,
|
||||||
|
)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[tree_hash(enum_behaviour = "transparent")]
|
||||||
|
#[ssz(enum_behaviour = "transparent")]
|
||||||
|
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
|
||||||
|
#[arbitrary(bound = "E: EthSpec")]
|
||||||
|
pub struct LightClientHeader<E: EthSpec> {
|
||||||
pub beacon: BeaconBlockHeader,
|
pub beacon: BeaconBlockHeader,
|
||||||
|
|
||||||
|
#[superstruct(
|
||||||
|
only(Capella),
|
||||||
|
partial_getter(rename = "execution_payload_header_capella")
|
||||||
|
)]
|
||||||
|
pub execution: ExecutionPayloadHeaderCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_header_deneb"))]
|
||||||
|
pub execution: ExecutionPayloadHeaderDeneb<E>,
|
||||||
|
|
||||||
|
#[superstruct(only(Capella, Deneb))]
|
||||||
|
pub execution_branch: FixedVector<Hash256, ExecutionPayloadProofLen>,
|
||||||
|
|
||||||
|
#[ssz(skip_serializing, skip_deserializing)]
|
||||||
|
#[tree_hash(skip_hashing)]
|
||||||
|
#[serde(skip)]
|
||||||
|
#[arbitrary(default)]
|
||||||
|
pub _phantom_data: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BeaconBlockHeader> for LightClientHeader {
|
impl<E: EthSpec> LightClientHeader<E> {
|
||||||
fn from(beacon: BeaconBlockHeader) -> Self {
|
pub fn block_to_light_client_header(
|
||||||
LightClientHeader { beacon }
|
block: &SignedBeaconBlock<E>,
|
||||||
|
chain_spec: &ChainSpec,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let header = match block
|
||||||
|
.fork_name(chain_spec)
|
||||||
|
.map_err(|_| Error::InconsistentFork)?
|
||||||
|
{
|
||||||
|
ForkName::Base => return Err(Error::AltairForkNotActive),
|
||||||
|
ForkName::Altair | ForkName::Merge => LightClientHeader::Altair(
|
||||||
|
LightClientHeaderAltair::block_to_light_client_header(block)?,
|
||||||
|
),
|
||||||
|
ForkName::Capella => LightClientHeader::Capella(
|
||||||
|
LightClientHeaderCapella::block_to_light_client_header(block)?,
|
||||||
|
),
|
||||||
|
ForkName::Deneb => LightClientHeader::Deneb(
|
||||||
|
LightClientHeaderDeneb::block_to_light_client_header(block)?,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Ok(header)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
|
||||||
|
let header = match fork_name {
|
||||||
|
ForkName::Altair | ForkName::Merge => {
|
||||||
|
let header = LightClientHeaderAltair::from_ssz_bytes(bytes)?;
|
||||||
|
LightClientHeader::Altair(header)
|
||||||
|
}
|
||||||
|
ForkName::Capella => {
|
||||||
|
let header = LightClientHeaderCapella::from_ssz_bytes(bytes)?;
|
||||||
|
LightClientHeader::Capella(header)
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let header = LightClientHeaderDeneb::from_ssz_bytes(bytes)?;
|
||||||
|
LightClientHeader::Deneb(header)
|
||||||
|
}
|
||||||
|
ForkName::Base => {
|
||||||
|
return Err(ssz::DecodeError::BytesInvalid(format!(
|
||||||
|
"LightClientHeader decoding for {fork_name} not implemented"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(header)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Custom SSZ decoder that takes a `ForkName` as context.
|
||||||
|
pub fn from_ssz_bytes_for_fork(
|
||||||
|
bytes: &[u8],
|
||||||
|
fork_name: ForkName,
|
||||||
|
) -> Result<Self, ssz::DecodeError> {
|
||||||
|
Self::from_ssz_bytes(bytes, fork_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> LightClientHeaderAltair<E> {
|
||||||
|
pub fn block_to_light_client_header(block: &SignedBeaconBlock<E>) -> Result<Self, Error> {
|
||||||
|
Ok(LightClientHeaderAltair {
|
||||||
|
beacon: block.message().block_header(),
|
||||||
|
_phantom_data: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> LightClientHeaderCapella<E> {
|
||||||
|
pub fn block_to_light_client_header(block: &SignedBeaconBlock<E>) -> Result<Self, Error> {
|
||||||
|
let payload = block
|
||||||
|
.message()
|
||||||
|
.execution_payload()?
|
||||||
|
.execution_payload_capella()?;
|
||||||
|
|
||||||
|
let header = ExecutionPayloadHeaderCapella::from(payload);
|
||||||
|
let beacon_block_body = BeaconBlockBody::from(
|
||||||
|
block
|
||||||
|
.message()
|
||||||
|
.body_capella()
|
||||||
|
.map_err(|_| Error::BeaconBlockBodyError)?
|
||||||
|
.to_owned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let execution_branch =
|
||||||
|
beacon_block_body.block_body_merkle_proof(EXECUTION_PAYLOAD_INDEX)?;
|
||||||
|
|
||||||
|
return Ok(LightClientHeaderCapella {
|
||||||
|
beacon: block.message().block_header(),
|
||||||
|
execution: header,
|
||||||
|
execution_branch: FixedVector::new(execution_branch)?,
|
||||||
|
_phantom_data: PhantomData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> LightClientHeaderDeneb<E> {
|
||||||
|
pub fn block_to_light_client_header(block: &SignedBeaconBlock<E>) -> Result<Self, Error> {
|
||||||
|
let payload = block
|
||||||
|
.message()
|
||||||
|
.execution_payload()?
|
||||||
|
.execution_payload_deneb()?;
|
||||||
|
|
||||||
|
let header = ExecutionPayloadHeaderDeneb::from(payload);
|
||||||
|
let beacon_block_body = BeaconBlockBody::from(
|
||||||
|
block
|
||||||
|
.message()
|
||||||
|
.body_deneb()
|
||||||
|
.map_err(|_| Error::BeaconBlockBodyError)?
|
||||||
|
.to_owned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let execution_branch =
|
||||||
|
beacon_block_body.block_body_merkle_proof(EXECUTION_PAYLOAD_INDEX)?;
|
||||||
|
|
||||||
|
Ok(LightClientHeaderDeneb {
|
||||||
|
beacon: block.message().block_header(),
|
||||||
|
execution: header,
|
||||||
|
execution_branch: FixedVector::new(execution_branch)?,
|
||||||
|
_phantom_data: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> ForkVersionDeserialize for LightClientHeader<T> {
|
||||||
|
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
|
||||||
|
value: serde_json::value::Value,
|
||||||
|
fork_name: ForkName,
|
||||||
|
) -> Result<Self, D::Error> {
|
||||||
|
match fork_name {
|
||||||
|
ForkName::Altair | ForkName::Merge => serde_json::from_value(value)
|
||||||
|
.map(|light_client_header| Self::Altair(light_client_header))
|
||||||
|
.map_err(serde::de::Error::custom),
|
||||||
|
ForkName::Capella => serde_json::from_value(value)
|
||||||
|
.map(|light_client_header| Self::Capella(light_client_header))
|
||||||
|
.map_err(serde::de::Error::custom),
|
||||||
|
ForkName::Deneb => serde_json::from_value(value)
|
||||||
|
.map(|light_client_header| Self::Deneb(light_client_header))
|
||||||
|
.map_err(serde::de::Error::custom),
|
||||||
|
ForkName::Base => Err(serde::de::Error::custom(format!(
|
||||||
|
"LightClientHeader deserialization for {fork_name} not implemented"
|
||||||
|
))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,65 +1,155 @@
|
|||||||
use super::{EthSpec, ForkName, ForkVersionDeserialize, Slot, SyncAggregate};
|
use super::{EthSpec, ForkName, ForkVersionDeserialize, Slot, SyncAggregate};
|
||||||
use crate::light_client_header::LightClientHeader;
|
use crate::test_utils::TestRandom;
|
||||||
use crate::{
|
use crate::{
|
||||||
light_client_update::Error, test_utils::TestRandom, BeaconState, ChainSpec, SignedBeaconBlock,
|
light_client_update::*, ChainSpec, LightClientHeaderAltair, LightClientHeaderCapella,
|
||||||
|
LightClientHeaderDeneb, SignedBeaconBlock,
|
||||||
};
|
};
|
||||||
|
use derivative::Derivative;
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz::Decode;
|
||||||
|
use ssz_derive::Decode;
|
||||||
|
use ssz_derive::Encode;
|
||||||
|
use superstruct::superstruct;
|
||||||
use test_random_derive::TestRandom;
|
use test_random_derive::TestRandom;
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::Hash256;
|
||||||
|
use tree_hash_derive::TreeHash;
|
||||||
|
|
||||||
/// A LightClientOptimisticUpdate is the update we send on each slot,
|
/// A LightClientOptimisticUpdate is the update we send on each slot,
|
||||||
/// it is based off the current unfinalized epoch is verified only against BLS signature.
|
/// it is based off the current unfinalized epoch is verified only against BLS signature.
|
||||||
#[derive(
|
#[superstruct(
|
||||||
|
variants(Altair, Capella, Deneb),
|
||||||
|
variant_attributes(
|
||||||
|
derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
Serialize,
|
Serialize,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
Encode,
|
Derivative,
|
||||||
Decode,
|
Decode,
|
||||||
|
Encode,
|
||||||
TestRandom,
|
TestRandom,
|
||||||
arbitrary::Arbitrary,
|
arbitrary::Arbitrary,
|
||||||
|
TreeHash,
|
||||||
|
),
|
||||||
|
serde(bound = "E: EthSpec", deny_unknown_fields),
|
||||||
|
arbitrary(bound = "E: EthSpec"),
|
||||||
|
)
|
||||||
)]
|
)]
|
||||||
#[serde(bound = "T: EthSpec")]
|
#[derive(
|
||||||
#[arbitrary(bound = "T: EthSpec")]
|
Debug, Clone, Serialize, Encode, TreeHash, Deserialize, arbitrary::Arbitrary, PartialEq,
|
||||||
pub struct LightClientOptimisticUpdate<T: EthSpec> {
|
)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[tree_hash(enum_behaviour = "transparent")]
|
||||||
|
#[ssz(enum_behaviour = "transparent")]
|
||||||
|
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
|
||||||
|
#[arbitrary(bound = "E: EthSpec")]
|
||||||
|
pub struct LightClientOptimisticUpdate<E: EthSpec> {
|
||||||
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
||||||
pub attested_header: LightClientHeader,
|
#[superstruct(only(Altair), partial_getter(rename = "attested_header_altair"))]
|
||||||
|
pub attested_header: LightClientHeaderAltair<E>,
|
||||||
|
#[superstruct(only(Capella), partial_getter(rename = "attested_header_capella"))]
|
||||||
|
pub attested_header: LightClientHeaderCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))]
|
||||||
|
pub attested_header: LightClientHeaderDeneb<E>,
|
||||||
/// current sync aggreggate
|
/// current sync aggreggate
|
||||||
pub sync_aggregate: SyncAggregate<T>,
|
pub sync_aggregate: SyncAggregate<E>,
|
||||||
/// Slot of the sync aggregated singature
|
/// Slot of the sync aggregated singature
|
||||||
pub signature_slot: Slot,
|
pub signature_slot: Slot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> LightClientOptimisticUpdate<T> {
|
impl<E: EthSpec> LightClientOptimisticUpdate<E> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
attested_block: &SignedBeaconBlock<E>,
|
||||||
|
sync_aggregate: SyncAggregate<E>,
|
||||||
|
signature_slot: Slot,
|
||||||
chain_spec: &ChainSpec,
|
chain_spec: &ChainSpec,
|
||||||
block: &SignedBeaconBlock<T>,
|
|
||||||
attested_state: &BeaconState<T>,
|
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let altair_fork_epoch = chain_spec
|
let optimistic_update = match attested_block
|
||||||
.altair_fork_epoch
|
.fork_name(chain_spec)
|
||||||
.ok_or(Error::AltairForkNotActive)?;
|
.map_err(|_| Error::InconsistentFork)?
|
||||||
if attested_state.slot().epoch(T::slots_per_epoch()) < altair_fork_epoch {
|
{
|
||||||
return Err(Error::AltairForkNotActive);
|
ForkName::Altair | ForkName::Merge => {
|
||||||
|
let optimistic_update = LightClientOptimisticUpdateAltair {
|
||||||
|
attested_header: LightClientHeaderAltair::block_to_light_client_header(
|
||||||
|
attested_block,
|
||||||
|
)?,
|
||||||
|
sync_aggregate,
|
||||||
|
signature_slot,
|
||||||
|
};
|
||||||
|
Self::Altair(optimistic_update)
|
||||||
|
}
|
||||||
|
ForkName::Capella => {
|
||||||
|
let optimistic_update = LightClientOptimisticUpdateCapella {
|
||||||
|
attested_header: LightClientHeaderCapella::block_to_light_client_header(
|
||||||
|
attested_block,
|
||||||
|
)?,
|
||||||
|
sync_aggregate,
|
||||||
|
signature_slot,
|
||||||
|
};
|
||||||
|
Self::Capella(optimistic_update)
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let optimistic_update = LightClientOptimisticUpdateDeneb {
|
||||||
|
attested_header: LightClientHeaderDeneb::block_to_light_client_header(
|
||||||
|
attested_block,
|
||||||
|
)?,
|
||||||
|
sync_aggregate,
|
||||||
|
signature_slot,
|
||||||
|
};
|
||||||
|
Self::Deneb(optimistic_update)
|
||||||
|
}
|
||||||
|
ForkName::Base => return Err(Error::AltairForkNotActive),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(optimistic_update)
|
||||||
}
|
}
|
||||||
|
|
||||||
let sync_aggregate = block.message().body().sync_aggregate()?;
|
pub fn get_slot<'a>(&'a self) -> Slot {
|
||||||
if sync_aggregate.num_set_bits() < chain_spec.min_sync_committee_participants as usize {
|
map_light_client_optimistic_update_ref!(&'a _, self.to_ref(), |inner, cons| {
|
||||||
return Err(Error::NotEnoughSyncCommitteeParticipants);
|
cons(inner);
|
||||||
}
|
inner.attested_header.beacon.slot
|
||||||
|
|
||||||
// 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: attested_header.into(),
|
|
||||||
sync_aggregate: sync_aggregate.clone(),
|
|
||||||
signature_slot: block.slot(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_canonical_root<'a>(&'a self) -> Hash256 {
|
||||||
|
map_light_client_optimistic_update_ref!(&'a _, self.to_ref(), |inner, cons| {
|
||||||
|
cons(inner);
|
||||||
|
inner.attested_header.beacon.canonical_root()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_parent_root<'a>(&'a self) -> Hash256 {
|
||||||
|
map_light_client_optimistic_update_ref!(&'a _, self.to_ref(), |inner, cons| {
|
||||||
|
cons(inner);
|
||||||
|
inner.attested_header.beacon.parent_root
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
|
||||||
|
let optimistic_update = match fork_name {
|
||||||
|
ForkName::Altair | ForkName::Merge => {
|
||||||
|
let optimistic_update = LightClientOptimisticUpdateAltair::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Altair(optimistic_update)
|
||||||
|
}
|
||||||
|
ForkName::Capella => {
|
||||||
|
let optimistic_update = LightClientOptimisticUpdateCapella::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Capella(optimistic_update)
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let optimistic_update = LightClientOptimisticUpdateDeneb::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Deneb(optimistic_update)
|
||||||
|
}
|
||||||
|
ForkName::Base => {
|
||||||
|
return Err(ssz::DecodeError::BytesInvalid(format!(
|
||||||
|
"LightClientOptimisticUpdate decoding for {fork_name} not implemented"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(optimistic_update)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> ForkVersionDeserialize for LightClientOptimisticUpdate<T> {
|
impl<T: EthSpec> ForkVersionDeserialize for LightClientOptimisticUpdate<T> {
|
||||||
@ -68,16 +158,14 @@ impl<T: EthSpec> ForkVersionDeserialize for LightClientOptimisticUpdate<T> {
|
|||||||
fork_name: ForkName,
|
fork_name: ForkName,
|
||||||
) -> Result<Self, D::Error> {
|
) -> Result<Self, D::Error> {
|
||||||
match fork_name {
|
match fork_name {
|
||||||
ForkName::Altair | ForkName::Merge => Ok(serde_json::from_value::<
|
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => Ok(
|
||||||
LightClientOptimisticUpdate<T>,
|
serde_json::from_value::<LightClientOptimisticUpdate<T>>(value)
|
||||||
>(value)
|
.map_err(serde::de::Error::custom),
|
||||||
.map_err(serde::de::Error::custom))?,
|
)?,
|
||||||
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
|
ForkName::Base => Err(serde::de::Error::custom(format!(
|
||||||
Err(serde::de::Error::custom(format!(
|
|
||||||
"LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'",
|
"LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'",
|
||||||
fork_name
|
fork_name
|
||||||
)))
|
))),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,5 +175,5 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::MainnetEthSpec;
|
use crate::MainnetEthSpec;
|
||||||
|
|
||||||
ssz_tests!(LightClientOptimisticUpdate<MainnetEthSpec>);
|
ssz_tests!(LightClientOptimisticUpdateDeneb<MainnetEthSpec>);
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,38 @@
|
|||||||
use super::{BeaconBlockHeader, EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee};
|
use super::{EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee};
|
||||||
use crate::{
|
use crate::{
|
||||||
beacon_state, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec, ForkName,
|
beacon_state, test_utils::TestRandom, BeaconBlock, BeaconBlockHeader, BeaconState, ChainSpec,
|
||||||
ForkVersionDeserialize, LightClientHeader,
|
ForkName, ForkVersionDeserialize, LightClientHeaderAltair, LightClientHeaderCapella,
|
||||||
|
LightClientHeaderDeneb, SignedBeaconBlock,
|
||||||
};
|
};
|
||||||
|
use derivative::Derivative;
|
||||||
use safe_arith::ArithError;
|
use safe_arith::ArithError;
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz::Decode;
|
||||||
use ssz_types::typenum::{U5, U6};
|
use ssz_derive::Decode;
|
||||||
|
use ssz_derive::Encode;
|
||||||
|
use ssz_types::typenum::{U4, U5, U6};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use superstruct::superstruct;
|
||||||
use test_random_derive::TestRandom;
|
use test_random_derive::TestRandom;
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
|
use tree_hash_derive::TreeHash;
|
||||||
|
|
||||||
pub const FINALIZED_ROOT_INDEX: usize = 105;
|
pub const FINALIZED_ROOT_INDEX: usize = 105;
|
||||||
pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54;
|
pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54;
|
||||||
pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55;
|
pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55;
|
||||||
|
pub const EXECUTION_PAYLOAD_INDEX: usize = 25;
|
||||||
|
|
||||||
pub type FinalizedRootProofLen = U6;
|
pub type FinalizedRootProofLen = U6;
|
||||||
pub type CurrentSyncCommitteeProofLen = U5;
|
pub type CurrentSyncCommitteeProofLen = U5;
|
||||||
|
pub type ExecutionPayloadProofLen = U4;
|
||||||
|
|
||||||
pub type NextSyncCommitteeProofLen = U5;
|
pub type NextSyncCommitteeProofLen = U5;
|
||||||
|
|
||||||
pub const FINALIZED_ROOT_PROOF_LEN: usize = 6;
|
pub const FINALIZED_ROOT_PROOF_LEN: usize = 6;
|
||||||
pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
|
pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
|
||||||
pub const NEXT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
|
pub const NEXT_SYNC_COMMITTEE_PROOF_LEN: usize = 5;
|
||||||
|
pub const EXECUTION_PAYLOAD_PROOF_LEN: usize = 4;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -33,6 +43,8 @@ pub enum Error {
|
|||||||
NotEnoughSyncCommitteeParticipants,
|
NotEnoughSyncCommitteeParticipants,
|
||||||
MismatchingPeriods,
|
MismatchingPeriods,
|
||||||
InvalidFinalizedBlock,
|
InvalidFinalizedBlock,
|
||||||
|
BeaconBlockBodyError,
|
||||||
|
InconsistentFork,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ssz_types::Error> for Error {
|
impl From<ssz_types::Error> for Error {
|
||||||
@ -53,77 +65,114 @@ impl From<ArithError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A LightClientUpdate is the update we request solely to either complete the bootstraping process,
|
/// A LightClientUpdate is the update we request solely to either complete the bootstrapping process,
|
||||||
/// or to sync up to the last committee period, we need to have one ready for each ALTAIR period
|
/// 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].
|
/// we go over, note: there is no need to keep all of the updates from [ALTAIR_PERIOD, CURRENT_PERIOD].
|
||||||
#[derive(
|
#[superstruct(
|
||||||
|
variants(Altair, Capella, Deneb),
|
||||||
|
variant_attributes(
|
||||||
|
derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
Serialize,
|
Serialize,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
Encode,
|
Derivative,
|
||||||
Decode,
|
Decode,
|
||||||
|
Encode,
|
||||||
TestRandom,
|
TestRandom,
|
||||||
arbitrary::Arbitrary,
|
arbitrary::Arbitrary,
|
||||||
|
TreeHash,
|
||||||
|
),
|
||||||
|
serde(bound = "E: EthSpec", deny_unknown_fields),
|
||||||
|
arbitrary(bound = "E: EthSpec"),
|
||||||
|
)
|
||||||
)]
|
)]
|
||||||
#[serde(bound = "T: EthSpec")]
|
#[derive(
|
||||||
#[arbitrary(bound = "T: EthSpec")]
|
Debug, Clone, Serialize, Encode, TreeHash, Deserialize, arbitrary::Arbitrary, PartialEq,
|
||||||
pub struct LightClientUpdate<T: EthSpec> {
|
)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[tree_hash(enum_behaviour = "transparent")]
|
||||||
|
#[ssz(enum_behaviour = "transparent")]
|
||||||
|
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
|
||||||
|
#[arbitrary(bound = "E: EthSpec")]
|
||||||
|
pub struct LightClientUpdate<E: EthSpec> {
|
||||||
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
/// The last `BeaconBlockHeader` from the last attested block by the sync committee.
|
||||||
pub attested_header: LightClientHeader,
|
#[superstruct(only(Altair), partial_getter(rename = "attested_header_altair"))]
|
||||||
|
pub attested_header: LightClientHeaderAltair<E>,
|
||||||
|
#[superstruct(only(Capella), partial_getter(rename = "attested_header_capella"))]
|
||||||
|
pub attested_header: LightClientHeaderCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))]
|
||||||
|
pub attested_header: LightClientHeaderDeneb<E>,
|
||||||
/// The `SyncCommittee` used in the next period.
|
/// The `SyncCommittee` used in the next period.
|
||||||
pub next_sync_committee: Arc<SyncCommittee<T>>,
|
pub next_sync_committee: Arc<SyncCommittee<E>>,
|
||||||
/// Merkle proof for next sync committee
|
/// Merkle proof for next sync committee
|
||||||
pub next_sync_committee_branch: FixedVector<Hash256, NextSyncCommitteeProofLen>,
|
pub next_sync_committee_branch: FixedVector<Hash256, NextSyncCommitteeProofLen>,
|
||||||
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
|
/// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch).
|
||||||
pub finalized_header: LightClientHeader,
|
#[superstruct(only(Altair), partial_getter(rename = "finalized_header_altair"))]
|
||||||
|
pub finalized_header: LightClientHeaderAltair<E>,
|
||||||
|
#[superstruct(only(Capella), partial_getter(rename = "finalized_header_capella"))]
|
||||||
|
pub finalized_header: LightClientHeaderCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "finalized_header_deneb"))]
|
||||||
|
pub finalized_header: LightClientHeaderDeneb<E>,
|
||||||
/// Merkle proof attesting finalized header.
|
/// Merkle proof attesting finalized header.
|
||||||
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
|
pub finality_branch: FixedVector<Hash256, FinalizedRootProofLen>,
|
||||||
/// current sync aggreggate
|
/// current sync aggreggate
|
||||||
pub sync_aggregate: SyncAggregate<T>,
|
pub sync_aggregate: SyncAggregate<E>,
|
||||||
/// Slot of the sync aggregated singature
|
/// Slot of the sync aggregated signature
|
||||||
pub signature_slot: Slot,
|
pub signature_slot: Slot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> LightClientUpdate<T> {
|
impl<E: EthSpec> ForkVersionDeserialize for LightClientUpdate<E> {
|
||||||
pub fn new(
|
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
|
||||||
chain_spec: ChainSpec,
|
value: Value,
|
||||||
beacon_state: BeaconState<T>,
|
fork_name: ForkName,
|
||||||
block: BeaconBlock<T>,
|
) -> Result<Self, D::Error> {
|
||||||
attested_state: &mut BeaconState<T>,
|
match fork_name {
|
||||||
finalized_block: BeaconBlock<T>,
|
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => {
|
||||||
) -> Result<Self, Error> {
|
Ok(serde_json::from_value::<LightClientUpdate<E>>(value)
|
||||||
let altair_fork_epoch = chain_spec
|
.map_err(serde::de::Error::custom))?
|
||||||
.altair_fork_epoch
|
}
|
||||||
.ok_or(Error::AltairForkNotActive)?;
|
ForkName::Base => Err(serde::de::Error::custom(format!(
|
||||||
if attested_state.slot().epoch(T::slots_per_epoch()) < altair_fork_epoch {
|
"LightClientUpdate failed to deserialize: unsupported fork '{}'",
|
||||||
return Err(Error::AltairForkNotActive);
|
fork_name
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> LightClientUpdate<E> {
|
||||||
|
pub fn new(
|
||||||
|
beacon_state: BeaconState<E>,
|
||||||
|
block: BeaconBlock<E>,
|
||||||
|
attested_state: &mut BeaconState<E>,
|
||||||
|
attested_block: &SignedBeaconBlock<E>,
|
||||||
|
finalized_block: &SignedBeaconBlock<E>,
|
||||||
|
chain_spec: &ChainSpec,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
let sync_aggregate = block.body().sync_aggregate()?;
|
let sync_aggregate = block.body().sync_aggregate()?;
|
||||||
if sync_aggregate.num_set_bits() < chain_spec.min_sync_committee_participants as usize {
|
if sync_aggregate.num_set_bits() < chain_spec.min_sync_committee_participants as usize {
|
||||||
return Err(Error::NotEnoughSyncCommitteeParticipants);
|
return Err(Error::NotEnoughSyncCommitteeParticipants);
|
||||||
}
|
}
|
||||||
|
|
||||||
let signature_period = block.epoch().sync_committee_period(&chain_spec)?;
|
let signature_period = block.epoch().sync_committee_period(chain_spec)?;
|
||||||
// Compute and validate attested header.
|
// Compute and validate attested header.
|
||||||
let mut attested_header = attested_state.latest_block_header().clone();
|
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.tree_hash_root();
|
||||||
let attested_period = attested_header
|
let attested_period = attested_header
|
||||||
.slot
|
.slot
|
||||||
.epoch(T::slots_per_epoch())
|
.epoch(E::slots_per_epoch())
|
||||||
.sync_committee_period(&chain_spec)?;
|
.sync_committee_period(chain_spec)?;
|
||||||
if attested_period != signature_period {
|
if attested_period != signature_period {
|
||||||
return Err(Error::MismatchingPeriods);
|
return Err(Error::MismatchingPeriods);
|
||||||
}
|
}
|
||||||
// Build finalized header from finalized block
|
// Build finalized header from finalized block
|
||||||
let finalized_header = BeaconBlockHeader {
|
let finalized_header = BeaconBlockHeader {
|
||||||
slot: finalized_block.slot(),
|
slot: finalized_block.slot(),
|
||||||
proposer_index: finalized_block.proposer_index(),
|
proposer_index: finalized_block.message().proposer_index(),
|
||||||
parent_root: finalized_block.parent_root(),
|
parent_root: finalized_block.parent_root(),
|
||||||
state_root: finalized_block.state_root(),
|
state_root: finalized_block.state_root(),
|
||||||
body_root: finalized_block.body_root(),
|
body_root: finalized_block.message().body_root(),
|
||||||
};
|
};
|
||||||
if finalized_header.tree_hash_root() != beacon_state.finalized_checkpoint().root {
|
if finalized_header.tree_hash_root() != beacon_state.finalized_checkpoint().root {
|
||||||
return Err(Error::InvalidFinalizedBlock);
|
return Err(Error::InvalidFinalizedBlock);
|
||||||
@ -131,35 +180,84 @@ impl<T: EthSpec> LightClientUpdate<T> {
|
|||||||
let next_sync_committee_branch =
|
let next_sync_committee_branch =
|
||||||
attested_state.compute_merkle_proof(NEXT_SYNC_COMMITTEE_INDEX)?;
|
attested_state.compute_merkle_proof(NEXT_SYNC_COMMITTEE_INDEX)?;
|
||||||
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
|
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
|
||||||
Ok(Self {
|
|
||||||
attested_header: attested_header.into(),
|
let light_client_update = match attested_block
|
||||||
|
.fork_name(chain_spec)
|
||||||
|
.map_err(|_| Error::InconsistentFork)?
|
||||||
|
{
|
||||||
|
ForkName::Base => return Err(Error::AltairForkNotActive),
|
||||||
|
ForkName::Altair | ForkName::Merge => {
|
||||||
|
let attested_header =
|
||||||
|
LightClientHeaderAltair::block_to_light_client_header(attested_block)?;
|
||||||
|
let finalized_header =
|
||||||
|
LightClientHeaderAltair::block_to_light_client_header(finalized_block)?;
|
||||||
|
Self::Altair(LightClientUpdateAltair {
|
||||||
|
attested_header,
|
||||||
next_sync_committee: attested_state.next_sync_committee()?.clone(),
|
next_sync_committee: attested_state.next_sync_committee()?.clone(),
|
||||||
next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?,
|
next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?,
|
||||||
finalized_header: finalized_header.into(),
|
finalized_header,
|
||||||
finality_branch: FixedVector::new(finality_branch)?,
|
finality_branch: FixedVector::new(finality_branch)?,
|
||||||
sync_aggregate: sync_aggregate.clone(),
|
sync_aggregate: sync_aggregate.clone(),
|
||||||
signature_slot: block.slot(),
|
signature_slot: block.slot(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
ForkName::Capella => {
|
||||||
|
let attested_header =
|
||||||
|
LightClientHeaderCapella::block_to_light_client_header(attested_block)?;
|
||||||
|
let finalized_header =
|
||||||
|
LightClientHeaderCapella::block_to_light_client_header(finalized_block)?;
|
||||||
|
Self::Capella(LightClientUpdateCapella {
|
||||||
|
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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let attested_header =
|
||||||
|
LightClientHeaderDeneb::block_to_light_client_header(attested_block)?;
|
||||||
|
let finalized_header =
|
||||||
|
LightClientHeaderDeneb::block_to_light_client_header(finalized_block)?;
|
||||||
|
Self::Deneb(LightClientUpdateDeneb {
|
||||||
|
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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(light_client_update)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> ForkVersionDeserialize for LightClientUpdate<T> {
|
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
|
||||||
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
|
let update = match fork_name {
|
||||||
value: Value,
|
|
||||||
fork_name: ForkName,
|
|
||||||
) -> Result<Self, D::Error> {
|
|
||||||
match fork_name {
|
|
||||||
ForkName::Altair | ForkName::Merge => {
|
ForkName::Altair | ForkName::Merge => {
|
||||||
Ok(serde_json::from_value::<LightClientUpdate<T>>(value)
|
let update = LightClientUpdateAltair::from_ssz_bytes(bytes)?;
|
||||||
.map_err(serde::de::Error::custom))?
|
Self::Altair(update)
|
||||||
}
|
}
|
||||||
ForkName::Base | ForkName::Capella | ForkName::Deneb => {
|
ForkName::Capella => {
|
||||||
Err(serde::de::Error::custom(format!(
|
let update = LightClientUpdateCapella::from_ssz_bytes(bytes)?;
|
||||||
"LightClientUpdate failed to deserialize: unsupported fork '{}'",
|
Self::Capella(update)
|
||||||
fork_name
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let update = LightClientUpdateDeneb::from_ssz_bytes(bytes)?;
|
||||||
|
Self::Deneb(update)
|
||||||
|
}
|
||||||
|
ForkName::Base => {
|
||||||
|
return Err(ssz::DecodeError::BytesInvalid(format!(
|
||||||
|
"LightClientUpdate decoding for {fork_name} not implemented"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +267,7 @@ mod tests {
|
|||||||
use crate::MainnetEthSpec;
|
use crate::MainnetEthSpec;
|
||||||
use ssz_types::typenum::Unsigned;
|
use ssz_types::typenum::Unsigned;
|
||||||
|
|
||||||
ssz_tests!(LightClientUpdate<MainnetEthSpec>);
|
ssz_tests!(LightClientUpdateDeneb<MainnetEthSpec>);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn finalized_root_params() {
|
fn finalized_root_params() {
|
||||||
|
@ -20,7 +20,6 @@ macro_rules! ssz_tests {
|
|||||||
let original = <$type>::random_for_test(&mut rng);
|
let original = <$type>::random_for_test(&mut rng);
|
||||||
|
|
||||||
let bytes = ssz_encode(&original);
|
let bytes = ssz_encode(&original);
|
||||||
println!("bytes length: {}", bytes.len());
|
|
||||||
let decoded = <$type>::from_ssz_bytes(&bytes).unwrap();
|
let decoded = <$type>::from_ssz_bytes(&bytes).unwrap();
|
||||||
|
|
||||||
assert_eq!(original, decoded);
|
assert_eq!(original, decoded);
|
||||||
|
@ -29,18 +29,8 @@ excluded_paths = [
|
|||||||
"tests/.*/.*/light_client",
|
"tests/.*/.*/light_client",
|
||||||
# LightClientStore
|
# LightClientStore
|
||||||
"tests/.*/.*/ssz_static/LightClientStore",
|
"tests/.*/.*/ssz_static/LightClientStore",
|
||||||
# LightClientUpdate
|
|
||||||
"tests/.*/.*/ssz_static/LightClientUpdate",
|
|
||||||
# LightClientSnapshot
|
# LightClientSnapshot
|
||||||
"tests/.*/.*/ssz_static/LightClientSnapshot",
|
"tests/.*/.*/ssz_static/LightClientSnapshot",
|
||||||
# LightClientBootstrap
|
|
||||||
"tests/.*/.*/ssz_static/LightClientBootstrap",
|
|
||||||
# LightClientOptimistic
|
|
||||||
"tests/.*/.*/ssz_static/LightClientOptimistic",
|
|
||||||
# LightClientFinalityUpdate
|
|
||||||
"tests/.*/.*/ssz_static/LightClientFinalityUpdate",
|
|
||||||
# LightClientHeader
|
|
||||||
"tests/.*/.*/ssz_static/LightClientHeader",
|
|
||||||
# One of the EF researchers likes to pack the tarballs on a Mac
|
# One of the EF researchers likes to pack the tarballs on a Mac
|
||||||
".*\.DS_Store.*",
|
".*\.DS_Store.*",
|
||||||
# More Mac weirdness.
|
# More Mac weirdness.
|
||||||
|
@ -73,6 +73,38 @@ type_name!(Fork);
|
|||||||
type_name!(ForkData);
|
type_name!(ForkData);
|
||||||
type_name_generic!(HistoricalBatch);
|
type_name_generic!(HistoricalBatch);
|
||||||
type_name_generic!(IndexedAttestation);
|
type_name_generic!(IndexedAttestation);
|
||||||
|
type_name_generic!(LightClientBootstrap);
|
||||||
|
type_name_generic!(LightClientBootstrapAltair, "LightClientBootstrap");
|
||||||
|
type_name_generic!(LightClientBootstrapCapella, "LightClientBootstrap");
|
||||||
|
type_name_generic!(LightClientBootstrapDeneb, "LightClientBootstrap");
|
||||||
|
type_name_generic!(LightClientFinalityUpdate);
|
||||||
|
type_name_generic!(LightClientFinalityUpdateAltair, "LightClientFinalityUpdate");
|
||||||
|
type_name_generic!(
|
||||||
|
LightClientFinalityUpdateCapella,
|
||||||
|
"LightClientFinalityUpdate"
|
||||||
|
);
|
||||||
|
type_name_generic!(LightClientFinalityUpdateDeneb, "LightClientFinalityUpdate");
|
||||||
|
type_name_generic!(LightClientHeader);
|
||||||
|
type_name_generic!(LightClientHeaderDeneb, "LightClientHeader");
|
||||||
|
type_name_generic!(LightClientHeaderCapella, "LightClientHeader");
|
||||||
|
type_name_generic!(LightClientHeaderAltair, "LightClientHeader");
|
||||||
|
type_name_generic!(LightClientOptimisticUpdate);
|
||||||
|
type_name_generic!(
|
||||||
|
LightClientOptimisticUpdateAltair,
|
||||||
|
"LightClientOptimisticUpdate"
|
||||||
|
);
|
||||||
|
type_name_generic!(
|
||||||
|
LightClientOptimisticUpdateCapella,
|
||||||
|
"LightClientOptimisticUpdate"
|
||||||
|
);
|
||||||
|
type_name_generic!(
|
||||||
|
LightClientOptimisticUpdateDeneb,
|
||||||
|
"LightClientOptimisticUpdate"
|
||||||
|
);
|
||||||
|
type_name_generic!(LightClientUpdate);
|
||||||
|
type_name_generic!(LightClientUpdateAltair, "LightClientUpdate");
|
||||||
|
type_name_generic!(LightClientUpdateCapella, "LightClientUpdate");
|
||||||
|
type_name_generic!(LightClientUpdateDeneb, "LightClientUpdate");
|
||||||
type_name_generic!(PendingAttestation);
|
type_name_generic!(PendingAttestation);
|
||||||
type_name!(ProposerSlashing);
|
type_name!(ProposerSlashing);
|
||||||
type_name_generic!(SignedAggregateAndProof);
|
type_name_generic!(SignedAggregateAndProof);
|
||||||
|
@ -217,7 +217,7 @@ mod ssz_static {
|
|||||||
use ef_tests::{Handler, SszStaticHandler, SszStaticTHCHandler, SszStaticWithSpecHandler};
|
use ef_tests::{Handler, SszStaticHandler, SszStaticTHCHandler, SszStaticWithSpecHandler};
|
||||||
use types::blob_sidecar::BlobIdentifier;
|
use types::blob_sidecar::BlobIdentifier;
|
||||||
use types::historical_summary::HistoricalSummary;
|
use types::historical_summary::HistoricalSummary;
|
||||||
use types::*;
|
use types::{LightClientBootstrapAltair, *};
|
||||||
|
|
||||||
ssz_static_test!(aggregate_and_proof, AggregateAndProof<_>);
|
ssz_static_test!(aggregate_and_proof, AggregateAndProof<_>);
|
||||||
ssz_static_test!(attestation, Attestation<_>);
|
ssz_static_test!(attestation, Attestation<_>);
|
||||||
@ -236,7 +236,6 @@ mod ssz_static {
|
|||||||
ssz_static_test!(fork_data, ForkData);
|
ssz_static_test!(fork_data, ForkData);
|
||||||
ssz_static_test!(historical_batch, HistoricalBatch<_>);
|
ssz_static_test!(historical_batch, HistoricalBatch<_>);
|
||||||
ssz_static_test!(indexed_attestation, IndexedAttestation<_>);
|
ssz_static_test!(indexed_attestation, IndexedAttestation<_>);
|
||||||
// NOTE: LightClient* intentionally omitted
|
|
||||||
ssz_static_test!(pending_attestation, PendingAttestation<_>);
|
ssz_static_test!(pending_attestation, PendingAttestation<_>);
|
||||||
ssz_static_test!(proposer_slashing, ProposerSlashing);
|
ssz_static_test!(proposer_slashing, ProposerSlashing);
|
||||||
ssz_static_test!(signed_aggregate_and_proof, SignedAggregateAndProof<_>);
|
ssz_static_test!(signed_aggregate_and_proof, SignedAggregateAndProof<_>);
|
||||||
@ -250,7 +249,6 @@ mod ssz_static {
|
|||||||
ssz_static_test!(signing_data, SigningData);
|
ssz_static_test!(signing_data, SigningData);
|
||||||
ssz_static_test!(validator, Validator);
|
ssz_static_test!(validator, Validator);
|
||||||
ssz_static_test!(voluntary_exit, VoluntaryExit);
|
ssz_static_test!(voluntary_exit, VoluntaryExit);
|
||||||
|
|
||||||
// BeaconBlockBody has no internal indicator of which fork it is for, so we test it separately.
|
// BeaconBlockBody has no internal indicator of which fork it is for, so we test it separately.
|
||||||
#[test]
|
#[test]
|
||||||
fn beacon_block_body() {
|
fn beacon_block_body() {
|
||||||
@ -285,6 +283,135 @@ mod ssz_static {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LightClientBootstrap has no internal indicator of which fork it is for, so we test it separately.
|
||||||
|
#[test]
|
||||||
|
fn light_client_bootstrap() {
|
||||||
|
SszStaticHandler::<LightClientBootstrapAltair<MinimalEthSpec>, MinimalEthSpec>::altair_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientBootstrapAltair<MainnetEthSpec>, MainnetEthSpec>::altair_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientBootstrapAltair<MinimalEthSpec>, MinimalEthSpec>::merge_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientBootstrapAltair<MainnetEthSpec>, MainnetEthSpec>::merge_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientBootstrapCapella<MinimalEthSpec>, MinimalEthSpec>::capella_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientBootstrapCapella<MainnetEthSpec>, MainnetEthSpec>::capella_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientBootstrapDeneb<MinimalEthSpec>, MinimalEthSpec>::deneb_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientBootstrapDeneb<MainnetEthSpec>, MainnetEthSpec>::deneb_only()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LightClientHeader has no internal indicator of which fork it is for, so we test it separately.
|
||||||
|
#[test]
|
||||||
|
fn light_client_header() {
|
||||||
|
SszStaticHandler::<LightClientHeaderAltair<MinimalEthSpec>, MinimalEthSpec>::altair_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientHeaderAltair<MainnetEthSpec>, MainnetEthSpec>::altair_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientHeaderAltair<MinimalEthSpec>, MinimalEthSpec>::merge_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientHeaderAltair<MainnetEthSpec>, MainnetEthSpec>::merge_only()
|
||||||
|
.run();
|
||||||
|
|
||||||
|
SszStaticHandler::<LightClientHeaderCapella<MinimalEthSpec>, MinimalEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientHeaderCapella<MainnetEthSpec>, MainnetEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
SszStaticHandler::<LightClientHeaderDeneb<MinimalEthSpec>, MinimalEthSpec>::deneb_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientHeaderDeneb<MainnetEthSpec>, MainnetEthSpec>::deneb_only()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LightClientOptimisticUpdate has no internal indicator of which fork it is for, so we test it separately.
|
||||||
|
#[test]
|
||||||
|
fn light_client_optimistic_update() {
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateAltair<MinimalEthSpec>, MinimalEthSpec>::altair_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateAltair<MainnetEthSpec>, MainnetEthSpec>::altair_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateAltair<MinimalEthSpec>, MinimalEthSpec>::merge_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateAltair<MainnetEthSpec>, MainnetEthSpec>::merge_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateCapella<MinimalEthSpec>, MinimalEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateCapella<MainnetEthSpec>, MainnetEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateDeneb<MinimalEthSpec>, MinimalEthSpec>::deneb_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientOptimisticUpdateDeneb<MainnetEthSpec>, MainnetEthSpec>::deneb_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LightClientFinalityUpdate has no internal indicator of which fork it is for, so we test it separately.
|
||||||
|
#[test]
|
||||||
|
fn light_client_finality_update() {
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateAltair<MinimalEthSpec>, MinimalEthSpec>::altair_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateAltair<MainnetEthSpec>, MainnetEthSpec>::altair_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateAltair<MinimalEthSpec>, MinimalEthSpec>::merge_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateAltair<MainnetEthSpec>, MainnetEthSpec>::merge_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateCapella<MinimalEthSpec>, MinimalEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateCapella<MainnetEthSpec>, MainnetEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateDeneb<MinimalEthSpec>, MinimalEthSpec>::deneb_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientFinalityUpdateDeneb<MainnetEthSpec>, MainnetEthSpec>::deneb_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LightClientUpdate has no internal indicator of which fork it is for, so we test it separately.
|
||||||
|
#[test]
|
||||||
|
fn light_client_update() {
|
||||||
|
SszStaticHandler::<LightClientUpdateAltair<MinimalEthSpec>, MinimalEthSpec>::altair_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientUpdateAltair<MainnetEthSpec>, MainnetEthSpec>::altair_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientUpdateAltair<MinimalEthSpec>, MinimalEthSpec>::merge_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientUpdateAltair<MainnetEthSpec>, MainnetEthSpec>::merge_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientUpdateCapella<MinimalEthSpec>, MinimalEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientUpdateCapella<MainnetEthSpec>, MainnetEthSpec>::capella_only(
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientUpdateDeneb<MinimalEthSpec>, MinimalEthSpec>::deneb_only()
|
||||||
|
.run();
|
||||||
|
SszStaticHandler::<LightClientUpdateDeneb<MainnetEthSpec>, MainnetEthSpec>::deneb_only()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn signed_contribution_and_proof() {
|
fn signed_contribution_and_proof() {
|
||||||
SszStaticHandler::<SignedContributionAndProof<MinimalEthSpec>, MinimalEthSpec>::altair_and_later().run();
|
SszStaticHandler::<SignedContributionAndProof<MinimalEthSpec>, MinimalEthSpec>::altair_and_later().run();
|
||||||
|
@ -287,13 +287,13 @@ pub(crate) async fn verify_light_client_updates<E: EthSpec>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify light client optimistic update. `signature_slot_distance` should be 1 in the ideal scenario.
|
// Verify light client optimistic update. `signature_slot_distance` should be 1 in the ideal scenario.
|
||||||
let signature_slot = client
|
let signature_slot = *client
|
||||||
.get_beacon_light_client_optimistic_update::<E>()
|
.get_beacon_light_client_optimistic_update::<E>()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("Error while getting light client updates: {:?}", e))?
|
.map_err(|e| format!("Error while getting light client updates: {:?}", e))?
|
||||||
.ok_or(format!("Light client optimistic update not found {slot:?}"))?
|
.ok_or(format!("Light client optimistic update not found {slot:?}"))?
|
||||||
.data
|
.data
|
||||||
.signature_slot;
|
.signature_slot();
|
||||||
let signature_slot_distance = slot - signature_slot;
|
let signature_slot_distance = slot - signature_slot;
|
||||||
if signature_slot_distance > light_client_update_slot_tolerance {
|
if signature_slot_distance > light_client_update_slot_tolerance {
|
||||||
return Err(format!("Existing optimistic update too old: signature slot {signature_slot}, current slot {slot:?}"));
|
return Err(format!("Existing optimistic update too old: signature slot {signature_slot}, current slot {slot:?}"));
|
||||||
@ -316,13 +316,13 @@ pub(crate) async fn verify_light_client_updates<E: EthSpec>(
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let signature_slot = client
|
let signature_slot = *client
|
||||||
.get_beacon_light_client_finality_update::<E>()
|
.get_beacon_light_client_finality_update::<E>()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("Error while getting light client updates: {:?}", e))?
|
.map_err(|e| format!("Error while getting light client updates: {:?}", e))?
|
||||||
.ok_or(format!("Light client finality update not found {slot:?}"))?
|
.ok_or(format!("Light client finality update not found {slot:?}"))?
|
||||||
.data
|
.data
|
||||||
.signature_slot;
|
.signature_slot();
|
||||||
let signature_slot_distance = slot - signature_slot;
|
let signature_slot_distance = slot - signature_slot;
|
||||||
if signature_slot_distance > light_client_update_slot_tolerance {
|
if signature_slot_distance > light_client_update_slot_tolerance {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
|
Loading…
Reference in New Issue
Block a user