Every note keeps a list of up to 100 witnesses, one per block, so that a
reorg can roll it back; a full list takes roughly 40-90 KB of the wallet
file. The block scan kept duplicating and trimming that list for every
note that had one, spent notes included, although a spent note never
reads its history again once its spend can no longer be rolled back. So
every note the wallet ever spent kept costing memory, per-block scan
time and file size.
Once the block window is full (101 blocks), a spent note whose spending
transaction's block is below the window's first block now gets an empty
witness list. This runs before each block's witness update and once when
a wallet is loaded. Such a spend can only be undone by a reorg that pops
the whole window, and that already empties every witness list, so reorgs
behave exactly as before.
Behaviour changes:
- notes whose spend lies below a full reorg window lose their witness
history in memory, at load and in the next save; existing large
wallets shrink on their first save (which, like every save, copies the
previous file to wallet.dat.bak);
- the wallet format (v9) is unchanged; old builds and SilentDragonXLite
read an empty list as an unspendable note, which a spent note already
is;
- new info log at load: "Dropped witness history of N notes whose spend
is deeper than 100 blocks".
The oracle now also accepts an empty list for a spent note that was
cleared before a later partial reorg.
New witness tests compare every block's lists with a model of the old
algorithm, including reorgs across the clear point and an old file with
full lists, which loads and re-saves to the new bytes. The 1000-seed
oracle run passes (12,207 reorgs, 993 sends verified offline), and a
differential run against the unmodified backend is byte-identical apart
from exactly the cleared lists; the old build reads the new files, and
17 sends it built from them verified offline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>