2020-08-31 03:32:41 +00:00
|
|
|
{.used.}
|
|
|
|
|
|
|
|
import
|
2020-09-01 02:09:54 +00:00
|
|
|
std/unittest,
|
|
|
|
chronicles, chronos, stew/shims/net as stewNet, stew/byteutils,
|
2020-08-31 03:32:41 +00:00
|
|
|
libp2p/crypto/crypto,
|
|
|
|
libp2p/crypto/secp,
|
2021-01-25 11:03:52 +00:00
|
|
|
libp2p/peerid,
|
|
|
|
libp2p/multiaddress,
|
2020-09-02 03:15:25 +00:00
|
|
|
libp2p/switch,
|
2020-08-31 03:32:41 +00:00
|
|
|
eth/keys,
|
2021-01-06 09:35:05 +00:00
|
|
|
../../waku/v2/protocol/[waku_relay, waku_message, message_notifier],
|
2020-11-24 04:34:32 +00:00
|
|
|
../../waku/v2/protocol/waku_store/waku_store,
|
2021-01-05 04:52:10 +00:00
|
|
|
../../waku/v2/protocol/waku_filter/waku_filter,
|
2021-01-25 11:03:52 +00:00
|
|
|
../../waku/v2/utils/peers,
|
2020-11-17 09:34:53 +00:00
|
|
|
../../waku/v2/node/wakunode2,
|
2021-01-06 09:35:05 +00:00
|
|
|
../test_helpers
|
2020-07-29 13:24:01 +00:00
|
|
|
|
|
|
|
procSuite "WakuNode":
|
2020-09-02 03:15:25 +00:00
|
|
|
let rng = keys.newRng()
|
2020-07-29 13:24:01 +00:00
|
|
|
asyncTest "Message published with content filter is retrievable":
|
2020-09-01 02:09:54 +00:00
|
|
|
let
|
|
|
|
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node = WakuNode.init(nodeKey, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60000))
|
2020-09-02 03:15:25 +00:00
|
|
|
pubSubTopic = "chat"
|
2020-10-21 08:55:06 +00:00
|
|
|
contentTopic = ContentTopic(1)
|
2020-11-10 07:13:16 +00:00
|
|
|
filterRequest = FilterRequest(topic: pubSubTopic, contentFilters: @[ContentFilter(topics: @[contentTopic])], subscribe: true)
|
2020-09-02 03:15:25 +00:00
|
|
|
message = WakuMessage(payload: "hello world".toBytes(),
|
|
|
|
contentTopic: contentTopic)
|
|
|
|
|
|
|
|
# This could/should become a more fixed handler (at least default) that
|
|
|
|
# would be enforced on WakuNode level.
|
|
|
|
proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
let msg = WakuMessage.init(data)
|
|
|
|
if msg.isOk():
|
|
|
|
check:
|
|
|
|
topic == "chat"
|
2020-10-02 12:48:56 +00:00
|
|
|
node.filters.notify(msg.value(), topic)
|
2020-09-02 03:15:25 +00:00
|
|
|
|
|
|
|
var completionFut = newFuture[bool]()
|
|
|
|
|
|
|
|
# This would be the actual application handler
|
2020-10-02 12:48:56 +00:00
|
|
|
proc contentHandler(msg: WakuMessage) {.gcsafe, closure.} =
|
|
|
|
let message = string.fromBytes(msg.payload)
|
2020-09-02 03:15:25 +00:00
|
|
|
check:
|
2020-10-02 12:48:56 +00:00
|
|
|
message == "hello world"
|
2020-09-02 03:15:25 +00:00
|
|
|
completionFut.complete(true)
|
2020-07-29 13:24:01 +00:00
|
|
|
|
2020-09-01 02:09:54 +00:00
|
|
|
await node.start()
|
2020-07-29 13:24:01 +00:00
|
|
|
|
2020-10-20 02:36:27 +00:00
|
|
|
await node.mountRelay()
|
|
|
|
|
2020-09-02 03:15:25 +00:00
|
|
|
# Subscribe our node to the pubSubTopic where all chat data go onto.
|
2020-09-16 04:23:10 +00:00
|
|
|
await node.subscribe(pubSubTopic, relayHandler)
|
2020-10-02 12:48:56 +00:00
|
|
|
|
2020-09-02 03:15:25 +00:00
|
|
|
# Subscribe a contentFilter to trigger a specific application handler when
|
|
|
|
# WakuMessages with that content are received
|
2020-10-02 12:48:56 +00:00
|
|
|
await node.subscribe(filterRequest, contentHandler)
|
|
|
|
|
|
|
|
await sleepAsync(2000.millis)
|
2020-09-02 03:15:25 +00:00
|
|
|
|
2020-12-02 08:40:53 +00:00
|
|
|
await node.publish(pubSubTopic, message)
|
2020-09-02 03:15:25 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
(await completionFut.withTimeout(5.seconds)) == true
|
|
|
|
|
|
|
|
await node.stop()
|
|
|
|
|
|
|
|
asyncTest "Content filtered publishing over network":
|
2020-09-01 02:09:54 +00:00
|
|
|
let
|
2020-09-02 03:15:25 +00:00
|
|
|
nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node1 = WakuNode.init(nodeKey1, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60000))
|
|
|
|
nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node2 = WakuNode.init(nodeKey2, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60002))
|
|
|
|
pubSubTopic = "chat"
|
2020-10-21 08:55:06 +00:00
|
|
|
contentTopic = ContentTopic(1)
|
2020-11-10 07:13:16 +00:00
|
|
|
filterRequest = FilterRequest(topic: pubSubTopic, contentFilters: @[ContentFilter(topics: @[contentTopic])], subscribe: true)
|
2020-09-02 03:15:25 +00:00
|
|
|
message = WakuMessage(payload: "hello world".toBytes(),
|
2020-09-01 15:20:38 +00:00
|
|
|
contentTopic: contentTopic)
|
2020-07-29 13:24:01 +00:00
|
|
|
|
2020-09-02 03:15:25 +00:00
|
|
|
var completionFut = newFuture[bool]()
|
|
|
|
|
|
|
|
# This could/should become a more fixed handler (at least default) that
|
|
|
|
# would be enforced on WakuNode level.
|
|
|
|
proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
let msg = WakuMessage.init(data)
|
|
|
|
if msg.isOk():
|
|
|
|
check:
|
|
|
|
topic == "chat"
|
2020-10-02 12:48:56 +00:00
|
|
|
node1.filters.notify(msg.value(), topic)
|
2020-09-02 03:15:25 +00:00
|
|
|
|
|
|
|
# This would be the actual application handler
|
2020-10-02 12:48:56 +00:00
|
|
|
proc contentHandler(msg: WakuMessage) {.gcsafe, closure.} =
|
|
|
|
let message = string.fromBytes(msg.payload)
|
2020-09-02 03:15:25 +00:00
|
|
|
check:
|
2020-10-02 12:48:56 +00:00
|
|
|
message == "hello world"
|
2020-09-02 03:15:25 +00:00
|
|
|
completionFut.complete(true)
|
|
|
|
|
|
|
|
await allFutures([node1.start(), node2.start()])
|
|
|
|
|
2020-10-20 02:36:27 +00:00
|
|
|
await node1.mountRelay()
|
|
|
|
await node2.mountRelay()
|
|
|
|
|
|
|
|
node1.mountFilter()
|
|
|
|
node2.mountFilter()
|
|
|
|
|
2020-09-02 03:15:25 +00:00
|
|
|
# Subscribe our node to the pubSubTopic where all chat data go onto.
|
2020-09-16 04:23:10 +00:00
|
|
|
await node1.subscribe(pubSubTopic, relayHandler)
|
2020-09-02 03:15:25 +00:00
|
|
|
# Subscribe a contentFilter to trigger a specific application handler when
|
|
|
|
# WakuMessages with that content are received
|
2020-10-02 12:48:56 +00:00
|
|
|
node1.wakuFilter.setPeer(node2.peerInfo)
|
|
|
|
await node1.subscribe(filterRequest, contentHandler)
|
|
|
|
await sleepAsync(2000.millis)
|
|
|
|
|
2020-09-02 03:15:25 +00:00
|
|
|
# Connect peers by dialing from node2 to node1
|
2020-11-20 13:59:29 +00:00
|
|
|
let conn = await node2.switch.dial(node1.peerInfo.peerId, node1.peerInfo.addrs, WakuRelayCodec)
|
2020-10-02 12:48:56 +00:00
|
|
|
|
2020-09-16 04:23:10 +00:00
|
|
|
# We need to sleep to allow the subscription to go through
|
|
|
|
info "Going to sleep to allow subscribe to go through"
|
|
|
|
await sleepAsync(2000.millis)
|
|
|
|
|
|
|
|
info "Waking up and publishing"
|
2020-12-02 08:40:53 +00:00
|
|
|
await node2.publish(pubSubTopic, message)
|
2020-09-02 03:15:25 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
(await completionFut.withTimeout(5.seconds)) == true
|
2020-09-24 02:16:25 +00:00
|
|
|
await node1.stop()
|
|
|
|
await node2.stop()
|
|
|
|
|
|
|
|
asyncTest "Store protocol returns expected message":
|
|
|
|
let
|
|
|
|
nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node1 = WakuNode.init(nodeKey1, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60000))
|
|
|
|
nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node2 = WakuNode.init(nodeKey2, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60002))
|
2020-10-21 08:55:06 +00:00
|
|
|
contentTopic = ContentTopic(1)
|
2020-09-24 02:16:25 +00:00
|
|
|
message = WakuMessage(payload: "hello world".toBytes(), contentTopic: contentTopic)
|
|
|
|
|
|
|
|
var completionFut = newFuture[bool]()
|
|
|
|
|
|
|
|
await node1.start()
|
2020-10-20 02:36:27 +00:00
|
|
|
node1.mountStore()
|
2020-09-24 02:16:25 +00:00
|
|
|
await node2.start()
|
2020-10-20 02:36:27 +00:00
|
|
|
node2.mountStore()
|
2020-09-24 02:16:25 +00:00
|
|
|
|
2020-10-28 05:19:07 +00:00
|
|
|
await node2.subscriptions.notify("/waku/2/default-waku/proto", message)
|
2020-09-24 02:16:25 +00:00
|
|
|
|
|
|
|
await sleepAsync(2000.millis)
|
|
|
|
|
|
|
|
node1.wakuStore.setPeer(node2.peerInfo)
|
|
|
|
|
|
|
|
proc storeHandler(response: HistoryResponse) {.gcsafe, closure.} =
|
|
|
|
check:
|
|
|
|
response.messages[0] == message
|
|
|
|
completionFut.complete(true)
|
|
|
|
|
2020-09-25 14:02:13 +00:00
|
|
|
await node1.query(HistoryQuery(topics: @[contentTopic]), storeHandler)
|
2020-09-24 02:16:25 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
(await completionFut.withTimeout(5.seconds)) == true
|
|
|
|
await node1.stop()
|
|
|
|
await node2.stop()
|
2020-10-02 12:48:56 +00:00
|
|
|
|
|
|
|
asyncTest "Filter protocol returns expected message":
|
|
|
|
let
|
|
|
|
nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node1 = WakuNode.init(nodeKey1, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60000))
|
|
|
|
nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node2 = WakuNode.init(nodeKey2, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60002))
|
2020-10-21 08:55:06 +00:00
|
|
|
contentTopic = ContentTopic(1)
|
2020-10-02 12:48:56 +00:00
|
|
|
message = WakuMessage(payload: "hello world".toBytes(), contentTopic: contentTopic)
|
|
|
|
|
|
|
|
var completionFut = newFuture[bool]()
|
|
|
|
|
|
|
|
await node1.start()
|
2020-10-20 02:36:27 +00:00
|
|
|
node1.mountFilter()
|
2020-10-02 12:48:56 +00:00
|
|
|
await node2.start()
|
2020-10-20 02:36:27 +00:00
|
|
|
node2.mountFilter()
|
2020-10-02 12:48:56 +00:00
|
|
|
|
|
|
|
node1.wakuFilter.setPeer(node2.peerInfo)
|
|
|
|
|
|
|
|
proc handler(msg: WakuMessage) {.gcsafe, closure.} =
|
|
|
|
check:
|
|
|
|
msg == message
|
|
|
|
completionFut.complete(true)
|
|
|
|
|
2020-11-10 07:13:16 +00:00
|
|
|
await node1.subscribe(FilterRequest(topic: "/waku/2/default-waku/proto", contentFilters: @[ContentFilter(topics: @[contentTopic])], subscribe: true), handler)
|
2020-10-02 12:48:56 +00:00
|
|
|
|
|
|
|
await sleepAsync(2000.millis)
|
|
|
|
|
2020-10-28 05:19:07 +00:00
|
|
|
await node2.subscriptions.notify("/waku/2/default-waku/proto", message)
|
2020-10-02 12:48:56 +00:00
|
|
|
|
|
|
|
await sleepAsync(2000.millis)
|
|
|
|
|
|
|
|
check:
|
|
|
|
(await completionFut.withTimeout(5.seconds)) == true
|
|
|
|
await node1.stop()
|
|
|
|
await node2.stop()
|
2020-10-22 11:12:00 +00:00
|
|
|
|
|
|
|
asyncTest "Messages are correctly relayed":
|
|
|
|
let
|
|
|
|
nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node1 = WakuNode.init(nodeKey1, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60000))
|
|
|
|
nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node2 = WakuNode.init(nodeKey2, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60002))
|
|
|
|
nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
|
|
node3 = WakuNode.init(nodeKey3, ValidIpAddress.init("0.0.0.0"),
|
|
|
|
Port(60003))
|
|
|
|
pubSubTopic = "test"
|
|
|
|
contentTopic = ContentTopic(1)
|
|
|
|
payload = "hello world".toBytes()
|
|
|
|
message = WakuMessage(payload: payload, contentTopic: contentTopic)
|
|
|
|
|
|
|
|
await node1.start()
|
|
|
|
await node1.mountRelay(@[pubSubTopic])
|
|
|
|
|
|
|
|
await node2.start()
|
|
|
|
await node2.mountRelay(@[pubSubTopic])
|
|
|
|
|
|
|
|
await node3.start()
|
|
|
|
await node3.mountRelay(@[pubSubTopic])
|
|
|
|
|
|
|
|
await node1.connectToNodes(@[node2.peerInfo])
|
|
|
|
await node3.connectToNodes(@[node2.peerInfo])
|
|
|
|
|
|
|
|
var completionFut = newFuture[bool]()
|
|
|
|
proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
let msg = WakuMessage.init(data)
|
|
|
|
if msg.isOk():
|
|
|
|
let val = msg.value()
|
|
|
|
check:
|
|
|
|
topic == pubSubTopic
|
|
|
|
val.contentTopic == contentTopic
|
|
|
|
val.payload == payload
|
|
|
|
completionFut.complete(true)
|
|
|
|
|
|
|
|
await node3.subscribe(pubSubTopic, relayHandler)
|
|
|
|
await sleepAsync(2000.millis)
|
|
|
|
|
2020-12-02 08:40:53 +00:00
|
|
|
await node1.publish(pubSubTopic, message)
|
2020-10-22 11:12:00 +00:00
|
|
|
await sleepAsync(2000.millis)
|
|
|
|
|
|
|
|
check:
|
|
|
|
(await completionFut.withTimeout(5.seconds)) == true
|
|
|
|
await node1.stop()
|
|
|
|
await node2.stop()
|
|
|
|
await node3.stop()
|
2021-01-25 11:03:52 +00:00
|
|
|
|
|
|
|
asyncTest "Peer info parses correctly":
|
|
|
|
## This is such an important utility function for wakunode2
|
|
|
|
## that it deserves its own test :)
|
|
|
|
|
|
|
|
# First test the `happy path` expected case
|
|
|
|
let
|
|
|
|
addrStr = "/ip4/127.0.0.1/tcp/60002/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
|
|
|
|
peerInfo = parsePeerInfo(addrStr)
|
|
|
|
|
|
|
|
check:
|
|
|
|
$(peerInfo.peerId) == "16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
|
|
|
|
$(peerInfo.addrs[0][0].tryGet()) == "/ip4/127.0.0.1"
|
|
|
|
$(peerInfo.addrs[0][1].tryGet()) == "/tcp/60002"
|
|
|
|
|
|
|
|
# Now test some common corner cases
|
|
|
|
expect ValueError:
|
|
|
|
# gibberish
|
|
|
|
discard parsePeerInfo("/p2p/$UCH GIBBER!SH")
|
|
|
|
|
|
|
|
expect ValueError:
|
|
|
|
# leading whitespace
|
|
|
|
discard parsePeerInfo(" /ip4/127.0.0.1/tcp/60002/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc")
|
|
|
|
|
|
|
|
expect ValueError:
|
|
|
|
# trailing whitespace
|
|
|
|
discard parsePeerInfo("/ip4/127.0.0.1/tcp/60002/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc ")
|
|
|
|
|
|
|
|
expect ValueError:
|
|
|
|
# invalid IP address
|
|
|
|
discard parsePeerInfo("/ip4/127.0.0.0.1/tcp/60002/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc")
|
|
|
|
|
|
|
|
expect ValueError:
|
|
|
|
# no PeerID
|
|
|
|
discard parsePeerInfo("/ip4/127.0.0.1/tcp/60002")
|
|
|
|
|
|
|
|
expect ValueError:
|
|
|
|
# unsupported transport
|
|
|
|
discard parsePeerInfo("/ip4/127.0.0.1/udp/60002/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc")
|