Rln-relay integration into chat2 (#835)

* adds ProofMetadata

* adds EPOCH_INTERVAL

* adds messageLog field

* adds updateLog, toEpoch, fromEpoch, getEpoch, compareTo

* adds unit test for toEpoch and fromEpoch

* adds unit test for Epoch comparison

* adds result codes for updateLog

* adds unit test for update log

* renames epoch related consts

* modifies updateLog with new return type and new logic of spam detection

* adds unit text for the modified updateLog

* changes max epoch gap type size

* splits updateLog into two procs isSpam and updateLog

* updates unittests

* fixes a bug, returns false when the message is not spam

* renames messageLog to nullifierLog

* renames isSpam to hasDuplicate

* updates the rln validator, adds comments

* adds appendRLNProof proc plus some code beatification

* unit test for validate message

* adds unhappy test to validateMessage unit test

* renames EPOCH_UNIT_SECONDS

* renames MAX_CLOCK_GAP_SECONDS

* WIP: integration test

* fixes compile errors

* sets a real epoch value

* updates on old unittests

* adds comments to the rln relay tests

* adds more comments

* makes rln import conditional

* adds todos

* adds more todos

* adds rln-relay mount process into chat2

* further todos

* logs contentTopic

* introduces rln relay configs

* changes default pubsub topic

* adds contentTopic config

* imports rln relay dependencies

* consolidates imports

* removes module identifier from ContentTopic

* adds contentTopic field

* adds contentTopic argument to mountRlnRelay calls

* appends rln proof to chat2 messages

* changes the default chat2 contentTopic

* adds missing content topic fields

* fixes a bug

* adds a new logic about empty content topics

* appends proof only when rln flag is active

* removes unnecessary todos

* fixes an indentation issue

* adds log messages

* verifies the proof against the concatenation of msg payload and content topic

* a bug fix

* removes duplicate epoch time calculation

* updates log level to trace

* updates default rln-relay content topic

* adds support for empty content topics

* updates changelog

* changelog updates

* removes a commented code block

* updates addRLNRelayValidator string doc
This commit is contained in:
Sanaz Taheri Boshrooyeh 2022-02-04 15:58:27 -08:00 committed by GitHub
parent dd184fb85b
commit 0cd7003df2
9 changed files with 186 additions and 57 deletions

View File

@ -1,3 +1,22 @@
## Next Release
Release highlights:
-
The full list of changes is below.
### Features
- The `waku-rln-relay` now supports spam-protection for a specific combination of `pubsubTopic` and `contentTopic` (available under the `rln` compiler flag).
- The `waku-rln-relay` protocol in integrated into `chat2` (available under the`rln` compiler flag)
### Changes
- ...
### Fixes
- ...
## 2021-01-19 v0.7 ## 2021-01-19 v0.7
Release highlights: Release highlights:

View File

@ -27,6 +27,13 @@ import ../../waku/v2/node/[wakunode2, waku_payload],
../../waku/common/utils/nat, ../../waku/common/utils/nat,
./config_chat2 ./config_chat2
when defined(rln):
import
libp2p/protocols/pubsub/rpc/messages,
libp2p/protocols/pubsub/pubsub,
web3,
../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils]
const Help = """ const Help = """
Commands: /[?|help|connect|nick|exit] Commands: /[?|help|connect|nick|exit]
help: Prints this help help: Prints this help
@ -191,7 +198,8 @@ proc readNick(transp: StreamTransport): Future[string] {.async.} =
proc publish(c: Chat, line: string) = proc publish(c: Chat, line: string) =
# First create a Chat2Message protobuf with this line of text # First create a Chat2Message protobuf with this line of text
let chat2pb = Chat2Message(timestamp: getTime().toUnix(), let time = getTime().toUnix()
let chat2pb = Chat2Message(timestamp: time,
nick: c.nick, nick: c.nick,
payload: line.toBytes()).encode() payload: line.toBytes()).encode()
@ -206,8 +214,17 @@ proc publish(c: Chat, line: string) =
version = 1'u32 version = 1'u32
encodedPayload = payload.encode(version, c.node.rng[]) encodedPayload = payload.encode(version, c.node.rng[])
if encodedPayload.isOk(): if encodedPayload.isOk():
let message = WakuMessage(payload: encodedPayload.get(), var message = WakuMessage(payload: encodedPayload.get(),
contentTopic: c.contentTopic, version: version) contentTopic: c.contentTopic, version: version)
when defined(rln):
if not isNil(c.node.wakuRlnRelay):
# for future version when we support more than one rln protected content topic,
# we should check the message content topic as well
let success = c.node.wakuRlnRelay.appendRLNProof(message, float64(time))
if not success:
debug "could not append rate limit proof to the message", success=success
else:
debug "rate limit proof is appended to the message", success=success
if not c.node.wakuLightPush.isNil(): if not c.node.wakuLightPush.isNil():
# Attempt lightpush # Attempt lightpush
asyncSpawn c.node.lightpush(DefaultTopic, message, handler) asyncSpawn c.node.lightpush(DefaultTopic, message, handler)
@ -217,8 +234,18 @@ proc publish(c: Chat, line: string) =
warn "Payload encoding failed", error = encodedPayload.error warn "Payload encoding failed", error = encodedPayload.error
else: else:
# No payload encoding/encryption from Waku # No payload encoding/encryption from Waku
let message = WakuMessage(payload: chat2pb.buffer, var message = WakuMessage(payload: chat2pb.buffer,
contentTopic: c.contentTopic, version: 0) contentTopic: c.contentTopic, version: 0)
when defined(rln):
if not isNil(c.node.wakuRlnRelay):
# for future version when we support more than one rln protected content topic,
# we should check the message content topic as well
let success = c.node.wakuRlnRelay.appendRLNProof(message, float64(time))
if not success:
debug "could not append rate limit proof to the message", success=success
else:
debug "rate limit proof is appended to the message", success=success
if not c.node.wakuLightPush.isNil(): if not c.node.wakuLightPush.isNil():
# Attempt lightpush # Attempt lightpush
asyncSpawn c.node.lightpush(DefaultTopic, message, handler) asyncSpawn c.node.lightpush(DefaultTopic, message, handler)
@ -326,7 +353,6 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} =
wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift), wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift),
wsEnabled = conf.websocketSupport, wsEnabled = conf.websocketSupport,
wssEnabled = conf.websocketSecureSupport) wssEnabled = conf.websocketSecureSupport)
await node.start() await node.start()
node.mountRelay(conf.topics.split(" "), node.mountRelay(conf.topics.split(" "),
@ -465,6 +491,33 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} =
let topic = cast[Topic](DefaultTopic) let topic = cast[Topic](DefaultTopic)
node.subscribe(topic, handler) node.subscribe(topic, handler)
when defined(rln):
if conf.rlnRelay:
info "WakuRLNRelay is enabled"
# 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)
trace "membership id key", idkey=memKeyPairOpt.get().idKey.toHex
trace "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"
trace "the calculated root", root
trace "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic
await chat.readWriteLoop() await chat.readWriteLoop()
if conf.keepAlive: if conf.keepAlive:

View File

@ -5,8 +5,9 @@ import
libp2p/crypto/crypto, libp2p/crypto/crypto,
libp2p/crypto/secp, libp2p/crypto/secp,
nimcrypto/utils, nimcrypto/utils,
eth/keys eth/keys,
../../waku/v2/protocol/waku_rln_relay/waku_rln_relay_types,
../../waku/v2/protocol/waku_message
type type
Fleet* = enum Fleet* = enum
none none
@ -75,11 +76,6 @@ type
defaultValue: true defaultValue: true
name: "relay" }: bool name: "relay" }: bool
rlnRelay* {.
desc: "Enable spam protection through rln-relay: true|false",
defaultValue: false
name: "rln-relay" }: bool
staticnodes* {. staticnodes* {.
desc: "Peer multiaddr to directly connect with. Argument may be repeated." desc: "Peer multiaddr to directly connect with. Argument may be repeated."
name: "staticnode" }: seq[string] name: "staticnode" }: seq[string]
@ -231,6 +227,28 @@ type
defaultValue: false defaultValue: false
name: "websocket-secure-support" }: bool name: "websocket-secure-support" }: bool
## rln-relay configuration
rlnRelay* {.
desc: "Enable spam protection through rln-relay: true|false",
defaultValue: false
name: "rln-relay" }: bool
rlnRelayMemIndex* {.
desc: "(experimental) the index of node in the rln-relay group: a value between 0-99 inclusive",
defaultValue: MembershipIndex(0)
name: "rln-relay-membership-index" }: MembershipIndex
rlnRelayContentTopic* {.
desc: "the pubsub topic for which rln-relay gets enabled",
defaultValue: "waku/2/rln-relay/proto"
name: "rln-relay-content-topic" }: ContentTopic
rlnRelayPubsubTopic* {.
desc: "the pubsub topic for which rln-relay gets enabled",
defaultValue: "/waku/2/default-waku/proto"
name: "rln-relay-pubsub-topic" }: string
# NOTE: Keys are different in nim-libp2p # NOTE: Keys are different in nim-libp2p
proc parseCmdArg*(T: type crypto.PrivateKey, p: TaintedString): T = proc parseCmdArg*(T: type crypto.PrivateKey, p: TaintedString): T =
try: try:

