chore(rln-relay-v2): wakunode testing + improvements (#2501)

* chore(rln-relay-v2): additional testing

* fix: bump librln to v0.4.2 for v2

* fix: catch possible error from the copyFrom

* ci: rename step title for rln-version
This commit is contained in:
Aaryamann Challani 2024-03-12 16:20:30 +05:30 committed by GitHub
parent 097cb36279
commit 059cb97518
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 380 additions and 214 deletions

View File

@ -55,11 +55,12 @@ jobs:
if: ${{ needs.changes.outputs.v2 == 'true' || needs.changes.outputs.common == 'true' }}
strategy:
matrix:
rln_version : [1, 2]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
name: build-${{ matrix.os }}
name: build-${{ matrix.os }}-rln-v${{ matrix.rln_version }}
steps:
- name: Checkout code
uses: actions/checkout@v3
@ -78,18 +79,19 @@ jobs:
key: ${{ runner.os }}-vendor-modules-${{ steps.submodules.outputs.hash }}
- name: Build binaries
run: make V=1 QUICK_AND_DIRTY_COMPILER=1 all tools
run: make RLN_V${{matrix.rln_version}}=true V=1 QUICK_AND_DIRTY_COMPILER=1 all tools
test:
needs: changes
if: ${{ needs.changes.outputs.v2 == 'true' || needs.changes.outputs.common == 'true' }}
strategy:
matrix:
rln_version : [1, 2]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
name: test-${{ matrix.os }}
name: test-${{ matrix.os }}-rln-v${{ matrix.rln_version }}
steps:
- name: Checkout code
uses: actions/checkout@v3
@ -118,7 +120,7 @@ jobs:
sudo docker run --rm -d -e POSTGRES_PASSWORD=test123 -p 5432:5432 postgres:15.4-alpine3.18
fi
make V=1 LOG_LEVEL=DEBUG QUICK_AND_DIRTY_COMPILER=1 POSTGRES=1 test testwakunode2
make RLN_V${{matrix.rln_version}}=true V=1 LOG_LEVEL=DEBUG QUICK_AND_DIRTY_COMPILER=1 POSTGRES=1 test testwakunode2
build-docker-image:
needs: changes

View File

@ -20,11 +20,12 @@ jobs:
build-docker-image:
strategy:
matrix:
rln_v2: [true, false]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
name: docker-build-${{ matrix.os }}
name: docker-build-${{ matrix.os }}-rln-v2-${{ matrix.rln_v2 }}
outputs:
image: ${{ steps.build.outputs.image }}
steps:
@ -48,12 +49,12 @@ jobs:
id: build
run: |
make -j${NPROC} V=1 QUICK_AND_DIRTY_COMPILER=1 NIMFLAGS="-d:disableMarchNative -d:postgres" wakunode2
make RLN_V2=${{matrix.rln_v2}} -j${NPROC} V=1 QUICK_AND_DIRTY_COMPILER=1 NIMFLAGS="-d:disableMarchNative -d:postgres" wakunode2
SHORT_REF=$(git rev-parse --short HEAD)
TAG=$([ "${PR_NUMBER}" == "" ] && echo "${SHORT_REF}" || echo "${PR_NUMBER}")
IMAGE=quay.io/wakuorg/nwaku-pr:${TAG}
IMAGE=quay.io/wakuorg/nwaku-pr:${TAG}-rln-v2-${{matrix.rln_v2}}
echo "image=${IMAGE}" >> $GITHUB_OUTPUT
echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT

View File

@ -135,7 +135,7 @@ clean: | clean-libbacktrace
LIBRLN_BUILDDIR := $(CURDIR)/vendor/zerokit
ifeq ($(RLN_V2),true)
LIBRLN_VERSION := v0.4.1
LIBRLN_VERSION := v0.4.2
else
LIBRLN_VERSION := v0.3.6
endif

View File

@ -80,9 +80,4 @@ import
./wakunode_rest/test_rest_admin,
./wakunode_rest/test_rest_cors
import
./waku_rln_relay/test_waku_rln_relay,
./waku_rln_relay/test_wakunode_rln_relay,
./waku_rln_relay/test_rln_group_manager_onchain,
./waku_rln_relay/test_rln_group_manager_static,
./wakunode_rest/test_rest_health
import ./waku_rln_relay/test_all

View File

@ -1,6 +1,30 @@
import std/tempfiles
import ../../../waku/waku_rln_relay/[rln, protocol_types]
import
../../../waku/waku_rln_relay,
../../../waku/waku_rln_relay/[rln, protocol_types]
proc createRLNInstanceWrapper*(): RLNResult =
return createRlnInstance(tree_path = genTempPath("rln_tree", "waku_rln_relay"))
proc unsafeAppendRLNProof*(rlnPeer: WakuRLNRelay,
msg: var WakuMessage,
senderEpochTime: float64): RlnRelayResult[void] =
## this proc derived from appendRLNProof, does not perform nonce check to
## facilitate bad message id generation for testing
let input = msg.toRLNSignal()
let epoch = rlnPeer.calcEpoch(senderEpochTime)
when defined(rln_v2):
# we do not fetch a nonce from the nonce manager,
# instead we use 0 as the nonce
let proof = rlnPeer.groupManager.generateProof(input, epoch, 0).valueOr:
return err("could not generate rln-v2 proof: " & $error)
else:
let proof = rlnPeer.groupManager.generateProof(input, epoch).valueOr:
return err("could not generate rln proof: " & $error)
msg.proof = proof.encode().buffer
return ok()

View File

@ -73,7 +73,7 @@ suite "RLN Relay v2: serde":
userMessageLimit = rateCommitment.userMessageLimit,
messageId = 0,
index = 0,
epoch = rln.calcEpoch(epochTime()))
epoch = uint64(epochTime()/1.float64).toEpoch())
assert proofRes.isOk, $proofRes.error

