Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rln-relay-v2): nonce/messageId manager #2413

Merged
merged 3 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions apps/chat2/chat2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ proc publish(c: Chat, line: string) =
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
let appendRes = c.node.wakuRlnRelay.appendRLNProof(message, float64(time))
if appendRes.isErr():
debug "could not append rate limit proof to the message"
else:
debug "rate limit proof is appended to the message", success=success
debug "rate limit proof is appended to the message"
let decodeRes = RateLimitProof.init(message.proof)
if decodeRes.isErr():
error "could not decode the RLN proof"
Expand Down Expand Up @@ -514,14 +514,25 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =

echo "rln-relay preparation is in progress..."

let rlnConf = WakuRlnConfig(
rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: conf.rlnRelayCredIndex,
rlnRelayEthContractAddress: conf.rlnRelayEthContractAddress,
rlnRelayEthClientAddress: conf.rlnRelayEthClientAddress,
rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword
)
when defined(rln_v2):
let rlnConf = WakuRlnConfig(
rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: conf.rlnRelayCredIndex,
rlnRelayEthContractAddress: conf.rlnRelayEthContractAddress,
rlnRelayEthClientAddress: conf.rlnRelayEthClientAddress,
rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword,
rlnRelayUserMessageLimit: conf.rlnRelayUserMessageLimit,
)
else:
let rlnConf = WakuRlnConfig(
rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: conf.rlnRelayCredIndex,
rlnRelayEthContractAddress: conf.rlnRelayEthContractAddress,
rlnRelayEthClientAddress: conf.rlnRelayEthClientAddress,
rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword,
)

waitFor node.mountRlnRelay(rlnConf,
spamHandler=some(spamHandler))
Expand Down
5 changes: 5 additions & 0 deletions apps/chat2/config_chat2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ type
defaultValue: ""
name: "rln-relay-cred-password" }: string

rlnRelayUserMessageLimit* {.
desc: "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.",
defaultValue: 1,
name: "rln-relay-user-message-limit" .}: uint64

# NOTE: Keys are different in nim-libp2p
proc parseCmdArg*(T: type crypto.PrivateKey, p: string): T =
try:
Expand Down
30 changes: 21 additions & 9 deletions apps/wakunode2/app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -462,15 +462,27 @@ proc setupProtocols(node: WakuNode,

if conf.rlnRelay:

let rlnConf = WakuRlnConfig(
rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: conf.rlnRelayCredIndex,
rlnRelayEthContractAddress: conf.rlnRelayEthContractAddress,
rlnRelayEthClientAddress: conf.rlnRelayEthClientAddress,
rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword,
rlnRelayTreePath: conf.rlnRelayTreePath,
)
when defined(rln_v2):
let rlnConf = WakuRlnConfig(
rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: conf.rlnRelayCredIndex,
rlnRelayEthContractAddress: conf.rlnRelayEthContractAddress,
rlnRelayEthClientAddress: conf.rlnRelayEthClientAddress,
rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword,
rlnRelayTreePath: conf.rlnRelayTreePath,
rlnRelayUserMessageLimit: conf.rlnRelayUserMessageLimit,
)
else:
let rlnConf = WakuRlnConfig(
rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: conf.rlnRelayCredIndex,
rlnRelayEthContractAddress: conf.rlnRelayEthContractAddress,
rlnRelayEthClientAddress: conf.rlnRelayEthClientAddress,
rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword,
rlnRelayTreePath: conf.rlnRelayTreePath,
)

try:
waitFor node.mountRlnRelay(rlnConf)
Expand Down
10 changes: 5 additions & 5 deletions tests/node/test_wakunode_relay_rln.nim
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ proc sendRlnMessage(
payload: seq[byte] = "Hello".toBytes(),
): Future[bool] {.async.} =
var message = WakuMessage(payload: payload, contentTopic: contentTopic)
doAssert(client.wakuRlnRelay.appendRLNProof(message, epochTime()))
doAssert(client.wakuRlnRelay.appendRLNProof(message, epochTime()).isOk())
discard await client.publish(some(pubsubTopic), message)
let isCompleted = await completionFuture.withTimeout(FUTURE_TIMEOUT)
return isCompleted
Expand Down Expand Up @@ -249,18 +249,18 @@ suite "Waku RlnRelay - End to End":
WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic)

