nimbus-eth2/beacon_chain/networking/eth2_discovery.nim

103 lines
3.7 KiB
Nim

# beacon_chain
# Copyright (c) 2018-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
chronicles, stew/shims/net, stew/results,
eth/p2p/discoveryv5/[enr, protocol, node],
".."/[conf, conf_light_client]
from std/os import splitFile
from std/strutils import cmpIgnoreCase, split, startsWith, strip, toLowerAscii
export protocol
type
Eth2DiscoveryProtocol* = protocol.Protocol
Eth2DiscoveryId* = NodeId
export
Eth2DiscoveryProtocol, open, start, close, closeWait, queryRandom,
updateRecord, results
func parseBootstrapAddress*(address: string):
Result[enr.Record, cstring] =
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
# Ignore comments in
# https://github.com/eth-clients/eth2-networks/blob/063f826a03676c33c95a66306916f18b690d35eb/shared/mainnet/bootstrap_nodes.txt
let enrRes = parseBootstrapAddress(bootstrapAddr.split(" # ")[0])
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
else:
error "Unknown bootstrap file format", ext
quit 1
proc new*(T: type Eth2DiscoveryProtocol,
config: BeaconNodeConf | LightClientConf,
enrIp: Option[IpAddress], enrTcpPort, enrUdpPort: Option[Port],
pk: PrivateKey,
enrFields: openArray[(string, seq[byte])], rng: ref HmacDrbgContext):
T =
# TODO
# Implement more configuration options:
# * for setting up a specific key
# * for using a persistent database
var bootstrapEnrs: seq[enr.Record]
for node in config.bootstrapNodes:
addBootstrapNode(node, bootstrapEnrs)
loadBootstrapFile(string config.bootstrapNodesFile, bootstrapEnrs)
when config is BeaconNodeConf:
let persistentBootstrapFile = config.dataDir / "bootstrap_nodes.txt"
if fileExists(persistentBootstrapFile):
loadBootstrapFile(persistentBootstrapFile, bootstrapEnrs)
newProtocol(pk, enrIp, enrTcpPort, enrUdpPort, enrFields, bootstrapEnrs,
bindPort = config.udpPort, bindIp = config.listenAddress,
enrAutoUpdate = config.enrAutoUpdate, rng = rng)