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); } }
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); // Cap last block file size, and mine new block in a new block file. CBlockIndex* const nullBlock = nullptr; CBlockIndex* oldTip = chainActive.Tip(); GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = chainActive.Tip(); // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } // 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. { CWallet wallet; vpwallets.insert(vpwallets.begin(), &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)); vpwallets.erase(vpwallets.begin()); } }
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); // 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(); // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } // 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. { CWallet wallet; CWallet *backup = ::pwalletMain; ::pwalletMain = &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); 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\":\"Failed to rescan before time %d, transactions may be missing.\"}},{\"success\":true}]", newTip->GetBlockTimeMax())); ::pwalletMain = backup; } // Verify ScanForWalletTransactions does not return null when the scan is // elided due to the nTimeFirstKey optimization. { CWallet wallet; { LOCK(wallet.cs_wallet); wallet.UpdateTimeFirstKey(newTip->GetBlockTime() + 7200 + 1); } BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(newTip)); } }
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) { auto chain = interfaces::MakeChain(); // Cap last block file size, and mine new block in a new block file. const CBlockIndex* const null_block = nullptr; 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(); // Verify ScanForWalletTransactions accomodates a null start block. { CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(nullptr, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); BOOST_CHECK_EQUAL(failed_block, null_block); BOOST_CHECK_EQUAL(stop_block, null_block); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); } // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); BOOST_CHECK_EQUAL(failed_block, null_block); BOOST_CHECK_EQUAL(stop_block, newTip); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. { CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE); BOOST_CHECK_EQUAL(failed_block, oldTip); BOOST_CHECK_EQUAL(stop_block, newTip); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } // Prune the remaining block file. PruneOneBlockFile(newTip->GetBlockPos().nFile); UnlinkPrunedFiles({newTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions scans no blocks. { CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE); BOOST_CHECK_EQUAL(failed_block, newTip); BOOST_CHECK_EQUAL(stop_block, null_block); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); } }