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)
|
|
|
|
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)
|