doAssert(
client.wakuRlnRelay.appendRLNProof(message1b, epoch + EpochUnitSeconds * 0)
client.wakuRlnRelay.appendRLNProof(message1b, epoch + EpochUnitSeconds * 0).isOk()
)
doAssert(
client.wakuRlnRelay.appendRLNProof(message1kib, epoch + EpochUnitSeconds * 1)
client.wakuRlnRelay.appendRLNProof(message1kib, epoch + EpochUnitSeconds * 1).isOk()
)
doAssert(
client.wakuRlnRelay.appendRLNProof(message150kib, epoch + EpochUnitSeconds * 2)
client.wakuRlnRelay.appendRLNProof(message150kib, epoch + EpochUnitSeconds * 2).isOk()
)
doAssert(
client.wakuRlnRelay.appendRLNProof(
message151kibPlus, epoch + EpochUnitSeconds * 3
)
).isOk()
)

# When sending the 1B message
Expand Down
3 changes: 2 additions & 1 deletion tests/waku_rln_relay/test_all.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import
./test_rln_group_manager_onchain,
./test_rln_group_manager_static,
./test_waku_rln_relay,
./test_wakunode_rln_relay
./test_wakunode_rln_relay,
./test_rln_nonce_manager
51 changes: 51 additions & 0 deletions tests/waku_rln_relay/test_rln_nonce_manager.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{.used.}

import
testutils/unittests,
chronos,
os
import
../../../waku/waku_rln_relay/nonce_manager


suite "Nonce manager":
test "should initialize successfully":
let nm = NonceManager.init(nonceLimit = 100.uint)

check:
nm.nonceLimit == 100.uint
nm.nextNonce == 0.uint

test "should generate a new nonce":
let nm = NonceManager.init(nonceLimit = 100.uint)
let nonceRes = nm.get()

assert nonceRes.isOk(), $nonceRes.error

check:
nonceRes.get() == 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

check:
nonceRes2.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()
sleep(1)
let nonceRes2 = nm.get()

assert nonceRes.isOk(), $nonceRes.error
assert nonceRes2.isOk(), $nonceRes2.error

check:
nonceRes.value == 0.uint
nonceRes2.value == 0.uint
6 changes: 3 additions & 3 deletions tests/waku_rln_relay/test_waku_rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -687,9 +687,9 @@ suite "Waku rln relay":

# ensure proofs are added
require:
proofAdded1
proofAdded2
proofAdded3
proofAdded1.isOk()
proofAdded2.isOk()
proofAdded3.isOk()

# validate messages
# validateMessage proc checks the validity of the message fields and adds it to the log (if valid)
Expand Down
18 changes: 9 additions & 9 deletions tests/waku_rln_relay/test_wakunode_rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ procSuite "WakuNode - RLN relay":

# prepare the epoch
var message = WakuMessage(payload: @payload, contentTopic: contentTopic)
doAssert(node1.wakuRlnRelay.appendRLNProof(message, epochTime()))
doAssert(node1.wakuRlnRelay.appendRLNProof(message, epochTime()).isOk())


## node1 publishes a message with a rate limit proof, the message is then relayed to node2 which in turn
Expand Down Expand Up @@ -155,12 +155,12 @@ 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))
doAssert(nodes[0].wakuRlnRelay.appendRLNProof(message, epochTime).isOk())
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))
doAssert(nodes[1].wakuRlnRelay.appendRLNProof(message, epochTime).isOk())
messages2.add(message)

# publish 3 messages from node[0] (last 2 are spam, window is 10 secs)
Expand Down Expand Up @@ -346,9 +346,9 @@ procSuite "WakuNode - RLN relay":

# check proofs are added correctly
check:
proofAdded1
proofAdded2
proofAdded3
proofAdded1.isOk()
proofAdded2.isOk()
proofAdded3.isOk()

# relay handler for node3
var completionFut1 = newFuture[bool]()
Expand Down Expand Up @@ -452,9 +452,9 @@ procSuite "WakuNode - RLN relay":

# check proofs are added correctly
check:
proofAdded1
proofAdded2
proofAdded3
proofAdded1.isOk()
proofAdded2.isOk()
proofAdded3.isOk()