View File

@ -6,3 +6,6 @@ import
./test_waku_rln_relay,
./test_wakunode_rln_relay,
./test_rln_nonce_manager
when defined(rln_v2):
import ./rln_v2/test_rln_relay_v2_serde

View File

@ -18,34 +18,31 @@ suite "Nonce manager":
test "should generate a new nonce":
let nm = NonceManager.init(nonceLimit = 100.uint)
let nonceRes = nm.get()
assert nonceRes.isOk(), $nonceRes.error
let nonce = nm.getNonce().valueOr:
raiseAssert $error
check:
nonceRes.get() == 0.uint
nonce == 0.uint
nm.nextNonce == 1.uint
test "should fail to generate a new nonce if limit is reached":
let nm = NonceManager.init(nonceLimit = 1.uint)
let nonceRes = nm.get()
let nonceRes2 = nm.get()
assert nonceRes.isOk(), $nonceRes.error
assert nonceRes2.isErr(), "Expected error, got: " & $nonceRes2.value
let nonce = nm.getNonce().valueOr:
raiseAssert $error
let failedNonceRes = nm.getNonce()
check:
nonceRes2.error.kind == NonceManagerErrorKind.NonceLimitReached
failedNonceRes.isErr()
failedNonceRes.error.kind == NonceManagerErrorKind.NonceLimitReached
test "should generate a new nonce if epoch is crossed":
let nm = NonceManager.init(nonceLimit = 1.uint, epoch = float(0.000001))
let nonceRes = nm.get()
let nonce = nm.getNonce().valueOr:
raiseAssert $error
sleep(1)
let nonceRes2 = nm.get()
assert nonceRes.isOk(), $nonceRes.error
assert nonceRes2.isOk(), $nonceRes2.error
let nonce2 = nm.getNonce().valueOr:
raiseAssert $error
check:
nonceRes.value == 0.uint
nonceRes2.value == 0.uint
nonce == 0.uint
nonce2 == 0.uint

