From 5a2680dba8aa58b1ab3b91d40331d6fff25f68b0 Mon Sep 17 00:00:00 2001 From: staheri14 Date: Wed, 16 Feb 2022 23:22:59 +0000 Subject: [PATCH] deploy: d851d4842489b1cf0cfeb65a9bd03689ce09d991 --- examples/v2/chat2.nim | 48 ++++++++++++++++--- examples/v2/config_chat2.nim | 2 +- tests/v2/test_wakunode.nim | 6 +-- .../vendor/libbacktrace-upstream/libtool | 2 +- waku/v2/node/wakunode2.nim | 21 ++++---- .../waku_rln_relay/waku_rln_relay_types.nim | 4 +- .../waku_rln_relay/waku_rln_relay_utils.nim | 29 ++++++++--- 7 files changed, 83 insertions(+), 29 deletions(-) diff --git a/examples/v2/chat2.nim b/examples/v2/chat2.nim index 3fc843877..71ba300fd 100644 --- a/examples/v2/chat2.nim +++ b/examples/v2/chat2.nim @@ -129,6 +129,32 @@ proc showChatPrompt(c: Chat) = except IOError: discard +proc getChatLine(c: Chat, msg:WakuMessage): Result[string, string]= + when PayloadV1: + # Use Waku v1 payload encoding/encryption + let + keyInfo = KeyInfo(kind: Symmetric, symKey: c.symKey) + decodedPayload = decodePayload(decoded.get(), keyInfo) + + if decodedPayload.isOK(): + let + pb = Chat2Message.init(decodedPayload.get().payload) + chatLine = if pb.isOk: pb[].toString() + else: string.fromBytes(decodedPayload.get().payload) + return ok(chatLine) + else: + debug "Invalid encoded WakuMessage payload", + error = decodedPayload.error + return err("Invalid encoded WakuMessage payload") + else: + # No payload encoding/encryption from Waku + let + pb = Chat2Message.init(msg.payload) + chatLine = if pb.isOk: pb[].toString() + else: string.fromBytes(msg.payload) + return ok(chatline) + + proc printReceivedMessage(c: Chat, msg: WakuMessage) = when PayloadV1: # Use Waku v1 payload encoding/encryption @@ -225,6 +251,8 @@ 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 + # TODO move it to log after doogfooding + echo "--rln epoch: ", fromEpoch(message.proof.epoch) if not c.node.wakuLightPush.isNil(): # Attempt lightpush asyncSpawn c.node.lightpush(DefaultTopic, message, handler) @@ -245,6 +273,7 @@ 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) if not c.node.wakuLightPush.isNil(): # Attempt lightpush @@ -372,7 +401,6 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} = prompt: false, contentTopic: conf.contentTopic, symKey: generateSymKey(conf.contentTopic)) - if conf.staticnodes.len > 0: await connectToNodes(chat, conf.staticnodes) elif conf.dnsDiscovery and conf.dnsDiscoveryUrl != "": @@ -495,16 +523,24 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} = if conf.rlnRelay: info "WakuRLNRelay is enabled" + proc spamHandler(wakuMessage: WakuMessage) {.gcsafe, closure.} = + debug "spam handler is called" + let chatLineResult = chat.getChatLine(wakuMessage) + if chatLineResult.isOk(): + echo "A spam message is found and discarded : ", chatLineResult.value + else: + echo "A spam message is found and discarded" + # set up rln relay inputs let (groupOpt, memKeyPairOpt, memIndexOpt) = rlnRelaySetUp(conf.rlnRelayMemIndex) if memIndexOpt.isNone: error "failed to mount WakuRLNRelay" else: # mount rlnrelay in offline mode (for now) - waitFor node.mountRlnRelay(groupOpt = groupOpt, memKeyPairOpt = memKeyPairOpt, memIndexOpt= memIndexOpt, onchainMode = false, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) + waitFor node.mountRlnRelay(groupOpt = groupOpt, memKeyPairOpt = memKeyPairOpt, memIndexOpt= memIndexOpt, onchainMode = false, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic, spamHandler = some(spamHandler)) - trace "membership id key", idkey=memKeyPairOpt.get().idKey.toHex - trace "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex + debug "membership id key", idkey=memKeyPairOpt.get().idKey.toHex + debug "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 @@ -514,8 +550,8 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} = expectedRoot = STATIC_GROUP_MERKLE_ROOT if root != expectedRoot: error "root mismatch: something went wrong not in Merkle tree construction" - trace "the calculated root", root - trace "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic + debug "the calculated root", root + debug "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic await chat.readWriteLoop() diff --git a/examples/v2/config_chat2.nim b/examples/v2/config_chat2.nim index 75fb33183..b8afdb4d3 100644 --- a/examples/v2/config_chat2.nim +++ b/examples/v2/config_chat2.nim @@ -241,7 +241,7 @@ type rlnRelayContentTopic* {. desc: "the pubsub topic for which rln-relay gets enabled", - defaultValue: "waku/2/rln-relay/proto" + defaultValue: "/toy-chat/2/luzhou/proto" name: "rln-relay-content-topic" }: ContentTopic rlnRelayPubsubTopic* {. diff --git a/tests/v2/test_wakunode.nim b/tests/v2/test_wakunode.nim index cc4458e3e..ed935875b 100644 --- a/tests/v2/test_wakunode.nim +++ b/tests/v2/test_wakunode.nim @@ -911,12 +911,12 @@ procSuite "WakuNode": wm1 = WakuMessage(payload: "message 1".toBytes(), contentTopic: contentTopic) proofAdded1 = node3.wakuRlnRelay.appendRLNProof(wm1, time) # another message in the same epoch as wm1, it will break the messaging rate limit - wm2 = WakuMessage(payload: "message2".toBytes(), contentTopic: contentTopic) + wm2 = WakuMessage(payload: "message 2".toBytes(), contentTopic: contentTopic) proofAdded2 = node3.wakuRlnRelay.appendRLNProof(wm2, time) # wm3 points to the next epoch wm3 = WakuMessage(payload: "message 3".toBytes(), contentTopic: contentTopic) proofAdded3 = node3.wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS) - wm4 = WakuMessage(payload: "message4".toBytes(), contentTopic: contentTopic) + wm4 = WakuMessage(payload: "message 4".toBytes(), contentTopic: contentTopic) # check proofs are added correctly check: @@ -966,9 +966,7 @@ procSuite "WakuNode": res2 = await completionFut2.withTimeout(10.seconds) check: - res1 or res2 == true # either of the wm1 and wm2 is relayed (res1 and res2) == false # either of the wm1 and wm2 is found as spam hence not relayed - (await completionFut2.withTimeout(10.seconds)) == true (await completionFut3.withTimeout(10.seconds)) == true (await completionFut4.withTimeout(10.seconds)) == false diff --git a/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool b/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool index 41760c003..b97bec04c 100755 --- a/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool +++ b/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool @@ -2,7 +2,7 @@ # libtool - Provide generalized library-building support services. # Generated automatically by config.status (libbacktrace) version-unused -# Libtool was configured on host fv-az190-599: +# Libtool was configured on host fv-az272-316: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, diff --git a/waku/v2/node/wakunode2.nim b/waku/v2/node/wakunode2.nim index c7812017e..5e0519042 100644 --- a/waku/v2/node/wakunode2.nim +++ b/waku/v2/node/wakunode2.nim @@ -470,30 +470,34 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo node.switch.mount(node.wakuStore, protocolMatcher(WakuStoreCodec)) when defined(rln): - proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic) = + 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.} = + debug "rln-relay topic validator is called" let msg = WakuMessage.init(message.data) if msg.isOk(): let wakumessage = msg.value() # check the contentTopic if (wakumessage.contentTopic != "") and (contentTopic != "") and (wakumessage.contentTopic != contentTopic): - trace "content topic did not match:", contentTopic=wakumessage.contentTopic, payload=string.fromBytes(wakumessage.payload) + debug "content topic did not match:", contentTopic=wakumessage.contentTopic, payload=string.fromBytes(wakumessage.payload) return pubsub.ValidationResult.Accept # validate the message let validationRes = node.wakuRlnRelay.validateMessage(wakumessage) case validationRes: of Valid: - trace "message validity is verified, relaying:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload) + debug "message validity is verified, relaying:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload) return pubsub.ValidationResult.Accept of Invalid: - trace "message validity could not be verified, discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload) + debug "message validity could not be verified, discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload) return pubsub.ValidationResult.Reject of Spam: - trace "A spam message is found! yay! discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload) + debug "A spam message is found! yay! discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload) + 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) @@ -508,7 +512,8 @@ when defined(rln): memIndexOpt: Option[MembershipIndex] = none(MembershipIndex), onchainMode: bool = true, pubsubTopic: string, - contentTopic: ContentTopic) {.async.} = + contentTopic: ContentTopic, + spamHandler: Option[SpamHandler] = none(SpamHandler)) {.async.} = # TODO return a bool value to indicate the success of the call # check whether inputs are provided @@ -601,8 +606,8 @@ when defined(rln): # 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) - debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic + addRLNRelayValidator(node, pubsubTopic, contentTopic, spamHandler) + debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic, contentTopic=contentTopic node.wakuRlnRelay = rlnPeer 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 131577b6b..197064165 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 @@ -116,8 +116,8 @@ const # the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here STATIC_GROUP_MERKLE_ROOT* = "a1877a553eff12e1b21632a0545a916a5c5b8060ad7cc6c69956741134397b2d" -const EPOCH_UNIT_SECONDS* = float64(2) -const MAX_CLOCK_GAP_SECONDS* = 20.0 # the maximum clock difference between peers +const EPOCH_UNIT_SECONDS* = float64(10) # the rln-relay epoch length in seconds +const MAX_CLOCK_GAP_SECONDS* = 20.0 # the maximum clock difference between peers in seconds # maximum allowed gap between the epochs of messages' RateLimitProofs const MAX_EPOCH_GAP* = int64(MAX_CLOCK_GAP_SECONDS/EPOCH_UNIT_SECONDS) 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 59c232ebd..e0020b9b8 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 @@ -16,6 +16,8 @@ logScope: type RLNResult* = Result[RLN[Bn256], string] type MerkleNodeResult* = Result[MerkleNode, string] type RateLimitProofResult* = Result[RateLimitProof, string] +type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure, raises: [Defect].} + # membership contract interface contract(MembershipContract): # TODO define a return type of bool for register method to signify a successful registration @@ -157,6 +159,8 @@ proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: Membersh msg = data) var inputBuffer = toBuffer(serializedInputs) + debug "input buffer ", inputBuffer + # generate the proof var proof: Buffer let proofIsSuccessful = generate_proof(rlnInstance, addr inputBuffer, addr proof) @@ -445,16 +449,19 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio # get current rln epoch epoch = getCurrentEpoch() + debug "current epoch", currentEpoch=fromEpoch(epoch) let msgEpoch = msg.proof.epoch # calculate the gaps gap = compare(epoch, msgEpoch) - + + debug "message epoch", msgEpoch=fromEpoch(msgEpoch) + # validate the epoch if abs(gap) >= MAX_EPOCH_GAP: # message's epoch is too old or too ahead # accept messages whose epoch is within +-MAX_EPOCH_GAP from the current epoch - debug "invalid message: epoch gap exceeds a threshold",gap=gap + debug "invalid message: epoch gap exceeds a threshold",gap=gap, payload=string.fromBytes(msg.payload) return MessageValidationResult.Invalid # verify the proof @@ -463,31 +470,39 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio input = concat(msg.payload, contentTopicBytes) if not rlnPeer.rlnInstance.proofVerify(input, msg.proof): # invalid proof - debug "invalid message: invalid proof" + debug "invalid message: invalid proof", payload=string.fromBytes(msg.payload) return MessageValidationResult.Invalid # check if double messaging has happened let hasDup = rlnPeer.hasDuplicate(msg) if hasDup.isOk and hasDup.value == true: - debug "invalid message: message is a spam" + debug "invalid message: message is a spam", payload=string.fromBytes(msg.payload) return MessageValidationResult.Spam # insert the message to the log # the result of `updateLog` is discarded because message insertion is guaranteed by the implementation i.e., # it will never error out discard rlnPeer.updateLog(msg) + debug "message is valid", payload=string.fromBytes(msg.payload) return MessageValidationResult.Valid +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` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence + let + contentTopicBytes = wakumessage.contentTopic.toBytes + output = concat(wakumessage.payload, contentTopicBytes) + return output + + proc appendRLNProof*(rlnPeer: WakuRLNRelay, msg: var WakuMessage, senderEpochTime: float64): bool = ## returns true if it can create and append a `RateLimitProof` to the supplied `msg` ## returns false otherwise ## `senderEpochTime` indicates the number of seconds passed since Unix epoch. The fractional part holds sub-seconds. ## The `epoch` field of `RateLimitProof` is derived from the provided `senderEpochTime` (using `calcEpoch()`) - let - contentTopicBytes = msg.contentTopic.toBytes - input = concat(msg.payload, contentTopicBytes) + let input = msg.toRLNSignal() var proof: RateLimitProofResult = proofGen(rlnInstance = rlnPeer.rlnInstance, data = input, memKeys = rlnPeer.membershipKeyPair,