Add light client mode to fluffy (#1396)

This commit is contained in:
KonradStaniec 2022-12-27 15:57:10 +01:00 committed by GitHub
parent 270ce41d5c
commit 2bce04afc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 168 additions and 4 deletions

View File

@ -12,6 +12,8 @@ import
uri, confutils, confutils/std/net, chronicles, uri, confutils, confutils/std/net, chronicles,
eth/keys, eth/net/nat, eth/p2p/discoveryv5/[enr, node], eth/keys, eth/net/nat, eth/p2p/discoveryv5/[enr, node],
json_rpc/rpcproxy, json_rpc/rpcproxy,
nimcrypto/hash,
stew/byteutils,
./network/wire/portal_protocol_config ./network/wire/portal_protocol_config
proc defaultDataDir*(): string = proc defaultDataDir*(): string =
@ -40,6 +42,8 @@ const
defaultStorageSizeDesc* = $defaultStorageSize defaultStorageSizeDesc* = $defaultStorageSize
type type
TrustedDigest* = MDigest[32 * 8]
PortalCmd* = enum PortalCmd* = enum
noCommand noCommand
@ -208,12 +212,25 @@ type
defaultValueDesc: $defaultStorageSizeDesc defaultValueDesc: $defaultStorageSizeDesc
name: "storage-size" .}: uint32 name: "storage-size" .}: uint32
trustedBlockRoot* {.
desc: "Recent trusted finalized block root to initialize the consensus light client from. " &
"If not provided by the user, portal light client will be disabled."
defaultValue: none(TrustedDigest)
name: "trusted-block-root" .}: Option[TrustedDigest]
case cmd* {. case cmd* {.
command command
defaultValue: noCommand .}: PortalCmd defaultValue: noCommand .}: PortalCmd
of noCommand: of noCommand:
discard discard
func parseCmdArg*(T: type TrustedDigest, input: string): T
{.raises: [ValueError, Defect].} =
TrustedDigest.fromHex(input)
func completeCmdArg*(T: type TrustedDigest, input: string): seq[string] =
return @[]
proc parseCmdArg*(T: type enr.Record, p: TaintedString): T proc parseCmdArg*(T: type enr.Record, p: TaintedString): T
{.raises: [Defect, ConfigurationError].} = {.raises: [Defect, ConfigurationError].} =
if not fromURI(result, p): if not fromURI(result, p):

View File

@ -14,11 +14,22 @@ import
json_rpc/rpcproxy, stew/[byteutils, io2], json_rpc/rpcproxy, stew/[byteutils, io2],
eth/keys, eth/net/nat, eth/keys, eth/net/nat,
eth/p2p/discoveryv5/protocol as discv5_protocol, eth/p2p/discoveryv5/protocol as discv5_protocol,
beacon_chain/beacon_clock,
beacon_chain/spec/forks,
beacon_chain/spec/datatypes/altair,
beacon_chain/gossip_processing/light_client_processor,
./conf, ./network_metadata, ./common/common_utils, ./conf, ./network_metadata, ./common/common_utils,
./rpc/[rpc_eth_api, bridge_client, rpc_discovery_api, rpc_portal_api, ./rpc/[rpc_eth_api, bridge_client, rpc_discovery_api, rpc_portal_api,
rpc_portal_debug_api], rpc_portal_debug_api],
./network/state/[state_network, state_content], ./network/state/[state_network, state_content],
./network/history/[history_network, history_content], ./network/history/[history_network, history_content],
./network/beacon_light_client/[
light_client_init_loader,
light_client_content,
beacon_light_client,
light_client_db,
light_client_network
],
./network/wire/[portal_stream, portal_protocol_config], ./network/wire/[portal_stream, portal_protocol_config],
./data/[history_data_seeding, history_data_parser], ./data/[history_data_seeding, history_data_parser],
./content_db ./content_db
@ -163,6 +174,78 @@ proc run(config: PortalConf) {.raises: [CatchableError, Defect].} =
let bridgeClient = initializeBridgeClient(config.bridgeUri) let bridgeClient = initializeBridgeClient(config.bridgeUri)
d.start() d.start()
# TODO: Currently disabled by default as it is not stable/polished enough,
# ultimatetely this should probably be always on.
if config.trustedBlockRoot.isSome():
# fluffy light client works only over mainnet data
let
networkData = loadNetworkData("mainnet")
db = LightClientDb.new(config.dataDir / "lightClientDb")
lightClientNetwork = LightClientNetwork.new(
d,
db,
streamManager,
networkData.forks,
bootstrapRecords = bootstrapRecords)
getBeaconTime = networkData.clock.getBeaconTimeFn()
refDigests = newClone networkData.forks
lc = LightClient.new(
lightClientNetwork,
rng,
networkData.metadata.cfg,
refDigests,
getBeaconTime,
networkData.genesis_validators_root,
LightClientFinalizationMode.Optimistic
)
# TODO: For now just log headers. Ultimately we should also use callbacks for each
# lc object to save them to db and offer them to the network.
proc onFinalizedHeader(
lightClient: LightClient, finalizedHeader: BeaconBlockHeader) =
info "New LC finalized header",
finalized_header = shortLog(finalizedHeader)
proc onOptimisticHeader(
lightClient: LightClient, optimisticHeader: BeaconBlockHeader) =
info "New LC optimistic header",
optimistic_header = shortLog(optimisticHeader)
lc.onFinalizedHeader = onFinalizedHeader
lc.onOptimisticHeader = onOptimisticHeader
lc.trustedBlockRoot = config.trustedBlockRoot
proc onSecond(time: Moment) =
let wallSlot = getBeaconTime().slotOrZero()
# TODO this is a place to enable/disable gossip based on the current status
# of light client
# lc.updateGossipStatus(wallSlot + 1)
proc runOnSecondLoop() {.async.} =
let sleepTime = chronos.seconds(1)
while true:
let start = chronos.now(chronos.Moment)
await chronos.sleepAsync(sleepTime)
let afterSleep = chronos.now(chronos.Moment)
let sleepTime = afterSleep - start
onSecond(start)
let finished = chronos.now(chronos.Moment)
let processingTime = finished - afterSleep
trace "onSecond task completed", sleepTime, processingTime
onSecond(Moment.now())
lightClientNetwork.start()
lc.start()
asyncSpawn runOnSecondLoop()
historyNetwork.start() historyNetwork.start()
stateNetwork.start() stateNetwork.start()

