diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index f3bca6205..aa66ef7c7 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://www.opensource.org/licenses/mit-license.php . #include "validationinterface.h" @@ -28,15 +28,18 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.RescanWallet.connect(boost::bind(&CValidationInterface::RescanWallet, pwalletIn)); g_signals.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); + g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); + g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { + g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); + g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); @@ -45,11 +48,12 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); - g_signals.RescanWallet.disconnect(boost::bind(&CValidationInterface::RescanWallet, pwalletIn)); g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1)); } void UnregisterAllValidationInterfaces() { + g_signals.BlockFound.disconnect_all_slots(); + g_signals.ScriptForMining.disconnect_all_slots(); g_signals.BlockChecked.disconnect_all_slots(); g_signals.Broadcast.disconnect_all_slots(); g_signals.Inventory.disconnect_all_slots(); @@ -58,7 +62,6 @@ void UnregisterAllValidationInterfaces() { g_signals.UpdatedTransaction.disconnect_all_slots(); g_signals.EraseTransaction.disconnect_all_slots(); g_signals.SyncTransaction.disconnect_all_slots(); - g_signals.RescanWallet.disconnect_all_slots(); g_signals.UpdatedBlockTip.disconnect_all_slots(); } @@ -129,13 +132,119 @@ void ThreadNotifyWallets(CBlockIndex *pindexLastTip) // Fetch recently-conflicted transactions. These will include any // block that has been connected since the last cycle, but we only // notify for the conflicts created by the current active chain. + recentlyConflicted = DrainRecentlyConflicted(); + + // Iterate backwards over the connected blocks we need to notify. + while (pindex && pindex != pindexFork) { + // Get the Sprout commitment tree as of the start of this block. + SproutMerkleTree oldSproutTree; + assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, oldSproutTree)); + + // Get the Sapling commitment tree as of the start of this block. + // We can get this from the `hashFinalSaplingRoot` of the last block + // However, this is only reliable if the last block was on or after + // the Sapling activation height. Otherwise, the last anchor was the + // empty root. + SaplingMerkleTree oldSaplingTree; + if (chainParams.GetConsensus().NetworkUpgradeActive( + pindex->pprev->nHeight, Consensus::UPGRADE_SAPLING)) { + assert(pcoinsTip->GetSaplingAnchorAt( + pindex->pprev->hashFinalSaplingRoot, oldSaplingTree)); + } else { + assert(pcoinsTip->GetSaplingAnchorAt(SaplingMerkleTree::empty_root(), oldSaplingTree)); + } + + blockStack.emplace_back( + pindex, + std::make_pair(oldSproutTree, oldSaplingTree), + recentlyConflicted.first.at(pindex)); + + pindex = pindex->pprev; + } + + recentlyAdded = mempool.DrainRecentlyAdded(); + } + + // + // Execute wallet logic based on the collected state. We MUST NOT take + // the cs_main or mempool.cs locks again until after the next sleep; + // doing so introduces a locking side-channel between this code and the + // network message processing thread. + // + + // Notify block disconnects + while (pindexLastTip && pindexLastTip != pindexFork) { + // Read block from disk. + CBlock block; + if (!ReadBlockFromDisk(block, pindexLastTip, chainParams.GetConsensus())) { + LogPrintf("*** %s\n", "Failed to read block while notifying wallets of block disconnects"); + uiInterface.ThreadSafeMessageBox( + _("Error: A fatal internal error occurred, see debug.log for details"), + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + } + + // Let wallets know transactions went from 1-confirmed to + // 0-confirmed or conflicted: + for (const CTransaction &tx : block.vtx) { + SyncWithWallets(tx, NULL); + } + // Update cached incremental witnesses + GetMainSignals().ChainTip(pindexLastTip, &block, boost::none); + + // On to the next block! + pindexLastTip = pindexLastTip->pprev; + } + + // Notify block connections + while (!blockStack.empty()) { + auto blockData = blockStack.back(); + blockStack.pop_back(); + + // Read block from disk. + CBlock block; + if (!ReadBlockFromDisk(block, blockData.pindex, chainParams.GetConsensus())) { + LogPrintf("*** %s\n", "Failed to read block while notifying wallets of block connects"); + uiInterface.ThreadSafeMessageBox( + _("Error: A fatal internal error occurred, see debug.log for details"), + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + } + + // Tell wallet about transactions that went from mempool + // to conflicted: + for (const CTransaction &tx : blockData.txConflicted) { + SyncWithWallets(tx, NULL); + } + // ... and about transactions that got confirmed: + for (const CTransaction &tx : block.vtx) { + SyncWithWallets(tx, &block); + } + // Update cached incremental witnesses + GetMainSignals().ChainTip(blockData.pindex, &block, blockData.oldTrees); + + // This block is done! + pindexLastTip = blockData.pindex; + } + + // Notify transactions in the mempool + for (auto tx : recentlyAdded.first) { + try { + SyncWithWallets(tx, NULL); + } catch (const boost::thread_interrupted&) { + throw; + } catch (const std::exception& e) { + PrintExceptionContinue(&e, "ThreadNotifyWallets()"); + } catch (...) { + PrintExceptionContinue(NULL, "ThreadNotifyWallets()"); + } + } + + // Update the notified sequence numbers. We only need this in regtest mode, + // and should not lock on cs or cs_main here otherwise. + if (chainParams.NetworkIDString() == "regtest") { + SetChainNotifiedSequence(recentlyConflicted.second); + mempool.SetNotifiedSequence(recentlyAdded.second); } } } -void EraseFromWallets(const uint256 &hash) { - g_signals.EraseTransaction(hash); -} - -void RescanWallets() { - g_signals.RescanWallet(); -}