View File

@ -12,6 +12,7 @@ import
./test_utils ./test_utils
const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto" const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto"
const RLNRELAY_CONTENT_TOPIC = "waku/2/rlnrelay/proto"
# POSEIDON_HASHER_CODE holds the bytecode of Poseidon hasher solidity smart contract: # POSEIDON_HASHER_CODE holds the bytecode of Poseidon hasher solidity smart contract:
# https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/crypto/PoseidonHasher.sol # https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/crypto/PoseidonHasher.sol
@ -250,7 +251,14 @@ procSuite "Waku rln relay":
# start rln-relay # start rln-relay
node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC])
await node.mountRlnRelay(ethClientAddrOpt = some(ETH_CLIENT), ethAccAddrOpt = some(ethAccountAddress), memContractAddOpt = some(membershipContractAddress), groupOpt = some(group), memKeyPairOpt = some(keypair.get()), memIndexOpt = some(index), pubsubTopic = RLNRELAY_PUBSUB_TOPIC) await node.mountRlnRelay(ethClientAddrOpt = some(EthClient),
ethAccAddrOpt = some(ethAccountAddress),
memContractAddOpt = some(membershipContractAddress),
groupOpt = some(group),
memKeyPairOpt = some(keypair.get()),
memIndexOpt = some(index),
pubsubTopic = RLNRELAY_PUBSUB_TOPIC,
contentTopic = RLNRELAY_CONTENT_TOPIC)
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
debug "calculated root ", calculatedRoot debug "calculated root ", calculatedRoot
@ -286,7 +294,12 @@ procSuite "Waku rln relay":
# -------- mount rln-relay in the off-chain mode # -------- mount rln-relay in the off-chain mode
node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC])
await node.mountRlnRelay(groupOpt = some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[index]), memIndexOpt = some(index), onchainMode = false, pubsubTopic = RLNRELAY_PUBSUB_TOPIC) await node.mountRlnRelay(groupOpt = some(groupIDCommitments),
memKeyPairOpt = some(groupKeyPairs[index]),
memIndexOpt = some(index),
onchainMode = false,
pubsubTopic = RLNRELAY_PUBSUB_TOPIC,
contentTopic = RLNRELAY_CONTENT_TOPIC)
# get the root of Merkle tree which is constructed inside the mountRlnRelay proc # get the root of Merkle tree which is constructed inside the mountRlnRelay proc
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex

