diff --git a/eth/p2p/discoveryv5/dcli.nim b/eth/p2p/discoveryv5/dcli.nim index f56074a..f0c89f9 100644 --- a/eth/p2p/discoveryv5/dcli.nim +++ b/eth/p2p/discoveryv5/dcli.nim @@ -79,7 +79,7 @@ type distance* {. defaultValue: 255 desc: "Distance parameter for the findNode message" - name: "distance" .}: uint32 + name: "distance" .}: uint16 # TODO: Order here matters as else the help message does not show all the # information, see: https://github.com/status-im/nim-confutils/issues/15 findNodeTarget* {. diff --git a/eth/p2p/discoveryv5/messages.nim b/eth/p2p/discoveryv5/messages.nim index 46a8c4c..d6fbf56 100644 --- a/eth/p2p/discoveryv5/messages.nim +++ b/eth/p2p/discoveryv5/messages.nim @@ -47,7 +47,7 @@ type port*: uint16 FindNodeMessage* = object - distances*: seq[uint32] + distances*: seq[uint16] NodesMessage* = object total*: uint32 diff --git a/eth/p2p/discoveryv5/node.nim b/eth/p2p/discoveryv5/node.nim index 03b8af8..d5c1f07 100644 --- a/eth/p2p/discoveryv5/node.nim +++ b/eth/p2p/discoveryv5/node.nim @@ -31,6 +31,8 @@ type func toNodeId*(pk: PublicKey): NodeId = ## Convert public key to a node identifier. + # Keccak256 hash is used as defined in ENR spec for scheme v4: + # https://github.com/ethereum/devp2p/blob/master/enr.md#v4-identity-scheme readUintBE[256](keccak256.digest(pk.toRaw()).data) func newNode*(r: Record): Result[Node, cstring] = diff --git a/eth/p2p/discoveryv5/protocol.nim b/eth/p2p/discoveryv5/protocol.nim index aa0366c..4712b6e 100644 --- a/eth/p2p/discoveryv5/protocol.nim +++ b/eth/p2p/discoveryv5/protocol.nim @@ -197,9 +197,15 @@ proc randomNodes*(d: Protocol, maxAmount: int, ## the nodes selected are filtered by provided `enrField`. d.randomNodes(maxAmount, proc(x: Node): bool = x.record.contains(enrField)) -proc neighbours*(d: Protocol, id: NodeId, k: int = BUCKET_SIZE): seq[Node] = +proc neighbours*(d: Protocol, id: NodeId, k: int = BUCKET_SIZE, + seenOnly = false): seq[Node] = ## Return up to k neighbours (closest node ids) of the given node id. - d.routingTable.neighbours(id, k) + d.routingTable.neighbours(id, k, seenOnly) + +proc neighboursAtDistances*(d: Protocol, distances: seq[uint16], + k: int = BUCKET_SIZE, seenOnly = false): seq[Node] = + ## Return up to k neighbours (closest node ids) at given distances. + d.routingTable.neighboursAtDistances(distances, k, seenOnly) proc nodesDiscovered*(d: Protocol): int = d.routingTable.len @@ -293,7 +299,7 @@ proc handleFindNode(d: Protocol, fromId: NodeId, fromAddr: Address, d.sendNodes(fromId, fromAddr, reqId, [d.localNode]) else: # TODO: Still deduplicate also? - if fn.distances.all(proc (x: uint32): bool = return x <= 256): + if fn.distances.all(proc (x: uint16): bool = return x <= 256): d.sendNodes(fromId, fromAddr, reqId, d.routingTable.neighboursAtDistances(fn.distances, seenOnly = true)) else: @@ -491,7 +497,7 @@ proc waitMessage(d: Protocol, fromNode: Node, reqId: RequestId): d.awaitedMessages[key] = result proc verifyNodesRecords*(enrs: openarray[Record], fromNode: Node, - distances: varargs[uint32]): seq[Node] = + distances: varargs[uint16]): seq[Node] = ## Verify and convert ENRs to a sequence of nodes. Only ENRs that pass ## verification will be added. ENRs are verified for duplicates, invalid ## addresses and invalid distances. @@ -609,7 +615,7 @@ proc ping*(d: Protocol, toNode: Node): discovery_message_requests_outgoing.inc(labelValues = ["no_response"]) return err("Pong message not received in time") -proc findNode*(d: Protocol, toNode: Node, distances: seq[uint32]): +proc findNode*(d: Protocol, toNode: Node, distances: seq[uint16]): Future[DiscResult[seq[Node]]] {.async.} = ## Send a discovery findNode message. ## @@ -648,14 +654,14 @@ proc talkreq*(d: Protocol, toNode: Node, protocol, request: seq[byte]): discovery_message_requests_outgoing.inc(labelValues = ["no_response"]) return err("Talk response message not received in time") -proc lookupDistances(target, dest: NodeId): seq[uint32] = +proc lookupDistances(target, dest: NodeId): seq[uint16] = let td = logDist(target, dest) result.add(td) - var i = 1'u32 + var i = 1'u16 while result.len < lookupRequestLimit: if td + i < 256: result.add(td + i) - if td - i > 0'u32: + if td - i > 0'u16: result.add(td - i) inc i @@ -804,7 +810,7 @@ proc resolve*(d: Protocol, id: NodeId): Future[Option[Node]] {.async.} = let node = d.getNode(id) if node.isSome(): - let request = await d.findNode(node.get(), @[0'u32]) + let request = await d.findNode(node.get(), @[0'u16]) # TODO: Handle failures better. E.g. stop on different failures than timeout if request.isOk() and request[].len > 0: @@ -853,7 +859,7 @@ proc revalidateNode*(d: Protocol, n: Node) {.async.} = let res = pong.get() if res.enrSeq > n.record.seqNum: # Request new ENR - let nodes = await d.findNode(n, @[0'u32]) + let nodes = await d.findNode(n, @[0'u16]) if nodes.isOk() and nodes[].len > 0: discard d.addNode(nodes[][0]) diff --git a/eth/p2p/discoveryv5/routing_table.nim b/eth/p2p/discoveryv5/routing_table.nim index 52b878d..687b7a5 100644 --- a/eth/p2p/discoveryv5/routing_table.nim +++ b/eth/p2p/discoveryv5/routing_table.nim @@ -98,7 +98,7 @@ proc distanceTo*(n: Node, id: NodeId): UInt256 = ## Calculate the distance to a NodeId. n.id xor id -proc logDist*(a, b: NodeId): uint32 = +proc logDist*(a, b: NodeId): uint16 = ## Calculate the logarithmic distance between two `NodeId`s. ## ## According the specification, this is the log base 2 of the distance. But it @@ -115,7 +115,7 @@ proc logDist*(a, b: NodeId): uint32 = else: lz += bitops.countLeadingZeroBits(x) break - return uint32(a.len * 8 - lz) + return uint16(a.len * 8 - lz) proc newKBucket(istart, iend: NodeId, bucketIpLimit: uint): KBucket = result.new() @@ -421,7 +421,7 @@ proc neighbours*(r: RoutingTable, id: NodeId, k: int = BUCKET_SIZE, if result.len > k: result.setLen(k) -proc idAtDistance*(id: NodeId, dist: uint32): NodeId = +proc idAtDistance*(id: NodeId, dist: uint16): NodeId = ## Calculate the "lowest" `NodeId` for given logarithmic distance. ## A logarithmic distance obviously covers a whole range of distances and thus ## potential `NodeId`s. @@ -429,7 +429,7 @@ proc idAtDistance*(id: NodeId, dist: uint32): NodeId = # zeroes and xor those` with the id. id xor (1.stuint(256) shl (dist.int - 1)) -proc neighboursAtDistance*(r: RoutingTable, distance: uint32, +proc neighboursAtDistance*(r: RoutingTable, distance: uint16, k: int = BUCKET_SIZE, seenOnly = false): seq[Node] = ## Return up to k neighbours at given logarithmic distance. result = r.neighbours(idAtDistance(r.thisNode.id, distance), k, seenOnly) @@ -437,7 +437,7 @@ proc neighboursAtDistance*(r: RoutingTable, distance: uint32, # that are exactly the requested distance. keepIf(result, proc(n: Node): bool = logDist(n.id, r.thisNode.id) == distance) -proc neighboursAtDistances*(r: RoutingTable, distances: seq[uint32], +proc neighboursAtDistances*(r: RoutingTable, distances: seq[uint16], k: int = BUCKET_SIZE, seenOnly = false): seq[Node] = ## Return up to k neighbours at given logarithmic distances. # TODO: This will currently return nodes with neighbouring distances on the diff --git a/tests/p2p/test_discoveryv5.nim b/tests/p2p/test_discoveryv5.nim index b98b997..26340e5 100644 --- a/tests/p2p/test_discoveryv5.nim +++ b/tests/p2p/test_discoveryv5.nim @@ -62,19 +62,19 @@ procSuite "Discovery v5 Tests": const targetId = "0x0000" testValues = [ - ("0x0001", 1'u32), - ("0x0002", 2'u32), - ("0x0003", 2'u32), - ("0x0004", 3'u32), - ("0x0007", 3'u32), - ("0x0008", 4'u32), - ("0x000f", 4'u32), - ("0x0080", 8'u32), - ("0x00ff", 8'u32), - ("0x0100", 9'u32), - ("0x01ff", 9'u32), - ("0x8000", 16'u32), - ("0xffff", 16'u32) + ("0x0001", 1'u16), + ("0x0002", 2'u16), + ("0x0003", 2'u16), + ("0x0004", 3'u16), + ("0x0007", 3'u16), + ("0x0008", 4'u16), + ("0x000f", 4'u16), + ("0x0080", 8'u16), + ("0x00ff", 8'u16), + ("0x0100", 9'u16), + ("0x01ff", 9'u16), + ("0x8000", 16'u16), + ("0xffff", 16'u16) ] for (id, d) in testValues: @@ -84,12 +84,12 @@ procSuite "Discovery v5 Tests": const targetKey = "5d485bdcbe9bc89314a10ae9231e429d33853e3a8fa2af39f5f827370a2e4185e344ace5d16237491dad41f278f1d3785210d29ace76cd627b9147ee340b1125" testValues = [ - ("29738ba0c1a4397d6a65f292eee07f02df8e58d41594ba2be3cf84ce0fc58169", 251'u32), - ("1c9b1cafbec00848d2c174b858219914b42a7d5c9359b1ca03fd650e8239ae94", 252'u32), - ("2d0511ae9bf590166597eeab86b6f27b1ab761761eaea8965487b162f8703847", 253'u32), - ("dec742079ec00ff4ec1284d7905bc3de2366f67a0769431fd16f80fd68c58a7c", 254'u32), - ("da8645f90826e57228d9ea72aff84500060ad111a5d62e4af831ed8e4b5acfb8", 255'u32), - ("8c5b422155d33ea8e9d46f71d1ad3e7b24cb40051413ffa1a81cff613d243ba9", 256'u32) + ("29738ba0c1a4397d6a65f292eee07f02df8e58d41594ba2be3cf84ce0fc58169", 251'u16), + ("1c9b1cafbec00848d2c174b858219914b42a7d5c9359b1ca03fd650e8239ae94", 252'u16), + ("2d0511ae9bf590166597eeab86b6f27b1ab761761eaea8965487b162f8703847", 253'u16), + ("dec742079ec00ff4ec1284d7905bc3de2366f67a0769431fd16f80fd68c58a7c", 254'u16), + ("da8645f90826e57228d9ea72aff84500060ad111a5d62e4af831ed8e4b5acfb8", 255'u16), + ("8c5b422155d33ea8e9d46f71d1ad3e7b24cb40051413ffa1a81cff613d243ba9", 256'u16) ] let targetId = toNodeId(PublicKey.fromHex(targetKey)[]) @@ -102,13 +102,13 @@ procSuite "Discovery v5 Tests": const targetId = "0x0000" testValues = [ # possible id in that distance range - ("0x0001", 1'u32), - ("0x0002", 2'u32), - ("0x0004", 3'u32), - ("0x0008", 4'u32), - ("0x0080", 8'u32), - ("0x0100", 9'u32), - ("0x8000", 16'u32) + ("0x0001", 1'u16), + ("0x0002", 2'u16), + ("0x0004", 3'u16), + ("0x0008", 4'u16), + ("0x0080", 8'u16), + ("0x0100", 9'u16), + ("0x8000", 16'u16) ] for (id, d) in testValues: @@ -118,12 +118,12 @@ procSuite "Discovery v5 Tests": const targetKey = "5d485bdcbe9bc89314a10ae9231e429d33853e3a8fa2af39f5f827370a2e4185e344ace5d16237491dad41f278f1d3785210d29ace76cd627b9147ee340b1125" testValues = [ # possible id in that distance range - ("9e5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 251'u32), - ("925b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 252'u32), - ("8a5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 253'u32), - ("ba5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 254'u32), - ("da5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 255'u32), - ("1a5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 256'u32) + ("9e5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 251'u16), + ("925b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 252'u16), + ("8a5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 253'u16), + ("ba5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 254'u16), + ("da5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 255'u16), + ("1a5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 256'u16) ] let targetId = toNodeId(PublicKey.fromHex(targetKey)[]) @@ -132,7 +132,7 @@ procSuite "Discovery v5 Tests": check idAtDistance(targetId, d) == parse(id, UInt256, 16) asyncTest "FindNode Test": - const dist = 253'u32 + const dist = 253'u16 let mainNodeKey = PrivateKey.fromHex( "a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[] @@ -152,7 +152,7 @@ procSuite "Discovery v5 Tests": # Get ENR of the node itself var discovered = - await findNode(testNode, mainNode.localNode, @[0'u32]) + await findNode(testNode, mainNode.localNode, @[0'u16]) check: discovered.isOk discovered[].len == 1 @@ -167,14 +167,14 @@ procSuite "Discovery v5 Tests": # Too high logarithmic distance, should return no nodes. discovered = - await findNode(testNode, mainNode.localNode, @[4294967295'u32]) + await findNode(testNode, mainNode.localNode, @[high(uint16)]) check: discovered.isOk discovered[].len == 0 # Logarithmic distance of 256 should only return the testNode discovered = - await findNode(testNode, mainNode.localNode, @[256'u32]) + await findNode(testNode, mainNode.localNode, @[256'u16]) check: discovered.isOk discovered[].len == 1 @@ -182,7 +182,7 @@ procSuite "Discovery v5 Tests": # Empty bucket discovered = - await findNode(testNode, mainNode.localNode, @[254'u32]) + await findNode(testNode, mainNode.localNode, @[254'u16]) check discovered.isOk check discovered[].len == 0 @@ -297,7 +297,7 @@ procSuite "Discovery v5 Tests": # Request the target ENR and manually add it to the routing table. # Ping for handshake based ENR passing will not work as our previous # session will still be in the LRU cache. - let nodes = await mainNode.findNode(targetNode.localNode, @[0'u32]) + let nodes = await mainNode.findNode(targetNode.localNode, @[0'u16]) check: nodes.isOk() nodes[].len == 1 @@ -524,7 +524,7 @@ procSuite "Discovery v5 Tests": 1, pk, some(ValidIpAddress.init("12.13.14.15")), some(port), some(port))[] records = [recordInvalidDistance] - test = verifyNodesRecords(records, fromNode, 0'u32) + test = verifyNodesRecords(records, fromNode, 0'u16) check test.len == 0 asyncTest "Handshake cleanup: different ids": diff --git a/tests/p2p/test_discoveryv5_encoding.nim b/tests/p2p/test_discoveryv5_encoding.nim index 873b831..f93ee5e 100644 --- a/tests/p2p/test_discoveryv5_encoding.nim +++ b/tests/p2p/test_discoveryv5_encoding.nim @@ -51,7 +51,7 @@ suite "Discovery v5.1 Protocol Message Encodings": test "FindNode Request": let - distances = @[0x0100'u32] + distances = @[0x0100'u16] fn = FindNodeMessage(distances: distances) reqId = RequestId(id: @[1.byte])