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

View File

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

View File

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