Add light client mode to fluffy (#1396)
This commit is contained in:
parent
270ce41d5c
commit
2bce04afc7
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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]:
|
||||||
|
|
Loading…
Reference in New Issue