From 0a1700e239b926b32798cb918bf5ac6a25da2c19 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:49:20 +0200 Subject: [PATCH] replace RLN specific Result with generic Result (#4011) --- logos_delivery/waku/rln/nullifier_log.nim | 4 +- logos_delivery/waku/rln/proof.nim | 2 +- logos_delivery/waku/rln/protocol_types.nim | 15 ++----- logos_delivery/waku/rln/rln.nim | 4 +- logos_delivery/waku/rln/rln/rln_interface.nim | 12 +++--- logos_delivery/waku/rln/rln/wrappers.nim | 40 ++++++++++--------- .../rln/waku_rln_relay_utils.nim | 5 ++- tests/waku_rln_relay/utils_onchain.nim | 2 +- 8 files changed, 41 insertions(+), 43 deletions(-) diff --git a/logos_delivery/waku/rln/nullifier_log.nim b/logos_delivery/waku/rln/nullifier_log.nim index 372433c72..8a1a3cd6c 100644 --- a/logos_delivery/waku/rln/nullifier_log.nim +++ b/logos_delivery/waku/rln/nullifier_log.nim @@ -10,7 +10,7 @@ logScope: proc hasDuplicate*( rlnPeer: Rln, epoch: Epoch, proofMetadata: ProofMetadata -): RlnResult[bool] = +): Result[bool, string] = ## returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same ## epoch and nullifier as `proofMetadata`'s epoch and nullifier ## otherwise, returns false @@ -32,7 +32,7 @@ proc hasDuplicate*( proc updateLog*( rlnPeer: Rln, epoch: Epoch, proofMetadata: ProofMetadata -): RlnResult[void] = +): Result[void, string] = ## saves supplied proofMetadata `proofMetadata` ## in the `nullifierLog` of the `rlnPeer` ## Returns an error if it cannot update the log diff --git a/logos_delivery/waku/rln/proof.nim b/logos_delivery/waku/rln/proof.nim index 4a0c332c1..fbbe2292e 100644 --- a/logos_delivery/waku/rln/proof.nim +++ b/logos_delivery/waku/rln/proof.nim @@ -56,7 +56,7 @@ proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] = proc generateRLNProof*( rln: Rln, input: seq[byte], senderEpochTime: float64 -): Future[RlnResult[seq[byte]]] {.async.} = +): Future[Result[seq[byte], string]] {.async.} = let epoch = rln.calcEpoch(senderEpochTime) let nonce = rln.nonceManager.getNonce().valueOr: return err("could not get new message id to generate an rln proof: " & $error) diff --git a/logos_delivery/waku/rln/protocol_types.nim b/logos_delivery/waku/rln/protocol_types.nim index 29ee97083..66aa72f53 100644 --- a/logos_delivery/waku/rln/protocol_types.nim +++ b/logos_delivery/waku/rln/protocol_types.nim @@ -5,11 +5,8 @@ import ../waku_core, ../waku_keystore, ../common/protobuf export waku_keystore, waku_core -type RlnResult*[T] = Result[T, string] - ## RLN is a Nim wrapper for the data types used in zerokit RLN type RlnRaw* {.incompleteStruct.} = object -type RlnInstanceResult* = RlnResult[ptr RlnRaw] type MerkleNode* = array[32, byte] @@ -72,14 +69,10 @@ type ProofMetadata* = object shareY*: MerkleNode externalNullifier*: Nullifier -type - MessageValidationResult* {.pure.} = enum - Valid - Invalid - Spam - - MerkleNodeResult* = RlnResult[MerkleNode] - RateLimitProofResult* = RlnResult[RateLimitProof] +type MessageValidationResult* {.pure.} = enum + Valid + Invalid + Spam # Protobufs enc and init proc init*(T: type RateLimitProof, buffer: seq[byte]): ProtoResult[T] = diff --git a/logos_delivery/waku/rln/rln.nim b/logos_delivery/waku/rln/rln.nim index dab5ec3ef..ec20cf0c8 100644 --- a/logos_delivery/waku/rln/rln.nim +++ b/logos_delivery/waku/rln/rln.nim @@ -183,7 +183,7 @@ proc monitorEpochs(rln: Rln) {.async.} = proc mount( conf: WakuRlnConfig, registrationHandler = none(RegistrationHandler) -): Future[RlnResult[Rln]] {.async.} = +): Future[Result[Rln, string]] {.async.} = var groupManager: GroupManager rln: Rln @@ -258,7 +258,7 @@ proc isReady*(rlnPeer: Rln): Future[bool] {.async.} = proc new*( T: type Rln, conf: WakuRlnConfig, registrationHandler = none(RegistrationHandler) -): Future[RlnResult[Rln]] {.async.} = +): Future[Result[Rln, string]] {.async.} = ## Mounts the rln-relay protocol on the node. ## The rln-relay protocol can be mounted in two modes: on-chain and off-chain. ## Returns an error if the rln-relay protocol could not be mounted. diff --git a/logos_delivery/waku/rln/rln/rln_interface.nim b/logos_delivery/waku/rln/rln/rln_interface.nim index 60272ea3e..d9b2fc903 100644 --- a/logos_delivery/waku/rln/rln/rln_interface.nim +++ b/logos_delivery/waku/rln/rln/rln_interface.nim @@ -318,14 +318,14 @@ proc vecToSeq*(data: Vec_uint8): seq[byte] = if result.len > 0: copyMem(addr result[0], data.dataPtr, result.len) -proc seqToFixed32*(data: openArray[byte]): RlnResult[array[32, byte]] = +proc seqToFixed32*(data: openArray[byte]): Result[array[32, byte], string] = if data.len != FieldElementSize: return err("Expected 32 bytes, got " & $data.len) var output: array[32, byte] copyMem(addr output[0], unsafeAddr data[0], FieldElementSize) ok(output) -proc cfrToBytesLe*(cfr: ptr CFr): RlnResult[array[32, byte]] = +proc cfrToBytesLe*(cfr: ptr CFr): Result[array[32, byte], string] = let bytes = ffi_cfr_to_bytes_le(cfr) defer: ffi_vec_u8_free(bytes) @@ -333,7 +333,7 @@ proc cfrToBytesLe*(cfr: ptr CFr): RlnResult[array[32, byte]] = return err("Invalid field byte length: " & $bytes.len) seqToFixed32(vecToSeq(bytes)) -proc bytesToCfrLe*(data: openArray[byte]): RlnResult[ptr CFr] = +proc bytesToCfrLe*(data: openArray[byte]): Result[ptr CFr, string] = ## Allocate a ptr CFr from raw bytes. Caller MUST ffi_cfr_free(x). var vec = toVecUint8(data) let res = ffi_bytes_le_to_cfr(addr vec) @@ -343,7 +343,7 @@ proc bytesToCfrLe*(data: openArray[byte]): RlnResult[ptr CFr] = proc cfrResultToBytes*( res: CResultCFrPtrVecU8, prefix: string -): RlnResult[array[32, byte]] = +): Result[array[32, byte], string] = ## Consume a CResultCFrPtrVecU8: read bytes if ok, free the CFr, or ## propagate the error (also freeing the error string). if res.ok.isNil: @@ -352,7 +352,7 @@ proc cfrResultToBytes*( ffi_cfr_free(res.ok) cfrToBytesLe(res.ok) -proc hashToFieldLe*(data: openArray[byte]): RlnResult[ptr CFr] = +proc hashToFieldLe*(data: openArray[byte]): Result[ptr CFr, string] = ## Caller MUST ffi_cfr_free the returned ptr. var vec = toVecUint8(data) let cfr = ffi_hash_to_field_le(addr vec) @@ -360,7 +360,7 @@ proc hashToFieldLe*(data: openArray[byte]): RlnResult[ptr CFr] = return err("Failed to hash to field") ok(cfr) -proc poseidonPairLe*(a, b: openArray[byte]): RlnResult[array[32, byte]] = +proc poseidonPairLe*(a, b: openArray[byte]): Result[array[32, byte], string] = ## Poseidon hash of exactly two 32-byte field elements (little-endian). ## zerokit v2 FFI only exposes pair-input Poseidon; unary is not supported. let aPtr = bytesToCfrLe(a).valueOr: diff --git a/logos_delivery/waku/rln/rln/wrappers.nim b/logos_delivery/waku/rln/rln/wrappers.nim index 5a39aedf0..4f46fc164 100644 --- a/logos_delivery/waku/rln/rln/wrappers.nim +++ b/logos_delivery/waku/rln/rln/wrappers.nim @@ -12,9 +12,9 @@ logScope: # Forward decl; body defined below. proc generateExternalNullifier*( epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnResult[ExternalNullifier] +): Result[ExternalNullifier, string] -proc toRootVec(validRoots: seq[MerkleNode]): RlnResult[Vec_CFr] = +proc toRootVec(validRoots: seq[MerkleNode]): Result[Vec_CFr, string] = ## Caller MUST ffi_vec_cfr_free the returned Vec_CFr. var roots = ffi_vec_cfr_new(csize_t(validRoots.len)) for root in validRoots: @@ -27,7 +27,7 @@ proc toRootVec(validRoots: seq[MerkleNode]): RlnResult[Vec_CFr] = proc proofPtrToRateLimitProof( proofPtr: ptr FFI_RLNProof, epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnResult[RateLimitProof] = +): Result[RateLimitProof, string] = var proofHandle = proofPtr let proofBytesRes = ffi_rln_proof_to_bytes_le(addr proofHandle) if hasError(proofBytesRes.err): @@ -93,7 +93,7 @@ proc proofPtrToRateLimitProof( ok(output) -proc parseCredentialVec(vec: var Vec_CFr): RlnResult[IdentityCredential] = +proc parseCredentialVec(vec: var Vec_CFr): Result[IdentityCredential, string] = ## Vec_CFr order: idTrapdoor, idNullifier, idSecretHash, idCommitment. if int(ffi_vec_cfr_len(addr vec)) != 4: return err("Unexpected credential element count") @@ -120,13 +120,13 @@ proc parseCredentialVec(vec: var Vec_CFr): RlnResult[IdentityCredential] = ) ) -proc membershipKeyGen*(): RlnResult[IdentityCredential] = +proc membershipKeyGen*(): Result[IdentityCredential, string] = var vec = ffi_extended_key_gen() defer: ffi_vec_cfr_free(vec) parseCredentialVec(vec) -proc createRLNInstanceLocal(): RlnInstanceResult = +proc createRLNInstanceLocal(): Result[ptr RlnRaw, string] = ## Creates a stateless RLN instance (no local Merkle tree). let res = ffi_rln_new() if res.ok.isNil(): @@ -135,18 +135,18 @@ proc createRLNInstanceLocal(): RlnInstanceResult = return err(msg) ok(res.ok) -proc createRLNInstance*(): RlnInstanceResult = +proc createRLNInstance*(): Result[ptr RlnRaw, string] = ## Wraps createRLNInstanceLocal with metrics timing. - var res: RlnInstanceResult + var res: Result[ptr RlnRaw, string] waku_rln_instance_creation_duration_seconds.nanosecondTime: res = createRLNInstanceLocal() return res -proc poseidon*(left, right: seq[byte]): RlnResult[array[32, byte]] = +proc poseidon*(left, right: seq[byte]): Result[array[32, byte], string] = ## Poseidon hash of exactly 2 inputs; zerokit v2 FFI only exposes the pair variant. poseidonPairLe(left, right) -proc toLeaf*(rateCommitment: RateCommitment): RlnResult[seq[byte]] = +proc toLeaf*(rateCommitment: RateCommitment): Result[seq[byte], string] = let idCommitment = rateCommitment.idCommitment var userMessageLimit: array[32, byte] try: @@ -164,7 +164,7 @@ proc toLeaf*(rateCommitment: RateCommitment): RlnResult[seq[byte]] = retLeaf[i] = leaf[i] return ok(retLeaf) -proc toLeaves*(rateCommitments: seq[RateCommitment]): RlnResult[seq[seq[byte]]] = +proc toLeaves*(rateCommitments: seq[RateCommitment]): Result[seq[seq[byte]], string] = var leaves = newSeq[seq[byte]]() for rateCommitment in rateCommitments: let leaf = toLeaf(rateCommitment).valueOr: @@ -174,7 +174,7 @@ proc toLeaves*(rateCommitments: seq[RateCommitment]): RlnResult[seq[seq[byte]]] proc generateExternalNullifier*( epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnResult[ExternalNullifier] = +): Result[ExternalNullifier, string] = ## externalNullifier = Poseidon(H(epoch), H(rlnIdentifier)); H = ffi_hash_to_field_le. let epochFr = hashToFieldLe(@epoch).valueOr: return err("Failed to hash epoch to field: " & error) @@ -194,7 +194,7 @@ proc generateExternalNullifier*( "Failed to serialize external nullifier: " & e ) -proc extractMetadata*(proof: RateLimitProof): RlnResult[ProofMetadata] = +proc extractMetadata*(proof: RateLimitProof): Result[ProofMetadata, string] = let externalNullifier = generateExternalNullifier(proof.epoch, proof.rlnIdentifier).valueOr: return err("Failed to compute external nullifier: " & error) return ok( @@ -206,7 +206,9 @@ proc extractMetadata*(proof: RateLimitProof): RlnResult[ProofMetadata] = ) ) -proc buildPathElementsVec(pathElements: seq[byte], depth: int): RlnResult[Vec_CFr] = +proc buildPathElementsVec( + pathElements: seq[byte], depth: int +): Result[Vec_CFr, string] = ## Caller MUST ffi_vec_cfr_free the returned Vec_CFr. var vec = ffi_vec_cfr_new(csize_t(depth)) for i in 0 ..< depth: @@ -222,7 +224,9 @@ proc buildPathElementsVec(pathElements: seq[byte], depth: int): RlnResult[Vec_CF ffi_cfr_free(element) ok(vec) -proc buildWitnessInput(witness: RLNWitnessInput): RlnResult[ptr FFI_RLNWitnessInput] = +proc buildWitnessInput( + witness: RLNWitnessInput +): Result[ptr FFI_RLNWitnessInput, string] = ## ffi_rln_witness_input_new copies all inputs, so the intermediate CFrs/vecs ## are freed here. Caller MUST ffi_rln_witness_input_free the returned handle. let depth = witness.identity_path_index.len @@ -287,7 +291,7 @@ proc generateRlnProofWithWitness*( witness: RLNWitnessInput, epoch: Epoch, rlnIdentifier: RlnIdentifier, -): RlnResult[RateLimitProof] = +): Result[RateLimitProof, string] = let witnessHandle = buildWitnessInput(witness).valueOr: return err("failed call to buildWitnessInput in generateRlnProofWithWitness: " & error) @@ -306,7 +310,7 @@ proc generateRlnProofWithWitness*( proc buildRlnProof( proof: RateLimitProof, externalNullifier: ExternalNullifier -): RlnResult[ptr FFI_RLNProof] = +): Result[ptr FFI_RLNProof, string] = ## ffi_rln_proof_new copies all inputs, so the intermediate CFrs are freed ## here. Caller MUST ffi_rln_proof_free the returned handle. var groth16Vec = toVecUint8(proof.proof) @@ -345,7 +349,7 @@ proc verifyRlnProof*( proof: RateLimitProof, signal: openArray[byte], validRoots: seq[MerkleNode], -): RlnResult[bool] = +): Result[bool, string] = if validRoots.len == 0: return err("verifyRlnProof requires at least one valid root (stateless mode)") diff --git a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim index 15647a5ef..d7577b88a 100644 --- a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim +++ b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim @@ -1,5 +1,6 @@ import std/options import std/tempfiles +import results import logos_delivery/waku/rln, @@ -8,12 +9,12 @@ import protocol_metrics, nonce_manager, ] -proc createRLNInstanceWrapper*(): RlnInstanceResult = +proc createRLNInstanceWrapper*(): Result[ptr RlnRaw, string] = return createRlnInstance() proc unsafeAppendRLNProof*( rlnPeer: Rln, msg: var WakuMessage, epoch: Epoch, messageId: MessageId -): RlnResult[void] = +): Result[void, string] = ## Test helper derived from the publish-path proof flow. ## - Skips nonce validation to intentionally allow generating "bad" message IDs for tests. ## - Forces a real-time on-chain Merkle root refresh via `updateRoots()` and fetches Merkle diff --git a/tests/waku_rln_relay/utils_onchain.nim b/tests/waku_rln_relay/utils_onchain.nim index 422b8c351..4955d8d64 100644 --- a/tests/waku_rln_relay/utils_onchain.nim +++ b/tests/waku_rln_relay/utils_onchain.nim @@ -37,7 +37,7 @@ proc generateCredentials*(): IdentityCredential = proc getRateCommitment*( idCredential: IdentityCredential, userMessageLimit: UserMessageLimit -): RlnResult[RawRateCommitment] = +): Result[RawRateCommitment, string] = return RateCommitment( idCommitment: idCredential.idCommitment, userMessageLimit: userMessageLimit ).toLeaf()