View File

@ -518,18 +518,23 @@ suite "Waku rln relay":
let rln = rlnInstance.get()
# create a Merkle tree
when defined(rln_v2):
let rateCommitments = groupIDCommitments.mapIt(RateCommitment(idCommitment: it,
userMessageLimit: 20))
let leaves = rateCommitments.toLeaves().valueOr:
raiseAssert $error
let membersAdded = rln.insertMembers(0, leaves)
else:
let membersAdded = rln.insertMembers(0, groupIDCommitments)
require:
membersAdded
let rootRes = rln.getMerkleRoot()
require:
rootRes.isOk()
assert membersAdded, "members should be added"
let rawRoot = rln.getMerkleRoot().valueOr:
raiseAssert $error
let root = rootRes.get().inHex()
let root = rawRoot.inHex()
debug "groupIdCredentials", groupIdCredentials
debug "groupIDCommitments", groupIDCommitments
debug "groupIDCommitments", groupIDCommitments = groupIDCommitments.mapIt(it.inHex())
debug "root", root
check:
@ -597,8 +602,8 @@ suite "Waku rln relay":
test "updateLog and hasDuplicate tests":
let
wakurlnrelay = WakuRLNRelay()
epoch = wakurlnrelay.getCurrentEpoch()
wakuRlnRelay = WakuRLNRelay()
epoch = wakuRlnRelay.getCurrentEpoch()
# create some dummy nullifiers and secret shares
var nullifier1: Nullifier
@ -640,37 +645,44 @@ suite "Waku rln relay":
# check whether hasDuplicate correctly finds records with the same nullifiers but different secret shares
# no duplicate for proof1 should be found, since the log is empty
let result1 = wakurlnrelay.hasDuplicate(epoch, proof1.extractMetadata().tryGet())
assert result1.isOk(), $result1.error
assert result1.value == false, "no duplicate should be found"
let proofMetadata1 = proof1.extractMetadata().tryGet()
let isDuplicate1 = wakuRlnRelay.hasDuplicate(epoch, proofMetadata1).valueOr:
raiseAssert $error
assert isDuplicate1 == false, "no duplicate should be found"
# add it to the log
discard wakurlnrelay.updateLog(epoch, proof1.extractMetadata().tryGet())
discard wakuRlnRelay.updateLog(epoch, proofMetadata1)
# no duplicate for proof2 should be found, its nullifier differs from proof1
let result2 = wakurlnrelay.hasDuplicate(epoch, proof2.extractMetadata().tryGet())
assert result2.isOk(), $result2.error
let proofMetadata2 = proof2.extractMetadata().tryGet()
let isDuplicate2 = wakuRlnRelay.hasDuplicate(epoch, proofMetadata2).valueOr:
raiseAssert $error
# no duplicate is found
assert result2.value == false, "no duplicate should be found"
assert isDuplicate2 == false, "no duplicate should be found"
# add it to the log
discard wakurlnrelay.updateLog(epoch, proof2.extractMetadata().tryGet())
discard wakuRlnRelay.updateLog(epoch, proofMetadata2)
# proof3 has the same nullifier as proof1 but different secret shares, it should be detected as duplicate
let result3 = wakurlnrelay.hasDuplicate(epoch, proof3.extractMetadata().tryGet())
assert result3.isOk(), $result3.error
let isDuplicate3 = wakuRlnRelay.hasDuplicate(epoch, proof3.extractMetadata().tryGet()).valueOr:
raiseAssert $error
# it is a duplicate
assert result3.value, "duplicate should be found"
assert isDuplicate3, "duplicate should be found"
asyncTest "validateMessageAndUpdateLog test":
let index = MembershipIndex(5)
let rlnConf = WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_2"))
else:
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_2"))
let wakuRlnRelayRes = await WakuRlnRelay.new(rlnConf)
require:
wakuRlnRelayRes.isOk()
let wakuRlnRelay = wakuRlnRelayRes.get()
let wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr:
raiseAssert $error
# get the current epoch time
let time = epochTime()
@ -684,16 +696,12 @@ suite "Waku rln relay":
wm3 = WakuMessage(payload: "Valid message".toBytes())
wm4 = WakuMessage(payload: "Invalid message".toBytes())
let
proofAdded1 = wakuRlnRelay.appendRLNProof(wm1, time)
proofAdded2 = wakuRlnRelay.appendRLNProof(wm2, time)
proofAdded3 = wakuRlnRelay.appendRLNProof(wm3, time+float64(wakuRlnRelay.rlnEpochSizeSec))
# ensure proofs are added
require:
proofAdded1.isOk()
proofAdded2.isOk()
proofAdded3.isOk()
wakuRlnRelay.unsafeAppendRLNProof(wm1, time).isOkOr:
raiseAssert $error
wakuRlnRelay.unsafeAppendRLNProof(wm2, time).isOkOr:
raiseAssert $error
wakuRlnRelay.unsafeAppendRLNProof(wm3, time+float64(wakuRlnRelay.rlnEpochSizeSec)).isOkOr:
raiseAssert $error
# validate messages
# validateMessage proc checks the validity of the message fields and adds it to the log (if valid)
@ -717,13 +725,28 @@ suite "Waku rln relay":
let index1 = MembershipIndex(5)
let index2 = MembershipIndex(6)
when defined(rln_v2):
let rlnConf1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index1),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_3"))
else:
let rlnConf1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index1),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_3"))
let wakuRlnRelay1 = (await WakuRlnRelay.new(rlnConf1)).valueOr:
raiseAssert "failed to create waku rln relay: " & $error
when defined(rln_v2):
let rlnConf2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index2),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_4"))
else:
let rlnConf2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index2),
rlnEpochSizeSec: 1,
@ -740,13 +763,10 @@ suite "Waku rln relay":
wm2 = WakuMessage(payload: "Valid message from sender 2".toBytes())
let
proofAdded1 = wakuRlnRelay1.appendRLNProof(wm1, time)
proofAdded2 = wakuRlnRelay2.appendRLNProof(wm2, time)
# ensure proofs are added
assert proofAdded1.isOk(), "failed to append rln proof: " & $proofAdded1.error
assert proofAdded2.isOk(), "failed to append rln proof: " & $proofAdded2.error
wakuRlnRelay1.appendRLNProof(wm1, time).isOkOr:
raiseAssert $error
wakuRlnRelay2.appendRLNProof(wm2, time).isOkOr:
raiseAssert $error
# validate messages
# validateMessage proc checks the validity of the message fields and adds it to the log (if valid)