# relay handler for node2
var completionFut1 = newFuture[bool]()
Expand Down
10 changes: 4 additions & 6 deletions waku/waku_api/jsonrpc/relay/handlers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,8 @@ proc installRelayApiHandlers*(node: WakuNode, server: RpcServer, cache: MessageC
# if RLN is mounted, append the proof to the message
if not node.wakuRlnRelay.isNil():
# append the proof to the message
let success = node.wakuRlnRelay.appendRLNProof(message,
float64(getTime().toUnix()))
if not success:
node.wakuRlnRelay.appendRLNProof(message,
float64(getTime().toUnix())).isOkOr:
raise newException(ValueError, "Failed to publish: error appending RLN proof to message")
# validate the message before sending it
let result = node.wakuRlnRelay.validateMessageAndUpdateLog(message)
Expand Down Expand Up @@ -201,9 +200,8 @@ proc installRelayApiHandlers*(node: WakuNode, server: RpcServer, cache: MessageC
# if RLN is mounted, append the proof to the message
if not node.wakuRlnRelay.isNil():
# append the proof to the message
let success = node.wakuRlnRelay.appendRLNProof(message,
float64(getTime().toUnix()))
if not success:
node.wakuRlnRelay.appendRLNProof(message,
float64(getTime().toUnix())).isOkOr:
raise newException(ValueError, "Failed to publish: error appending RLN proof to message")
# validate the message before sending it
let result = node.wakuRlnRelay.validateMessageAndUpdateLog(message)
Expand Down
7 changes: 3 additions & 4 deletions waku/waku_api/rest/relay/handlers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,8 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes
# if RLN is mounted, append the proof to the message
if not node.wakuRlnRelay.isNil():
# append the proof to the message
let success = node.wakuRlnRelay.appendRLNProof(message,
float64(getTime().toUnix()))
if not success:
node.wakuRlnRelay.appendRLNProof(message,
float64(getTime().toUnix())).isOkOr:
return RestApiResponse.internalServerError("Failed to publish: error appending RLN proof to message")

(await node.wakuRelay.validateMessage(pubsubTopic, message)).isOkOr:
Expand Down Expand Up @@ -219,7 +218,7 @@ proc installRelayApiHandlers*(router: var RestRouter, node: WakuNode, cache: Mes

# if RLN is mounted, append the proof to the message
if not node.wakuRlnRelay.isNil():
if not node.wakuRlnRelay.appendRLNProof(message, float64(getTime().toUnix())):
node.wakuRlnRelay.appendRLNProof(message, float64(getTime().toUnix())).isOkOr:
return RestApiResponse.internalServerError(
"Failed to publish: error appending RLN proof to message")

Expand Down
10 changes: 6 additions & 4 deletions waku/waku_rln_relay/group_manager/group_manager_base.nim
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,12 @@ when defined(rln_v2):
return err("user message limit is not set")
waku_rln_proof_generation_duration_seconds.nanosecondTime:
let proof = proofGen(rlnInstance = g.rlnInstance,
data = data,
memKeys = g.idCredentials.get(),
memIndex = g.membershipIndex.get(),
epoch = epoch).valueOr:
data = data,
membership = g.idCredentials.get(),
index = g.membershipIndex.get(),
epoch = epoch,
userMessageLimit = g.userMessageLimit.get(),
messageId = messageId).valueOr:
return err("proof generation failed: " & $error)
return ok(proof)
else:
Expand Down
67 changes: 67 additions & 0 deletions waku/waku_rln_relay/nonce_manager.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}

import
chronos,
stew/results,
times
import
./constants

export
chronos,
times,
results,
constants

# This module contains the NonceManager interface
# The NonceManager is responsible for managing the messageId used to generate RLN proofs
# It should be used to fetch a new messageId every time a proof is generated
# It refreshes the messageId every `epoch` seconds

type
Nonce* = uint64
NonceManager* = ref object of RootObj
epoch*: float64
nextNonce*: Nonce
lastNonceTime*: float64
nonceLimit*: Nonce

NonceManagerErrorKind* = enum
NonceLimitReached

NonceManagerError* = object
kind*: NonceManagerErrorKind
error*: string

NonceManagerResult*[T] = Result[T, NonceManagerError]

proc `$`*(ne: NonceManagerError): string =
case ne.kind
of NonceLimitReached:
return "NonceLimitReached: " & ne.error

proc init*(T: type NonceManager, nonceLimit: Nonce, epoch = EpochUnitSeconds): T =
return NonceManager(
epoch: epoch,
nextNonce: 0,
lastNonceTime: 0,
nonceLimit: nonceLimit
)


proc get*(n: NonceManager): NonceManagerResult[Nonce] =
let now = getTime().toUnixFloat()
var retNonce = n.nextNonce
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the if/else you set the value of retNonce in both cases. So this assigment will be overridden no matter what? Meaning its not needed?

or well, you can avoid the else, something like?

var retNonce = n.nextNonce
if now - n.lastNonceTime >= n.epoch:
  retNonce = 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in 699a5bc


if now - n.lastNonceTime >= n.epoch: retNonce = 0
n.nextNonce = retNonce + 1
n.lastNonceTime = now

if retNonce >= n.nonceLimit:
return err(NonceManagerError(kind: NonceLimitReached,
error: "Nonce limit reached. Please wait for the next epoch"))

return ok(retNonce)
Loading
Loading