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
import asyncnet, asyncdispatch, net, times, nativesockets, algorithm, logging
import kademlia
import kademlia, enode
import eth_keys, rlp, ranges, ttmath, nimcrypto
export Address, Node
export Node
const
MAINNET_BOOTNODES* = [
@ -115,17 +115,20 @@ proc sendTo*(socket: AsyncFD, data: seq[byte], ip: IpAddress, port: Port,
error "sendTo failed: ", getCurrentExceptionMsg()
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] =
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)
result = msg[0 ..< MAC_SIZE]
debug ">>> ping ", n
d.send(n, msg)
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)
debug ">>> pong ", n
d.send(n, msg)
@ -152,7 +155,8 @@ proc sendNeighbours*(d: DiscoveryProtocol, node: Node, neighbours: seq[Node]) =
nodes.setLen(0)
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:
flush()

View File

@ -14,7 +14,7 @@ from strutils import parseInt
export sets # TODO: This should not be needed, but compilation fails otherwise
import eth_keys, ttmath, nimcrypto
import eth_keys, ttmath, nimcrypto, enode
type
KademliaProtocol* [Wire] = ref object
@ -28,15 +28,9 @@ type
NodeId* = UInt256 # This is probably too small...
Node* = ref object
pubkey*: PublicKey
address*: Address
node*: ENode
id*: NodeId
Address* = object
ip*: IpAddress
udpPort*: Port
tcpPort*: Port
RoutingTable = object
thisNode: Node
buckets: seq[KBucket]
@ -59,23 +53,21 @@ proc toNodeId(pk: PublicKey): NodeId =
proc newNode*(pk: PublicKey, address: Address): Node =
result.new()
result.pubkey = pk
result.address = address
result.node = initENode(pk, address)
result.id = pk.toNodeId()
proc newNode*(uriString: string): Node =
let u = parseUri(uriString)
let k = initPublicKey(u.username)
let port = parseInt(u.port).Port
newNode(k, Address(ip: parseIpAddress(u.hostname), udpPort: port, tcpPort: port))
result.new()
result.node = initENode(uriString)
result.id = result.node.pubkey.toNodeId()
proc distanceTo(n: Node, id: NodeId): UInt256 = n.id xor id
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 `==`*(a, b: Node): bool = a.pubkey == b.pubkey
proc hash*(n: Node): hashes.Hash = hash(n.node.pubkey.data)
proc `==`*(a, b: Node): bool = a.node.pubkey == b.node.pubkey
proc newKBucket(istart, iend: NodeId): KBucket =
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 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.
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)
# Prevents edge cases where bisect_left returns an out of range index
if bucketPos < buckets.len:
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
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 =
## 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 =
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.thisNode = thisNode
result.wire = wire
@ -256,9 +250,9 @@ template onTimeout(b: untyped) =
asyncCheck doSleep() do():
b
proc waitPong(k: KademliaProtocol, node: Node, token: seq[byte]): Future[bool] =
let pingid = token & @(node.pubkey.data)
assert(pingid notin k.pongFutures, "Already waiting for pong from " & $node)
proc waitPong(k: KademliaProtocol, n: Node, token: seq[byte]): Future[bool] =
let pingid = token & @(n.node.pubkey.data)
assert(pingid notin k.pongFutures, "Already waiting for pong from " & $n)
result = newFuture[bool]("waitPong")
let fut = result
k.pongFutures[pingid] = result
@ -410,21 +404,21 @@ proc bootstrap*(k: KademliaProtocol, bootstrapNodes: seq[Node]) {.async.} =
return
discard await k.lookupRandom()
proc recvPong*(k: KademliaProtocol, node: Node, token: seq[byte]) =
debug "<<< pong from ", node
proc recvPong*(k: KademliaProtocol, n: Node, token: seq[byte]) =
debug "<<< pong from ", n
let pingid = token & @(node.pubkey.data)
let pingid = token & @(n.node.pubkey.data)
var future: Future[bool]
if k.pongFutures.take(pingid, future):
future.complete(true)
proc recvPing*(k: KademliaProtocol, node: Node, msgHash: any) =
debug "<<< ping from ", node
k.updateRoutingTable(node)
k.wire.sendPong(node, msgHash)
proc recvPing*(k: KademliaProtocol, n: Node, msgHash: any) =
debug "<<< ping from ", n
k.updateRoutingTable(n)
k.wire.sendPong(n, msgHash)
var future: Future[bool]
if k.pingFutures.take(node, future):
if k.pingFutures.take(n, future):
future.complete(true)
proc recvNeighbours*(k: KademliaProtocol, remote: Node, neighbours: seq[Node]) =