View File

@ -14,11 +14,13 @@ import
../../../waku/waku_node,
../../../waku/waku_rln_relay,
../testlib/wakucore,
../testlib/wakunode
../testlib/wakunode,
./rln/waku_rln_relay_utils
from std/times import epochTime
procSuite "WakuNode - RLN relay":
# NOTE: we set the rlnRelayUserMessageLimit to 1 to make the tests easier to reason about
asyncTest "testing rln-relay with valid proof":
let
@ -39,33 +41,54 @@ procSuite "WakuNode - RLN relay":
await node1.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node1.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode"))
else:
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode"))
await node1.mountRlnRelay(wakuRlnConfig1)
await node1.start()
# node 2
await node2.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node2.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_2"))
else:
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_2"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_2"))
await node2.mountRlnRelay(wakuRlnConfig2)
await node2.start()
# node 3
await node3.mountRelay(@[DefaultPubsubTopic])
await node3.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig3 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(3.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_3"))
else:
let wakuRlnConfig3 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(3.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_3"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_3"))
await node3.mountRlnRelay(wakuRlnConfig3)
await node3.start()
@ -88,7 +111,7 @@ procSuite "WakuNode - RLN relay":
# prepare the epoch
var message = WakuMessage(payload: @payload, contentTopic: contentTopic)
doAssert(node1.wakuRlnRelay.appendRLNProof(message, epochTime()).isOk())
doAssert(node1.wakuRlnRelay.unsafeAppendRLNProof(message, epochTime()).isOk())
## node1 publishes a message with a rate limit proof, the message is then relayed to node2 which in turn
@ -123,10 +146,18 @@ procSuite "WakuNode - RLN relay":
# mount rlnrelay in off-chain mode
for index, node in nodes:
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index.uint + 1),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_" & $(index+1)))
else:
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(index.uint + 1),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_" & $(index+1))))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_" & $(index+1)))
await node.mountRlnRelay(wakuRlnConfig)
# start them
await allFutures(nodes.mapIt(it.start()))
@ -159,12 +190,14 @@ procSuite "WakuNode - RLN relay":
for i in 0..<3:
var message = WakuMessage(payload: ("Payload_" & $i).toBytes(), contentTopic: contentTopics[0])
doAssert(nodes[0].wakuRlnRelay.appendRLNProof(message, epochTime).isOk())
nodes[0].wakuRlnRelay.unsafeAppendRLNProof(message, epochTime).isOkOr:
raiseAssert $error
messages1.add(message)
for i in 0..<3:
var message = WakuMessage(payload: ("Payload_" & $i).toBytes(), contentTopic: contentTopics[1])
doAssert(nodes[1].wakuRlnRelay.appendRLNProof(message, epochTime).isOk())
nodes[1].wakuRlnRelay.unsafeAppendRLNProof(message, epochTime).isOkOr:
raiseAssert $error
messages2.add(message)
# publish 3 messages from node[0] (last 2 are spam, window is 10 secs)
@ -202,34 +235,54 @@ procSuite "WakuNode - RLN relay":
await node1.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node1.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_4"))
else:
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_4"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_4"))
await node1.mountRlnRelay(wakuRlnConfig1)
await node1.start()
# node 2
await node2.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node2.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_5"))
else:
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_5"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_5"))
await node2.mountRlnRelay(wakuRlnConfig2)
await node2.start()
# node 3
await node3.mountRelay(@[DefaultPubsubTopic])
await node3.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig3 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(3.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_6"))
else:
let wakuRlnConfig3 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(3.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_6"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_6"))
await node3.mountRlnRelay(wakuRlnConfig3)
await node3.start()
# connect them together
@ -261,14 +314,13 @@ procSuite "WakuNode - RLN relay":
when defined(rln_v2):
let nonceManager = node1.wakuRlnRelay.nonceManager
let rateLimitProofRes = node1.wakuRlnRelay.groupManager.generateProof(input,
let rateLimitProofRes = node1.wakuRlnRelay.groupManager.generateProof(concat(input, extraBytes),
epoch,
MessageId(0))
else:
let rateLimitProofRes = node1.wakuRlnRelay.groupManager.generateProof(concat(input, extraBytes), # we add extra bytes to invalidate proof verification against original payload
epoch)
require:
rateLimitProofRes.isOk()
assert rateLimitProofRes.isOk(), $rateLimitProofRes.error # check the proof is generated correctly outside when block to avoid duplication
let rateLimitProof = rateLimitProofRes.get().encode().buffer
let message = WakuMessage(payload: @payload,
@ -311,11 +363,18 @@ procSuite "WakuNode - RLN relay":
await node1.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node1.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_7"))
else:
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_7"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_7"))
await node1.mountRlnRelay(wakuRlnConfig1)
await node1.start()
@ -323,23 +382,36 @@ procSuite "WakuNode - RLN relay":
await node2.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node2.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_8"))
else:
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_8"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_8"))
await node2.mountRlnRelay(wakuRlnConfig2)
await node2.start()
# node 3
await node3.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node3.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig3 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(3.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_9"))
else:
let wakuRlnConfig3 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(3.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_9"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_9"))
await node3.mountRlnRelay(wakuRlnConfig3)
await node3.start()
@ -352,20 +424,18 @@ procSuite "WakuNode - RLN relay":
# create some messages with rate limit proofs
var
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: "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+float64(node3.wakuRlnRelay.rlnEpochSizeSec))
wm4 = WakuMessage(payload: "message 4".toBytes(), contentTopic: contentTopic)
# check proofs are added correctly
check:
proofAdded1.isOk()
proofAdded2.isOk()
proofAdded3.isOk()
node3.wakuRlnRelay.unsafeAppendRLNProof(wm1, time).isOkOr:
raiseAssert $error
node3.wakuRlnRelay.unsafeAppendRLNProof(wm2, time).isOkOr:
raiseAssert $error
node3.wakuRlnRelay.unsafeAppendRLNProof(wm3, time+float64(node3.wakuRlnRelay.rlnEpochSizeSec)).isOkOr:
raiseAssert $error
# relay handler for node3
var completionFut1 = newFuture[bool]()
@ -434,11 +504,18 @@ procSuite "WakuNode - RLN relay":
await node1.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node1.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_10"))
else:
let wakuRlnConfig1 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_10"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_10"))
await node1.mountRlnRelay(wakuRlnConfig1)
await node1.start()
@ -446,11 +523,18 @@ procSuite "WakuNode - RLN relay":
await node2.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node2.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_11"))
else:
let wakuRlnConfig2 = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(2.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_11"),
))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_11"))
await node2.mountRlnRelay(wakuRlnConfig2)
await node2.start()
@ -461,19 +545,17 @@ procSuite "WakuNode - RLN relay":
# create some messages with rate limit proofs
var
wm1 = WakuMessage(payload: "message 1".toBytes(), contentTopic: contentTopic)
proofAdded1 = node1.wakuRlnRelay.appendRLNProof(wm1, time)
# another message in the same epoch as wm1, it will break the messaging rate limit
wm2 = WakuMessage(payload: "message 2".toBytes(), contentTopic: contentTopic)
proofAdded2 = node1.wakuRlnRelay.appendRLNProof(wm2, time)
# wm3 points to the next epoch
wm3 = WakuMessage(payload: "message 3".toBytes(), contentTopic: contentTopic)
proofAdded3 = node1.wakuRlnRelay.appendRLNProof(wm3, time + float64(node1.wakuRlnRelay.rlnEpochSizeSec * 2))
# check proofs are added correctly
check:
proofAdded1.isOk()
proofAdded2.isOk()
proofAdded3.isOk()
node1.wakuRlnRelay.unsafeAppendRLNProof(wm1, time).isOkOr:
raiseAssert $error
node1.wakuRlnRelay.unsafeAppendRLNProof(wm2, time).isOkOr:
raiseAssert $error
node1.wakuRlnRelay.unsafeAppendRLNProof(wm3, time + float64(node1.wakuRlnRelay.rlnEpochSizeSec * 2)).isOkOr:
raiseAssert $error
# relay handler for node2
var completionFut1 = newFuture[bool]()

