perf(validator): defer source-balance lookup until after cheap reserve rejections#466
Merged
Merged
Conversation
…e rejections The BTC source-balance check is an uncached external Esplora/gomaestro HTTP call, and it ran before the commitment/slippage/already-reserved/ cooldown rejections — so every reserve request that was going to be rejected anyway still forced one external API call. Under a reserve spam burst this is a per-request amplification vector (one upstream call each, even for doomed requests) that burns provider quota and backs up the axon threadpool. Move the balance lookup to the last gate before vote_reserve, after all the cheap in-process/substrate rejections. Spam destined for those now rejects without ever touching Esplora. The TAO (substrate) balance read still serialises under axon_lock and the BTC read stays lock-free, both unchanged — just relocated. Tradeoff: the balance call now sits between the already-reserved check and the vote, so a concurrent request could reserve the miner first. That race costs at most one doomed vote_reserve, which the contract rejects atomically, so the early-reject guarantee is unchanged.
entrius
approved these changes
Jun 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Move the source-balance lookup in
handle_swap_reserveto the last gate beforevote_reserve, after the cheap commitment / slippage / already-reserved / cooldown rejections.Why
For a BTC source,
provider.get_balanceis an uncached external Esplora/gomaestro HTTP call, and it previously ran before all the in-process/substrate rejections. So every reserve request that was going to be rejected anyway (wrong slippage band, miner already reserved, address on cooldown) still forced one upstream API call first.Under a reserve spam burst — the kind we see hammering a single UID with dozens of
SwapReserverequests/sec — this is a per-request amplification vector: N spam requests = N Esplora calls, even for guaranteed-reject requests. That burns provider quota (and on quota exhaustion the failover eventually returns balance 0, which would false-reject real users) and backs up the axon threadpool (the source of theNonce is too oldaging we observed).Reordering so the balance call runs only after a request clears every cheap rejection means spam destined for those paths never touches Esplora.
Preserved
axon_lock; BTC read stays lock-free — both unchanged, just relocated (keeps the fix(validator): serialize TAO source-balance check under axon_lock #456 invariant: a slow Esplora call can't stall the forward loop).Tradeoff
The balance call now sits between the already-reserved check and the vote, so a concurrent request could reserve the miner during the lookup. That race costs at most one doomed
vote_reserve, which the contract rejects atomically — the early-reject guarantee is unchanged. (No pre-vote re-check added: it would conflict with the pin'sget_miner_reserved_untilread for marginal benefit.)Test
test_tao_source_balance_check_holds_axon_lockupdated to build a self-consistent tao→btc quote (derived from the handler's own functions) so it reaches the now-later balance check; the lock assertion is unchanged.