-
Notifications
You must be signed in to change notification settings - Fork 31
RoutingTable node no longer removed in replaceNode when replacement cache empty #791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -195,7 +195,8 @@ func ipLimitDec(r: var RoutingTable, b: KBucket, n: Node) = | |
| r.ipLimits.dec(ip) | ||
|
|
||
| func getNode*(r: RoutingTable, id: NodeId): Opt[Node] | ||
| proc replaceNode*(r: var RoutingTable, n: Node) {.gcsafe.} | ||
| proc removeNode*(r: var RoutingTable, n: Node) {.gcsafe.} | ||
| proc replaceNode*(r: var RoutingTable, n: Node): bool {.discardable, gcsafe.} | ||
|
|
||
| proc banNode*(r: var RoutingTable, nodeId: NodeId, period: chronos.Duration) = | ||
| ## Ban a node from the routing table for the given period. The node is removed | ||
|
|
@@ -214,8 +215,9 @@ proc banNode*(r: var RoutingTable, nodeId: NodeId, period: chronos.Duration) = | |
|
|
||
| # Remove the node from the routing table | ||
| let node = r.getNode(nodeId) | ||
| if node.isSome(): | ||
| r.replaceNode(node.get()) | ||
| if node.isSome() and not r.replaceNode(node.get()): | ||
| # Remove the node if the node was not replaced due to an empty replacement cache | ||
| r.removeNode(node.get()) | ||
|
|
||
| proc isBanned*(r: RoutingTable, nodeId: NodeId): bool = | ||
| if not r.bannedNodes.contains(nodeId): | ||
|
|
@@ -458,20 +460,35 @@ proc removeNode*(r: var RoutingTable, n: Node) = | |
| if b.remove(n): | ||
| ipLimitDec(r, b, n) | ||
|
|
||
| proc replaceNode*(r: var RoutingTable, n: Node) = | ||
| proc replaceNode*(r: var RoutingTable, n: Node): bool {.discardable.} = | ||
| ## Replace node `n` with last entry in the replacement cache. If there are | ||
| ## no entries in the replacement cache, node `n` will simply be removed. | ||
| # TODO: Kademlia paper recommends here to not remove nodes if there are no | ||
| ## no entries in the replacement cache, node `n` will simply be marked as | ||
| ## not seen. Returns bool indictating whether or not the node was successfully | ||
| ## replaced. | ||
|
|
||
| # Note: Kademlia paper recommends here to not remove nodes if there are no | ||
| # replacements. However, that would require a bit more complexity in the | ||
| # revalidation as you don't want to try pinging that node all the time. | ||
|
|
||
| var replaced = false | ||
|
|
||
| let b = r.bucketForNode(n.id) | ||
| if b.remove(n): | ||
|
|
||
| if b.replacementCache.len == 0: | ||
| let idx = b.nodes.find(n) | ||
| if idx >= 0 and n.seen: | ||
| b.nodes[idx].seen = false | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kdeme When marking the node as not seen do you think we should also move the node to the end of the bucket (the least recently seen position)? |
||
|
|
||
| elif b.remove(n): | ||
| ipLimitDec(r, b, n) | ||
|
|
||
| if b.replacementCache.len > 0: | ||
| # Nodes in the replacement cache are already included in the ip limits. | ||
| b.add(b.replacementCache[high(b.replacementCache)]) | ||
| b.replacementCache.delete(high(b.replacementCache)) | ||
| # Nodes in the replacement cache are already included in the ip limits. | ||
| b.add(b.replacementCache[high(b.replacementCache)]) | ||
| b.replacementCache.delete(high(b.replacementCache)) | ||
| replaced = true | ||
|
|
||
| return replaced | ||
|
|
||
|
|
||
| func getNode*(r: RoutingTable, id: NodeId): Opt[Node] = | ||
| ## Get the `Node` with `id` as `NodeId` from the routing table. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -148,7 +148,7 @@ suite "Routing Table Tests": | |
| table.replaceNode(generateNode(PrivateKey.random(rng[]))) | ||
| check table.len == 1 | ||
|
|
||
| table.replaceNode(addedNode) | ||
| table.removeNode(addedNode) | ||
| check table.len == 0 | ||
|
|
||
| test "Empty replacement cache": | ||
|
|
@@ -162,8 +162,8 @@ suite "Routing Table Tests": | |
| check table.addNode(n) == Added | ||
|
|
||
| table.replaceNode(table.nodeToRevalidate()) | ||
| # This node should still be removed | ||
| check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone() | ||
| # This node should not be removed | ||
| check (table.getNode(bucketNodes[bucketNodes.high].id)).isSome() | ||
|
||
|
|
||
| test "Double add": | ||
| let node = generateNode(PrivateKey.random(rng[])) | ||
|
|
@@ -191,7 +191,7 @@ suite "Routing Table Tests": | |
| check: | ||
| res.isSome() | ||
| res.get() == doubleNode | ||
| table.len == 1 | ||
| table.len == 16 | ||
|
||
|
|
||
| test "Double replacement add": | ||
| let node = generateNode(PrivateKey.random(rng[])) | ||
|
|
@@ -237,7 +237,7 @@ suite "Routing Table Tests": | |
|
|
||
| for n in bucketNodes: | ||
| table.replaceNode(table.nodeToRevalidate()) | ||
| check (table.getNode(n.id)).isNone() | ||
| check (table.getNode(n.id)).isSome() | ||
|
||
|
|
||
| test "Just seen replacement": | ||
| let node = generateNode(PrivateKey.random(rng[])) | ||
|
|
@@ -286,7 +286,7 @@ suite "Routing Table Tests": | |
| check table.addNode(anotherSameIpNode) == IpLimitReached | ||
|
|
||
| # Remove one and try add again | ||
| table.replaceNode(table.nodeToRevalidate()) | ||
| table.removeNode(table.nodeToRevalidate()) | ||
| check table.addNode(anotherSameIpNode) == Added | ||
|
|
||
| # Further fill the bucket with nodes with different ip. | ||
|
|
@@ -386,10 +386,10 @@ suite "Routing Table Tests": | |
|
|
||
| table.getNode(anotherSameIpNode2.id).isNone() | ||
|
|
||
| block: # Replace again to see if the first one never becomes available | ||
| block: # Replace again to see if the first one becomes available | ||
| table.replaceNode(table.nodeToRevalidate()) | ||
| check: | ||
| table.getNode(anotherSameIpNode1.id).isNone() | ||
| table.getNode(anotherSameIpNode1.id).isSome() | ||
| table.getNode(anotherSameIpNode2.id).isNone() | ||
|
|
||
| test "Ip limits on replacement cache: deletion": | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I find it slightly cleaner to just return the resulting bool directly in each if/else clause
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, will update.