View File

@ -5,8 +5,11 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # * 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. # at your option. This file may not be copied, modified, or distributed except according to those terms.
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
{.push raises: [Defect].}
import import
chronicles, chronicles,

View File

@ -0,0 +1,56 @@
# Nimbus - Portal Network
# 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.
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
import
beacon_chain/networking/network_metadata,
beacon_chain/spec/forks,
beacon_chain/spec/datatypes/altair,
beacon_chain/beacon_clock,
beacon_chain/nimbus_binary_common,
beacon_chain/conf
type
NetworkInitData* = object
clock*: BeaconClock
metaData*: Eth2NetworkMetadata
forks*: ForkDigests
genesis_validators_root*: Eth2Digest
proc loadNetworkData*(networkName: string): NetworkInitData {.raises: [CatchableError, Defect].}=
let
metadata =
try:
loadEth2Network(some("mainnet"))
except CatchableError as exc:
raiseAssert(exc.msg)
genesisState =
try:
template genesisData(): auto = metadata.genesisData
newClone(readSszForkedHashedBeaconState(
metadata.cfg, genesisData.toOpenArrayByte(genesisData.low, genesisData.high)))
except CatchableError as err:
raiseAssert "Invalid baked-in state: " & err.msg
beaconClock = BeaconClock.init(getStateField(genesisState[], genesis_time))
genesis_validators_root =
getStateField(genesisState[], genesis_validators_root)
forks = newClone ForkDigests.init(metadata.cfg, genesis_validators_root)
return NetworkInitData(
clock: beaconClock,
metaData: metaData,
forks: forks[],
genesis_validators_root: genesis_validators_root
)

View File

@ -14,7 +14,11 @@ import
eth/[common/eth_types, rlp], eth/[common/eth_types, rlp],
../../nimbus/rpc/[rpc_types, hexstrings, filters], ../../nimbus/rpc/[rpc_types, hexstrings, filters],
../../nimbus/transaction, ../../nimbus/transaction,
../../nimbus/common/chain_config, # TODO: this is a bit weird but having this import makes beacon_light_client
# to fail compilation due throwing undeclared `CatchableError` in
# `vendor/nimbus-eth2/beacon_chain/spec/keystore.nim`. This is most probably
# caused by `readValue` clashing ?
# ../../nimbus/common/chain_config
../network/history/[history_network, history_content] ../network/history/[history_network, history_content]
# Subset of Eth JSON-RPC API: https://eth.wiki/json-rpc/API # Subset of Eth JSON-RPC API: https://eth.wiki/json-rpc/API
@ -190,8 +194,9 @@ proc installEthApiHandlers*(
# Supported API through the Portal Network # Supported API through the Portal Network
rpcServerWithProxy.rpc("eth_chainId") do() -> HexQuantityStr: rpcServerWithProxy.rpc("eth_chainId") do() -> HexQuantityStr:
# The Portal Network can only support MainNet at the moment # The Portal Network can only support MainNet at the moment, so always return
return encodeQuantity(distinctBase(MainNet.ChainId)) # 1
return encodeQuantity(uint64(1))
rpcServerWithProxy.rpc("eth_getBlockByHash") do( rpcServerWithProxy.rpc("eth_getBlockByHash") do(
data: EthHashStr, fullTransactions: bool) -> Option[BlockObject]: data: EthHashStr, fullTransactions: bool) -> Option[BlockObject]: