Auto merge of #4252 - str4d:wallet-interface-refactor, r=str4d

Wallet interface refactor

This refactors the logic introduced in #4144 to improve the separation between the node and wallet. The notifier thread now lives next in `src/validationinterface.cpp` directly next to the existing `CMainSignals` node-wallet interface.

Part of #3877.
This commit is contained in:
Homu 2019-12-18 22:48:26 +00:00
commit 0a31c163c6
24 changed files with 296 additions and 97 deletions

View File

@ -56,6 +56,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
spends2_id = [ self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw ]
blocks.extend(self.nodes[0].generate(1))
self.sync_all()
# mempool should be empty, all txns confirmed
assert_equal(set(self.nodes[0].getrawmempool()), set())
@ -76,6 +77,8 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
# Generate another block, they should all get mined
self.nodes[0].generate(1)
self.sync_all()
# mempool should be empty, all txns confirmed
assert_equal(set(self.nodes[0].getrawmempool()), set())
for txid in spends1_id+spends2_id:

View File

@ -62,6 +62,7 @@ class MergeToAddressHelper:
do_not_shield_taddr = test.nodes[0].getnewaddress()
test.nodes[0].generate(4)
test.sync_all()
walletinfo = test.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 50)
assert_equal(walletinfo['balance'], 0)

View File

@ -37,6 +37,7 @@ class PaymentDisclosureTest (BitcoinTestFramework):
print "Mining blocks..."
self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 40)
assert_equal(walletinfo['balance'], 0)

View File

