Skip to content

Commit 68dc406

Browse files
dappliondanielrachi1
authored andcommitted
Compute recent lightclient updates (sigp#4969)
* Compute recent lightclient updates * Review PR * Merge remote-tracking branch 'upstream/unstable' into lc-prod-recent-updates * Review PR * consistent naming * add metrics * revert dropping reprocessing queue * Update light client optimistic update re-processing logic. (sigp#7) * Add light client server simulator tests. Co-authored by @dapplion. * Merge branch 'unstable' into fork/dapplion/lc-prod-recent-updates * Fix lint * Enable light client server in simulator test. * Fix test for light client optimistic updates and finality updates.
1 parent 0c6e373 commit 68dc406

File tree

27 files changed

+609
-195
lines changed

27 files changed

+609
-195
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use crate::light_client_finality_update_verification::{
3838
use crate::light_client_optimistic_update_verification::{
3939
Error as LightClientOptimisticUpdateError, VerifiedLightClientOptimisticUpdate,
4040
};
41+
use crate::light_client_server_cache::LightClientServerCache;
4142
use crate::migrate::BackgroundMigrator;
4243
use crate::naive_aggregation_pool::{
4344
AggregatedAttestationMap, Error as NaiveAggregationError, NaiveAggregationPool,
@@ -339,6 +340,8 @@ struct PartialBeaconBlock<E: EthSpec> {
339340
bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
340341
}
341342

343+
pub type LightClientProducerEvent<T> = (Hash256, Slot, SyncAggregate<T>);
344+
342345
pub type BeaconForkChoice<T> = ForkChoice<
343346
BeaconForkChoiceStore<
344347
<T as BeaconChainTypes>::EthSpec,
@@ -420,10 +423,6 @@ pub struct BeaconChain<T: BeaconChainTypes> {
420423
/// Maintains a record of which validators we've seen BLS to execution changes for.
421424
pub(crate) observed_bls_to_execution_changes:
422425
Mutex<ObservedOperations<SignedBlsToExecutionChange, T::EthSpec>>,
423-
/// The most recently validated light client finality update received on gossip.
424-
pub latest_seen_finality_update: Mutex<Option<LightClientFinalityUpdate<T::EthSpec>>>,
425-
/// The most recently validated light client optimistic update received on gossip.
426-
pub latest_seen_optimistic_update: Mutex<Option<LightClientOptimisticUpdate<T::EthSpec>>>,
427426
/// Provides information from the Ethereum 1 (PoW) chain.
428427
pub eth1_chain: Option<Eth1Chain<T::Eth1Chain, T::EthSpec>>,
429428
/// Interfaces with the execution client.
@@ -466,6 +465,10 @@ pub struct BeaconChain<T: BeaconChainTypes> {
466465
pub block_times_cache: Arc<RwLock<BlockTimesCache>>,
467466
/// A cache used to track pre-finalization block roots for quick rejection.
468467
pub pre_finalization_block_cache: PreFinalizationBlockCache,
468+
/// A cache used to produce light_client server messages
469+
pub light_client_server_cache: LightClientServerCache<T>,
470+
/// Sender to signal the light_client server to produce new updates
471+
pub light_client_server_tx: Option<Sender<LightClientProducerEvent<T::EthSpec>>>,
469472
/// Sender given to tasks, so that if they encounter a state in which execution cannot
470473
/// continue they can request that everything shuts down.
471474
pub shutdown_sender: Sender<ShutdownReason>,
@@ -1344,6 +1347,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
13441347
self.state_at_slot(load_slot, StateSkipConfig::WithoutStateRoots)
13451348
}
13461349

1350+
pub fn recompute_and_cache_light_client_updates(
1351+
&self,
1352+
(parent_root, slot, sync_aggregate): LightClientProducerEvent<T::EthSpec>,
1353+
) -> Result<(), Error> {
1354+
self.light_client_server_cache.recompute_and_cache_updates(
1355+
&self.log,
1356+
self.store.clone(),
1357+
&parent_root,
1358+
slot,
1359+
&sync_aggregate,
1360+
)
1361+
}
1362+
13471363
/// Returns the current heads of the `BeaconChain`. For the canonical head, see `Self::head`.
13481364
///
13491365
/// Returns `(block_root, block_slot)`.
@@ -3521,6 +3537,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
35213537
};
35223538
let current_finalized_checkpoint = state.finalized_checkpoint();
35233539

3540+
// compute state proofs for light client updates before inserting the state into the
3541+
// snapshot cache.
3542+
if self.config.enable_light_client_server {
3543+
self.light_client_server_cache
3544+
.cache_state_data(
3545+
&self.spec, block, block_root,
3546+
// mutable reference on the state is needed to compute merkle proofs
3547+
&mut state,
3548+
)
3549+
.unwrap_or_else(|e| {
3550+
error!(self.log, "error caching light_client data {:?}", e);
3551+
});
3552+
}
3553+
35243554
self.snapshot_cache
35253555
.try_write_for(BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT)
35263556
.ok_or(Error::SnapshotCacheLockTimeout)
@@ -3893,6 +3923,28 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
38933923
}));
38943924
}
38953925
}
3926+
3927+
// Do not trigger light_client server update producer for old blocks, to extra work
3928+
// during sync.
3929+
if self.config.enable_light_client_server
3930+
&& block_delay_total < self.slot_clock.slot_duration() * 32
3931+
{
3932+
if let Some(mut light_client_server_tx) = self.light_client_server_tx.clone() {
3933+
if let Ok(sync_aggregate) = block.body().sync_aggregate() {
3934+
if let Err(e) = light_client_server_tx.try_send((
3935+
block.parent_root(),
3936+
block.slot(),
3937+
sync_aggregate.clone(),
3938+
)) {
3939+
warn!(
3940+
self.log,
3941+
"Failed to send light_client server event";
3942+
"error" => ?e
3943+
);
3944+
}
3945+
}
3946+
}
3947+
}
38963948
}
38973949

