mirror of https://github.com/vacp2p/research.git
discv5 (#19)
* readme * create package * started experimenting * cleanup * adding a peer * more granular control * fix * fix * removed * experimenting * using debug * updated code to start N nodes * fix * fix * dean is too retarded for nim part 5 * confused * fi * added todo, playing around * updated * distance func * todo * fix * fix * made it work * changes * update * multiple runs * minor improvements * using for * minor * little error * woops * update * using sequence * changes * updated * unused * moved * fixed * changed sleep to var * changed node count * moved start * using contains * using sample functions * added a random version * back * experimenting with random pairs * starting bootstrap node only * made config flag * remove echo * minor change * added some comments * printing port instead, more informative * fixes * fix * overhaul * more * updated random * added count * fixes * started working on ENR lookup * using smarter distribution * using countit * fix
This commit is contained in:
parent
149c7133a0
commit
ba2916ebb2
|
@ -0,0 +1,4 @@
|
|||
# discv5 feasibility study
|
||||
|
||||
This contains the study as documented by the issue [vacp2p/research#15](https://github.com/vacp2p/research/issues/15).
|
||||
Semi-inspired by [zilm13/discv5](https://github.com/zilm13/discv5).
|
|
@ -0,0 +1,16 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "decanus"
|
||||
description = "A new awesome nimble package"
|
||||
license = "MIT"
|
||||
srcDir = "src"
|
||||
bin = @["discv5"]
|
||||
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.0.6",
|
||||
"eth",
|
||||
"confutils"
|
|
@ -0,0 +1,208 @@
|
|||
import
|
||||
random, chronos, sequtils, chronicles, tables, stint, options, std/bitops, sequtils,
|
||||
eth/[keys, rlp, async_utils], eth/p2p/enode, eth/trie/db,
|
||||
eth/p2p/discoveryv5/[discovery_db, enr, node, types, routing_table, encoding],
|
||||
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||
./utils
|
||||
|
||||
const
|
||||
# the amount of nodes
|
||||
N = 100
|
||||
|
||||
MAX_LOOKUPS = 100
|
||||
RUNS = 100
|
||||
|
||||
# the cooldown period between runs.
|
||||
COOLDOWN = 0
|
||||
|
||||
# the sleep period before starting our runs.
|
||||
SLEEP = 600
|
||||
VERBOSE = true
|
||||
|
||||
# if true, nodes are randomly added to other nodes using the `addNode` function.
|
||||
# otherwise we use discv5s native paring functionality letting each node find peers using the boostrap.
|
||||
USE_MANUAL_PAIRING = false
|
||||
|
||||
# when manual pairing is enabled this indicates the amount of nodes to pair with.
|
||||
PEERS_PER_NODE = 16
|
||||
|
||||
# True if looking for a node with field rather than a specific node
|
||||
LOOK_FOR_FIELD = true
|
||||
|
||||
# The amount of nodes that will have our specific field to look for
|
||||
LOOKUP_FIELD_DISTRIBUTION = 5
|
||||
|
||||
proc write(str: string) =
|
||||
if VERBOSE:
|
||||
echo str
|
||||
|
||||
proc runWith(node: discv5_protocol.Protocol, nodes: seq[discv5_protocol.Protocol]) {.async.} =
|
||||
randomize()
|
||||
|
||||
let target = sample(nodes).localNode
|
||||
let tid = recordToNodeID(target.record)
|
||||
|
||||
var peer: Node
|
||||
while true:
|
||||
randomize()
|
||||
peer = sample(nodes).localNode
|
||||
if peer.record.toUri() != target.record.toUri():
|
||||
break
|
||||
|
||||
var distance = logDist(recordToNodeID(peer.record), tid)
|
||||
|
||||
var called = newSeq[string](0)
|
||||
|
||||
for i in 0..<MAX_LOOKUPS:
|
||||
var lookup = await node.findNode(peer, distance)
|
||||
called.add(peer.record.toUri())
|
||||
|
||||
keepIf(lookup, proc (x: Node): bool =
|
||||
x.record.toUri() != node.localNode.record.toUri() and not called.contains(x.record.toUri())
|
||||
)
|
||||
|
||||
if lookup.len == 0:
|
||||
if distance != 256:
|
||||
distance = 256
|
||||
continue
|
||||
|
||||
write("Lookup from node " & $((get peer.record.toTypedRecord()).udp.get()) & " found no results at 256")
|
||||
return
|
||||
|
||||
if lookup.countIt(it.record.toUri() == target.record.toUri()) == 1:
|
||||
echo "Found target in ", i + 1, " lookups"
|
||||
return
|
||||
|
||||
let lastPeer = peer
|
||||
for n in items(lookup):
|
||||
let d = logDist(recordToNodeID(n.record), tid)
|
||||
if d <= distance:
|
||||
peer = n
|
||||
distance = d
|
||||
|
||||
# This ensures we get a random node from the last lookup if we have already called the new peer.
|
||||
# We let this run lookup*2 times, otherwise we could reach deadlock.
|
||||
for i in 0..<(lookup.len*2):
|
||||
if lastPeer.record.toUri() != peer.record.toUri():
|
||||
break
|
||||
|
||||
peer = sample(lookup)
|
||||
|
||||
echo "Not found in max iterations"
|
||||
|
||||
proc runWithENR(node: discv5_protocol.Protocol, nodes: seq[discv5_protocol.Protocol]) {.async.} =
|
||||
randomize()
|
||||
|
||||
var peer = sample(nodes).localNode
|
||||
|
||||
let distance = uint32(256)
|
||||
|
||||
var called = newSeq[string](0)
|
||||
|
||||
for i in 0..<MAX_LOOKUPS:
|
||||
var lookup = await node.findNode(peer, distance)
|
||||
called.add(peer.record.toUri())
|
||||
|
||||
keepIf(lookup, proc (x: Node): bool =
|
||||
x.record.toUri() != node.localNode.record.toUri() and not called.contains(x.record.toUri())
|
||||
)
|
||||
|
||||
if lookup.countIt(it.record.tryGet("search", seq[byte]).isSome) >= 1:
|
||||
echo i + 1
|
||||
return
|
||||
|
||||
if lookup.len == 0:
|
||||
write("Lookup from node " & $((get peer.record.toTypedRecord()).udp.get()) & " found no results at 256")
|
||||
return
|
||||
|
||||
peer = sample(lookup)
|
||||
|
||||
echo "Not found in max iterations"
|
||||
|
||||
proc runWithRandom(node: discv5_protocol.Protocol, nodes: seq[discv5_protocol.Protocol]) {.async.} =
|
||||
randomize()
|
||||
|
||||
let target = sample(nodes).localNode
|
||||
let tid = recordToNodeID(target.record)
|
||||
|
||||
var peer: Node
|
||||
while true:
|
||||
randomize()
|
||||
peer = sample(nodes).localNode
|
||||
if peer.record.toUri() != target.record.toUri():
|
||||
break
|
||||
|
||||
var called = newSeq[string](0)
|
||||
|
||||
for i in 0..<MAX_LOOKUPS:
|
||||
var lookup = await node.findNode(peer, 256)
|
||||
called.add(peer.record.toUri())
|
||||
|
||||
keepIf(lookup, proc (x: Node): bool =
|
||||
x.record.toUri() != node.localNode.record.toUri() and not called.contains(x.record.toUri())
|
||||
)
|
||||
|
||||
if lookup.len == 0:
|
||||
write("Lookup from node " & $((get peer.record.toTypedRecord()).udp.get()) & " found no results at 256")
|
||||
return
|
||||
|
||||
let findings = filter(lookup, proc (x: Node): bool =
|
||||
x.record.toUri() == target.record.toUri()
|
||||
)
|
||||
|
||||
if findings.len == 1:
|
||||
echo "Found target in ", i + 1, " lookups"
|
||||
return
|
||||
|
||||
while true: # This ensures we get a random node from the last lookup if we have already called the new peer.
|
||||
if not called.contains(peer.record.toUri()):
|
||||
break
|
||||
|
||||
peer = sample(lookup)
|
||||
|
||||
echo "Not found in max iterations"
|
||||
|
||||
proc pair(node: discv5_protocol.Protocol, nodes: seq[discv5_protocol.Protocol]) =
|
||||
for _ in 0..<PEERS_PER_NODE:
|
||||
randomize()
|
||||
sample(nodes).addNode(node.localNode)
|
||||
|
||||
proc run() {.async.} =
|
||||
var nodes = newSeq[discv5_protocol.Protocol](0)
|
||||
|
||||
echo "Setting up ", N, " nodes"
|
||||
|
||||
let divisor = int(N / LOOKUP_FIELD_DISTRIBUTION)
|
||||
|
||||
for i in 0..<N:
|
||||
let node = initDiscoveryNode(
|
||||
PrivateKey.random().get,
|
||||
localAddress(20300 + i),
|
||||
if i > 0: @[nodes[0].localNode.record] else: @[],
|
||||
if i mod divisor == 0: 1 else: 0
|
||||
)
|
||||
nodes.add(node)
|
||||
|
||||
if (USE_MANUAL_PAIRING and i == 0) or not USE_MANUAL_PAIRING:
|
||||
node.start()
|
||||
|
||||
if USE_MANUAL_PAIRING:
|
||||
for n in nodes:
|
||||
pair(n, nodes)
|
||||
|
||||
if not USE_MANUAL_PAIRING:
|
||||
echo "Sleeping for ", SLEEP, " seconds"
|
||||
await sleepAsync(SLEEP.seconds)
|
||||
|
||||
let node = initDiscoveryNode(PrivateKey.random().get, localAddress(20300 + N), @[nodes[0].localNode.record], 0)
|
||||
|
||||
for i in 0..<RUNS:
|
||||
if LOOK_FOR_FIELD:
|
||||
await runWithENR(node, nodes)
|
||||
else:
|
||||
await runWith(node, nodes)
|
||||
|
||||
await sleepAsync(COOLDOWN.seconds)
|
||||
|
||||
when isMainModule:
|
||||
waitFor run()
|
|
@ -0,0 +1,40 @@
|
|||
import
|
||||
chronos, options, std/bitops,
|
||||
eth/keys, eth/p2p/enode, eth/trie/db, stint, nimcrypto,
|
||||
eth/p2p/discoveryv5/[discovery_db, enr, node, types],
|
||||
eth/p2p/discoveryv5/protocol as discv5_protocol
|
||||
|
||||
type ToNodeIDError* = object of CatchableError
|
||||
|
||||
proc localAddress*(port: int): Address =
|
||||
let port = Port(port)
|
||||
result = Address(
|
||||
udpPort: port,
|
||||
tcpPort: port,
|
||||
ip: parseIpAddress("127.0.0.1")
|
||||
)
|
||||
|
||||
proc initDiscoveryNode*(privKey: PrivateKey, address: Address, bootstrapRecords: seq[Record], field: uint): discv5_protocol.Protocol =
|
||||
var db = DiscoveryDB.init(newMemoryDB())
|
||||
result = newProtocol(privKey, db, some(parseIpAddress("127.0.0.1")), address.tcpPort, address.udpPort, bootstrapRecords)
|
||||
let old = result.localNode.record
|
||||
|
||||
if field == 1:
|
||||
let enr = initRecord(
|
||||
old.seqNum,
|
||||
privKey,
|
||||
{
|
||||
"udp": uint32(address.udpPort),
|
||||
"tcp": uint32(address.tcpPort),
|
||||
"ip": [byte 127, 0, 0, 1],
|
||||
"search": field
|
||||
}
|
||||
)
|
||||
|
||||
result.localNode.record = enr
|
||||
|
||||
result.open()
|
||||
|
||||
proc recordToNodeID*(r: Record): NodeId =
|
||||
var pk = r.get(PublicKey)
|
||||
result = readUintBE[256](keccak256.digest(pk.get.toRaw()).data)
|
Loading…
Reference in New Issue