Skip to content

Commit 653a46d

Browse files
committed
Merge #11022: Basic keypool topup
d34957e [wallet] [tests] Add keypool topup functional test (Jonas Schnelli) 095142d [wallet] keypool mark-used and topup (John Newbery) c25d90f [wallet] Add HasUnusedKeys() helper (John Newbery) f2123e3 [wallet] Cache keyid -> keypool id mappings (John Newbery) 83f1ec3 [wallet] Don't hold cs_LastBlockFile while calling setBestChain (John Newbery) 2376bfc [wallet] [moveonly] Move LoadKeyPool to cpp (Matt Corallo) cab8557 [wallet] [moveonly] Move CAffectedKeysVisitor (Jonas Schnelli) Pull request description: This PR contains the first part of #10882 : - if a key from the keypool is used, mark all keys up to that key as used, and then try to top up the keypool - top up the keypool on startup Notably, it does not stop the node or prevent the best block from advancing if the keypool drops below a threshold (which means that transactions may be missed and funds lost if restoring from an old HD wallet backup). Tree-SHA512: ac681fefeaf7ec2aab2fa1da93d12273ea80bd05eb48d7b3b551ea6e5d975dd97ba7de52b7fba52993823280ac4079cc36cf78a27dac708107ebf8fb6326142b
2 parents 98aa3f6 + d34957e commit 653a46d

File tree

7 files changed

+289
-161
lines changed

7 files changed

+289
-161
lines changed

src/validation.cpp

