discv5: Pass bootstrap nodes and add deletion of nodes

This commit is contained in:
kdeme 2020-03-13 17:48:03 +01:00 committed by zah
parent 2b7a25175b
commit 267a06dbe6
4 changed files with 55 additions and 30 deletions

View File

@ -241,6 +241,9 @@ proc decodeAuthResp(c: Codec, fromId: NodeId, head: AuthHeader,
# 2. Should verify ENR and check for correct id in case an ENR is included # 2. Should verify ENR and check for correct id in case an ENR is included
# 3. Should verify id nonce signature # 3. Should verify id nonce signature
# More TODO:
# This will also not work if ENR does not contain an IP address or if the
# IP address is out of date and doesn't match current UDP end point
newNode = newNode(authResp.record) newNode = newNode(authResp.record)
return true return true

View File

@ -44,6 +44,10 @@ proc newNode*(r: Record): Node =
result = newNode(initENode(pk, a)) result = newNode(initENode(pk, a))
result.record = r result.record = r
proc newNodes*(records: openarray[Record]): seq[Node] =
for record in records:
result.add(newNode(record))
proc hash*(n: Node): hashes.Hash = hash(n.node.pubkey.data) proc hash*(n: Node): hashes.Hash = hash(n.node.pubkey.data)
proc `==`*(a, b: Node): bool = (a.isNil and b.isNil) or (not a.isNil and not b.isNil and a.node.pubkey == b.node.pubkey) proc `==`*(a, b: Node): bool = (a.isNil and b.isNil) or (not a.isNil and not b.isNil and a.node.pubkey == b.node.pubkey)

View File

@ -38,11 +38,32 @@ type
awaitedPackets: Table[(Node, RequestId), Future[Option[Packet]]] awaitedPackets: Table[(Node, RequestId), Future[Option[Packet]]]
lookupLoop: Future[void] lookupLoop: Future[void]
revalidateLoop: Future[void] revalidateLoop: Future[void]
bootstrapNodes: seq[Node]
PendingRequest = object PendingRequest = object
node: Node node: Node
packet: seq[byte] packet: seq[byte]
proc addNode*(d: Protocol, node: Node) =
discard d.routingTable.addNode(node)
template addNode*(d: Protocol, enode: ENode) =
addNode d, newNode(enode)
template addNode*(d: Protocol, r: Record) =
addNode d, newNode(r)
proc addNode*(d: Protocol, enr: EnrUri) =
var r: Record
let res = r.fromUri(enr)
doAssert(res)
d.addNode newNode(r)
proc randomNodes*(k: Protocol, count: int): seq[Node] =
k.routingTable.randomNodes(count)
proc nodesDiscovered*(k: Protocol): int {.inline.} = k.routingTable.len
proc whoareyouMagic(toNode: NodeId): array[magicSize, byte] = proc whoareyouMagic(toNode: NodeId): array[magicSize, byte] =
const prefix = "WHOAREYOU" const prefix = "WHOAREYOU"
var data: array[prefix.len + sizeof(toNode), byte] var data: array[prefix.len + sizeof(toNode), byte]
@ -51,7 +72,8 @@ proc whoareyouMagic(toNode: NodeId): array[magicSize, byte] =
sha256.digest(data).data sha256.digest(data).data
proc newProtocol*(privKey: PrivateKey, db: Database, proc newProtocol*(privKey: PrivateKey, db: Database,
ip: IpAddress, tcpPort, udpPort: Port): Protocol = ip: IpAddress, tcpPort, udpPort: Port,
bootstrapRecords: openarray[Record] = []): Protocol =
let let
a = Address(ip: ip, tcpPort: tcpPort, udpPort: udpPort) a = Address(ip: ip, tcpPort: tcpPort, udpPort: udpPort)
enode = initENode(privKey.getPublicKey(), a) enode = initENode(privKey.getPublicKey(), a)
@ -64,7 +86,8 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
localNode: node, localNode: node,
whoareyouMagic: whoareyouMagic(node.id), whoareyouMagic: whoareyouMagic(node.id),
idHash: sha256.digest(node.id.toByteArrayBE).data, idHash: sha256.digest(node.id.toByteArrayBE).data,
codec: Codec(localNode: node, privKey: privKey, db: db)) codec: Codec(localNode: node, privKey: privKey, db: db),
bootstrapNodes: newNodes(bootstrapRecords))
result.routingTable.init(node) result.routingTable.init(node)
@ -355,6 +378,7 @@ proc ping(p: Protocol, toNode: Node): RequestId =
proc revalidateNode(p: Protocol, n: Node) proc revalidateNode(p: Protocol, n: Node)
{.async, raises:[Defect, Exception].} = # TODO: Exception {.async, raises:[Defect, Exception].} = # TODO: Exception
trace "Ping to revalidate node", node = $n
let reqId = p.ping(n) let reqId = p.ping(n)
let resp = await p.waitPacket(n, reqId) let resp = await p.waitPacket(n, reqId)
@ -367,8 +391,15 @@ proc revalidateNode(p: Protocol, n: Node)
p.routingTable.setJustSeen(n) p.routingTable.setJustSeen(n)
trace "Revalidated node", node = $n trace "Revalidated node", node = $n
else: else:
if false: # TODO: if not bootnode: # For now we never remove bootstrap nodes. It might make sense to actually
# do so and to retry them only in case we drop to a really low amount of
# peers in the DHT
if n notin p.bootstrapNodes:
trace "Revalidation of node failed, removing node", node = $n
p.routingTable.removeNode(n) p.routingTable.removeNode(n)
# TODO: Do we delete the shared secrets here?
# And if so, the current way they are stored, we might not have the key
# (specifically if the ENR does not have the correct address)
proc revalidateLoop(p: Protocol) {.async.} = proc revalidateLoop(p: Protocol) {.async.} =
try: try:
@ -389,8 +420,12 @@ proc lookupLoop(d: Protocol) {.async.} =
## TODO: Same story as for `revalidateLoop` ## TODO: Same story as for `revalidateLoop`
try: try:
while true: while true:
let nodes = await d.lookupRandom() # lookup self (neighbour nodes)
trace "Discovered nodes", nodes = $nodes var nodes = await d.lookup(d.localNode.id)
trace "Discovered nodes in self lookup", nodes = $nodes
nodes = await d.lookupRandom()
trace "Discovered nodes in random lookup", nodes = $nodes
await sleepAsync(lookupInterval) await sleepAsync(lookupInterval)
except CancelledError: except CancelledError:
trace "lookupLoop canceled" trace "lookupLoop canceled"
@ -401,6 +436,10 @@ proc open*(d: Protocol) =
# TODO allow binding to specific IP / IPv6 / etc # TODO allow binding to specific IP / IPv6 / etc
let ta = initTAddress(IPv4_any(), d.localNode.node.address.udpPort) let ta = initTAddress(IPv4_any(), d.localNode.node.address.udpPort)
d.transp = newDatagramTransport(processClient, udata = d, local = ta) d.transp = newDatagramTransport(processClient, udata = d, local = ta)
for node in d.bootstrapNodes:
d.addNode(node)
# Might want to move these to a separate proc if this turns out to be needed. # Might want to move these to a separate proc if this turns out to be needed.
d.lookupLoop = lookupLoop(d) d.lookupLoop = lookupLoop(d)
d.revalidateLoop = revalidateLoop(d) d.revalidateLoop = revalidateLoop(d)
@ -425,26 +464,6 @@ proc closeWait*(d: Protocol) {.async.} =
d.lookupLoop.cancelAndWait()]) d.lookupLoop.cancelAndWait()])
await d.transp.closeWait() await d.transp.closeWait()
proc addNode*(d: Protocol, node: Node) =
discard d.routingTable.addNode(node)
template addNode*(d: Protocol, enode: ENode) =
addNode d, newNode(enode)
template addNode*(d: Protocol, r: Record) =
addNode d, newNode(r)
proc addNode*(d: Protocol, enr: EnrUri) =
var r: Record
let res = r.fromUri(enr)
doAssert(res)
d.addNode newNode(r)
proc randomNodes*(k: Protocol, count: int): seq[Node] =
k.routingTable.randomNodes(count)
proc nodesDiscovered*(k: Protocol): int {.inline.} = k.routingTable.len
when isMainModule: when isMainModule:
import discovery_db import discovery_db
import eth/trie/db import eth/trie/db

View File

@ -6,14 +6,13 @@ import
./p2p_test_helper ./p2p_test_helper
proc initDiscoveryNode*(privKey: PrivateKey, address: Address, proc initDiscoveryNode*(privKey: PrivateKey, address: Address,
bootnodes: seq[Record]): discv5_protocol.Protocol = bootstrapRecords: seq[Record]):
discv5_protocol.Protocol =
var db = DiscoveryDB.init(newMemoryDB()) var db = DiscoveryDB.init(newMemoryDB())
result = newProtocol(privKey, db, result = newProtocol(privKey, db,
parseIpAddress("127.0.0.1"), parseIpAddress("127.0.0.1"),
address.tcpPort, address.udpPort) address.tcpPort, address.udpPort,
bootstrapRecords)
for node in bootnodes:
result.addNode(node)
result.open() result.open()