Skip to content

Commit

Permalink
fix(rln-relay): integrate group manager. todo spam and reg handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
rymnc committed Jan 22, 2023
1 parent 6a1a473 commit 714c9bc
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 57 deletions.
8 changes: 4 additions & 4 deletions tests/v2/test_rln_group_manager_static.nim
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ suite "Static group manager":

let manager {.used.} = StaticGroupManager(rlnInstance: rlnInstance,
groupSize: 10,
membershipIndex: 5,
membershipIndex: some(5),
groupKeys: credentials)

asyncTest "should initialize successfully":
Expand All @@ -58,7 +58,7 @@ suite "Static group manager":
manager.idCredentials.isSome()
manager.groupKeys.len == 10
manager.groupSize == 10
manager.membershipIndex == 5
manager.membershipIndex == some(5)
manager.groupKeys[5] == manager.idCredentials.get()
manager.latestIndex == 9
merkleRootAfter.inHex() != merkleRootBefore.inHex()
Expand All @@ -72,7 +72,7 @@ suite "Static group manager":

asyncTest "startGroupSync: should guard against uninitialized state":
let manager = StaticGroupManager(groupSize: 0,
membershipIndex: 0,
membershipIndex: some(0),
groupKeys: @[],
rlnInstance: rlnInstance)

Expand All @@ -81,7 +81,7 @@ suite "Static group manager":

asyncTest "register: should guard against uninitialized state":
let manager = StaticGroupManager(groupSize: 0,
membershipIndex: 0,
membershipIndex: some(0),
groupKeys: @[],
rlnInstance: rlnInstance)

Expand Down
21 changes: 0 additions & 21 deletions waku/v2/protocol/waku_rln_relay/conversion_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -126,27 +126,6 @@ proc toIdentityCredentials*(groupKeys: seq[(string, string, string, string)]): R
return err("could not convert the group key to bytes: " & err.msg)
return ok(groupIdCredentials)

# Converts a sequence of tuples containing 2 string (i.e. identity secret hash and commitment) to an IndentityCredential
proc toIdentityCredentials*(groupKeys: seq[(string, string)]): RlnRelayResult[seq[
IdentityCredential]] =
## groupKeys is sequence of membership key tuples in the form of (identity key, identity commitment) all in the hexadecimal format
## the toIdentityCredentials proc populates a sequence of IdentityCredentials using the supplied groupKeys
## Returns an error if the conversion fails

var groupIdCredentials = newSeq[IdentityCredential]()

for i in 0..groupKeys.len-1:
try:
let
idSecretHash = hexToUint[IdentitySecretHash.len*8](groupKeys[i][0]).toBytesLE()
idCommitment = hexToUint[IDCommitment.len*8](groupKeys[i][1]).toBytesLE()
groupIdCredentials.add(IdentityCredential(idSecretHash: idSecretHash,
idCommitment: idCommitment))
except ValueError as err:
warn "could not convert the group key to bytes", err = err.msg
return err("could not convert the group key to bytes: " & err.msg)
return ok(groupIdCredentials)

proc toEpoch*(t: uint64): Epoch =
## converts `t` to `Epoch` in little-endian order
let bytes = toBytes(t, Endianness.littleEndian)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import
../protocol_types
../protocol_types,
../rln
import
options,
chronos,
Expand Down Expand Up @@ -30,6 +31,7 @@ type GroupManagerResult*[T] = Result[T, string]
type
GroupManager* = ref object of RootObj
idCredentials*: Option[IdentityCredential]
membershipIndex*: Option[MembershipIndex]
registerCb*: Option[OnRegisterCallback]
withdrawCb*: Option[OnWithdrawCallback]
rlnInstance*: ptr RLN
Expand Down Expand Up @@ -97,3 +99,38 @@ proc updateValidRootQueue*(rootQueue: var Deque[MerkleNode], root: MerkleNode):
rootQueue.popFirst()
# Push the next root into the queue
rootQueue.addLast(root)