View File

@ -2,6 +2,7 @@
import import
testutils/unittests, testutils/unittests,
std/sequtils,
chronicles, chronos, stew/shims/net as stewNet, stew/byteutils, std/os, chronicles, chronos, stew/shims/net as stewNet, stew/byteutils, std/os,
libp2p/crypto/crypto, libp2p/crypto/crypto,
libp2p/crypto/secp, libp2p/crypto/secp,
@ -617,7 +618,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt1, memKeyPairOpt = memKeyPairOpt1,
memIndexOpt= memIndexOpt1, memIndexOpt= memIndexOpt1,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node1.start() await node1.start()
# node 2 # node 2
@ -628,7 +630,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt2, memKeyPairOpt = memKeyPairOpt2,
memIndexOpt= memIndexOpt2, memIndexOpt= memIndexOpt2,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node2.start() await node2.start()
# node 3 # node 3
@ -639,7 +642,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt3, memKeyPairOpt = memKeyPairOpt3,
memIndexOpt= memIndexOpt3, memIndexOpt= memIndexOpt3,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node3.start() await node3.start()
# connect them together # connect them together
@ -663,19 +667,11 @@ procSuite "WakuNode":
let payload = "Hello".toBytes() let payload = "Hello".toBytes()
# prepare the epoch # prepare the epoch
let epoch = getCurrentEpoch() let epoch = getCurrentEpoch()
# prepare the proof var message = WakuMessage(payload: @payload,
let rateLimitProofRes = node1.wakuRlnRelay.rlnInstance.proofGen(data = payload, contentTopic: contentTopic)
memKeys = node1.wakuRlnRelay.membershipKeyPair, doAssert(node1.wakuRlnRelay.appendRLNProof(message, epochTime()))
memIndex = node1.wakuRlnRelay.membershipIndex,
epoch = epoch)
doAssert(rateLimitProofRes.isOk())
let rateLimitProof = rateLimitProofRes.value
let message = WakuMessage(payload: @payload,
contentTopic: contentTopic,
proof: rateLimitProof)
## node1 publishes a message with a rate limit proof, the message is then relayed to node2 which in turn ## node1 publishes a message with a rate limit proof, the message is then relayed to node2 which in turn
@ -715,7 +711,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt1, memKeyPairOpt = memKeyPairOpt1,
memIndexOpt= memIndexOpt1, memIndexOpt= memIndexOpt1,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node1.start() await node1.start()
# node 2 # node 2
@ -726,7 +723,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt2, memKeyPairOpt = memKeyPairOpt2,
memIndexOpt= memIndexOpt2, memIndexOpt= memIndexOpt2,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node2.start() await node2.start()
# node 3 # node 3
@ -737,7 +735,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt3, memKeyPairOpt = memKeyPairOpt3,
memIndexOpt= memIndexOpt3, memIndexOpt= memIndexOpt3,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node3.start() await node3.start()
# connect them together # connect them together
@ -765,7 +764,10 @@ procSuite "WakuNode":
let epoch = getCurrentEpoch() let epoch = getCurrentEpoch()
# prepare the proof # prepare the proof
let rateLimitProofRes = node1.wakuRlnRelay.rlnInstance.proofGen(data = payload, let
contentTopicBytes = contentTopic.toBytes
input = concat(payload, contentTopicBytes)
rateLimitProofRes = node1.wakuRlnRelay.rlnInstance.proofGen(data = input,
memKeys = node1.wakuRlnRelay.membershipKeyPair, memKeys = node1.wakuRlnRelay.membershipKeyPair,
memIndex = MembershipIndex(4), memIndex = MembershipIndex(4),
epoch = epoch) epoch = epoch)
@ -817,7 +819,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt1, memKeyPairOpt = memKeyPairOpt1,
memIndexOpt= memIndexOpt1, memIndexOpt= memIndexOpt1,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node1.start() await node1.start()
# node 2 # node 2
@ -828,7 +831,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt2, memKeyPairOpt = memKeyPairOpt2,
memIndexOpt= memIndexOpt2, memIndexOpt= memIndexOpt2,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node2.start() await node2.start()
# node 3 # node 3
@ -839,7 +843,8 @@ procSuite "WakuNode":
memKeyPairOpt = memKeyPairOpt3, memKeyPairOpt = memKeyPairOpt3,
memIndexOpt= memIndexOpt3, memIndexOpt= memIndexOpt3,
onchainMode = false, onchainMode = false,
pubsubTopic = rlnRelayPubSubTopic) pubsubTopic = rlnRelayPubSubTopic,
contentTopic = contentTopic)
await node3.start() await node3.start()
# connect the nodes together node1 <-> node2 <-> node3 # connect the nodes together node1 <-> node2 <-> node3
@ -850,15 +855,15 @@ procSuite "WakuNode":
let time = epochTime() let time = epochTime()
# create some messages with rate limit proofs # create some messages with rate limit proofs
var var
wm1 = WakuMessage(payload: "message 1".toBytes()) wm1 = WakuMessage(payload: "message 1".toBytes(), contentTopic: contentTopic)
proofAdded1 = node3.wakuRlnRelay.appendRLNProof(wm1, time) proofAdded1 = node3.wakuRlnRelay.appendRLNProof(wm1, time)
# another message in the same epoch as wm1, it will break the messaging rate limit # another message in the same epoch as wm1, it will break the messaging rate limit
wm2 = WakuMessage(payload: "message2".toBytes()) wm2 = WakuMessage(payload: "message2".toBytes(), contentTopic: contentTopic)
proofAdded2 = node3.wakuRlnRelay.appendRLNProof(wm2, time) proofAdded2 = node3.wakuRlnRelay.appendRLNProof(wm2, time)
# wm3 points to the next epoch # wm3 points to the next epoch
wm3 = WakuMessage(payload: "message 3".toBytes()) wm3 = WakuMessage(payload: "message 3".toBytes(), contentTopic: contentTopic)
proofAdded3 = node3.wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS) proofAdded3 = node3.wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS)
wm4 = WakuMessage(payload: "message4".toBytes()) wm4 = WakuMessage(payload: "message4".toBytes(), contentTopic: contentTopic)
# check proofs are added correctly # check proofs are added correctly
check: check:

