A watched coin can be spent by a transaction that pays nothing back to the wallet, so the transaction touches the wallet only through its input. We found this via CoinJoin collateral burns (sole output OP_RETURN, zero value), but it applies to any such transaction — a send-everything with no change, or an exact-amount payment that leaves no change. Burns made it easy to miss for a long time because the amounts are dust; a no-change send of a large coin fails the same way, silently.
On a restored testnet wallet, 4 of 5 burns were never found. The transactions are absent, the coins stay marked unspent, and the balance is overstated by 0.00048773 DASH with no error surfaced. Verified against a dashj wallet on the same seed, which holds all five.
| burn tx |
height |
spends coin |
result |
55f3bd9f7d58f58bde04d38251d9bc4c824e5a0a5221ff0c6f38f65410173935 |
1153140 |
d5dfbe889f8ec3c9a79aaacde1a4780119b08a049ddd06162f5d4f2deaf3cb03:0 |
found |
f2fe8ad5aad30d6d0859a55db01b2c9240ee028eaff792516051f32c795377b7 |
1153176 |
6c3add0e8f9fee0bf36864e0839de335871c86e249b6f197d8aa89842270421e:0 |
missed |
e821cec4c233d3e7a34f688727d04468ed136ebf1b8553f1ab046177ae813afb |
1190152 |
ed7217e95dc1aab6fecd9da1f164249959f3f17540512770f37d87bfed46f1a7:0 |
missed |
560b22d38c08b8a7be8d3732a8e05b822df7d47d5a1fda9e52f23d7cdc7c69e1 |
1190172 |
6b6b3c5cf0d3c73f498f391add2c44e219e8585da056cef7e78ffe53eca1846d:0 |
missed |
da763356f267c57925400c44973e716d389c624f6c29fa69ed1d45e095cb8426 |
1194323 |
b29fb8c29ce5507be5bd9f4be5d58a52e50d80cb35bd3e9f0960f099d1486eef:0 |
missed |
Not a gap-limit problem. Checking every unspent coin against dashj's spent set returns exactly these four; the other 1,062 agree. The wallet's CoinJoin chain runs past index 8,800 and ordinary spends there are found reliably. The only shared property of the misses is the transaction shape.
Likely mechanism. An ordinary spend can be recorded via an owned output or via a connected input; these transactions offer only the input. If the coin isn't in the UTXO set when that block is processed, the input can't be connected, the transaction is judged irrelevant, and nothing revisits it — a later filter match re-tests the height but doesn't re-attribute a spend an earlier pass discarded. So either the committed-range sweep never re-tests those heights for input-side matches, or it does and attribution still drops them. This makes restore and rescan the exposed paths: during live operation the wallet authored the send and already holds the coin.
Suggested regression test: fund a watched coin at H, spend it at H+n with a transaction that has no wallet-owned output, and process blocks so the coin isn't in the UTXO set when H+n is first scanned. Assert the coin ends spent.
Testnet; wallet synced to 1,542,559. Store snapshot and full audit available on request.
A watched coin can be spent by a transaction that pays nothing back to the wallet, so the transaction touches the wallet only through its input. We found this via CoinJoin collateral burns (sole output
OP_RETURN, zero value), but it applies to any such transaction — a send-everything with no change, or an exact-amount payment that leaves no change. Burns made it easy to miss for a long time because the amounts are dust; a no-change send of a large coin fails the same way, silently.On a restored testnet wallet, 4 of 5 burns were never found. The transactions are absent, the coins stay marked unspent, and the balance is overstated by 0.00048773 DASH with no error surfaced. Verified against a dashj wallet on the same seed, which holds all five.
55f3bd9f7d58f58bde04d38251d9bc4c824e5a0a5221ff0c6f38f65410173935d5dfbe889f8ec3c9a79aaacde1a4780119b08a049ddd06162f5d4f2deaf3cb03:0f2fe8ad5aad30d6d0859a55db01b2c9240ee028eaff792516051f32c795377b76c3add0e8f9fee0bf36864e0839de335871c86e249b6f197d8aa89842270421e:0e821cec4c233d3e7a34f688727d04468ed136ebf1b8553f1ab046177ae813afbed7217e95dc1aab6fecd9da1f164249959f3f17540512770f37d87bfed46f1a7:0560b22d38c08b8a7be8d3732a8e05b822df7d47d5a1fda9e52f23d7cdc7c69e16b6b3c5cf0d3c73f498f391add2c44e219e8585da056cef7e78ffe53eca1846d:0da763356f267c57925400c44973e716d389c624f6c29fa69ed1d45e095cb8426b29fb8c29ce5507be5bd9f4be5d58a52e50d80cb35bd3e9f0960f099d1486eef:0Not a gap-limit problem. Checking every unspent coin against dashj's spent set returns exactly these four; the other 1,062 agree. The wallet's CoinJoin chain runs past index 8,800 and ordinary spends there are found reliably. The only shared property of the misses is the transaction shape.
Likely mechanism. An ordinary spend can be recorded via an owned output or via a connected input; these transactions offer only the input. If the coin isn't in the UTXO set when that block is processed, the input can't be connected, the transaction is judged irrelevant, and nothing revisits it — a later filter match re-tests the height but doesn't re-attribute a spend an earlier pass discarded. So either the committed-range sweep never re-tests those heights for input-side matches, or it does and attribution still drops them. This makes restore and rescan the exposed paths: during live operation the wallet authored the send and already holds the coin.
Suggested regression test: fund a watched coin at H, spend it at H+n with a transaction that has no wallet-owned output, and process blocks so the coin isn't in the UTXO set when H+n is first scanned. Assert the coin ends spent.
Testnet; wallet synced to 1,542,559. Store snapshot and full audit available on request.