mirror of
https://github.com/logos-messaging/logos-delivery.git
synced 2026-07-22 04:29:42 +00:00
Bound only the proof refresh on lightpush RLN retry, not the republish
RlnRefreshRetryTimeout previously wrapped the whole retry leg: merkle path refetch + proof regeneration + the second publish. Sharing one clock meant a slow (but successful) refetch could leave no budget for the publish, cancelling it mid-flight — burning the freshly drawn nonce and, worse, reporting the original RLN rejection while the retried request may already have reached the service node and been relayed. Move the withTimeout to cover only attachRLNProof (the on-chain eth_call + proof regeneration — the one step with an unbounded external dependency). The retried publish runs unbounded after it, matching the first attempt's semantics, so slow transports (e.g. an optional mix route) cannot be cancelled by the refresh budget and the caller always receives the publish's actual result. On refresh timeout the retry publish never starts and the original rejection is returned, so a "failed" answer now guarantees no second request is in flight. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6bfcd1e43e
commit
29dfaa12b6
@ -114,25 +114,23 @@ proc runRlnRefreshRetry(
|
||||
peer: RemotePeerInfo,
|
||||
fallback: legacy_lightpush_protocol.WakuLightPushResult[string],
|
||||
): Future[legacy_lightpush_protocol.WakuLightPushResult[string]] {.async, gcsafe.} =
|
||||
## Force-refreshes the RLN merkle proof path and retries the publish once,
|
||||
## bounded by RlnRefreshRetryTimeout. Returns `fallback` on timeout so a
|
||||
## hanging RPC/libp2p round-trip cannot stall the caller indefinitely.
|
||||
## Force-refreshes the RLN merkle proof path and retries the publish once.
|
||||
## Only the refresh (on-chain refetch + proof regeneration) is bounded by
|
||||
## RlnRefreshRetryTimeout — a hanging RPC cannot stall the caller
|
||||
## indefinitely and `fallback` (the original rejection) is returned instead.
|
||||
## The retried publish itself runs unbounded, matching the first attempt.
|
||||
info "legacy lightpush send rejected as RLN-invalid; " &
|
||||
"refreshing merkle proof and retrying once"
|
||||
rln.get().groupManager.invalidateMerkleProofCache()
|
||||
|
||||
proc runRetry(): Future[legacy_lightpush_protocol.WakuLightPushResult[string]] {.
|
||||
async, gcsafe
|
||||
.} =
|
||||
let retryMsg = (await attachRLNProof(rln.get(), msgWithProof)).valueOr:
|
||||
return err("failed call attachRLNProof from lightpush retry: " & error)
|
||||
return await internalLegacyLightpushPublish(node, pubsubForPublish, retryMsg, peer)
|
||||
|
||||
let retryFut = runRetry()
|
||||
if not (await retryFut.withTimeout(RlnRefreshRetryTimeout)):
|
||||
warn "legacy lightpush RLN-refresh retry timed out; returning original error"
|
||||
let refreshFut = attachRLNProof(rln.get(), msgWithProof)
|
||||
if not (await refreshFut.withTimeout(RlnRefreshRetryTimeout)):
|
||||
warn "legacy lightpush RLN proof refresh timed out; returning original error"
|
||||
return fallback
|
||||
return retryFut.read()
|
||||
let retryMsg = refreshFut.read().valueOr:
|
||||
return err("failed call attachRLNProof from lightpush retry: " & error)
|
||||
|
||||
return await internalLegacyLightpushPublish(node, pubsubForPublish, retryMsg, peer)
|
||||
|
||||
proc legacyLightpushPublish*(
|
||||
node: WakuNode,
|
||||
@ -367,15 +365,16 @@ proc lightpushPublish*(
|
||||
statusCode = $firstResult.error.code
|
||||
rln.get().groupManager.invalidateMerkleProofCache()
|
||||
|
||||
proc runRetry(): Future[lightpush_protocol.WakuLightPushResult] {.async, gcsafe.} =
|
||||
let retryMsg = (await attachRLNProof(rln.get(), msgWithProof)).valueOr:
|
||||
return lighpushErrorResult(LightPushErrorCode.OUT_OF_RLN_PROOF, error)
|
||||
return
|
||||
await lightpushPublishHandler(node, pubsubForPublish, retryMsg, toPeer, mixify)
|
||||
|
||||
let retryFut = runRetry()
|
||||
if not (await retryFut.withTimeout(RlnRefreshRetryTimeout)):
|
||||
warn "lightpush RLN-refresh retry timed out; returning original error",
|
||||
# Only the refresh (on-chain refetch + proof regeneration) is bounded — a
|
||||
# hanging RPC cannot stall the caller indefinitely and the original
|
||||
# rejection is returned instead. The retried publish itself runs unbounded,
|
||||
# matching the first attempt.
|
||||
let refreshFut = attachRLNProof(rln.get(), msgWithProof)
|
||||
if not (await refreshFut.withTimeout(RlnRefreshRetryTimeout)):
|
||||
warn "lightpush RLN proof refresh timed out; returning original error",
|
||||
statusCode = $firstResult.error.code
|
||||
return firstResult
|
||||
return retryFut.read()
|
||||
let retryMsg = refreshFut.read().valueOr:
|
||||
return lighpushErrorResult(LightPushErrorCode.OUT_OF_RLN_PROOF, error)
|
||||
|
||||
return await lightpushPublishHandler(node, pubsubForPublish, retryMsg, toPeer, mixify)
|
||||
|
||||
@ -20,10 +20,11 @@ const RlnCredentialsFilename* = "rlnCredentials.txt"
|
||||
# RLN Validator message rejection Error string, is used to trigger proof refresh and publish retry in the lightpush client
|
||||
const RlnValidatorErrorMsg* = "RLN validation failed"
|
||||
|
||||
# Upper bound on the reactive refresh + retry after a stale-RLN rejection.
|
||||
# The retry synchronously fetches a fresh merkle path from the RLN contract
|
||||
# (eth_call round-trip) and republishes; without a bound a hanging RPC endpoint
|
||||
# could stall the caller indefinitely. Matches the REST `futTimeout`.
|
||||
# Upper bound on the reactive merkle proof refresh after a stale-RLN
|
||||
# rejection: fetching a fresh merkle path from the RLN contract (eth_call
|
||||
# round-trip) plus proof regeneration. Without a bound a hanging RPC endpoint
|
||||
# could stall the caller indefinitely. The retried publish is not covered —
|
||||
# it runs unbounded, matching the first attempt.
|
||||
const RlnRefreshRetryTimeout* = 5.seconds
|
||||
|
||||
# inputs of the membership contract constructor
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user