diff options
| author | Bitflash <btf01@proton.me> | 2026-08-01 14:48:27 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-01 14:48:27 -0300 |
| commit | b65c19e559e2fec511f40a4d5481bcf6a35c311c (patch) | |
| tree | e54c0c58d44c46befae58924b1df913816b569e9 /src/main.cpp | |
| parent | ae81fa37123730982c4e054d7433c9d0c944a373 (diff) | |
wallet: walk the chain for coins a key owns but the wallet never saw (#88)
A transaction only entered the wallet when its block arrived over the network,
so a key added afterwards was invisible: coins on the chain, private key in
wallet.dat, balance zero. /importwallet has always ended that way -- it writes
the keys and the money never shows up, which looks exactly like a failed import.
ScanForWalletTransactions walks the main chain and hands every transaction that
IsMine() to AddToWalletIfMine, the same two primitives the live path uses. Wired
into /importwallet, and offered as /rescan.
It is also the half that any recovery-phrase feature has to stand on: deriving
keys from twelve words is easy, and without this a restore restores nothing.
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp index b027cef..68ae44c 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -305,6 +305,55 @@ bool AddToWalletIfMine(const CTransaction& tx, const CBlock* pblock) | |||
| 305 | return true; | 305 | return true; |
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | int ScanForWalletTransactions(CBlockIndex* pindexStart) | ||
| 309 | { | ||
| 310 | // Walk the chain looking for transactions that belong to keys this wallet | ||
| 311 | // holds now, whatever it held when those blocks arrived. | ||
| 312 | // | ||
| 313 | // Nothing in this tree could do that. A key added after the fact -- from | ||
| 314 | // /importwallet, or from a backup restored onto a node that had since | ||
| 315 | // moved on -- was simply invisible: the coins were on the chain, the | ||
| 316 | // private key was in wallet.dat, and the balance did not show them, | ||
| 317 | // because a transaction only ever entered the wallet at the moment its | ||
| 318 | // block arrived over the network. | ||
| 319 | // | ||
| 320 | // Bitcoin grew the same function for the same reason. It is also what any | ||
| 321 | // recovery-phrase feature has to stand on: deriving the keys is the easy | ||
| 322 | // half, and without this the other half does not exist. | ||
| 323 | int nFound = 0; | ||
| 324 | int nScanned = 0; | ||
| 325 | CBlockIndex* pindex = pindexStart; | ||
| 326 | CRITICAL_BLOCK(cs_main) | ||
| 327 | { | ||
| 328 | while (pindex) | ||
| 329 | { | ||
| 330 | CBlock block; | ||
| 331 | if (block.ReadFromDisk(pindex, true)) | ||
| 332 | { | ||
| 333 | foreach(CTransaction& tx, block.vtx) | ||
| 334 | { | ||
| 335 | // Ask before handing it over: AddToWalletIfMine returns | ||
| 336 | // true for a transaction that is none of our business, so | ||
| 337 | // its return value cannot be counted. | ||
| 338 | if (tx.IsMine()) | ||
| 339 | { | ||
| 340 | if (AddToWalletIfMine(tx, &block)) | ||
| 341 | nFound++; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | } | ||
| 345 | nScanned++; | ||
| 346 | if ((nScanned % 500) == 0) | ||
| 347 | printf("ScanForWalletTransactions() : %d blocks scanned, %d transaction(s) found\n", | ||
| 348 | nScanned, nFound); | ||
| 349 | pindex = pindex->pnext; | ||
| 350 | } | ||
| 351 | } | ||
| 352 | printf("ScanForWalletTransactions() : %d block(s) scanned, %d transaction(s) added or updated\n", | ||
| 353 | nScanned, nFound); | ||
| 354 | return nFound; | ||
| 355 | } | ||
| 356 | |||
| 308 | bool EraseFromWallet(uint256 hash) | 357 | bool EraseFromWallet(uint256 hash) |
| 309 | { | 358 | { |
| 310 | CRITICAL_BLOCK(cs_mapWallet) | 359 | CRITICAL_BLOCK(cs_mapWallet) |