View File

@ -208,10 +208,18 @@ suite "Waku v2 Rest API - Relay":
let node = testWakuNode()
await node.start()
await node.mountRelay()
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 20,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
else:
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1")))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
await node.mountRlnRelay(wakuRlnConfig)
# RPC server setup
var restPort = Port(0)
@ -411,10 +419,18 @@ suite "Waku v2 Rest API - Relay":
let node = testWakuNode()
await node.start()
await node.mountRelay()
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 20,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
else:
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1")))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
await node.mountRlnRelay(wakuRlnConfig)
# RPC server setup
var restPort = Port(0)
@ -456,10 +472,18 @@ suite "Waku v2 Rest API - Relay":
let node = testWakuNode()
await node.start()
await node.mountRelay()
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 20,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
else:
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1")))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
await node.mountRlnRelay(wakuRlnConfig)
# RPC server setup
var restPort = Port(0)
@ -496,10 +520,18 @@ suite "Waku v2 Rest API - Relay":
let node = testWakuNode()
await node.start()
await node.mountRelay()
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 20,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
else:
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1")))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
await node.mountRlnRelay(wakuRlnConfig)
# RPC server setup
var restPort = Port(0)
@ -541,10 +573,18 @@ suite "Waku v2 Rest API - Relay":
let node = testWakuNode()
await node.start()
await node.mountRelay()
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
when defined(rln_v2):
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayUserMessageLimit: 20,
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
else:
let wakuRlnConfig = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnEpochSizeSec: 1,
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1")))
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1"))
await node.mountRlnRelay(wakuRlnConfig)
# RPC server setup
var restPort = Port(0)

