2020-05-29 10:03:29 +00:00
|
|
|
{.push raises: [Defect].}
|
2020-04-05 19:07:13 +00:00
|
|
|
|
2019-12-13 17:30:39 +00:00
|
|
|
import
|
2020-09-11 17:46:48 +00:00
|
|
|
std/[os, strutils],
|
|
|
|
chronicles, stew/shims/net, stew/results, bearssl,
|
|
|
|
eth/keys, eth/p2p/discoveryv5/[enr, protocol, node],
|
2019-12-13 17:30:39 +00:00
|
|
|
conf
|
|
|
|
|
2020-10-27 09:00:57 +00:00
|
|
|
export protocol, keys
|
|
|
|
|
2019-12-13 17:30:39 +00:00
|
|
|
type
|
|
|
|
Eth2DiscoveryProtocol* = protocol.Protocol
|
|
|
|
Eth2DiscoveryId* = NodeId
|
2020-02-19 09:59:38 +00:00
|
|
|
PublicKey = keys.PublicKey
|
2019-12-13 17:30:39 +00:00
|
|
|
|
|
|
|
export
|
2020-05-29 10:03:29 +00:00
|
|
|
Eth2DiscoveryProtocol, open, start, close, closeWait, randomNodes, results
|
2019-12-13 17:30:39 +00:00
|
|
|
|
2020-05-29 10:03:29 +00:00
|
|
|
proc parseBootstrapAddress*(address: TaintedString):
|
|
|
|
Result[enr.Record, cstring] =
|
2020-02-05 20:40:14 +00:00
|
|
|
logScope:
|
|
|
|
address = string(address)
|
|
|
|
|
|
|
|
if address[0] == '/':
|
2020-02-12 13:41:07 +00:00
|
|
|
return err "MultiAddress bootstrap addresses are not supported"
|
2020-02-05 20:40:14 +00:00
|
|
|
else:
|
|
|
|
let lowerCaseAddress = toLowerAscii(string address)
|
|
|
|
if lowerCaseAddress.startsWith("enr:"):
|
|
|
|
var enrRec: enr.Record
|
|
|
|
if enrRec.fromURI(string address):
|
2020-02-12 13:41:07 +00:00
|
|
|
return ok enrRec
|
2020-02-05 20:40:14 +00:00
|
|
|
return err "Invalid ENR bootstrap record"
|
|
|
|
elif lowerCaseAddress.startsWith("enode:"):
|
2020-02-12 13:41:07 +00:00
|
|
|
return err "ENode bootstrap addresses are not supported"
|
2020-02-05 20:40:14 +00:00
|
|
|
else:
|
|
|
|
return err "Ignoring unrecognized bootstrap address type"
|
|
|
|
|
2020-10-29 11:09:03 +00:00
|
|
|
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
|
|
|
|
|
2020-02-12 13:41:07 +00:00
|
|
|
proc addBootstrapNode*(bootstrapAddr: string,
|
2020-09-11 17:46:48 +00:00
|
|
|
bootstrapEnrs: var seq[enr.Record]) =
|
2020-10-27 07:44:46 +00:00
|
|
|
# Ignore empty lines or lines starting with #
|
|
|
|
if bootstrapAddr.len == 0 or bootstrapAddr[0] == '#':
|
|
|
|
return
|
|
|
|
|
2020-02-19 09:59:38 +00:00
|
|
|
let enrRes = parseBootstrapAddress(bootstrapAddr)
|
|
|
|
if enrRes.isOk:
|
2020-05-29 10:03:29 +00:00
|
|
|
bootstrapEnrs.add enrRes.value
|
2020-02-05 20:40:14 +00:00
|
|
|
else:
|
|
|
|
warn "Ignoring invalid bootstrap address",
|
2020-02-19 09:59:38 +00:00
|
|
|
bootstrapAddr, reason = enrRes.error
|
2020-02-05 20:40:14 +00:00
|
|
|
|
2020-02-12 13:41:07 +00:00
|
|
|
proc loadBootstrapFile*(bootstrapFile: string,
|
2020-09-11 17:46:48 +00:00
|
|
|
bootstrapEnrs: var seq[enr.Record]) =
|
2020-02-05 20:40:14 +00:00
|
|
|
if bootstrapFile.len == 0: return
|
|
|
|
let ext = splitFile(bootstrapFile).ext
|
2020-06-19 17:42:28 +00:00
|
|
|
if cmpIgnoreCase(ext, ".txt") == 0 or cmpIgnoreCase(ext, ".enr") == 0 :
|
2020-05-29 10:03:29 +00:00
|
|
|
try:
|
2020-10-29 11:09:03 +00:00
|
|
|
for ln in strippedLines(bootstrapFile):
|
2020-09-11 17:46:48 +00:00
|
|
|
addBootstrapNode(ln, bootstrapEnrs)
|
2020-05-29 10:03:29 +00:00
|
|
|
except IOError as e:
|
|
|
|
error "Could not read bootstrap file", msg = e.msg
|
|
|
|
quit 1
|
|
|
|
|
2020-02-05 20:40:14 +00:00
|
|
|
elif cmpIgnoreCase(ext, ".yaml") == 0:
|
|
|
|
# TODO. This is very ugly, but let's try to negotiate the
|
|
|
|
# removal of YAML metadata.
|
2020-05-29 10:03:29 +00:00
|
|
|
try:
|
2020-10-29 11:09:03 +00:00
|
|
|
for ln in strippedLines(bootstrapFile):
|
2020-09-11 17:46:48 +00:00
|
|
|
addBootstrapNode(string(ln.strip()[3..^2]), bootstrapEnrs)
|
2020-05-29 10:03:29 +00:00
|
|
|
except IOError as e:
|
|
|
|
error "Could not read bootstrap file", msg = e.msg
|
|
|
|
quit 1
|
2020-02-05 20:40:14 +00:00
|
|
|
else:
|
|
|
|
error "Unknown bootstrap file format", ext
|
|
|
|
quit 1
|
|
|
|
|
2020-03-24 09:54:17 +00:00
|
|
|
proc new*(T: type Eth2DiscoveryProtocol,
|
|
|
|
conf: BeaconNodeConf,
|
2020-06-05 15:08:50 +00:00
|
|
|
ip: Option[ValidIpAddress], tcpPort, udpPort: Port,
|
2020-06-22 19:40:19 +00:00
|
|
|
pk: PrivateKey,
|
2020-10-28 18:35:31 +00:00
|
|
|
enrFields: openArray[(string, seq[byte])], rng: ref BrHmacDrbgContext):
|
2020-05-29 10:03:29 +00:00
|
|
|
T {.raises: [Exception, Defect].} =
|
2020-03-24 09:54:17 +00:00
|
|
|
# TODO
|
|
|
|
# Implement more configuration options:
|
|
|
|
# * for setting up a specific key
|
|
|
|
# * for using a persistent database
|
2020-05-29 10:03:29 +00:00
|
|
|
var bootstrapEnrs: seq[enr.Record]
|
2020-03-24 09:54:17 +00:00
|
|
|
for node in conf.bootstrapNodes:
|
2020-09-11 17:46:48 +00:00
|
|
|
addBootstrapNode(node, bootstrapEnrs)
|
|
|
|
loadBootstrapFile(string conf.bootstrapNodesFile, bootstrapEnrs)
|
2020-03-24 09:54:17 +00:00
|
|
|
|
|
|
|
let persistentBootstrapFile = conf.dataDir / "bootstrap_nodes.txt"
|
|
|
|
if fileExists(persistentBootstrapFile):
|
2020-09-11 17:46:48 +00:00
|
|
|
loadBootstrapFile(persistentBootstrapFile, bootstrapEnrs)
|
2020-03-24 09:54:17 +00:00
|
|
|
|
2020-09-27 20:00:24 +00:00
|
|
|
newProtocol(pk, ip, tcpPort, udpPort, enrFields, bootstrapEnrs,
|
|
|
|
bindIp = conf.listenAddress, rng = rng)
|