Clean-up routing table object constructions (#395)

This commit is contained in:
Kim De Mey 2021-09-07 11:56:16 +02:00 committed by GitHub
parent c078f85e48
commit bea1f1c6a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 83 deletions

View File

@ -984,7 +984,7 @@ proc newProtocol*(privKey: PrivateKey,
# TODO Consider whether this should be a Defect
doAssert rng != nil, "RNG initialization failed"
result = Protocol(
Protocol(
privateKey: privKey,
localNode: node,
bindAddress: Address(ip: ValidIpAddress.init(bindIp), port: bindPort),
@ -993,10 +993,9 @@ proc newProtocol*(privKey: PrivateKey,
bootstrapRecords: @bootstrapRecords,
ipVote: IpVote.init(),
enrAutoUpdate: enrAutoUpdate,
routingTable: RoutingTable.init(node, DefaultBitsPerHop, tableIpLimits, rng),
rng: rng)
result.routingTable.init(node, DefaultBitsPerHop, tableIpLimits, rng)
proc open*(d: Protocol) {.raises: [Defect, CatchableError].} =
info "Starting discovery node", node = d.localNode,
bindAddress = d.bindAddress

View File

@ -139,13 +139,13 @@ const
XorDistanceCalculator* = DistanceCalculator(calculateDistance: distanceTo,
calculateLogDistance: logDist, calculateIdAtDistance: idAtDistance)
proc newKBucket(istart, iend: NodeId, bucketIpLimit: uint): KBucket =
result.new()
result.istart = istart
result.iend = iend
result.nodes = @[]
result.replacementCache = @[]
result.ipLimits.limit = bucketIpLimit
proc new(T: type KBucket, istart, iend: NodeId, bucketIpLimit: uint): T =
KBucket(
istart: istart,
iend: iend,
nodes: @[],
replacementCache: @[],
ipLimits: IpLimits(limit: bucketIpLimit))
proc midpoint(k: KBucket): NodeId =
k.istart + (k.iend - k.istart) div 2.u256
@ -196,12 +196,12 @@ proc remove(k: KBucket, n: Node): bool =
proc split(k: KBucket): tuple[lower, upper: KBucket] =
## Split the kbucket `k` at the median id.
let splitid = k.midpoint
result.lower = newKBucket(k.istart, splitid, k.ipLimits.limit)
result.upper = newKBucket(splitid + 1.u256, k.iend, k.ipLimits.limit)
result.lower = KBucket.new(k.istart, splitid, k.ipLimits.limit)
result.upper = KBucket.new(splitid + 1.u256, k.iend, k.ipLimits.limit)
for node in k.nodes:
let bucket = if node.id <= splitid: result.lower else: result.upper
bucket.nodes.add(node)
# Ip limits got reset because of the newKBuckets, so there is the need to
# Ip limits got reset because of the KBucket.new, so there is the need to
# increment again for each added node. It should however never fail as the
# previous bucket had the same limits.
doAssert(bucket.ipLimits.inc(node.address.get().ip),
@ -252,16 +252,18 @@ proc computeSharedPrefixBits(nodes: openarray[NodeId]): int =
# Reaching this would mean that all node ids are equal.
doAssert(false, "Unable to calculate number of shared prefix bits")
proc init*(r: var RoutingTable, thisNode: Node, bitsPerHop = DefaultBitsPerHop,
ipLimits = DefaultTableIpLimits, rng: ref BrHmacDrbgContext, distanceCalculator = XorDistanceCalculator) =
proc init*(T: type RoutingTable, thisNode: Node, bitsPerHop = DefaultBitsPerHop,
ipLimits = DefaultTableIpLimits, rng: ref BrHmacDrbgContext,
distanceCalculator = XorDistanceCalculator): T =
## Initialize the routing table for provided `Node` and bitsPerHop value.
## `bitsPerHop` is default set to 5 as recommended by original Kademlia paper.
r.thisNode = thisNode
r.buckets = @[newKBucket(0.u256, high(Uint256), ipLimits.bucketIpLimit)]
r.bitsPerHop = bitsPerHop
r.ipLimits.limit = ipLimits.tableIpLimit
r.distanceCalculator = distanceCalculator
r.rng = rng
RoutingTable(
thisNode: thisNode,
buckets: @[KBucket.new(0.u256, high(Uint256), ipLimits.bucketIpLimit)],
bitsPerHop: bitsPerHop,
ipLimits: IpLimits(limit: ipLimits.tableIpLimit),
distanceCalculator: distanceCalculator,
rng: rng)
proc splitBucket(r: var RoutingTable, index: int) =
let bucket = r.buckets[index]

View File

@ -37,18 +37,14 @@ suite "Routing Table Tests":
test "Add local node":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
check table.addNode(node) == LocalNode
test "Bucket splitting in range branch b=1":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
for j in 0..5'u32:
for i in 0..<BUCKET_SIZE:
@ -57,10 +53,8 @@ suite "Routing Table Tests":
test "Bucket splitting off range branch b=1":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
# Add 16 nodes, distance 256
for i in 0..<BUCKET_SIZE:
@ -77,10 +71,8 @@ suite "Routing Table Tests":
test "Bucket splitting off range branch b=2":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 2, allow not in range branch to split once (2 buckets).
table.init(node, 2, ipLimits, rng = rng)
var table = RoutingTable.init(node, 2, ipLimits, rng = rng)
# Add 16 nodes, distance 256 from `node`, but all with 2 bits shared prefix
# among themselves.
@ -107,10 +99,8 @@ suite "Routing Table Tests":
test "Replacement cache":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
# create a full bucket
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
@ -141,10 +131,8 @@ suite "Routing Table Tests":
test "Empty bucket":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
check table.nodeToRevalidate().isNil()
@ -165,10 +153,8 @@ suite "Routing Table Tests":
test "Empty replacement cache":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
# create a full bucket TODO: no need to store bucketNodes
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
@ -181,10 +167,8 @@ suite "Routing Table Tests":
test "Double add":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
let doubleNode = node.nodeAtDistance(rng[], 256)
# Try to add the node twice
@ -211,10 +195,8 @@ suite "Routing Table Tests":
test "Double replacement add":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
# create a full bucket
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
@ -241,10 +223,8 @@ suite "Routing Table Tests":
test "Just seen":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
# create a full bucket
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
@ -261,10 +241,8 @@ suite "Routing Table Tests":
test "Just seen replacement":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, ipLimits, rng = rng)
var table = RoutingTable.init(node, 1, ipLimits, rng = rng)
# create a full bucket
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
@ -294,10 +272,8 @@ suite "Routing Table Tests":
test "Ip limits on bucket":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
block: # First bucket
let sameIpNodes = node.nodesAtDistance(rng[], 256,
@ -344,10 +320,8 @@ suite "Routing Table Tests":
test "Ip limits on routing table":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
let amount = uint32(DefaultTableIpLimits.tableIpLimit div
DefaultTableIpLimits.bucketIpLimit)
@ -384,9 +358,7 @@ suite "Routing Table Tests":
test "Ip limits on replacement cache":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
let diffIpNodes = node.nodesAtDistanceUniqueIp(rng[], 256,
int(BUCKET_SIZE - DefaultTableIpLimits.bucketIpLimit + 1),
@ -422,9 +394,7 @@ suite "Routing Table Tests":
test "Ip limits on replacement cache: deletion":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
block: # Fill bucket
let sameIpNodes = node.nodesAtDistance(rng[], 256,
@ -462,9 +432,7 @@ suite "Routing Table Tests":
test "Ip limits on replacement cache: double add":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
# Fill bucket
let diffIpNodes = node.nodesAtDistanceUniqueIp(rng[], 256, BUCKET_SIZE,
@ -484,9 +452,7 @@ suite "Routing Table Tests":
test "Ip limits on bucket: double add with new ip":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
let pk = PrivateKey.random(rng[])
let sameIpNode1 = generateNode(pk)
@ -509,9 +475,7 @@ suite "Routing Table Tests":
test "Ip limits on replacement cache: double add with new ip":
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
# Fill bucket
let diffIpNodes = node.nodesAtDistanceUniqueIp(rng[], 256, BUCKET_SIZE,
@ -535,9 +499,7 @@ suite "Routing Table Tests":
test "Ip limits on bucket: even more adds with new ip":
# This tests against an issue where the ip of the nodes would not get updated
let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(node, 1, DefaultTableIpLimits, rng = rng)
var table = RoutingTable.init(node, 1, DefaultTableIpLimits, rng = rng)
let pk = PrivateKey.random(rng[])
let sameIpNode1 = generateNode(pk)
@ -563,8 +525,8 @@ suite "Routing Table Tests":
test "Custom distance calculator: distance":
let numNodes = 10
let local = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(local, 1, ipLimits, rng = rng, distanceCalculator = customDistanceCalculator)
var table = RoutingTable.init(local, 1, ipLimits, rng = rng,
distanceCalculator = customDistanceCalculator)
let nodes = generateNRandomNodes(rng, numNodes)
@ -583,8 +545,8 @@ suite "Routing Table Tests":
test "Custom distance calculator: at log distance":
let numNodes = 10
let local = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable
table.init(local, 1, ipLimits, rng = rng, distanceCalculator = customDistanceCalculator)
var table = RoutingTable.init(local, 1, ipLimits, rng = rng,
distanceCalculator = customDistanceCalculator)
let nodes = generateNRandomNodes(rng, numNodes)
@ -600,4 +562,3 @@ suite "Routing Table Tests":
# there may be more than one node at provided distance
check len(neighboursAtLogDist) >= 1
check neighboursAtLogDist.contains(n)