Skip to content

Commit

Permalink
Compute recent lightclient updates
Browse files Browse the repository at this point in the history
  • Loading branch information
lion authored and dapplion committed Nov 15, 2023
1 parent dfcb336 commit 23fdb20
Show file tree
Hide file tree
Showing 15 changed files with 619 additions and 184 deletions.
76 changes: 71 additions & 5 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::light_client_finality_update_verification::{
use crate::light_client_optimistic_update_verification::{
Error as LightClientOptimisticUpdateError, VerifiedLightClientOptimisticUpdate,
};
use crate::lightclient_proofs_cache::LightclientServerCache;
use crate::migrate::BackgroundMigrator;
use crate::naive_aggregation_pool::{
AggregatedAttestationMap, Error as NaiveAggregationError, NaiveAggregationPool,
Expand Down Expand Up @@ -106,6 +107,8 @@ use task_executor::{ShutdownReason, TaskExecutor};
use tokio_stream::Stream;
use tree_hash::TreeHash;
use types::beacon_state::CloneConfig;
use types::light_client_bootstrap::LightClientBootstrap;
use types::light_client_update::LightClientUpdate;
use types::*;

pub type ForkChoiceError = fork_choice::Error<crate::ForkChoiceStoreError>;
Expand Down Expand Up @@ -286,6 +289,8 @@ struct PartialBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E>> {
bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
}

pub type LightclientProducerEvent<T: EthSpec> = (Hash256, Slot, SyncAggregate<T>);

pub type BeaconForkChoice<T> = ForkChoice<
BeaconForkChoiceStore<
<T as BeaconChainTypes>::EthSpec,
Expand Down Expand Up @@ -363,10 +368,6 @@ pub struct BeaconChain<T: BeaconChainTypes> {
/// Maintains a record of which validators we've seen BLS to execution changes for.
pub(crate) observed_bls_to_execution_changes:
Mutex<ObservedOperations<SignedBlsToExecutionChange, T::EthSpec>>,
/// The most recently validated light client finality update received on gossip.
pub latest_seen_finality_update: Mutex<Option<LightClientFinalityUpdate<T::EthSpec>>>,
/// The most recently validated light client optimistic update received on gossip.
pub latest_seen_optimistic_update: Mutex<Option<LightClientOptimisticUpdate<T::EthSpec>>>,
/// Provides information from the Ethereum 1 (PoW) chain.
pub eth1_chain: Option<Eth1Chain<T::Eth1Chain, T::EthSpec>>,
/// Interfaces with the execution client.
Expand Down Expand Up @@ -409,6 +410,10 @@ pub struct BeaconChain<T: BeaconChainTypes> {
pub block_times_cache: Arc<RwLock<BlockTimesCache>>,
/// A cache used to track pre-finalization block roots for quick rejection.
pub pre_finalization_block_cache: PreFinalizationBlockCache,
/// A cache used to produce lightclient server messages
pub lightclient_server_cache: LightclientServerCache<T>,
/// Sender to signal the lightclient server to produce new updates
pub lightclient_server_tx: Option<Sender<LightclientProducerEvent<T::EthSpec>>>,
/// Sender given to tasks, so that if they encounter a state in which execution cannot
/// continue they can request that everything shuts down.
pub shutdown_sender: Sender<ShutdownReason>,
Expand Down Expand Up @@ -1172,6 +1177,41 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.state_at_slot(load_slot, StateSkipConfig::WithoutStateRoots)
}

/// Returns a `LightclientBootstrap` message at `block_root`. Attempts to reconstruct the
/// message from pre-calculated branches first.
pub fn get_lightclient_bootstrap(
&self,
block_root: Hash256,
) -> Result<Option<LightClientBootstrap<T::EthSpec>>, Error> {
self.lightclient_server_cache
.produce_bootstrap(self.store.clone(), &self.spec, block_root)
}

/// Returns the best `LightclientUpdate` available given the rules of `is_better_update()`.
/// For historical updates, only returns pre-computed messages.
pub fn get_lightclient_update(
&self,
sync_committee_period: u64,
) -> Result<LightClientUpdate<T::EthSpec>, Error> {
self.lightclient_server_cache
.produce_update(self.store.clone(), sync_committee_period)
}

pub fn recompute_and_cache_lightclient_updates(
&self,
block_parent_root: &Hash256,
block_slot: Slot,
block_sync_aggregate: &SyncAggregate<T::EthSpec>,
) -> Result<(), Error> {
self.lightclient_server_cache.recompute_and_cache_updates(
self.store.clone(),
&self.spec,
block_parent_root,
block_slot,
block_sync_aggregate,
)
}

/// Returns the current heads of the `BeaconChain`. For the canonical head, see `Self::head`.
///
/// Returns `(block_root, block_slot)`.
Expand Down Expand Up @@ -3053,6 +3093,22 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
};
let current_finalized_checkpoint = state.finalized_checkpoint();

// compute state proofs for light client updates before inserting the state into the
// snapshot cache.
self.lightclient_server_cache
.cache_state_data(
self.store.clone(),
&self.spec,
block,
block_root,
// mutable reference on the state is needed to compute merkle proofs
&mut state,
parent_block.slot(),
)
.unwrap_or_else(|e| {
error!(self.log, "error caching lightclient data {:?}", e);
});

self.snapshot_cache
.try_write_for(BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT)
.ok_or(Error::SnapshotCacheLockTimeout)
Expand Down Expand Up @@ -3425,6 +3481,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}));
}
}

