Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to collect light client data #3474

Merged
merged 1 commit into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion AllTests-mainnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,13 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
+ [SCRYPT] Network Keystore encryption OK
```
OK: 9/9 Fail: 0/9 Skip: 0/9
## Light client [Preset: mainnet]
```diff
+ Init from checkpoint OK
+ Light client sync OK
+ Pre-Altair OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## ListKeys requests [Preset: mainnet]
```diff
+ Correct token provided [Preset: mainnet] OK
Expand Down Expand Up @@ -513,4 +520,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
OK: 1/1 Fail: 0/1 Skip: 0/1

---TOTAL---
OK: 282/287 Fail: 0/287 Skip: 5/287
OK: 285/290 Fail: 0/290 Skip: 5/290
16 changes: 16 additions & 0 deletions beacon_chain/conf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import
./validators/slashing_protection_common,
./filepath

from consensus_object_pools/block_pools_types_light_client
import ImportLightClientData

export
uri, nat, enr,
defaultEth2TcpPort, enabledLogLevel, ValidIpAddress,
Expand Down Expand Up @@ -419,6 +422,19 @@ type
desc: "A file specifying the authorization token required for accessing the keymanager API"
name: "keymanager-token-file" }: Option[InputFile]

serveLightClientData* {.
hidden
desc: "BETA: Serve data for enabling light clients to stay in sync with the network"
defaultValue: false
name: "serve-light-client-data"}: bool

importLightClientData* {.
hidden
desc: "BETA: Which classes of light client data to import. " &
"Must be one of: none, only-new, full (slow startup), on-demand (may miss validator duties)"
defaultValue: ImportLightClientData.None
name: "import-light-client-data"}: ImportLightClientData

inProcessValidators* {.
desc: "Disable the push model (the beacon node tells a signing process with the private keys of the validators what to sign and when) and load the validators in the beacon node itself"
defaultValue: true # the use of the nimbus_signing_process binary by default will be delayed until async I/O over stdin/stdout is developed for the child process.
Expand Down
5 changes: 4 additions & 1 deletion beacon_chain/consensus_object_pools/block_clearance.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import
chronicles,
stew/[assign2, results],
../spec/[forks, signatures, signatures_batch, state_transition],
"."/[block_dag, blockchain_dag]
"."/[block_dag, blockchain_dag, blockchain_dag_light_client]

export results, signatures_batch, block_dag, blockchain_dag

Expand Down Expand Up @@ -84,6 +84,9 @@ proc addResolvedHeadBlock(
putBlockDur = putBlockTick - startTick,
epochRefDur = epochRefTick - putBlockTick

# Update light client data
dag.processNewBlockForLightClient(state, trustedBlock, parent)

# Notify others of the new block before processing the quarantine, such that
# notifications for parents happens before those of the children
if onBlockAdded != nil:
Expand Down
20 changes: 18 additions & 2 deletions beacon_chain/consensus_object_pools/block_pools_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import
../spec/datatypes/[phase0, altair, bellatrix],
".."/beacon_chain_db,
../validators/validator_monitor,
./block_dag
./block_dag, block_pools_types_light_client

export
options, sets, tables, hashes, helpers, beacon_chain_db, block_dag,
validator_monitor
block_pools_types_light_client, validator_monitor

# ChainDAG and types related to forming a DAG of blocks, keeping track of their
# relationships and allowing various forms of lookups
Expand Down Expand Up @@ -178,6 +178,12 @@ type

cfg*: RuntimeConfig

serveLightClientData*: bool
## Whether to make local light client data available or not

importLightClientData*: ImportLightClientData
## Which classes of light client data to import

epochRefs*: array[32, EpochRef]
## Cached information about a particular epoch ending with the given
## block - we limit the number of held EpochRefs to put a cap on
Expand All @@ -189,6 +195,14 @@ type
## value with other components which don't have access to the
## full ChainDAG.

# -----------------------------------
# Data to enable light clients to stay in sync with the network

lightClientCache*: LightClientCache

# -----------------------------------
# Callbacks

onBlockAdded*: OnBlockCallback
## On block added callback
onHeadChanged*: OnHeadCallback
Expand All @@ -197,6 +211,8 @@ type
## On beacon chain reorganization
onFinHappened*: OnFinalizedCallback
## On finalization callback
onOptimisticLightClientUpdate*: OnOptimisticLightClientUpdateCallback
## On `OptimisticLightClientUpdate` updated callback

headSyncCommittees*: SyncCommitteeCache
## A cache of the sync committees, as they appear in the head state -
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# 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
# Status libraries
stew/bitops2,
# Beacon chain internals
../spec/datatypes/altair,
./block_dag

type
OnOptimisticLightClientUpdateCallback* =
proc(data: OptimisticLightClientUpdate) {.gcsafe, raises: [Defect].}

ImportLightClientData* {.pure.} = enum
## Controls which classes of light client data are imported.
None = "none"
## Import no light client data.
OnlyNew = "only-new"
## Import only new light client data.
Full = "full"
## Import light client data for entire weak subjectivity period.
OnDemand = "on-demand"
## No precompute of historic data. Is slow and may miss validator duties.

CachedLightClientData* = object
## Cached data from historical non-finalized states to improve speed when
## creating future `LightClientUpdate` and `LightClientBootstrap` instances.
current_sync_committee_branch*:
array[log2trunc(altair.CURRENT_SYNC_COMMITTEE_INDEX), Eth2Digest]

next_sync_committee_branch*:
array[log2trunc(altair.NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]

finalized_bid*: BlockId
finality_branch*:
array[log2trunc(altair.FINALIZED_ROOT_INDEX), Eth2Digest]

CachedLightClientBootstrap* = object
## Cached data from historical finalized epoch boundary blocks to improve
## speed when creating future `LightClientBootstrap` instances.
current_sync_committee_branch*:
array[log2trunc(altair.CURRENT_SYNC_COMMITTEE_INDEX), Eth2Digest]

LightClientCache* = object
data*: Table[BlockId, CachedLightClientData]
## Cached data for creating future `LightClientUpdate` instances.
## Key is the block ID of which the post state was used to get the data.
## Data is stored for the most recent 4 finalized checkpoints, as well as
## for all non-finalized blocks.

bootstrap*: Table[Slot, CachedLightClientBootstrap]
## Cached data for creating future `LightClientBootstrap` instances.
## Key is the block slot of which the post state was used to get the data.
## Data is stored for finalized epoch boundary blocks.

latestCheckpoints*: array[4, Checkpoint]
## Keeps track of the latest four `finalized_checkpoint` references
## leading to `finalizedHead`. Used to prune `data`.
## Non-finalized states may only refer to these checkpoints.

lastCheckpointIndex*: int
## Last index that was modified in `latestCheckpoints`.

bestUpdates*: Table[SyncCommitteePeriod, altair.LightClientUpdate]
## Stores the `LightClientUpdate` with the most `sync_committee_bits` per
## `SyncCommitteePeriod`. Updates with a finality proof have precedence.

pendingBestUpdates*:
Table[(SyncCommitteePeriod, Eth2Digest), altair.LightClientUpdate]
## Same as `bestUpdates`, but for `SyncCommitteePeriod` with
## `next_sync_committee` that are not finalized. Key is `(period,
## hash_tree_root(current_sync_committee | next_sync_committee)`.

latestUpdate*: altair.LightClientUpdate
## Tracks the `LightClientUpdate` for the latest slot. This may be older
## than head for empty slots or if not signed by sync committee.

optimisticUpdate*: OptimisticLightClientUpdate
## Tracks the `OptimisticLightClientUpdate` for the latest slot. This may
## be older than head for empty slots or if not signed by sync committee.

importTailSlot*: Slot
## The earliest slot for which light client data is collected.
## Only relevant for `ImportLightClientData.OnlyNew`.
Comment on lines +92 to +94
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added importTailSlot for OnlyNew import mode. Data is only collected from this slot onward.

30 changes: 28 additions & 2 deletions beacon_chain/consensus_object_pools/blockchain_dag.nim
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,23 @@ proc updateBeaconMetrics(state: StateData, cache: var StateCache) =
beacon_active_validators.set(active_validators)
beacon_current_active_validators.set(active_validators)

import blockchain_dag_light_client

export
blockchain_dag_light_client.getBestLightClientUpdateForPeriod,
blockchain_dag_light_client.getLatestLightClientUpdate,
blockchain_dag_light_client.getOptimisticLightClientUpdate,
blockchain_dag_light_client.getLightClientBootstrap

proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
validatorMonitor: ref ValidatorMonitor, updateFlags: UpdateFlags,
onBlockCb: OnBlockCallback = nil, onHeadCb: OnHeadCallback = nil,
onReorgCb: OnReorgCallback = nil,
onFinCb: OnFinalizedCallback = nil): ChainDAGRef =
onFinCb: OnFinalizedCallback = nil,
onOptimisticLCUpdateCb: OnOptimisticLightClientUpdateCallback = nil,
serveLightClientData = false,
importLightClientData = ImportLightClientData.None
): ChainDAGRef =
# TODO we require that the db contains both a head and a tail block -
# asserting here doesn't seem like the right way to go about it however..

