240 lines
8.0 KiB
Nim
240 lines
8.0 KiB
Nim
# Fluffy
|
|
# Copyright (c) 2023-2024 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: [].}
|
|
|
|
import
|
|
chronos,
|
|
chronicles, chronicles/topics_registry,
|
|
stew/byteutils,
|
|
eth/async_utils,
|
|
json_rpc/clients/httpclient,
|
|
beacon_chain/spec/eth2_apis/rest_beacon_client,
|
|
../../network/beacon/beacon_content,
|
|
../../rpc/portal_rpc_client,
|
|
../eth_data_exporter/cl_data_exporter
|
|
|
|
const
|
|
restRequestsTimeout = 30.seconds
|
|
|
|
# TODO: From nimbus_binary_common, but we don't want to import that.
|
|
proc sleepAsync*(t: TimeDiff): Future[void] =
|
|
sleepAsync(nanoseconds(
|
|
if t.nanoseconds < 0: 0'i64 else: t.nanoseconds))
|
|
|
|
proc gossipLCBootstrapUpdate*(
|
|
restClient: RestClientRef, portalRpcClient: RpcHttpClient,
|
|
trustedBlockRoot: Eth2Digest,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests):
|
|
Future[Result[void, string]] {.async.} =
|
|
var bootstrap =
|
|
try:
|
|
info "Downloading LC bootstrap"
|
|
awaitWithTimeout(
|
|
restClient.getLightClientBootstrap(
|
|
trustedBlockRoot,
|
|
cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
return err("Attempt to download LC bootstrap timed out")
|
|
except CatchableError as exc:
|
|
return err("Unable to download LC bootstrap: " & exc.msg)
|
|
|
|
withForkyObject(bootstrap):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
slot = forkyObject.header.beacon.slot
|
|
contentKey = encode(bootstrapContentKey(trustedBlockRoot))
|
|
forkDigest = forkDigestAtEpoch(
|
|
forkDigests[], epoch(slot), cfg)
|
|
content = encodeBootstrapForked(
|
|
forkDigest,
|
|
bootstrap
|
|
)
|
|
|
|
proc GossipRpcAndClose(): Future[Result[void, string]] {.async.} =
|
|
try:
|
|
let
|
|
contentKeyHex = contentKey.asSeq().toHex()
|
|
peers = await portalRpcClient.portal_beaconRandomGossip(
|
|
contentKeyHex,
|
|
content.toHex())
|
|
info "Beacon LC bootstrap gossiped", peers,
|
|
contentKey = contentKeyHex
|
|
return ok()
|
|
except CatchableError as e:
|
|
return err("JSON-RPC error: " & $e.msg)
|
|
|
|
let res = await GossipRpcAndClose()
|
|
if res.isOk():
|
|
return ok()
|
|
else:
|
|
return err(res.error)
|
|
|
|
else:
|
|
return err("No LC bootstraps pre Altair")
|
|
|
|
proc gossipLCUpdates*(
|
|
restClient: RestClientRef, portalRpcClient: RpcHttpClient,
|
|
startPeriod: uint64, count: uint64,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests):
|
|
Future[Result[void, string]] {.async.} =
|
|
var updates =
|
|
try:
|
|
info "Downloading LC updates", count
|
|
awaitWithTimeout(
|
|
restClient.getLightClientUpdatesByRange(
|
|
SyncCommitteePeriod(startPeriod), count, cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
return err("Attempt to download LC updates timed out")
|
|
except CatchableError as exc:
|
|
return err("Unable to download LC updates: " & exc.msg)
|
|
|
|
if updates.len() > 0:
|
|
withForkyObject(updates[0]):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
slot = forkyObject.attested_header.beacon.slot
|
|
period = slot.sync_committee_period
|
|
contentKey = encode(updateContentKey(period.uint64, count))
|
|
forkDigest = forkDigestAtEpoch(forkDigests[], epoch(slot), cfg)
|
|
|
|
content = encodeLightClientUpdatesForked(
|
|
forkDigest,
|
|
updates
|
|
)
|
|
|
|
proc GossipRpcAndClose(): Future[Result[void, string]] {.async.} =
|
|
try:
|
|
let
|
|
contentKeyHex = contentKey.asSeq().toHex()
|
|
peers = await portalRpcClient.portal_beaconRandomGossip(
|
|
contentKeyHex,
|
|
content.toHex())
|
|
info "Beacon LC update gossiped", peers,
|
|
contentKey = contentKeyHex, period, count
|
|
return ok()
|
|
except CatchableError as e:
|
|
return err("JSON-RPC error: " & $e.msg)
|
|
|
|
let res = await GossipRpcAndClose()
|
|
if res.isOk():
|
|
return ok()
|
|
else:
|
|
return err(res.error)
|
|
else:
|
|
return err("No LC updates pre Altair")
|
|
else:
|
|
# TODO:
|
|
# currently only error if no updates at all found. This might be due
|
|
# to selecting future period or too old period.
|
|
# Might want to error here in case count != updates.len or might not want to
|
|
# error at all and perhaps return the updates.len.
|
|
return err("No updates downloaded")
|
|
|
|
proc gossipLCFinalityUpdate*(
|
|
restClient: RestClientRef, portalRpcClient: RpcHttpClient,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests):
|
|
Future[Result[Slot, string]] {.async.} =
|
|
var update =
|
|
try:
|
|
info "Downloading LC finality update"
|
|
awaitWithTimeout(
|
|
restClient.getLightClientFinalityUpdate(
|
|
cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
return err("Attempt to download LC finality update timed out")
|
|
except CatchableError as exc:
|
|
return err("Unable to download LC finality update: " & exc.msg)
|
|
|
|
withForkyObject(update):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
finalizedSlot = forkyObject.finalized_header.beacon.slot
|
|
contentKey = encode(finalityUpdateContentKey(finalizedSlot.uint64))
|
|
forkDigest = forkDigestAtEpoch(
|
|
forkDigests[], epoch(forkyObject.attested_header.beacon.slot), cfg)
|
|
content = encodeFinalityUpdateForked(
|
|
forkDigest,
|
|
update
|
|
)
|
|
|
|
proc GossipRpcAndClose(): Future[Result[void, string]] {.async.} =
|
|
try:
|
|
let
|
|
contentKeyHex = contentKey.asSeq().toHex()
|
|
peers = await portalRpcClient.portal_beaconRandomGossip(
|
|
contentKeyHex,
|
|
content.toHex())
|
|
info "Beacon LC finality update gossiped", peers,
|
|
contentKey = contentKeyHex, finalizedSlot
|
|
return ok()
|
|
except CatchableError as e:
|
|
return err("JSON-RPC error: " & $e.msg)
|
|
|
|
let res = await GossipRpcAndClose()
|
|
if res.isOk():
|
|
return ok(finalizedSlot)
|
|
else:
|
|
return err(res.error)
|
|
|
|
else:
|
|
return err("No LC updates pre Altair")
|
|
|
|
proc gossipLCOptimisticUpdate*(
|
|
restClient: RestClientRef, portalRpcClient: RpcHttpClient,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests):
|
|
Future[Result[Slot, string]] {.async.} =
|
|
var update =
|
|
try:
|
|
info "Downloading LC optimistic update"
|
|
awaitWithTimeout(
|
|
restClient.getLightClientOptimisticUpdate(
|
|
cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
return err("Attempt to download LC optimistic update timed out")
|
|
except CatchableError as exc:
|
|
return err("Unable to download LC optimistic update: " & exc.msg)
|
|
|
|
withForkyObject(update):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
slot = forkyObject.signature_slot
|
|
contentKey = encode(optimisticUpdateContentKey(slot.uint64))
|
|
forkDigest = forkDigestAtEpoch(
|
|
forkDigests[], epoch(forkyObject.attested_header.beacon.slot), cfg)
|
|
content = encodeOptimisticUpdateForked(
|
|
forkDigest,
|
|
update
|
|
)
|
|
|
|
proc GossipRpcAndClose(): Future[Result[void, string]] {.async.} =
|
|
try:
|
|
let
|
|
contentKeyHex = contentKey.asSeq().toHex()
|
|
peers = await portalRpcClient.portal_beaconRandomGossip(
|
|
contentKeyHex,
|
|
content.toHex())
|
|
info "Beacon LC optimistic update gossiped", peers,
|
|
contentKey = contentKeyHex, slot
|
|
|
|
return ok()
|
|
except CatchableError as e:
|
|
return err("JSON-RPC error: " & $e.msg)
|
|
|
|
let res = await GossipRpcAndClose()
|
|
if res.isOk():
|
|
return ok(slot)
|
|
else:
|
|
return err(res.error)
|
|
|
|
else:
|
|
return err("No LC updates pre Altair")
|