From fbaf295ac1490b2ce8f15394a5e3e62863b6d9dd Mon Sep 17 00:00:00 2001 From: Lorenzo Delgado Date: Tue, 6 Sep 2022 20:43:46 +0200 Subject: [PATCH] refactor(wakunode): split wakunode rln-relay tests into a new file --- tests/all_tests_v2.nim | 4 +- tests/v2/test_wakunode.nim | 328 +------------------------ tests/v2/test_wakunode_rln_relay.nim | 344 +++++++++++++++++++++++++++ 3 files changed, 348 insertions(+), 328 deletions(-) create mode 100644 tests/v2/test_wakunode_rln_relay.nim diff --git a/tests/all_tests_v2.nim b/tests/all_tests_v2.nim index 7cfdc36b1..62496240e 100644 --- a/tests/all_tests_v2.nim +++ b/tests/all_tests_v2.nim @@ -35,7 +35,9 @@ import ./v2/test_waku_noise when defined(rln) or defined(rlnzerokit): - import ./v2/test_waku_rln_relay + import + ./v2/test_waku_rln_relay, + ./v2/test_wakunode_rln_relay when defined(onchain_rln): import ./v2/test_waku_rln_relay_onchain diff --git a/tests/v2/test_wakunode.nim b/tests/v2/test_wakunode.nim index 7303fb4d4..73e4d2cd6 100644 --- a/tests/v2/test_wakunode.nim +++ b/tests/v2/test_wakunode.nim @@ -29,12 +29,6 @@ import from std/times import epochTime -when defined(rln): - import std/sequtils - import - ../../waku/v2/protocol/waku_rln_relay/[waku_rln_relay_utils, waku_rln_relay_types] - -const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto" template sourceDir: string = currentSourcePath.parentDir() const KEY_PATH = sourceDir / "resources/test_key.pem" const CERT_PATH = sourceDir / "resources/test_cert.pem" @@ -643,327 +637,7 @@ procSuite "WakuNode": await node1.stop() await node2.stop() await node3.stop() - - when defined(rln): - asyncTest "testing rln-relay with valid proof": - - let - # publisher node - nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node1 = WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(60000)) - # Relay node - nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60002)) - # Subscriber - nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node3 = WakuNode.new(nodeKey3, ValidIpAddress.init("0.0.0.0"), Port(60003)) - - rlnRelayPubSubTopic = RLNRELAY_PUBSUB_TOPIC - contentTopic = ContentTopic("/waku/2/default-content/proto") - - # set up three nodes - # node1 - node1.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt1, memKeyPairOpt1, memIndexOpt1) = rlnRelayStaticSetUp(1) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node1.mountRlnRelayStatic(group = groupOpt1.get(), - memKeyPair = memKeyPairOpt1.get(), - memIndex = memIndexOpt1.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node1.start() - - # node 2 - node2.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt2, memKeyPairOpt2, memIndexOpt2) = rlnRelayStaticSetUp(2) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node2.mountRlnRelayStatic(group = groupOpt2.get(), - memKeyPair = memKeyPairOpt2.get(), - memIndex = memIndexOpt2.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node2.start() - - # node 3 - node3.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt3, memKeyPairOpt3, memIndexOpt3) = rlnRelayStaticSetUp(3) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node3.mountRlnRelayStatic(group = groupOpt3.get(), - memKeyPair = memKeyPairOpt3.get(), - memIndex = memIndexOpt3.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node3.start() - - # connect them together - await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) - await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) - - 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() - debug "The received topic:", topic - if topic == rlnRelayPubSubTopic: - completionFut.complete(true) - - # mount the relay handler - node3.subscribe(rlnRelayPubSubTopic, relayHandler) - await sleepAsync(2000.millis) - - # prepare the message payload - let payload = "Hello".toBytes() - - # prepare the epoch - let epoch = getCurrentEpoch() - - var message = WakuMessage(payload: @payload, - contentTopic: contentTopic) - doAssert(node1.wakuRlnRelay.appendRLNProof(message, epochTime())) - - - ## node1 publishes a message with a rate limit proof, the message is then relayed to node2 which in turn - ## verifies the rate limit proof of the message and relays the message to node3 - ## verification at node2 occurs inside a topic validator which is installed as part of the waku-rln-relay mount proc - await node1.publish(rlnRelayPubSubTopic, message) - await sleepAsync(2000.millis) - - - check: - (await completionFut.withTimeout(10.seconds)) == true - - await node1.stop() - await node2.stop() - await node3.stop() - asyncTest "testing rln-relay with invalid proof": - let - # publisher node - nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node1 = WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(60000)) - # Relay node - nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60002)) - # Subscriber - nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node3 = WakuNode.new(nodeKey3, ValidIpAddress.init("0.0.0.0"), Port(60003)) - - rlnRelayPubSubTopic = RLNRELAY_PUBSUB_TOPIC - contentTopic = ContentTopic("/waku/2/default-content/proto") - - # set up three nodes - # node1 - node1.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt1, memKeyPairOpt1, memIndexOpt1) = rlnRelayStaticSetUp(1) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node1.mountRlnRelayStatic(group = groupOpt1.get(), - memKeyPair = memKeyPairOpt1.get(), - memIndex = memIndexOpt1.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node1.start() - - # node 2 - node2.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt2, memKeyPairOpt2, memIndexOpt2) = rlnRelayStaticSetUp(2) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node2.mountRlnRelayStatic(group = groupOpt2.get(), - memKeyPair = memKeyPairOpt2.get(), - memIndex = memIndexOpt2.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node2.start() - - # node 3 - node3.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt3, memKeyPairOpt3, memIndexOpt3) = rlnRelayStaticSetUp(3) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node3.mountRlnRelayStatic(group = groupOpt3.get(), - memKeyPair = memKeyPairOpt3.get(), - memIndex= memIndexOpt3.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node3.start() - - # connect them together - await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) - await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) - - # define a custom relay handler - 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() - debug "The received topic:", topic - if topic == rlnRelayPubSubTopic: - completionFut.complete(true) - - # mount the relay handler - node3.subscribe(rlnRelayPubSubTopic, relayHandler) - await sleepAsync(2000.millis) - - # prepare the message payload - let payload = "Hello".toBytes() - - # prepare the epoch - let epoch = getCurrentEpoch() - - # prepare the proof - let - contentTopicBytes = contentTopic.toBytes - input = concat(payload, contentTopicBytes) - rateLimitProofRes = node1.wakuRlnRelay.rlnInstance.proofGen(data = input, - memKeys = node1.wakuRlnRelay.membershipKeyPair, - memIndex = MembershipIndex(4), - epoch = epoch) - doAssert(rateLimitProofRes.isOk()) - let rateLimitProof = rateLimitProofRes.value - - let message = WakuMessage(payload: @payload, - contentTopic: contentTopic, - proof: rateLimitProof) - - - ## node1 publishes a message with an invalid rln proof, the message is then relayed to node2 which in turn - ## attempts to verify the rate limit proof and fails hence does not relay the message to node3, thus the relayHandler of node3 - ## never gets called - ## verification at node2 occurs inside a topic validator which is installed as part of the waku-rln-relay mount proc - await node1.publish(rlnRelayPubSubTopic, message) - await sleepAsync(2000.millis) - - check: - # the relayHandler of node3 never gets called - (await completionFut.withTimeout(10.seconds)) == false - - await node1.stop() - await node2.stop() - await node3.stop() - - asyncTest "testing rln-relay double-signaling detection": - - let - # publisher node - nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node1 = WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(60000)) - # Relay node - nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60002)) - # Subscriber - nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[] - node3 = WakuNode.new(nodeKey3, ValidIpAddress.init("0.0.0.0"), Port(60003)) - - rlnRelayPubSubTopic = RLNRELAY_PUBSUB_TOPIC - contentTopic = ContentTopic("/waku/2/default-content/proto") - - # set up three nodes - # node1 - node1.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt1, memKeyPairOpt1, memIndexOpt1) = rlnRelayStaticSetUp(1) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node1.mountRlnRelayStatic(group = groupOpt1.get(), - memKeyPair = memKeyPairOpt1.get(), - memIndex = memIndexOpt1.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node1.start() - - # node 2 - node2.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt2, memKeyPairOpt2, memIndexOpt2) = rlnRelayStaticSetUp(2) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node2.mountRlnRelayStatic(group = groupOpt2.get(), - memKeyPair = memKeyPairOpt2.get(), - memIndex = memIndexOpt2.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node2.start() - - # node 3 - node3.mountRelay(@[rlnRelayPubSubTopic]) - let (groupOpt3, memKeyPairOpt3, memIndexOpt3) = rlnRelayStaticSetUp(3) # set up rln relay inputs - # mount rlnrelay in off-chain mode - node3.mountRlnRelayStatic(group = groupOpt3.get(), - memKeyPair = memKeyPairOpt3.get(), - memIndex = memIndexOpt3.get(), - pubsubTopic = rlnRelayPubSubTopic, - contentTopic = contentTopic) - await node3.start() - - # connect the nodes together node1 <-> node2 <-> node3 - await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) - await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) - - # get the current epoch time - let time = epochTime() - # create some messages with rate limit proofs - var - wm1 = WakuMessage(payload: "message 1".toBytes(), contentTopic: contentTopic) - proofAdded1 = node3.wakuRlnRelay.appendRLNProof(wm1, time) - # another message in the same epoch as wm1, it will break the messaging rate limit - wm2 = WakuMessage(payload: "message 2".toBytes(), contentTopic: contentTopic) - proofAdded2 = node3.wakuRlnRelay.appendRLNProof(wm2, time) - # wm3 points to the next epoch - wm3 = WakuMessage(payload: "message 3".toBytes(), contentTopic: contentTopic) - proofAdded3 = node3.wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS) - wm4 = WakuMessage(payload: "message 4".toBytes(), contentTopic: contentTopic) - - # check proofs are added correctly - check: - proofAdded1 - proofAdded2 - proofAdded3 - - # relay handler for node3 - var completionFut1 = newFuture[bool]() - var completionFut2 = newFuture[bool]() - var completionFut3 = newFuture[bool]() - var completionFut4 = newFuture[bool]() - proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} = - let msg = WakuMessage.init(data) - if msg.isOk(): - let wm = msg.value() - debug "The received topic:", topic - if topic == rlnRelayPubSubTopic: - if wm == wm1: - completionFut1.complete(true) - if wm == wm2: - completionFut2.complete(true) - if wm == wm3: - completionFut3.complete(true) - if wm == wm4: - completionFut4.complete(true) - - - # mount the relay handler for node3 - node3.subscribe(rlnRelayPubSubTopic, relayHandler) - await sleepAsync(2000.millis) - - ## node1 publishes and relays 4 messages to node2 - ## verification at node2 occurs inside a topic validator which is installed as part of the waku-rln-relay mount proc - ## node2 relays either of wm1 or wm2 to node3, depending on which message arrives at node2 first - ## node2 should detect either of wm1 or wm2 as spam and not relay it - ## node2 should relay wm3 to node3 - ## node2 should not relay wm4 because it has no valid rln proof - await node1.publish(rlnRelayPubSubTopic, wm1) - await node1.publish(rlnRelayPubSubTopic, wm2) - await node1.publish(rlnRelayPubSubTopic, wm3) - await node1.publish(rlnRelayPubSubTopic, wm4) - await sleepAsync(2000.millis) - - let - res1 = await completionFut1.withTimeout(10.seconds) - res2 = await completionFut2.withTimeout(10.seconds) - - check: - (res1 and res2) == false # either of the wm1 and wm2 is found as spam hence not relayed - (await completionFut3.withTimeout(10.seconds)) == true - (await completionFut4.withTimeout(10.seconds)) == false - - await node1.stop() - await node2.stop() - await node3.stop() - + asyncTest "Relay protocol is started correctly": let nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] diff --git a/tests/v2/test_wakunode_rln_relay.nim b/tests/v2/test_wakunode_rln_relay.nim new file mode 100644 index 000000000..8c24ad220 --- /dev/null +++ b/tests/v2/test_wakunode_rln_relay.nim @@ -0,0 +1,344 @@ +{.used.} + +import + std/sequtils, + stew/byteutils, + stew/shims/net as stewNet, + testutils/unittests, + chronicles, + chronos, + libp2p/crypto/crypto, + libp2p/peerid, + libp2p/multiaddress, + libp2p/switch, + libp2p/protocols/pubsub/rpc/messages, + libp2p/protocols/pubsub/pubsub, + eth/keys +import + ../../waku/v2/protocol/waku_rln_relay/[waku_rln_relay_utils, waku_rln_relay_types], + ../../waku/v2/protocol/[waku_relay, waku_message], + ../../waku/v2/utils/peers, + ../../waku/v2/node/wakunode2 + +from std/times import epochTime + + + +const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto" + +procSuite "WakuNode - RLN relay": + let rng = keys.newRng() + + asyncTest "testing rln-relay with valid proof": + + let + # publisher node + nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node1 = WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(60000)) + # Relay node + nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60002)) + # Subscriber + nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node3 = WakuNode.new(nodeKey3, ValidIpAddress.init("0.0.0.0"), Port(60003)) + + rlnRelayPubSubTopic = RLNRELAY_PUBSUB_TOPIC + contentTopic = ContentTopic("/waku/2/default-content/proto") + + # set up three nodes + # node1 + node1.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt1, memKeyPairOpt1, memIndexOpt1) = rlnRelayStaticSetUp(1) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node1.mountRlnRelayStatic(group = groupOpt1.get(), + memKeyPair = memKeyPairOpt1.get(), + memIndex = memIndexOpt1.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node1.start() + + # node 2 + node2.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt2, memKeyPairOpt2, memIndexOpt2) = rlnRelayStaticSetUp(2) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node2.mountRlnRelayStatic(group = groupOpt2.get(), + memKeyPair = memKeyPairOpt2.get(), + memIndex = memIndexOpt2.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node2.start() + + # node 3 + node3.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt3, memKeyPairOpt3, memIndexOpt3) = rlnRelayStaticSetUp(3) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node3.mountRlnRelayStatic(group = groupOpt3.get(), + memKeyPair = memKeyPairOpt3.get(), + memIndex = memIndexOpt3.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node3.start() + + # connect them together + await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) + await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) + + var completionFut = newFuture[bool]() + proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} = + let msg = WakuMessage.init(data) + if msg.isOk(): + debug "The received topic:", topic + if topic == rlnRelayPubSubTopic: + completionFut.complete(true) + + # mount the relay handler + node3.subscribe(rlnRelayPubSubTopic, relayHandler) + await sleepAsync(2000.millis) + + # prepare the message payload + let payload = "Hello".toBytes() + + # prepare the epoch + var message = WakuMessage(payload: @payload, contentTopic: contentTopic) + doAssert(node1.wakuRlnRelay.appendRLNProof(message, epochTime())) + + + ## node1 publishes a message with a rate limit proof, the message is then relayed to node2 which in turn + ## verifies the rate limit proof of the message and relays the message to node3 + ## verification at node2 occurs inside a topic validator which is installed as part of the waku-rln-relay mount proc + await node1.publish(rlnRelayPubSubTopic, message) + await sleepAsync(2000.millis) + + + check: + (await completionFut.withTimeout(10.seconds)) == true + + await node1.stop() + await node2.stop() + await node3.stop() + asyncTest "testing rln-relay with invalid proof": + let + # publisher node + nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node1 = WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(60000)) + # Relay node + nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60002)) + # Subscriber + nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node3 = WakuNode.new(nodeKey3, ValidIpAddress.init("0.0.0.0"), Port(60003)) + + rlnRelayPubSubTopic = RLNRELAY_PUBSUB_TOPIC + contentTopic = ContentTopic("/waku/2/default-content/proto") + + # set up three nodes + # node1 + node1.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt1, memKeyPairOpt1, memIndexOpt1) = rlnRelayStaticSetUp(1) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node1.mountRlnRelayStatic(group = groupOpt1.get(), + memKeyPair = memKeyPairOpt1.get(), + memIndex = memIndexOpt1.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node1.start() + + # node 2 + node2.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt2, memKeyPairOpt2, memIndexOpt2) = rlnRelayStaticSetUp(2) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node2.mountRlnRelayStatic(group = groupOpt2.get(), + memKeyPair = memKeyPairOpt2.get(), + memIndex = memIndexOpt2.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node2.start() + + # node 3 + node3.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt3, memKeyPairOpt3, memIndexOpt3) = rlnRelayStaticSetUp(3) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node3.mountRlnRelayStatic(group = groupOpt3.get(), + memKeyPair = memKeyPairOpt3.get(), + memIndex= memIndexOpt3.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node3.start() + + # connect them together + await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) + await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) + + # define a custom relay handler + var completionFut = newFuture[bool]() + proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} = + let msg = WakuMessage.init(data) + if msg.isOk(): + debug "The received topic:", topic + if topic == rlnRelayPubSubTopic: + completionFut.complete(true) + + # mount the relay handler + node3.subscribe(rlnRelayPubSubTopic, relayHandler) + await sleepAsync(2000.millis) + + # prepare the message payload + let payload = "Hello".toBytes() + + # prepare the epoch + let epoch = getCurrentEpoch() + + # prepare the proof + let + contentTopicBytes = contentTopic.toBytes + input = concat(payload, contentTopicBytes) + rateLimitProofRes = node1.wakuRlnRelay.rlnInstance.proofGen(data = input, + memKeys = node1.wakuRlnRelay.membershipKeyPair, + memIndex = MembershipIndex(4), + epoch = epoch) + doAssert(rateLimitProofRes.isOk()) + let rateLimitProof = rateLimitProofRes.value + + let message = WakuMessage(payload: @payload, + contentTopic: contentTopic, + proof: rateLimitProof) + + + ## node1 publishes a message with an invalid rln proof, the message is then relayed to node2 which in turn + ## attempts to verify the rate limit proof and fails hence does not relay the message to node3, thus the relayHandler of node3 + ## never gets called + ## verification at node2 occurs inside a topic validator which is installed as part of the waku-rln-relay mount proc + await node1.publish(rlnRelayPubSubTopic, message) + await sleepAsync(2000.millis) + + check: + # the relayHandler of node3 never gets called + (await completionFut.withTimeout(10.seconds)) == false + + await node1.stop() + await node2.stop() + await node3.stop() + + asyncTest "testing rln-relay double-signaling detection": + + let + # publisher node + nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node1 = WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(60000)) + # Relay node + nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60002)) + # Subscriber + nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node3 = WakuNode.new(nodeKey3, ValidIpAddress.init("0.0.0.0"), Port(60003)) + + rlnRelayPubSubTopic = RLNRELAY_PUBSUB_TOPIC + contentTopic = ContentTopic("/waku/2/default-content/proto") + + # set up three nodes + # node1 + node1.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt1, memKeyPairOpt1, memIndexOpt1) = rlnRelayStaticSetUp(1) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node1.mountRlnRelayStatic(group = groupOpt1.get(), + memKeyPair = memKeyPairOpt1.get(), + memIndex = memIndexOpt1.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node1.start() + + # node 2 + node2.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt2, memKeyPairOpt2, memIndexOpt2) = rlnRelayStaticSetUp(2) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node2.mountRlnRelayStatic(group = groupOpt2.get(), + memKeyPair = memKeyPairOpt2.get(), + memIndex = memIndexOpt2.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node2.start() + + # node 3 + node3.mountRelay(@[rlnRelayPubSubTopic]) + let (groupOpt3, memKeyPairOpt3, memIndexOpt3) = rlnRelayStaticSetUp(3) # set up rln relay inputs + # mount rlnrelay in off-chain mode + node3.mountRlnRelayStatic(group = groupOpt3.get(), + memKeyPair = memKeyPairOpt3.get(), + memIndex = memIndexOpt3.get(), + pubsubTopic = rlnRelayPubSubTopic, + contentTopic = contentTopic) + await node3.start() + + # connect the nodes together node1 <-> node2 <-> node3 + await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) + await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) + + # get the current epoch time + let time = epochTime() + # create some messages with rate limit proofs + var + wm1 = WakuMessage(payload: "message 1".toBytes(), contentTopic: contentTopic) + proofAdded1 = node3.wakuRlnRelay.appendRLNProof(wm1, time) + # another message in the same epoch as wm1, it will break the messaging rate limit + wm2 = WakuMessage(payload: "message 2".toBytes(), contentTopic: contentTopic) + proofAdded2 = node3.wakuRlnRelay.appendRLNProof(wm2, time) + # wm3 points to the next epoch + wm3 = WakuMessage(payload: "message 3".toBytes(), contentTopic: contentTopic) + proofAdded3 = node3.wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS) + wm4 = WakuMessage(payload: "message 4".toBytes(), contentTopic: contentTopic) + + # check proofs are added correctly + check: + proofAdded1 + proofAdded2 + proofAdded3 + + # relay handler for node3 + var completionFut1 = newFuture[bool]() + var completionFut2 = newFuture[bool]() + var completionFut3 = newFuture[bool]() + var completionFut4 = newFuture[bool]() + proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} = + let msg = WakuMessage.init(data) + if msg.isOk(): + let wm = msg.value() + debug "The received topic:", topic + if topic == rlnRelayPubSubTopic: + if wm == wm1: + completionFut1.complete(true) + if wm == wm2: + completionFut2.complete(true) + if wm == wm3: + completionFut3.complete(true) + if wm == wm4: + completionFut4.complete(true) + + + # mount the relay handler for node3 + node3.subscribe(rlnRelayPubSubTopic, relayHandler) + await sleepAsync(2000.millis) + + ## node1 publishes and relays 4 messages to node2 + ## verification at node2 occurs inside a topic validator which is installed as part of the waku-rln-relay mount proc + ## node2 relays either of wm1 or wm2 to node3, depending on which message arrives at node2 first + ## node2 should detect either of wm1 or wm2 as spam and not relay it + ## node2 should relay wm3 to node3 + ## node2 should not relay wm4 because it has no valid rln proof + await node1.publish(rlnRelayPubSubTopic, wm1) + await node1.publish(rlnRelayPubSubTopic, wm2) + await node1.publish(rlnRelayPubSubTopic, wm3) + await node1.publish(rlnRelayPubSubTopic, wm4) + await sleepAsync(2000.millis) + + let + res1 = await completionFut1.withTimeout(10.seconds) + res2 = await completionFut2.withTimeout(10.seconds) + + check: + (res1 and res2) == false # either of the wm1 and wm2 is found as spam hence not relayed + (await completionFut3.withTimeout(10.seconds)) == true + (await completionFut4.withTimeout(10.seconds)) == false + + await node1.stop() + await node2.stop() + await node3.stop() \ No newline at end of file