method indexOfRoot*(g: GroupManager, root: MerkleNode): int {.base,gcsafe,raises:[].} =
## returns the index of the root in the merkle tree.
## returns -1 if the root is not found
return g.validRoots.find(root)

method validateRoot*(g: GroupManager, root: MerkleNode): bool {.base,gcsafe,raises:[].} =
## validates the root against the valid roots queue
# Check if the root is in the valid roots queue
if g.indexOfRoot(root) >= 0:
return true
return false

method verifyProof*(g: GroupManager,
input: openArray[byte],
proof: RateLimitProof): GroupManagerResult[bool] {.base,gcsafe,raises:[].} =
## verifies the proof against the input and the current merkle root
let proofVerifyRes = g.rlnInstance.proofVerify(input, proof)
if proofVerifyRes.isErr():
return err("proof verification failed: " & $proofVerifyRes.error())
return ok(proofVerifyRes.value())

method generateProof*(g: GroupManager,
data: openArray[byte],
epoch: Epoch): GroupManagerResult[RateLimitProof] {.base,gcsafe,raises:[].} =
## generates a proof for the given data and epoch
## the proof is generated using the current merkle root
let proofGenRes = proofGen(rlnInstance = g.rlnInstance,
data = data,
memKeys = g.idCredentials.get(),
memIndex = g.membershipIndex.get(),
epoch = epoch)
if proofGenRes.isErr():
return err("proof generation failed: " & $proofGenRes.error())
return ok(proofGenRes.value())
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ type
ethRpc*: Option[Web3]
rlnContract*: Option[RlnContractWithSender]
membershipFee*: Option[Uint256]
membershipIndex*: Option[MembershipIndex]
latestProcessedBlock*: Option[BlockNumber]

template initializedGuard*(g: OnchainGroupManager): untyped =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ type
StaticGroupManager* = ref object of GroupManager
groupKeys*: seq[IdentityCredential]
groupSize*: uint
membershipIndex*: MembershipIndex

template initializedGuard*(g: StaticGroupManager): untyped =
if not g.initialized:
raise newException(ValueError, "StaticGroupManager is not initialized")

proc init*(g: StaticGroupManager): Future[void] {.async,gcsafe.} =
if g.membershipIndex.isNone():
raise newException(ValueError, "Membership index is not set")

let
groupSize = g.groupSize
groupKeys = g.groupKeys
membershipIndex = g.membershipIndex
membershipIndex = g.membershipIndex.get()

if membershipIndex < MembershipIndex(0) or membershipIndex >= MembershipIndex(groupSize):
raise newException(ValueError, "Invalid membership index. Must be within 0 and " & $(groupSize - 1) & "but was " & $membershipIndex)
Expand Down
58 changes: 30 additions & 28 deletions waku/v2/protocol/waku_rln_relay/rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import
import
./group_manager,
./rln,
./credentials,
# ./credentials,
./conversion_utils,
./constants,
./protocol_types,
Expand Down Expand Up @@ -106,6 +106,17 @@ proc calcEpoch*(t: float64): Epoch =
let e = uint64(t/EpochUnitSeconds)
return toEpoch(e)

type WakuRLNRelay* = ref object of RootObj
identityCredential*: IdentityCredential
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
nullifierLog*: Table[Epoch, seq[ProofMetadata]]
lastEpoch*: Epoch # the epoch of the last published rln message
groupManager*: GroupManager

proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): RlnRelayResult[bool] =
## returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same
## epoch and nullifier as `msg`'s epoch and nullifier but different Shamir secret shares
Expand Down Expand Up @@ -244,8 +255,9 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage,
waku_rln_invalid_messages_total.inc(labelValues=["invalid_epoch"])
return MessageValidationResult.Invalid

