From 9bba8b0f9c196e1915435267c23d03fe30b8b715 Mon Sep 17 00:00:00 2001 From: Darshan K <35736874+darshankabariya@users.noreply.github.com> Date: Wed, 10 Sep 2025 16:18:51 +0530 Subject: [PATCH] fix: refact `rln-relay` and post sync test (#3434) --- apps/benchmarks/benchmarks.nim | 59 +- tests/node/test_wakunode_legacy_lightpush.nim | 41 +- tests/node/test_wakunode_lightpush.nim | 39 +- tests/waku_rln_relay/rln/test_wrappers.nim | 46 +- .../rln/waku_rln_relay_utils.nim | 54 +- tests/waku_rln_relay/test_all.nim | 4 +- .../test_rln_group_manager_onchain.nim | 32 +- .../test_rln_group_manager_static.nim | 228 -------- tests/waku_rln_relay/test_rln_serde.nim | 68 --- tests/waku_rln_relay/test_waku_rln_relay.nim | 513 +++--------------- .../test_wakunode_rln_relay.nim | 459 ++++++++-------- .../{utils_static.nim => utils_offchain.nim} | 0 tests/wakunode_rest/test_rest_health.nim | 24 +- tests/wakunode_rest/test_rest_relay.nim | 166 ++++-- tools/rln_db_inspector/rln_db_inspector.nim | 27 +- .../rln_keystore_generator.nim | 4 +- waku.nimble | 2 +- waku/waku_rln_relay/group_manager.nim | 4 +- .../group_manager/group_manager_base.nim | 70 +-- .../group_manager/on_chain/group_manager.nim | 23 +- waku/waku_rln_relay/group_manager/static.nim | 3 - .../group_manager/static/group_manager.nim | 144 ----- waku/waku_rln_relay/rln/rln_interface.nim | 75 --- waku/waku_rln_relay/rln/wrappers.nim | 210 +------ waku/waku_rln_relay/rln_relay.nim | 109 ++-- 25 files changed, 672 insertions(+), 1732 deletions(-) delete mode 100644 tests/waku_rln_relay/test_rln_group_manager_static.nim delete mode 100644 tests/waku_rln_relay/test_rln_serde.nim rename tests/waku_rln_relay/{utils_static.nim => utils_offchain.nim} (100%) delete mode 100644 waku/waku_rln_relay/group_manager/static.nim delete mode 100644 waku/waku_rln_relay/group_manager/static/group_manager.nim diff --git a/apps/benchmarks/benchmarks.nim b/apps/benchmarks/benchmarks.nim index 655954a24..a005fcbfd 100644 --- a/apps/benchmarks/benchmarks.nim +++ b/apps/benchmarks/benchmarks.nim @@ -1,42 +1,59 @@ import - math, - std/sequtils, - results, - options, + std/[strutils, times, sequtils, osproc], math, results, options, testutils/unittests + +import waku/[ waku_rln_relay/protocol_types, waku_rln_relay/rln, waku_rln_relay, waku_rln_relay/conversion_utils, - waku_rln_relay/group_manager/static/group_manager, - ] - -import std/[times, os] + waku_rln_relay/group_manager/on_chain/group_manager, + ], + tests/waku_rln_relay/utils_onchain proc main(): Future[string] {.async, gcsafe.} = - let rlnIns = createRLNInstance(20).get() - let credentials = toSeq(0 .. 1000).mapIt(membershipKeyGen(rlnIns).get()) + # Spin up a local Ethereum JSON-RPC (Anvil) and deploy the RLN contract + let anvilProc = runAnvil() + defer: + stopAnvil(anvilProc) - let manager = StaticGroupManager( - rlnInstance: rlnIns, - groupSize: 1000, - membershipIndex: some(MembershipIndex(900)), - groupKeys: credentials, - ) + # Set up an On-chain group manager (includes contract deployment) + let manager = await setupOnchainGroupManager() + (await manager.init()).isOkOr: + raiseAssert $error - await manager.init() + # Register a new member so that we can later generate proofs + let idCredentials = generateCredentials(manager.rlnInstance) + try: + await manager.register(idCredentials, UserMessageLimit(100)) + except Exception, CatchableError: + assert false, "exception raised: " & getCurrentExceptionMsg() + + let rootUpdated = await manager.updateRoots() + + if rootUpdated: + let proofResult = await manager.fetchMerkleProofElements() + if proofResult.isErr(): + error "Failed to fetch Merkle proof", error = proofResult.error + manager.merkleProofCache = proofResult.get() + + let epoch = default(Epoch) + debug "epoch in bytes", epochHex = epoch.inHex() let data: seq[byte] = newSeq[byte](1024) var proofGenTimes: seq[times.Duration] = @[] var proofVerTimes: seq[times.Duration] = @[] - for i in 0 .. 50: + + for i in 1 .. 100: var time = getTime() - let proof = manager.generateProof(data, default(Epoch)).get() + let proof = manager.generateProof(data, epoch, MessageId(i.uint8)).valueOr: + raiseAssert $error proofGenTimes.add(getTime() - time) time = getTime() - let res = manager.verifyProof(data, proof).get() + let ok = manager.verifyProof(data, proof).valueOr: + raiseAssert $error proofVerTimes.add(getTime() - time) echo "Proof generation times: ", sum(proofGenTimes) div len(proofGenTimes) @@ -44,6 +61,6 @@ proc main(): Future[string] {.async, gcsafe.} = when isMainModule: try: - waitFor(main()) + discard waitFor main() except CatchableError as e: raise e diff --git a/tests/node/test_wakunode_legacy_lightpush.nim b/tests/node/test_wakunode_legacy_lightpush.nim index 5d01e9f58..bd9666ae1 100644 --- a/tests/node/test_wakunode_legacy_lightpush.nim +++ b/tests/node/test_wakunode_legacy_lightpush.nim @@ -1,7 +1,7 @@ {.used.} import - std/[options, tempfiles, net], + std/[options, tempfiles, net, osproc], testutils/unittests, chronos, std/strformat, @@ -17,8 +17,9 @@ import waku_lightpush_legacy/protocol_metrics, waku_rln_relay, ], - ../testlib/[wakucore, wakunode, testasync, futures], - ../resources/payloads + ../testlib/[wakucore, wakunode, testasync, futures, testutils], + ../resources/payloads, + ../waku_rln_relay/[rln/waku_rln_relay_utils, utils_onchain] suite "Waku Legacy Lightpush - End To End": var @@ -110,6 +111,8 @@ suite "RLN Proofs as a Lightpush Service": server {.threadvar.}: WakuNode client {.threadvar.}: WakuNode + anvilProc {.threadvar.}: Process + manager {.threadvar.}: OnchainGroupManager serverRemotePeerInfo {.threadvar.}: RemotePeerInfo pubsubTopic {.threadvar.}: PubsubTopic @@ -131,13 +134,14 @@ suite "RLN Proofs as a Lightpush Service": server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + anvilProc = runAnvil() + manager = waitFor setupOnchainGroupManager() + # mount rln-relay - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_1"), + index = MembershipIndex(1), ) await allFutures(server.start(), client.start()) @@ -149,6 +153,24 @@ suite "RLN Proofs as a Lightpush Service": await server.mountLegacyLightPush() client.mountLegacyLightPushClient() + let manager1 = cast[OnchainGroupManager](server.wakuRlnRelay.groupManager) + let idCredentials1 = generateCredentials(manager1.rlnInstance) + + try: + waitFor manager1.register(idCredentials1, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated1 = waitFor manager1.updateRoots() + debug "Updated root for node1", rootUpdated1 + + if rootUpdated1: + let proofResult = waitFor manager1.fetchMerkleProofElements() + if proofResult.isErr(): + error "Failed to fetch Merkle proof", error = proofResult.error + manager1.merkleProofCache = proofResult.get() + serverRemotePeerInfo = server.peerInfo.toRemotePeerInfo() pubsubTopic = DefaultPubsubTopic contentTopic = DefaultContentTopic @@ -156,6 +178,7 @@ suite "RLN Proofs as a Lightpush Service": asyncTeardown: await server.stop() + stopAnvil(anvilProc) suite "Lightpush attaching RLN proofs": asyncTest "Message is published when RLN enabled": diff --git a/tests/node/test_wakunode_lightpush.nim b/tests/node/test_wakunode_lightpush.nim index ee68710d1..7ad69716d 100644 --- a/tests/node/test_wakunode_lightpush.nim +++ b/tests/node/test_wakunode_lightpush.nim @@ -1,7 +1,7 @@ {.used.} import - std/[options, tempfiles], + std/[options, tempfiles, osproc], testutils/unittests, chronos, std/strformat, @@ -10,7 +10,8 @@ import import waku/[waku_core, node/peer_manager, node/waku_node, waku_lightpush, waku_rln_relay], ../testlib/[wakucore, wakunode, testasync, futures], - ../resources/payloads + ../resources/payloads, + ../waku_rln_relay/[rln/waku_rln_relay_utils, utils_onchain] const PublishedToOnePeer = 1 @@ -104,6 +105,8 @@ suite "RLN Proofs as a Lightpush Service": server {.threadvar.}: WakuNode client {.threadvar.}: WakuNode + anvilProc {.threadvar.}: Process + manager {.threadvar.}: OnchainGroupManager serverRemotePeerInfo {.threadvar.}: RemotePeerInfo pubsubTopic {.threadvar.}: PubsubTopic @@ -125,13 +128,14 @@ suite "RLN Proofs as a Lightpush Service": server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + anvilProc = runAnvil() + manager = waitFor setupOnchainGroupManager() + # mount rln-relay - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_1"), + index = MembershipIndex(1), ) await allFutures(server.start(), client.start()) @@ -143,6 +147,24 @@ suite "RLN Proofs as a Lightpush Service": await server.mountLightPush() client.mountLightPushClient() + let manager1 = cast[OnchainGroupManager](server.wakuRlnRelay.groupManager) + let idCredentials1 = generateCredentials(manager1.rlnInstance) + + try: + waitFor manager1.register(idCredentials1, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated1 = waitFor manager1.updateRoots() + debug "Updated root for node1", rootUpdated1 + + if rootUpdated1: + let proofResult = waitFor manager1.fetchMerkleProofElements() + if proofResult.isErr(): + error "Failed to fetch Merkle proof", error = proofResult.error + manager1.merkleProofCache = proofResult.get() + serverRemotePeerInfo = server.peerInfo.toRemotePeerInfo() pubsubTopic = DefaultPubsubTopic contentTopic = DefaultContentTopic @@ -150,6 +172,7 @@ suite "RLN Proofs as a Lightpush Service": asyncTeardown: await server.stop() + stopAnvil(anvilProc) suite "Lightpush attaching RLN proofs": asyncTest "Message is published when RLN enabled": diff --git a/tests/waku_rln_relay/rln/test_wrappers.nim b/tests/waku_rln_relay/rln/test_wrappers.nim index f19599e4f..36526570b 100644 --- a/tests/waku_rln_relay/rln/test_wrappers.nim +++ b/tests/waku_rln_relay/rln/test_wrappers.nim @@ -15,7 +15,8 @@ import waku/waku_rln_relay/rln/wrappers, ./waku_rln_relay_utils, ../../testlib/[simple_mock, assertions], - ../../waku_keystore/utils + ../../waku_keystore/utils, + ../../testlib/testutils from std/times import epochTime @@ -98,7 +99,7 @@ suite "RlnConfig": suite "createRLNInstance": test "ok": # When we create the RLN instance - let rlnRes: RLNResult = createRLNInstance(15, "my.db") + let rlnRes: RLNResult = createRLNInstance(15) # Then it succeeds check: @@ -124,7 +125,7 @@ suite "RlnConfig": newCircuitMock # When we create the RLN instance - let rlnRes: RLNResult = createRLNInstance(15, "my.db") + let rlnRes: RLNResult = createRLNInstance(15) # Then it fails check: @@ -133,42 +134,3 @@ suite "RlnConfig": # Cleanup mock(new_circuit): backup - - suite "proofGen": - test "Valid zk proof": - # this test vector is from zerokit - let rlnInstanceRes = createRLNInstanceWrapper() - assertResultOk(rlnInstanceRes) - let rlnInstance = rlnInstanceRes.value - - let identityCredential = defaultIdentityCredential() - assert rlnInstance.insertMember(identityCredential.idCommitment) - - let merkleRootRes = rlnInstance.getMerkleRoot() - assertResultOk(merkleRootRes) - let merkleRoot = merkleRootRes.value - - let proofGenRes = rlnInstance.proofGen( - data = @[], - memKeys = identityCredential, - memIndex = MembershipIndex(0), - epoch = uint64(epochTime() / 1.float64).toEpoch(), - ) - assertResultOk(proofGenRes) - - let - rateLimitProof = proofGenRes.value - proofVerifyRes = rlnInstance.proofVerify( - data = @[], proof = rateLimitProof, validRoots = @[merkleRoot] - ) - - assertResultOk(proofVerifyRes) - assert proofVerifyRes.value, "proof verification failed" - - # Assert the proof fields adhere to the specified types and lengths - check: - typeEq(rateLimitProof.proof, array[256, byte]) - typeEq(rateLimitProof.merkleRoot, array[32, byte]) - typeEq(rateLimitProof.shareX, array[32, byte]) - typeEq(rateLimitProof.shareY, array[32, byte]) - typeEq(rateLimitProof.nullifier, array[32, byte]) diff --git a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim index 383f45c65..e94e45164 100644 --- a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim +++ b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim @@ -1,23 +1,59 @@ import std/tempfiles -import waku/waku_rln_relay, waku/waku_rln_relay/[rln, protocol_types] +import + waku/waku_rln_relay, + waku/waku_rln_relay/[ + group_manager, rln, conversion_utils, constants, protocol_types, protocol_metrics, + nonce_manager, + ] proc createRLNInstanceWrapper*(): RLNResult = return createRlnInstance(tree_path = genTempPath("rln_tree", "waku_rln_relay")) proc unsafeAppendRLNProof*( - rlnPeer: WakuRLNRelay, msg: var WakuMessage, senderEpochTime: float64 + rlnPeer: WakuRLNRelay, msg: var WakuMessage, epoch: Epoch, messageId: MessageId ): RlnRelayResult[void] = - ## this proc derived from appendRLNProof, does not perform nonce check to - ## facilitate bad message id generation for testing + ## Test helper derived from `appendRLNProof`. + ## - Skips nonce validation to intentionally allow generating "bad" message IDs for tests. + ## - Forces a real-time on-chain Merkle root refresh via `updateRoots()` and fetches Merkle + ## proof elements, updating `merkleProofCache` (bypasses `trackRootsChanges`). + ## WARNING: For testing only - let input = msg.toRLNSignal() - let epoch = rlnPeer.calcEpoch(senderEpochTime) + let manager = cast[OnchainGroupManager](rlnPeer.groupManager) + let rootUpdated = waitFor manager.updateRoots() - # we do not fetch a nonce from the nonce manager, - # instead we use 0 as the nonce - let proof = rlnPeer.groupManager.generateProof(input, epoch, 0).valueOr: + # Fetch Merkle proof either when a new root was detected *or* when the cache is empty. + if rootUpdated or manager.merkleProofCache.len == 0: + let proofResult = waitFor manager.fetchMerkleProofElements() + if proofResult.isErr(): + error "Failed to fetch Merkle proof", error = proofResult.error + manager.merkleProofCache = proofResult.get() + + let proof = manager.generateProof(msg.toRLNSignal(), epoch, messageId).valueOr: return err("could not generate rln-v2 proof: " & $error) msg.proof = proof.encode().buffer return ok() + +proc getWakuRlnConfig*( + manager: OnchainGroupManager, + userMessageLimit: uint64 = 1, + epochSizeSec: uint64 = 1, + treePath: string = genTempPath("rln_tree", "waku_rln_relay"), + index: MembershipIndex = MembershipIndex(0), +): WakuRlnConfig = + let wakuRlnConfig = WakuRlnConfig( + dynamic: true, + ethClientUrls: @[EthClient], + ethContractAddress: manager.ethContractAddress, + chainId: manager.chainId, + credIndex: some(index), + userMessageLimit: userMessageLimit, + epochSizeSec: epochSizeSec, + treePath: treePath, + ethPrivateKey: some(manager.ethPrivateKey.get()), + onFatalErrorAction: proc(errStr: string) = + warn "non-fatal onchain test error", errStr + , + ) + return wakuRlnConfig diff --git a/tests/waku_rln_relay/test_all.nim b/tests/waku_rln_relay/test_all.nim index fe23eb9fe..706fff49e 100644 --- a/tests/waku_rln_relay/test_all.nim +++ b/tests/waku_rln_relay/test_all.nim @@ -2,8 +2,6 @@ import ./test_rln_group_manager_onchain, - ./test_rln_group_manager_static, ./test_waku_rln_relay, ./test_wakunode_rln_relay, - ./test_rln_nonce_manager, - ./test_rln_serde + ./test_rln_nonce_manager diff --git a/tests/waku_rln_relay/test_rln_group_manager_onchain.nim b/tests/waku_rln_relay/test_rln_group_manager_onchain.nim index 45f44b242..5d773183a 100644 --- a/tests/waku_rln_relay/test_rln_group_manager_onchain.nim +++ b/tests/waku_rln_relay/test_rln_group_manager_onchain.nim @@ -3,7 +3,7 @@ {.push raises: [].} import - std/[options, sequtils, deques, random, locks], + std/[options, sequtils, deques, random, locks, osproc], results, stew/byteutils, testutils/unittests, @@ -28,24 +28,16 @@ import ../testlib/wakucore, ./utils_onchain -var testLock: Lock -initLock(testLock) - suite "Onchain group manager": + var anvilProc {.threadVar.}: Process + var manager {.threadVar.}: OnchainGroupManager + setup: - # Acquire lock to ensure tests run sequentially - acquire(testLock) - - let runAnvil {.used.} = runAnvil() - - var manager {.threadvar.}: OnchainGroupManager + anvilProc = runAnvil() manager = waitFor setupOnchainGroupManager() teardown: - waitFor manager.stop() - stopAnvil(runAnvil) - # Release lock after test completes - release(testLock) + stopAnvil(anvilProc) test "should initialize successfully": (waitFor manager.init()).isOkOr: @@ -119,11 +111,6 @@ suite "Onchain group manager": (waitFor manager.init()).isErrOr: raiseAssert "Expected error when keystore file doesn't exist" - test "trackRootChanges: start tracking roots": - (waitFor manager.init()).isOkOr: - raiseAssert $error - discard manager.trackRootChanges() - test "trackRootChanges: should guard against uninitialized state": try: discard manager.trackRootChanges() @@ -161,10 +148,9 @@ suite "Onchain group manager": test "trackRootChanges: should fetch history correctly": # TODO: We can't use `trackRootChanges()` directly in this test because its current implementation - # relies on a busy loop rather than event-based monitoring. As a result, some root changes - # may be missed, leading to inconsistent test results (i.e., it may randomly return true or false). - # To ensure reliability, we use the `updateRoots()` function to validate the `validRoots` window - # after each registration. + # relies on a busy loop rather than event-based monitoring. but that busy loop fetch root every 5 seconds + # so we can't use it in this test. + const credentialCount = 6 let credentials = generateCredentials(manager.rlnInstance, credentialCount) (waitFor manager.init()).isOkOr: diff --git a/tests/waku_rln_relay/test_rln_group_manager_static.nim b/tests/waku_rln_relay/test_rln_group_manager_static.nim deleted file mode 100644 index 73dff8a8b..000000000 --- a/tests/waku_rln_relay/test_rln_group_manager_static.nim +++ /dev/null @@ -1,228 +0,0 @@ -{.used.} - -{.push raises: [].} - -import - testutils/unittests, - results, - options, - waku/[ - waku_rln_relay/protocol_types, - waku_rln_relay/rln, - waku_rln_relay/conversion_utils, - waku_rln_relay/group_manager/static/group_manager, - ] - -import chronos, libp2p/crypto/crypto, eth/keys, dnsdisc/builder - -import std/tempfiles - -proc generateCredentials(rlnInstance: ptr RLN): IdentityCredential = - let credRes = membershipKeyGen(rlnInstance) - return credRes.get() - -proc generateCredentials(rlnInstance: ptr RLN, n: int): seq[IdentityCredential] = - var credentials: seq[IdentityCredential] - for i in 0 ..< n: - credentials.add(generateCredentials(rlnInstance)) - return credentials - -suite "Static group manager": - setup: - let rlnInstance = createRlnInstance( - tree_path = genTempPath("rln_tree", "group_manager_static") - ).valueOr: - raiseAssert $error - - let credentials = generateCredentials(rlnInstance, 10) - - let manager {.used.} = StaticGroupManager( - rlnInstance: rlnInstance, - groupSize: 10, - membershipIndex: some(MembershipIndex(5)), - groupKeys: credentials, - ) - - asyncTest "should initialize successfully": - let merkleRootBefore = manager.rlnInstance.getMerkleRoot().valueOr: - raiseAssert $error - - (await manager.init()).isOkOr: - raiseAssert $error - let merkleRootAfter = manager.rlnInstance.getMerkleRoot().valueOr: - raiseAssert $error - - check: - manager.idCredentials.isSome() - manager.groupKeys.len == 10 - manager.groupSize == 10 - manager.membershipIndex == some(MembershipIndex(5)) - manager.groupKeys[5] == manager.idCredentials.get() - manager.latestIndex == 9 - merkleRootAfter.inHex() != merkleRootBefore.inHex() - - asyncTest "startGroupSync: should start group sync": - (await manager.init()).isOkOr: - raiseAssert $error - require: - manager.validRoots.len() == 1 - manager.rlnInstance.getMerkleRoot().get() == manager.validRoots[0] - - (await manager.startGroupSync()).isOkOr: - raiseAssert $error - - asyncTest "startGroupSync: should guard against uninitialized state": - let manager = StaticGroupManager( - groupSize: 0, - membershipIndex: some(MembershipIndex(0)), - groupKeys: @[], - rlnInstance: rlnInstance, - ) - - (await manager.startGroupSync()).isErrOr: - raiseAssert "StartGroupSync: expected error" - - asyncTest "register: should guard against uninitialized state": - let manager = StaticGroupManager( - groupSize: 0, - membershipIndex: some(MembershipIndex(0)), - groupKeys: @[], - rlnInstance: rlnInstance, - ) - - let dummyCommitment = default(IDCommitment) - - try: - await manager.register( - RateCommitment( - idCommitment: dummyCommitment, userMessageLimit: DefaultUserMessageLimit - ) - ) - except ValueError: - assert true - except Exception, CatchableError: - assert false, "exception raised: " & getCurrentExceptionMsg() - - asyncTest "register: should register successfully": - (await manager.init()).isOkOr: - raiseAssert $error - (await manager.startGroupSync()).isOkOr: - raiseAssert $error - - let idCommitment = generateCredentials(manager.rlnInstance).idCommitment - let merkleRootBefore = manager.rlnInstance.getMerkleRoot().valueOr: - raiseAssert $error - try: - await manager.register( - RateCommitment( - idCommitment: idCommitment, userMessageLimit: DefaultUserMessageLimit - ) - ) - except Exception, CatchableError: - assert false, "exception raised: " & getCurrentExceptionMsg() - let merkleRootAfter = manager.rlnInstance.getMerkleRoot().valueOr: - raiseAssert $error - check: - merkleRootAfter.inHex() != merkleRootBefore.inHex() - manager.latestIndex == 10 - - asyncTest "register: callback is called": - var callbackCalled = false - let idCommitment = generateCredentials(manager.rlnInstance).idCommitment - - let fut = newFuture[void]() - - proc callback(registrations: seq[Membership]): Future[void] {.async.} = - require: - registrations.len == 1 - registrations[0].index == 10 - registrations[0].rateCommitment == - RateCommitment( - idCommitment: idCommitment, userMessageLimit: DefaultUserMessageLimit - ) - .toLeaf() - .get() - callbackCalled = true - fut.complete() - - try: - manager.onRegister(callback) - (await manager.init()).isOkOr: - raiseAssert $error - (await manager.startGroupSync()).isOkOr: - raiseAssert $error - await manager.register( - RateCommitment( - idCommitment: idCommitment, userMessageLimit: DefaultUserMessageLimit - ) - ) - except Exception, CatchableError: - assert false, "exception raised: " & getCurrentExceptionMsg() - - await fut - check: - callbackCalled - - asyncTest "withdraw: should guard against uninitialized state": - let idSecretHash = credentials[0].idSecretHash - - try: - await manager.withdraw(idSecretHash) - except ValueError: - assert true - except Exception, CatchableError: - assert false, "exception raised: " & getCurrentExceptionMsg() - - asyncTest "withdraw: should withdraw successfully": - (await manager.init()).isOkOr: - raiseAssert $error - (await manager.startGroupSync()).isOkOr: - raiseAssert $error - - let idSecretHash = credentials[0].idSecretHash - let merkleRootBefore = manager.rlnInstance.getMerkleRoot().valueOr: - raiseAssert $error - - try: - await manager.withdraw(idSecretHash) - except Exception, CatchableError: - assert false, "exception raised: " & getCurrentExceptionMsg() - let merkleRootAfter = manager.rlnInstance.getMerkleRoot().valueOr: - raiseAssert $error - check: - merkleRootAfter.inHex() != merkleRootBefore.inHex() - - asyncTest "withdraw: callback is called": - var callbackCalled = false - let idSecretHash = credentials[0].idSecretHash - let idCommitment = credentials[0].idCommitment - let fut = newFuture[void]() - - proc callback(withdrawals: seq[Membership]): Future[void] {.async.} = - require: - withdrawals.len == 1 - withdrawals[0].index == 0 - withdrawals[0].rateCommitment == - RateCommitment( - idCommitment: idCommitment, userMessageLimit: DefaultUserMessageLimit - ) - .toLeaf() - .get() - - callbackCalled = true - fut.complete() - - try: - manager.onWithdraw(callback) - (await manager.init()).isOkOr: - raiseAssert $error - (await manager.startGroupSync()).isOkOr: - raiseAssert $error - - await manager.withdraw(idSecretHash) - except Exception, CatchableError: - assert false, "exception raised: " & getCurrentExceptionMsg() - - await fut - check: - callbackCalled diff --git a/tests/waku_rln_relay/test_rln_serde.nim b/tests/waku_rln_relay/test_rln_serde.nim deleted file mode 100644 index 1b1d8cd5f..000000000 --- a/tests/waku_rln_relay/test_rln_serde.nim +++ /dev/null @@ -1,68 +0,0 @@ -{.used.} - -{.push raises: [].} - -import results - -import - ./rln/waku_rln_relay_utils, - waku/[ - waku_keystore/protocol_types, - waku_rln_relay, - waku_rln_relay/rln, - waku_rln_relay/protocol_types, - ], - ../waku_keystore/utils, - testutils/unittests - -from std/times import epochTime - -func defaultRateCommitment*(): RateCommitment = - let idCredential = defaultIdentityCredential() - return RateCommitment(idCommitment: idCredential.idCommitment, userMessageLimit: 100) - -suite "RLN Relay v2: serde": - test "toLeaf: converts a rateCommitment to a valid leaf": - # this test vector is from zerokit - let rateCommitment = defaultRateCommitment() - - let leafRes = toLeaf(rateCommitment) - assert leafRes.isOk(), $leafRes.error - - let expectedLeaf = - "09beac7784abfadc9958b3176b352389d0b969ccc7f8bccf3e968ed632e26eca" - check expectedLeaf == leafRes.value.inHex() - - test "proofGen: generates a valid zk proof": - # this test vector is from zerokit - let rlnInstance = createRLNInstanceWrapper() - assert rlnInstance.isOk, $rlnInstance.error - let rln = rlnInstance.value - - let credential = defaultIdentityCredential() - let rateCommitment = defaultRateCommitment() - let success = rln.insertMember(@(rateCommitment.toLeaf().value)) - let merkleRootRes = rln.getMerkleRoot() - assert merkleRootRes.isOk, $merkleRootRes.error - let merkleRoot = merkleRootRes.value - - assert success, "failed to insert member" - - let proofRes = rln.proofGen( - data = @[], - membership = credential, - userMessageLimit = rateCommitment.userMessageLimit, - messageId = 0, - index = 0, - epoch = uint64(epochTime() / 1.float64).toEpoch(), - ) - - assert proofRes.isOk, $proofRes.error - - let proof = proofRes.value - - let proofVerifyRes = - rln.proofVerify(data = @[], proof = proof, validRoots = @[merkleRoot]) - - assert proofVerifyRes.isOk, $proofVerifyRes.error - assert proofVerifyRes.value, "proof verification failed" diff --git a/tests/waku_rln_relay/test_waku_rln_relay.nim b/tests/waku_rln_relay/test_waku_rln_relay.nim index d09764ca2..94a2315e1 100644 --- a/tests/waku_rln_relay/test_waku_rln_relay.nim +++ b/tests/waku_rln_relay/test_waku_rln_relay.nim @@ -1,7 +1,7 @@ {.used.} import - std/[options, os, sequtils, tempfiles], + std/[options, os, sequtils, tempfiles, strutils, osproc], stew/byteutils, testutils/unittests, chronos, @@ -17,11 +17,22 @@ import waku_keystore, ], ./rln/waku_rln_relay_utils, + ./utils_onchain, ../testlib/[wakucore, futures, wakunode, testutils] from std/times import epochTime suite "Waku rln relay": + var anvilProc {.threadVar.}: Process + var manager {.threadVar.}: OnchainGroupManager + + setup: + anvilProc = runAnvil() + manager = waitFor setupOnchainGroupManager() + + teardown: + stopAnvil(anvilProc) + test "key_gen Nim Wrappers": let merkleDepth: csize_t = 20 @@ -68,173 +79,6 @@ suite "Waku rln relay": debug "the generated identity credential: ", idCredential - test "getRoot Nim binding": - # create an RLN instance which also includes an empty Merkle tree - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - - # read the Merkle Tree root - let - root1 {.noinit.}: Buffer = Buffer() - rootPtr1 = unsafeAddr(root1) - getRootSuccessful1 = getRoot(rlnInstance.get(), rootPtr1) - require: - getRootSuccessful1 - root1.len == 32 - - # read the Merkle Tree root - let - root2 {.noinit.}: Buffer = Buffer() - rootPtr2 = unsafeAddr(root2) - getRootSuccessful2 = getRoot(rlnInstance.get(), rootPtr2) - require: - getRootSuccessful2 - root2.len == 32 - - let rootValue1 = cast[ptr array[32, byte]](root1.`ptr`) - let rootHex1 = rootValue1[].inHex - - let rootValue2 = cast[ptr array[32, byte]](root2.`ptr`) - let rootHex2 = rootValue2[].inHex - - # the two roots must be identical - check: - rootHex1 == rootHex2 - test "getMerkleRoot utils": - # create an RLN instance which also includes an empty Merkle tree - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - - # read the Merkle Tree root - let root1 = getMerkleRoot(rln) - require: - root1.isOk() - let rootHex1 = root1.value().inHex - - # read the Merkle Tree root - let root2 = getMerkleRoot(rln) - require: - root2.isOk() - let rootHex2 = root2.value().inHex - - # the two roots must be identical - check: - rootHex1 == rootHex2 - - test "update_next_member Nim Wrapper": - # create an RLN instance which also includes an empty Merkle tree - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - # generate an identity credential - let idCredentialRes = membershipKeyGen(rln) - require: - idCredentialRes.isOk() - - let idCredential = idCredentialRes.get() - let pkBuffer = toBuffer(idCredential.idCommitment) - let pkBufferPtr = unsafeAddr(pkBuffer) - - # add the member to the tree - let memberAdded = updateNextMember(rln, pkBufferPtr) - check: - memberAdded - - test "getMember Nim wrapper": - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - # generate an identity credential - let idCredentialRes = membershipKeyGen(rln) - require: - idCredentialRes.isOk() - - let idCredential = idCredentialRes.get() - let pkBuffer = toBuffer(idCredential.idCommitment) - let pkBufferPtr = unsafeAddr(pkBuffer) - - let - root1 {.noinit.}: Buffer = Buffer() - rootPtr1 = unsafeAddr(root1) - getRootSuccessful1 = getRoot(rlnInstance.get(), rootPtr1) - - # add the member to the tree - let memberAdded = updateNextMember(rln, pkBufferPtr) - require: - memberAdded - - let leafRes = getMember(rln, 0) - require: - leafRes.isOk() - let leaf = leafRes.get() - let leafHex = leaf.inHex() - check: - leafHex == idCredential.idCommitment.inHex() - - test "delete_member Nim wrapper": - # create an RLN instance which also includes an empty Merkle tree - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - # generate an identity credential - let rln = rlnInstance.get() - let idCredentialRes = rln.membershipKeyGen() - require: - idCredentialRes.isOk() - rln.insertMember(idCredentialRes.get().idCommitment) - - # delete the first member - let deletedMemberIndex = MembershipIndex(0) - let deletionSuccess = rln.deleteMember(deletedMemberIndex) - check: - deletionSuccess - - test "insertMembers rln utils": - # create an RLN instance which also includes an empty Merkle tree - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - # generate an identity credential - let idCredentialRes = rln.membershipKeyGen() - require: - idCredentialRes.isOk() - check: - rln.insertMembers(0, @[idCredentialRes.get().idCommitment]) - rln.leavesSet() == 1 - - test "insertMember rln utils": - # create an RLN instance which also includes an empty Merkle tree - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - # generate an identity credential - let idCredentialRes = rln.membershipKeyGen() - require: - idCredentialRes.isOk() - check: - rln.insertMember(idCredentialRes.get().idCommitment) - - test "removeMember rln utils": - # create an RLN instance which also includes an empty Merkle tree - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - - let idCredentialRes = rln.membershipKeyGen() - require: - idCredentialRes.isOk() - rln.insertMember(idCredentialRes.get().idCommitment) - check: - rln.removeMember(MembershipIndex(0)) - test "setMetadata rln utils": # create an RLN instance which also includes an empty Merkle tree let rlnInstance = createRLNInstanceWrapper() @@ -290,135 +134,6 @@ suite "Waku rln relay": check: metadata.isNone() - test "Merkle tree consistency check between deletion and insertion": - # create an RLN instance - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - - let rln = rlnInstance.get() - - # read the Merkle Tree root - let - root1 {.noinit.}: Buffer = Buffer() - rootPtr1 = unsafeAddr(root1) - getRootSuccessful1 = getRoot(rln, rootPtr1) - require: - getRootSuccessful1 - root1.len == 32 - - # generate an identity credential - let idCredentialRes = membershipKeyGen(rln) - require: - idCredentialRes.isOk() - - let idCredential = idCredentialRes.get() - let pkBuffer = toBuffer(idCredential.idCommitment) - let pkBufferPtr = unsafeAddr(pkBuffer) - - # add the member to the tree - let memberAdded = updateNextMember(rln, pkBufferPtr) - require: - memberAdded - - # read the Merkle Tree root after insertion - let - root2 {.noinit.}: Buffer = Buffer() - rootPtr2 = unsafeAddr(root2) - getRootSuccessful = getRoot(rln, rootPtr2) - require: - getRootSuccessful - root2.len == 32 - - # delete the first member - let deletedMemberIndex = MembershipIndex(0) - let deletionSuccess = deleteMember(rln, deletedMemberIndex) - require: - deletionSuccess - - # read the Merkle Tree root after the deletion - let - root3 {.noinit.}: Buffer = Buffer() - rootPtr3 = unsafeAddr(root3) - getRootSuccessful3 = getRoot(rln, rootPtr3) - require: - getRootSuccessful3 - root3.len == 32 - - let rootValue1 = cast[ptr array[32, byte]](root1.`ptr`) - let rootHex1 = rootValue1[].inHex - debug "The initial root", rootHex1 - - let rootValue2 = cast[ptr array[32, byte]](root2.`ptr`) - let rootHex2 = rootValue2[].inHex - debug "The root after insertion", rootHex2 - - let rootValue3 = cast[ptr array[32, byte]](root3.`ptr`) - let rootHex3 = rootValue3[].inHex - debug "The root after deletion", rootHex3 - - # the root must change after the insertion - check: - not (rootHex1 == rootHex2) - - ## The initial root of the tree (empty tree) must be identical to - ## the root of the tree after one insertion followed by a deletion - check: - rootHex1 == rootHex3 - - test "Merkle tree consistency check between deletion and insertion using rln utils": - # create an RLN instance - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - - let rln = rlnInstance.get() - - # read the Merkle Tree root - let root1 = rln.getMerkleRoot() - require: - root1.isOk() - let rootHex1 = root1.value().inHex() - - # generate an identity credential - let idCredentialRes = rln.membershipKeyGen() - require: - idCredentialRes.isOk() - let memberInserted = rln.insertMembers(0, @[idCredentialRes.get().idCommitment]) - require: - memberInserted - - # read the Merkle Tree root after insertion - let root2 = rln.getMerkleRoot() - require: - root2.isOk() - let rootHex2 = root2.value().inHex() - - # delete the first member - let deletedMemberIndex = MembershipIndex(0) - let deletionSuccess = rln.removeMember(deletedMemberIndex) - require: - deletionSuccess - - # read the Merkle Tree root after the deletion - let root3 = rln.getMerkleRoot() - require: - root3.isOk() - let rootHex3 = root3.value().inHex() - - debug "The initial root", rootHex1 - debug "The root after insertion", rootHex2 - debug "The root after deletion", rootHex3 - - # the root must change after the insertion - check: - not (rootHex1 == rootHex2) - - ## The initial root of the tree (empty tree) must be identical to - ## the root of the tree after one insertion followed by a deletion - check: - rootHex1 == rootHex3 - test "hash Nim Wrappers": # create an RLN instance let rlnInstance = createRLNInstanceWrapper() @@ -488,69 +203,6 @@ suite "Waku rln relay": "28a15a991fe3d2a014485c7fa905074bfb55c0909112f865ded2be0a26a932c3" == hashRes.get().inHex() - test "create a list of membership keys and construct a Merkle tree based on the list": - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - - let - groupSize = 100 - memListRes = rln.createMembershipList(groupSize) - - require: - memListRes.isOk() - - let (list, root) = memListRes.get() - - debug "created membership key list", number_of_keys = list.len - debug "Merkle tree root", size_calculated_tree_root = root.len - - check: - list.len == groupSize # check the number of keys - root.len == HashHexSize # check the size of the calculated tree root - - test "check correctness of toIdentityCredentials": - let groupKeys = StaticGroupKeys - - # create a set of IdentityCredentials objects from groupKeys - let groupIdCredentialsRes = groupKeys.toIdentityCredentials() - require: - groupIdCredentialsRes.isOk() - - let groupIdCredentials = groupIdCredentialsRes.get() - # extract the id commitments - let groupIDCommitments = groupIdCredentials.mapIt(it.idCommitment) - # calculate the Merkle tree root out of the extracted id commitments - let rlnInstance = createRLNInstanceWrapper() - require: - rlnInstance.isOk() - let rln = rlnInstance.get() - - # create a Merkle tree - let rateCommitments = - groupIDCommitments.mapIt(RateCommitment(idCommitment: it, userMessageLimit: 20)) - let leaves = rateCommitments.toLeaves().valueOr: - raiseAssert $error - let membersAdded = rln.insertMembers(0, leaves) - - assert membersAdded, "members should be added" - let rawRoot = rln.getMerkleRoot().valueOr: - raiseAssert $error - - let root = rawRoot.inHex() - - debug "groupIdCredentials", num_group_id_credentials = groupIdCredentials.len - # debug "groupIDCommitments", leaving commented in case needed to debug in the future - # groupIDCommitments = groupIDCommitments.mapIt(it.inHex()) - debug "root", root - - check: - # check that the correct number of identity credentials is created - groupIdCredentials.len == StaticGroupSize - # compare the calculated root against the correct root - root == StaticGroupMerkleRoot - test "RateLimitProof Protobuf encode/init test": var proof: ZKSNARK @@ -691,49 +343,51 @@ suite "Waku rln relay": asyncTest "validateMessageAndUpdateLog: against epoch gap": let index = MembershipIndex(5) - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(index), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "waku_rln_relay_2"), - ) - + let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = index) let wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr: raiseAssert $error - let time_1 = epochTime() + let manager = cast[OnchainGroupManager](wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let epoch1 = wakuRlnRelay.getCurrentEpoch() + + # Create messages from the same peer and append RLN proof to them (except wm4) var - # create some messages from the same peer and append rln proof to them, except wm4 wm1 = WakuMessage(payload: "Valid message".toBytes(), timestamp: now()) - # another message in the same epoch as wm1, it will break the messaging rate limit + # Another message in the same epoch as wm1, expected to break the rate limit wm2 = WakuMessage(payload: "Spam message".toBytes(), timestamp: now()) await sleepAsync(1.seconds) - let time_2 = epochTime() + let epoch2 = wakuRlnRelay.getCurrentEpoch() var - # wm3 points to the next epoch bcz of the sleep + # wm3 points to the next epoch due to the sleep wm3 = WakuMessage(payload: "Valid message".toBytes(), timestamp: now()) wm4 = WakuMessage(payload: "Invalid message".toBytes(), timestamp: now()) - wakuRlnRelay.unsafeAppendRLNProof(wm1, time_1).isOkOr: + # Append RLN proofs + wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch1, MessageId(1)).isOkOr: raiseAssert $error - wakuRlnRelay.unsafeAppendRLNProof(wm2, time_1).isOkOr: + wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch1, MessageId(1)).isOkOr: + raiseAssert $error + wakuRlnRelay.unsafeAppendRLNProof(wm3, epoch2, MessageId(3)).isOkOr: raiseAssert $error - wakuRlnRelay.unsafeAppendRLNProof(wm3, time_2).isOkOr: - raiseAssert $error - - # validate messages + # Validate messages let msgValidate1 = wakuRlnRelay.validateMessageAndUpdateLog(wm1) - # wm2 is published within the same Epoch as wm1 and should be found as spam + # wm2 is within the same epoch as wm1 → should be spam msgValidate2 = wakuRlnRelay.validateMessageAndUpdateLog(wm2) - # a valid message should be validated successfully + # wm3 is in the next epoch → should be valid msgValidate3 = wakuRlnRelay.validateMessageAndUpdateLog(wm3) - # wm4 has no rln proof and should not be validated + # wm4 has no RLN proof → should be invalid msgValidate4 = wakuRlnRelay.validateMessageAndUpdateLog(wm4) check: @@ -745,30 +399,41 @@ suite "Waku rln relay": asyncTest "validateMessageAndUpdateLog: against timestamp gap": let index = MembershipIndex(5) - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(index), - userMessageLimit: 10, - epochSizeSec: 10, - treePath: genTempPath("rln_tree", "waku_rln_relay_2"), - ) + let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = index) let wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr: raiseAssert $error - # usually it's 20 seconds but we set it to 2 for testing purposes which make the test faster + let manager = cast[OnchainGroupManager](wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) + + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + # usually it's 20 seconds but we set it to 1 for testing purposes which make the test faster wakuRlnRelay.rlnMaxTimestampGap = 1 - var time = epochTime() + var epoch = wakuRlnRelay.getCurrentEpoch() var - wm1 = WakuMessage(payload: "timestamp message".toBytes(), timestamp: now()) - wm2 = WakuMessage(payload: "timestamp message".toBytes(), timestamp: now()) + wm1 = WakuMessage( + payload: "timestamp message".toBytes(), + contentTopic: DefaultPubsubTopic, + timestamp: now(), + ) + wm2 = WakuMessage( + payload: "timestamp message".toBytes(), + contentTopic: DefaultPubsubTopic, + timestamp: now(), + ) - wakuRlnRelay.unsafeAppendRLNProof(wm1, time).isOkOr: + wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch, MessageId(1)).isOkOr: raiseAssert $error - wakuRlnRelay.unsafeAppendRLNProof(wm2, time).isOkOr: + wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch, MessageId(2)).isOkOr: raiseAssert $error # validate the first message because it's timestamp is the same as the generated timestamp @@ -777,40 +442,43 @@ suite "Waku rln relay": # wait for 2 seconds to make the timestamp different from generated timestamp await sleepAsync(2.seconds) - # invalidate the second message because it's timestamp is different from the generated timestamp let msgValidate2 = wakuRlnRelay.validateMessageAndUpdateLog(wm2) check: msgValidate1 == MessageValidationResult.Valid msgValidate2 == MessageValidationResult.Invalid - asyncTest "validateMessageAndUpdateLog: multiple senders with same external nullifier": + asyncTest "multiple senders with same external nullifier": let index1 = MembershipIndex(5) - let index2 = MembershipIndex(6) - - let rlnConf1 = WakuRlnConfig( - dynamic: false, - credIndex: some(index1), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "waku_rln_relay_3"), - ) - + let rlnConf1 = getWakuRlnConfig(manager = manager, index = index1) let wakuRlnRelay1 = (await WakuRlnRelay.new(rlnConf1)).valueOr: raiseAssert "failed to create waku rln relay: " & $error - let rlnConf2 = WakuRlnConfig( - dynamic: false, - credIndex: some(index2), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "waku_rln_relay_4"), - ) + let manager1 = cast[OnchainGroupManager](wakuRlnRelay1.groupManager) + let idCredentials1 = generateCredentials(manager1.rlnInstance) + try: + waitFor manager1.register(idCredentials1, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let index2 = MembershipIndex(6) + let rlnConf2 = getWakuRlnConfig(manager = manager, index = index2) let wakuRlnRelay2 = (await WakuRlnRelay.new(rlnConf2)).valueOr: raiseAssert "failed to create waku rln relay: " & $error + + let manager2 = cast[OnchainGroupManager](wakuRlnRelay2.groupManager) + let idCredentials2 = generateCredentials(manager2.rlnInstance) + + try: + waitFor manager2.register(idCredentials2, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + # get the current epoch time - let time = epochTime() + let epoch = wakuRlnRelay1.getCurrentEpoch() # create messages from different peers and append rln proofs to them var @@ -820,16 +488,13 @@ suite "Waku rln relay": wm2 = WakuMessage(payload: "Valid message from sender 2".toBytes(), timestamp: now()) - wakuRlnRelay1.appendRLNProof(wm1, time).isOkOr: + wakuRlnRelay1.unsafeAppendRLNProof(wm1, epoch, MessageId(1)).isOkOr: raiseAssert $error - wakuRlnRelay2.appendRLNProof(wm2, time).isOkOr: + wakuRlnRelay2.unsafeAppendRLNProof(wm2, epoch, MessageId(1)).isOkOr: raiseAssert $error - # validate messages - # validateMessage proc checks the validity of the message fields and adds it to the log (if valid) let msgValidate1 = wakuRlnRelay1.validateMessageAndUpdateLog(wm1) - # since this message is from a different sender, it should be validated successfully msgValidate2 = wakuRlnRelay1.validateMessageAndUpdateLog(wm2) check: @@ -942,12 +607,8 @@ suite "Waku rln relay": let index = MembershipIndex(0) proc runTestForEpochSizeSec(rlnEpochSizeSec: uint) {.async.} = - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(index), - userMessageLimit: 1, - epochSizeSec: rlnEpochSizeSec, - treePath: genTempPath("rln_tree", "waku_rln_relay_4"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, index = index, epochSizeSec = rlnEpochSizeSec.uint64 ) let wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr: diff --git a/tests/waku_rln_relay/test_wakunode_rln_relay.nim b/tests/waku_rln_relay/test_wakunode_rln_relay.nim index 8b5a47174..a65d7a394 100644 --- a/tests/waku_rln_relay/test_wakunode_rln_relay.nim +++ b/tests/waku_rln_relay/test_wakunode_rln_relay.nim @@ -1,7 +1,7 @@ {.used.} import - std/[sequtils, tempfiles], + std/[options, os, sequtils, tempfiles, strutils, osproc], stew/byteutils, testutils/unittests, chronicles, @@ -11,26 +11,11 @@ import import waku/[waku_core, waku_node, waku_rln_relay], ../testlib/[wakucore, futures, wakunode, testutils], + ./utils_onchain, ./rln/waku_rln_relay_utils from std/times import epochTime -proc buildWakuRlnConfig( - credIndex: uint, - epochSizeSec: uint64, - treeFilename: string, - userMessageLimit: uint64 = 1, -): WakuRlnConfig = - let treePath = genTempPath("rln_tree", treeFilename) - # Off-chain - return WakuRlnConfig( - dynamic: false, - credIndex: some(credIndex.uint), - userMessageLimit: userMessageLimit, - epochSizeSec: epochSizeSec, - treePath: treePath, - ) - proc waitForNullifierLog(node: WakuNode, expectedLen: int): Future[bool] {.async.} = ## Helper function for i in 0 .. 100: # Try for up to 50 seconds (100 * 500ms) @@ -41,6 +26,16 @@ proc waitForNullifierLog(node: WakuNode, expectedLen: int): Future[bool] {.async procSuite "WakuNode - RLN relay": # NOTE: we set the rlnRelayUserMessageLimit to 1 to make the tests easier to reason about + var anvilProc {.threadVar.}: Process + var manager {.threadVar.}: OnchainGroupManager + + setup: + anvilProc = runAnvil() + manager = waitFor setupOnchainGroupManager() + + teardown: + stopAnvil(anvilProc) + asyncTest "testing rln-relay with valid proof": let # publisher node @@ -61,50 +56,62 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode - let wakuRlnConfig1 = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode"), + let wakuRlnConfig1 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_1"), + index = MembershipIndex(1), ) await node1.mountRlnRelay(wakuRlnConfig1) - await node1.start() + # Registration is mandatory before sending messages with rln-relay + let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let idCredentials1 = generateCredentials(manager1.rlnInstance) + + try: + waitFor manager1.register(idCredentials1, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated1 = waitFor manager1.updateRoots() + debug "Updated root for node1", rootUpdated1 + # node 2 (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode - let wakuRlnConfig2 = WakuRlnConfig( - dynamic: false, - credIndex: some(2.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_2"), + let wakuRlnConfig2 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_2"), + index = MembershipIndex(2), ) await node2.mountRlnRelay(wakuRlnConfig2) - await node2.start() + let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let rootUpdated2 = waitFor manager2.updateRoots() + debug "Updated root for node2", rootUpdated2 + # node 3 (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" - let wakuRlnConfig3 = WakuRlnConfig( - dynamic: false, - credIndex: some(3.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_3"), + let wakuRlnConfig3 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_3"), + index = MembershipIndex(3), ) await node3.mountRlnRelay(wakuRlnConfig3) - await node3.start() + let manager3 = cast[OnchainGroupManager](node3.wakuRlnRelay.groupManager) + let rootUpdated3 = waitFor manager3.updateRoots() + debug "Updated root for node3", rootUpdated3 + # connect them together await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) @@ -138,9 +145,13 @@ procSuite "WakuNode - RLN relay": # prepare the epoch var message = WakuMessage(payload: @payload, contentTopic: contentTopic, timestamp: now()) - doAssert(node1.wakuRlnRelay.unsafeAppendRLNProof(message, epochTime()).isOk()) + doAssert( + node1.wakuRlnRelay + .unsafeAppendRLNProof(message, node1.wakuRlnRelay.getCurrentEpoch(), MessageId(0)) + .isOk() + ) - debug "Nodes participating in the test", + debug " Nodes participating in the test", node1 = shortLog(node1.switch.peerInfo.peerId), node2 = shortLog(node2.switch.peerInfo.peerId), node3 = shortLog(node3.switch.peerInfo.peerId) @@ -149,9 +160,7 @@ procSuite "WakuNode - RLN relay": ## 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 discard await node1.publish(some(DefaultPubsubTopic), message) - await sleepAsync(2000.millis) - - assert (await completionFut.withTimeout(10.seconds)), "completionFut timed out" + assert (await completionFut.withTimeout(15.seconds)), "completionFut timed out" await node1.stop() await node2.stop() @@ -177,18 +186,25 @@ procSuite "WakuNode - RLN relay": # mount rlnrelay in off-chain mode for index, node in nodes: - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(index.uint + 1), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_" & $(index + 1)), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_" & $(index + 1)), + index = MembershipIndex(index + 1), ) await node.mountRlnRelay(wakuRlnConfig) + await node.start() + let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) - # start them - await allFutures(nodes.mapIt(it.start())) + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated = waitFor manager.updateRoots() + debug "Updated root for node", node = index + 1, rootUpdated = rootUpdated # connect them together await nodes[0].connectToNodes(@[nodes[1].switch.peerInfo.toRemotePeerInfo()]) @@ -228,32 +244,34 @@ procSuite "WakuNode - RLN relay": var messages1: seq[WakuMessage] = @[] var messages2: seq[WakuMessage] = @[] - var epochTime = epochTime() - for i in 0 ..< 3: var message = WakuMessage( payload: ("Payload_" & $i).toBytes(), timestamp: now(), contentTopic: contentTopics[0], ) - nodes[0].wakuRlnRelay.unsafeAppendRLNProof(message, epochTime).isOkOr: + + nodes[0].wakuRlnRelay.unsafeAppendRLNProof( + message, nodes[0].wakuRlnRelay.getCurrentEpoch(), MessageId(i.uint8) + ).isOkOr: raiseAssert $error messages1.add(message) - epochTime = epochTime() - for i in 0 ..< 3: var message = WakuMessage( payload: ("Payload_" & $i).toBytes(), timestamp: now(), contentTopic: contentTopics[1], ) - nodes[1].wakuRlnRelay.unsafeAppendRLNProof(message, epochTime).isOkOr: + + nodes[1].wakuRlnRelay.unsafeAppendRLNProof( + message, nodes[1].wakuRlnRelay.getCurrentEpoch(), MessageId(i.uint8) + ).isOkOr: raiseAssert $error messages2.add(message) - # publish 3 messages from node[0] (last 2 are spam, window is 10 secs) - # publish 3 messages from node[1] (last 2 are spam, window is 10 secs) + # publish 3 messages from node[0] (last 2 are spam, window is 10 secs) + # publish 3 messages from node[1] (last 2 are spam, window is 10 secs) for msg in messages1: discard await nodes[0].publish(some($shards[0]), msg) for msg in messages2: @@ -265,8 +283,8 @@ procSuite "WakuNode - RLN relay": # check that node[2] got messages from both topics # and that rln was applied (just 1 msg is rx, rest are spam) check: - rxMessagesTopic1 == 1 - rxMessagesTopic2 == 1 + rxMessagesTopic1 == 3 + rxMessagesTopic2 == 3 await allFutures(nodes.mapIt(it.stop())) @@ -290,49 +308,61 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode - let wakuRlnConfig1 = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_4"), + let wakuRlnConfig1 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_1"), + index = MembershipIndex(1), ) await node1.mountRlnRelay(wakuRlnConfig1) - await node1.start() + let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let idCredentials1 = generateCredentials(manager1.rlnInstance) + + try: + waitFor manager1.register(idCredentials1, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated1 = waitFor manager1.updateRoots() + debug "Updated root for node1", rootUpdated1 + # node 2 (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode - let wakuRlnConfig2 = WakuRlnConfig( - dynamic: false, - credIndex: some(2.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_5"), + let wakuRlnConfig2 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_2"), + index = MembershipIndex(2), ) await node2.mountRlnRelay(wakuRlnConfig2) - await node2.start() + let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let rootUpdated2 = waitFor manager2.updateRoots() + debug "Updated root for node2", rootUpdated2 + # node 3 (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" - let wakuRlnConfig3 = WakuRlnConfig( - dynamic: false, - credIndex: some(3.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_6"), + let wakuRlnConfig3 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_3"), + index = MembershipIndex(3), ) await node3.mountRlnRelay(wakuRlnConfig3) await node3.start() + let manager3 = cast[OnchainGroupManager](node3.wakuRlnRelay.groupManager) + let rootUpdated3 = waitFor manager3.updateRoots() + debug "Updated root for node3", rootUpdated3 + # connect them together await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) @@ -362,37 +392,19 @@ procSuite "WakuNode - RLN relay": await sleepAsync(2000.millis) # prepare the message payload - let payload = "Hello".toBytes() - + let payload = "valid".toBytes() # prepare the epoch let epoch = node1.wakuRlnRelay.getCurrentEpoch() - # prepare the proof - let - contentTopicBytes = contentTopic.toBytes - input = concat(payload, contentTopicBytes) - extraBytes: seq[byte] = @[byte(1), 2, 3] + var message = + WakuMessage(payload: @payload, contentTopic: DefaultPubsubTopic, timestamp: now()) - let nonceManager = node1.wakuRlnRelay.nonceManager - let rateLimitProofRes = node1.wakuRlnRelay.groupManager.generateProof( - concat(input, extraBytes), epoch, MessageId(0) - ) + node1.wakuRlnRelay.unsafeAppendRLNProof(message, epoch, MessageId(0)).isOkOr: + assert false, "Failed to append rln proof: " & $error - assert rateLimitProofRes.isOk(), $rateLimitProofRes.error - # check the proof is generated correctly outside when block to avoid duplication - let rateLimitProof = rateLimitProofRes.get().encode().buffer + # message.payload = "Invalid".toBytes() + message.proof[0] = message.proof[0] xor 0x01 - let message = WakuMessage( - payload: @payload, - contentTopic: contentTopic, - proof: rateLimitProof, - timestamp: now(), - ) - - ## 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 discard await node1.publish(some(DefaultPubsubTopic), message) await sleepAsync(2000.millis) @@ -424,86 +436,109 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode - let wakuRlnConfig1 = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_7"), + let wakuRlnConfig1 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_1"), + index = MembershipIndex(1), ) await node1.mountRlnRelay(wakuRlnConfig1) - await node1.start() + # Registration is mandatory before sending messages with rln-relay + let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let idCredentials1 = generateCredentials(manager1.rlnInstance) + + try: + waitFor manager1.register(idCredentials1, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated1 = waitFor manager1.updateRoots() + debug "Updated root for node1", rootUpdated1 + # node 2 (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode - let wakuRlnConfig2 = WakuRlnConfig( - dynamic: false, - credIndex: some(2.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_8"), + let wakuRlnConfig2 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_2"), + index = MembershipIndex(2), ) await node2.mountRlnRelay(wakuRlnConfig2) await node2.start() + # Registration is mandatory before sending messages with rln-relay + let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let rootUpdated2 = waitFor manager2.updateRoots() + debug "Updated root for node2", rootUpdated2 + # node 3 (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode - let wakuRlnConfig3 = WakuRlnConfig( - dynamic: false, - credIndex: some(3.uint), - userMessageLimit: 1, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_9"), + let wakuRlnConfig3 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_3"), + index = MembershipIndex(3), ) await node3.mountRlnRelay(wakuRlnConfig3) - await node3.start() + # Registration is mandatory before sending messages with rln-relay + let manager3 = cast[OnchainGroupManager](node3.wakuRlnRelay.groupManager) + let rootUpdated3 = waitFor manager3.updateRoots() + debug "Updated root for node3", rootUpdated3 + # 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_1 = epochTime() + let epoch_1 = node1.wakuRlnRelay.getCurrentEpoch() # create some messages with rate limit proofs var wm1 = WakuMessage( - payload: "message 1".toBytes(), timestamp: now(), contentTopic: contentTopic + payload: "message 1".toBytes(), + timestamp: now(), + contentTopic: DefaultPubsubTopic, ) # another message in the same epoch as wm1, it will break the messaging rate limit wm2 = WakuMessage( - payload: "message 2".toBytes(), timestamp: now(), contentTopic: contentTopic + payload: "message 2".toBytes(), + timestamp: now(), + contentTopic: DefaultPubsubTopic, ) # wm3 points to the next epoch await sleepAsync(1000.millis) - let time_2 = epochTime() + let epoch_2 = node1.wakuRlnRelay.getCurrentEpoch() var wm3 = WakuMessage( - payload: "message 3".toBytes(), timestamp: now(), contentTopic: contentTopic + payload: "message 3".toBytes(), + timestamp: now(), + contentTopic: DefaultPubsubTopic, ) wm4 = WakuMessage( - payload: "message 4".toBytes(), timestamp: now(), contentTopic: contentTopic + payload: "message 4".toBytes(), + timestamp: now(), + contentTopic: DefaultPubsubTopic, ) - node3.wakuRlnRelay.unsafeAppendRLNProof(wm1, time_1).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch_1, MessageId(0)).isOkOr: raiseAssert $error - node3.wakuRlnRelay.unsafeAppendRLNProof(wm2, time_1).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch_1, MessageId(0)).isOkOr: raiseAssert $error - node3.wakuRlnRelay.unsafeAppendRLNProof(wm3, time_2).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm3, epoch_2, MessageId(2)).isOkOr: raiseAssert $error # relay handler for node3 @@ -581,15 +616,42 @@ procSuite "WakuNode - RLN relay": # Given both nodes mount relay and rlnrelay (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" - let wakuRlnConfig1 = buildWakuRlnConfig(1, epochSizeSec, "wakunode_10") - (await node1.mountRlnRelay(wakuRlnConfig1)).isOkOr: - assert false, "Failed to mount rlnrelay" + let wakuRlnConfig1 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_1"), + index = MembershipIndex(1), + ) + await node1.mountRlnRelay(wakuRlnConfig1) + await node1.start() + + # Registration is mandatory before sending messages with rln-relay + let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let idCredentials1 = generateCredentials(manager1.rlnInstance) + + try: + waitFor manager1.register(idCredentials1, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated1 = waitFor manager1.updateRoots() + debug "Updated root for node1", rootUpdated1 # Mount rlnrelay in node2 in off-chain mode (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" - let wakuRlnConfig2 = buildWakuRlnConfig(2, epochSizeSec, "wakunode_11") + let wakuRlnConfig2 = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode_2"), + index = MembershipIndex(2), + ) await node2.mountRlnRelay(wakuRlnConfig2) + await node2.start() + + # Registration is mandatory before sending messages with rln-relay + let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let rootUpdated2 = waitFor manager2.updateRoots() + debug "Updated root for node2", rootUpdated2 # Given the two nodes are started and connected waitFor allFutures(node1.start(), node2.start()) @@ -597,12 +659,18 @@ procSuite "WakuNode - RLN relay": # Given some messages var - wm1 = WakuMessage(payload: "message 1".toBytes(), contentTopic: contentTopic) - wm2 = WakuMessage(payload: "message 2".toBytes(), contentTopic: contentTopic) - wm3 = WakuMessage(payload: "message 3".toBytes(), contentTopic: contentTopic) - wm4 = WakuMessage(payload: "message 4".toBytes(), contentTopic: contentTopic) - wm5 = WakuMessage(payload: "message 5".toBytes(), contentTopic: contentTopic) - wm6 = WakuMessage(payload: "message 6".toBytes(), contentTopic: contentTopic) + wm1 = + WakuMessage(payload: "message 1".toBytes(), contentTopic: DefaultPubsubTopic) + wm2 = + WakuMessage(payload: "message 2".toBytes(), contentTopic: DefaultPubsubTopic) + wm3 = + WakuMessage(payload: "message 3".toBytes(), contentTopic: DefaultPubsubTopic) + wm4 = + WakuMessage(payload: "message 4".toBytes(), contentTopic: DefaultPubsubTopic) + wm5 = + WakuMessage(payload: "message 5".toBytes(), contentTopic: DefaultPubsubTopic) + wm6 = + WakuMessage(payload: "message 6".toBytes(), contentTopic: DefaultPubsubTopic) # And node2 mounts a relay handler that completes the respective future when a message is received var @@ -635,14 +703,26 @@ procSuite "WakuNode - RLN relay": # Given all messages have an rln proof and are published by the node 1 let publishSleepDuration: Duration = 5000.millis - let startTime = epochTime() + let epoch_1 = node1.wakuRlnRelay.calcEpoch(epochTime().float64) + let epoch_2 = node1.wakuRlnRelay.calcEpoch( + epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 1 + ) + let epoch_3 = node1.wakuRlnRelay.calcEpoch( + epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 2 + ) + let epoch_4 = node1.wakuRlnRelay.calcEpoch( + epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 3 + ) + let epoch_5 = node1.wakuRlnRelay.calcEpoch( + epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 4 + ) # Epoch 1 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm1, startTime).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch_1, MessageId(0)).isOkOr: raiseAssert $error # Message wm2 is published in the same epoch as wm1, so it'll be considered spam - node1.wakuRlnRelay.unsafeAppendRLNProof(wm2, startTime).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch_1, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm1) @@ -654,7 +734,7 @@ procSuite "WakuNode - RLN relay": # Epoch 2 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm3, startTime + float(1 * epochSizeSec)).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm3, epoch_2, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm3) @@ -666,7 +746,7 @@ procSuite "WakuNode - RLN relay": await node2.waitForNullifierLog(2) # Epoch 3 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm4, startTime + float(2 * epochSizeSec)).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm4, epoch_3, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm4) @@ -676,7 +756,7 @@ procSuite "WakuNode - RLN relay": await node2.waitForNullifierLog(3) # Epoch 4 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm5, startTime + float(3 * epochSizeSec)).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm5, epoch_4, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm5) @@ -686,7 +766,7 @@ procSuite "WakuNode - RLN relay": await node2.waitForNullifierLog(4) # Epoch 5 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm6, startTime + float(4 * epochSizeSec)).isOkOr: + node1.wakuRlnRelay.unsafeAppendRLNProof(wm6, epoch_5, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm6) @@ -707,78 +787,3 @@ procSuite "WakuNode - RLN relay": # Cleanup waitFor allFutures(node1.stop(), node2.stop()) - - asyncTest "Spam Detection and Slashing (currently gossipsub score decrease)": - # Given two nodes - let - contentTopic = ContentTopic("/waku/2/default-content/proto") - shardSeq = @[DefaultRelayShard] - nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) - nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) - epochSizeSec: uint64 = 5 # This means rlnMaxEpochGap = 4 - - # Given both nodes mount relay and rlnrelay - # Mount rlnrelay in node1 in off-chain mode - (await node1.mountRelay()).isOkOr: - assert false, "Failed to mount relay" - let wakuRlnConfig1 = buildWakuRlnConfig(1, epochSizeSec, "wakunode_10") - await node1.mountRlnRelay(wakuRlnConfig1) - - # Mount rlnrelay in node2 in off-chain mode - (await node2.mountRelay()).isOkOr: - assert false, "Failed to mount relay" - let wakuRlnConfig2 = buildWakuRlnConfig(2, epochSizeSec, "wakunode_11") - await node2.mountRlnRelay(wakuRlnConfig2) - - proc simpleHandler( - topic: PubsubTopic, msg: WakuMessage - ): Future[void] {.async, gcsafe.} = - await sleepAsync(0.milliseconds) - - node1.subscribe((kind: PubsubSub, topic: DefaultPubsubTopic), simpleHandler).isOkOr: - assert false, "Failed to subscribe to pubsub topic in node2: " & $error - node2.subscribe((kind: PubsubSub, topic: DefaultPubsubTopic), simpleHandler).isOkOr: - assert false, "Failed to subscribe to pubsub topic in node1: " & $error - - # Given the two nodes are started and connected - waitFor allFutures(node1.start(), node2.start()) - await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) - - # Given some messages with rln proofs - let time = epochTime() - var - msg1 = WakuMessage( - payload: "message 1".toBytes(), timestamp: now(), contentTopic: contentTopic - ) - msg2 = WakuMessage( - payload: "message 2".toBytes(), timestamp: now(), contentTopic: contentTopic - ) - - node1.wakuRlnRelay.unsafeAppendRLNProof(msg1, time).isOkOr: - raiseAssert $error - # Message wm2 is published in the same epoch as wm1, so it'll be considered spam - node1.wakuRlnRelay.unsafeAppendRLNProof(msg2, time).isOkOr: - raiseAssert $error - - # When publishing the first message (valid) - discard await node1.publish(some(DefaultPubsubTopic), msg1) - await sleepAsync(FUTURE_TIMEOUT_SCORING) # Wait for scoring - - # Then the score of node2 should increase - check: - node1.wakuRelay.peerStats[node2.switch.peerInfo.peerId].score == 0.1 - node2.wakuRelay.peerStats[node1.switch.peerInfo.peerId].score == 1.1 - - # When publishing the second message (spam) - discard await node1.publish(some(DefaultPubsubTopic), msg2) - await sleepAsync(FUTURE_TIMEOUT_SCORING) - - # Then the score of node2 should decrease - check: - node1.wakuRelay.peerStats[node2.switch.peerInfo.peerId].score == 0.1 - node2.wakuRelay.peerStats[node1.switch.peerInfo.peerId].score == -99.4 - - await node1.stop() - await node2.stop() diff --git a/tests/waku_rln_relay/utils_static.nim b/tests/waku_rln_relay/utils_offchain.nim similarity index 100% rename from tests/waku_rln_relay/utils_static.nim rename to tests/waku_rln_relay/utils_offchain.nim diff --git a/tests/wakunode_rest/test_rest_health.nim b/tests/wakunode_rest/test_rest_health.nim index 964e09c5b..b37fd2b26 100644 --- a/tests/wakunode_rest/test_rest_health.nim +++ b/tests/wakunode_rest/test_rest_health.nim @@ -1,7 +1,7 @@ {.used.} import - std/tempfiles, + std/[tempfiles, osproc], testutils/unittests, presto, presto/client as presto_client, @@ -23,7 +23,8 @@ import ], ../testlib/common, ../testlib/wakucore, - ../testlib/wakunode + ../testlib/wakunode, + ../waku_rln_relay/[rln/waku_rln_relay_utils, utils_onchain] proc testWakuNode(): WakuNode = let @@ -36,6 +37,16 @@ proc testWakuNode(): WakuNode = suite "Waku v2 REST API - health": # TODO: better test for health + var anvilProc {.threadVar.}: Process + var manager {.threadVar.}: OnchainGroupManager + + setup: + anvilProc = runAnvil() + manager = waitFor setupOnchainGroupManager() + + teardown: + stopAnvil(anvilProc) + asyncTest "Get node health info - GET /health": # Given let node = testWakuNode() @@ -67,11 +78,10 @@ suite "Waku v2 REST API - health": # now kick in rln (currently the only check for health) await node.mountRlnRelay( - WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode"), + getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "wakunode"), + index = MembershipIndex(1), ) ) diff --git a/tests/wakunode_rest/test_rest_relay.nim b/tests/wakunode_rest/test_rest_relay.nim index 26a4c0b63..c2a9e132d 100644 --- a/tests/wakunode_rest/test_rest_relay.nim +++ b/tests/wakunode_rest/test_rest_relay.nim @@ -1,7 +1,7 @@ {.used.} import - std/[sequtils, strformat, tempfiles], + std/[sequtils, strformat, tempfiles, osproc], stew/byteutils, testutils/unittests, presto, @@ -24,7 +24,8 @@ import ], ../testlib/wakucore, ../testlib/wakunode, - ../resources/payloads + ../resources/payloads, + ../waku_rln_relay/[rln/waku_rln_relay_utils, utils_onchain] proc testWakuNode(): WakuNode = let @@ -36,6 +37,16 @@ proc testWakuNode(): WakuNode = newTestWakuNode(privkey, bindIp, port, some(extIp), some(port)) suite "Waku v2 Rest API - Relay": + var anvilProc {.threadVar.}: Process + var manager {.threadVar.}: OnchainGroupManager + + setup: + anvilProc = runAnvil() + manager = waitFor setupOnchainGroupManager() + + teardown: + stopAnvil(anvilProc) + asyncTest "Subscribe a node to an array of pubsub topics - POST /relay/v1/subscriptions": # Given let node = testWakuNode() @@ -246,18 +257,33 @@ suite "Waku v2 Rest API - Relay": ## "Relay API: publish and subscribe/unsubscribe": # Given let node = testWakuNode() - await node.start() (await node.mountRelay()).isOkOr: assert false, "Failed to mount relay" - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 20, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_1"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "waku_rest_relay_1"), + index = MembershipIndex(1), ) await node.mountRlnRelay(wakuRlnConfig) + await node.start() + # Registration is mandatory before sending messages with rln-relay + let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) + + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated = waitFor manager.updateRoots() + debug "Updated root for node", rootUpdated + + let proofRes = waitFor manager.fetchMerkleProofElements() + if proofRes.isErr(): + assert false, "failed to fetch merkle proof: " & proofRes.error + manager.merkleProofCache = proofRes.get() # RPC server setup var restPort = Port(0) @@ -484,20 +510,35 @@ suite "Waku v2 Rest API - Relay": ## "Relay API: publish and subscribe/unsubscribe": # Given let node = testWakuNode() - await node.start() (await node.mountRelay()).isOkOr: assert false, "Failed to mount relay" require node.mountAutoSharding(1, 8).isOk - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 20, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_1"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "waku_rest_relay_1"), + index = MembershipIndex(1), ) await node.mountRlnRelay(wakuRlnConfig) + await node.start() + # Registration is mandatory before sending messages with rln-relay + let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) + + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated = waitFor manager.updateRoots() + debug "Updated root for node", rootUpdated + + let proofRes = waitFor manager.fetchMerkleProofElements() + if proofRes.isErr(): + assert false, "failed to fetch merkle proof: " & proofRes.error + manager.merkleProofCache = proofRes.get() # RPC server setup var restPort = Port(0) @@ -545,20 +586,35 @@ suite "Waku v2 Rest API - Relay": ## "Relay API: publish and subscribe/unsubscribe": # Given let node = testWakuNode() - await node.start() (await node.mountRelay()).isOkOr: assert false, "Failed to mount relay" require node.mountAutoSharding(1, 8).isOk - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 20, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_1"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "waku_rest_relay_1"), + index = MembershipIndex(1), ) - await node.mountRlnRelay(wakuRlnConfig) + await node.start() + + # Registration is mandatory before sending messages with rln-relay + let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) + + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated = waitFor manager.updateRoots() + debug "Updated root for node", rootUpdated + + let proofRes = waitFor manager.fetchMerkleProofElements() + if proofRes.isErr(): + assert false, "failed to fetch merkle proof: " & proofRes.error + manager.merkleProofCache = proofRes.get() # RPC server setup var restPort = Port(0) @@ -598,18 +654,33 @@ suite "Waku v2 Rest API - Relay": asyncTest "Post a message larger than maximum size - POST /relay/v1/messages/{topic}": # Given let node = testWakuNode() - await node.start() (await node.mountRelay()).isOkOr: assert false, "Failed to mount relay" - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 20, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_1"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "waku_rest_relay_1"), + index = MembershipIndex(1), ) - await node.mountRlnRelay(wakuRlnConfig) + await node.start() + + # Registration is mandatory before sending messages with rln-relay + let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) + + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated = waitFor manager.updateRoots() + debug "Updated root for node", rootUpdated + + let proofRes = waitFor manager.fetchMerkleProofElements() + if proofRes.isErr(): + assert false, "failed to fetch merkle proof: " & proofRes.error + manager.merkleProofCache = proofRes.get() # RPC server setup var restPort = Port(0) @@ -660,20 +731,35 @@ suite "Waku v2 Rest API - Relay": asyncTest "Post a message larger than maximum size - POST /relay/v1/auto/messages/{topic}": # Given let node = testWakuNode() - await node.start() (await node.mountRelay()).isOkOr: assert false, "Failed to mount relay" require node.mountAutoSharding(1, 8).isOk - let wakuRlnConfig = WakuRlnConfig( - dynamic: false, - credIndex: some(1.uint), - userMessageLimit: 20, - epochSizeSec: 1, - treePath: genTempPath("rln_tree", "wakunode_1"), + let wakuRlnConfig = getWakuRlnConfig( + manager = manager, + treePath = genTempPath("rln_tree", "waku_rest_relay_1"), + index = MembershipIndex(1), ) - await node.mountRlnRelay(wakuRlnConfig) + await node.start() + + # Registration is mandatory before sending messages with rln-relay + let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let idCredentials = generateCredentials(manager.rlnInstance) + + try: + waitFor manager.register(idCredentials, UserMessageLimit(20)) + except Exception, CatchableError: + assert false, + "exception raised when calling register: " & getCurrentExceptionMsg() + + let rootUpdated = waitFor manager.updateRoots() + debug "Updated root for node", rootUpdated + + let proofRes = waitFor manager.fetchMerkleProofElements() + if proofRes.isErr(): + assert false, "failed to fetch merkle proof: " & proofRes.error + manager.merkleProofCache = proofRes.get() # RPC server setup var restPort = Port(0) diff --git a/tools/rln_db_inspector/rln_db_inspector.nim b/tools/rln_db_inspector/rln_db_inspector.nim index e1d093e86..f62f8ab68 100644 --- a/tools/rln_db_inspector/rln_db_inspector.nim +++ b/tools/rln_db_inspector/rln_db_inspector.nim @@ -18,7 +18,7 @@ proc doInspectRlnDb*(conf: InspectRlnDbConf) = trace "configuration", conf = $conf # 2. initialize rlnInstance - let rlnInstance = createRLNInstance(d = 20, tree_path = conf.treePath).valueOr: + let rlnInstance = createRLNInstance(d = 20).valueOr: error "failure while creating RLN instance", error quit(1) @@ -38,29 +38,4 @@ proc doInspectRlnDb*(conf: InspectRlnDbConf) = contractAddress = metadata.contractAddress, validRoots = metadata.validRoots.mapIt(it.inHex()) - var index: uint = 0 - var hits: uint = 0 - var zeroLeafIndices: seq[uint] = @[] - var assumeEmptyAfter: uint = 10 - while true: - let leaf = rlnInstance.getMember(index).valueOr: - error "failure while getting RLN leaf", error - quit(1) - - if leaf.inHex() == "0000000000000000000000000000000000000000000000000000000000000000": - zeroLeafIndices.add(index) - hits = hits + 1 - else: - hits = 0 - - if hits > assumeEmptyAfter: - info "reached end of RLN tree", index = index - assumeEmptyAfter - # remove zeroLeafIndices that are not at the end of the tree - zeroLeafIndices = zeroLeafIndices.filterIt(it < index - assumeEmptyAfter) - break - - index = index + 1 - - info "zero leaf indices", zeroLeafIndices - quit(0) diff --git a/tools/rln_keystore_generator/rln_keystore_generator.nim b/tools/rln_keystore_generator/rln_keystore_generator.nim index ee5911abf..a6181a1f5 100644 --- a/tools/rln_keystore_generator/rln_keystore_generator.nim +++ b/tools/rln_keystore_generator/rln_keystore_generator.nim @@ -31,9 +31,7 @@ proc doRlnKeystoreGenerator*(conf: RlnKeystoreGeneratorConf) = trace "configuration", conf = $conf # 2. initialize rlnInstance - let rlnInstanceRes = createRLNInstance( - d = 20, tree_path = genTempPath("rln_tree", "rln_keystore_generator") - ) + let rlnInstanceRes = createRLNInstance(d = 20) if rlnInstanceRes.isErr(): error "failure while creating RLN instance", error = rlnInstanceRes.error quit(1) diff --git a/waku.nimble b/waku.nimble index 8014621f5..9f98b52be 100644 --- a/waku.nimble +++ b/waku.nimble @@ -116,7 +116,7 @@ task wakunode2, "Build Waku v2 cli node": task benchmarks, "Some benchmarks": let name = "benchmarks" - buildBinary name, "apps/benchmarks/" + buildBinary name, "apps/benchmarks/", "-p:../.." task wakucanary, "Build waku-canary tool": let name = "wakucanary" diff --git a/waku/waku_rln_relay/group_manager.nim b/waku/waku_rln_relay/group_manager.nim index db825db5f..14fda5496 100644 --- a/waku/waku_rln_relay/group_manager.nim +++ b/waku/waku_rln_relay/group_manager.nim @@ -1,3 +1,3 @@ -import group_manager/[static, on_chain] +import group_manager/[on_chain] -export static, on_chain +export on_chain diff --git a/waku/waku_rln_relay/group_manager/group_manager_base.nim b/waku/waku_rln_relay/group_manager/group_manager_base.nim index 4a1c84e55..9ddcdee54 100644 --- a/waku/waku_rln_relay/group_manager/group_manager_base.nim +++ b/waku/waku_rln_relay/group_manager/group_manager_base.nim @@ -119,58 +119,23 @@ method stop*(g: GroupManager): Future[void] {.base, async.} = method onWithdraw*(g: GroupManager, cb: OnWithdrawCallback) {.base, gcsafe.} = g.withdrawCb = some(cb) -proc slideRootQueue*( - rootQueue: var Deque[MerkleNode], root: MerkleNode -): seq[MerkleNode] = - ## updates the root queue with the latest root and pops the oldest one when the capacity of `AcceptableRootWindowSize` is reached - let overflowCount = rootQueue.len - AcceptableRootWindowSize + 1 - var overflowedRoots = newSeq[MerkleNode]() - if overflowCount > 0: - # Delete the oldest `overflowCount` roots in the deque (index 0..`overflowCount`) - # insert into overflowedRoots seq and return - for i in 0 ..< overflowCount: - overFlowedRoots.add(rootQueue.popFirst()) - # Push the next root into the queue - rootQueue.addLast(root) - return overFlowedRoots - method indexOfRoot*( g: GroupManager, root: MerkleNode ): int {.base, gcsafe, raises: [].} = - ## returns the index of the root in the merkle tree. - ## returns -1 if the root is not found + ## returns the index of the root in the merkle tree and returns -1 if the root is not found return g.validRoots.find(root) method validateRoot*( g: GroupManager, root: MerkleNode ): bool {.base, gcsafe, raises: [].} = ## validates the root against the valid roots queue - if g.indexOfRoot(root) >= 0: - return true - return false - -template slideRootQueue*(g: GroupManager): untyped = - let rootRes = g.rlnInstance.getMerkleRoot() - if rootRes.isErr(): - raise newException(ValueError, "failed to get merkle root") - let rootAfterUpdate = rootRes.get() - - var rootBuffer: Deque[MerkleNode] - let overflowedRoots = slideRootQueue(g.validRoots, rootAfterUpdate) - if overflowedRoots.len > 0: - for root in overflowedRoots: - discard rootBuffer.slideRootQueue(root) - rootBuffer + return g.indexOfRoot(root) >= 0 method verifyProof*( - g: GroupManager, input: openArray[byte], proof: RateLimitProof + g: GroupManager, input: seq[byte], proof: RateLimitProof ): GroupManagerResult[bool] {.base, gcsafe, raises: [].} = - ## verifies the proof against the input and the current merkle root - let proofVerifyRes = - g.rlnInstance.proofVerify(input, proof, g.validRoots.items().toSeq()) - if proofVerifyRes.isErr(): - return err("proof verification failed: " & $proofVerifyRes.error()) - return ok(proofVerifyRes.value()) + ## Dummy implementation for verifyProof + return err("verifyProof is not implemented") method generateProof*( g: GroupManager, @@ -179,29 +144,8 @@ method generateProof*( messageId: MessageId, rlnIdentifier = DefaultRlnIdentifier, ): GroupManagerResult[RateLimitProof] {.base, gcsafe, raises: [].} = - var lastProcessedEpoch {.global.}: Epoch - ## generates a proof for the given data and epoch - ## the proof is generated using the current merkle root - if g.idCredentials.isNone(): - return err("identity credentials are not set") - if g.membershipIndex.isNone(): - return err("membership index is not set") - if g.userMessageLimit.isNone(): - return err("user message limit is not set") - - waku_rln_proof_generation_duration_seconds.nanosecondTime: - let proof = proofGen( - rlnInstance = g.rlnInstance, - data = data, - membership = g.idCredentials.get(), - index = g.membershipIndex.get(), - epoch = epoch, - userMessageLimit = g.userMessageLimit.get(), - messageId = messageId, - ).valueOr: - return err("proof generation failed: " & $error) - - return ok(proof) + ## Dummy implementation for generateProof + return err("generateProof is not implemented") method isReady*(g: GroupManager): Future[bool] {.base, async.} = raise newException( diff --git a/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim b/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim index cbf42d2d0..6b840323e 100644 --- a/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim +++ b/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim @@ -173,11 +173,6 @@ template retryWrapper( retryWrapper(res, RetryStrategy.new(), errStr, g.onFatalErrorAction): body -method validateRoot*(g: OnchainGroupManager, root: MerkleNode): bool = - if g.validRoots.find(root) >= 0: - return true - return false - proc updateRoots*(g: OnchainGroupManager): Future[bool] {.async.} = let rootRes = await g.fetchMerkleRoot() if rootRes.isErr(): @@ -200,21 +195,17 @@ proc updateRoots*(g: OnchainGroupManager): Future[bool] {.async.} = proc trackRootChanges*(g: OnchainGroupManager) {.async: (raises: [CatchableError]).} = try: initializedGuard(g) - let ethRpc = g.ethRpc.get() - let wakuRlnContract = g.wakuRlnContract.get() - const rpcDelay = 5.seconds while true: + await sleepAsync(rpcDelay) let rootUpdated = await g.updateRoots() if rootUpdated: - if g.membershipIndex.isNone(): - error "membershipIndex is not set; skipping proof update" + let proofResult = await g.fetchMerkleProofElements() + if proofResult.isErr(): + error "Failed to fetch Merkle proof", error = proofResult.error else: - let proofResult = await g.fetchMerkleProofElements() - if proofResult.isErr(): - error "Failed to fetch Merkle proof", error = proofResult.error g.merkleProofCache = proofResult.get() let nextFreeIndex = await g.fetchNextFreeIndex() @@ -226,8 +217,6 @@ proc trackRootChanges*(g: OnchainGroupManager) {.async: (raises: [CatchableError let memberCount = cast[int64](nextFreeIndex.get()) waku_rln_number_registered_memberships.set(float64(memberCount)) - - await sleepAsync(rpcDelay) except CatchableError: error "Fatal error in trackRootChanges", error = getCurrentExceptionMsg() @@ -457,7 +446,7 @@ method generateProof*( nullifier: nullifier, ) - debug "Proof generated successfully" + debug "Proof generated successfully", proof = output waku_rln_remaining_proofs_per_epoch.dec() waku_rln_total_generated_proofs.inc() @@ -493,7 +482,7 @@ method verifyProof*( if not ffiOk: return err("could not verify the proof") else: - trace "Proof verified successfully !" + debug "Proof verified successfully" return ok(validProof) diff --git a/waku/waku_rln_relay/group_manager/static.nim b/waku/waku_rln_relay/group_manager/static.nim deleted file mode 100644 index 0d385c298..000000000 --- a/waku/waku_rln_relay/group_manager/static.nim +++ /dev/null @@ -1,3 +0,0 @@ -import static/group_manager - -export group_manager diff --git a/waku/waku_rln_relay/group_manager/static/group_manager.nim b/waku/waku_rln_relay/group_manager/static/group_manager.nim deleted file mode 100644 index 8eb0b0a71..000000000 --- a/waku/waku_rln_relay/group_manager/static/group_manager.nim +++ /dev/null @@ -1,144 +0,0 @@ -import ../group_manager_base, ../../rln, std/sequtils, ../../constants - -export group_manager_base - -type StaticGroupManager* = ref object of GroupManager - groupKeys*: seq[IdentityCredential] - groupSize*: uint - -template initializedGuard*(g: StaticGroupManager): untyped = - if not g.initialized: - raise newException(ValueError, "StaticGroupManager is not initialized") - -proc resultifiedInitGuard(g: StaticGroupManager): GroupManagerResult[void] = - try: - initializedGuard(g) - return ok() - except CatchableError: - return err("StaticGroupManager is not initialized") - -method init*(g: StaticGroupManager): Future[GroupManagerResult[void]] {.async.} = - let - groupSize = g.groupSize - groupKeys = g.groupKeys - membershipIndex = - if g.membershipIndex.isSome(): - g.membershipIndex.get() - else: - return err("membershipIndex is not set") - - if membershipIndex < MembershipIndex(0) or - membershipIndex >= MembershipIndex(groupSize): - return err( - "Invalid membership index. Must be within 0 and " & $(groupSize - 1) & "but was " & - $membershipIndex - ) - g.userMessageLimit = some(DefaultUserMessageLimit) - - g.idCredentials = some(groupKeys[membershipIndex]) - # Seed the received commitments into the merkle tree - let rateCommitments = groupKeys.mapIt( - RateCommitment( - idCommitment: it.idCommitment, userMessageLimit: g.userMessageLimit.get() - ) - ) - let leaves = rateCommitments.toLeaves().valueOr: - return err("Failed to convert rate commitments to leaves: " & $error) - let membersInserted = g.rlnInstance.insertMembers(g.latestIndex, leaves) - - discard g.slideRootQueue() - - g.latestIndex += MembershipIndex(groupKeys.len - 1) - - g.initialized = true - - return ok() - -method startGroupSync*( - g: StaticGroupManager -): Future[GroupManagerResult[void]] {.async.} = - ?g.resultifiedInitGuard() - # No-op - return ok() - -method register*( - g: StaticGroupManager, rateCommitment: RateCommitment -): Future[void] {.async: (raises: [Exception]).} = - initializedGuard(g) - - let leaf = rateCommitment.toLeaf().get() - - await g.registerBatch(@[leaf]) - -method registerBatch*( - g: StaticGroupManager, rateCommitments: seq[RawRateCommitment] -): Future[void] {.async: (raises: [Exception]).} = - initializedGuard(g) - - let membersInserted = g.rlnInstance.insertMembers(g.latestIndex + 1, rateCommitments) - if not membersInserted: - raise newException(ValueError, "Failed to insert members into the merkle tree") - - if g.registerCb.isSome(): - var memberSeq = newSeq[Membership]() - for i in 0 ..< rateCommitments.len: - memberSeq.add( - Membership( - rateCommitment: rateCommitments[i], - index: g.latestIndex + MembershipIndex(i) + 1, - ) - ) - await g.registerCb.get()(memberSeq) - - discard g.slideRootQueue() - - g.latestIndex += MembershipIndex(rateCommitments.len) - return - -method withdraw*( - g: StaticGroupManager, idSecretHash: IdentitySecretHash -): Future[void] {.async: (raises: [Exception]).} = - initializedGuard(g) - - let groupKeys = g.groupKeys - - for i in 0 ..< groupKeys.len: - if groupKeys[i].idSecretHash == idSecretHash: - let idCommitment = groupKeys[i].idCommitment - let index = MembershipIndex(i) - let rateCommitment = RateCommitment( - idCommitment: idCommitment, userMessageLimit: g.userMessageLimit.get() - ).toLeaf().valueOr: - raise newException(ValueError, "Failed to parse rateCommitment") - let memberRemoved = g.rlnInstance.removeMember(index) - if not memberRemoved: - raise newException(ValueError, "Failed to remove member from the merkle tree") - - if g.withdrawCb.isSome(): - let withdrawCb = g.withdrawCb.get() - await withdrawCb(@[Membership(rateCommitment: rateCommitment, index: index)]) - - return - -method withdrawBatch*( - g: StaticGroupManager, idSecretHashes: seq[IdentitySecretHash] -): Future[void] {.async: (raises: [Exception]).} = - initializedGuard(g) - - # call withdraw on each idSecretHash - for idSecretHash in idSecretHashes: - await g.withdraw(idSecretHash) - -method onRegister*(g: StaticGroupManager, cb: OnRegisterCallback) {.gcsafe.} = - g.registerCb = some(cb) - -method onWithdraw*(g: StaticGroupManager, cb: OnWithdrawCallback) {.gcsafe.} = - g.withdrawCb = some(cb) - -method stop*(g: StaticGroupManager): Future[void] {.async.} = - initializedGuard(g) - # No-op - -method isReady*(g: StaticGroupManager): Future[bool] {.async.} = - initializedGuard(g) - return true diff --git a/waku/waku_rln_relay/rln/rln_interface.nim b/waku/waku_rln_relay/rln/rln_interface.nim index 27b3bbee9..98d415657 100644 --- a/waku/waku_rln_relay/rln/rln_interface.nim +++ b/waku/waku_rln_relay/rln/rln_interface.nim @@ -21,81 +21,6 @@ proc toBuffer*(x: openArray[byte]): Buffer = ## RLN Zerokit module APIs ###################################################################### -#------------------------------ Merkle Tree operations ----------------------------------------- -proc update_next_member*( - ctx: ptr RLN, input_buffer: ptr Buffer -): bool {.importc: "set_next_leaf".} - -## adds an element in the merkle tree to the next available position -## input_buffer points to the id commitment byte seq -## the return bool value indicates the success or failure of the operation - -proc delete_member*(ctx: ptr RLN, index: uint): bool {.importc: "delete_leaf".} -## index is the position of the id commitment key to be deleted from the tree -## the deleted id commitment key is replaced with a zero leaf -## the return bool value indicates the success or failure of the operation - -proc get_root*(ctx: ptr RLN, output_buffer: ptr Buffer): bool {.importc: "get_root".} -## get_root populates the passed pointer output_buffer with the current tree root -## the output_buffer holds the Merkle tree root of size 32 bytes -## the return bool value indicates the success or failure of the operation - -proc get_merkle_proof*( - ctx: ptr RLN, index: uint, output_buffer: ptr Buffer -): bool {.importc: "get_proof".} - -## populates the passed pointer output_buffer with the merkle proof for the leaf at position index in the tree stored by ctx -## the output_buffer holds a serialized Merkle proof (vector of 32 bytes nodes) -## the return bool value indicates the success or failure of the operation - -proc set_leaf*( - ctx: ptr RLN, index: uint, input_buffer: ptr Buffer -): bool {.importc: "set_leaf".} - -## sets the leaf at position index in the tree stored by ctx to the value passed by input_buffer -## the input_buffer holds a serialized leaf of 32 bytes -## the return bool value indicates the success or failure of the operation - -proc get_leaf*( - ctx: ptr RLN, index: uint, output_buffer: ptr Buffer -): bool {.importc: "get_leaf".} - -## gets the leaf at position index in the tree stored by ctx -## the output_buffer holds a serialized leaf of 32 bytes -## the return bool value indicates the success or failure of the operation - -proc leaves_set*(ctx: ptr RLN): uint {.importc: "leaves_set".} -## gets the number of leaves set in the tree stored by ctx -## the return uint value indicates the number of leaves set in the tree - -proc init_tree_with_leaves*( - ctx: ptr RLN, input_buffer: ptr Buffer -): bool {.importc: "init_tree_with_leaves".} - -## sets multiple leaves in the tree stored by ctx to the value passed by input_buffer -## the input_buffer holds a serialized vector of leaves (32 bytes each) -## the input_buffer size is prefixed by a 8 bytes integer indicating the number of leaves -## leaves are set one after each other starting from index 0 -## the return bool value indicates the success or failure of the operation - -proc atomic_write*( - ctx: ptr RLN, index: uint, leaves_buffer: ptr Buffer, indices_buffer: ptr Buffer -): bool {.importc: "atomic_operation".} - -## sets multiple leaves, and zeroes out indices in the tree stored by ctx to the value passed by input_buffer -## the leaves_buffer holds a serialized vector of leaves (32 bytes each) -## the leaves_buffer size is prefixed by a 8 bytes integer indicating the number of leaves -## the indices_bufffer holds a serialized vector of indices (8 bytes each) -## the indices_buffer size is prefixed by a 8 bytes integer indicating the number of indices -## leaves are set one after each other starting from index `index` -## the return bool value indicates the success or failure of the operation - -proc reset_tree*(ctx: ptr RLN, tree_height: uint): bool {.importc: "set_tree".} -## resets the tree stored by ctx to the empty tree (all leaves set to 0) of height tree_height -## the return bool value indicates the success or failure of the operation - -#---------------------------------------------------------------------------------------------- - #-------------------------------- zkSNARKs operations ----------------------------------------- proc key_gen*( ctx: ptr RLN, output_buffer: ptr Buffer diff --git a/waku/waku_rln_relay/rln/wrappers.nim b/waku/waku_rln_relay/rln/wrappers.nim index 2e10c7e37..acbac7df4 100644 --- a/waku/waku_rln_relay/rln/wrappers.nim +++ b/waku/waku_rln_relay/rln/wrappers.nim @@ -98,7 +98,7 @@ proc createRLNInstanceLocal( mode: "high_throughput", compression: false, flush_every_ms: 500, - path: if tree_path != "": tree_path else: DefaultRlnTreePath, + path: tree_path, ), ) @@ -201,214 +201,6 @@ proc extractMetadata*(proof: RateLimitProof): RlnRelayResult[ProofMetadata] = ) ) -proc proofGen*( - rlnInstance: ptr RLN, - data: openArray[byte], - membership: IdentityCredential, - userMessageLimit: UserMessageLimit, - messageId: MessageId, - index: MembershipIndex, - epoch: Epoch, - rlnIdentifier = DefaultRlnIdentifier, -): RateLimitProofResult = - # obtain the external nullifier - let externalNullifierRes = poseidon(@[@(epoch), @(rlnIdentifier)]) - - if externalNullifierRes.isErr(): - return err("could not construct the external nullifier") - - # serialize inputs - let serializedInputs = serialize( - idSecretHash = membership.idSecretHash, - memIndex = index, - userMessageLimit = userMessageLimit, - messageId = messageId, - externalNullifier = externalNullifierRes.get(), - msg = data, - ) - var inputBuffer = toBuffer(serializedInputs) - - debug "input buffer ", inputBuffer = repr(inputBuffer) - - # generate the proof - var proof: Buffer - let proofIsSuccessful = generate_proof(rlnInstance, addr inputBuffer, addr proof) - # check whether the generate_proof call is done successfully - if not proofIsSuccessful: - return err("could not generate the proof") - - var proofValue = cast[ptr array[320, byte]](proof.`ptr`) - let proofBytes: array[320, byte] = proofValue[] - debug "proof content", proofHex = proofValue[].toHex - - ## parse the proof as [ proof<128> | root<32> | external_nullifier<32> | share_x<32> | share_y<32> | nullifier<32> ] - - let - proofOffset = 128 - rootOffset = proofOffset + 32 - externalNullifierOffset = rootOffset + 32 - shareXOffset = externalNullifierOffset + 32 - shareYOffset = shareXOffset + 32 - nullifierOffset = shareYOffset + 32 - - var - zkproof: ZKSNARK - proofRoot, shareX, shareY: MerkleNode - externalNullifier: ExternalNullifier - nullifier: Nullifier - - discard zkproof.copyFrom(proofBytes[0 .. proofOffset - 1]) - discard proofRoot.copyFrom(proofBytes[proofOffset .. rootOffset - 1]) - discard - externalNullifier.copyFrom(proofBytes[rootOffset .. externalNullifierOffset - 1]) - discard shareX.copyFrom(proofBytes[externalNullifierOffset .. shareXOffset - 1]) - discard shareY.copyFrom(proofBytes[shareXOffset .. shareYOffset - 1]) - discard nullifier.copyFrom(proofBytes[shareYOffset .. nullifierOffset - 1]) - - let output = RateLimitProof( - proof: zkproof, - merkleRoot: proofRoot, - externalNullifier: externalNullifier, - epoch: epoch, - rlnIdentifier: rlnIdentifier, - shareX: shareX, - shareY: shareY, - nullifier: nullifier, - ) - return ok(output) - -# validRoots should contain a sequence of roots in the acceptable windows. -# As default, it is set to an empty sequence of roots. This implies that the validity check for the proof's root is skipped -proc proofVerify*( - rlnInstance: ptr RLN, - data: openArray[byte], - proof: RateLimitProof, - validRoots: seq[MerkleNode] = @[], -): RlnRelayResult[bool] = - ## verifies the proof, returns an error if the proof verification fails - ## returns true if the proof is valid - var normalizedProof = proof - # when we do this, we ensure that we compute the proof for the derived value - # of the externalNullifier. The proof verification will fail if a malicious peer - # attaches invalid epoch+rlnidentifier pair - normalizedProof.externalNullifier = poseidon( - @[@(proof.epoch), @(proof.rlnIdentifier)] - ).valueOr: - return err("could not construct the external nullifier") - var - proofBytes = serialize(normalizedProof, data) - proofBuffer = proofBytes.toBuffer() - validProof: bool - rootsBytes = serialize(validRoots) - rootsBuffer = rootsBytes.toBuffer() - - trace "serialized proof", proof = byteutils.toHex(proofBytes) - - let verifyIsSuccessful = - verify_with_roots(rlnInstance, addr proofBuffer, addr rootsBuffer, addr validProof) - if not verifyIsSuccessful: - # something went wrong in verification call - warn "could not verify validity of the proof", proof = proof - return err("could not verify the proof") - - if not validProof: - return ok(false) - else: - return ok(true) - -proc insertMember*(rlnInstance: ptr RLN, idComm: IDCommitment): bool = - ## inserts a member to the tree - ## returns true if the member is inserted successfully - ## returns false if the member could not be inserted - var pkBuffer = toBuffer(idComm) - let pkBufferPtr = addr pkBuffer - - # add the member to the tree - let memberAdded = update_next_member(rlnInstance, pkBufferPtr) - return memberAdded - -proc getMember*( - rlnInstance: ptr RLN, index: MembershipIndex -): RlnRelayResult[IDCommitment] = - ## returns the member at the given index - ## returns an error if the index is out of bounds - ## returns the member if the index is valid - var - idCommitment {.noinit.}: Buffer = Buffer() - idCommitmentPtr = addr(idCommitment) - memberRetrieved = get_leaf(rlnInstance, index, idCommitmentPtr) - - if not memberRetrieved: - return err("could not get the member") - - if not idCommitment.len == 32: - return err("wrong output size") - - let idCommitmentValue = (cast[ptr array[32, byte]](idCommitment.`ptr`))[] - - return ok(@idCommitmentValue) - -proc atomicWrite*( - rlnInstance: ptr RLN, - index = none(MembershipIndex), - idComms = newSeq[IDCommitment](), - toRemoveIndices = newSeq[MembershipIndex](), -): bool = - ## Insert multiple members i.e., identity commitments, and remove multiple members - ## returns true if the operation is successful - ## returns false if the operation fails - - let startIndex = - if index.isNone(): - MembershipIndex(0) - else: - index.get() - - # serialize the idComms - let idCommsBytes = serialize(idComms) - var idCommsBuffer = idCommsBytes.toBuffer() - let idCommsBufferPtr = addr idCommsBuffer - - # serialize the toRemoveIndices - let indicesBytes = serialize(toRemoveIndices) - var indicesBuffer = indicesBytes.toBuffer() - let indicesBufferPtr = addr indicesBuffer - - let operationSuccess = - atomic_write(rlnInstance, startIndex, idCommsBufferPtr, indicesBufferPtr) - return operationSuccess - -proc insertMembers*( - rlnInstance: ptr RLN, index: MembershipIndex, idComms: seq[IDCommitment] -): bool = - ## Insert multiple members i.e., identity commitments - ## returns true if the insertion is successful - ## returns false if any of the insertions fails - ## Note: This proc is atomic, i.e., if any of the insertions fails, all the previous insertions are rolled back - - return atomicWrite(rlnInstance, some(index), idComms) - -proc removeMember*(rlnInstance: ptr RLN, index: MembershipIndex): bool = - let deletionSuccess = delete_member(rlnInstance, index) - return deletionSuccess - -proc removeMembers*(rlnInstance: ptr RLN, indices: seq[MembershipIndex]): bool = - return atomicWrite(rlnInstance, idComms = @[], toRemoveIndices = indices) - -proc getMerkleRoot*(rlnInstance: ptr RLN): MerkleNodeResult = - # read the Merkle Tree root after insertion - var - root {.noinit.}: Buffer = Buffer() - rootPtr = addr(root) - getRootSuccessful = getRoot(rlnInstance, rootPtr) - if not getRootSuccessful: - return err("could not get the root") - if not root.len == 32: - return err("wrong output size") - - var rootValue = cast[ptr MerkleNode](root.`ptr`)[] - return ok(rootValue) - type RlnMetadata* = object lastProcessedBlock*: uint64 chainId*: UInt256 diff --git a/waku/waku_rln_relay/rln_relay.nim b/waku/waku_rln_relay/rln_relay.nim index 965c8c021..ea76c6ad6 100644 --- a/waku/waku_rln_relay/rln_relay.nim +++ b/waku/waku_rln_relay/rln_relay.nim @@ -49,45 +49,11 @@ type RlnRelayConf* = object of RootObj treePath*: string epochSizeSec*: uint64 userMessageLimit*: uint64 + ethPrivateKey*: Option[string] type WakuRlnConfig* = object of RlnRelayConf onFatalErrorAction*: OnFatalErrorHandler -proc createMembershipList*( - rln: ptr RLN, n: int -): RlnRelayResult[(seq[RawMembershipCredentials], string)] = - ## createMembershipList produces a sequence of identity credentials in the form of (identity trapdoor, identity nullifier, identity secret hash, id commitment) in the hexadecimal format - ## this proc also returns the root of a Merkle tree constructed out of the identity commitment keys of the generated list - ## the output of this proc is used to initialize a static group keys (to test waku-rln-relay in the off-chain mode) - ## Returns an error if it cannot create the membership list - - var output = newSeq[RawMembershipCredentials]() - var idCommitments = newSeq[IDCommitment]() - - for i in 0 .. n - 1: - # generate an identity credential - let idCredentialRes = rln.membershipKeyGen() - if idCredentialRes.isErr(): - return - err("could not generate an identity credential: " & idCredentialRes.error()) - let idCredential = idCredentialRes.get() - let idTuple = ( - idCredential.idTrapdoor.inHex(), - idCredential.idNullifier.inHex(), - idCredential.idSecretHash.inHex(), - idCredential.idCommitment.inHex(), - ) - output.add(idTuple) - idCommitments.add(idCredential.idCommitment) - - # Insert members into tree - let membersAdded = rln.insertMembers(0, idCommitments) - if not membersAdded: - return err("could not insert members into the tree") - - let root = rln.getMerkleRoot().value().inHex() - return ok((output, root)) - type WakuRLNRelay* = ref object of RootObj # the log of nullifiers and Shamir shares of the past messages grouped per epoch nullifierLog*: OrderedTable[Epoch, Table[Nullifier, ProofMetadata]] @@ -176,7 +142,6 @@ proc updateLog*( err("the epoch was not found: " & getCurrentExceptionMsg()) # should never happen proc getCurrentEpoch*(rlnPeer: WakuRLNRelay): Epoch = - ## gets the current rln Epoch time return rlnPeer.calcEpoch(epochTime()) proc absDiff*(e1, e2: Epoch): uint64 = @@ -194,6 +159,16 @@ proc absDiff*(e1, e2: Epoch): uint64 = else: return epoch2 - epoch1 +proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] = + ## it is a utility proc that prepares the `data` parameter of the proof generation procedure i.e., `proofGen` that resides in the current module + ## it extracts the `contentTopic`, `timestamp` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence + + let + contentTopicBytes = toBytes(wakumessage.contentTopic) + timestampBytes = toBytes(wakumessage.timestamp.uint64) + output = concat(wakumessage.payload, contentTopicBytes, @(timestampBytes)) + return output + proc validateMessage*( rlnPeer: WakuRLNRelay, msg: WakuMessage ): MessageValidationResult = @@ -251,7 +226,8 @@ proc validateMessage*( waku_rln_proof_verification_total.inc() waku_rln_proof_verification_duration_seconds.nanosecondTime: - let proofVerificationRes = rlnPeer.groupManager.verifyProof(input, proof) + let proofVerificationRes = + rlnPeer.groupManager.verifyProof(msg.toRLNSignal(), proof) if proofVerificationRes.isErr(): waku_rln_errors_total.inc(labelValues = ["proof_verification"]) @@ -309,16 +285,6 @@ proc validateMessageAndUpdateLog*( return isValidMessage -proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] = - ## it is a utility proc that prepares the `data` parameter of the proof generation procedure i.e., `proofGen` that resides in the current module - ## it extracts the `contentTopic`, `timestamp` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence - - let - contentTopicBytes = toBytes(wakumessage.contentTopic) - timestampBytes = toBytes(wakumessage.timestamp.uint64) - output = concat(wakumessage.payload, contentTopicBytes, @(timestampBytes)) - return output - proc appendRLNProof*( rlnPeer: WakuRLNRelay, msg: var WakuMessage, senderEpochTime: float64 ): RlnRelayResult[void] = @@ -445,38 +411,25 @@ proc mount( let rlnInstance = createRLNInstance(tree_path = conf.treePath).valueOr: return err("could not create RLN instance: " & $error) - if not conf.dynamic: - # static setup - let parsedGroupKeys = StaticGroupKeys.toIdentityCredentials().valueOr: - return err("could not parse static group keys: " & $error) + let (rlnRelayCredPath, rlnRelayCredPassword) = + if conf.creds.isSome: + (some(conf.creds.get().path), some(conf.creds.get().password)) + else: + (none(string), none(string)) - groupManager = StaticGroupManager( - groupSize: StaticGroupSize, - groupKeys: parsedGroupKeys, - membershipIndex: conf.credIndex, - rlnInstance: rlnInstance, - onFatalErrorAction: conf.onFatalErrorAction, - ) - # we don't persist credentials in static mode since they exist in ./constants.nim - else: - let (rlnRelayCredPath, rlnRelayCredPassword) = - if conf.creds.isSome: - (some(conf.creds.get().path), some(conf.creds.get().password)) - else: - (none(string), none(string)) - - groupManager = OnchainGroupManager( - userMessageLimit: some(conf.userMessageLimit), - ethClientUrls: conf.ethClientUrls, - ethContractAddress: $conf.ethContractAddress, - chainId: conf.chainId, - rlnInstance: rlnInstance, - registrationHandler: registrationHandler, - keystorePath: rlnRelayCredPath, - keystorePassword: rlnRelayCredPassword, - membershipIndex: conf.credIndex, - onFatalErrorAction: conf.onFatalErrorAction, - ) + groupManager = OnchainGroupManager( + userMessageLimit: some(conf.userMessageLimit), + ethClientUrls: conf.ethClientUrls, + ethContractAddress: $conf.ethContractAddress, + chainId: conf.chainId, + rlnInstance: rlnInstance, + registrationHandler: registrationHandler, + keystorePath: rlnRelayCredPath, + keystorePassword: rlnRelayCredPassword, + ethPrivateKey: conf.ethPrivateKey, + membershipIndex: conf.credIndex, + onFatalErrorAction: conf.onFatalErrorAction, + ) # Initialize the groupManager (await groupManager.init()).isOkOr: