From 94a9c51d8ad646e76bfab74d7fc1da5a29d10db7 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Mon, 4 Nov 2019 22:03:22 +0200 Subject: [PATCH] Initial commit. --- beacon_chain/beacon_node.nim | 9 ++- beacon_chain/eth2_network.nim | 80 ++++++++++++-------------- beacon_chain/libp2p_backend.nim | 3 + beacon_chain/libp2p_daemon_backend.nim | 34 +++++++++++ vendor/nim-json-serialization | 2 +- vendor/nim-libp2p | 2 +- vendor/nim-serialization | 2 +- vendor/nim-web3 | 2 +- 8 files changed, 81 insertions(+), 53 deletions(-) diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index 478ece69f..f7f594b94 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -113,7 +113,7 @@ proc addBootstrapNode(node: BeaconNode, bootstrapNode: BootstrapAddr) = proc useBootstrapFile(node: BeaconNode, bootstrapFile: string) = for ln in lines(bootstrapFile): - node.addBootstrapNode BootstrapAddr.init(string ln) + node.addBootstrapNode BootstrapAddr.initAddress(string ln) proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async.} = new result @@ -756,6 +756,8 @@ proc run*(node: BeaconNode) = addTimer(second) do (p: pointer): asyncCheck node.onSecond(second) + asyncCheck node.network.backendLoop() + runForever() var gPidFile: string @@ -973,10 +975,7 @@ when isMainModule: let bootstrapFile = config.outputBootstrapFile.string if bootstrapFile.len > 0: - let bootstrapAddrLine = when networkBackend != rlpxBackend: - $bootstrapAddress.addresses[0] & "/p2p/" & bootstrapAddress.peer.pretty - else: - $bootstrapAddress + let bootstrapAddrLine = $bootstrapAddress writeFile(bootstrapFile, bootstrapAddrLine) echo "Wrote ", bootstrapFile diff --git a/beacon_chain/eth2_network.nim b/beacon_chain/eth2_network.nim index ba7b26973..2578a2c2e 100644 --- a/beacon_chain/eth2_network.nim +++ b/beacon_chain/eth2_network.nim @@ -144,7 +144,7 @@ else: else: import - libp2p/daemon/daemonapi, libp2p_daemon_backend + libp2p/daemon/daemonapi, libp2p/multiaddress, libp2p_daemon_backend export libp2p_daemon_backend @@ -154,19 +154,16 @@ else: networkKeyFilename = "privkey.protobuf" type - BootstrapAddr* = PeerInfo - Eth2NodeIdentity* = PeerInfo + BootstrapAddr* = MultiAddress + Eth2NodeIdentity* = KeyPair - proc init*(T: type BootstrapAddr, str: string): T = - # TODO: The code below is quite awkward. - # How do we parse a PeerInfo object out of a bootstrap MultiAddress string such as: - # /ip4/10.20.30.40/tcp/9100/p2p/16Uiu2HAmEAmp4FdpPzypKwTMmsbCdnUafDvXZCpFrUDbYJZNk7hX - var parts = str.split("/p2p/") - if parts.len == 2: - result.peer = PeerID.init(parts[1]) - result.addresses.add MultiAddress.init(parts[0]) + proc initAddress*(T: type BootstrapAddr, str: string): T = + let address = MultiAddress.init(str) + if IPFS.match(address) and matchPartial(multiaddress.TCP, address): + result = address else: - raise newException(ValueError, "Invalid bootstrap multi-address") + raise newException(MultiAddressError, + "Invalid bootstrap node multi-address") proc ensureNetworkIdFile(conf: BeaconNodeConf): string = result = conf.dataDir / networkKeyFilename @@ -176,13 +173,17 @@ else: writeFile(result, pk.getBytes) proc getPersistentNetIdentity*(conf: BeaconNodeConf): Eth2NodeIdentity = - # Using waitFor here is reasonable, because this proc is needed only - # prior to connecting to the network. The RLPx alternative reads from - # file and it's much easier to use if it's not async. - # TODO: revisit in the future when we have our own Lib2P2 implementation. - let daemon = waitFor newDaemonApi(id = conf.ensureNetworkIdFile) - result = waitFor daemon.identity() - waitFor daemon.close() + let privateKeyFile = conf.dataDir / networkKeyFilename + var privKey: PrivateKey + if not fileExists(privateKeyFile): + createDir conf.dataDir.string + privKey = PrivateKey.random(Secp256k1) + writeFile(privateKeyFile, privKey.getBytes()) + else: + let strdata = readFile(privateKeyFile) + privKey = PrivateKey.init(cast[seq[byte]](strdata)) + + result = KeyPair(seckey: privKey, pubkey: privKey.getKey()) template tcpEndPoint(address, port): auto = MultiAddress.init(address, Protocol.IPPROTO_TCP, port) @@ -191,8 +192,7 @@ else: proc allMultiAddresses(nodes: seq[BootstrapAddr]): seq[string] = for node in nodes: - for a in node.addresses: - result.add $a & "/ipfs/" & node.peer.pretty() + result.add $node proc createEth2Node*(conf: BeaconNodeConf, bootstrapNodes: seq[BootstrapAddr]): Future[Eth2Node] {.async.} = @@ -219,10 +219,11 @@ else: bootstrapNodes = allMultiAddresses(bootstrapNodes), peersRequired = 1) - info "Daemon started" mainDaemon = await daemonFut + info "LibP2P daemon started" + proc closeDaemon() {.noconv.} = info "Shutting down the LibP2P daemon" waitFor mainDaemon.close() @@ -232,33 +233,24 @@ else: proc getPersistenBootstrapAddr*(conf: BeaconNodeConf, ip: IpAddress, port: Port): BootstrapAddr = - result = getPersistentNetIdentity(conf) - result.addresses = @[tcpEndPoint(ip, port)] + let pair = getPersistentNetIdentity(conf) + let pidma = MultiAddress.init(multiCodec("p2p"), PeerID.init(pair.pubkey)) + result = tcpEndPoint(ip, port) & pidma proc isSameNode*(bootstrapNode: BootstrapAddr, id: Eth2NodeIdentity): bool = - bootstrapNode.peer == id.peer + if IPFS.match(bootstrapNode): + let pid1 = PeerID.init(bootstrapNode[2].protoAddress()) + let pid2 = PeerID.init(id.pubkey) + result = (pid1 == pid2) proc shortForm*(id: Eth2NodeIdentity): string = - # TODO: Make this shorter - $id + $PeerID.init(id.pubkey) - proc connectToNetwork*(node: Eth2Node, bootstrapNodes: seq[PeerInfo]) {.async.} = - # TODO: perhaps we should do these in parallel - var connected = false - for bootstrapNode in bootstrapNodes: - try: - await node.daemon.connect(bootstrapNode.peer, bootstrapNode.addresses) - var peer = node.getPeer(bootstrapNode.peer) - peer.wasDialed = true - await initializeConnection(peer) - connected = true - except CatchableError as err: - error "Failed to connect to bootstrap node", - node = bootstrapNode, err = err.msg - - if bootstrapNodes.len > 0 and connected == false: - fatal "Failed to connect to any bootstrap node. Quitting." - quit 1 + proc connectToNetwork*(node: Eth2Node, + bootstrapNodes: seq[MultiAddress]) {.async.} = + ## go-libp2p-daemon will perform connection to the network automatically, + ## we just need to follow it. + discard proc saveConnectionAddressFile*(node: Eth2Node, filename: string) = let id = waitFor node.daemon.identity() diff --git a/beacon_chain/libp2p_backend.nim b/beacon_chain/libp2p_backend.nim index a0b273171..86f91d39c 100644 --- a/beacon_chain/libp2p_backend.nim +++ b/beacon_chain/libp2p_backend.nim @@ -470,6 +470,9 @@ proc init*[MsgType](T: type Responder[MsgType], peer: Peer, stream: P2PStream): T = T(UntypedResponder(peer: peer, stream: stream)) +proc backendLoop*(node: Eth2Node) {.async.} = + discard + import typetraits diff --git a/beacon_chain/libp2p_daemon_backend.nim b/beacon_chain/libp2p_daemon_backend.nim index a0b273171..d2e812057 100644 --- a/beacon_chain/libp2p_daemon_backend.nim +++ b/beacon_chain/libp2p_daemon_backend.nim @@ -470,6 +470,40 @@ proc init*[MsgType](T: type Responder[MsgType], peer: Peer, stream: P2PStream): T = T(UntypedResponder(peer: peer, stream: stream)) +proc backendLoop*(node: Eth2Node) {.async.} = + var peerFuts = newSeq[Future[void]]() + var peerStore = newSeq[tuple[peer: Peer, future: Future[void]]]() + while true: + var list = await node.daemon.listPeers() + debug "Daemon's peer list", count = len(list) + + peerFuts.setLen(0) + peerStore.setLen(0) + + for item in list: + var peerCheck = node.peers.getOrDefault(item.peer) + if isNil(peerCheck): + var peer = node.getPeer(item.peer) + info "Handshaking with new peer", peer + let fut = initializeConnection(peer) + peerStore.add((peer, fut)) + peerFuts.add(fut) + + await allFutures(peerFuts) + + for item in peerFuts: + var peer: Peer + for storeItem in peerStore: + if item == storeItem.future: + peer = storeItem.peer + break + if item.finished(): + info "Handshake with peer succeeded", peer + elif item.failed(): + info "Handshake with peer failed", peer, error = item.error.msg + + await sleepAsync(1.seconds) + import typetraits diff --git a/vendor/nim-json-serialization b/vendor/nim-json-serialization index 88b79e230..173c7b4a8 160000 --- a/vendor/nim-json-serialization +++ b/vendor/nim-json-serialization @@ -1 +1 @@ -Subproject commit 88b79e230005d8301c3ae950abdbf8ad55e37f19 +Subproject commit 173c7b4a86e6d75a69577166526b0f5840c45003 diff --git a/vendor/nim-libp2p b/vendor/nim-libp2p index f3fc76389..1c16eb5d6 160000 --- a/vendor/nim-libp2p +++ b/vendor/nim-libp2p @@ -1 +1 @@ -Subproject commit f3fc763895986e96d53349e0696c897303104765 +Subproject commit 1c16eb5d69a9fb8e9577d68d3fb97843a40d31a4 diff --git a/vendor/nim-serialization b/vendor/nim-serialization index ae60eef4e..448a03ed4 160000 --- a/vendor/nim-serialization +++ b/vendor/nim-serialization @@ -1 +1 @@ -Subproject commit ae60eef4e8413e49fb0dbcae9a343fb479509fa0 +Subproject commit 448a03ed4bd5837e18a3c50e10c6e31d25a6c9e5 diff --git a/vendor/nim-web3 b/vendor/nim-web3 index 7bc29e747..37fc46111 160000 --- a/vendor/nim-web3 +++ b/vendor/nim-web3 @@ -1 +1 @@ -Subproject commit 7bc29e747004b8aa7988eab537029ecab73dcb45 +Subproject commit 37fc46111489bc521731b59f52f42b6327fa7829