-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
- Loading branch information
Showing
4 changed files
with
268 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |