diff --git a/tests/waku_rln_relay/rln_v2/test_rln_relay_v2_serde.nim b/tests/waku_rln_relay/rln_v2/test_rln_relay_v2_serde.nim new file mode 100644 index 000000000..d14054ccc --- /dev/null +++ b/tests/waku_rln_relay/rln_v2/test_rln_relay_v2_serde.nim @@ -0,0 +1,90 @@ +{.used.} + +when (NimMajor, NimMinor) < (1, 4): + {.push raises: [Defect].} +else: + {.push raises: [].} + +import + ../rln/waku_rln_relay_utils, + ../../../waku/waku_keystore/protocol_types, + ../../../waku/waku_rln_relay, + ../../../waku/waku_rln_relay/rln + +import + testutils/unittests +import + stew/results, + stint +from std/times import epochTime + +func fromStrToBytesLe(v: string): seq[byte] = + try: + return @(hexToUint[256](v).toBytesLE()) + except ValueError: + # this should never happen + return @[] + +func defaultIdentityCredential*(): IdentityCredential = + # zero out the values we don't need + return IdentityCredential( + idTrapdoor: default(IdentityTrapdoor), + idNullifier: default(IdentityNullifier), + idSecretHash: fromStrToBytesLe("7984f7c054ad7793d9f31a1e9f29eaa8d05966511e546bced89961eb8874ab9"), + idCommitment: fromStrToBytesLe("51c31de3bff7e52dc7b2eb34fc96813bacf38bde92d27fe326ce5d8296322a7") + ) + +func defaultRateCommitment*(): RateCommitment = + let idCredential = defaultIdentityCredential() + return RateCommitment( + idCommitment: idCredential.idCommitment, + userMessageLimit: 100 + ) + +suite "RLN Relay v2: serde": + test "toLeaf: converts a rateCommitment to a valid leaf": + # this test vector is from zerokit + let rateCommitment = defaultRateCommitment() + + let leafRes = toLeaf(rateCommitment) + assert leafRes.isOk(), $leafRes.error + + let expectedLeaf = "09beac7784abfadc9958b3176b352389d0b969ccc7f8bccf3e968ed632e26eca" + check expectedLeaf == leafRes.value.inHex() + + + test "proofGen: generates a valid zk proof": + # this test vector is from zerokit + let rlnInstance = createRLNInstanceWrapper() + assert rlnInstance.isOk, $rlnInstance.error + let rln = rlnInstance.value + + let credential = defaultIdentityCredential() + let rateCommitment = defaultRateCommitment() + let success = rln.insertMember(@(rateCommitment.toLeaf().value)) + let merkleRootRes = rln.getMerkleRoot() + assert merkleRootRes.isOk, $merkleRootRes.error + let merkleRoot = merkleRootRes.value + + assert success, "failed to insert member" + + let proofRes = rln.proofGen(data = @[], + membership = credential, + userMessageLimit = rateCommitment.userMessageLimit, + messageId = 0, + index = 0, + epoch = calcEpoch(epochTime())) + + assert proofRes.isOk, $proofRes.error + + let proof = proofRes.value + + let proofVerifyRes = rln.proofVerify(data = @[], + proof = proof, + validRoots = @[merkleRoot]) + + assert proofVerifyRes.isOk, $proofVerifyRes.error + assert proofVerifyRes.value, "proof verification failed" + + + diff --git a/waku/waku_rln_relay/conversion_utils.nim b/waku/waku_rln_relay/conversion_utils.nim index c00e6a50d..814eb98af 100644 --- a/waku/waku_rln_relay/conversion_utils.nim +++ b/waku/waku_rln_relay/conversion_utils.nim @@ -57,6 +57,17 @@ proc encodeLengthPrefix*(input: openArray[byte]): seq[byte] = output = concat(@len, @input) return output +proc serialize*(v: uint64): array[32, byte] = + ## a private proc to convert uint64 to a byte seq + ## this conversion is used in the proofGen proc + + ## converts `v` to a byte seq in little-endian order + let bytes = toBytes(v, Endianness.littleEndian) + var output: array[32, byte] + discard output.copyFrom(bytes) + return output + + when defined(rln_v2): proc serialize*(idSecretHash: IdentitySecretHash, memIndex: MembershipIndex, @@ -69,8 +80,8 @@ when defined(rln_v2): ## the serialization is done as instructed in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L146 ## [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal ] let memIndexBytes = toBytes(uint64(memIndex), Endianness.littleEndian) - let userMessageLimitBytes = toBytes(uint64(userMessageLimit), Endianness.littleEndian) - let messageIdBytes = toBytes(uint64(messageId), Endianness.littleEndian) + let userMessageLimitBytes = userMessageLimit.serialize() + let messageIdBytes = messageId.serialize() let lenPrefMsg = encodeLengthPrefix(msg) let output = concat(@idSecretHash, @memIndexBytes, @userMessageLimitBytes, @messageIdBytes, @externalNullifier, lenPrefMsg) return output diff --git a/waku/waku_rln_relay/rln/wrappers.nim b/waku/waku_rln_relay/rln/wrappers.nim index 384db2538..da1a1d3b9 100644 --- a/waku/waku_rln_relay/rln/wrappers.nim +++ b/waku/waku_rln_relay/rln/wrappers.nim @@ -163,8 +163,8 @@ proc poseidon*(data: seq[seq[byte]]): RlnRelayResult[array[32, byte]] = when defined(rln_v2): func toLeaf*(rateCommitment: RateCommitment): RlnRelayResult[MerkleNode] {.inline.} = let idCommitment = rateCommitment.idCommitment - let userMessageLimit = rateCommitment.userMessageLimit - let leafRes = poseidon(@[@idCommitment, cast[seq[byte]](userMessageLimit)]) + let userMessageLimit = cast[array[32, byte]](rateCommitment.userMessageLimit) + let leafRes = poseidon(@[@idCommitment, @userMessageLimit]) return leafRes func toLeaves*(rateCommitments: seq[RateCommitment]): RlnRelayResult[seq[MerkleNode]] {.inline.} =