Expand Down Expand Up @@ -653,11 +665,14 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
# allow skipping some validation.
updateFlags: {verifyFinalization} * updateFlags,
cfg: cfg,
serveLightClientData: serveLightClientData,
importLightClientData: importLightClientData,

onBlockAdded: onBlockCb,
onHeadChanged: onHeadCb,
onReorgHappened: onReorgCb,
onFinHappened: onFinCb
onFinHappened: onFinCb,
onOptimisticLightClientUpdate: onOptimisticLCUpdateCb
)

block: # Initialize dag states
Expand Down Expand Up @@ -779,6 +794,8 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
stateDur = stateTick - summariesTick,
indexDur = Moment.now() - stateTick

dag.initLightClientCache()

dag

template genesisValidatorsRoot*(dag: ChainDAGRef): Eth2Digest =
Expand Down Expand Up @@ -1286,6 +1303,9 @@ proc pruneBlocksDAG(dag: ChainDAGRef) =

var cur = head.atSlot()
while not cur.blck.isAncestorOf(dag.finalizedHead.blck):
# Update light client data
dag.deleteLightClientData(cur.blck.bid)

dag.delState(cur) # TODO: should we move that disk I/O to `onSlotEnd`

if cur.isProposed():
Expand Down Expand Up @@ -1485,6 +1505,9 @@ proc updateHead*(
doAssert (not finalizedHead.blck.isNil),
"Block graph should always lead to a finalized block"

# Update light client data
dag.processHeadChangeForLightClient()

let (isAncestor, ancestorDepth) = lastHead.getDepth(newHead)
if not(isAncestor):
notice "Updated head block with chain reorg",
Expand Down Expand Up @@ -1571,6 +1594,9 @@ proc updateHead*(
# therefore no longer be considered as part of the chain we're following
dag.pruneBlocksDAG()

# Update light client data
dag.processFinalizationForLightClient()

# Send notification about new finalization point via callback.
if not(isNil(dag.onFinHappened)):
let stateRoot =
Expand Down
Loading