Make on-chain retry pacing injectable; fast retries in tests

register() built RetryStrategy.new() inline (4s delay, 15 tries) at each
retry site. The receipt poll's first attempt races the node's tx
indexing and routinely lost, so every registration stalled a full 4s
even on instantly-mining Anvil. Store the strategy on
OnchainGroupManager instead; init() replaces a zero value with the
previous defaults, so production behavior is unchanged. Test managers
use 100ms pacing, cutting the onchain group manager suite from ~97s to
~7s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
stubbsta 2026-07-12 18:16:10 +02:00
parent 6ff7b14a24
commit 8ee2312dd5
No known key found for this signature in database
2 changed files with 22 additions and 4 deletions

View File

@ -48,6 +48,9 @@ type
proofPathRefreshInFlightFut*: Future[seq[byte]]
lastRootsRefreshMoment*: Moment
rootsRefreshInFlightFut*: Future[void]
# Pacing for retried on-chain transaction calls; a zero value is replaced
# with the RetryStrategy defaults during init.
retryStrategy*: RetryStrategy
# The below code is not working with the latest web3 version due to chainId being null (specifically on linea-sepolia)
# TODO: find better solution than this custom sendEthCallWithoutParams call
@ -358,7 +361,7 @@ method register*(
let gasPrice = (
await retryWrapper(
RetryStrategy.new(),
g.retryStrategy,
"Failed to get gas price",
proc(): Future[int] {.async.} =
let fetchedGasPrice = uint64(await ethRpc.provider.eth_gasPrice())
@ -383,7 +386,7 @@ method register*(
idCommitmentsToErase = idCommitmentsToErase
let txHash = (
await retryWrapper(
RetryStrategy.new(),
g.retryStrategy,
"Failed to register the member",
proc(): Future[TxHash] {.async.} =
return await wakuRlnContract
@ -396,7 +399,7 @@ method register*(
# wait for the transaction to be mined and get the receipt
let tsReceipt = (
await retryWrapper(
RetryStrategy.new(),
g.retryStrategy,
"Failed to get the transaction receipt",
proc(): Future[ReceiptObject] {.async.} =
let r = await ethRpc.provider.eth_getTransactionReceipt(txHash)
@ -610,6 +613,9 @@ proc establishConnection(
return ok(ethRpc)
method init*(g: OnchainGroupManager): Future[GroupManagerResult[void]] {.async.} =
if g.retryStrategy.retryCount == 0:
g.retryStrategy = RetryStrategy.new()
# check if the Ethereum client is reachable
let ethRpc: Web3 = (await establishConnection(g)).valueOr:
return err("failed to connect to Ethereum clients: " & $error)

View File

@ -20,7 +20,13 @@ import
results
import
logos_delivery/waku/[rln, rln/protocol_types, rln/constants, rln/bindings],
logos_delivery/waku/[
rln,
rln/protocol_types,
rln/constants,
rln/bindings,
rln/group_manager/on_chain/retry_wrapper,
],
../testlib/common
const CHAIN_ID* = 1234'u256
@ -40,6 +46,10 @@ const WAKU_RLNV2_PROXY_ADDRESS* = "0x5fc8d32690cc91d4c39d9d3abcbd16989f875707"
const FUNDED_TEST_PRIVATE_KEY* =
"1111111111111111111111111111111111111111111111111111111111111111"
# Anvil mines instantly, so the multi-second production retry delay only adds
# dead time when polling for transaction receipts.
const FastRetryStrategy = RetryStrategy(retryDelay: 100.millis, retryCount: 15)
proc generateCredentials*(): IdentityCredential =
let credRes = membershipKeyGen()
return credRes.get()
@ -672,6 +682,7 @@ proc buildOnchainGroupManager*(
chainId: CHAIN_ID,
ethPrivateKey: some(privateKey),
rlnInstance: rlnInstanceRes.get(),
retryStrategy: FastRetryStrategy,
onFatalErrorAction: proc(errStr: string) =
raiseAssert errStr
,
@ -764,6 +775,7 @@ proc setupOnchainGroupManager*(
chainId: CHAIN_ID,
ethPrivateKey: some($privateKey),
rlnInstance: rlnInstance,
retryStrategy: FastRetryStrategy,
onFatalErrorAction: proc(errStr: string) =
raiseAssert errStr
,