nimbus-eth2/beacon_chain/eth2_network.nim

132 lines
4.0 KiB
Nim

import
options, chronos, json_serialization, strutils,
spec/digest, version, conf
const
clientId = "Nimbus beacon node v" & fullVersionStr()
when useRLPx:
import
os,
eth/[rlp, p2p, keys], gossipsub_protocol,
eth/p2p/peer_pool # for log on connected peers
export
p2p, rlp, gossipsub_protocol
const
netBackendName* = "rlpx"
type
Eth2Node* = EthereumNode
BootstrapAddr* = ENode
template libp2pProtocol*(name, version: string) {.pragma.}
func parseNat(nat: string): IpAddress =
# TODO we should try to discover the actual external IP, in case we're
# behind a nat / upnp / etc..
if nat.startsWith("extip:"):
parseIpAddress(nat[6..^1])
else:
parseIpAddress("127.0.0.1")
proc ensureNetworkKeys*(conf: BeaconNodeConf): KeyPair =
let privateKeyFile = conf.dataDir / "network.privkey"
var privKey: PrivateKey
if not fileExists(privateKeyFile):
privKey = newPrivateKey()
createDir conf.dataDir.string
writeFile(privateKeyFile, $privKey)
else:
privKey = initPrivateKey(readFile(privateKeyFile).string)
KeyPair(seckey: privKey, pubkey: privKey.getPublicKey())
proc getPersistenBootstrapAddr*(conf: BeaconNodeConf,
ip: IpAddress, port: Port): BootstrapAddr =
let
keys = ensureNetworkKeys(conf)
address = Address(ip: ip, tcpPort: port, udpPort: port)
initENode(keys.pubKey, address)
proc writeValue*(writer: var JsonWriter, value: BootstrapAddr) {.inline.} =
writer.writeValue $value
proc readValue*(reader: var JsonReader, value: var BootstrapAddr) {.inline.} =
value = initENode reader.readValue(string)
proc createEth2Node*(conf: BeaconNodeConf): Future[EthereumNode] {.async.} =
let
keys = ensureNetworkKeys(conf)
address = Address(ip: parseNat(conf.nat),
tcpPort: Port conf.tcpPort,
udpPort: Port conf.udpPort)
# TODO there are more networking options to add here: local bind ip, ipv6
# etc.
return newEthereumNode(keys, address, 0,
nil, clientId, minPeers = 1)
proc saveConnectionAddressFile*(node: Eth2Node, filename: string) =
writeFile(filename, $node.listeningAddress)
proc init*(T: type BootstrapAddr, str: string): T =
initENode(str)
func connectedPeers*(enode: EthereumNode): int =
enode.peerPool.len
else:
import
libp2p/daemon/daemonapi, chronicles,
libp2p_backend
export
libp2p_backend
type
BootstrapAddr* = PeerInfo
const
netBackendName* = "libp2p"
proc writeValue*(writer: var JsonWriter, value: PeerID) {.inline.} =
writer.writeValue value.pretty
proc readValue*(reader: var JsonReader, value: var PeerID) {.inline.} =
value = PeerID.init reader.readValue(string)
proc writeValue*(writer: var JsonWriter, value: MultiAddress) {.inline.} =
writer.writeValue $value
proc readValue*(reader: var JsonReader, value: var MultiAddress) {.inline.} =
value = MultiAddress.init reader.readValue(string)
proc init*(T: type BootstrapAddr, str: string): T =
Json.decode(str, PeerInfo)
proc createEth2Node*(conf: BeaconNodeConf): Future[Eth2Node] {.async.} =
var node = new Eth2Node
await node.init()
return node
proc connectToNetwork*(node: Eth2Node, bootstrapNodes: seq[PeerInfo]) {.async.} =
# TODO: perhaps we should do these in parallel
for bootstrapNode in bootstrapNodes:
try:
await node.daemon.connect(bootstrapNode.peer, bootstrapNode.addresses)
let peer = node.getPeer(bootstrapNode.peer)
await peer.performProtocolHandshakes()
except PeerDisconnected:
error "Failed to connect to bootstrap node", node = bootstrapNode
proc saveConnectionAddressFile*(node: Eth2Node, filename: string) =
let id = waitFor node.daemon.identity()
Json.saveFile(filename, id, pretty = false)
proc loadConnectionAddressFile*(filename: string): PeerInfo =
Json.loadFile(filename, PeerInfo)