fix(journal): retry the gas-price read in prepare_payment - #18
Conversation
The journal hardening routed every read in `prepare_payment` through the
retrying `rpc(...)` wrapper except the EIP-1559 fee estimate, which still
went through the single-shot `get_eip1559_fees`. One 429 or `-32000 context
deadline exceeded` from a public RPC therefore failed the upload with
"Could not get current gas price", a regression against the legacy
`send_transaction_with_retries` path.
Split `get_eip1559_fees` into `needs_fee_estimate` and a pure
`apply_fee_policy`, keeping the per-mode logic unchanged. The journal now
reads the estimate through `rpc("gas price", ...)` and applies the policy
afterwards, so only the RPC read is retried and a `GasPriceAboveLimit`
still returns at once. `get_eip1559_fees` stays a single-estimate wrapper
for the legacy path, which already retries at the outer level.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
dirvine
left a comment
There was a problem hiding this comment.
Review: no material blockers
Reviewed WithAutonomi/evmlib at 68d3bcbdf0319e1d853606c9d2f99dccf807a1c6 against main; rechecked that this remains the open PR head and all GitHub checks are successful.
The change puts the fee-estimate read inside the existing journal RPC retry wrapper, while keeping apply_fee_policy outside it. GasPriceAboveLimit therefore returns immediately after a successful estimate; all four fee modes preserve the previous behaviour. The legacy send path still performs one estimate per outer send attempt, so this does not introduce nested retries. Signing, nonce selection, broadcast and receipt handling are unchanged.
Verified locally
cargo test— passed, including the integration suites.cargo test --lib --test cryptography --no-default-features --features rpc,external-signer— 22 library and 4 cryptography tests passed.cargo clippy --all-targets --all-features -- -D warnings— passed.cargo fmt --checkandgit diff --check— passed.- Both WASM library checks, without default features and with
rpc,external-signer— passed.
Independent review and caveats
GLM-5.2 and Codex independently found no introduced blockers, consistent with the fee-policy review and my code/test verification. One reviewer flagged unfiltered RPC retries and their latency as blocking. I do not consider that a blocker here: this deliberately reuses the existing read-only preparation backoff, not the shorter observation-window backoff, and the policy rejection remains outside retries. The operational trade-off is real: a persistently failing fee estimate incurs 56 seconds of retry sleeps, plus RPC time; permanent RPC errors are also retried. A retry-exhaustion/error-prefix regression test would be a useful non-blocking addition.
The DS4 review attempt timed out and is not counted as an opinion. This is not a claim of unanimous six-seat approval.
No source changes made and no merge performed. This review does not replace any required human release/acceptance sign-off.
dirvine
left a comment
There was a problem hiding this comment.
Approved at Chris’s explicit request following the completed review: #18 (review) . Rechecked that the head is unchanged and all CI checks pass. No material blockers; the documented retry-latency caveat remains non-blocking. No merge performed.
Summary
The journal payment path (
prepare_payment) routed every RPC read through the retryingrpc(...)wrapper except the EIP-1559 fee estimate, which still called the single-shotretry::get_eip1559_fees. One 429 or-32000 context deadline exceededfrom a public RPC therefore failed the upload outright withCould not get current gas price: …(seen on DEV-03 runs 591 and 593). The legacysend_transaction_with_retriespath, used by the released client, retries this three times, so ant-client main had regressed.retry.rs:get_eip1559_feesis split intoneeds_fee_estimate(false only forUnlimited) and a pureapply_fee_policyholding the unchanged per-mode logic.get_eip1559_feesremains a thin single-estimate wrapper, so the legacy path behaves exactly as before and does not nest retries.journal.rs: newjournal_fee_readreads the estimate viarpc("gas price", None, …)and then applies the policy. Only the RPC read is retried; a definitiveGasPriceAboveLimitreturns at once. An exhausted retry still surfaces asCould not get current gas price: ….Linear issue
Closes V2-1288 — Linear issue
Risk tier
Proposed for human review: adds retries to one read-only RPC call on the client payment path; the fee policy, the signed transaction and the payment semantics are unchanged.
Compatibility
pub(crate))Semver impact
Test evidence
apply_fee_policy(Auto passthrough; LimitedAuto under and over the limit; Custom below and above the estimate; Unlimited →None) andneeds_fee_estimate.journal_fee_readtests on alloy's mocked transport (Asserter+connect_mocked_client): a transient-32000 context deadline exceededfollowed by a valideth_feeHistorysucceeds; a fee over aLimitedAutolimit fails withGasPriceAboveLimitwithout backoff.cargo test --lib: 30 passed.cargo test --lib --test cryptography --no-default-features --features rpc,external-signer: 22 + 4 passed.cargo clippy --all-targets -- -D warnings,cargo clippy --all-targets --all-features -- -D warnings,cargo fmt --check: passed.cargo check --lib --no-default-features --target wasm32-unknown-unknownand… --features rpc,external-signer --target wasm32-unknown-unknown: passed.cargo test --release).New dependency
none
ADR
n/a
Mitigation / rollback
Revert this commit and re-pin ant-protocol / ant-client / ant-node to the previous evmlib rev; the change only adds retries to a read.
🤖 Generated with Claude Code