mirror of https://github.com/status-im/nim-eth.git
Fix lookup to sort and query closest nodes
This commit is contained in:
parent
0820dbba46
commit
68c9b7b3ad
|
@ -73,7 +73,7 @@
|
||||||
## This might be a concern for mobile devices.
|
## This might be a concern for mobile devices.
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[tables, sets, options, math, sequtils],
|
std/[tables, sets, options, math, sequtils, algorithm],
|
||||||
stew/shims/net as stewNet, json_serialization/std/net,
|
stew/shims/net as stewNet, json_serialization/std/net,
|
||||||
stew/endians2, chronicles, chronos, stint, bearssl,
|
stew/endians2, chronicles, chronos, stint, bearssl,
|
||||||
eth/[rlp, keys, async_utils],
|
eth/[rlp, keys, async_utils],
|
||||||
|
@ -633,6 +633,7 @@ proc lookupWorker(d: Protocol, destNode: Node, target: NodeId):
|
||||||
let dists = lookupDistances(target, destNode.id)
|
let dists = lookupDistances(target, destNode.id)
|
||||||
var i = 0
|
var i = 0
|
||||||
# TODO: We can make use of the multiple distances here now.
|
# TODO: We can make use of the multiple distances here now.
|
||||||
|
# Do findNode requests with different distances until we hit limits.
|
||||||
while i < lookupRequestLimit and result.len < findNodeResultLimit:
|
while i < lookupRequestLimit and result.len < findNodeResultLimit:
|
||||||
let r = await d.findNode(destNode, @[dists[i]])
|
let r = await d.findNode(destNode, @[dists[i]])
|
||||||
# TODO: Handle failures better. E.g. stop on different failures than timeout
|
# TODO: Handle failures better. E.g. stop on different failures than timeout
|
||||||
|
@ -648,21 +649,25 @@ proc lookup*(d: Protocol, target: NodeId): Future[seq[Node]]
|
||||||
{.async, raises: [Exception, Defect].} =
|
{.async, raises: [Exception, Defect].} =
|
||||||
## Perform a lookup for the given target, return the closest n nodes to the
|
## Perform a lookup for the given target, return the closest n nodes to the
|
||||||
## target. Maximum value for n is `BUCKET_SIZE`.
|
## target. Maximum value for n is `BUCKET_SIZE`.
|
||||||
# TODO: Sort the returned nodes on distance
|
# `closestNodes` holds the k closest nodes to target found, sorted by distance
|
||||||
# Also use unseen nodes as a form of validation.
|
# Unvalidated nodes are used for requests as a form of validation.
|
||||||
result = d.routingTable.neighbours(target, BUCKET_SIZE, seenOnly = false)
|
var closestNodes = d.routingTable.neighbours(target, BUCKET_SIZE,
|
||||||
var asked = initHashSet[NodeId]()
|
seenOnly = false)
|
||||||
asked.incl(d.localNode.id)
|
|
||||||
var seen = asked
|
var asked, seen = initHashSet[NodeId]()
|
||||||
for node in result:
|
asked.incl(d.localNode.id) # No need to ask our own node
|
||||||
|
seen.incl(d.localNode.id) # No need to discover our own node
|
||||||
|
for node in closestNodes:
|
||||||
seen.incl(node.id)
|
seen.incl(node.id)
|
||||||
|
|
||||||
var pendingQueries = newSeqOfCap[Future[seq[Node]]](alpha)
|
var pendingQueries = newSeqOfCap[Future[seq[Node]]](alpha)
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < result.len and pendingQueries.len < alpha:
|
# Doing `alpha` amount of requests at once as long as closer non queried
|
||||||
let n = result[i]
|
# nodes are discovered.
|
||||||
|
while i < closestNodes.len and pendingQueries.len < alpha:
|
||||||
|
let n = closestNodes[i]
|
||||||
if not asked.containsOrIncl(n.id):
|
if not asked.containsOrIncl(n.id):
|
||||||
pendingQueries.add(d.lookupWorker(n, target))
|
pendingQueries.add(d.lookupWorker(n, target))
|
||||||
inc i
|
inc i
|
||||||
|
@ -682,10 +687,19 @@ proc lookup*(d: Protocol, target: NodeId): Future[seq[Node]]
|
||||||
error "Resulting query should have beeen in the pending queries"
|
error "Resulting query should have beeen in the pending queries"
|
||||||
|
|
||||||
let nodes = query.read
|
let nodes = query.read
|
||||||
|
# TODO: Remove node on timed-out query?
|
||||||
for n in nodes:
|
for n in nodes:
|
||||||
if not seen.containsOrIncl(n.id):
|
if not seen.containsOrIncl(n.id):
|
||||||
if result.len < BUCKET_SIZE:
|
# If it wasn't seen before, insert node while remaining sorted
|
||||||
result.add(n)
|
closestNodes.insert(n, closestNodes.lowerBound(n,
|
||||||
|
proc(x: Node, n: Node): int =
|
||||||
|
cmp(distanceTo(x, target), distanceTo(n, target))
|
||||||
|
))
|
||||||
|
|
||||||
|
if closestNodes.len > BUCKET_SIZE:
|
||||||
|
closestNodes.del(closestNodes.high())
|
||||||
|
|
||||||
|
return closestNodes
|
||||||
|
|
||||||
proc lookupRandom*(d: Protocol): Future[seq[Node]]
|
proc lookupRandom*(d: Protocol): Future[seq[Node]]
|
||||||
{.async, raises:[Exception, Defect].} =
|
{.async, raises:[Exception, Defect].} =
|
||||||
|
|
|
@ -87,7 +87,7 @@ const
|
||||||
DefaultTableIpLimits* = TableIpLimits(tableIpLimit: DefaultTableIpLimit,
|
DefaultTableIpLimits* = TableIpLimits(tableIpLimit: DefaultTableIpLimit,
|
||||||
bucketIpLimit: DefaultBucketIpLimit)
|
bucketIpLimit: DefaultBucketIpLimit)
|
||||||
|
|
||||||
proc distanceTo(n: Node, id: NodeId): UInt256 =
|
proc distanceTo*(n: Node, id: NodeId): UInt256 =
|
||||||
## Calculate the distance to a NodeId.
|
## Calculate the distance to a NodeId.
|
||||||
n.id xor id
|
n.id xor id
|
||||||
|
|
||||||
|
|
|
@ -256,8 +256,7 @@ procSuite "Discovery v5 Tests":
|
||||||
let target = nodes[i]
|
let target = nodes[i]
|
||||||
let discovered = await nodes[nodeCount-1].lookup(target.localNode.id)
|
let discovered = await nodes[nodeCount-1].lookup(target.localNode.id)
|
||||||
debug "Lookup result", target = target.localNode, discovered
|
debug "Lookup result", target = target.localNode, discovered
|
||||||
# if lookUp would return ordered on distance we could check discovered[0]
|
check discovered[0] == target.localNode
|
||||||
check discovered.contains(target.localNode)
|
|
||||||
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
await node.closeWait()
|
await node.closeWait()
|
||||||
|
|
Loading…
Reference in New Issue