288 lines
8.6 KiB
Nim
288 lines
8.6 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2023 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
|
|
chronicles, chronos,
|
|
stew/byteutils,
|
|
eth/async_utils,
|
|
beacon_chain/networking/network_metadata,
|
|
beacon_chain/spec//eth2_apis/rest_beacon_client,
|
|
beacon_chain/beacon_clock,
|
|
../../network/beacon_light_client/beacon_light_client_content,
|
|
./exporter_common
|
|
|
|
export beacon_clock
|
|
|
|
const
|
|
restRequestsTimeout = 30.seconds
|
|
|
|
proc getBeaconData*(): (
|
|
RuntimeConfig, ref ForkDigests, BeaconClock) {.raises: [IOError].} =
|
|
let
|
|
metadata = getMetadataForNetwork("mainnet")
|
|
genesisState =
|
|
try:
|
|
template genesisData(): auto = metadata.genesis.bakedBytes
|
|
newClone(readSszForkedHashedBeaconState(
|
|
metadata.cfg,
|
|
genesisData.toOpenArray(genesisData.low, genesisData.high)))
|
|
except CatchableError as err:
|
|
raiseAssert "Invalid baked-in state: " & err.msg
|
|
genesis_validators_root =
|
|
getStateField(genesisState[], genesis_validators_root)
|
|
forkDigests = newClone ForkDigests.init(
|
|
metadata.cfg, genesis_validators_root)
|
|
|
|
beaconClock = BeaconClock.init(getStateField(genesisState[], genesis_time))
|
|
|
|
|
|
return (metadata.cfg, forkDigests, beaconClock)
|
|
|
|
func forkDigestAtEpoch(
|
|
forkDigests: ForkDigests, epoch: Epoch, cfg: RuntimeConfig): ForkDigest =
|
|
forkDigests.atEpoch(epoch, cfg)
|
|
|
|
proc exportLCBootstrapUpdate*(
|
|
restUrl: string, dataDir: string,
|
|
trustedBlockRoot: Eth2Digest,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests) {.async.} =
|
|
let file = "light-client-bootstrap.json"
|
|
let fh = createAndOpenFile(dataDir, file)
|
|
|
|
defer:
|
|
try:
|
|
fh.close()
|
|
except IOError as e:
|
|
fatal "Error occured while closing file", error = e.msg
|
|
quit 1
|
|
|
|
var
|
|
client = RestClientRef.new(restUrl).valueOr:
|
|
error "Cannot connect to server", error = error
|
|
quit 1
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
|
|
var update =
|
|
try:
|
|
notice "Downloading LC bootstrap"
|
|
awaitWithTimeout(
|
|
client.getLightClientBootstrap(
|
|
trustedBlockRoot,
|
|
cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
error "Attempt to download LC bootstrap timed out"
|
|
quit 1
|
|
except CatchableError as exc:
|
|
error "Unable to download LC bootstrap", error = exc.msg
|
|
quit 1
|
|
|
|
withForkyObject(update):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
slot = forkyObject.header.beacon.slot
|
|
contentKey = encode(bootstrapContentKey(trustedBlockRoot))
|
|
forkDigest = forkDigestAtEpoch(
|
|
forkDigests[], epoch(slot), cfg)
|
|
content = encodeBootstrapForked(
|
|
forkDigest,
|
|
update
|
|
)
|
|
|
|
let portalContent = JsonPortalContent(
|
|
content_key: contentKey.asSeq().to0xHex(),
|
|
content_value: content.to0xHex()
|
|
)
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
contentTable[$slot] = portalContent
|
|
|
|
writePortalContentToJson(fh, contentTable)
|
|
|
|
proc exportLCUpdates*(
|
|
restUrl: string, dataDir: string,
|
|
startPeriod: uint64, count: uint64,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests) {.async.} =
|
|
let file = "light-client-updates.json"
|
|
let fh = createAndOpenFile(dataDir, file)
|
|
|
|
defer:
|
|
try:
|
|
fh.close()
|
|
except IOError as e:
|
|
fatal "Error occured while closing file", error = e.msg
|
|
quit 1
|
|
|
|
var
|
|
client = RestClientRef.new(restUrl).valueOr:
|
|
error "Cannot connect to server", error = error
|
|
quit 1
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
|
|
var updates =
|
|
try:
|
|
notice "Downloading LC updates"
|
|
awaitWithTimeout(
|
|
client.getLightClientUpdatesByRange(
|
|
SyncCommitteePeriod(startPeriod), count, cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
error "Attempt to download LC updates timed out"
|
|
quit 1
|
|
except CatchableError as exc:
|
|
error "Unable to download LC updates", error = exc.msg
|
|
quit 1
|
|
|
|
if updates.len() > 0:
|
|
withForkyObject(updates[0]):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
slot = forkyObject.attested_header.beacon.slot
|
|
period = forkyObject.attested_header.beacon.slot.sync_committee_period
|
|
contentKey = encode(updateContentKey(period.uint64, count))
|
|
forkDigest = forkDigestAtEpoch(
|
|
forkDigests[], epoch(forkyObject.attested_header.beacon.slot), cfg)
|
|
|
|
content = encodeLightClientUpdatesForked(
|
|
forkDigest,
|
|
updates
|
|
)
|
|
|
|
|
|
let portalContent = JsonPortalContent(
|
|
content_key: contentKey.asSeq().to0xHex(),
|
|
content_value: content.to0xHex()
|
|
)
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
contentTable[$slot] = portalContent
|
|
|
|
writePortalContentToJson(fh, contentTable)
|
|
else:
|
|
error "No updates downloaded"
|
|
quit 1
|
|
|
|
proc exportLCFinalityUpdate*(
|
|
restUrl: string, dataDir: string,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests) {.async.} =
|
|
let file = "light-client-finality-update.json"
|
|
let fh = createAndOpenFile(dataDir, file)
|
|
|
|
defer:
|
|
try:
|
|
fh.close()
|
|
except IOError as e:
|
|
fatal "Error occured while closing file", error = e.msg
|
|
quit 1
|
|
|
|
var
|
|
client = RestClientRef.new(restUrl).valueOr:
|
|
error "Cannot connect to server", error = error
|
|
quit 1
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
|
|
var update =
|
|
try:
|
|
notice "Downloading LC finality update"
|
|
awaitWithTimeout(
|
|
client.getLightClientFinalityUpdate(
|
|
cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
error "Attempt to download LC finality update timed out"
|
|
quit 1
|
|
except CatchableError as exc:
|
|
error "Unable to download LC finality update", error = exc.msg
|
|
quit 1
|
|
|
|
withForkyObject(update):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
finalizedSlot = forkyObject.finalized_header.beacon.slot
|
|
optimisticSlot = forkyObject.attested_header.beacon.slot
|
|
contentKey = encode(finalityUpdateContentKey(
|
|
finalizedSlot.uint64, optimisticSlot.uint64))
|
|
contentId = beacon_light_client_content.toContentId(contentKey)
|
|
forkDigest = forkDigestAtEpoch(
|
|
forkDigests[], epoch(forkyObject.attested_header.beacon.slot), cfg)
|
|
content = encodeFinalityUpdateForked(
|
|
forkDigest,
|
|
update
|
|
)
|
|
|
|
let portalContent = JsonPortalContent(
|
|
content_key: contentKey.asSeq().to0xHex(),
|
|
content_value: content.to0xHex()
|
|
)
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
contentTable[$optimisticSlot] = portalContent
|
|
|
|
writePortalContentToJson(fh, contentTable)
|
|
|
|
proc exportLCOptimisticUpdate*(
|
|
restUrl: string, dataDir: string,
|
|
cfg: RuntimeConfig, forkDigests: ref ForkDigests) {.async.} =
|
|
let file = "light-client-optimistic-update.json"
|
|
let fh = createAndOpenFile(dataDir, file)
|
|
|
|
defer:
|
|
try:
|
|
fh.close()
|
|
except IOError as e:
|
|
fatal "Error occured while closing file", error = e.msg
|
|
quit 1
|
|
|
|
var
|
|
client = RestClientRef.new(restUrl).valueOr:
|
|
error "Cannot connect to server", error = error
|
|
quit 1
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
|
|
var update =
|
|
try:
|
|
notice "Downloading LC optimistic update"
|
|
awaitWithTimeout(
|
|
client.getLightClientOptimisticUpdate(
|
|
cfg, forkDigests),
|
|
restRequestsTimeout
|
|
):
|
|
error "Attempt to download LC optimistic update timed out"
|
|
quit 1
|
|
except CatchableError as exc:
|
|
error "Unable to download LC optimistic update", error = exc.msg
|
|
quit 1
|
|
|
|
withForkyObject(update):
|
|
when lcDataFork > LightClientDataFork.None:
|
|
let
|
|
slot = forkyObject.attested_header.beacon.slot
|
|
contentKey = encode(optimisticUpdateContentKey(slot.uint64))
|
|
contentId = beacon_light_client_content.toContentId(contentKey)
|
|
forkDigest = forkDigestAtEpoch(
|
|
forkDigests[], epoch(forkyObject.attested_header.beacon.slot), cfg)
|
|
content = encodeOptimisticUpdateForked(
|
|
forkDigest,
|
|
update
|
|
)
|
|
|
|
let portalContent = JsonPortalContent(
|
|
content_key: contentKey.asSeq().to0xHex(),
|
|
content_value: content.to0xHex()
|
|
)
|
|
|
|
var contentTable: JsonPortalContentTable
|
|
contentTable[$slot] = portalContent
|
|
|
|
writePortalContentToJson(fh, contentTable)
|