View File

@ -132,7 +132,7 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes
# append the proof to the message
node.wakuRlnRelay.appendRLNProof(message,
float64(getTime().toUnix())).isOkOr:
return RestApiResponse.internalServerError("Failed to publish: error appending RLN proof to message")
return RestApiResponse.internalServerError("Failed to publish: error appending RLN proof to message: " & $error)
(await node.wakuRelay.validateMessage(pubsubTopic, message)).isOkOr:
return RestApiResponse.badRequest("Failed to publish: " & error)
@ -220,7 +220,7 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes
if not node.wakuRlnRelay.isNil():
node.wakuRlnRelay.appendRLNProof(message, float64(getTime().toUnix())).isOkOr:
return RestApiResponse.internalServerError(
"Failed to publish: error appending RLN proof to message")
"Failed to publish: error appending RLN proof to message: " & $error)
(await node.wakuRelay.validateMessage(pubsubTopic, message)).isOkOr:
return RestApiResponse.badRequest("Failed to publish: " & error)

View File

@ -58,7 +58,7 @@ const
# rln-v2: rate commitments are used for the Merkle tree construction, defaulting the UserMessageLimit to 20
# the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here
when defined(rln_v2):
const StaticGroupMerkleRoot* = "0a15ba7c5753ee78e8126603113028a343c1a01ffcb389565c76626be158e964"
const StaticGroupMerkleRoot* = "2c149e48886b5ba3da2edf8db8d7a364ae7a25618489c04cf0c0380f7cdd4d6f"
else:
const StaticGroupMerkleRoot* = "1e534adab58f7d300aaeecae57a25e0a0b18c368a09f720280da92b288950901"

