nimbus-eth2/beacon_chain/eth2_discovery.nim

106 lines
3.4 KiB
Nim

{.push raises: [Defect].}
import
std/[os, strutils],
chronicles, stew/shims/net, stew/results, bearssl,
eth/keys, eth/p2p/discoveryv5/[enr, protocol, node],
conf
export protocol, keys
type
Eth2DiscoveryProtocol* = protocol.Protocol
Eth2DiscoveryId* = NodeId
PublicKey = keys.PublicKey
export
Eth2DiscoveryProtocol, open, start, close, closeWait, randomNodes, results
proc parseBootstrapAddress*(address: TaintedString):
Result[enr.Record, cstring] =
logScope:
address = string(address)
if address[0] == '/':
return err "MultiAddress bootstrap addresses are not supported"
else:
let lowerCaseAddress = toLowerAscii(string address)
if lowerCaseAddress.startsWith("enr:"):
var enrRec: enr.Record
if enrRec.fromURI(string address):
return ok enrRec
return err "Invalid ENR bootstrap record"
elif lowerCaseAddress.startsWith("enode:"):
return err "ENode bootstrap addresses are not supported"
else:
return err "Ignoring unrecognized bootstrap address type"
iterator strippedLines(filename: string): string {.raises: [ref IOError].} =
for line in lines(filename):
let stripped = strip(line)
if stripped.startsWith('#'): # Comments
continue
if stripped.len > 0:
yield stripped
proc addBootstrapNode*(bootstrapAddr: string,
bootstrapEnrs: var seq[enr.Record]) =
# Ignore empty lines or lines starting with #
if bootstrapAddr.len == 0 or bootstrapAddr[0] == '#':
return
let enrRes = parseBootstrapAddress(bootstrapAddr)
if enrRes.isOk:
bootstrapEnrs.add enrRes.value
else:
warn "Ignoring invalid bootstrap address",
bootstrapAddr, reason = enrRes.error
proc loadBootstrapFile*(bootstrapFile: string,
bootstrapEnrs: var seq[enr.Record]) =
if bootstrapFile.len == 0: return
let ext = splitFile(bootstrapFile).ext
if cmpIgnoreCase(ext, ".txt") == 0 or cmpIgnoreCase(ext, ".enr") == 0 :
try:
for ln in strippedLines(bootstrapFile):
addBootstrapNode(ln, bootstrapEnrs)
except IOError as e:
error "Could not read bootstrap file", msg = e.msg
quit 1
elif cmpIgnoreCase(ext, ".yaml") == 0:
# TODO. This is very ugly, but let's try to negotiate the
# removal of YAML metadata.
try:
for ln in strippedLines(bootstrapFile):
addBootstrapNode(string(ln.strip()[3..^2]), bootstrapEnrs)
except IOError as e:
error "Could not read bootstrap file", msg = e.msg
quit 1
else:
error "Unknown bootstrap file format", ext
quit 1
proc new*(T: type Eth2DiscoveryProtocol,
conf: BeaconNodeConf,
ip: Option[ValidIpAddress], tcpPort, udpPort: Port,
pk: PrivateKey,
enrFields: openArray[(string, seq[byte])], rng: ref BrHmacDrbgContext):
T {.raises: [Exception, Defect].} =
# TODO
# Implement more configuration options:
# * for setting up a specific key
# * for using a persistent database
var bootstrapEnrs: seq[enr.Record]
for node in conf.bootstrapNodes:
addBootstrapNode(node, bootstrapEnrs)
loadBootstrapFile(string conf.bootstrapNodesFile, bootstrapEnrs)
let persistentBootstrapFile = conf.dataDir / "bootstrap_nodes.txt"
if fileExists(persistentBootstrapFile):
loadBootstrapFile(persistentBootstrapFile, bootstrapEnrs)
newProtocol(pk, ip, tcpPort, udpPort, enrFields, bootstrapEnrs,
bindIp = conf.listenAddress, rng = rng)