From bc296196790425577e8eb00f3a5d9da0d2f958dd Mon Sep 17 00:00:00 2001 From: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com> Date: Wed, 24 Aug 2022 15:47:06 -0700 Subject: [PATCH] feat(Rln relay): Improves rln-chat2 interface, displays links to the registration tx, warns about spam messages (#1082) * warns about publishing spam messages, display tx hash on Goerli * passes registration handler --- examples/v2/chat2.nim | 22 +++++++++++++--- .../waku_rln_relay/waku_rln_relay_types.nim | 1 + .../waku_rln_relay/waku_rln_relay_utils.nim | 26 +++++++++++-------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/examples/v2/chat2.nim b/examples/v2/chat2.nim index a08293433..c4409af5e 100644 --- a/examples/v2/chat2.nim +++ b/examples/v2/chat2.nim @@ -229,7 +229,13 @@ proc publish(c: Chat, line: string) = else: debug "rate limit proof is appended to the message", success=success # TODO move it to log after doogfooding - echo "--rln epoch: ", fromEpoch(message.proof.epoch) + let msgEpoch = fromEpoch(message.proof.epoch) + if fromEpoch(c.node.wakuRlnRelay.lastEpoch) == fromEpoch(message.proof.epoch): + echo "--rln epoch: ", msgEpoch, " ⚠️ message rate violation! you are spamming the network!" + else: + echo "--rln epoch: ", msgEpoch + # update the last epoch + c.node.wakuRlnRelay.lastEpoch = message.proof.epoch if not c.node.wakuLightPush.isNil(): # Attempt lightpush asyncSpawn c.node.lightpush(DefaultTopic, message, handler) @@ -250,7 +256,14 @@ proc publish(c: Chat, line: string) = debug "could not append rate limit proof to the message", success=success else: debug "rate limit proof is appended to the message", success=success - echo "--rln epoch: ", fromEpoch(message.proof.epoch) + # TODO move it to log after doogfooding + let msgEpoch = fromEpoch(message.proof.epoch) + if fromEpoch(c.node.wakuRlnRelay.lastEpoch) == msgEpoch: + echo "--rln epoch: ", msgEpoch, " ⚠️ message rate violation! you are spamming the network!" + else: + echo "--rln epoch: ", msgEpoch + # update the last epoch + c.node.wakuRlnRelay.lastEpoch = message.proof.epoch if not c.node.wakuLightPush.isNil(): # Attempt lightpush @@ -511,8 +524,11 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} = echo "A spam message is found and discarded" chat.prompt = false showChatPrompt(chat) + proc registrationHandler(txHash: string) {.gcsafe, closure.} = + echo "You are registered to the rln membership contract, find details of your registration transaction in https://goerli.etherscan.io/tx/0x", txHash + echo "rln-relay preparation is in progress ..." - node.mountRlnRelay(conf, some(spamHandler)) + node.mountRlnRelay(conf = conf, spamHandler = some(spamHandler), registrationHandler = some(registrationHandler)) echo "your membership index is: ", node.wakuRlnRelay.membershipIndex echo "your rln identity key is: ", node.wakuRlnRelay.membershipKeyPair.idKey.toHex() echo "your rln identity commitment key is: ", node.wakuRlnRelay.membershipKeyPair.idCommitment.toHex() diff --git a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim index bf70eebb9..74235f1cb 100644 --- a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim +++ b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim @@ -120,6 +120,7 @@ when defined(rln) or (not defined(rln) and not defined(rlnzerokit)): contentTopic*: string # the log of nullifiers and Shamir shares of the past messages grouped per epoch nullifierLog*: Table[Epoch, seq[ProofMetadata]] + lastEpoch*: Epoch # the epoch of the last published rln message when defined(rlnzerokit): type WakuRLNRelay* = ref object 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 0cbd357fe..1720a8dfd 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 @@ -33,8 +33,8 @@ when defined(rlnzerokit): type MerkleNodeResult* = Result[MerkleNode, string] type RateLimitProofResult* = Result[RateLimitProof, string] -type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure, - raises: [Defect].} +type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure, raises: [Defect].} +type RegistrationHandler* = proc(txHash: string): void {.gcsafe, closure, raises: [Defect].} # membership contract interface contract(MembershipContract): @@ -190,7 +190,7 @@ proc toMembershipIndex(v: UInt256): MembershipIndex = let result: MembershipIndex = cast[MembershipIndex](v) return result -proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethAccountPrivKey: keys.PrivateKey, ethClientAddress: string, membershipContractAddress: Address): Future[Result[MembershipIndex, string]] {.async.} = +proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethAccountPrivKey: keys.PrivateKey, ethClientAddress: string, membershipContractAddress: Address, registrationHandler: Option[RegistrationHandler] = none(RegistrationHandler)): Future[Result[MembershipIndex, string]] {.async.} = # TODO may need to also get eth Account Private Key as PrivateKey ## registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress let web3 = await newWeb3(ethClientAddress) @@ -234,13 +234,16 @@ proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethAccountPrivK await web3.close() + if registrationHandler.isSome(): + let handler = registrationHandler.get + handler(toHex(txHash)) return ok(toMembershipIndex(eventIndex)) -proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} = +proc register*(rlnPeer: WakuRLNRelay, registrationHandler: Option[RegistrationHandler] = none(RegistrationHandler)): Future[bool] {.async.} = ## registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey ## into the membership contract whose address is in rlnPeer.membershipContractAddress let pk = rlnPeer.membershipKeyPair.idCommitment - discard await register(idComm = pk, ethAccountAddress = rlnPeer.ethAccountAddress, ethAccountPrivKey = rlnPeer.ethAccountPrivateKey.get(), ethClientAddress = rlnPeer.ethClientAddress, membershipContractAddress = rlnPeer.membershipContractAddress ) + discard await register(idComm = pk, ethAccountAddress = rlnPeer.ethAccountAddress, ethAccountPrivKey = rlnPeer.ethAccountPrivateKey.get(), ethClientAddress = rlnPeer.ethClientAddress, membershipContractAddress = rlnPeer.membershipContractAddress, registrationHandler = registrationHandler) return true @@ -957,7 +960,8 @@ proc mountRlnRelayDynamic*(node: WakuNode, memIndex: Option[MembershipIndex] = none(MembershipIndex), pubsubTopic: string, contentTopic: ContentTopic, - spamHandler: Option[SpamHandler] = none(SpamHandler)) {.async.} = + spamHandler: Option[SpamHandler] = none(SpamHandler), + registrationHandler: Option[RegistrationHandler] = none(RegistrationHandler)) {.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 @@ -986,7 +990,7 @@ proc mountRlnRelayDynamic*(node: WakuNode, doAssert(keyPairOpt.isSome) keyPair = keyPairOpt.get() # register the rln-relay peer to the membership contract - let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccAddr, ethAccountPrivKey = ethAccountPrivKeyOpt.get(), ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr) + let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccAddr, ethAccountPrivKey = ethAccountPrivKeyOpt.get(), ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr, registrationHandler = registrationHandler) # check whether registration is done doAssert(regIndexRes.isOk()) rlnIndex = regIndexRes.value @@ -1042,7 +1046,7 @@ proc readPersistentRlnCredentials*(path: string) : RlnMembershipCredentials {.ra debug "Deserialized Rln credentials", rlnCredentials=deserializedRlnCredentials result = deserializedRlnCredentials -proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: Option[SpamHandler] = none(SpamHandler)) {.raises: [Defect, ValueError, IOError, CatchableError, Exception].} = +proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: Option[SpamHandler] = none(SpamHandler), registrationHandler: Option[RegistrationHandler] = none(RegistrationHandler)) {.raises: [Defect, ValueError, IOError, CatchableError, Exception].} = if not conf.rlnRelayDynamic: info " setting up waku-rln-relay in off-chain mode... " # set up rln relay inputs @@ -1091,14 +1095,14 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: O # mount rln-relay with the provided rln-relay credential waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, memKeyPair = some(credentials.membershipKeyPair), memIndex = some(credentials.rlnIndex), ethAccAddr = ethAccountAddr, - ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler) + ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler, registrationHandler = registrationHandler) else: # there is no credential file available in the supplied path # mount the rln-relay protocol leaving rln-relay credentials arguments unassigned # this infroms mountRlnRelayDynamic proc that new credentials should be generated and registered to the membership contract info "no rln credential is provided" waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, - contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler) + contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler, registrationHandler = registrationHandler) # Persist generated credentials var rlnMembershipCredentials = RlnMembershipCredentials(membershipKeyPair: node.wakuRlnRelay.membershipKeyPair, rlnIndex: node.wakuRlnRelay.membershipIndex) @@ -1112,4 +1116,4 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: O info "no need to persist or use a persisted rln-relay credential" waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, - contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler) \ No newline at end of file + contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler, registrationHandler = registrationHandler) \ No newline at end of file