Hush Full Node software. We were censored from Github, this is where all development happens now. https://hush.is
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

252 lines
11 KiB

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2016-2024 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html .
#include "validationinterface.h"
#include "chainparams.h"
#include "init.h"
#include "main.h"
#include "txmempool.h"
#include "ui_interface.h"
#include <boost/thread.hpp>
#include <chrono>
#include <thread>
#include <functional>
static CMainSignals g_signals;
CMainSignals& GetMainSignals()
{
return g_signals;
}
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1));
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.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3));
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
if(GetArg("-resendtx", true)) {
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
} else {
LogPrintf("%s: automatic resending of wallet transactions disabled\n", __func__);
}
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
//g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
//g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
if(GetArg("-resendtx", true)) {
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
}
g_signals.ChainTip.disconnect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3));
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
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.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.ChainTip.disconnect_all_slots();
g_signals.SetBestChain.disconnect_all_slots();
g_signals.UpdatedTransaction.disconnect_all_slots();
g_signals.EraseTransaction.disconnect_all_slots();
g_signals.SyncTransaction.disconnect_all_slots();
g_signals.UpdatedBlockTip.disconnect_all_slots();
}
void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) {
g_signals.SyncTransaction(tx, pblock);
}
struct CachedBlockData {
CBlockIndex *pindex;
std::pair<SproutMerkleTree, SaplingMerkleTree> oldTrees;
std::list<CTransaction> txConflicted;
CachedBlockData(
CBlockIndex *pindex,
std::pair<SproutMerkleTree, SaplingMerkleTree> oldTrees,
std::list<CTransaction> txConflicted):
pindex(pindex), oldTrees(oldTrees), txConflicted(txConflicted) {}
};
void ThreadNotifyWallets(CBlockIndex *pindexLastTip)
{
// 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);
}
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();
auto chainParams = Params();
//
// Collect all the state we require
//
// 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.
std::pair<std::map<CBlockIndex*, std::list<CTransaction>>, uint64_t> recentlyConflicted;
// 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) {
// Get the Sprout commitment tree as of the start of this block.
SproutMerkleTree 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;
const bool sapling = pindex->pprev->GetHeight() >= 1 ? true : false;
if (sapling) { // NetworkUpgradeActive(pindex->pprev->GetHeight(),Params().GetConsensus(), 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,1)) {
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, 1)) {
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);
}
}
}