diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/qt/bitcoingui.cpp | 5 | ||||
-rw-r--r-- | src/qt/modaloverlay.cpp | 6 | ||||
-rw-r--r-- | src/qt/modaloverlay.h | 2 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 3 | ||||
-rw-r--r-- | src/txmempool.cpp | 23 | ||||
-rw-r--r-- | src/txmempool.h | 25 | ||||
-rw-r--r-- | src/validation.cpp | 55 | ||||
-rw-r--r-- | src/validationinterface.h | 11 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 18 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 12 |
10 files changed, 128 insertions, 32 deletions
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index b86437cede..f86b09644b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -31,6 +31,7 @@ #include "macdockiconhandler.h" #endif +#include "chainparams.h" #include "init.h" #include "ui_interface.h" #include "util.h" @@ -752,8 +753,8 @@ void BitcoinGUI::updateHeadersSyncProgressLabel() { int64_t headersTipTime = clientModel->getHeaderTipTime(); int headersTipHeight = clientModel->getHeaderTipHeight(); - int estHeadersLeft = (GetTime() - headersTipTime)/600; - if (estHeadersLeft > REQ_HEADER_HEIGHT_DELTA_SYNC) + int estHeadersLeft = (GetTime() - headersTipTime) / Params().GetConsensus().nPowTargetSpacing; + if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC) progressBarLabel->setText(tr("Syncing Headers (%1%)...").arg(QString::number(100.0 / (headersTipHeight+estHeadersLeft)*headersTipHeight, 'f', 1))); } diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index 89fb159956..4779ffa43f 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -7,6 +7,8 @@ #include "guiutil.h" +#include "chainparams.h" + #include <QResizeEvent> #include <QPropertyAnimation> @@ -125,11 +127,11 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri // estimate the number of headers left based on nPowTargetSpacing // and check if the gui is not aware of the the best header (happens rarely) - int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / 600; + int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing; bool hasBestHeader = bestHeaderHeight >= count; // show remaining number of blocks - if (estimateNumHeadersLeft < 24 && hasBestHeader) { + if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) { ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count)); } else { ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1)...").arg(bestHeaderHeight)); diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h index 6d1f12164e..21ccdbd839 100644 --- a/src/qt/modaloverlay.h +++ b/src/qt/modaloverlay.h @@ -9,7 +9,7 @@ #include <QWidget> //! The required delta of headers to the estimated number of available headers until we show the IBD progress -static const int REQ_HEADER_HEIGHT_DELTA_SYNC = 24; +static constexpr int HEADER_HEIGHT_DELTA_SYNC = 24; namespace Ui { class ModalOverlay; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 5aeda7a30d..1e2842df73 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -16,6 +16,7 @@ #include "walletmodel.h" #include "base58.h" +#include "chainparams.h" #include "wallet/coincontrol.h" #include "validation.h" // mempool and minRelayTxFee #include "ui_interface.h" @@ -608,7 +609,7 @@ void SendCoinsDialog::updateGlobalFeeVariables() CoinControlDialog::coinControl->nMinimumTotalFee = 0; // show the estimated reuquired time for confirmation - ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget*600)+" / "+tr("%n block(s)", "", nConfirmTarget)); + ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget * Params().GetConsensus().nPowTargetSpacing) + " / " + tr("%n block(s)", "", nConfirmTarget)); } else { diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 5b085f492d..54400caccc 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -393,6 +393,7 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n) bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate) { + NotifyEntryAdded(entry.GetSharedTx()); // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. @@ -449,8 +450,9 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, return true; } -void CTxMemPool::removeUnchecked(txiter it) +void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { + NotifyEntryRemoved(it->GetSharedTx(), reason); const uint256 hash = it->GetTx().GetHash(); BOOST_FOREACH(const CTxIn& txin, it->GetTx().vin) mapNextTx.erase(txin.prevout); @@ -502,7 +504,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants } } -void CTxMemPool::removeRecursive(const CTransaction &origTx) +void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason) { // Remove transaction from memory pool { @@ -529,7 +531,8 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx) BOOST_FOREACH(txiter it, txToRemove) { CalculateDescendants(it, setAllRemoves); } - RemoveStaged(setAllRemoves, false); + + RemoveStaged(setAllRemoves, false, reason); } } @@ -567,7 +570,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem for (txiter it : txToRemove) { CalculateDescendants(it, setAllRemoves); } - RemoveStaged(setAllRemoves, false); + RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG); } void CTxMemPool::removeConflicts(const CTransaction &tx) @@ -581,7 +584,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx) if (txConflict != tx) { ClearPrioritisation(txConflict.GetHash()); - removeRecursive(txConflict); + removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT); } } } @@ -610,7 +613,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne if (it != mapTx.end()) { setEntries stage; stage.insert(it); - RemoveStaged(stage, true); + RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); } removeConflicts(*tx); ClearPrioritisation(tx->GetHash()); @@ -989,11 +992,11 @@ size_t CTxMemPool::DynamicMemoryUsage() const { return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage; } -void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants) { +void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) { AssertLockHeld(cs); UpdateForRemoveFromMempool(stage, updateDescendants); BOOST_FOREACH(const txiter& it, stage) { - removeUnchecked(it); + removeUnchecked(it, reason); } } @@ -1009,7 +1012,7 @@ int CTxMemPool::Expire(int64_t time) { BOOST_FOREACH(txiter removeit, toremove) { CalculateDescendants(removeit, stage); } - RemoveStaged(stage, false); + RemoveStaged(stage, false, MemPoolRemovalReason::EXPIRY); return stage.size(); } @@ -1118,7 +1121,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe BOOST_FOREACH(txiter iter, stage) txn.push_back(iter->GetTx()); } - RemoveStaged(stage, false); + RemoveStaged(stage, false, MemPoolRemovalReason::SIZELIMIT); if (pvNoSpendsRemaining) { BOOST_FOREACH(const CTransaction& tx, txn) { BOOST_FOREACH(const CTxIn& txin, tx.vin) { diff --git a/src/txmempool.h b/src/txmempool.h index ffb1c1309b..f842a07dd6 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -25,6 +25,8 @@ #include "boost/multi_index/ordered_index.hpp" #include "boost/multi_index/hashed_index.hpp" +#include <boost/signals2/signal.hpp> + class CAutoFile; class CBlockIndex; @@ -333,6 +335,19 @@ struct TxMempoolInfo int64_t nFeeDelta; }; +/** Reason why a transaction was removed from the mempool, + * this is passed to the notification signal. + */ +enum class MemPoolRemovalReason { + UNKNOWN = 0, //! Manually removed or unknown reason + EXPIRY, //! Expired from mempool + SIZELIMIT, //! Removed in size limiting + REORG, //! Removed for reorganization + BLOCK, //! Removed for block + CONFLICT, //! Removed for conflict with in-block transaction + REPLACED //! Removed for replacement +}; + /** * CTxMemPool stores valid-according-to-the-current-best-chain transactions * that may be included in the next block. @@ -521,10 +536,11 @@ public: bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true); bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true); - void removeRecursive(const CTransaction &tx); + void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeConflicts(const CTransaction &tx); void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight); + void clear(); void _clear(); //lock free bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); @@ -551,7 +567,7 @@ public: * Set updateDescendants to true when removing a tx that was in a block, so * that any in-mempool descendants have their ancestor state updated. */ - void RemoveStaged(setEntries &stage, bool updateDescendants); + void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); /** When adding transactions from a disconnected block back to the mempool, * new mempool entries may have children in the mempool (which is generally @@ -647,6 +663,9 @@ public: size_t DynamicMemoryUsage() const; + boost::signals2::signal<void (CTransactionRef)> NotifyEntryAdded; + boost::signals2::signal<void (CTransactionRef, MemPoolRemovalReason)> NotifyEntryRemoved; + private: /** UpdateForDescendants is used by UpdateTransactionsFromBlock to update * the descendants for a single transaction that has been added to the @@ -683,7 +702,7 @@ private: * transactions in a chain before we've updated all the state for the * removal. */ - void removeUnchecked(txiter entry); + void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); }; /** diff --git a/src/validation.cpp b/src/validation.cpp index 4fe03e4192..b882416961 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -157,6 +157,39 @@ namespace { set<int> setDirtyFileInfo; } // anon namespace +/* Use this class to start tracking transactions that are removed from the + * mempool and pass all those transactions through SyncTransaction when the + * object goes out of scope. This is currently only used to call SyncTransaction + * on conflicts removed from the mempool during block connection. Applied in + * ActivateBestChain around ActivateBestStep which in turn calls: + * ConnectTip->removeForBlock->removeConflicts + */ +class MemPoolConflictRemovalTracker +{ +private: + std::vector<CTransactionRef> conflictedTxs; + CTxMemPool &pool; + +public: + MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) { + pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + } + + void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) { + if (reason == MemPoolRemovalReason::CONFLICT) { + conflictedTxs.push_back(txRemoved); + } + } + + ~MemPoolConflictRemovalTracker() { + pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + for (const auto& tx : conflictedTxs) { + GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); + } + conflictedTxs.clear(); + } +}; + CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { // Find the first block the caller has in the main chain @@ -956,7 +989,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (plTxnReplaced) plTxnReplaced->push_back(it->GetSharedTx()); } - pool.RemoveStaged(allConflicting, false); + pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); // This transaction should only count for fee estimation if // the node is not behind and it is not dependent on any other @@ -2166,7 +2199,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // ignore validation errors in resurrected transactions CValidationState stateDummy; if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, NULL, true)) { - mempool.removeRecursive(tx); + mempool.removeRecursive(tx, MemPoolRemovalReason::REORG); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); } @@ -2453,6 +2486,14 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, bool fInitialDownload; { LOCK(cs_main); + { // TODO: Tempoarily ensure that mempool removals are notified before + // connected transactions. This shouldn't matter, but the abandoned + // state of transactions in our wallet is currently cleared when we + // receive another notification and there is a race condition where + // notification of a connected conflict might cause an outside process + // to abandon a transaction and then have it inadvertantly cleared by + // the notification that the conflicted transaction was evicted. + MemPoolConflictRemovalTracker mrt(mempool); CBlockIndex *pindexOldTip = chainActive.Tip(); if (pindexMostWork == NULL) { pindexMostWork = FindMostWorkChain(); @@ -2476,6 +2517,10 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, fInitialDownload = IsInitialBlockDownload(); // throw all transactions though the signal-interface + + } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified + + // Transactions in the connnected block are notified for (const auto& pair : connectTrace.blocksConnected) { assert(pair.second); const CBlock& block = *(pair.second); @@ -3597,7 +3642,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus())) - return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, + return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -3768,7 +3813,7 @@ bool LoadBlockIndex(const CChainParams& chainparams) return true; } -bool InitBlockIndex(const CChainParams& chainparams) +bool InitBlockIndex(const CChainParams& chainparams) { LOCK(cs_main); @@ -4202,7 +4247,7 @@ void DumpMempool(void) { LOCK(mempool.cs); for (const auto &i : mempool.mapDeltas) { - mapDeltas[i.first] = i.second.first; + mapDeltas[i.first] = i.second.second; } vinfo = mempool.infoAll(); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 594072719c..a2e76f2036 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -50,9 +50,16 @@ protected: struct CMainSignals { /** Notifies listeners of updated block chain tip */ boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip; - /** A posInBlock value for SyncTransaction which indicates the transaction was conflicted, disconnected, or not in a block */ + /** A posInBlock value for SyncTransaction calls for tranactions not + * included in connected blocks such as transactions removed from mempool, + * accepted to mempool or appearing in disconnected blocks.*/ static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1; - /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ + /** Notifies listeners of updated transaction data (transaction, and + * optionally the block it is found in). Called with block data when + * transaction is included in a connected block, and without block data when + * transaction was accepted to mempool, removed from mempool (only when + * removal was due to conflict from connected block), or appeared in a + * disconnected block.*/ boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction; /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ boost::signals2::signal<void (const uint256 &)> UpdatedTransaction; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 55e7558498..6d08f60483 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1681,7 +1681,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) LOCK2(cs_main, pwalletMain->cs_wallet); - CBlockIndex *pindex = NULL; + const CBlockIndex *pindex = NULL; int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; @@ -1692,7 +1692,16 @@ UniValue listsinceblock(const JSONRPCRequest& request) blockId.SetHex(request.params[0].get_str()); BlockMap::iterator it = mapBlockIndex.find(blockId); if (it != mapBlockIndex.end()) + { pindex = it->second; + if (chainActive[pindex->nHeight] != pindex) + { + // the block being asked for is a part of a deactivated chain; + // we don't want to depend on its perceived height in the block + // chain, we want to instead use the last common ancestor + pindex = chainActive.FindFork(pindex); + } + } } if (request.params.size() > 1) @@ -1703,9 +1712,10 @@ UniValue listsinceblock(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } - if(request.params.size() > 2) - if(request.params[2].get_bool()) - filter = filter | ISMINE_WATCH_ONLY; + if (request.params.size() > 2 && request.params[2].get_bool()) + { + filter = filter | ISMINE_WATCH_ONLY; + } int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 358a5ecfc1..b4715622cf 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1003,9 +1003,17 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn) } /** - * Add a transaction to the wallet, or update it. - * pblock is optional, but should be provided if the transaction is known to be in a block. + * Add a transaction to the wallet, or update it. pIndex and posInBlock should + * be set when the transaction was known to be included in a block. When + * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not + * updated in AddToWallet, but notifications happen and cached balances are + * marked dirty. * If fUpdate is true, existing transactions will be updated. + * TODO: One exception to this is that the abandoned state is cleared under the + * assumption that any further notification of a transaction that was considered + * abandoned is an indication that it is not safe to be considered abandoned. + * Abandoned state should probably be more carefuly tracked via different + * posInBlock signals or by checking mempool presence when necessary. */ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) { |