replace RLN specific Result with generic Result (#4011)

This commit is contained in:
Tanya S 2026-07-06 17:49:20 +02:00 committed by GitHub
parent 21569ef884
commit 0a1700e239
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 41 additions and 43 deletions

View File

@ -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

View File

@ -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)

View File

@ -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] =

View File

@ -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.

View File

@ -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:

View File

@ -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)")

View File

@ -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

View File

@ -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()