View File

@ -149,7 +149,7 @@ proc serialize*(memIndices: seq[MembershipIndex]): seq[byte] =
proc toEpoch*(t: uint64): Epoch =
## converts `t` to `Epoch` in little-endian order
let bytes = toBytes(t, Endianness.littleEndian)
debug "bytes", bytes = bytes
trace "epoch bytes", bytes = bytes
var epoch: Epoch
discard epoch.copyFrom(bytes)
return epoch

View File

@ -43,9 +43,9 @@ proc `$`*(ne: NonceManagerError): string =
of NonceLimitReached:
return "NonceLimitReached: " & ne.error
proc init*(T: type NonceManager, nonceLimit: Nonce): T =
proc init*(T: type NonceManager, nonceLimit: Nonce, epoch = 1.float64): T =
return NonceManager(
epoch: 0,
epoch: epoch,
nextNonce: 0,
lastNonceTime: 0,
nonceLimit: nonceLimit
@ -62,6 +62,6 @@ proc getNonce*(n: NonceManager): NonceManagerResult[Nonce] =
if retNonce >= n.nonceLimit:
return err(NonceManagerError(kind: NonceLimitReached,
error: "Nonce limit reached. Please wait for the next epoch"))
error: "Nonce limit reached. Please wait for the next epoch. requested nonce: " & $retNonce & " & nonceLimit: " & $n.nonceLimit))
return ok(retNonce)

