102 lines
3.7 KiB
Nim

# Presets are hard-coded configuration bundles that get compiled into code. They can refer
# to different things, but the canonical example are sets of bootstrap nodes that define
# logically different networks; e.g., "logos.dev" and "logos.test" refer to the Logos
# devnet and latest testnet, respectively.
import std/options
import std/strutils
import std/json
import pkg/chronicles
import pkg/codexdht/discv5/protocol
import pkg/libp2p/[errors, routing_record]
import pkg/results
import pkg/stew/base64
# A NetworkPreset is a set of bootstrap nodes (represented
# by their signed peer records) along with some description metadata.
type NetworkPreset* = object
name*: string
description*: string
unparsedRecords: seq[string]
proc init*(
T: type NetworkPreset, name: string, description: string, records: seq[string]
): T =
result.name = name
result.description = description
# We have to delay parsing of records to runtime because
# of https://github.com/nim-lang/Nim/issues/23468
result.unparsedRecords = records
func `$`*(preset: NetworkPreset): string =
"[" & preset.name & "]: " & preset.description
func describePresets(presets: openArray[NetworkPreset]): string =
result = ""
for preset in presets:
result &= $preset & "; "
proc parse*(T: type SignedPeerRecord, p: string): Result[SignedPeerRecord, string] =
var res: SignedPeerRecord
try:
if not res.fromURI(p):
return err("The uri is not a valid SignedPeerRecord: " & p)
return ok(res)
except LPError, Base64Error:
let e = getCurrentException()
return err(e.msg)
proc `bootstrapNodes`*(self: NetworkPreset): seq[SignedPeerRecord] =
for record in self.unparsedRecords:
# Having an invalid SPR in a hardcoded config is a bug, a+
# it should crash the node.
result.add(parse(SignedPeerRecord, record).tryGet())
# Bootstrap node SPRs live in a single source-of-truth config file at the repo
# root. staticRead embeds it into the binary at build time, so the node remains
# self-contained (the file is not needed alongside the binary at runtime). The
# same file is read at runtime by the bootstrap liveness checker (tools/check_spr).
const networkPresetsJson = staticRead("../network_presets.json")
proc parsePresetsJson(jsonStr: string): seq[NetworkPreset] =
let root = parseJson(jsonStr)
for p in root["presets"].items:
var records: seq[string]
for r in p["records"].items:
records.add(r.getStr)
result.add(NetworkPreset.init(p["name"].getStr, p["description"].getStr, records))
const NetworkPresets* = parsePresetsJson(networkPresetsJson)
proc loadNetworkPresets*(path: string): seq[NetworkPreset] =
## Runtime loader for the same bootstrap-node config file embedded at compile
## time into `NetworkPresets`. Used by the bootstrap liveness checker so the
## node and the checker share one source of truth.
parsePresetsJson(readFile(path))
proc rawRecords*(self: NetworkPreset): seq[string] =
## The unparsed `spr:` strings for this preset (for tooling that wants to
## handle parse failures per-record instead of crashing).
self.unparsedRecords
proc `default`*(presets: openArray[NetworkPreset]): NetworkPreset =
presets[0]
# Precomputes those as as consts so we can use them in nim-confutils CLI
# help strings.
const
NetworkPresetsDescription* = describePresets(NetworkPresets)
DefaultNetworkPreset* = NetworkPresets.default
proc find*(presets: openArray[NetworkPreset], p: string): Option[NetworkPreset] =
for preset in presets:
if preset.name == p:
return some(preset)
return none(NetworkPreset)
proc findByPrefix*(presets: openArray[NetworkPreset], val: string): seq[string] =
for p in presets:
if p.name.startsWith(val):
result.add p.name