Generic re-style with nph 0.5.1 (#2396)

This commit is contained in:
Ivan FB 2024-03-16 00:08:47 +01:00 committed by GitHub
parent dde94d4b52
commit cf6298ca1f
343 changed files with 17273 additions and 14393 deletions

View File

@ -1,7 +1,7 @@
## chat2 is an example of usage of Waku v2. For suggested usage options, please ## chat2 is an example of usage of Waku v2. For suggested usage options, please
## see dingpu tutorial in docs folder. ## see dingpu tutorial in docs folder.
when not(compileOption("threads")): when not (compileOption("threads")):
{.fatal: "Please, compile this program with the --threads:on option!".} {.fatal: "Please, compile this program with the --threads:on option!".}
when (NimMajor, NimMinor) < (1, 4): when (NimMajor, NimMinor) < (1, 4):
@ -10,18 +10,29 @@ else:
{.push raises: [].} {.push raises: [].}
import std/[strformat, strutils, times, options, random] import std/[strformat, strutils, times, options, random]
import confutils, chronicles, chronos, stew/shims/net as stewNet, import
eth/keys, bearssl, stew/[byteutils, results], confutils,
metrics, chronicles,
metrics/chronos_httpserver chronos,
import libp2p/[switch, # manage transports, a single entry point for dialing and listening stew/shims/net as stewNet,
crypto/crypto, # cryptographic functions eth/keys,
stream/connection, # create and close stream read / write connections bearssl,
multiaddress, # encode different addressing schemes. For example, /ip4/7.7.7.7/tcp/6543 means it is using IPv4 protocol and TCP stew/[byteutils, results],
peerinfo, # manage the information of a peer, such as peer ID and public / private key metrics,
peerid, # Implement how peers interact metrics/chronos_httpserver
protobuf/minprotobuf, # message serialisation/deserialisation from and to protobufs import
nameresolving/dnsresolver]# define DNS resolution libp2p/[
switch, # manage transports, a single entry point for dialing and listening
crypto/crypto, # cryptographic functions
stream/connection, # create and close stream read / write connections
multiaddress,
# encode different addressing schemes. For example, /ip4/7.7.7.7/tcp/6543 means it is using IPv4 protocol and TCP
peerinfo,
# manage the information of a peer, such as peer ID and public / private key
peerid, # Implement how peers interact
protobuf/minprotobuf, # message serialisation/deserialisation from and to protobufs
nameresolving/dnsresolver,
] # define DNS resolution
import import
../../waku/waku_core, ../../waku/waku_core,
../../waku/waku_lightpush/common, ../../waku/waku_lightpush/common,
@ -37,13 +48,11 @@ import
../../waku/common/utils/nat, ../../waku/common/utils/nat,
./config_chat2 ./config_chat2
import import libp2p/protocols/pubsub/rpc/messages, libp2p/protocols/pubsub/pubsub
libp2p/protocols/pubsub/rpc/messages, import ../../waku/waku_rln_relay
libp2p/protocols/pubsub/pubsub
import
../../waku/waku_rln_relay
const Help = """ const Help =
"""
Commands: /[?|help|connect|nick|exit] Commands: /[?|help|connect|nick|exit]
help: Prints this help help: Prints this help
connect: dials a remote peer connect: dials a remote peer
@ -55,14 +64,14 @@ const Help = """
# Could poll connection pool or something here, I suppose # Could poll connection pool or something here, I suppose
# TODO Ensure connected turns true on incoming connections, or get rid of it # TODO Ensure connected turns true on incoming connections, or get rid of it
type Chat = ref object type Chat = ref object
node: WakuNode # waku node for publishing, subscribing, etc node: WakuNode # waku node for publishing, subscribing, etc
transp: StreamTransport # transport streams between read & write file descriptor transp: StreamTransport # transport streams between read & write file descriptor
subscribed: bool # indicates if a node is subscribed or not to a topic subscribed: bool # indicates if a node is subscribed or not to a topic
connected: bool # if the node is connected to another peer connected: bool # if the node is connected to another peer
started: bool # if the node has started started: bool # if the node has started
nick: string # nickname for this chat session nick: string # nickname for this chat session
prompt: bool # chat prompt is showing prompt: bool # chat prompt is showing
contentTopic: string # default content topic for chat messages contentTopic: string # default content topic for chat messages
type type
PrivateKey* = crypto.PrivateKey PrivateKey* = crypto.PrivateKey
@ -85,11 +94,11 @@ proc init*(T: type Chat2Message, buffer: seq[byte]): ProtoResult[T] =
let pb = initProtoBuffer(buffer) let pb = initProtoBuffer(buffer)
var timestamp: uint64 var timestamp: uint64
discard ? pb.getField(1, timestamp) discard ?pb.getField(1, timestamp)
msg.timestamp = int64(timestamp) msg.timestamp = int64(timestamp)
discard ? pb.getField(2, msg.nick) discard ?pb.getField(2, msg.nick)
discard ? pb.getField(3, msg.payload) discard ?pb.getField(3, msg.payload)
ok(msg) ok(msg)
@ -124,19 +133,25 @@ proc showChatPrompt(c: Chat) =
except IOError: except IOError:
discard discard
proc getChatLine(c: Chat, msg:WakuMessage): Result[string, string]= proc getChatLine(c: Chat, msg: WakuMessage): Result[string, string] =
# No payload encoding/encryption from Waku # No payload encoding/encryption from Waku
let let
pb = Chat2Message.init(msg.payload) pb = Chat2Message.init(msg.payload)
chatLine = if pb.isOk: pb[].toString() chatLine =
else: string.fromBytes(msg.payload) if pb.isOk:
pb[].toString()
else:
string.fromBytes(msg.payload)
return ok(chatline) return ok(chatline)
proc printReceivedMessage(c: Chat, msg: WakuMessage) = proc printReceivedMessage(c: Chat, msg: WakuMessage) =
let let
pb = Chat2Message.init(msg.payload) pb = Chat2Message.init(msg.payload)
chatLine = if pb.isOk: pb[].toString() chatLine =
else: string.fromBytes(msg.payload) if pb.isOk:
pb[].toString()
else:
string.fromBytes(msg.payload)
try: try:
echo &"{chatLine}" echo &"{chatLine}"
except ValueError: except ValueError:
@ -145,8 +160,8 @@ proc printReceivedMessage(c: Chat, msg: WakuMessage) =
c.prompt = false c.prompt = false
showChatPrompt(c) showChatPrompt(c)
trace "Printing message", topic=DefaultPubsubTopic, chatLine, trace "Printing message",
contentTopic = msg.contentTopic topic = DefaultPubsubTopic, chatLine, contentTopic = msg.contentTopic
proc readNick(transp: StreamTransport): Future[string] {.async.} = proc readNick(transp: StreamTransport): Future[string] {.async.} =
# Chat prompt # Chat prompt
@ -154,9 +169,10 @@ proc readNick(transp: StreamTransport): Future[string] {.async.} =
stdout.flushFile() stdout.flushFile()
return await transp.readLine() return await transp.readLine()
proc startMetricsServer(
proc startMetricsServer(serverIp: IpAddress, serverPort: Port): Result[MetricsHttpServerRef, string] = serverIp: IpAddress, serverPort: Port
info "Starting metrics HTTP server", serverIp= $serverIp, serverPort= $serverPort ): Result[MetricsHttpServerRef, string] =
info "Starting metrics HTTP server", serverIp = $serverIp, serverPort = $serverPort
let metricsServerRes = MetricsHttpServerRef.new($serverIp, serverPort) let metricsServerRes = MetricsHttpServerRef.new($serverIp, serverPort)
if metricsServerRes.isErr(): if metricsServerRes.isErr():
@ -168,23 +184,25 @@ proc startMetricsServer(serverIp: IpAddress, serverPort: Port): Result[MetricsHt
except CatchableError: except CatchableError:
return err("metrics HTTP server start failed: " & getCurrentExceptionMsg()) return err("metrics HTTP server start failed: " & getCurrentExceptionMsg())
info "Metrics HTTP server started", serverIp= $serverIp, serverPort= $serverPort info "Metrics HTTP server started", serverIp = $serverIp, serverPort = $serverPort
ok(metricsServerRes.value) ok(metricsServerRes.value)
proc publish(c: Chat, line: string) = proc publish(c: Chat, line: string) =
# First create a Chat2Message protobuf with this line of text # First create a Chat2Message protobuf with this line of text
let time = getTime().toUnix() let time = getTime().toUnix()
let chat2pb = Chat2Message(timestamp: time, let chat2pb =
nick: c.nick, Chat2Message(timestamp: time, nick: c.nick, payload: line.toBytes()).encode()
payload: line.toBytes()).encode()
## @TODO: error handling on failure ## @TODO: error handling on failure
proc handler(response: PushResponse) {.gcsafe, closure.} = proc handler(response: PushResponse) {.gcsafe, closure.} =
trace "lightpush response received", response=response trace "lightpush response received", response = response
var message = WakuMessage(payload: chat2pb.buffer, var message = WakuMessage(
contentTopic: c.contentTopic, version: 0, timestamp: getNanosecondTime(time)) payload: chat2pb.buffer,
contentTopic: c.contentTopic,
version: 0,
timestamp: getNanosecondTime(time),
)
if not isNil(c.node.wakuRlnRelay): if not isNil(c.node.wakuRlnRelay):
# for future version when we support more than one rln protected content topic, # for future version when we support more than one rln protected content topic,
# we should check the message content topic as well # we should check the message content topic as well
@ -201,7 +219,8 @@ proc publish(c: Chat, line: string) =
# TODO move it to log after dogfooding # TODO move it to log after dogfooding
let msgEpoch = fromEpoch(proof.epoch) let msgEpoch = fromEpoch(proof.epoch)
if fromEpoch(c.node.wakuRlnRelay.lastEpoch) == msgEpoch: if fromEpoch(c.node.wakuRlnRelay.lastEpoch) == msgEpoch:
echo "--rln epoch: ", msgEpoch, " ⚠️ message rate violation! you are spamming the network!" echo "--rln epoch: ",
msgEpoch, " ⚠️ message rate violation! you are spamming the network!"
else: else:
echo "--rln epoch: ", msgEpoch echo "--rln epoch: ", msgEpoch
# update the last epoch # update the last epoch
@ -216,25 +235,25 @@ proc publish(c: Chat, line: string) =
(waitFor c.node.publish(some(DefaultPubsubTopic), message)).isOkOr: (waitFor c.node.publish(some(DefaultPubsubTopic), message)).isOkOr:
error "failed to publish message", error = error error "failed to publish message", error = error
except CatchableError: except CatchableError:
error "caught error publishing message: ", error = getCurrentExceptionMsg() error "caught error publishing message: ", error = getCurrentExceptionMsg()
# TODO This should read or be subscribe handler subscribe # TODO This should read or be subscribe handler subscribe
proc readAndPrint(c: Chat) {.async.} = proc readAndPrint(c: Chat) {.async.} =
while true: while true:
# while p.connected: # while p.connected:
# # TODO: echo &"{p.id} -> " # # TODO: echo &"{p.id} -> "
# #
# echo cast[string](await p.conn.readLp(1024)) # echo cast[string](await p.conn.readLp(1024))
#echo "readAndPrint subscribe NYI" #echo "readAndPrint subscribe NYI"
await sleepAsync(100.millis) await sleepAsync(100.millis)
# TODO Implement # TODO Implement
proc writeAndPrint(c: Chat) {.async.} = proc writeAndPrint(c: Chat) {.async.} =
while true: while true:
# Connect state not updated on incoming WakuRelay connections # Connect state not updated on incoming WakuRelay connections
# if not c.connected: # if not c.connected:
# echo "type an address or wait for a connection:" # echo "type an address or wait for a connection:"
# echo "type /[help|?] for help" # echo "type /[help|?] for help"
# Chat prompt # Chat prompt
showChatPrompt(c) showChatPrompt(c)
@ -244,11 +263,11 @@ proc writeAndPrint(c: Chat) {.async.} =
echo Help echo Help
continue continue
# if line.startsWith("/disconnect"): # if line.startsWith("/disconnect"):
# echo "Ending current session" # echo "Ending current session"
# if p.connected and p.conn.closed.not: # if p.connected and p.conn.closed.not:
# await p.conn.close() # await p.conn.close()
# p.connected = false # p.connected = false
elif line.startsWith("/connect"): elif line.startsWith("/connect"):
# TODO Should be able to connect to multiple peers for Waku chat # TODO Should be able to connect to multiple peers for Waku chat
if c.connected: if c.connected:
@ -259,19 +278,21 @@ proc writeAndPrint(c: Chat) {.async.} =
let address = await c.transp.readLine() let address = await c.transp.readLine()
if address.len > 0: if address.len > 0:
await c.connectToNodes(@[address]) await c.connectToNodes(@[address])
elif line.startsWith("/nick"): elif line.startsWith("/nick"):
# Set a new nickname # Set a new nickname
c.nick = await readNick(c.transp) c.nick = await readNick(c.transp)
echo "You are now known as " & c.nick echo "You are now known as " & c.nick
elif line.startsWith("/exit"): elif line.startsWith("/exit"):
if not c.node.wakuFilterLegacy.isNil(): if not c.node.wakuFilterLegacy.isNil():
echo "unsubscribing from content filters..." echo "unsubscribing from content filters..."
let peerOpt = c.node.peerManager.selectPeer(WakuLegacyFilterCodec) let peerOpt = c.node.peerManager.selectPeer(WakuLegacyFilterCodec)
if peerOpt.isSome(): if peerOpt.isSome():
await c.node.legacyFilterUnsubscribe(pubsubTopic=some(DefaultPubsubTopic), contentTopics=c.contentTopic, peer=peerOpt.get()) await c.node.legacyFilterUnsubscribe(
pubsubTopic = some(DefaultPubsubTopic),
contentTopics = c.contentTopic,
peer = peerOpt.get(),
)
echo "quitting..." echo "quitting..."
@ -307,21 +328,28 @@ proc readInput(wfd: AsyncFD) {.thread, raises: [Defect, CatchableError].} =
let line = stdin.readLine() let line = stdin.readLine()
discard waitFor transp.write(line & "\r\n") discard waitFor transp.write(line & "\r\n")
{.pop.} # @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError {.pop.}
# @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError
proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} = proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
let let
transp = fromPipe(rfd) transp = fromPipe(rfd)
conf = Chat2Conf.load() conf = Chat2Conf.load()
nodekey = if conf.nodekey.isSome(): conf.nodekey.get() nodekey =
else: PrivateKey.random(Secp256k1, rng[]).tryGet() if conf.nodekey.isSome():
conf.nodekey.get()
else:
PrivateKey.random(Secp256k1, rng[]).tryGet()
# set log level # set log level
if conf.logLevel != LogLevel.NONE: if conf.logLevel != LogLevel.NONE:
setLogLevel(conf.logLevel) setLogLevel(conf.logLevel)
let natRes = setupNat(conf.nat, clientId, let natRes = setupNat(
Port(uint16(conf.tcpPort) + conf.portsShift), conf.nat,
Port(uint16(conf.udpPort) + conf.portsShift)) clientId,
Port(uint16(conf.tcpPort) + conf.portsShift),
Port(uint16(conf.udpPort) + conf.portsShift),
)
if natRes.isErr(): if natRes.isErr():
raise newException(ValueError, "setupNat error " & natRes.error) raise newException(ValueError, "setupNat error " & natRes.error)
@ -333,20 +361,28 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
let recordRes = enrBuilder.build() let recordRes = enrBuilder.build()
let record = let record =
if recordRes.isErr(): if recordRes.isErr():
error "failed to create enr record", error=recordRes.error error "failed to create enr record", error = recordRes.error
quit(QuitFailure) quit(QuitFailure)
else: recordRes.get() else:
recordRes.get()
let node = block: let node = block:
var builder = WakuNodeBuilder.init() var builder = WakuNodeBuilder.init()
builder.withNodeKey(nodeKey) builder.withNodeKey(nodeKey)
builder.withRecord(record) builder.withRecord(record)
builder.withNetworkConfigurationDetails(conf.listenAddress, Port(uint16(conf.tcpPort) + conf.portsShift),
extIp, extTcpPort, builder
wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift), .withNetworkConfigurationDetails(
wsEnabled = conf.websocketSupport, conf.listenAddress,
wssEnabled = conf.websocketSecureSupport).tryGet() Port(uint16(conf.tcpPort) + conf.portsShift),
builder.build().tryGet() extIp,
extTcpPort,
wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift),
wsEnabled = conf.websocketSupport,
wssEnabled = conf.websocketSecureSupport,
)
.tryGet()
builder.build().tryGet()
await node.start() await node.start()
@ -361,14 +397,16 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
let nick = await readNick(transp) let nick = await readNick(transp)
echo "Welcome, " & nick & "!" echo "Welcome, " & nick & "!"
var chat = Chat(node: node, var chat = Chat(
transp: transp, node: node,
subscribed: true, transp: transp,
connected: false, subscribed: true,
started: true, connected: false,
nick: nick, started: true,
prompt: false, nick: nick,
contentTopic: conf.contentTopic) prompt: false,
contentTopic: conf.contentTopic,
)
if conf.staticnodes.len > 0: if conf.staticnodes.len > 0:
echo "Connecting to static peers..." echo "Connecting to static peers..."
@ -381,14 +419,17 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
echo "Connecting to " & $conf.fleet & " fleet using DNS discovery..." echo "Connecting to " & $conf.fleet & " fleet using DNS discovery..."
if conf.fleet == Fleet.test: if conf.fleet == Fleet.test:
dnsDiscoveryUrl = some("enrtree://AO47IDOLBKH72HIZZOXQP6NMRESAN7CHYWIBNXDXWRJRZWLODKII6@test.wakuv2.nodes.status.im") dnsDiscoveryUrl = some(
"enrtree://AO47IDOLBKH72HIZZOXQP6NMRESAN7CHYWIBNXDXWRJRZWLODKII6@test.wakuv2.nodes.status.im"
)
else: else:
# Connect to prod by default # Connect to prod by default
dnsDiscoveryUrl = some("enrtree://ANEDLO25QVUGJOUTQFRYKWX6P4Z4GKVESBMHML7DZ6YK4LGS5FC5O@prod.wakuv2.nodes.status.im") dnsDiscoveryUrl = some(
"enrtree://ANEDLO25QVUGJOUTQFRYKWX6P4Z4GKVESBMHML7DZ6YK4LGS5FC5O@prod.wakuv2.nodes.status.im"
)
elif conf.dnsDiscovery and conf.dnsDiscoveryUrl != "": elif conf.dnsDiscovery and conf.dnsDiscoveryUrl != "":
# No pre-selected fleet. Discover nodes via DNS using user config # No pre-selected fleet. Discover nodes via DNS using user config
debug "Discovering nodes using Waku DNS discovery", url=conf.dnsDiscoveryUrl debug "Discovering nodes using Waku DNS discovery", url = conf.dnsDiscoveryUrl
dnsDiscoveryUrl = some(conf.dnsDiscoveryUrl) dnsDiscoveryUrl = some(conf.dnsDiscoveryUrl)
var discoveredNodes: seq[RemotePeerInfo] var discoveredNodes: seq[RemotePeerInfo]
@ -401,12 +442,11 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
let dnsResolver = DnsResolver.new(nameServers) let dnsResolver = DnsResolver.new(nameServers)
proc resolver(domain: string): Future[string] {.async, gcsafe.} = proc resolver(domain: string): Future[string] {.async, gcsafe.} =
trace "resolving", domain=domain trace "resolving", domain = domain
let resolved = await dnsResolver.resolveTxt(domain) let resolved = await dnsResolver.resolveTxt(domain)
return resolved[0] # Use only first answer return resolved[0] # Use only first answer
var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl.get(), var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl.get(), resolver)
resolver)
if wakuDnsDiscovery.isOk: if wakuDnsDiscovery.isOk:
let discoveredPeers = wakuDnsDiscovery.get().findPeers() let discoveredPeers = wakuDnsDiscovery.get().findPeers()
if discoveredPeers.isOk: if discoveredPeers.isOk:
@ -432,10 +472,9 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
storenode = some(peerInfo.value) storenode = some(peerInfo.value)
else: else:
error "Incorrect conf.storenode", error = peerInfo.error error "Incorrect conf.storenode", error = peerInfo.error
elif discoveredNodes.len > 0: elif discoveredNodes.len > 0:
echo "Store enabled, but no store nodes configured. Choosing one at random from discovered peers" echo "Store enabled, but no store nodes configured. Choosing one at random from discovered peers"
storenode = some(discoveredNodes[rand(0..len(discoveredNodes) - 1)]) storenode = some(discoveredNodes[rand(0 .. len(discoveredNodes) - 1)])
if storenode.isSome(): if storenode.isSome():
# We have a viable storenode. Let's query it for historical messages. # We have a viable storenode. Let's query it for historical messages.
@ -448,8 +487,11 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
for msg in response.messages: for msg in response.messages:
let let
pb = Chat2Message.init(msg.payload) pb = Chat2Message.init(msg.payload)
chatLine = if pb.isOk: pb[].toString() chatLine =
else: string.fromBytes(msg.payload) if pb.isOk:
pb[].toString()
else:
string.fromBytes(msg.payload)
echo &"{chatLine}" echo &"{chatLine}"
info "Hit store handler" info "Hit store handler"
@ -466,7 +508,7 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
node.peerManager.addServicePeer(peerInfo.value, WakuLightpushCodec) node.peerManager.addServicePeer(peerInfo.value, WakuLightpushCodec)
else: else:
error "LightPush not mounted. Couldn't parse conf.lightpushnode", error "LightPush not mounted. Couldn't parse conf.lightpushnode",
error = peerInfo.error error = peerInfo.error
if conf.filternode != "": if conf.filternode != "":
let peerInfo = parsePeerInfo(conf.filternode) let peerInfo = parsePeerInfo(conf.filternode)
@ -476,19 +518,22 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
await node.mountFilterClient() await node.mountFilterClient()
node.peerManager.addServicePeer(peerInfo.value, WakuLegacyFilterCodec) node.peerManager.addServicePeer(peerInfo.value, WakuLegacyFilterCodec)
proc filterHandler(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async, gcsafe, closure.} = proc filterHandler(
trace "Hit filter handler", contentTopic=msg.contentTopic pubsubTopic: PubsubTopic, msg: WakuMessage
) {.async, gcsafe, closure.} =
trace "Hit filter handler", contentTopic = msg.contentTopic
chat.printReceivedMessage(msg) chat.printReceivedMessage(msg)
await node.legacyFilterSubscribe(pubsubTopic=some(DefaultPubsubTopic), await node.legacyFilterSubscribe(
contentTopics=chat.contentTopic, pubsubTopic = some(DefaultPubsubTopic),
filterHandler, contentTopics = chat.contentTopic,
peerInfo.value) filterHandler,
peerInfo.value,
)
# TODO: Here to support FilterV2 relevant subscription, but still # TODO: Here to support FilterV2 relevant subscription, but still
# Legacy Filter is concurrent to V2 untill legacy filter will be removed # Legacy Filter is concurrent to V2 untill legacy filter will be removed
else: else:
error "Filter not mounted. Couldn't parse conf.filternode", error "Filter not mounted. Couldn't parse conf.filternode", error = peerInfo.error
error = peerInfo.error
# Subscribe to a topic, if relay is mounted # Subscribe to a topic, if relay is mounted
if conf.relay: if conf.relay:
@ -524,7 +569,7 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
rlnRelayCredPath: conf.rlnRelayCredPath, rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword, rlnRelayCredPassword: conf.rlnRelayCredPassword,
rlnRelayUserMessageLimit: conf.rlnRelayUserMessageLimit, rlnRelayUserMessageLimit: conf.rlnRelayUserMessageLimit,
rlnEpochSizeSec: conf.rlnEpochSizeSec rlnEpochSizeSec: conf.rlnEpochSizeSec,
) )
else: else:
let rlnConf = WakuRlnConfig( let rlnConf = WakuRlnConfig(
@ -534,16 +579,16 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
rlnRelayEthClientAddress: string(conf.rlnRelayethClientAddress), rlnRelayEthClientAddress: string(conf.rlnRelayethClientAddress),
rlnRelayCredPath: conf.rlnRelayCredPath, rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword, rlnRelayCredPassword: conf.rlnRelayCredPassword,
rlnEpochSizeSec: conf.rlnEpochSizeSec rlnEpochSizeSec: conf.rlnEpochSizeSec,
) )
waitFor node.mountRlnRelay(rlnConf, waitFor node.mountRlnRelay(rlnConf, spamHandler = some(spamHandler))
spamHandler=some(spamHandler))
let membershipIndex = node.wakuRlnRelay.groupManager.membershipIndex.get() let membershipIndex = node.wakuRlnRelay.groupManager.membershipIndex.get()
let identityCredential = node.wakuRlnRelay.groupManager.idCredentials.get() let identityCredential = node.wakuRlnRelay.groupManager.idCredentials.get()
echo "your membership index is: ", membershipIndex echo "your membership index is: ", membershipIndex
echo "your rln identity commitment key is: ", identityCredential.idCommitment.inHex() echo "your rln identity commitment key is: ",
identityCredential.idCommitment.inHex()
else: else:
info "WakuRLNRelay is disabled" info "WakuRLNRelay is disabled"
echo "WakuRLNRelay is disabled, please enable it by passing in the --rln-relay flag" echo "WakuRLNRelay is disabled, please enable it by passing in the --rln-relay flag"
@ -552,11 +597,9 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
if conf.metricsServer: if conf.metricsServer:
let metricsServer = startMetricsServer( let metricsServer = startMetricsServer(
conf.metricsServerAddress, conf.metricsServerAddress, Port(conf.metricsServerPort + conf.portsShift)
Port(conf.metricsServerPort + conf.portsShift)
) )
await chat.readWriteLoop() await chat.readWriteLoop()
if conf.keepAlive: if conf.keepAlive:
@ -578,7 +621,6 @@ proc main(rng: ref HmacDrbgContext) {.async.} =
except ConfigurationError as e: except ConfigurationError as e:
raise e raise e
when isMainModule: # isMainModule = true when the module is compiled as the main file when isMainModule: # isMainModule = true when the module is compiled as the main file
let rng = crypto.newRng() let rng = crypto.newRng()
try: try:

View File

@ -1,254 +1,268 @@
import import
chronicles, chronos, chronicles,
confutils, confutils/defs, confutils/std/net, chronos,
confutils,
confutils/defs,
confutils/std/net,
eth/keys, eth/keys,
libp2p/crypto/crypto, libp2p/crypto/crypto,
libp2p/crypto/secp, libp2p/crypto/secp,
nimcrypto/utils, nimcrypto/utils,
std/strutils, std/strutils,
regex regex
import import ../../../waku/waku_core
../../../waku/waku_core
type type
Fleet* = enum Fleet* = enum
none none
prod prod
test test
EthRpcUrl = distinct string EthRpcUrl = distinct string
Chat2Conf* = object Chat2Conf* = object ## General node config
## General node config
logLevel* {. logLevel* {.
desc: "Sets the log level." desc: "Sets the log level.", defaultValue: LogLevel.INFO, name: "log-level"
defaultValue: LogLevel.INFO .}: LogLevel
name: "log-level" }: LogLevel
nodekey* {. nodekey* {.desc: "P2P node private key as 64 char hex string.", name: "nodekey".}:
desc: "P2P node private key as 64 char hex string.", Option[crypto.PrivateKey]
name: "nodekey" }: Option[crypto.PrivateKey]
listenAddress* {. listenAddress* {.
defaultValue: defaultListenAddress(config) defaultValue: defaultListenAddress(config),
desc: "Listening address for the LibP2P traffic." desc: "Listening address for the LibP2P traffic.",
name: "listen-address"}: IpAddress name: "listen-address"
.}: IpAddress
tcpPort* {. tcpPort* {.desc: "TCP listening port.", defaultValue: 60000, name: "tcp-port".}:
desc: "TCP listening port." Port
defaultValue: 60000
name: "tcp-port" }: Port
udpPort* {. udpPort* {.desc: "UDP listening port.", defaultValue: 60000, name: "udp-port".}:
desc: "UDP listening port." Port
defaultValue: 60000
name: "udp-port" }: Port
portsShift* {. portsShift* {.
desc: "Add a shift to all port numbers." desc: "Add a shift to all port numbers.", defaultValue: 0, name: "ports-shift"
defaultValue: 0 .}: uint16
name: "ports-shift" }: uint16
nat* {. nat* {.
desc: "Specify method to use for determining public address. " & desc:
"Must be one of: any, none, upnp, pmp, extip:<IP>." "Specify method to use for determining public address. " &
defaultValue: "any" }: string "Must be one of: any, none, upnp, pmp, extip:<IP>.",
defaultValue: "any"
.}: string
## Persistence config ## Persistence config
dbPath* {. dbPath* {.
desc: "The database path for peristent storage", desc: "The database path for peristent storage", defaultValue: "", name: "db-path"
defaultValue: "" .}: string
name: "db-path" }: string
persistPeers* {. persistPeers* {.
desc: "Enable peer persistence: true|false", desc: "Enable peer persistence: true|false",
defaultValue: false defaultValue: false,
name: "persist-peers" }: bool name: "persist-peers"
.}: bool
persistMessages* {. persistMessages* {.
desc: "Enable message persistence: true|false", desc: "Enable message persistence: true|false",
defaultValue: false defaultValue: false,
name: "persist-messages" }: bool name: "persist-messages"
.}: bool
## Relay config ## Relay config
relay* {. relay* {.
desc: "Enable relay protocol: true|false", desc: "Enable relay protocol: true|false", defaultValue: true, name: "relay"
defaultValue: true .}: bool
name: "relay" }: bool
staticnodes* {. staticnodes* {.
desc: "Peer multiaddr to directly connect with. Argument may be repeated." desc: "Peer multiaddr to directly connect with. Argument may be repeated.",
name: "staticnode" }: seq[string] name: "staticnode"
.}: seq[string]
keepAlive* {. keepAlive* {.
desc: "Enable keep-alive for idle connections: true|false", desc: "Enable keep-alive for idle connections: true|false",
defaultValue: false defaultValue: false,
name: "keep-alive" }: bool name: "keep-alive"
.}: bool
topics* {. topics* {.
desc: "Default topics to subscribe to (space separated list)." desc: "Default topics to subscribe to (space separated list).",
defaultValue: "/waku/2/default-waku/proto" defaultValue: "/waku/2/default-waku/proto",
name: "topics" .}: string name: "topics"
.}: string
## Store config ## Store config
store* {. store* {.
desc: "Enable store protocol: true|false", desc: "Enable store protocol: true|false", defaultValue: true, name: "store"
defaultValue: true .}: bool
name: "store" }: bool
storenode* {. storenode* {.
desc: "Peer multiaddr to query for storage.", desc: "Peer multiaddr to query for storage.", defaultValue: "", name: "storenode"
defaultValue: "" .}: string
name: "storenode" }: string
## Filter config ## Filter config
filter* {. filter* {.
desc: "Enable filter protocol: true|false", desc: "Enable filter protocol: true|false", defaultValue: false, name: "filter"
defaultValue: false .}: bool
name: "filter" }: bool
filternode* {. filternode* {.
desc: "Peer multiaddr to request content filtering of messages.", desc: "Peer multiaddr to request content filtering of messages.",
defaultValue: "" defaultValue: "",
name: "filternode" }: string name: "filternode"
.}: string
## Lightpush config ## Lightpush config
lightpush* {. lightpush* {.
desc: "Enable lightpush protocol: true|false", desc: "Enable lightpush protocol: true|false",
defaultValue: false defaultValue: false,
name: "lightpush" }: bool name: "lightpush"
.}: bool
lightpushnode* {. lightpushnode* {.
desc: "Peer multiaddr to request lightpush of published messages.", desc: "Peer multiaddr to request lightpush of published messages.",
defaultValue: "" defaultValue: "",
name: "lightpushnode" }: string name: "lightpushnode"
.}: string
## Metrics config ## Metrics config
metricsServer* {. metricsServer* {.
desc: "Enable the metrics server: true|false" desc: "Enable the metrics server: true|false",
defaultValue: false defaultValue: false,
name: "metrics-server" }: bool name: "metrics-server"
.}: bool
metricsServerAddress* {. metricsServerAddress* {.
desc: "Listening address of the metrics server." desc: "Listening address of the metrics server.",
defaultValue: parseIpAddress("127.0.0.1") defaultValue: parseIpAddress("127.0.0.1"),
name: "metrics-server-address" }: IpAddress name: "metrics-server-address"
.}: IpAddress
metricsServerPort* {. metricsServerPort* {.
desc: "Listening HTTP port of the metrics server." desc: "Listening HTTP port of the metrics server.",
defaultValue: 8008 defaultValue: 8008,
name: "metrics-server-port" }: uint16 name: "metrics-server-port"
.}: uint16
metricsLogging* {. metricsLogging* {.
desc: "Enable metrics logging: true|false" desc: "Enable metrics logging: true|false",
defaultValue: true defaultValue: true,
name: "metrics-logging" }: bool name: "metrics-logging"
.}: bool
## DNS discovery config ## DNS discovery config
dnsDiscovery* {. dnsDiscovery* {.
desc: "Enable discovering nodes via DNS" desc: "Enable discovering nodes via DNS",
defaultValue: false defaultValue: false,
name: "dns-discovery" }: bool name: "dns-discovery"
.}: bool
dnsDiscoveryUrl* {. dnsDiscoveryUrl* {.
desc: "URL for DNS node list in format 'enrtree://<key>@<fqdn>'", desc: "URL for DNS node list in format 'enrtree://<key>@<fqdn>'",
defaultValue: "" defaultValue: "",
name: "dns-discovery-url" }: string name: "dns-discovery-url"
.}: string
dnsDiscoveryNameServers* {. dnsDiscoveryNameServers* {.
desc: "DNS name server IPs to query. Argument may be repeated." desc: "DNS name server IPs to query. Argument may be repeated.",
defaultValue: @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")] defaultValue: @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")],
name: "dns-discovery-name-server" }: seq[IpAddress] name: "dns-discovery-name-server"
.}: seq[IpAddress]
## Chat2 configuration ## Chat2 configuration
fleet* {. fleet* {.
desc: "Select the fleet to connect to. This sets the DNS discovery URL to the selected fleet." desc:
defaultValue: Fleet.prod "Select the fleet to connect to. This sets the DNS discovery URL to the selected fleet.",
name: "fleet" }: Fleet defaultValue: Fleet.prod,
name: "fleet"
.}: Fleet
contentTopic* {. contentTopic* {.
desc: "Content topic for chat messages." desc: "Content topic for chat messages.",
defaultValue: "/toy-chat/2/huilong/proto" defaultValue: "/toy-chat/2/huilong/proto",
name: "content-topic" }: string name: "content-topic"
.}: string
## Websocket Configuration ## Websocket Configuration
websocketSupport* {. websocketSupport* {.
desc: "Enable websocket: true|false", desc: "Enable websocket: true|false",
defaultValue: false defaultValue: false,
name: "websocket-support"}: bool name: "websocket-support"
.}: bool
websocketPort* {. websocketPort* {.
desc: "WebSocket listening port." desc: "WebSocket listening port.", defaultValue: 8000, name: "websocket-port"
defaultValue: 8000 .}: Port
name: "websocket-port" }: Port
websocketSecureSupport* {. websocketSecureSupport* {.
desc: "WebSocket Secure Support." desc: "WebSocket Secure Support.",
defaultValue: false defaultValue: false,
name: "websocket-secure-support" }: bool name: "websocket-secure-support"
.}: bool
## rln-relay configuration ## rln-relay configuration
rlnRelay* {. rlnRelay* {.
desc: "Enable spam protection through rln-relay: true|false", desc: "Enable spam protection through rln-relay: true|false",
defaultValue: false defaultValue: false,
name: "rln-relay" }: bool name: "rln-relay"
.}: bool
rlnRelayCredPath* {. rlnRelayCredPath* {.
desc: "The path for peristing rln-relay credential", desc: "The path for peristing rln-relay credential",
defaultValue: "" defaultValue: "",
name: "rln-relay-cred-path" }: string name: "rln-relay-cred-path"
.}: string
rlnRelayCredIndex* {. rlnRelayCredIndex* {.
desc: "the index of the onchain commitment to use", desc: "the index of the onchain commitment to use", name: "rln-relay-cred-index"
name: "rln-relay-cred-index" }: Option[uint] .}: Option[uint]
rlnRelayDynamic* {. rlnRelayDynamic* {.
desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false", desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false",
defaultValue: false defaultValue: false,
name: "rln-relay-dynamic" }: bool name: "rln-relay-dynamic"
.}: bool
rlnRelayIdKey* {. rlnRelayIdKey* {.
desc: "Rln relay identity secret key as a Hex string", desc: "Rln relay identity secret key as a Hex string",
defaultValue: "" defaultValue: "",
name: "rln-relay-id-key" }: string name: "rln-relay-id-key"
.}: string
rlnRelayIdCommitmentKey* {. rlnRelayIdCommitmentKey* {.
desc: "Rln relay identity commitment key as a Hex string", desc: "Rln relay identity commitment key as a Hex string",
defaultValue: "" defaultValue: "",
name: "rln-relay-id-commitment-key" }: string name: "rln-relay-id-commitment-key"
.}: string
rlnRelayEthClientAddress* {. rlnRelayEthClientAddress* {.
desc: "HTTP address of an Ethereum testnet client e.g., http://localhost:8540/", desc: "HTTP address of an Ethereum testnet client e.g., http://localhost:8540/",
defaultValue: "http://localhost:8540/" defaultValue: "http://localhost:8540/",
name: "rln-relay-eth-client-address" }: EthRpcUrl name: "rln-relay-eth-client-address"
.}: EthRpcUrl
rlnRelayEthContractAddress* {. rlnRelayEthContractAddress* {.
desc: "Address of membership contract on an Ethereum testnet", desc: "Address of membership contract on an Ethereum testnet",
defaultValue: "" defaultValue: "",
name: "rln-relay-eth-contract-address" }: string name: "rln-relay-eth-contract-address"
.}: string
rlnRelayCredPassword* {. rlnRelayCredPassword* {.
desc: "Password for encrypting RLN credentials", desc: "Password for encrypting RLN credentials",
defaultValue: "" defaultValue: "",
name: "rln-relay-cred-password" }: string name: "rln-relay-cred-password"
.}: string
rlnRelayUserMessageLimit* {. rlnRelayUserMessageLimit* {.
desc: "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.", desc:
"Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.",
defaultValue: 1, defaultValue: 1,
name: "rln-relay-user-message-limit" .}: uint64 name: "rln-relay-user-message-limit"
.}: uint64
rlnEpochSizeSec* {. rlnEpochSizeSec* {.
desc: "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.", desc:
defaultValue: 1 "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.",
name: "rln-relay-epoch-sec" .}: uint64 defaultValue: 1,
name: "rln-relay-epoch-sec"
.}: uint64
# NOTE: Keys are different in nim-libp2p # NOTE: Keys are different in nim-libp2p
proc parseCmdArg*(T: type crypto.PrivateKey, p: string): T = proc parseCmdArg*(T: type crypto.PrivateKey, p: string): T =
@ -300,10 +314,14 @@ proc parseCmdArg*(T: type EthRpcUrl, s: string): T =
## https://url:port/path?query ## https://url:port/path?query
## disallowed patterns: ## disallowed patterns:
## any valid/invalid ws or wss url ## any valid/invalid ws or wss url
var httpPattern = re2"^(https?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*" var httpPattern =
var wsPattern = re2"^(wss?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*" re2"^(https?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
var wsPattern =
re2"^(wss?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
if regex.match(s, wsPattern): if regex.match(s, wsPattern):
raise newException(ValueError, "Websocket RPC URL is not supported, Please use an HTTP URL") raise newException(
ValueError, "Websocket RPC URL is not supported, Please use an HTTP URL"
)
if not regex.match(s, httpPattern): if not regex.match(s, httpPattern):
raise newException(ValueError, "Invalid HTTP RPC URL") raise newException(ValueError, "Invalid HTTP RPC URL")
return EthRpcUrl(s) return EthRpcUrl(s)

View File

@ -5,8 +5,13 @@ else:
import import
std/[tables, times, strutils, hashes, sequtils], std/[tables, times, strutils, hashes, sequtils],
chronos, confutils, chronicles, chronicles/topics_registry, chronos/streams/tlsstream, chronos,
metrics, metrics/chronos_httpserver, confutils,
chronicles,
chronicles/topics_registry,
chronos/streams/tlsstream,
metrics,
metrics/chronos_httpserver,
stew/byteutils, stew/byteutils,
eth/net/nat, eth/net/nat,
json_rpc/rpcserver, json_rpc/rpcserver,
@ -27,7 +32,8 @@ import
# Common cli config # Common cli config
./config_chat2bridge ./config_chat2bridge
declarePublicCounter chat2_mb_transfers, "Number of messages transferred between chat2 and Matterbridge", ["type"] declarePublicCounter chat2_mb_transfers,
"Number of messages transferred between chat2 and Matterbridge", ["type"]
declarePublicCounter chat2_mb_dropped, "Number of messages dropped", ["reason"] declarePublicCounter chat2_mb_dropped, "Number of messages dropped", ["reason"]
logScope: logScope:
@ -37,8 +43,7 @@ logScope:
# Default values # # Default values #
################## ##################
const const DeduplQSize = 20 # Maximum number of seen messages to keep in deduplication queue
DeduplQSize = 20 # Maximum number of seen messages to keep in deduplication queue
######### #########
# Types # # Types #
@ -53,7 +58,7 @@ type
seen: seq[Hash] #FIFO queue seen: seq[Hash] #FIFO queue
contentTopic: string contentTopic: string
MbMessageHandler = proc (jsonNode: JsonNode) {.async.} MbMessageHandler = proc(jsonNode: JsonNode) {.async.}
################### ###################
# Helper funtions # # Helper funtions #
@ -65,25 +70,27 @@ proc containsOrAdd(sequence: var seq[Hash], hash: Hash): bool =
if sequence.len >= DeduplQSize: if sequence.len >= DeduplQSize:
trace "Deduplication queue full. Removing oldest item." trace "Deduplication queue full. Removing oldest item."
sequence.delete 0, 0 # Remove first item in queue sequence.delete 0, 0 # Remove first item in queue
sequence.add(hash) sequence.add(hash)
return false return false
proc toWakuMessage(cmb: Chat2MatterBridge, jsonNode: JsonNode): WakuMessage {.raises: [Defect, KeyError]} = proc toWakuMessage(
cmb: Chat2MatterBridge, jsonNode: JsonNode
): WakuMessage {.raises: [Defect, KeyError].} =
# Translates a Matterbridge API JSON response to a Waku v2 message # Translates a Matterbridge API JSON response to a Waku v2 message
let msgFields = jsonNode.getFields() let msgFields = jsonNode.getFields()
# @TODO error handling here - verify expected fields # @TODO error handling here - verify expected fields
let chat2pb = Chat2Message(timestamp: getTime().toUnix(), # @TODO use provided timestamp let chat2pb = Chat2Message(
nick: msgFields["username"].getStr(), timestamp: getTime().toUnix(), # @TODO use provided timestamp
payload: msgFields["text"].getStr().toBytes()).encode() nick: msgFields["username"].getStr(),
payload: msgFields["text"].getStr().toBytes(),
).encode()
WakuMessage(payload: chat2pb.buffer, WakuMessage(payload: chat2pb.buffer, contentTopic: cmb.contentTopic, version: 0)
contentTopic: cmb.contentTopic,
version: 0)
proc toChat2(cmb: Chat2MatterBridge, jsonNode: JsonNode) {.async.} = proc toChat2(cmb: Chat2MatterBridge, jsonNode: JsonNode) {.async.} =
let msg = cmb.toWakuMessage(jsonNode) let msg = cmb.toWakuMessage(jsonNode)
@ -100,7 +107,9 @@ proc toChat2(cmb: Chat2MatterBridge, jsonNode: JsonNode) {.async.} =
(await cmb.nodev2.publish(some(DefaultPubsubTopic), msg)).isOkOr: (await cmb.nodev2.publish(some(DefaultPubsubTopic), msg)).isOkOr:
error "failed to publish message", error = error error "failed to publish message", error = error
proc toMatterbridge(cmb: Chat2MatterBridge, msg: WakuMessage) {.gcsafe, raises: [Exception].} = proc toMatterbridge(
cmb: Chat2MatterBridge, msg: WakuMessage
) {.gcsafe, raises: [Exception].} =
if cmb.seen.containsOrAdd(msg.payload.hash()): if cmb.seen.containsOrAdd(msg.payload.hash()):
# This is a duplicate message. Return. # This is a duplicate message. Return.
chat2_mb_dropped.inc(labelValues = ["duplicate"]) chat2_mb_dropped.inc(labelValues = ["duplicate"])
@ -119,8 +128,9 @@ proc toMatterbridge(cmb: Chat2MatterBridge, msg: WakuMessage) {.gcsafe, raises:
assert chat2Msg.isOk assert chat2Msg.isOk
let postRes = cmb.mbClient.postMessage(text = string.fromBytes(chat2Msg[].payload), let postRes = cmb.mbClient.postMessage(
username = chat2Msg[].nick) text = string.fromBytes(chat2Msg[].payload), username = chat2Msg[].nick
)
if postRes.isErr() or (postRes[] == false): if postRes.isErr() or (postRes[] == false):
chat2_mb_dropped.inc(labelValues = ["duplicate"]) chat2_mb_dropped.inc(labelValues = ["duplicate"])
@ -142,41 +152,50 @@ proc pollMatterbridge(cmb: Chat2MatterBridge, handler: MbMessageHandler) {.async
############## ##############
# Public API # # Public API #
############## ##############
proc new*(T: type Chat2MatterBridge, proc new*(
# Matterbridge initialisation T: type Chat2MatterBridge,
mbHostUri: string, # Matterbridge initialisation
mbGateway: string, mbHostUri: string,
# NodeV2 initialisation mbGateway: string,
nodev2Key: crypto.PrivateKey, # NodeV2 initialisation
nodev2BindIp: IpAddress, nodev2BindPort: Port, nodev2Key: crypto.PrivateKey,
nodev2ExtIp = none[IpAddress](), nodev2ExtPort = none[Port](), nodev2BindIp: IpAddress,
contentTopic: string): T nodev2BindPort: Port,
{.raises: [Defect, ValueError, KeyError, TLSStreamProtocolError, IOError, LPError].} = nodev2ExtIp = none[IpAddress](),
nodev2ExtPort = none[Port](),
contentTopic: string,
): T {.
raises: [Defect, ValueError, KeyError, TLSStreamProtocolError, IOError, LPError]
.} =
# Setup Matterbridge # Setup Matterbridge
let let mbClient = MatterbridgeClient.new(mbHostUri, mbGateway)
mbClient = MatterbridgeClient.new(mbHostUri, mbGateway)
# Let's verify the Matterbridge configuration before continuing # Let's verify the Matterbridge configuration before continuing
let clientHealth = mbClient.isHealthy() let clientHealth = mbClient.isHealthy()
if clientHealth.isOk() and clientHealth[]: if clientHealth.isOk() and clientHealth[]:
info "Reached Matterbridge host", host=mbClient.host info "Reached Matterbridge host", host = mbClient.host
else: else:
raise newException(ValueError, "Matterbridge client not reachable/healthy") raise newException(ValueError, "Matterbridge client not reachable/healthy")
# Setup Waku v2 node # Setup Waku v2 node
let nodev2 = block: let nodev2 = block:
var builder = WakuNodeBuilder.init() var builder = WakuNodeBuilder.init()
builder.withNodeKey(nodev2Key) builder.withNodeKey(nodev2Key)
builder.withNetworkConfigurationDetails(nodev2BindIp, nodev2BindPort, nodev2ExtIp, nodev2ExtPort).tryGet() builder
builder.build().tryGet() .withNetworkConfigurationDetails(
nodev2BindIp, nodev2BindPort, nodev2ExtIp, nodev2ExtPort
)
.tryGet()
builder.build().tryGet()
return Chat2MatterBridge(mbClient: mbClient, return Chat2MatterBridge(
nodev2: nodev2, mbClient: mbClient,
running: false, nodev2: nodev2,
pollPeriod: chronos.seconds(1), running: false,
contentTopic: contentTopic) pollPeriod: chronos.seconds(1),
contentTopic: contentTopic,
)
proc start*(cmb: Chat2MatterBridge) {.async.} = proc start*(cmb: Chat2MatterBridge) {.async.} =
info "Starting Chat2MatterBridge" info "Starting Chat2MatterBridge"
@ -187,7 +206,7 @@ proc start*(cmb: Chat2MatterBridge) {.async.} =
# Start Matterbridge polling (@TODO: use streaming interface) # Start Matterbridge polling (@TODO: use streaming interface)
proc mbHandler(jsonNode: JsonNode) {.async.} = proc mbHandler(jsonNode: JsonNode) {.async.} =
trace "Bridging message from Matterbridge to chat2", jsonNode=jsonNode trace "Bridging message from Matterbridge to chat2", jsonNode = jsonNode
waitFor cmb.toChat2(jsonNode) waitFor cmb.toChat2(jsonNode)
asyncSpawn cmb.pollMatterbridge(mbHandler) asyncSpawn cmb.pollMatterbridge(mbHandler)
@ -203,8 +222,10 @@ proc start*(cmb: Chat2MatterBridge) {.async.} =
# Bridging # Bridging
# Handle messages on Waku v2 and bridge to Matterbridge # Handle messages on Waku v2 and bridge to Matterbridge
proc relayHandler(pubsubTopic: PubsubTopic, msg: WakuMessage): Future[void] {.async.} = proc relayHandler(
trace "Bridging message from Chat2 to Matterbridge", msg=msg pubsubTopic: PubsubTopic, msg: WakuMessage
): Future[void] {.async.} =
trace "Bridging message from Chat2 to Matterbridge", msg = msg
try: try:
cmb.toMatterbridge(msg) cmb.toMatterbridge(msg)
except: except:
@ -219,11 +240,10 @@ proc stop*(cmb: Chat2MatterBridge) {.async: (raises: [Exception]).} =
await cmb.nodev2.stop() await cmb.nodev2.stop()
{.pop.} # @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError {.pop.}
# @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError
when isMainModule: when isMainModule:
import import ../../../waku/common/utils/nat, ../../waku/waku_api/message_cache
../../../waku/common/utils/nat,
../../waku/waku_api/message_cache
let let
rng = newRng() rng = newRng()
@ -232,9 +252,12 @@ when isMainModule:
if conf.logLevel != LogLevel.NONE: if conf.logLevel != LogLevel.NONE:
setLogLevel(conf.logLevel) setLogLevel(conf.logLevel)
let natRes = setupNat(conf.nat, clientId, let natRes = setupNat(
Port(uint16(conf.libp2pTcpPort) + conf.portsShift), conf.nat,
Port(uint16(conf.udpPort) + conf.portsShift)) clientId,
Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
Port(uint16(conf.udpPort) + conf.portsShift),
)
if natRes.isErr(): if natRes.isErr():
error "Error in setupNat", error = natRes.error error "Error in setupNat", error = natRes.error
@ -243,19 +266,22 @@ when isMainModule:
(nodev2ExtIp, nodev2ExtPort, _) = natRes.get() (nodev2ExtIp, nodev2ExtPort, _) = natRes.get()
## The following heuristic assumes that, in absence of manual ## The following heuristic assumes that, in absence of manual
## config, the external port is the same as the bind port. ## config, the external port is the same as the bind port.
extPort = if nodev2ExtIp.isSome() and nodev2ExtPort.isNone(): extPort =
some(Port(uint16(conf.libp2pTcpPort) + conf.portsShift)) if nodev2ExtIp.isSome() and nodev2ExtPort.isNone():
else: some(Port(uint16(conf.libp2pTcpPort) + conf.portsShift))
nodev2ExtPort else:
nodev2ExtPort
let let bridge = Chat2Matterbridge.new(
bridge = Chat2Matterbridge.new( mbHostUri = "http://" & $initTAddress(conf.mbHostAddress, Port(conf.mbHostPort)),
mbHostUri = "http://" & $initTAddress(conf.mbHostAddress, Port(conf.mbHostPort)), mbGateway = conf.mbGateway,
mbGateway = conf.mbGateway, nodev2Key = conf.nodekey,
nodev2Key = conf.nodekey, nodev2BindIp = conf.listenAddress,
nodev2BindIp = conf.listenAddress, nodev2BindPort = Port(uint16(conf.libp2pTcpPort) + conf.portsShift), nodev2BindPort = Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
nodev2ExtIp = nodev2ExtIp, nodev2ExtPort = extPort, nodev2ExtIp = nodev2ExtIp,
contentTopic = conf.contentTopic) nodev2ExtPort = extPort,
contentTopic = conf.contentTopic,
)
waitFor bridge.start() waitFor bridge.start()
@ -284,7 +310,9 @@ when isMainModule:
let filterPeer = parsePeerInfo(conf.filternode) let filterPeer = parsePeerInfo(conf.filternode)
if filterPeer.isOk(): if filterPeer.isOk():
bridge.nodev2.peerManager.addServicePeer(filterPeer.value, WakuLegacyFilterCodec) bridge.nodev2.peerManager.addServicePeer(filterPeer.value, WakuLegacyFilterCodec)
bridge.nodev2.peerManager.addServicePeer(filterPeer.value, WakuFilterSubscribeCodec) bridge.nodev2.peerManager.addServicePeer(
filterPeer.value, WakuFilterSubscribeCodec
)
else: else:
error "Error parsing conf.filternode", error = filterPeer.error error "Error parsing conf.filternode", error = filterPeer.error

View File

@ -1,118 +1,125 @@
import import
confutils, confutils/defs, confutils/std/net, chronicles, chronos, confutils,
confutils/defs,
confutils/std/net,
chronicles,
chronos,
libp2p/crypto/[crypto, secp], libp2p/crypto/[crypto, secp],
eth/keys eth/keys
type type Chat2MatterbridgeConf* = object
Chat2MatterbridgeConf* = object logLevel* {.
logLevel* {. desc: "Sets the log level", defaultValue: LogLevel.INFO, name: "log-level"
desc: "Sets the log level" .}: LogLevel
defaultValue: LogLevel.INFO
name: "log-level" .}: LogLevel
listenAddress* {. listenAddress* {.
defaultValue: defaultListenAddress(config) defaultValue: defaultListenAddress(config),
desc: "Listening address for the LibP2P traffic" desc: "Listening address for the LibP2P traffic",
name: "listen-address"}: IpAddress name: "listen-address"
.}: IpAddress
libp2pTcpPort* {. libp2pTcpPort* {.
desc: "Libp2p TCP listening port (for Waku v2)" desc: "Libp2p TCP listening port (for Waku v2)",
defaultValue: 9000 defaultValue: 9000,
name: "libp2p-tcp-port" .}: uint16 name: "libp2p-tcp-port"
.}: uint16
udpPort* {. udpPort* {.desc: "UDP listening port", defaultValue: 9000, name: "udp-port".}: uint16
desc: "UDP listening port"
defaultValue: 9000
name: "udp-port" .}: uint16
portsShift* {. portsShift* {.
desc: "Add a shift to all default port numbers" desc: "Add a shift to all default port numbers",
defaultValue: 0 defaultValue: 0,
name: "ports-shift" .}: uint16 name: "ports-shift"
.}: uint16
nat* {. nat* {.
desc: "Specify method to use for determining public address. " & desc:
"Must be one of: any, none, upnp, pmp, extip:<IP>" "Specify method to use for determining public address. " &
defaultValue: "any" .}: string "Must be one of: any, none, upnp, pmp, extip:<IP>",
defaultValue: "any"
.}: string
metricsServer* {. metricsServer* {.
desc: "Enable the metrics server" desc: "Enable the metrics server", defaultValue: false, name: "metrics-server"
defaultValue: false .}: bool
name: "metrics-server" .}: bool
metricsServerAddress* {. metricsServerAddress* {.
desc: "Listening address of the metrics server" desc: "Listening address of the metrics server",
defaultValue: parseIpAddress("127.0.0.1") defaultValue: parseIpAddress("127.0.0.1"),
name: "metrics-server-address" }: IpAddress name: "metrics-server-address"
.}: IpAddress
metricsServerPort* {. metricsServerPort* {.
desc: "Listening HTTP port of the metrics server" desc: "Listening HTTP port of the metrics server",
defaultValue: 8008 defaultValue: 8008,
name: "metrics-server-port" .}: uint16 name: "metrics-server-port"
.}: uint16
### Waku v2 options ### Waku v2 options
staticnodes* {.
desc: "Multiaddr of peer to directly connect with. Argument may be repeated",
name: "staticnode"
.}: seq[string]
staticnodes* {. nodekey* {.
desc: "Multiaddr of peer to directly connect with. Argument may be repeated" desc: "P2P node private key as hex",
name: "staticnode" }: seq[string] defaultValue: crypto.PrivateKey.random(Secp256k1, newRng()[]).tryGet(),
name: "nodekey"
.}: crypto.PrivateKey
nodekey* {. topics* {.
desc: "P2P node private key as hex" desc: "Default topics to subscribe to (space separated list)",
defaultValue: crypto.PrivateKey.random(Secp256k1, newRng()[]).tryGet() defaultValue: "/waku/2/default-waku/proto",
name: "nodekey" }: crypto.PrivateKey name: "topics"
.}: string
topics* {. store* {.
desc: "Default topics to subscribe to (space separated list)" desc: "Flag whether to start store protocol", defaultValue: true, name: "store"
defaultValue: "/waku/2/default-waku/proto" .}: bool
name: "topics" .}: string
store* {. filter* {.
desc: "Flag whether to start store protocol", desc: "Flag whether to start filter protocol", defaultValue: false, name: "filter"
defaultValue: true .}: bool
name: "store" }: bool
filter* {. relay* {.
desc: "Flag whether to start filter protocol", desc: "Flag whether to start relay protocol", defaultValue: true, name: "relay"
defaultValue: false .}: bool
name: "filter" }: bool
relay* {. storenode* {.
desc: "Flag whether to start relay protocol", desc: "Multiaddr of peer to connect with for waku store protocol",
defaultValue: true defaultValue: "",
name: "relay" }: bool name: "storenode"
.}: string
storenode* {. filternode* {.
desc: "Multiaddr of peer to connect with for waku store protocol" desc: "Multiaddr of peer to connect with for waku filter protocol",
defaultValue: "" defaultValue: "",
name: "storenode" }: string name: "filternode"
.}: string
filternode* {. # Matterbridge options
desc: "Multiaddr of peer to connect with for waku filter protocol" mbHostAddress* {.
defaultValue: "" desc: "Listening address of the Matterbridge host",
name: "filternode" }: string defaultValue: parseIpAddress("127.0.0.1"),
name: "mb-host-address"
.}: IpAddress
# Matterbridge options mbHostPort* {.
mbHostAddress* {. desc: "Listening port of the Matterbridge host",
desc: "Listening address of the Matterbridge host", defaultValue: 4242,
defaultValue: parseIpAddress("127.0.0.1") name: "mb-host-port"
name: "mb-host-address" }: IpAddress .}: uint16
mbHostPort* {. mbGateway* {.
desc: "Listening port of the Matterbridge host", desc: "Matterbridge gateway", defaultValue: "gateway1", name: "mb-gateway"
defaultValue: 4242 .}: string
name: "mb-host-port" }: uint16
mbGateway* {. ## Chat2 options
desc: "Matterbridge gateway" contentTopic* {.
defaultValue: "gateway1" desc: "Content topic to bridge chat messages to.",
name: "mb-gateway" }: string defaultValue: "/toy-chat/2/huilong/proto",
name: "content-topic"
## Chat2 options .}: string
contentTopic* {.
desc: "Content topic to bridge chat messages to."
defaultValue: "/toy-chat/2/huilong/proto"
name: "content-topic" }: string
proc parseCmdArg*(T: type keys.KeyPair, p: string): T = proc parseCmdArg*(T: type keys.KeyPair, p: string): T =
try: try:

View File

@ -4,7 +4,7 @@ else:
{.push raises: [].} {.push raises: [].}
import import
std/[tables,strutils,times,sequtils], std/[tables, strutils, times, sequtils],
stew/results, stew/results,
stew/shims/net, stew/shims/net,
chronicles, chronicles,
@ -44,19 +44,22 @@ const AvgPingWindow = 10.0
const git_version* {.strdefine.} = "n/a" const git_version* {.strdefine.} = "n/a"
proc setDiscoveredPeersCapabilities( proc setDiscoveredPeersCapabilities(routingTableNodes: seq[Node]) =
routingTableNodes: seq[Node]) =
for capability in @[Relay, Store, Filter, Lightpush]: for capability in @[Relay, Store, Filter, Lightpush]:
let nOfNodesWithCapability = routingTableNodes.countIt(it.record.supportsCapability(capability)) let nOfNodesWithCapability =
info "capabilities as per ENR waku flag", capability=capability, amount=nOfNodesWithCapability routingTableNodes.countIt(it.record.supportsCapability(capability))
networkmonitor_peer_type_as_per_enr.set(int64(nOfNodesWithCapability), labelValues = [$capability]) info "capabilities as per ENR waku flag",
capability = capability, amount = nOfNodesWithCapability
networkmonitor_peer_type_as_per_enr.set(
int64(nOfNodesWithCapability), labelValues = [$capability]
)
proc analyzePeer( proc analyzePeer(
customPeerInfo: CustomPeerInfoRef, customPeerInfo: CustomPeerInfoRef,
peerInfo: RemotePeerInfo, peerInfo: RemotePeerInfo,
node: WakuNode, node: WakuNode,
timeout: chronos.Duration timeout: chronos.Duration,
): Future[Result[string, string]] {.async.} = ): Future[Result[string, string]] {.async.} =
var pingDelay: chronos.Duration var pingDelay: chronos.Duration
proc ping(): Future[Result[void, string]] {.async, gcsafe.} = proc ping(): Future[Result[void, string]] {.async, gcsafe.} =
@ -64,12 +67,11 @@ proc analyzePeer(
let conn = await node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec) let conn = await node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec)
pingDelay = await node.libp2pPing.ping(conn) pingDelay = await node.libp2pPing.ping(conn)
return ok() return ok()
except CatchableError: except CatchableError:
var msg = getCurrentExceptionMsg() var msg = getCurrentExceptionMsg()
if msg == "Future operation cancelled!": if msg == "Future operation cancelled!":
msg = "timedout" msg = "timedout"
warn "failed to ping the peer", peer=peerInfo, err=msg warn "failed to ping the peer", peer = peerInfo, err = msg
customPeerInfo.connError = msg customPeerInfo.connError = msg
return err("could not ping peer: " & msg) return err("could not ping peer: " & msg)
@ -81,36 +83,44 @@ proc analyzePeer(
return err(customPeerInfo.connError) return err(customPeerInfo.connError)
customPeerInfo.connError = "" customPeerInfo.connError = ""
info "successfully pinged peer", peer=peerInfo, duration=pingDelay.millis info "successfully pinged peer", peer = peerInfo, duration = pingDelay.millis
networkmonitor_peer_ping.observe(pingDelay.millis) networkmonitor_peer_ping.observe(pingDelay.millis)
if customPeerInfo.avgPingDuration == 0.millis: if customPeerInfo.avgPingDuration == 0.millis:
customPeerInfo.avgPingDuration = pingDelay customPeerInfo.avgPingDuration = pingDelay
# TODO: check why the calculation ends up losing precision # TODO: check why the calculation ends up losing precision
customPeerInfo.avgPingDuration = int64((float64(customPeerInfo.avgPingDuration.millis) * (AvgPingWindow - 1.0) + float64(pingDelay.millis)) / AvgPingWindow).millis customPeerInfo.avgPingDuration = int64(
(
float64(customPeerInfo.avgPingDuration.millis) * (AvgPingWindow - 1.0) +
float64(pingDelay.millis)
) / AvgPingWindow
).millis
customPeerInfo.lastPingDuration = pingDelay customPeerInfo.lastPingDuration = pingDelay
return ok(customPeerInfo.peerId) return ok(customPeerInfo.peerId)
proc shouldReconnect(customPeerInfo: CustomPeerInfoRef): bool = proc shouldReconnect(customPeerInfo: CustomPeerInfoRef): bool =
let reconnetIntervalCheck = getTime().toUnix() >= customPeerInfo.lastTimeConnected + ReconnectTime let reconnetIntervalCheck =
getTime().toUnix() >= customPeerInfo.lastTimeConnected + ReconnectTime
var retriesCheck = customPeerInfo.retries < MaxConnectionRetries var retriesCheck = customPeerInfo.retries < MaxConnectionRetries
if not retriesCheck and getTime().toUnix() >= customPeerInfo.lastTimeConnected + ResetRetriesAfter: if not retriesCheck and
getTime().toUnix() >= customPeerInfo.lastTimeConnected + ResetRetriesAfter:
customPeerInfo.retries = 0 customPeerInfo.retries = 0
retriesCheck = true retriesCheck = true
info "resetting retries counter", peerId=customPeerInfo.peerId info "resetting retries counter", peerId = customPeerInfo.peerId
return reconnetIntervalCheck and retriesCheck return reconnetIntervalCheck and retriesCheck
# TODO: Split in discover, connect # TODO: Split in discover, connect
proc setConnectedPeersMetrics(discoveredNodes: seq[Node], proc setConnectedPeersMetrics(
node: WakuNode, discoveredNodes: seq[Node],
timeout: chronos.Duration, node: WakuNode,
restClient: RestClientRef, timeout: chronos.Duration,
allPeers: CustomPeersTableRef) {.async.} = restClient: RestClientRef,
allPeers: CustomPeersTableRef,
) {.async.} =
let currentTime = getTime().toUnix() let currentTime = getTime().toUnix()
var newPeers = 0 var newPeers = 0
@ -122,18 +132,18 @@ proc setConnectedPeersMetrics(discoveredNodes: seq[Node],
for discNode in discoveredNodes: for discNode in discoveredNodes:
let typedRecord = discNode.record.toTypedRecord() let typedRecord = discNode.record.toTypedRecord()
if not typedRecord.isOk(): if not typedRecord.isOk():
warn "could not convert record to typed record", record=discNode.record warn "could not convert record to typed record", record = discNode.record
continue continue
let secp256k1 = typedRecord.get().secp256k1 let secp256k1 = typedRecord.get().secp256k1
if not secp256k1.isSome(): if not secp256k1.isSome():
warn "could not get secp256k1 key", typedRecord=typedRecord.get() warn "could not get secp256k1 key", typedRecord = typedRecord.get()
continue continue
let peerRes = toRemotePeerInfo(discNode.record) let peerRes = toRemotePeerInfo(discNode.record)
let peerInfo = peerRes.valueOr(): let peerInfo = peerRes.valueOr:
warn "error converting record to remote peer info", record=discNode.record warn "error converting record to remote peer info", record = discNode.record
continue continue
# create new entry if new peerId found # create new entry if new peerId found
@ -143,7 +153,7 @@ proc setConnectedPeersMetrics(discoveredNodes: seq[Node],
allPeers[peerId] = CustomPeerInfoRef(peerId: peerId) allPeers[peerId] = CustomPeerInfoRef(peerId: peerId)
newPeers += 1 newPeers += 1
else: else:
info "already seen", peerId=peerId info "already seen", peerId = peerId
let customPeerInfo = allPeers[peerId] let customPeerInfo = allPeers[peerId]
@ -153,7 +163,7 @@ proc setConnectedPeersMetrics(discoveredNodes: seq[Node],
customPeerInfo.discovered += 1 customPeerInfo.discovered += 1
if not typedRecord.get().ip.isSome(): if not typedRecord.get().ip.isSome():
warn "ip field is not set", record=typedRecord.get() warn "ip field is not set", record = typedRecord.get()
continue continue
let ip = $typedRecord.get().ip.get().join(".") let ip = $typedRecord.get().ip.get().join(".")
@ -162,7 +172,8 @@ proc setConnectedPeersMetrics(discoveredNodes: seq[Node],
# try to ping the peer # try to ping the peer
if shouldReconnect(customPeerInfo): if shouldReconnect(customPeerInfo):
if customPeerInfo.retries > 0: if customPeerInfo.retries > 0:
warn "trying to dial failed peer again", peerId=peerId, retry=customPeerInfo.retries warn "trying to dial failed peer again",
peerId = peerId, retry = customPeerInfo.retries
analyzeFuts.add(analyzePeer(customPeerInfo, peerInfo, node, timeout)) analyzeFuts.add(analyzePeer(customPeerInfo, peerInfo, node, timeout))
# Wait for all connection attempts to finish # Wait for all connection attempts to finish
@ -170,16 +181,16 @@ proc setConnectedPeersMetrics(discoveredNodes: seq[Node],
for peerIdFut in analyzedPeers: for peerIdFut in analyzedPeers:
let peerIdRes = await peerIdFut let peerIdRes = await peerIdFut
let peerIdStr = peerIdRes.valueOr(): let peerIdStr = peerIdRes.valueOr:
continue continue
successfulConnections += 1 successfulConnections += 1
let peerId = PeerId.init(peerIdStr).valueOr(): let peerId = PeerId.init(peerIdStr).valueOr:
warn "failed to parse peerId", peerId=peerIdStr warn "failed to parse peerId", peerId = peerIdStr
continue continue
var customPeerInfo = allPeers[peerIdStr] var customPeerInfo = allPeers[peerIdStr]
debug "connected to peer", peer=customPeerInfo[] debug "connected to peer", peer = customPeerInfo[]
# after connection, get supported protocols # after connection, get supported protocols
let lp2pPeerStore = node.switch.peerStore let lp2pPeerStore = node.switch.peerStore
@ -191,9 +202,9 @@ proc setConnectedPeersMetrics(discoveredNodes: seq[Node],
let nodeUserAgent = lp2pPeerStore[AgentBook][peerId] let nodeUserAgent = lp2pPeerStore[AgentBook][peerId]
customPeerInfo.userAgent = nodeUserAgent customPeerInfo.userAgent = nodeUserAgent
info "number of newly discovered peers", amount=newPeers info "number of newly discovered peers", amount = newPeers
# inform the total connections that we did in this round # inform the total connections that we did in this round
info "number of successful connections", amount=successfulConnections info "number of successful connections", amount = successfulConnections
proc updateMetrics(allPeersRef: CustomPeersTableRef) {.gcsafe.} = proc updateMetrics(allPeersRef: CustomPeersTableRef) {.gcsafe.} =
var allProtocols: Table[string, int] var allProtocols: Table[string, int]
@ -207,8 +218,9 @@ proc updateMetrics(allPeersRef: CustomPeersTableRef) {.gcsafe.} =
for protocol in peerInfo.supportedProtocols: for protocol in peerInfo.supportedProtocols:
allProtocols[protocol] = allProtocols.mgetOrPut(protocol, 0) + 1 allProtocols[protocol] = allProtocols.mgetOrPut(protocol, 0) + 1
# store available user-agents in the network # store available user-agents in the network
allAgentStrings[peerInfo.userAgent] = allAgentStrings.mgetOrPut(peerInfo.userAgent, 0) + 1 allAgentStrings[peerInfo.userAgent] =
allAgentStrings.mgetOrPut(peerInfo.userAgent, 0) + 1
if peerInfo.country != "": if peerInfo.country != "":
countries[peerInfo.country] = countries.mgetOrPut(peerInfo.country, 0) + 1 countries[peerInfo.country] = countries.mgetOrPut(peerInfo.country, 0) + 1
@ -219,25 +231,32 @@ proc updateMetrics(allPeersRef: CustomPeersTableRef) {.gcsafe.} =
networkmonitor_peer_count.set(int64(connectedPeers), labelValues = ["true"]) networkmonitor_peer_count.set(int64(connectedPeers), labelValues = ["true"])
networkmonitor_peer_count.set(int64(failedPeers), labelValues = ["false"]) networkmonitor_peer_count.set(int64(failedPeers), labelValues = ["false"])
# update count on each protocol # update count on each protocol
for protocol in allProtocols.keys(): for protocol in allProtocols.keys():
let countOfProtocols = allProtocols.mgetOrPut(protocol, 0) let countOfProtocols = allProtocols.mgetOrPut(protocol, 0)
networkmonitor_peer_type_as_per_protocol.set(int64(countOfProtocols), labelValues = [protocol]) networkmonitor_peer_type_as_per_protocol.set(
info "supported protocols in the network", protocol=protocol, count=countOfProtocols int64(countOfProtocols), labelValues = [protocol]
)
info "supported protocols in the network",
protocol = protocol, count = countOfProtocols
# update count on each user-agent # update count on each user-agent
for userAgent in allAgentStrings.keys(): for userAgent in allAgentStrings.keys():
let countOfUserAgent = allAgentStrings.mgetOrPut(userAgent, 0) let countOfUserAgent = allAgentStrings.mgetOrPut(userAgent, 0)
networkmonitor_peer_user_agents.set(int64(countOfUserAgent), labelValues = [userAgent]) networkmonitor_peer_user_agents.set(
info "user agents participating in the network", userAgent=userAgent, count=countOfUserAgent int64(countOfUserAgent), labelValues = [userAgent]
)
info "user agents participating in the network",
userAgent = userAgent, count = countOfUserAgent
for country in countries.keys(): for country in countries.keys():
let peerCount = countries.mgetOrPut(country, 0) let peerCount = countries.mgetOrPut(country, 0)
networkmonitor_peer_country_count.set(int64(peerCount), labelValues = [country]) networkmonitor_peer_country_count.set(int64(peerCount), labelValues = [country])
info "number of peers per country", country=country, count=peerCount info "number of peers per country", country = country, count = peerCount
proc populateInfoFromIp(allPeersRef: CustomPeersTableRef, proc populateInfoFromIp(
restClient: RestClientRef) {.async.} = allPeersRef: CustomPeersTableRef, restClient: RestClientRef
) {.async.} =
for peer in allPeersRef.keys(): for peer in allPeersRef.keys():
if allPeersRef[peer].country != "" and allPeersRef[peer].city != "": if allPeersRef[peer].country != "" and allPeersRef[peer].city != "":
continue continue
@ -252,7 +271,7 @@ proc populateInfoFromIp(allPeersRef: CustomPeersTableRef,
let response = await restClient.ipToLocation(allPeersRef[peer].ip) let response = await restClient.ipToLocation(allPeersRef[peer].ip)
location = response.data location = response.data
except CatchableError: except CatchableError:
warn "could not get location", ip=allPeersRef[peer].ip warn "could not get location", ip = allPeersRef[peer].ip
continue continue
allPeersRef[peer].country = location.country allPeersRef[peer].country = location.country
allPeersRef[peer].city = location.city allPeersRef[peer].city = location.city
@ -260,12 +279,13 @@ proc populateInfoFromIp(allPeersRef: CustomPeersTableRef,
# TODO: Split in discovery, connections, and ip2location # TODO: Split in discovery, connections, and ip2location
# crawls the network discovering peers and trying to connect to them # crawls the network discovering peers and trying to connect to them
# metrics are processed and exposed # metrics are processed and exposed
proc crawlNetwork(node: WakuNode, proc crawlNetwork(
wakuDiscv5: WakuDiscoveryV5, node: WakuNode,
restClient: RestClientRef, wakuDiscv5: WakuDiscoveryV5,
conf: NetworkMonitorConf, restClient: RestClientRef,
allPeersRef: CustomPeersTableRef) {.async.} = conf: NetworkMonitorConf,
allPeersRef: CustomPeersTableRef,
) {.async.} =
let crawlInterval = conf.refreshInterval * 1000 let crawlInterval = conf.refreshInterval * 1000
while true: while true:
let startTime = Moment.now() let startTime = Moment.now()
@ -281,7 +301,9 @@ proc crawlNetwork(node: WakuNode,
# tries to connect to all newly discovered nodes # tries to connect to all newly discovered nodes
# and populates metrics related to peers we could connect # and populates metrics related to peers we could connect
# note random discovered nodes can be already known # note random discovered nodes can be already known
await setConnectedPeersMetrics(discoveredNodes, node, conf.timeout, restClient, allPeersRef) await setConnectedPeersMetrics(
discoveredNodes, node, conf.timeout, restClient, allPeersRef
)
updateMetrics(allPeersRef) updateMetrics(allPeersRef)
@ -291,7 +313,7 @@ proc crawlNetwork(node: WakuNode,
let totalNodes = flatNodes.len let totalNodes = flatNodes.len
let seenNodes = flatNodes.countIt(it.seen) let seenNodes = flatNodes.countIt(it.seen)
info "discovered nodes: ", total=totalNodes, seen=seenNodes info "discovered nodes: ", total = totalNodes, seen = seenNodes
# Notes: # Notes:
# we dont run ipMajorityLoop # we dont run ipMajorityLoop
@ -299,14 +321,16 @@ proc crawlNetwork(node: WakuNode,
let endTime = Moment.now() let endTime = Moment.now()
let elapsed = (endTime - startTime).nanos let elapsed = (endTime - startTime).nanos
info "crawl duration", time=elapsed.millis info "crawl duration", time = elapsed.millis
await sleepAsync(crawlInterval.millis - elapsed.millis) await sleepAsync(crawlInterval.millis - elapsed.millis)
proc retrieveDynamicBootstrapNodes(dnsDiscovery: bool, dnsDiscoveryUrl: string, dnsDiscoveryNameServers: seq[IpAddress]): Result[seq[RemotePeerInfo], string] = proc retrieveDynamicBootstrapNodes(
dnsDiscovery: bool, dnsDiscoveryUrl: string, dnsDiscoveryNameServers: seq[IpAddress]
): Result[seq[RemotePeerInfo], string] =
if dnsDiscovery and dnsDiscoveryUrl != "": if dnsDiscovery and dnsDiscoveryUrl != "":
# DNS discovery # DNS discovery
debug "Discovering nodes using Waku DNS discovery", url=dnsDiscoveryUrl debug "Discovering nodes using Waku DNS discovery", url = dnsDiscoveryUrl
var nameServers: seq[TransportAddress] var nameServers: seq[TransportAddress]
for ip in dnsDiscoveryNameServers: for ip in dnsDiscoveryNameServers:
@ -315,24 +339,29 @@ proc retrieveDynamicBootstrapNodes(dnsDiscovery: bool, dnsDiscoveryUrl: string,
let dnsResolver = DnsResolver.new(nameServers) let dnsResolver = DnsResolver.new(nameServers)
proc resolver(domain: string): Future[string] {.async, gcsafe.} = proc resolver(domain: string): Future[string] {.async, gcsafe.} =
trace "resolving", domain=domain trace "resolving", domain = domain
let resolved = await dnsResolver.resolveTxt(domain) let resolved = await dnsResolver.resolveTxt(domain)
return resolved[0] # Use only first answer return resolved[0] # Use only first answer
var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl, resolver) var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl, resolver)
if wakuDnsDiscovery.isOk(): if wakuDnsDiscovery.isOk():
return wakuDnsDiscovery.get().findPeers() return wakuDnsDiscovery.get().findPeers().mapErr(
.mapErr(proc (e: cstring): string = $e) proc(e: cstring): string =
$e
)
else: else:
warn "Failed to init Waku DNS discovery" warn "Failed to init Waku DNS discovery"
debug "No method for retrieving dynamic bootstrap nodes specified." debug "No method for retrieving dynamic bootstrap nodes specified."
ok(newSeq[RemotePeerInfo]()) # Return an empty seq by default ok(newSeq[RemotePeerInfo]()) # Return an empty seq by default
proc getBootstrapFromDiscDns(conf: NetworkMonitorConf): Result[seq[enr.Record], string] = proc getBootstrapFromDiscDns(
conf: NetworkMonitorConf
): Result[seq[enr.Record], string] =
try: try:
let dnsNameServers = @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")] let dnsNameServers = @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")]
let dynamicBootstrapNodesRes = retrieveDynamicBootstrapNodes(true, conf.dnsDiscoveryUrl, dnsNameServers) let dynamicBootstrapNodesRes =
retrieveDynamicBootstrapNodes(true, conf.dnsDiscoveryUrl, dnsNameServers)
if not dynamicBootstrapNodesRes.isOk(): if not dynamicBootstrapNodesRes.isOk():
error("failed discovering peers from DNS") error("failed discovering peers from DNS")
let dynamicBootstrapNodes = dynamicBootstrapNodesRes.get() let dynamicBootstrapNodes = dynamicBootstrapNodesRes.get()
@ -345,22 +374,28 @@ proc getBootstrapFromDiscDns(conf: NetworkMonitorConf): Result[seq[enr.Record],
let let
enr = n.enr.get() enr = n.enr.get()
tenrRes = enr.toTypedRecord() tenrRes = enr.toTypedRecord()
if tenrRes.isOk() and (tenrRes.get().udp.isSome() or tenrRes.get().udp6.isSome()): if tenrRes.isOk() and (
tenrRes.get().udp.isSome() or tenrRes.get().udp6.isSome()
):
discv5BootstrapEnrs.add(enr) discv5BootstrapEnrs.add(enr)
return ok(discv5BootstrapEnrs) return ok(discv5BootstrapEnrs)
except CatchableError: except CatchableError:
error("failed discovering peers from DNS") error("failed discovering peers from DNS")
proc initAndStartApp(conf: NetworkMonitorConf): Result[(WakuNode, WakuDiscoveryV5), string] = proc initAndStartApp(
let bindIp = try: conf: NetworkMonitorConf
parseIpAddress("0.0.0.0") ): Result[(WakuNode, WakuDiscoveryV5), string] =
except CatchableError: let bindIp =
return err("could not start node: " & getCurrentExceptionMsg()) try:
parseIpAddress("0.0.0.0")
except CatchableError:
return err("could not start node: " & getCurrentExceptionMsg())
let extIp = try: let extIp =
parseIpAddress("127.0.0.1") try:
except CatchableError: parseIpAddress("127.0.0.1")
return err("could not start node: " & getCurrentExceptionMsg()) except CatchableError:
return err("could not start node: " & getCurrentExceptionMsg())
let let
# some hardcoded parameters # some hardcoded parameters
@ -368,34 +403,33 @@ proc initAndStartApp(conf: NetworkMonitorConf): Result[(WakuNode, WakuDiscoveryV
key = crypto.PrivateKey.random(Secp256k1, rng[])[] key = crypto.PrivateKey.random(Secp256k1, rng[])[]
nodeTcpPort = Port(60000) nodeTcpPort = Port(60000)
nodeUdpPort = Port(9000) nodeUdpPort = Port(9000)
flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) flags = CapabilitiesBitfield.init(
lightpush = false, filter = false, store = false, relay = true
)
var builder = EnrBuilder.init(key) var builder = EnrBuilder.init(key)
builder.withIpAddressAndPorts( builder.withIpAddressAndPorts(
ipAddr = some(extIp), ipAddr = some(extIp), tcpPort = some(nodeTcpPort), udpPort = some(nodeUdpPort)
tcpPort = some(nodeTcpPort),
udpPort = some(nodeUdpPort),
) )
builder.withWakuCapabilities(flags) builder.withWakuCapabilities(flags)
let addShardedTopics = builder.withShardedTopics(conf.pubsubTopics) let addShardedTopics = builder.withShardedTopics(conf.pubsubTopics)
if addShardedTopics.isErr(): if addShardedTopics.isErr():
error "failed to add sharded topics to ENR", error=addShardedTopics.error error "failed to add sharded topics to ENR", error = addShardedTopics.error
return err($addShardedTopics.error) return err($addShardedTopics.error)
let recordRes = builder.build() let recordRes = builder.build()
let record = let record =
if recordRes.isErr(): if recordRes.isErr():
return err("cannot build record: " & $recordRes.error) return err("cannot build record: " & $recordRes.error)
else: recordRes.get() else:
recordRes.get()
var nodeBuilder = WakuNodeBuilder.init() var nodeBuilder = WakuNodeBuilder.init()
nodeBuilder.withNodeKey(key) nodeBuilder.withNodeKey(key)
nodeBuilder.withRecord(record) nodeBuilder.withRecord(record)
nodeBuilder.withPeerManagerConfig( nodeBuilder.withPeerManagerConfig(maxRelayPeers = none(int), shardAware = true)
maxRelayPeers = none(int),
shardAware = true)
let res = nodeBuilder.withNetworkConfigurationDetails(bindIp, nodeTcpPort) let res = nodeBuilder.withNetworkConfigurationDetails(bindIp, nodeTcpPort)
if res.isErr(): if res.isErr():
return err("node building error" & $res.error) return err("node building error" & $res.error)
@ -404,7 +438,8 @@ proc initAndStartApp(conf: NetworkMonitorConf): Result[(WakuNode, WakuDiscoveryV
let node = let node =
if nodeRes.isErr(): if nodeRes.isErr():
return err("node building error" & $res.error) return err("node building error" & $res.error)
else: nodeRes.get() else:
nodeRes.get()
var discv5BootstrapEnrsRes = getBootstrapFromDiscDns(conf) var discv5BootstrapEnrsRes = getBootstrapFromDiscDns(conf)
if discv5BootstrapEnrsRes.isErr(): if discv5BootstrapEnrsRes.isErr():
@ -422,7 +457,7 @@ proc initAndStartApp(conf: NetworkMonitorConf): Result[(WakuNode, WakuDiscoveryV
port: nodeUdpPort, port: nodeUdpPort,
privateKey: keys.PrivateKey(key.skkey), privateKey: keys.PrivateKey(key.skkey),
bootstrapRecords: discv5BootstrapEnrs, bootstrapRecords: discv5BootstrapEnrs,
autoupdateRecord: false autoupdateRecord: false,
) )
let wakuDiscv5 = WakuDiscoveryV5.new(node.rng, discv5Conf, some(record)) let wakuDiscv5 = WakuDiscoveryV5.new(node.rng, discv5Conf, some(record))
@ -434,15 +469,17 @@ proc initAndStartApp(conf: NetworkMonitorConf): Result[(WakuNode, WakuDiscoveryV
ok((node, wakuDiscv5)) ok((node, wakuDiscv5))
proc startRestApiServer(conf: NetworkMonitorConf, proc startRestApiServer(
allPeersInfo: CustomPeersTableRef, conf: NetworkMonitorConf,
numMessagesPerContentTopic: ContentTopicMessageTableRef allPeersInfo: CustomPeersTableRef,
): Result[void, string] = numMessagesPerContentTopic: ContentTopicMessageTableRef,
): Result[void, string] =
try: try:
let serverAddress = initTAddress(conf.metricsRestAddress & ":" & $conf.metricsRestPort) let serverAddress =
initTAddress(conf.metricsRestAddress & ":" & $conf.metricsRestPort)
proc validate(pattern: string, value: string): int = proc validate(pattern: string, value: string): int =
if pattern.startsWith("{") and pattern.endsWith("}"): 0 if pattern.startsWith("{") and pattern.endsWith("}"): 0 else: 1
else: 1
var router = RestRouter.init(validate) var router = RestRouter.init(validate)
router.installHandler(allPeersInfo, numMessagesPerContentTopic) router.installHandler(allPeersInfo, numMessagesPerContentTopic)
var sres = RestServerRef.new(router, serverAddress) var sres = RestServerRef.new(router, serverAddress)
@ -454,13 +491,16 @@ proc startRestApiServer(conf: NetworkMonitorConf,
# handles rx of messages over a topic (see subscribe) # handles rx of messages over a topic (see subscribe)
# counts the number of messages per content topic # counts the number of messages per content topic
proc subscribeAndHandleMessages(node: WakuNode, proc subscribeAndHandleMessages(
pubsubTopic: PubsubTopic, node: WakuNode,
msgPerContentTopic: ContentTopicMessageTableRef) = pubsubTopic: PubsubTopic,
msgPerContentTopic: ContentTopicMessageTableRef,
) =
# handle function # handle function
proc handler(pubsubTopic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} = proc handler(
trace "rx message", pubsubTopic=pubsubTopic, contentTopic=msg.contentTopic pubsubTopic: PubsubTopic, msg: WakuMessage
): Future[void] {.async, gcsafe.} =
trace "rx message", pubsubTopic = pubsubTopic, contentTopic = msg.contentTopic
# If we reach a table limit size, remove c topics with the least messages. # If we reach a table limit size, remove c topics with the least messages.
let tableSize = 100 let tableSize = 100
@ -482,11 +522,11 @@ when isMainModule:
{.pop.} {.pop.}
let confRes = NetworkMonitorConf.loadConfig() let confRes = NetworkMonitorConf.loadConfig()
if confRes.isErr(): if confRes.isErr():
error "could not load cli variables", err=confRes.error error "could not load cli variables", err = confRes.error
quit(1) quit(1)
var conf = confRes.get() var conf = confRes.get()
info "cli flags", conf=conf info "cli flags", conf = conf
if conf.clusterId == 1: if conf.clusterId == 1:
let twnClusterConf = ClusterConf.TheWakuNetworkConf() let twnClusterConf = ClusterConf.TheWakuNetworkConf()
@ -509,22 +549,23 @@ when isMainModule:
# start metrics server # start metrics server
if conf.metricsServer: if conf.metricsServer:
let res = startMetricsServer(conf.metricsServerAddress, Port(conf.metricsServerPort)) let res =
startMetricsServer(conf.metricsServerAddress, Port(conf.metricsServerPort))
if res.isErr(): if res.isErr():
error "could not start metrics server", err=res.error error "could not start metrics server", err = res.error
quit(1) quit(1)
# start rest server for custom metrics # start rest server for custom metrics
let res = startRestApiServer(conf, allPeersInfo, msgPerContentTopic) let res = startRestApiServer(conf, allPeersInfo, msgPerContentTopic)
if res.isErr(): if res.isErr():
error "could not start rest api server", err=res.error error "could not start rest api server", err = res.error
quit(1) quit(1)
# create a rest client # create a rest client
let clientRest = RestClientRef.new(url="http://ip-api.com", let clientRest =
connectTimeout=ctime.seconds(2)) RestClientRef.new(url = "http://ip-api.com", connectTimeout = ctime.seconds(2))
if clientRest.isErr(): if clientRest.isErr():
error "could not start rest api client", err=res.error error "could not start rest api client", err = res.error
quit(1) quit(1)
let restClient = clientRest.get() let restClient = clientRest.get()
@ -540,7 +581,6 @@ when isMainModule:
waitFor node.mountLibp2pPing() waitFor node.mountLibp2pPing()
if conf.rlnRelayEthContractAddress != "": if conf.rlnRelayEthContractAddress != "":
let rlnConf = WakuRlnConfig( let rlnConf = WakuRlnConfig(
rlnRelayDynamic: conf.rlnRelayDynamic, rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: some(uint(0)), rlnRelayCredIndex: some(uint(0)),
@ -549,17 +589,17 @@ when isMainModule:
rlnRelayCredPath: "", rlnRelayCredPath: "",
rlnRelayCredPassword: "", rlnRelayCredPassword: "",
rlnRelayTreePath: conf.rlnRelayTreePath, rlnRelayTreePath: conf.rlnRelayTreePath,
rlnEpochSizeSec: conf.rlnEpochSizeSec rlnEpochSizeSec: conf.rlnEpochSizeSec,
) )
try: try:
waitFor node.mountRlnRelay(rlnConf) waitFor node.mountRlnRelay(rlnConf)
except CatchableError: except CatchableError:
error "failed to setup RLN", err=getCurrentExceptionMsg() error "failed to setup RLN", err = getCurrentExceptionMsg()
quit 1 quit 1
node.mountMetadata(conf.clusterId).isOkOr: node.mountMetadata(conf.clusterId).isOkOr:
error "failed to mount waku metadata protocol: ", err=error error "failed to mount waku metadata protocol: ", err = error
quit 1 quit 1
for pubsubTopic in conf.pubsubTopics: for pubsubTopic in conf.pubsubTopics:

View File

@ -10,106 +10,127 @@ import
type EthRpcUrl = distinct string type EthRpcUrl = distinct string
type type NetworkMonitorConf* = object
NetworkMonitorConf* = object logLevel* {.
logLevel* {. desc: "Sets the log level",
desc: "Sets the log level", defaultValue: LogLevel.INFO,
defaultValue: LogLevel.INFO, name: "log-level",
name: "log-level", abbr: "l"
abbr: "l" .}: LogLevel .}: LogLevel
timeout* {. timeout* {.
desc: "Timeout to consider that the connection failed", desc: "Timeout to consider that the connection failed",
defaultValue: chronos.seconds(10), defaultValue: chronos.seconds(10),
name: "timeout", name: "timeout",
abbr: "t" }: chronos.Duration abbr: "t"
.}: chronos.Duration
bootstrapNodes* {. bootstrapNodes* {.
desc: "Bootstrap ENR node. Argument may be repeated.", desc: "Bootstrap ENR node. Argument may be repeated.",
defaultValue: @[""], defaultValue: @[""],
name: "bootstrap-node", name: "bootstrap-node",
abbr: "b" }: seq[string] abbr: "b"
.}: seq[string]
dnsDiscoveryUrl* {. dnsDiscoveryUrl* {.
desc: "URL for DNS node list in format 'enrtree://<key>@<fqdn>'", desc: "URL for DNS node list in format 'enrtree://<key>@<fqdn>'",
defaultValue: "" defaultValue: "",
name: "dns-discovery-url" }: string name: "dns-discovery-url"
.}: string
pubsubTopics* {. pubsubTopics* {.
desc: "Default pubsub topic to subscribe to. Argument may be repeated." desc: "Default pubsub topic to subscribe to. Argument may be repeated.",
name: "pubsub-topic" .}: seq[string] name: "pubsub-topic"
.}: seq[string]
refreshInterval* {. refreshInterval* {.
desc: "How often new peers are discovered and connected to (in seconds)", desc: "How often new peers are discovered and connected to (in seconds)",
defaultValue: 5, defaultValue: 5,
name: "refresh-interval", name: "refresh-interval",
abbr: "r" }: int abbr: "r"
.}: int
clusterId* {. clusterId* {.
desc: "Cluster id that the node is running in. Node in a different cluster id is disconnected." desc:
defaultValue: 1 "Cluster id that the node is running in. Node in a different cluster id is disconnected.",
name: "cluster-id" }: uint32 defaultValue: 1,
name: "cluster-id"
.}: uint32
rlnRelay* {. rlnRelay* {.
desc: "Enable spam protection through rln-relay: true|false", desc: "Enable spam protection through rln-relay: true|false",
defaultValue: true defaultValue: true,
name: "rln-relay" }: bool name: "rln-relay"
.}: bool
rlnRelayDynamic* {. rlnRelayDynamic* {.
desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false", desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false",
defaultValue: true defaultValue: true,
name: "rln-relay-dynamic" }: bool name: "rln-relay-dynamic"
.}: bool
rlnRelayTreePath* {. rlnRelayTreePath* {.
desc: "Path to the RLN merkle tree sled db (https://github.com/spacejam/sled)", desc: "Path to the RLN merkle tree sled db (https://github.com/spacejam/sled)",
defaultValue: "" defaultValue: "",
name: "rln-relay-tree-path" }: string name: "rln-relay-tree-path"
.}: string
rlnRelayEthClientAddress* {. rlnRelayEthClientAddress* {.
desc: "HTTP address of an Ethereum testnet client e.g., http://localhost:8540/", desc: "HTTP address of an Ethereum testnet client e.g., http://localhost:8540/",
defaultValue: "http://localhost:8540/", defaultValue: "http://localhost:8540/",
name: "rln-relay-eth-client-address" }: EthRpcUrl name: "rln-relay-eth-client-address"
.}: EthRpcUrl
rlnRelayEthContractAddress* {. rlnRelayEthContractAddress* {.
desc: "Address of membership contract on an Ethereum testnet", desc: "Address of membership contract on an Ethereum testnet",
defaultValue: "", defaultValue: "",
name: "rln-relay-eth-contract-address" }: string name: "rln-relay-eth-contract-address"
.}: string
rlnEpochSizeSec* {. rlnEpochSizeSec* {.
desc: "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.", desc:
defaultValue: 1 "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.",
name: "rln-relay-epoch-sec" .}: uint64 defaultValue: 1,
name: "rln-relay-epoch-sec"
.}: uint64
rlnRelayUserMessageLimit* {. rlnRelayUserMessageLimit* {.
desc: "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.", desc:
defaultValue: 1, "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.",
name: "rln-relay-user-message-limit" .}: uint64 defaultValue: 1,
name: "rln-relay-user-message-limit"
.}: uint64
## Prometheus metrics config ## Prometheus metrics config
metricsServer* {. metricsServer* {.
desc: "Enable the metrics server: true|false" desc: "Enable the metrics server: true|false",
defaultValue: true defaultValue: true,
name: "metrics-server" }: bool name: "metrics-server"
.}: bool
metricsServerAddress* {. metricsServerAddress* {.
desc: "Listening address of the metrics server." desc: "Listening address of the metrics server.",
defaultValue: parseIpAddress("127.0.0.1") defaultValue: parseIpAddress("127.0.0.1"),
name: "metrics-server-address" }: IpAddress name: "metrics-server-address"
.}: IpAddress
metricsServerPort* {. metricsServerPort* {.
desc: "Listening HTTP port of the metrics server." desc: "Listening HTTP port of the metrics server.",
defaultValue: 8008 defaultValue: 8008,
name: "metrics-server-port" }: uint16 name: "metrics-server-port"
.}: uint16
## Custom metrics rest server ## Custom metrics rest server
metricsRestAddress* {. metricsRestAddress* {.
desc: "Listening address of the metrics rest server.", desc: "Listening address of the metrics rest server.",
defaultValue: "127.0.0.1", defaultValue: "127.0.0.1",
name: "metrics-rest-address" }: string name: "metrics-rest-address"
metricsRestPort* {. .}: string
desc: "Listening HTTP port of the metrics rest server.", metricsRestPort* {.
defaultValue: 8009, desc: "Listening HTTP port of the metrics rest server.",
name: "metrics-rest-port" }: uint16 defaultValue: 8009,
name: "metrics-rest-port"
.}: uint16
proc parseCmdArg*(T: type IpAddress, p: string): T = proc parseCmdArg*(T: type IpAddress, p: string): T =
try: try:
@ -143,18 +164,22 @@ proc parseCmdArg*(T: type EthRpcUrl, s: string): T =
## https://url:port/path?query ## https://url:port/path?query
## disallowed patterns: ## disallowed patterns:
## any valid/invalid ws or wss url ## any valid/invalid ws or wss url
var httpPattern = re2"^(https?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*" var httpPattern =
var wsPattern = re2"^(wss?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*" re2"^(https?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
var wsPattern =
re2"^(wss?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
if regex.match(s, wsPattern): if regex.match(s, wsPattern):
echo "here" echo "here"
raise newException(ValueError, "Websocket RPC URL is not supported, Please use an HTTP URL") raise newException(
ValueError, "Websocket RPC URL is not supported, Please use an HTTP URL"
)
if not regex.match(s, httpPattern): if not regex.match(s, httpPattern):
raise newException(ValueError, "Invalid HTTP RPC URL") raise newException(ValueError, "Invalid HTTP RPC URL")
return EthRpcUrl(s) return EthRpcUrl(s)
proc loadConfig*(T: type NetworkMonitorConf): Result[T, string] = proc loadConfig*(T: type NetworkMonitorConf): Result[T, string] =
try: try:
let conf = NetworkMonitorConf.load(version=git_version) let conf = NetworkMonitorConf.load(version = git_version)
ok(conf) ok(conf)
except CatchableError: except CatchableError:
err(getCurrentExceptionMsg()) err(getCurrentExceptionMsg())

View File

@ -4,7 +4,7 @@ else:
{.push raises: [].} {.push raises: [].}
import import
std/[json,tables,sequtils], std/[json, tables, sequtils],
chronicles, chronicles,
chronicles/topics_registry, chronicles/topics_registry,
chronos, chronos,
@ -26,32 +26,29 @@ logScope:
#discovery_message_requests_outgoing_total{response="no_response"} #discovery_message_requests_outgoing_total{response="no_response"}
declarePublicGauge networkmonitor_peer_type_as_per_enr, declarePublicGauge networkmonitor_peer_type_as_per_enr,
"Number of peers supporting each capability according to the ENR", "Number of peers supporting each capability according to the ENR",
labels = ["capability"] labels = ["capability"]
declarePublicGauge networkmonitor_peer_type_as_per_protocol, declarePublicGauge networkmonitor_peer_type_as_per_protocol,
"Number of peers supporting each protocol, after a successful connection) ", "Number of peers supporting each protocol, after a successful connection) ",
labels = ["protocols"] labels = ["protocols"]
declarePublicGauge networkmonitor_peer_user_agents, declarePublicGauge networkmonitor_peer_user_agents,
"Number of peers with each user agent", "Number of peers with each user agent", labels = ["user_agent"]
labels = ["user_agent"]
declarePublicHistogram networkmonitor_peer_ping, declarePublicHistogram networkmonitor_peer_ping,
"Histogram tracking ping durations for discovered peers", "Histogram tracking ping durations for discovered peers",
buckets = [100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 2000.0, Inf] buckets =
[100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 2000.0, Inf]
declarePublicGauge networkmonitor_peer_count, declarePublicGauge networkmonitor_peer_count,
"Number of discovered peers", "Number of discovered peers", labels = ["connected"]
labels = ["connected"]
declarePublicGauge networkmonitor_peer_country_count, declarePublicGauge networkmonitor_peer_country_count,
"Number of peers per country", "Number of peers per country", labels = ["country"]
labels = ["country"]
type type
CustomPeerInfo* = object CustomPeerInfo* = object # populated after discovery
# populated after discovery
lastTimeDiscovered*: int64 lastTimeDiscovered*: int64
discovered*: int64 discovered*: int64
peerId*: string peerId*: string
@ -80,23 +77,32 @@ type
# stores the content topic and the count of rx messages # stores the content topic and the count of rx messages
ContentTopicMessageTableRef* = TableRef[string, int] ContentTopicMessageTableRef* = TableRef[string, int]
proc installHandler*(router: var RestRouter, proc installHandler*(
allPeers: CustomPeersTableRef, router: var RestRouter,
numMessagesPerContentTopic: ContentTopicMessageTableRef) = allPeers: CustomPeersTableRef,
router.api(MethodGet, "/allpeersinfo") do () -> RestApiResponse: numMessagesPerContentTopic: ContentTopicMessageTableRef,
) =
router.api(MethodGet, "/allpeersinfo") do() -> RestApiResponse:
let values = toSeq(allPeers.values()) let values = toSeq(allPeers.values())
return RestApiResponse.response(values.toJson(), contentType="application/json") return RestApiResponse.response(values.toJson(), contentType = "application/json")
router.api(MethodGet, "/contenttopics") do () -> RestApiResponse: router.api(MethodGet, "/contenttopics") do() -> RestApiResponse:
# TODO: toJson() includes the hash # TODO: toJson() includes the hash
return RestApiResponse.response($(%numMessagesPerContentTopic), contentType="application/json") return RestApiResponse.response(
$(%numMessagesPerContentTopic), contentType = "application/json"
)
proc startMetricsServer*(serverIp: IpAddress, serverPort: Port): Result[void, string] = proc startMetricsServer*(serverIp: IpAddress, serverPort: Port): Result[void, string] =
info "Starting metrics HTTP server", serverIp, serverPort info "Starting metrics HTTP server", serverIp, serverPort
try: try:
startMetricsHttpServer($serverIp, serverPort) startMetricsHttpServer($serverIp, serverPort)
except Exception as e: except Exception as e:
error("Failed to start metrics HTTP server", serverIp=serverIp, serverPort=serverPort, msg=e.msg) error(
"Failed to start metrics HTTP server",
serverIp = serverIp,
serverPort = serverPort,
msg = e.msg,
)
info "Metrics HTTP server started", serverIp, serverPort info "Metrics HTTP server started", serverIp, serverPort
ok() ok()

View File

@ -10,15 +10,14 @@ import
chronicles, chronicles,
chronicles/topics_registry, chronicles/topics_registry,
chronos, chronos,
presto/[client,common] presto/[client, common]
type type NodeLocation* = object
NodeLocation* = object country*: string
country*: string city*: string
city*: string lat*: string
lat*: string long*: string
long*: string isp*: string
isp*: string
proc flatten*[T](a: seq[seq[T]]): seq[T] = proc flatten*[T](a: seq[seq[T]]): seq[T] =
var aFlat = newSeq[T](0) var aFlat = newSeq[T](0)
@ -26,8 +25,9 @@ proc flatten*[T](a: seq[seq[T]]): seq[T] =
aFlat &= subseq aFlat &= subseq
return aFlat return aFlat
proc decodeBytes*(t: typedesc[NodeLocation], value: openArray[byte], proc decodeBytes*(
contentType: Opt[ContentTypeData]): RestResult[NodeLocation] = t: typedesc[NodeLocation], value: openArray[byte], contentType: Opt[ContentTypeData]
): RestResult[NodeLocation] =
var res: string var res: string
if len(value) > 0: if len(value) > 0:
res = newString(len(value)) res = newString(len(value))
@ -35,19 +35,23 @@ proc decodeBytes*(t: typedesc[NodeLocation], value: openArray[byte],
try: try:
let jsonContent = parseJson(res) let jsonContent = parseJson(res)
if $jsonContent["status"].getStr() != "success": if $jsonContent["status"].getStr() != "success":
error "query failed", result=jsonContent error "query failed", result = jsonContent
return err("query failed") return err("query failed")
return ok(NodeLocation( return ok(
country: jsonContent["country"].getStr(), NodeLocation(
city: jsonContent["city"].getStr(), country: jsonContent["country"].getStr(),
lat: $jsonContent["lat"].getFloat(), city: jsonContent["city"].getStr(),
long: $jsonContent["lon"].getFloat(), lat: $jsonContent["lat"].getFloat(),
isp: jsonContent["isp"].getStr() long: $jsonContent["lon"].getFloat(),
)) isp: jsonContent["isp"].getStr(),
)
)
except Exception: except Exception:
return err("failed to get the location: " & getCurrentExceptionMsg()) return err("failed to get the location: " & getCurrentExceptionMsg())
proc encodeString*(value: string): RestResult[string] = proc encodeString*(value: string): RestResult[string] =
ok(value) ok(value)
proc ipToLocation*(ip: string): RestResponse[NodeLocation] {.rest, endpoint: "json/{ip}", meth: MethodGet.} proc ipToLocation*(
ip: string
): RestResponse[NodeLocation] {.rest, endpoint: "json/{ip}", meth: MethodGet.}

View File

@ -1,8 +1,4 @@
import import osproc, os, httpclient, strutils
osproc,
os,
httpclient,
strutils
proc getPublicIP(): string = proc getPublicIP(): string =
let client = newHttpClient() let client = newHttpClient()
@ -14,8 +10,7 @@ proc getPublicIP(): string =
return "127.0.0.1" return "127.0.0.1"
# Function to generate a self-signed certificate # Function to generate a self-signed certificate
proc generateSelfSignedCertificate*(certPath: string, keyPath: string) : int = proc generateSelfSignedCertificate*(certPath: string, keyPath: string): int =
# Ensure the OpenSSL is installed # Ensure the OpenSSL is installed
if findExe("openssl") == "": if findExe("openssl") == "":
echo "OpenSSL is not installed or not in the PATH." echo "OpenSSL is not installed or not in the PATH."
@ -28,9 +23,10 @@ proc generateSelfSignedCertificate*(certPath: string, keyPath: string) : int =
# Command to generate private key and cert # Command to generate private key and cert
let let
cmd = "openssl req -x509 -newkey rsa:4096 -keyout " & keyPath & " -out " & certPath & cmd =
" -sha256 -days 3650 -nodes -subj '/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=" & "openssl req -x509 -newkey rsa:4096 -keyout " & keyPath & " -out " & certPath &
publicIP & "'" " -sha256 -days 3650 -nodes -subj '/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=" &
publicIP & "'"
res = execCmd(cmd) res = execCmd(cmd)
if res == 0: if res == 0:
@ -39,4 +35,3 @@ proc generateSelfSignedCertificate*(certPath: string, keyPath: string) : int =
echo "Failed to generate certificate and key." echo "Failed to generate certificate and key."
return res return res

View File

@ -30,52 +30,58 @@ const WebSocketPortOffset = 1000
const CertsDirectory = "./certs" const CertsDirectory = "./certs"
# cli flags # cli flags
type type WakuCanaryConf* = object
WakuCanaryConf* = object address* {.
address* {. desc: "Multiaddress of the peer node to attempt to dial",
desc: "Multiaddress of the peer node to attempt to dial", defaultValue: "",
defaultValue: "", name: "address",
name: "address", abbr: "a"
abbr: "a".}: string .}: string
timeout* {. timeout* {.
desc: "Timeout to consider that the connection failed", desc: "Timeout to consider that the connection failed",
defaultValue: chronos.seconds(10), defaultValue: chronos.seconds(10),
name: "timeout", name: "timeout",
abbr: "t".}: chronos.Duration abbr: "t"
.}: chronos.Duration
protocols* {. protocols* {.
desc: "Protocol required to be supported: store,relay,lightpush,filter (can be used multiple times)", desc:
name: "protocol", "Protocol required to be supported: store,relay,lightpush,filter (can be used multiple times)",
abbr: "p".}: seq[string] name: "protocol",
abbr: "p"
.}: seq[string]
logLevel* {. logLevel* {.
desc: "Sets the log level", desc: "Sets the log level",
defaultValue: LogLevel.INFO, defaultValue: LogLevel.INFO,
name: "log-level", name: "log-level",
abbr: "l".}: LogLevel abbr: "l"
.}: LogLevel
nodePort* {. nodePort* {.
desc: "Listening port for waku node", desc: "Listening port for waku node",
defaultValue: 60000, defaultValue: 60000,
name: "node-port", name: "node-port",
abbr: "np".}: uint16 abbr: "np"
.}: uint16
## websocket secure config ## websocket secure config
websocketSecureKeyPath* {. websocketSecureKeyPath* {.
desc: "Secure websocket key path: '/path/to/key.txt' ", desc: "Secure websocket key path: '/path/to/key.txt' ",
defaultValue: "" defaultValue: "",
name: "websocket-secure-key-path".}: string name: "websocket-secure-key-path"
.}: string
websocketSecureCertPath* {. websocketSecureCertPath* {.
desc: "Secure websocket Certificate path: '/path/to/cert.txt' ", desc: "Secure websocket Certificate path: '/path/to/cert.txt' ",
defaultValue: "" defaultValue: "",
name: "websocket-secure-cert-path".}: string name: "websocket-secure-cert-path"
.}: string
ping* {. ping* {.
desc: "Ping the peer node to measure latency", desc: "Ping the peer node to measure latency", defaultValue: true, name: "ping"
defaultValue: true, .}: bool
name: "ping" .}: bool
proc parseCmdArg*(T: type chronos.Duration, p: string): T = proc parseCmdArg*(T: type chronos.Duration, p: string): T =
try: try:
@ -88,17 +94,15 @@ proc completeCmdArg*(T: type chronos.Duration, val: string): seq[string] =
# checks if rawProtocols (skipping version) are supported in nodeProtocols # checks if rawProtocols (skipping version) are supported in nodeProtocols
proc areProtocolsSupported( proc areProtocolsSupported(
rawProtocols: seq[string], rawProtocols: seq[string], nodeProtocols: seq[string]
nodeProtocols: seq[string]): bool = ): bool =
var numOfSupportedProt: int = 0 var numOfSupportedProt: int = 0
for nodeProtocol in nodeProtocols: for nodeProtocol in nodeProtocols:
for rawProtocol in rawProtocols: for rawProtocol in rawProtocols:
let protocolTag = ProtocolsTable[rawProtocol] let protocolTag = ProtocolsTable[rawProtocol]
if nodeProtocol.startsWith(protocolTag): if nodeProtocol.startsWith(protocolTag):
info "Supported protocol ok", expected = protocolTag, info "Supported protocol ok", expected = protocolTag, supported = nodeProtocol
supported = nodeProtocol
numOfSupportedProt += 1 numOfSupportedProt += 1
break break
@ -107,26 +111,29 @@ proc areProtocolsSupported(
return false return false
proc pingNode(node: WakuNode, peerInfo: RemotePeerInfo): Future[void] {.async, gcsafe.} = proc pingNode(
node: WakuNode, peerInfo: RemotePeerInfo
): Future[void] {.async, gcsafe.} =
try: try:
let conn = await node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec) let conn = await node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec)
let pingDelay = await node.libp2pPing.ping(conn) let pingDelay = await node.libp2pPing.ping(conn)
info "Peer response time (ms)", peerId = peerInfo.peerId, ping=pingDelay.millis info "Peer response time (ms)", peerId = peerInfo.peerId, ping = pingDelay.millis
except CatchableError: except CatchableError:
var msg = getCurrentExceptionMsg() var msg = getCurrentExceptionMsg()
if msg == "Future operation cancelled!": if msg == "Future operation cancelled!":
msg = "timedout" msg = "timedout"
error "Failed to ping the peer", peer=peerInfo, err=msg error "Failed to ping the peer", peer = peerInfo, err = msg
proc main(rng: ref HmacDrbgContext): Future[int] {.async.} = proc main(rng: ref HmacDrbgContext): Future[int] {.async.} =
let conf: WakuCanaryConf = WakuCanaryConf.load() let conf: WakuCanaryConf = WakuCanaryConf.load()
# create dns resolver # create dns resolver
let let
nameServers = @[ nameServers =
initTAddress(parseIpAddress("1.1.1.1"), Port(53)), @[
initTAddress(parseIpAddress("1.0.0.1"), Port(53))] initTAddress(parseIpAddress("1.1.1.1"), Port(53)),
initTAddress(parseIpAddress("1.0.0.1"), Port(53)),
]
resolver: DnsResolver = DnsResolver.new(nameServers) resolver: DnsResolver = DnsResolver.new(nameServers)
if conf.logLevel != LogLevel.NONE: if conf.logLevel != LogLevel.NONE:
@ -158,14 +165,16 @@ proc main(rng: ref HmacDrbgContext): Future[int] {.async.} =
nodeTcpPort = Port(conf.nodePort) nodeTcpPort = Port(conf.nodePort)
isWs = peer.addrs[0].contains(multiCodec("ws")).get() isWs = peer.addrs[0].contains(multiCodec("ws")).get()
isWss = peer.addrs[0].contains(multiCodec("wss")).get() isWss = peer.addrs[0].contains(multiCodec("wss")).get()
keyPath = if conf.websocketSecureKeyPath.len > 0: keyPath =
conf.websocketSecureKeyPath if conf.websocketSecureKeyPath.len > 0:
else: conf.websocketSecureKeyPath
CertsDirectory & "/key.pem" else:
certPath = if conf.websocketSecureCertPath.len > 0: CertsDirectory & "/key.pem"
conf.websocketSecureCertPath certPath =
else: if conf.websocketSecureCertPath.len > 0:
CertsDirectory & "/cert.pem" conf.websocketSecureCertPath
else:
CertsDirectory & "/cert.pem"
var builder = WakuNodeBuilder.init() var builder = WakuNodeBuilder.init()
builder.withNodeKey(nodeKey) builder.withNodeKey(nodeKey)
@ -183,12 +192,13 @@ proc main(rng: ref HmacDrbgContext): Future[int] {.async.} =
let recordRes = enrBuilder.build() let recordRes = enrBuilder.build()
let record = let record =
if recordRes.isErr(): if recordRes.isErr():
error "failed to create enr record", error=recordRes.error error "failed to create enr record", error = recordRes.error
quit(QuitFailure) quit(QuitFailure)
else: recordRes.get() else:
recordRes.get()
if isWss and (conf.websocketSecureKeyPath.len == 0 or if isWss and
conf.websocketSecureCertPath.len == 0): (conf.websocketSecureKeyPath.len == 0 or conf.websocketSecureCertPath.len == 0):
info "WebSocket Secure requires key and certificate. Generating them" info "WebSocket Secure requires key and certificate. Generating them"
if not dirExists(CertsDirectory): if not dirExists(CertsDirectory):
createDir(CertsDirectory) createDir(CertsDirectory)
@ -199,9 +209,7 @@ proc main(rng: ref HmacDrbgContext): Future[int] {.async.} =
builder.withRecord(record) builder.withRecord(record)
builder.withNetworkConfiguration(netConfig.tryGet()) builder.withNetworkConfiguration(netConfig.tryGet())
builder.withSwitchConfiguration( builder.withSwitchConfiguration(
secureKey = some(keyPath), secureKey = some(keyPath), secureCert = some(certPath), nameResolver = resolver
secureCert = some(certPath),
nameResolver = resolver,
) )
let node = builder.build().tryGet() let node = builder.build().tryGet()
@ -215,7 +223,7 @@ proc main(rng: ref HmacDrbgContext): Future[int] {.async.} =
await node.start() await node.start()
var pingFut:Future[bool] var pingFut: Future[bool]
if conf.ping: if conf.ping:
pingFut = pingNode(node, peer).withTimeout(conf.timeout) pingFut = pingNode(node, peer).withTimeout(conf.timeout)
@ -233,8 +241,8 @@ proc main(rng: ref HmacDrbgContext): Future[int] {.async.} =
if conStatus in [Connected, CanConnect]: if conStatus in [Connected, CanConnect]:
let nodeProtocols = lp2pPeerStore[ProtoBook][peer.peerId] let nodeProtocols = lp2pPeerStore[ProtoBook][peer.peerId]
if not areProtocolsSupported(conf.protocols, nodeProtocols): if not areProtocolsSupported(conf.protocols, nodeProtocols):
error "Not all protocols are supported", expected = conf.protocols, error "Not all protocols are supported",
supported = nodeProtocols expected = conf.protocols, supported = nodeProtocols
return 1 return 1
elif conStatus == CannotConnect: elif conStatus == CannotConnect:
error "Could not connect", peerId = peer.peerId error "Could not connect", peerId = peer.peerId

View File

@ -57,7 +57,6 @@ import
logScope: logScope:
topics = "wakunode app" topics = "wakunode app"
# Git version in git describe format (defined at compile time) # Git version in git describe format (defined at compile time)
const git_version* {.strdefine.} = "n/a" const git_version* {.strdefine.} = "n/a"
@ -78,7 +77,6 @@ type
AppResult*[T] = Result[T, string] AppResult*[T] = Result[T, string]
func node*(app: App): WakuNode = func node*(app: App): WakuNode =
app.node app.node
@ -87,14 +85,12 @@ func version*(app: App): string =
## Retrieve dynamic bootstrap nodes (DNS discovery) ## Retrieve dynamic bootstrap nodes (DNS discovery)
proc retrieveDynamicBootstrapNodes*(dnsDiscovery: bool, proc retrieveDynamicBootstrapNodes*(
dnsDiscoveryUrl: string, dnsDiscovery: bool, dnsDiscoveryUrl: string, dnsDiscoveryNameServers: seq[IpAddress]
dnsDiscoveryNameServers: seq[IpAddress]): ): Result[seq[RemotePeerInfo], string] =
Result[seq[RemotePeerInfo], string] =
if dnsDiscovery and dnsDiscoveryUrl != "": if dnsDiscovery and dnsDiscoveryUrl != "":
# DNS discovery # DNS discovery
debug "Discovering nodes using Waku DNS discovery", url=dnsDiscoveryUrl debug "Discovering nodes using Waku DNS discovery", url = dnsDiscoveryUrl
var nameServers: seq[TransportAddress] var nameServers: seq[TransportAddress]
for ip in dnsDiscoveryNameServers: for ip in dnsDiscoveryNameServers:
@ -103,14 +99,16 @@ proc retrieveDynamicBootstrapNodes*(dnsDiscovery: bool,
let dnsResolver = DnsResolver.new(nameServers) let dnsResolver = DnsResolver.new(nameServers)
proc resolver(domain: string): Future[string] {.async, gcsafe.} = proc resolver(domain: string): Future[string] {.async, gcsafe.} =
trace "resolving", domain=domain trace "resolving", domain = domain
let resolved = await dnsResolver.resolveTxt(domain) let resolved = await dnsResolver.resolveTxt(domain)
return resolved[0] # Use only first answer return resolved[0] # Use only first answer
var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl, resolver) var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl, resolver)
if wakuDnsDiscovery.isOk(): if wakuDnsDiscovery.isOk():
return wakuDnsDiscovery.get().findPeers() return wakuDnsDiscovery.get().findPeers().mapErr(
.mapErr(proc (e: cstring): string = $e) proc(e: cstring): string =
$e
)
else: else:
warn "Failed to init Waku DNS discovery" warn "Failed to init Waku DNS discovery"
@ -120,7 +118,6 @@ proc retrieveDynamicBootstrapNodes*(dnsDiscovery: bool,
## Initialisation ## Initialisation
proc init*(T: type App, conf: WakuNodeConf): Result[App, string] = proc init*(T: type App, conf: WakuNodeConf): Result[App, string] =
var confCopy = conf var confCopy = conf
let rng = crypto.newRng() let rng = crypto.newRng()
@ -132,35 +129,37 @@ proc init*(T: type App, conf: WakuNodeConf): Result[App, string] =
confCopy.nodekey = some(keyRes.get()) confCopy.nodekey = some(keyRes.get())
debug "Retrieve dynamic bootstrap nodes" debug "Retrieve dynamic bootstrap nodes"
let dynamicBootstrapNodesRes = retrieveDynamicBootstrapNodes(confCopy.dnsDiscovery, let dynamicBootstrapNodesRes = retrieveDynamicBootstrapNodes(
confCopy.dnsDiscoveryUrl, confCopy.dnsDiscovery, confCopy.dnsDiscoveryUrl, confCopy.dnsDiscoveryNameServers
confCopy.dnsDiscoveryNameServers) )
if dynamicBootstrapNodesRes.isErr(): if dynamicBootstrapNodesRes.isErr():
error "Retrieving dynamic bootstrap nodes failed", error = dynamicBootstrapNodesRes.error error "Retrieving dynamic bootstrap nodes failed",
return err("Retrieving dynamic bootstrap nodes failed: " & dynamicBootstrapNodesRes.error) error = dynamicBootstrapNodesRes.error
return err(
"Retrieving dynamic bootstrap nodes failed: " & dynamicBootstrapNodesRes.error
)
let nodeRes = setupNode(confCopy, some(rng)) let nodeRes = setupNode(confCopy, some(rng))
if nodeRes.isErr(): if nodeRes.isErr():
error "Failed setting up node", error=nodeRes.error error "Failed setting up node", error = nodeRes.error
return err("Failed setting up node: " & nodeRes.error) return err("Failed setting up node: " & nodeRes.error)
var app = App( var app = App(
version: git_version, version: git_version,
conf: confCopy, conf: confCopy,
rng: rng, rng: rng,
key: confCopy.nodekey.get(), key: confCopy.nodekey.get(),
node: nodeRes.get(), node: nodeRes.get(),
dynamicBootstrapNodes: dynamicBootstrapNodesRes.get() dynamicBootstrapNodes: dynamicBootstrapNodesRes.get(),
) )
ok(app) ok(app)
## Setup DiscoveryV5 ## Setup DiscoveryV5
proc setupDiscoveryV5*(app: App): WakuDiscoveryV5 = proc setupDiscoveryV5*(app: App): WakuDiscoveryV5 =
let dynamicBootstrapEnrs = app.dynamicBootstrapNodes let dynamicBootstrapEnrs =
.filterIt(it.hasUdpPort()) app.dynamicBootstrapNodes.filterIt(it.hasUdpPort()).mapIt(it.enr.get())
.mapIt(it.enr.get())
var discv5BootstrapEnrs: seq[enr.Record] var discv5BootstrapEnrs: seq[enr.Record]
@ -170,9 +169,9 @@ proc setupDiscoveryV5*(app: App): WakuDiscoveryV5 =
discv5BootstrapEnrs.add(dynamicBootstrapEnrs) discv5BootstrapEnrs.add(dynamicBootstrapEnrs)
let discv5Config = DiscoveryConfig.init(app.conf.discv5TableIpLimit, let discv5Config = DiscoveryConfig.init(
app.conf.discv5BucketIpLimit, app.conf.discv5TableIpLimit, app.conf.discv5BucketIpLimit, app.conf.discv5BitsPerHop
app.conf.discv5BitsPerHop) )
let discv5UdpPort = Port(uint16(app.conf.discv5UdpPort) + app.conf.portsShift) let discv5UdpPort = Port(uint16(app.conf.discv5UdpPort) + app.conf.portsShift)
@ -193,9 +192,9 @@ proc setupDiscoveryV5*(app: App): WakuDiscoveryV5 =
app.node.topicSubscriptionQueue, app.node.topicSubscriptionQueue,
) )
proc getPorts(listenAddrs: seq[MultiAddress]): proc getPorts(
AppResult[tuple[tcpPort, websocketPort: Option[Port]]] = listenAddrs: seq[MultiAddress]
): AppResult[tuple[tcpPort, websocketPort: Option[Port]]] =
var tcpPort, websocketPort = none(Port) var tcpPort, websocketPort = none(Port)
for a in listenAddrs: for a in listenAddrs:
@ -212,7 +211,6 @@ proc getPorts(listenAddrs: seq[MultiAddress]):
return ok((tcpPort: tcpPort, websocketPort: websocketPort)) return ok((tcpPort: tcpPort, websocketPort: websocketPort))
proc getRunningNetConfig(app: App): AppResult[NetConfig] = proc getRunningNetConfig(app: App): AppResult[NetConfig] =
var conf = app.conf var conf = app.conf
let (tcpPort, websocketPort) = getPorts(app.node.switch.peerInfo.listenAddrs).valueOr: let (tcpPort, websocketPort) = getPorts(app.node.switch.peerInfo.listenAddrs).valueOr:
return err("Could not retrieve ports " & error) return err("Could not retrieve ports " & error)
@ -230,7 +228,6 @@ proc getRunningNetConfig(app: App): AppResult[NetConfig] =
return ok(netConf) return ok(netConf)
proc updateEnr(app: var App, netConf: NetConfig): AppResult[void] = proc updateEnr(app: var App, netConf: NetConfig): AppResult[void] =
let record = enrConfiguration(app.conf, netConf, app.key).valueOr: let record = enrConfiguration(app.conf, netConf, app.key).valueOr:
return err("ENR setup failed: " & error) return err("ENR setup failed: " & error)
@ -242,9 +239,7 @@ proc updateEnr(app: var App, netConf: NetConfig): AppResult[void] =
return ok() return ok()
proc updateApp(app: var App): AppResult[void] = proc updateApp(app: var App): AppResult[void] =
if app.conf.tcpPort == Port(0) or app.conf.websocketPort == Port(0): if app.conf.tcpPort == Port(0) or app.conf.websocketPort == Port(0):
let netConf = getRunningNetConfig(app).valueOr: let netConf = getRunningNetConfig(app).valueOr:
return err("error calling updateNetConfig: " & $error) return err("error calling updateNetConfig: " & $error)
@ -258,8 +253,8 @@ proc updateApp(app: var App): AppResult[void] =
return ok() return ok()
proc startApp*(app: var App): AppResult[void] = proc startApp*(app: var App): AppResult[void] =
let nodeRes = catch:
let nodeRes = catch: (waitFor startNode(app.node, app.conf, app.dynamicBootstrapNodes)) (waitFor startNode(app.node, app.conf, app.dynamicBootstrapNodes))
if nodeRes.isErr(): if nodeRes.isErr():
return err("exception starting node: " & nodeRes.error.msg) return err("exception starting node: " & nodeRes.error.msg)
@ -276,7 +271,8 @@ proc startApp*(app: var App): AppResult[void] =
if app.wakuDiscv5.isSome(): if app.wakuDiscv5.isSome():
let wakuDiscv5 = app.wakuDiscv5.get() let wakuDiscv5 = app.wakuDiscv5.get()
let catchRes = catch: (waitFor wakuDiscv5.start()) let catchRes = catch:
(waitFor wakuDiscv5.start())
let startRes = catchRes.valueOr: let startRes = catchRes.valueOr:
return err("failed to start waku discovery v5: " & catchRes.error.msg) return err("failed to start waku discovery v5: " & catchRes.error.msg)
@ -285,38 +281,37 @@ proc startApp*(app: var App): AppResult[void] =
return ok() return ok()
## Monitoring and external interfaces ## Monitoring and external interfaces
proc startRestServer(app: App, proc startRestServer(
address: IpAddress, app: App, address: IpAddress, port: Port, conf: WakuNodeConf
port: Port, ): AppResult[WakuRestServerRef] =
conf: WakuNodeConf):
AppResult[WakuRestServerRef] =
# Used to register api endpoints that are not currently installed as keys, # Used to register api endpoints that are not currently installed as keys,
# values are holding error messages to be returned to the client # values are holding error messages to be returned to the client
var notInstalledTab: Table[string, string] = initTable[string, string]() var notInstalledTab: Table[string, string] = initTable[string, string]()
let requestErrorHandler : RestRequestErrorHandler = proc (error: RestRequestError, let requestErrorHandler: RestRequestErrorHandler = proc(
request: HttpRequestRef): error: RestRequestError, request: HttpRequestRef
Future[HttpResponseRef] ): Future[HttpResponseRef] {.async: (raises: [CancelledError]).} =
{.async: (raises: [CancelledError]).} =
try: try:
case error case error
of RestRequestError.Invalid: of RestRequestError.Invalid:
return await request.respond(Http400, "Invalid request", HttpTable.init()) return await request.respond(Http400, "Invalid request", HttpTable.init())
of RestRequestError.NotFound: of RestRequestError.NotFound:
let paths = request.rawPath.split("/") let paths = request.rawPath.split("/")
let rootPath = if len(paths) > 1: let rootPath =
paths[1] if len(paths) > 1:
else: paths[1]
"" else:
""
notInstalledTab.withValue(rootPath, errMsg): notInstalledTab.withValue(rootPath, errMsg):
return await request.respond(Http404, errMsg[], HttpTable.init()) return await request.respond(Http404, errMsg[], HttpTable.init())
do: do:
return await request.respond(Http400, "Bad request initiated. Invalid path or method used.", HttpTable.init()) return await request.respond(
Http400,
"Bad request initiated. Invalid path or method used.",
HttpTable.init(),
)
of RestRequestError.InvalidContentBody: of RestRequestError.InvalidContentBody:
return await request.respond(Http400, "Invalid content body", HttpTable.init()) return await request.respond(Http400, "Invalid content body", HttpTable.init())
of RestRequestError.InvalidContentType: of RestRequestError.InvalidContentType:
@ -329,14 +324,19 @@ proc startRestServer(app: App,
return defaultResponse() return defaultResponse()
let allowedOrigin = if len(conf.restAllowOrigin) > 0 : let allowedOrigin =
some(conf.restAllowOrigin.join(",")) if len(conf.restAllowOrigin) > 0:
else: some(conf.restAllowOrigin.join(","))
none(string) else:
none(string)
let server = ? newRestHttpServer(address, port, let server =
allowedOrigin = allowedOrigin, ?newRestHttpServer(
requestErrorHandler = requestErrorHandler) address,
port,
allowedOrigin = allowedOrigin,
requestErrorHandler = requestErrorHandler,
)
## Admin REST API ## Admin REST API
if conf.restAdmin: if conf.restAdmin:
@ -364,59 +364,65 @@ proc startRestServer(app: App,
installRelayApiHandlers(server.router, app.node, cache) installRelayApiHandlers(server.router, app.node, cache)
else: else:
notInstalledTab["relay"] = "/relay endpoints are not available. Please check your configuration: --relay" notInstalledTab["relay"] =
"/relay endpoints are not available. Please check your configuration: --relay"
## Filter REST API ## Filter REST API
if conf.filternode != "" and if conf.filternode != "" and app.node.wakuFilterClient != nil and
app.node.wakuFilterClient != nil and app.node.wakuFilterClientLegacy != nil:
app.node.wakuFilterClientLegacy != nil:
let legacyFilterCache = MessageCache.init() let legacyFilterCache = MessageCache.init()
rest_legacy_filter_api.installLegacyFilterRestApiHandlers(server.router, app.node, legacyFilterCache) rest_legacy_filter_api.installLegacyFilterRestApiHandlers(
server.router, app.node, legacyFilterCache
)
let filterCache = MessageCache.init() let filterCache = MessageCache.init()
let filterDiscoHandler = let filterDiscoHandler =
if app.wakuDiscv5.isSome(): if app.wakuDiscv5.isSome():
some(defaultDiscoveryHandler(app.wakuDiscv5.get(), Filter)) some(defaultDiscoveryHandler(app.wakuDiscv5.get(), Filter))
else: none(DiscoveryHandler) else:
none(DiscoveryHandler)
rest_filter_api.installFilterRestApiHandlers( rest_filter_api.installFilterRestApiHandlers(
server.router, server.router, app.node, filterCache, filterDiscoHandler
app.node,
filterCache,
filterDiscoHandler,
) )
else: else:
notInstalledTab["filter"] = "/filter endpoints are not available. Please check your configuration: --filternode" notInstalledTab["filter"] =
"/filter endpoints are not available. Please check your configuration: --filternode"
## Store REST API ## Store REST API
let storeDiscoHandler = let storeDiscoHandler =
if app.wakuDiscv5.isSome(): if app.wakuDiscv5.isSome():
some(defaultDiscoveryHandler(app.wakuDiscv5.get(), Store)) some(defaultDiscoveryHandler(app.wakuDiscv5.get(), Store))
else: none(DiscoveryHandler) else:
none(DiscoveryHandler)
installStoreApiHandlers(server.router, app.node, storeDiscoHandler) installStoreApiHandlers(server.router, app.node, storeDiscoHandler)
## Light push API ## Light push API
if conf.lightpushnode != "" and if conf.lightpushnode != "" and app.node.wakuLightpushClient != nil:
app.node.wakuLightpushClient != nil:
let lightDiscoHandler = let lightDiscoHandler =
if app.wakuDiscv5.isSome(): if app.wakuDiscv5.isSome():
some(defaultDiscoveryHandler(app.wakuDiscv5.get(), Lightpush)) some(defaultDiscoveryHandler(app.wakuDiscv5.get(), Lightpush))
else: none(DiscoveryHandler) else:
none(DiscoveryHandler)
rest_lightpush_api.installLightPushRequestHandler(server.router, app.node, lightDiscoHandler) rest_lightpush_api.installLightPushRequestHandler(
server.router, app.node, lightDiscoHandler
)
else: else:
notInstalledTab["lightpush"] = "/lightpush endpoints are not available. Please check your configuration: --lightpushnode" notInstalledTab["lightpush"] =
"/lightpush endpoints are not available. Please check your configuration: --lightpushnode"
server.start() server.start()
info "Starting REST HTTP server", url = "http://" & $address & ":" & $port & "/" info "Starting REST HTTP server", url = "http://" & $address & ":" & $port & "/"
ok(server) ok(server)
proc startMetricsServer(serverIp: IpAddress, serverPort: Port): AppResult[MetricsHttpServerRef] = proc startMetricsServer(
info "Starting metrics HTTP server", serverIp= $serverIp, serverPort= $serverPort serverIp: IpAddress, serverPort: Port
): AppResult[MetricsHttpServerRef] =
info "Starting metrics HTTP server", serverIp = $serverIp, serverPort = $serverPort
let metricsServerRes = MetricsHttpServerRef.new($serverIp, serverPort) let metricsServerRes = MetricsHttpServerRef.new($serverIp, serverPort)
if metricsServerRes.isErr(): if metricsServerRes.isErr():
@ -428,7 +434,7 @@ proc startMetricsServer(serverIp: IpAddress, serverPort: Port): AppResult[Metric
except CatchableError: except CatchableError:
return err("metrics HTTP server start failed: " & getCurrentExceptionMsg()) return err("metrics HTTP server start failed: " & getCurrentExceptionMsg())
info "Metrics HTTP server started", serverIp= $serverIp, serverPort= $serverPort info "Metrics HTTP server started", serverIp = $serverIp, serverPort = $serverPort
ok(server) ok(server)
proc startMetricsLogging(): AppResult[void] = proc startMetricsLogging(): AppResult[void] =
@ -437,28 +443,34 @@ proc startMetricsLogging(): AppResult[void] =
proc setupMonitoringAndExternalInterfaces*(app: var App): AppResult[void] = proc setupMonitoringAndExternalInterfaces*(app: var App): AppResult[void] =
if app.conf.rest: if app.conf.rest:
let startRestServerRes = startRestServer(app, app.conf.restAddress, Port(app.conf.restPort + app.conf.portsShift), app.conf) let startRestServerRes = startRestServer(
app, app.conf.restAddress, Port(app.conf.restPort + app.conf.portsShift), app.conf
)
if startRestServerRes.isErr(): if startRestServerRes.isErr():
error "Starting REST server failed. Continuing in current state.", error=startRestServerRes.error error "Starting REST server failed. Continuing in current state.",
error = startRestServerRes.error
else: else:
app.restServer = some(startRestServerRes.value) app.restServer = some(startRestServerRes.value)
if app.conf.metricsServer: if app.conf.metricsServer:
let startMetricsServerRes = startMetricsServer(app.conf.metricsServerAddress, Port(app.conf.metricsServerPort + app.conf.portsShift)) let startMetricsServerRes = startMetricsServer(
app.conf.metricsServerAddress,
Port(app.conf.metricsServerPort + app.conf.portsShift),
)
if startMetricsServerRes.isErr(): if startMetricsServerRes.isErr():
error "Starting metrics server failed. Continuing in current state.", error=startMetricsServerRes.error error "Starting metrics server failed. Continuing in current state.",
error = startMetricsServerRes.error
else: else:
app.metricsServer = some(startMetricsServerRes.value) app.metricsServer = some(startMetricsServerRes.value)
if app.conf.metricsLogging: if app.conf.metricsLogging:
let startMetricsLoggingRes = startMetricsLogging() let startMetricsLoggingRes = startMetricsLogging()
if startMetricsLoggingRes.isErr(): if startMetricsLoggingRes.isErr():
error "Starting metrics console logging failed. Continuing in current state.", error=startMetricsLoggingRes.error error "Starting metrics console logging failed. Continuing in current state.",
error = startMetricsLoggingRes.error
ok() ok()
# App shutdown # App shutdown
proc stop*(app: App): Future[void] {.async: (raises: [Exception]).} = proc stop*(app: App): Future[void] {.async: (raises: [Exception]).} =

View File

@ -33,13 +33,13 @@ proc TheWakuNetworkConf*(T: type ClusterConf): ClusterConf =
pubsubTopics: pubsubTopics:
@[ @[
"/waku/2/rs/1/0", "/waku/2/rs/1/1", "/waku/2/rs/1/2", "/waku/2/rs/1/3", "/waku/2/rs/1/0", "/waku/2/rs/1/1", "/waku/2/rs/1/2", "/waku/2/rs/1/3",
"/waku/2/rs/1/4", "/waku/2/rs/1/5", "/waku/2/rs/1/6", "/waku/2/rs/1/7" "/waku/2/rs/1/4", "/waku/2/rs/1/5", "/waku/2/rs/1/6", "/waku/2/rs/1/7",
], ],
discv5Discovery: true, discv5Discovery: true,
discv5BootstrapNodes: discv5BootstrapNodes:
@[ @[
"enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Ugl_r25UHQJ3f1rIRrpzxJXSMaJe4yk1XFSAYJpZIJ2NIJpcISygI2rim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQJATXRSRSUyTw_QLB6H_U3oziVQgNRgrXpK7wp2AMyNxYN0Y3CCdl-DdWRwgiMohXdha3UyDw", "enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Ugl_r25UHQJ3f1rIRrpzxJXSMaJe4yk1XFSAYJpZIJ2NIJpcISygI2rim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQJATXRSRSUyTw_QLB6H_U3oziVQgNRgrXpK7wp2AMyNxYN0Y3CCdl-DdWRwgiMohXdha3UyDw",
"enr:-QEkuECnZ3IbVAgkOzv-QLnKC4dRKAPRY80m1-R7G8jZ7yfT3ipEfBrhKN7ARcQgQ-vg-h40AQzyvAkPYlHPaFKk6u9uAYJpZIJ2NIJpcIQiEAFDim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQMIJwesBVgUiBCi8yiXGx7RWylBQkYm1U9dvEy-neLG2YN0Y3CCdl-DdWRwgiMohXdha3UyDw", "enr:-QEkuECnZ3IbVAgkOzv-QLnKC4dRKAPRY80m1-R7G8jZ7yfT3ipEfBrhKN7ARcQgQ-vg-h40AQzyvAkPYlHPaFKk6u9uAYJpZIJ2NIJpcIQiEAFDim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQMIJwesBVgUiBCi8yiXGx7RWylBQkYm1U9dvEy-neLG2YN0Y3CCdl-DdWRwgiMohXdha3UyDw",
"enr:-QEkuEDzQyIAhs-CgBHIrJqtBv3EY1uP1Psrc-y8yJKsmxW7dh3DNcq2ergMUWSFVcJNlfcgBeVsFPkgd_QopRIiCV2pAYJpZIJ2NIJpcIQI2ttrim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwA2Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQJIN4qwz3v4r2Q8Bv8zZD0eqBcKw6bdLvdkV7-JLjqIj4N0Y3CCdl-DdWRwgiMohXdha3UyDw" "enr:-QEkuEDzQyIAhs-CgBHIrJqtBv3EY1uP1Psrc-y8yJKsmxW7dh3DNcq2ergMUWSFVcJNlfcgBeVsFPkgd_QopRIiCV2pAYJpZIJ2NIJpcIQI2ttrim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwA2Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQJIN4qwz3v4r2Q8Bv8zZD0eqBcKw6bdLvdkV7-JLjqIj4N0Y3CCdl-DdWRwgiMohXdha3UyDw",
], ],
) )

View File

@ -32,12 +32,10 @@ proc logConfig(conf: WakuNodeConf) =
lightpush = conf.lightpush, lightpush = conf.lightpush,
peerExchange = conf.peerExchange peerExchange = conf.peerExchange
info "Configuration. Network", info "Configuration. Network", cluster = conf.clusterId, maxPeers = conf.maxRelayPeers
cluster = conf.clusterId,
maxPeers = conf.maxRelayPeers
for shard in conf.pubsubTopics: for shard in conf.pubsubTopics:
info "Configuration. Shards", shard=shard info "Configuration. Shards", shard = shard
for i in conf.discv5BootstrapNodes: for i in conf.discv5BootstrapNodes:
info "Configuration. Bootstrap nodes", node = i info "Configuration. Bootstrap nodes", node = i

View File

@ -1,11 +1,7 @@
## Example showing how a resource restricted client may ## Example showing how a resource restricted client may
## subscribe to messages without relay ## subscribe to messages without relay
import import chronicles, chronos, stew/byteutils, stew/results
chronicles,
chronos,
stew/byteutils,
stew/results
import import
../../../waku/common/logging, ../../../waku/common/logging,
../../../waku/node/peer_manager, ../../../waku/node/peer_manager,
@ -13,34 +9,42 @@ import
../../../waku/waku_filter_v2/client ../../../waku/waku_filter_v2/client
const const
FilterPeer = "/ip4/104.154.239.128/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS" # node-01.gc-us-central1-a.wakuv2.test.statusim.net on wakuv2.test FilterPeer =
"/ip4/104.154.239.128/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS"
# node-01.gc-us-central1-a.wakuv2.test.statusim.net on wakuv2.test
FilterPubsubTopic = PubsubTopic("/waku/2/default-waku/proto") FilterPubsubTopic = PubsubTopic("/waku/2/default-waku/proto")
FilterContentTopic = ContentTopic("/examples/1/light-pubsub-example/proto") FilterContentTopic = ContentTopic("/examples/1/light-pubsub-example/proto")
proc unsubscribe(wfc: WakuFilterClient, proc unsubscribe(
filterPeer: RemotePeerInfo, wfc: WakuFilterClient,
filterPubsubTopic: PubsubTopic, filterPeer: RemotePeerInfo,
filterContentTopic: ContentTopic) {.async.} = filterPubsubTopic: PubsubTopic,
filterContentTopic: ContentTopic,
) {.async.} =
notice "unsubscribing from filter" notice "unsubscribing from filter"
let unsubscribeRes = await wfc.unsubscribe(filterPeer, filterPubsubTopic, @[filterContentTopic]) let unsubscribeRes =
await wfc.unsubscribe(filterPeer, filterPubsubTopic, @[filterContentTopic])
if unsubscribeRes.isErr: if unsubscribeRes.isErr:
notice "unsubscribe request failed", err=unsubscribeRes.error notice "unsubscribe request failed", err = unsubscribeRes.error
else: else:
notice "unsubscribe request successful" notice "unsubscribe request successful"
proc messagePushHandler(pubsubTopic: PubsubTopic, message: WakuMessage) proc messagePushHandler(
{.async, gcsafe.} = pubsubTopic: PubsubTopic, message: WakuMessage
) {.async, gcsafe.} =
let payloadStr = string.fromBytes(message.payload) let payloadStr = string.fromBytes(message.payload)
notice "message received", payload=payloadStr, notice "message received",
pubsubTopic=pubsubTopic, payload = payloadStr,
contentTopic=message.contentTopic, pubsubTopic = pubsubTopic,
timestamp=message.timestamp contentTopic = message.contentTopic,
timestamp = message.timestamp
proc maintainSubscription(
proc maintainSubscription(wfc: WakuFilterClient, wfc: WakuFilterClient,
filterPeer: RemotePeerInfo, filterPeer: RemotePeerInfo,
filterPubsubTopic: PubsubTopic, filterPubsubTopic: PubsubTopic,
filterContentTopic: ContentTopic) {.async.} = filterContentTopic: ContentTopic,
) {.async.} =
while true: while true:
notice "maintaining subscription" notice "maintaining subscription"
# First use filter-ping to check if we have an active subscription # First use filter-ping to check if we have an active subscription
@ -49,10 +53,11 @@ proc maintainSubscription(wfc: WakuFilterClient,
# No subscription found. Let's subscribe. # No subscription found. Let's subscribe.
notice "no subscription found. Sending subscribe request" notice "no subscription found. Sending subscribe request"
let subscribeRes = await wfc.subscribe(filterPeer, filterPubsubTopic, @[filterContentTopic]) let subscribeRes =
await wfc.subscribe(filterPeer, filterPubsubTopic, @[filterContentTopic])
if subscribeRes.isErr(): if subscribeRes.isErr():
notice "subscribe request failed. Quitting.", err=subscribeRes.error notice "subscribe request failed. Quitting.", err = subscribeRes.error
break break
else: else:
notice "subscribe request successful." notice "subscribe request successful."
@ -78,7 +83,9 @@ proc setupAndSubscribe(rng: ref HmacDrbgContext) =
wfc.registerPushHandler(messagePushHandler) wfc.registerPushHandler(messagePushHandler)
# Start maintaining subscription # Start maintaining subscription
asyncSpawn maintainSubscription(wfc, filterPeer, FilterPubsubTopic, FilterContentTopic) asyncSpawn maintainSubscription(
wfc, filterPeer, FilterPubsubTopic, FilterContentTopic
)
when isMainModule: when isMainModule:
let rng = newRng() let rng = newRng()

View File

@ -1,11 +1,7 @@
## Example showing how a resource restricted client may ## Example showing how a resource restricted client may
## use lightpush to publish messages without relay ## use lightpush to publish messages without relay
import import chronicles, chronos, stew/byteutils, stew/results
chronicles,
chronos,
stew/byteutils,
stew/results
import import
../../../waku/common/logging, ../../../waku/common/logging,
../../../waku/node/peer_manager, ../../../waku/node/peer_manager,
@ -13,27 +9,33 @@ import
../../../waku/waku_lightpush/client ../../../waku/waku_lightpush/client
const const
LightpushPeer = "/ip4/134.209.139.210/tcp/30303/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ" # node-01.do-ams3.wakuv2.test.statusim.net on wakuv2.test LightpushPeer =
"/ip4/134.209.139.210/tcp/30303/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ"
# node-01.do-ams3.wakuv2.test.statusim.net on wakuv2.test
LightpushPubsubTopic = PubsubTopic("/waku/2/default-waku/proto") LightpushPubsubTopic = PubsubTopic("/waku/2/default-waku/proto")
LightpushContentTopic = ContentTopic("/examples/1/light-pubsub-example/proto") LightpushContentTopic = ContentTopic("/examples/1/light-pubsub-example/proto")
proc publishMessages(wlc: WakuLightpushClient, proc publishMessages(
lightpushPeer: RemotePeerInfo, wlc: WakuLightpushClient,
lightpushPubsubTopic: PubsubTopic, lightpushPeer: RemotePeerInfo,
lightpushContentTopic: ContentTopic) {.async.} = lightpushPubsubTopic: PubsubTopic,
lightpushContentTopic: ContentTopic,
) {.async.} =
while true: while true:
let text = "hi there i'm a lightpush publisher" let text = "hi there i'm a lightpush publisher"
let message = WakuMessage(payload: toBytes(text), # content of the message let message = WakuMessage(
contentTopic: lightpushContentTopic, # content topic to publish to payload: toBytes(text), # content of the message
ephemeral: true, # tell store nodes to not store it contentTopic: lightpushContentTopic, # content topic to publish to
timestamp: getNowInNanosecondTime()) # current timestamp ephemeral: true, # tell store nodes to not store it
timestamp: getNowInNanosecondTime(),
) # current timestamp
let wlpRes = await wlc.publish(lightpushPubsubTopic, message, lightpushPeer) let wlpRes = await wlc.publish(lightpushPubsubTopic, message, lightpushPeer)
if wlpRes.isOk(): if wlpRes.isOk():
notice "published message using lightpush", message=message notice "published message using lightpush", message = message
else: else:
notice "failed to publish message using lightpush", err=wlpRes.error() notice "failed to publish message using lightpush", err = wlpRes.error()
await sleepAsync(5000) # Publish every 5 seconds await sleepAsync(5000) # Publish every 5 seconds
@ -49,7 +51,9 @@ proc setupAndPublish(rng: ref HmacDrbgContext) =
wlc = WakuLightpushClient.new(pm, rng) wlc = WakuLightpushClient.new(pm, rng)
# Start maintaining subscription # Start maintaining subscription
asyncSpawn publishMessages(wlc, lightpushPeer, LightpushPubsubTopic, LightpushContentTopic) asyncSpawn publishMessages(
wlc, lightpushPeer, LightpushPubsubTopic, LightpushContentTopic
)
when isMainModule: when isMainModule:
let rng = newRng() let rng = newRng()

View File

@ -1,5 +1,5 @@
import import
std/[tables,times,sequtils], std/[tables, times, sequtils],
stew/byteutils, stew/byteutils,
stew/shims/net, stew/shims/net,
chronicles, chronicles,
@ -23,103 +23,113 @@ proc now*(): Timestamp =
# An accesible bootstrap node. See wakuv2.prod fleets.status.im # An accesible bootstrap node. See wakuv2.prod fleets.status.im
const bootstrapNode =
const bootstrapNode = "enr:-Nm4QOdTOKZJKTUUZ4O_W932CXIET-M9NamewDnL78P5u9D" & "enr:-Nm4QOdTOKZJKTUUZ4O_W932CXIET-M9NamewDnL78P5u9D" &
"OGnZlK0JFZ4k0inkfe6iY-0JAaJVovZXc575VV3njeiABgmlkgn" & "OGnZlK0JFZ4k0inkfe6iY-0JAaJVovZXc575VV3njeiABgmlkgn" &
"Y0gmlwhAjS3ueKbXVsdGlhZGRyc7g6ADg2MW5vZGUtMDEuYWMtY" & "Y0gmlwhAjS3ueKbXVsdGlhZGRyc7g6ADg2MW5vZGUtMDEuYWMtY" &
"24taG9uZ2tvbmctYy53YWt1djIucHJvZC5zdGF0dXNpbS5uZXQG" & "24taG9uZ2tvbmctYy53YWt1djIucHJvZC5zdGF0dXNpbS5uZXQG" &
"H0DeA4lzZWNwMjU2azGhAo0C-VvfgHiXrxZi3umDiooXMGY9FvY" & "H0DeA4lzZWNwMjU2azGhAo0C-VvfgHiXrxZi3umDiooXMGY9FvY" &
"j5_d1Q4EeS7eyg3RjcIJ2X4N1ZHCCIyiFd2FrdTIP" "j5_d1Q4EeS7eyg3RjcIJ2X4N1ZHCCIyiFd2FrdTIP"
# careful if running pub and sub in the same machine # careful if running pub and sub in the same machine
const wakuPort = 60000 const wakuPort = 60000
const discv5Port = 9000 const discv5Port = 9000
proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} = proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} =
# use notice to filter all waku messaging # use notice to filter all waku messaging
setupLogLevel(logging.LogLevel.NOTICE) setupLogLevel(logging.LogLevel.NOTICE)
notice "starting publisher", wakuPort=wakuPort, discv5Port=discv5Port notice "starting publisher", wakuPort = wakuPort, discv5Port = discv5Port
let let
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get() nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get()
ip = parseIpAddress("0.0.0.0") ip = parseIpAddress("0.0.0.0")
flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) flags = CapabilitiesBitfield.init(
lightpush = false, filter = false, store = false, relay = true
var enrBuilder = EnrBuilder.init(nodeKey)
let recordRes = enrBuilder.build()
let record =
if recordRes.isErr():
error "failed to create enr record", error=recordRes.error
quit(QuitFailure)
else: recordRes.get()
var builder = WakuNodeBuilder.init()
builder.withNodeKey(nodeKey)
builder.withRecord(record)
builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
let node = builder.build().tryGet()
var bootstrapNodeEnr: enr.Record
discard bootstrapNodeEnr.fromURI(bootstrapNode)
let discv5Conf = WakuDiscoveryV5Config(
discv5Config: none(DiscoveryConfig),
address: ip,
port: Port(discv5Port),
privateKey: keys.PrivateKey(nodeKey.skkey),
bootstrapRecords: @[bootstrapNodeEnr],
autoupdateRecord: true,
) )
# assumes behind a firewall, so not care about being discoverable var enrBuilder = EnrBuilder.init(nodeKey)
let wakuDiscv5 = WakuDiscoveryV5.new(
node.rng,
discv5Conf,
some(node.enr),
some(node.peerManager),
node.topicSubscriptionQueue,
)
await node.start() let recordRes = enrBuilder.build()
await node.mountRelay() let record =
node.peerManager.start() if recordRes.isErr():
error "failed to create enr record", error = recordRes.error
quit(QuitFailure)
else:
recordRes.get()
(await wakuDiscv5.start()).isOkOr: var builder = WakuNodeBuilder.init()
error "failed to start discv5", error = error builder.withNodeKey(nodeKey)
quit(1) builder.withRecord(record)
builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
let node = builder.build().tryGet()
# wait for a minimum of peers to be connected, otherwise messages wont be gossiped var bootstrapNodeEnr: enr.Record
while true: discard bootstrapNodeEnr.fromURI(bootstrapNode)
let numConnectedPeers = node.peerManager.peerStore[ConnectionBook].book.values().countIt(it == Connected)
if numConnectedPeers >= 6:
notice "publisher is ready", connectedPeers=numConnectedPeers, required=6
break
notice "waiting to be ready", connectedPeers=numConnectedPeers, required=6
await sleepAsync(5000)
# Make sure it matches the publisher. Use default value let discv5Conf = WakuDiscoveryV5Config(
# see spec: https://rfc.vac.dev/spec/23/ discv5Config: none(DiscoveryConfig),
let pubSubTopic = PubsubTopic("/waku/2/default-waku/proto") address: ip,
port: Port(discv5Port),
privateKey: keys.PrivateKey(nodeKey.skkey),
bootstrapRecords: @[bootstrapNodeEnr],
autoupdateRecord: true,
)
# any content topic can be chosen # assumes behind a firewall, so not care about being discoverable
let contentTopic = ContentTopic("/examples/1/pubsub-example/proto") let wakuDiscv5 = WakuDiscoveryV5.new(
node.rng,
discv5Conf,
some(node.enr),
some(node.peerManager),
node.topicSubscriptionQueue,
)
notice "publisher service started" await node.start()
while true: await node.mountRelay()
let text = "hi there i'm a publisher" node.peerManager.start()
let message = WakuMessage(payload: toBytes(text), # content of the message
contentTopic: contentTopic, # content topic to publish to
ephemeral: true, # tell store nodes to not store it
timestamp: now()) # current timestamp
let res = await node.publish(some(pubSubTopic), message) (await wakuDiscv5.start()).isOkOr:
error "failed to start discv5", error = error
quit(1)
if res.isOk: # wait for a minimum of peers to be connected, otherwise messages wont be gossiped
notice "published message", text = text, timestamp = message.timestamp, psTopic = pubSubTopic, contentTopic = contentTopic while true:
else: let numConnectedPeers =
error "failed to publish message", error = res.error node.peerManager.peerStore[ConnectionBook].book.values().countIt(it == Connected)
if numConnectedPeers >= 6:
notice "publisher is ready", connectedPeers = numConnectedPeers, required = 6
break
notice "waiting to be ready", connectedPeers = numConnectedPeers, required = 6
await sleepAsync(5000)
await sleepAsync(5000) # Make sure it matches the publisher. Use default value
# see spec: https://rfc.vac.dev/spec/23/
let pubSubTopic = PubsubTopic("/waku/2/default-waku/proto")
# any content topic can be chosen
let contentTopic = ContentTopic("/examples/1/pubsub-example/proto")
notice "publisher service started"
while true:
let text = "hi there i'm a publisher"
let message = WakuMessage(
payload: toBytes(text), # content of the message
contentTopic: contentTopic, # content topic to publish to
ephemeral: true, # tell store nodes to not store it
timestamp: now(),
) # current timestamp
let res = await node.publish(some(pubSubTopic), message)
if res.isOk:
notice "published message",
text = text,
timestamp = message.timestamp,
psTopic = pubSubTopic,
contentTopic = contentTopic
else:
error "failed to publish message", error = res.error
await sleepAsync(5000)
when isMainModule: when isMainModule:
let rng = crypto.newRng() let rng = crypto.newRng()

View File

@ -19,94 +19,100 @@ import
../../../waku/factory/builder ../../../waku/factory/builder
# An accesible bootstrap node. See wakuv2.prod fleets.status.im # An accesible bootstrap node. See wakuv2.prod fleets.status.im
const bootstrapNode = "enr:-Nm4QOdTOKZJKTUUZ4O_W932CXIET-M9NamewDnL78P5u9DOGnZl" & const bootstrapNode =
"K0JFZ4k0inkfe6iY-0JAaJVovZXc575VV3njeiABgmlkgnY0gmlwhAjS" & "enr:-Nm4QOdTOKZJKTUUZ4O_W932CXIET-M9NamewDnL78P5u9DOGnZl" &
"3ueKbXVsdGlhZGRyc7g6ADg2MW5vZGUtMDEuYWMtY24taG9uZ2tvbmct" & "K0JFZ4k0inkfe6iY-0JAaJVovZXc575VV3njeiABgmlkgnY0gmlwhAjS" &
"Yy53YWt1djIucHJvZC5zdGF0dXNpbS5uZXQGH0DeA4lzZWNwMjU2azGh" & "3ueKbXVsdGlhZGRyc7g6ADg2MW5vZGUtMDEuYWMtY24taG9uZ2tvbmct" &
"Ao0C-VvfgHiXrxZi3umDiooXMGY9FvYj5_d1Q4EeS7eyg3RjcIJ2X4N1" & "Yy53YWt1djIucHJvZC5zdGF0dXNpbS5uZXQGH0DeA4lzZWNwMjU2azGh" &
"ZHCCIyiFd2FrdTIP" "Ao0C-VvfgHiXrxZi3umDiooXMGY9FvYj5_d1Q4EeS7eyg3RjcIJ2X4N1" & "ZHCCIyiFd2FrdTIP"
# careful if running pub and sub in the same machine # careful if running pub and sub in the same machine
const wakuPort = 50000 const wakuPort = 50000
const discv5Port = 8000 const discv5Port = 8000
proc setupAndSubscribe(rng: ref HmacDrbgContext) {.async.} = proc setupAndSubscribe(rng: ref HmacDrbgContext) {.async.} =
# use notice to filter all waku messaging # use notice to filter all waku messaging
setupLogLevel(logging.LogLevel.NOTICE) setupLogLevel(logging.LogLevel.NOTICE)
notice "starting subscriber", wakuPort=wakuPort, discv5Port=discv5Port notice "starting subscriber", wakuPort = wakuPort, discv5Port = discv5Port
let let
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
ip = parseIpAddress("0.0.0.0") ip = parseIpAddress("0.0.0.0")
flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) flags = CapabilitiesBitfield.init(
lightpush = false, filter = false, store = false, relay = true
var enrBuilder = EnrBuilder.init(nodeKey)
let recordRes = enrBuilder.build()
let record =
if recordRes.isErr():
error "failed to create enr record", error=recordRes.error
quit(QuitFailure)
else: recordRes.get()
var builder = WakuNodeBuilder.init()
builder.withNodeKey(nodeKey)
builder.withRecord(record)
builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
let node = builder.build().tryGet()
var bootstrapNodeEnr: enr.Record
discard bootstrapNodeEnr.fromURI(bootstrapNode)
let discv5Conf = WakuDiscoveryV5Config(
discv5Config: none(DiscoveryConfig),
address: ip,
port: Port(discv5Port),
privateKey: keys.PrivateKey(nodeKey.skkey),
bootstrapRecords: @[bootstrapNodeEnr],
autoupdateRecord: true,
) )
# assumes behind a firewall, so not care about being discoverable var enrBuilder = EnrBuilder.init(nodeKey)
let wakuDiscv5 = WakuDiscoveryV5.new(
node.rng,
discv5Conf,
some(node.enr),
some(node.peerManager),
node.topicSubscriptionQueue,
)
await node.start() let recordRes = enrBuilder.build()
await node.mountRelay() let record =
node.peerManager.start() if recordRes.isErr():
error "failed to create enr record", error = recordRes.error
quit(QuitFailure)
else:
recordRes.get()
(await wakuDiscv5.start()).isOkOr: var builder = WakuNodeBuilder.init()
error "failed to start discv5", error = error builder.withNodeKey(nodeKey)
quit(1) builder.withRecord(record)
builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
let node = builder.build().tryGet()
# wait for a minimum of peers to be connected, otherwise messages wont be gossiped var bootstrapNodeEnr: enr.Record
while true: discard bootstrapNodeEnr.fromURI(bootstrapNode)
let numConnectedPeers = node.peerManager.peerStore[ConnectionBook].book.values().countIt(it == Connected)
if numConnectedPeers >= 6:
notice "subscriber is ready", connectedPeers=numConnectedPeers, required=6
break
notice "waiting to be ready", connectedPeers=numConnectedPeers, required=6
await sleepAsync(5000)
# Make sure it matches the publisher. Use default value let discv5Conf = WakuDiscoveryV5Config(
# see spec: https://rfc.vac.dev/spec/23/ discv5Config: none(DiscoveryConfig),
let pubSubTopic = PubsubTopic("/waku/2/default-waku/proto") address: ip,
port: Port(discv5Port),
privateKey: keys.PrivateKey(nodeKey.skkey),
bootstrapRecords: @[bootstrapNodeEnr],
autoupdateRecord: true,
)
# any content topic can be chosen. make sure it matches the publisher # assumes behind a firewall, so not care about being discoverable
let contentTopic = ContentTopic("/examples/1/pubsub-example/proto") let wakuDiscv5 = WakuDiscoveryV5.new(
node.rng,
discv5Conf,
some(node.enr),
some(node.peerManager),
node.topicSubscriptionQueue,
)
proc handler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} = await node.start()
let payloadStr = string.fromBytes(msg.payload) await node.mountRelay()
if msg.contentTopic == contentTopic: node.peerManager.start()
notice "message received", payload=payloadStr,
pubsubTopic=pubsubTopic, (await wakuDiscv5.start()).isOkOr:
contentTopic=msg.contentTopic, error "failed to start discv5", error = error
timestamp=msg.timestamp quit(1)
node.subscribe((kind: PubsubSub, topic: pubsubTopic), some(handler))
# wait for a minimum of peers to be connected, otherwise messages wont be gossiped
while true:
let numConnectedPeers =
node.peerManager.peerStore[ConnectionBook].book.values().countIt(it == Connected)
if numConnectedPeers >= 6:
notice "subscriber is ready", connectedPeers = numConnectedPeers, required = 6
break
notice "waiting to be ready", connectedPeers = numConnectedPeers, required = 6
await sleepAsync(5000)
# Make sure it matches the publisher. Use default value
# see spec: https://rfc.vac.dev/spec/23/
let pubSubTopic = PubsubTopic("/waku/2/default-waku/proto")
# any content topic can be chosen. make sure it matches the publisher
let contentTopic = ContentTopic("/examples/1/pubsub-example/proto")
proc handler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} =
let payloadStr = string.fromBytes(msg.payload)
if msg.contentTopic == contentTopic:
notice "message received",
payload = payloadStr,
pubsubTopic = pubsubTopic,
contentTopic = msg.contentTopic,
timestamp = msg.timestamp
node.subscribe((kind: PubsubSub, topic: pubsubTopic), some(handler))
when isMainModule: when isMainModule:
let rng = crypto.newRng() let rng = crypto.newRng()

View File

@ -30,32 +30,47 @@ type CKeyPair* = object
private_key: CFr private_key: CFr
public_key: CG1Projective public_key: CG1Projective
proc drop_ffi_derive_public_key*(ptrx: ptr CReturn[CG1Projective]) {.importc: "drop_ffi_derive_public_key".} proc drop_ffi_derive_public_key*(
ptrx: ptr CReturn[CG1Projective]
) {.importc: "drop_ffi_derive_public_key".}
proc drop_ffi_generate_random_fr*(ptrx: ptr CReturn[CFr]) {.importc: "drop_ffi_generate_random_fr".} proc drop_ffi_generate_random_fr*(
ptrx: ptr CReturn[CFr]
) {.importc: "drop_ffi_generate_random_fr".}
proc drop_ffi_generate_stealth_commitment*(ptrx: ptr CReturn[CStealthCommitment]) {.importc: "drop_ffi_generate_stealth_commitment".} proc drop_ffi_generate_stealth_commitment*(
ptrx: ptr CReturn[CStealthCommitment]
) {.importc: "drop_ffi_generate_stealth_commitment".}
proc drop_ffi_generate_stealth_private_key*(ptrx: ptr CReturn[CFr]) {.importc: "drop_ffi_generate_stealth_private_key".} proc drop_ffi_generate_stealth_private_key*(
ptrx: ptr CReturn[CFr]
) {.importc: "drop_ffi_generate_stealth_private_key".}
proc drop_ffi_random_keypair*(ptrx: ptr CReturn[CKeyPair]) {.importc: "drop_ffi_random_keypair".} proc drop_ffi_random_keypair*(
ptrx: ptr CReturn[CKeyPair]
) {.importc: "drop_ffi_random_keypair".}
proc ffi_derive_public_key*(private_key: ptr CFr): (ptr CReturn[CG1Projective]) {.importc: "ffi_derive_public_key".} proc ffi_derive_public_key*(
private_key: ptr CFr
): (ptr CReturn[CG1Projective]) {.importc: "ffi_derive_public_key".}
proc ffi_generate_random_fr*(): (ptr CReturn[CFr]) {.importc: "ffi_generate_random_fr".} proc ffi_generate_random_fr*(): (ptr CReturn[CFr]) {.importc: "ffi_generate_random_fr".}
proc ffi_generate_stealth_commitment*(viewing_public_key: ptr CG1Projective, proc ffi_generate_stealth_commitment*(
spending_public_key: ptr CG1Projective, viewing_public_key: ptr CG1Projective,
ephemeral_private_key: ptr CFr): (ptr CReturn[CStealthCommitment]) {.importc: "ffi_generate_stealth_commitment".} spending_public_key: ptr CG1Projective,
ephemeral_private_key: ptr CFr,
): (ptr CReturn[CStealthCommitment]) {.importc: "ffi_generate_stealth_commitment".}
proc ffi_generate_stealth_private_key*(ephemeral_public_key: ptr CG1Projective, proc ffi_generate_stealth_private_key*(
spending_key: ptr CFr, ephemeral_public_key: ptr CG1Projective,
viewing_key: ptr CFr, spending_key: ptr CFr,
view_tag: ptr uint64): (ptr CReturn[CFr]) {.importc: "ffi_generate_stealth_private_key".} viewing_key: ptr CFr,
view_tag: ptr uint64,
): (ptr CReturn[CFr]) {.importc: "ffi_generate_stealth_private_key".}
proc ffi_random_keypair*(): (ptr CReturn[CKeyPair]) {.importc: "ffi_random_keypair".} proc ffi_random_keypair*(): (ptr CReturn[CKeyPair]) {.importc: "ffi_random_keypair".}
## Nim wrappers and types for the ERC-5564-BN254 module ## Nim wrappers and types for the ERC-5564-BN254 module
type FFIResult[T] = Result[T, string] type FFIResult[T] = Result[T, string]
@ -64,9 +79,11 @@ type G1Projective = array[32, uint8]
type KeyPair* = object type KeyPair* = object
private_key*: Fr private_key*: Fr
public_key*: G1Projective public_key*: G1Projective
type StealthCommitment* = object type StealthCommitment* = object
stealth_commitment*: G1Projective stealth_commitment*: G1Projective
view_tag*: uint64 view_tag*: uint64
type PrivateKey* = Fr type PrivateKey* = Fr
type PublicKey* = G1Projective type PublicKey* = G1Projective
@ -88,13 +105,18 @@ proc generateKeypair*(): FFIResult[KeyPair] =
drop_ffi_random_keypair(res_ptr) drop_ffi_random_keypair(res_ptr)
return err("Error generating random keypair: " & $res_value.err_code) return err("Error generating random keypair: " & $res_value.err_code)
let ret = KeyPair(private_key: res_value.value.private_key.x0, public_key: res_value.value.public_key.x0) let ret = KeyPair(
private_key: res_value.value.private_key.x0,
public_key: res_value.value.public_key.x0,
)
drop_ffi_random_keypair(res_ptr) drop_ffi_random_keypair(res_ptr)
return ok(ret) return ok(ret)
proc generateStealthCommitment*(viewing_public_key: G1Projective, proc generateStealthCommitment*(
spending_public_key: G1Projective, viewing_public_key: G1Projective,
ephemeral_private_key: Fr): FFIResult[StealthCommitment] = spending_public_key: G1Projective,
ephemeral_private_key: Fr,
): FFIResult[StealthCommitment] =
let viewing_public_key = CG1Projective(x0: viewing_public_key) let viewing_public_key = CG1Projective(x0: viewing_public_key)
let viewing_public_key_ptr = unsafeAddr(viewing_public_key) let viewing_public_key_ptr = unsafeAddr(viewing_public_key)
let spending_public_key = CG1Projective(x0: spending_public_key) let spending_public_key = CG1Projective(x0: spending_public_key)
@ -102,20 +124,29 @@ proc generateStealthCommitment*(viewing_public_key: G1Projective,
let ephemeral_private_key = CFr(x0: ephemeral_private_key) let ephemeral_private_key = CFr(x0: ephemeral_private_key)
let ephemeral_private_key_ptr = unsafeAddr(ephemeral_private_key) let ephemeral_private_key_ptr = unsafeAddr(ephemeral_private_key)
let res_ptr = (ffi_generate_stealth_commitment(viewing_public_key_ptr, spending_public_key_ptr, ephemeral_private_key_ptr)) let res_ptr = (
ffi_generate_stealth_commitment(
viewing_public_key_ptr, spending_public_key_ptr, ephemeral_private_key_ptr
)
)
let res_value = res_ptr[] let res_value = res_ptr[]
if res_value.err_code != 0: if res_value.err_code != 0:
drop_ffi_generate_stealth_commitment(res_ptr) drop_ffi_generate_stealth_commitment(res_ptr)
return err("Error generating stealth commitment: " & $res_value.err_code) return err("Error generating stealth commitment: " & $res_value.err_code)
let ret = StealthCommitment(stealth_commitment: res_value.value.stealth_commitment.x0, view_tag: res_value.value.view_tag) let ret = StealthCommitment(
stealth_commitment: res_value.value.stealth_commitment.x0,
view_tag: res_value.value.view_tag,
)
drop_ffi_generate_stealth_commitment(res_ptr) drop_ffi_generate_stealth_commitment(res_ptr)
return ok(ret) return ok(ret)
proc generateStealthPrivateKey*(ephemeral_public_key: G1Projective, proc generateStealthPrivateKey*(
spending_key: Fr, ephemeral_public_key: G1Projective,
viewing_key: Fr, spending_key: Fr,
view_tag: uint64): FFIResult[Fr] = viewing_key: Fr,
view_tag: uint64,
): FFIResult[Fr] =
let ephemeral_public_key = CG1Projective(x0: ephemeral_public_key) let ephemeral_public_key = CG1Projective(x0: ephemeral_public_key)
let ephemeral_public_key_ptr = unsafeAddr(ephemeral_public_key) let ephemeral_public_key_ptr = unsafeAddr(ephemeral_public_key)
let spending_key = CFr(x0: spending_key) let spending_key = CFr(x0: spending_key)
@ -124,7 +155,11 @@ proc generateStealthPrivateKey*(ephemeral_public_key: G1Projective,
let viewing_key_ptr = unsafeAddr(viewing_key) let viewing_key_ptr = unsafeAddr(viewing_key)
let view_tag_ptr = unsafeAddr(view_tag) let view_tag_ptr = unsafeAddr(view_tag)
let res_ptr = (ffi_generate_stealth_private_key(ephemeral_public_key_ptr, spending_key_ptr, viewing_key_ptr, view_tag_ptr)) let res_ptr = (
ffi_generate_stealth_private_key(
ephemeral_public_key_ptr, spending_key_ptr, viewing_key_ptr, view_tag_ptr
)
)
let res_value = res_ptr[] let res_value = res_ptr[]
if res_value.err_code != 0: if res_value.err_code != 0:
drop_ffi_generate_stealth_private_key(res_ptr) drop_ffi_generate_stealth_private_key(res_ptr)

View File

@ -15,19 +15,8 @@ import
libp2p/crypto/crypto libp2p/crypto/crypto
export export
networks_config, networks_config, app, logging, options, strutils, os, sequtils, stewNet, chronicles,
app, chronos, metrics, libbacktrace, crypto
logging,
options,
strutils,
os,
sequtils,
stewNet,
chronicles,
chronos,
metrics,
libbacktrace,
crypto
proc setup*(): App = proc setup*(): App =
const versionString = "version / git commit hash: " & app.git_version const versionString = "version / git commit hash: " & app.git_version

View File

@ -12,9 +12,7 @@ import
./node_spec, ./node_spec,
./wire_spec ./wire_spec
export export wire_spec, logging
wire_spec,
logging
type StealthCommitmentProtocol* = object type StealthCommitmentProtocol* = object
wakuApp: App wakuApp: App
@ -22,28 +20,36 @@ type StealthCommitmentProtocol* = object
spendingKeyPair: StealthCommitmentFFI.KeyPair spendingKeyPair: StealthCommitmentFFI.KeyPair
viewingKeyPair: StealthCommitmentFFI.KeyPair viewingKeyPair: StealthCommitmentFFI.KeyPair
proc deserialize(T: type StealthCommitmentFFI.PublicKey, v: SerializedKey): Result[T, string] = proc deserialize(
T: type StealthCommitmentFFI.PublicKey, v: SerializedKey
): Result[T, string] =
# deserialize seq[byte] into array[32, uint8] # deserialize seq[byte] into array[32, uint8]
if v.len != 32: if v.len != 32:
return err("invalid key length") return err("invalid key length")
var buf: array[32, uint8] var buf: array[32, uint8]
for i in 0..<v.len: for i in 0 ..< v.len:
buf[i] = v[i] buf[i] = v[i]
return ok(buf) return ok(buf)
proc serialize(v: StealthCommitmentFFI.PublicKey | StealthCommitmentFFI.PrivateKey): SerializedKey = proc serialize(
v: StealthCommitmentFFI.PublicKey | StealthCommitmentFFI.PrivateKey
): SerializedKey =
# serialize array[32, uint8] into seq[byte] # serialize array[32, uint8] into seq[byte]
var buf = newSeq[byte](v.len) var buf = newSeq[byte](v.len)
for i in 0..<v.len: for i in 0 ..< v.len:
buf[i] = v[i] buf[i] = v[i]
return buf return buf
proc sendThruWaku*(self: StealthCommitmentProtocol, msg: seq[byte]): Future[Result[void, string]] {.async.} = proc sendThruWaku*(
self: StealthCommitmentProtocol, msg: seq[byte]
): Future[Result[void, string]] {.async.} =
let time = getTime().toUnix() let time = getTime().toUnix()
var message = WakuMessage(payload: msg, var message = WakuMessage(
contentTopic: self.contentTopic, payload: msg,
version: 0, contentTopic: self.contentTopic,
timestamp: getNanosecondTime(time)) version: 0,
timestamp: getNanosecondTime(time),
)
(self.wakuApp.node.wakuRlnRelay.appendRLNProof(message, float64(time))).isOkOr: (self.wakuApp.node.wakuRlnRelay.appendRLNProof(message, float64(time))).isOkOr:
return err("could not append rate limit proof to the message: " & $error) return err("could not append rate limit proof to the message: " & $error)
@ -55,28 +61,44 @@ proc sendThruWaku*(self: StealthCommitmentProtocol, msg: seq[byte]): Future[Resu
return ok() return ok()
proc sendRequest*(self: StealthCommitmentProtocol): Future[Result[void, string]] {.async.} = proc sendRequest*(
let request = constructRequest(serialize(self.spendingKeyPair.publicKey), serialize(self.viewingKeyPair.publicKey)).encode() self: StealthCommitmentProtocol
): Future[Result[void, string]] {.async.} =
let request = constructRequest(
serialize(self.spendingKeyPair.publicKey),
serialize(self.viewingKeyPair.publicKey),
)
.encode()
try: try:
(await self.sendThruWaku(request.buffer)).isOkOr: (await self.sendThruWaku(request.buffer)).isOkOr:
return err("Could not send stealth commitment payload thru waku: " & $error) return err("Could not send stealth commitment payload thru waku: " & $error)
except CatchableError: except CatchableError:
return err("Could not send stealth commitment payload thru waku: " & getCurrentExceptionMsg()) return err(
"Could not send stealth commitment payload thru waku: " & getCurrentExceptionMsg()
)
return ok() return ok()
proc sendResponse*(
proc sendResponse*(self: StealthCommitmentProtocol, stealthCommitment: StealthCommitmentFFI.PublicKey, ephemeralPubKey: StealthCommitmentFFI.PublicKey, viewTag: uint64): Future[Result[void, string]] {.async.} = self: StealthCommitmentProtocol,
let response = constructResponse(serialize(stealthCommitment), serialize(ephemeralPubKey), viewTag).encode() stealthCommitment: StealthCommitmentFFI.PublicKey,
ephemeralPubKey: StealthCommitmentFFI.PublicKey,
viewTag: uint64,
): Future[Result[void, string]] {.async.} =
let response = constructResponse(
serialize(stealthCommitment), serialize(ephemeralPubKey), viewTag
)
.encode()
try: try:
(await self.sendThruWaku(response.buffer)).isOkOr: (await self.sendThruWaku(response.buffer)).isOkOr:
return err("Could not send stealth commitment payload thru waku: " & $error) return err("Could not send stealth commitment payload thru waku: " & $error)
except CatchableError: except CatchableError:
return err("Could not send stealth commitment payload thru waku: " & getCurrentExceptionMsg()) return err(
"Could not send stealth commitment payload thru waku: " & getCurrentExceptionMsg()
)
return ok() return ok()
type SCPHandler* = proc (msg: WakuMessage): Future[void] {.async.} type SCPHandler* = proc(msg: WakuMessage): Future[void] {.async.}
proc getSCPHandler(self: StealthCommitmentProtocol): SCPHandler = proc getSCPHandler(self: StealthCommitmentProtocol): SCPHandler =
let handler = proc(msg: WakuMessage): Future[void] {.async.} = let handler = proc(msg: WakuMessage): Future[void] {.async.} =
let decodedRes = WakuStealthCommitmentMsg.decode(msg.payload) let decodedRes = WakuStealthCommitmentMsg.decode(msg.payload)
if decodedRes.isErr(): if decodedRes.isErr():
@ -85,27 +107,36 @@ proc getSCPHandler(self: StealthCommitmentProtocol): SCPHandler =
if decoded.request == false: if decoded.request == false:
# check if the generated stealth commitment belongs to the receiver # check if the generated stealth commitment belongs to the receiver
# if not, continue # if not, continue
let ephemeralPubKeyRes = deserialize(StealthCommitmentFFI.PublicKey, decoded.ephemeralPubKey.get()) let ephemeralPubKeyRes =
deserialize(StealthCommitmentFFI.PublicKey, decoded.ephemeralPubKey.get())
if ephemeralPubKeyRes.isErr(): if ephemeralPubKeyRes.isErr():
error "could not deserialize ephemeral public key: ", err = ephemeralPubKeyRes.error() error "could not deserialize ephemeral public key: ",
err = ephemeralPubKeyRes.error()
let ephemeralPubKey = ephemeralPubKeyRes.get() let ephemeralPubKey = ephemeralPubKeyRes.get()
let stealthCommitmentPrivateKeyRes = StealthCommitmentFFI.generateStealthPrivateKey(ephemeralPubKey, let stealthCommitmentPrivateKeyRes = StealthCommitmentFFI.generateStealthPrivateKey(
self.spendingKeyPair.privateKey, ephemeralPubKey,
self.viewingKeyPair.privateKey, self.spendingKeyPair.privateKey,
decoded.viewTag.get()) self.viewingKeyPair.privateKey,
decoded.viewTag.get(),
)
if stealthCommitmentPrivateKeyRes.isErr(): if stealthCommitmentPrivateKeyRes.isErr():
info "received stealth commitment does not belong to the receiver: ", err = stealthCommitmentPrivateKeyRes.error() info "received stealth commitment does not belong to the receiver: ",
err = stealthCommitmentPrivateKeyRes.error()
let stealthCommitmentPrivateKey = stealthCommitmentPrivateKeyRes.get() let stealthCommitmentPrivateKey = stealthCommitmentPrivateKeyRes.get()
info "received stealth commitment belongs to the receiver: ", stealthCommitmentPrivateKey, stealthCommitmentPubKey = decoded.stealthCommitment.get() info "received stealth commitment belongs to the receiver: ",
stealthCommitmentPrivateKey,
stealthCommitmentPubKey = decoded.stealthCommitment.get()
return return
# send response # send response
# deseralize the keys # deseralize the keys
let spendingKeyRes = deserialize(StealthCommitmentFFI.PublicKey, decoded.spendingPubKey.get()) let spendingKeyRes =
deserialize(StealthCommitmentFFI.PublicKey, decoded.spendingPubKey.get())
if spendingKeyRes.isErr(): if spendingKeyRes.isErr():
error "could not deserialize spending key: ", err = spendingKeyRes.error() error "could not deserialize spending key: ", err = spendingKeyRes.error()
let spendingKey = spendingKeyRes.get() let spendingKey = spendingKeyRes.get()
let viewingKeyRes = (deserialize(StealthCommitmentFFI.PublicKey, decoded.viewingPubKey.get())) let viewingKeyRes =
(deserialize(StealthCommitmentFFI.PublicKey, decoded.viewingPubKey.get()))
if viewingKeyRes.isErr(): if viewingKeyRes.isErr():
error "could not deserialize viewing key: ", err = viewingKeyRes.error() error "could not deserialize viewing key: ", err = viewingKeyRes.error()
let viewingKey = viewingKeyRes.get() let viewingKey = viewingKeyRes.get()
@ -117,17 +148,27 @@ proc getSCPHandler(self: StealthCommitmentProtocol): SCPHandler =
error "could not generate ephemeral key pair: ", err = ephemeralKeyPairRes.error() error "could not generate ephemeral key pair: ", err = ephemeralKeyPairRes.error()
let ephemeralKeyPair = ephemeralKeyPairRes.get() let ephemeralKeyPair = ephemeralKeyPairRes.get()
let stealthCommitmentRes = StealthCommitmentFFI.generateStealthCommitment(spendingKey, viewingKey, ephemeralKeyPair.privateKey) let stealthCommitmentRes = StealthCommitmentFFI.generateStealthCommitment(
spendingKey, viewingKey, ephemeralKeyPair.privateKey
)
if stealthCommitmentRes.isErr(): if stealthCommitmentRes.isErr():
error "could not generate stealth commitment: ", err = stealthCommitmentRes.error() error "could not generate stealth commitment: ",
err = stealthCommitmentRes.error()
let stealthCommitment = stealthCommitmentRes.get() let stealthCommitment = stealthCommitmentRes.get()
(await self.sendResponse(stealthCommitment.stealthCommitment, ephemeralKeyPair.publicKey, stealthCommitment.viewTag)).isOkOr: (
await self.sendResponse(
stealthCommitment.stealthCommitment, ephemeralKeyPair.publicKey,
stealthCommitment.viewTag,
)
).isOkOr:
error "could not send response: ", err = $error error "could not send response: ", err = $error
return handler return handler
proc new*(wakuApp: App, contentTopic = ContentTopic("/wakustealthcommitments/1/app/proto")): Result[StealthCommitmentProtocol, string] = proc new*(
wakuApp: App, contentTopic = ContentTopic("/wakustealthcommitments/1/app/proto")
): Result[StealthCommitmentProtocol, string] =
let spendingKeyPair = StealthCommitmentFFI.generateKeyPair().valueOr: let spendingKeyPair = StealthCommitmentFFI.generateKeyPair().valueOr:
return err("could not generate spending key pair: " & $error) return err("could not generate spending key pair: " & $error)
let viewingKeyPair = StealthCommitmentFFI.generateKeyPair().valueOr: let viewingKeyPair = StealthCommitmentFFI.generateKeyPair().valueOr:
@ -136,11 +177,12 @@ proc new*(wakuApp: App, contentTopic = ContentTopic("/wakustealthcommitments/1/a
info "spending public key", publicKey = spendingKeyPair.publicKey info "spending public key", publicKey = spendingKeyPair.publicKey
info "viewing public key", publicKey = viewingKeyPair.publicKey info "viewing public key", publicKey = viewingKeyPair.publicKey
let SCP = StealthCommitmentProtocol(wakuApp: wakuApp, let SCP = StealthCommitmentProtocol(
contentTopic: contentTopic, wakuApp: wakuApp,
spendingKeyPair: spendingKeyPair, contentTopic: contentTopic,
viewingKeyPair: viewingKeyPair) spendingKeyPair: spendingKeyPair,
viewingKeyPair: viewingKeyPair,
)
proc handler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} = proc handler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} =
let scpHandler = getSCPHandler(SCP) let scpHandler = getSCPHandler(SCP)

View File

@ -4,10 +4,7 @@ else:
{.push raises: [].} {.push raises: [].}
import import
stew/results, stew/results, chronicles, ./node_spec as Waku, ./stealth_commitment_protocol as SCP
chronicles,
./node_spec as Waku,
./stealth_commitment_protocol as SCP
logScope: logScope:
topics = "waku stealthcommitments" topics = "waku stealthcommitments"

View File

@ -1,51 +1,44 @@
import std/[times, options] import std/[times, options]
import import confutils, chronicles, chronos, stew/results
confutils,
chronicles,
chronos,
stew/results
import import ../../waku/waku_core, ../../waku/common/protobuf
../../waku/waku_core,
../../waku/common/protobuf
import libp2p/protobuf/minprotobuf import libp2p/protobuf/minprotobuf
export export
times, times, options, confutils, chronicles, chronos, results, waku_core, protobuf,
options,
confutils,
chronicles,
chronos,
results,
waku_core,
protobuf,
minprotobuf minprotobuf
type SerializedKey* = seq[byte] type SerializedKey* = seq[byte]
type type WakuStealthCommitmentMsg* = object
WakuStealthCommitmentMsg* = object request*: bool
request*: bool spendingPubKey*: Option[SerializedKey]
spendingPubKey*: Option[SerializedKey] viewingPubKey*: Option[SerializedKey]
viewingPubKey*: Option[SerializedKey] ephemeralPubKey*: Option[SerializedKey]
ephemeralPubKey*: Option[SerializedKey] stealthCommitment*: Option[SerializedKey]
stealthCommitment*: Option[SerializedKey] viewTag*: Option[uint64]
viewTag*: Option[uint64]
proc decode*(T: type WakuStealthCommitmentMsg, buffer: seq[byte]): ProtoResult[T] = proc decode*(T: type WakuStealthCommitmentMsg, buffer: seq[byte]): ProtoResult[T] =
var msg = WakuStealthCommitmentMsg() var msg = WakuStealthCommitmentMsg()
let pb = initProtoBuffer(buffer) let pb = initProtoBuffer(buffer)
var request: uint64 var request: uint64
discard ? pb.getField(1, request) discard ?pb.getField(1, request)
msg.request = request == 1 msg.request = request == 1
var spendingPubKey = newSeq[byte]() var spendingPubKey = newSeq[byte]()
discard ? pb.getField(2, spendingPubKey) discard ?pb.getField(2, spendingPubKey)
msg.spendingPubKey = if spendingPubKey.len > 0: some(spendingPubKey) else: none(SerializedKey) msg.spendingPubKey =
if spendingPubKey.len > 0:
some(spendingPubKey)
else:
none(SerializedKey)
var viewingPubKey = newSeq[byte]() var viewingPubKey = newSeq[byte]()
discard ? pb.getField(3, viewingPubKey) discard ?pb.getField(3, viewingPubKey)
msg.viewingPubKey = if viewingPubKey.len > 0: some(viewingPubKey) else: none(SerializedKey) msg.viewingPubKey =
if viewingPubKey.len > 0:
some(viewingPubKey)
else:
none(SerializedKey)
if msg.spendingPubKey.isSome() and msg.viewingPubKey.isSome(): if msg.spendingPubKey.isSome() and msg.viewingPubKey.isSome():
msg.stealthCommitment = none(SerializedKey) msg.stealthCommitment = none(SerializedKey)
@ -58,20 +51,32 @@ proc decode*(T: type WakuStealthCommitmentMsg, buffer: seq[byte]): ProtoResult[T
if msg.request == true and msg.spendingPubKey.isNone() and msg.viewingPubKey.isNone(): if msg.request == true and msg.spendingPubKey.isNone() and msg.viewingPubKey.isNone():
return err(ProtoError.RequiredFieldMissing) return err(ProtoError.RequiredFieldMissing)
var stealthCommitment = newSeq[byte]() var stealthCommitment = newSeq[byte]()
discard ? pb.getField(4, stealthCommitment) discard ?pb.getField(4, stealthCommitment)
msg.stealthCommitment = if stealthCommitment.len > 0: some(stealthCommitment) else: none(SerializedKey) msg.stealthCommitment =
if stealthCommitment.len > 0:
some(stealthCommitment)
else:
none(SerializedKey)
var ephemeralPubKey = newSeq[byte]() var ephemeralPubKey = newSeq[byte]()
discard ? pb.getField(5, ephemeralPubKey) discard ?pb.getField(5, ephemeralPubKey)
msg.ephemeralPubKey = if ephemeralPubKey.len > 0: some(ephemeralPubKey) else: none(SerializedKey) msg.ephemeralPubKey =
if ephemeralPubKey.len > 0:
some(ephemeralPubKey)
else:
none(SerializedKey)
var viewTag: uint64 var viewTag: uint64
discard ? pb.getField(6, viewTag) discard ?pb.getField(6, viewTag)
msg.viewTag = if viewTag != 0: some(viewTag) else: none(uint64) msg.viewTag =
if viewTag != 0:
some(viewTag)
else:
none(uint64)
if msg.stealthCommitment.isNone() and msg.viewTag.isNone() and msg.ephemeralPubKey.isNone(): if msg.stealthCommitment.isNone() and msg.viewTag.isNone() and
msg.ephemeralPubKey.isNone():
return err(ProtoError.RequiredFieldMissing) return err(ProtoError.RequiredFieldMissing)
if msg.stealthCommitment.isSome() and msg.viewTag.isNone(): if msg.stealthCommitment.isSome() and msg.viewTag.isNone():
@ -108,8 +113,21 @@ func toByteSeq*(str: string): seq[byte] {.inline.} =
## Converts a string to the corresponding byte sequence. ## Converts a string to the corresponding byte sequence.
@(str.toOpenArrayByte(0, str.high)) @(str.toOpenArrayByte(0, str.high))
proc constructRequest*(spendingPubKey: SerializedKey, viewingPubKey: SerializedKey): WakuStealthCommitmentMsg = proc constructRequest*(
WakuStealthCommitmentMsg(request: true, spendingPubKey: some(spendingPubKey), viewingPubKey: some(viewingPubKey)) spendingPubKey: SerializedKey, viewingPubKey: SerializedKey
): WakuStealthCommitmentMsg =
WakuStealthCommitmentMsg(
request: true,
spendingPubKey: some(spendingPubKey),
viewingPubKey: some(viewingPubKey),
)
proc constructResponse*(stealthCommitment: SerializedKey, ephemeralPubKey: SerializedKey, viewTag: uint64): WakuStealthCommitmentMsg = proc constructResponse*(
WakuStealthCommitmentMsg(request: false, stealthCommitment: some(stealthCommitment), ephemeralPubKey: some(ephemeralPubKey), viewTag: some(viewTag)) stealthCommitment: SerializedKey, ephemeralPubKey: SerializedKey, viewTag: uint64
): WakuStealthCommitmentMsg =
WakuStealthCommitmentMsg(
request: false,
stealthCommitment: some(stealthCommitment),
ephemeralPubKey: some(ephemeralPubKey),
viewTag: some(viewTag),
)

View File

@ -13,7 +13,7 @@ proc alloc*(str: string): cstring =
## There should be the corresponding manual deallocation with deallocShared ! ## There should be the corresponding manual deallocation with deallocShared !
var ret = cast[cstring](allocShared(str.len + 1)) var ret = cast[cstring](allocShared(str.len + 1))
let s = cast[seq[char]](str) let s = cast[seq[char]](str)
for i in 0..<str.len: for i in 0 ..< str.len:
ret[i] = s[i] ret[i] = s[i]
ret[str.len] = '\0' ret[str.len] = '\0'
return ret return ret
@ -32,6 +32,6 @@ proc toSeq*[T](s: SharedSeq[T]): seq[T] =
## Creates a seq[T] from a SharedSeq[T]. No explicit dealloc is required ## Creates a seq[T] from a SharedSeq[T]. No explicit dealloc is required
## as req[T] is a GC managed type. ## as req[T] is a GC managed type.
var ret = newSeq[T]() var ret = newSeq[T]()
for i in 0..<s.len: for i in 0 ..< s.len:
ret.add(s.data[i]) ret.add(s.data[i])
return ret return ret

View File

@ -1,6 +1,3 @@
type WakuCallBack* = proc(
type callerRet: cint, msg: ptr cchar, len: csize_t, userData: pointer
WakuCallBack* = proc(callerRet: cint, ) {.cdecl, gcsafe, raises: [].}
msg: ptr cchar,
len: csize_t,
userData: pointer) {.cdecl, gcsafe, raises: [].}

View File

@ -1,8 +1,6 @@
type JsonEvent* = ref object of RootObj # https://rfc.vac.dev/spec/36/#jsonsignal-type
type JsonEvent* = ref object of RootObj
# https://rfc.vac.dev/spec/36/#jsonsignal-type
eventType* {.requiresInit.}: string eventType* {.requiresInit.}: string
method `$`*(jsonEvent: JsonEvent): string {.base.} = discard method `$`*(jsonEvent: JsonEvent): string {.base.} =
discard
# All events should implement this # All events should implement this

View File

@ -1,25 +1,19 @@
import system, std/[json, sequtils]
import stew/[byteutils, results]
import
../../waku/common/base64,
../../waku/waku_core/message,
../../waku/waku_core/message/message,
./json_base_event
import type JsonMessage* = ref object # https://rfc.vac.dev/spec/36/#jsonmessage-type
system, payload*: Base64String
std/[json,sequtils] contentTopic*: string
import version*: uint
stew/[byteutils,results] timestamp*: int64
import ephemeral*: bool
../../waku/common/base64, meta*: Base64String
../../waku/waku_core/message, proof*: Base64String
../../waku/waku_core/message/message,
./json_base_event
type
JsonMessage* = ref object
# https://rfc.vac.dev/spec/36/#jsonmessage-type
payload*: Base64String
contentTopic*: string
version*: uint
timestamp*: int64
ephemeral*: bool
meta*: Base64String
proof*: Base64String
func fromJsonNode*(T: type JsonMessage, jsonContent: JsonNode): JsonMessage = func fromJsonNode*(T: type JsonMessage, jsonContent: JsonNode): JsonMessage =
# Visit https://rfc.vac.dev/spec/14/ for further details # Visit https://rfc.vac.dev/spec/14/ for further details
@ -30,7 +24,7 @@ func fromJsonNode*(T: type JsonMessage, jsonContent: JsonNode): JsonMessage =
timestamp: int64(jsonContent{"timestamp"}.getBiggestInt()), timestamp: int64(jsonContent{"timestamp"}.getBiggestInt()),
ephemeral: jsonContent{"ephemeral"}.getBool(), ephemeral: jsonContent{"ephemeral"}.getBool(),
meta: Base64String(jsonContent{"meta"}.getStr()), meta: Base64String(jsonContent{"meta"}.getStr()),
proof: Base64String(jsonContent{"proof"}.getStr()) proof: Base64String(jsonContent{"proof"}.getStr()),
) )
proc toWakuMessage*(self: JsonMessage): Result[WakuMessage, string] = proc toWakuMessage*(self: JsonMessage): Result[WakuMessage, string] =
@ -43,15 +37,17 @@ proc toWakuMessage*(self: JsonMessage): Result[WakuMessage, string] =
let proof = base64.decode(self.proof).valueOr: let proof = base64.decode(self.proof).valueOr:
return err("invalid proof format: " & error) return err("invalid proof format: " & error)
ok(WakuMessage( ok(
payload: payload, WakuMessage(
meta: meta, payload: payload,
contentTopic: self.contentTopic, meta: meta,
version: uint32(self.version), contentTopic: self.contentTopic,
timestamp: self.timestamp, version: uint32(self.version),
ephemeral: self.ephemeral, timestamp: self.timestamp,
proof: proof, ephemeral: self.ephemeral,
)) proof: proof,
)
)
proc `%`*(value: Base64String): JsonNode = proc `%`*(value: Base64String): JsonNode =
%(value.string) %(value.string)
@ -60,13 +56,11 @@ proc `%`*(value: WakuMessageHash): JsonNode =
%(to0xHex(value)) %(to0xHex(value))
type JsonMessageEvent* = ref object of JsonEvent type JsonMessageEvent* = ref object of JsonEvent
pubsubTopic*: string pubsubTopic*: string
messageHash*: WakuMessageHash messageHash*: WakuMessageHash
wakuMessage*: JsonMessage wakuMessage*: JsonMessage
proc new*(T: type JsonMessageEvent, proc new*(T: type JsonMessageEvent, pubSubTopic: string, msg: WakuMessage): T =
pubSubTopic: string,
msg: WakuMessage): T =
# Returns a WakuMessage event as indicated in # Returns a WakuMessage event as indicated in
# https://rfc.vac.dev/spec/36/#jsonmessageevent-type # https://rfc.vac.dev/spec/36/#jsonmessageevent-type
@ -89,15 +83,15 @@ proc new*(T: type JsonMessageEvent,
pubSubTopic: pubSubTopic, pubSubTopic: pubSubTopic,
messageHash: msgHash, messageHash: msgHash,
wakuMessage: JsonMessage( wakuMessage: JsonMessage(
payload: base64.encode(payload), payload: base64.encode(payload),
contentTopic: msg.contentTopic, contentTopic: msg.contentTopic,
version: msg.version, version: msg.version,
timestamp: int64(msg.timestamp), timestamp: int64(msg.timestamp),
ephemeral: msg.ephemeral, ephemeral: msg.ephemeral,
meta: base64.encode(meta), meta: base64.encode(meta),
proof: base64.encode(proof), proof: base64.encode(proof),
) ),
) )
method `$`*(jsonMessage: JsonMessageEvent): string = method `$`*(jsonMessage: JsonMessageEvent): string =
$( %* jsonMessage ) $(%*jsonMessage)

View File

@ -1,13 +1,9 @@
{.pragma: exported, exportc, cdecl, raises: [].} {.pragma: exported, exportc, cdecl, raises: [].}
{.pragma: callback, cdecl, raises: [], gcsafe.} {.pragma: callback, cdecl, raises: [], gcsafe.}
{.passc: "-fPIC".} {.passc: "-fPIC".}
import import std/[json, sequtils, times, strformat, options, atomics, strutils]
std/[json,sequtils,times,strformat,options,atomics,strutils] import chronicles, chronos
import
chronicles,
chronos
import import
../../waku/common/base64, ../../waku/common/base64,
../../waku/waku_core/message/message, ../../waku/waku_core/message/message,
@ -44,7 +40,9 @@ const RET_MISSING_CALLBACK: cint = 2
### Not-exported components ### Not-exported components
proc relayEventCallback(ctx: ptr Context): WakuRelayHandler = proc relayEventCallback(ctx: ptr Context): WakuRelayHandler =
return proc (pubsubTopic: PubsubTopic, msg: WakuMessage): Future[system.void]{.async.} = return proc(
pubsubTopic: PubsubTopic, msg: WakuMessage
): Future[system.void] {.async.} =
# Callback that hadles the Waku Relay events. i.e. messages or errors. # Callback that hadles the Waku Relay events. i.e. messages or errors.
if isNil(ctx[].eventCallback): if isNil(ctx[].eventCallback):
error "eventCallback is nil" error "eventCallback is nil"
@ -56,12 +54,14 @@ proc relayEventCallback(ctx: ptr Context): WakuRelayHandler =
try: try:
let event = $JsonMessageEvent.new(pubsubTopic, msg) let event = $JsonMessageEvent.new(pubsubTopic, msg)
cast[WakuCallBack](ctx[].eventCallback)(RET_OK, unsafeAddr event[0], cast[csize_t](len(event)), ctx[].eventUserData) cast[WakuCallBack](ctx[].eventCallback)(
except Exception,CatchableError: RET_OK, unsafeAddr event[0], cast[csize_t](len(event)), ctx[].eventUserData
let msg = "Exception when calling 'eventCallBack': " & )
getCurrentExceptionMsg() except Exception, CatchableError:
cast[WakuCallBack](ctx[].eventCallback)(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), ctx[].eventUserData) let msg = "Exception when calling 'eventCallBack': " & getCurrentExceptionMsg()
cast[WakuCallBack](ctx[].eventCallback)(
RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), ctx[].eventUserData
)
### End of not-exported components ### End of not-exported components
################################################################################ ################################################################################
@ -69,10 +69,9 @@ proc relayEventCallback(ctx: ptr Context): WakuRelayHandler =
################################################################################ ################################################################################
### Exported procs ### Exported procs
proc waku_new(configJson: cstring, proc waku_new(
callback: WakuCallback, configJson: cstring, callback: WakuCallback, userData: pointer
userData: pointer): pointer ): pointer {.dynlib, exportc, cdecl.} =
{.dynlib, exportc, cdecl.} =
## Creates a new instance of the WakuNode. ## Creates a new instance of the WakuNode.
if isNil(callback): if isNil(callback):
@ -88,11 +87,10 @@ proc waku_new(configJson: cstring,
ctx.userData = userData ctx.userData = userData
let sendReqRes = waku_thread.sendRequestToWakuThread( let sendReqRes = waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.LIFECYCLE, RequestType.LIFECYCLE,
NodeLifecycleRequest.createShared( NodeLifecycleRequest.createShared(NodeLifecycleMsgType.CREATE_NODE, configJson),
NodeLifecycleMsgType.CREATE_NODE, )
configJson))
if sendReqRes.isErr(): if sendReqRes.isErr():
let msg = $sendReqRes.error let msg = $sendReqRes.error
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
@ -100,10 +98,9 @@ proc waku_new(configJson: cstring,
return ctx return ctx
proc waku_destroy(ctx: ptr Context, proc waku_destroy(
callback: WakuCallBack, ctx: ptr Context, callback: WakuCallBack, userData: pointer
userData: pointer): cint {.dynlib, exportc.} = ): cint {.dynlib, exportc.} =
if isNil(callback): if isNil(callback):
return RET_MISSING_CALLBACK return RET_MISSING_CALLBACK
@ -114,33 +111,38 @@ proc waku_destroy(ctx: ptr Context,
return RET_OK return RET_OK
proc waku_version(ctx: ptr Context, proc waku_version(
callback: WakuCallBack, ctx: ptr Context, callback: WakuCallBack, userData: pointer
userData: pointer): cint {.dynlib, exportc.} = ): cint {.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
if isNil(callback): if isNil(callback):
return RET_MISSING_CALLBACK return RET_MISSING_CALLBACK
callback(RET_OK, cast[ptr cchar](WakuNodeVersionString), callback(
cast[csize_t](len(WakuNodeVersionString)), userData) RET_OK,
cast[ptr cchar](WakuNodeVersionString),
cast[csize_t](len(WakuNodeVersionString)),
userData,
)
return RET_OK return RET_OK
proc waku_set_event_callback(ctx: ptr Context, proc waku_set_event_callback(
callback: WakuCallBack, ctx: ptr Context, callback: WakuCallBack, userData: pointer
userData: pointer) {.dynlib, exportc.} = ) {.dynlib, exportc.} =
ctx[].eventCallback = cast[pointer](callback) ctx[].eventCallback = cast[pointer](callback)
ctx[].eventUserData = userData ctx[].eventUserData = userData
proc waku_content_topic(ctx: ptr Context, proc waku_content_topic(
appName: cstring, ctx: ptr Context,
appVersion: cuint, appName: cstring,
contentTopicName: cstring, appVersion: cuint,
encoding: cstring, contentTopicName: cstring,
callback: WakuCallBack, encoding: cstring,
userData: pointer): cint {.dynlib, exportc.} = callback: WakuCallBack,
userData: pointer,
): cint {.dynlib, exportc.} =
# https://rfc.vac.dev/spec/36/#extern-char-waku_content_topicchar-applicationname-unsigned-int-applicationversion-char-contenttopicname-char-encoding # https://rfc.vac.dev/spec/36/#extern-char-waku_content_topicchar-applicationname-unsigned-int-applicationversion-char-contenttopicname-char-encoding
ctx[].userData = userData ctx[].userData = userData
@ -153,7 +155,9 @@ proc waku_content_topic(ctx: ptr Context,
let encodingStr = encoding.alloc() let encodingStr = encoding.alloc()
let contentTopic = fmt"/{$appStr}/{appVersion}/{$ctnStr}/{$encodingStr}" let contentTopic = fmt"/{$appStr}/{appVersion}/{$ctnStr}/{$encodingStr}"
callback(RET_OK, unsafeAddr contentTopic[0], cast[csize_t](len(contentTopic)), userData) callback(
RET_OK, unsafeAddr contentTopic[0], cast[csize_t](len(contentTopic)), userData
)
deallocShared(appStr) deallocShared(appStr)
deallocShared(ctnStr) deallocShared(ctnStr)
@ -161,10 +165,9 @@ proc waku_content_topic(ctx: ptr Context,
return RET_OK return RET_OK
proc waku_pubsub_topic(ctx: ptr Context, proc waku_pubsub_topic(
topicName: cstring, ctx: ptr Context, topicName: cstring, callback: WakuCallBack, userData: pointer
callback: WakuCallBack, ): cint {.dynlib, exportc, cdecl.} =
userData: pointer): cint {.dynlib, exportc, cdecl.} =
# https://rfc.vac.dev/spec/36/#extern-char-waku_pubsub_topicchar-name-char-encoding # https://rfc.vac.dev/spec/36/#extern-char-waku_pubsub_topicchar-name-char-encoding
ctx[].userData = userData ctx[].userData = userData
@ -175,15 +178,17 @@ proc waku_pubsub_topic(ctx: ptr Context,
let topicNameStr = topicName.alloc() let topicNameStr = topicName.alloc()
let outPubsubTopic = fmt"/waku/2/{$topicNameStr}" let outPubsubTopic = fmt"/waku/2/{$topicNameStr}"
callback(RET_OK, unsafeAddr outPubsubTopic[0], cast[csize_t](len(outPubsubTopic)), userData) callback(
RET_OK, unsafeAddr outPubsubTopic[0], cast[csize_t](len(outPubsubTopic)), userData
)
deallocShared(topicNameStr) deallocShared(topicNameStr)
return RET_OK return RET_OK
proc waku_default_pubsub_topic(ctx: ptr Context, proc waku_default_pubsub_topic(
callback: WakuCallBack, ctx: ptr Context, callback: WakuCallBack, userData: pointer
userData: pointer): cint {.dynlib, exportc.} = ): cint {.dynlib, exportc.} =
# https://rfc.vac.dev/spec/36/#extern-char-waku_default_pubsub_topic # https://rfc.vac.dev/spec/36/#extern-char-waku_default_pubsub_topic
ctx[].userData = userData ctx[].userData = userData
@ -191,18 +196,23 @@ proc waku_default_pubsub_topic(ctx: ptr Context,
if isNil(callback): if isNil(callback):
return RET_MISSING_CALLBACK return RET_MISSING_CALLBACK
callback(RET_OK, cast[ptr cchar](DefaultPubsubTopic), cast[csize_t](len(DefaultPubsubTopic)), userData) callback(
RET_OK,
cast[ptr cchar](DefaultPubsubTopic),
cast[csize_t](len(DefaultPubsubTopic)),
userData,
)
return RET_OK return RET_OK
proc waku_relay_publish(ctx: ptr Context, proc waku_relay_publish(
pubSubTopic: cstring, ctx: ptr Context,
jsonWakuMessage: cstring, pubSubTopic: cstring,
timeoutMs: cuint, jsonWakuMessage: cstring,
callback: WakuCallBack, timeoutMs: cuint,
userData: pointer): cint callback: WakuCallBack,
userData: pointer,
{.dynlib, exportc, cdecl.} = ): cint {.dynlib, exportc, cdecl.} =
# https://rfc.vac.dev/spec/36/#extern-char-waku_relay_publishchar-messagejson-char-pubsubtopic-int-timeoutms # https://rfc.vac.dev/spec/36/#extern-char-waku_relay_publishchar-messagejson-char-pubsubtopic-int-timeoutms
ctx[].userData = userData ctx[].userData = userData
@ -211,7 +221,7 @@ proc waku_relay_publish(ctx: ptr Context,
return RET_MISSING_CALLBACK return RET_MISSING_CALLBACK
let jwm = jsonWakuMessage.alloc() let jwm = jsonWakuMessage.alloc()
var jsonMessage:JsonMessage var jsonMessage: JsonMessage
try: try:
let jsonContent = parseJson($jwm) let jsonContent = parseJson($jwm)
jsonMessage = JsonMessage.fromJsonNode(jsonContent) jsonMessage = JsonMessage.fromJsonNode(jsonContent)
@ -230,18 +240,22 @@ proc waku_relay_publish(ctx: ptr Context,
let pst = pubSubTopic.alloc() let pst = pubSubTopic.alloc()
let targetPubSubTopic = if len(pst) == 0: let targetPubSubTopic =
DefaultPubsubTopic if len(pst) == 0:
else: DefaultPubsubTopic
$pst else:
$pst
let sendReqRes = waku_thread.sendRequestToWakuThread( let sendReqRes = waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.RELAY, RequestType.RELAY,
RelayRequest.createShared(RelayMsgType.PUBLISH, RelayRequest.createShared(
PubsubTopic($pst), RelayMsgType.PUBLISH,
WakuRelayHandler(relayEventCallback(ctx)), PubsubTopic($pst),
wakuMessage)) WakuRelayHandler(relayEventCallback(ctx)),
wakuMessage,
),
)
deallocShared(pst) deallocShared(pst)
if sendReqRes.isErr(): if sendReqRes.isErr():
@ -253,46 +267,42 @@ proc waku_relay_publish(ctx: ptr Context,
callback(RET_OK, unsafeAddr msgHash[0], cast[csize_t](len(msgHash)), userData) callback(RET_OK, unsafeAddr msgHash[0], cast[csize_t](len(msgHash)), userData)
return RET_OK return RET_OK
proc waku_start(ctx: ptr Context, proc waku_start(
callback: WakuCallBack, ctx: ptr Context, callback: WakuCallBack, userData: pointer
userData: pointer): cint {.dynlib, exportc.} = ): cint {.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
## TODO: handle the error ## TODO: handle the error
discard waku_thread.sendRequestToWakuThread( discard waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.LIFECYCLE, RequestType.LIFECYCLE,
NodeLifecycleRequest.createShared( NodeLifecycleRequest.createShared(NodeLifecycleMsgType.START_NODE),
NodeLifecycleMsgType.START_NODE)) )
proc waku_stop(ctx: ptr Context, proc waku_stop(
callback: WakuCallBack, ctx: ptr Context, callback: WakuCallBack, userData: pointer
userData: pointer): cint {.dynlib, exportc.} = ): cint {.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
## TODO: handle the error ## TODO: handle the error
discard waku_thread.sendRequestToWakuThread( discard waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.LIFECYCLE, RequestType.LIFECYCLE,
NodeLifecycleRequest.createShared( NodeLifecycleRequest.createShared(NodeLifecycleMsgType.STOP_NODE),
NodeLifecycleMsgType.STOP_NODE)) )
proc waku_relay_subscribe( proc waku_relay_subscribe(
ctx: ptr Context, ctx: ptr Context, pubSubTopic: cstring, callback: WakuCallBack, userData: pointer
pubSubTopic: cstring, ): cint {.dynlib, exportc.} =
callback: WakuCallBack,
userData: pointer): cint
{.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
let pst = pubSubTopic.alloc() let pst = pubSubTopic.alloc()
var cb = relayEventCallback(ctx) var cb = relayEventCallback(ctx)
let sendReqRes = waku_thread.sendRequestToWakuThread( let sendReqRes = waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.RELAY, RequestType.RELAY,
RelayRequest.createShared(RelayMsgType.SUBSCRIBE, RelayRequest.createShared(
PubsubTopic($pst), RelayMsgType.SUBSCRIBE, PubsubTopic($pst), WakuRelayHandler(cb)
WakuRelayHandler(cb))) ),
)
deallocShared(pst) deallocShared(pst)
if sendReqRes.isErr(): if sendReqRes.isErr():
@ -303,22 +313,21 @@ proc waku_relay_subscribe(
return RET_OK return RET_OK
proc waku_relay_unsubscribe( proc waku_relay_unsubscribe(
ctx: ptr Context, ctx: ptr Context, pubSubTopic: cstring, callback: WakuCallBack, userData: pointer
pubSubTopic: cstring, ): cint {.dynlib, exportc.} =
callback: WakuCallBack,
userData: pointer): cint
{.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
let pst = pubSubTopic.alloc() let pst = pubSubTopic.alloc()
let sendReqRes = waku_thread.sendRequestToWakuThread( let sendReqRes = waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.RELAY, RequestType.RELAY,
RelayRequest.createShared(RelayMsgType.SUBSCRIBE, RelayRequest.createShared(
PubsubTopic($pst), RelayMsgType.SUBSCRIBE,
WakuRelayHandler(relayEventCallback(ctx)))) PubsubTopic($pst),
WakuRelayHandler(relayEventCallback(ctx)),
),
)
deallocShared(pst) deallocShared(pst)
if sendReqRes.isErr(): if sendReqRes.isErr():
@ -328,22 +337,22 @@ proc waku_relay_unsubscribe(
return RET_OK return RET_OK
proc waku_connect(ctx: ptr Context, proc waku_connect(
peerMultiAddr: cstring, ctx: ptr Context,
timeoutMs: cuint, peerMultiAddr: cstring,
callback: WakuCallBack, timeoutMs: cuint,
userData: pointer): cint callback: WakuCallBack,
{.dynlib, exportc.} = userData: pointer,
): cint {.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
let connRes = waku_thread.sendRequestToWakuThread( let connRes = waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.PEER_MANAGER, RequestType.PEER_MANAGER,
PeerManagementRequest.createShared( PeerManagementRequest.createShared(
PeerManagementMsgType.CONNECT_TO, PeerManagementMsgType.CONNECT_TO, $peerMultiAddr, chronos.milliseconds(timeoutMs)
$peerMultiAddr, ),
chronos.milliseconds(timeoutMs))) )
if connRes.isErr(): if connRes.isErr():
let msg = $connRes.error let msg = $connRes.error
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
@ -351,14 +360,14 @@ proc waku_connect(ctx: ptr Context,
return RET_OK return RET_OK
proc waku_store_query(ctx: ptr Context, proc waku_store_query(
queryJson: cstring, ctx: ptr Context,
peerId: cstring, queryJson: cstring,
timeoutMs: cint, peerId: cstring,
callback: WakuCallBack, timeoutMs: cint,
userData: pointer): cint callback: WakuCallBack,
{.dynlib, exportc.} = userData: pointer,
): cint {.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
## TODO: implement the logic that make the "self" node to act as a Store client ## TODO: implement the logic that make the "self" node to act as a Store client
@ -370,18 +379,16 @@ proc waku_store_query(ctx: ptr Context,
return RET_OK return RET_OK
proc waku_listen_addresses(ctx: ptr Context, proc waku_listen_addresses(
callback: WakuCallBack, ctx: ptr Context, callback: WakuCallBack, userData: pointer
userData: pointer): cint ): cint {.dynlib, exportc.} =
{.dynlib, exportc.} =
ctx[].userData = userData ctx[].userData = userData
let connRes = waku_thread.sendRequestToWakuThread( let connRes = waku_thread.sendRequestToWakuThread(
ctx, ctx,
RequestType.DEBUG, RequestType.DEBUG,
DebugNodeRequest.createShared( DebugNodeRequest.createShared(DebugNodeMsgType.RETRIEVE_LISTENING_ADDRESSES),
DebugNodeMsgType.RETRIEVE_LISTENING_ADDRESSES)) )
if connRes.isErr(): if connRes.isErr():
let msg = $connRes.error let msg = $connRes.error
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)

View File

@ -1,7 +1,4 @@
import std/[json, strformat, options]
import
std/[json,strformat,options]
import import
libp2p/crypto/crypto, libp2p/crypto/crypto,
libp2p/crypto/secp, libp2p/crypto/secp,
@ -12,10 +9,9 @@ import
../../waku/node/config, ../../waku/node/config,
../events/json_base_event ../events/json_base_event
proc parsePrivateKey(jsonNode: JsonNode, proc parsePrivateKey(
privateKey: var PrivateKey, jsonNode: JsonNode, privateKey: var PrivateKey, errorResp: var string
errorResp: var string): bool = ): bool =
if not jsonNode.contains("key") or jsonNode["key"].kind == JsonNodeKind.JNull: if not jsonNode.contains("key") or jsonNode["key"].kind == JsonNodeKind.JNull:
privateKey = PrivateKey.random(Secp256k1, newRng()[]).tryGet() privateKey = PrivateKey.random(Secp256k1, newRng()[]).tryGet()
return true return true
@ -36,10 +32,9 @@ proc parsePrivateKey(jsonNode: JsonNode,
return true return true
proc parseListenAddr(jsonNode: JsonNode, proc parseListenAddr(
listenAddr: var IpAddress, jsonNode: JsonNode, listenAddr: var IpAddress, errorResp: var string
errorResp: var string): bool = ): bool =
if not jsonNode.contains("host"): if not jsonNode.contains("host"):
errorResp = "host attribute is required" errorResp = "host attribute is required"
return false return false
@ -59,10 +54,7 @@ proc parseListenAddr(jsonNode: JsonNode,
return true return true
proc parsePort(jsonNode: JsonNode, proc parsePort(jsonNode: JsonNode, port: var int, errorResp: var string): bool =
port: var int,
errorResp: var string): bool =
if not jsonNode.contains("port"): if not jsonNode.contains("port"):
errorResp = "port attribute is required" errorResp = "port attribute is required"
return false return false
@ -75,10 +67,7 @@ proc parsePort(jsonNode: JsonNode,
return true return true
proc parseRelay(jsonNode: JsonNode, proc parseRelay(jsonNode: JsonNode, relay: var bool, errorResp: var string): bool =
relay: var bool,
errorResp: var string): bool =
if not jsonNode.contains("relay"): if not jsonNode.contains("relay"):
errorResp = "relay attribute is required" errorResp = "relay attribute is required"
return false return false
@ -91,16 +80,17 @@ proc parseRelay(jsonNode: JsonNode,
return true return true
proc parseStore(jsonNode: JsonNode, proc parseStore(
store: var bool, jsonNode: JsonNode,
storeNode: var string, store: var bool,
storeRetentionPolicy: var string, storeNode: var string,
storeDbUrl: var string, storeRetentionPolicy: var string,
storeVacuum: var bool, storeDbUrl: var string,
storeDbMigration: var bool, storeVacuum: var bool,
storeMaxNumDbConnections: var int, storeDbMigration: var bool,
errorResp: var string): bool = storeMaxNumDbConnections: var int,
errorResp: var string,
): bool =
if not jsonNode.contains("store"): if not jsonNode.contains("store"):
## the store parameter is not required. By default is is disabled ## the store parameter is not required. By default is is disabled
store = false store = false
@ -163,20 +153,21 @@ proc parseTopics(jsonNode: JsonNode, topics: var seq[string]) =
else: else:
topics = @["/waku/2/default-waku/proto"] topics = @["/waku/2/default-waku/proto"]
proc parseConfig*(configNodeJson: string, proc parseConfig*(
privateKey: var PrivateKey, configNodeJson: string,
netConfig: var NetConfig, privateKey: var PrivateKey,
relay: var bool, netConfig: var NetConfig,
topics: var seq[string], relay: var bool,
store: var bool, topics: var seq[string],
storeNode: var string, store: var bool,
storeRetentionPolicy: var string, storeNode: var string,
storeDbUrl: var string, storeRetentionPolicy: var string,
storeVacuum: var bool, storeDbUrl: var string,
storeDbMigration: var bool, storeVacuum: var bool,
storeMaxNumDbConnections: var int, storeDbMigration: var bool,
errorResp: var string): bool {.raises: [].} = storeMaxNumDbConnections: var int,
errorResp: var string,
): bool {.raises: [].} =
if configNodeJson.len == 0: if configNodeJson.len == 0:
errorResp = "The configNodeJson is empty" errorResp = "The configNodeJson is empty"
return false return false
@ -215,19 +206,18 @@ proc parseConfig*(configNodeJson: string,
errorResp = "Exception calling parsePort: " & getCurrentExceptionMsg() errorResp = "Exception calling parsePort: " & getCurrentExceptionMsg()
return false return false
let natRes = setupNat("any", clientId, let natRes = setupNat("any", clientId, Port(uint16(port)), Port(uint16(port)))
Port(uint16(port)),
Port(uint16(port)))
if natRes.isErr(): if natRes.isErr():
errorResp = "failed to setup NAT: " & $natRes.error errorResp = "failed to setup NAT: " & $natRes.error
return false return false
let (extIp, extTcpPort, _) = natRes.get() let (extIp, extTcpPort, _) = natRes.get()
let extPort = if extIp.isSome() and extTcpPort.isNone(): let extPort =
some(Port(uint16(port))) if extIp.isSome() and extTcpPort.isNone():
else: some(Port(uint16(port)))
extTcpPort else:
extTcpPort
# relay # relay
try: try:
@ -246,26 +236,26 @@ proc parseConfig*(configNodeJson: string,
# store # store
try: try:
if not parseStore(jsonNode, store, storeNode, storeRetentionPolicy, storeDbUrl, if not parseStore(
storeVacuum, storeDbMigration, storeMaxNumDbConnections, errorResp): jsonNode, store, storeNode, storeRetentionPolicy, storeDbUrl, storeVacuum,
storeDbMigration, storeMaxNumDbConnections, errorResp,
):
return false return false
except Exception, KeyError: except Exception, KeyError:
errorResp = "Exception calling parseStore: " & getCurrentExceptionMsg() errorResp = "Exception calling parseStore: " & getCurrentExceptionMsg()
return false return false
let wakuFlags = CapabilitiesBitfield.init( let wakuFlags = CapabilitiesBitfield.init(
lightpush = false, lightpush = false, filter = false, store = false, relay = relay
filter = false, )
store = false,
relay = relay
)
let netConfigRes = NetConfig.init( let netConfigRes = NetConfig.init(
bindIp = listenAddr, bindIp = listenAddr,
bindPort = Port(uint16(port)), bindPort = Port(uint16(port)),
extIp = extIp, extIp = extIp,
extPort = extPort, extPort = extPort,
wakuFlags = some(wakuFlags)) wakuFlags = some(wakuFlags),
)
if netConfigRes.isErr(): if netConfigRes.isErr():
errorResp = "Error creating NetConfig: " & $netConfigRes.error errorResp = "Error creating NetConfig: " & $netConfigRes.error

View File

@ -1,26 +1,14 @@
import std/[options, sequtils, strutils, json]
import chronicles, chronos, stew/results, stew/shims/net
import ../../../../waku/node/waku_node, ../../../alloc
import type DebugNodeMsgType* = enum
std/[options,sequtils,strutils,json] RETRIEVE_LISTENING_ADDRESSES
import
chronicles,
chronos,
stew/results,
stew/shims/net
import
../../../../waku/node/waku_node,
../../../alloc
type type DebugNodeRequest* = object
DebugNodeMsgType* = enum operation: DebugNodeMsgType
RETRIEVE_LISTENING_ADDRESSES
type
DebugNodeRequest* = object
operation: DebugNodeMsgType
proc createShared*(T: type DebugNodeRequest,
op: DebugNodeMsgType): ptr type T =
proc createShared*(T: type DebugNodeRequest, op: DebugNodeMsgType): ptr type T =
var ret = createShared(T) var ret = createShared(T)
ret[].operation = op ret[].operation = op
return ret return ret
@ -31,14 +19,14 @@ proc destroyShared(self: ptr DebugNodeRequest) =
proc getMultiaddresses(node: WakuNode): seq[string] = proc getMultiaddresses(node: WakuNode): seq[string] =
return node.info().listenAddresses return node.info().listenAddresses
proc process*(self: ptr DebugNodeRequest, proc process*(
node: WakuNode): Future[Result[string, string]] {.async.} = self: ptr DebugNodeRequest, node: WakuNode
): Future[Result[string, string]] {.async.} =
defer:
destroyShared(self)
defer: destroyShared(self) case self.operation
of RETRIEVE_LISTENING_ADDRESSES:
case self.operation: return ok($(%*node.getMultiaddresses()))
of RETRIEVE_LISTENING_ADDRESSES:
return ok($( %* node.getMultiaddresses()))
return err("unsupported operation in DebugNodeRequest") return err("unsupported operation in DebugNodeRequest")

View File

@ -1,11 +1,5 @@
import std/options
import import chronos, chronicles, stew/results, stew/shims/net
std/options
import
chronos,
chronicles,
stew/results,
stew/shims/net
import import
../../../../waku/common/enr/builder, ../../../../waku/common/enr/builder,
../../../../waku/waku_enr/capabilities, ../../../../waku/waku_enr/capabilities,
@ -24,25 +18,22 @@ import
../../../../waku/waku_relay/protocol, ../../../../waku/waku_relay/protocol,
../../../../waku/waku_store, ../../../../waku/waku_store,
../../../../waku/factory/builder, ../../../../waku/factory/builder,
../../../events/[json_message_event,json_base_event], ../../../events/[json_message_event, json_base_event],
../../../alloc, ../../../alloc,
../../config ../../config
type type NodeLifecycleMsgType* = enum
NodeLifecycleMsgType* = enum CREATE_NODE
CREATE_NODE START_NODE
START_NODE STOP_NODE
STOP_NODE
type type NodeLifecycleRequest* = object
NodeLifecycleRequest* = object operation: NodeLifecycleMsgType
operation: NodeLifecycleMsgType configJson: cstring ## Only used in 'CREATE_NODE' operation
configJson: cstring ## Only used in 'CREATE_NODE' operation
proc createShared*(T: type NodeLifecycleRequest,
op: NodeLifecycleMsgType,
configJson: cstring = ""): ptr type T =
proc createShared*(
T: type NodeLifecycleRequest, op: NodeLifecycleMsgType, configJson: cstring = ""
): ptr type T =
var ret = createShared(T) var ret = createShared(T)
ret[].operation = op ret[].operation = op
ret[].configJson = configJson.alloc() ret[].configJson = configJson.alloc()
@ -52,14 +43,15 @@ proc destroyShared(self: ptr NodeLifecycleRequest) =
deallocShared(self[].configJson) deallocShared(self[].configJson)
deallocShared(self) deallocShared(self)
proc configureStore(node: WakuNode, proc configureStore(
storeNode: string, node: WakuNode,
storeRetentionPolicy: string, storeNode: string,
storeDbUrl: string, storeRetentionPolicy: string,
storeVacuum: bool, storeDbUrl: string,
storeDbMigration: bool, storeVacuum: bool,
storeMaxNumDbConnections: int): storeDbMigration: bool,
Future[Result[void, string]] {.async.} = storeMaxNumDbConnections: int,
): Future[Result[void, string]] {.async.} =
## This snippet is extracted/duplicated from the app.nim file ## This snippet is extracted/duplicated from the app.nim file
var onFatalErrorAction = proc(msg: string) {.gcsafe, closure.} = var onFatalErrorAction = proc(msg: string) {.gcsafe, closure.} =
@ -70,11 +62,10 @@ proc configureStore(node: WakuNode,
discard discard
# Archive setup # Archive setup
let archiveDriverRes = await ArchiveDriver.new(storeDbUrl, let archiveDriverRes = await ArchiveDriver.new(
storeVacuum, storeDbUrl, storeVacuum, storeDbMigration, storeMaxNumDbConnections,
storeDbMigration, onFatalErrorAction,
storeMaxNumDbConnections, )
onFatalErrorAction)
if archiveDriverRes.isErr(): if archiveDriverRes.isErr():
return err("failed to setup archive driver: " & archiveDriverRes.error) return err("failed to setup archive driver: " & archiveDriverRes.error)
@ -82,8 +73,7 @@ proc configureStore(node: WakuNode,
if retPolicyRes.isErr(): if retPolicyRes.isErr():
return err("failed to create retention policy: " & retPolicyRes.error) return err("failed to create retention policy: " & retPolicyRes.error)
let mountArcRes = node.mountArchive(archiveDriverRes.get(), let mountArcRes = node.mountArchive(archiveDriverRes.get(), retPolicyRes.get())
retPolicyRes.get())
if mountArcRes.isErr(): if mountArcRes.isErr():
return err("failed to mount waku archive protocol: " & mountArcRes.error) return err("failed to mount waku archive protocol: " & mountArcRes.error)
@ -103,12 +93,9 @@ proc configureStore(node: WakuNode,
return ok() return ok()
proc createNode(configJson: cstring): proc createNode(configJson: cstring): Future[Result[WakuNode, string]] {.async.} =
Future[Result[WakuNode, string]] {.async.} =
var privateKey: PrivateKey var privateKey: PrivateKey
var netConfig = NetConfig.init(parseIpAddress("127.0.0.1"), var netConfig = NetConfig.init(parseIpAddress("127.0.0.1"), Port(60000'u16)).value
Port(60000'u16)).value
## relay ## relay
var relay: bool var relay: bool
var topics = @[""] var topics = @[""]
@ -125,19 +112,21 @@ proc createNode(configJson: cstring):
var errorResp: string var errorResp: string
try: try:
if not parseConfig($configJson, if not parseConfig(
privateKey, $configJson,
netConfig, privateKey,
relay, netConfig,
topics, relay,
store, topics,
storeNode, store,
storeRetentionPolicy, storeNode,
storeDbUrl, storeRetentionPolicy,
storeVacuum, storeDbUrl,
storeDbMigration, storeVacuum,
storeMaxNumDbConnections, storeDbMigration,
errorResp): storeMaxNumDbConnections,
errorResp,
):
return err(errorResp) return err(errorResp)
except Exception: except Exception:
return err("exception calling parseConfig: " & getCurrentExceptionMsg()) return err("exception calling parseConfig: " & getCurrentExceptionMsg())
@ -145,9 +134,7 @@ proc createNode(configJson: cstring):
var enrBuilder = EnrBuilder.init(privateKey) var enrBuilder = EnrBuilder.init(privateKey)
enrBuilder.withIpAddressAndPorts( enrBuilder.withIpAddressAndPorts(
netConfig.enrIp, netConfig.enrIp, netConfig.enrPort, netConfig.discv5UdpPort
netConfig.enrPort,
netConfig.discv5UdpPort
) )
if netConfig.wakuFlags.isSome(): if netConfig.wakuFlags.isSome():
@ -165,8 +152,8 @@ proc createNode(configJson: cstring):
if recordRes.isErr(): if recordRes.isErr():
let msg = "Error building enr record: " & $recordRes.error let msg = "Error building enr record: " & $recordRes.error
return err(msg) return err(msg)
else:
else: recordRes.get() recordRes.get()
## TODO: make the next const configurable from 'configJson'. ## TODO: make the next const configurable from 'configJson'.
const MAX_CONNECTIONS = 50.int const MAX_CONNECTIONS = 50.int
@ -176,9 +163,7 @@ proc createNode(configJson: cstring):
builder.withNodeKey(privateKey) builder.withNodeKey(privateKey)
builder.withRecord(record) builder.withRecord(record)
builder.withNetworkConfiguration(netConfig) builder.withNetworkConfiguration(netConfig)
builder.withSwitchConfiguration( builder.withSwitchConfiguration(maxConnections = some(MAX_CONNECTIONS))
maxConnections = some(MAX_CONNECTIONS)
)
let wakuNodeRes = builder.build() let wakuNodeRes = builder.build()
if wakuNodeRes.isErr(): if wakuNodeRes.isErr():
@ -192,36 +177,35 @@ proc createNode(configJson: cstring):
newNode.peerManager.start() newNode.peerManager.start()
if store: if store:
(await newNode.configureStore(storeNode, (
storeRetentionPolicy, await newNode.configureStore(
storeDbUrl, storeNode, storeRetentionPolicy, storeDbUrl, storeVacuum, storeDbMigration,
storeVacuum, storeMaxNumDbConnections,
storeDbMigration, )
storeMaxNumDbConnections)).isOkOr: ).isOkOr:
return err("error configuring store: " & $error) return err("error configuring store: " & $error)
return ok(newNode) return ok(newNode)
proc process*(self: ptr NodeLifecycleRequest, proc process*(
node: ptr WakuNode): Future[Result[string, string]] {.async.} = self: ptr NodeLifecycleRequest, node: ptr WakuNode
): Future[Result[string, string]] {.async.} =
defer:
destroyShared(self)
defer: destroyShared(self) case self.operation
of CREATE_NODE:
let newNodeRes = await createNode(self.configJson)
if newNodeRes.isErr():
return err(newNodeRes.error)
case self.operation: node[] = newNodeRes.get()
of CREATE_NODE: of START_NODE:
let newNodeRes = await createNode(self.configJson) await node[].start()
if newNodeRes.isErr(): of STOP_NODE:
return err(newNodeRes.error) try:
await node[].stop()
node[] = newNodeRes.get() except Exception:
return err("exception stopping node: " & getCurrentExceptionMsg())
of START_NODE:
await node[].start()
of STOP_NODE:
try:
await node[].stop()
except Exception:
return err("exception stopping node: " & getCurrentExceptionMsg())
return ok("") return ok("")

View File

@ -1,30 +1,21 @@
import std/[options, sequtils, strutils]
import chronicles, chronos, stew/results, stew/shims/net
import ../../../../waku/node/waku_node, ../../../alloc
import type PeerManagementMsgType* = enum
std/[options,sequtils,strutils] CONNECT_TO
import
chronicles,
chronos,
stew/results,
stew/shims/net
import
../../../../waku/node/waku_node,
../../../alloc
type type PeerManagementRequest* = object
PeerManagementMsgType* = enum operation: PeerManagementMsgType
CONNECT_TO peerMultiAddr: cstring
dialTimeout: Duration
type
PeerManagementRequest* = object
operation: PeerManagementMsgType
peerMultiAddr: cstring
dialTimeout: Duration
proc createShared*(T: type PeerManagementRequest,
op: PeerManagementMsgType,
peerMultiAddr: string,
dialTimeout: Duration): ptr type T =
proc createShared*(
T: type PeerManagementRequest,
op: PeerManagementMsgType,
peerMultiAddr: string,
dialTimeout: Duration,
): ptr type T =
var ret = createShared(T) var ret = createShared(T)
ret[].operation = op ret[].operation = op
ret[].peerMultiAddr = peerMultiAddr.alloc() ret[].peerMultiAddr = peerMultiAddr.alloc()
@ -35,14 +26,13 @@ proc destroyShared(self: ptr PeerManagementRequest) =
deallocShared(self[].peerMultiAddr) deallocShared(self[].peerMultiAddr)
deallocShared(self) deallocShared(self)
proc connectTo(node: WakuNode, proc connectTo(
peerMultiAddr: string, node: WakuNode, peerMultiAddr: string, dialTimeout: Duration
dialTimeout: Duration): Result[void, string] = ): Result[void, string] =
let peers = (peerMultiAddr).split(",").mapIt(strip(it)) let peers = (peerMultiAddr).split(",").mapIt(strip(it))
# TODO: the dialTimeout is not being used at all! # TODO: the dialTimeout is not being used at all!
let connectFut = node.connectToNodes(peers, source="static") let connectFut = node.connectToNodes(peers, source = "static")
while not connectFut.finished(): while not connectFut.finished():
poll() poll()
@ -52,16 +42,16 @@ proc connectTo(node: WakuNode,
return ok() return ok()
proc process*(self: ptr PeerManagementRequest, proc process*(
node: WakuNode): Future[Result[string, string]] {.async.} = self: ptr PeerManagementRequest, node: WakuNode
): Future[Result[string, string]] {.async.} =
defer:
destroyShared(self)
defer: destroyShared(self) case self.operation
of CONNECT_TO:
case self.operation: let ret = node.connectTo($self[].peerMultiAddr, self[].dialTimeout)
if ret.isErr():
of CONNECT_TO: return err(ret.error)
let ret = node.connectTo($self[].peerMultiAddr, self[].dialTimeout)
if ret.isErr():
return err(ret.error)
return ok("") return ok("")

View File

@ -1,12 +1,5 @@
import std/[options, sequtils, strutils]
import import chronicles, chronos, stew/byteutils, stew/results, stew/shims/net
std/[options,sequtils,strutils]
import
chronicles,
chronos,
stew/byteutils,
stew/results,
stew/shims/net
import import
../../../../../waku/waku_core/message/message, ../../../../../waku/waku_core/message/message,
../../../../../waku/node/waku_node, ../../../../../waku/node/waku_node,
@ -16,48 +9,46 @@ import
../../../../../waku/waku_relay/protocol, ../../../../../waku/waku_relay/protocol,
../../../../alloc ../../../../alloc
type type RelayMsgType* = enum
RelayMsgType* = enum SUBSCRIBE
SUBSCRIBE UNSUBSCRIBE
UNSUBSCRIBE PUBLISH
PUBLISH
type type ThreadSafeWakuMessage* = object
ThreadSafeWakuMessage* = object payload: SharedSeq[byte]
payload: SharedSeq[byte] contentTopic: cstring
contentTopic: cstring meta: SharedSeq[byte]
meta: SharedSeq[byte] version: uint32
version: uint32 timestamp: Timestamp
timestamp: Timestamp ephemeral: bool
ephemeral: bool when defined(rln):
when defined(rln): proof: SharedSeq[byte]
proof: SharedSeq[byte]
type type RelayRequest* = object
RelayRequest* = object operation: RelayMsgType
operation: RelayMsgType pubsubTopic: cstring
pubsubTopic: cstring relayEventCallback: WakuRelayHandler # not used in 'PUBLISH' requests
relayEventCallback: WakuRelayHandler # not used in 'PUBLISH' requests message: ThreadSafeWakuMessage # only used in 'PUBLISH' requests
message: ThreadSafeWakuMessage # only used in 'PUBLISH' requests
proc createShared*(T: type RelayRequest,
op: RelayMsgType,
pubsubTopic: PubsubTopic,
relayEventCallback: WakuRelayHandler = nil,
m = WakuMessage()): ptr type T =
proc createShared*(
T: type RelayRequest,
op: RelayMsgType,
pubsubTopic: PubsubTopic,
relayEventCallback: WakuRelayHandler = nil,
m = WakuMessage(),
): ptr type T =
var ret = createShared(T) var ret = createShared(T)
ret[].operation = op ret[].operation = op
ret[].pubsubTopic = pubsubTopic.alloc() ret[].pubsubTopic = pubsubTopic.alloc()
ret[].relayEventCallback = relayEventCallback ret[].relayEventCallback = relayEventCallback
ret[].message = ThreadSafeWakuMessage( ret[].message = ThreadSafeWakuMessage(
payload: allocSharedSeq(m.payload), payload: allocSharedSeq(m.payload),
contentTopic: m.contentTopic.alloc(), contentTopic: m.contentTopic.alloc(),
meta: allocSharedSeq(m.meta), meta: allocSharedSeq(m.meta),
version: m.version, version: m.version,
timestamp: m.timestamp, timestamp: m.timestamp,
ephemeral: m.ephemeral, ephemeral: m.ephemeral,
) )
when defined(rln): when defined(rln):
ret[].message.proof = allocSharedSeq(m.proof) ret[].message.proof = allocSharedSeq(m.proof)
@ -87,35 +78,31 @@ proc toWakuMessage(m: ThreadSafeWakuMessage): WakuMessage =
return wakuMessage return wakuMessage
proc process*(self: ptr RelayRequest, proc process*(
node: ptr WakuNode): Future[Result[string, string]] {.async.} = self: ptr RelayRequest, node: ptr WakuNode
): Future[Result[string, string]] {.async.} =
defer: destroyShared(self) defer:
destroyShared(self)
if node.wakuRelay.isNil(): if node.wakuRelay.isNil():
return err("Operation not supported without Waku Relay enabled.") return err("Operation not supported without Waku Relay enabled.")
case self.operation: case self.operation
of SUBSCRIBE:
# TO DO: properly perform 'subscribe'
discard node.wakuRelay.subscribe($self.pubsubTopic, self.relayEventCallback)
of UNSUBSCRIBE:
# TODO: properly perform 'unsubscribe'
node.wakuRelay.unsubscribeAll($self.pubsubTopic)
of PUBLISH:
let msg = self.message.toWakuMessage()
let pubsubTopic = $self.pubsubTopic
of SUBSCRIBE: let numPeers = await node.wakuRelay.publish(pubsubTopic, msg)
# TO DO: properly perform 'subscribe' if numPeers == 0:
discard node.wakuRelay.subscribe($self.pubsubTopic, self.relayEventCallback) return err("Message not sent because no peers found.")
elif numPeers > 0:
of UNSUBSCRIBE: let msgHash = computeMessageHash(pubSubTopic, msg).to0xHex
# TODO: properly perform 'unsubscribe' return ok(msgHash)
node.wakuRelay.unsubscribeAll($self.pubsubTopic)
of PUBLISH:
let msg = self.message.toWakuMessage()
let pubsubTopic = $self.pubsubTopic
let numPeers = await node.wakuRelay.publish(pubsubTopic,
msg)
if numPeers == 0:
return err("Message not sent because no peers found.")
elif numPeers > 0:
let msgHash = computeMessageHash(pubSubTopic, msg).to0xHex
return ok(msgHash)
return ok("") return ok("")

View File

@ -1,10 +1,5 @@
import std/[options, sequtils, strutils]
import import chronos, stew/results, stew/shims/net
std/[options,sequtils,strutils]
import
chronos,
stew/results,
stew/shims/net
import import
../../../../../waku/node/waku_node, ../../../../../waku/node/waku_node,
../../../../../waku/waku_archive/driver/builder, ../../../../../waku/waku_archive/driver/builder,
@ -14,36 +9,34 @@ import
../../../../alloc, ../../../../alloc,
../../../../callback ../../../../callback
type type StoreReqType* = enum
StoreReqType* = enum REMOTE_QUERY ## to perform a query to another Store node
REMOTE_QUERY ## to perform a query to another Store node LOCAL_QUERY ## to retrieve the data from 'self' node
LOCAL_QUERY ## to retrieve the data from 'self' node
type type StoreQueryRequest* = object
StoreQueryRequest* = object queryJson: cstring
queryJson: cstring peerAddr: cstring
peerAddr: cstring timeoutMs: cint
timeoutMs: cint storeCallback: WakuCallBack
storeCallback: WakuCallBack
type type StoreRequest* = object
StoreRequest* = object operation: StoreReqType
operation: StoreReqType storeReq: pointer
storeReq: pointer
proc createShared*(T: type StoreRequest, proc createShared*(
operation: StoreReqType, T: type StoreRequest, operation: StoreReqType, request: pointer
request: pointer): ptr type T = ): ptr type T =
var ret = createShared(T) var ret = createShared(T)
ret[].request = request ret[].request = request
return ret return ret
proc createShared*(T: type StoreQueryRequest, proc createShared*(
queryJson: cstring, T: type StoreQueryRequest,
peerAddr: cstring, queryJson: cstring,
timeoutMs: cint, peerAddr: cstring,
storeCallback: WakuCallBack = nil): ptr type T = timeoutMs: cint,
storeCallback: WakuCallBack = nil,
): ptr type T =
var ret = createShared(T) var ret = createShared(T)
ret[].timeoutMs = timeoutMs ret[].timeoutMs = timeoutMs
ret[].queryJson = queryJson.alloc() ret[].queryJson = queryJson.alloc()
@ -56,20 +49,23 @@ proc destroyShared(self: ptr StoreQueryRequest) =
deallocShared(self[].peerAddr) deallocShared(self[].peerAddr)
deallocShared(self) deallocShared(self)
proc process(self: ptr StoreQueryRequest, proc process(
node: ptr WakuNode): Future[Result[string, string]] {.async.} = self: ptr StoreQueryRequest, node: ptr WakuNode
defer: destroyShared(self) ): Future[Result[string, string]] {.async.} =
defer:
destroyShared(self)
proc process*(self: ptr StoreRequest, proc process*(
node: ptr WakuNode): Future[Result[string, string]] {.async.} = self: ptr StoreRequest, node: ptr WakuNode
): Future[Result[string, string]] {.async.} =
defer:
deallocShared(self)
defer: deallocShared(self) case self.operation
of REMOTE_QUERY:
case self.operation: return await cast[ptr StoreQueryRequest](self[].storeReq).process(node)
of REMOTE_QUERY: of LOCAL_QUERY:
return await cast[ptr StoreQueryRequest](self[].storeReq).process(node) discard
of LOCAL_QUERY: # cast[ptr StoreQueryRequest](request[].reqContent).process(node)
discard
# cast[ptr StoreQueryRequest](request[].reqContent).process(node)
return ok("") return ok("")

View File

@ -1,13 +1,9 @@
## This file contains the base message request type that will be handled. ## This file contains the base message request type that will be handled.
## The requests are created by the main thread and processed by ## The requests are created by the main thread and processed by
## the Waku Thread. ## the Waku Thread.
import import std/json, stew/results
std/json, import chronos
stew/results
import
chronos
import import
../../../waku/node/waku_node, ../../../waku/node/waku_node,
./requests/node_lifecycle_request, ./requests/node_lifecycle_request,
@ -16,48 +12,46 @@ import
./requests/protocols/store_request, ./requests/protocols/store_request,
./requests/debug_node_request ./requests/debug_node_request
type type RequestType* {.pure.} = enum
RequestType* {.pure.} = enum LIFECYCLE
LIFECYCLE, PEER_MANAGER
PEER_MANAGER, RELAY
RELAY, STORE
STORE, DEBUG
DEBUG,
type type InterThreadRequest* = object
InterThreadRequest* = object reqType: RequestType
reqType: RequestType reqContent: pointer
reqContent: pointer
proc createShared*(T: type InterThreadRequest, proc createShared*(
reqType: RequestType, T: type InterThreadRequest, reqType: RequestType, reqContent: pointer
reqContent: pointer): ptr type T = ): ptr type T =
var ret = createShared(T) var ret = createShared(T)
ret[].reqType = reqType ret[].reqType = reqType
ret[].reqContent = reqContent ret[].reqContent = reqContent
return ret return ret
proc process*(T: type InterThreadRequest, proc process*(
request: ptr InterThreadRequest, T: type InterThreadRequest, request: ptr InterThreadRequest, node: ptr WakuNode
node: ptr WakuNode): ): Future[Result[string, string]] {.async.} =
Future[Result[string, string]] {.async.} =
## Processes the request and deallocates its memory ## Processes the request and deallocates its memory
defer: deallocShared(request) defer:
deallocShared(request)
echo "Request received: " & $request[].reqType echo "Request received: " & $request[].reqType
let retFut = let retFut =
case request[].reqType case request[].reqType
of LIFECYCLE: of LIFECYCLE:
cast[ptr NodeLifecycleRequest](request[].reqContent).process(node) cast[ptr NodeLifecycleRequest](request[].reqContent).process(node)
of PEER_MANAGER: of PEER_MANAGER:
cast[ptr PeerManagementRequest](request[].reqContent).process(node[]) cast[ptr PeerManagementRequest](request[].reqContent).process(node[])
of RELAY: of RELAY:
cast[ptr RelayRequest](request[].reqContent).process(node) cast[ptr RelayRequest](request[].reqContent).process(node)
of STORE: of STORE:
cast[ptr StoreRequest](request[].reqContent).process(node) cast[ptr StoreRequest](request[].reqContent).process(node)
of DEBUG: of DEBUG:
cast[ptr DebugNodeRequest](request[].reqContent).process(node[]) cast[ptr DebugNodeRequest](request[].reqContent).process(node[])
return await retFut return await retFut

View File

@ -1,26 +1,21 @@
## This file contains the base message response type that will be handled. ## This file contains the base message response type that will be handled.
## The response will be created from the Waku Thread and processed in ## The response will be created from the Waku Thread and processed in
## the main thread. ## the main thread.
import import std/json, stew/results
std/json, import ../../alloc
stew/results
import
../../alloc
type type ResponseType {.pure.} = enum
ResponseType {.pure.} = enum OK
OK, ERR
ERR,
type type InterThreadResponse* = object
InterThreadResponse* = object respType: ResponseType
respType: ResponseType content: cstring
content: cstring
proc createShared*(T: type InterThreadResponse, proc createShared*(
res: Result[string, string]): ptr type T = T: type InterThreadResponse, res: Result[string, string]
): ptr type T =
## Converts a `Result[string, string]` into a `ptr InterThreadResponse` ## Converts a `Result[string, string]` into a `ptr InterThreadResponse`
## so that it can be transfered to another thread in a safe way. ## so that it can be transfered to another thread in a safe way.
@ -35,9 +30,9 @@ proc createShared*(T: type InterThreadResponse,
ret[].content = res.error.alloc() ret[].content = res.error.alloc()
return ret return ret
proc process*(T: type InterThreadResponse, proc process*(
resp: ptr InterThreadResponse): T: type InterThreadResponse, resp: ptr InterThreadResponse
Result[string, string] = ): Result[string, string] =
## Converts the received `ptr InterThreadResponse` into a ## Converts the received `ptr InterThreadResponse` into a
## `Result[string, string]`. Notice that the response is expected to be ## `Result[string, string]`. Notice that the response is expected to be
## allocated from the Waku Thread and deallocated by the main thread. ## allocated from the Waku Thread and deallocated by the main thread.
@ -47,7 +42,7 @@ proc process*(T: type InterThreadResponse,
deallocShared(resp) deallocShared(resp)
case resp[].respType case resp[].respType
of OK: of OK:
return ok($resp[].content) return ok($resp[].content)
of ERR: of ERR:
return err($resp[].content) return err($resp[].content)

View File

@ -1,10 +1,8 @@
{.pragma: exported, exportc, cdecl, raises: [].} {.pragma: exported, exportc, cdecl, raises: [].}
{.pragma: callback, cdecl, raises: [], gcsafe.} {.pragma: callback, cdecl, raises: [], gcsafe.}
{.passc: "-fPIC".} {.passc: "-fPIC".}
import import std/[json, sequtils, times, strformat, options, atomics, strutils, os]
std/[json,sequtils,times,strformat,options,atomics,strutils,os]
import import
chronicles, chronicles,
chronos, chronos,
@ -14,20 +12,19 @@ import
stew/shims/net stew/shims/net
import import
../../../waku/node/waku_node, ../../../waku/node/waku_node,
../events/[json_message_event,json_base_event], ../events/[json_message_event, json_base_event],
./inter_thread_communication/waku_thread_request, ./inter_thread_communication/waku_thread_request,
./inter_thread_communication/waku_thread_response ./inter_thread_communication/waku_thread_response
type type Context* = object
Context* = object thread: Thread[(ptr Context)]
thread: Thread[(ptr Context)] reqChannel: ChannelSPSCSingle[ptr InterThreadRequest]
reqChannel: ChannelSPSCSingle[ptr InterThreadRequest] reqSignal: ThreadSignalPtr
reqSignal: ThreadSignalPtr respChannel: ChannelSPSCSingle[ptr InterThreadResponse]
respChannel: ChannelSPSCSingle[ptr InterThreadResponse] respSignal: ThreadSignalPtr
respSignal: ThreadSignalPtr userData*: pointer
userData*: pointer eventCallback*: pointer
eventCallback*: pointer eventUserdata*: pointer
eventUserdata*: pointer
# To control when the thread is running # To control when the thread is running
var running: Atomic[bool] var running: Atomic[bool]
@ -40,7 +37,8 @@ var initialized: Atomic[bool]
proc waku_init() = proc waku_init() =
if not initialized.exchange(true): if not initialized.exchange(true):
NimMain() # Every Nim library needs to call `NimMain` once exactly NimMain() # Every Nim library needs to call `NimMain` once exactly
when declared(setupForeignThreadGc): setupForeignThreadGc() when declared(setupForeignThreadGc):
setupForeignThreadGc()
when declared(nimGC_setStackBottom): when declared(nimGC_setStackBottom):
var locals {.volatile, noinit.}: pointer var locals {.volatile, noinit.}: pointer
locals = addr(locals) locals = addr(locals)
@ -59,8 +57,7 @@ proc run(ctx: ptr Context) {.thread.} =
waitFor ctx.reqSignal.wait() waitFor ctx.reqSignal.wait()
let recvOk = ctx.reqChannel.tryRecv(request) let recvOk = ctx.reqChannel.tryRecv(request)
if recvOk == true: if recvOk == true:
let resultResponse = let resultResponse = waitFor InterThreadRequest.process(request, addr node)
waitFor InterThreadRequest.process(request, addr node)
## Converting a `Result` into a thread-safe transferable response type ## Converting a `Result` into a thread-safe transferable response type
let threadSafeResp = InterThreadResponse.createShared(resultResponse) let threadSafeResp = InterThreadResponse.createShared(resultResponse)
@ -106,10 +103,9 @@ proc stopWakuThread*(ctx: ptr Context): Result[void, string] =
freeShared(ctx) freeShared(ctx)
return ok() return ok()
proc sendRequestToWakuThread*(ctx: ptr Context, proc sendRequestToWakuThread*(
reqType: RequestType, ctx: ptr Context, reqType: RequestType, reqContent: pointer
reqContent: pointer): Result[string, string] = ): Result[string, string] =
let req = InterThreadRequest.createShared(reqType, reqContent) let req = InterThreadRequest.createShared(reqType, reqContent)
## Sending the request ## Sending the request

View File

@ -1,4 +1,5 @@
const ContentScriptVersion_1* = """ const ContentScriptVersion_1* =
"""
CREATE TABLE IF NOT EXISTS messages ( CREATE TABLE IF NOT EXISTS messages (
pubsubTopic VARCHAR NOT NULL, pubsubTopic VARCHAR NOT NULL,
contentTopic VARCHAR NOT NULL, contentTopic VARCHAR NOT NULL,

View File

@ -1,4 +1,5 @@
const ContentScriptVersion_2* = """ const ContentScriptVersion_2* =
"""
ALTER TABLE IF EXISTS messages_backup RENAME TO messages; ALTER TABLE IF EXISTS messages_backup RENAME TO messages;
ALTER TABLE messages RENAME TO messages_backup; ALTER TABLE messages RENAME TO messages_backup;
ALTER TABLE messages_backup DROP CONSTRAINT messageIndex; ALTER TABLE messages_backup DROP CONSTRAINT messageIndex;

View File

@ -1,37 +1,22 @@
import content_script_version_1, content_script_version_2
import type MigrationScript* = object
content_script_version_1, version*: int
content_script_version_2 scriptContent*: string
type proc init*(T: type MigrationScript, targetVersion: int, scriptContent: string): T =
MigrationScript* = object return MigrationScript(targetVersion: targetVersion, scriptContent: scriptContent)
version*: int
scriptContent*: string
proc init*(T: type MigrationScript, const PgMigrationScripts* =
targetVersion: int, @[
scriptContent: string): T = MigrationScript(version: 1, scriptContent: ContentScriptVersion_1),
MigrationScript(version: 2, scriptContent: ContentScriptVersion_2),
]
return MigrationScript( proc getMigrationScripts*(currentVersion: int64, targetVersion: int64): seq[string] =
targetVersion: targetVersion,
scriptContent: scriptContent)
const PgMigrationScripts* = @[
MigrationScript(
version: 1,
scriptContent: ContentScriptVersion_1),
MigrationScript(
version: 2,
scriptContent: ContentScriptVersion_2)
]
proc getMigrationScripts*(currentVersion: int64,
targetVersion: int64): seq[string] =
var ret = newSeq[string]() var ret = newSeq[string]()
var v = currentVersion var v = currentVersion
while v < targetVersion: while v < targetVersion:
ret.add(PgMigrationScripts[v].scriptContent) ret.add(PgMigrationScripts[v].scriptContent)
v.inc() v.inc()
return ret return ret

View File

@ -21,11 +21,10 @@ import
const os* {.strdefine.} = "" const os* {.strdefine.} = ""
when os == "Linux" and when os == "Linux" and
# GitHub only supports container actions on Linux # GitHub only supports container actions on Linux
# and we need to start a postgress database in a docker container # and we need to start a postgress database in a docker container
defined(postgres): defined(postgres):
import import ./waku_archive/test_driver_postgres_query, ./waku_archive/test_driver_postgres
./waku_archive/test_driver_postgres_query, ./waku_archive/test_driver_postgres
# Waku store test suite # Waku store test suite
import import

View File

@ -1,37 +1,31 @@
{.used.} {.used.}
import import std/strutils, stew/[results, byteutils], testutils/unittests
std/strutils, import ../../waku/common/base64
stew/[results, byteutils],
testutils/unittests
import
../../waku/common/base64
suite "Waku Common - stew base64 wrapper": suite "Waku Common - stew base64 wrapper":
const TestData = @[ const TestData =
# Test vectors from RFC 4648 @[
# See: https://datatracker.ietf.org/doc/html/rfc4648#section-10 # Test vectors from RFC 4648
("", Base64String("")), # See: https://datatracker.ietf.org/doc/html/rfc4648#section-10
("f", Base64String("Zg==")), ("", Base64String("")),
("fo", Base64String("Zm8=")), ("f", Base64String("Zg==")),
("foo", Base64String("Zm9v")), ("fo", Base64String("Zm8=")),
("foob", Base64String("Zm9vYg==")), ("foo", Base64String("Zm9v")),
("fooba", Base64String("Zm9vYmE=")), ("foob", Base64String("Zm9vYg==")),
("foobar", Base64String("Zm9vYmFy")), ("fooba", Base64String("Zm9vYmE=")),
("foobar", Base64String("Zm9vYmFy")),
# Custom test vectors # Custom test vectors
("\x01", Base64String("AQ==")), ("\x01", Base64String("AQ==")),
("\x13", Base64String("Ew==")), ("\x13", Base64String("Ew==")),
("\x01\x02\x03\x04", Base64String("AQIDBA==")) ("\x01\x02\x03\x04", Base64String("AQIDBA==")),
] ]
for (plaintext, encoded) in TestData: for (plaintext, encoded) in TestData:
test "encode into base64 (" & escape(plaintext) & " -> \"" & string(encoded) & "\")": test "encode into base64 (" & escape(plaintext) & " -> \"" & string(encoded) & "\")":
## Given ## Given
let data = plaintext let data = plaintext
## When ## When
let encodedData = base64.encode(data) let encodedData = base64.encode(data)
@ -40,7 +34,6 @@ suite "Waku Common - stew base64 wrapper":
check: check:
encodedData == encoded encodedData == encoded
test "decode from base64 (\"" & string(encoded) & "\" -> " & escape(plaintext) & ")": test "decode from base64 (\"" & string(encoded) & "\" -> " & escape(plaintext) & ")":
## Given ## Given
let data = encoded let data = encoded
@ -55,4 +48,3 @@ suite "Waku Common - stew base64 wrapper":
let decoded = decodedRes.tryGet() let decoded = decodedRes.tryGet()
check: check:
decoded == toBytes(plaintext) decoded == toBytes(plaintext)

View File

@ -15,31 +15,28 @@ import
type ConfResult[T] = Result[T, string] type ConfResult[T] = Result[T, string]
type TestConf = object type TestConf = object
configFile* {. configFile* {.desc: "Configuration file path", name: "config-file".}:
desc: "Configuration file path" Option[InputFile]
name: "config-file" }: Option[InputFile]
testFile* {. testFile* {.desc: "Configuration test file path", name: "test-file".}:
desc: "Configuration test file path" Option[InputFile]
name: "test-file" }: Option[InputFile]
listenAddress* {. listenAddress* {.
defaultValue: parseIpAddress("127.0.0.1"), defaultValue: parseIpAddress("127.0.0.1"),
desc: "Listening address", desc: "Listening address",
name: "listen-address"}: IpAddress name: "listen-address"
.}: IpAddress
tcpPort* {. tcpPort* {.desc: "TCP listening port", defaultValue: 60000, name: "tcp-port".}: Port
desc: "TCP listening port",
defaultValue: 60000,
name: "tcp-port" }: Port
{.push warning[ProveInit]: off.} {.push warning[ProveInit]: off.}
proc load*(T: type TestConf, prefix: string): ConfResult[T] = proc load*(T: type TestConf, prefix: string): ConfResult[T] =
try: try:
let conf = TestConf.load( let conf = TestConf.load(
secondarySources = proc (conf: TestConf, sources: auto) secondarySources = proc(
{.gcsafe, raises: [ConfigurationError].} = conf: TestConf, sources: auto
) {.gcsafe, raises: [ConfigurationError].} =
sources.addConfigFile(Envvar, InputFile(prefix)) sources.addConfigFile(Envvar, InputFile(prefix))
) )
ok(conf) ok(conf)

View File

@ -1,17 +1,9 @@
{.used.} {.used.}
import import std/options, stew/results, stew/shims/net, testutils/unittests
std/options, import ../../waku/common/enr, ../testlib/wakucore
stew/results,
stew/shims/net,
testutils/unittests
import
../../waku/common/enr,
../testlib/wakucore
suite "nim-eth ENR - builder and typed record": suite "nim-eth ENR - builder and typed record":
test "Non-supported private key (ECDSA)": test "Non-supported private key (ECDSA)":
## Given ## Given
let privateKey = generateEcdsaKey() let privateKey = generateEcdsaKey()
@ -45,28 +37,28 @@ suite "nim-eth ENR - builder and typed record":
publicKey.isSome() publicKey.isSome()
@(publicKey.get()) == expectedPubKey @(publicKey.get()) == expectedPubKey
suite "nim-eth ENR - Ext: IP address and TCP/UDP ports": suite "nim-eth ENR - Ext: IP address and TCP/UDP ports":
test "EIP-778 test vector": test "EIP-778 test vector":
## Given ## Given
# Test vector from EIP-778 # Test vector from EIP-778
# See: https://eips.ethereum.org/EIPS/eip-778#test-vectors # See: https://eips.ethereum.org/EIPS/eip-778#test-vectors
let expectedEnr = "-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04j" & let expectedEnr =
"RzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJ" & "-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04j" &
"c2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0x" & "RzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJ" &
"OIN1ZHCCdl8" "c2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0x" & "OIN1ZHCCdl8"
let let
seqNum = 1u64 seqNum = 1u64
privateKey = ethSecp256k1Key("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") privateKey = ethSecp256k1Key(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
)
enrIpAddr = parseIpAddress("127.0.0.1") enrIpAddr = parseIpAddress("127.0.0.1")
enrUdpPort = Port(30303) enrUdpPort = Port(30303)
## When ## When
var builder = EnrBuilder.init(privateKey, seqNum) var builder = EnrBuilder.init(privateKey, seqNum)
builder.withIpAddressAndPorts(ipAddr=some(enrIpAddr), udpPort=some(enrUdpPort)) builder.withIpAddressAndPorts(ipAddr = some(enrIpAddr), udpPort = some(enrUdpPort))
let enrRes = builder.build() let enrRes = builder.build()
@ -89,10 +81,7 @@ suite "nim-eth ENR - Ext: IP address and TCP/UDP ports":
## When ## When
var builder = EnrBuilder.init(privateKey, seqNum) var builder = EnrBuilder.init(privateKey, seqNum)
builder.withIpAddressAndPorts( builder.withIpAddressAndPorts(ipAddr = some(enrIpAddr), tcpPort = some(enrTcpPort))
ipAddr=some(enrIpAddr),
tcpPort=some(enrTcpPort),
)
let enrRes = builder.build() let enrRes = builder.build()
@ -119,10 +108,7 @@ suite "nim-eth ENR - Ext: IP address and TCP/UDP ports":
## When ## When
var builder = EnrBuilder.init(privateKey, seqNum) var builder = EnrBuilder.init(privateKey, seqNum)
builder.withIpAddressAndPorts( builder.withIpAddressAndPorts(ipAddr = some(enrIpAddr), udpPort = some(enrUdpPort))
ipAddr=some(enrIpAddr),
udpPort=some(enrUdpPort),
)
let enrRes = builder.build() let enrRes = builder.build()

View File

@ -1,10 +1,7 @@
{.used.} {.used.}
import import testutils/unittests
testutils/unittests import ../../waku/common/envvar_serialization/utils
import
../../waku/common/envvar_serialization/utils
suite "nim-envvar-serialization - utils": suite "nim-envvar-serialization - utils":
test "construct env var key": test "construct env var key":

View File

@ -1,10 +1,7 @@
{.used.} {.used.}
import import testutils/unittests, stew/results
testutils/unittests, import ../../waku/common/utils/parse_size_units
stew/results
import
../../waku/common/utils/parse_size_units
suite "Size serialization test": suite "Size serialization test":
test "parse normal sizes": test "parse normal sizes":

View File

@ -1,18 +1,14 @@
{.used.} {.used.}
import import testutils/unittests
testutils/unittests import ../../waku/common/protobuf
import
../../waku/common/protobuf
## Fixtures ## Fixtures
const MaxTestRpcFieldLen = 5 const MaxTestRpcFieldLen = 5
type TestRpc = object type TestRpc = object
testField*: string testField*: string
proc init(T: type TestRpc, field: string): T = proc init(T: type TestRpc, field: string): T =
T(testField: field) T(testField: field)
@ -40,11 +36,9 @@ proc decode(T: type TestRpc, buf: seq[byte]): ProtobufResult[T] =
ok(TestRpc.init(field)) ok(TestRpc.init(field))
## Tests ## Tests
suite "Waku Common - libp2p minprotobuf wrapper": suite "Waku Common - libp2p minprotobuf wrapper":
test "serialize and deserialize - valid length field": test "serialize and deserialize - valid length field":
## Given ## Given
let field = "12345" let field = "12345"
@ -82,10 +76,9 @@ suite "Waku Common - libp2p minprotobuf wrapper":
error.kind == ProtobufErrorKind.MissingRequiredField error.kind == ProtobufErrorKind.MissingRequiredField
error.field == "test_field" error.field == "test_field"
test "serialize and deserialize - invalid length field": test "serialize and deserialize - invalid length field":
## Given ## Given
let field = "123456" # field.len = MaxTestRpcFieldLen + 1 let field = "123456" # field.len = MaxTestRpcFieldLen + 1
let rpc = TestRpc.init(field) let rpc = TestRpc.init(field)

View File

@ -1,19 +1,12 @@
{.used.} {.used.}
import import std/[strutils, os], stew/results, testutils/unittests
std/[strutils, os], import ../../waku/common/databases/db_sqlite {.all.}, ../waku_archive/archive_utils
stew/results,
testutils/unittests
import
../../waku/common/databases/db_sqlite {.all.},
../waku_archive/archive_utils
template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0]
template sourceDir(): string =
currentSourcePath.rsplit(DirSep, 1)[0]
suite "SQLite - migrations": suite "SQLite - migrations":
test "set and get user version": test "set and get user version":
## Given ## Given
let database = newSqliteDatabase() let database = newSqliteDatabase()
@ -36,16 +29,17 @@ suite "SQLite - migrations":
test "filter and order migration script file paths": test "filter and order migration script file paths":
## Given ## Given
let paths = @[ let paths =
sourceDir / "00001_valid.up.sql", @[
sourceDir / "00002_alsoValidWithUpperCaseExtension.UP.SQL", sourceDir / "00001_valid.up.sql",
sourceDir / "00007_unorderedValid.up.sql", sourceDir / "00002_alsoValidWithUpperCaseExtension.UP.SQL",
sourceDir / "00003_validRepeated.up.sql", sourceDir / "00007_unorderedValid.up.sql",
sourceDir / "00003_validRepeated.up.sql", sourceDir / "00003_validRepeated.up.sql",
sourceDir / "00666_noMigrationScript.bmp", sourceDir / "00003_validRepeated.up.sql",
sourceDir / "00X00_invalidVersion.down.sql", sourceDir / "00666_noMigrationScript.bmp",
sourceDir / "00008_notWithinVersionRange.up.sql", sourceDir / "00X00_invalidVersion.down.sql",
] sourceDir / "00008_notWithinVersionRange.up.sql",
]
let let
lowerVersion = 0 lowerVersion = 0
@ -53,29 +47,33 @@ suite "SQLite - migrations":
## When ## When
var migrationSciptPaths: seq[string] var migrationSciptPaths: seq[string]
migrationSciptPaths = filterMigrationScripts(paths, lowerVersion, highVersion, direction="up") migrationSciptPaths =
filterMigrationScripts(paths, lowerVersion, highVersion, direction = "up")
migrationSciptPaths = sortMigrationScripts(migrationSciptPaths) migrationSciptPaths = sortMigrationScripts(migrationSciptPaths)
## Then ## Then
check: check:
migrationSciptPaths == @[ migrationSciptPaths ==
sourceDir / "00001_valid.up.sql", @[
sourceDir / "00002_alsoValidWithUpperCaseExtension.UP.SQL", sourceDir / "00001_valid.up.sql",
sourceDir / "00003_validRepeated.up.sql", sourceDir / "00002_alsoValidWithUpperCaseExtension.UP.SQL",
sourceDir / "00003_validRepeated.up.sql", sourceDir / "00003_validRepeated.up.sql",
sourceDir / "00007_unorderedValid.up.sql" sourceDir / "00003_validRepeated.up.sql",
] sourceDir / "00007_unorderedValid.up.sql",
]
test "break migration scripts into queries": test "break migration scripts into queries":
## Given ## Given
let statement1 = """CREATE TABLE contacts1 ( let statement1 =
"""CREATE TABLE contacts1 (
contact_id INTEGER PRIMARY KEY, contact_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL, last_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE,
phone TEXT NOT NULL UNIQUE phone TEXT NOT NULL UNIQUE
);""" );"""
let statement2 = """CREATE TABLE contacts2 ( let statement2 =
"""CREATE TABLE contacts2 (
contact_id INTEGER PRIMARY KEY, contact_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL, last_name TEXT NOT NULL,
@ -89,18 +87,20 @@ suite "SQLite - migrations":
## Then ## Then
check: check:
statements == @[statement1, statement2] statements == @[statement1, statement2]
test "break statements script into queries - empty statements": test "break statements script into queries - empty statements":
## Given ## Given
let statement1 = """CREATE TABLE contacts1 ( let statement1 =
"""CREATE TABLE contacts1 (
contact_id INTEGER PRIMARY KEY, contact_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL, last_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE,
phone TEXT NOT NULL UNIQUE phone TEXT NOT NULL UNIQUE
);""" );"""
let statement2 = """CREATE TABLE contacts2 ( let statement2 =
"""CREATE TABLE contacts2 (
contact_id INTEGER PRIMARY KEY, contact_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL, last_name TEXT NOT NULL,

View File

@ -1,13 +1,8 @@
{.used.} {.used.}
import import testutils/unittests, chronos
testutils/unittests,
chronos
import import ../testlib/wakunode, ../../waku/factory/node_factory, ../../waku/waku_node
../testlib/wakunode,
../../waku/factory/node_factory,
../../waku/waku_node
suite "Node Factory": suite "Node Factory":
test "Set up a node based on default configurations": test "Set up a node based on default configurations":
@ -37,31 +32,32 @@ suite "Node Factory":
not node.wakuArchive.isNil() not node.wakuArchive.isNil()
test "Set up a node with Filter enabled": test "Set up a node with Filter enabled":
var conf = defaultTestWakuNodeConf() var conf = defaultTestWakuNodeConf()
conf.filter = true conf.filter = true
let node = setupNode(conf).valueOr: let node = setupNode(conf).valueOr:
raiseAssert error raiseAssert error
check: check:
not node.isNil() not node.isNil()
not node.wakuFilter.isNil() not node.wakuFilter.isNil()
test "Start a node based on default configurations": test "Start a node based on default configurations":
let conf = defaultTestWakuNodeConf() let conf = defaultTestWakuNodeConf()
let node = setupNode(conf).valueOr: let node = setupNode(conf).valueOr:
raiseAssert error raiseAssert error
assert not node.isNil(), "Node can't be nil" assert not node.isNil(), "Node can't be nil"
let startRes = catch: (waitFor startNode(node, conf)) let startRes = catch:
(waitFor startNode(node, conf))
assert not startRes.isErr(), "Exception starting node" assert not startRes.isErr(), "Exception starting node"
assert startRes.get().isOk(), "Error starting node " & startRes.get().error assert startRes.get().isOk(), "Error starting node " & startRes.get().error
check: check:
node.started == true node.started == true
## Cleanup ## Cleanup
waitFor node.stop() waitFor node.stop()

View File

@ -17,9 +17,7 @@ suite "PeerStorage":
suite "getAll": suite "getAll":
test "unimplemented": test "unimplemented":
let let emptyClosure = proc(remotePeerInfo: RemotePeerInfo) =
emptyClosure = discard
proc(remotePeerInfo: RemotePeerInfo) =
discard
check: check:
peerStorage.getAll(emptyClosure) == PeerStorageResult[void].err("Unimplemented") peerStorage.getAll(emptyClosure) == PeerStorageResult[void].err("Unimplemented")

View File

@ -13,23 +13,22 @@ import
../../../../waku/node/peer_manager/peer_store/waku_peer_storage ../../../../waku/node/peer_manager/peer_store/waku_peer_storage
proc `==`(a, b: RemotePeerInfo): bool = proc `==`(a, b: RemotePeerInfo): bool =
let let comparisons =
comparisons = @[
@[ a.peerId == b.peerId,
a.peerId == b.peerId, a.addrs == b.addrs,
a.addrs == b.addrs, a.enr == b.enr,
a.enr == b.enr, a.protocols == b.protocols,
a.protocols == b.protocols, a.agent == b.agent,
a.agent == b.agent, a.protoVersion == b.protoVersion,
a.protoVersion == b.protoVersion, a.publicKey == b.publicKey,
a.publicKey == b.publicKey, a.connectedness == b.connectedness,
a.connectedness == b.connectedness, a.disconnectTime == b.disconnectTime,
a.disconnectTime == b.disconnectTime, a.origin == b.origin,
a.origin == b.origin, a.direction == b.direction,
a.direction == b.direction, a.lastFailedConn == b.lastFailedConn,
a.lastFailedConn == b.lastFailedConn, a.numberFailedConn == b.numberFailedConn,
a.numberFailedConn == b.numberFailedConn ]
]
allIt(comparisons, it == true) allIt(comparisons, it == true)
@ -65,19 +64,18 @@ suite "Protobuf Serialisation":
suite "encode": suite "encode":
test "simple": test "simple":
# Given the expected bytes representation of a valid RemotePeerInfo # Given the expected bytes representation of a valid RemotePeerInfo
let let expectedBuffer: seq[byte] =
expectedBuffer: seq[byte] = @[
@[ 10, 39, 0, 37, 8, 2, 18, 33, 3, 43, 246, 238, 219, 109, 147, 79, 129, 40, 145,
10, 39, 0, 37, 8, 2, 18, 33, 3, 43, 246, 238, 219, 109, 147, 79, 129, 40, 217, 209, 109, 105, 185, 186, 200, 180, 203, 72, 166, 220, 196, 232, 170, 74,
145, 217, 209, 109, 105, 185, 186, 200, 180, 203, 72, 166, 220, 196, 232, 141, 125, 255, 112, 238, 204, 18, 8, 4, 192, 168, 0, 1, 6, 31, 144, 34, 95, 8,
170, 74, 141, 125, 255, 112, 238, 204, 18, 8, 4, 192, 168, 0, 1, 6, 31, 144, 3, 18, 91, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134,
34, 95, 8, 3, 18, 91, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 222, 61, 48, 15, 163, 106, 224, 232, 245,
8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 222, 61, 48, 15, 163, 106, 213, 48, 137, 157, 131, 171, 171, 68, 171, 243, 22, 31, 22, 42, 75, 201, 1,
224, 232, 245, 213, 48, 137, 157, 131, 171, 171, 68, 171, 243, 22, 31, 22, 216, 230, 236, 218, 2, 14, 139, 109, 95, 141, 163, 5, 37, 231, 29, 104, 81,
42, 75, 201, 1, 216, 230, 236, 218, 2, 14, 139, 109, 95, 141, 163, 5, 37, 81, 12, 9, 142, 92, 71, 198, 70, 165, 151, 251, 77, 206, 192, 52, 233, 247,
231, 29, 104, 81, 81, 12, 9, 142, 92, 71, 198, 70, 165, 151, 251, 77, 206, 124, 64, 158, 98, 40, 0, 48, 0,
192, 52, 233, 247, 124, 64, 158, 98, 40, 0, 48, 0 ]
]
# When converting a valid RemotePeerInfo to a ProtoBuffer # When converting a valid RemotePeerInfo to a ProtoBuffer
let encodedRemotePeerInfo = encode(remotePeerInfo).get() let encodedRemotePeerInfo = encode(remotePeerInfo).get()
@ -92,19 +90,18 @@ suite "Protobuf Serialisation":
suite "decode": suite "decode":
test "simple": test "simple":
# Given the bytes representation of a valid RemotePeerInfo # Given the bytes representation of a valid RemotePeerInfo
let let buffer: seq[byte] =
buffer: seq[byte] = @[
@[ 10, 39, 0, 37, 8, 2, 18, 33, 3, 43, 246, 238, 219, 109, 147, 79, 129, 40, 145,
10, 39, 0, 37, 8, 2, 18, 33, 3, 43, 246, 238, 219, 109, 147, 79, 129, 40, 217, 209, 109, 105, 185, 186, 200, 180, 203, 72, 166, 220, 196, 232, 170, 74,
145, 217, 209, 109, 105, 185, 186, 200, 180, 203, 72, 166, 220, 196, 232, 141, 125, 255, 112, 238, 204, 18, 8, 4, 192, 168, 0, 1, 6, 31, 144, 34, 95, 8,
170, 74, 141, 125, 255, 112, 238, 204, 18, 8, 4, 192, 168, 0, 1, 6, 31, 144, 3, 18, 91, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134,
34, 95, 8, 3, 18, 91, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 222, 61, 48, 15, 163, 106, 224, 232, 245,
8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 222, 61, 48, 15, 163, 106, 213, 48, 137, 157, 131, 171, 171, 68, 171, 243, 22, 31, 22, 42, 75, 201, 1,
224, 232, 245, 213, 48, 137, 157, 131, 171, 171, 68, 171, 243, 22, 31, 22, 216, 230, 236, 218, 2, 14, 139, 109, 95, 141, 163, 5, 37, 231, 29, 104, 81,
42, 75, 201, 1, 216, 230, 236, 218, 2, 14, 139, 109, 95, 141, 163, 5, 37, 81, 12, 9, 142, 92, 71, 198, 70, 165, 151, 251, 77, 206, 192, 52, 233, 247,
231, 29, 104, 81, 81, 12, 9, 142, 92, 71, 198, 70, 165, 151, 251, 77, 206, 124, 64, 158, 98, 40, 0, 48, 0,
192, 52, 233, 247, 124, 64, 158, 98, 40, 0, 48, 0 ]
]
# When converting a valid buffer to RemotePeerInfo # When converting a valid buffer to RemotePeerInfo
let decodedRemotePeerInfo = RemotePeerInfo.decode(buffer).get() let decodedRemotePeerInfo = RemotePeerInfo.decode(buffer).get()

View File

@ -16,7 +16,7 @@ import
node/waku_node, node/waku_node,
waku_filter_v2, waku_filter_v2,
waku_filter_v2/client, waku_filter_v2/client,
waku_filter_v2/subscriptions waku_filter_v2/subscriptions,
], ],
../testlib/[common, wakucore, wakunode, testasync, futures, testutils] ../testlib/[common, wakucore, wakunode, testasync, futures, testutils]
@ -34,11 +34,10 @@ suite "Waku Filter - End to End":
asyncSetup: asyncSetup:
pushHandlerFuture = newFuture[(string, WakuMessage)]() pushHandlerFuture = newFuture[(string, WakuMessage)]()
messagePushHandler = messagePushHandler = proc(
proc(pubsubTopic: PubsubTopic, message: WakuMessage): Future[void] {. pubsubTopic: PubsubTopic, message: WakuMessage
async, closure, gcsafe ): Future[void] {.async, closure, gcsafe.} =
.} = pushHandlerFuture.complete((pubsubTopic, message))
pushHandlerFuture.complete((pubsubTopic, message))
pubsubTopic = DefaultPubsubTopic pubsubTopic = DefaultPubsubTopic
contentTopic = DefaultContentTopic contentTopic = DefaultContentTopic
@ -72,11 +71,9 @@ suite "Waku Filter - End to End":
asyncTest "Client Node receives Push from Server Node, via Filter": asyncTest "Client Node receives Push from Server Node, via Filter":
# When a client node subscribes to a filter node # When a client node subscribes to a filter node
let let subscribeResponse = await client.filterSubscribe(
subscribeResponse = some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
await client.filterSubscribe( )
some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
)
# Then the subscription is successful # Then the subscription is successful
check: check:
@ -96,11 +93,9 @@ suite "Waku Filter - End to End":
pushedMsg1 == msg1 pushedMsg1 == msg1
# When unsubscribing from the subscription # When unsubscribing from the subscription
let let unsubscribeResponse = await client.filterUnsubscribe(
unsubscribeResponse = some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
await client.filterUnsubscribe( )
some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
)
# Then the unsubscription is successful # Then the unsubscription is successful
check: check:
@ -121,11 +116,9 @@ suite "Waku Filter - End to End":
await server.mountRelay() await server.mountRelay()
# And valid filter subscription # And valid filter subscription
let let subscribeResponse = await client.filterSubscribe(
subscribeResponse = some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
await client.filterSubscribe( )
some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
)
require: require:
subscribeResponse.isOk() subscribeResponse.isOk()
server.wakuFilter.subscriptions.subscribedPeerCount() == 1 server.wakuFilter.subscriptions.subscribedPeerCount() == 1
@ -149,22 +142,18 @@ suite "Waku Filter - End to End":
let serverRemotePeerInfo = server.peerInfo.toRemotePeerInfo() let serverRemotePeerInfo = server.peerInfo.toRemotePeerInfo()
# When a client node subscribes to the server node # When a client node subscribes to the server node
let let subscribeResponse = await client.filterSubscribe(
subscribeResponse = some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
await client.filterSubscribe( )
some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
)
# Then the subscription is successful # Then the subscription is successful
check (not subscribeResponse.isOk()) check (not subscribeResponse.isOk())
asyncTest "Filter Client Node can receive messages after subscribing and restarting, via Filter": asyncTest "Filter Client Node can receive messages after subscribing and restarting, via Filter":
# Given a valid filter subscription # Given a valid filter subscription
let let subscribeResponse = await client.filterSubscribe(
subscribeResponse = some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
await client.filterSubscribe( )
some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
)
require: require:
subscribeResponse.isOk() subscribeResponse.isOk()
server.wakuFilter.subscriptions.subscribedPeerCount() == 1 server.wakuFilter.subscriptions.subscribedPeerCount() == 1
@ -188,11 +177,9 @@ suite "Waku Filter - End to End":
await server.mountRelay() await server.mountRelay()
# Given a valid filter subscription # Given a valid filter subscription
let let subscribeResponse = await client.filterSubscribe(
subscribeResponse = some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
await client.filterSubscribe( )
some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
)
require: require:
subscribeResponse.isOk() subscribeResponse.isOk()
server.wakuFilter.subscriptions.subscribedPeerCount() == 1 server.wakuFilter.subscriptions.subscribedPeerCount() == 1
@ -209,11 +196,9 @@ suite "Waku Filter - End to End":
check (not await pushHandlerFuture.withTimeout(FUTURE_TIMEOUT)) check (not await pushHandlerFuture.withTimeout(FUTURE_TIMEOUT))
# Given the client refreshes the subscription # Given the client refreshes the subscription
let let subscribeResponse2 = await clientClone.filterSubscribe(
subscribeResponse2 = some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
await clientClone.filterSubscribe( )
some(pubsubTopic), contentTopicSeq, serverRemotePeerInfo
)
check: check:
subscribeResponse2.isOk() subscribeResponse2.isOk()
server.wakuFilter.subscriptions.subscribedPeerCount() == 1 server.wakuFilter.subscriptions.subscribedPeerCount() == 1

View File

@ -21,7 +21,7 @@ import
waku_lightpush/common, waku_lightpush/common,
waku_lightpush/client, waku_lightpush/client,
waku_lightpush/protocol_metrics, waku_lightpush/protocol_metrics,
waku_lightpush/rpc waku_lightpush/rpc,
], ],
../testlib/[assertions, common, wakucore, wakunode, testasync, futures, testutils] ../testlib/[assertions, common, wakucore, wakunode, testasync, futures, testutils]
@ -40,12 +40,11 @@ suite "Waku Lightpush - End To End":
asyncSetup: asyncSetup:
handlerFuture = newPushHandlerFuture() handlerFuture = newPushHandlerFuture()
handler = handler = proc(
proc(
peer: PeerId, pubsubTopic: PubsubTopic, message: WakuMessage peer: PeerId, pubsubTopic: PubsubTopic, message: WakuMessage
): Future[WakuLightPushResult[void]] {.async.} = ): Future[WakuLightPushResult[void]] {.async.} =
handlerFuture.complete((pubsubTopic, message)) handlerFuture.complete((pubsubTopic, message))
return ok() return ok()
let let
serverKey = generateSecp256k1Key() serverKey = generateSecp256k1Key()
@ -72,19 +71,14 @@ suite "Waku Lightpush - End To End":
suite "Assessment of Message Relaying Mechanisms": suite "Assessment of Message Relaying Mechanisms":
asyncTest "Via 11/WAKU2-RELAY from Relay/Full Node": asyncTest "Via 11/WAKU2-RELAY from Relay/Full Node":
# Given a light lightpush client # Given a light lightpush client
let let lightpushClient =
lightpushClient = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
newTestWakuNode(
generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0)
)
lightpushClient.mountLightpushClient() lightpushClient.mountLightpushClient()
# When the client publishes a message # When the client publishes a message
let let publishResponse = await lightpushClient.lightpushPublish(
publishResponse = some(pubsubTopic), message, serverRemotePeerInfo
await lightpushClient.lightpushPublish( )
some(pubsubTopic), message, serverRemotePeerInfo
)
if not publishResponse.isOk(): if not publishResponse.isOk():
echo "Publish failed: ", publishResponse.error() echo "Publish failed: ", publishResponse.error()

View File

@ -19,7 +19,7 @@ import
waku_peer_exchange, waku_peer_exchange,
node/peer_manager, node/peer_manager,
waku_relay/protocol, waku_relay/protocol,
waku_core waku_core,
], ],
../waku_peer_exchange/utils, ../waku_peer_exchange/utils,
../testlib/[wakucore, wakunode, testasync] ../testlib/[wakucore, wakunode, testasync]
@ -187,82 +187,72 @@ suite "Waku Peer Exchange with discv5":
## Given (copied from test_waku_discv5.nim) ## Given (copied from test_waku_discv5.nim)
let let
# todo: px flag # todo: px flag
flags = flags = CapabilitiesBitfield.init(
CapabilitiesBitfield.init( lightpush = false, filter = false, store = false, relay = true
lightpush = false, filter = false, store = false, relay = true )
)
bindIp = parseIpAddress("0.0.0.0") bindIp = parseIpAddress("0.0.0.0")
extIp = parseIpAddress("127.0.0.1") extIp = parseIpAddress("127.0.0.1")
nodeKey1 = generateSecp256k1Key() nodeKey1 = generateSecp256k1Key()
nodeTcpPort1 = Port(64010) nodeTcpPort1 = Port(64010)
nodeUdpPort1 = Port(9000) nodeUdpPort1 = Port(9000)
node1 = node1 = newTestWakuNode(
newTestWakuNode( nodeKey1,
nodeKey1, bindIp,
bindIp, nodeTcpPort1,
nodeTcpPort1, some(extIp),
some(extIp), wakuFlags = some(flags),
wakuFlags = some(flags), discv5UdpPort = some(nodeUdpPort1),
discv5UdpPort = some(nodeUdpPort1), )
)
nodeKey2 = generateSecp256k1Key() nodeKey2 = generateSecp256k1Key()
nodeTcpPort2 = Port(64012) nodeTcpPort2 = Port(64012)
nodeUdpPort2 = Port(9002) nodeUdpPort2 = Port(9002)
node2 = node2 = newTestWakuNode(
newTestWakuNode( nodeKey2,
nodeKey2, bindIp,
bindIp, nodeTcpPort2,
nodeTcpPort2, some(extIp),
some(extIp), wakuFlags = some(flags),
wakuFlags = some(flags), discv5UdpPort = some(nodeUdpPort2),
discv5UdpPort = some(nodeUdpPort2), )
)
nodeKey3 = generateSecp256k1Key() nodeKey3 = generateSecp256k1Key()
nodeTcpPort3 = Port(64014) nodeTcpPort3 = Port(64014)
nodeUdpPort3 = Port(9004) nodeUdpPort3 = Port(9004)
node3 = node3 = newTestWakuNode(
newTestWakuNode( nodeKey3,
nodeKey3, bindIp,
bindIp, nodeTcpPort3,
nodeTcpPort3, some(extIp),
some(extIp), wakuFlags = some(flags),
wakuFlags = some(flags), discv5UdpPort = some(nodeUdpPort3),
discv5UdpPort = some(nodeUdpPort3), )
)
# discv5 # discv5
let let conf1 = WakuDiscoveryV5Config(
conf1 = discv5Config: none(DiscoveryConfig),
WakuDiscoveryV5Config( address: bindIp,
discv5Config: none(DiscoveryConfig), port: nodeUdpPort1,
address: bindIp, privateKey: keys.PrivateKey(nodeKey1.skkey),
port: nodeUdpPort1, bootstrapRecords: @[],
privateKey: keys.PrivateKey(nodeKey1.skkey), autoupdateRecord: true,
bootstrapRecords: @[], )
autoupdateRecord: true,
)
let let disc1 =
disc1 = WakuDiscoveryV5.new(node1.rng, conf1, some(node1.enr), some(node1.peerManager))
WakuDiscoveryV5.new(node1.rng, conf1, some(node1.enr), some(node1.peerManager))
let let conf2 = WakuDiscoveryV5Config(
conf2 = discv5Config: none(DiscoveryConfig),
WakuDiscoveryV5Config( address: bindIp,
discv5Config: none(DiscoveryConfig), port: nodeUdpPort2,
address: bindIp, privateKey: keys.PrivateKey(nodeKey2.skkey),
port: nodeUdpPort2, bootstrapRecords: @[disc1.protocol.getRecord()],
privateKey: keys.PrivateKey(nodeKey2.skkey), autoupdateRecord: true,
bootstrapRecords: @[disc1.protocol.getRecord()], )
autoupdateRecord: true,
)
let let disc2 =
disc2 = WakuDiscoveryV5.new(node2.rng, conf2, some(node2.enr), some(node2.peerManager))
WakuDiscoveryV5.new(node2.rng, conf2, some(node2.enr), some(node2.peerManager))
await allFutures(node1.start(), node2.start(), node3.start()) await allFutures(node1.start(), node2.start(), node3.start())
let resultDisc1StartRes = await disc1.start() let resultDisc1StartRes = await disc1.start()
@ -286,9 +276,8 @@ suite "Waku Peer Exchange with discv5":
await node1.mountPeerExchange() await node1.mountPeerExchange()
await node3.mountPeerExchange() await node3.mountPeerExchange()
let let dialResponse =
dialResponse = await node3.dialForPeerExchange(node1.switch.peerInfo.toRemotePeerInfo())
await node3.dialForPeerExchange(node1.switch.peerInfo.toRemotePeerInfo())
check dialResponse.isOk check dialResponse.isOk

View File

@ -12,13 +12,8 @@ import
from std/times import epochTime from std/times import epochTime
import import
../../../waku/[ ../../../waku/
node/waku_node, [node/waku_node, node/peer_manager, waku_core, waku_node, waku_rln_relay],
node/peer_manager,
waku_core,
waku_node,
waku_rln_relay,
],
../waku_store/store_utils, ../waku_store/store_utils,
../waku_archive/archive_utils, ../waku_archive/archive_utils,
../testlib/[wakucore, wakunode, testasync, futures], ../testlib/[wakucore, wakunode, testasync, futures],
@ -30,7 +25,7 @@ proc setupRln(node: WakuNode, identifier: uint) {.async.} =
rlnRelayDynamic: false, rlnRelayDynamic: false,
rlnRelayCredIndex: some(identifier), rlnRelayCredIndex: some(identifier),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_" & $identifier), rlnRelayTreePath: genTempPath("rln_tree", "wakunode_" & $identifier),
rlnEpochSizeSec: 1 rlnEpochSizeSec: 1,
) )
) )
@ -73,12 +68,11 @@ proc sendRlnMessageWithInvalidProof(
): Future[bool] {.async.} = ): Future[bool] {.async.} =
let let
extraBytes: seq[byte] = @[byte(1), 2, 3] extraBytes: seq[byte] = @[byte(1), 2, 3]
rateLimitProofRes = rateLimitProofRes = client.wakuRlnRelay.groupManager.generateProof(
client.wakuRlnRelay.groupManager.generateProof( concat(payload, extraBytes),
concat(payload, extraBytes), # we add extra bytes to invalidate proof verification against original payload
# we add extra bytes to invalidate proof verification against original payload client.wakuRlnRelay.getCurrentEpoch(),
client.wakuRlnRelay.getCurrentEpoch() )
)
rateLimitProof = rateLimitProofRes.get().encode().buffer rateLimitProof = rateLimitProofRes.get().encode().buffer
message = message =
WakuMessage(payload: @payload, contentTopic: contentTopic, proof: rateLimitProof) WakuMessage(payload: @payload, contentTopic: contentTopic, proof: rateLimitProof)
@ -127,10 +121,8 @@ suite "Waku RlnRelay - End to End":
server.wakuRlnRelay == nil server.wakuRlnRelay == nil
# When RlnRelay is mounted # When RlnRelay is mounted
let let catchRes = catch:
catchRes = await server.setupRln(1)
catch:
await server.setupRln(1)
# Then Relay and RLN are not mounted,and the process fails # Then Relay and RLN are not mounted,and the process fails
check: check:
@ -156,9 +148,8 @@ suite "Waku RlnRelay - End to End":
var completionFuture = subscribeCompletionHandler(server, pubsubTopic) var completionFuture = subscribeCompletionHandler(server, pubsubTopic)
# When the client sends a valid RLN message # When the client sends a valid RLN message
let let isCompleted1 =
isCompleted1 = await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
# Then the valid RLN message is relayed # Then the valid RLN message is relayed
check: check:
@ -167,11 +158,9 @@ suite "Waku RlnRelay - End to End":
# When the client sends an invalid RLN message # When the client sends an invalid RLN message
completionFuture = newBoolFuture() completionFuture = newBoolFuture()
let let isCompleted2 = await sendRlnMessageWithInvalidProof(
isCompleted2 = client, pubsubTopic, contentTopic, completionFuture
await sendRlnMessageWithInvalidProof( )
client, pubsubTopic, contentTopic, completionFuture
)
# Then the invalid RLN message is not relayed # Then the invalid RLN message is not relayed
check: check:
@ -191,9 +180,8 @@ suite "Waku RlnRelay - End to End":
await sleepAsync(FUTURE_TIMEOUT) await sleepAsync(FUTURE_TIMEOUT)
# When the client sends a valid RLN message # When the client sends a valid RLN message
let let isCompleted1 =
isCompleted1 = await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
# Then the valid RLN message is relayed # Then the valid RLN message is relayed
check: check:
@ -202,11 +190,9 @@ suite "Waku RlnRelay - End to End":
# When the client sends an invalid RLN message # When the client sends an invalid RLN message
completionFuture = newBoolFuture() completionFuture = newBoolFuture()
let let isCompleted2 = await sendRlnMessageWithInvalidProof(
isCompleted2 = client, pubsubTopic, contentTopic, completionFuture
await sendRlnMessageWithInvalidProof( )
client, pubsubTopic, contentTopic, completionFuture
)
# Then the invalid RLN message is not relayed # Then the invalid RLN message is not relayed
check: check:
@ -250,18 +236,26 @@ suite "Waku RlnRelay - End to End":
WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic) WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic)
doAssert( doAssert(
client.wakuRlnRelay.appendRLNProof(message1b, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 0).isOk() client.wakuRlnRelay
.appendRLNProof(message1b, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 0)
.isOk()
) )
doAssert( doAssert(
client.wakuRlnRelay.appendRLNProof(message1kib, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 1).isOk() client.wakuRlnRelay
.appendRLNProof(message1kib, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 1)
.isOk()
) )
doAssert( doAssert(
client.wakuRlnRelay.appendRLNProof(message150kib, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 2).isOk() client.wakuRlnRelay
.appendRLNProof(message150kib, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 2)
.isOk()
) )
doAssert( doAssert(
client.wakuRlnRelay.appendRLNProof( client.wakuRlnRelay
.appendRLNProof(
message151kibPlus, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 3 message151kibPlus, epoch + client.wakuRlnRelay.rlnEpochSizeSec * 3
).isOk() )
.isOk()
) )
# When sending the 1B message # When sending the 1B message
@ -319,9 +313,8 @@ suite "Waku RlnRelay - End to End":
overhead: uint64 = 419 overhead: uint64 = 419
payload150kibPlus = getByteSequence((150 * 1024) - overhead + 1) payload150kibPlus = getByteSequence((150 * 1024) - overhead + 1)
var var message151kibPlus =
message151kibPlus = WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic)
WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic)
doAssert( doAssert(
client.wakuRlnRelay.appendRLNProof( client.wakuRlnRelay.appendRLNProof(

View File

@ -17,7 +17,7 @@ import
waku_store/client, waku_store/client,
waku_archive, waku_archive,
waku_archive/driver/sqlite_driver, waku_archive/driver/sqlite_driver,
common/databases/db_sqlite common/databases/db_sqlite,
], ],
../waku_store/store_utils, ../waku_store/store_utils,
../waku_archive/archive_utils, ../waku_archive/archive_utils,
@ -55,16 +55,15 @@ suite "Waku Store - End to End - Sorted Archive":
fakeWakuMessage(@[byte 06], ts = ts(60, timeOrigin)), fakeWakuMessage(@[byte 06], ts = ts(60, timeOrigin)),
fakeWakuMessage(@[byte 07], ts = ts(70, timeOrigin)), fakeWakuMessage(@[byte 07], ts = ts(70, timeOrigin)),
fakeWakuMessage(@[byte 08], ts = ts(80, timeOrigin)), fakeWakuMessage(@[byte 08], ts = ts(80, timeOrigin)),
fakeWakuMessage(@[byte 09], ts = ts(90, timeOrigin)) fakeWakuMessage(@[byte 09], ts = ts(90, timeOrigin)),
] ]
historyQuery = historyQuery = HistoryQuery(
HistoryQuery( pubsubTopic: some(pubsubTopic),
pubsubTopic: some(pubsubTopic), contentTopics: contentTopicSeq,
contentTopics: contentTopicSeq, direction: PagingDirection.Forward,
direction: PagingDirection.Forward, pageSize: 5,
pageSize: 5, )
)
let let
serverKey = generateSecp256k1Key() serverKey = generateSecp256k1Key()
@ -95,26 +94,24 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse.get().messages == archiveMessages[0..<5] queryResponse.get().messages == archiveMessages[0 ..< 5]
# Given the next query # Given the next query
var var otherHistoryQuery = HistoryQuery(
otherHistoryQuery = cursor: queryResponse.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 5,
direction: PagingDirection.FORWARD, )
pageSize: 5,
)
# When making the next history query # When making the next history query
let let otherQueryResponse =
otherQueryResponse = await client.query(otherHistoryQuery, serverRemotePeerInfo) await client.query(otherHistoryQuery, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
otherQueryResponse.get().messages == archiveMessages[5..<10] otherQueryResponse.get().messages == archiveMessages[5 ..< 10]
asyncTest "Backward Pagination": asyncTest "Backward Pagination":
# Given the history query is backward # Given the history query is backward
@ -125,26 +122,24 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse.get().messages == archiveMessages[5..<10] queryResponse.get().messages == archiveMessages[5 ..< 10]
# Given the next query # Given the next query
var var nextHistoryQuery = HistoryQuery(
nextHistoryQuery = cursor: queryResponse.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.BACKWARD,
contentTopics: contentTopicSeq, pageSize: 5,
direction: PagingDirection.BACKWARD, )
pageSize: 5,
)
# When making the next history query # When making the next history query
let let otherQueryResponse =
otherQueryResponse = await client.query(nextHistoryQuery, serverRemotePeerInfo) await client.query(nextHistoryQuery, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
otherQueryResponse.get().messages == archiveMessages[0..<5] otherQueryResponse.get().messages == archiveMessages[0 ..< 5]
suite "Pagination with Differente Page Sizes": suite "Pagination with Differente Page Sizes":
asyncTest "Pagination with Small Page Size": asyncTest "Pagination with Small Page Size":
@ -156,79 +151,71 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse1.get().messages == archiveMessages[0..<2] queryResponse1.get().messages == archiveMessages[0 ..< 2]
# Given the next query (2/5) # Given the next query (2/5)
let let historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse1.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse1.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 2,
direction: PagingDirection.FORWARD, )
pageSize: 2,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse2.get().messages == archiveMessages[2..<4] queryResponse2.get().messages == archiveMessages[2 ..< 4]
# Given the next query (3/5) # Given the next query (3/5)
let let historyQuery3 = HistoryQuery(
historyQuery3 = cursor: queryResponse2.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse2.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 2,
direction: PagingDirection.FORWARD, )
pageSize: 2,
)
# When making the next history query # When making the next history query
let queryResponse3 = await client.query(historyQuery3, serverRemotePeerInfo) let queryResponse3 = await client.query(historyQuery3, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse3.get().messages == archiveMessages[4..<6] queryResponse3.get().messages == archiveMessages[4 ..< 6]
# Given the next query (4/5) # Given the next query (4/5)
let let historyQuery4 = HistoryQuery(
historyQuery4 = cursor: queryResponse3.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse3.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 2,
direction: PagingDirection.FORWARD, )
pageSize: 2,
)
# When making the next history query # When making the next history query
let queryResponse4 = await client.query(historyQuery4, serverRemotePeerInfo) let queryResponse4 = await client.query(historyQuery4, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse4.get().messages == archiveMessages[6..<8] queryResponse4.get().messages == archiveMessages[6 ..< 8]
# Given the next query (5/5) # Given the next query (5/5)
let let historyQuery5 = HistoryQuery(
historyQuery5 = cursor: queryResponse4.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse4.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 2,
direction: PagingDirection.FORWARD, )
pageSize: 2,
)
# When making the next history query # When making the next history query
let queryResponse5 = await client.query(historyQuery5, serverRemotePeerInfo) let queryResponse5 = await client.query(historyQuery5, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse5.get().messages == archiveMessages[8..<10] queryResponse5.get().messages == archiveMessages[8 ..< 10]
asyncTest "Pagination with Large Page Size": asyncTest "Pagination with Large Page Size":
# Given the first query (1/2) # Given the first query (1/2)
@ -239,25 +226,23 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse1.get().messages == archiveMessages[0..<8] queryResponse1.get().messages == archiveMessages[0 ..< 8]
# Given the next query (2/2) # Given the next query (2/2)
let let historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse1.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse1.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 8,
direction: PagingDirection.FORWARD, )
pageSize: 8,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse2.get().messages == archiveMessages[8..<10] queryResponse2.get().messages == archiveMessages[8 ..< 10]
asyncTest "Pagination with Excessive Page Size": asyncTest "Pagination with Excessive Page Size":
# Given the first query (1/1) # Given the first query (1/1)
@ -268,7 +253,7 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse1.get().messages == archiveMessages[0..<10] queryResponse1.get().messages == archiveMessages[0 ..< 10]
asyncTest "Pagination with Mixed Page Size": asyncTest "Pagination with Mixed Page Size":
# Given the first query (1/3) # Given the first query (1/3)
@ -279,43 +264,39 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse1.get().messages == archiveMessages[0..<2] queryResponse1.get().messages == archiveMessages[0 ..< 2]
# Given the next query (2/3) # Given the next query (2/3)
let let historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse1.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse1.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 4,
direction: PagingDirection.FORWARD, )
pageSize: 4,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse2.get().messages == archiveMessages[2..<6] queryResponse2.get().messages == archiveMessages[2 ..< 6]
# Given the next query (3/3) # Given the next query (3/3)
let let historyQuery3 = HistoryQuery(
historyQuery3 = cursor: queryResponse2.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse2.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 6,
direction: PagingDirection.FORWARD, )
pageSize: 6,
)
# When making the next history query # When making the next history query
let queryResponse3 = await client.query(historyQuery3, serverRemotePeerInfo) let queryResponse3 = await client.query(historyQuery3, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse3.get().messages == archiveMessages[6..<10] queryResponse3.get().messages == archiveMessages[6 ..< 10]
asyncTest "Pagination with Zero Page Size (Behaves as DefaultPageSize)": asyncTest "Pagination with Zero Page Size (Behaves as DefaultPageSize)":
# Given a message list of size higher than the default page size # Given a message list of size higher than the default page size
@ -326,7 +307,7 @@ suite "Waku Store - End to End - Sorted Archive":
let lastMessageTimestamp = archiveMessages[archiveMessages.len - 1].timestamp let lastMessageTimestamp = archiveMessages[archiveMessages.len - 1].timestamp
var extraMessages: seq[WakuMessage] = @[] var extraMessages: seq[WakuMessage] = @[]
for i in 0..<missingMessagesAmount: for i in 0 ..< missingMessagesAmount:
let let
timestampOffset = 10 * int(i + 1) timestampOffset = 10 * int(i + 1)
# + 1 to avoid collision with existing messages # + 1 to avoid collision with existing messages
@ -345,18 +326,16 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the archive.DefaultPageSize messages # Then the response contains the archive.DefaultPageSize messages
check: check:
queryResponse1.get().messages == totalMessages[0..<archive.DefaultPageSize] queryResponse1.get().messages == totalMessages[0 ..< archive.DefaultPageSize]
# Given the next query (2/2) # Given the next query (2/2)
let let historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse1.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse1.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 0,
direction: PagingDirection.FORWARD, )
pageSize: 0,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
@ -364,7 +343,7 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the remaining messages # Then the response contains the remaining messages
check: check:
queryResponse2.get().messages == queryResponse2.get().messages ==
totalMessages[archive.DefaultPageSize..<archive.DefaultPageSize + 5] totalMessages[archive.DefaultPageSize ..< archive.DefaultPageSize + 5]
asyncTest "Pagination with Default Page Size": asyncTest "Pagination with Default Page Size":
# Given a message list of size higher than the default page size # Given a message list of size higher than the default page size
@ -375,7 +354,7 @@ suite "Waku Store - End to End - Sorted Archive":
let lastMessageTimestamp = archiveMessages[archiveMessages.len - 1].timestamp let lastMessageTimestamp = archiveMessages[archiveMessages.len - 1].timestamp
var extraMessages: seq[WakuMessage] = @[] var extraMessages: seq[WakuMessage] = @[]
for i in 0..<missingMessagesAmount: for i in 0 ..< missingMessagesAmount:
let let
timestampOffset = 10 * int(i + 1) timestampOffset = 10 * int(i + 1)
# + 1 to avoid collision with existing messages # + 1 to avoid collision with existing messages
@ -387,29 +366,26 @@ suite "Waku Store - End to End - Sorted Archive":
let totalMessages = archiveMessages & extraMessages let totalMessages = archiveMessages & extraMessages
# Given a query with default page size (1/2) # Given a query with default page size (1/2)
historyQuery = historyQuery = HistoryQuery(
HistoryQuery( pubsubTopic: some(pubsubTopic),
pubsubTopic: some(pubsubTopic), contentTopics: contentTopicSeq,
contentTopics: contentTopicSeq, direction: PagingDirection.FORWARD,
direction: PagingDirection.FORWARD, )
)
# When making a history query # When making a history query
let queryResponse = await client.query(historyQuery, serverRemotePeerInfo) let queryResponse = await client.query(historyQuery, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse.get().messages == totalMessages[0..<archive.DefaultPageSize] queryResponse.get().messages == totalMessages[0 ..< archive.DefaultPageSize]
# Given the next query (2/2) # Given the next query (2/2)
let let historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, )
direction: PagingDirection.FORWARD,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
@ -417,7 +393,7 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse2.get().messages == queryResponse2.get().messages ==
totalMessages[archive.DefaultPageSize..<archive.DefaultPageSize + 5] totalMessages[archive.DefaultPageSize ..< archive.DefaultPageSize + 5]
suite "Pagination with Different Cursors": suite "Pagination with Different Cursors":
asyncTest "Starting Cursor": asyncTest "Starting Cursor":
@ -431,7 +407,7 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the message # Then the response contains the message
check: check:
queryResponse.get().messages == archiveMessages[1..<2] queryResponse.get().messages == archiveMessages[1 ..< 2]
asyncTest "Middle Cursor": asyncTest "Middle Cursor":
# Given a cursor pointing to the middle message1 # Given a cursor pointing to the middle message1
@ -444,7 +420,7 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the message # Then the response contains the message
check: check:
queryResponse.get().messages == archiveMessages[6..<7] queryResponse.get().messages == archiveMessages[6 ..< 7]
asyncTest "Ending Cursor": asyncTest "Ending Cursor":
# Given a cursor pointing to the last message # Given a cursor pointing to the last message
@ -482,28 +458,25 @@ suite "Waku Store - End to End - Sorted Archive":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse.get().messages == archiveMessages[0..<5] queryResponse.get().messages == archiveMessages[0 ..< 5]
# Given the cursor from the first query # Given the cursor from the first query
let cursor = queryResponse.get().cursor let cursor = queryResponse.get().cursor
# When making a history query to the second server node # When making a history query to the second server node
let let otherHistoryQuery = HistoryQuery(
otherHistoryQuery = cursor: cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 5,
direction: PagingDirection.FORWARD, )
pageSize: 5, let otherQueryResponse =
) await client.query(otherHistoryQuery, otherServerRemotePeerInfo)
let
otherQueryResponse =
await client.query(otherHistoryQuery, otherServerRemotePeerInfo)
# Then the response contains the remaining messages # Then the response contains the remaining messages
check: check:
otherQueryResponse.get().messages == archiveMessages[5..<10] otherQueryResponse.get().messages == archiveMessages[5 ..< 10]
# Cleanup # Cleanup
waitFor otherServer.stop() waitFor otherServer.stop()
@ -526,13 +499,12 @@ suite "Waku Store - End to End - Unsorted Archive":
contentTopic = DefaultContentTopic contentTopic = DefaultContentTopic
contentTopicSeq = @[contentTopic] contentTopicSeq = @[contentTopic]
historyQuery = historyQuery = HistoryQuery(
HistoryQuery( pubsubTopic: some(pubsubTopic),
pubsubTopic: some(pubsubTopic), contentTopics: contentTopicSeq,
contentTopics: contentTopicSeq, direction: PagingDirection.FORWARD,
direction: PagingDirection.FORWARD, pageSize: 5,
pageSize: 5, )
)
let timeOrigin = now() let timeOrigin = now()
unsortedArchiveMessages = unsortedArchiveMessages =
@ -546,7 +518,7 @@ suite "Waku Store - End to End - Unsorted Archive":
fakeWakuMessage(@[byte 06], ts = ts(20, timeOrigin)), # 6 fakeWakuMessage(@[byte 06], ts = ts(20, timeOrigin)), # 6
fakeWakuMessage(@[byte 01], ts = ts(20, timeOrigin)), # 9 fakeWakuMessage(@[byte 01], ts = ts(20, timeOrigin)), # 9
fakeWakuMessage(@[byte 04], ts = ts(20, timeOrigin)), # 7 fakeWakuMessage(@[byte 04], ts = ts(20, timeOrigin)), # 7
fakeWakuMessage(@[byte 05], ts = ts(20, timeOrigin)) # 8 fakeWakuMessage(@[byte 05], ts = ts(20, timeOrigin)), # 8
] ]
let let
@ -586,19 +558,17 @@ suite "Waku Store - End to End - Unsorted Archive":
unsortedArchiveMessages[0], unsortedArchiveMessages[0],
unsortedArchiveMessages[1], unsortedArchiveMessages[1],
unsortedArchiveMessages[4], unsortedArchiveMessages[4],
unsortedArchiveMessages[3] unsortedArchiveMessages[3],
] ]
# Given the next query # Given the next query
var var historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse.get().cursor,
HistoryQuery( pubsubTopic: some(pubsubTopic),
cursor: queryResponse.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: some(pubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 5,
direction: PagingDirection.FORWARD, )
pageSize: 5,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
@ -611,7 +581,7 @@ suite "Waku Store - End to End - Unsorted Archive":
unsortedArchiveMessages[6], unsortedArchiveMessages[6],
unsortedArchiveMessages[8], unsortedArchiveMessages[8],
unsortedArchiveMessages[9], unsortedArchiveMessages[9],
unsortedArchiveMessages[7] unsortedArchiveMessages[7],
] ]
asyncTest "Backward pagination with Ascending Sorting": asyncTest "Backward pagination with Ascending Sorting":
@ -629,7 +599,7 @@ suite "Waku Store - End to End - Unsorted Archive":
@[ @[
unsortedArchiveMessages[2], unsortedArchiveMessages[2],
unsortedArchiveMessages[0], unsortedArchiveMessages[0],
unsortedArchiveMessages[1] unsortedArchiveMessages[1],
] ]
asyncTest "Forward Pagination with Ascending Sorting": asyncTest "Forward Pagination with Ascending Sorting":
@ -649,7 +619,7 @@ suite "Waku Store - End to End - Unsorted Archive":
unsortedArchiveMessages[5], unsortedArchiveMessages[5],
unsortedArchiveMessages[6], unsortedArchiveMessages[6],
unsortedArchiveMessages[8], unsortedArchiveMessages[8],
unsortedArchiveMessages[9] unsortedArchiveMessages[9],
] ]
suite "Waku Store - End to End - Archive with Multiple Topics": suite "Waku Store - End to End - Archive with Multiple Topics":
@ -680,18 +650,16 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
contentTopicSeq = contentTopicSeq =
@[contentTopic, contentTopicB, contentTopicC, contentTopicSpecials] @[contentTopic, contentTopicB, contentTopicC, contentTopicSpecials]
historyQuery = historyQuery = HistoryQuery(
HistoryQuery( pubsubTopic: some(pubsubTopic),
pubsubTopic: some(pubsubTopic), contentTopics: contentTopicSeq,
contentTopics: contentTopicSeq, direction: PagingDirection.FORWARD,
direction: PagingDirection.FORWARD, pageSize: 5,
pageSize: 5, )
)
let timeOrigin = now() let timeOrigin = now()
originTs = originTs = proc(offset = 0): Timestamp {.gcsafe, raises: [].} =
proc(offset = 0): Timestamp {.gcsafe, raises: [].} = ts(offset, timeOrigin)
ts(offset, timeOrigin)
archiveMessages = archiveMessages =
@[ @[
@ -706,7 +674,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
fakeWakuMessage(@[byte 08], ts = originTs(80), contentTopic = contentTopicC), fakeWakuMessage(@[byte 08], ts = originTs(80), contentTopic = contentTopicC),
fakeWakuMessage( fakeWakuMessage(
@[byte 09], ts = originTs(90), contentTopic = contentTopicSpecials @[byte 09], ts = originTs(90), contentTopic = contentTopicSpecials
) ),
] ]
let let
@ -716,11 +684,9 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
server = newTestWakuNode(serverKey, ValidIpAddress.init("0.0.0.0"), Port(0)) server = newTestWakuNode(serverKey, ValidIpAddress.init("0.0.0.0"), Port(0))
client = newTestWakuNode(clientKey, ValidIpAddress.init("0.0.0.0"), Port(0)) client = newTestWakuNode(clientKey, ValidIpAddress.init("0.0.0.0"), Port(0))
let let archiveDriver = newSqliteArchiveDriver()
archiveDriver = .put(pubsubTopic, archiveMessages[0 ..< 6])
newSqliteArchiveDriver().put(pubsubTopic, archiveMessages[0..<6]).put( .put(pubsubTopicB, archiveMessages[6 ..< 10])
pubsubTopicB, archiveMessages[6..<10]
)
let mountSortedArchiveResult = server.mountArchive(archiveDriver) let mountSortedArchiveResult = server.mountArchive(archiveDriver)
assert mountSortedArchiveResult.isOk() assert mountSortedArchiveResult.isOk()
@ -761,7 +727,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
archiveMessages[0], archiveMessages[0],
archiveMessages[1], archiveMessages[1],
archiveMessages[3], archiveMessages[3],
archiveMessages[4] archiveMessages[4],
] ]
asyncTest "Empty Content Filtering": asyncTest "Empty Content Filtering":
@ -773,25 +739,23 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse.get().messages == archiveMessages[0..<5] queryResponse.get().messages == archiveMessages[0 ..< 5]
# Given the next query # Given the next query
let let historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse.get().cursor,
HistoryQuery( pubsubTopic: none(PubsubTopic),
cursor: queryResponse.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: none(PubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 5,
direction: PagingDirection.FORWARD, )
pageSize: 5,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse2.get().messages == archiveMessages[5..<10] queryResponse2.get().messages == archiveMessages[5 ..< 10]
asyncTest "Non-Existent Content Topic": asyncTest "Non-Existent Content Topic":
# Given a history query with non-existent content filtering # Given a history query with non-existent content filtering
@ -830,7 +794,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
archiveMessages[6], archiveMessages[6],
archiveMessages[7], archiveMessages[7],
archiveMessages[8], archiveMessages[8],
archiveMessages[9] archiveMessages[9],
] ]
asyncTest "PubsubTopic Left Empty": asyncTest "PubsubTopic Left Empty":
@ -842,25 +806,23 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse.get().messages == archiveMessages[0..<5] queryResponse.get().messages == archiveMessages[0 ..< 5]
# Given the next query # Given the next query
let let historyQuery2 = HistoryQuery(
historyQuery2 = cursor: queryResponse.get().cursor,
HistoryQuery( pubsubTopic: none(PubsubTopic),
cursor: queryResponse.get().cursor, contentTopics: contentTopicSeq,
pubsubTopic: none(PubsubTopic), direction: PagingDirection.FORWARD,
contentTopics: contentTopicSeq, pageSize: 5,
direction: PagingDirection.FORWARD, )
pageSize: 5,
)
# When making the next history query # When making the next history query
let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo) let queryResponse2 = await client.query(historyQuery2, serverRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
queryResponse2.get().messages == archiveMessages[5..<10] queryResponse2.get().messages == archiveMessages[5 ..< 10]
suite "Validation of Time-based Filtering": suite "Validation of Time-based Filtering":
asyncTest "Basic Time Filtering": asyncTest "Basic Time Filtering":
@ -891,7 +853,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
archiveMessages[2], archiveMessages[2],
archiveMessages[3], archiveMessages[3],
archiveMessages[4], archiveMessages[4],
archiveMessages[5] archiveMessages[5],
] ]
asyncTest "Only End Time Specified": asyncTest "Only End Time Specified":
@ -910,7 +872,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
archiveMessages[1], archiveMessages[1],
archiveMessages[2], archiveMessages[2],
archiveMessages[3], archiveMessages[3],
archiveMessages[4] archiveMessages[4],
] ]
asyncTest "Invalid Time Range": asyncTest "Invalid Time Range":
@ -959,7 +921,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
@[ @[
fakeWakuMessage(@[byte 00], ts = ts(00), ephemeral = true), fakeWakuMessage(@[byte 00], ts = ts(00), ephemeral = true),
fakeWakuMessage(@[byte 01], ts = ts(10), ephemeral = true), fakeWakuMessage(@[byte 01], ts = ts(10), ephemeral = true),
fakeWakuMessage(@[byte 02], ts = ts(20), ephemeral = true) fakeWakuMessage(@[byte 02], ts = ts(20), ephemeral = true),
] ]
ephemeralArchiveDriver = ephemeralArchiveDriver =
newSqliteArchiveDriver().put(pubsubTopic, ephemeralMessages) newSqliteArchiveDriver().put(pubsubTopic, ephemeralMessages)
@ -978,8 +940,8 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
let ephemeralServerRemotePeerInfo = ephemeralServer.peerInfo.toRemotePeerInfo() let ephemeralServerRemotePeerInfo = ephemeralServer.peerInfo.toRemotePeerInfo()
# When making a history query to the server with only ephemeral messages # When making a history query to the server with only ephemeral messages
let let queryResponse =
queryResponse = await client.query(historyQuery, ephemeralServerRemotePeerInfo) await client.query(historyQuery, ephemeralServerRemotePeerInfo)
# Then the response contains no messages # Then the response contains no messages
check: check:
@ -995,18 +957,17 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
@[ @[
fakeWakuMessage(@[byte 00], ts = ts(00), ephemeral = true), fakeWakuMessage(@[byte 00], ts = ts(00), ephemeral = true),
fakeWakuMessage(@[byte 01], ts = ts(10), ephemeral = true), fakeWakuMessage(@[byte 01], ts = ts(10), ephemeral = true),
fakeWakuMessage(@[byte 02], ts = ts(20), ephemeral = true) fakeWakuMessage(@[byte 02], ts = ts(20), ephemeral = true),
] ]
nonEphemeralMessages = nonEphemeralMessages =
@[ @[
fakeWakuMessage(@[byte 03], ts = ts(30), ephemeral = false), fakeWakuMessage(@[byte 03], ts = ts(30), ephemeral = false),
fakeWakuMessage(@[byte 04], ts = ts(40), ephemeral = false), fakeWakuMessage(@[byte 04], ts = ts(40), ephemeral = false),
fakeWakuMessage(@[byte 05], ts = ts(50), ephemeral = false) fakeWakuMessage(@[byte 05], ts = ts(50), ephemeral = false),
] ]
mixedArchiveDriver = mixedArchiveDriver = newSqliteArchiveDriver()
newSqliteArchiveDriver().put(pubsubTopic, ephemeralMessages).put( .put(pubsubTopic, ephemeralMessages)
pubsubTopic, nonEphemeralMessages .put(pubsubTopic, nonEphemeralMessages)
)
# And a server node with the mixed archive # And a server node with the mixed archive
let let
@ -1060,12 +1021,11 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
asyncTest "Voluminous Message Store": asyncTest "Voluminous Message Store":
# Given a voluminous archive (1M+ messages) # Given a voluminous archive (1M+ messages)
var voluminousArchiveMessages: seq[WakuMessage] = @[] var voluminousArchiveMessages: seq[WakuMessage] = @[]
for i in 0..<100000: for i in 0 ..< 100000:
let topic = "topic" & $i let topic = "topic" & $i
voluminousArchiveMessages.add(fakeWakuMessage(@[byte i], contentTopic = topic)) voluminousArchiveMessages.add(fakeWakuMessage(@[byte i], contentTopic = topic))
let let voluminousArchiveDriverWithMessages =
voluminousArchiveDriverWithMessages = newArchiveDriverWithMessages(pubsubTopic, voluminousArchiveMessages)
newArchiveDriverWithMessages(pubsubTopic, voluminousArchiveMessages)
# And a server node with the voluminous archive # And a server node with the voluminous archive
let let
@ -1085,8 +1045,8 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
@["topic10000", "topic30000", "topic50000", "topic70000", "topic90000"] @["topic10000", "topic30000", "topic50000", "topic70000", "topic90000"]
# When making a history query to the server with a voluminous archive # When making a history query to the server with a voluminous archive
let let queryResponse =
queryResponse = await client.query(historyQuery, voluminousServerRemotePeerInfo) await client.query(historyQuery, voluminousServerRemotePeerInfo)
# Then the response contains the messages # Then the response contains the messages
check: check:
@ -1096,7 +1056,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
voluminousArchiveMessages[30000], voluminousArchiveMessages[30000],
voluminousArchiveMessages[50000], voluminousArchiveMessages[50000],
voluminousArchiveMessages[70000], voluminousArchiveMessages[70000],
voluminousArchiveMessages[90000] voluminousArchiveMessages[90000],
] ]
# Cleanup # Cleanup
@ -1105,7 +1065,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics":
asyncTest "Large contentFilters Array": asyncTest "Large contentFilters Array":
# Given a history query with the max contentFilters len, 10 # Given a history query with the max contentFilters len, 10
historyQuery.contentTopics = @[contentTopic] historyQuery.contentTopics = @[contentTopic]
for i in 0..<9: for i in 0 ..< 9:
let topic = "topic" & $i let topic = "topic" & $i
historyQuery.contentTopics.add(topic) historyQuery.contentTopics.add(topic)

View File

@ -1,7 +1,11 @@
proc getContentTopic*(applicationName: string, applicationVersion: int, contentTopicName: string, encoding: string): string = proc getContentTopic*(
applicationName: string,
applicationVersion: int,
contentTopicName: string,
encoding: string,
): string =
return "/$applicationName/$applicationVersion/$contentTopicName/$enconding" return "/$applicationName/$applicationVersion/$contentTopicName/$enconding"
const const
CURRENT* = getContentTopic("application", 1, "content-topic", "proto") CURRENT* = getContentTopic("application", 1, "content-topic", "proto")
TESTNET* = getContentTopic("toychat", 2, "huilong", "proto") TESTNET* = getContentTopic("toychat", 2, "huilong", "proto")

View File

@ -1,13 +1,15 @@
import import std/json
std/json
const const
ALPHABETIC* = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ALPHABETIC* = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
ALPHANUMERIC* = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ALPHANUMERIC* = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
ALPHANUMERIC_SPECIAL* = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}|;':\\\",./<>?`~" ALPHANUMERIC_SPECIAL* =
EMOJI* = "😀 😃 😄 😁 😆 😅 🤣 😂 🙂 🙃 😉 😊 😇 🥰 😍 🤩 😘 😗 😚 😙" "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}|;':\\\",./<>?`~"
EMOJI* =
"😀 😃 😄 😁 😆 😅 🤣 😂 🙂 🙃 😉 😊 😇 🥰 😍 🤩 😘 😗 😚 😙"
CODE* = "def main():\n\tprint('Hello, world!')" CODE* = "def main():\n\tprint('Hello, world!')"
QUERY* = """ QUERY* =
"""
SELECT SELECT
u.id, u.id,
u.name, u.name,
@ -28,7 +30,8 @@ const
u.id = 1 u.id = 1
""" """
TEXT_SMALL* = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." TEXT_SMALL* = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
TEXT_LARGE* = """ TEXT_LARGE* =
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida vulputate semper. Proin Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida vulputate semper. Proin
eleifend varius cursus. Morbi lacinia posuere quam sit amet pretium. Sed non metus fermentum, eleifend varius cursus. Morbi lacinia posuere quam sit amet pretium. Sed non metus fermentum,
venenatis nisl id, vestibulum eros. Quisque non lorem sit amet lectus faucibus elementum eu venenatis nisl id, vestibulum eros. Quisque non lorem sit amet lectus faucibus elementum eu
@ -40,35 +43,12 @@ const
proc getSampleJsonDictionary*(): JsonNode = proc getSampleJsonDictionary*(): JsonNode =
%*{ %*{
"shapes": [ "shapes": [{"type": "circle", "radius": 10}, {"type": "square", "side": 10}],
{ "colours": ["red", "green", "blue"],
"type": "circle",
"radius": 10
},
{
"type": "square",
"side": 10
}
],
"colours": [
"red",
"green",
"blue"
]
} }
proc getSampleJsonList*(): JsonNode = proc getSampleJsonList*(): JsonNode =
%*[ %*[{"type": "cat", "name": "Salem"}, {"type": "dog", "name": "Oberon"}]
{
"type": "cat",
"name": "Salem"
},
{
"type": "dog",
"name": "Oberon"
},
]
proc getByteSequence*(bytesNumber: uint64): seq[byte] = proc getByteSequence*(bytesNumber: uint64): seq[byte] =
result = newSeq[byte](bytesNumber) result = newSeq[byte](bytesNumber)

View File

@ -3,7 +3,6 @@ import std/strformat
proc getPubsubTopic*(pubsubTopicName: string): string = proc getPubsubTopic*(pubsubTopicName: string): string =
return fmt"/waku/2/{pubsubTopicName}" return fmt"/waku/2/{pubsubTopicName}"
const const
CURRENT* = getPubsubTopic("test") CURRENT* = getPubsubTopic("test")
CURRENT_NESTED* = getPubsubTopic("test/nested") CURRENT_NESTED* = getPubsubTopic("test/nested")

View File

@ -1,6 +1,3 @@
{.used.} {.used.}
import import ./all_tests_common, ./all_tests_waku, ./all_tests_wakunode2
./all_tests_common,
./all_tests_waku,
./all_tests_wakunode2

View File

@ -1,6 +1,4 @@
import import chronos, bearssl/rand, eth/[keys, p2p]
chronos, bearssl/rand,
eth/[keys, p2p]
import libp2p/crypto/crypto import libp2p/crypto/crypto
@ -8,20 +6,23 @@ var nextPort = 30303
proc localAddress*(port: int): Address = proc localAddress*(port: int): Address =
let port = Port(port) let port = Port(port)
result = Address(udpPort: port, tcpPort: port, result = Address(udpPort: port, tcpPort: port, ip: parseIpAddress("127.0.0.1"))
ip: parseIpAddress("127.0.0.1"))
proc setupTestNode*( proc setupTestNode*(
rng: ref HmacDrbgContext, rng: ref HmacDrbgContext, capabilities: varargs[ProtocolInfo, `protocolInfo`]
capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode = ): EthereumNode =
let let
keys1 = keys.KeyPair.random(rng[]) keys1 = keys.KeyPair.random(rng[])
address = localAddress(nextPort) address = localAddress(nextPort)
result = newEthereumNode(keys1, address, NetworkId(1), result = newEthereumNode(
addAllCapabilities = false, keys1,
bindUdpPort = address.udpPort, # Assume same as external address,
bindTcpPort = address.tcpPort, # Assume same as external NetworkId(1),
rng = rng) addAllCapabilities = false,
bindUdpPort = address.udpPort, # Assume same as external
bindTcpPort = address.tcpPort, # Assume same as external
rng = rng,
)
nextPort.inc nextPort.inc
for capability in capabilities: for capability in capabilities:
result.addCapability capability result.addCapability capability

View File

@ -1,13 +1,7 @@
{.used.} {.used.}
import import std/sets, stew/[results, byteutils], testutils/unittests
std/sets, import ../../waku/waku_core, ../../waku/waku_api/message_cache, ./testlib/wakucore
stew/[results, byteutils],
testutils/unittests
import
../../waku/waku_core,
../../waku/waku_api/message_cache,
./testlib/wakucore
suite "MessageCache": suite "MessageCache":
setup: setup:
@ -77,7 +71,7 @@ suite "MessageCache":
cache.addMessage(testPubsubTopic, testMessage) cache.addMessage(testPubsubTopic, testMessage)
## When ## When
var res = cache.getMessages(testPubsubTopic, clear=true) var res = cache.getMessages(testPubsubTopic, clear = true)
require(res.isOk()) require(res.isOk())
res = cache.getMessages(testPubsubTopic) res = cache.getMessages(testPubsubTopic)
@ -121,15 +115,15 @@ suite "MessageCache":
## Then ## Then
let res = cache.getMessages(testPubsubTopic) let res = cache.getMessages(testPubsubTopic)
check: check:
res.isErr() res.isErr()
res.error() == "not subscribed to any pubsub topics" res.error() == "not subscribed to any pubsub topics"
test "add messages beyond the capacity": test "add messages beyond the capacity":
## Given ## Given
var testMessages = @[fakeWakuMessage(toBytes("MSG-1"))] var testMessages = @[fakeWakuMessage(toBytes("MSG-1"))]
# Prevent duplicate messages timestamp # Prevent duplicate messages timestamp
for i in 0..<5: for i in 0 ..< 5:
var msg = fakeWakuMessage(toBytes("MSG-1")) var msg = fakeWakuMessage(toBytes("MSG-1"))
while msg.timestamp <= testMessages[i].timestamp: while msg.timestamp <= testMessages[i].timestamp:

View File

@ -37,20 +37,28 @@ import
procSuite "Peer Manager": procSuite "Peer Manager":
asyncTest "connectRelay() works": asyncTest "connectRelay() works":
# Create 2 nodes # Create 2 nodes
let nodes = toSeq(0..<2).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 2).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
let connOk = await nodes[0].peerManager.connectRelay(nodes[1].peerInfo.toRemotePeerInfo()) let connOk =
await nodes[0].peerManager.connectRelay(nodes[1].peerInfo.toRemotePeerInfo())
await sleepAsync(chronos.milliseconds(500)) await sleepAsync(chronos.milliseconds(500))
check: check:
connOk == true connOk == true
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[1].peerInfo.peerId) nodes[0].peerManager.peerStore.peers().anyIt(
nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) == Connectedness.Connected it.peerId == nodes[1].peerInfo.peerId
)
nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) ==
Connectedness.Connected
asyncTest "dialPeer() works": asyncTest "dialPeer() works":
# Create 2 nodes # Create 2 nodes
let nodes = toSeq(0..<2).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 2).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
await allFutures(nodes.mapIt(it.mountRelay())) await allFutures(nodes.mapIt(it.mountRelay()))
@ -58,7 +66,9 @@ procSuite "Peer Manager":
await allFutures(nodes.mapIt(it.mountLegacyFilter())) await allFutures(nodes.mapIt(it.mountLegacyFilter()))
# Dial node2 from node1 # Dial node2 from node1
let conn = await nodes[0].peerManager.dialPeer(nodes[1].peerInfo.toRemotePeerInfo(), WakuLegacyFilterCodec) let conn = await nodes[0].peerManager.dialPeer(
nodes[1].peerInfo.toRemotePeerInfo(), WakuLegacyFilterCodec
)
await sleepAsync(chronos.milliseconds(500)) await sleepAsync(chronos.milliseconds(500))
# Check connection # Check connection
@ -69,32 +79,42 @@ procSuite "Peer Manager":
# Check that node2 is being managed in node1 # Check that node2 is being managed in node1
check: check:
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[1].peerInfo.peerId) nodes[0].peerManager.peerStore.peers().anyIt(
it.peerId == nodes[1].peerInfo.peerId
)
# Check connectedness # Check connectedness
check: check:
nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) == Connectedness.Connected nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) ==
Connectedness.Connected
await allFutures(nodes.mapIt(it.stop())) await allFutures(nodes.mapIt(it.stop()))
asyncTest "dialPeer() fails gracefully": asyncTest "dialPeer() fails gracefully":
# Create 2 nodes and start them # Create 2 nodes and start them
let nodes = toSeq(0..<2).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 2).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
await allFutures(nodes.mapIt(it.mountRelay())) await allFutures(nodes.mapIt(it.mountRelay()))
let nonExistentPeerRes = parsePeerInfo("/ip4/0.0.0.0/tcp/1000/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e") let nonExistentPeerRes = parsePeerInfo(
"/ip4/0.0.0.0/tcp/1000/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e"
)
require nonExistentPeerRes.isOk() require nonExistentPeerRes.isOk()
let nonExistentPeer = nonExistentPeerRes.value let nonExistentPeer = nonExistentPeerRes.value
# Dial non-existent peer from node1 # Dial non-existent peer from node1
let conn1 = await nodes[0].peerManager.dialPeer(nonExistentPeer, WakuLegacyFilterCodec) let conn1 =
await nodes[0].peerManager.dialPeer(nonExistentPeer, WakuLegacyFilterCodec)
check: check:
conn1.isNone() conn1.isNone()
# Dial peer not supporting given protocol # Dial peer not supporting given protocol
let conn2 = await nodes[0].peerManager.dialPeer(nodes[1].peerInfo.toRemotePeerInfo(), WakuLegacyFilterCodec) let conn2 = await nodes[0].peerManager.dialPeer(
nodes[1].peerInfo.toRemotePeerInfo(), WakuLegacyFilterCodec
)
check: check:
conn2.isNone() conn2.isNone()
@ -102,7 +122,8 @@ procSuite "Peer Manager":
asyncTest "Adding, selecting and filtering peers work": asyncTest "Adding, selecting and filtering peers work":
let let
node = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0)) node =
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
# Create filter peer # Create filter peer
filterLoc = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet() filterLoc = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet()
@ -117,23 +138,29 @@ procSuite "Peer Manager":
node.mountStoreClient() node.mountStoreClient()
node.peerManager.addServicePeer(storePeer.toRemotePeerInfo(), WakuStoreCodec) node.peerManager.addServicePeer(storePeer.toRemotePeerInfo(), WakuStoreCodec)
node.peerManager.addServicePeer(filterPeer.toRemotePeerInfo(), WakuLegacyFilterCodec) node.peerManager.addServicePeer(
filterPeer.toRemotePeerInfo(), WakuLegacyFilterCodec
)
# Check peers were successfully added to peer manager # Check peers were successfully added to peer manager
check: check:
node.peerManager.peerStore.peers().len == 2 node.peerManager.peerStore.peers().len == 2
node.peerManager.peerStore.peers(WakuLegacyFilterCodec).allIt(it.peerId == filterPeer.peerId and node.peerManager.peerStore.peers(WakuLegacyFilterCodec).allIt(
it.addrs.contains(filterLoc) and it.peerId == filterPeer.peerId and it.addrs.contains(filterLoc) and
it.protocols.contains(WakuLegacyFilterCodec)) it.protocols.contains(WakuLegacyFilterCodec)
node.peerManager.peerStore.peers(WakuStoreCodec).allIt(it.peerId == storePeer.peerId and )
it.addrs.contains(storeLoc) and node.peerManager.peerStore.peers(WakuStoreCodec).allIt(
it.protocols.contains(WakuStoreCodec)) it.peerId == storePeer.peerId and it.addrs.contains(storeLoc) and
it.protocols.contains(WakuStoreCodec)
)
await node.stop() await node.stop()
asyncTest "Peer manager keeps track of connections": asyncTest "Peer manager keeps track of connections":
# Create 2 nodes # Create 2 nodes
let nodes = toSeq(0..<2).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 2).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
await allFutures(nodes.mapIt(it.mountRelay())) await allFutures(nodes.mapIt(it.mountRelay()))
@ -142,10 +169,13 @@ procSuite "Peer Manager":
nodes[0].peerManager.addPeer(nodes[1].peerInfo.toRemotePeerInfo()) nodes[0].peerManager.addPeer(nodes[1].peerInfo.toRemotePeerInfo())
check: check:
# No information about node2's connectedness # No information about node2's connectedness
nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) == NotConnected nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) ==
NotConnected
# Failed connection # Failed connection
let nonExistentPeerRes = parsePeerInfo("/ip4/0.0.0.0/tcp/1000/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e") let nonExistentPeerRes = parsePeerInfo(
"/ip4/0.0.0.0/tcp/1000/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e"
)
require: require:
nonExistentPeerRes.isOk() nonExistentPeerRes.isOk()
@ -156,11 +186,13 @@ procSuite "Peer Manager":
check: check:
# Cannot connect to node2 # Cannot connect to node2
nodes[0].peerManager.peerStore.connectedness(nonExistentPeer.peerId) == CannotConnect nodes[0].peerManager.peerStore.connectedness(nonExistentPeer.peerId) ==
CannotConnect
# Successful connection # Successful connection
require: require:
(await nodes[0].peerManager.connectRelay(nodes[1].peerInfo.toRemotePeerInfo())) == true (await nodes[0].peerManager.connectRelay(nodes[1].peerInfo.toRemotePeerInfo())) ==
true
await sleepAsync(chronos.milliseconds(500)) await sleepAsync(chronos.milliseconds(500))
check: check:
@ -171,18 +203,23 @@ procSuite "Peer Manager":
await nodes[0].stop() await nodes[0].stop()
check: check:
# Not currently connected to node2, but had recent, successful connection. # Not currently connected to node2, but had recent, successful connection.
nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) == CanConnect nodes[0].peerManager.peerStore.connectedness(nodes[1].peerInfo.peerId) ==
CanConnect
await nodes[1].stop() await nodes[1].stop()
asyncTest "Peer manager updates failed peers correctly": asyncTest "Peer manager updates failed peers correctly":
# Create 2 nodes # Create 2 nodes
let nodes = toSeq(0..<2).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 2).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
await allFutures(nodes.mapIt(it.mountRelay())) await allFutures(nodes.mapIt(it.mountRelay()))
let nonExistentPeerRes = parsePeerInfo("/ip4/0.0.0.0/tcp/1000/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e") let nonExistentPeerRes = parsePeerInfo(
"/ip4/0.0.0.0/tcp/1000/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e"
)
require nonExistentPeerRes.isOk() require nonExistentPeerRes.isOk()
let nonExistentPeer = nonExistentPeerRes.value let nonExistentPeer = nonExistentPeerRes.value
@ -197,8 +234,10 @@ procSuite "Peer Manager":
let conn1Ok = await nodes[0].peerManager.connectRelay(nonExistentPeer) let conn1Ok = await nodes[0].peerManager.connectRelay(nonExistentPeer)
check: check:
# Cannot connect to node2 # Cannot connect to node2
nodes[0].peerManager.peerStore.connectedness(nonExistentPeer.peerId) == CannotConnect nodes[0].peerManager.peerStore.connectedness(nonExistentPeer.peerId) ==
nodes[0].peerManager.peerStore[ConnectionBook][nonExistentPeer.peerId] == CannotConnect CannotConnect
nodes[0].peerManager.peerStore[ConnectionBook][nonExistentPeer.peerId] ==
CannotConnect
nodes[0].peerManager.peerStore[NumberFailedConnBook][nonExistentPeer.peerId] == 1 nodes[0].peerManager.peerStore[NumberFailedConnBook][nonExistentPeer.peerId] == 1
# Connection attempt failed # Connection attempt failed
@ -216,7 +255,8 @@ procSuite "Peer Manager":
# After a successful connection, the number of failed connections is reset # After a successful connection, the number of failed connections is reset
nodes[0].peerManager.peerStore[NumberFailedConnBook][nodes[1].peerInfo.peerId] = 4 nodes[0].peerManager.peerStore[NumberFailedConnBook][nodes[1].peerInfo.peerId] = 4
let conn2Ok = await nodes[0].peerManager.connectRelay(nodes[1].peerInfo.toRemotePeerInfo()) let conn2Ok =
await nodes[0].peerManager.connectRelay(nodes[1].peerInfo.toRemotePeerInfo())
check: check:
conn2Ok == true conn2Ok == true
nodes[0].peerManager.peerStore[NumberFailedConnBook][nodes[1].peerInfo.peerId] == 0 nodes[0].peerManager.peerStore[NumberFailedConnBook][nodes[1].peerInfo.peerId] == 0
@ -231,9 +271,11 @@ procSuite "Peer Manager":
generateSecp256k1Key(), generateSecp256k1Key(),
ValidIpAddress.init("127.0.0.1"), ValidIpAddress.init("127.0.0.1"),
Port(44048), Port(44048),
peerStorage = storage peerStorage = storage,
) )
node2 = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("127.0.0.1"), Port(34023)) node2 = newTestWakuNode(
generateSecp256k1Key(), ValidIpAddress.init("127.0.0.1"), Port(34023)
)
node1.mountMetadata(0).expect("Mounted Waku Metadata") node1.mountMetadata(0).expect("Mounted Waku Metadata")
node2.mountMetadata(0).expect("Mounted Waku Metadata") node2.mountMetadata(0).expect("Mounted Waku Metadata")
@ -252,7 +294,8 @@ procSuite "Peer Manager":
assert is12Connected == true, "Node 1 and 2 not connected" assert is12Connected == true, "Node 1 and 2 not connected"
check: check:
node1.peerManager.peerStore[AddressBook][remotePeerInfo2.peerId] == remotePeerInfo2.addrs node1.peerManager.peerStore[AddressBook][remotePeerInfo2.peerId] ==
remotePeerInfo2.addrs
# wait for the peer store update # wait for the peer store update
await sleepAsync(chronos.milliseconds(500)) await sleepAsync(chronos.milliseconds(500))
@ -268,8 +311,8 @@ procSuite "Peer Manager":
generateSecp256k1Key(), generateSecp256k1Key(),
ValidIpAddress.init("127.0.0.1"), ValidIpAddress.init("127.0.0.1"),
Port(56037), Port(56037),
peerStorage = storage peerStorage = storage,
) )
node3.mountMetadata(0).expect("Mounted Waku Metadata") node3.mountMetadata(0).expect("Mounted Waku Metadata")
@ -303,9 +346,11 @@ procSuite "Peer Manager":
generateSecp256k1Key(), generateSecp256k1Key(),
ValidIpAddress.init("127.0.0.1"), ValidIpAddress.init("127.0.0.1"),
Port(44048), Port(44048),
peerStorage = storage peerStorage = storage,
) )
node2 = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("127.0.0.1"), Port(34023)) node2 = newTestWakuNode(
generateSecp256k1Key(), ValidIpAddress.init("127.0.0.1"), Port(34023)
)
node1.mountMetadata(0).expect("Mounted Waku Metadata") node1.mountMetadata(0).expect("Mounted Waku Metadata")
node2.mountMetadata(0).expect("Mounted Waku Metadata") node2.mountMetadata(0).expect("Mounted Waku Metadata")
@ -324,7 +369,8 @@ procSuite "Peer Manager":
assert is12Connected == true, "Node 1 and 2 not connected" assert is12Connected == true, "Node 1 and 2 not connected"
check: check:
node1.peerManager.peerStore[AddressBook][remotePeerInfo2.peerId] == remotePeerInfo2.addrs node1.peerManager.peerStore[AddressBook][remotePeerInfo2.peerId] ==
remotePeerInfo2.addrs
# wait for the peer store update # wait for the peer store update
await sleepAsync(chronos.milliseconds(500)) await sleepAsync(chronos.milliseconds(500))
@ -340,8 +386,8 @@ procSuite "Peer Manager":
generateSecp256k1Key(), generateSecp256k1Key(),
ValidIpAddress.init("127.0.0.1"), ValidIpAddress.init("127.0.0.1"),
Port(56037), Port(56037),
peerStorage = storage peerStorage = storage,
) )
node3.mountMetadata(0).expect("Mounted Waku Metadata") node3.mountMetadata(0).expect("Mounted Waku Metadata")
@ -405,13 +451,19 @@ procSuite "Peer Manager":
await allFutures([node1.start(), node2.start(), node3.start()]) await allFutures([node1.start(), node2.start(), node3.start()])
# 1->2 (fails) # 1->2 (fails)
let conn1 = await node1.peerManager.dialPeer(node2.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec) let conn1 = await node1.peerManager.dialPeer(
node2.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec
)
# 1->3 (fails) # 1->3 (fails)
let conn2 = await node1.peerManager.dialPeer(node3.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec) let conn2 = await node1.peerManager.dialPeer(
node3.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec
)
# 2->3 (succeeds) # 2->3 (succeeds)
let conn3 = await node2.peerManager.dialPeer(node3.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec) let conn3 = await node2.peerManager.dialPeer(
node3.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec
)
check: check:
conn1.isNone conn1.isNone
@ -423,8 +475,14 @@ procSuite "Peer Manager":
let let
database = SqliteDatabase.new(":memory:")[] database = SqliteDatabase.new(":memory:")[]
storage = WakuPeerStorage.new(database)[] storage = WakuPeerStorage.new(database)[]
node1 = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0), peerStorage = storage) node1 = newTestWakuNode(
node2 = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0)) generateSecp256k1Key(),
ValidIpAddress.init("0.0.0.0"),
Port(0),
peerStorage = storage,
)
node2 =
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
peerInfo2 = node2.switch.peerInfo peerInfo2 = node2.switch.peerInfo
betaCodec = "/vac/waku/relay/2.0.0-beta2" betaCodec = "/vac/waku/relay/2.0.0-beta2"
stableCodec = "/vac/waku/relay/2.0.0" stableCodec = "/vac/waku/relay/2.0.0"
@ -443,12 +501,18 @@ procSuite "Peer Manager":
# Currently connected to node2 # Currently connected to node2
node1.peerManager.peerStore.peers().len == 1 node1.peerManager.peerStore.peers().len == 1
node1.peerManager.peerStore.peers().anyIt(it.peerId == peerInfo2.peerId) node1.peerManager.peerStore.peers().anyIt(it.peerId == peerInfo2.peerId)
node1.peerManager.peerStore.peers().anyIt(it.protocols.contains(node2.wakuRelay.codec)) node1.peerManager.peerStore.peers().anyIt(
it.protocols.contains(node2.wakuRelay.codec)
)
node1.peerManager.peerStore.connectedness(peerInfo2.peerId) == Connected node1.peerManager.peerStore.connectedness(peerInfo2.peerId) == Connected
# Simulate restart by initialising a new node using the same storage # Simulate restart by initialising a new node using the same storage
let let node3 = newTestWakuNode(
node3 = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0), peerStorage = storage) generateSecp256k1Key(),
ValidIpAddress.init("0.0.0.0"),
Port(0),
peerStorage = storage,
)
await node3.mountRelay() await node3.mountRelay()
node3.wakuRelay.codec = stableCodec node3.wakuRelay.codec = stableCodec
@ -476,16 +540,14 @@ procSuite "Peer Manager":
asyncTest "Peer manager connects to all peers supporting a given protocol": asyncTest "Peer manager connects to all peers supporting a given protocol":
# Create 4 nodes # Create 4 nodes
let nodes = let nodes = toSeq(0 ..< 4).mapIt(
toSeq(0..<4) newTestWakuNode(
.mapIt( nodeKey = generateSecp256k1Key(),
newTestWakuNode( bindIp = ValidIpAddress.init("0.0.0.0"),
nodeKey = generateSecp256k1Key(), bindPort = Port(0),
bindIp = ValidIpAddress.init("0.0.0.0"), wakuFlags = some(CapabilitiesBitfield.init(@[Relay])),
bindPort = Port(0),
wakuFlags = some(CapabilitiesBitfield.init(@[Relay]))
)
) )
)
# Start them # Start them
discard nodes.mapIt(it.mountMetadata(0)) discard nodes.mapIt(it.mountMetadata(0))
@ -494,7 +556,7 @@ procSuite "Peer Manager":
# Get all peer infos # Get all peer infos
let peerInfos = collect: let peerInfos = collect:
for i in 0..nodes.high: for i in 0 .. nodes.high:
let peerInfo = nodes[i].switch.peerInfo.toRemotePeerInfo() let peerInfo = nodes[i].switch.peerInfo.toRemotePeerInfo()
peerInfo.enr = some(nodes[i].enr) peerInfo.enr = some(nodes[i].enr)
peerInfo peerInfo
@ -512,34 +574,47 @@ procSuite "Peer Manager":
nodes[0].peerManager.peerStore.peers().len == 3 nodes[0].peerManager.peerStore.peers().len == 3
# All peer ids are correct # All peer ids are correct
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[1].switch.peerInfo.peerId) nodes[0].peerManager.peerStore.peers().anyIt(
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[2].switch.peerInfo.peerId) it.peerId == nodes[1].switch.peerInfo.peerId
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[3].switch.peerInfo.peerId) )
nodes[0].peerManager.peerStore.peers().anyIt(
it.peerId == nodes[2].switch.peerInfo.peerId
)
nodes[0].peerManager.peerStore.peers().anyIt(
it.peerId == nodes[3].switch.peerInfo.peerId
)
# All peers support the relay protocol # All peers support the relay protocol
nodes[0].peerManager.peerStore[ProtoBook][nodes[1].switch.peerInfo.peerId].contains(WakuRelayCodec) nodes[0].peerManager.peerStore[ProtoBook][nodes[1].switch.peerInfo.peerId].contains(
nodes[0].peerManager.peerStore[ProtoBook][nodes[2].switch.peerInfo.peerId].contains(WakuRelayCodec) WakuRelayCodec
nodes[0].peerManager.peerStore[ProtoBook][nodes[3].switch.peerInfo.peerId].contains(WakuRelayCodec) )
nodes[0].peerManager.peerStore[ProtoBook][nodes[2].switch.peerInfo.peerId].contains(
WakuRelayCodec
)
nodes[0].peerManager.peerStore[ProtoBook][nodes[3].switch.peerInfo.peerId].contains(
WakuRelayCodec
)
# All peers are connected # All peers are connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[1].switch.peerInfo.peerId] == Connected nodes[0].peerManager.peerStore[ConnectionBook][nodes[1].switch.peerInfo.peerId] ==
nodes[0].peerManager.peerStore[ConnectionBook][nodes[2].switch.peerInfo.peerId] == Connected Connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[3].switch.peerInfo.peerId] == Connected nodes[0].peerManager.peerStore[ConnectionBook][nodes[2].switch.peerInfo.peerId] ==
Connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[3].switch.peerInfo.peerId] ==
Connected
await allFutures(nodes.mapIt(it.stop())) await allFutures(nodes.mapIt(it.stop()))
asyncTest "Sharded peer manager connects to all peers supporting a given protocol": asyncTest "Sharded peer manager connects to all peers supporting a given protocol":
# Create 4 nodes # Create 4 nodes
let nodes = let nodes = toSeq(0 ..< 4).mapIt(
toSeq(0..<4) newTestWakuNode(
.mapIt( nodeKey = generateSecp256k1Key(),
newTestWakuNode( bindIp = ValidIpAddress.init("0.0.0.0"),
nodeKey = generateSecp256k1Key(), bindPort = Port(0),
bindIp = ValidIpAddress.init("0.0.0.0"), wakuFlags = some(CapabilitiesBitfield.init(@[Relay])),
bindPort = Port(0),
wakuFlags = some(CapabilitiesBitfield.init(@[Relay]))
)
) )
)
# Start them # Start them
discard nodes.mapIt(it.mountMetadata(0)) discard nodes.mapIt(it.mountMetadata(0))
@ -548,7 +623,7 @@ procSuite "Peer Manager":
# Get all peer infos # Get all peer infos
let peerInfos = collect: let peerInfos = collect:
for i in 0..nodes.high: for i in 0 .. nodes.high:
let peerInfo = nodes[i].switch.peerInfo.toRemotePeerInfo() let peerInfo = nodes[i].switch.peerInfo.toRemotePeerInfo()
peerInfo.enr = some(nodes[i].enr) peerInfo.enr = some(nodes[i].enr)
peerInfo peerInfo
@ -566,25 +641,42 @@ procSuite "Peer Manager":
nodes[0].peerManager.peerStore.peers().len == 3 nodes[0].peerManager.peerStore.peers().len == 3
# All peer ids are correct # All peer ids are correct
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[1].switch.peerInfo.peerId) nodes[0].peerManager.peerStore.peers().anyIt(
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[2].switch.peerInfo.peerId) it.peerId == nodes[1].switch.peerInfo.peerId
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[3].switch.peerInfo.peerId) )
nodes[0].peerManager.peerStore.peers().anyIt(
it.peerId == nodes[2].switch.peerInfo.peerId
)
nodes[0].peerManager.peerStore.peers().anyIt(
it.peerId == nodes[3].switch.peerInfo.peerId
)
# All peers support the relay protocol # All peers support the relay protocol
nodes[0].peerManager.peerStore[ProtoBook][nodes[1].switch.peerInfo.peerId].contains(WakuRelayCodec) nodes[0].peerManager.peerStore[ProtoBook][nodes[1].switch.peerInfo.peerId].contains(
nodes[0].peerManager.peerStore[ProtoBook][nodes[2].switch.peerInfo.peerId].contains(WakuRelayCodec) WakuRelayCodec
nodes[0].peerManager.peerStore[ProtoBook][nodes[3].switch.peerInfo.peerId].contains(WakuRelayCodec) )
nodes[0].peerManager.peerStore[ProtoBook][nodes[2].switch.peerInfo.peerId].contains(
WakuRelayCodec
)
nodes[0].peerManager.peerStore[ProtoBook][nodes[3].switch.peerInfo.peerId].contains(
WakuRelayCodec
)
# All peers are connected # All peers are connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[1].switch.peerInfo.peerId] == Connected nodes[0].peerManager.peerStore[ConnectionBook][nodes[1].switch.peerInfo.peerId] ==
nodes[0].peerManager.peerStore[ConnectionBook][nodes[2].switch.peerInfo.peerId] == Connected Connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[3].switch.peerInfo.peerId] == Connected nodes[0].peerManager.peerStore[ConnectionBook][nodes[2].switch.peerInfo.peerId] ==
Connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[3].switch.peerInfo.peerId] ==
Connected
await allFutures(nodes.mapIt(it.stop())) await allFutures(nodes.mapIt(it.stop()))
asyncTest "Peer store keeps track of incoming connections": asyncTest "Peer store keeps track of incoming connections":
# Create 4 nodes # Create 4 nodes
let nodes = toSeq(0..<4).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 4).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
# Start them # Start them
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
@ -616,29 +708,50 @@ procSuite "Peer Manager":
nodes[3].peerManager.peerStore.getPeersByDirection(Outbound).len == 1 nodes[3].peerManager.peerStore.getPeersByDirection(Outbound).len == 1
# All peer ids are correct # All peer ids are correct
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[1].switch.peerInfo.peerId) nodes[0].peerManager.peerStore.peers().anyIt(
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[2].switch.peerInfo.peerId) it.peerId == nodes[1].switch.peerInfo.peerId
nodes[0].peerManager.peerStore.peers().anyIt(it.peerId == nodes[3].switch.peerInfo.peerId) )
nodes[0].peerManager.peerStore.peers().anyIt(
it.peerId == nodes[2].switch.peerInfo.peerId
)
nodes[0].peerManager.peerStore.peers().anyIt(
it.peerId == nodes[3].switch.peerInfo.peerId
)
# All peers support the relay protocol # All peers support the relay protocol
nodes[0].peerManager.peerStore[ProtoBook][nodes[1].switch.peerInfo.peerId].contains(WakuRelayCodec) nodes[0].peerManager.peerStore[ProtoBook][nodes[1].switch.peerInfo.peerId].contains(
nodes[0].peerManager.peerStore[ProtoBook][nodes[2].switch.peerInfo.peerId].contains(WakuRelayCodec) WakuRelayCodec
nodes[0].peerManager.peerStore[ProtoBook][nodes[3].switch.peerInfo.peerId].contains(WakuRelayCodec) )
nodes[0].peerManager.peerStore[ProtoBook][nodes[2].switch.peerInfo.peerId].contains(
WakuRelayCodec
)
nodes[0].peerManager.peerStore[ProtoBook][nodes[3].switch.peerInfo.peerId].contains(
WakuRelayCodec
)
# All peers are connected # All peers are connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[1].switch.peerInfo.peerId] == Connected nodes[0].peerManager.peerStore[ConnectionBook][nodes[1].switch.peerInfo.peerId] ==
nodes[0].peerManager.peerStore[ConnectionBook][nodes[2].switch.peerInfo.peerId] == Connected Connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[3].switch.peerInfo.peerId] == Connected nodes[0].peerManager.peerStore[ConnectionBook][nodes[2].switch.peerInfo.peerId] ==
Connected
nodes[0].peerManager.peerStore[ConnectionBook][nodes[3].switch.peerInfo.peerId] ==
Connected
# All peers are Inbound in peer 0 # All peers are Inbound in peer 0
nodes[0].peerManager.peerStore[DirectionBook][nodes[1].switch.peerInfo.peerId] == Inbound nodes[0].peerManager.peerStore[DirectionBook][nodes[1].switch.peerInfo.peerId] ==
nodes[0].peerManager.peerStore[DirectionBook][nodes[2].switch.peerInfo.peerId] == Inbound Inbound
nodes[0].peerManager.peerStore[DirectionBook][nodes[3].switch.peerInfo.peerId] == Inbound nodes[0].peerManager.peerStore[DirectionBook][nodes[2].switch.peerInfo.peerId] ==
Inbound
nodes[0].peerManager.peerStore[DirectionBook][nodes[3].switch.peerInfo.peerId] ==
Inbound
# All peers have an Outbound connection with peer 0 # All peers have an Outbound connection with peer 0
nodes[1].peerManager.peerStore[DirectionBook][nodes[0].switch.peerInfo.peerId] == Outbound nodes[1].peerManager.peerStore[DirectionBook][nodes[0].switch.peerInfo.peerId] ==
nodes[2].peerManager.peerStore[DirectionBook][nodes[0].switch.peerInfo.peerId] == Outbound Outbound
nodes[3].peerManager.peerStore[DirectionBook][nodes[0].switch.peerInfo.peerId] == Outbound nodes[2].peerManager.peerStore[DirectionBook][nodes[0].switch.peerInfo.peerId] ==
Outbound
nodes[3].peerManager.peerStore[DirectionBook][nodes[0].switch.peerInfo.peerId] ==
Outbound
await allFutures(nodes.mapIt(it.stop())) await allFutures(nodes.mapIt(it.stop()))
@ -647,13 +760,12 @@ procSuite "Peer Manager":
let basePeerId = "16Uiu2HAm7QGEZKujdSbbo1aaQyfDPQ6Bw3ybQnj6fruH5Dxwd7D" let basePeerId = "16Uiu2HAm7QGEZKujdSbbo1aaQyfDPQ6Bw3ybQnj6fruH5Dxwd7D"
let let
node = newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0)) node =
peers = toSeq(1..5) newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
.mapIt( peers = toSeq(1 .. 5)
parsePeerInfo("/ip4/0.0.0.0/tcp/30300/p2p/" & basePeerId & $it) .mapIt(parsePeerInfo("/ip4/0.0.0.0/tcp/30300/p2p/" & basePeerId & $it))
) .filterIt(it.isOk())
.filterIt(it.isOk()) .mapIt(it.value)
.mapIt(it.value)
require: require:
peers.len == 5 peers.len == 5
@ -689,7 +801,9 @@ procSuite "Peer Manager":
asyncTest "connectedPeers() returns expected number of connections per protocol": asyncTest "connectedPeers() returns expected number of connections per protocol":
# Create 4 nodes # Create 4 nodes
let nodes = toSeq(0..<4).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 4).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
# Start them with relay + filter # Start them with relay + filter
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
@ -706,12 +820,14 @@ procSuite "Peer Manager":
(await nodes[0].peerManager.connectRelay(pInfos[2])) == true (await nodes[0].peerManager.connectRelay(pInfos[2])) == true
(await nodes[1].peerManager.connectRelay(pInfos[2])) == true (await nodes[1].peerManager.connectRelay(pInfos[2])) == true
(await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() == true (await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() ==
(await nodes[0].peerManager.dialPeer(pInfos[2], WakuLegacyFilterCodec)).isSome() == true true
(await nodes[0].peerManager.dialPeer(pInfos[2], WakuLegacyFilterCodec)).isSome() ==
true
# isolated dial creates a relay conn under the hood (libp2p behaviour) # isolated dial creates a relay conn under the hood (libp2p behaviour)
(await nodes[2].peerManager.dialPeer(pInfos[3], WakuLegacyFilterCodec)).isSome() == true (await nodes[2].peerManager.dialPeer(pInfos[3], WakuLegacyFilterCodec)).isSome() ==
true
# assert physical connections # assert physical connections
check: check:
@ -741,7 +857,9 @@ procSuite "Peer Manager":
asyncTest "getNumStreams() returns expected number of connections per protocol": asyncTest "getNumStreams() returns expected number of connections per protocol":
# Create 2 nodes # Create 2 nodes
let nodes = toSeq(0..<2).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 2).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
# Start them with relay + filter # Start them with relay + filter
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))
@ -754,10 +872,14 @@ procSuite "Peer Manager":
require: require:
# multiple streams are multiplexed over a single connection. # multiple streams are multiplexed over a single connection.
# note that a relay connection is created under the hood when dialing a peer (libp2p behaviour) # note that a relay connection is created under the hood when dialing a peer (libp2p behaviour)
(await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() == true (await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() ==
(await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() == true true
(await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() == true (await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() ==
(await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() == true true
(await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() ==
true
(await nodes[0].peerManager.dialPeer(pInfos[1], WakuLegacyFilterCodec)).isSome() ==
true
check: check:
nodes[0].peerManager.getNumStreams(WakuRelayCodec) == (1, 1) nodes[0].peerManager.getNumStreams(WakuRelayCodec) == (1, 1)
@ -773,19 +895,21 @@ procSuite "Peer Manager":
# Create peer manager # Create peer manager
let pm = PeerManager.new( let pm = PeerManager.new(
switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise().build(), switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise().build(),
storage = nil) storage = nil,
)
# Create 3 peer infos # Create 3 peer infos
let peers = toSeq(1..3) let peers = toSeq(1 .. 3)
.mapIt(parsePeerInfo("/ip4/0.0.0.0/tcp/30300/p2p/" & basePeerId & $it)) .mapIt(parsePeerInfo("/ip4/0.0.0.0/tcp/30300/p2p/" & basePeerId & $it))
.filterIt(it.isOk()) .filterIt(it.isOk())
.mapIt(it.value) .mapIt(it.value)
require: require:
peers.len == 3 peers.len == 3
# Add a peer[0] to the peerstore # Add a peer[0] to the peerstore
pm.peerStore[AddressBook][peers[0].peerId] = peers[0].addrs pm.peerStore[AddressBook][peers[0].peerId] = peers[0].addrs
pm.peerStore[ProtoBook][peers[0].peerId] = @[WakuRelayCodec, WakuStoreCodec, WakuLegacyFilterCodec] pm.peerStore[ProtoBook][peers[0].peerId] =
@[WakuRelayCodec, WakuStoreCodec, WakuLegacyFilterCodec]
# When no service peers, we get one from the peerstore # When no service peers, we get one from the peerstore
let selectedPeer1 = pm.selectPeer(WakuStoreCodec) let selectedPeer1 = pm.selectPeer(WakuStoreCodec)
@ -826,28 +950,38 @@ procSuite "Peer Manager":
expect(Defect): expect(Defect):
let pm = PeerManager.new( let pm = PeerManager.new(
switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise() switch = SwitchBuilder
.withPeerStore(peerStoreSize) .new()
.withMaxConnections(maxConnections) .withRng(rng)
.build(), .withMplex()
storage = nil) .withNoise()
.withPeerStore(peerStoreSize)
.withMaxConnections(maxConnections)
.build(),
storage = nil,
)
test "prunePeerStore() correctly removes peers to match max quota": test "prunePeerStore() correctly removes peers to match max quota":
# Create peer manager # Create peer manager
let pm = PeerManager.new( let pm = PeerManager.new(
switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise() switch = SwitchBuilder
.withPeerStore(10) .new()
.withMaxConnections(5) .withRng(rng)
.build(), .withMplex()
.withNoise()
.withPeerStore(10)
.withMaxConnections(5)
.build(),
maxFailedAttempts = 1, maxFailedAttempts = 1,
maxRelayPeers = some(5), maxRelayPeers = some(5),
storage = nil) storage = nil,
)
# Create 15 peers and add them to the peerstore # Create 15 peers and add them to the peerstore
let peers = toSeq(1..15) let peers = toSeq(1 .. 15)
.mapIt(parsePeerInfo("/ip4/0.0.0.0/tcp/0/p2p/" & $PeerId.random().get())) .mapIt(parsePeerInfo("/ip4/0.0.0.0/tcp/0/p2p/" & $PeerId.random().get()))
.filterIt(it.isOk()) .filterIt(it.isOk())
.mapIt(it.value) .mapIt(it.value)
for p in peers: for p in peers:
pm.addPeer(p) pm.addPeer(p)
@ -886,19 +1020,24 @@ procSuite "Peer Manager":
asyncTest "canBeConnected() returns correct value": asyncTest "canBeConnected() returns correct value":
let pm = PeerManager.new( let pm = PeerManager.new(
switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise() switch = SwitchBuilder
.withPeerStore(10) .new()
.withMaxConnections(5) .withRng(rng)
.build(), .withMplex()
initialBackoffInSec = 1, # with InitialBackoffInSec = 1 backoffs are: 1, 2, 4, 8secs. .withNoise()
.withPeerStore(10)
.withMaxConnections(5)
.build(),
initialBackoffInSec = 1,
# with InitialBackoffInSec = 1 backoffs are: 1, 2, 4, 8secs.
backoffFactor = 2, backoffFactor = 2,
maxFailedAttempts = 10, maxFailedAttempts = 10,
maxRelayPeers = some(5), maxRelayPeers = some(5),
storage = nil) storage = nil,
)
var p1: PeerId var p1: PeerId
require p1.init("QmeuZJbXrszW2jdT7GdduSjQskPU3S7vvGWKtKgDfkDvW" & "1") require p1.init("QmeuZJbXrszW2jdT7GdduSjQskPU3S7vvGWKtKgDfkDvW" & "1")
# new peer with no errors can be connected # new peer with no errors can be connected
check: check:
pm.canBeConnected(p1) == true pm.canBeConnected(p1) == true
@ -938,37 +1077,54 @@ procSuite "Peer Manager":
# Should result in overflow exception # Should result in overflow exception
expect(Defect): expect(Defect):
let pm = PeerManager.new( let pm = PeerManager.new(
switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise() switch = SwitchBuilder
.withPeerStore(10) .new()
.withMaxConnections(5) .withRng(rng)
.build(), .withMplex()
.withNoise()
.withPeerStore(10)
.withMaxConnections(5)
.build(),
maxRelayPeers = some(5), maxRelayPeers = some(5),
maxFailedAttempts = 150, maxFailedAttempts = 150,
storage = nil) storage = nil,
)
# Should result in backoff > 1 week # Should result in backoff > 1 week
expect(Defect): expect(Defect):
let pm = PeerManager.new( let pm = PeerManager.new(
switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise() switch = SwitchBuilder
.new()
.withRng(rng)
.withMplex()
.withNoise()
.withPeerStore(10)
.withMaxConnections(5)
.build(),
maxFailedAttempts = 10,
maxRelayPeers = some(5),
storage = nil,
)
let pm = PeerManager.new(
switch = SwitchBuilder
.new()
.withRng(rng)
.withMplex()
.withNoise()
.withPeerStore(10) .withPeerStore(10)
.withMaxConnections(5) .withMaxConnections(5)
.build(), .build(),
maxFailedAttempts = 10,
maxRelayPeers = some(5),
storage = nil)
let pm = PeerManager.new(
switch = SwitchBuilder.new().withRng(rng).withMplex().withNoise()
.withPeerStore(10)
.withMaxConnections(5)
.build(),
maxFailedAttempts = 5, maxFailedAttempts = 5,
maxRelayPeers = some(5), maxRelayPeers = some(5),
storage = nil) storage = nil,
)
asyncTest "colocationLimit is enforced by pruneConnsByIp()": asyncTest "colocationLimit is enforced by pruneConnsByIp()":
# Create 5 nodes # Create 5 nodes
let nodes = toSeq(0..<5).mapIt(newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))) let nodes = toSeq(0 ..< 5).mapIt(
newTestWakuNode(generateSecp256k1Key(), ValidIpAddress.init("0.0.0.0"), Port(0))
)
# Start them with relay + filter # Start them with relay + filter
await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.start()))

View File

@ -1,10 +1,6 @@
{.used.} {.used.}
import import std/options, testutils/unittests, eth/p2p/discoveryv5/enr, libp2p/crypto/crypto
std/options,
testutils/unittests,
eth/p2p/discoveryv5/enr,
libp2p/crypto/crypto
import import
../../waku/common/databases/db_sqlite, ../../waku/common/databases/db_sqlite,
../../waku/node/peer_manager/peer_manager, ../../waku/node/peer_manager/peer_manager,
@ -12,9 +8,7 @@ import
../../waku/waku_enr, ../../waku/waku_enr,
./testlib/wakucore ./testlib/wakucore
suite "Peer Storage": suite "Peer Storage":
test "Store, replace and retrieve from persistent peer storage": test "Store, replace and retrieve from persistent peer storage":
let let
database = SqliteDatabase.new(":memory:").tryGet() database = SqliteDatabase.new(":memory:").tryGet()
@ -35,15 +29,17 @@ suite "Peer Storage":
let record = enrBuilder.build().expect("Valid record") let record = enrBuilder.build().expect("Valid record")
let stored = RemotePeerInfo( let stored = RemotePeerInfo(
peerId: peer.peerId, peerId: peer.peerId,
addrs: @[peerLoc], addrs: @[peerLoc],
enr: some(record), enr: some(record),
protocols: @[peerProto], protocols: @[peerProto],
publicKey: peerKey.getPublicKey().tryGet(), publicKey: peerKey.getPublicKey().tryGet(),
connectedness: connectedness, connectedness: connectedness,
disconnectTime: disconn) disconnectTime: disconn,
)
defer: storage.close() defer:
storage.close()
# Test insert and retrieve # Test insert and retrieve
@ -71,7 +67,8 @@ suite "Peer Storage":
resStoredInfo.disconnectTime == disconn resStoredInfo.disconnectTime == disconn
assert resStoredInfo.enr.isSome(), "The ENR info wasn't properly stored" assert resStoredInfo.enr.isSome(), "The ENR info wasn't properly stored"
check: resStoredInfo.enr.get() == record check:
resStoredInfo.enr.get() == record
# Test replace and retrieve (update an existing entry) # Test replace and retrieve (update an existing entry)
stored.connectedness = CannotConnect stored.connectedness = CannotConnect

View File

@ -14,7 +14,6 @@ import
../../waku/waku_node, ../../waku/waku_node,
./testlib/wakucore ./testlib/wakucore
suite "Extended nim-libp2p Peer Store": suite "Extended nim-libp2p Peer Store":
# Valid peerId missing the last digit. Useful for creating new peerIds # Valid peerId missing the last digit. Useful for creating new peerIds
# basePeerId & "1" # basePeerId & "1"
@ -64,7 +63,8 @@ suite "Extended nim-libp2p Peer Store":
# Peer3: Connected # Peer3: Connected
peerStore[AddressBook][p3] = @[MultiAddress.init("/ip4/127.0.0.1/tcp/3").tryGet()] peerStore[AddressBook][p3] = @[MultiAddress.init("/ip4/127.0.0.1/tcp/3").tryGet()]
peerStore[ProtoBook][p3] = @["/vac/waku/lightpush/2.0.0", "/vac/waku/store/2.0.0-beta1"] peerStore[ProtoBook][p3] =
@["/vac/waku/lightpush/2.0.0", "/vac/waku/store/2.0.0-beta1"]
peerStore[KeyBook][p3] = generateEcdsaKeyPair().pubkey peerStore[KeyBook][p3] = generateEcdsaKeyPair().pubkey
peerStore[AgentBook][p3] = "gowaku" peerStore[AgentBook][p3] = "gowaku"
peerStore[ProtoVersionBook][p3] = "protoVersion3" peerStore[ProtoVersionBook][p3] = "protoVersion3"
@ -180,7 +180,8 @@ suite "Extended nim-libp2p Peer Store":
# Only p3 supports that protocol # Only p3 supports that protocol
lpPeers.len == 1 lpPeers.len == 1
lpPeers.anyIt(it.peerId == p3) lpPeers.anyIt(it.peerId == p3)
lpPeers[0].protocols == @["/vac/waku/lightpush/2.0.0", "/vac/waku/store/2.0.0-beta1"] lpPeers[0].protocols ==
@["/vac/waku/lightpush/2.0.0", "/vac/waku/store/2.0.0-beta1"]
test "peers() returns all StoredInfo matching a given protocolMatcher": test "peers() returns all StoredInfo matching a given protocolMatcher":
# When # When
@ -197,15 +198,20 @@ suite "Extended nim-libp2p Peer Store":
pMatcherStorePeers.anyIt(it.peerId == p5) pMatcherStorePeers.anyIt(it.peerId == p5)
check: check:
pMatcherStorePeers.filterIt(it.peerId == p1)[0].protocols == @["/vac/waku/relay/2.0.0-beta1", "/vac/waku/store/2.0.0"] pMatcherStorePeers.filterIt(it.peerId == p1)[0].protocols ==
pMatcherStorePeers.filterIt(it.peerId == p2)[0].protocols == @["/vac/waku/relay/2.0.0", "/vac/waku/store/2.0.0"] @["/vac/waku/relay/2.0.0-beta1", "/vac/waku/store/2.0.0"]
pMatcherStorePeers.filterIt(it.peerId == p3)[0].protocols == @["/vac/waku/lightpush/2.0.0", "/vac/waku/store/2.0.0-beta1"] pMatcherStorePeers.filterIt(it.peerId == p2)[0].protocols ==
pMatcherStorePeers.filterIt(it.peerId == p5)[0].protocols == @["/vac/waku/swap/2.0.0", "/vac/waku/store/2.0.0-beta2"] @["/vac/waku/relay/2.0.0", "/vac/waku/store/2.0.0"]
pMatcherStorePeers.filterIt(it.peerId == p3)[0].protocols ==
@["/vac/waku/lightpush/2.0.0", "/vac/waku/store/2.0.0-beta1"]
pMatcherStorePeers.filterIt(it.peerId == p5)[0].protocols ==
@["/vac/waku/swap/2.0.0", "/vac/waku/store/2.0.0-beta2"]
check: check:
pMatcherSwapPeers.len == 1 pMatcherSwapPeers.len == 1
pMatcherSwapPeers.anyIt(it.peerId == p5) pMatcherSwapPeers.anyIt(it.peerId == p5)
pMatcherSwapPeers[0].protocols == @["/vac/waku/swap/2.0.0", "/vac/waku/store/2.0.0-beta2"] pMatcherSwapPeers[0].protocols ==
@["/vac/waku/swap/2.0.0", "/vac/waku/store/2.0.0-beta2"]
test "toRemotePeerInfo() converts a StoredInfo to a RemotePeerInfo": test "toRemotePeerInfo() converts a StoredInfo to a RemotePeerInfo":
# Given # Given

View File

@ -11,10 +11,7 @@ import
libp2p/protocols/pubsub/gossipsub libp2p/protocols/pubsub/gossipsub
import import
../../waku/waku_core, ../../waku/waku_core, ../../waku/waku_node, ./testlib/wakucore, ./testlib/wakunode
../../waku/waku_node,
./testlib/wakucore,
./testlib/wakunode
procSuite "Relay (GossipSub) Peer Exchange": procSuite "Relay (GossipSub) Peer Exchange":
asyncTest "Mount relay without peer exchange handler": asyncTest "Mount relay without peer exchange handler":

View File

@ -1,7 +1,6 @@
{.used.} {.used.}
import import testutils/unittests
testutils/unittests
import import
stew/results, stew/results,
../../waku/waku_core/message, ../../waku/waku_core/message,
@ -9,7 +8,6 @@ import
./testlib/common ./testlib/common
suite "Waku Payload": suite "Waku Payload":
test "Encode/Decode waku message with timestamp": test "Encode/Decode waku message with timestamp":
## Test encoding and decoding of the timestamp field of a WakuMessage ## Test encoding and decoding of the timestamp field of a WakuMessage
@ -21,7 +19,7 @@ suite "Waku Payload":
msg = WakuMessage(payload: payload, version: version, timestamp: timestamp) msg = WakuMessage(payload: payload, version: version, timestamp: timestamp)
## When ## When
let pb = msg.encode() let pb = msg.encode()
let msgDecoded = WakuMessage.decode(pb.buffer) let msgDecoded = WakuMessage.decode(pb.buffer)
## Then ## Then
@ -42,7 +40,7 @@ suite "Waku Payload":
msg = WakuMessage(payload: payload, version: version) msg = WakuMessage(payload: payload, version: version)
## When ## When
let pb = msg.encode() let pb = msg.encode()
let msgDecoded = WakuMessage.decode(pb.buffer) let msgDecoded = WakuMessage.decode(pb.buffer)
## Then ## Then

View File

@ -42,9 +42,12 @@ suite "Waku DNS Discovery":
await allFutures([node1.start(), node2.start(), node3.start()]) await allFutures([node1.start(), node2.start(), node3.start()])
# Build and sign tree # Build and sign tree
var tree = buildTree(1, # Seq no var tree = buildTree(
@[enr1, enr2, enr3], # ENR entries 1, # Seq no
@[]).get() # No link entries @[enr1, enr2, enr3], # ENR entries
@[],
)
.get() # No link entries
let treeKeys = keys.KeyPair.random(rng[]) let treeKeys = keys.KeyPair.random(rng[])
@ -57,7 +60,8 @@ suite "Waku DNS Discovery":
domain = "testnodes.aq" domain = "testnodes.aq"
zoneTxts = tree.buildTXT(domain).get() zoneTxts = tree.buildTXT(domain).get()
username = Base32.encode(treeKeys.pubkey().toRawCompressed()) username = Base32.encode(treeKeys.pubkey().toRawCompressed())
location = LinkPrefix & username & "@" & domain # See EIP-1459: https://eips.ethereum.org/EIPS/eip-1459 location = LinkPrefix & username & "@" & domain
# See EIP-1459: https://eips.ethereum.org/EIPS/eip-1459
# Create a resolver for the domain # Create a resolver for the domain
@ -90,11 +94,20 @@ suite "Waku DNS Discovery":
check: check:
# We have successfully connected to all discovered nodes # We have successfully connected to all discovered nodes
node4.peerManager.peerStore.peers().anyIt(it.peerId == node1.switch.peerInfo.peerId) node4.peerManager.peerStore.peers().anyIt(
node4.peerManager.peerStore.connectedness(node1.switch.peerInfo.peerId) == Connected it.peerId == node1.switch.peerInfo.peerId
node4.peerManager.peerStore.peers().anyIt(it.peerId == node2.switch.peerInfo.peerId) )
node4.peerManager.peerStore.connectedness(node2.switch.peerInfo.peerId) == Connected node4.peerManager.peerStore.connectedness(node1.switch.peerInfo.peerId) ==
node4.peerManager.peerStore.peers().anyIt(it.peerId == node3.switch.peerInfo.peerId) Connected
node4.peerManager.peerStore.connectedness(node3.switch.peerInfo.peerId) == Connected node4.peerManager.peerStore.peers().anyIt(
it.peerId == node2.switch.peerInfo.peerId
)
node4.peerManager.peerStore.connectedness(node2.switch.peerInfo.peerId) ==
Connected
node4.peerManager.peerStore.peers().anyIt(
it.peerId == node3.switch.peerInfo.peerId
)
node4.peerManager.peerStore.connectedness(node3.switch.peerInfo.peerId) ==
Connected
await allFutures([node1.stop(), node2.stop(), node3.stop(), node4.stop()]) await allFutures([node1.stop(), node2.stop(), node3.stop(), node4.stop()])

View File

@ -1,19 +1,12 @@
{.used.} {.used.}
import import std/[options, sequtils], stew/results, testutils/unittests
std/[options, sequtils], import ../../waku/waku_core, ../../waku/waku_enr, ./testlib/wakucore
stew/results,
testutils/unittests
import
../../waku/waku_core,
../../waku/waku_enr,
./testlib/wakucore
suite "Waku ENR - Capabilities bitfield": suite "Waku ENR - Capabilities bitfield":
test "check capabilities support": test "check capabilities support":
## Given ## Given
let bitfield: CapabilitiesBitfield = 0b0000_1101u8 # Lightpush, Filter, Relay let bitfield: CapabilitiesBitfield = 0b0000_1101u8 # Lightpush, Filter, Relay
## Then ## Then
check: check:
@ -25,11 +18,8 @@ suite "Waku ENR - Capabilities bitfield":
test "bitfield to capabilities list": test "bitfield to capabilities list":
## Given ## Given
let bitfield = CapabilitiesBitfield.init( let bitfield = CapabilitiesBitfield.init(
relay = true, relay = true, store = false, lightpush = true, filter = true
store = false, )
lightpush = true,
filter = true
)
## When ## When
let caps = bitfield.toCapabilities() let caps = bitfield.toCapabilities()
@ -83,9 +73,10 @@ suite "Waku ENR - Capabilities bitfield":
test "check capabilities on a waku node record": test "check capabilities on a waku node record":
## Given ## Given
let wakuRecord = "-Hy4QC73_E3B_FkZhsOakaD4pHe-U--UoGASdG9N0F3SFFUDY_jdQbud8" & let wakuRecord =
"EXVyrlOZ5pZ7VYFBDPMRCENwy87Lh74dFIBgmlkgnY0iXNlY3AyNTZrMaECvNt1jIWbWGp" & "-Hy4QC73_E3B_FkZhsOakaD4pHe-U--UoGASdG9N0F3SFFUDY_jdQbud8" &
"AWWdlLGYm1E1OjlkQk3ONoxDC5sfw8oOFd2FrdTID" "EXVyrlOZ5pZ7VYFBDPMRCENwy87Lh74dFIBgmlkgnY0iXNlY3AyNTZrMaECvNt1jIWbWGp" &
"AWWdlLGYm1E1OjlkQk3ONoxDC5sfw8oOFd2FrdTID"
## When ## When
var record: Record var record: Record
@ -109,9 +100,10 @@ suite "Waku ENR - Capabilities bitfield":
test "check capabilities on a non-waku node record": test "check capabilities on a non-waku node record":
## Given ## Given
# non waku enr, i.e. Ethereum one # non waku enr, i.e. Ethereum one
let nonWakuEnr = "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2G" & let nonWakuEnr =
"xb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNl" & "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2G" &
"Y3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA" "xb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNl" &
"Y3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA"
## When ## When
var record: Record var record: Record
@ -131,26 +123,33 @@ suite "Waku ENR - Capabilities bitfield":
record.supportsCapability(Capabilities.Filter) == false record.supportsCapability(Capabilities.Filter) == false
record.supportsCapability(Capabilities.Lightpush) == false record.supportsCapability(Capabilities.Lightpush) == false
suite "Waku ENR - Multiaddresses": suite "Waku ENR - Multiaddresses":
test "decode record with multiaddrs field": test "decode record with multiaddrs field":
## Given ## Given
let enrUri = "enr:-QEnuEBEAyErHEfhiQxAVQoWowGTCuEF9fKZtXSd7H_PymHFhGJA3rGAYDVSH" & let enrUri =
"KCyJDGRLBGsloNbS8AZF33IVuefjOO6BIJpZIJ2NIJpcIQS39tkim11bHRpYWRkcn" & "enr:-QEnuEBEAyErHEfhiQxAVQoWowGTCuEF9fKZtXSd7H_PymHFhGJA3rGAYDVSH" &
"O4lgAvNihub2RlLTAxLmRvLWFtczMud2FrdXYyLnRlc3Quc3RhdHVzaW0ubmV0BgG" & "KCyJDGRLBGsloNbS8AZF33IVuefjOO6BIJpZIJ2NIJpcIQS39tkim11bHRpYWRkcn" &
"73gMAODcxbm9kZS0wMS5hYy1jbi1ob25na29uZy1jLndha3V2Mi50ZXN0LnN0YXR1" & "O4lgAvNihub2RlLTAxLmRvLWFtczMud2FrdXYyLnRlc3Quc3RhdHVzaW0ubmV0BgG" &
"c2ltLm5ldAYBu94DACm9A62t7AQL4Ef5ZYZosRpQTzFVAB8jGjf1TER2wH-0zBOe1" & "73gMAODcxbm9kZS0wMS5hYy1jbi1ob25na29uZy1jLndha3V2Mi50ZXN0LnN0YXR1" &
"-MDBNLeA4lzZWNwMjU2azGhAzfsxbxyCkgCqq8WwYsVWH7YkpMLnU2Bw5xJSimxKa" & "c2ltLm5ldAYBu94DACm9A62t7AQL4Ef5ZYZosRpQTzFVAB8jGjf1TER2wH-0zBOe1" &
"v-g3VkcIIjKA" "-MDBNLeA4lzZWNwMjU2azGhAzfsxbxyCkgCqq8WwYsVWH7YkpMLnU2Bw5xJSimxKa" &
"v-g3VkcIIjKA"
var record: Record var record: Record
require record.fromURI(enrUri) require record.fromURI(enrUri)
let let
expectedAddr1 = MultiAddress.init("/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss").get() expectedAddr1 = MultiAddress
expectedAddr2 = MultiAddress.init("/dns6/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss").get() .init("/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss")
expectedAddr3 = MultiAddress.init("/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234/wss").get() .get()
expectedAddr2 = MultiAddress
.init("/dns6/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss")
.get()
expectedAddr3 = MultiAddress
.init(
"/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234/wss"
)
.get()
## When ## When
let typedRecord = record.toTyped() let typedRecord = record.toTyped()
@ -225,7 +224,11 @@ suite "Waku ENR - Multiaddresses":
enrPrivKey = generatesecp256k1key() enrPrivKey = generatesecp256k1key()
let let
addr1 = MultiAddress.init("/ip4/127.0.0.1/tcp/80/ws/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr31iDQpSN5Qa882BCjjwgrD").get() addr1 = MultiAddress
.init(
"/ip4/127.0.0.1/tcp/80/ws/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr31iDQpSN5Qa882BCjjwgrD"
)
.get()
addr2 = MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss").get() addr2 = MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss").get()
let expectedAddr1 = MultiAddress.init("/ip4/127.0.0.1/tcp/80/ws").get() let expectedAddr1 = MultiAddress.init("/ip4/127.0.0.1/tcp/80/ws").get()
@ -252,9 +255,7 @@ suite "Waku ENR - Multiaddresses":
multiaddrs.contains(expectedAddr1) multiaddrs.contains(expectedAddr1)
multiaddrs.contains(addr2) multiaddrs.contains(addr2)
suite "Waku ENR - Relay static sharding": suite "Waku ENR - Relay static sharding":
test "new relay shards object with single invalid shard id": test "new relay shards object with single invalid shard id":
## Given ## Given
let let
@ -374,7 +375,8 @@ suite "Waku ENR - Relay static sharding":
enrSeqNum = 1u64 enrSeqNum = 1u64
enrPrivKey = generatesecp256k1key() enrPrivKey = generatesecp256k1key()
let shardsTopics = RelayShards.init(33, toSeq(0u16 ..< 64u16)).expect("Valid Shards") let shardsTopics =
RelayShards.init(33, toSeq(0u16 ..< 64u16)).expect("Valid Shards")
var builder = EnrBuilder.init(enrPrivKey, seqNum = enrSeqNum) var builder = EnrBuilder.init(enrPrivKey, seqNum = enrSeqNum)
require builder.withWakuRelaySharding(shardsTopics).isOk() require builder.withWakuRelaySharding(shardsTopics).isOk()
@ -402,9 +404,12 @@ suite "Waku ENR - Relay static sharding":
enrPrivKey = generatesecp256k1key() enrPrivKey = generatesecp256k1key()
let let
relayShardsIndicesList = RelayShards.init(22, @[1u16, 1u16, 2u16, 3u16, 5u16, 8u16]).expect("Valid Shards") relayShardsIndicesList = RelayShards
relayShardsBitVector = RelayShards.init(33, @[13u16, 24u16, 37u16, 61u16, 98u16, 159u16]).expect("Valid Shards") .init(22, @[1u16, 1u16, 2u16, 3u16, 5u16, 8u16])
.expect("Valid Shards")
relayShardsBitVector = RelayShards
.init(33, @[13u16, 24u16, 37u16, 61u16, 98u16, 159u16])
.expect("Valid Shards")
var builder = EnrBuilder.init(enrPrivKey, seqNum = enrSeqNum) var builder = EnrBuilder.init(enrPrivKey, seqNum = enrSeqNum)
require builder.withWakuRelayShardingIndicesList(relayShardsIndicesList).isOk() require builder.withWakuRelayShardingIndicesList(relayShardsIndicesList).isOk()

View File

@ -1,11 +1,7 @@
{.used.} {.used.}
import import
std/[options, tables], std/[options, tables], testutils/unittests, chronicles, chronos, libp2p/crypto/crypto
testutils/unittests,
chronicles,
chronos,
libp2p/crypto/crypto
import import
../../waku/node/peer_manager, ../../waku/node/peer_manager,
../../waku/waku_core, ../../waku/waku_core,
@ -14,8 +10,9 @@ import
./testlib/common, ./testlib/common,
./testlib/wakucore ./testlib/wakucore
proc newTestWakuFilterNode(
proc newTestWakuFilterNode(switch: Switch, timeout: Duration = 2.hours): Future[WakuFilterLegacy] {.async.} = switch: Switch, timeout: Duration = 2.hours
): Future[WakuFilterLegacy] {.async.} =
let let
peerManager = PeerManager.new(switch) peerManager = PeerManager.new(switch)
proto = WakuFilterLegacy.new(peerManager, rng, timeout) proto = WakuFilterLegacy.new(peerManager, rng, timeout)
@ -35,7 +32,6 @@ proc newTestWakuFilterClient(switch: Switch): Future[WakuFilterClientLegacy] {.a
return proto return proto
# TODO: Extend test coverage # TODO: Extend test coverage
suite "Waku Filter": suite "Waku Filter":
asyncTest "should forward messages to client after subscribed": asyncTest "should forward messages to client after subscribed":
@ -54,16 +50,20 @@ suite "Waku Filter":
let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo() let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo()
let pushHandlerFuture = newFuture[(string, WakuMessage)]() let pushHandlerFuture = newFuture[(string, WakuMessage)]()
proc pushHandler(pubsubTopic: PubsubTopic, message: WakuMessage) {.async, gcsafe, closure.} = proc pushHandler(
pubsubTopic: PubsubTopic, message: WakuMessage
) {.async, gcsafe, closure.} =
pushHandlerFuture.complete((pubsubTopic, message)) pushHandlerFuture.complete((pubsubTopic, message))
let let
pubsubTopic = DefaultPubsubTopic pubsubTopic = DefaultPubsubTopic
contentTopic = "test-content-topic" contentTopic = "test-content-topic"
msg = fakeWakuMessage(contentTopic=contentTopic) msg = fakeWakuMessage(contentTopic = contentTopic)
## When ## When
require (await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer=serverAddr)).isOk() require (
await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer = serverAddr)
).isOk()
# WARN: Sleep necessary to avoid a race condition between the subscription and the handle message proc # WARN: Sleep necessary to avoid a race condition between the subscription and the handle message proc
await sleepAsync(500.milliseconds) await sleepAsync(500.milliseconds)
@ -97,16 +97,20 @@ suite "Waku Filter":
let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo() let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo()
var pushHandlerFuture = newFuture[void]() var pushHandlerFuture = newFuture[void]()
proc pushHandler(pubsubTopic: PubsubTopic, message: WakuMessage) {.async, gcsafe, closure.} = proc pushHandler(
pubsubTopic: PubsubTopic, message: WakuMessage
) {.async, gcsafe, closure.} =
pushHandlerFuture.complete() pushHandlerFuture.complete()
let let
pubsubTopic = DefaultPubsubTopic pubsubTopic = DefaultPubsubTopic
contentTopic = "test-content-topic" contentTopic = "test-content-topic"
msg = fakeWakuMessage(contentTopic=contentTopic) msg = fakeWakuMessage(contentTopic = contentTopic)
## When ## When
require (await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer=serverAddr)).isOk() require (
await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer = serverAddr)
).isOk()
# WARN: Sleep necessary to avoid a race condition between the subscription and the handle message proc # WARN: Sleep necessary to avoid a race condition between the subscription and the handle message proc
await sleepAsync(500.milliseconds) await sleepAsync(500.milliseconds)
@ -118,7 +122,7 @@ suite "Waku Filter":
# Reset to test unsubscribe # Reset to test unsubscribe
pushHandlerFuture = newFuture[void]() pushHandlerFuture = newFuture[void]()
require (await client.unsubscribe(pubsubTopic, contentTopic, peer=serverAddr)).isOk() require (await client.unsubscribe(pubsubTopic, contentTopic, peer = serverAddr)).isOk()
# WARN: Sleep necessary to avoid a race condition between the unsubscription and the handle message proc # WARN: Sleep necessary to avoid a race condition between the unsubscription and the handle message proc
await sleepAsync(500.milliseconds) await sleepAsync(500.milliseconds)
@ -126,7 +130,8 @@ suite "Waku Filter":
await server.handleMessage(pubsubTopic, msg) await server.handleMessage(pubsubTopic, msg)
## Then ## Then
let handlerWasCalledAfterUnsubscription = await pushHandlerFuture.withTimeout(1.seconds) let handlerWasCalledAfterUnsubscription =
await pushHandlerFuture.withTimeout(1.seconds)
check: check:
not handlerWasCalledAfterUnsubscription not handlerWasCalledAfterUnsubscription
@ -142,23 +147,27 @@ suite "Waku Filter":
await allFutures(serverSwitch.start(), clientSwitch.start()) await allFutures(serverSwitch.start(), clientSwitch.start())
let let
server = await newTestWakuFilterNode(serverSwitch, timeout=200.milliseconds) server = await newTestWakuFilterNode(serverSwitch, timeout = 200.milliseconds)
client = await newTestWakuFilterClient(clientSwitch) client = await newTestWakuFilterClient(clientSwitch)
## Given ## Given
let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo() let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo()
var pushHandlerFuture = newFuture[void]() var pushHandlerFuture = newFuture[void]()
proc pushHandler(pubsubTopic: PubsubTopic, message: WakuMessage) {.async, gcsafe, closure.} = proc pushHandler(
pubsubTopic: PubsubTopic, message: WakuMessage
) {.async, gcsafe, closure.} =
pushHandlerFuture.complete() pushHandlerFuture.complete()
let let
pubsubTopic = DefaultPubsubTopic pubsubTopic = DefaultPubsubTopic
contentTopic = "test-content-topic" contentTopic = "test-content-topic"
msg = fakeWakuMessage(contentTopic=contentTopic) msg = fakeWakuMessage(contentTopic = contentTopic)
## When ## When
require (await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer=serverAddr)).isOk() require (
await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer = serverAddr)
).isOk()
# WARN: Sleep necessary to avoid a race condition between the unsubscription and the handle message proc # WARN: Sleep necessary to avoid a race condition between the unsubscription and the handle message proc
await sleepAsync(500.milliseconds) await sleepAsync(500.milliseconds)
@ -207,23 +216,27 @@ suite "Waku Filter":
await allFutures(serverSwitch.start(), clientSwitch.start()) await allFutures(serverSwitch.start(), clientSwitch.start())
let let
server = await newTestWakuFilterNode(serverSwitch, timeout=200.milliseconds) server = await newTestWakuFilterNode(serverSwitch, timeout = 200.milliseconds)
client = await newTestWakuFilterClient(clientSwitch) client = await newTestWakuFilterClient(clientSwitch)
## Given ## Given
let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo() let serverAddr = serverSwitch.peerInfo.toRemotePeerInfo()
var pushHandlerFuture = newFuture[void]() var pushHandlerFuture = newFuture[void]()
proc pushHandler(pubsubTopic: PubsubTopic, message: WakuMessage) {.async, gcsafe, closure.} = proc pushHandler(
pubsubTopic: PubsubTopic, message: WakuMessage
) {.async, gcsafe, closure.} =
pushHandlerFuture.complete() pushHandlerFuture.complete()
let let
pubsubTopic = DefaultPubsubTopic pubsubTopic = DefaultPubsubTopic
contentTopic = "test-content-topic" contentTopic = "test-content-topic"
msg = fakeWakuMessage(contentTopic=contentTopic) msg = fakeWakuMessage(contentTopic = contentTopic)
## When ## When
require (await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer=serverAddr)).isOk() require (
await client.subscribe(pubsubTopic, contentTopic, pushHandler, peer = serverAddr)
).isOk()
# WARN: Sleep necessary to avoid a race condition between the unsubscription and the handle message proc # WARN: Sleep necessary to avoid a race condition between the unsubscription and the handle message proc
await sleepAsync(500.milliseconds) await sleepAsync(500.milliseconds)
@ -255,8 +268,7 @@ suite "Waku Filter":
# Start switch with same key as before # Start switch with same key as before
let clientSwitch2 = newTestSwitch( let clientSwitch2 = newTestSwitch(
some(clientSwitch.peerInfo.privateKey), some(clientSwitch.peerInfo.privateKey), some(clientSwitch.peerInfo.addrs[0])
some(clientSwitch.peerInfo.addrs[0])
) )
await clientSwitch2.start() await clientSwitch2.start()
await client.start() await client.start()

View File

@ -12,14 +12,9 @@ import
libp2p/stream/connection, libp2p/stream/connection,
libp2p/crypto/crypto libp2p/crypto/crypto
import import
../../waku/waku_core, ../../waku/waku_core, ../../waku/waku_node, ./testlib/wakucore, ./testlib/wakunode
../../waku/waku_node,
./testlib/wakucore,
./testlib/wakunode
suite "Waku Keepalive": suite "Waku Keepalive":
asyncTest "handle ping keepalives": asyncTest "handle ping keepalives":
let let
nodeKey1 = generateSecp256k1Key() nodeKey1 = generateSecp256k1Key()

View File

@ -1,38 +1,30 @@
{.used.} {.used.}
import import std/[os, json], chronos, testutils/unittests
std/[os, json], import ../../waku/waku_keystore, ./testlib/common
chronos,
testutils/unittests
import
../../waku/waku_keystore,
./testlib/common
from ../../waku/waku_noise/noise_utils import randomSeqByte from ../../waku/waku_noise/noise_utils import randomSeqByte
procSuite "Credentials test suite": procSuite "Credentials test suite":
let testAppInfo = AppInfo(application: "test", appIdentifier: "1234", version: "0.1") let testAppInfo = AppInfo(application: "test", appIdentifier: "1234", version: "0.1")
test "Create keystore": test "Create keystore":
let filepath = "./testAppKeystore.txt" let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath) defer:
removeFile(filepath)
let keystoreRes = createAppKeystore(path = filepath, let keystoreRes = createAppKeystore(path = filepath, appInfo = testAppInfo)
appInfo = testAppInfo)
check: check:
keystoreRes.isOk() keystoreRes.isOk()
test "Load keystore": test "Load keystore":
let filepath = "./testAppKeystore.txt" let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath) defer:
removeFile(filepath)
# If no keystore exists at filepath, a new one is created for appInfo and empty credentials # If no keystore exists at filepath, a new one is created for appInfo and empty credentials
let keystoreRes = loadAppKeystore(path = filepath, let keystoreRes = loadAppKeystore(path = filepath, appInfo = testAppInfo)
appInfo = testAppInfo)
check: check:
keystoreRes.isOk() keystoreRes.isOk()
@ -48,106 +40,134 @@ procSuite "Credentials test suite":
keystore["credentials"].len() == 0 keystore["credentials"].len() == 0
test "Add credentials to keystore": test "Add credentials to keystore":
let filepath = "./testAppKeystore.txt" let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath) defer:
removeFile(filepath)
# We generate a random identity credential (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen) # We generate a random identity credential (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen)
var var
idTrapdoor = randomSeqByte(rng[], 32) idTrapdoor = randomSeqByte(rng[], 32)
idNullifier = randomSeqByte(rng[], 32) idNullifier = randomSeqByte(rng[], 32)
idSecretHash = randomSeqByte(rng[], 32) idSecretHash = randomSeqByte(rng[], 32)
idCommitment = randomSeqByte(rng[], 32) idCommitment = randomSeqByte(rng[], 32)
var idCredential = IdentityCredential(idTrapdoor: idTrapdoor, idNullifier: idNullifier, idSecretHash: idSecretHash, idCommitment: idCommitment) var idCredential = IdentityCredential(
idTrapdoor: idTrapdoor,
idNullifier: idNullifier,
idSecretHash: idSecretHash,
idCommitment: idCommitment,
)
var contract = MembershipContract(chainId: "5", address: "0x0123456789012345678901234567890123456789") var contract = MembershipContract(
chainId: "5", address: "0x0123456789012345678901234567890123456789"
)
var index = MembershipIndex(1) var index = MembershipIndex(1)
let membershipCredential = KeystoreMembership(membershipContract: contract, let membershipCredential = KeystoreMembership(
treeIndex: index, membershipContract: contract, treeIndex: index, identityCredential: idCredential
identityCredential: idCredential) )
let password = "%m0um0ucoW%" let password = "%m0um0ucoW%"
let keystoreRes = addMembershipCredentials(path = filepath, let keystoreRes = addMembershipCredentials(
membership = membershipCredential, path = filepath,
password = password, membership = membershipCredential,
appInfo = testAppInfo) password = password,
appInfo = testAppInfo,
)
check: check:
keystoreRes.isOk() keystoreRes.isOk()
test "Add/retrieve credentials in keystore": test "Add/retrieve credentials in keystore":
let filepath = "./testAppKeystore.txt" let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath) defer:
removeFile(filepath)
# We generate two random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen) # We generate two random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen)
var var
idTrapdoor = randomSeqByte(rng[], 32) idTrapdoor = randomSeqByte(rng[], 32)
idNullifier = randomSeqByte(rng[], 32) idNullifier = randomSeqByte(rng[], 32)
idSecretHash = randomSeqByte(rng[], 32) idSecretHash = randomSeqByte(rng[], 32)
idCommitment = randomSeqByte(rng[], 32) idCommitment = randomSeqByte(rng[], 32)
idCredential = IdentityCredential(idTrapdoor: idTrapdoor, idNullifier: idNullifier, idSecretHash: idSecretHash, idCommitment: idCommitment) idCredential = IdentityCredential(
idTrapdoor: idTrapdoor,
idNullifier: idNullifier,
idSecretHash: idSecretHash,
idCommitment: idCommitment,
)
# We generate two distinct membership groups # We generate two distinct membership groups
var contract = MembershipContract(chainId: "5", address: "0x0123456789012345678901234567890123456789") var contract = MembershipContract(
chainId: "5", address: "0x0123456789012345678901234567890123456789"
)
var index = MembershipIndex(1) var index = MembershipIndex(1)
var membershipCredential = KeystoreMembership(membershipContract: contract, var membershipCredential = KeystoreMembership(
treeIndex: index, membershipContract: contract, treeIndex: index, identityCredential: idCredential
identityCredential: idCredential) )
let password = "%m0um0ucoW%" let password = "%m0um0ucoW%"
# We add credentials to the keystore. Note that only 3 credentials should be effectively added, since rlnMembershipCredentials3 is equal to membershipCredentials2 # We add credentials to the keystore. Note that only 3 credentials should be effectively added, since rlnMembershipCredentials3 is equal to membershipCredentials2
let keystoreRes = addMembershipCredentials(path = filepath, let keystoreRes = addMembershipCredentials(
membership = membershipCredential, path = filepath,
password = password, membership = membershipCredential,
appInfo = testAppInfo) password = password,
appInfo = testAppInfo,
)
check: check:
keystoreRes.isOk() keystoreRes.isOk()
# We test retrieval of credentials. # We test retrieval of credentials.
var expectedMembership = membershipCredential var expectedMembership = membershipCredential
let membershipQuery = KeystoreMembership(membershipContract: contract, let membershipQuery =
treeIndex: index) KeystoreMembership(membershipContract: contract, treeIndex: index)
var recoveredCredentialsRes = getMembershipCredentials(path = filepath, var recoveredCredentialsRes = getMembershipCredentials(
password = password, path = filepath,
query = membershipQuery, password = password,
appInfo = testAppInfo) query = membershipQuery,
appInfo = testAppInfo,
)
check: check:
recoveredCredentialsRes.isOk() recoveredCredentialsRes.isOk()
recoveredCredentialsRes.get() == expectedMembership recoveredCredentialsRes.get() == expectedMembership
test "if the keystore contains only one credential, fetch that irrespective of treeIndex": test "if the keystore contains only one credential, fetch that irrespective of treeIndex":
let filepath = "./testAppKeystore.txt" let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath) defer:
removeFile(filepath)
# We generate random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen) # We generate random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen)
let let
idTrapdoor = randomSeqByte(rng[], 32) idTrapdoor = randomSeqByte(rng[], 32)
idNullifier = randomSeqByte(rng[], 32) idNullifier = randomSeqByte(rng[], 32)
idSecretHash = randomSeqByte(rng[], 32) idSecretHash = randomSeqByte(rng[], 32)
idCommitment = randomSeqByte(rng[], 32) idCommitment = randomSeqByte(rng[], 32)
idCredential = IdentityCredential(idTrapdoor: idTrapdoor, idNullifier: idNullifier, idSecretHash: idSecretHash, idCommitment: idCommitment) idCredential = IdentityCredential(
idTrapdoor: idTrapdoor,
idNullifier: idNullifier,
idSecretHash: idSecretHash,
idCommitment: idCommitment,
)
let contract = MembershipContract(chainId: "5", address: "0x0123456789012345678901234567890123456789") let contract = MembershipContract(
chainId: "5", address: "0x0123456789012345678901234567890123456789"
)
let index = MembershipIndex(1) let index = MembershipIndex(1)
let membershipCredential = KeystoreMembership(membershipContract: contract, let membershipCredential = KeystoreMembership(
treeIndex: index, membershipContract: contract, treeIndex: index, identityCredential: idCredential
identityCredential: idCredential) )
let password = "%m0um0ucoW%" let password = "%m0um0ucoW%"
let keystoreRes = addMembershipCredentials(path = filepath, let keystoreRes = addMembershipCredentials(
membership = membershipCredential, path = filepath,
password = password, membership = membershipCredential,
appInfo = testAppInfo) password = password,
appInfo = testAppInfo,
)
assert(keystoreRes.isOk(), $keystoreRes.error) assert(keystoreRes.isOk(), $keystoreRes.error)
@ -155,56 +175,73 @@ procSuite "Credentials test suite":
let expectedMembership = membershipCredential let expectedMembership = membershipCredential
let membershipQuery = KeystoreMembership(membershipContract: contract) let membershipQuery = KeystoreMembership(membershipContract: contract)
let recoveredCredentialsRes = getMembershipCredentials(path = filepath, let recoveredCredentialsRes = getMembershipCredentials(
password = password, path = filepath,
query = membershipQuery, password = password,
appInfo = testAppInfo) query = membershipQuery,
appInfo = testAppInfo,
)
assert(recoveredCredentialsRes.isOk(), $recoveredCredentialsRes.error) assert(recoveredCredentialsRes.isOk(), $recoveredCredentialsRes.error)
check: recoveredCredentialsRes.get() == expectedMembership check:
recoveredCredentialsRes.get() == expectedMembership
test "if the keystore contains multiple credentials, then error out if treeIndex has not been passed in": test "if the keystore contains multiple credentials, then error out if treeIndex has not been passed in":
let filepath = "./testAppKeystore.txt" let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath) defer:
removeFile(filepath)
# We generate random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen) # We generate random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen)
let let
idTrapdoor = randomSeqByte(rng[], 32) idTrapdoor = randomSeqByte(rng[], 32)
idNullifier = randomSeqByte(rng[], 32) idNullifier = randomSeqByte(rng[], 32)
idSecretHash = randomSeqByte(rng[], 32) idSecretHash = randomSeqByte(rng[], 32)
idCommitment = randomSeqByte(rng[], 32) idCommitment = randomSeqByte(rng[], 32)
idCredential = IdentityCredential(idTrapdoor: idTrapdoor, idNullifier: idNullifier, idSecretHash: idSecretHash, idCommitment: idCommitment) idCredential = IdentityCredential(
idTrapdoor: idTrapdoor,
idNullifier: idNullifier,
idSecretHash: idSecretHash,
idCommitment: idCommitment,
)
# We generate two distinct membership groups # We generate two distinct membership groups
let contract = MembershipContract(chainId: "5", address: "0x0123456789012345678901234567890123456789") let contract = MembershipContract(
chainId: "5", address: "0x0123456789012345678901234567890123456789"
)
let index = MembershipIndex(1) let index = MembershipIndex(1)
var membershipCredential = KeystoreMembership(membershipContract: contract, var membershipCredential = KeystoreMembership(
treeIndex: index, membershipContract: contract, treeIndex: index, identityCredential: idCredential
identityCredential: idCredential) )
let password = "%m0um0ucoW%" let password = "%m0um0ucoW%"
let keystoreRes = addMembershipCredentials(path = filepath, let keystoreRes = addMembershipCredentials(
membership = membershipCredential, path = filepath,
password = password, membership = membershipCredential,
appInfo = testAppInfo) password = password,
appInfo = testAppInfo,
)
assert(keystoreRes.isOk(), $keystoreRes.error) assert(keystoreRes.isOk(), $keystoreRes.error)
membershipCredential.treeIndex = MembershipIndex(2) membershipCredential.treeIndex = MembershipIndex(2)
let keystoreRes2 = addMembershipCredentials(path = filepath, let keystoreRes2 = addMembershipCredentials(
membership = membershipCredential, path = filepath,
password = password, membership = membershipCredential,
appInfo = testAppInfo) password = password,
appInfo = testAppInfo,
)
assert(keystoreRes2.isOk(), $keystoreRes2.error) assert(keystoreRes2.isOk(), $keystoreRes2.error)
# We test retrieval of credentials. # We test retrieval of credentials.
let membershipQuery = KeystoreMembership(membershipContract: contract) let membershipQuery = KeystoreMembership(membershipContract: contract)
let recoveredCredentialsRes = getMembershipCredentials(path = filepath, let recoveredCredentialsRes = getMembershipCredentials(
password = password, path = filepath,
query = membershipQuery, password = password,
appInfo = testAppInfo) query = membershipQuery,
appInfo = testAppInfo,
)
check: check:
recoveredCredentialsRes.isErr() recoveredCredentialsRes.isErr()

View File

@ -1,26 +1,19 @@
{.used.} {.used.}
import import std/[json, os], stew/byteutils, testutils/unittests, chronos, eth/keys
std/[json, os], import ../../waku/waku_keystore, ./testlib/common
stew/byteutils,
testutils/unittests, chronos,
eth/keys
import
../../waku/waku_keystore,
./testlib/common
from ../../waku/waku_noise/noise_utils import randomSeqByte from ../../waku/waku_noise/noise_utils import randomSeqByte
suite "KeyFile test suite": suite "KeyFile test suite":
test "Create/Save/Load single keyfile": test "Create/Save/Load single keyfile":
# The password we use to encrypt our secret # The password we use to encrypt our secret
let password = "randompassword" let password = "randompassword"
# The filepath were the keyfile will be stored # The filepath were the keyfile will be stored
let filepath = "./test.keyfile" let filepath = "./test.keyfile"
defer: removeFile(filepath) defer:
removeFile(filepath)
# The secret # The secret
var secret = randomSeqByte(rng[], 300) var secret = randomSeqByte(rng[], 300)
@ -48,7 +41,6 @@ suite "KeyFile test suite":
secret == decodedSecret.get() secret == decodedSecret.get()
test "Create/Save/Load multiple keyfiles in same file": test "Create/Save/Load multiple keyfiles in same file":
# We set different passwords for different keyfiles that will be stored in same file # We set different passwords for different keyfiles that will be stored in same file
let password1 = string.fromBytes(randomSeqByte(rng[], 20)) let password1 = string.fromBytes(randomSeqByte(rng[], 20))
let password2 = "" let password2 = ""
@ -56,7 +48,8 @@ suite "KeyFile test suite":
var keyfile: KfResult[JsonNode] var keyfile: KfResult[JsonNode]
let filepath = "./test.keyfile" let filepath = "./test.keyfile"
defer: removeFile(filepath) defer:
removeFile(filepath)
# We generate 6 different secrets and we encrypt them using 3 different passwords, and we store the obtained keystore # We generate 6 different secrets and we encrypt them using 3 different passwords, and we store the obtained keystore
@ -133,218 +126,218 @@ suite "KeyFile test suite":
secret3 == decodedSecretsPassword3[0].get() secret3 == decodedSecretsPassword3[0].get()
secret4 == decodedSecretsPassword3[1].get() secret4 == decodedSecretsPassword3[1].get()
# The following tests are originally from the nim-eth keyfile tests module https://github.com/status-im/nim-eth/blob/master/tests/keyfile/test_keyfile.nim # The following tests are originally from the nim-eth keyfile tests module https://github.com/status-im/nim-eth/blob/master/tests/keyfile/test_keyfile.nim
# and are slightly adapted to test backwards compatibility with nim-eth implementation of our customized version of the utils/keyfile module # and are slightly adapted to test backwards compatibility with nim-eth implementation of our customized version of the utils/keyfile module
# Note: the original nim-eth "Create/Save/Load test" is redefined and expanded above in "KeyFile test suite" # Note: the original nim-eth "Create/Save/Load test" is redefined and expanded above in "KeyFile test suite"
suite "KeyFile test suite (adapted from nim-eth keyfile tests)": suite "KeyFile test suite (adapted from nim-eth keyfile tests)":
# Testvectors originally from https://github.com/status-im/nim-eth/blob/fef47331c37ee8abb8608037222658737ff498a6/tests/keyfile/test_keyfile.nim#L22-L168 # Testvectors originally from https://github.com/status-im/nim-eth/blob/fef47331c37ee8abb8608037222658737ff498a6/tests/keyfile/test_keyfile.nim#L22-L168
let TestVectors = [ let TestVectors = [
%*{ %*{
"keyfile": { "keyfile": {
"crypto" : { "crypto": {
"cipher" : "aes-128-ctr", "cipher": "aes-128-ctr",
"cipherparams" : {"iv" : "6087dab2f9fdbbfaddc31a909735c1e6"}, "cipherparams": {"iv": "6087dab2f9fdbbfaddc31a909735c1e6"},
"ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", "ciphertext":
"kdf" : "pbkdf2", "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
"kdfparams" : { "kdf": "pbkdf2",
"c" : 262144, "kdfparams": {
"dklen" : 32, "c": 262144,
"prf" : "hmac-sha256", "dklen": 32,
"salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd" "prf": "hmac-sha256",
"salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd",
}, },
"mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2" "mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2",
}, },
"id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", "id": "3198bc9c-6672-5ab3-d995-4942343ae5b6",
"version" : 3 "version": 3,
}, },
"name": "test1", "name": "test1",
"password": "testpassword", "password": "testpassword",
"priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d" "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d",
}, },
%*{ %*{
"keyfile": { "keyfile": {
"version": 3, "version": 3,
"crypto": { "crypto": {
"ciphertext": "ee75456c006b1e468133c5d2a916bacd3cf515ced4d9b021b5c59978007d1e87", "ciphertext":
"ee75456c006b1e468133c5d2a916bacd3cf515ced4d9b021b5c59978007d1e87",
"version": 1, "version": 1,
"kdf": "pbkdf2", "kdf": "pbkdf2",
"kdfparams": { "kdfparams": {
"dklen": 32, "dklen": 32,
"c": 262144, "c": 262144,
"prf": "hmac-sha256", "prf": "hmac-sha256",
"salt": "504490577620f64f43d73f29479c2cf0" "salt": "504490577620f64f43d73f29479c2cf0",
}, },
"mac": "196815708465de9af7504144a1360d08874fc3c30bb0e648ce88fbc36830d35d", "mac": "196815708465de9af7504144a1360d08874fc3c30bb0e648ce88fbc36830d35d",
"cipherparams": {"iv": "514ccc8c4fb3e60e5538e0cf1e27c233"}, "cipherparams": {"iv": "514ccc8c4fb3e60e5538e0cf1e27c233"},
"cipher": "aes-128-ctr" "cipher": "aes-128-ctr",
}, },
"id": "98d193c7-5174-4c7c-5345-c1daf95477b5" "id": "98d193c7-5174-4c7c-5345-c1daf95477b5",
}, },
"name": "python_generated_test_with_odd_iv", "name": "python_generated_test_with_odd_iv",
"password": "foo", "password": "foo",
"priv": "0101010101010101010101010101010101010101010101010101010101010101" "priv": "0101010101010101010101010101010101010101010101010101010101010101",
}, },
%*{ %*{
"keyfile": { "keyfile": {
"version": 3, "version": 3,
"crypto": { "crypto": {
"ciphertext": "d69313b6470ac1942f75d72ebf8818a0d484ac78478a132ee081cd954d6bd7a9", "ciphertext":
"d69313b6470ac1942f75d72ebf8818a0d484ac78478a132ee081cd954d6bd7a9",
"cipherparams": {"iv": "ffffffffffffffffffffffffffffffff"}, "cipherparams": {"iv": "ffffffffffffffffffffffffffffffff"},
"kdf": "pbkdf2", "kdf": "pbkdf2",
"kdfparams": { "kdfparams": {
"dklen": 32, "dklen": 32,
"c": 262144, "c": 262144,
"prf": "hmac-sha256", "prf": "hmac-sha256",
"salt": "c82ef14476014cbf438081a42709e2ed" "salt": "c82ef14476014cbf438081a42709e2ed",
}, },
"mac": "cf6bfbcc77142a22c4a908784b4a16f1023a1d0e2aff404c20158fa4f1587177", "mac": "cf6bfbcc77142a22c4a908784b4a16f1023a1d0e2aff404c20158fa4f1587177",
"cipher": "aes-128-ctr", "cipher": "aes-128-ctr",
"version": 1 "version": 1,
}, },
"id": "abb67040-8dbe-0dad-fc39-2b082ef0ee5f" "id": "abb67040-8dbe-0dad-fc39-2b082ef0ee5f",
}, },
"name": "evilnonce", "name": "evilnonce",
"password": "bar", "password": "bar",
"priv": "0202020202020202020202020202020202020202020202020202020202020202" "priv": "0202020202020202020202020202020202020202020202020202020202020202",
}, },
%*{ %*{
"keyfile": { "keyfile": {
"version" : 3, "version": 3,
"crypto" : { "crypto": {
"cipher" : "aes-128-ctr", "cipher": "aes-128-ctr",
"cipherparams" : { "cipherparams": {"iv": "83dbcc02d8ccb40e466191a123791e0e"},
"iv" : "83dbcc02d8ccb40e466191a123791e0e" "ciphertext":
}, "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",
"ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", "kdf": "scrypt",
"kdf" : "scrypt", "kdfparams": {
"kdfparams" : { "dklen": 32,
"dklen" : 32, "n": 262144,
"n" : 262144, "r": 1,
"r" : 1, "p": 8,
"p" : 8, "salt": "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19",
"salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19" },
}, "mac": "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097",
"mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"
}, },
"id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6" "id": "3198bc9c-6672-5ab3-d995-4942343ae5b6",
}, },
"name" : "test2", "name": "test2",
"password": "testpassword", "password": "testpassword",
"priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d" "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d",
}, },
%*{ %*{
"keyfile": { "keyfile": {
"version": 3, "version": 3,
"address": "460121576cc7df020759730751f92bd62fd78dd6", "address": "460121576cc7df020759730751f92bd62fd78dd6",
"crypto": { "crypto": {
"ciphertext": "54ae683c6287fa3d58321f09d56e26d94e58a00d4f90bdd95782ae0e4aab618b", "ciphertext":
"cipherparams": { "54ae683c6287fa3d58321f09d56e26d94e58a00d4f90bdd95782ae0e4aab618b",
"iv": "681679cdb125bba9495d068b002816a4" "cipherparams": {"iv": "681679cdb125bba9495d068b002816a4"},
}, "cipher": "aes-128-ctr",
"cipher": "aes-128-ctr", "kdf": "scrypt",
"kdf": "scrypt", "kdfparams": {
"kdfparams": { "dklen": 32,
"dklen": 32, "salt": "c3407f363fce02a66e3c4bf4a8f6b7da1c1f54266cef66381f0625c251c32785",
"salt": "c3407f363fce02a66e3c4bf4a8f6b7da1c1f54266cef66381f0625c251c32785", "n": 8192,
"n": 8192, "r": 8,
"r": 8, "p": 1,
"p": 1 },
}, "mac": "dea6bdf22a2f522166ed82808c22a6311e84c355f4bbe100d4260483ff675a46",
"mac": "dea6bdf22a2f522166ed82808c22a6311e84c355f4bbe100d4260483ff675a46"
}, },
"id": "0eb785e0-340a-4290-9c42-90a11973ee47" "id": "0eb785e0-340a-4290-9c42-90a11973ee47",
}, },
"name": "mycrypto", "name": "mycrypto",
"password": "foobartest121", "password": "foobartest121",
"priv": "05a4d3eb46c742cb8850440145ce70cbc80b59f891cf5f50fd3e9c280b50c4e4" "priv": "05a4d3eb46c742cb8850440145ce70cbc80b59f891cf5f50fd3e9c280b50c4e4",
}, },
%*{ %*{
"keyfile": { "keyfile": {
"crypto": { "crypto": {
"cipher": "aes-128-ctr", "cipher": "aes-128-ctr",
"cipherparams": { "cipherparams": {"iv": "7e7b02d2b4ef45d6c98cb885e75f48d5"},
"iv": "7e7b02d2b4ef45d6c98cb885e75f48d5", "ciphertext":
}, "a7a5743a6c7eb3fa52396bd3fd94043b79075aac3ccbae8e62d3af94db00397c",
"ciphertext": "a7a5743a6c7eb3fa52396bd3fd94043b79075aac3ccbae8e62d3af94db00397c", "kdf": "scrypt",
"kdf": "scrypt", "kdfparams": {
"kdfparams": { "dklen": 32,
"dklen": 32, "n": 8192,
"n": 8192, "p": 1,
"p": 1, "r": 8,
"r": 8, "salt": "247797c7a357b707a3bdbfaa55f4c553756bca09fec20ddc938e7636d21e4a20",
"salt": "247797c7a357b707a3bdbfaa55f4c553756bca09fec20ddc938e7636d21e4a20",
},
"mac": "5a3ba5bebfda2c384586eda5fcda9c8397d37c9b0cc347fea86525cf2ea3a468",
}, },
"address": "0b6f2de3dee015a95d3330dcb7baf8e08aa0112d", "mac": "5a3ba5bebfda2c384586eda5fcda9c8397d37c9b0cc347fea86525cf2ea3a468",
"id": "3c8efdd6-d538-47ec-b241-36783d3418b9", },
"version": 3 "address": "0b6f2de3dee015a95d3330dcb7baf8e08aa0112d",
"id": "3c8efdd6-d538-47ec-b241-36783d3418b9",
"version": 3,
}, },
"password": "moomoocow", "password": "moomoocow",
"priv": "21eac69b9a52f466bfe9047f0f21c9caf3a5cdaadf84e2750a9b3265d450d481", "priv": "21eac69b9a52f466bfe9047f0f21c9caf3a5cdaadf84e2750a9b3265d450d481",
"name": "eth-keyfile-conftest" "name": "eth-keyfile-conftest",
} },
] ]
test "Testing nim-eth test vectors": test "Testing nim-eth test vectors":
var secret: KfResult[seq[byte]] var secret: KfResult[seq[byte]]
var expectedSecret: seq[byte] var expectedSecret: seq[byte]
for i in 0..<TestVectors.len: for i in 0 ..< TestVectors.len:
# Decryption with correct password # Decryption with correct password
expectedSecret = decodeHex(TestVectors[i].getOrDefault("priv").getStr()) expectedSecret = decodeHex(TestVectors[i].getOrDefault("priv").getStr())
secret = secret = decodeKeyFileJson(
decodeKeyFileJson(TestVectors[i].getOrDefault("keyfile"), TestVectors[i].getOrDefault("keyfile"),
TestVectors[i].getOrDefault("password").getStr()) TestVectors[i].getOrDefault("password").getStr(),
)
check: check:
secret.isOk() secret.isOk()
secret.get() == expectedSecret secret.get() == expectedSecret
# Decryption with wrong password # Decryption with wrong password
secret = decodeKeyFileJson(TestVectors[i].getOrDefault("keyfile"), "wrongpassword") secret =
decodeKeyFileJson(TestVectors[i].getOrDefault("keyfile"), "wrongpassword")
check: check:
secret.isErr() secret.isErr()
secret.error == KeyFileError.KeyfileIncorrectMac secret.error == KeyFileError.KeyfileIncorrectMac
test "Wrong mac in keyfile": test "Wrong mac in keyfile":
# This keyfile is the same as the first one in TestVectors, # This keyfile is the same as the first one in TestVectors,
# but the last byte of mac is changed to 00. # but the last byte of mac is changed to 00.
# While ciphertext is the correct encryption of priv under password, # While ciphertext is the correct encryption of priv under password,
# mac verfication should fail and nothing will be decrypted # mac verfication should fail and nothing will be decrypted
let keyfileWrongMac = %*{ let keyfileWrongMac =
%*{
"keyfile": { "keyfile": {
"crypto" : { "crypto": {
"cipher" : "aes-128-ctr", "cipher": "aes-128-ctr",
"cipherparams" : {"iv" : "6087dab2f9fdbbfaddc31a909735c1e6"}, "cipherparams": {"iv": "6087dab2f9fdbbfaddc31a909735c1e6"},
"ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", "ciphertext":
"kdf" : "pbkdf2", "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
"kdfparams" : { "kdf": "pbkdf2",
"c" : 262144, "kdfparams": {
"dklen" : 32, "c": 262144,
"prf" : "hmac-sha256", "dklen": 32,
"salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd" "prf": "hmac-sha256",
"salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd",
}, },
"mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e900" "mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e900",
}, },
"id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", "id": "3198bc9c-6672-5ab3-d995-4942343ae5b6",
"version" : 3 "version": 3,
}, },
"name": "test1", "name": "test1",
"password": "testpassword", "password": "testpassword",
"priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d" "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d",
} }
# Decryption with correct password # Decryption with correct password
let expectedSecret = decodeHex(keyfileWrongMac.getOrDefault("priv").getStr()) let expectedSecret = decodeHex(keyfileWrongMac.getOrDefault("priv").getStr())
let secret = let secret = decodeKeyFileJson(
decodeKeyFileJson(keyfileWrongMac.getOrDefault("keyfile"), keyfileWrongMac.getOrDefault("keyfile"),
keyfileWrongMac.getOrDefault("password").getStr()) keyfileWrongMac.getOrDefault("password").getStr(),
)
check: check:
secret.isErr() secret.isErr()
secret.error == KeyFileError.KeyFileIncorrectMac secret.error == KeyFileError.KeyFileIncorrectMac
@ -364,11 +357,10 @@ suite "KeyFile test suite (adapted from nim-eth keyfile tests)":
let secret = decodeKeyFileJson(jsonKeyfile.get(), password) let secret = decodeKeyFileJson(jsonKeyfile.get(), password)
check: check:
secret.isOk() secret.isOk()
secret.get() == expectedSecret secret.get() == expectedSecret
test "Load non-existent keyfile test": test "Load non-existent keyfile test":
check: check:
loadKeyFiles("nonexistant.keyfile", "password").error == loadKeyFiles("nonexistant.keyfile", "password").error ==
KeyFileError.KeyfileDoesNotExist KeyFileError.KeyfileDoesNotExist

View File

@ -22,13 +22,22 @@ import
./testlib/wakucore, ./testlib/wakucore,
./testlib/wakunode ./testlib/wakunode
procSuite "Waku Metadata Protocol": procSuite "Waku Metadata Protocol":
asyncTest "request() returns the supported metadata of the peer": asyncTest "request() returns the supported metadata of the peer":
let clusterId = 10.uint32 let clusterId = 10.uint32
let let
node1 = newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0), clusterId = clusterId) node1 = newTestWakuNode(
node2 = newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0), clusterId = clusterId) generateSecp256k1Key(),
parseIpAddress("0.0.0.0"),
Port(0),
clusterId = clusterId,
)
node2 = newTestWakuNode(
generateSecp256k1Key(),
parseIpAddress("0.0.0.0"),
Port(0),
clusterId = clusterId,
)
# Start nodes # Start nodes
await allFutures([node1.start(), node2.start()]) await allFutures([node1.start(), node2.start()])
@ -37,7 +46,9 @@ procSuite "Waku Metadata Protocol":
node1.topicSubscriptionQueue.emit((kind: PubsubSub, topic: "/waku/2/rs/10/6")) node1.topicSubscriptionQueue.emit((kind: PubsubSub, topic: "/waku/2/rs/10/6"))
# Create connection # Create connection
let connOpt = await node2.peerManager.dialPeer(node1.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec) let connOpt = await node2.peerManager.dialPeer(
node1.switch.peerInfo.toRemotePeerInfo(), WakuMetadataCodec
)
require: require:
connOpt.isSome connOpt.isSome
@ -51,4 +62,3 @@ procSuite "Waku Metadata Protocol":
check: check:
response1.get().clusterId.get() == clusterId response1.get().clusterId.get() == clusterId
response1.get().shards == @[uint32(6), uint32(7)] response1.get().shards == @[uint32(6), uint32(7)]

View File

@ -1,30 +1,18 @@
{.used.} {.used.}
import import chronos, confutils/toml/std/net, libp2p/multiaddress, testutils/unittests
chronos,
confutils/toml/std/net,
libp2p/multiaddress,
testutils/unittests
import import ./testlib/wakunode, ../../waku/waku_enr/capabilities
./testlib/wakunode,
../../waku/waku_enr/capabilities
include include ../../waku/node/config
../../waku/node/config
proc defaultTestWakuFlags(): CapabilitiesBitfield = proc defaultTestWakuFlags(): CapabilitiesBitfield =
CapabilitiesBitfield.init( CapabilitiesBitfield.init(
lightpush = false, lightpush = false, filter = false, store = false, relay = true
filter = false,
store = false,
relay = true
) )
suite "Waku NetConfig": suite "Waku NetConfig":
asyncTest "Create NetConfig with default values": asyncTest "Create NetConfig with default values":
let conf = defaultTestWakuNodeConf() let conf = defaultTestWakuNodeConf()
let wakuFlags = defaultTestWakuFlags() let wakuFlags = defaultTestWakuFlags()
@ -40,32 +28,28 @@ suite "Waku NetConfig":
wssEnabled = conf.websocketSecureSupport, wssEnabled = conf.websocketSecureSupport,
dns4DomainName = none(string), dns4DomainName = none(string),
discv5UdpPort = none(Port), discv5UdpPort = none(Port),
wakuFlags = some(wakuFlags) wakuFlags = some(wakuFlags),
) )
check: check:
netConfigRes.isOk() netConfigRes.isOk()
asyncTest "AnnouncedAddresses contains only bind address when no external addresses are provided": asyncTest "AnnouncedAddresses contains only bind address when no external addresses are provided":
let conf = defaultTestWakuNodeConf() let conf = defaultTestWakuNodeConf()
let netConfigRes = NetConfig.init( let netConfigRes =
bindIp = conf.listenAddress, NetConfig.init(bindIp = conf.listenAddress, bindPort = conf.tcpPort)
bindPort = conf.tcpPort
)
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 1 # Only bind address should be present netConfig.announcedAddresses.len == 1 # Only bind address should be present
netConfig.announcedAddresses[0] == formatListenAddress(ip4TcpEndPoint(conf.listenAddress, conf.tcpPort)) netConfig.announcedAddresses[0] ==
formatListenAddress(ip4TcpEndPoint(conf.listenAddress, conf.tcpPort))
asyncTest "AnnouncedAddresses contains external address if extIp/Port are provided": asyncTest "AnnouncedAddresses contains external address if extIp/Port are provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
extIp = parseIpAddress("1.2.3.4") extIp = parseIpAddress("1.2.3.4")
@ -75,7 +59,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
extIp = some(extIp), extIp = some(extIp),
extPort = some(extPort) extPort = some(extPort),
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -83,11 +67,10 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 1 # Only external address should be present netConfig.announcedAddresses.len == 1 # Only external address should be present
netConfig.announcedAddresses[0] == ip4TcpEndPoint(extIp, extPort) netConfig.announcedAddresses[0] == ip4TcpEndPoint(extIp, extPort)
asyncTest "AnnouncedAddresses contains dns4DomainName if provided": asyncTest "AnnouncedAddresses contains dns4DomainName if provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
dns4DomainName = "example.com" dns4DomainName = "example.com"
@ -97,7 +80,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
dns4DomainName = some(dns4DomainName), dns4DomainName = some(dns4DomainName),
extPort = some(extPort) extPort = some(extPort),
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -105,11 +88,10 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 1 # Only DNS address should be present netConfig.announcedAddresses.len == 1 # Only DNS address should be present
netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort) netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort)
asyncTest "AnnouncedAddresses includes extMultiAddrs when provided": asyncTest "AnnouncedAddresses includes extMultiAddrs when provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
extIp = parseIpAddress("1.2.3.4") extIp = parseIpAddress("1.2.3.4")
@ -119,7 +101,7 @@ suite "Waku NetConfig":
let netConfigRes = NetConfig.init( let netConfigRes = NetConfig.init(
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
extMultiAddrs = extMultiAddrs extMultiAddrs = extMultiAddrs,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -127,12 +109,10 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # Bind address + extAddress netConfig.announcedAddresses.len == 2 # Bind address + extAddress
netConfig.announcedAddresses[1] == extMultiAddrs[0] netConfig.announcedAddresses[1] == extMultiAddrs[0]
asyncTest "AnnouncedAddresses uses dns4DomainName over extIp when both are provided": asyncTest "AnnouncedAddresses uses dns4DomainName over extIp when both are provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
dns4DomainName = "example.com" dns4DomainName = "example.com"
@ -144,7 +124,7 @@ suite "Waku NetConfig":
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
dns4DomainName = some(dns4DomainName), dns4DomainName = some(dns4DomainName),
extIp = some(extIp), extIp = some(extIp),
extPort = some(extPort) extPort = some(extPort),
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -152,11 +132,10 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 1 # DNS address netConfig.announcedAddresses.len == 1 # DNS address
netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort) netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort)
asyncTest "AnnouncedAddresses includes WebSocket addresses when enabled": asyncTest "AnnouncedAddresses includes WebSocket addresses when enabled":
var var
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
wssEnabled = false wssEnabled = false
@ -165,7 +144,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
wsEnabled = true, wsEnabled = true,
wssEnabled = wssEnabled wssEnabled = wssEnabled,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -173,9 +152,9 @@ suite "Waku NetConfig":
var netConfig = netConfigRes.get() var netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # Bind address + wsHostAddress netConfig.announcedAddresses.len == 2 # Bind address + wsHostAddress
netConfig.announcedAddresses[1] == (ip4TcpEndPoint(conf.listenAddress, netConfig.announcedAddresses[1] ==
conf.websocketPort) & wsFlag(wssEnabled)) (ip4TcpEndPoint(conf.listenAddress, conf.websocketPort) & wsFlag(wssEnabled))
## Now try the same for the case of wssEnabled = true ## Now try the same for the case of wssEnabled = true
@ -185,7 +164,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
wsEnabled = true, wsEnabled = true,
wssEnabled = wssEnabled wssEnabled = wssEnabled,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -193,12 +172,11 @@ suite "Waku NetConfig":
netConfig = netConfigRes.get() netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # Bind address + wsHostAddress netConfig.announcedAddresses.len == 2 # Bind address + wsHostAddress
netConfig.announcedAddresses[1] == (ip4TcpEndPoint(conf.listenAddress, netConfig.announcedAddresses[1] ==
conf.websocketPort) & wsFlag(wssEnabled)) (ip4TcpEndPoint(conf.listenAddress, conf.websocketPort) & wsFlag(wssEnabled))
asyncTest "Announced WebSocket address contains external IP if provided": asyncTest "Announced WebSocket address contains external IP if provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
extIp = parseIpAddress("1.2.3.4") extIp = parseIpAddress("1.2.3.4")
@ -211,7 +189,7 @@ suite "Waku NetConfig":
extIp = some(extIp), extIp = some(extIp),
extPort = some(extPort), extPort = some(extPort),
wsEnabled = true, wsEnabled = true,
wssEnabled = wssEnabled wssEnabled = wssEnabled,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -219,12 +197,11 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # External address + wsHostAddress netConfig.announcedAddresses.len == 2 # External address + wsHostAddress
netConfig.announcedAddresses[1] == (ip4TcpEndPoint(extIp, netConfig.announcedAddresses[1] ==
conf.websocketPort) & wsFlag(wssEnabled)) (ip4TcpEndPoint(extIp, conf.websocketPort) & wsFlag(wssEnabled))
asyncTest "Announced WebSocket address contains dns4DomainName if provided": asyncTest "Announced WebSocket address contains dns4DomainName if provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
dns4DomainName = "example.com" dns4DomainName = "example.com"
@ -237,7 +214,7 @@ suite "Waku NetConfig":
dns4DomainName = some(dns4DomainName), dns4DomainName = some(dns4DomainName),
extPort = some(extPort), extPort = some(extPort),
wsEnabled = true, wsEnabled = true,
wssEnabled = wssEnabled wssEnabled = wssEnabled,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -245,12 +222,11 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # Bind address + wsHostAddress netConfig.announcedAddresses.len == 2 # Bind address + wsHostAddress
netConfig.announcedAddresses[1] == (dns4TcpEndPoint(dns4DomainName, conf.websocketPort) & netConfig.announcedAddresses[1] ==
wsFlag(wssEnabled)) (dns4TcpEndPoint(dns4DomainName, conf.websocketPort) & wsFlag(wssEnabled))
asyncTest "Announced WebSocket address contains dns4DomainName if provided alongside extIp": asyncTest "Announced WebSocket address contains dns4DomainName if provided alongside extIp":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
dns4DomainName = "example.com" dns4DomainName = "example.com"
@ -265,7 +241,7 @@ suite "Waku NetConfig":
extIp = some(extIp), extIp = some(extIp),
extPort = some(extPort), extPort = some(extPort),
wsEnabled = true, wsEnabled = true,
wssEnabled = wssEnabled wssEnabled = wssEnabled,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -273,19 +249,16 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # DNS address + wsHostAddress netConfig.announcedAddresses.len == 2 # DNS address + wsHostAddress
netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort) netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort)
netConfig.announcedAddresses[1] == (dns4TcpEndPoint(dns4DomainName, conf.websocketPort) & netConfig.announcedAddresses[1] ==
wsFlag(wssEnabled)) (dns4TcpEndPoint(dns4DomainName, conf.websocketPort) & wsFlag(wssEnabled))
asyncTest "ENR is set with bindIp/Port if no extIp/Port are provided": asyncTest "ENR is set with bindIp/Port if no extIp/Port are provided":
let conf = defaultTestWakuNodeConf() let conf = defaultTestWakuNodeConf()
let netConfigRes = NetConfig.init( let netConfigRes =
bindIp = conf.listenAddress, NetConfig.init(bindIp = conf.listenAddress, bindPort = conf.tcpPort)
bindPort = conf.tcpPort
)
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -296,7 +269,6 @@ suite "Waku NetConfig":
netConfig.enrPort.get() == conf.tcpPort netConfig.enrPort.get() == conf.tcpPort
asyncTest "ENR is set with extIp/Port if provided": asyncTest "ENR is set with extIp/Port if provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
extIp = parseIpAddress("1.2.3.4") extIp = parseIpAddress("1.2.3.4")
@ -306,7 +278,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
extIp = some(extIp), extIp = some(extIp),
extPort = some(extPort) extPort = some(extPort),
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -318,7 +290,6 @@ suite "Waku NetConfig":
netConfig.enrPort.get() == extPort netConfig.enrPort.get() == extPort
asyncTest "ENR is set with dns4DomainName if provided": asyncTest "ENR is set with dns4DomainName if provided":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
dns4DomainName = "example.com" dns4DomainName = "example.com"
@ -328,7 +299,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
dns4DomainName = some(dns4DomainName), dns4DomainName = some(dns4DomainName),
extPort = some(extPort) extPort = some(extPort),
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -339,7 +310,6 @@ suite "Waku NetConfig":
netConfig.enrMultiaddrs.contains(dns4TcpEndPoint(dns4DomainName, extPort)) netConfig.enrMultiaddrs.contains(dns4TcpEndPoint(dns4DomainName, extPort))
asyncTest "wsHostAddress is not announced if a WS/WSS address is provided in extMultiAddrs": asyncTest "wsHostAddress is not announced if a WS/WSS address is provided in extMultiAddrs":
var var
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
extAddIp = parseIpAddress("1.2.3.4") extAddIp = parseIpAddress("1.2.3.4")
@ -352,7 +322,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
extMultiAddrs = extMultiAddrs, extMultiAddrs = extMultiAddrs,
wsEnabled = wsEnabled wsEnabled = wsEnabled,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -360,7 +330,7 @@ suite "Waku NetConfig":
var netConfig = netConfigRes.get() var netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # Bind address + extAddress netConfig.announcedAddresses.len == 2 # Bind address + extAddress
netConfig.announcedAddresses[1] == extMultiAddrs[0] netConfig.announcedAddresses[1] == extMultiAddrs[0]
# Now same test for WSS external address # Now same test for WSS external address
@ -371,7 +341,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
extMultiAddrs = extMultiAddrs, extMultiAddrs = extMultiAddrs,
wssEnabled = wssEnabled wssEnabled = wssEnabled,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -379,11 +349,10 @@ suite "Waku NetConfig":
netConfig = netConfigRes.get() netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 2 # Bind address + extAddress netConfig.announcedAddresses.len == 2 # Bind address + extAddress
netConfig.announcedAddresses[1] == extMultiAddrs[0] netConfig.announcedAddresses[1] == extMultiAddrs[0]
asyncTest "Only extMultiAddrs are published when enabling extMultiAddrsOnly flag": asyncTest "Only extMultiAddrs are published when enabling extMultiAddrsOnly flag":
let let
conf = defaultTestWakuNodeConf() conf = defaultTestWakuNodeConf()
extAddIp = parseIpAddress("1.2.3.4") extAddIp = parseIpAddress("1.2.3.4")
@ -394,7 +363,7 @@ suite "Waku NetConfig":
bindIp = conf.listenAddress, bindIp = conf.listenAddress,
bindPort = conf.tcpPort, bindPort = conf.tcpPort,
extMultiAddrs = extMultiAddrs, extMultiAddrs = extMultiAddrs,
extMultiAddrsOnly = true extMultiAddrsOnly = true,
) )
assert netConfigRes.isOk(), $netConfigRes.error assert netConfigRes.isOk(), $netConfigRes.error
@ -402,6 +371,5 @@ suite "Waku NetConfig":
let netConfig = netConfigRes.get() let netConfig = netConfigRes.get()
check: check:
netConfig.announcedAddresses.len == 1 # ExtAddress netConfig.announcedAddresses.len == 1 # ExtAddress
netConfig.announcedAddresses[0] == extMultiAddrs[0] netConfig.announcedAddresses[0] == extMultiAddrs[0]

View File

@ -17,16 +17,13 @@ import
../../waku/waku_core, ../../waku/waku_core,
./testlib/common ./testlib/common
procSuite "Waku Noise": procSuite "Waku Noise":
common.randomize() common.randomize()
test "PKCS#7 Padding/Unpadding": test "PKCS#7 Padding/Unpadding":
# We test padding for different message lengths # We test padding for different message lengths
let maxMessageLength = 3 * NoisePaddingBlockSize let maxMessageLength = 3 * NoisePaddingBlockSize
for messageLen in 0..maxMessageLength: for messageLen in 0 .. maxMessageLength:
let let
message = randomSeqByte(rng[], messageLen) message = randomSeqByte(rng[], messageLen)
padded = pkcs7_pad(message, NoisePaddingBlockSize) padded = pkcs7_pad(message, NoisePaddingBlockSize)
@ -38,12 +35,11 @@ procSuite "Waku Noise":
message == unpadded message == unpadded
test "ChaChaPoly Encryption/Decryption: random byte sequences": test "ChaChaPoly Encryption/Decryption: random byte sequences":
let cipherState = randomChaChaPolyCipherState(rng[]) let cipherState = randomChaChaPolyCipherState(rng[])
# We encrypt/decrypt random byte sequences # We encrypt/decrypt random byte sequences
let let
plaintext: seq[byte] = randomSeqByte(rng[], rand(1..128)) plaintext: seq[byte] = randomSeqByte(rng[], rand(1 .. 128))
ciphertext: ChaChaPolyCiphertext = encrypt(cipherState, plaintext) ciphertext: ChaChaPolyCiphertext = encrypt(cipherState, plaintext)
decryptedCiphertext: seq[byte] = decrypt(cipherState, ciphertext) decryptedCiphertext: seq[byte] = decrypt(cipherState, ciphertext)
@ -51,12 +47,11 @@ procSuite "Waku Noise":
plaintext == decryptedCiphertext plaintext == decryptedCiphertext
test "ChaChaPoly Encryption/Decryption: random strings": test "ChaChaPoly Encryption/Decryption: random strings":
let cipherState = randomChaChaPolyCipherState(rng[]) let cipherState = randomChaChaPolyCipherState(rng[])
# We encrypt/decrypt random strings # We encrypt/decrypt random strings
var plaintext: string var plaintext: string
for _ in 1..rand(1..128): for _ in 1 .. rand(1 .. 128):
add(plaintext, char(rand(int('A') .. int('z')))) add(plaintext, char(rand(int('A') .. int('z'))))
let let
@ -67,7 +62,6 @@ procSuite "Waku Noise":
plaintext.toBytes() == decryptedCiphertext plaintext.toBytes() == decryptedCiphertext
test "Noise public keys: encrypt and decrypt a public key": test "Noise public keys: encrypt and decrypt a public key":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let let
@ -79,7 +73,6 @@ procSuite "Waku Noise":
noisePublicKey == decryptedPk noisePublicKey == decryptedPk
test "Noise public keys: decrypt an unencrypted public key": test "Noise public keys: decrypt an unencrypted public key":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let let
@ -90,7 +83,6 @@ procSuite "Waku Noise":
noisePublicKey == decryptedPk noisePublicKey == decryptedPk
test "Noise public keys: encrypt an encrypted public key": test "Noise public keys: encrypt an encrypted public key":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let let
@ -102,7 +94,6 @@ procSuite "Waku Noise":
encryptedPk == encryptedPk2 encryptedPk == encryptedPk2
test "Noise public keys: encrypt, decrypt and decrypt a public key": test "Noise public keys: encrypt, decrypt and decrypt a public key":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let let
@ -115,32 +106,31 @@ procSuite "Waku Noise":
decryptedPk == decryptedPk2 decryptedPk == decryptedPk2
test "Noise public keys: serialize and deserialize an unencrypted public key": test "Noise public keys: serialize and deserialize an unencrypted public key":
let let
noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
serializedNoisePublicKey: seq[byte] = serializeNoisePublicKey(noisePublicKey) serializedNoisePublicKey: seq[byte] = serializeNoisePublicKey(noisePublicKey)
deserializedNoisePublicKey: NoisePublicKey = intoNoisePublicKey(serializedNoisePublicKey) deserializedNoisePublicKey: NoisePublicKey =
intoNoisePublicKey(serializedNoisePublicKey)
check: check:
noisePublicKey == deserializedNoisePublicKey noisePublicKey == deserializedNoisePublicKey
test "Noise public keys: encrypt, serialize, deserialize and decrypt a public key": test "Noise public keys: encrypt, serialize, deserialize and decrypt a public key":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let let
cs: ChaChaPolyCipherState = randomChaChaPolyCipherState(rng[]) cs: ChaChaPolyCipherState = randomChaChaPolyCipherState(rng[])
encryptedPk: NoisePublicKey = encryptNoisePublicKey(cs, noisePublicKey) encryptedPk: NoisePublicKey = encryptNoisePublicKey(cs, noisePublicKey)
serializedNoisePublicKey: seq[byte] = serializeNoisePublicKey(encryptedPk) serializedNoisePublicKey: seq[byte] = serializeNoisePublicKey(encryptedPk)
deserializedNoisePublicKey: NoisePublicKey = intoNoisePublicKey(serializedNoisePublicKey) deserializedNoisePublicKey: NoisePublicKey =
decryptedPk: NoisePublicKey = decryptNoisePublicKey(cs, deserializedNoisePublicKey) intoNoisePublicKey(serializedNoisePublicKey)
decryptedPk: NoisePublicKey =
decryptNoisePublicKey(cs, deserializedNoisePublicKey)
check: check:
noisePublicKey == decryptedPk noisePublicKey == decryptedPk
test "PayloadV2: serialize/deserialize PayloadV2 to byte sequence": test "PayloadV2: serialize/deserialize PayloadV2 to byte sequence":
let let
payload2: PayloadV2 = randomPayloadV2(rng[]) payload2: PayloadV2 = randomPayloadV2(rng[])
serializedPayload = serializePayloadV2(payload2) serializedPayload = serializePayloadV2(payload2)
@ -154,9 +144,7 @@ procSuite "Waku Noise":
deserializedPayload.isOk() deserializedPayload.isOk()
payload2 == deserializedPayload.get() payload2 == deserializedPayload.get()
test "PayloadV2: Encode/Decode a Waku Message (version 2) to a PayloadV2": test "PayloadV2: Encode/Decode a Waku Message (version 2) to a PayloadV2":
# We encode to a WakuMessage a random PayloadV2 # We encode to a WakuMessage a random PayloadV2
let let
payload2 = randomPayloadV2(rng[]) payload2 = randomPayloadV2(rng[])
@ -181,7 +169,6 @@ procSuite "Waku Noise":
payload2 == decoded.get() payload2 == decoded.get()
test "Noise State Machine: Diffie-Hellman operation": test "Noise State Machine: Diffie-Hellman operation":
#We generate random keypairs #We generate random keypairs
let let
aliceKey = genKeyPair(rng[]) aliceKey = genKeyPair(rng[])
@ -197,13 +184,12 @@ procSuite "Waku Noise":
dh1 == dh2 dh1 == dh2
test "Noise State Machine: Cipher State primitives": test "Noise State Machine: Cipher State primitives":
# We generate a random Cipher State, associated data ad and plaintext # We generate a random Cipher State, associated data ad and plaintext
var var
cipherState: CipherState = randomCipherState(rng[]) cipherState: CipherState = randomCipherState(rng[])
nonce: uint64 = uint64(rand(0 .. int.high)) nonce: uint64 = uint64(rand(0 .. int.high))
ad: seq[byte] = randomSeqByte(rng[], rand(1..128)) ad: seq[byte] = randomSeqByte(rng[], rand(1 .. 128))
plaintext: seq[byte] = randomSeqByte(rng[], rand(1..128)) plaintext: seq[byte] = randomSeqByte(rng[], rand(1 .. 128))
# We set the random nonce generated in the cipher state # We set the random nonce generated in the cipher state
setNonce(cipherState, nonce) setNonce(cipherState, nonce)
@ -230,7 +216,7 @@ procSuite "Waku Noise":
setCipherStateKey(cipherState, EmptyKey) setCipherStateKey(cipherState, EmptyKey)
nonce = getNonce(cipherState) nonce = getNonce(cipherState)
plaintext = randomSeqByte(rng[], rand(1..128)) plaintext = randomSeqByte(rng[], rand(1 .. 128))
ciphertext = encryptWithAd(cipherState, ad, plaintext) ciphertext = encryptWithAd(cipherState, ad, plaintext)
check: check:
@ -242,7 +228,7 @@ procSuite "Waku Noise":
nonce = getNonce(cipherState) nonce = getNonce(cipherState)
# Note that we set ciphertext minimum length to 16 to not trigger checks on authentication tag length # Note that we set ciphertext minimum length to 16 to not trigger checks on authentication tag length
ciphertext = randomSeqByte(rng[], rand(16..128)) ciphertext = randomSeqByte(rng[], rand(16 .. 128))
plaintext = decryptWithAd(cipherState, ad, ciphertext) plaintext = decryptWithAd(cipherState, ad, ciphertext)
check: check:
@ -255,10 +241,10 @@ procSuite "Waku Noise":
# We generate a test Cipher State with nonce set to MaxNonce # We generate a test Cipher State with nonce set to MaxNonce
cipherState = randomCipherState(rng[]) cipherState = randomCipherState(rng[])
setNonce(cipherState, NonceMax) setNonce(cipherState, NonceMax)
plaintext = randomSeqByte(rng[], rand(1..128)) plaintext = randomSeqByte(rng[], rand(1 .. 128))
# We test if encryption fails with a NoiseNonceMaxError error. Any subsequent encryption call over the Cipher State should fail similarly and leave the nonce unchanged # We test if encryption fails with a NoiseNonceMaxError error. Any subsequent encryption call over the Cipher State should fail similarly and leave the nonce unchanged
for _ in [1..5]: for _ in [1 .. 5]:
expect NoiseNonceMaxError: expect NoiseNonceMaxError:
ciphertext = encryptWithAd(cipherState, ad, plaintext) ciphertext = encryptWithAd(cipherState, ad, plaintext)
@ -271,14 +257,14 @@ procSuite "Waku Noise":
# To perform such test, we then need to encrypt a test plaintext using directly ChaChaPoly primitive # To perform such test, we then need to encrypt a test plaintext using directly ChaChaPoly primitive
cipherState = randomCipherState(rng[]) cipherState = randomCipherState(rng[])
setNonce(cipherState, NonceMax) setNonce(cipherState, NonceMax)
plaintext = randomSeqByte(rng[], rand(1..128)) plaintext = randomSeqByte(rng[], rand(1 .. 128))
# We perform encryption using the Cipher State key, NonceMax and ad # We perform encryption using the Cipher State key, NonceMax and ad
# By Noise specification the nonce is 8 bytes long out of the 12 bytes supported by ChaChaPoly, thus we copy the Little endian conversion of the nonce to a ChaChaPolyNonce # By Noise specification the nonce is 8 bytes long out of the 12 bytes supported by ChaChaPoly, thus we copy the Little endian conversion of the nonce to a ChaChaPolyNonce
var var
encNonce: ChaChaPolyNonce encNonce: ChaChaPolyNonce
authorizationTag: ChaChaPolyTag authorizationTag: ChaChaPolyTag
encNonce[4..<12] = toBytesLE(NonceMax) encNonce[4 ..< 12] = toBytesLE(NonceMax)
ChaChaPoly.encrypt(getKey(cipherState), encNonce, authorizationTag, plaintext, ad) ChaChaPoly.encrypt(getKey(cipherState), encNonce, authorizationTag, plaintext, ad)
# The output ciphertext is stored in the plaintext variable after ChaChaPoly.encrypt is called: we copy it along with the authorization tag. # The output ciphertext is stored in the plaintext variable after ChaChaPoly.encrypt is called: we copy it along with the authorization tag.
@ -289,7 +275,7 @@ procSuite "Waku Noise":
# At this point ciphertext is a proper encryption of the original plaintext obtained with nonce equal to NonceMax # At this point ciphertext is a proper encryption of the original plaintext obtained with nonce equal to NonceMax
# We can now test if decryption fails with a NoiseNonceMaxError error. Any subsequent decryption call over the Cipher State should fail similarly and leave the nonce unchanged # We can now test if decryption fails with a NoiseNonceMaxError error. Any subsequent decryption call over the Cipher State should fail similarly and leave the nonce unchanged
# Note that decryptWithAd doesn't fail in decrypting the ciphertext (otherwise a NoiseDecryptTagError would have been triggered) # Note that decryptWithAd doesn't fail in decrypting the ciphertext (otherwise a NoiseDecryptTagError would have been triggered)
for _ in [1..5]: for _ in [1 .. 5]:
expect NoiseNonceMaxError: expect NoiseNonceMaxError:
plaintext = decryptWithAd(cipherState, ad, ciphertext) plaintext = decryptWithAd(cipherState, ad, ciphertext)
@ -297,11 +283,10 @@ procSuite "Waku Noise":
getNonce(cipherState) == NonceMax + 1 getNonce(cipherState) == NonceMax + 1
test "Noise State Machine: Symmetric State primitives": test "Noise State Machine: Symmetric State primitives":
# We select one supported handshake pattern and we initialize a symmetric state # We select one supported handshake pattern and we initialize a symmetric state
var var
hsPattern = NoiseHandshakePatterns["XX"] hsPattern = NoiseHandshakePatterns["XX"]
symmetricState: SymmetricState = SymmetricState.init(hsPattern) symmetricState: SymmetricState = SymmetricState.init(hsPattern)
# We get all the Symmetric State field # We get all the Symmetric State field
# cs : Cipher State # cs : Cipher State
@ -321,7 +306,7 @@ procSuite "Waku Noise":
######################################## ########################################
# We generate a random byte sequence and execute a mixHash over it # We generate a random byte sequence and execute a mixHash over it
mixHash(symmetricState, randomSeqByte(rng[], rand(1..128))) mixHash(symmetricState, randomSeqByte(rng[], rand(1 .. 128)))
# mixHash changes only the handshake hash value of the Symmetric state # mixHash changes only the handshake hash value of the Symmetric state
check: check:
@ -337,7 +322,7 @@ procSuite "Waku Noise":
######################################## ########################################
# We generate random input key material and we execute mixKey # We generate random input key material and we execute mixKey
var inputKeyMaterial = randomSeqByte(rng[], rand(1..128)) var inputKeyMaterial = randomSeqByte(rng[], rand(1 .. 128))
mixKey(symmetricState, inputKeyMaterial) mixKey(symmetricState, inputKeyMaterial)
# mixKey changes the Symmetric State's chaining key and encryption key of the embedded Cipher State # mixKey changes the Symmetric State's chaining key and encryption key of the embedded Cipher State
@ -358,7 +343,7 @@ procSuite "Waku Noise":
######################################## ########################################
# We generate random input key material and we execute mixKeyAndHash # We generate random input key material and we execute mixKeyAndHash
inputKeyMaterial = randomSeqByte(rng[], rand(1..128)) inputKeyMaterial = randomSeqByte(rng[], rand(1 .. 128))
mixKeyAndHash(symmetricState, inputKeyMaterial) mixKeyAndHash(symmetricState, inputKeyMaterial)
# mixKeyAndHash executes a mixKey and a mixHash using the input key material # mixKeyAndHash executes a mixKey and a mixHash using the input key material
@ -431,12 +416,12 @@ procSuite "Waku Noise":
getKey(cs1) != getKey(cs2) getKey(cs1) != getKey(cs2)
test "Noise XX Handhshake and message encryption (extended test)": test "Noise XX Handhshake and message encryption (extended test)":
let hsPattern = NoiseHandshakePatterns["XX"] let hsPattern = NoiseHandshakePatterns["XX"]
# We initialize Alice's and Bob's Handshake State # We initialize Alice's and Bob's Handshake State
let aliceStaticKey = genKeyPair(rng[]) let aliceStaticKey = genKeyPair(rng[])
var aliceHS = initialize(hsPattern = hsPattern, staticKey = aliceStaticKey, initiator = true) var aliceHS =
initialize(hsPattern = hsPattern, staticKey = aliceStaticKey, initiator = true)
let bobStaticKey = genKeyPair(rng[]) let bobStaticKey = genKeyPair(rng[])
var bobHS = initialize(hsPattern = hsPattern, staticKey = bobStaticKey) var bobHS = initialize(hsPattern = hsPattern, staticKey = bobStaticKey)
@ -457,7 +442,8 @@ procSuite "Waku Noise":
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message # By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message # and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -489,7 +475,8 @@ procSuite "Waku Noise":
sentTransportMessage = randomSeqByte(rng[], 32) sentTransportMessage = randomSeqByte(rng[], 32)
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message # Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -504,10 +491,14 @@ procSuite "Waku Noise":
let prevAliceHS = aliceHS let prevAliceHS = aliceHS
let prevBobHS = bobHS let prevBobHS = bobHS
let bobStep1 = stepHandshake(rng[], bobHS, transportMessage = sentTransportMessage).get() let bobStep1 =
let aliceStep1 = stepHandshake(rng[], aliceHS, readPayloadV2 = bobStep1.payload2).get() stepHandshake(rng[], bobHS, transportMessage = sentTransportMessage).get()
let aliceStep2 = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() let aliceStep1 =
let bobStep2 = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep2.payload2).get() stepHandshake(rng[], aliceHS, readPayloadV2 = bobStep1.payload2).get()
let aliceStep2 =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
let bobStep2 =
stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep2.payload2).get()
check: check:
aliceStep1 == default(HandshakeStepResult) aliceStep1 == default(HandshakeStepResult)
@ -534,12 +525,12 @@ procSuite "Waku Noise":
readMessage: seq[byte] readMessage: seq[byte]
defaultMessageNametagBuffer: MessageNametagBuffer defaultMessageNametagBuffer: MessageNametagBuffer
for _ in 0..10: for _ in 0 .. 10:
# Alice writes to Bob # Alice writes to Bob
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage
@ -547,13 +538,13 @@ procSuite "Waku Noise":
# Bob writes to Alice # Bob writes to Alice
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage
test "Noise XXpsk0 Handhshake and message encryption (short test)": test "Noise XXpsk0 Handhshake and message encryption (short test)":
let hsPattern = NoiseHandshakePatterns["XXpsk0"] let hsPattern = NoiseHandshakePatterns["XXpsk0"]
# We generate a random psk # We generate a random psk
@ -561,7 +552,9 @@ procSuite "Waku Noise":
# We initialize Alice's and Bob's Handshake State # We initialize Alice's and Bob's Handshake State
let aliceStaticKey = genKeyPair(rng[]) let aliceStaticKey = genKeyPair(rng[])
var aliceHS = initialize(hsPattern = hsPattern, staticKey = aliceStaticKey, psk = psk, initiator = true) var aliceHS = initialize(
hsPattern = hsPattern, staticKey = aliceStaticKey, psk = psk, initiator = true
)
let bobStaticKey = genKeyPair(rng[]) let bobStaticKey = genKeyPair(rng[])
var bobHS = initialize(hsPattern = hsPattern, staticKey = bobStaticKey, psk = psk) var bobHS = initialize(hsPattern = hsPattern, staticKey = bobStaticKey, psk = psk)
@ -582,7 +575,8 @@ procSuite "Waku Noise":
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message # By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message # and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -614,7 +608,8 @@ procSuite "Waku Noise":
sentTransportMessage = randomSeqByte(rng[], 32) sentTransportMessage = randomSeqByte(rng[], 32)
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message # Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transportMessage alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transportMessage alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -641,12 +636,12 @@ procSuite "Waku Noise":
readMessage: seq[byte] readMessage: seq[byte]
defaultMessageNametagBuffer: MessageNametagBuffer defaultMessageNametagBuffer: MessageNametagBuffer
for _ in 0..10: for _ in 0 .. 10:
# Alice writes to Bob # Alice writes to Bob
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage
@ -654,13 +649,13 @@ procSuite "Waku Noise":
# Bob writes to Alice # Bob writes to Alice
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage
test "Noise K1K1 Handhshake and message encryption (short test)": test "Noise K1K1 Handhshake and message encryption (short test)":
let hsPattern = NoiseHandshakePatterns["K1K1"] let hsPattern = NoiseHandshakePatterns["K1K1"]
# We initialize Alice's and Bob's Handshake State # We initialize Alice's and Bob's Handshake State
@ -672,10 +667,21 @@ procSuite "Waku Noise":
# <- s # <- s
# ... # ...
# So we define accordingly the sequence of the pre-message public keys # So we define accordingly the sequence of the pre-message public keys
let preMessagePKs: seq[NoisePublicKey] = @[toNoisePublicKey(getPublicKey(aliceStaticKey)), toNoisePublicKey(getPublicKey(bobStaticKey))] let preMessagePKs: seq[NoisePublicKey] =
@[
toNoisePublicKey(getPublicKey(aliceStaticKey)),
toNoisePublicKey(getPublicKey(bobStaticKey)),
]
var aliceHS = initialize(hsPattern = hsPattern, staticKey = aliceStaticKey, preMessagePKs = preMessagePKs, initiator = true) var aliceHS = initialize(
var bobHS = initialize(hsPattern = hsPattern, staticKey = bobStaticKey, preMessagePKs = preMessagePKs) hsPattern = hsPattern,
staticKey = aliceStaticKey,
preMessagePKs = preMessagePKs,
initiator = true,
)
var bobHS = initialize(
hsPattern = hsPattern, staticKey = bobStaticKey, preMessagePKs = preMessagePKs
)
var var
sentTransportMessage: seq[byte] sentTransportMessage: seq[byte]
@ -693,7 +699,8 @@ procSuite "Waku Noise":
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message # By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message # and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -725,7 +732,8 @@ procSuite "Waku Noise":
sentTransportMessage = randomSeqByte(rng[], 32) sentTransportMessage = randomSeqByte(rng[], 32)
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake_message and the (encrypted) transportMessage # Similarly as in first step, Alice writes a Waku2 payload containing the handshake_message and the (encrypted) transportMessage
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transportMessage alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transportMessage alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -752,12 +760,12 @@ procSuite "Waku Noise":
readMessage: seq[byte] readMessage: seq[byte]
defaultMessageNametagBuffer: MessageNametagBuffer defaultMessageNametagBuffer: MessageNametagBuffer
for _ in 0..10: for _ in 0 .. 10:
# Alice writes to Bob # Alice writes to Bob
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage
@ -765,14 +773,13 @@ procSuite "Waku Noise":
# Bob writes to Alice # Bob writes to Alice
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage
test "Noise XK1 Handhshake and message encryption (short test)": test "Noise XK1 Handhshake and message encryption (short test)":
let hsPattern = NoiseHandshakePatterns["XK1"] let hsPattern = NoiseHandshakePatterns["XK1"]
# We initialize Alice's and Bob's Handshake State # We initialize Alice's and Bob's Handshake State
@ -783,10 +790,18 @@ procSuite "Waku Noise":
# <- s # <- s
# ... # ...
# So we define accordingly the sequence of the pre-message public keys # So we define accordingly the sequence of the pre-message public keys
let preMessagePKs: seq[NoisePublicKey] = @[toNoisePublicKey(getPublicKey(bobStaticKey))] let preMessagePKs: seq[NoisePublicKey] =
@[toNoisePublicKey(getPublicKey(bobStaticKey))]
var aliceHS = initialize(hsPattern = hsPattern, staticKey = aliceStaticKey, preMessagePKs = preMessagePKs, initiator = true) var aliceHS = initialize(
var bobHS = initialize(hsPattern = hsPattern, staticKey = bobStaticKey, preMessagePKs = preMessagePKs) hsPattern = hsPattern,
staticKey = aliceStaticKey,
preMessagePKs = preMessagePKs,
initiator = true,
)
var bobHS = initialize(
hsPattern = hsPattern, staticKey = bobStaticKey, preMessagePKs = preMessagePKs
)
var var
sentTransportMessage: seq[byte] sentTransportMessage: seq[byte]
@ -804,7 +819,8 @@ procSuite "Waku Noise":
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message # By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message # and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -836,7 +852,8 @@ procSuite "Waku Noise":
sentTransportMessage = randomSeqByte(rng[], 32) sentTransportMessage = randomSeqByte(rng[], 32)
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message # Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get() aliceStep =
stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage).get()
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get() bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = aliceStep.payload2).get()
@ -863,12 +880,12 @@ procSuite "Waku Noise":
readMessage: seq[byte] readMessage: seq[byte]
defaultMessageNametagBuffer: MessageNametagBuffer defaultMessageNametagBuffer: MessageNametagBuffer
for _ in 0..10: for _ in 0 .. 10:
# Alice writes to Bob # Alice writes to Bob
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(aliceHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(bobHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage
@ -876,7 +893,8 @@ procSuite "Waku Noise":
# Bob writes to Alice # Bob writes to Alice
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer) payload2 = writeMessage(bobHSResult, message, defaultMessageNametagBuffer)
readMessage = readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get() readMessage =
readMessage(aliceHSResult, payload2, defaultMessageNametagBuffer).get()
check: check:
message == readMessage message == readMessage

View File

@ -1,9 +1,6 @@
{.used.} {.used.}
import import std/tables, stew/[results, byteutils], testutils/unittests
std/tables,
stew/[results, byteutils],
testutils/unittests
import import
../../waku/common/protobuf, ../../waku/common/protobuf,
../../waku/utils/noise as waku_message_utils, ../../waku/utils/noise as waku_message_utils,
@ -19,7 +16,6 @@ procSuite "Waku Noise Sessions":
# This test implements the Device pairing and Secure Transfers with Noise # This test implements the Device pairing and Secure Transfers with Noise
# detailed in the 43/WAKU2-DEVICE-PAIRING RFC https://rfc.vac.dev/spec/43/ # detailed in the 43/WAKU2-DEVICE-PAIRING RFC https://rfc.vac.dev/spec/43/
test "Noise Waku Pairing Handhshake and Secure transfer": test "Noise Waku Pairing Handhshake and Secure transfer":
######################### #########################
# Pairing Phase # Pairing Phase
######################### #########################
@ -47,10 +43,19 @@ procSuite "Waku Noise Sessions":
# Out-of-band Communication # Out-of-band Communication
# Bob prepares the QR and sends it out-of-band to Alice # Bob prepares the QR and sends it out-of-band to Alice
let qr = toQr(applicationName, applicationVersion, shardId, getPublicKey(bobEphemeralKey), bobCommittedStaticKey) let qr = toQr(
applicationName,
applicationVersion,
shardId,
getPublicKey(bobEphemeralKey),
bobCommittedStaticKey,
)
# Alice deserializes the QR code # Alice deserializes the QR code
let (readApplicationName, readApplicationVersion, readShardId, readEphemeralKey, readCommittedStaticKey) = fromQr(qr) let (
readApplicationName, readApplicationVersion, readShardId, readEphemeralKey,
readCommittedStaticKey,
) = fromQr(qr)
# We check if QR serialization/deserialization works # We check if QR serialization/deserialization works
check: check:
@ -61,19 +66,35 @@ procSuite "Waku Noise Sessions":
bobCommittedStaticKey == readCommittedStaticKey bobCommittedStaticKey == readCommittedStaticKey
# We set the contentTopic from the content topic parameters exchanged in the QR # We set the contentTopic from the content topic parameters exchanged in the QR
let contentTopic: ContentTopic = "/" & applicationName & "/" & applicationVersion & "/wakunoise/1/sessions_shard-" & shardId & "/proto" let contentTopic: ContentTopic =
"/" & applicationName & "/" & applicationVersion & "/wakunoise/1/sessions_shard-" &
shardId & "/proto"
############### ###############
# Pre-handshake message # Pre-handshake message
# #
# <- eB {H(sB||r), contentTopicParams, messageNametag} # <- eB {H(sB||r), contentTopicParams, messageNametag}
############### ###############
let preMessagePKs: seq[NoisePublicKey] = @[toNoisePublicKey(getPublicKey(bobEphemeralKey))] let preMessagePKs: seq[NoisePublicKey] =
@[toNoisePublicKey(getPublicKey(bobEphemeralKey))]
# We initialize the Handshake states. # We initialize the Handshake states.
# Note that we pass the whole qr serialization as prologue information # Note that we pass the whole qr serialization as prologue information
var aliceHS = initialize(hsPattern = hsPattern, ephemeralKey = aliceEphemeralKey, staticKey = aliceStaticKey, prologue = qr.toBytes, preMessagePKs = preMessagePKs, initiator = true) var aliceHS = initialize(
var bobHS = initialize(hsPattern = hsPattern, ephemeralKey = bobEphemeralKey, staticKey = bobStaticKey, prologue = qr.toBytes, preMessagePKs = preMessagePKs) hsPattern = hsPattern,
ephemeralKey = aliceEphemeralKey,
staticKey = aliceStaticKey,
prologue = qr.toBytes,
preMessagePKs = preMessagePKs,
initiator = true,
)
var bobHS = initialize(
hsPattern = hsPattern,
ephemeralKey = bobEphemeralKey,
staticKey = bobStaticKey,
prologue = qr.toBytes,
preMessagePKs = preMessagePKs,
)
############### ###############
# Pairing Handshake # Pairing Handshake
@ -109,7 +130,13 @@ procSuite "Waku Noise Sessions":
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message # By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message # and the (encrypted) transport message
# The message is sent with a messageNametag equal to the one received through the QR code # The message is sent with a messageNametag equal to the one received through the QR code
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage, messageNametag = qrMessageNametag).get() aliceStep = stepHandshake(
rng[],
aliceHS,
transportMessage = sentTransportMessage,
messageNametag = qrMessageNametag,
)
.get()
############################################### ###############################################
# We prepare a Waku message from Alice's payload2 # We prepare a Waku message from Alice's payload2
@ -138,7 +165,10 @@ procSuite "Waku Noise Sessions":
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
# Note that Bob verifies if the received payloadv2 has the expected messageNametag set # Note that Bob verifies if the received payloadv2 has the expected messageNametag set
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = readPayloadV2, messageNametag = qrMessageNametag).get() bobStep = stepHandshake(
rng[], bobHS, readPayloadV2 = readPayloadV2, messageNametag = qrMessageNametag
)
.get()
check: check:
bobStep.transportMessage == sentTransportMessage bobStep.transportMessage == sentTransportMessage
@ -166,7 +196,13 @@ procSuite "Waku Noise Sessions":
sentTransportMessage = r sentTransportMessage = r
# At this step, Bob writes and returns a payload # At this step, Bob writes and returns a payload
bobStep = stepHandshake(rng[], bobHS, transportMessage = sentTransportMessage, messageNametag = bobMessageNametag).get() bobStep = stepHandshake(
rng[],
bobHS,
transportMessage = sentTransportMessage,
messageNametag = bobMessageNametag,
)
.get()
############################################### ###############################################
# We prepare a Waku message from Bob's payload2 # We prepare a Waku message from Bob's payload2
@ -194,13 +230,20 @@ procSuite "Waku Noise Sessions":
############################################### ###############################################
# While Alice reads and returns the (decrypted) transport message # While Alice reads and returns the (decrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, readPayloadV2 = readPayloadV2, messageNametag = aliceMessageNametag).get() aliceStep = stepHandshake(
rng[],
aliceHS,
readPayloadV2 = readPayloadV2,
messageNametag = aliceMessageNametag,
)
.get()
check: check:
aliceStep.transportMessage == sentTransportMessage aliceStep.transportMessage == sentTransportMessage
# Alice further checks if Bob's commitment opens to Bob's static key she just received # Alice further checks if Bob's commitment opens to Bob's static key she just received
let expectedBobCommittedStaticKey = commitPublicKey(aliceHS.rs, aliceStep.transportMessage) let expectedBobCommittedStaticKey =
commitPublicKey(aliceHS.rs, aliceStep.transportMessage)
check: check:
expectedBobCommittedStaticKey == bobCommittedStaticKey expectedBobCommittedStaticKey == bobCommittedStaticKey
@ -219,7 +262,13 @@ procSuite "Waku Noise Sessions":
sentTransportMessage = s sentTransportMessage = s
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message # Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep = stepHandshake(rng[], aliceHS, transportMessage = sentTransportMessage, messageNametag = aliceMessageNametag).get() aliceStep = stepHandshake(
rng[],
aliceHS,
transportMessage = sentTransportMessage,
messageNametag = aliceMessageNametag,
)
.get()
############################################### ###############################################
# We prepare a Waku message from Bob's payload2 # We prepare a Waku message from Bob's payload2
@ -247,13 +296,17 @@ procSuite "Waku Noise Sessions":
############################################### ###############################################
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = readPayloadV2, messageNametag = bobMessageNametag).get() bobStep = stepHandshake(
rng[], bobHS, readPayloadV2 = readPayloadV2, messageNametag = bobMessageNametag
)
.get()
check: check:
bobStep.transportMessage == sentTransportMessage bobStep.transportMessage == sentTransportMessage
# Bob further checks if Alice's commitment opens to Alice's static key he just received # Bob further checks if Alice's commitment opens to Alice's static key he just received
let expectedAliceCommittedStaticKey = commitPublicKey(bobHS.rs, bobStep.transportMessage) let expectedAliceCommittedStaticKey =
commitPublicKey(bobHS.rs, bobStep.transportMessage)
check: check:
expectedAliceCommittedStaticKey == aliceCommittedStaticKey expectedAliceCommittedStaticKey == aliceCommittedStaticKey
@ -277,19 +330,36 @@ procSuite "Waku Noise Sessions":
# We test message exchange # We test message exchange
# Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds # Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds
for i in 0 .. 10 * MessageNametagBufferSize: for i in 0 .. 10 * MessageNametagBufferSize:
# Alice writes to Bob # Alice writes to Bob
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound) payload2 = writeMessage(
readMessage = readMessage(bobHSResult, payload2, inboundMessageNametagBuffer = bobHSResult.nametagsInbound).get() aliceHSResult,
message,
outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound,
)
readMessage = readMessage(
bobHSResult,
payload2,
inboundMessageNametagBuffer = bobHSResult.nametagsInbound,
)
.get()
check: check:
message == readMessage message == readMessage
# Bob writes to Alice # Bob writes to Alice
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(bobHSResult, message, outboundMessageNametagBuffer = bobHSResult.nametagsOutbound) payload2 = writeMessage(
readMessage = readMessage(aliceHSResult, payload2, inboundMessageNametagBuffer = aliceHSResult.nametagsInbound).get() bobHSResult,
message,
outboundMessageNametagBuffer = bobHSResult.nametagsOutbound,
)
readMessage = readMessage(
aliceHSResult,
payload2,
inboundMessageNametagBuffer = aliceHSResult.nametagsInbound,
)
.get()
check: check:
message == readMessage message == readMessage
@ -297,25 +367,53 @@ procSuite "Waku Noise Sessions":
# We test how nametag buffers help in detecting lost messages # We test how nametag buffers help in detecting lost messages
# Alice writes two messages to Bob, but only the second is received # Alice writes two messages to Bob, but only the second is received
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound) payload2 = writeMessage(
aliceHSResult,
message,
outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound,
)
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound) payload2 = writeMessage(
aliceHSResult,
message,
outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound,
)
expect NoiseSomeMessagesWereLost: expect NoiseSomeMessagesWereLost:
readMessage = readMessage(bobHSResult, payload2, inboundMessageNametagBuffer = bobHSResult.nametagsInbound).get() readMessage = readMessage(
bobHSResult,
payload2,
inboundMessageNametagBuffer = bobHSResult.nametagsInbound,
)
.get()
# We adjust bob nametag buffer for next test (i.e. the missed message is correctly recovered) # We adjust bob nametag buffer for next test (i.e. the missed message is correctly recovered)
delete(bobHSResult.nametagsInbound, 2) delete(bobHSResult.nametagsInbound, 2)
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(bobHSResult, message, outboundMessageNametagBuffer = bobHSResult.nametagsOutbound) payload2 = writeMessage(
readMessage = readMessage(aliceHSResult, payload2, inboundMessageNametagBuffer = aliceHSResult.nametagsInbound).get() bobHSResult, message, outboundMessageNametagBuffer = bobHSResult.nametagsOutbound
)
readMessage = readMessage(
aliceHSResult,
payload2,
inboundMessageNametagBuffer = aliceHSResult.nametagsInbound,
)
.get()
check: check:
message == readMessage message == readMessage
# We test if a missing nametag is correctly detected # We test if a missing nametag is correctly detected
message = randomSeqByte(rng[], 32) message = randomSeqByte(rng[], 32)
payload2 = writeMessage(aliceHSResult, message, outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound) payload2 = writeMessage(
aliceHSResult,
message,
outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound,
)
delete(bobHSResult.nametagsInbound, 1) delete(bobHSResult.nametagsInbound, 1)
expect NoiseMessageNametagError: expect NoiseMessageNametagError:
readMessage = readMessage(bobHSResult, payload2, inboundMessageNametagBuffer = bobHSResult.nametagsInbound).get() readMessage = readMessage(
bobHSResult,
payload2,
inboundMessageNametagBuffer = bobHSResult.nametagsInbound,
)
.get()

View File

@ -1,25 +1,17 @@
{.used.} {.used.}
import import std/[options, sequtils, tables], testutils/unittests, chronos, chronicles
std/[options, sequtils, tables],
testutils/unittests,
chronos,
chronicles
import import
../../waku/waku_metadata, ../../waku/waku_metadata,
../../waku/waku_metadata/rpc, ../../waku/waku_metadata/rpc,
./testlib/wakucore, ./testlib/wakucore,
./testlib/wakunode ./testlib/wakunode
procSuite "Waku Protobufs": procSuite "Waku Protobufs":
# TODO: Missing test coverage in many encode/decode protobuf functions # TODO: Missing test coverage in many encode/decode protobuf functions
test "WakuMetadataResponse": test "WakuMetadataResponse":
let res = WakuMetadataResponse( let res = WakuMetadataResponse(clusterId: some(7), shards: @[10, 23, 33])
clusterId: some(7),
shards: @[10, 23, 33],
)
let buffer = res.encode() let buffer = res.encode()
@ -30,10 +22,7 @@ procSuite "Waku Protobufs":
decodedBuff.get().shards == res.shards decodedBuff.get().shards == res.shards
test "WakuMetadataRequest": test "WakuMetadataRequest":
let req = WakuMetadataRequest( let req = WakuMetadataRequest(clusterId: some(5), shards: @[100, 2, 0])
clusterId: some(5),
shards: @[100, 2, 0],
)
let buffer = req.encode() let buffer = req.encode()
@ -42,4 +31,3 @@ procSuite "Waku Protobufs":
decodedBuff.isOk() decodedBuff.isOk()
decodedBuff.get().clusterId.get() == req.clusterId.get() decodedBuff.get().clusterId.get() == req.clusterId.get()
decodedBuff.get().shards == req.shards decodedBuff.get().shards == req.shards

View File

@ -1,25 +1,19 @@
{.used.} {.used.}
import import chronos, testutils/unittests, libp2p, libp2p/protocols/rendezvous
chronos,
testutils/unittests,
libp2p,
libp2p/protocols/rendezvous
import import ../../waku/node/waku_switch, ./testlib/common, ./testlib/wakucore
../../waku/node/waku_switch,
./testlib/common,
./testlib/wakucore
proc newRendezvousClientSwitch(rdv: RendezVous): Switch = proc newRendezvousClientSwitch(rdv: RendezVous): Switch =
SwitchBuilder.new() SwitchBuilder
.withRng(rng()) .new()
.withAddresses(@[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]) .withRng(rng())
.withTcpTransport() .withAddresses(@[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()])
.withMplex() .withTcpTransport()
.withNoise() .withMplex()
.withRendezVous(rdv) .withNoise()
.build() .withRendezVous(rdv)
.build()
procSuite "Waku Rendezvous": procSuite "Waku Rendezvous":
asyncTest "Waku Switch uses Rendezvous": asyncTest "Waku Switch uses Rendezvous":
@ -55,5 +49,3 @@ procSuite "Waku Rendezvous":
res1[0] == sourceSwitch.peerInfo.signedPeerRecord.data res1[0] == sourceSwitch.peerInfo.signedPeerRecord.data
await allFutures(wakuSwitch.stop(), sourceSwitch.stop(), destSwitch.stop()) await allFutures(wakuSwitch.stop(), sourceSwitch.stop(), destSwitch.stop())

View File

@ -8,23 +8,20 @@ import
libp2p/protocols/connectivity/relay/relay, libp2p/protocols/connectivity/relay/relay,
libp2p/protocols/connectivity/relay/client, libp2p/protocols/connectivity/relay/client,
stew/byteutils stew/byteutils
import import ../../waku/node/waku_switch, ./testlib/common, ./testlib/wakucore
../../waku/node/waku_switch,
./testlib/common,
./testlib/wakucore
proc newCircuitRelayClientSwitch(relayClient: RelayClient): Switch = proc newCircuitRelayClientSwitch(relayClient: RelayClient): Switch =
SwitchBuilder.new() SwitchBuilder
.withRng(rng()) .new()
.withAddresses(@[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]) .withRng(rng())
.withTcpTransport() .withAddresses(@[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()])
.withMplex() .withTcpTransport()
.withNoise() .withMplex()
.withCircuitRelay(relayClient) .withNoise()
.build() .withCircuitRelay(relayClient)
.build()
suite "Waku Switch": suite "Waku Switch":
asyncTest "Waku Switch works with AutoNat": asyncTest "Waku Switch works with AutoNat":
## Given ## Given
let let
@ -35,7 +32,9 @@ suite "Waku Switch":
## When ## When
await sourceSwitch.connect(wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs) await sourceSwitch.connect(wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs)
let ma = await AutonatClient.new().dialMe(sourceSwitch, wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs) let ma = await AutonatClient.new().dialMe(
sourceSwitch, wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs
)
## Then ## Then
check: check:
@ -62,8 +61,12 @@ suite "Waku Switch":
## Given ## Given
let let
# Create a relay address to destSwitch using wakuSwitch as the relay # Create a relay address to destSwitch using wakuSwitch as the relay
addrs = MultiAddress.init($wakuSwitch.peerInfo.addrs[0] & "/p2p/" & addrs = MultiAddress
$wakuSwitch.peerInfo.peerId & "/p2p-circuit").get() .init(
$wakuSwitch.peerInfo.addrs[0] & "/p2p/" & $wakuSwitch.peerInfo.peerId &
"/p2p-circuit"
)
.get()
msg = "Just one relay away..." msg = "Just one relay away..."
# Create a custom protocol # Create a custom protocol
@ -87,10 +90,12 @@ suite "Waku Switch":
await sourceSwitch.connect(wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs) await sourceSwitch.connect(wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs)
# destClient reserves a slot on the relay. # destClient reserves a slot on the relay.
let rsvp = await destClient.reserve(wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs) let rsvp =
await destClient.reserve(wakuSwitch.peerInfo.peerId, wakuSwitch.peerInfo.addrs)
# sourceSwitch dial destSwitch using the relay # sourceSwitch dial destSwitch using the relay
let conn = await sourceSwitch.dial(destSwitch.peerInfo.peerId, @[addrs], customProtoCodec) let conn =
await sourceSwitch.dial(destSwitch.peerInfo.peerId, @[addrs], customProtoCodec)
await conn.writeLp(msg) await conn.writeLp(msg)

View File

@ -1,7 +1,7 @@
{.used.} {.used.}
import import
std/[sequtils,strutils], std/[sequtils, strutils],
stew/byteutils, stew/byteutils,
stew/shims/net as stewNet, stew/shims/net as stewNet,
testutils/unittests, testutils/unittests,
@ -25,9 +25,7 @@ import
./testlib/wakucore, ./testlib/wakucore,
./testlib/wakunode ./testlib/wakunode
suite "WakuNode": suite "WakuNode":
asyncTest "Protocol matcher works as expected": asyncTest "Protocol matcher works as expected":
let let
nodeKey1 = generateSecp256k1Key() nodeKey1 = generateSecp256k1Key()
@ -53,14 +51,16 @@ suite "WakuNode":
check: check:
# Check that mounted codecs are actually different # Check that mounted codecs are actually different
node1.wakuRelay.codec == "/vac/waku/relay/2.0.0" node1.wakuRelay.codec == "/vac/waku/relay/2.0.0"
node2.wakuRelay.codec == "/vac/waku/relay/2.0.0-beta2" node2.wakuRelay.codec == "/vac/waku/relay/2.0.0-beta2"
# Now verify that protocol matcher returns `true` and relay works # Now verify that protocol matcher returns `true` and relay works
await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()])
var completionFut = newFuture[bool]() var completionFut = newFuture[bool]()
proc relayHandler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} = proc relayHandler(
topic: PubsubTopic, msg: WakuMessage
): Future[void] {.async, gcsafe.} =
check: check:
topic == pubSubTopic topic == pubSubTopic
msg.contentTopic == contentTopic msg.contentTopic == contentTopic
@ -87,7 +87,9 @@ suite "WakuNode":
let let
nodeKey1 = generateSecp256k1Key() nodeKey1 = generateSecp256k1Key()
node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(61020), nameResolver = resolver) node1 = newTestWakuNode(
nodeKey1, parseIpAddress("0.0.0.0"), Port(61020), nameResolver = resolver
)
nodeKey2 = generateSecp256k1Key() nodeKey2 = generateSecp256k1Key()
node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(61022)) node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(61022))
@ -112,14 +114,16 @@ suite "WakuNode":
let let
maxConnections = 2 maxConnections = 2
nodeKey1 = generateSecp256k1Key() nodeKey1 = generateSecp256k1Key()
node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), node1 = newTestWakuNode(
Port(60010), maxConnections = maxConnections) nodeKey1,
parseIpAddress("0.0.0.0"),
Port(60010),
maxConnections = maxConnections,
)
nodeKey2 = generateSecp256k1Key() nodeKey2 = generateSecp256k1Key()
node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(60012))
Port(60012))
nodeKey3 = generateSecp256k1Key() nodeKey3 = generateSecp256k1Key()
node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), Port(60013))
Port(60013))
check: check:
# Sanity check, to verify config was applied # Sanity check, to verify config was applied
@ -137,9 +141,11 @@ suite "WakuNode":
await node3.start() await node3.start()
await node3.mountRelay() await node3.mountRelay()
discard await node1.peerManager.connectRelay(node2.switch.peerInfo.toRemotePeerInfo()) discard
await node1.peerManager.connectRelay(node2.switch.peerInfo.toRemotePeerInfo())
await sleepAsync(3.seconds) await sleepAsync(3.seconds)
discard await node1.peerManager.connectRelay(node3.switch.peerInfo.toRemotePeerInfo()) discard
await node1.peerManager.connectRelay(node3.switch.peerInfo.toRemotePeerInfo())
check: check:
# Verify that only the first connection succeeded # Verify that only the first connection succeeded
@ -153,11 +159,14 @@ suite "WakuNode":
expect ResultDefect: expect ResultDefect:
# gibberish # gibberish
discard newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), discard newTestWakuNode(
nodeKey1,
parseIpAddress("0.0.0.0"),
bindPort = Port(61004), bindPort = Port(61004),
wsBindPort = Port(8000), wsBindPort = Port(8000),
wssEnabled = true, wssEnabled = true,
secureKey = "../../waku/node/key_dummy.txt") secureKey = "../../waku/node/key_dummy.txt",
)
asyncTest "Peer info updates with correct announced addresses": asyncTest "Peer info updates with correct announced addresses":
let let
@ -166,10 +175,7 @@ suite "WakuNode":
bindPort = Port(61006) bindPort = Port(61006)
extIp = some(parseIpAddress("127.0.0.1")) extIp = some(parseIpAddress("127.0.0.1"))
extPort = some(Port(61008)) extPort = some(Port(61008))
node = newTestWakuNode( node = newTestWakuNode(nodeKey, bindIp, bindPort, extIp, extPort)
nodeKey,
bindIp, bindPort,
extIp, extPort)
let let
bindEndpoint = MultiAddress.init(bindIp, tcpProtocol, bindPort) bindEndpoint = MultiAddress.init(bindIp, tcpProtocol, bindPort)
@ -206,12 +212,11 @@ suite "WakuNode":
extIp = some(parseIpAddress("127.0.0.1")) extIp = some(parseIpAddress("127.0.0.1"))
extPort = some(Port(61012)) extPort = some(Port(61012))
domainName = "example.com" domainName = "example.com"
expectedDns4Addr = MultiAddress.init("/dns4/" & domainName & "/tcp/" & $(extPort.get())).get() expectedDns4Addr =
MultiAddress.init("/dns4/" & domainName & "/tcp/" & $(extPort.get())).get()
node = newTestWakuNode( node = newTestWakuNode(
nodeKey, nodeKey, bindIp, bindPort, extIp, extPort, dns4DomainName = some(domainName)
bindIp, bindPort, )
extIp, extPort,
dns4DomainName = some(domainName))
check: check:
node.announcedAddresses.len == 1 node.announcedAddresses.len == 1
@ -224,16 +229,14 @@ suite "WakuNode":
bindPort = Port(0) bindPort = Port(0)
domainName = "status.im" domainName = "status.im"
node = newTestWakuNode( node =
nodeKey, newTestWakuNode(nodeKey, bindIp, bindPort, dns4DomainName = some(domainName))
bindIp, bindPort,
dns4DomainName = some(domainName))
var ipStr = "" var ipStr = ""
var enrIp = node.enr.tryGet("ip", array[4, byte]) var enrIp = node.enr.tryGet("ip", array[4, byte])
if enrIp.isSome(): if enrIp.isSome():
ipStr &= $ipv4(enrIp.get()) ipStr &= $ipv4(enrIp.get())
# Check that the IP filled is the one received by the DNS lookup # Check that the IP filled is the one received by the DNS lookup
# As IPs may change, we check that it's not empty, not the 0 IP and not localhost # As IPs may change, we check that it's not empty, not the 0 IP and not localhost
@ -257,18 +260,15 @@ suite "WakuNode":
# Create node with inexistent domain # Create node with inexistent domain
try: try:
let node = newTestWakuNode( let node = newTestWakuNode(
nodeKey, nodeKey, bindIp, bindPort, dns4DomainName = some(inexistentDomain)
bindIp, bindPort, )
dns4DomainName = some(inexistentDomain))
except Exception as e: except Exception as e:
inexistentDomainErr = e.msg inexistentDomainErr = e.msg
# Create node with invalid domain # Create node with invalid domain
try: try:
let node = newTestWakuNode( let node =
nodeKey, newTestWakuNode(nodeKey, bindIp, bindPort, dns4DomainName = some(invalidDomain))
bindIp, bindPort,
dns4DomainName = some(invalidDomain))
except Exception as e: except Exception as e:
invalidDomainErr = e.msg invalidDomainErr = e.msg
@ -287,8 +287,12 @@ suite "WakuNode":
let let
# node with custom agent string # node with custom agent string
nodeKey1 = generateSecp256k1Key() nodeKey1 = generateSecp256k1Key()
node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(61014), node1 = newTestWakuNode(
agentString = some(expectedAgentString1)) nodeKey1,
parseIpAddress("0.0.0.0"),
Port(61014),
agentString = some(expectedAgentString1),
)
# node with default agent string from libp2p # node with default agent string from libp2p
nodeKey2 = generateSecp256k1Key() nodeKey2 = generateSecp256k1Key()
@ -303,8 +307,10 @@ suite "WakuNode":
await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()])
await node2.connectToNodes(@[node1.switch.peerInfo.toRemotePeerInfo()]) await node2.connectToNodes(@[node1.switch.peerInfo.toRemotePeerInfo()])
let node1Agent = node2.switch.peerStore[AgentBook][node1.switch.peerInfo.toRemotePeerInfo().peerId] let node1Agent =
let node2Agent = node1.switch.peerStore[AgentBook][node2.switch.peerInfo.toRemotePeerInfo().peerId] node2.switch.peerStore[AgentBook][node1.switch.peerInfo.toRemotePeerInfo().peerId]
let node2Agent =
node1.switch.peerStore[AgentBook][node2.switch.peerInfo.toRemotePeerInfo().peerId]
check: check:
node1Agent == expectedAgentString1 node1Agent == expectedAgentString1
@ -322,8 +328,12 @@ suite "WakuNode":
let let
# node with custom multiaddress # node with custom multiaddress
nodeKey1 = generateSecp256k1Key() nodeKey1 = generateSecp256k1Key()
node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(61018), node1 = newTestWakuNode(
extMultiAddrs = @[expectedMultiaddress1]) nodeKey1,
parseIpAddress("0.0.0.0"),
Port(61018),
extMultiAddrs = @[expectedMultiaddress1],
)
# node with default multiaddress # node with default multiaddress
nodeKey2 = generateSecp256k1Key() nodeKey2 = generateSecp256k1Key()
@ -338,7 +348,9 @@ suite "WakuNode":
await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()])
await node2.connectToNodes(@[node1.switch.peerInfo.toRemotePeerInfo()]) await node2.connectToNodes(@[node1.switch.peerInfo.toRemotePeerInfo()])
let node1MultiAddrs = node2.switch.peerStore[AddressBook][node1.switch.peerInfo.toRemotePeerInfo().peerId] let node1MultiAddrs = node2.switch.peerStore[AddressBook][
node1.switch.peerInfo.toRemotePeerInfo().peerId
]
check: check:
node1MultiAddrs.contains(expectedMultiaddress1) node1MultiAddrs.contains(expectedMultiaddress1)

View File

@ -15,9 +15,7 @@ import
./testlib/wakucore, ./testlib/wakucore,
./testlib/wakunode ./testlib/wakunode
suite "WakuNode - Filter": suite "WakuNode - Filter":
asyncTest "subscriber should receive the message handled by the publisher": asyncTest "subscriber should receive the message handled by the publisher":
## Setup ## Setup
let let
@ -38,14 +36,18 @@ suite "WakuNode - Filter":
let let
pubSubTopic = DefaultPubsubTopic pubSubTopic = DefaultPubsubTopic
contentTopic = DefaultContentTopic contentTopic = DefaultContentTopic
message = fakeWakuMessage(contentTopic=contentTopic) message = fakeWakuMessage(contentTopic = contentTopic)
var filterPushHandlerFut = newFuture[(PubsubTopic, WakuMessage)]() var filterPushHandlerFut = newFuture[(PubsubTopic, WakuMessage)]()
proc filterPushHandler(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async, gcsafe, closure.} = proc filterPushHandler(
pubsubTopic: PubsubTopic, msg: WakuMessage
) {.async, gcsafe, closure.} =
filterPushHandlerFut.complete((pubsubTopic, msg)) filterPushHandlerFut.complete((pubsubTopic, msg))
## When ## When
await client.legacyFilterSubscribe(some(pubsubTopic), contentTopic, filterPushHandler, peer=serverPeerInfo) await client.legacyFilterSubscribe(
some(pubsubTopic), contentTopic, filterPushHandler, peer = serverPeerInfo
)
# Wait for subscription to take effect # Wait for subscription to take effect
waitFor sleepAsync(100.millis) waitFor sleepAsync(100.millis)

View File

@ -1,10 +1,6 @@
{.used.} {.used.}
import import std/options, stew/shims/net as stewNet, testutils/unittests, chronos
std/options,
stew/shims/net as stewNet,
testutils/unittests,
chronos
import import
../../waku/waku_core, ../../waku/waku_core,
../../waku/waku_lightpush/common, ../../waku/waku_lightpush/common,
@ -13,7 +9,6 @@ import
./testlib/wakucore, ./testlib/wakucore,
./testlib/wakunode ./testlib/wakunode
suite "WakuNode - Lightpush": suite "WakuNode - Lightpush":
asyncTest "Lightpush message return success": asyncTest "Lightpush message return success":
## Setup ## Setup
@ -32,7 +27,9 @@ suite "WakuNode - Lightpush":
await bridgeNode.mountLightPush() await bridgeNode.mountLightPush()
lightNode.mountLightPushClient() lightNode.mountLightPushClient()
discard await lightNode.peerManager.dialPeer(bridgeNode.peerInfo.toRemotePeerInfo(), WakuLightPushCodec) discard await lightNode.peerManager.dialPeer(
bridgeNode.peerInfo.toRemotePeerInfo(), WakuLightPushCodec
)
await sleepAsync(100.milliseconds) await sleepAsync(100.milliseconds)
await destNode.connectToNodes(@[bridgeNode.peerInfo.toRemotePeerInfo()]) await destNode.connectToNodes(@[bridgeNode.peerInfo.toRemotePeerInfo()])
@ -40,11 +37,14 @@ suite "WakuNode - Lightpush":
let message = fakeWakuMessage() let message = fakeWakuMessage()
var completionFutRelay = newFuture[bool]() var completionFutRelay = newFuture[bool]()
proc relayHandler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} = proc relayHandler(
topic: PubsubTopic, msg: WakuMessage
): Future[void] {.async, gcsafe.} =
check: check:
topic == DefaultPubsubTopic topic == DefaultPubsubTopic
msg == message msg == message
completionFutRelay.complete(true) completionFutRelay.complete(true)
destNode.subscribe((kind: PubsubSub, topic: DefaultPubsubTopic), some(relayHandler)) destNode.subscribe((kind: PubsubSub, topic: DefaultPubsubTopic), some(relayHandler))
# Wait for subscription to take effect # Wait for subscription to take effect

View File

@ -1,8 +1,4 @@
import import std/[times, random], bearssl/rand, libp2p/crypto/crypto
std/[times, random],
bearssl/rand,
libp2p/crypto/crypto
## Randomization ## Randomization
@ -12,12 +8,11 @@ proc randomize*() =
let now = getTime() let now = getTime()
randomize(now.toUnix() * 1_000_000_000 + now.nanosecond) randomize(now.toUnix() * 1_000_000_000 + now.nanosecond)
## RNG ## RNG
# Copied from here: https://github.com/status-im/nim-libp2p/blob/d522537b19a532bc4af94fcd146f779c1f23bad0/tests/helpers.nim#L28 # Copied from here: https://github.com/status-im/nim-libp2p/blob/d522537b19a532bc4af94fcd146f779c1f23bad0/tests/helpers.nim#L28
type Rng = object type Rng = object
rng: ref HmacDrbgContext rng: ref HmacDrbgContext
# Typically having a module variable is considered bad design. This case should # Typically having a module variable is considered bad design. This case should
# be considered as an exception and it should be used only in the tests. # be considered as an exception and it should be used only in the tests.
@ -33,5 +28,5 @@ proc getRng(): ref HmacDrbgContext =
rngVar.rng rngVar.rng
template rng*(): ref HmacDrbgContext =
template rng*(): ref HmacDrbgContext = getRng() getRng()

View File

@ -1,7 +1,4 @@
import chronicles, chronos
import
chronicles,
chronos
import import
../../../waku/waku_archive, ../../../waku/waku_archive,
../../../waku/waku_archive/driver as driver_module, ../../../waku/waku_archive/driver as driver_module,
@ -11,7 +8,6 @@ import
const storeMessageDbUrl = "postgres://postgres:test123@localhost:5432/postgres" const storeMessageDbUrl = "postgres://postgres:test123@localhost:5432/postgres"
proc newTestPostgresDriver*(): Future[Result[ArchiveDriver, string]] {.async.} = proc newTestPostgresDriver*(): Future[Result[ArchiveDriver, string]] {.async.} =
proc onErr(errMsg: string) {.gcsafe, closure.} = proc onErr(errMsg: string) {.gcsafe, closure.} =
error "error creating ArchiveDriver", error = errMsg error "error creating ArchiveDriver", error = errMsg
quit(QuitFailure) quit(QuitFailure)
@ -21,13 +17,9 @@ proc newTestPostgresDriver*(): Future[Result[ArchiveDriver, string]] {.async.} =
migrate = true migrate = true
maxNumConn = 50 maxNumConn = 50
let driverRes = await ArchiveDriver.new(storeMessageDbUrl, let driverRes =
vacuum, await ArchiveDriver.new(storeMessageDbUrl, vacuum, migrate, maxNumConn, onErr)
migrate,
maxNumConn,
onErr)
if driverRes.isErr(): if driverRes.isErr():
onErr("could not create archive driver: " & driverRes.error) onErr("could not create archive driver: " & driverRes.error)
return ok(driverRes.get()) return ok(driverRes.get())

View File

@ -2,10 +2,9 @@
import posix import posix
type type Instr {.union.} = object
Instr {.union.} = object bytes: array[8, byte]
bytes: array[8, byte] value: uint64
value: uint64
proc mockImpl(target, replacement: pointer) = proc mockImpl(target, replacement: pointer) =
# YOLO who needs alignment # YOLO who needs alignment
@ -13,20 +12,18 @@ proc mockImpl(target, replacement: pointer) =
var page = cast[pointer](cast[ByteAddress](target) and (not 0xfff)) var page = cast[pointer](cast[ByteAddress](target) and (not 0xfff))
doAssert mprotect(page, 4096, PROT_WRITE or PROT_EXEC) == 0 doAssert mprotect(page, 4096, PROT_WRITE or PROT_EXEC) == 0
let rel = cast[ByteAddress](replacement) - cast[ByteAddress](target) - 5 let rel = cast[ByteAddress](replacement) - cast[ByteAddress](target) - 5
var var instr = Instr(
instr = bytes: [
Instr( 0xe9.byte,
bytes: [ (rel shr 0).byte,
0xe9.byte, (rel shr 8).byte,
(rel shr 0).byte, (rel shr 16).byte,
(rel shr 8).byte, (rel shr 24).byte,
(rel shr 16).byte, 0,
(rel shr 24).byte, 0,
0, 0,
0, ]
0 )
]
)
cast[ptr uint64](target)[] = instr.value cast[ptr uint64](target)[] = instr.value
doAssert mprotect(page, 4096, PROT_EXEC) == 0 doAssert mprotect(page, 4096, PROT_EXEC) == 0

View File

@ -1,26 +1,19 @@
import import std/[tables, sequtils, options]
std/[
tables,
sequtils,
options
]
import import ../../../waku/waku_core/topics, ../testlib/wakucore
../../../waku/waku_core/topics,
../testlib/wakucore
proc `==`*(
table: Table[pubsub_topic.NsPubsubTopic, seq[NsContentTopic]],
other: array[0 .. 0, (string, seq[string])],
): bool =
proc `==`*(table: Table[pubsub_topic.NsPubsubTopic, seq[NsContentTopic]], other: array[0..0, (string, seq[string])]): bool =
let otherTyped = other.map( let otherTyped = other.map(
proc(item: (string, seq[string])): (NsPubsubTopic, seq[NsContentTopic]) = proc(item: (string, seq[string])): (NsPubsubTopic, seq[NsContentTopic]) =
let let
(pubsubTopic, contentTopics) = item (pubsubTopic, contentTopics) = item
nsPubsubTopic = NsPubsubTopic.parse(pubsubTopic).value() nsPubsubTopic = NsPubsubTopic.parse(pubsubTopic).value()
nsContentTopics = contentTopics.map( nsContentTopics = contentTopics.map(
proc(contentTopic: string): NsContentTopic = NsContentTopic.parse(contentTopic).value() proc(contentTopic: string): NsContentTopic =
NsContentTopic.parse(contentTopic).value()
) )
return (nsPubsubTopic, nsContentTopics) return (nsPubsubTopic, nsContentTopics)
) )

View File

@ -3,14 +3,18 @@
template asyncTeardown*(body: untyped): untyped = template asyncTeardown*(body: untyped): untyped =
teardown: teardown:
waitFor(( waitFor(
proc() {.async, gcsafe.} = (
body proc() {.async, gcsafe.} =
)()) body
)()
)
template asyncSetup*(body: untyped): untyped = template asyncSetup*(body: untyped): untyped =
setup: setup:
waitFor(( waitFor(
proc() {.async, gcsafe.} = (
body proc() {.async, gcsafe.} =
)()) body
)()
)

View File

@ -1,12 +1,16 @@
import import testutils/unittests
testutils/unittests
template xsuite*(name: string, body: untyped) = discard template xsuite*(name: string, body: untyped) =
template suitex*(name: string, body: untyped) = discard discard
template xprocSuite*(name: string, body: untyped) = discard template suitex*(name: string, body: untyped) =
template procSuitex*(name: string, body: untyped) = discard discard
template xprocSuite*(name: string, body: untyped) =
discard
template procSuitex*(name: string, body: untyped) =
discard
template xtest*(name: string, body: untyped) = template xtest*(name: string, body: untyped) =
test name: test name:

View File

@ -7,22 +7,18 @@ import
libp2p/builders, libp2p/builders,
libp2p/crypto/crypto as libp2p_keys, libp2p/crypto/crypto as libp2p_keys,
eth/keys as eth_keys eth/keys as eth_keys
import import ../../../waku/waku_core, ./common
../../../waku/waku_core,
./common
export switch export switch
# Time # Time
proc now*(): Timestamp = proc now*(): Timestamp =
getNanosecondTime(getTime().toUnixFloat()) getNanosecondTime(getTime().toUnixFloat())
proc ts*(offset=0, origin=now()): Timestamp = proc ts*(offset = 0, origin = now()): Timestamp =
origin + getNanosecondTime(int64(offset)) origin + getNanosecondTime(int64(offset))
# Switch # Switch
proc generateEcdsaKey*(): libp2p_keys.PrivateKey = proc generateEcdsaKey*(): libp2p_keys.PrivateKey =
@ -37,26 +33,23 @@ proc generateSecp256k1Key*(): libp2p_keys.PrivateKey =
proc ethSecp256k1Key*(hex: string): eth_keys.PrivateKey = proc ethSecp256k1Key*(hex: string): eth_keys.PrivateKey =
eth_keys.PrivateKey.fromHex(hex).get() eth_keys.PrivateKey.fromHex(hex).get()
proc newTestSwitch*(
proc newTestSwitch*(key=none(libp2p_keys.PrivateKey), address=none(MultiAddress)): Switch = key = none(libp2p_keys.PrivateKey), address = none(MultiAddress)
): Switch =
let peerKey = key.get(generateSecp256k1Key()) let peerKey = key.get(generateSecp256k1Key())
let peerAddr = address.get(MultiAddress.init("/ip4/127.0.0.1/tcp/0").get()) let peerAddr = address.get(MultiAddress.init("/ip4/127.0.0.1/tcp/0").get())
return newStandardSwitch(some(peerKey), addrs=peerAddr) return newStandardSwitch(some(peerKey), addrs = peerAddr)
# Waku message # Waku message
export export waku_core.DefaultPubsubTopic, waku_core.DefaultContentTopic
waku_core.DefaultPubsubTopic,
waku_core.DefaultContentTopic
proc fakeWakuMessage*( proc fakeWakuMessage*(
payload: string|seq[byte] = "TEST-PAYLOAD", payload: string | seq[byte] = "TEST-PAYLOAD",
contentTopic = DefaultContentTopic, contentTopic = DefaultContentTopic,
meta = newSeq[byte](), meta = newSeq[byte](),
ts = now(), ts = now(),
ephemeral = false ephemeral = false,
): WakuMessage = ): WakuMessage =
var payloadBytes: seq[byte] var payloadBytes: seq[byte]
when payload is string: when payload is string:
@ -70,5 +63,5 @@ proc fakeWakuMessage*(
meta: meta, meta: meta,
version: 2, version: 2,
timestamp: ts, timestamp: ts,
ephemeral: ephemeral ephemeral: ephemeral,
) )

View File

@ -18,7 +18,6 @@ import
../../../waku/factory/builder, ../../../waku/factory/builder,
./common ./common
# Waku node # Waku node
proc defaultTestWakuNodeConf*(): WakuNodeConf = proc defaultTestWakuNodeConf*(): WakuNodeConf =
@ -36,38 +35,41 @@ proc defaultTestWakuNodeConf*(): WakuNodeConf =
clusterId: 1.uint32, clusterId: 1.uint32,
topics: @["/waku/2/rs/1/0"], topics: @["/waku/2/rs/1/0"],
relay: true, relay: true,
storeMessageDbUrl: "sqlite://store.sqlite3" storeMessageDbUrl: "sqlite://store.sqlite3",
) )
proc newTestWakuNode*(nodeKey: crypto.PrivateKey, proc newTestWakuNode*(
bindIp: IpAddress, nodeKey: crypto.PrivateKey,
bindPort: Port, bindIp: IpAddress,
extIp = none(IpAddress), bindPort: Port,
extPort = none(Port), extIp = none(IpAddress),
extMultiAddrs = newSeq[MultiAddress](), extPort = none(Port),
peerStorage: PeerStorage = nil, extMultiAddrs = newSeq[MultiAddress](),
maxConnections = builders.MaxConnections, peerStorage: PeerStorage = nil,
wsBindPort: Port = (Port)8000, maxConnections = builders.MaxConnections,
wsEnabled: bool = false, wsBindPort: Port = (Port) 8000,
wssEnabled: bool = false, wsEnabled: bool = false,
secureKey: string = "", wssEnabled: bool = false,
secureCert: string = "", secureKey: string = "",
wakuFlags = none(CapabilitiesBitfield), secureCert: string = "",
nameResolver: NameResolver = nil, wakuFlags = none(CapabilitiesBitfield),
sendSignedPeerRecord = false, nameResolver: NameResolver = nil,
dns4DomainName = none(string), sendSignedPeerRecord = false,
discv5UdpPort = none(Port), dns4DomainName = none(string),
agentString = none(string), discv5UdpPort = none(Port),
clusterId: uint32 = 1.uint32, agentString = none(string),
topics: seq[string] = @["/waku/2/rs/1/0"], clusterId: uint32 = 1.uint32,
peerStoreCapacity = none(int)): WakuNode = topics: seq[string] = @["/waku/2/rs/1/0"],
peerStoreCapacity = none(int),
): WakuNode =
var resolvedExtIp = extIp var resolvedExtIp = extIp
# Update extPort to default value if it's missing and there's an extIp or a DNS domain # Update extPort to default value if it's missing and there's an extIp or a DNS domain
let extPort = let extPort =
if (extIp.isSome() or dns4DomainName.isSome()) and extPort.isNone(): some(Port(60000)) if (extIp.isSome() or dns4DomainName.isSome()) and extPort.isNone():
else: extPort some(Port(60000))
else:
extPort
var conf = defaultTestWakuNodeConf() var conf = defaultTestWakuNodeConf()
@ -103,9 +105,7 @@ proc newTestWakuNode*(nodeKey: crypto.PrivateKey,
raise newException(Defect, "Invalid record: " & error) raise newException(Defect, "Invalid record: " & error)
enrBuilder.withIpAddressAndPorts( enrBuilder.withIpAddressAndPorts(
ipAddr = netConf.enrIp, ipAddr = netConf.enrIp, tcpPort = netConf.enrPort, udpPort = netConf.discv5UdpPort
tcpPort = netConf.enrPort,
udpPort = netConf.discv5UdpPort,
) )
enrBuilder.withMultiaddrs(netConf.enrMultiaddrs) enrBuilder.withMultiaddrs(netConf.enrMultiaddrs)
@ -114,7 +114,7 @@ proc newTestWakuNode*(nodeKey: crypto.PrivateKey,
enrBuilder.withWakuCapabilities(netConf.wakuFlags.get()) enrBuilder.withWakuCapabilities(netConf.wakuFlags.get())
let record = enrBuilder.build().valueOr: let record = enrBuilder.build().valueOr:
raise newException(Defect, "Invalid record: " & $error) raise newException(Defect, "Invalid record: " & $error)
var builder = WakuNodeBuilder.init() var builder = WakuNodeBuilder.init()
builder.withRng(rng()) builder.withRng(rng())
@ -126,10 +126,19 @@ proc newTestWakuNode*(nodeKey: crypto.PrivateKey,
maxConnections = some(maxConnections), maxConnections = some(maxConnections),
nameResolver = nameResolver, nameResolver = nameResolver,
sendSignedPeerRecord = sendSignedPeerRecord, sendSignedPeerRecord = sendSignedPeerRecord,
secureKey = if secureKey != "": some(secureKey) else: none(string), secureKey =
secureCert = if secureCert != "": some(secureCert) else: none(string), if secureKey != "":
some(secureKey)
else:
none(string)
,
secureCert =
if secureCert != "":
some(secureCert)
else:
none(string)
,
agentString = agentString, agentString = agentString,
) )
return builder.build().get() return builder.build().get()

View File

@ -9,7 +9,7 @@ import
waku_archive, waku_archive,
waku_archive/common, waku_archive/common,
waku_archive/driver/sqlite_driver, waku_archive/driver/sqlite_driver,
common/databases/db_sqlite common/databases/db_sqlite,
], ],
../testlib/[wakucore] ../testlib/[wakucore]
@ -42,7 +42,7 @@ proc put*(
msgDigest = computeDigest(msg) msgDigest = computeDigest(msg)
msgHash = computeMessageHash(pubsubTopic, msg) msgHash = computeMessageHash(pubsubTopic, msg)
_ = waitFor driver.put(pubsubTopic, msg, msgDigest, msgHash, msg.timestamp) _ = waitFor driver.put(pubsubTopic, msg, msgDigest, msgHash, msg.timestamp)
# discard crashes # discard crashes
return driver return driver
proc newArchiveDriverWithMessages*( proc newArchiveDriverWithMessages*(

View File

@ -1,9 +1,6 @@
{.used.} {.used.}
import import std/[sequtils, options], testutils/unittests, chronos
std/[sequtils,options],
testutils/unittests,
chronos
import import
../../../waku/waku_archive, ../../../waku/waku_archive,
../../../waku/waku_archive/driver/postgres_driver, ../../../waku/waku_archive/driver/postgres_driver,
@ -13,9 +10,7 @@ import
../testlib/testasync, ../testlib/testasync,
../testlib/postgres ../testlib/postgres
proc computeTestCursor(pubsubTopic: PubsubTopic, proc computeTestCursor(pubsubTopic: PubsubTopic, message: WakuMessage): ArchiveCursor =
message: WakuMessage):
ArchiveCursor =
ArchiveCursor( ArchiveCursor(
pubsubTopic: pubsubTopic, pubsubTopic: pubsubTopic,
senderTime: message.timestamp, senderTime: message.timestamp,
@ -60,12 +55,14 @@ suite "Postgres driver":
asyncTest "Insert a message": asyncTest "Insert a message":
const contentTopic = "test-content-topic" const contentTopic = "test-content-topic"
let msg = fakeWakuMessage(contentTopic=contentTopic) let msg = fakeWakuMessage(contentTopic = contentTopic)
let computedDigest = computeDigest(msg) let computedDigest = computeDigest(msg)
let computedHash = computeMessageHash(DefaultPubsubTopic, msg) let computedHash = computeMessageHash(DefaultPubsubTopic, msg)
let putRes = await driver.put(DefaultPubsubTopic, msg, computedDigest, computedHash, msg.timestamp) let putRes = await driver.put(
DefaultPubsubTopic, msg, computedDigest, computedHash, msg.timestamp
)
assert putRes.isOk(), putRes.error assert putRes.isOk(), putRes.error
let storedMsg = (await driver.getAllMessages()).tryGet() let storedMsg = (await driver.getAllMessages()).tryGet()
@ -85,14 +82,26 @@ suite "Postgres driver":
const pubsubTopic1 = "pubsubtopic-1" const pubsubTopic1 = "pubsubtopic-1"
const pubsubTopic2 = "pubsubtopic-2" const pubsubTopic2 = "pubsubtopic-2"
let msg1 = fakeWakuMessage(contentTopic=contentTopic1) let msg1 = fakeWakuMessage(contentTopic = contentTopic1)
var putRes = await driver.put(pubsubTopic1, msg1, computeDigest(msg1), computeMessageHash(pubsubTopic1, msg1), msg1.timestamp) var putRes = await driver.put(
pubsubTopic1,
msg1,
computeDigest(msg1),
computeMessageHash(pubsubTopic1, msg1),
msg1.timestamp,
)
assert putRes.isOk(), putRes.error assert putRes.isOk(), putRes.error
let msg2 = fakeWakuMessage(contentTopic=contentTopic2) let msg2 = fakeWakuMessage(contentTopic = contentTopic2)
putRes = await driver.put(pubsubTopic2, msg2, computeDigest(msg2), computeMessageHash(pubsubTopic2, msg2), msg2.timestamp) putRes = await driver.put(
pubsubTopic2,
msg2,
computeDigest(msg2),
computeMessageHash(pubsubTopic2, msg2),
msg2.timestamp,
)
assert putRes.isOk(), putRes.error assert putRes.isOk(), putRes.error
let countMessagesRes = await driver.getMessagesCount() let countMessagesRes = await driver.getMessagesCount()
@ -106,17 +115,17 @@ suite "Postgres driver":
assert messagesRes.get().len == 1 assert messagesRes.get().len == 1
# Get both content topics, check ordering # Get both content topics, check ordering
messagesRes = await driver.getMessages(contentTopic = @[contentTopic1, messagesRes =
contentTopic2]) await driver.getMessages(contentTopic = @[contentTopic1, contentTopic2])
assert messagesRes.isOk(), messagesRes.error assert messagesRes.isOk(), messagesRes.error
assert messagesRes.get().len == 2 assert messagesRes.get().len == 2
assert messagesRes.get()[0][1].contentTopic == contentTopic1 assert messagesRes.get()[0][1].contentTopic == contentTopic1
# Descending order # Descending order
messagesRes = await driver.getMessages(contentTopic = @[contentTopic1, messagesRes = await driver.getMessages(
contentTopic2], contentTopic = @[contentTopic1, contentTopic2], ascendingOrder = false
ascendingOrder = false) )
assert messagesRes.isOk(), messagesRes.error assert messagesRes.isOk(), messagesRes.error
assert messagesRes.get().len == 2 assert messagesRes.get().len == 2
@ -124,28 +133,26 @@ suite "Postgres driver":
# cursor # cursor
# Get both content topics # Get both content topics
messagesRes = messagesRes = await driver.getMessages(
await driver.getMessages(contentTopic = @[contentTopic1, contentTopic = @[contentTopic1, contentTopic2],
contentTopic2], cursor = some(computeTestCursor(pubsubTopic1, messagesRes.get()[1][1])),
cursor = some( )
computeTestCursor(pubsubTopic1,
messagesRes.get()[1][1])))
assert messagesRes.isOk() assert messagesRes.isOk()
assert messagesRes.get().len == 1 assert messagesRes.get().len == 1
# Get both content topics but one pubsub topic # Get both content topics but one pubsub topic
messagesRes = await driver.getMessages(contentTopic = @[contentTopic1, messagesRes = await driver.getMessages(
contentTopic2], contentTopic = @[contentTopic1, contentTopic2], pubsubTopic = some(pubsubTopic1)
pubsubTopic = some(pubsubTopic1)) )
assert messagesRes.isOk(), messagesRes.error assert messagesRes.isOk(), messagesRes.error
assert messagesRes.get().len == 1 assert messagesRes.get().len == 1
assert messagesRes.get()[0][1].contentTopic == contentTopic1 assert messagesRes.get()[0][1].contentTopic == contentTopic1
# Limit # Limit
messagesRes = await driver.getMessages(contentTopic = @[contentTopic1, messagesRes = await driver.getMessages(
contentTopic2], contentTopic = @[contentTopic1, contentTopic2], maxPageSize = 1
maxPageSize = 1) )
assert messagesRes.isOk(), messagesRes.error assert messagesRes.isOk(), messagesRes.error
assert messagesRes.get().len == 1 assert messagesRes.get().len == 1
@ -157,11 +164,20 @@ suite "Postgres driver":
let msg1 = fakeWakuMessage(ts = now) let msg1 = fakeWakuMessage(ts = now)
let msg2 = fakeWakuMessage(ts = now) let msg2 = fakeWakuMessage(ts = now)
var putRes = await driver.put(DefaultPubsubTopic, var putRes = await driver.put(
msg1, computeDigest(msg1), computeMessageHash(DefaultPubsubTopic, msg1), msg1.timestamp) DefaultPubsubTopic,
msg1,
computeDigest(msg1),
computeMessageHash(DefaultPubsubTopic, msg1),
msg1.timestamp,
)
assert putRes.isOk(), putRes.error assert putRes.isOk(), putRes.error
putRes = await driver.put(DefaultPubsubTopic, putRes = await driver.put(
msg2, computeDigest(msg2), computeMessageHash(DefaultPubsubTopic, msg2), msg2.timestamp) DefaultPubsubTopic,
msg2,
computeDigest(msg2),
computeMessageHash(DefaultPubsubTopic, msg2),
msg2.timestamp,
)
assert not putRes.isOk() assert not putRes.isOk()

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,19 @@
{.used.} {.used.}
import import std/options, stew/results, testutils/unittests
std/options,
stew/results,
testutils/unittests
import import
../../../waku/waku_archive, ../../../waku/waku_archive,
../../../waku/waku_archive/driver/queue_driver/queue_driver {.all.}, ../../../waku/waku_archive/driver/queue_driver/queue_driver {.all.},
../../../waku/waku_archive/driver/queue_driver/index, ../../../waku/waku_archive/driver/queue_driver/index,
../../../waku/waku_core ../../../waku/waku_core
# Helper functions # Helper functions
proc genIndexedWakuMessage(i: int8): (Index, WakuMessage) = proc genIndexedWakuMessage(i: int8): (Index, WakuMessage) =
## Use i to generate an Index WakuMessage ## Use i to generate an Index WakuMessage
var data {.noinit.}: array[32, byte] var data {.noinit.}: array[32, byte]
for x in data.mitems: x = i.byte for x in data.mitems:
x = i.byte
let let
message = WakuMessage(payload: @[byte i], timestamp: Timestamp(i)) message = WakuMessage(payload: @[byte i], timestamp: Timestamp(i))
@ -24,7 +21,7 @@ proc genIndexedWakuMessage(i: int8): (Index, WakuMessage) =
receiverTime: Timestamp(i), receiverTime: Timestamp(i),
senderTime: Timestamp(i), senderTime: Timestamp(i),
digest: MessageDigest(data: data), digest: MessageDigest(data: data),
pubsubTopic: "test-pubsub-topic" pubsubTopic: "test-pubsub-topic",
) )
(cursor, message) (cursor, message)
@ -38,9 +35,7 @@ proc getPrepopulatedTestQueue(unsortedSet: auto, capacity: int): QueueDriver =
driver driver
procSuite "Sorted driver queue": procSuite "Sorted driver queue":
test "queue capacity - add a message over the limit": test "queue capacity - add a message over the limit":
## Given ## Given
let capacity = 5 let capacity = 5
@ -48,7 +43,7 @@ procSuite "Sorted driver queue":
## When ## When
# Fill up the queue # Fill up the queue
for i in 1..capacity: for i in 1 .. capacity:
let (index, message) = genIndexedWakuMessage(i.int8) let (index, message) = genIndexedWakuMessage(i.int8)
require(driver.add(index, message).isOk()) require(driver.add(index, message).isOk())
@ -67,7 +62,7 @@ procSuite "Sorted driver queue":
## When ## When
# Fill up the queue # Fill up the queue
for i in 1..capacity: for i in 1 .. capacity:
let (index, message) = genIndexedWakuMessage(i.int8) let (index, message) = genIndexedWakuMessage(i.int8)
require(driver.add(index, message).isOk()) require(driver.add(index, message).isOk())
@ -89,7 +84,7 @@ procSuite "Sorted driver queue":
## Given ## Given
let let
capacity = 5 capacity = 5
unsortedSet = [5,1,3,2,4] unsortedSet = [5, 1, 3, 2, 4]
let driver = getPrepopulatedTestQueue(unsortedSet, capacity) let driver = getPrepopulatedTestQueue(unsortedSet, capacity)
# Walk forward through the set and verify ascending order # Walk forward through the set and verify ascending order
@ -110,7 +105,7 @@ procSuite "Sorted driver queue":
## Given ## Given
let let
capacity = 5 capacity = 5
unsortedSet = [5,1,3,2,4] unsortedSet = [5, 1, 3, 2, 4]
let driver = getPrepopulatedTestQueue(unsortedSet, capacity) let driver = getPrepopulatedTestQueue(unsortedSet, capacity)
## When ## When
@ -141,7 +136,7 @@ procSuite "Sorted driver queue":
## Given ## Given
let let
capacity = 5 capacity = 5
unsortedSet = [5,1,3,2,4] unsortedSet = [5, 1, 3, 2, 4]
let driver = getPrepopulatedTestQueue(unsortedSet, capacity) let driver = getPrepopulatedTestQueue(unsortedSet, capacity)
## When ## When
@ -172,7 +167,7 @@ procSuite "Sorted driver queue":
## Given ## Given
let let
capacity = 5 capacity = 5
unsortedSet = [5,1,3,2,4] unsortedSet = [5, 1, 3, 2, 4]
let driver = getPrepopulatedTestQueue(unsortedSet, capacity) let driver = getPrepopulatedTestQueue(unsortedSet, capacity)
let let

View File

@ -1,18 +1,11 @@
{.used.} {.used.}
import import std/times, stew/byteutils, testutils/unittests, nimcrypto
std/times, import ../../../waku/waku_core, ../../../waku/waku_archive/driver/queue_driver/index
stew/byteutils,
testutils/unittests,
nimcrypto
import
../../../waku/waku_core,
../../../waku/waku_archive/driver/queue_driver/index
## Helpers ## Helpers
proc getTestTimestamp(offset=0): Timestamp = proc getTestTimestamp(offset = 0): Timestamp =
let now = getNanosecondTime(epochTime() + float(offset)) let now = getNanosecondTime(epochTime() + float(offset))
Timestamp(now) Timestamp(now)
@ -26,52 +19,75 @@ proc hashFromStr(input: string): MDigest[256] =
return hashed return hashed
suite "Queue Driver - index": suite "Queue Driver - index":
## Test vars ## Test vars
let let
smallIndex1 = Index(digest: hashFromStr("1234"), smallIndex1 = Index(
receiverTime: getNanosecondTime(0), digest: hashFromStr("1234"),
senderTime: getNanosecondTime(1000)) receiverTime: getNanosecondTime(0),
smallIndex2 = Index(digest: hashFromStr("1234567"), # digest is less significant than senderTime senderTime: getNanosecondTime(1000),
receiverTime: getNanosecondTime(0), )
senderTime: getNanosecondTime(1000)) smallIndex2 = Index(
largeIndex1 = Index(digest: hashFromStr("1234"), digest: hashFromStr("1234567"), # digest is less significant than senderTime
receiverTime: getNanosecondTime(0), receiverTime: getNanosecondTime(0),
senderTime: getNanosecondTime(9000)) # only senderTime differ from smallIndex1 senderTime: getNanosecondTime(1000),
largeIndex2 = Index(digest: hashFromStr("12345"), # only digest differs from smallIndex1 )
receiverTime: getNanosecondTime(0), largeIndex1 = Index(
senderTime: getNanosecondTime(1000)) digest: hashFromStr("1234"),
eqIndex1 = Index(digest: hashFromStr("0003"), receiverTime: getNanosecondTime(0),
receiverTime: getNanosecondTime(0), senderTime: getNanosecondTime(9000),
senderTime: getNanosecondTime(54321)) ) # only senderTime differ from smallIndex1
eqIndex2 = Index(digest: hashFromStr("0003"), largeIndex2 = Index(
receiverTime: getNanosecondTime(0), digest: hashFromStr("12345"), # only digest differs from smallIndex1
senderTime: getNanosecondTime(54321)) receiverTime: getNanosecondTime(0),
eqIndex3 = Index(digest: hashFromStr("0003"), senderTime: getNanosecondTime(1000),
receiverTime: getNanosecondTime(9999), # receiverTime difference should have no effect on comparisons )
senderTime: getNanosecondTime(54321)) eqIndex1 = Index(
diffPsTopic = Index(digest: hashFromStr("1234"), digest: hashFromStr("0003"),
receiverTime: getNanosecondTime(0), receiverTime: getNanosecondTime(0),
senderTime: getNanosecondTime(1000), senderTime: getNanosecondTime(54321),
pubsubTopic: "zzzz") )
noSenderTime1 = Index(digest: hashFromStr("1234"), eqIndex2 = Index(
receiverTime: getNanosecondTime(1100), digest: hashFromStr("0003"),
senderTime: getNanosecondTime(0), receiverTime: getNanosecondTime(0),
pubsubTopic: "zzzz") senderTime: getNanosecondTime(54321),
noSenderTime2 = Index(digest: hashFromStr("1234"), )
receiverTime: getNanosecondTime(10000), eqIndex3 = Index(
senderTime: getNanosecondTime(0), digest: hashFromStr("0003"),
pubsubTopic: "zzzz") receiverTime: getNanosecondTime(9999),
noSenderTime3 = Index(digest: hashFromStr("1234"), # receiverTime difference should have no effect on comparisons
receiverTime: getNanosecondTime(1200), senderTime: getNanosecondTime(54321),
senderTime: getNanosecondTime(0), )
pubsubTopic: "aaaa") diffPsTopic = Index(
noSenderTime4 = Index(digest: hashFromStr("0"), digest: hashFromStr("1234"),
receiverTime: getNanosecondTime(1200), receiverTime: getNanosecondTime(0),
senderTime: getNanosecondTime(0), senderTime: getNanosecondTime(1000),
pubsubTopic: "zzzz") pubsubTopic: "zzzz",
)
noSenderTime1 = Index(
digest: hashFromStr("1234"),
receiverTime: getNanosecondTime(1100),
senderTime: getNanosecondTime(0),
pubsubTopic: "zzzz",
)
noSenderTime2 = Index(
digest: hashFromStr("1234"),
receiverTime: getNanosecondTime(10000),
senderTime: getNanosecondTime(0),
pubsubTopic: "zzzz",
)
noSenderTime3 = Index(
digest: hashFromStr("1234"),
receiverTime: getNanosecondTime(1200),
senderTime: getNanosecondTime(0),
pubsubTopic: "aaaa",
)
noSenderTime4 = Index(
digest: hashFromStr("0"),
receiverTime: getNanosecondTime(1200),
senderTime: getNanosecondTime(0),
pubsubTopic: "zzzz",
)
test "Index comparison": test "Index comparison":
# Index comparison with senderTime diff # Index comparison with senderTime diff
@ -125,9 +141,9 @@ suite "Queue Driver - index":
# Receiver time plays no role, even without sender time # Receiver time plays no role, even without sender time
check: check:
eqIndex1 == eqIndex3 eqIndex1 == eqIndex3
noSenderTime1 == noSenderTime2 # only receiver time differs, indices are equal noSenderTime1 == noSenderTime2 # only receiver time differs, indices are equal
noSenderTime1 != noSenderTime3 # pubsubTopics differ noSenderTime1 != noSenderTime3 # pubsubTopics differ
noSenderTime1 != noSenderTime4 # digests differ noSenderTime1 != noSenderTime4 # digests differ
# Unequal sender time # Unequal sender time
check: check:

View File

@ -1,9 +1,7 @@
{.used.} {.used.}
import import
std/[options, sequtils, algorithm], std/[options, sequtils, algorithm], testutils/unittests, libp2p/protobuf/minprotobuf
testutils/unittests,
libp2p/protobuf/minprotobuf
import import
../../../waku/waku_archive, ../../../waku/waku_archive,
../../../waku/waku_archive/driver/queue_driver/queue_driver {.all.}, ../../../waku/waku_archive/driver/queue_driver/queue_driver {.all.},
@ -12,28 +10,26 @@ import
../testlib/common, ../testlib/common,
../testlib/wakucore ../testlib/wakucore
proc getTestQueueDriver(numMessages: int): QueueDriver = proc getTestQueueDriver(numMessages: int): QueueDriver =
let testQueueDriver = QueueDriver.new(numMessages) let testQueueDriver = QueueDriver.new(numMessages)
var data {.noinit.}: array[32, byte] var data {.noinit.}: array[32, byte]
for x in data.mitems: x = 1 for x in data.mitems:
x = 1
for i in 0..<numMessages:
for i in 0 ..< numMessages:
let msg = WakuMessage(payload: @[byte i], timestamp: Timestamp(i)) let msg = WakuMessage(payload: @[byte i], timestamp: Timestamp(i))
let index = Index( let index = Index(
receiverTime: Timestamp(i), receiverTime: Timestamp(i),
senderTime: Timestamp(i), senderTime: Timestamp(i),
digest: MessageDigest(data: data) digest: MessageDigest(data: data),
) )
discard testQueueDriver.add(index, msg) discard testQueueDriver.add(index, msg)
return testQueueDriver return testQueueDriver
procSuite "Queue driver - pagination": procSuite "Queue driver - pagination":
let driver = getTestQueueDriver(10) let driver = getTestQueueDriver(10)
let let
@ -48,13 +44,13 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.len == 2 data.len == 2
data == msgList[4..5] data == msgList[4 .. 5]
test "Forward pagination - initial pagination request with an empty cursor": test "Forward pagination - initial pagination request with an empty cursor":
## Given ## Given
@ -64,13 +60,13 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.len == 2 data.len == 2
data == msgList[0..1] data == msgList[0 .. 1]
test "Forward pagination - initial pagination request with an empty cursor to fetch the entire history": test "Forward pagination - initial pagination request with an empty cursor to fetch the entire history":
## Given ## Given
@ -80,13 +76,13 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.len == 10 data.len == 10
data == msgList[0..9] data == msgList[0 .. 9]
test "Forward pagination - empty msgList": test "Forward pagination - empty msgList":
## Given ## Given
@ -97,7 +93,7 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -112,13 +108,13 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.len == 6 data.len == 6
data == msgList[4..9] data == msgList[4 .. 9]
test "Forward pagination - page size larger than the maximum allowed page size": test "Forward pagination - page size larger than the maximum allowed page size":
## Given ## Given
@ -128,7 +124,7 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -143,7 +139,7 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -152,12 +148,12 @@ procSuite "Queue driver - pagination":
test "Forward pagination - invalid cursor": test "Forward pagination - invalid cursor":
## Given ## Given
let msg = fakeWakuMessage(payload= @[byte 10]) let msg = fakeWakuMessage(payload = @[byte 10])
let index = ArchiveCursor( let index = ArchiveCursor(
pubsubTopic: DefaultPubsubTopic, pubsubTopic: DefaultPubsubTopic,
senderTime: msg.timestamp, senderTime: msg.timestamp,
storeTime: msg.timestamp, storeTime: msg.timestamp,
digest: computeDigest(msg) digest: computeDigest(msg),
).toIndex() ).toIndex()
let let
@ -166,7 +162,7 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let error = page.tryError() let error = page.tryError()
@ -182,7 +178,7 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -198,7 +194,7 @@ procSuite "Queue driver - pagination":
forward: bool = true forward: bool = true
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -212,17 +208,19 @@ procSuite "Queue driver - pagination":
cursor: Option[Index] = none(Index) cursor: Option[Index] = none(Index)
forward = true forward = true
proc onlyEvenTimes(index: Index, msg: WakuMessage): bool = msg.timestamp.int64 mod 2 == 0 proc onlyEvenTimes(index: Index, msg: WakuMessage): bool =
msg.timestamp.int64 mod 2 == 0
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor, predicate=onlyEvenTimes) let page = driver.getPage(
pageSize = pageSize, forward = forward, cursor = cursor, predicate = onlyEvenTimes
)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.mapIt(it.timestamp.int) == @[0, 2, 4] data.mapIt(it.timestamp.int) == @[0, 2, 4]
test "Backward pagination - normal pagination": test "Backward pagination - normal pagination":
## Given ## Given
let let
@ -231,12 +229,12 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data == msgList[1..2].reversed data == msgList[1 .. 2].reversed
test "Backward pagination - empty msgList": test "Backward pagination - empty msgList":
## Given ## Given
@ -247,7 +245,7 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -262,13 +260,13 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.len == 2 data.len == 2
data == msgList[8..9].reversed data == msgList[8 .. 9].reversed
test "Backward pagination - initial pagination request with an empty cursor to fetch the entire history": test "Backward pagination - initial pagination request with an empty cursor to fetch the entire history":
## Given ## Given
@ -278,13 +276,13 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.len == 10 data.len == 10
data == msgList[0..9].reversed data == msgList[0 .. 9].reversed
test "Backward pagination - page size larger than the remaining messages": test "Backward pagination - page size larger than the remaining messages":
## Given ## Given
@ -294,12 +292,12 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data == msgList[0..2].reversed data == msgList[0 .. 2].reversed
test "Backward pagination - page size larger than the Maximum allowed page size": test "Backward pagination - page size larger than the Maximum allowed page size":
## Given ## Given
@ -309,7 +307,7 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -324,7 +322,7 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -333,12 +331,12 @@ procSuite "Queue driver - pagination":
test "Backward pagination - invalid cursor": test "Backward pagination - invalid cursor":
## Given ## Given
let msg = fakeWakuMessage(payload= @[byte 10]) let msg = fakeWakuMessage(payload = @[byte 10])
let index = ArchiveCursor( let index = ArchiveCursor(
pubsubTopic: DefaultPubsubTopic, pubsubTopic: DefaultPubsubTopic,
senderTime: msg.timestamp, senderTime: msg.timestamp,
storeTime: msg.timestamp, storeTime: msg.timestamp,
digest: computeDigest(msg) digest: computeDigest(msg),
).toIndex() ).toIndex()
let let
@ -347,7 +345,7 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let error = page.tryError() let error = page.tryError()
@ -363,7 +361,7 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -379,7 +377,7 @@ procSuite "Queue driver - pagination":
forward: bool = false forward: bool = false
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor) let page = driver.getPage(pageSize = pageSize, forward = forward, cursor = cursor)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
@ -393,12 +391,15 @@ procSuite "Queue driver - pagination":
cursor: Option[Index] = none(Index) cursor: Option[Index] = none(Index)
forward = false forward = false
proc onlyOddTimes(index: Index, msg: WakuMessage): bool = msg.timestamp.int64 mod 2 != 0 proc onlyOddTimes(index: Index, msg: WakuMessage): bool =
msg.timestamp.int64 mod 2 != 0
## When ## When
let page = driver.getPage(pageSize=pageSize, forward=forward, cursor=cursor, predicate=onlyOddTimes) let page = driver.getPage(
pageSize = pageSize, forward = forward, cursor = cursor, predicate = onlyOddTimes
)
## Then ## Then
let data = page.tryGet().mapIt(it[1]) let data = page.tryGet().mapIt(it[1])
check: check:
data.mapIt(it.timestamp.int) == @[5, 7,9].reversed data.mapIt(it.timestamp.int) == @[5, 7, 9].reversed

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,6 @@
{.used.} {.used.}
import import std/sequtils, testutils/unittests, chronos
std/sequtils,
testutils/unittests,
chronos
import import
../../../waku/common/databases/db_sqlite, ../../../waku/common/databases/db_sqlite,
../../../waku/waku_archive, ../../../waku/waku_archive,
@ -13,9 +10,7 @@ import
../testlib/common, ../testlib/common,
../testlib/wakucore ../testlib/wakucore
suite "SQLite driver": suite "SQLite driver":
test "init driver and database": test "init driver and database":
## Given ## Given
let database = newSqliteDatabase() let database = newSqliteDatabase()
@ -40,11 +35,13 @@ suite "SQLite driver":
let driver = newSqliteArchiveDriver() let driver = newSqliteArchiveDriver()
let msg = fakeWakuMessage(contentTopic=contentTopic) let msg = fakeWakuMessage(contentTopic = contentTopic)
let msgHash = computeMessageHash(DefaultPubsubTopic, msg) let msgHash = computeMessageHash(DefaultPubsubTopic, msg)
## When ## When
let putRes = waitFor driver.put(DefaultPubsubTopic, msg, computeDigest(msg), msgHash, msg.timestamp) let putRes = waitFor driver.put(
DefaultPubsubTopic, msg, computeDigest(msg), msgHash, msg.timestamp
)
## Then ## Then
check: check:
@ -53,11 +50,10 @@ suite "SQLite driver":
let storedMsg = (waitFor driver.getAllMessages()).tryGet() let storedMsg = (waitFor driver.getAllMessages()).tryGet()
check: check:
storedMsg.len == 1 storedMsg.len == 1
storedMsg.all do (item: auto) -> bool: storedMsg.all do(item: auto) -> bool:
let (pubsubTopic, msg, _, _, hash) = item let (pubsubTopic, msg, _, _, hash) = item
msg.contentTopic == contentTopic and msg.contentTopic == contentTopic and pubsubTopic == DefaultPubsubTopic and
pubsubTopic == DefaultPubsubTopic and hash == msgHash
hash == msgHash
## Cleanup ## Cleanup
(waitFor driver.close()).expect("driver to close") (waitFor driver.close()).expect("driver to close")

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,6 @@
{.used.} {.used.}
import import std/[sequtils, times], stew/results, testutils/unittests, chronos
std/[sequtils,times],
stew/results,
testutils/unittests,
chronos
import import
../../../waku/common/databases/db_sqlite, ../../../waku/common/databases/db_sqlite,
../../../waku/waku_core, ../../../waku/waku_core,
@ -18,9 +14,7 @@ import
../testlib/common, ../testlib/common,
../testlib/wakucore ../testlib/wakucore
suite "Waku Archive - Retention policy": suite "Waku Archive - Retention policy":
test "capacity retention policy - windowed message deletion": test "capacity retention policy - windowed message deletion":
## Given ## Given
let let
@ -29,13 +23,24 @@ suite "Waku Archive - Retention policy":
let driver = newSqliteArchiveDriver() let driver = newSqliteArchiveDriver()
let retentionPolicy: RetentionPolicy = CapacityRetentionPolicy.new(capacity=capacity) let retentionPolicy: RetentionPolicy =
CapacityRetentionPolicy.new(capacity = capacity)
var putFutures = newSeq[Future[ArchiveDriverResult[void]]]() var putFutures = newSeq[Future[ArchiveDriverResult[void]]]()
## When ## When
for i in 1..capacity+excess: for i in 1 .. capacity + excess:
let msg = fakeWakuMessage(payload= @[byte i], contentTopic=DefaultContentTopic, ts=Timestamp(i)) let msg = fakeWakuMessage(
putFutures.add(driver.put(DefaultPubsubTopic, msg, computeDigest(msg), computeMessageHash(DefaultPubsubTopic, msg), msg.timestamp)) payload = @[byte i], contentTopic = DefaultContentTopic, ts = Timestamp(i)
)
putFutures.add(
driver.put(
DefaultPubsubTopic,
msg,
computeDigest(msg),
computeMessageHash(DefaultPubsubTopic, msg),
msg.timestamp,
)
)
discard waitFor allFinished(putFutures) discard waitFor allFinished(putFutures)
@ -56,12 +61,12 @@ suite "Waku Archive - Retention policy":
## Given ## Given
let let
# in bytes # in bytes
sizeLimit:int64 = 52428 sizeLimit: int64 = 52428
excess = 325 excess = 325
let driver = newSqliteArchiveDriver() let driver = newSqliteArchiveDriver()
let retentionPolicy: RetentionPolicy = SizeRetentionPolicy.new(size=sizeLimit) let retentionPolicy: RetentionPolicy = SizeRetentionPolicy.new(size = sizeLimit)
var putFutures = newSeq[Future[ArchiveDriverResult[void]]]() var putFutures = newSeq[Future[ArchiveDriverResult[void]]]()
# make sure that the db is empty to before test begins # make sure that the db is empty to before test begins
@ -69,16 +74,26 @@ suite "Waku Archive - Retention policy":
# if there are messages in db, empty them # if there are messages in db, empty them
if storedMsg.len > 0: if storedMsg.len > 0:
let now = getNanosecondTime(getTime().toUnixFloat()) let now = getNanosecondTime(getTime().toUnixFloat())
require (waitFor driver.deleteMessagesOlderThanTimestamp(ts=now)).isOk() require (waitFor driver.deleteMessagesOlderThanTimestamp(ts = now)).isOk()
require (waitFor driver.performVacuum()).isOk() require (waitFor driver.performVacuum()).isOk()
## When ## When
## ##
# create a number of messages so that the size of the DB overshoots # create a number of messages so that the size of the DB overshoots
for i in 1..excess: for i in 1 .. excess:
let msg = fakeWakuMessage(payload= @[byte i], contentTopic=DefaultContentTopic, ts=Timestamp(i)) let msg = fakeWakuMessage(
putFutures.add(driver.put(DefaultPubsubTopic, msg, computeDigest(msg), computeMessageHash(DefaultPubsubTopic, msg), msg.timestamp)) payload = @[byte i], contentTopic = DefaultContentTopic, ts = Timestamp(i)
)
putFutures.add(
driver.put(
DefaultPubsubTopic,
msg,
computeDigest(msg),
computeMessageHash(DefaultPubsubTopic, msg),
msg.timestamp,
)
)
# waitFor is used to synchronously wait for the futures to complete. # waitFor is used to synchronously wait for the futures to complete.
discard waitFor allFinished(putFutures) discard waitFor allFinished(putFutures)
@ -115,33 +130,40 @@ suite "Waku Archive - Retention policy":
let let
driver = newSqliteArchiveDriver() driver = newSqliteArchiveDriver()
retentionPolicy: RetentionPolicy = CapacityRetentionPolicy.new(capacity=capacity) retentionPolicy: RetentionPolicy =
CapacityRetentionPolicy.new(capacity = capacity)
let messages = @[ let messages =
fakeWakuMessage(contentTopic=DefaultContentTopic, ts=ts(0)), @[
fakeWakuMessage(contentTopic=DefaultContentTopic, ts=ts(1)), fakeWakuMessage(contentTopic = DefaultContentTopic, ts = ts(0)),
fakeWakuMessage(contentTopic = DefaultContentTopic, ts = ts(1)),
fakeWakuMessage(contentTopic=contentTopic, ts=ts(2)), fakeWakuMessage(contentTopic = contentTopic, ts = ts(2)),
fakeWakuMessage(contentTopic=contentTopic, ts=ts(3)), fakeWakuMessage(contentTopic = contentTopic, ts = ts(3)),
fakeWakuMessage(contentTopic=contentTopic, ts=ts(4)), fakeWakuMessage(contentTopic = contentTopic, ts = ts(4)),
fakeWakuMessage(contentTopic=contentTopic, ts=ts(5)), fakeWakuMessage(contentTopic = contentTopic, ts = ts(5)),
fakeWakuMessage(contentTopic=contentTopic, ts=ts(6)) fakeWakuMessage(contentTopic = contentTopic, ts = ts(6)),
] ]
## When ## When
for msg in messages: for msg in messages:
require (waitFor driver.put(DefaultPubsubTopic, msg, computeDigest(msg), computeMessageHash(DefaultPubsubTopic, msg), msg.timestamp)).isOk() require (
waitFor driver.put(
DefaultPubsubTopic,
msg,
computeDigest(msg),
computeMessageHash(DefaultPubsubTopic, msg),
msg.timestamp,
)
).isOk()
require (waitFor retentionPolicy.execute(driver)).isOk() require (waitFor retentionPolicy.execute(driver)).isOk()
## Then ## Then
let storedMsg = (waitFor driver.getAllMessages()).tryGet() let storedMsg = (waitFor driver.getAllMessages()).tryGet()
check: check:
storedMsg.len == capacity storedMsg.len == capacity
storedMsg.all do (item: auto) -> bool: storedMsg.all do(item: auto) -> bool:
let (pubsubTopic, msg, _, _, _) = item let (pubsubTopic, msg, _, _, _) = item
msg.contentTopic == contentTopic and msg.contentTopic == contentTopic and pubsubTopic == DefaultPubsubTopic
pubsubTopic == DefaultPubsubTopic
## Cleanup ## Cleanup
(waitFor driver.close()).expect("driver to close") (waitFor driver.close()).expect("driver to close")

Some files were not shown because too many files have changed in this diff Show More