Skip to content

Commit

Permalink
feat(rln-relay-v2): nonce/messageId manager
Browse files Browse the repository at this point in the history
  • Loading branch information
rymnc committed Feb 9, 2024
1 parent 2d46c35 commit 39e473b
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 50 deletions.
31 changes: 21 additions & 10 deletions apps/chat2/chat2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ 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:
let appendRes = c.node.wakuRlnRelay.appendRLNProof(message, float64(time))
if appendRes.isErr():
debug "could not append rate limit proof to the message", success=success
else:
debug "rate limit proof is appended to the message", success=success
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
12 changes: 6 additions & 6 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(not client.wakuRlnRelay.appendRLNProof(message, epochTime()).isErr())
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)
not client.wakuRlnRelay.appendRLNProof(message1b, epoch + EpochUnitSeconds * 0).isErr()
)
doAssert(
client.wakuRlnRelay.appendRLNProof(message1kib, epoch + EpochUnitSeconds * 1)
not client.wakuRlnRelay.appendRLNProof(message1kib, epoch + EpochUnitSeconds * 1).isErr()
)
doAssert(
client.wakuRlnRelay.appendRLNProof(message150kib, epoch + EpochUnitSeconds * 2)
not client.wakuRlnRelay.appendRLNProof(message150kib, epoch + EpochUnitSeconds * 2).isErr()
)
doAssert(
client.wakuRlnRelay.appendRLNProof(
not client.wakuRlnRelay.appendRLNProof(
message151kibPlus, epoch + EpochUnitSeconds * 3
)
).isErr()
)

# When sending the 1B message
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
60 changes: 60 additions & 0 deletions waku/waku_rln_relay/nonce_manager.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}

import
options,
chronos,
stew/results,
times
import
./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
lastNonce: Nonce
lastNonceTime: float64
nonceLimit: Nonce

NonceManagerError* = enum
NonceLimitReached

NonceManagerResult[T] = Result[T, NonceManagerError]

proc `$`*(ne: NonceManagerError): string =
case ne
of NonceManagerError.NonceLimitReached: return "Nonce limit reached"

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


proc get*(n: NonceManager): NonceManagerResult[Nonce] =
let now = getTime().toUnixFloat()

if now - n.lastNonceTime > n.epoch:
n.lastNonce = n.lastNonce + 1
n.lastNonceTime = now
else:
# we are in a new epoch
n.lastNonce = 0
n.lastNonceTime = now

if n.lastNonce > n.nonceLimit:
return err(NonceManagerError.NonceLimitReached)

return ok(n.lastNonce)

33 changes: 22 additions & 11 deletions waku/waku_rln_relay/rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import
./constants,
./protocol_types,
./protocol_metrics

when defined(rln_v2):
import ./nonce_manager

import
../waku_relay, # for WakuRelayHandler
../waku_core,
Expand All @@ -37,6 +41,8 @@ type WakuRlnConfig* = object
rlnRelayCredPath*: string
rlnRelayCredPassword*: string
rlnRelayTreePath*: string
when defined(rln_v2):
rlnRelayUserMessageLimit*: uint64

proc createMembershipList*(rln: ptr RLN, n: int): RlnRelayResult[(
seq[RawMembershipCredentials], string
Expand Down Expand Up @@ -78,7 +84,8 @@ type WakuRLNRelay* = ref object of RootObj
nullifierLog*: OrderedTable[Epoch, seq[ProofMetadata]]
lastEpoch*: Epoch # the epoch of the last published rln message
groupManager*: GroupManager
nonce*: uint64
when defined(rln_v2):
nonceManager: NonceManager

method stop*(rlnPeer: WakuRLNRelay) {.async: (raises: [Exception]).} =
## stops the rln-relay protocol
Expand Down Expand Up @@ -282,7 +289,7 @@ proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] =

proc appendRLNProof*(rlnPeer: WakuRLNRelay,
msg: var WakuMessage,
senderEpochTime: float64): bool =
senderEpochTime: float64): RlnRelayResult[void] =
## returns true if it can create and append a `RateLimitProof` to the supplied `msg`
## returns false otherwise
## `senderEpochTime` indicates the number of seconds passed since Unix epoch. The fractional part holds sub-seconds.
Expand All @@ -292,16 +299,16 @@ proc appendRLNProof*(rlnPeer: WakuRLNRelay,
let epoch = calcEpoch(senderEpochTime)

when defined(rln_v2):
# TODO: add support for incrementing nonce, will address in another PR
let proofGenRes = rlnPeer.groupManager.generateProof(input, epoch, 1)
let nonce = rlnPeer.nonceManager.get().valueOr:
return err("could not get new message id to generate an rln proof: " & $error)
let proof = rlnPeer.groupManager.generateProof(input, epoch, nonce).valueOr:
return err("could not generate rln-v2 proof: " & $error)
else:
let proofGenRes = rlnPeer.groupManager.generateProof(input, epoch)
let proof = rlnPeer.groupManager.generateProof(input, epoch).valueOr:
return err("could not generate rln proof: " & $error)

if proofGenRes.isErr():
return false

msg.proof = proofGenRes.get().encode().buffer
return true
msg.proof = proof.encode().buffer
return ok()

proc clearNullifierLog(rlnPeer: WakuRlnRelay) =
# clear the first MaxEpochGap epochs of the nullifer log
Expand Down Expand Up @@ -397,7 +404,11 @@ proc mount(conf: WakuRlnConfig,
# Start the group sync
await groupManager.startGroupSync()

return WakuRLNRelay(groupManager: groupManager)
when defined(rln_v2):
return WakuRLNRelay(groupManager: groupManager,
nonceManager: NonceManager.init(conf.rlnRelayUserMessageLimit))
else:
return WakuRLNRelay(groupManager: groupManager)

proc isReady*(rlnPeer: WakuRLNRelay): Future[bool] {.async: (raises: [Exception]).} =
## returns true if the rln-relay protocol is ready to relay messages
Expand Down

0 comments on commit 39e473b

Please sign in to comment.