Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

discv5 #19

Merged
merged 66 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
4e9956b
readme
decanus Mar 24, 2020
9869801
create package
decanus Mar 24, 2020
3c252ae
started experimenting
decanus Mar 24, 2020
80346ed
cleanup
decanus Mar 24, 2020
96b4a9b
adding a peer
decanus Mar 24, 2020
1108369
more granular control
decanus Mar 24, 2020
3288987
fix
decanus Mar 24, 2020
0fdc556
fix
decanus Mar 24, 2020
c1c4338
removed
decanus Mar 24, 2020
f1259d9
experimenting
decanus Mar 24, 2020
232e151
using debug
decanus Mar 24, 2020
a08a802
updated code to start N nodes
decanus Mar 25, 2020
7362cec
fix
decanus Mar 25, 2020
6cd619a
fix
decanus Mar 25, 2020
de04f7d
dean is too retarded for nim part 5
decanus Mar 25, 2020
fd280b1
confused
decanus Mar 25, 2020
762ad00
fi
decanus Mar 25, 2020
2687d9a
added todo, playing around
decanus Mar 26, 2020
23efa82
updated
decanus Mar 26, 2020
8cda0d7
distance func
decanus Mar 26, 2020
bf34d72
todo
decanus Mar 26, 2020
6fe7419
fix
decanus Mar 27, 2020
acad5f0
fix
decanus Mar 27, 2020
57b0041
made it work
decanus Mar 27, 2020
22d353d
changes
decanus Mar 27, 2020
4a0385c
update
decanus Mar 27, 2020
bca6584
multiple runs
decanus Mar 27, 2020
f044c55
minor improvements
decanus Mar 27, 2020
220d48e
using for
decanus Mar 27, 2020
6fb16fa
minor
decanus Mar 27, 2020
9e95aeb
little error
decanus Mar 27, 2020
6ac9040
woops
decanus Mar 27, 2020
e7bb186
update
decanus Mar 27, 2020
9dfd761
using sequence
decanus Mar 30, 2020
7342f15
changes
decanus Mar 30, 2020
ff9064d
updated
decanus Mar 30, 2020
289e5d8
unused
decanus Mar 30, 2020
5c73ae4
moved
decanus Mar 30, 2020
2322ddd
fixed
decanus Mar 31, 2020
73141d8
changed sleep to var
decanus Mar 31, 2020
c8a6ff8
changed node count
decanus Mar 31, 2020
b4546da
moved start
decanus Mar 31, 2020
2d41810
using contains
decanus Mar 31, 2020
acdc9bd
using sample functions
decanus Mar 31, 2020
690e5e0
added a random version
decanus Mar 31, 2020
4417457
back
decanus Mar 31, 2020
8508e9a
experimenting with random pairs
decanus Apr 1, 2020
4fce088
starting bootstrap node only
decanus Apr 1, 2020
7ae6a9f
made config flag
decanus Apr 1, 2020
ab8c6d8
remove echo
decanus Apr 1, 2020
c7c1e74
minor change
decanus Apr 1, 2020
8f40b34
added some comments
decanus Apr 1, 2020
fda0f82
printing port instead, more informative
decanus Apr 1, 2020
5d77e37
fixes
decanus Apr 1, 2020
6e05c6f
fix
decanus Apr 1, 2020
65e48b0
overhaul
decanus Apr 1, 2020
9ad274a
more
decanus Apr 1, 2020
f12922f
updated random
decanus Apr 1, 2020
0958f7f
added count
decanus Apr 1, 2020
7139b4e
fixes
decanus Apr 14, 2020
f8127c1
started working on ENR lookup
decanus Apr 15, 2020
bc825fd
using smarter distribution
decanus Apr 15, 2020
fd395cd
using countit
decanus Apr 15, 2020
335cb75
merged
decanus Apr 15, 2020
3c96d91
fix
decanus Apr 17, 2020
4294931
Merge pull request #25 from vacp2p/discv5-enr-lookup
decanus Apr 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions discv5/README.md
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).
16 changes: 16 additions & 0 deletions discv5/discv5.nimble
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"
208 changes: 208 additions & 0 deletions discv5/src/discv5.nim
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.} =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please elaborate on the purpose of this method?

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()
40 changes: 40 additions & 0 deletions discv5/src/utils.nim
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)