Skip to content

Commit

Permalink
chore: saving peers enr capabilities (#3127)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielmer authored Oct 24, 2024
1 parent dbf0222 commit 69d9524
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 50 deletions.
24 changes: 23 additions & 1 deletion tests/test_waku_enr.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{.used.}

import std/[options, sequtils], stew/results, testutils/unittests
import waku/waku_core, waku/waku_enr, ./testlib/wakucore
import waku/waku_core, waku/waku_enr, ./testlib/wakucore, waku/waku_core/codecs

suite "Waku ENR - Capabilities bitfield":
test "check capabilities support":
Expand Down Expand Up @@ -97,6 +97,28 @@ suite "Waku ENR - Capabilities bitfield":
bitfield.supportsCapability(Capabilities.Lightpush) == false
bitfield.toCapabilities() == @[Capabilities.Relay, Capabilities.Store]

test "get capabilities codecs from record":
## Given
let
enrSeqNum = 1u64
enrPrivKey = generatesecp256k1key()

## When
var builder = EnrBuilder.init(enrPrivKey, seqNum = enrSeqNum)
builder.withWakuCapabilities(Capabilities.Relay, Capabilities.Store)

let recordRes = builder.build()

## Then
assert recordRes.isOk(), $recordRes.error
let record = recordRes.tryGet()

let codecs = record.getCapabilitiesCodecs()
check:
codecs.len == 2
codecs.contains(WakuRelayCodec)
codecs.contains(WakuStoreCodec)

test "check capabilities on a non-waku node record":
## Given
# non waku enr, i.e. Ethereum one
Expand Down
40 changes: 38 additions & 2 deletions tests/waku_core/test_peers.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{.used.}

import
stew/results, testutils/unittests, libp2p/multiaddress, libp2p/peerid, libp2p/errors
import waku/waku_core
stew/results,
testutils/unittests,
libp2p/multiaddress,
libp2p/peerid,
libp2p/errors,
confutils/toml/std/net
import waku/[waku_core, waku_core/codecs, waku_enr], ../testlib/wakucore

suite "Waku Core - Peers":
test "Peer info parses correctly":
Expand Down Expand Up @@ -141,3 +146,34 @@ suite "Waku Core - Peers":
## Then
check:
parsePeerInfo(address).isErr()

test "ENRs capabilities are filled when creating RemotePeerInfo":
let
enrSeqNum = 1u64
enrPrivKey = generatesecp256k1key()

## When
var builder = EnrBuilder.init(enrPrivKey, seqNum = enrSeqNum)
builder.withIpAddressAndPorts(
ipAddr = some(parseIpAddress("127.0.0.1")),
tcpPort = some(Port(0)),
udpPort = some(Port(0)),
)
builder.withWakuCapabilities(Capabilities.Relay, Capabilities.Store)

let recordRes = builder.build()

## Then
assert recordRes.isOk(), $recordRes.error
let record = recordRes.tryGet()

let remotePeerInfoRes = record.toRemotePeerInfo()
assert remotePeerInfoRes.isOk(),
"failed creating RemotePeerInfo: " & $remotePeerInfoRes.error()

let remotePeerInfo = remotePeerInfoRes.get()

check:
remotePeerInfo.protocols.len == 2
remotePeerInfo.protocols.contains(WakuRelayCodec)
remotePeerInfo.protocols.contains(WakuStoreCodec)
102 changes: 73 additions & 29 deletions tests/waku_discv5/test_waku_discv5.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ import

import
waku/[waku_core/topics, waku_enr, discovery/waku_discv5, common/enr],
../testlib/[wakucore, testasync, assertions, futures],
../testlib/[wakucore, testasync, assertions, futures, wakunode],
../waku_enr/utils,
./utils

import eth/p2p/discoveryv5/enr as ethEnr

procSuite "Waku Discovery v5":
include waku/factory/waku

suite "Waku Discovery v5":
const validEnr =
"enr:-K64QGAvsATunmvMT5c3LFjKS0tG39zlQ1195Z2pWu6RoB5fWP3EXz9QPlRXN" &
"wOtDoRLgm4bATUB53AC8uml-ZtUE_kBgmlkgnY0gmlwhApkZgOKbXVsdGlhZGRyc4" &
"CCcnOTAAAIAAAAAQACAAMABAAFAAYAB4lzZWNwMjU2azGhAwG-CMmXpAPj84f6dCt" &
"MZ6xVYOa6bdmgAiKYG6LKGQlbg3RjcILqYIV3YWt1MgE"

let
rng = eth_keys.newRng()
pk1 = eth_keys.PrivateKey.random(rng[])
pk2 = eth_keys.PrivateKey.random(rng[])

enrNoCapabilities =
initRecord(1, pk1, {"rs": @[0.byte, 0.byte, 1.byte, 0.byte, 0.byte]}).value()
enrRelay = initRecord(
1, pk2, {"waku2": @[1.byte], "rs": @[0.byte, 1.byte, 1.byte, 0.byte, 1.byte]}
)
.value()
enrNoShardingInfo = initRecord(1, pk1, {"waku2": @[1.byte]}).value()

suite "shardingPredicate":
var
recordCluster21 {.threadvar.}: Record
Expand Down Expand Up @@ -120,6 +120,14 @@ procSuite "Waku Discovery v5":

asyncTest "filter peer per bootnode":
let
enrRelay = initRecord(
1,
pk2,
{"waku2": @[1.byte], "rs": @[0.byte, 1.byte, 1.byte, 0.byte, 1.byte]},
)
.value()
enrNoCapabilities =
initRecord(1, pk1, {"rs": @[0.byte, 0.byte, 1.byte, 0.byte, 0.byte]}).value()
predicateNoCapabilities =
shardingPredicate(enrNoCapabilities, @[enrNoCapabilities]).get()
predicateNoCapabilitiesWithBoth =
Expand Down Expand Up @@ -151,8 +159,10 @@ procSuite "Waku Discovery v5":
predicateRecord.isNone()

asyncTest "no relay sharding info":
let predicateNoShardingInfo =
shardingPredicate(enrNoShardingInfo, @[enrNoShardingInfo])
let
enrNoShardingInfo = initRecord(1, pk1, {"waku2": @[1.byte]}).value()
predicateNoShardingInfo =
shardingPredicate(enrNoShardingInfo, @[enrNoShardingInfo])

check:
predicateNoShardingInfo.isNone()
Expand All @@ -166,7 +176,7 @@ procSuite "Waku Discovery v5":
indices: seq[uint64] = @[],
recordFlags: Option[CapabilitiesBitfield] = none(CapabilitiesBitfield),
bootstrapRecords: seq[waku_enr.Record] = @[],
): (WakuDiscoveryV5, Record) =
): (WakuDiscoveryV5, Record) {.raises: [ValueError, LPError].} =
let
privKey = generateSecp256k1Key()
record = newTestEnrRecord(
Expand All @@ -188,17 +198,6 @@ procSuite "Waku Discovery v5":

(node, record)

let filterForStore: WakuDiscv5Predicate = proc(record: waku_enr.Record): bool =
let typedRecord = record.toTyped()
if typedRecord.isErr():
return false

let capabilities = typedRecord.value.waku2
if capabilities.isNone():
return false

return capabilities.get().supportsCapability(Capabilities.Store)

asyncTest "find random peers without predicate":
# Given 3 nodes
let
Expand Down Expand Up @@ -234,6 +233,17 @@ procSuite "Waku Discovery v5":
await allFutures(node1.stop(), node2.stop(), node3.stop())

asyncTest "find random peers with parameter predicate":
let filterForStore: WakuDiscv5Predicate = proc(record: waku_enr.Record): bool =
let typedRecord = record.toTyped()
if typedRecord.isErr():
return false

let capabilities = typedRecord.value.waku2
if capabilities.isNone():
return false

return capabilities.get().supportsCapability(Capabilities.Store)

# Given 4 nodes
let
(node3, record3) = buildNode(
Expand Down Expand Up @@ -346,11 +356,6 @@ procSuite "Waku Discovery v5":
await allFutures(node1.stop(), node2.stop(), node3.stop(), node4.stop())

suite "addBoostrapNode":
let validEnr =
"enr:-I-4QG3mX250ArniAs2DLpW-QHOLKSD5x_Ibp8AYcQZbz1HhHFJtl2dNDGcha" &
"U5ugLbDKRgtTDZH8NsxXlTXDpYAgzgBgmlkgnY0gnJzjwAVBgABAAIABQAHAAkAC4" &
"lzZWNwMjU2azGhA4_KwN0NRRmmfQ-B9B2h2PZjoJvBnaIOi6sR_b2UTQBBhXdha3U" & "yAQ"

asyncTest "address is valid":
# Given an empty list of enrs
var enrs: seq[Record] = @[]
Expand Down Expand Up @@ -400,3 +405,42 @@ procSuite "Waku Discovery v5":
# Then the enr is not added to the list
check:
enrs.len == 0

suite "waku discv5 initialization":
asyncTest "Discv5 bootstrap nodes should be added to the peer store":
var conf = defaultTestWakuNodeConf()

conf.discv5BootstrapNodes = @[validEnr]

let waku = Waku.init(conf).valueOr:
raiseAssert error

discard setupDiscoveryV5(
waku.node.enr, waku.node.peerManager, waku.node.topicSubscriptionQueue,
waku.conf, waku.dynamicBootstrapNodes, waku.rng, waku.key,
)

check:
waku.node.peerManager.wakuPeerStore.peers().anyIt(
it.enr.isSome() and it.enr.get().toUri() == validEnr
)

asyncTest "Invalid discv5 bootstrap node ENRs are ignored":
var conf = defaultTestWakuNodeConf()

let invalidEnr = "invalid-enr"

conf.discv5BootstrapNodes = @[invalidEnr]

let waku = Waku.init(conf).valueOr:
raiseAssert error

discard setupDiscoveryV5(
waku.node.enr, waku.node.peerManager, waku.node.topicSubscriptionQueue,
waku.conf, waku.dynamicBootstrapNodes, waku.rng, waku.key,
)

check:
not waku.node.peerManager.wakuPeerStore.peers().anyIt(
it.enr.isSome() and it.enr.get().toUri() == invalidEnr
)
8 changes: 8 additions & 0 deletions waku/discovery/waku_discv5.nim
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,14 @@ proc setupDiscoveryV5*(
for enrUri in conf.discv5BootstrapNodes:
addBootstrapNode(enrUri, discv5BootstrapEnrs)

for enr in discv5BootstrapEnrs:
let peerInfoRes = enr.toRemotePeerInfo()
if peerInfoRes.isOk():
nodePeerManager.addPeer(peerInfoRes.get(), PeerOrigin.Discv5)
else:
debug "could not convert discv5 bootstrap node to peerInfo, not adding peer to Peer Store",
enr = enr.toUri(), error = peerInfoRes.error

discv5BootstrapEnrs.add(dynamicBootstrapEnrs)

let discv5Config = DiscoveryConfig.init(
Expand Down
6 changes: 6 additions & 0 deletions waku/factory/node_factory.nim
Original file line number Diff line number Diff line change
Expand Up @@ -399,12 +399,18 @@ proc startNode*(

# Connect to configured static nodes
if conf.staticnodes.len > 0:
if not conf.relay:
return err("waku relay (--relay=true) should be set when configuring staticnodes")
try:
await connectToNodes(node, conf.staticnodes, "static")
except CatchableError:
return err("failed to connect to static nodes: " & getCurrentExceptionMsg())

if dynamicBootstrapNodes.len > 0:
if not conf.relay:
return err(
"waku relay (--relay=true) should be set when configuring dynamicBootstrapNodes"
)
info "Connecting to dynamic bootstrap peers"
try:
await connectToNodes(node, dynamicBootstrapNodes, "dynamic bootstrap")
Expand Down
10 changes: 10 additions & 0 deletions waku/waku_core/codecs.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const
WakuRelayCodec* = "/vac/waku/relay/2.0.0"
WakuStoreCodec* = "/vac/waku/store-query/3.0.0"
WakuFilterSubscribeCodec* = "/vac/waku/filter-subscribe/2.0.0-beta1"
WakuFilterPushCodec* = "/vac/waku/filter-push/2.0.0-beta1"
WakuLightPushCodec* = "/vac/waku/lightpush/2.0.0-beta1"
WakuSyncCodec* = "/vac/waku/sync/1.0.0"
WakuMetadataCodec* = "/vac/waku/metadata/1.0.0"
WakuPeerExchangeCodec* = "/vac/waku/peer-exchange/2.0.0-alpha1"
WakuLegacyStoreCodec* = "/vac/waku/store/2.0.0-beta4"
16 changes: 14 additions & 2 deletions waku/waku_core/peers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import
std/[options, sequtils, strutils, uri, net],
results,
chronos,
chronicles,
eth/keys,
eth/p2p/discoveryv5/enr,
eth/net/utils,
Expand All @@ -16,6 +17,7 @@ import
libp2p/peerinfo,
libp2p/routing_record,
json_serialization
import ../waku_enr/capabilities

type
Connectedness* = enum
Expand Down Expand Up @@ -243,7 +245,17 @@ proc toRemotePeerInfo*(enr: enr.Record): Result[RemotePeerInfo, cstring] =
if addrs.len == 0:
return err("enr: no addresses in record")

return ok(RemotePeerInfo.init(peerId, addrs, some(enr)))
let protocolsRes = catch:
enr.getCapabilitiesCodecs()

var protocols: seq[string]
if not protocolsRes.isErr():
protocols = protocolsRes.get()
else:
error "Could not retrieve supported protocols from enr",
peerId = peerId, msg = protocolsRes.error.msg

return ok(RemotePeerInfo.init(peerId, addrs, some(enr), protocols))

converter toRemotePeerInfo*(peerRecord: PeerRecord): RemotePeerInfo =
## Converts peer records to dialable RemotePeerInfo
Expand All @@ -256,7 +268,7 @@ converter toRemotePeerInfo*(peerInfo: PeerInfo): RemotePeerInfo =
RemotePeerInfo(
peerId: peerInfo.peerId,
addrs: peerInfo.listenAddrs,
enr: none(Record),
enr: none(enr.Record),
protocols: peerInfo.protocols,
agent: peerInfo.agentVersion,
protoVersion: peerInfo.protoVersion,
Expand Down
17 changes: 15 additions & 2 deletions waku/waku_enr/capabilities.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{.push raises: [].}

import std/[options, bitops, sequtils, net], results, eth/keys, libp2p/crypto/crypto
import ../common/enr
import
std/[options, bitops, sequtils, net, tables], results, eth/keys, libp2p/crypto/crypto
import ../common/enr, ../waku_core/codecs

const CapabilitiesEnrField* = "waku2"

Expand All @@ -20,6 +21,14 @@ type
Lightpush = 3
Sync = 4

const capabilityToCodec = {
Capabilities.Relay: WakuRelayCodec,
Capabilities.Store: WakuStoreCodec,
Capabilities.Filter: WakuFilterSubscribeCodec,
Capabilities.Lightpush: WakuLightPushCodec,
Capabilities.Sync: WakuSyncCodec,
}.toTable

func init*(
T: type CapabilitiesBitfield, lightpush, filter, store, relay, sync: bool = false
): T =
Expand Down Expand Up @@ -101,3 +110,7 @@ proc getCapabilities*(r: Record): seq[Capabilities] =

let bitfield = bitfieldOpt.get()
bitfield.toCapabilities()

proc getCapabilitiesCodecs*(r: Record): seq[string] {.raises: [ValueError].} =
let capabilities = r.getCapabilities()
return capabilities.mapIt(capabilityToCodec[it])
5 changes: 2 additions & 3 deletions waku/waku_filter_v2/common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import results

const
WakuFilterSubscribeCodec* = "/vac/waku/filter-subscribe/2.0.0-beta1"
WakuFilterPushCodec* = "/vac/waku/filter-push/2.0.0-beta1"
from ../waku_core/codecs import WakuFilterSubscribeCodec, WakuFilterPushCodec
export WakuFilterSubscribeCodec, WakuFilterPushCodec

type
FilterSubscribeErrorKind* {.pure.} = enum
Expand Down
3 changes: 2 additions & 1 deletion waku/waku_lightpush/common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import results, chronos, libp2p/peerid
import ../waku_core

const WakuLightPushCodec* = "/vac/waku/lightpush/2.0.0-beta1"
from ../waku_core/codecs import WakuLightPushCodec
export WakuLightPushCodec

type WakuLightPushResult*[T] = Result[T, string]

Expand Down
Loading

0 comments on commit 69d9524

Please sign in to comment.