Extract REST relay handlers' RLN attach+validate+retry into a helper

Both the static and auto POST handlers duplicated the same block: attach an
RLN proof, run wakuRelay.validateMessage, and on RlnValidatorErrorMsg
substring force-refresh the merkle path and revalidate. Lift the block into
attachRlnProofValidateWithRetry alongside a small RlnPublishError variant
so the handlers can keep dispatching 500 for proof-gen failures and 400
for validator rejections. Each handler is now ~15 lines instead of ~35 and
the retry contract lives in one place.

Pure refactor; behavior unchanged. Verified against
tests/wakunode_rest/test_rest_relay.nim (13/13) including the two stale-
RLN-proof retry tests added earlier on this branch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
stubbsta 2026-07-08 10:38:09 +02:00
parent e705e4d51d
commit a95dbdfc72
No known key found for this signature in database

View File

@ -51,6 +51,56 @@ proc validatePubSubTopics(topics: seq[PubsubTopic]): Result[void, RestApiRespons
return ok()
type
RlnPublishErrorKind = enum
ProofGenFailed ## Local proof generation failed — server-side (500).
ValidationRejected ## Validator rejected the message — client-side (400).
RlnPublishError = object
kind: RlnPublishErrorKind
desc: string
proc attachRlnProofValidateWithRetry(
rln: Rln, wakuRelay: WakuRelay, pubsubTopic: PubsubTopic, message: WakuMessage
): Future[Result[WakuMessage, RlnPublishError]] {.async.} =
## Attaches an RLN proof to `message`, validates it via `wakuRelay`, and if
## the validator rejects it as RLN-invalid (error contains RlnValidatorErrorMsg)
## force-refreshes the merkle proof path and retries once. Returns the message
## with its final proof on success. Callers invoke only when RLN is mounted.
var msg = message
msg.proof = (
await rln.generateRLNProof(msg.toRLNSignal(), float64(getTime().toUnix()))
).valueOr:
return err(
RlnPublishError(
kind: ProofGenFailed, desc: "error appending RLN proof to message: " & $error
)
)
let firstValidateResult = await wakuRelay.validateMessage(pubsubTopic, msg)
if firstValidateResult.isOk():
return ok(msg)
if not firstValidateResult.error.contains(RlnValidatorErrorMsg):
return
err(RlnPublishError(kind: ValidationRejected, desc: firstValidateResult.error))
info "relay publish rejected as RLN-invalid; refreshing merkle proof and retrying once"
msg.proof = (
await rln.generateRLNProof(
msg.toRLNSignal(), float64(getTime().toUnix()), forceMerkleProofRefresh = true
)
).valueOr:
return err(
RlnPublishError(
kind: ProofGenFailed, desc: "error appending RLN proof on retry: " & $error
)
)
(await wakuRelay.validateMessage(pubsubTopic, msg)).isOkOr:
return err(RlnPublishError(kind: ValidationRejected, desc: error))
return ok(msg)
proc installRelayApiHandlers*(
router: var RestRouter, node: WakuNode, cache: MessageCache
) =
@ -166,38 +216,18 @@ proc installRelayApiHandlers*(
var message: WakuMessage = reqWakuMessage.toWakuMessage(version = 0).valueOr:
return RestApiResponse.badRequest($error)
# if RLN is mounted, append the proof to the message
if not node.rln.isNil():
# append the proof to the message
message.proof = (
await node.rln.generateRLNProof(
message.toRLNSignal(), float64(getTime().toUnix())
message = (
await attachRlnProofValidateWithRetry(
node.rln, node.wakuRelay, pubsubTopic, message
)
).valueOr:
return RestApiResponse.internalServerError(
"Failed to publish: error appending RLN proof to message: " & $error
)
let firstValidateResult = await node.wakuRelay.validateMessage(pubsubTopic, message)
if firstValidateResult.isErr():
if node.rln.isNil() or not firstValidateResult.error.contains(
RlnValidatorErrorMsg
):
return
RestApiResponse.badRequest("Failed to publish: " & firstValidateResult.error)
# Stale RLN merkle root; force-refresh the cached path and retry validation once
info "relay publish rejected as RLN-invalid; refreshing merkle proof and retrying once"
message.proof = (
await node.rln.generateRLNProof(
message.toRLNSignal(),
float64(getTime().toUnix()),
forceMerkleProofRefresh = true,
)
).valueOr:
return RestApiResponse.internalServerError(
"Failed to publish: error appending RLN proof on retry: " & $error
)
case error.kind
of ProofGenFailed:
return RestApiResponse.internalServerError("Failed to publish: " & error.desc)
of ValidationRejected:
return RestApiResponse.badRequest("Failed to publish: " & error.desc)
else:
(await node.wakuRelay.validateMessage(pubsubTopic, message)).isOkOr:
return RestApiResponse.badRequest("Failed to publish: " & error)
@ -316,36 +346,18 @@ proc installRelayApiHandlers*(
error "publish error", err = msg
return RestApiResponse.badRequest("Failed to publish. " & msg)
# if RLN is mounted, append the proof to the message
if not node.rln.isNil():
message.proof = (
await node.rln.generateRLNProof(
message.toRLNSignal(), float64(getTime().toUnix())
message = (
await attachRlnProofValidateWithRetry(
node.rln, node.wakuRelay, pubsubTopic, message
)
).valueOr:
return RestApiResponse.internalServerError(
"Failed to publish: error appending RLN proof to message: " & error
)
let firstValidateResult = await node.wakuRelay.validateMessage(pubsubTopic, message)
if firstValidateResult.isErr():
if node.rln.isNil() or not firstValidateResult.error.contains(
RlnValidatorErrorMsg
):
return
RestApiResponse.badRequest("Failed to publish: " & firstValidateResult.error)
# Stale RLN merkle root; force-refresh the cached path and retry validation once
info "relay publish rejected as RLN-invalid; refreshing merkle proof and retrying once"
message.proof = (
await node.rln.generateRLNProof(
message.toRLNSignal(),
float64(getTime().toUnix()),
forceMerkleProofRefresh = true,
)
).valueOr:
return RestApiResponse.internalServerError(
"Failed to publish: error appending RLN proof on retry: " & error
)
case error.kind
of ProofGenFailed:
return RestApiResponse.internalServerError("Failed to publish: " & error.desc)
of ValidationRejected:
return RestApiResponse.badRequest("Failed to publish: " & error.desc)
else:
(await node.wakuRelay.validateMessage(pubsubTopic, message)).isOkOr:
return RestApiResponse.badRequest("Failed to publish: " & error)