Adopt discovery/kademlia to new enode type.

This commit is contained in:
cheatfate 2018-05-01 02:46:04 +03:00
parent 144e486224
commit 80c1c98119
2 changed files with 37 additions and 39 deletions

View File

@ -10,10 +10,10 @@
from strutils import nil from strutils import nil
import asyncnet, asyncdispatch, net, times, nativesockets, algorithm, logging import asyncnet, asyncdispatch, net, times, nativesockets, algorithm, logging
import kademlia import kademlia, enode
import eth_keys, rlp, ranges, ttmath, nimcrypto import eth_keys, rlp, ranges, ttmath, nimcrypto
export Address, Node export Node
const const
MAINNET_BOOTNODES* = [ MAINNET_BOOTNODES* = [
@ -115,17 +115,20 @@ proc sendTo*(socket: AsyncFD, data: seq[byte], ip: IpAddress, port: Port,
error "sendTo failed: ", getCurrentExceptionMsg() error "sendTo failed: ", getCurrentExceptionMsg()
proc send(d: DiscoveryProtocol, n: Node, data: seq[byte]) = proc send(d: DiscoveryProtocol, n: Node, data: seq[byte]) =
asyncCheck d.socket.getFd().AsyncFD.sendTo(data, n.address.ip, n.address.udpPort) asyncCheck d.socket.getFd().AsyncFD.sendTo(data,
n.node.address.ip,
n.node.address.udpPort)
proc sendPing*(d: DiscoveryProtocol, n: Node): seq[byte] = proc sendPing*(d: DiscoveryProtocol, n: Node): seq[byte] =
let payload = rlp.encode((PROTO_VERSION, d.address, n.address, expiration())) let payload = rlp.encode((PROTO_VERSION, d.address, n.node.address,
expiration()))
let msg = pack(cmdPing, payload, d.privKey) let msg = pack(cmdPing, payload, d.privKey)
result = msg[0 ..< MAC_SIZE] result = msg[0 ..< MAC_SIZE]
debug ">>> ping ", n debug ">>> ping ", n
d.send(n, msg) d.send(n, msg)
proc sendPong*(d: DiscoveryProtocol, n: Node, token: MDigest[256]) = proc sendPong*(d: DiscoveryProtocol, n: Node, token: MDigest[256]) =
let payload = rlp.encode((n.address, token, expiration())) let payload = rlp.encode((n.node.address, token, expiration()))
let msg = pack(cmdPong, payload, d.privKey) let msg = pack(cmdPong, payload, d.privKey)
debug ">>> pong ", n debug ">>> pong ", n
d.send(n, msg) d.send(n, msg)
@ -152,7 +155,8 @@ proc sendNeighbours*(d: DiscoveryProtocol, node: Node, neighbours: seq[Node]) =
nodes.setLen(0) nodes.setLen(0)
for i, n in neighbours: for i, n in neighbours:
nodes.add((n.address.ip, n.address.udpPort, n.address.tcpPort, n.pubkey)) nodes.add((n.node.address.ip, n.node.address.udpPort,
n.node.address.tcpPort, n.node.pubkey))
if nodes.len == MAX_NEIGHBOURS_PER_PACKET: if nodes.len == MAX_NEIGHBOURS_PER_PACKET:
flush() flush()

View File

@ -14,7 +14,7 @@ from strutils import parseInt
export sets # TODO: This should not be needed, but compilation fails otherwise export sets # TODO: This should not be needed, but compilation fails otherwise
import eth_keys, ttmath, nimcrypto import eth_keys, ttmath, nimcrypto, enode
type type
KademliaProtocol* [Wire] = ref object KademliaProtocol* [Wire] = ref object
@ -28,15 +28,9 @@ type
NodeId* = UInt256 # This is probably too small... NodeId* = UInt256 # This is probably too small...
Node* = ref object Node* = ref object
pubkey*: PublicKey node*: ENode
address*: Address
id*: NodeId id*: NodeId
Address* = object
ip*: IpAddress
udpPort*: Port
tcpPort*: Port
RoutingTable = object RoutingTable = object
thisNode: Node thisNode: Node
buckets: seq[KBucket] buckets: seq[KBucket]
@ -59,23 +53,21 @@ proc toNodeId(pk: PublicKey): NodeId =
proc newNode*(pk: PublicKey, address: Address): Node = proc newNode*(pk: PublicKey, address: Address): Node =
result.new() result.new()
result.pubkey = pk result.node = initENode(pk, address)
result.address = address
result.id = pk.toNodeId() result.id = pk.toNodeId()
proc newNode*(uriString: string): Node = proc newNode*(uriString: string): Node =
let u = parseUri(uriString) result.new()
let k = initPublicKey(u.username) result.node = initENode(uriString)
let port = parseInt(u.port).Port result.id = result.node.pubkey.toNodeId()
newNode(k, Address(ip: parseIpAddress(u.hostname), udpPort: port, tcpPort: port))
proc distanceTo(n: Node, id: NodeId): UInt256 = n.id xor id proc distanceTo(n: Node, id: NodeId): UInt256 = n.id xor id
proc `$`*(n: Node): string = proc `$`*(n: Node): string =
"Node[" & $n.address.ip & ":" & $n.address.udpPort & "]" "Node[" & $n.node.address.ip & ":" & $n.node.address.udpPort & "]"
proc hash*(n: Node): hashes.Hash = hash(n.pubkey.data) proc hash*(n: Node): hashes.Hash = hash(n.node.pubkey.data)
proc `==`*(a, b: Node): bool = a.pubkey == b.pubkey proc `==`*(a, b: Node): bool = a.node.pubkey == b.node.pubkey
proc newKBucket(istart, iend: NodeId): KBucket = proc newKBucket(istart, iend: NodeId): KBucket =
result.new() result.new()
@ -140,18 +132,19 @@ proc isFull(k: KBucket): bool = k.len == BUCKET_SIZE
proc contains(k: KBucket, n: Node): bool = n in k.nodes proc contains(k: KBucket, n: Node): bool = n in k.nodes
proc binaryGetBucketForNode(buckets: openarray[KBucket], node: Node): KBucket {.inline.} = proc binaryGetBucketForNode(buckets: openarray[KBucket],
n: Node): KBucket {.inline.} =
## Given a list of ordered buckets, returns the bucket for a given node. ## Given a list of ordered buckets, returns the bucket for a given node.
let bucketPos = lowerBound(buckets, node.id) do(a: KBucket, b: NodeId) -> int: let bucketPos = lowerBound(buckets, n.id) do(a: KBucket, b: NodeId) -> int:
cmp(a.iend, b) cmp(a.iend, b)
# Prevents edge cases where bisect_left returns an out of range index # Prevents edge cases where bisect_left returns an out of range index
if bucketPos < buckets.len: if bucketPos < buckets.len:
let bucket = buckets[bucketPos] let bucket = buckets[bucketPos]
if bucket.istart <= node.id and node.id <= bucket.iend: if bucket.istart <= n.id and n.id <= bucket.iend:
result = bucket result = bucket
if result.isNil: if result.isNil:
raise newException(ValueError, "No bucket found for node with id " & $node.id) raise newException(ValueError, "No bucket found for node with id " & $n.id)
proc computeSharedPrefixBits(nodes: openarray[Node]): int = proc computeSharedPrefixBits(nodes: openarray[Node]): int =
## Count the number of prefix bits shared by all nodes. ## Count the number of prefix bits shared by all nodes.
@ -227,7 +220,8 @@ proc neighbours(r: RoutingTable, id: NodeId, k: int = BUCKET_SIZE): seq[Node] =
proc len(r: RoutingTable): int = proc len(r: RoutingTable): int =
for b in r.buckets: result += b.len for b in r.buckets: result += b.len
proc newKademliaProtocol*[Wire](thisNode: Node, wire: Wire): KademliaProtocol[Wire] = proc newKademliaProtocol*[Wire](thisNode: Node,
wire: Wire): KademliaProtocol[Wire] =
result.new() result.new()
result.thisNode = thisNode result.thisNode = thisNode
result.wire = wire result.wire = wire
@ -256,9 +250,9 @@ template onTimeout(b: untyped) =
asyncCheck doSleep() do(): asyncCheck doSleep() do():
b b
proc waitPong(k: KademliaProtocol, node: Node, token: seq[byte]): Future[bool] = proc waitPong(k: KademliaProtocol, n: Node, token: seq[byte]): Future[bool] =
let pingid = token & @(node.pubkey.data) let pingid = token & @(n.node.pubkey.data)
assert(pingid notin k.pongFutures, "Already waiting for pong from " & $node) assert(pingid notin k.pongFutures, "Already waiting for pong from " & $n)
result = newFuture[bool]("waitPong") result = newFuture[bool]("waitPong")
let fut = result let fut = result
k.pongFutures[pingid] = result k.pongFutures[pingid] = result
@ -410,21 +404,21 @@ proc bootstrap*(k: KademliaProtocol, bootstrapNodes: seq[Node]) {.async.} =
return return
discard await k.lookupRandom() discard await k.lookupRandom()
proc recvPong*(k: KademliaProtocol, node: Node, token: seq[byte]) = proc recvPong*(k: KademliaProtocol, n: Node, token: seq[byte]) =
debug "<<< pong from ", node debug "<<< pong from ", n
let pingid = token & @(node.pubkey.data) let pingid = token & @(n.node.pubkey.data)
var future: Future[bool] var future: Future[bool]
if k.pongFutures.take(pingid, future): if k.pongFutures.take(pingid, future):
future.complete(true) future.complete(true)
proc recvPing*(k: KademliaProtocol, node: Node, msgHash: any) = proc recvPing*(k: KademliaProtocol, n: Node, msgHash: any) =
debug "<<< ping from ", node debug "<<< ping from ", n
k.updateRoutingTable(node) k.updateRoutingTable(n)
k.wire.sendPong(node, msgHash) k.wire.sendPong(n, msgHash)
var future: Future[bool] var future: Future[bool]
if k.pingFutures.take(node, future): if k.pingFutures.take(n, future):
future.complete(true) future.complete(true)
proc recvNeighbours*(k: KademliaProtocol, remote: Node, neighbours: seq[Node]) = proc recvNeighbours*(k: KademliaProtocol, remote: Node, neighbours: seq[Node]) =