diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index 1cbbd2cd5..faa97d461 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -54,6 +54,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()) @@ -74,6 +75,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: diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index b41c4b451..78c66089a 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -60,6 +60,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 diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index 5d221a28c..0268dd292 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -31,6 +31,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) diff --git a/qa/rpc-tests/wallet_1941.py b/qa/rpc-tests/wallet_1941.py index d70b514fc..6f90f33bd 100755 --- a/qa/rpc-tests/wallet_1941.py +++ b/qa/rpc-tests/wallet_1941.py @@ -45,6 +45,7 @@ class Wallet1941RegressionTest (BitcoinTestFramework): self.nodes[0].setmocktime(starttime) self.nodes[0].generate(101) + self.sync_all() mytaddr = self.nodes[0].getnewaddress() # where coins were mined myzaddr = self.nodes[0].z_getnewaddress() @@ -63,6 +64,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) diff --git a/qa/rpc-tests/wallet_anchorfork.py b/qa/rpc-tests/wallet_anchorfork.py index 0e2d19385..1cb16e152 100755 --- a/qa/rpc-tests/wallet_anchorfork.py +++ b/qa/rpc-tests/wallet_anchorfork.py @@ -27,6 +27,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) diff --git a/qa/rpc-tests/wallet_shieldcoinbase.py b/qa/rpc-tests/wallet_shieldcoinbase.py index d8366c81d..f325b29ca 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase.py +++ b/qa/rpc-tests/wallet_shieldcoinbase.py @@ -44,6 +44,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework): print "Mining blocks..." self.nodes[0].generate(1) + self.sync_all() do_not_shield_taddr = self.nodes[0].getnewaddress() self.nodes[0].generate(4) diff --git a/qa/rpc-tests/zcjoinsplit.py b/qa/rpc-tests/zcjoinsplit.py index 7e5aba6e3..b3ca745f8 100755 --- a/qa/rpc-tests/zcjoinsplit.py +++ b/qa/rpc-tests/zcjoinsplit.py @@ -30,6 +30,7 @@ class JoinSplitTest(BitcoinTestFramework): protect_tx = self.nodes[0].signrawtransaction(joinsplit_result["rawtxn"]) self.nodes[0].sendrawtransaction(protect_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) @@ -39,6 +40,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); @@ -47,6 +49,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"]) diff --git a/qa/rpc-tests/zmq_test.py b/qa/rpc-tests/zmq_test.py index d70e73114..dcb899861 100755 --- a/qa/rpc-tests/zmq_test.py +++ b/qa/rpc-tests/zmq_test.py @@ -37,15 +37,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(' 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( - now + std::chrono::seconds(1)); - std::this_thread::sleep_until( - std::chrono::time_point(nextFire)); - - boost::this_thread::interruption_point(); - - mempool.NotifyRecentlyAdded(); - } -} - /** Sanity checks * Ensure that Hush is running in a usable environment with all * necessary library support. @@ -2088,6 +2072,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif // ENABLE_MINING + // Start the thread that notifies listeners of transactions that have been + // recently added to the mempool, or have been added to or removed from the + // chain. We perform this before step 10 (import blocks) so that the + // original value of chainActive.Tip(), which corresponds with the wallet's + // view of the chaintip, is passed to ThreadNotifyWallets before the chain + // tip changes again. + boost::function threadnotifywallets = boost::bind(&ThreadNotifyWallets, chainActive.Tip()); + threadGroup.create_thread( + boost::bind(&TraceThread>, "txnotify", threadnotifywallets) + ); + // ********************************************************* Step 9: data directory maintenance // if pruning, unset the service bit and perform the initial blockstore prune @@ -2148,17 +2143,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); LogPrintf("nBestHeight = %d\n", chainActive.Height()); #ifdef ENABLE_WALLET - RescanWallets(); - LogPrintf("setKeyPool.size() = %u\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); LogPrintf("mapWallet.size() = %u\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); #endif - // Start the thread that notifies listeners of transactions that have been - // recently added to the mempool. - threadGroup.create_thread(boost::bind(&TraceThread, "txnotify", &ThreadNotifyRecentlyAdded)); - if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); diff --git a/src/main.cpp b/src/main.cpp index 2e0394779..af8191782 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2060,8 +2060,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } } } - // This should be here still? - //SyncWithWallets(tx, NULL); return true; } @@ -4068,21 +4066,8 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); - // 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: - std::vector TxToRemove; - for (int i = 0; i < block.vtx.size(); i++) - { - CTransaction &tx = block.vtx[i]; - SyncWithWallets(tx, NULL); - } - // Update cached incremental witnesses - GetMainSignals().ChainTip(pindexDelete, &block, newSproutTree, newSaplingTree, false); + // Updates to connected wallets are triggered by ThreadNotifyWallets + return true; } @@ -4156,6 +4141,11 @@ static int64_t nTimeFlush = 0; static int64_t nTimeChainState = 0; static int64_t nTimePostConnect = 0; +// Protected by cs_main +std::map> 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. @@ -4175,14 +4165,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * } KOMODO_CONNECTING = (int32_t)pindexNew->GetHeight(); //fprintf(stderr,"%s connecting ht.%d maxsize.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pindexNew->GetHeight(),MAX_BLOCK_SIZE(pindexNew->GetHeight()),(int32_t)::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); - // Get the current commitment tree - SproutMerkleTree oldSproutTree; - SaplingMerkleTree oldSaplingTree; - if ( KOMODO_NSPV_FULLNODE ) - { - 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; @@ -4221,7 +4204,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * 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 txConflicted; + std::list txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->GetHeight(), txConflicted, !IsInitialBlockDownload()); // Remove transactions that expire at new block height from mempool @@ -4231,18 +4214,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * UpdateTip(pindexNew); if ( KOMODO_NSPV_FULLNODE ) { - // 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->GetHeight()); @@ -4278,6 +4254,31 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * //fprintf(stderr,"%s: returning true\n", __FUNCTION__); return true; } +} +std::pair>, uint64_t> DrainRecentlyConflicted() +{ + uint64_t recentlyConflictedSequence; + std::map> 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 @@ -5316,11 +5317,6 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C return(false); } - if (ptx) - { - SyncWithWallets(*ptx, &block); - } - if ( ASSETCHAINS_CC != 0 ) { LOCK2(cs_main,mempool.cs); diff --git a/src/main.h b/src/main.h index c4bf4ea64..060b6b7cc 100644 --- a/src/main.h +++ b/src/main.h @@ -949,4 +949,8 @@ uint64_t CalculateCurrentUsage(); /** Return a CMutableTransaction with contextual default values based on set of consensus rules at height */ CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight); +std::pair>, uint64_t> DrainRecentlyConflicted(); +void SetChainNotifiedSequence(uint64_t recentlyConflictedSequence); +bool ChainIsFullyNotified(); + #endif // BITCOIN_MAIN_H diff --git a/src/miner.cpp b/src/miner.cpp index 5bc83fac4..8d3c5d4c4 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -983,6 +983,9 @@ static bool ProcessBlockFound(CBlock* pblock) } } + // Inform about the new block + GetMainSignals().BlockFound(pblock->GetHash()); + #ifdef ENABLE_WALLET // Remove key from key pool if ( IS_KOMODO_NOTARY == 0 ) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index ed5951e08..8a99a99e6 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -844,7 +844,7 @@ bool CTxMemPool::nullifierExists(const uint256& nullifier, ShieldedType type) co } } -void CTxMemPool::NotifyRecentlyAdded() +std::pair, uint64_t> CTxMemPool::DrainRecentlyAdded() { uint64_t recentlyAddedSequence; std::vector txs; @@ -857,29 +857,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&) { - fprintf(stderr,"%s: thread interrupted\n", __FUNCTION__); - 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() { diff --git a/src/txmempool.h b/src/txmempool.h index fbf7e4784..f8aa0e9a7 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -240,7 +240,8 @@ public: bool nullifierExists(const uint256& nullifier, ShieldedType type) const; - void NotifyRecentlyAdded(); + std::pair, uint64_t> DrainRecentlyAdded(); + void SetNotifiedSequence(uint64_t recentlyAddedSequence); bool IsFullyNotified(); unsigned long size() diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 6ea07be9e..799a60ceb 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -1,10 +1,21 @@ // 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" +#include "chainparams.h" +#include "init.h" +#include "main.h" +#include "txmempool.h" +#include "ui_interface.h" + +#include + +#include +#include + static CMainSignals g_signals; CMainSignals& GetMainSignals() @@ -17,28 +28,32 @@ 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, _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)); 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)); - 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)); 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(); @@ -47,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(); } @@ -55,10 +69,180 @@ void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) { g_signals.SyncTransaction(tx, pblock); } -void EraseFromWallets(const uint256 &hash) { - g_signals.EraseTransaction(hash); -} +struct CachedBlockData { + CBlockIndex *pindex; + std::pair oldTrees; + std::list txConflicted; -void RescanWallets() { - g_signals.RescanWallet(); -} \ No newline at end of file + CachedBlockData( + CBlockIndex *pindex, + std::pair oldTrees, + std::list 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( + now + std::chrono::seconds(1)); + std::this_thread::sleep_until( + std::chrono::time_point(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 blockStack; + // Transactions that have been recently conflicted out of the mempool. + std::pair>, uint64_t> recentlyConflicted; + // Transactions that have been recently added to the mempool. + std::pair, 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 (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); + } + } +} diff --git a/src/validationinterface.h b/src/validationinterface.h index 30b9cf0a7..bc5477c99 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -26,8 +26,6 @@ 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); /** Erase a transaction from all registered wallets */ void EraseFromWallets(const uint256 &hash); /** Rescan all registered wallets */ @@ -39,12 +37,13 @@ protected: virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {} virtual void EraseFromWallet(const uint256 &hash) {} virtual void RescanWallet() {} - 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> added) {} virtual void SetBestChain(const CBlockLocator &locator) {} virtual void UpdatedTransaction(const uint256 &hash) {} virtual void Inventory(const uint256 &hash) {} virtual void ResendWalletTransactions(int64_t nBestBlockTime) {} virtual void BlockChecked(const CBlock&, const CValidationState&) {} + virtual void ResetRequestCount(const uint256 &hash) {}; friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); friend void ::UnregisterAllValidationInterfaces(); @@ -62,7 +61,7 @@ struct CMainSignals { /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ boost::signals2::signal UpdatedTransaction; /** Notifies listeners of a change to the tip of the active block chain. */ - boost::signals2::signal ChainTip; + boost::signals2::signal>)> ChainTip; /** Notifies listeners of a new active block chain. */ boost::signals2::signal SetBestChain; /** Notifies listeners about an inventory item being seen on the network. */ @@ -71,8 +70,14 @@ struct CMainSignals { boost::signals2::signal Broadcast; /** Notifies listeners of a block validation result */ boost::signals2::signal BlockChecked; + // boost::signals2::signal&)> ScriptForMining; + /** Notifies listeners that a block has been successfully mined */ + boost::signals2::signal BlockFound; + }; CMainSignals& GetMainSignals(); +void ThreadNotifyWallets(CBlockIndex *pindexLastTip); + #endif // BITCOIN_VALIDATIONINTERFACE_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 953f738ef..f8127e83d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -588,9 +588,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, void CWallet::ChainTip(const CBlockIndex *pindex, const CBlock *pblock, - SproutMerkleTree sproutTree, - SaplingMerkleTree saplingTree, - bool added) + boost::optional> added) { if (added) { // Prevent witness cache building && consolidation transactions diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index ea327b2d9..d9dc9e0a1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1253,7 +1253,10 @@ public: CAmount GetCredit(const CTransaction& tx, int32_t voutNum, 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> added); void RunSaplingConsolidation(int blockHeight); void CommitConsolidationTx(const CTransaction& tx); /** Saves witness caches and best block locator to disk. */ @@ -1281,6 +1284,13 @@ public: } } + //void GetScriptForMining(boost::shared_ptr &script); + void ResetRequestCount(const uint256 &hash) + { + LOCK(cs_wallet); + mapRequestCount[hash] = 0; + }; + unsigned int GetKeyPoolSize() { AssertLockHeld(cs_wallet); // setKeyPool diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index c3783e97a..2f79e454b 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -309,7 +309,7 @@ double benchmark_increment_note_witnesses(size_t nTxs) index1.SetHeight(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; @@ -333,7 +333,7 @@ double benchmark_increment_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); }