View File

@ -163,7 +163,11 @@ proc poseidon*(data: seq[seq[byte]]): RlnRelayResult[array[32, byte]] =
when defined(rln_v2):
proc toLeaf*(rateCommitment: RateCommitment): RlnRelayResult[seq[byte]] =
let idCommitment = rateCommitment.idCommitment
let userMessageLimit = cast[array[32, byte]](rateCommitment.userMessageLimit)
var userMessageLimit: array[32, byte]
try:
discard userMessageLimit.copyFrom(toBytes(rateCommitment.userMessageLimit, Endianness.littleEndian))
except CatchableError:
return err("could not convert the user message limit to bytes: " & getCurrentExceptionMsg())
let leaf = poseidon(@[@idCommitment, @userMessageLimit]).valueOr:
return err("could not convert the rate commitment to a leaf")
var retLeaf = newSeq[byte](leaf.len)
@ -179,26 +183,15 @@ when defined(rln_v2):
leaves.add(leaf)
return ok(leaves)
# TODO: collocate this proc with the definition of the RateLimitProof
# and the ProofMetadata types
proc extractMetadata*(proof: RateLimitProof): RlnRelayResult[ProofMetadata] =
return ok(ProofMetadata(
nullifier: proof.nullifier,
shareX: proof.shareX,
shareY: proof.shareY,
externalNullifier: proof.externalNullifier
))
else:
proc extractMetadata*(proof: RateLimitProof): RlnRelayResult[ProofMetadata] =
let externalNullifierRes = poseidon(@[@(proof.epoch),
@(proof.rlnIdentifier)])
if externalNullifierRes.isErr():
let externalNullifier = poseidon(@[@(proof.epoch),
@(proof.rlnIdentifier)]).valueOr:
return err("could not construct the external nullifier")
return ok(ProofMetadata(
nullifier: proof.nullifier,
shareX: proof.shareX,
shareY: proof.shareY,
externalNullifier: externalNullifierRes.get()
externalNullifier: externalNullifier
))
when defined(rln_v2):
@ -266,6 +259,8 @@ when defined(rln_v2):
let output = RateLimitProof(proof: zkproof,
merkleRoot: proofRoot,
externalNullifier: externalNullifier,
epoch: epoch,
rlnIdentifier: rlnIdentifier,
shareX: shareX,
shareY: shareY,
nullifier: nullifier)
@ -339,8 +334,15 @@ proc proofVerify*(rlnInstance: ptr RLN,
validRoots: seq[MerkleNode] = @[]): RlnRelayResult[bool] =
## verifies the proof, returns an error if the proof verification fails
## returns true if the proof is valid
var normalizedProof = proof
when defined(rln_v2):
# when we do this, we ensure that we compute the proof for the derived value
# of the externalNullifier. The proof verification will fail if a malicious peer
# attaches invalid epoch+rlnidentifier pair
normalizedProof.externalNullifier = poseidon(@[@(proof.epoch), @(proof.rlnIdentifier)]).valueOr:
return err("could not construct the external nullifier")
var
proofBytes = serialize(proof, data)
proofBytes = serialize(normalizedProof, data)
proofBuffer = proofBytes.toBuffer()
validProof: bool
rootsBytes = serialize(validRoots)

View File

@ -408,7 +408,7 @@ proc mount(conf: WakuRlnConfig,
when defined(rln_v2):
return WakuRLNRelay(groupManager: groupManager,
nonceManager: NonceManager.init(conf.rlnRelayUserMessageLimit),
nonceManager: NonceManager.init(conf.rlnRelayUserMessageLimit, conf.rlnEpochSizeSec.float),
rlnEpochSizeSec: conf.rlnEpochSizeSec,
rlnMaxEpochGap: uint64(MaxClockGapSeconds/float64(conf.rlnEpochSizeSec)),
onFatalErrorAction: conf.onFatalErrorAction)