Log distance to uint16 and add public neighbours calls (#371)

* Use uint16 instead of uint32 for discv5 log distance

* Make neighboursAtDistances and neighbours calls available
This commit is contained in:
Kim De Mey 2021-07-13 10:05:46 +02:00 committed by GitHub
parent 41127eaee8
commit 79911ed5d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 57 deletions

View File

@ -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* {.

View File

@ -47,7 +47,7 @@ type
port*: uint16
FindNodeMessage* = object
distances*: seq[uint32]
distances*: seq[uint16]
NodesMessage* = object
total*: uint32

View File

@ -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] =

View File

@ -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])

View File

@ -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

View File

@ -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":

View File

@ -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])