Lines changed: 84 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,95 +1863,100 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
18631863
*/
18641864
bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
18651865
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
1866-
LOCK2(cs_main, cs_LastBlockFile);
1866+
LOCK(cs_main);
18671867
static int64_t nLastWrite = 0;
18681868
static int64_t nLastFlush = 0;
18691869
static int64_t nLastSetChain = 0;
18701870
std::set<int> setFilesToPrune;
18711871
bool fFlushForPrune = false;
1872+
bool fDoFullFlush = false;
1873+
int64_t nNow = 0;
18721874
try {
1873-
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
1874-
if (nManualPruneHeight > 0) {
1875-
FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight);
1876-
} else {
1877-
FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight());
1878-
fCheckForPruning = false;
1879-
}
1880-
if (!setFilesToPrune.empty()) {
1881-
fFlushForPrune = true;
1882-
if (!fHavePruned) {
1883-
pblocktree->WriteFlag("prunedblockfiles", true);
1884-
fHavePruned = true;
1885-
}
1886-
}
1887-
}
1888-
int64_t nNow = GetTimeMicros();
1889-
// Avoid writing/flushing immediately after startup.
1890-
if (nLastWrite == 0) {
1891-
nLastWrite = nNow;
1892-
}
1893-
if (nLastFlush == 0) {
1894-
nLastFlush = nNow;
1895-
}
1896-
if (nLastSetChain == 0) {
1897-
nLastSetChain = nNow;
1898-
}
1899-
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
1900-
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
1901-
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
1902-
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
1903-
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
1904-
// The cache is over the limit, we have to write now.
1905-
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace;
1906-
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
1907-
bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
1908-
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
1909-
bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
1910-
// Combine all conditions that result in a full cache flush.
1911-
bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
1912-
// Write blocks and block index to disk.
1913-
if (fDoFullFlush || fPeriodicWrite) {
1914-
// Depend on nMinDiskSpace to ensure we can write block index
1915-
if (!CheckDiskSpace(0))
1916-
return state.Error("out of disk space");
1917-
// First make sure all block and undo data is flushed to disk.
1918-
FlushBlockFile();
1919-
// Then update all block file information (which may refer to block and undo files).
1920-
{
1921-
std::vector<std::pair<int, const CBlockFileInfo*> > vFiles;
1922-
vFiles.reserve(setDirtyFileInfo.size());
1923-
for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
1924-
vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it]));
1925-
setDirtyFileInfo.erase(it++);
1875+
{
1876+
LOCK(cs_LastBlockFile);
1877+
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
1878+
if (nManualPruneHeight > 0) {
1879+
FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight);
1880+
} else {
1881+
FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight());
1882+
fCheckForPruning = false;
19261883
}
1927-
std::vector<const CBlockIndex*> vBlocks;
1928-
vBlocks.reserve(setDirtyBlockIndex.size());
1929-
for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) {
1930-
vBlocks.push_back(*it);
1931-
setDirtyBlockIndex.erase(it++);
1884+
if (!setFilesToPrune.empty()) {
1885+
fFlushForPrune = true;
1886+
if (!fHavePruned) {
1887+
pblocktree->WriteFlag("prunedblockfiles", true);
1888+
fHavePruned = true;
1889+
}
19321890
}
1933-
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
1934-
return AbortNode(state, "Failed to write to block index database");
1891+
}
1892+
nNow = GetTimeMicros();
1893+
// Avoid writing/flushing immediately after startup.
1894+
if (nLastWrite == 0) {
1895+
nLastWrite = nNow;
1896+
}
1897+
if (nLastFlush == 0) {
1898+
nLastFlush = nNow;
1899+
}
1900+
if (nLastSetChain == 0) {
1901+
nLastSetChain = nNow;
1902+
}
1903+
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
1904+
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
1905+
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
1906+
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
1907+
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
1908+
// The cache is over the limit, we have to write now.
1909+
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace;
1910+
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
1911+
bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
1912+
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
1913+
bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
1914+
// Combine all conditions that result in a full cache flush.
1915+
fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
1916+
// Write blocks and block index to disk.
1917+
if (fDoFullFlush || fPeriodicWrite) {
1918+
// Depend on nMinDiskSpace to ensure we can write block index
1919+
if (!CheckDiskSpace(0))
1920+
return state.Error("out of disk space");
1921+
// First make sure all block and undo data is flushed to disk.
1922+
FlushBlockFile();
1923+
// Then update all block file information (which may refer to block and undo files).
1924+
{
1925+
std::vector<std::pair<int, const CBlockFileInfo*> > vFiles;
1926+
vFiles.reserve(setDirtyFileInfo.size());
1927+
for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
1928+
vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it]));
1929+
setDirtyFileInfo.erase(it++);
1930+
}
1931+
std::vector<const CBlockIndex*> vBlocks;
1932+
vBlocks.reserve(setDirtyBlockIndex.size());
1933+
for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) {
1934+
vBlocks.push_back(*it);
1935+
setDirtyBlockIndex.erase(it++);
1936+
}
1937+
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
1938+
return AbortNode(state, "Failed to write to block index database");
1939+
}
19351940
}
1941+
// Finally remove any pruned files
1942+
if (fFlushForPrune)
1943+
UnlinkPrunedFiles(setFilesToPrune);
1944+
nLastWrite = nNow;
1945+
}
1946+
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
1947+
if (fDoFullFlush) {
1948+
// Typical Coin structures on disk are around 48 bytes in size.
1949+
// Pushing a new one to the database can cause it to be written
1950+
// twice (once in the log, and once in the tables). This is already
1951+
// an overestimation, as most will delete an existing entry or
1952+
// overwrite one. Still, use a conservative safety factor of 2.
1953+
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
1954+
return state.Error("out of disk space");
1955+
// Flush the chainstate (which may refer to block index entries).
1956+
if (!pcoinsTip->Flush())
1957+
return AbortNode(state, "Failed to write to coin database");
1958+
nLastFlush = nNow;
19361959
}
1937-
// Finally remove any pruned files
1938-
if (fFlushForPrune)
1939-
UnlinkPrunedFiles(setFilesToPrune);
1940-
nLastWrite = nNow;
1941-
}
1942-
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
1943-
if (fDoFullFlush) {
1944-
// Typical Coin structures on disk are around 48 bytes in size.
1945-
// Pushing a new one to the database can cause it to be written
1946-
// twice (once in the log, and once in the tables). This is already
1947-
// an overestimation, as most will delete an existing entry or
1948-
// overwrite one. Still, use a conservative safety factor of 2.
1949-
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
1950-
return state.Error("out of disk space");
1951-
// Flush the chainstate (which may refer to block index entries).
1952-
if (!pcoinsTip->Flush())
1953-
return AbortNode(state, "Failed to write to coin database");
1954-
nLastFlush = nNow;
19551960
}
19561961
if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) {
19571962
// Update best block in wallet (so we can detect restored wallets).

src/wallet/rpcdump.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -619,9 +619,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
619619
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
620620

621621
std::map<CTxDestination, int64_t> mapKeyBirth;
622-
std::set<CKeyID> setKeyPool;
622+
const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys();
623623
pwallet->GetKeyBirthTimes(mapKeyBirth);
624-
pwallet->GetAllReserveKeys(setKeyPool);
625624

626625
// sort time/key pairs
627626
std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
@@ -666,7 +665,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
666665
file << strprintf("label=%s", EncodeDumpString(pwallet->mapAddressBook[keyid].name));
667666
} else if (keyid == masterKeyID) {
668667
file << "hdmaster=1";
669-
} else if (setKeyPool.count(keyid)) {
668+
} else if (mapKeyPool.count(keyid)) {
670669
file << "reserve=1";
671670
} else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") {
672671
file << "inactivehdmaster=1";

0 commit comments

Comments
 (0)