mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-09 09:23:14 +00:00
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:
parent
dd184fb85b
commit
0cd7003df2
19
CHANGELOG.md
19
CHANGELOG.md
@ -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:
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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."
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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]]
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user