@ -19,8 +19,6 @@ from test_framework.util import (
class ShorterBlockTimes(BitcoinTestFramework):
def setup_nodes(self):
return start_nodes(4, self.options.tmpdir, [[
'-nuparams=5ba81b19:0', # Overwinter
'-nuparams=76b809bb:0', # Sapling
'-nuparams=2bb40e60:106', # Blossom
]] * 4)

View File

@ -46,7 +46,8 @@ def str_to_b64str(string):
def sync_blocks(rpc_connections, wait=1):
"""
Wait until everybody has the same block count
Wait until everybody has the same block count, and has notified
all internal listeners of them
"""
while True:
counts = [ x.getblockcount() for x in rpc_connections ]
@ -54,6 +55,14 @@ def sync_blocks(rpc_connections, wait=1):
break
time.sleep(wait)
# Now that the block counts are in sync, wait for the internal
# notifications to finish
while True:
notified = [ x.getblockchaininfo()['fullyNotified'] for x in rpc_connections ]
if notified == [ True ] * len(notified):
break
time.sleep(wait)
def sync_mempools(rpc_connections, wait=1):
"""
Wait until everybody has the same transactions in their memory

View File

@ -32,6 +32,7 @@ class WalletTest (BitcoinTestFramework):
print "Mining blocks..."
self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 40)

View File

@ -48,6 +48,7 @@ class Wallet1941RegressionTest (BitcoinTestFramework):
self.nodes[0].setmocktime(starttime)
self.nodes[0].generate(101)
self.sync_all()
mytaddr = get_coinbase_address(self.nodes[0])
myzaddr = self.nodes[0].z_getnewaddress('sprout')
@ -66,6 +67,7 @@ class Wallet1941RegressionTest (BitcoinTestFramework):
self.nodes[0].generate(1)
self.nodes[0].setmocktime(starttime + 9000)
self.nodes[0].generate(1)
self.sync_all()
# Confirm the balance on node 0.
resp = self.nodes[0].z_getbalance(myzaddr)

View File

@ -29,6 +29,7 @@ class WalletAnchorForkTest (BitcoinTestFramework):
def run_test (self):
print "Mining blocks..."
self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 40)

View File

@ -39,6 +39,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
self.nodes[0].generate(1)
self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 50)
assert_equal(walletinfo['balance'], 0)

View File

@ -47,6 +47,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
print "Mining blocks..."
self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 40)

View File

@ -93,6 +93,7 @@ class WalletBackupTest(BitcoinTestFramework):
# Must sync mempools before mining.
sync_mempools(self.nodes)
self.nodes[3].generate(1)
self.sync_all()
# As above, this mirrors the original bash test.
def start_three(self):

View File

@ -32,6 +32,7 @@ class JoinSplitTest(BitcoinTestFramework):
shield_tx = self.nodes[0].signrawtransaction(joinsplit_result["rawtxn"])
self.nodes[0].sendrawtransaction(shield_tx["hex"])
self.nodes[0].generate(1)
self.sync_all()
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"])
assert_equal(receive_result["exists"], True)
@ -41,6 +42,7 @@ class JoinSplitTest(BitcoinTestFramework):
addrtest = self.nodes[0].getnewaddress()
for xx in range(0,10):
self.nodes[0].generate(1)
self.sync_all()
for x in range(0,50):
self.nodes[0].sendtoaddress(addrtest, 0.01);
@ -49,6 +51,7 @@ class JoinSplitTest(BitcoinTestFramework):
self.nodes[0].sendrawtransaction(joinsplit_result["rawtxn"])
self.nodes[0].generate(1)
self.sync_all()
print "Done!"
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"])

View File

@ -39,15 +39,6 @@ class ZMQTest(BitcoinTestFramework):
self.sync_all()
print "listen..."
msg = self.zmqSubSocket.recv_multipart()
topic = msg[0]
assert_equal(topic, b"hashtx")
body = msg[1]
nseq = msg[2]
[nseq] # hush pyflakes
msgSequence = struct.unpack('<I', msg[-1])[-1]
assert_equal(msgSequence, 0) # must be sequence 0 on hashtx
msg = self.zmqSubSocket.recv_multipart()
topic = msg[0]
body = msg[1]
@ -57,6 +48,15 @@ class ZMQTest(BitcoinTestFramework):
assert_equal(genhashes[0], blkhash) #blockhash from generate must be equal to the hash received over zmq
msg = self.zmqSubSocket.recv_multipart()
topic = msg[0]
assert_equal(topic, b"hashtx")
body = msg[1]
nseq = msg[2]
[nseq] # hush pyflakes
msgSequence = struct.unpack('<I', msg[-1])[-1]
assert_equal(msgSequence, 0) # must be sequence 0 on hashtx
n = 10
genhashes = self.nodes[1].generate(n)
self.sync_all()

View File

@ -676,22 +676,6 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
}
void ThreadNotifyRecentlyAdded()
{
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();
mempool.NotifyRecentlyAdded();
}
}
bool InitExperimentalMode()
{
fExperimentalMode = GetBoolArg("-experimentalfeatures", false);
@ -1868,8 +1852,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#endif
// Start the thread that notifies listeners of transactions that have been
// recently added to the mempool.
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "txnotify", &ThreadNotifyRecentlyAdded));
// recently added to the mempool, or have been added to or removed from the
// chain.
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "txnotify", &ThreadNotifyWallets));
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler);

View File

@ -3012,18 +3012,9 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara
// Update chainActive and related variables.
UpdateTip(pindexDelete->pprev, chainparams);
// Get the current commitment tree
SproutMerkleTree newSproutTree;
SaplingMerkleTree newSaplingTree;
assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), newSproutTree));
assert(pcoinsTip->GetSaplingAnchorAt(pcoinsTip->GetBestAnchor(SAPLING), newSaplingTree));
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
SyncWithWallets(tx, NULL);
}
// Update cached incremental witnesses
GetMainSignals().ChainTip(pindexDelete, &block, newSproutTree, newSaplingTree, false);
// Updates to connected wallets are triggered by ThreadNotifyWallets
return true;
}
@ -3033,6 +3024,11 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
// Protected by cs_main
std::map<CBlockIndex*, std::list<CTransaction>> recentlyConflictedTxs;
uint64_t nRecentlyConflictedSequence = 0;
uint64_t nNotifiedSequence = 0;
/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
@ -3049,11 +3045,6 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
return AbortNode(state, "Failed to read block");
pblock = &block;
}
// Get the current commitment tree
SproutMerkleTree oldSproutTree;
SaplingMerkleTree oldSaplingTree;
assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), oldSproutTree));
assert(pcoinsTip->GetSaplingAnchorAt(pcoinsTip->GetBestAnchor(SAPLING), oldSaplingTree));
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
@ -3080,7 +3071,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool.
list<CTransaction> txConflicted;
std::list<CTransaction> txConflicted;
mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload(chainparams));
// Remove transactions that expire at new block height from mempool
@ -3088,17 +3079,11 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
// Update chainActive & related variables.
UpdateTip(pindexNew, chainparams);
// Tell wallet about transactions that went from mempool
// to conflicted:
BOOST_FOREACH(const CTransaction &tx, txConflicted) {
SyncWithWallets(tx, NULL);
}
// ... and about transactions that got confirmed:
BOOST_FOREACH(const CTransaction &tx, pblock->vtx) {
SyncWithWallets(tx, pblock);
}
// Update cached incremental witnesses
GetMainSignals().ChainTip(pindexNew, pblock, oldSproutTree, oldSaplingTree, true);
// Cache the conflicted transactions for subsequent notification.
// Updates to connected wallets are triggered by ThreadNotifyWallets
recentlyConflictedTxs.insert(std::make_pair(pindexNew, txConflicted));
nRecentlyConflictedSequence += 1;
EnforceNodeDeprecation(pindexNew->nHeight);
@ -3108,6 +3093,31 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
return true;
}
std::pair<std::map<CBlockIndex*, std::list<CTransaction>>, uint64_t> DrainRecentlyConflicted()
{
uint64_t recentlyConflictedSequence;
std::map<CBlockIndex*, std::list<CTransaction>> txs;
{
LOCK(cs_main);
recentlyConflictedSequence = nRecentlyConflictedSequence;
txs.swap(recentlyConflictedTxs);
}
return std::make_pair(txs, recentlyConflictedSequence);
}
void SetChainNotifiedSequence(uint64_t recentlyConflictedSequence) {
assert(Params().NetworkIDString() == "regtest");
LOCK(cs_main);
nNotifiedSequence = recentlyConflictedSequence;
}
bool ChainIsFullyNotified() {
assert(Params().NetworkIDString() == "regtest");
LOCK(cs_main);
return nRecentlyConflictedSequence == nNotifiedSequence;
}
/**
* Return the tip of the chain with the most work in it, that isn't
* known to be invalid (it's however far from certain to be valid).

View File

@ -525,4 +525,8 @@ uint64_t CalculateCurrentUsage();
*/
CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight);
std::pair<std::map<CBlockIndex*, std::list<CTransaction>>, uint64_t> DrainRecentlyConflicted();
void SetChainNotifiedSequence(uint64_t recentlyConflictedSequence);
bool ChainIsFullyNotified();
#endif // BITCOIN_MAIN_H

View File

@ -1070,6 +1070,11 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("pruneheight", block->nHeight));
}
if (Params().NetworkIDString() == "regtest") {
obj.push_back(Pair("fullyNotified", ChainIsFullyNotified()));
}
return obj;
}

View File

@ -734,7 +734,7 @@ bool CTxMemPool::nullifierExists(const uint256& nullifier, ShieldedType type) co
}
}
void CTxMemPool::NotifyRecentlyAdded()
std::pair<std::vector<CTransaction>, uint64_t> CTxMemPool::DrainRecentlyAdded()
{
uint64_t recentlyAddedSequence;
std::vector<CTransaction> txs;
@ -747,28 +747,13 @@ void CTxMemPool::NotifyRecentlyAdded()
mapRecentlyAddedTx.clear();
}
// A race condition can occur here between these SyncWithWallets calls, and
// the ones triggered by block logic (in ConnectTip and DisconnectTip). It
// is harmless because calling SyncWithWallets(_, NULL) does not alter the
// wallet transaction's block information.
for (auto tx : txs) {
try {
SyncWithWallets(tx, NULL);
} catch (const boost::thread_interrupted&) {
throw;
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "CTxMemPool::NotifyRecentlyAdded()");
} catch (...) {
PrintExceptionContinue(NULL, "CTxMemPool::NotifyRecentlyAdded()");
}
}
return std::make_pair(txs, recentlyAddedSequence);
}
// Update the notified sequence number. We only need this in regtest mode,
// and should not lock on cs after calling SyncWithWallets otherwise.
if (Params().NetworkIDString() == "regtest") {
LOCK(cs);
nNotifiedSequence = recentlyAddedSequence;
}
void CTxMemPool::SetNotifiedSequence(uint64_t recentlyAddedSequence) {
assert(Params().NetworkIDString() == "regtest");
LOCK(cs);
nNotifiedSequence = recentlyAddedSequence;
}
bool CTxMemPool::IsFullyNotified() {

View File

@ -224,7 +224,8 @@ public:
bool nullifierExists(const uint256& nullifier, ShieldedType type) const;
void NotifyRecentlyAdded();
std::pair<std::vector<CTransaction>, uint64_t> DrainRecentlyAdded();
void SetNotifiedSequence(uint64_t recentlyAddedSequence);
bool IsFullyNotified();
unsigned long size()

View File

@ -5,6 +5,17 @@
#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>
static CMainSignals g_signals;
CMainSignals& GetMainSignals()
@ -17,7 +28,7 @@ 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.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3, _4, _5));
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));
@ -32,7 +43,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
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));
g_signals.ChainTip.disconnect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3, _4, _5));
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));
@ -57,3 +68,178 @@ void UnregisterAllValidationInterfaces() {
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 = nullptr;
{
LOCK(cs_main);
pindexLastTip = chainActive.Tip();
}
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;
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);
}
}
}

View File

@ -28,15 +28,13 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn);
void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */
void UnregisterAllValidationInterfaces();
/** Push an updated transaction to all registered wallets */
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL);
class CValidationInterface {
protected:
virtual void UpdatedBlockTip(const CBlockIndex *pindex) {}
virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {}
virtual void EraseFromWallet(const uint256 &hash) {}
virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree, bool added) {}
virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>> added) {}
virtual void SetBestChain(const CBlockLocator &locator) {}
virtual void UpdatedTransaction(const uint256 &hash) {}
virtual void Inventory(const uint256 &hash) {}
@ -59,7 +57,7 @@ struct CMainSignals {
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
/** Notifies listeners of a change to the tip of the active block chain. */
boost::signals2::signal<void (const CBlockIndex *, const CBlock *, SproutMerkleTree, SaplingMerkleTree, bool)> ChainTip;
boost::signals2::signal<void (const CBlockIndex *, const CBlock *, boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>>)> ChainTip;
/** Notifies listeners of a new active block chain. */
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
/** Notifies listeners about an inventory item being seen on the network. */
@ -76,4 +74,6 @@ struct CMainSignals {
CMainSignals& GetMainSignals();
void ThreadNotifyWallets();
#endif // BITCOIN_VALIDATIONINTERFACE_H

View File

@ -571,12 +571,10 @@ void CWallet::ChainTipAdded(const CBlockIndex *pindex,
void CWallet::ChainTip(const CBlockIndex *pindex,
const CBlock *pblock,
SproutMerkleTree sproutTree,
SaplingMerkleTree saplingTree,
bool added)
boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>> added)
{
if (added) {
ChainTipAdded(pindex, pblock, sproutTree, saplingTree);
ChainTipAdded(pindex, pblock, added->first, added->second);
// Prevent migration transactions from being created when node is syncing after launch,
// and also when node wakes up from suspension/hibernation and incoming blocks are old.
if (!IsInitialBlockDownload(Params()) &&

View File

@ -1227,7 +1227,10 @@ public:
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree, bool added);
void ChainTip(
const CBlockIndex *pindex,
const CBlock *pblock,
boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>> added);
void RunSaplingMigration(int blockHeight);
void AddPendingSaplingMigrationTx(const CTransaction& tx);
/** Saves witness caches and best block locator to disk. */

View File

@ -350,7 +350,7 @@ double benchmark_increment_sprout_note_witnesses(size_t nTxs)
index1.nHeight = 1;
// Increment to get transactions witnessed
wallet.ChainTip(&index1, &block1, sproutTree, saplingTree, true);
wallet.ChainTip(&index1, &block1, std::make_pair(sproutTree, saplingTree));
// Second block
CBlock block2;
@ -366,7 +366,7 @@ double benchmark_increment_sprout_note_witnesses(size_t nTxs)
struct timeval tv_start;
timer_start(tv_start);
wallet.ChainTip(&index2, &block2, sproutTree, saplingTree, true);
wallet.ChainTip(&index2, &block2, std::make_pair(sproutTree, saplingTree));
return timer_stop(tv_start);
}
@ -412,7 +412,7 @@ double benchmark_increment_sapling_note_witnesses(size_t nTxs)
index1.nHeight = 1;
// Increment to get transactions witnessed
wallet.ChainTip(&index1, &block1, sproutTree, saplingTree, true);
wallet.ChainTip(&index1, &block1, std::make_pair(sproutTree, saplingTree));
// Second block
CBlock block2;
@ -428,7 +428,7 @@ double benchmark_increment_sapling_note_witnesses(size_t nTxs)
struct timeval tv_start;
timer_start(tv_start);
wallet.ChainTip(&index2, &block2, sproutTree, saplingTree, true);
wallet.ChainTip(&index2, &block2, std::make_pair(sproutTree, saplingTree));
return timer_stop(tv_start);
}