From 700c42b85d20e624bef4228eef062c93084efab5 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 27 Jul 2017 10:08:31 -0400 Subject: Add height, depth, and hash methods to the Chain interface And use them to remove uses of chainActive and mapBlockIndex in wallet code This commit does not change behavior. Co-authored-by: Ben Woosley --- src/wallet/rpcdump.cpp | 6 +++--- src/wallet/rpcwallet.cpp | 7 ++++--- src/wallet/test/wallet_tests.cpp | 4 ++-- src/wallet/wallet.cpp | 36 ++++++++++++++---------------------- src/wallet/wallet.h | 4 ++-- 5 files changed, 25 insertions(+), 32 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index ed1e2d3940..f2b91e7c3d 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -379,8 +379,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request) if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) { auto locked_chain = pwallet->chain().lock(); - const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); - if (!pindex || !chainActive.Contains(pindex)) { + if (locked_chain->getBlockHeight(merkleBlock.header.GetHash()) == nullopt) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } @@ -773,7 +772,8 @@ UniValue dumpwallet(const JSONRPCRequest& request) // produce output file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD); file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime())); - file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); + const Optional tip_height = locked_chain->getHeight(); + file << strprintf("# * Best block at time of backup was %i (%s),\n", tip_height.value_or(-1), tip_height ? locked_chain->getBlockHash(*tip_height).ToString() : "(missing block hash)"); file << strprintf("# mined on %s\n", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime())); file << "\n"; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 39c17743ec..c835d2928d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1607,7 +1607,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request) bool include_removed = (request.params[3].isNull() || request.params[3].get_bool()); - int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; + const Optional tip_height = locked_chain->getHeight(); + int depth = tip_height && pindex ? (1 + *tip_height - pindex->nHeight) : -1; UniValue transactions(UniValue::VARR); @@ -1638,8 +1639,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request) paltindex = paltindex->pprev; } - CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; - uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); + int last_height = tip_height ? *tip_height + 1 - target_confirms : -1; + uint256 lastblock = last_height >= 0 ? locked_chain->getBlockHash(last_height) : uint256(); UniValue ret(UniValue::VOBJ); ret.pushKV("transactions", transactions); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 0db22cf6fe..4377e9f29d 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -276,7 +276,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64 CWalletTx wtx(&wallet, MakeTransactionRef(tx)); if (block) { - wtx.SetMerkleBranch(block, 0); + wtx.SetMerkleBranch(block->GetBlockHash(), 0); } { LOCK(cs_main); @@ -372,7 +372,7 @@ public: LOCK(wallet->cs_wallet); auto it = wallet->mapWallet.find(tx->GetHash()); BOOST_CHECK(it != wallet->mapWallet.end()); - it->second.SetMerkleBranch(chainActive.Tip(), 1); + it->second.SetMerkleBranch(chainActive.Tip()->GetBlockHash(), 1); return it->second; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 098055673b..c34295a11a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -935,19 +935,19 @@ void CWallet::LoadToWallet(const CWalletTx& wtxIn) } } -bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) +bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool fUpdate) { const CTransaction& tx = *ptx; { AssertLockHeld(cs_wallet); - if (pIndex != nullptr) { + if (!block_hash.IsNull()) { for (const CTxIn& txin : tx.vin) { std::pair range = mapTxSpends.equal_range(txin.prevout); while (range.first != range.second) { if (range.first->second != tx.GetHash()) { - WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); - MarkConflicted(pIndex->GetBlockHash(), range.first->second); + WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), block_hash.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); + MarkConflicted(block_hash, range.first->second); } range.first++; } @@ -983,8 +983,8 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI CWalletTx wtx(this, ptx); // Get merkle branch if transaction was found in a block - if (pIndex != nullptr) - wtx.SetMerkleBranch(pIndex, posInBlock); + if (!block_hash.IsNull()) + wtx.SetMerkleBranch(block_hash, posInBlock); return AddToWallet(wtx, false); } @@ -1071,11 +1071,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) auto locked_chain = chain().lock(); LOCK(cs_wallet); - int conflictconfirms = 0; - CBlockIndex* pindex = LookupBlockIndex(hashBlock); - if (pindex && chainActive.Contains(pindex)) { - conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); - } + int conflictconfirms = -locked_chain->getBlockDepth(hashBlock); // If number of conflict confirms cannot be determined, this means // that the block is still unknown or not yet part of the main chain, // for example when loading the wallet during a reindex. Do nothing in that @@ -1122,7 +1118,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock, bool update_tx) { - if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, update_tx)) + if (!AddToWalletIfInvolvingMe(ptx, pindex->GetBlockHash(), posInBlock, update_tx)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -2573,6 +2569,7 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock& locked_chain) */ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_chain) { + uint32_t const height = locked_chain.getHeight().value_or(-1); uint32_t locktime; // Discourage fee sniping. // @@ -2595,7 +2592,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_cha // now we ensure code won't be written that makes assumptions about // nLockTime that preclude a fix later. if (IsCurrentForAntiFeeSniping(locked_chain)) { - locktime = chainActive.Height(); + locktime = height; // Secondly occasionally randomly pick a nLockTime even further back, so // that transactions that are delayed after signing for whatever reason, @@ -2609,7 +2606,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_cha // unique "nLockTime fingerprint", set nLockTime to a constant. locktime = 0; } - assert(locktime <= (unsigned int)chainActive.Height()); + assert(locktime <= height); assert(locktime < LOCKTIME_THRESHOLD); return locktime; } @@ -4282,10 +4279,10 @@ CWalletKey::CWalletKey(int64_t nExpires) nTimeExpires = nExpires; } -void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) +void CMerkleTx::SetMerkleBranch(const uint256& block_hash, int posInBlock) { // Update the tx's hashBlock - hashBlock = pindex->GetBlockHash(); + hashBlock = block_hash; // set the position of the transaction in the block nIndex = posInBlock; @@ -4298,12 +4295,7 @@ int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const AssertLockHeld(cs_main); - // Find the block it claims to be in - CBlockIndex* pindex = LookupBlockIndex(hashBlock); - if (!pindex || !chainActive.Contains(pindex)) - return 0; - - return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); + return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1); } int CMerkleTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6872fbad2d..d8981f7385 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -287,7 +287,7 @@ public: READWRITE(nIndex); } - void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock); + void SetMerkleBranch(const uint256& block_hash, int posInBlock); /** * Return depth of transaction in blockchain: @@ -667,7 +667,7 @@ private: * Abandoned state should probably be more carefully tracked via different * posInBlock signals or by checking mempool presence when necessary. */ - bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const uint256& block_hash, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ void MarkConflicted(const uint256& hashBlock, const uint256& hashTx); -- cgit v1.2.3 From d93c4c1d6e0aab5f32306ecd7c1237257b26940d Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 7 Jan 2019 22:35:47 -0800 Subject: Add time methods to the Chain interface And use them to remove uses of chainActive and mapBlockIndex in wallet code This commit does not change behavior. Co-authored-by: Ben Woosley --- src/wallet/rpcdump.cpp | 12 +++++++----- src/wallet/wallet.cpp | 19 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index f2b91e7c3d..c24703968d 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -569,7 +569,8 @@ UniValue importwallet(const JSONRPCRequest& request) if (!file.is_open()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); } - nTimeBegin = chainActive.Tip()->GetBlockTime(); + Optional tip_height = locked_chain->getHeight(); + nTimeBegin = tip_height ? locked_chain->getBlockTime(*tip_height) : 0; int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); @@ -774,7 +775,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime())); const Optional tip_height = locked_chain->getHeight(); file << strprintf("# * Best block at time of backup was %i (%s),\n", tip_height.value_or(-1), tip_height ? locked_chain->getBlockHash(*tip_height).ToString() : "(missing block hash)"); - file << strprintf("# mined on %s\n", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime())); + file << strprintf("# mined on %s\n", tip_height ? FormatISO8601DateTime(locked_chain->getBlockTime(*tip_height)) : "(missing block time)"); file << "\n"; // add the base58check encoded extended master if the wallet uses HD @@ -1232,15 +1233,16 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) EnsureWalletIsUnlocked(pwallet); // Verify all timestamps are present before importing any keys. - now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0; + const Optional tip_height = locked_chain->getHeight(); + now = tip_height ? locked_chain->getBlockMedianTimePast(*tip_height) : 0; for (const UniValue& data : requests.getValues()) { GetImportTimestamp(data, now); } const int64_t minimumTimestamp = 1; - if (fRescan && chainActive.Tip()) { - nLowestTimestamp = chainActive.Tip()->GetBlockTime(); + if (fRescan && tip_height) { + nLowestTimestamp = locked_chain->getBlockTime(*tip_height); } else { fRescan = false; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c34295a11a..35660372cf 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3715,11 +3715,12 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map mapKeyFirstBlock; + const Optional tip_height = locked_chain.getHeight(); + const int max_height = tip_height && *tip_height > 144 ? *tip_height - 144 : 0; // the tip can be reorganized; use a 144-block safety margin + std::map mapKeyFirstBlock; for (const CKeyID &keyid : GetKeys()) { if (mapKeyBirth.count(keyid) == 0) - mapKeyFirstBlock[keyid] = pindexMax; + mapKeyFirstBlock[keyid] = max_height; } // if there are no such keys, we're done @@ -3730,17 +3731,15 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map height = locked_chain.getBlockHeight(wtx.hashBlock)) { // ... which are already in a block - int nHeight = pindex->nHeight; for (const CTxOut &txout : wtx.tx->vout) { // iterate over all their outputs for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *this)) { // ... and all their affected keys - std::map::iterator rit = mapKeyFirstBlock.find(keyid); - if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) - rit->second = pindex; + std::map::iterator rit = mapKeyFirstBlock.find(keyid); + if (rit != mapKeyFirstBlock.end() && *height < rit->second) + rit->second = *height; } } } @@ -3748,7 +3747,7 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::mapGetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off + mapKeyBirth[entry.first] = locked_chain.getBlockTime(entry.second) - TIMESTAMP_WINDOW; // block times can be 2h off } /** -- cgit v1.2.3 From 2ffb07929ef480bd114defdc10b6a84463f222be Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 7 Jan 2019 22:56:46 -0800 Subject: Add findFork and findBlock to the Chain interface And use them to remove uses of chainActive and mapBlockIndex in wallet code This commit does not change behavior. Co-authored-by: Ben Woosley --- src/wallet/rpcwallet.cpp | 32 +++++++++++++++----------------- src/wallet/wallet.cpp | 4 ++-- 2 files changed, 17 insertions(+), 19 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c835d2928d..298a674b07 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -102,7 +102,10 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); - entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); + int64_t block_time; + bool found_block = chain.findBlock(wtx.hashBlock, nullptr /* block */, &block_time); + assert(found_block); + entry.pushKV("blocktime", block_time); } else { entry.pushKV("trusted", wtx.IsTrusted(locked_chain)); } @@ -1573,24 +1576,18 @@ static UniValue listsinceblock(const JSONRPCRequest& request) auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); - const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. - const CBlockIndex* paltindex = nullptr; // Block index of the specified block, even if it's in a deactivated chain. + Optional height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain. + Optional altheight; // Height of the specified block, even if it's in a deactivated chain. int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; + uint256 blockId; if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { - uint256 blockId(ParseHashV(request.params[0], "blockhash")); - - paltindex = pindex = LookupBlockIndex(blockId); - if (!pindex) { + blockId = ParseHashV(request.params[0], "blockhash"); + height = locked_chain->findFork(blockId, &altheight); + if (!height) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - if (chainActive[pindex->nHeight] != pindex) { - // the block being asked for is a part of a deactivated chain; - // we don't want to depend on its perceived height in the block - // chain, we want to instead use the last common ancestor - pindex = chainActive.FindFork(pindex); - } } if (!request.params[1].isNull()) { @@ -1608,7 +1605,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request) bool include_removed = (request.params[3].isNull() || request.params[3].get_bool()); const Optional tip_height = locked_chain->getHeight(); - int depth = tip_height && pindex ? (1 + *tip_height - pindex->nHeight) : -1; + int depth = tip_height && height ? (1 + *tip_height - *height) : -1; UniValue transactions(UniValue::VARR); @@ -1623,9 +1620,9 @@ static UniValue listsinceblock(const JSONRPCRequest& request) // when a reorg'd block is requested, we also list any relevant transactions // in the blocks of the chain that was detached UniValue removed(UniValue::VARR); - while (include_removed && paltindex && paltindex != pindex) { + while (include_removed && altheight && *altheight > *height) { CBlock block; - if (!ReadBlockFromDisk(block, paltindex, Params().GetConsensus())) { + if (!pwallet->chain().findBlock(blockId, &block) || block.IsNull()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); } for (const CTransactionRef& tx : block.vtx) { @@ -1636,7 +1633,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request) ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */); } } - paltindex = paltindex->pprev; + blockId = block.hashPrevBlock; + --*altheight; } int last_height = tip_height ? *tip_height + 1 - target_confirms : -1; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 35660372cf..32275c6180 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3775,7 +3775,8 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const { unsigned int nTimeSmart = wtx.nTimeReceived; if (!wtx.hashUnset()) { - if (const CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock)) { + int64_t blocktime; + if (chain().findBlock(wtx.hashBlock, nullptr /* block */, &blocktime)) { int64_t latestNow = wtx.nTimeReceived; int64_t latestEntry = 0; @@ -3801,7 +3802,6 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const } } - int64_t blocktime = pindex->GetBlockTime(); nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else { WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.hashBlock.ToString()); -- cgit v1.2.3 From db21f0264855406c9b6ec4c084a15988c5c71b32 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 7 Jan 2019 23:38:53 -0800 Subject: Convert CWallet::ScanForWalletTransactions and SyncTransaction to the new Chain apis Only change in behavior is "Rescan started from block " message replaced by "Rescan started from block " message in ScanForWalletTransactions. Co-authored-by: Ben Woosley --- src/wallet/rpcwallet.cpp | 48 +++++++------- src/wallet/test/wallet_tests.cpp | 47 ++++++------- src/wallet/wallet.cpp | 138 +++++++++++++++++++++------------------ src/wallet/wallet.h | 24 +++++-- 4 files changed, 139 insertions(+), 118 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 298a674b07..32173a92d0 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3387,50 +3387,48 @@ UniValue rescanblockchain(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } - CBlockIndex *pindexStart = nullptr; - CBlockIndex *pindexStop = nullptr; - CBlockIndex *pChainTip = nullptr; + int start_height = 0; + uint256 start_block, stop_block; { auto locked_chain = pwallet->chain().lock(); - pindexStart = chainActive.Genesis(); - pChainTip = chainActive.Tip(); + Optional tip_height = locked_chain->getHeight(); if (!request.params[0].isNull()) { - pindexStart = chainActive[request.params[0].get_int()]; - if (!pindexStart) { + start_height = request.params[0].get_int(); + if (start_height < 0 || !tip_height || start_height > *tip_height) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height"); } } + Optional stop_height; if (!request.params[1].isNull()) { - pindexStop = chainActive[request.params[1].get_int()]; - if (!pindexStop) { + stop_height = request.params[1].get_int(); + if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height"); } - else if (pindexStop->nHeight < pindexStart->nHeight) { + else if (*stop_height < start_height) { throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height"); } } - } - // We can't rescan beyond non-pruned blocks, stop and throw an error - if (fPruneMode) { - auto locked_chain = pwallet->chain().lock(); - CBlockIndex *block = pindexStop ? pindexStop : pChainTip; - while (block && block->nHeight >= pindexStart->nHeight) { - if (!(block->nStatus & BLOCK_HAVE_DATA)) { - throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); + // We can't rescan beyond non-pruned blocks, stop and throw an error + if (locked_chain->findPruned(start_height, stop_height)) { + throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); + } + + if (tip_height) { + start_block = locked_chain->getBlockHash(start_height); + if (stop_height) { + stop_block = locked_chain->getBlockHash(*stop_height); } - block = block->pprev; } } - const CBlockIndex *failed_block, *stopBlock; CWallet::ScanResult result = - pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, failed_block, stopBlock, true); - switch (result) { + pwallet->ScanForWalletTransactions(start_block, stop_block, reserver, true /* fUpdate */); + switch (result.status) { case CWallet::ScanResult::SUCCESS: - break; // stopBlock set by ScanForWalletTransactions + break; case CWallet::ScanResult::FAILURE: throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files."); case CWallet::ScanResult::USER_ABORT: @@ -3438,8 +3436,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request) // no default case, so the compiler can warn about missing cases } UniValue response(UniValue::VOBJ); - response.pushKV("start_height", pindexStart->nHeight); - response.pushKV("stop_height", stopBlock->nHeight); + response.pushKV("start_height", start_height); + response.pushKV("stop_height", result.stop_height ? *result.stop_height : UniValue()); return response; } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 4377e9f29d..8c380f1257 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -39,7 +39,6 @@ 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())); @@ -53,10 +52,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) 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); + CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); + BOOST_CHECK(result.failed_block.IsNull()); + BOOST_CHECK(result.stop_block.IsNull()); + BOOST_CHECK(!result.stop_height); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); } @@ -67,10 +67,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) 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); + CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); + BOOST_CHECK(result.failed_block.IsNull()); + BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash()); + BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } @@ -85,10 +86,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) 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); + CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE); + BOOST_CHECK_EQUAL(result.failed_block, oldTip->GetBlockHash()); + BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash()); + BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } @@ -102,10 +104,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) 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); + CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE); + BOOST_CHECK_EQUAL(result.failed_block, newTip->GetBlockHash()); + BOOST_CHECK(result.stop_block.IsNull()); + BOOST_CHECK(!result.stop_height); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); } } @@ -340,11 +343,11 @@ public: AddKey(*wallet, coinbaseKey); WalletRescanReserver reserver(wallet.get()); reserver.reserve(); - const CBlockIndex* const null_block = nullptr; - const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; - BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); - BOOST_CHECK_EQUAL(stop_block, chainActive.Tip()); - BOOST_CHECK_EQUAL(failed_block, null_block); + CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); + BOOST_CHECK_EQUAL(result.stop_block, chainActive.Tip()->GetBlockHash()); + BOOST_CHECK_EQUAL(*result.stop_height, chainActive.Height()); + BOOST_CHECK(result.failed_block.IsNull()); } ~ListCoinsTestingSetup() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 32275c6180..4b1d010fed 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1117,8 +1117,8 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } } -void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock, bool update_tx) { - if (!AddToWalletIfInvolvingMe(ptx, pindex->GetBlockHash(), posInBlock, update_tx)) +void CWallet::SyncTransaction(const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool update_tx) { + if (!AddToWalletIfInvolvingMe(ptx, block_hash, posInBlock, update_tx)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -1130,7 +1130,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) { auto locked_chain = chain().lock(); LOCK(cs_wallet); - SyncTransaction(ptx); + SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */); auto it = mapWallet.find(ptx->GetHash()); if (it != mapWallet.end()) { @@ -1158,11 +1158,11 @@ void CWallet::BlockConnected(const std::shared_ptr& pblock, const // the notification that the conflicted transaction was evicted. for (const CTransactionRef& ptx : vtxConflicted) { - SyncTransaction(ptx); + SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */); TransactionRemovedFromMempool(ptx); } for (size_t i = 0; i < pblock->vtx.size(); i++) { - SyncTransaction(pblock->vtx[i], pindex, i); + SyncTransaction(pblock->vtx[i], pindex->GetBlockHash(), i); TransactionRemovedFromMempool(pblock->vtx[i]); } @@ -1174,7 +1174,7 @@ void CWallet::BlockDisconnected(const std::shared_ptr& pblock) { LOCK(cs_wallet); for (const CTransactionRef& ptx : pblock->vtx) { - SyncTransaction(ptx); + SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */); } } @@ -1587,132 +1587,143 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r // Find starting block. May be null if nCreateTime is greater than the // highest blockchain timestamp, in which case there is nothing that needs // to be scanned. - CBlockIndex* startBlock = nullptr; + uint256 start_block; { auto locked_chain = chain().lock(); - startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); - WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); + const Optional start_height = locked_chain->findFirstBlockWithTime(startTime - TIMESTAMP_WINDOW, &start_block); + const Optional tip_height = locked_chain->getHeight(); + WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0); } - if (startBlock) { - const CBlockIndex *failedBlock, *stop_block; + if (!start_block.IsNull()) { // TODO: this should take into account failure by ScanResult::USER_ABORT - if (ScanResult::FAILURE == ScanForWalletTransactions(startBlock, nullptr, reserver, failedBlock, stop_block, update)) { - return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; + ScanResult result = ScanForWalletTransactions(start_block, {} /* stop_block */, reserver, update); + if (result.status == ScanResult::FAILURE) { + int64_t time_max; + if (!chain().findBlock(result.failed_block, nullptr /* block */, nullptr /* time */, &time_max)) { + throw std::logic_error("ScanForWalletTransactions returned invalid block hash"); + } + return time_max + TIMESTAMP_WINDOW + 1; } } return startTime; } /** - * Scan the block chain (starting in pindexStart) for transactions + * Scan the block chain (starting in start_block) for transactions * from or to us. If fUpdate is true, found transactions that already * exist in the wallet will be updated. * - * @param[in] pindexStop if not a nullptr, the scan will stop at this block-index - * @param[out] failed_block if FAILURE is returned, the most recent block - * that could not be scanned, otherwise nullptr - * @param[out] stop_block the most recent block that could be scanned, - * otherwise nullptr if no block could be scanned + * @param[in] start_block if not null, the scan will start at this block instead + * of the genesis block + * @param[in] stop_block if not null, the scan will stop at this block instead + * of the chain tip * * @return ScanResult indicating success or failure of the scan. SUCCESS if * scan was successful. FAILURE if a complete rescan was not possible (due to * pruning or corruption). USER_ABORT if the rescan was aborted before it * could complete. * - * @pre Caller needs to make sure pindexStop (and the optional pindexStart) are on + * @pre Caller needs to make sure start_block (and the optional stop_block) are on * the main chain after to the addition of any new keys you want to detect * transactions for. */ -CWallet::ScanResult CWallet::ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate) +CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, const uint256& stop_block, const WalletRescanReserver& reserver, bool fUpdate) { int64_t nNow = GetTime(); - const CChainParams& chainParams = Params(); assert(reserver.isReserved()); - if (pindexStop) { - assert(pindexStop->nHeight >= pindexStart->nHeight); - } - const CBlockIndex* pindex = pindexStart; - failed_block = nullptr; - stop_block = nullptr; + uint256 block_hash = start_block; + ScanResult result; - if (pindex) WalletLogPrintf("Rescan started from block %d...\n", pindex->nHeight); + WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString()); { fAbortRescan = false; ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup - CBlockIndex* tip = nullptr; + uint256 tip_hash; + Optional block_height; double progress_begin; double progress_end; { auto locked_chain = chain().lock(); - progress_begin = GuessVerificationProgress(chainParams.TxData(), pindex); - if (pindexStop == nullptr) { - tip = chainActive.Tip(); - progress_end = GuessVerificationProgress(chainParams.TxData(), tip); - } else { - progress_end = GuessVerificationProgress(chainParams.TxData(), pindexStop); + if (Optional tip_height = locked_chain->getHeight()) { + tip_hash = locked_chain->getBlockHash(*tip_height); } + block_height = locked_chain->getBlockHeight(block_hash); + progress_begin = chain().guessVerificationProgress(block_hash); + progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block); } double progress_current = progress_begin; - while (pindex && !fAbortRescan && !ShutdownRequested()) { - if (pindex->nHeight % 100 == 0 && progress_end - progress_begin > 0.0) { + while (block_height && !fAbortRescan && !ShutdownRequested()) { + if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) { ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100)))); } if (GetTime() >= nNow + 60) { nNow = GetTime(); - WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, progress_current); + WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", *block_height, progress_current); } CBlock block; - if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { + if (chain().findBlock(block_hash, &block) && !block.IsNull()) { auto locked_chain = chain().lock(); LOCK(cs_wallet); - if (pindex && !chainActive.Contains(pindex)) { + if (!locked_chain->getBlockHeight(block_hash)) { // Abort scan if current block is no longer active, to prevent // marking transactions as coming from the wrong block. - failed_block = pindex; + // TODO: This should return success instead of failure, see + // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518 + result.failed_block = block_hash; + result.status = ScanResult::FAILURE; break; } for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { - SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate); + SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate); } // scan succeeded, record block as most recent successfully scanned - stop_block = pindex; + result.stop_block = block_hash; + result.stop_height = *block_height; } else { // could not scan block, keep scanning but record this block as the most recent failure - failed_block = pindex; + result.failed_block = block_hash; + result.status = ScanResult::FAILURE; } - if (pindex == pindexStop) { + if (block_hash == stop_block) { break; } { auto locked_chain = chain().lock(); - pindex = chainActive.Next(pindex); - progress_current = GuessVerificationProgress(chainParams.TxData(), pindex); - if (pindexStop == nullptr && tip != chainActive.Tip()) { - tip = chainActive.Tip(); + Optional tip_height = locked_chain->getHeight(); + if (!tip_height || *tip_height <= block_height || !locked_chain->getBlockHeight(block_hash)) { + // break successfully when rescan has reached the tip, or + // previous block is no longer on the chain due to a reorg + break; + } + + // increment block and verification progress + block_hash = locked_chain->getBlockHash(++*block_height); + progress_current = chain().guessVerificationProgress(block_hash); + + // handle updated tip hash + const uint256 prev_tip_hash = tip_hash; + tip_hash = locked_chain->getBlockHash(*tip_height); + if (stop_block.IsNull() && prev_tip_hash != tip_hash) { // in case the tip has changed, update progress max - progress_end = GuessVerificationProgress(chainParams.TxData(), tip); + progress_end = chain().guessVerificationProgress(tip_hash); } } } ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI - if (pindex && fAbortRescan) { - WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, progress_current); - return ScanResult::USER_ABORT; - } else if (pindex && ShutdownRequested()) { - WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", pindex->nHeight, progress_current); - return ScanResult::USER_ABORT; + if (block_height && fAbortRescan) { + WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height.value_or(0), progress_current); + result.status = ScanResult::USER_ABORT; + } else if (block_height && ShutdownRequested()) { + WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height.value_or(0), progress_current); + result.status = ScanResult::USER_ABORT; } } - if (failed_block) { - return ScanResult::FAILURE; - } else { - return ScanResult::SUCCESS; - } + return result; } void CWallet::ReacceptWalletTransactions() @@ -4194,8 +4205,7 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, nStart = GetTimeMillis(); { WalletRescanReserver reserver(walletInstance.get()); - const CBlockIndex *stop_block, *failed_block; - if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, failed_block, stop_block, true))) { + if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(pindexRescan->GetBlockHash(), {} /* stop block */, reserver, true /* update */).status)) { InitError(_("Failed to rescan the wallet during initialization")); return nullptr; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index d8981f7385..7a541db778 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -678,8 +678,8 @@ private: void SyncMetaData(std::pair) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions. - * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */ - void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */ + void SyncTransaction(const CTransactionRef& tx, const uint256& block_hash, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* the HD chain data model (external chain counters) */ CHDChain hdChain; @@ -909,12 +909,22 @@ public: void BlockDisconnected(const std::shared_ptr& pblock) override; int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update); - enum class ScanResult { - SUCCESS, - FAILURE, - USER_ABORT + struct ScanResult { + enum { SUCCESS, FAILURE, USER_ABORT } status = SUCCESS; + + //! Hash and height of most recent block that was successfully scanned. + //! Unset if no blocks were scanned due to read errors or the chain + //! being empty. + uint256 stop_block; + Optional stop_height; + + //! Height of the most recent block that could not be scanned due to + //! read errors or pruning. Will be set if status is FAILURE, unset if + //! status is SUCCESS, and may or may not be set if status is + //! USER_ABORT. + uint256 failed_block; }; - ScanResult ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate = false); + ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate); void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main); -- cgit v1.2.3 From 44de1561aaf7556bb8ae8b582c233742ff76767d Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 8 Jan 2019 00:06:24 -0800 Subject: Remove remaining chainActive references from CWallet This commit does not change behavior. Co-authored-by: Ben Woosley --- src/wallet/wallet.cpp | 47 ++++++++++++++++++++++++++++------------------- src/wallet/wallet.h | 5 +---- 2 files changed, 29 insertions(+), 23 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4b1d010fed..bea9ebefa3 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1166,7 +1166,7 @@ void CWallet::BlockConnected(const std::shared_ptr& pblock, const TransactionRemovedFromMempool(pblock->vtx[i]); } - m_last_block_processed = pindex; + m_last_block_processed = pindex->GetBlockHash(); } void CWallet::BlockDisconnected(const std::shared_ptr& pblock) { @@ -1191,9 +1191,8 @@ void CWallet::BlockUntilSyncedToCurrentChain() { // protected by cs_wallet instead of cs_main, but as long as we need // cs_main here anyway, it's easier to just call it cs_main-protected. auto locked_chain = chain().lock(); - const CBlockIndex* initialChainTip = chainActive.Tip(); - if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) { + if (!m_last_block_processed.IsNull() && locked_chain->isPotentialTip(m_last_block_processed)) { return; } } @@ -4074,7 +4073,7 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, } auto locked_chain = chain.assumeLocked(); // Temporary. Removed in upcoming lock cleanup - walletInstance->ChainStateFlushed(chainActive.GetLocator()); + walletInstance->ChainStateFlushed(locked_chain->getLocator()); } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { // Make it impossible to disable private keys after creation InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile)); @@ -4161,57 +4160,67 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, // Try to top up keypool. No-op if the wallet is locked. walletInstance->TopUpKeyPool(); - LockAnnotation lock(::cs_main); // Temporary, for FindForkInGlobalIndex below. Removed in upcoming commit. auto locked_chain = chain.lock(); LOCK(walletInstance->cs_wallet); - CBlockIndex *pindexRescan = chainActive.Genesis(); + int rescan_height = 0; if (!gArgs.GetBoolArg("-rescan", false)) { WalletBatch batch(*walletInstance->database); CBlockLocator locator; - if (batch.ReadBestBlock(locator)) - pindexRescan = FindForkInGlobalIndex(chainActive, locator); + if (batch.ReadBestBlock(locator)) { + if (const Optional fork_height = locked_chain->findLocatorFork(locator)) { + rescan_height = *fork_height; + } + } } - walletInstance->m_last_block_processed = chainActive.Tip(); + const Optional tip_height = locked_chain->getHeight(); + if (tip_height) { + walletInstance->m_last_block_processed = locked_chain->getBlockHash(*tip_height); + } else { + walletInstance->m_last_block_processed.SetNull(); + } - if (chainActive.Tip() && chainActive.Tip() != pindexRescan) + if (tip_height && *tip_height != rescan_height) { //We can't rescan beyond non-pruned blocks, stop and throw an error //this might happen if a user uses an old wallet within a pruned node // or if he ran -disablewallet for a longer time, then decided to re-enable if (fPruneMode) { - CBlockIndex *block = chainActive.Tip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block) - block = block->pprev; + int block_height = *tip_height; + while (block_height > 0 && locked_chain->haveBlockOnDisk(block_height - 1) && rescan_height != block_height) { + --block_height; + } - if (pindexRescan != block) { + if (rescan_height != block_height) { InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)")); return nullptr; } } uiInterface.InitMessage(_("Rescanning...")); - walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); + walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height); // No need to read and scan block if block was created before // our wallet birthday (as adjusted for block time variability) - while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) { - pindexRescan = chainActive.Next(pindexRescan); + if (walletInstance->nTimeFirstKey) { + if (Optional first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height)) { + rescan_height = *first_block; + } } nStart = GetTimeMillis(); { WalletRescanReserver reserver(walletInstance.get()); - if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(pindexRescan->GetBlockHash(), {} /* stop block */, reserver, true /* update */).status)) { + if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) { InitError(_("Failed to rescan the wallet during initialization")); return nullptr; } } walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart); - walletInstance->ChainStateFlushed(chainActive.GetLocator()); + walletInstance->ChainStateFlushed(locked_chain->getLocator()); walletInstance->database->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7a541db778..fa18e3a6fa 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -95,7 +95,6 @@ static const bool DEFAULT_DISABLE_WALLET = false; //! Pre-calculated constants for input size estimation in *virtual size* static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91; -class CBlockIndex; class CCoinControl; class COutput; class CReserveKey; @@ -723,10 +722,8 @@ private: * Note that this is *not* how far we've processed, we may need some rescan * to have seen all transactions in the chain, but is only used to track * live BlockConnected callbacks. - * - * Protected by cs_main (see BlockUntilSyncedToCurrentChain) */ - const CBlockIndex* m_last_block_processed = nullptr; + uint256 m_last_block_processed; public: /* -- cgit v1.2.3