Repository navigation
Conversation
- Query::mempool() recovers from a poisoned lock via unwrap_or_else(|e| e.into_inner()) rather than unwrapping. Under panic=abort the process terminates before a poison marker is observable, but serving stale reads is strictly better than aborting the front-end for anyone who turns that off later. - Remove with_mempool() and with_read_snapshot() along with the test that only covered the helper. The five REST call sites now bind the guard directly via let mempool = query.mempool(); with an explicit drop(mempool); at the end of the atomic section. The atomicity guarantee comes from scope, and the codebase keeps a single mempool-access idiom. - Delete Query::lookup_txos. With prepare_txs taking a lookup closure instead of a HashMap, the wrapper had no non-test callers. - HttpError::lookup emits a warn log before returning so operators can see what is driving REST 404 and 503 responses. - Note the pre-existing race between chain.history() and the mempool snapshot in the address and asset history handlers. - Adjust tests/mempool_eviction_race.rs to call query.mempool().lookup_txos(...) directly instead of the deleted Query::lookup_txos wrapper. Two separate mempool snapshots is exactly the pre-fix code path the test needs to exercise. Also add query, mempool, and daemon accessors on TestRunner in tests/common.rs.
Keep one mempool read guard across transaction selection and prevout resolution in the eviction regression test. Assert that a writer cannot acquire the lock during this atomic section and can acquire it after the snapshot is released. Retain coverage that an evicted prevout returns MissingTxo instead of panicking.
Capture mempool transactions and prevouts before chain reads, then release the lock before response preparation. Deduplicate confirmed history entries and preserve transaction confirmation status. Exercise REST preparation in regression tests and fix Liquid test compatibility. Validation: 111 Bitcoin tests and 112 Liquid tests passed.
Check chain storage before capturing mempool transactions. Return 400 for invalid block page indices, 503 for storage and confirmed-prevout failures, and 404 for vanished unconfirmed mempool prevouts.
…he final UTXO set
Fetch missing transactions through bounded proxied RPCs outside the mempool lock. Recheck presence before insertion, log unavailable txids, and cover lock behavior with a regression test.
- stop killing the process on transient mempool sync errors - back off between retry_reconnect attempts - replace the overloaded bool mempool sync result with an enum - stop pausing the main sync loop on mempool add failures - fix off-by-one in the mempool sync retry cap - add mempool_sync_failed_to_index metric - cover reconnect backoff and interruption in tests
…hot merge The prepare_history path from fix/rest-mempool-snapshot-race wraps chain lookups in HttpError::lookup (503); the scan-limit branch expects TooBigHistory to reach clients as 400. Classify it explicitly.
…nse limits Bound utxo checkpoints with --utxos-checkpoint-limit, retry TooBigHistory in precache, stop cursor scans past the cursor height, and reject a zero history scan limit. Keep the V record readable by older binaries and store index flags under VF. Index broadcasts from the submitted hex, isolate unresolvable mempool txs and stop after repeated sync failures. Reserve rebuild memory in the checkpoint Merkle cache, require a write timeout or max age with a finite response budget, do not abort completed writes at the deadline, and fix the header range overflow and the Liquid MissingTxo txid parsing.
philippem
approved these changes
Oct 5, 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.
Summary
This branch hardens electrs against heavy addresses, slow or non-reading Electrum clients, and mempool churn. Each fix bounds a cost that used to grow without limit:
Changes
Scan limits (history / UTXO / stats)
--history-scan-limit(default100000, must be ≥ 1). It limits how many history rows one utxo/stats lookup reads. Once the limit is hit, the scan finishes the current height and then stops withTooBigHistory("too popular"). It never stops in the middle of a height, which used to make it stall forever.--utxos-checkpoint-limit(default10 × --utxos-limit, must be ≥--utxos-limit). It limits the size of a partially scanned UTXO set and its saved checkpoint, as well as how many unconfirmed history entries are scanned per address.--utxos-limitis now checked against the final UTXO set, not against intermediate scan state.stats_deltadouble-counting on resume.TooBigHistory.TooBigHistoryas HTTP 400.REST mempool consistency
Each handler takes a single mempool read snapshot for transaction selection and prevout resolution. It captures that snapshot before chain reads and releases it before building the response, so the lock is not held during serialization.
Confirmed history entries are deduplicated, and confirmation status is preserved when a transaction confirms during a request.
Error classification:
Lookup failures are logged at
warn.Query::mempool()recovers from a poisoned lock on both the read and write paths.Electrum response limits
--electrum-rpc-max-response-num-byteslimits the size of one reply line. Defaults: 8 MiB on Bitcoin, 32 MiB on Liquid. A reply that overflows gets a correlated error (code 1), and the remaining batch elements are not executed.--electrum-rpc-global-response-budget-byteslimits reply buffer memory across all connections combined. Defaults: 64 MiB on Bitcoin, 256 MiB on Liquid.--electrum-rpc-write-timeout(default30s) is a deadline for transmitting a complete reply. It is not extended by partial progress. When it expires, the connection is closed and its buffers are released.--electrum-rpc-conn-max-age.electrum_per_line_response_overflows,electrum_global_response_budget_rejections,electrum_client_write_timeouts_total.Checkpoint Merkle proofs
cp_height. A cached height is served without a rebuild.--electrum-checkpoint-merkle-cache-mb(default256). Cached trees and in-flight rebuilds share this one budget. Cached trees use at most half of it, and rebuild memory is reserved up front.--electrum-checkpoint-proof-concurrency-limitnow applies only to rebuilds. Its default is half the CPU cores (minimum 1) instead of2.Mempool sync and daemon
FailedToIndex.mempool_sync_failed_to_index.retry_reconnectbacks off between attempts.DB compatibility
index_unspendablesandaddress_searchare now included in the compatibility check. They are stored under a newVFkey, and theVrecord keeps its old format so older binaries can still read it.VFrecord the current flags and log a warning.--address-searchis documented as best-effort with respect to reorgs.Liquid
MissingTxotxid parsing.Behavior changes for operators
New limits are enabled by default:
To restore the old unbounded behavior, set the response caps and the timeout to
0.--history-scan-limitcan only be raised.The default for
--electrum-checkpoint-proof-concurrency-limitchanges from2to half the CPU cores.REST error responses: some lookups that used to return 404 or panic now return 400 or 503.
A DB built with different
--index-unspendables/--address-searchvalues now refuses to start.