From 89f6d045c8e3dacbfbb369a3ac89c0c33de22b4c Mon Sep 17 00:00:00 2001 From: mtg0001 <106211915+mtg0001@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:56:50 -0300 Subject: Keep HD change recoverable and bound message stalls (#109) Change from an HD wallet now goes to a newly derived phrase-backed key instead of reusing a spent input's key. Reusing an input key silently moved value back to a key the phrase cannot reproduce; if derivation fails the transaction is refused rather than creating unrecoverable change. Legacy wallets with no seed keep the 0.1.0 behaviour. Restore also scans past the pre-generated key-pool window before treating an empty batch as the end. The message parser no longer consumes a header it cannot complete: it reads from a copy, so the O(n) reinsert and the 100 ms sleep inside the shared message thread are gone, and a peer that sits on an incomplete payload is disconnected after two minutes. The wire cap drops from 256 MB to MAX_SIZE. New net-message self-test covers complete, incomplete, stale and oversized headers. Verified here: clean rebuild, all self-tests pass, lock order checked (no path takes cs_keyPool before cs_mapWallet), and a node built from this branch synced from genesis to the chain tip on the live network with zero rejections. --- src/walletcmd.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/walletcmd.cpp') diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 8eb3592..fae2095 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp @@ -25,6 +25,10 @@ // would restore a wallet that looks empty. static const int RESTORE_BATCH = 100; static const int RESTORE_MAX = 10000; +// The wallet may have a full unused key pool in front of a change address +// created while spending pre-phrase coins. Looking only one empty batch ahead +// can stop just before that change output. +static const int RESTORE_MIN_SCAN = KEYPOOL_SIZE + RESTORE_BATCH; int CmdNewPhrase() { @@ -105,6 +109,7 @@ bool RestoreFromPhrase(const std::string& strMnemonic, // up nothing. Every derived key is written to the wallet before the scan, // because the scan asks the wallet what belongs to it. int nTotalDerived = (int)nHDNext; + int nStopDepth = max(nMinDepth, RESTORE_MIN_SCAN); while (nTotalDerived < RESTORE_MAX) { // Count wallet transactions, not scan hits. @@ -146,7 +151,7 @@ bool RestoreFromPhrase(const std::string& strMnemonic, // A whole batch with nothing in it means far enough -- unless the // caller asked to look deeper anyway. - if (nWalletAfter == nWalletBefore && nTotalDerived >= nMinDepth) + if (nWalletAfter == nWalletBefore && nTotalDerived >= nStopDepth) break; } -- cgit v1.2.3