38983950
// For the current and next epoch of this state, ensure we have the shuffling from this

beacon_node/beacon_chain/src/builder.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
use crate::beacon_chain::{CanonicalHead, BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, OP_POOL_DB_KEY};
1+
use crate::beacon_chain::{
2+
CanonicalHead, LightClientProducerEvent, BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, OP_POOL_DB_KEY,
3+
};
24
use crate::beacon_proposer_cache::BeaconProposerCache;
35
use crate::data_availability_checker::DataAvailabilityChecker;
46
use crate::eth1_chain::{CachingEth1Backend, SszEth1};
57
use crate::eth1_finalization_cache::Eth1FinalizationCache;
68
use crate::fork_choice_signal::ForkChoiceSignalTx;
79
use crate::fork_revert::{reset_fork_choice_to_finalization, revert_to_fork_boundary};
810
use crate::head_tracker::HeadTracker;
11+
use crate::light_client_server_cache::LightClientServerCache;
912
use crate::migrate::{BackgroundMigrator, MigratorConfig};
1013
use crate::persisted_beacon_chain::PersistedBeaconChain;
1114
use crate::shuffling_cache::{BlockShufflingIds, ShufflingCache};
@@ -87,6 +90,7 @@ pub struct BeaconChainBuilder<T: BeaconChainTypes> {
8790
event_handler: Option<ServerSentEventHandler<T::EthSpec>>,
8891
slot_clock: Option<T::SlotClock>,
8992
shutdown_sender: Option<Sender<ShutdownReason>>,
93+
light_client_server_tx: Option<Sender<LightClientProducerEvent<T::EthSpec>>>,
9094
head_tracker: Option<HeadTracker>,
9195
validator_pubkey_cache: Option<ValidatorPubkeyCache<T>>,
9296
spec: ChainSpec,
@@ -129,6 +133,7 @@ where
129133
event_handler: None,
130134
slot_clock: None,
131135
shutdown_sender: None,
136+
light_client_server_tx: None,
132137
head_tracker: None,
133138
validator_pubkey_cache: None,
134139
spec: TEthSpec::default_spec(),
@@ -603,6 +608,15 @@ where
603608
self
604609
}
605610

