Skip to content

fix(bitcoincore): correct address derivation, paginate listtransactions, collision-free wallet names - #29

Merged
LORDBABUINO merged 7 commits into
mainfrom
fix/bitcoincore-gateway-hardening
Sep 2, 2026
Merged

fix(bitcoincore): correct address derivation, paginate listtransactions, collision-free wallet names#29
LORDBABUINO merged 7 commits into
mainfrom
fix/bitcoincore-gateway-hardening

Conversation

@LORDBABUINO

Copy link
Copy Markdown
Collaborator

Three correctness fixes in the Bitcoin Core gateway, found by a code quality audit and hardened after an independent review pass.

  1. deriveaddresses was called with a range parameter for every descriptor, which Core rejects for un-ranged ones like addr(...). Worse, both call sites swallowed the error, so derived_addresses came back empty and is_ours() silently missed the wallet's own addresses. The range is now only sent for ranged descriptors and errors propagate. Same treatment for list_wallet_descriptors in scan_wallet, which had the same swallowing pattern.

  2. listtransactions used a fixed 10000 cap, silently truncating busy wallets. It now paginates with count and skip until a short page, preserving the oldest-first ordering across pages (the integration test asserts non-decreasing block heights and was mutation-checked: removing the reordering makes it fail). If the chain tip moves mid-pagination the pages are refetched once.

  3. Temporary scan wallet names were derived from the timestamp alone, so concurrent scans in the same millisecond collided. Names now include the process id and an atomic counter.

Tests: new integration module (engine/tests/integration/gateway.rs) covering the addr() derivation and pagination end to end on regtest, plus a unit test for name uniqueness. Full workspace suite passes 41/41 with clippy -D warnings clean. All work was test-first; each fix has recorded red-then-green evidence.

… swallowing errors

deriveaddresses was always called with a range argument, which Core
rejects for un-ranged descriptors like addr(...). Both call sites
silenced the failure with if-let-Ok, leaving derived_addresses empty
and causing false negatives. Pass the range only for ranged
descriptors and propagate derivation errors.
…10000

A single call with a fixed count of 10000 silently dropped older
transactions in large wallets. Fetch pages of tx_page_size (default
1000, injectable via with_tx_page_size) with count+skip until a short
page, reassembling pages oldest-first to keep the previous ordering.
Two concurrent scans starting in the same millisecond produced the
same _stealth_scan_{millis} wallet name, so one createwallet failed.
Extract name generation into scan_wallet_name(timestamp) and append a
global atomic counter: _stealth_scan_{millis}_{n}.
…llet

The if-let-Ok silently produced empty derived_addresses on failure,
the same false-negative class as the derive_addresses fix. Propagate
unconditionally: legacy (non-descriptor) wallets are not a supported
target since Core 30 removed legacy wallet loading entirely.
Confirm each funding tx in its own block so blockheights are distinct,
then assert non-decreasing heights in the final list. Verified the
assertion catches removal of the page reverse (mutation check).
count+skip pagination is not atomic: a block arriving between pages
shifts the skip windows and can drop entries after a reorg. Snapshot
getblockcount around the page loop and refetch once if the tip moved.
No deterministic test seam exists for a mid-loop block; the ordering
and completeness assertions in the pagination test cover reassembly.
The atomic counter is per-process, so CLI and API scans starting in
the same millisecond could still collide on the shared node. Name
format is now _stealth_scan_{millis}_{pid}_{n}.
@LORDBABUINO
LORDBABUINO merged commit d092257 into main Sep 2, 2026
3 checks passed
@LORDBABUINO
LORDBABUINO deleted the fix/bitcoincore-gateway-hardening branch September 2, 2026 20:04
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.

1 participant