View File

@ -6,7 +6,8 @@ import
libp2p/crypto/secp, libp2p/crypto/secp,
nimcrypto/utils, nimcrypto/utils,
eth/keys, eth/keys,
../protocol/waku_rln_relay/[waku_rln_relay_types] ../protocol/waku_rln_relay/waku_rln_relay_types,
../protocol/waku_message
type type
WakuNodeConf* = object WakuNodeConf* = object
@ -83,8 +84,13 @@ type
rlnRelayPubsubTopic* {. rlnRelayPubsubTopic* {.
desc: "the pubsub topic for which rln-relay gets enabled", desc: "the pubsub topic for which rln-relay gets enabled",
defaultValue: "waku/2/rlnrelay/proto" defaultValue: "/waku/2/default-waku/proto"
name: "rln-relay-pubsub-topic" }: string name: "rln-relay-pubsub-topic" }: string
rlnRelayContentTopic* {.
desc: "the pubsub topic for which rln-relay gets enabled",
defaultValue: "/toy-chat/2/huilong/proto"
name: "rln-relay-content-topic" }: ContentTopic
staticnodes* {. staticnodes* {.
desc: "Peer multiaddr to directly connect with. Argument may be repeated." desc: "Peer multiaddr to directly connect with. Argument may be repeated."

View File

@ -468,26 +468,30 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo
node.switch.mount(node.wakuStore, protocolMatcher(WakuStoreCodec)) node.switch.mount(node.wakuStore, protocolMatcher(WakuStoreCodec))
when defined(rln): when defined(rln):
proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string) = proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic) =
## this procedure is a thin wrapper for the pubsub addValidator method ## this procedure is a thin wrapper for the pubsub addValidator method
## it sets message validator on the given pubsubTopic, the validator will check that ## it sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic
## all the messages published in the pubsubTopic have a valid zero-knowledge proof ## 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.} = proc validator(topic: string, message: messages.Message): Future[pubsub.ValidationResult] {.async.} =
let msg = WakuMessage.init(message.data) let msg = WakuMessage.init(message.data)
if msg.isOk(): if msg.isOk():
let let wakumessage = msg.value()
wakumessage = msg.value() # check the contentTopic
# validate the message if (wakumessage.contentTopic != "") and (contentTopic != "") and (wakumessage.contentTopic != contentTopic):
validationRes = node.wakuRlnRelay.validateMessage(wakumessage) trace "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: case validationRes:
of Valid: of Valid:
info "message validity is verified, relaying:", wakumessage=wakumessage trace "message validity is verified, relaying:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
return pubsub.ValidationResult.Accept return pubsub.ValidationResult.Accept
of Invalid: of Invalid:
info "message validity could not be verified, discarding:", wakumessage=wakumessage trace "message validity could not be verified, discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
return pubsub.ValidationResult.Reject return pubsub.ValidationResult.Reject
of Spam: of Spam:
info "A spam message is found! yay! discarding:", wakumessage=wakumessage trace "A spam message is found! yay! discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
return pubsub.ValidationResult.Reject return pubsub.ValidationResult.Reject
# set a validator for the supplied pubsubTopic # set a validator for the supplied pubsubTopic
let pb = PubSub(node.wakuRelay) let pb = PubSub(node.wakuRelay)
@ -501,7 +505,8 @@ when defined(rln):
memKeyPairOpt: Option[MembershipKeyPair] = none(MembershipKeyPair), memKeyPairOpt: Option[MembershipKeyPair] = none(MembershipKeyPair),
memIndexOpt: Option[MembershipIndex] = none(MembershipIndex), memIndexOpt: Option[MembershipIndex] = none(MembershipIndex),
onchainMode: bool = true, onchainMode: bool = true,
pubsubTopic: string) {.async.} = pubsubTopic: string,
contentTopic: ContentTopic) {.async.} =
# TODO return a bool value to indicate the success of the call # TODO return a bool value to indicate the success of the call
# check whether inputs are provided # check whether inputs are provided
@ -581,7 +586,8 @@ when defined(rln):
ethClientAddress: ethClientAddr, ethClientAddress: ethClientAddr,
ethAccountAddress: ethAccAddr, ethAccountAddress: ethAccAddr,
rlnInstance: rln, rlnInstance: rln,
pubsubTopic: pubsubTopic) pubsubTopic: pubsubTopic,
contentTopic: contentTopic)
if onchainMode: if onchainMode:
# register the rln-relay peer to the membership contract # register the rln-relay peer to the membership contract
@ -593,7 +599,7 @@ when defined(rln):
# adds a topic validator for the supplied pubsub topic at the relay protocol # 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 # 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 # the topic validator checks for the correct non-spamming proof of the message
addRLNRelayValidator(node, pubsubTopic) addRLNRelayValidator(node, pubsubTopic, contentTopic)
debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic
node.wakuRlnRelay = rlnPeer node.wakuRlnRelay = rlnPeer
@ -1071,7 +1077,7 @@ when isMainModule:
error "failed to mount WakuRLNRelay" error "failed to mount WakuRLNRelay"
else: else:
# mount rlnrelay in offline mode (for now) # mount rlnrelay in offline mode (for now)
waitFor node.mountRlnRelay(groupOpt = groupOpt, memKeyPairOpt = memKeyPairOpt, memIndexOpt= memIndexOpt, onchainMode = false, pubsubTopic = conf.rlnRelayPubsubTopic) waitFor node.mountRlnRelay(groupOpt = groupOpt, memKeyPairOpt = memKeyPairOpt, memIndexOpt= memIndexOpt, onchainMode = false, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic)
info "membership id key", idkey=memKeyPairOpt.get().idKey.toHex info "membership id key", idkey=memKeyPairOpt.get().idKey.toHex
info "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex info "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex
@ -1085,7 +1091,7 @@ when isMainModule:
if root != expectedRoot: if root != expectedRoot:
error "root mismatch: something went wrong not in Merkle tree construction" error "root mismatch: something went wrong not in Merkle tree construction"
debug "the calculated root", root debug "the calculated root", root
info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic
if conf.swap: if conf.swap:
mountSwap(node) mountSwap(node)