if let Some(lightclient_server_tx) = self.lightclient_server_tx {
if let Ok(sync_aggregate) = block.body().sync_aggregate() {
lightclient_server_tx.try_send((
block.parent_root(),
block.slot(),
sync_aggregate.clone(),
));
}
}
}

// For the current and next epoch of this state, ensure we have the shuffling from this
Expand Down Expand Up @@ -5949,4 +6015,4 @@ impl<T: EthSpec> ChainSegmentResult<T> {
ChainSegmentResult::Successful { .. } => Ok(()),
}
}
}
}
22 changes: 18 additions & 4 deletions beacon_node/beacon_chain/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use crate::beacon_chain::{CanonicalHead, BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, OP_POOL_DB_KEY};
use crate::beacon_chain::{
CanonicalHead, LightclientProducerEvent, BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, OP_POOL_DB_KEY,
};
use crate::eth1_chain::{CachingEth1Backend, SszEth1};
use crate::eth1_finalization_cache::Eth1FinalizationCache;
use crate::fork_choice_signal::ForkChoiceSignalTx;
use crate::fork_revert::{reset_fork_choice_to_finalization, revert_to_fork_boundary};
use crate::head_tracker::HeadTracker;
use crate::lightclient_proofs_cache::LightclientServerCache;
use crate::migrate::{BackgroundMigrator, MigratorConfig};
use crate::persisted_beacon_chain::PersistedBeaconChain;
use crate::shuffling_cache::{BlockShufflingIds, ShufflingCache};
Expand Down Expand Up @@ -83,6 +86,7 @@ pub struct BeaconChainBuilder<T: BeaconChainTypes> {
event_handler: Option<ServerSentEventHandler<T::EthSpec>>,
slot_clock: Option<T::SlotClock>,
shutdown_sender: Option<Sender<ShutdownReason>>,
lightclient_server_tx: Option<Sender<LightclientProducerEvent<T::EthSpec>>>,
head_tracker: Option<HeadTracker>,
validator_pubkey_cache: Option<ValidatorPubkeyCache<T>>,
spec: ChainSpec,
Expand Down Expand Up @@ -124,6 +128,7 @@ where
event_handler: None,
slot_clock: None,
shutdown_sender: None,
lightclient_server_tx: None,
head_tracker: None,
validator_pubkey_cache: None,
spec: TEthSpec::default_spec(),
Expand Down Expand Up @@ -560,6 +565,15 @@ where
self
}

/// Sets a `Sender` to allow the beacon chain to send shutdown signals.
pub fn lightclient_server_tx(
mut self,
sender: Sender<LightclientProducerEvent<TEthSpec>>,
) -> Self {
self.lightclient_server_tx = Some(sender);
self
}