611+
/// Sets a `Sender` to allow the beacon chain to trigger light_client update production.
612+
pub fn light_client_server_tx(
613+
mut self,
614+
sender: Sender<LightClientProducerEvent<TEthSpec>>,
615+
) -> Self {
616+
self.light_client_server_tx = Some(sender);
617+
self
618+
}
619+
606620
/// Creates a new, empty operation pool.
607621
fn empty_op_pool(mut self) -> Self {
608622
self.op_pool = Some(OperationPool::new());
@@ -887,8 +901,6 @@ where
887901
observed_proposer_slashings: <_>::default(),
888902
observed_attester_slashings: <_>::default(),
889903
observed_bls_to_execution_changes: <_>::default(),
890-
latest_seen_finality_update: <_>::default(),
891-
latest_seen_optimistic_update: <_>::default(),
892904
eth1_chain: self.eth1_chain,
893905
execution_layer: self.execution_layer,
894906
genesis_validators_root,
@@ -916,6 +928,8 @@ where
916928
validator_pubkey_cache: TimeoutRwLock::new(validator_pubkey_cache),
917929
attester_cache: <_>::default(),
918930
early_attester_cache: <_>::default(),
931+
light_client_server_cache: LightClientServerCache::new(),
932+
light_client_server_tx: self.light_client_server_tx,
919933
shutdown_sender: self
920934
.shutdown_sender
921935
.ok_or("Cannot build without a shutdown sender.")?,

beacon_node/beacon_chain/src/chain_config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ pub struct ChainConfig {
8383
pub progressive_balances_mode: ProgressiveBalancesMode,
8484
/// Number of epochs between each migration of data from the hot database to the freezer.
8585
pub epochs_per_migration: u64,
86+
/// When set to true Light client server computes and caches state proofs for serving updates
87+
pub enable_light_client_server: bool,
8688
}
8789

8890
impl Default for ChainConfig {
@@ -114,6 +116,7 @@ impl Default for ChainConfig {
114116
always_prepare_payload: false,
115117
progressive_balances_mode: ProgressiveBalancesMode::Fast,
116118
epochs_per_migration: crate::migrate::DEFAULT_EPOCHS_PER_MIGRATION,
119+
enable_light_client_server: false,
117120
}
118121
}
119122
}

beacon_node/beacon_chain/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod historical_blocks;
3232
pub mod kzg_utils;
3333
pub mod light_client_finality_update_verification;
3434
pub mod light_client_optimistic_update_verification;
35+
mod light_client_server_cache;
3536
pub mod merge_readiness;
3637
pub mod metrics;
3738
pub mod migrate;
@@ -61,8 +62,8 @@ pub mod validator_pubkey_cache;
6162
pub use self::beacon_chain::{
6263
AttestationProcessingOutcome, AvailabilityProcessingStatus, BeaconBlockResponse,
6364
BeaconBlockResponseWrapper, BeaconChain, BeaconChainTypes, BeaconStore, ChainSegmentResult,
64-
ForkChoiceError, OverrideForkchoiceUpdate, ProduceBlockVerification, StateSkipConfig,
65-
WhenSlotSkipped, INVALID_FINALIZED_MERGE_TRANSITION_BLOCK_SHUTDOWN_REASON,
65+
ForkChoiceError, LightClientProducerEvent, OverrideForkchoiceUpdate, ProduceBlockVerification,
66+
StateSkipConfig, WhenSlotSkipped, INVALID_FINALIZED_MERGE_TRANSITION_BLOCK_SHUTDOWN_REASON,
6667
INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON,
6768
};
6869
pub use self::beacon_snapshot::BeaconSnapshot;

beacon_node/beacon_chain/src/light_client_finality_update_verification.rs

Lines changed: 18 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes};
1+
use crate::{BeaconChain, BeaconChainTypes};
22
use derivative::Derivative;
33
use slot_clock::SlotClock;
44
use std::time::Duration;
55
use strum::AsRefStr;
6-
use types::{
7-
light_client_update::Error as LightClientUpdateError, LightClientFinalityUpdate, Slot,
8-
};
6+
use types::LightClientFinalityUpdate;
97

