update light client protocol version (#3550)
* Use final `v1` version for light client protocols * Unhide LC data collection options * Default enable LC data serving * rm unneeded import * Connect to EL on startup * Add docs for LC based EL sync
This commit is contained in:
parent
ce9e50e275
commit
3ec7982293
2
Makefile
2
Makefile
|
@ -384,8 +384,6 @@ define CONNECT_TO_NETWORK_IN_DEV_MODE
|
||||||
--log-level="DEBUG; TRACE:discv5,networking; REQUIRED:none; DISABLED:none" \
|
--log-level="DEBUG; TRACE:discv5,networking; REQUIRED:none; DISABLED:none" \
|
||||||
--data-dir=build/data/shared_$(1)_$(NODE_ID) \
|
--data-dir=build/data/shared_$(1)_$(NODE_ID) \
|
||||||
--light-client-enable=on \
|
--light-client-enable=on \
|
||||||
--light-client-data-serve=on \
|
|
||||||
--light-client-data-import-mode=only-new \
|
|
||||||
--dump $(NODE_PARAMS)
|
--dump $(NODE_PARAMS)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ proc initLightClient*(
|
||||||
node.network, rng, config, cfg, forkDigests, getBeaconTime,
|
node.network, rng, config, cfg, forkDigests, getBeaconTime,
|
||||||
genesis_validators_root, LightClientFinalizationMode.Strict)
|
genesis_validators_root, LightClientFinalizationMode.Strict)
|
||||||
|
|
||||||
if config.lightClientEnable.get:
|
if config.lightClientEnable:
|
||||||
proc shouldSyncOptimistically(slot: Slot): bool =
|
proc shouldSyncOptimistically(slot: Slot): bool =
|
||||||
const
|
const
|
||||||
# Minimum number of slots to be ahead of DAG to use optimistic sync
|
# Minimum number of slots to be ahead of DAG to use optimistic sync
|
||||||
|
@ -83,14 +83,14 @@ proc initLightClient*(
|
||||||
|
|
||||||
elif config.lightClientTrustedBlockRoot.isSome:
|
elif config.lightClientTrustedBlockRoot.isSome:
|
||||||
warn "Ignoring `lightClientTrustedBlockRoot`, light client not enabled",
|
warn "Ignoring `lightClientTrustedBlockRoot`, light client not enabled",
|
||||||
lightClientEnable = config.lightClientEnable.get,
|
lightClientEnable = config.lightClientEnable,
|
||||||
lightClientTrustedBlockRoot = config.lightClientTrustedBlockRoot
|
lightClientTrustedBlockRoot = config.lightClientTrustedBlockRoot
|
||||||
|
|
||||||
node.lcOptSync = optSync
|
node.lcOptSync = optSync
|
||||||
node.lightClient = lightClient
|
node.lightClient = lightClient
|
||||||
|
|
||||||
proc startLightClient*(node: BeaconNode) =
|
proc startLightClient*(node: BeaconNode) =
|
||||||
if not node.config.lightClientEnable.get:
|
if not node.config.lightClientEnable:
|
||||||
return
|
return
|
||||||
|
|
||||||
node.lcOptSync.start()
|
node.lcOptSync.start()
|
||||||
|
@ -98,10 +98,10 @@ proc startLightClient*(node: BeaconNode) =
|
||||||
|
|
||||||
proc installLightClientMessageValidators*(node: BeaconNode) =
|
proc installLightClientMessageValidators*(node: BeaconNode) =
|
||||||
let eth2Processor =
|
let eth2Processor =
|
||||||
if node.config.lightClientDataServe.get:
|
if node.config.lightClientDataServe:
|
||||||
# Process gossip using both full node and light client
|
# Process gossip using both full node and light client
|
||||||
node.processor
|
node.processor
|
||||||
elif node.config.lightClientEnable.get:
|
elif node.config.lightClientEnable:
|
||||||
# Only process gossip using light client
|
# Only process gossip using light client
|
||||||
nil
|
nil
|
||||||
else:
|
else:
|
||||||
|
@ -113,7 +113,7 @@ proc installLightClientMessageValidators*(node: BeaconNode) =
|
||||||
proc updateLightClientGossipStatus*(
|
proc updateLightClientGossipStatus*(
|
||||||
node: BeaconNode, slot: Slot, dagIsBehind: bool) =
|
node: BeaconNode, slot: Slot, dagIsBehind: bool) =
|
||||||
let isBehind =
|
let isBehind =
|
||||||
if node.config.lightClientDataServe.get:
|
if node.config.lightClientDataServe:
|
||||||
# Forward DAG's readiness to handle light client gossip
|
# Forward DAG's readiness to handle light client gossip
|
||||||
dagIsBehind
|
dagIsBehind
|
||||||
else:
|
else:
|
||||||
|
@ -123,7 +123,7 @@ proc updateLightClientGossipStatus*(
|
||||||
node.lightClient.updateGossipStatus(slot, some isBehind)
|
node.lightClient.updateGossipStatus(slot, some isBehind)
|
||||||
|
|
||||||
proc updateLightClientFromDag*(node: BeaconNode) =
|
proc updateLightClientFromDag*(node: BeaconNode) =
|
||||||
if not node.config.lightClientEnable.get:
|
if not node.config.lightClientEnable:
|
||||||
return
|
return
|
||||||
if node.config.lightClientTrustedBlockRoot.isSome:
|
if node.config.lightClientTrustedBlockRoot.isSome:
|
||||||
return
|
return
|
||||||
|
|
|
@ -278,7 +278,8 @@ type
|
||||||
lightClientEnable* {.
|
lightClientEnable* {.
|
||||||
hidden
|
hidden
|
||||||
desc: "BETA: Accelerate sync using light client."
|
desc: "BETA: Accelerate sync using light client."
|
||||||
name: "light-client-enable" .}: Option[bool]
|
defaultValue: false
|
||||||
|
name: "light-client-enable" .}: bool
|
||||||
|
|
||||||
lightClientTrustedBlockRoot* {.
|
lightClientTrustedBlockRoot* {.
|
||||||
hidden
|
hidden
|
||||||
|
@ -454,19 +455,18 @@ type
|
||||||
name: "keymanager-token-file" .}: Option[InputFile]
|
name: "keymanager-token-file" .}: Option[InputFile]
|
||||||
|
|
||||||
lightClientDataServe* {.
|
lightClientDataServe* {.
|
||||||
hidden
|
desc: "Serve data for enabling light clients to stay in sync with the network"
|
||||||
desc: "BETA: Serve data for enabling light clients to stay in sync with the network"
|
defaultValue: true
|
||||||
name: "light-client-data-serve" .}: Option[bool]
|
name: "light-client-data-serve" .}: bool
|
||||||
|
|
||||||
lightClientDataImportMode* {.
|
lightClientDataImportMode* {.
|
||||||
hidden
|
desc: "Which classes of light client data to import. " &
|
||||||
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)"
|
"Must be one of: none, only-new, full (slow startup), on-demand (may miss validator duties)"
|
||||||
name: "light-client-data-import-mode" .}: Option[LightClientDataImportMode]
|
defaultValue: LightClientDataImportMode.OnlyNew
|
||||||
|
name: "light-client-data-import-mode" .}: LightClientDataImportMode
|
||||||
|
|
||||||
lightClientDataMaxPeriods* {.
|
lightClientDataMaxPeriods* {.
|
||||||
hidden
|
desc: "Maximum number of sync committee periods to retain light client data"
|
||||||
desc: "BETA: Maximum number of sync committee periods to retain light client data"
|
|
||||||
name: "light-client-data-max-periods" .}: Option[uint64]
|
name: "light-client-data-max-periods" .}: Option[uint64]
|
||||||
|
|
||||||
inProcessValidators* {.
|
inProcessValidators* {.
|
||||||
|
|
|
@ -1700,7 +1700,7 @@ proc new(T: type Eth2Node,
|
||||||
|
|
||||||
for msg in proto.messages:
|
for msg in proto.messages:
|
||||||
when config is BeaconNodeConf:
|
when config is BeaconNodeConf:
|
||||||
if msg.isLightClientRequest and not config.lightClientDataServe.get:
|
if msg.isLightClientRequest and not config.lightClientDataServe:
|
||||||
continue
|
continue
|
||||||
elif config is LightClientConf:
|
elif config is LightClientConf:
|
||||||
if not msg.isRequired:
|
if not msg.isRequired:
|
||||||
|
|
|
@ -18,9 +18,6 @@ import
|
||||||
../spec/eth2_ssz_serialization,
|
../spec/eth2_ssz_serialization,
|
||||||
../spec/datatypes/phase0
|
../spec/datatypes/phase0
|
||||||
|
|
||||||
from ../consensus_object_pools/block_pools_types_light_client
|
|
||||||
import LightClientDataImportMode
|
|
||||||
|
|
||||||
# ATTENTION! This file will produce a large C file, because we are inlining
|
# ATTENTION! This file will produce a large C file, because we are inlining
|
||||||
# genesis states as C literals in the generated code (and blobs in the final
|
# genesis states as C literals in the generated code (and blobs in the final
|
||||||
# binary). It makes sense to keep the file small and separated from the rest
|
# binary). It makes sense to keep the file small and separated from the rest
|
||||||
|
@ -42,12 +39,6 @@ type
|
||||||
goerli
|
goerli
|
||||||
sepolia
|
sepolia
|
||||||
|
|
||||||
Eth2NetworkConfigDefaults* = object
|
|
||||||
## Network specific config defaults
|
|
||||||
lightClientEnable*: bool
|
|
||||||
lightClientDataServe*: bool
|
|
||||||
lightClientDataImportMode*: LightClientDataImportMode
|
|
||||||
|
|
||||||
Eth2NetworkMetadata* = object
|
Eth2NetworkMetadata* = object
|
||||||
case incompatible*: bool
|
case incompatible*: bool
|
||||||
of false:
|
of false:
|
||||||
|
@ -82,8 +73,6 @@ type
|
||||||
# unknown genesis state.
|
# unknown genesis state.
|
||||||
genesisData*: string
|
genesisData*: string
|
||||||
genesisDepositsSnapshot*: string
|
genesisDepositsSnapshot*: string
|
||||||
|
|
||||||
configDefaults*: Eth2NetworkConfigDefaults
|
|
||||||
else:
|
else:
|
||||||
incompatibilityDesc*: string
|
incompatibilityDesc*: string
|
||||||
|
|
||||||
|
@ -221,21 +210,6 @@ proc loadEth2NetworkMetadata*(path: string, eth1Network = none(Eth1Network)): Et
|
||||||
else:
|
else:
|
||||||
""
|
""
|
||||||
|
|
||||||
deploymentPhase = genesisData.deploymentPhase
|
|
||||||
|
|
||||||
configDefaults =
|
|
||||||
Eth2NetworkConfigDefaults(
|
|
||||||
lightClientEnable:
|
|
||||||
false, # Only produces debug logs so far
|
|
||||||
lightClientDataServe:
|
|
||||||
deploymentPhase <= DeploymentPhase.Testnet,
|
|
||||||
lightClientDataImportMode:
|
|
||||||
if deploymentPhase <= DeploymentPhase.Testnet:
|
|
||||||
LightClientDataImportMode.OnlyNew
|
|
||||||
else:
|
|
||||||
LightClientDataImportMode.None
|
|
||||||
)
|
|
||||||
|
|
||||||
Eth2NetworkMetadata(
|
Eth2NetworkMetadata(
|
||||||
incompatible: false,
|
incompatible: false,
|
||||||
eth1Network: eth1Network,
|
eth1Network: eth1Network,
|
||||||
|
@ -243,8 +217,7 @@ proc loadEth2NetworkMetadata*(path: string, eth1Network = none(Eth1Network)): Et
|
||||||
bootstrapNodes: bootstrapNodes,
|
bootstrapNodes: bootstrapNodes,
|
||||||
depositContractDeployedAt: depositContractDeployedAt,
|
depositContractDeployedAt: depositContractDeployedAt,
|
||||||
genesisData: genesisData,
|
genesisData: genesisData,
|
||||||
genesisDepositsSnapshot: genesisDepositsSnapshot,
|
genesisDepositsSnapshot: genesisDepositsSnapshot)
|
||||||
configDefaults: configDefaults)
|
|
||||||
|
|
||||||
except PresetIncompatibleError as err:
|
except PresetIncompatibleError as err:
|
||||||
Eth2NetworkMetadata(incompatible: true,
|
Eth2NetworkMetadata(incompatible: true,
|
||||||
|
|
|
@ -183,17 +183,17 @@ proc loadChainDag(
|
||||||
if config.strictVerification: {strictVerification}
|
if config.strictVerification: {strictVerification}
|
||||||
else: {}
|
else: {}
|
||||||
onLightClientFinalityUpdateCb =
|
onLightClientFinalityUpdateCb =
|
||||||
if config.lightClientDataServe.get: onLightClientFinalityUpdate
|
if config.lightClientDataServe: onLightClientFinalityUpdate
|
||||||
else: nil
|
else: nil
|
||||||
onLightClientOptimisticUpdateCb =
|
onLightClientOptimisticUpdateCb =
|
||||||
if config.lightClientDataServe.get: onLightClientOptimisticUpdate
|
if config.lightClientDataServe: onLightClientOptimisticUpdate
|
||||||
else: nil
|
else: nil
|
||||||
dag = ChainDAGRef.init(
|
dag = ChainDAGRef.init(
|
||||||
cfg, db, validatorMonitor, extraFlags + chainDagFlags, config.eraDir,
|
cfg, db, validatorMonitor, extraFlags + chainDagFlags, config.eraDir,
|
||||||
vanityLogs = getPandas(detectTTY(config.logStdout)),
|
vanityLogs = getPandas(detectTTY(config.logStdout)),
|
||||||
lcDataConfig = LightClientDataConfig(
|
lcDataConfig = LightClientDataConfig(
|
||||||
serve: config.lightClientDataServe.get,
|
serve: config.lightClientDataServe,
|
||||||
importMode: config.lightClientDataImportMode.get,
|
importMode: config.lightClientDataImportMode,
|
||||||
maxPeriods: config.lightClientDataMaxPeriods,
|
maxPeriods: config.lightClientDataMaxPeriods,
|
||||||
onLightClientFinalityUpdate: onLightClientFinalityUpdateCb,
|
onLightClientFinalityUpdate: onLightClientFinalityUpdateCb,
|
||||||
onLightClientOptimisticUpdate: onLightClientOptimisticUpdateCb))
|
onLightClientOptimisticUpdate: onLightClientOptimisticUpdateCb))
|
||||||
|
@ -343,10 +343,9 @@ proc initFullNode(
|
||||||
getFrontfillSlot, dag.backfill.slot, blockVerifier, maxHeadAge = 0)
|
getFrontfillSlot, dag.backfill.slot, blockVerifier, maxHeadAge = 0)
|
||||||
router = (ref MessageRouter)(
|
router = (ref MessageRouter)(
|
||||||
processor: processor,
|
processor: processor,
|
||||||
network: node.network,
|
network: node.network)
|
||||||
)
|
|
||||||
|
|
||||||
if node.config.lightClientDataServe.get:
|
if node.config.lightClientDataServe:
|
||||||
proc scheduleSendingLightClientUpdates(slot: Slot) =
|
proc scheduleSendingLightClientUpdates(slot: Slot) =
|
||||||
if node.lightClientPool[].broadcastGossipFut != nil:
|
if node.lightClientPool[].broadcastGossipFut != nil:
|
||||||
return
|
return
|
||||||
|
@ -1848,18 +1847,6 @@ proc doRunBeaconNode(config: var BeaconNodeConf, rng: ref HmacDrbgContext) {.rai
|
||||||
for node in metadata.bootstrapNodes:
|
for node in metadata.bootstrapNodes:
|
||||||
config.bootstrapNodes.add node
|
config.bootstrapNodes.add node
|
||||||
|
|
||||||
template applyConfigDefault(field: untyped): untyped =
|
|
||||||
if config.`field`.isNone:
|
|
||||||
if not metadata.configDefaults.`field`.isZeroMemory:
|
|
||||||
info "Applying network config default",
|
|
||||||
eth2Network = config.eth2Network,
|
|
||||||
`field` = metadata.configDefaults.`field`
|
|
||||||
config.`field` = some metadata.configDefaults.`field`
|
|
||||||
|
|
||||||
applyConfigDefault(lightClientEnable)
|
|
||||||
applyConfigDefault(lightClientDataServe)
|
|
||||||
applyConfigDefault(lightClientDataImportMode)
|
|
||||||
|
|
||||||
let node = BeaconNode.init(
|
let node = BeaconNode.init(
|
||||||
metadata.cfg,
|
metadata.cfg,
|
||||||
rng,
|
rng,
|
||||||
|
|
|
@ -60,11 +60,13 @@ programMain:
|
||||||
|
|
||||||
eth1Monitor =
|
eth1Monitor =
|
||||||
if config.web3Urls.len > 0:
|
if config.web3Urls.len > 0:
|
||||||
Eth1Monitor.init(
|
let res = Eth1Monitor.init(
|
||||||
cfg, db = nil, getBeaconTime, config.web3Urls,
|
cfg, db = nil, getBeaconTime, config.web3Urls,
|
||||||
none(DepositContractSnapshot), metadata.eth1Network,
|
none(DepositContractSnapshot), metadata.eth1Network,
|
||||||
forcePolling = false,
|
forcePolling = false,
|
||||||
rng[].loadJwtSecret(config, allowCreate = false))
|
rng[].loadJwtSecret(config, allowCreate = false))
|
||||||
|
waitFor res.ensureDataProvider()
|
||||||
|
res
|
||||||
else:
|
else:
|
||||||
nil
|
nil
|
||||||
|
|
||||||
|
|
|
@ -100,12 +100,12 @@ func getSyncCommitteeContributionAndProofTopic*(forkDigest: ForkDigest): string
|
||||||
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#light_client_finality_update
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#light_client_finality_update
|
||||||
func getLightClientFinalityUpdateTopic*(forkDigest: ForkDigest): string =
|
func getLightClientFinalityUpdateTopic*(forkDigest: ForkDigest): string =
|
||||||
## For broadcasting or obtaining the latest `LightClientFinalityUpdate`.
|
## For broadcasting or obtaining the latest `LightClientFinalityUpdate`.
|
||||||
eth2Prefix(forkDigest) & "light_client_finality_update_v0/ssz_snappy"
|
eth2Prefix(forkDigest) & "light_client_finality_update/ssz_snappy"
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#light_client_optimistic_update
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#light_client_optimistic_update
|
||||||
func getLightClientOptimisticUpdateTopic*(forkDigest: ForkDigest): string =
|
func getLightClientOptimisticUpdateTopic*(forkDigest: ForkDigest): string =
|
||||||
## For broadcasting or obtaining the latest `LightClientOptimisticUpdate`.
|
## For broadcasting or obtaining the latest `LightClientOptimisticUpdate`.
|
||||||
eth2Prefix(forkDigest) & "light_client_optimistic_update_v0/ssz_snappy"
|
eth2Prefix(forkDigest) & "light_client_optimistic_update/ssz_snappy"
|
||||||
|
|
||||||
func getENRForkID*(cfg: RuntimeConfig,
|
func getENRForkID*(cfg: RuntimeConfig,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
|
|
|
@ -550,7 +550,7 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
blockRoot: Eth2Digest,
|
blockRoot: Eth2Digest,
|
||||||
response: SingleChunkResponse[altair.LightClientBootstrap])
|
response: SingleChunkResponse[altair.LightClientBootstrap])
|
||||||
{.async, libp2pProtocol("light_client_bootstrap", 0,
|
{.async, libp2pProtocol("light_client_bootstrap", 1,
|
||||||
isLightClientRequest = true).} =
|
isLightClientRequest = true).} =
|
||||||
trace "Received LC bootstrap request", peer, blockRoot
|
trace "Received LC bootstrap request", peer, blockRoot
|
||||||
let dag = peer.networkState.dag
|
let dag = peer.networkState.dag
|
||||||
|
@ -579,7 +579,7 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
startPeriod: SyncCommitteePeriod,
|
startPeriod: SyncCommitteePeriod,
|
||||||
reqCount: uint64,
|
reqCount: uint64,
|
||||||
response: MultipleChunksResponse[altair.LightClientUpdate])
|
response: MultipleChunksResponse[altair.LightClientUpdate])
|
||||||
{.async, libp2pProtocol("light_client_updates_by_range", 0,
|
{.async, libp2pProtocol("light_client_updates_by_range", 1,
|
||||||
isLightClientRequest = true).} =
|
isLightClientRequest = true).} =
|
||||||
trace "Received LC updates by range request", peer, startPeriod, reqCount
|
trace "Received LC updates by range request", peer, startPeriod, reqCount
|
||||||
let dag = peer.networkState.dag
|
let dag = peer.networkState.dag
|
||||||
|
@ -619,7 +619,7 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
proc lightClientFinalityUpdate(
|
proc lightClientFinalityUpdate(
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
response: SingleChunkResponse[altair.LightClientFinalityUpdate])
|
response: SingleChunkResponse[altair.LightClientFinalityUpdate])
|
||||||
{.async, libp2pProtocol("light_client_finality_update", 0,
|
{.async, libp2pProtocol("light_client_finality_update", 1,
|
||||||
isLightClientRequest = true).} =
|
isLightClientRequest = true).} =
|
||||||
trace "Received LC finality update request", peer
|
trace "Received LC finality update request", peer
|
||||||
let dag = peer.networkState.dag
|
let dag = peer.networkState.dag
|
||||||
|
@ -645,7 +645,7 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
proc lightClientOptimisticUpdate(
|
proc lightClientOptimisticUpdate(
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
response: SingleChunkResponse[altair.LightClientOptimisticUpdate])
|
response: SingleChunkResponse[altair.LightClientOptimisticUpdate])
|
||||||
{.async, libp2pProtocol("light_client_optimistic_update", 0,
|
{.async, libp2pProtocol("light_client_optimistic_update", 1,
|
||||||
isLightClientRequest = true).} =
|
isLightClientRequest = true).} =
|
||||||
trace "Received LC optimistic update request", peer
|
trace "Received LC optimistic update request", peer
|
||||||
let dag = peer.networkState.dag
|
let dag = peer.networkState.dag
|
||||||
|
|
|
@ -74,6 +74,7 @@ nav:
|
||||||
- General:
|
- General:
|
||||||
- 'keep-updated.md'
|
- 'keep-updated.md'
|
||||||
- 'eth1.md'
|
- 'eth1.md'
|
||||||
|
- 'el-light-client.md'
|
||||||
- 'goerli-eth.md'
|
- 'goerli-eth.md'
|
||||||
- 'beacon-node-systemd.md'
|
- 'beacon-node-systemd.md'
|
||||||
- 'logging.md'
|
- 'logging.md'
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
# Light client based EL sync
|
||||||
|
|
||||||
|
Execution layer (EL) implementations provide the [web3 API](https://ethereum.github.io/execution-apis/api-documentation/) to expose information stored on the Ethereum blockchain. With [the merge 🐼](./merge.md), EL's can no longer run standalone and require an external component to determine the latest state to sync to.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
It is recommended to pair the EL with a consensus layer (CL) full node. To use Nimbus, follow the [installation instructions](./install.md).
|
||||||
|
|
||||||
|
In environments where running a full node is not feasible, a light client may be used instead. Light clients delegate full validation to other network participants and operate under a honest supermajority (> 2/3) assumption among elected participants. Due to this delegation, light clients are typically behind by ~4/3 slots (~15 seconds on Ethereum mainnet). On the other hand, light clients do not require storing a big database and need much less bandwith and compute power to stay in sync with the Ethereum network.
|
||||||
|
|
||||||
|
## Building from source
|
||||||
|
|
||||||
|
The Nimbus light client is currently not bundled as part of the Docker images and needs to be built from source.
|
||||||
|
|
||||||
|
### 1. Clone the `nimbus-eth2` repository
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/status-im/nimbus-eth2
|
||||||
|
cd nimbus-eth2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Run the build process
|
||||||
|
|
||||||
|
To build the Nimbus light client and its dependencies, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make -j4 nimbus_light_client
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
Omit `-j4` on systems with 4GB of memory or less.
|
||||||
|
|
||||||
|
This may take a few minutes. When the process finishes, the `nimbus_light_client` executable can be found in the `build` subdirectory.
|
||||||
|
|
||||||
|
## Pairing with the EL
|
||||||
|
|
||||||
|
To ensure that only the light client can control the EL, a file with random content (JWT secret) must be created. The format is 64 hexadecimal (0-9, a-f) characters. To create one, the following command may be used:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
openssl rand -hex 32 | tr -d "\n" > "$HOME/jwtsecret"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
To adjust where the file is created, adjust the `$HOME/jwtsecret` portion in the command above. Also adjust other commands in this guide accordingly.
|
||||||
|
|
||||||
|
The JWT secret must be passed to both the EL and the light client to complete the pairing.
|
||||||
|
|
||||||
|
## Running the EL
|
||||||
|
|
||||||
|
In addition to the [regular instructions](./eth1.md) to run an EL, the JWT secret must be configured. The following sections explain how to do this for certain EL implementations.
|
||||||
|
|
||||||
|
### Geth
|
||||||
|
|
||||||
|
=== "Mainnet"
|
||||||
|
```sh
|
||||||
|
geth --ws --authrpc.jwtsecret="$HOME/jwtsecret"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Goerli"
|
||||||
|
```sh
|
||||||
|
geth --goerli --ws --authrpc.jwtsecret="$HOME/jwtsecret"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nethermind
|
||||||
|
|
||||||
|
=== "Mainnet"
|
||||||
|
```sh
|
||||||
|
nethermind --JsonRpc.JwtSecretFile="$HOME/jwtsecret"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Goerli"
|
||||||
|
```sh
|
||||||
|
nethermind --config goerli --JsonRpc.JwtSecretFile="$HOME/jwtsecret"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Others
|
||||||
|
|
||||||
|
Please consult your EL's documentation for instructions on how to configure the JWT secret and running the EL.
|
||||||
|
|
||||||
|
## Running the light client
|
||||||
|
|
||||||
|
The light client starts syncing from a trusted block. This trusted block should be somewhat recent ([~1-2 weeks](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/weak-subjectivity.md)) and needs to be configured each time when starting the light client.
|
||||||
|
|
||||||
|
1. Obtaining a trusted block root
|
||||||
|
|
||||||
|
A block root may be obtained from another trusted beacon node, or from a trusted provider.
|
||||||
|
|
||||||
|
=== "Trusted beacon node"
|
||||||
|
The REST interface must be enabled on the trusted beacon node (`--rest --rest-port=5052` for Nimbus).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -s "http://localhost:5052/eth/v1/beacon/headers/finalized" | \
|
||||||
|
jq -r '.data.root'
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Beaconcha.in"
|
||||||
|
On the [beaconcha.in](https://beaconcha.in) website ([Goerli](https://prater.beaconcha.in)), navigate to the `Epochs` section and select a recent `Finalized` epoch. Then, scroll down to the bottom of the page. If the bottom-most slot has a `Proposed` status, copy its `Root Hash`. Otherwise, for example if the bottom-most slot was `Missed`, go back and pick a different epoch.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Selecting a block root from an untrusted source or using an outdated block root may lead to the light client syncing to an unexpected state. If that happens, stop the light client and restart it with a new trusted block root. Depending on the EL, its database must be deleted and sync restarted from scratch.
|
||||||
|
|
||||||
|
2. Starting the light client
|
||||||
|
|
||||||
|
To start the light client, run the following commands (inserting your own trusted block root):
|
||||||
|
|
||||||
|
=== "Mainnet"
|
||||||
|
```sh
|
||||||
|
TRUSTED_BLOCK_ROOT=0x1234567890123456789012345678901234567890123456789012345678901234
|
||||||
|
build/nimbus_light_client \
|
||||||
|
--web3-url=ws://127.0.0.1:8551 --jwt-secret="$HOME/jwtsecret" \
|
||||||
|
--trusted-block-root=$TRUSTED_BLOCK_ROOT
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Goerli"
|
||||||
|
```sh
|
||||||
|
TRUSTED_BLOCK_ROOT=0x1234567890123456789012345678901234567890123456789012345678901234
|
||||||
|
build/nimbus_light_client --network=goerli \
|
||||||
|
--web3-url=ws://127.0.0.1:8551 --jwt-secret="$HOME/jwtsecret" \
|
||||||
|
--trusted-block-root=$TRUSTED_BLOCK_ROOT
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
The light client can be left running in the background. Note that a new trusted block root is required when restarting.
|
||||||
|
|
||||||
|
## Observing the sync process
|
||||||
|
|
||||||
|
After a while, the light client will pick up beacon block headers from the Ethereum network and start informing the EL about the latest data. You should see logs similar to the following:
|
||||||
|
|
||||||
|
### Nimbus
|
||||||
|
|
||||||
|
```
|
||||||
|
NOT 2022-07-24 21:57:57.537+02:00 Starting light client topics="lightcl" trusted_block_root=Some(f013a6f35bdfcffbf9cf8919c48bc0afb7720fb9c61f62a3659d7359f52386c4)
|
||||||
|
...
|
||||||
|
INF 2022-07-24 22:07:59.892+02:00 New LC optimistic header optimistic_header="(slot: 396960, proposer_index: 90824, parent_root: \"77d30de6\", state_root: \"9c7343a0\")"
|
||||||
|
INF 2022-07-24 22:07:59.892+02:00 New LC finalized header finalized_header="(slot: 396960, proposer_index: 90824, parent_root: \"77d30de6\", state_root: \"9c7343a0\")"
|
||||||
|
INF 2022-07-24 22:08:03.962+02:00 New LC optimistic header optimistic_header="(slot: 397539, proposer_index: 97474, parent_root: \"063c998d\", state_root: \"0f790eaf\")"
|
||||||
|
WRN 2022-07-24 22:08:09.217+02:00 Peer count low, no new peers discovered topics="networking" discovered_nodes=2 new_peers=@[] current_peers=11 wanted_peers=160
|
||||||
|
INF 2022-07-24 22:08:15.961+02:00 New LC optimistic header optimistic_header="(slot: 397540, proposer_index: 56720, parent_root: \"812d4790\", state_root: \"b846e95e\")"
|
||||||
|
INF 2022-07-24 22:08:27.961+02:00 New LC optimistic header optimistic_header="(slot: 397541, proposer_index: 65758, parent_root: \"725e435d\", state_root: \"559fd631\")"
|
||||||
|
INF 2022-07-24 22:08:39.960+02:00 New LC optimistic header optimistic_header="(slot: 397542, proposer_index: 90389, parent_root: \"903645d6\", state_root: \"873c9904\")"
|
||||||
|
WRN 2022-07-24 22:08:49.503+02:00 Peer count low, no new peers discovered topics="networking" discovered_nodes=1 new_peers=@[] current_peers=11 wanted_peers=160
|
||||||
|
INF 2022-07-24 22:08:51.960+02:00 New LC optimistic header optimistic_header="(slot: 397543, proposer_index: 73061, parent_root: \"1abdfcd1\", state_root: \"c8ee813c\")"
|
||||||
|
WRN 2022-07-24 22:08:55.097+02:00 Peer count low, no new peers discovered topics="networking" discovered_nodes=0 new_peers=@[] current_peers=11 wanted_peers=160
|
||||||
|
INF 2022-07-24 22:09:03.961+02:00 New LC optimistic header optimistic_header="(slot: 397544, proposer_index: 62086, parent_root: \"4797507d\", state_root: \"60815f6a\")"
|
||||||
|
NOT 2022-07-24 22:09:05.069+02:00 New LC optimistic block opt=c6cf8526:397409 wallSlot=397545
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The [light client protocol](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md) depends on consensus layer (CL) implementations to serve additional data. As this is a new protocol, not all implementations are supporting it yet. Therefore, it may take several minutes to discover supporting peers, during which no log messages may be produced.
|
||||||
|
|
||||||
|
### Geth
|
||||||
|
|
||||||
|
```
|
||||||
|
WARN [07-24|22:19:16.777] Ignoring payload with missing parent number=12,658,012 hash=306fad..bdfd44 parent=a22dc7..093bea
|
||||||
|
INFO [07-24|22:19:16.778] Forkchoice requested sync to new head number=12,658,012 hash=306fad..bdfd44
|
||||||
|
INFO [07-24|22:19:17.232] Syncing beacon headers downloaded=7168 left=12,650,843 eta=13m21.441s
|
||||||
|
INFO [07-24|22:19:21.626] Syncing beacon headers downloaded=75201 left=0 eta=0s
|
||||||
|
INFO [07-24|22:19:21.627] Block synchronisation started
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nethermind
|
||||||
|
|
||||||
|
```
|
||||||
|
2022-07-24 22:09:05.0853|Received a new payload: 12657968 (0xa5eedb4e4e4b0f84238464d563b82d7dddadfc68f21cfa2bfcbbbcdb944c4b63)
|
||||||
|
2022-07-24 22:09:05.1018|Insert block into cache without parent 12657968 (0xa5eedb...4c4b63)
|
||||||
|
2022-07-24 22:09:05.1141|Received: ForkchoiceState: (HeadBlockHash: 0xa5eedb4e4e4b0f84238464d563b82d7dddadfc68f21cfa2bfcbbbcdb944c4b63, SafeBlockHash: 0xa5eedb4e4e4b0f84238464d563b82d7dddadfc68f21cfa2bfcbbbcdb944c4b63, FinalizedBlockHash: 0x0000000000000000000000000000000000000000000000000000000000000000) .
|
||||||
|
2022-07-24 22:09:05.1141|Syncing... Block 0xa5eedb4e4e4b0f84238464d563b82d7dddadfc68f21cfa2bfcbbbcdb944c4b63 not found.
|
||||||
|
```
|
|
@ -14,7 +14,7 @@ You can pass one or more `--web3-url` parameters to the node. Any additional web
|
||||||
--web3-url=http://other:8545
|
--web3-url=http://other:8545
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! warn
|
!!! warning
|
||||||
You need to run your own execution client after [the merge](./merge.md) - relying on third-party services such as Infura, Alchemy and Pocket will not be possible.
|
You need to run your own execution client after [the merge](./merge.md) - relying on third-party services such as Infura, Alchemy and Pocket will not be possible.
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
|
|
|
@ -4,7 +4,7 @@ It's a good idea to add a backup web3 provider in case your main one goes down.
|
||||||
|
|
||||||
For example, if your primary execution client is a [local Geth](./eth1.md#geth), but you want to use [Infura](./infura-guide.md) as a backup you would run:
|
For example, if your primary execution client is a [local Geth](./eth1.md#geth), but you want to use [Infura](./infura-guide.md) as a backup you would run:
|
||||||
|
|
||||||
!!! warn
|
!!! warning
|
||||||
After [the merge](./merge.md), it will no longer be possible to rely on third-party services like Infura to run a beacon node!
|
After [the merge](./merge.md), it will no longer be possible to rely on third-party services like Infura to run a beacon node!
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
@ -925,8 +925,6 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
|
||||||
--rest-port="$(( BASE_REST_PORT + NUM_NODE ))" \
|
--rest-port="$(( BASE_REST_PORT + NUM_NODE ))" \
|
||||||
--metrics-port="$(( BASE_METRICS_PORT + NUM_NODE ))" \
|
--metrics-port="$(( BASE_METRICS_PORT + NUM_NODE ))" \
|
||||||
--light-client-enable=on \
|
--light-client-enable=on \
|
||||||
--light-client-data-serve=on \
|
|
||||||
--light-client-data-import-mode=only-new \
|
|
||||||
${EXTRA_ARGS} \
|
${EXTRA_ARGS} \
|
||||||
&> "${DATA_DIR}/log${NUM_NODE}.txt" &
|
&> "${DATA_DIR}/log${NUM_NODE}.txt" &
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ suite "Honest validator":
|
||||||
getAttesterSlashingsTopic(forkDigest) == "/eth2/00000000/attester_slashing/ssz_snappy"
|
getAttesterSlashingsTopic(forkDigest) == "/eth2/00000000/attester_slashing/ssz_snappy"
|
||||||
getAggregateAndProofsTopic(forkDigest) == "/eth2/00000000/beacon_aggregate_and_proof/ssz_snappy"
|
getAggregateAndProofsTopic(forkDigest) == "/eth2/00000000/beacon_aggregate_and_proof/ssz_snappy"
|
||||||
getSyncCommitteeContributionAndProofTopic(forkDigest) == "/eth2/00000000/sync_committee_contribution_and_proof/ssz_snappy"
|
getSyncCommitteeContributionAndProofTopic(forkDigest) == "/eth2/00000000/sync_committee_contribution_and_proof/ssz_snappy"
|
||||||
getLightClientFinalityUpdateTopic(forkDigest) == "/eth2/00000000/light_client_finality_update_v0/ssz_snappy"
|
getLightClientFinalityUpdateTopic(forkDigest) == "/eth2/00000000/light_client_finality_update/ssz_snappy"
|
||||||
getLightClientOptimisticUpdateTopic(forkDigest) == "/eth2/00000000/light_client_optimistic_update_v0/ssz_snappy"
|
getLightClientOptimisticUpdateTopic(forkDigest) == "/eth2/00000000/light_client_optimistic_update/ssz_snappy"
|
||||||
|
|
||||||
test "Mainnet attestation topics":
|
test "Mainnet attestation topics":
|
||||||
check:
|
check:
|
||||||
|
|
|
@ -169,9 +169,6 @@ proc startSingleNodeNetwork {.raises: [CatchableError, Defect].} =
|
||||||
"--keymanager-port=" & $keymanagerPort,
|
"--keymanager-port=" & $keymanagerPort,
|
||||||
"--keymanager-token-file=" & tokenFilePath,
|
"--keymanager-token-file=" & tokenFilePath,
|
||||||
"--suggested-fee-recipient=" & $defaultFeeRecipient,
|
"--suggested-fee-recipient=" & $defaultFeeRecipient,
|
||||||
"--light-client-enable=off",
|
|
||||||
"--light-client-data-serve=off",
|
|
||||||
"--light-client-data-import-mode=none",
|
|
||||||
"--doppelganger-detection=off"], it))
|
"--doppelganger-detection=off"], it))
|
||||||
except Exception as exc: # TODO fix confutils exceptions
|
except Exception as exc: # TODO fix confutils exceptions
|
||||||
raiseAssert exc.msg
|
raiseAssert exc.msg
|
||||||
|
|
Loading…
Reference in New Issue