/// Creates a new, empty operation pool.
fn empty_op_pool(mut self) -> Self {
self.op_pool = Some(OperationPool::new());
Expand Down Expand Up @@ -832,8 +846,6 @@ where
observed_proposer_slashings: <_>::default(),
observed_attester_slashings: <_>::default(),
observed_bls_to_execution_changes: <_>::default(),
latest_seen_finality_update: <_>::default(),
latest_seen_optimistic_update: <_>::default(),
eth1_chain: self.eth1_chain,
execution_layer: self.execution_layer,
genesis_validators_root,
Expand Down Expand Up @@ -861,6 +873,8 @@ where
validator_pubkey_cache: TimeoutRwLock::new(validator_pubkey_cache),
attester_cache: <_>::default(),
early_attester_cache: <_>::default(),
lightclient_server_cache: LightclientServerCache::new(),
lightclient_server_tx: self.lightclient_server_tx,
shutdown_sender: self
.shutdown_sender
.ok_or("Cannot build without a shutdown sender.")?,
Expand Down Expand Up @@ -1192,4 +1206,4 @@ mod test {
"validator count should be correct"
);
}
}
}
3 changes: 2 additions & 1 deletion beacon_node/beacon_chain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod head_tracker;
pub mod historical_blocks;
pub mod light_client_finality_update_verification;
pub mod light_client_optimistic_update_verification;
mod lightclient_proofs_cache;
pub mod merge_readiness;
pub mod metrics;
pub mod migrate;
Expand Down Expand Up @@ -81,4 +82,4 @@ pub use state_processing::per_block_processing::errors::{
};
pub use store;
pub use timeout_rw_lock::TimeoutRwLock;
pub use types;
pub use types;
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ use derivative::Derivative;
use slot_clock::SlotClock;
use std::time::Duration;
use strum::AsRefStr;
use types::{
light_client_update::Error as LightClientUpdateError, LightClientFinalityUpdate, Slot,
};
use types::{light_client_update::Error as LightClientUpdateError, LightClientFinalityUpdate};

/// Returned when a light client finality update was not successfully verified. It might not have been verified for
/// two reasons:
Expand Down Expand Up @@ -65,42 +63,27 @@ impl<T: BeaconChainTypes> VerifiedLightClientFinalityUpdate<T> {
/// Returns `Ok(Self)` if the `light_client_finality_update` is valid to be (re)published on the gossip
/// network.
pub fn verify(
light_client_finality_update: LightClientFinalityUpdate<T::EthSpec>,
rcv_finality_update: LightClientFinalityUpdate<T::EthSpec>,
chain: &BeaconChain<T>,
seen_timestamp: Duration,
) -> Result<Self, Error> {
let gossiped_finality_slot = light_client_finality_update.finalized_header.slot;
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
let signature_slot = light_client_finality_update.signature_slot;
let start_time = chain.slot_clock.start_of(signature_slot);
let mut latest_seen_finality_update = chain.latest_seen_finality_update.lock();

let head = chain.canonical_head.cached_head();
let head_block = &head.snapshot.beacon_block;
let attested_block_root = head_block.message().parent_root();
let attested_block = chain
.get_blinded_block(&attested_block_root)?
.ok_or(Error::FailedConstructingUpdate)?;
let mut attested_state = chain
.get_state(&attested_block.state_root(), Some(attested_block.slot()))?
.ok_or(Error::FailedConstructingUpdate)?;

let finalized_block_root = attested_state.finalized_checkpoint().root;
let finalized_block = chain
.get_blinded_block(&finalized_block_root)?
let latest_finality_update = chain
.lightclient_server_cache
.get_latest_finality_update()
.ok_or(Error::FailedConstructingUpdate)?;
let latest_seen_finality_update_slot = match latest_seen_finality_update.as_ref() {
Some(update) => update.finalized_header.slot,
None => Slot::new(0),
};

// verify that no other finality_update with a lower or equal
// finalized_header.slot was already forwarded on the network
if gossiped_finality_slot <= latest_seen_finality_update_slot {
if rcv_finality_update.finalized_header.slot <= latest_finality_update.finalized_header.slot
{
return Err(Error::FinalityUpdateAlreadySeen);
}

// verify that enough time has passed for the block to have been propagated
let start_time = chain
.slot_clock
.start_of(rcv_finality_update.signature_slot);
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
match start_time {
Some(time) => {
if seen_timestamp + MAXIMUM_GOSSIP_CLOCK_DISPARITY < time + one_third_slot_duration
Expand All @@ -111,25 +94,14 @@ impl<T: BeaconChainTypes> VerifiedLightClientFinalityUpdate<T> {
None => return Err(Error::SigSlotStartIsNone),
}

let head_state = &head.snapshot.beacon_state;
let finality_update = LightClientFinalityUpdate::new(
&chain.spec,
head_state,
head_block,
&mut attested_state,
&finalized_block,
)?;

// verify that the gossiped finality update is the same as the locally constructed one.
if finality_update != light_client_finality_update {
if latest_finality_update != rcv_finality_update {
return Err(Error::InvalidLightClientFinalityUpdate);
}

*latest_seen_finality_update = Some(light_client_finality_update.clone());

Ok(Self {
light_client_finality_update,
light_client_finality_update: rcv_finality_update,
seen_timestamp,
})
}
}
}
Loading

0 comments on commit 23fdb20

Please sign in to comment.