Skip to content

perf(validator): defer source-balance lookup until after cheap reserve rejections#466

Merged
entrius merged 1 commit into
testfrom
fix/reserve-check-order-before-balance-lookup
Jun 9, 2026
Merged

perf(validator): defer source-balance lookup until after cheap reserve rejections#466
entrius merged 1 commit into
testfrom
fix/reserve-check-order-before-balance-lookup

Conversation

@anderdc

@anderdc anderdc commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

What

Move the source-balance lookup in handle_swap_reserve to the last gate before vote_reserve, after the cheap commitment / slippage / already-reserved / cooldown rejections.

Why

For a BTC source, provider.get_balance is 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 SwapReserve requests/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 the Nonce is too old aging 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

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's get_miner_reserved_until read for marginal benefit.)

Test

  • Full suite green (691 passed).
  • test_tao_source_balance_check_holds_axon_lock updated 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.

…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 entrius merged commit 1bddd3d into test Jun 9, 2026
3 checks passed
@entrius entrius deleted the fix/reserve-check-order-before-balance-lookup branch June 9, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants