2015-02-04 16:11:44 -08:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
|
|
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2019-07-18 07:16:09 -07:00
|
|
|
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
2015-02-04 16:11:44 -08:00
|
|
|
|
|
|
|
#include "validationinterface.h"
|
|
|
|
|
2019-11-20 06:16:58 -08:00
|
|
|
#include "chainparams.h"
|
2019-11-21 12:09:06 -08:00
|
|
|
#include "init.h"
|
|
|
|
#include "main.h"
|
2019-11-20 04:52:58 -08:00
|
|
|
#include "txmempool.h"
|
2019-11-21 12:09:06 -08:00
|
|
|
#include "ui_interface.h"
|
2019-11-20 04:52:58 -08:00
|
|
|
|
|
|
|
#include <boost/thread.hpp>
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
#include <thread>
|
|
|
|
|
2020-10-01 04:09:02 -07:00
|
|
|
using namespace boost::placeholders;
|
|
|
|
|
2015-02-04 16:11:44 -08:00
|
|
|
static CMainSignals g_signals;
|
|
|
|
|
|
|
|
CMainSignals& GetMainSignals()
|
|
|
|
{
|
|
|
|
return g_signals;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
2015-05-07 07:49:00 -07:00
|
|
|
g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1));
|
2020-07-04 08:33:23 -07:00
|
|
|
g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
|
|
|
|
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
|
2019-11-21 09:00:22 -08:00
|
|
|
g_signals.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3));
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
2015-03-23 10:47:18 -07:00
|
|
|
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
2019-05-17 15:36:32 -07:00
|
|
|
g_signals.AddressForMining.connect(boost::bind(&CValidationInterface::GetAddressForMining, pwalletIn, _1));
|
2015-07-01 07:06:49 -07:00
|
|
|
g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
|
2015-02-04 16:11:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
2015-07-01 07:06:49 -07:00
|
|
|
g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
|
2019-05-17 15:36:32 -07:00
|
|
|
g_signals.AddressForMining.disconnect(boost::bind(&CValidationInterface::GetAddressForMining, pwalletIn, _1));
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
2015-03-23 10:47:18 -07:00
|
|
|
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
2019-11-21 09:00:22 -08:00
|
|
|
g_signals.ChainTip.disconnect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3));
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
|
|
|
|
g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
|
2020-07-04 08:33:23 -07:00
|
|
|
g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
|
2015-05-07 07:49:00 -07:00
|
|
|
g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1));
|
2015-02-04 16:11:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void UnregisterAllValidationInterfaces() {
|
2015-04-10 03:49:01 -07:00
|
|
|
g_signals.BlockFound.disconnect_all_slots();
|
2019-05-17 15:36:32 -07:00
|
|
|
g_signals.AddressForMining.disconnect_all_slots();
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.BlockChecked.disconnect_all_slots();
|
|
|
|
g_signals.Broadcast.disconnect_all_slots();
|
|
|
|
g_signals.Inventory.disconnect_all_slots();
|
2016-08-23 20:52:43 -07:00
|
|
|
g_signals.ChainTip.disconnect_all_slots();
|
2015-02-04 16:11:44 -08:00
|
|
|
g_signals.UpdatedTransaction.disconnect_all_slots();
|
|
|
|
g_signals.EraseTransaction.disconnect_all_slots();
|
|
|
|
g_signals.SyncTransaction.disconnect_all_slots();
|
2015-05-07 07:49:00 -07:00
|
|
|
g_signals.UpdatedBlockTip.disconnect_all_slots();
|
2015-02-04 16:11:44 -08:00
|
|
|
}
|
|
|
|
|
2020-07-04 08:33:23 -07:00
|
|
|
void SyncWithWallets(const CTransaction &tx, const CBlock *pblock, const int nHeight) {
|
|
|
|
g_signals.SyncTransaction(tx, pblock, nHeight);
|
2015-02-04 16:11:44 -08:00
|
|
|
}
|
2019-11-20 04:52:58 -08:00
|
|
|
|
2019-11-21 12:09:06 -08:00
|
|
|
struct CachedBlockData {
|
|
|
|
CBlockIndex *pindex;
|
2022-03-31 14:15:42 -07:00
|
|
|
MerkleFrontiers oldTrees;
|
2019-11-21 12:09:06 -08:00
|
|
|
std::list<CTransaction> txConflicted;
|
|
|
|
|
|
|
|
CachedBlockData(
|
|
|
|
CBlockIndex *pindex,
|
2022-03-31 14:15:42 -07:00
|
|
|
MerkleFrontiers oldTrees,
|
2019-11-21 12:09:06 -08:00
|
|
|
std::list<CTransaction> txConflicted):
|
|
|
|
pindex(pindex), oldTrees(oldTrees), txConflicted(txConflicted) {}
|
|
|
|
};
|
2019-11-20 04:52:58 -08:00
|
|
|
|
2020-02-26 10:19:33 -08:00
|
|
|
void ThreadNotifyWallets(CBlockIndex *pindexLastTip)
|
2019-11-20 04:52:58 -08:00
|
|
|
{
|
2020-02-26 13:47:22 -08:00
|
|
|
// If pindexLastTip == nullptr, the wallet is at genesis.
|
|
|
|
// However, the genesis block is not loaded synchronously.
|
|
|
|
// We need to wait for ThreadImport to finish.
|
|
|
|
while (pindexLastTip == nullptr) {
|
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
|
|
|
pindexLastTip = chainActive.Genesis();
|
|
|
|
}
|
|
|
|
MilliSleep(50);
|
|
|
|
}
|
2019-11-21 12:09:06 -08:00
|
|
|
|
2022-04-01 17:38:26 -07:00
|
|
|
// We cannot progress with wallet notification until the chain tip is no
|
|
|
|
// more than 100 blocks behind pindexLastTip. This can occur if the node
|
|
|
|
// shuts down abruptly without being able to write out chainActive; the
|
|
|
|
// node writes chain data out roughly hourly, while the wallet writes it
|
|
|
|
// every 10 minutes. We need to wait for ThreadImport to catch up.
|
|
|
|
while (true) {
|
|
|
|
boost::this_thread::interruption_point();
|
|
|
|
|
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
|
|
|
|
|
|
|
const CBlockIndex *pindexFork = chainActive.FindFork(pindexLastTip);
|
|
|
|
// We know we have the genesis block.
|
|
|
|
assert(pindexFork != nullptr);
|
|
|
|
|
|
|
|
if (pindexLastTip->nHeight < pindexFork->nHeight ||
|
|
|
|
pindexLastTip->nHeight - pindexFork->nHeight < 100)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MilliSleep(50);
|
|
|
|
}
|
|
|
|
|
2019-11-20 04:52:58 -08:00
|
|
|
while (true) {
|
|
|
|
// Run the notifier on an integer second in the steady clock.
|
|
|
|
auto now = std::chrono::steady_clock::now().time_since_epoch();
|
|
|
|
auto nextFire = std::chrono::duration_cast<std::chrono::seconds>(
|
|
|
|
now + std::chrono::seconds(1));
|
|
|
|
std::this_thread::sleep_until(
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock>(nextFire));
|
|
|
|
|
|
|
|
boost::this_thread::interruption_point();
|
|
|
|
|
2019-11-21 12:09:06 -08:00
|
|
|
auto chainParams = Params();
|
|
|
|
|
|
|
|
//
|
2019-11-20 06:16:58 -08:00
|
|
|
// Collect all the state we require
|
2019-11-21 12:09:06 -08:00
|
|
|
//
|
|
|
|
|
|
|
|
// The common ancestor between the last chain tip we notified and the
|
|
|
|
// current chain tip.
|
|
|
|
const CBlockIndex *pindexFork;
|
|
|
|
// The stack of blocks we will notify as having been connected.
|
|
|
|
// Pushed in reverse, popped in order.
|
|
|
|
std::vector<CachedBlockData> blockStack;
|
|
|
|
// Transactions that have been recently conflicted out of the mempool.
|
2019-11-21 12:46:00 -08:00
|
|
|
std::pair<std::map<CBlockIndex*, std::list<CTransaction>>, uint64_t> recentlyConflicted;
|
2019-11-21 12:09:06 -08:00
|
|
|
// Transactions that have been recently added to the mempool.
|
|
|
|
std::pair<std::vector<CTransaction>, uint64_t> recentlyAdded;
|
|
|
|
|
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
|
|
|
|
|
|
|
// Figure out the path from the last block we notified to the
|
|
|
|
// current chain tip.
|
|
|
|
CBlockIndex *pindex = chainActive.Tip();
|
|
|
|
pindexFork = chainActive.FindFork(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) {
|
2022-03-31 14:15:42 -07:00
|
|
|
MerkleFrontiers oldFrontiers;
|
2019-11-21 12:09:06 -08:00
|
|
|
// Get the Sprout commitment tree as of the start of this block.
|
2022-03-31 14:15:42 -07:00
|
|
|
assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, oldFrontiers.sprout));
|
2019-11-21 12:09:06 -08:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
if (chainParams.GetConsensus().NetworkUpgradeActive(
|
|
|
|
pindex->pprev->nHeight, Consensus::UPGRADE_SAPLING)) {
|
|
|
|
assert(pcoinsTip->GetSaplingAnchorAt(
|
2022-03-31 14:15:42 -07:00
|
|
|
pindex->pprev->hashFinalSaplingRoot, oldFrontiers.sapling));
|
2019-11-21 12:09:06 -08:00
|
|
|
} else {
|
2022-03-31 14:15:42 -07:00
|
|
|
assert(pcoinsTip->GetSaplingAnchorAt(SaplingMerkleTree::empty_root(), oldFrontiers.sapling));
|
2019-11-21 12:09:06 -08:00
|
|
|
}
|
|
|
|
|
2022-03-31 14:15:42 -07:00
|
|
|
// Get the Orchard Merkle frontier as of the start of this block.
|
|
|
|
// We can get this from the `hashFinalOrchardRoot` of the last block
|
|
|
|
// However, this is only reliable if the last block was on or after
|
|
|
|
// the Orchard activation height. Otherwise, the last anchor was the
|
|
|
|
// empty root.
|
|
|
|
if (chainParams.GetConsensus().NetworkUpgradeActive(
|
|
|
|
pindex->pprev->nHeight, Consensus::UPGRADE_NU5)) {
|
|
|
|
assert(pcoinsTip->GetOrchardAnchorAt(
|
|
|
|
pindex->pprev->hashFinalOrchardRoot, oldFrontiers.orchard));
|
|
|
|
} else {
|
|
|
|
assert(pcoinsTip->GetOrchardAnchorAt(
|
|
|
|
OrchardMerkleFrontier::empty_root(), oldFrontiers.orchard));
|
2019-11-21 12:09:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
blockStack.emplace_back(
|
|
|
|
pindex,
|
2022-03-31 14:15:42 -07:00
|
|
|
oldFrontiers,
|
2019-11-21 12:46:00 -08:00
|
|
|
recentlyConflicted.first.at(pindex));
|
2019-11-21 12:09:06 -08:00
|
|
|
|
|
|
|
pindex = pindex->pprev;
|
|
|
|
}
|
2019-11-20 06:16:58 -08:00
|
|
|
|
2019-11-21 12:09:06 -08:00
|
|
|
recentlyAdded = mempool.DrainRecentlyAdded();
|
|
|
|
}
|
2019-11-20 06:16:58 -08:00
|
|
|
|
2019-11-21 12:09:06 -08:00
|
|
|
//
|
2019-11-20 06:16:58 -08:00
|
|
|
// Execute wallet logic based on the collected state. We MUST NOT take
|
2019-12-17 09:11:05 -08:00
|
|
|
// 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.
|
2019-11-21 12:09:06 -08:00
|
|
|
//
|
|
|
|
|
|
|
|
// 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) {
|
2020-07-04 08:33:23 -07:00
|
|
|
SyncWithWallets(tx, NULL, pindexLastTip->nHeight);
|
2019-11-21 12:09:06 -08:00
|
|
|
}
|
|
|
|
// Update cached incremental witnesses
|
2021-08-25 10:46:58 -07:00
|
|
|
// This will take the cs_main lock in order to obtain the CBlockLocator
|
2021-08-31 11:56:25 -07:00
|
|
|
// used by `SetBestChain`, but as that write only occurs once every
|
2021-08-25 10:46:58 -07:00
|
|
|
// WRITE_WITNESS_INTERVAL * 1000000 microseconds this should not be
|
|
|
|
// exploitable as a timing channel.
|
2020-10-20 17:44:15 -07:00
|
|
|
GetMainSignals().ChainTip(pindexLastTip, &block, std::nullopt);
|
2019-11-21 12:09:06 -08:00
|
|
|
|
|
|
|
// 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) {
|
2020-07-04 08:33:23 -07:00
|
|
|
SyncWithWallets(tx, NULL, blockData.pindex->nHeight + 1);
|
2019-11-21 12:09:06 -08:00
|
|
|
}
|
|
|
|
// ... and about transactions that got confirmed:
|
|
|
|
for (const CTransaction &tx : block.vtx) {
|
2020-07-04 08:33:23 -07:00
|
|
|
SyncWithWallets(tx, &block, blockData.pindex->nHeight);
|
2019-11-21 12:09:06 -08:00
|
|
|
}
|
|
|
|
// Update cached incremental witnesses
|
2021-08-25 10:46:58 -07:00
|
|
|
// This will take the cs_main lock in order to obtain the CBlockLocator
|
2021-08-31 11:56:25 -07:00
|
|
|
// used by `SetBestChain`, but as that write only occurs once every
|
2021-08-25 10:46:58 -07:00
|
|
|
// WRITE_WITNESS_INTERVAL * 1000000 microseconds this should not be
|
|
|
|
// exploitable as a timing channel.
|
2019-11-21 12:09:06 -08:00
|
|
|
GetMainSignals().ChainTip(blockData.pindex, &block, blockData.oldTrees);
|
|
|
|
|
|
|
|
// This block is done!
|
|
|
|
pindexLastTip = blockData.pindex;
|
|
|
|
}
|
2019-11-20 06:16:58 -08:00
|
|
|
|
2019-11-21 12:09:06 -08:00
|
|
|
// Notify transactions in the mempool
|
2019-11-20 06:16:58 -08:00
|
|
|
for (auto tx : recentlyAdded.first) {
|
|
|
|
try {
|
2020-07-04 08:33:23 -07:00
|
|
|
SyncWithWallets(tx, NULL, pindexLastTip->nHeight + 1);
|
2019-11-20 06:16:58 -08:00
|
|
|
} catch (const boost::thread_interrupted&) {
|
|
|
|
throw;
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
PrintExceptionContinue(&e, "ThreadNotifyWallets()");
|
|
|
|
} catch (...) {
|
|
|
|
PrintExceptionContinue(NULL, "ThreadNotifyWallets()");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 12:09:06 -08:00
|
|
|
// 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") {
|
2020-07-10 10:34:53 -07:00
|
|
|
SetChainNotifiedSequence(chainParams, recentlyConflicted.second);
|
2019-11-20 06:16:58 -08:00
|
|
|
mempool.SetNotifiedSequence(recentlyAdded.second);
|
|
|
|
}
|
2019-11-20 04:52:58 -08:00
|
|
|
}
|
|
|
|
}
|