108
/// Returned when a light client finality update was not successfully verified. It might not have been verified for
119
/// two reasons:
@@ -16,8 +14,6 @@ use types::{
1614
/// (the `BeaconChainError` variant)
1715
#[derive(Debug, AsRefStr)]
1816
pub enum Error {
19-
/// Light client finality update message with a lower or equal finalized_header slot already forwarded.
20-
FinalityUpdateAlreadySeen,
2117
/// The light client finality message was received is prior to one-third of slot duration passage. (with
2218
/// respect to the gossip clock disparity and slot clock duration).
2319
///
@@ -26,29 +22,11 @@ pub enum Error {
2622
/// Assuming the local clock is correct, the peer has sent an invalid message.
2723
TooEarly,
2824
/// Light client finality update message does not match the locally constructed one.
29-
///
30-
/// ## Peer Scoring
31-
///
3225
InvalidLightClientFinalityUpdate,
3326
/// Signature slot start time is none.
3427
SigSlotStartIsNone,
3528
/// Failed to construct a LightClientFinalityUpdate from state.
3629
FailedConstructingUpdate,
37-
/// Beacon chain error occurred.
38-
BeaconChainError(BeaconChainError),
39-
LightClientUpdateError(LightClientUpdateError),
40-
}
41-
42-
impl From<BeaconChainError> for Error {
43-
fn from(e: BeaconChainError) -> Self {
44-
Error::BeaconChainError(e)
45-
}
46-
}
47-
48-
impl From<LightClientUpdateError> for Error {
49-
fn from(e: LightClientUpdateError) -> Self {
50-
Error::LightClientUpdateError(e)
51-
}
5230
}
5331

5432
/// Wraps a `LightClientFinalityUpdate` that has been verified for propagation on the gossip network.
@@ -63,71 +41,34 @@ impl<T: BeaconChainTypes> VerifiedLightClientFinalityUpdate<T> {
6341
/// Returns `Ok(Self)` if the `light_client_finality_update` is valid to be (re)published on the gossip
6442
/// network.
6543
pub fn verify(
66-
light_client_finality_update: LightClientFinalityUpdate<T::EthSpec>,
44+
rcv_finality_update: LightClientFinalityUpdate<T::EthSpec>,
6745
chain: &BeaconChain<T>,
6846
seen_timestamp: Duration,
6947
) -> Result<Self, Error> {
70-
let gossiped_finality_slot = light_client_finality_update.finalized_header.beacon.slot;
71-
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
72-
let signature_slot = light_client_finality_update.signature_slot;
73-
let start_time = chain.slot_clock.start_of(signature_slot);
74-
let mut latest_seen_finality_update = chain.latest_seen_finality_update.lock();
75-
76-
let head = chain.canonical_head.cached_head();
77-
let head_block = &head.snapshot.beacon_block;
78-
let attested_block_root = head_block.message().parent_root();
79-
let attested_block = chain
80-
.get_blinded_block(&attested_block_root)?
81-
.ok_or(Error::FailedConstructingUpdate)?;
82-
let mut attested_state = chain
83-
.get_state(&attested_block.state_root(), Some(attested_block.slot()))?
84-
.ok_or(Error::FailedConstructingUpdate)?;
85-
86-
let finalized_block_root = attested_state.finalized_checkpoint().root;
87-
let finalized_block = chain
88-
.get_blinded_block(&finalized_block_root)?
89-
.ok_or(Error::FailedConstructingUpdate)?;
90-
let latest_seen_finality_update_slot = match latest_seen_finality_update.as_ref() {
91-
Some(update) => update.finalized_header.beacon.slot,
92-
None => Slot::new(0),
93-
};
94-
95-
// verify that no other finality_update with a lower or equal
96-
// finalized_header.slot was already forwarded on the network
97-
if gossiped_finality_slot <= latest_seen_finality_update_slot {
98-
return Err(Error::FinalityUpdateAlreadySeen);
99-
}
100-
10148
// verify that enough time has passed for the block to have been propagated
102-
match start_time {
103-
Some(time) => {
104-
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
105-
< time + one_third_slot_duration
106-
{
107-
return Err(Error::TooEarly);
108-
}
109-
}
110-
None => return Err(Error::SigSlotStartIsNone),
49+
let start_time = chain
50+
.slot_clock
51+
.start_of(rcv_finality_update.signature_slot)
52+
.ok_or(Error::SigSlotStartIsNone)?;
53+
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
54+
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
55+
< start_time + one_third_slot_duration
56+
{
57+
return Err(Error::TooEarly);
11158
}
11259

113-
let head_state = &head.snapshot.beacon_state;
114-
let finality_update = LightClientFinalityUpdate::new(
115-
&chain.spec,
116-
head_state,
117-
head_block,
118-
&mut attested_state,
119-
&finalized_block,
120-
)?;
60+
let latest_finality_update = chain
61+
.light_client_server_cache
62+
.get_latest_finality_update()
63+
.ok_or(Error::FailedConstructingUpdate)?;
12164

12265
// verify that the gossiped finality update is the same as the locally constructed one.
123-
if finality_update != light_client_finality_update {
66+
if latest_finality_update != rcv_finality_update {
12467
return Err(Error::InvalidLightClientFinalityUpdate);
12568
}
12669

127-
*latest_seen_finality_update = Some(light_client_finality_update.clone());
128-
12970
Ok(Self {
130-
light_client_finality_update,
71+
light_client_finality_update: rcv_finality_update,
13172
seen_timestamp,
13273
})
13374
}

0 commit comments

Comments
 (0)