mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-09 13:56:23 +00:00
735c1df62f
The light client sync protocol employs heuristics to ensure it does not become stuck during non-finality or low sync committee participation. These can enable use cases that prefer availability of recent data over security. For our syncing use case, though, security is preferred. An option is added to light client processor to configure this tradeoff.
151 lines
5.1 KiB
Nim
151 lines
5.1 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2022 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
# This implements the pre-release proposal of the libp2p based light client sync
|
|
# protocol. See https://github.com/ethereum/consensus-specs/pull/2802
|
|
|
|
import
|
|
chronicles,
|
|
./beacon_node
|
|
|
|
logScope: topics = "beacnde"
|
|
|
|
proc initLightClient*(
|
|
node: BeaconNode,
|
|
rng: ref HmacDrbgContext,
|
|
cfg: RuntimeConfig,
|
|
forkDigests: ref ForkDigests,
|
|
getBeaconTime: GetBeaconTimeFn,
|
|
genesis_validators_root: Eth2Digest) =
|
|
template config(): auto = node.config
|
|
|
|
# Creating a light client is not dependent on `lightClientEnable`
|
|
# because the light client module also handles gossip subscriptions
|
|
# for broadcasting light client data as a server.
|
|
|
|
let
|
|
optimisticProcessor = proc(signedBlock: ForkedMsgTrustedSignedBeaconBlock):
|
|
Future[void] {.async.} =
|
|
debug "New LC optimistic block",
|
|
opt = signedBlock.toBlockId(),
|
|
dag = node.dag.head.bid,
|
|
wallSlot = node.currentSlot
|
|
return
|
|
optSync = initLCOptimisticSync(
|
|
node.network, getBeaconTime, optimisticProcessor,
|
|
config.safeSlotsToImportOptimistically)
|
|
|
|
lightClient = createLightClient(
|
|
node.network, rng, config, cfg, forkDigests, getBeaconTime,
|
|
genesis_validators_root, LightClientFinalizationMode.Strict)
|
|
|
|
if config.lightClientEnable.get:
|
|
proc shouldSyncOptimistically(slot: Slot): bool =
|
|
const
|
|
# Minimum number of slots to be ahead of DAG to use optimistic sync
|
|
minProgress = 8 * SLOTS_PER_EPOCH
|
|
# Maximum age of light client optimistic header to use optimistic sync
|
|
maxAge = 2 * SLOTS_PER_EPOCH
|
|
|
|
if slot < getStateField(node.dag.headState, slot) + minProgress:
|
|
false
|
|
elif getBeaconTime().slotOrZero > slot + maxAge:
|
|
false
|
|
else:
|
|
true
|
|
|
|
proc onFinalizedHeader(lightClient: LightClient) =
|
|
let optimisticHeader = lightClient.optimisticHeader.valueOr:
|
|
return
|
|
if not shouldSyncOptimistically(optimisticHeader.slot):
|
|
return
|
|
let finalizedHeader = lightClient.finalizedHeader.valueOr:
|
|
return
|
|
optSync.setOptimisticHeader(optimisticHeader)
|
|
optSync.setFinalizedHeader(finalizedHeader)
|
|
|
|
proc onOptimisticHeader(lightClient: LightClient) =
|
|
let optimisticHeader = lightClient.optimisticHeader.valueOr:
|
|
return
|
|
if not shouldSyncOptimistically(optimisticHeader.slot):
|
|
return
|
|
optSync.setOptimisticHeader(optimisticHeader)
|
|
|
|
lightClient.onFinalizedHeader = onFinalizedHeader
|
|
lightClient.onOptimisticHeader = onOptimisticHeader
|
|
lightClient.trustedBlockRoot = config.lightClientTrustedBlockRoot
|
|
|
|
elif config.lightClientTrustedBlockRoot.isSome:
|
|
warn "Ignoring `lightClientTrustedBlockRoot`, light client not enabled",
|
|
lightClientEnable = config.lightClientEnable.get,
|
|
lightClientTrustedBlockRoot = config.lightClientTrustedBlockRoot
|
|
|
|
node.lcOptSync = optSync
|
|
node.lightClient = lightClient
|
|
|
|
proc startLightClient*(node: BeaconNode) =
|
|
if not node.config.lightClientEnable.get:
|
|
return
|
|
|
|
node.lcOptSync.start()
|
|
node.lightClient.start()
|
|
|
|
proc installLightClientMessageValidators*(node: BeaconNode) =
|
|
let eth2Processor =
|
|
if node.config.lightClientDataServe.get:
|
|
# Process gossip using both full node and light client
|
|
node.processor
|
|
elif node.config.lightClientEnable.get:
|
|
# Only process gossip using light client
|
|
nil
|
|
else:
|
|
# Light client topics will never be subscribed to, no validators needed
|
|
return
|
|
|
|
node.lightClient.installMessageValidators(eth2Processor)
|
|
|
|
proc updateLightClientGossipStatus*(
|
|
node: BeaconNode, slot: Slot, dagIsBehind: bool) =
|
|
let isBehind =
|
|
if node.config.lightClientDataServe.get:
|
|
# Forward DAG's readiness to handle light client gossip
|
|
dagIsBehind
|
|
else:
|
|
# Full node is not interested in gossip
|
|
true
|
|
|
|
node.lightClient.updateGossipStatus(slot, some isBehind)
|
|
|
|
proc updateLightClientFromDag*(node: BeaconNode) =
|
|
if not node.config.lightClientEnable.get:
|
|
return
|
|
if node.config.lightClientTrustedBlockRoot.isSome:
|
|
return
|
|
|
|
let
|
|
dagHead = node.dag.finalizedHead
|
|
dagPeriod = dagHead.slot.sync_committee_period
|
|
if dagHead.slot < node.dag.cfg.ALTAIR_FORK_EPOCH.start_slot:
|
|
return
|
|
|
|
let lcHeader = node.lightClient.finalizedHeader
|
|
if lcHeader.isSome:
|
|
if dagPeriod <= lcHeader.get.slot.sync_committee_period:
|
|
return
|
|
|
|
let
|
|
bdata = node.dag.getForkedBlock(dagHead.blck.bid).valueOr:
|
|
return
|
|
header = bdata.toBeaconBlockHeader
|
|
current_sync_committee = block:
|
|
var tmpState = assignClone(node.dag.headState)
|
|
node.dag.currentSyncCommitteeForPeriod(tmpState[], dagPeriod).valueOr:
|
|
return
|
|
node.lightClient.resetToFinalizedHeader(header, current_sync_committee)
|