From 3e201279338bc3446fb8787211ec69764d58578c Mon Sep 17 00:00:00 2001 From: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com> Date: Mon, 27 Jun 2022 12:35:26 -0700 Subject: [PATCH] Chore(rln-relay): decouples rln-relay and wakunode2 modules (#1019) * adds wakunode2_types * removes unused imports --- tests/v2/test_waku_rln_relay_onchain.nim | 4 +- waku/v2/node/wakunode2.nim | 269 +----------------- waku/v2/node/wakunode2_types.nim | 47 +++ .../waku_rln_relay/waku_rln_relay_utils.nim | 232 ++++++++++++++- 4 files changed, 287 insertions(+), 265 deletions(-) create mode 100644 waku/v2/node/wakunode2_types.nim diff --git a/tests/v2/test_waku_rln_relay_onchain.nim b/tests/v2/test_waku_rln_relay_onchain.nim index e6c2634df..e450aec77 100644 --- a/tests/v2/test_waku_rln_relay_onchain.nim +++ b/tests/v2/test_waku_rln_relay_onchain.nim @@ -3,11 +3,11 @@ {.used.} import - std/options, sequtils, times, + std/options, testutils/unittests, chronos, chronicles, stint, web3, json, stew/byteutils, stew/shims/net as stewNet, libp2p/crypto/crypto, - ../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils, + ../../waku/v2/protocol/waku_rln_relay/[waku_rln_relay_utils, waku_rln_relay_types, rln_relay_contract], ../../waku/v2/node/wakunode2, ../test_helpers, diff --git a/waku/v2/node/wakunode2.nim b/waku/v2/node/wakunode2.nim index fa2bda29d..257c11220 100644 --- a/waku/v2/node/wakunode2.nim +++ b/waku/v2/node/wakunode2.nim @@ -24,7 +24,8 @@ import ./storage/migration/migration_types, ./peer_manager/peer_manager, ./dnsdisc/waku_dnsdisc, - ./discv5/waku_discv5 + ./discv5/waku_discv5, + wakunode2_types export builders, @@ -33,14 +34,11 @@ export waku_swap, waku_filter, waku_lightpush, - waku_rln_relay_types + waku_rln_relay_types, + wakunode2_types when defined(rln): - import - libp2p/protocols/pubsub/rpc/messages, - libp2p/protocols/pubsub/pubsub, - web3, web3/ethtypes, - ../protocol/waku_rln_relay/[rln, waku_rln_relay_utils] + import ../protocol/waku_rln_relay/waku_rln_relay_utils declarePublicCounter waku_node_messages, "number of messages received", ["type"] declarePublicGauge waku_node_filters, "number of content filter subscriptions" @@ -59,41 +57,6 @@ const defaultTopic* = "/waku/2/default-waku/proto" # Default Waku Filter Timeout const WakuFilterTimeout: Duration = 1.days - -# key and crypto modules different -type - KeyPair* = crypto.KeyPair - PublicKey* = crypto.PublicKey - PrivateKey* = crypto.PrivateKey - - # XXX: Weird type, should probably be using pubsub Topic object name? - Topic* = string - Message* = seq[byte] - - WakuInfo* = object - # NOTE One for simplicity, can extend later as needed - listenAddresses*: seq[string] - enrUri*: string - #multiaddrStrings*: seq[string] - - # NOTE based on Eth2Node in NBC eth2_network.nim - WakuNode* = ref object of RootObj - peerManager*: PeerManager - switch*: Switch - wakuRelay*: WakuRelay - wakuStore*: WakuStore - wakuFilter*: WakuFilter - wakuSwap*: WakuSwap - wakuRlnRelay*: WakuRLNRelay - wakuLightPush*: WakuLightPush - enr*: enr.Record - libp2pPing*: Ping - filters*: Filters - rng*: ref BrHmacDrbgContext - wakuDiscv5*: WakuDiscoveryV5 - announcedAddresses* : seq[MultiAddress] - started*: bool # Indicates that node has started listening - proc protocolMatcher(codec: string): Matcher = ## Returns a protocol matcher function for the provided codec proc match(proto: string): bool {.gcsafe.} = @@ -495,180 +458,6 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo node.wakuStore = WakuStore.init(node.peerManager, node.rng, store, node.wakuSwap, persistMessages=persistMessages, capacity=capacity, isSqliteOnly=isSqliteOnly) node.switch.mount(node.wakuStore, protocolMatcher(WakuStoreCodec)) - -when defined(rln): - proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic, spamHandler: Option[SpamHandler] = none(SpamHandler)) = - ## this procedure is a thin wrapper for the pubsub addValidator method - ## it sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic - ## if contentTopic is empty, then validation takes place for All the messages published on the given pubsubTopic - ## the message validation logic is according to https://rfc.vac.dev/spec/17/ - proc validator(topic: string, message: messages.Message): Future[pubsub.ValidationResult] {.async.} = - trace "rln-relay topic validator is called" - let msg = WakuMessage.init(message.data) - if msg.isOk(): - let - wakumessage = msg.value() - payload = string.fromBytes(wakumessage.payload) - - # check the contentTopic - if (wakumessage.contentTopic != "") and (contentTopic != "") and (wakumessage.contentTopic != contentTopic): - trace "content topic did not match:", contentTopic=wakumessage.contentTopic, payload=payload - return pubsub.ValidationResult.Accept - - # validate the message - let - validationRes = node.wakuRlnRelay.validateMessage(wakumessage) - proof = toHex(wakumessage.proof.proof) - epoch = fromEpoch(wakumessage.proof.epoch) - root = toHex(wakumessage.proof.merkleRoot) - shareX = toHex(wakumessage.proof.shareX) - shareY = toHex(wakumessage.proof.shareY) - nullifier = toHex(wakumessage.proof.nullifier) - case validationRes: - of Valid: - debug "message validity is verified, relaying:", contentTopic=wakumessage.contentTopic, epoch=epoch, timestamp=wakumessage.timestamp, payload=payload - trace "message validity is verified, relaying:", proof=proof, root=root, shareX=shareX, shareY=shareY, nullifier=nullifier - return pubsub.ValidationResult.Accept - of Invalid: - debug "message validity could not be verified, discarding:", contentTopic=wakumessage.contentTopic, epoch=epoch, timestamp=wakumessage.timestamp, payload=payload - trace "message validity could not be verified, discarding:", proof=proof, root=root, shareX=shareX, shareY=shareY, nullifier=nullifier - return pubsub.ValidationResult.Reject - of Spam: - debug "A spam message is found! yay! discarding:", contentTopic=wakumessage.contentTopic, epoch=epoch, timestamp=wakumessage.timestamp, payload=payload - trace "A spam message is found! yay! discarding:", proof=proof, root=root, shareX=shareX, shareY=shareY, nullifier=nullifier - if spamHandler.isSome: - let handler = spamHandler.get - handler(wakumessage) - return pubsub.ValidationResult.Reject - # set a validator for the supplied pubsubTopic - let pb = PubSub(node.wakuRelay) - pb.addValidator(pubsubTopic, validator) - - proc mountRlnRelayStatic*(node: WakuNode, - group: seq[IDCommitment], - memKeyPair: MembershipKeyPair, - memIndex: MembershipIndex, - pubsubTopic: string, - contentTopic: ContentTopic, - spamHandler: Option[SpamHandler] = none(SpamHandler)) {.raises: [Defect, IOError].}= - # TODO return a bool value to indicate the success of the call - - debug "mounting rln-relay in off-chain/static mode" - # check whether inputs are provided - # relay protocol is the prerequisite of rln-relay - if node.wakuRelay.isNil: - error "Failed to mount WakuRLNRelay. Relay protocol is not mounted." - return - # check whether the pubsub topic is supported at the relay level - if pubsubTopic notin node.wakuRelay.defaultTopics: - error "Failed to mount WakuRLNRelay. The relay protocol does not support the configured pubsub topic.", pubsubTopic=pubsubTopic - return - - debug "rln-relay input validation passed" - - # check the peer's index and the inclusion of user's identity commitment in the group - doAssert((memKeyPair.idCommitment) == group[int(memIndex)]) - - # create an RLN instance - var rlnInstance = createRLNInstance() - doAssert(rlnInstance.isOk) - var rln = rlnInstance.value - - # add members to the Merkle tree - for index in 0..group.len-1: - let member = group[index] - let member_is_added = rln.insertMember(member) - doAssert(member_is_added) - - # create the WakuRLNRelay - var rlnPeer = WakuRLNRelay(membershipKeyPair: memKeyPair, - membershipIndex: memIndex, - rlnInstance: rln, - pubsubTopic: pubsubTopic, - contentTopic: contentTopic) - - # adds a topic validator for the supplied pubsub topic at the relay protocol - # messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped - # the topic validator checks for the correct non-spamming proof of the message - node.addRLNRelayValidator(pubsubTopic, contentTopic, spamHandler) - debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic, contentTopic=contentTopic - - node.wakuRlnRelay = rlnPeer - - - proc mountRlnRelayDynamic*(node: WakuNode, - ethClientAddr: string = "", - ethAccAddr: web3.Address, - memContractAddr: web3.Address, - memKeyPair: Option[MembershipKeyPair] = none(MembershipKeyPair), - memIndex: Option[MembershipIndex] = none(MembershipIndex), - pubsubTopic: string, - contentTopic: ContentTopic, - spamHandler: Option[SpamHandler] = none(SpamHandler)) {.async.} = - debug "mounting rln-relay in on-chain/dynamic mode" - # TODO return a bool value to indicate the success of the call - # relay protocol is the prerequisite of rln-relay - if node.wakuRelay.isNil: - error "Failed to mount WakuRLNRelay. Relay protocol is not mounted." - return - # check whether the pubsub topic is supported at the relay level - if pubsubTopic notin node.wakuRelay.defaultTopics: - error "Failed to mount WakuRLNRelay. The relay protocol does not support the configured pubsub topic.", pubsubTopic=pubsubTopic - return - debug "rln-relay input validation passed" - - # create an RLN instance - var rlnInstance = createRLNInstance() - doAssert(rlnInstance.isOk) - var rln = rlnInstance.value - - # prepare rln membership key pair - var - keyPair: MembershipKeyPair - rlnIndex: MembershipIndex - if memKeyPair.isNone: # if non provided, create one and register to the contract - trace "no rln-relay key is provided, generating one" - let keyPairOpt = rln.membershipKeyGen() - doAssert(keyPairOpt.isSome) - keyPair = keyPairOpt.get() - # register the rln-relay peer to the membership contract - let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccAddr, ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr) - # check whether registration is done - doAssert(regIndexRes.isOk()) - rlnIndex = regIndexRes.value - debug "peer is successfully registered into the membership contract", rlnIndex=rlnIndex, idComm=keyPair.idCommitment.toHex(), idKey=keyPair.idKey.toHex() - else: - keyPair = memKeyPair.get() - rlnIndex = memIndex.get() - - # create the WakuRLNRelay - var rlnPeer = WakuRLNRelay(membershipKeyPair: keyPair, - membershipIndex: rlnIndex, - membershipContractAddress: memContractAddr, - ethClientAddress: ethClientAddr, - ethAccountAddress: ethAccAddr, - rlnInstance: rln, - pubsubTopic: pubsubTopic, - contentTopic: contentTopic) - - - proc handler(pubkey: Uint256, index: Uint256) = - debug "a new key is added", pubkey=pubkey - # assuming all the members arrive in order - let pk = pubkey.toIDCommitment() - let isSuccessful = rlnPeer.rlnInstance.insertMember(pk) - debug "received pk", pk=pk.toHex, index =index - doAssert(isSuccessful) - - asyncSpawn rlnPeer.handleGroupUpdates(handler) - debug "dynamic group management is started" - # adds a topic validator for the supplied pubsub topic at the relay protocol - # messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped - # the topic validator checks for the correct non-spamming proof of the message - addRLNRelayValidator(node, pubsubTopic, contentTopic, spamHandler) - debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic, contentTopic=contentTopic - - node.wakuRlnRelay = rlnPeer proc startRelay*(node: WakuNode) {.async.} = @@ -1226,52 +1015,8 @@ when isMainModule: when defined(rln): if conf.rlnRelay: - if not conf.rlnRelayDynamic: - info " setting up waku-rln-relay in on-chain mode... " - # set up rln relay inputs - let (groupOpt, memKeyPairOpt, memIndexOpt) = rlnRelayStaticSetUp(conf.rlnRelayMemIndex) - if memIndexOpt.isNone: - error "failed to mount WakuRLNRelay" - else: - # mount rlnrelay in off-chain mode with a static group of users - node.mountRlnRelayStatic(group = groupOpt.get(), memKeyPair = memKeyPairOpt.get(), memIndex= memIndexOpt.get(), pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) - - info "membership id key", idkey=memKeyPairOpt.get().idKey.toHex - info "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex - - # check the correct construction of the tree by comparing the calculated root against the expected root - # no error should happen as it is already captured in the unit tests - # TODO have added this check to account for unseen corner cases, will remove it later - let - root = node.wakuRlnRelay.rlnInstance.getMerkleRoot.value.toHex() - expectedRoot = STATIC_GROUP_MERKLE_ROOT - if root != expectedRoot: - error "root mismatch: something went wrong not in Merkle tree construction" - debug "the calculated root", root - info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic - else: - info " setting up waku-rln-relay in on-chain mode... " - - # read related inputs to run rln-relay in on-chain mode and do type conversion when needed - let - ethAccountAddr = web3.fromHex(web3.Address, conf.rlnRelayEthAccount) - ethClientAddr = conf.rlnRelayEthClientAddress - ethMemContractAddress = web3.fromHex(web3.Address, conf.rlnRelayEthMemContractAddress) - rlnRelayId = conf.rlnRelayIdKey - rlnRelayIdCommitmentKey = conf.rlnRelayIdCommitmentKey - rlnRelayIndex = conf.rlnRelayMemIndex - # check if the peer has provided its rln credentials - if rlnRelayIdCommitmentKey != "" and rlnRelayId != "": - # type conversation from hex strings to MembershipKeyPair - let keyPair = @[(rlnRelayId, rlnRelayIdCommitmentKey)] - let memKeyPair = keyPair.toMembershipKeyPairs()[0] - # mount the rln relay protocol in the on-chain/dynamic mode - waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, memKeyPair = some(memKeyPair), memIndex = some(rlnRelayIndex), ethAccAddr = ethAccountAddr, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) - else: - # no rln credential is provided - # mount the rln relay protocol in the on-chain/dynamic mode - waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, ethAccAddr = ethAccountAddr, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) - + node.mountRlnRelay(conf) + if conf.swap: mountSwap(node) # TODO Set swap peer, for now should be same as store peer diff --git a/waku/v2/node/wakunode2_types.nim b/waku/v2/node/wakunode2_types.nim new file mode 100644 index 000000000..591f5063b --- /dev/null +++ b/waku/v2/node/wakunode2_types.nim @@ -0,0 +1,47 @@ +import + eth/p2p/discoveryv5/enr, + libp2p/crypto/crypto, + libp2p/protocols/ping, + ../protocol/waku_relay, + ../protocol/waku_store/waku_store, + ../protocol/waku_swap/waku_swap, + ../protocol/waku_filter/waku_filter, + ../protocol/waku_lightpush/waku_lightpush, + ../protocol/waku_rln_relay/waku_rln_relay_types, + ./peer_manager/peer_manager, + ./discv5/waku_discv5 + + +# key and crypto modules different +type + KeyPair* = crypto.KeyPair + PublicKey* = crypto.PublicKey + PrivateKey* = crypto.PrivateKey + + # XXX: Weird type, should probably be using pubsub Topic object name? + Topic* = string + Message* = seq[byte] + + WakuInfo* = object + # NOTE One for simplicity, can extend later as needed + listenAddresses*: seq[string] + enrUri*: string + #multiaddrStrings*: seq[string] + + # NOTE based on Eth2Node in NBC eth2_network.nim + WakuNode* = ref object of RootObj + peerManager*: PeerManager + switch*: Switch + wakuRelay*: WakuRelay + wakuStore*: WakuStore + wakuFilter*: WakuFilter + wakuSwap*: WakuSwap + wakuRlnRelay*: WakuRLNRelay + wakuLightPush*: WakuLightPush + enr*: enr.Record + libp2pPing*: Ping + filters*: Filters + rng*: ref BrHmacDrbgContext + wakuDiscv5*: WakuDiscoveryV5 + announcedAddresses* : seq[MultiAddress] + started*: bool # Indicates that node has started listening diff --git a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim index de9189650..8b698640e 100644 --- a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim +++ b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim @@ -3,14 +3,21 @@ import std/sequtils, tables, times, chronicles, options, chronos, stint, + confutils, web3, json, + web3/ethtypes, eth/keys, + libp2p/protocols/pubsub/rpc/messages, + libp2p/protocols/pubsub/pubsub, stew/results, stew/[byteutils, arrayops, endians2], rln, waku_rln_relay_types, + ../../node/[wakunode2_types,config], ../waku_message + + logScope: topics = "wakurlnrelayutils" @@ -625,4 +632,227 @@ proc subscribeToGroupEvents(ethClientUri: string, contractAddress: Address, bloc proc handleGroupUpdates*(rlnPeer: WakuRLNRelay, handler: RegistrationEventHandler) {.async, gcsafe.} = # mounts the supplied handler for the registration events emitting from the membership contract - await subscribeToGroupEvents(ethClientUri = rlnPeer.ethClientAddress, contractAddress = rlnPeer.membershipContractAddress, handler = handler) \ No newline at end of file + await subscribeToGroupEvents(ethClientUri = rlnPeer.ethClientAddress, contractAddress = rlnPeer.membershipContractAddress, handler = handler) + +proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic, spamHandler: Option[SpamHandler] = none(SpamHandler)) = + ## this procedure is a thin wrapper for the pubsub addValidator method + ## it sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic + ## if contentTopic is empty, then validation takes place for All the messages published on the given pubsubTopic + ## the message validation logic is according to https://rfc.vac.dev/spec/17/ + proc validator(topic: string, message: messages.Message): Future[pubsub.ValidationResult] {.async.} = + trace "rln-relay topic validator is called" + let msg = WakuMessage.init(message.data) + if msg.isOk(): + let + wakumessage = msg.value() + payload = string.fromBytes(wakumessage.payload) + + # check the contentTopic + if (wakumessage.contentTopic != "") and (contentTopic != "") and (wakumessage.contentTopic != contentTopic): + trace "content topic did not match:", contentTopic=wakumessage.contentTopic, payload=payload + return pubsub.ValidationResult.Accept + + # validate the message + let + validationRes = node.wakuRlnRelay.validateMessage(wakumessage) + proof = toHex(wakumessage.proof.proof) + epoch = fromEpoch(wakumessage.proof.epoch) + root = toHex(wakumessage.proof.merkleRoot) + shareX = toHex(wakumessage.proof.shareX) + shareY = toHex(wakumessage.proof.shareY) + nullifier = toHex(wakumessage.proof.nullifier) + case validationRes: + of Valid: + debug "message validity is verified, relaying:", contentTopic=wakumessage.contentTopic, epoch=epoch, timestamp=wakumessage.timestamp, payload=payload + trace "message validity is verified, relaying:", proof=proof, root=root, shareX=shareX, shareY=shareY, nullifier=nullifier + return pubsub.ValidationResult.Accept + of Invalid: + debug "message validity could not be verified, discarding:", contentTopic=wakumessage.contentTopic, epoch=epoch, timestamp=wakumessage.timestamp, payload=payload + trace "message validity could not be verified, discarding:", proof=proof, root=root, shareX=shareX, shareY=shareY, nullifier=nullifier + return pubsub.ValidationResult.Reject + of Spam: + debug "A spam message is found! yay! discarding:", contentTopic=wakumessage.contentTopic, epoch=epoch, timestamp=wakumessage.timestamp, payload=payload + trace "A spam message is found! yay! discarding:", proof=proof, root=root, shareX=shareX, shareY=shareY, nullifier=nullifier + if spamHandler.isSome: + let handler = spamHandler.get + handler(wakumessage) + return pubsub.ValidationResult.Reject + # set a validator for the supplied pubsubTopic + let pb = PubSub(node.wakuRelay) + pb.addValidator(pubsubTopic, validator) + +proc mountRlnRelayStatic*(node: WakuNode, + group: seq[IDCommitment], + memKeyPair: MembershipKeyPair, + memIndex: MembershipIndex, + pubsubTopic: string, + contentTopic: ContentTopic, + spamHandler: Option[SpamHandler] = none(SpamHandler)) {.raises: [Defect, IOError].}= + # TODO return a bool value to indicate the success of the call + + debug "mounting rln-relay in off-chain/static mode" + # check whether inputs are provided + # relay protocol is the prerequisite of rln-relay + if node.wakuRelay.isNil: + error "Failed to mount WakuRLNRelay. Relay protocol is not mounted." + return + # check whether the pubsub topic is supported at the relay level + if pubsubTopic notin node.wakuRelay.defaultTopics: + error "Failed to mount WakuRLNRelay. The relay protocol does not support the configured pubsub topic.", pubsubTopic=pubsubTopic + return + + debug "rln-relay input validation passed" + + # check the peer's index and the inclusion of user's identity commitment in the group + doAssert((memKeyPair.idCommitment) == group[int(memIndex)]) + + # create an RLN instance + var rlnInstance = createRLNInstance() + doAssert(rlnInstance.isOk) + var rln = rlnInstance.value + + # add members to the Merkle tree + for index in 0..group.len-1: + let member = group[index] + let member_is_added = rln.insertMember(member) + doAssert(member_is_added) + + # create the WakuRLNRelay + var rlnPeer = WakuRLNRelay(membershipKeyPair: memKeyPair, + membershipIndex: memIndex, + rlnInstance: rln, + pubsubTopic: pubsubTopic, + contentTopic: contentTopic) + + # adds a topic validator for the supplied pubsub topic at the relay protocol + # messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped + # the topic validator checks for the correct non-spamming proof of the message + node.addRLNRelayValidator(pubsubTopic, contentTopic, spamHandler) + debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic, contentTopic=contentTopic + + node.wakuRlnRelay = rlnPeer + + + +proc mountRlnRelayDynamic*(node: WakuNode, + ethClientAddr: string = "", + ethAccAddr: web3.Address, + memContractAddr: web3.Address, + memKeyPair: Option[MembershipKeyPair] = none(MembershipKeyPair), + memIndex: Option[MembershipIndex] = none(MembershipIndex), + pubsubTopic: string, + contentTopic: ContentTopic, + spamHandler: Option[SpamHandler] = none(SpamHandler)) {.async.} = + debug "mounting rln-relay in on-chain/dynamic mode" + # TODO return a bool value to indicate the success of the call + # relay protocol is the prerequisite of rln-relay + if node.wakuRelay.isNil: + error "Failed to mount WakuRLNRelay. Relay protocol is not mounted." + return + # check whether the pubsub topic is supported at the relay level + if pubsubTopic notin node.wakuRelay.defaultTopics: + error "Failed to mount WakuRLNRelay. The relay protocol does not support the configured pubsub topic.", pubsubTopic=pubsubTopic + return + debug "rln-relay input validation passed" + + # create an RLN instance + var rlnInstance = createRLNInstance() + doAssert(rlnInstance.isOk) + var rln = rlnInstance.value + + # prepare rln membership key pair + var + keyPair: MembershipKeyPair + rlnIndex: MembershipIndex + if memKeyPair.isNone: # if non provided, create one and register to the contract + trace "no rln-relay key is provided, generating one" + let keyPairOpt = rln.membershipKeyGen() + doAssert(keyPairOpt.isSome) + keyPair = keyPairOpt.get() + # register the rln-relay peer to the membership contract + let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccAddr, ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr) + # check whether registration is done + doAssert(regIndexRes.isOk()) + rlnIndex = regIndexRes.value + debug "peer is successfully registered into the membership contract" + else: + keyPair = memKeyPair.get() + rlnIndex = memIndex.get() + + # create the WakuRLNRelay + var rlnPeer = WakuRLNRelay(membershipKeyPair: keyPair, + membershipIndex: rlnIndex, + membershipContractAddress: memContractAddr, + ethClientAddress: ethClientAddr, + ethAccountAddress: ethAccAddr, + rlnInstance: rln, + pubsubTopic: pubsubTopic, + contentTopic: contentTopic) + + + proc handler(pubkey: Uint256, index: Uint256) = + debug "a new key is added", pubkey=pubkey + # assuming all the members arrive in order + let pk = pubkey.toIDCommitment() + let isSuccessful = rlnPeer.rlnInstance.insertMember(pk) + debug "received pk", pk=pk.toHex, index =index + doAssert(isSuccessful) + + asyncSpawn rlnPeer.handleGroupUpdates(handler) + debug "dynamic group management is started" + # adds a topic validator for the supplied pubsub topic at the relay protocol + # messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped + # the topic validator checks for the correct non-spamming proof of the message + addRLNRelayValidator(node, pubsubTopic, contentTopic, spamHandler) + debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic, contentTopic=contentTopic + + node.wakuRlnRelay = rlnPeer + + +proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf) {.raises: [Defect, ValueError, IOError, CatchableError].} = + if not conf.rlnRelayDynamic: + info " setting up waku-rln-relay in on-chain mode... " + # set up rln relay inputs + let (groupOpt, memKeyPairOpt, memIndexOpt) = rlnRelayStaticSetUp(conf.rlnRelayMemIndex) + if memIndexOpt.isNone: + error "failed to mount WakuRLNRelay" + else: + # mount rlnrelay in off-chain mode with a static group of users + node.mountRlnRelayStatic(group = groupOpt.get(), memKeyPair = memKeyPairOpt.get(), memIndex= memIndexOpt.get(), pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) + + info "membership id key", idkey=memKeyPairOpt.get().idKey.toHex + info "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex + + # check the correct construction of the tree by comparing the calculated root against the expected root + # no error should happen as it is already captured in the unit tests + # TODO have added this check to account for unseen corner cases, will remove it later + let + root = node.wakuRlnRelay.rlnInstance.getMerkleRoot.value.toHex() + expectedRoot = STATIC_GROUP_MERKLE_ROOT + if root != expectedRoot: + error "root mismatch: something went wrong not in Merkle tree construction" + debug "the calculated root", root + info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic + else: + info " setting up waku-rln-relay in on-chain mode... " + + # read related inputs to run rln-relay in on-chain mode and do type conversion when needed + let + ethAccountAddr = web3.fromHex(web3.Address, conf.rlnRelayEthAccount) + ethClientAddr = conf.rlnRelayEthClientAddress + ethMemContractAddress = web3.fromHex(web3.Address, conf.rlnRelayEthMemContractAddress) + rlnRelayId = conf.rlnRelayIdKey + rlnRelayIdCommitmentKey = conf.rlnRelayIdCommitmentKey + rlnRelayIndex = conf.rlnRelayMemIndex + # check if the peer has provided its rln credentials + if rlnRelayIdCommitmentKey != "" and rlnRelayId != "": + # type conversation from hex strings to MembershipKeyPair + let keyPair = @[(rlnRelayId, rlnRelayIdCommitmentKey)] + let memKeyPair = keyPair.toMembershipKeyPairs()[0] + # mount the rln relay protocol in the on-chain/dynamic mode + waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, memKeyPair = some(memKeyPair), memIndex = some(rlnRelayIndex), ethAccAddr = ethAccountAddr, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) + else: + # no rln credential is provided + # mount the rln relay protocol in the on-chain/dynamic mode + waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, ethAccAddr = ethAccountAddr, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) +