if not rlnPeer.validateRoot(proof.merkleRoot):
debug "invalid message: provided root does not belong to acceptable window of roots", provided=proof.merkleRoot, validRoots=rlnPeer.validMerkleRoots.mapIt(it.inHex())
let rootValidationRes = rlnPeer.groupManager.validateRoot(proof.merkleRoot)
if not rootValidationRes:
debug "invalid message: provided root does not belong to acceptable window of roots", provided=proof.merkleRoot, validRoots=rlnPeer.groupManager.validRoots.mapIt(it.inHex())
waku_rln_invalid_messages_total.inc(labelValues=["invalid_root"])
return MessageValidationResult.Invalid

Expand All @@ -256,7 +268,7 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage,

waku_rln_proof_verification_total.inc()
waku_rln_proof_verification_duration_seconds.nanosecondTime:
let proofVerificationRes = rlnPeer.rlnInstance.proofVerify(input, proof)
let proofVerificationRes = rlnPeer.groupManager.verifyProof(input, proof)

if proofVerificationRes.isErr():
waku_rln_errors_total.inc(labelValues=["proof_verification"])
Expand All @@ -282,7 +294,7 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage,
# it will never error out
discard rlnPeer.updateLog(msg)
debug "message is valid", payload = string.fromBytes(msg.payload)
let rootIndex = rlnPeer.validMerkleRoots.find(proof.merkleRoot)
let rootIndex = rlnPeer.groupManager.indexOfRoot(proof.merkleRoot)
waku_rln_valid_messages_total.observe(rootIndex.toFloat())
return MessageValidationResult.Valid

Expand All @@ -294,35 +306,22 @@ proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] =
output = concat(wakumessage.payload, contentTopicBytes)
return output

type WakuRLNRelay* = ref object of RootObj
identityCredential*: IdentityCredential
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
nullifierLog*: Table[Epoch, seq[ProofMetadata]]
lastEpoch*: Epoch # the epoch of the last published rln message
groupManager*: GroupManager

proc appendRLNProof*(rlnPeer: WakuRLNRelay, msg: var WakuMessage,
senderEpochTime: float64): bool =
proc appendRLNProof*(rlnPeer: WakuRLNRelay,
msg: var WakuMessage,
senderEpochTime: float64): bool =
## 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.
## The `epoch` field of `RateLimitProof` is derived from the provided `senderEpochTime` (using `calcEpoch()`)

let input = msg.toRLNSignal()

var proof: RateLimitProofResult = proofGen(rlnInstance = rlnPeer.rlnInstance, data = input,
memKeys = rlnPeer.identityCredential,
memIndex = rlnPeer.membershipIndex,
epoch = calcEpoch(senderEpochTime))
let proofGenRes = rlnPeer.groupManager.generateProof(input, calcEpoch(senderEpochTime))

if proof.isErr():
if proofGenRes.isErr():
return false

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

proc addRLNRelayValidator*(wakuRlnRelay: WakuRLNRelay,
Expand Down Expand Up @@ -396,9 +395,12 @@ proc mount(wakuRelay: WakuRelay,
let rlnInstance = rlnInstanceRes.get()
if not conf.rlnRelayDynamic:
# static setup
let parsedGroupKeysRes = StaticGroupKeys.toIdentityCredentials()
if parsedGroupKeysRes.isErr():
raise newException(ValueError, "Static group keys are not valid")
let groupManager = StaticGroupManager(groupSize: StaticGroupSize,
groupKeys: StaticGroupKeys,
membershipIndex: MembershipIndex(conf.rlnRelayMembershipIndex),
groupKeys: parsedGroupKeysRes.get(),
membershipIndex: some(MembershipIndex(conf.rlnRelayMembershipIndex)),
rlnInstance: rlnInstance)
rlnRelay.groupManager = groupManager
# TODO: registration handler
Expand Down Expand Up @@ -436,10 +438,10 @@ proc new*(T: type WakuRlnRelay,

debug "rln-relay input validation passed"
waku_rln_relay_mounting_duration_seconds.nanosecondTime:
let rlnRelayRes = await mount(
let rlnRelay = await mount(
wakuRelay,
conf,
spamHandler,
registrationHandler
)
return rlnRelayRes
return ok(rlnRelay)

0 comments on commit 714c9bc

Please sign in to comment.