View File

@ -77,6 +77,9 @@ type WakuRLNRelay* = ref object
ethAccountPrivateKey*: Option[PrivateKey] ethAccountPrivateKey*: Option[PrivateKey]
rlnInstance*: RLN[Bn256] rlnInstance*: RLN[Bn256]
pubsubTopic*: string # the pubsub topic for which rln relay is mounted pubsubTopic*: string # the pubsub topic for which rln relay is mounted
# contentTopic should be of type waku_message.ContentTopic, however, due to recursive module dependency, the underlying type of ContentTopic is used instead
# TODO a long-term solution is to place types with recursive dependency inside one file
contentTopic*: string
# the log of nullifiers and Shamir shares of the past messages grouped per epoch # the log of nullifiers and Shamir shares of the past messages grouped per epoch
nullifierLog*: Table[Epoch, seq[ProofMetadata]] nullifierLog*: Table[Epoch, seq[ProofMetadata]]

View File

@ -454,16 +454,22 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio
if abs(gap) >= MAX_EPOCH_GAP: if abs(gap) >= MAX_EPOCH_GAP:
# message's epoch is too old or too ahead # message's epoch is too old or too ahead
# accept messages whose epoch is within +-MAX_EPOCH_GAP from the current epoch # accept messages whose epoch is within +-MAX_EPOCH_GAP from the current epoch
debug "invalid message: epoch gap exceeds a threshold",gap=gap
return MessageValidationResult.Invalid return MessageValidationResult.Invalid
# verify the proof # verify the proof
if not rlnPeer.rlnInstance.proofVerify(msg.payload, msg.proof): let
contentTopicBytes = msg.contentTopic.toBytes
input = concat(msg.payload, contentTopicBytes)
if not rlnPeer.rlnInstance.proofVerify(input, msg.proof):
# invalid proof # invalid proof
debug "invalid message: invalid proof"
return MessageValidationResult.Invalid return MessageValidationResult.Invalid
# check if double messaging has happened # check if double messaging has happened
let hasDup = rlnPeer.hasDuplicate(msg) let hasDup = rlnPeer.hasDuplicate(msg)
if hasDup.isOk and hasDup.value == true: if hasDup.isOk and hasDup.value == true:
debug "invalid message: message is a spam"
return MessageValidationResult.Spam return MessageValidationResult.Spam
# insert the message to the log # insert the message to the log