# # Ethereum P2P # (c) Copyright 2018 # Status Research & Development GmbH # # See the file "LICENSE", included in this # distribution, for details about the copyright. # {.used.} import std/[options, sequtils], asynctest, bearssl/rand, chronicles, chronos, nimcrypto, libp2p/crypto/[crypto, secp], libp2p/[multiaddress, multicodec, multihash, routing_record, signed_envelope], libp2pdht/dht, libp2pdht/discv5/crypto as dhtcrypto, libp2pdht/discv5/protocol as discv5_protocol, stew/byteutils, test_helper proc bootstrapNodes( nodecount: int, bootnodes: seq[SignedPeerRecord], rng = newRng(), delay: int = 0 ) : Future[seq[(discv5_protocol.Protocol, PrivateKey)]] {.async.} = debug "---- STARTING BOOSTRAPS ---" for i in 0.. 0: await sleepAsync(chronos.milliseconds(delay)) except TransportOsError as e: echo "skipping node ",i ,":", e.msg #await allFutures(result.mapIt(it.bootstrap())) # this waits for bootstrap based on bootENode, which includes bonding with all its ping pongs proc bootstrapNetwork( nodecount: int, rng = newRng(), delay: int = 0 ) : Future[seq[(discv5_protocol.Protocol, PrivateKey)]] {.async.} = let bootNodeKey = PrivateKey.fromHex( "a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617") .expect("Valid private key hex") bootNodeAddr = localAddress(20301) bootNode = initDiscoveryNode(rng, bootNodeKey, bootNodeAddr, @[]) # just a shortcut for new and open #waitFor bootNode.bootstrap() # immediate, since no bootnodes are defined above var res = await bootstrapNodes(nodecount - 1, @[bootnode.localNode.record], rng, delay) res.insert((bootNode, bootNodeKey), 0) return res # suite "Providers Tests": suite "Providers Tests: node alone": var rng: ref HmacDrbgContext nodes: seq[(discv5_protocol.Protocol, PrivateKey)] targetId: NodeId node0: discv5_protocol.Protocol privKey0: PrivateKey signedPeerRec0: SignedPeerRecord peerRec0: PeerRecord setupAll: rng = newRng() nodes = await bootstrapNetwork(nodecount=1) targetId = NodeId.example(rng) (node0, privKey0) = nodes[0] signedPeerRec0 = privKey0.toSignedPeerRecord peerRec0 = signedPeerRec0.data teardownAll: for (n, _) in nodes: await n.closeWait() await sleepAsync(chronos.seconds(3)) test "Node in isolation should store": debug "---- ADDING PROVIDERS ---", nodes = nodes.len let addedTo = await node0.addProvider(targetId, signedPeerRec0) debug "Provider added to: ", addedTo debug "---- STARTING CHECKS ---" check (addedTo.len == 1) check (addedTo[0].id == node0.localNode.id) check ((await node0.getProvidersLocal(targetId))[0].data.peerId == peerRec0.peerId) test "Node in isolation should retrieve": debug "---- STARTING PROVIDERS LOOKUP ---" let providersRes = await node0.getProviders(targetId) debug "---- STARTING CHECKS ---" check providersRes.isOk let providers = providersRes.get debug "Providers:", providers check (providers.len > 0 and providers[0].data.peerId == peerRec0.peerId) test "Should not retrieve bogus": let bogusId = NodeId.example(rng) debug "---- STARTING PROVIDERS LOOKUP ---" let providersRes = await node0.getProviders(bogusId) debug "---- STARTING CHECKS ---" check providersRes.isOk let providers = providersRes.get debug "Providers:", providers check (providers.len == 0) suite "Providers Tests: two nodes": var rng: ref HmacDrbgContext nodes: seq[(discv5_protocol.Protocol, PrivateKey)] targetId: NodeId node0: discv5_protocol.Protocol privKey0: PrivateKey signedPeerRec0: SignedPeerRecord peerRec0: PeerRecord setupAll: rng = newRng() nodes = await bootstrapNetwork(nodecount=3) targetId = NodeId.example(rng) (node0, privKey0) = nodes[0] signedPeerRec0 = privKey0.toSignedPeerRecord peerRec0 = signedPeerRec0.data teardownAll: for (n, _) in nodes: await n.closeWait() await sleepAsync(chronos.seconds(3)) test "2 nodes, store and retrieve from same": debug "---- ADDING PROVIDERS ---" let addedTo = await node0.addProvider(targetId, signedPeerRec0) debug "Provider added to: ", addedTo debug "---- STARTING PROVIDERS LOOKUP ---" let providersRes = await node0.getProviders(targetId) debug "---- STARTING CHECKS ---" check providersRes.isOk let providers = providersRes.get debug "Providers:", providers check (providers.len == 1 and providers[0].data.peerId == peerRec0.peerId) test "2 nodes, retrieve from other": debug "---- STARTING PROVIDERS LOOKUP ---" let (node1, _) = nodes[1] let providersRes = await node1.getProviders(targetId) debug "---- STARTING CHECKS ---" let providers = providersRes.get debug "Providers:", providers check (providers.len == 1 and providers[0].data.peerId == peerRec0.peerId) suite "Providers Tests: many nodes": let nodecount = 1000 delay_pernode = 10 # in millisec delay_init = 15*1000 # in millisec var rng: ref HmacDrbgContext nodes: seq[(discv5_protocol.Protocol, PrivateKey)] targetId: NodeId node0: discv5_protocol.Protocol privKey0: PrivateKey signedPeerRec0: SignedPeerRecord peerRec0: PeerRecord setupAll: rng = newRng() nodes = await bootstrapNetwork(nodecount=nodecount, delay=delay_pernode) targetId = NodeId.example(rng) (node0, privKey0) = nodes[0] signedPeerRec0 = privKey0.toSignedPeerRecord peerRec0 = signedPeerRec0.data await sleepAsync(chronos.milliseconds(delay_init)) teardownAll: for (n, _) in nodes: # if last test is enabled, we need nodes[1..^1] here await n.closeWait() test $nodecount & " nodes, store and retrieve from same": debug "---- ADDING PROVIDERS ---" let addedTo = await node0.addProvider(targetId, signedPeerRec0) debug "Provider added to: ", addedTo debug "---- STARTING PROVIDERS LOOKUP ---" let providersRes = await node0.getProviders(targetId) debug "---- STARTING CHECKS ---" let providers = providersRes.get debug "Providers:", providers check (providers.len == 1 and providers[0].data.peerId == peerRec0.peerId) test $nodecount & " nodes, retrieve from other": debug "---- STARTING PROVIDERS LOOKUP ---" let (node19, _) = nodes[^2] let providersRes = await node19.getProviders(targetId) debug "---- STARTING CHECKS ---" let providers = providersRes.get debug "Providers:", providers check (providers.len == 1 and providers[0].data.peerId == peerRec0.peerId) test $nodecount & " nodes, retrieve after bootnodes dies": debug "---- KILLING BOOTSTRAP NODE ---" let (node0, _) = nodes[0] let (node18, _) = nodes[^2] await node0.closeWait() nodes.del(0) debug "---- STARTING PROVIDERS LOOKUP ---" let providersRes = await node18.getProviders(targetId) debug "---- STARTING CHECKS ---" let providers = providersRes.get debug "Providers:", providers check (providers.len == 1 and providers[0].data.peerId == peerRec0.peerId) test $nodecount & " nodes, lookup each other": debug "---- STARTING NODE LOOKUP ---" var tested = 0 passed = 0 for (n, _) in nodes[1..^1]: for (target, _) in nodes[1..^1]: if n != target: # TODO: fix self-lookup info "Start lookup", src = n.localNode, dst = target.localNode let startTime = Moment.now() let discovered = await n.lookup(target.localNode.id, fast = true) let pass = (discovered[0] == target.localNode) info "Lookup", pass, src = n.localNode, dst = target.localNode, time = Moment.now() - startTime check pass tested += 1 passed += int(pass) info "Lookup ratio", passed, tested test $nodecount & " nodes, lookup random": debug "---- STARTING NODE LOOKUP ---" var tested = 0 passed = 0 for (n, _) in nodes[1..^1]: for (target, _) in nodes[1..^1]: if n != target: # TODO: fix self-lookup info "Start lookup", src = n.localNode, dst = target.localNode let startTime = Moment.now() let discovered = await n.lookup(target.localNode.id, fast = true) let pass = (discovered[0] == target.localNode) info "Lookup", pass, src = n.localNode, dst = target.localNode, time = Moment.now() - startTime check pass tested += 1 passed += int(pass) info "Lookup ratio", passed, tested, ratio = passed/tested test $nodecount & " nodes, addValue and getValue": let key = NodeId.example(rng) v = @[byte 1,2,3] debug "---- ADDING VALUE ---" let addedTo = await node0.addValue(key, v) debug "Value added to: ", addedTo debug "---- STARTING VALUE LOOKUP ---" let res = await node0.getValue(key) debug "---- STARTING CHECKS ---" let v2 = res.get debug "Value:", v2 check (v2 == v)