// Verify importwallet RPC starts rescan at earliest block with timestamp // greater or equal than key birthday. Previously there was a bug where // importwallet RPC would start the scan at the latest block with timestamp less // than or equal to key birthday. BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) { auto chain = interfaces::MakeChain(); // Create two blocks with same timestamp to verify that importwallet rescan // will pick up both blocks, not just the first. const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; SetMockTime(BLOCK_TIME); m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); // Set key birthday to block time increased by the timestamp window, so // rescan will start at the block time. const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW; SetMockTime(KEY_TIME); m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); auto locked_chain = chain->lock(); std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string(); // Import key into wallet and call dumpwallet to create backup file. { std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy()); LOCK(wallet->cs_wallet); wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); JSONRPCRequest request; request.params.setArray(); request.params.push_back(backup_file); AddWallet(wallet); ::dumpwallet(request); RemoveWallet(wallet); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // were scanned, and no prior blocks were scanned. { std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy()); JSONRPCRequest request; request.params.setArray(); request.params.push_back(backup_file); AddWallet(wallet); ::importwallet(request); RemoveWallet(wallet); LOCK(wallet->cs_wallet); BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U); BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U); for (size_t i = 0; i < m_coinbase_txns.size(); ++i) { bool found = wallet->GetWalletTx(m_coinbase_txns[i]->GetHash()); bool expected = i >= 100; BOOST_CHECK_EQUAL(found, expected); } } SetMockTime(0); }
void UnloadWallets() { auto wallets = GetWallets(); while (!wallets.empty()) { auto wallet = wallets.back(); wallets.pop_back(); RemoveWallet(wallet); UnloadWallet(std::move(wallet)); } }
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup) { auto chain = interfaces::MakeChain(); // Cap last block file size, and mine new block in a new block file. CBlockIndex* oldTip = chainActive.Tip(); GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = chainActive.Tip(); auto locked_chain = chain->lock(); // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify importmulti RPC returns failure for a key whose creation time is // before the missing block, and success for a key whose creation time is // after. { std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy()); AddWallet(wallet); UniValue keys; keys.setArray(); UniValue key; key.setObject(); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey()))); key.pushKV("timestamp", 0); key.pushKV("internal", UniValue(true)); keys.push_back(key); key.clear(); key.setObject(); CKey futureKey; futureKey.MakeNewKey(true); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey()))); key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1); key.pushKV("internal", UniValue(true)); keys.push_back(key); JSONRPCRequest request; request.params.setArray(); request.params.push_back(keys); UniValue response = importmulti(request); BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation " "timestamp %d. There was an error reading a block from time %d, which is after or within %d " "seconds of key creation, and could contain transactions pertaining to the key. As a result, " "transactions and coins using this key may not appear in the wallet. This error could be caused " "by pruning or data corruption (see bitcoind log for details) and could be dealt with by " "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); RemoveWallet(wallet); } }
void WalletInit::Close() const { for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { RemoveWallet(pwallet); } }