diff --git a/doc/release-notes.md b/doc/release-notes.md index 5a4117e25..8c86aed51 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,14 +1,21 @@ # Bitcoin ABC 0.24.1 Release Notes Bitcoin ABC version 0.24.1 is now available from: This release includes the following features and fixes: ## CLI A new `bitcoin-cli -generate` command, equivalent to RPC `generatenewaddress` followed by `generatetoaddress`, can generate blocks for command line testing purposes. This is a client-side version of the former `generate` RPC. See the help for details. + +## Notification changes + +`-walletnotify` notifications are now sent for wallet transactions that are +removed from the mempool because they conflict with a new block. These +notifications were sent previously before the v0.21.13 release, but had been +broken since that release. diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index 18cce87c1..2b3c0e996 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -1,470 +1,472 @@ // Copyright (c) 2018-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace interfaces { namespace { bool FillBlock(const CBlockIndex *index, const FoundBlock &block, UniqueLock &lock) { if (!index) { return false; } if (block.m_hash) { *block.m_hash = index->GetBlockHash(); } if (block.m_height) { *block.m_height = index->nHeight; } if (block.m_time) { *block.m_time = index->GetBlockTime(); } if (block.m_max_time) { *block.m_max_time = index->GetBlockTimeMax(); } if (block.m_mtp_time) { *block.m_mtp_time = index->GetMedianTimePast(); } if (block.m_data) { REVERSE_LOCK(lock); if (!ReadBlockFromDisk(*block.m_data, index, Params().GetConsensus())) { block.m_data->SetNull(); } } return true; } class NotificationsProxy : public CValidationInterface { public: explicit NotificationsProxy( std::shared_ptr notifications) : m_notifications(std::move(notifications)) {} virtual ~NotificationsProxy() = default; void TransactionAddedToMempool(const CTransactionRef &tx) override { m_notifications->transactionAddedToMempool(tx); } - void TransactionRemovedFromMempool(const CTransactionRef &tx) override { - m_notifications->transactionRemovedFromMempool(tx); + void + TransactionRemovedFromMempool(const CTransactionRef &tx, + MemPoolRemovalReason reason) override { + m_notifications->transactionRemovedFromMempool(tx, reason); } void BlockConnected(const std::shared_ptr &block, const CBlockIndex *index) override { m_notifications->blockConnected(*block, index->nHeight); } void BlockDisconnected(const std::shared_ptr &block, const CBlockIndex *index) override { m_notifications->blockDisconnected(*block, index->nHeight); } void UpdatedBlockTip(const CBlockIndex *index, const CBlockIndex *fork_index, bool is_ibd) override { m_notifications->updatedBlockTip(); } void ChainStateFlushed(const CBlockLocator &locator) override { m_notifications->chainStateFlushed(locator); } std::shared_ptr m_notifications; }; class NotificationsHandlerImpl : public Handler { public: explicit NotificationsHandlerImpl( std::shared_ptr notifications) : m_proxy(std::make_shared( std::move(notifications))) { RegisterSharedValidationInterface(m_proxy); } ~NotificationsHandlerImpl() override { disconnect(); } void disconnect() override { if (m_proxy) { UnregisterSharedValidationInterface(m_proxy); m_proxy.reset(); } } std::shared_ptr m_proxy; }; class RpcHandlerImpl : public Handler { public: explicit RpcHandlerImpl(const CRPCCommand &command) : m_command(command), m_wrapped_command(&command) { m_command.actor = [this](Config &config, const JSONRPCRequest &request, UniValue &result, bool last_handler) { if (!m_wrapped_command) { return false; } try { return m_wrapped_command->actor(config, request, result, last_handler); } catch (const UniValue &e) { // If this is not the last handler and a wallet not found // exception was thrown, return false so the next handler // can try to handle the request. Otherwise, reraise the // exception. if (!last_handler) { const UniValue &code = e["code"]; if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) { return false; } } throw; } }; ::tableRPC.appendCommand(m_command.name, &m_command); } void disconnect() final { if (m_wrapped_command) { m_wrapped_command = nullptr; ::tableRPC.removeCommand(m_command.name, &m_command); } } ~RpcHandlerImpl() override { disconnect(); } CRPCCommand m_command; const CRPCCommand *m_wrapped_command; }; class ChainImpl : public Chain { public: explicit ChainImpl(NodeContext &node, const CChainParams ¶ms) : m_node(node), m_params(params) {} std::optional getHeight() override { LOCK(::cs_main); int height = ::ChainActive().Height(); if (height >= 0) { return height; } return std::nullopt; } std::optional getBlockHeight(const BlockHash &hash) override { LOCK(::cs_main); CBlockIndex *block = LookupBlockIndex(hash); if (block && ::ChainActive().Contains(block)) { return block->nHeight; } return std::nullopt; } BlockHash getBlockHash(int height) override { LOCK(::cs_main); CBlockIndex *block = ::ChainActive()[height]; assert(block); return block->GetBlockHash(); } bool haveBlockOnDisk(int height) override { LOCK(cs_main); CBlockIndex *block = ::ChainActive()[height]; return block && (block->nStatus.hasData() != 0) && block->nTx > 0; } std::optional findFirstBlockWithTimeAndHeight(int64_t time, int height, BlockHash *hash) override { LOCK(cs_main); CBlockIndex *block = ::ChainActive().FindEarliestAtLeast(time, height); if (block) { if (hash) { *hash = block->GetBlockHash(); } return block->nHeight; } return std::nullopt; } CBlockLocator getTipLocator() override { LOCK(cs_main); return ::ChainActive().GetLocator(); } bool contextualCheckTransactionForCurrentBlock( const CTransaction &tx, TxValidationState &state) override { LOCK(cs_main); return ContextualCheckTransactionForCurrentBlock( m_params.GetConsensus(), tx, state); } std::optional findLocatorFork(const CBlockLocator &locator) override { LOCK(cs_main); if (CBlockIndex *fork = FindForkInGlobalIndex(::ChainActive(), locator)) { return fork->nHeight; } return std::nullopt; } bool findBlock(const BlockHash &hash, const FoundBlock &block) override { WAIT_LOCK(cs_main, lock); return FillBlock(LookupBlockIndex(hash), block, lock); } bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock &block) override { WAIT_LOCK(cs_main, lock); return FillBlock( ChainActive().FindEarliestAtLeast(min_time, min_height), block, lock); } bool findNextBlock(const BlockHash &block_hash, int block_height, const FoundBlock &next, bool *reorg) override { WAIT_LOCK(cs_main, lock); CBlockIndex *block = ChainActive()[block_height]; if (block && block->GetBlockHash() != block_hash) { block = nullptr; } if (reorg) { *reorg = !block; } return FillBlock(block ? ChainActive()[block_height + 1] : nullptr, next, lock); } bool findAncestorByHeight(const BlockHash &block_hash, int ancestor_height, const FoundBlock &ancestor_out) override { WAIT_LOCK(cs_main, lock); if (const CBlockIndex *block = LookupBlockIndex(block_hash)) { if (const CBlockIndex *ancestor = block->GetAncestor(ancestor_height)) { return FillBlock(ancestor, ancestor_out, lock); } } return FillBlock(nullptr, ancestor_out, lock); } bool findAncestorByHash(const BlockHash &block_hash, const BlockHash &ancestor_hash, const FoundBlock &ancestor_out) override { WAIT_LOCK(cs_main, lock); const CBlockIndex *block = LookupBlockIndex(block_hash); const CBlockIndex *ancestor = LookupBlockIndex(ancestor_hash); if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) { ancestor = nullptr; } return FillBlock(ancestor, ancestor_out, lock); } bool findCommonAncestor(const BlockHash &block_hash1, const BlockHash &block_hash2, const FoundBlock &ancestor_out, const FoundBlock &block1_out, const FoundBlock &block2_out) override { WAIT_LOCK(cs_main, lock); const CBlockIndex *block1 = LookupBlockIndex(block_hash1); const CBlockIndex *block2 = LookupBlockIndex(block_hash2); const CBlockIndex *ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr; // Using & instead of && below to avoid short circuiting and leaving // output uninitialized. return FillBlock(ancestor, ancestor_out, lock) & FillBlock(block1, block1_out, lock) & FillBlock(block2, block2_out, lock); } void findCoins(std::map &coins) override { return FindCoins(m_node, coins); } double guessVerificationProgress(const BlockHash &block_hash) override { LOCK(cs_main); return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash)); } bool hasBlocks(const BlockHash &block_hash, int min_height, std::optional max_height) override { // hasBlocks returns true if all ancestors of block_hash in // specified range have block data (are not pruned), false if any // ancestors in specified range are missing data. // // For simplicity and robustness, min_height and max_height are only // used to limit the range, and passing min_height that's too low or // max_height that's too high will not crash or change the result. LOCK(::cs_main); if (CBlockIndex *block = LookupBlockIndex(block_hash)) { if (max_height && block->nHeight >= *max_height) { block = block->GetAncestor(*max_height); } for (; block->nStatus.hasData(); block = block->pprev) { // Check pprev to not segfault if min_height is too low if (block->nHeight <= min_height || !block->pprev) { return true; } } } return false; } bool hasDescendantsInMempool(const TxId &txid) override { if (!m_node.mempool) { return false; } LOCK(m_node.mempool->cs); auto it = m_node.mempool->GetIter(txid); return it && (*it)->GetCountWithDescendants() > 1; } bool broadcastTransaction(const Config &config, const CTransactionRef &tx, const Amount &max_tx_fee, bool relay, std::string &err_string) override { const TransactionError err = BroadcastTransaction( m_node, config, tx, err_string, max_tx_fee, relay, /*wait_callback*/ false); // Chain clients only care about failures to accept the tx to the // mempool. Disregard non-mempool related failures. Note: this will // need to be updated if BroadcastTransactions() is updated to // return other non-mempool failures that Chain clients do not need // to know about. return err == TransactionError::OK; } void getTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants) override { ancestors = descendants = 0; if (!m_node.mempool) { return; } m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants); } void getPackageLimits(size_t &limit_ancestor_count, size_t &limit_descendant_count) override { limit_ancestor_count = size_t( std::max(1, gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT))); limit_descendant_count = size_t( std::max(1, gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT))); } bool checkChainLimits(const CTransactionRef &tx) override { if (!m_node.mempool) { return true; } LockPoints lp; CTxMemPoolEntry entry(tx, Amount(), 0, 0, false, 0, lp); CTxMemPool::setEntries ancestors; auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; std::string unused_error_string; LOCK(m_node.mempool->cs); return m_node.mempool->CalculateMemPoolAncestors( entry, ancestors, limit_ancestor_count, limit_ancestor_size, limit_descendant_count, limit_descendant_size, unused_error_string); } CFeeRate estimateFee() const override { if (!m_node.mempool) { return {}; } return m_node.mempool->estimateFee(); } CFeeRate relayMinFee() override { return ::minRelayTxFee; } CFeeRate relayDustFee() override { return ::dustRelayFee; } bool havePruned() override { LOCK(cs_main); return ::fHavePruned; } bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); } bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } bool shutdownRequested() override { return ShutdownRequested(); } int64_t getAdjustedTime() override { return GetAdjustedTime(); } void initMessage(const std::string &message) override { ::uiInterface.InitMessage(message); } void initWarning(const bilingual_str &message) override { InitWarning(message); } void initError(const bilingual_str &message) override { InitError(message); } void showProgress(const std::string &title, int progress, bool resume_possible) override { ::uiInterface.ShowProgress(title, progress, resume_possible); } std::unique_ptr handleNotifications( std::shared_ptr notifications) override { return std::make_unique( std::move(notifications)); } void waitForNotificationsIfTipChanged(const BlockHash &old_tip) override { if (!old_tip.IsNull()) { LOCK(::cs_main); if (old_tip == ::ChainActive().Tip()->GetBlockHash()) { return; } } SyncWithValidationInterfaceQueue(); } std::unique_ptr handleRpc(const CRPCCommand &command) override { return std::make_unique(command); } bool rpcEnableDeprecated(const std::string &method) override { return IsDeprecatedRPCEnabled(gArgs, method); } void rpcRunLater(const std::string &name, std::function fn, int64_t seconds) override { RPCRunLater(name, std::move(fn), seconds); } int rpcSerializationFlags() override { return RPCSerializationFlags(); } void requestMempoolTransactions(Notifications ¬ifications) override { if (!m_node.mempool) { return; } LOCK2(::cs_main, m_node.mempool->cs); for (const CTxMemPoolEntry &entry : m_node.mempool->mapTx) { notifications.transactionAddedToMempool(entry.GetSharedTx()); } } const CChainParams ¶ms() const override { return m_params; } NodeContext &m_node; const CChainParams &m_params; }; } // namespace std::unique_ptr MakeChain(NodeContext &node, const CChainParams ¶ms) { return std::make_unique(node, params); } } // namespace interfaces diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index a8402b91f..58ea78543 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -1,367 +1,370 @@ // Copyright (c) 2018-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_INTERFACES_CHAIN_H #define BITCOIN_INTERFACES_CHAIN_H #include #include #include #include #include #include #include #include #include class ArgsManager; class CBlock; class CChainParams; class Coin; class Config; class CRPCCommand; class CScheduler; class TxValidationState; +enum class MemPoolRemovalReason; + struct BlockHash; struct bilingual_str; struct CBlockLocator; struct NodeContext; namespace Consensus { struct Params; } namespace interfaces { class Handler; class Wallet; //! Helper for findBlock to selectively return pieces of block data. class FoundBlock { public: FoundBlock &hash(BlockHash &hash) { m_hash = &hash; return *this; } FoundBlock &height(int &height) { m_height = &height; return *this; } FoundBlock &time(int64_t &time) { m_time = &time; return *this; } FoundBlock &maxTime(int64_t &max_time) { m_max_time = &max_time; return *this; } FoundBlock &mtpTime(int64_t &mtp_time) { m_mtp_time = &mtp_time; return *this; } //! Read block data from disk. If the block exists but doesn't have data //! (for example due to pruning), the CBlock variable will be set to null. FoundBlock &data(CBlock &data) { m_data = &data; return *this; } BlockHash *m_hash = nullptr; int *m_height = nullptr; int64_t *m_time = nullptr; int64_t *m_max_time = nullptr; int64_t *m_mtp_time = nullptr; CBlock *m_data = nullptr; }; //! Interface giving clients (wallet processes, maybe other analysis tools in //! the future) ability to access to the chain state, receive notifications, //! estimate fees, and submit transactions. //! //! TODO: Current chain methods are too low level, exposing too much of the //! internal workings of the bitcoin node, and not being very convenient to use. //! Chain methods should be cleaned up and simplified over time. Examples: //! //! * The initMessages() and showProgress() methods which the wallet uses to //! send //! notifications to the GUI should go away when GUI and wallet can directly //! communicate with each other without going through the node //! (https://github.com/bitcoin/bitcoin/pull/15288#discussion_r253321096). //! //! * The handleRpc, registerRpcs, rpcEnableDeprecated methods and other RPC //! methods can go away if wallets listen for HTTP requests on their own //! ports instead of registering to handle requests on the node HTTP port. //! //! * Move fee estimation queries to an asynchronous interface and let the //! wallet cache it, fee estimation being driven by node mempool, wallet //! should be the consumer. //! //! * The `guessVerificationProgress`, `getBlockHeight`, `getBlockHash`, etc //! methods can go away if rescan logic is moved on the node side, and wallet //! only register rescan request. class Chain { public: virtual ~Chain() {} //! Get current chain height, not including genesis block (returns 0 if //! chain only contains genesis block, std::nullopt if chain does not //! contain any blocks) virtual std::optional getHeight() = 0; //! Get block height above genesis block. Returns 0 for genesis block, //! 1 for following block, and so on. Returns std::nullopt for a block not //! included in the current chain. virtual std::optional getBlockHeight(const BlockHash &hash) = 0; //! Get block hash. Height must be valid or this function will abort. virtual BlockHash getBlockHash(int height) = 0; //! Check that the block is available on disk (i.e. has not been //! pruned), and contains transactions. virtual bool haveBlockOnDisk(int height) = 0; //! Return height of the first block in the chain with timestamp equal //! or greater than the given time and height equal or greater than the //! given height, or std::nullopt if there is no block with a high enough //! timestamp and height. Also return the block hash as an optional output //! parameter (to avoid the cost of a second lookup in case this information //! is needed.) virtual std::optional findFirstBlockWithTimeAndHeight(int64_t time, int height, BlockHash *hash) = 0; //! Get locator for the current chain tip. virtual CBlockLocator getTipLocator() = 0; //! Return height of the highest block on chain in common with the locator, //! which will either be the original block used to create the locator, //! or one of its ancestors. virtual std::optional findLocatorFork(const CBlockLocator &locator) = 0; //! Check if transaction will be final given chain height current time. virtual bool contextualCheckTransactionForCurrentBlock(const CTransaction &tx, TxValidationState &state) = 0; //! Return whether node has the block and optionally return block metadata //! or contents. virtual bool findBlock(const BlockHash &hash, const FoundBlock &block = {}) = 0; //! Find first block in the chain with timestamp >= the given time //! and height >= than the given height, return false if there is no block //! with a high enough timestamp and height. Optionally return block //! information. virtual bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock &block = {}) = 0; //! Find next block if block is part of current chain. Also flag if //! there was a reorg and the specified block hash is no longer in the //! current chain, and optionally return block information. virtual bool findNextBlock(const BlockHash &block_hash, int block_height, const FoundBlock &next = {}, bool *reorg = nullptr) = 0; //! Find ancestor of block at specified height and optionally return //! ancestor information. virtual bool findAncestorByHeight(const BlockHash &block_hash, int ancestor_height, const FoundBlock &ancestor_out = {}) = 0; //! Return whether block descends from a specified ancestor, and //! optionally return ancestor information. virtual bool findAncestorByHash(const BlockHash &block_hash, const BlockHash &ancestor_hash, const FoundBlock &ancestor_out = {}) = 0; //! Find most recent common ancestor between two blocks and optionally //! return block information. virtual bool findCommonAncestor(const BlockHash &block_hash1, const BlockHash &block_hash2, const FoundBlock &ancestor_out = {}, const FoundBlock &block1_out = {}, const FoundBlock &block2_out = {}) = 0; //! Look up unspent output information. Returns coins in the mempool and in //! the current chain UTXO set. Iterates through all the keys in the map and //! populates the values. virtual void findCoins(std::map &coins) = 0; //! Estimate fraction of total transactions verified if blocks up to //! the specified block hash are verified. virtual double guessVerificationProgress(const BlockHash &block_hash) = 0; //! Return true if data is available for all blocks in the specified range //! of blocks. This checks all blocks that are ancestors of block_hash in //! the height range from min_height to max_height, inclusive. virtual bool hasBlocks(const BlockHash &block_hash, int min_height = 0, std::optional max_height = {}) = 0; //! Check if transaction has descendants in mempool. virtual bool hasDescendantsInMempool(const TxId &txid) = 0; //! Transaction is added to memory pool, if the transaction fee is below the //! amount specified by max_tx_fee, and broadcast to all peers if relay is //! set to true. Return false if the transaction could not be added due to //! the fee or for another reason. virtual bool broadcastTransaction(const Config &config, const CTransactionRef &tx, const Amount &max_tx_fee, bool relay, std::string &err_string) = 0; //! Calculate mempool ancestor and descendant counts for the given //! transaction. virtual void getTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants) = 0; //! Get the node's package limits. //! Currently only returns the ancestor and descendant count limits, but //! could be enhanced to return more policy settings. virtual void getPackageLimits(size_t &limit_ancestor_count, size_t &limit_descendant_count) = 0; //! Check if transaction will pass the mempool's chain limits. virtual bool checkChainLimits(const CTransactionRef &tx) = 0; //! Estimate fee virtual CFeeRate estimateFee() const = 0; //! Relay current minimum fee (from -minrelaytxfee settings). virtual CFeeRate relayMinFee() = 0; //! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's //! economical to spend. virtual CFeeRate relayDustFee() = 0; //! Check if any block has been pruned. virtual bool havePruned() = 0; //! Check if the node is ready to broadcast transactions. virtual bool isReadyToBroadcast() = 0; //! Check if in IBD. virtual bool isInitialBlockDownload() = 0; //! Check if shutdown requested. virtual bool shutdownRequested() = 0; //! Get adjusted time. virtual int64_t getAdjustedTime() = 0; //! Send init message. virtual void initMessage(const std::string &message) = 0; //! Send init warning. virtual void initWarning(const bilingual_str &message) = 0; //! Send init error. virtual void initError(const bilingual_str &message) = 0; //! Send progress indicator. virtual void showProgress(const std::string &title, int progress, bool resume_possible) = 0; //! Chain notifications. class Notifications { public: virtual ~Notifications() {} virtual void transactionAddedToMempool(const CTransactionRef &tx) {} - virtual void transactionRemovedFromMempool(const CTransactionRef &ptx) { - } + virtual void + transactionRemovedFromMempool(const CTransactionRef &ptx, + MemPoolRemovalReason reason) {} virtual void blockConnected(const CBlock &block, int height) {} virtual void blockDisconnected(const CBlock &block, int height) {} virtual void updatedBlockTip() {} virtual void chainStateFlushed(const CBlockLocator &locator) {} }; //! Register handler for notifications. virtual std::unique_ptr handleNotifications(std::shared_ptr notifications) = 0; //! Wait for pending notifications to be processed unless block hash points //! to the current chain tip. virtual void waitForNotificationsIfTipChanged(const BlockHash &old_tip) = 0; //! Register handler for RPC. Command is not copied, so reference //! needs to remain valid until Handler is disconnected. virtual std::unique_ptr handleRpc(const CRPCCommand &command) = 0; //! Check if deprecated RPC is enabled. virtual bool rpcEnableDeprecated(const std::string &method) = 0; //! Run function after given number of seconds. Cancel any previous calls //! with same name. virtual void rpcRunLater(const std::string &name, std::function fn, int64_t seconds) = 0; //! Current RPC serialization flags. virtual int rpcSerializationFlags() = 0; //! Synchronously send transactionAddedToMempool notifications about all //! current mempool transactions to the specified handler and return after //! the last one is sent. These notifications aren't coordinated with async //! notifications sent by handleNotifications, so out of date async //! notifications from handleNotifications can arrive during and after //! synchronous notifications from requestMempoolTransactions. Clients need //! to be prepared to handle this by ignoring notifications about unknown //! removed transactions and already added new transactions. virtual void requestMempoolTransactions(Notifications ¬ifications) = 0; //! This Chain's parameters virtual const CChainParams ¶ms() const = 0; }; //! Interface to let node manage chain clients (wallets, or maybe tools for //! monitoring and analysis in the future). class ChainClient { public: virtual ~ChainClient() {} //! Register rpcs. virtual void registerRpcs() = 0; //! Check for errors before loading. virtual bool verify(const CChainParams &chainParams) = 0; //! Load saved state. virtual bool load(const CChainParams &chainParams) = 0; //! Start client execution and provide a scheduler. virtual void start(CScheduler &scheduler) = 0; //! Save state to disk. virtual void flush() = 0; //! Shut down client. virtual void stop() = 0; //! Set mock time. virtual void setMockTime(int64_t time) = 0; //! Return interfaces for accessing wallets (if any). virtual std::vector> getWallets() = 0; }; //! Return implementation of Chain interface. std::unique_ptr MakeChain(NodeContext &node, const CChainParams ¶ms); //! Return implementation of ChainClient interface for a wallet client. This //! function will be undefined in builds where ENABLE_WALLET is false. //! //! Currently, wallets are the only chain clients. But in the future, other //! types of chain clients could be added, such as tools for monitoring, //! analysis, or fee estimation. These clients need to expose their own //! MakeXXXClient functions returning their implementations of the ChainClient //! interface. std::unique_ptr MakeWalletClient(Chain &chain, ArgsManager &args, std::vector wallet_filenames); } // namespace interfaces #endif // BITCOIN_INTERFACES_CHAIN_H diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8f6351aee..90d03154b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1,1458 +1,1459 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include // for GetConsensus. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef &_tx, const Amount _nFee, int64_t _nTime, unsigned int _entryHeight, bool _spendsCoinbase, int64_t _sigOpsCount, LockPoints lp) : tx(_tx), nFee(_nFee), nTxSize(tx->GetTotalSize()), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight), spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOpsCount), lockPoints(lp), m_epoch(0) { nCountWithDescendants = 1; nSizeWithDescendants = GetTxSize(); nSigOpCountWithDescendants = sigOpCount; nModFeesWithDescendants = nFee; feeDelta = Amount::zero(); nCountWithAncestors = 1; nSizeWithAncestors = GetTxSize(); nModFeesWithAncestors = nFee; nSigOpCountWithAncestors = sigOpCount; } size_t CTxMemPoolEntry::GetTxVirtualSize() const { return GetVirtualTransactionSize(nTxSize, sigOpCount); } uint64_t CTxMemPoolEntry::GetVirtualSizeWithDescendants() const { // note this is distinct from the sum of descendants' individual virtual // sizes, and may be smaller. return GetVirtualTransactionSize(nSizeWithDescendants, nSigOpCountWithDescendants); } uint64_t CTxMemPoolEntry::GetVirtualSizeWithAncestors() const { // note this is distinct from the sum of ancestors' individual virtual // sizes, and may be smaller. return GetVirtualTransactionSize(nSizeWithAncestors, nSigOpCountWithAncestors); } void CTxMemPoolEntry::UpdateFeeDelta(Amount newFeeDelta) { nModFeesWithDescendants += newFeeDelta - feeDelta; nModFeesWithAncestors += newFeeDelta - feeDelta; feeDelta = newFeeDelta; } void CTxMemPoolEntry::UpdateLockPoints(const LockPoints &lp) { lockPoints = lp; } // Update the given tx for any in-mempool descendants. // Assumes that setMemPoolChildren is correct for the given tx and all // descendants. void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set &setExclude) { setEntries stageEntries, setAllDescendants; stageEntries = GetMemPoolChildren(updateIt); while (!stageEntries.empty()) { const txiter cit = *stageEntries.begin(); setAllDescendants.insert(cit); stageEntries.erase(cit); const setEntries &setChildren = GetMemPoolChildren(cit); for (txiter childEntry : setChildren) { cacheMap::iterator cacheIt = cachedDescendants.find(childEntry); if (cacheIt != cachedDescendants.end()) { // We've already calculated this one, just add the entries for // this set but don't traverse again. for (txiter cacheEntry : cacheIt->second) { setAllDescendants.insert(cacheEntry); } } else if (!setAllDescendants.count(childEntry)) { // Schedule for later processing stageEntries.insert(childEntry); } } } // setAllDescendants now contains all in-mempool descendants of updateIt. // Update and add to cached descendant map int64_t modifySize = 0; int64_t modifyCount = 0; Amount modifyFee = Amount::zero(); int64_t modifySigOpCount = 0; for (txiter cit : setAllDescendants) { if (!setExclude.count(cit->GetTx().GetId())) { modifySize += cit->GetTxSize(); modifyFee += cit->GetModifiedFee(); modifyCount++; modifySigOpCount += cit->GetSigOpCount(); cachedDescendants[updateIt].insert(cit); // Update ancestor state for each descendant mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount())); } } mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount, modifySigOpCount)); } // txidsToUpdate is the set of transaction hashes from a disconnected block // which has been re-added to the mempool. For each entry, look for descendants // that are outside txidsToUpdate, and add fee/size information for such // descendants to the parent. For each such descendant, also update the ancestor // state to include the parent. void CTxMemPool::UpdateTransactionsFromBlock( const std::vector &txidsToUpdate) { AssertLockHeld(cs); // For each entry in txidsToUpdate, store the set of in-mempool, but not // in-txidsToUpdate transactions, so that we don't have to recalculate // descendants when we come across a previously seen entry. cacheMap mapMemPoolDescendantsToUpdate; // Use a set for lookups into txidsToUpdate (these entries are already // accounted for in the state of their ancestors) std::set setAlreadyIncluded(txidsToUpdate.begin(), txidsToUpdate.end()); // Iterate in reverse, so that whenever we are looking at a transaction // we are sure that all in-mempool descendants have already been processed. // This maximizes the benefit of the descendant cache and guarantees that // setMemPoolChildren will be updated, an assumption made in // UpdateForDescendants. for (const TxId &txid : reverse_iterate(txidsToUpdate)) { // calculate children from mapNextTx txiter it = mapTx.find(txid); if (it == mapTx.end()) { continue; } auto iter = mapNextTx.lower_bound(COutPoint(txid, 0)); // First calculate the children, and update setMemPoolChildren to // include them, and update their setMemPoolParents to include this tx. // we cache the in-mempool children to avoid duplicate updates { const auto epoch = GetFreshEpoch(); for (; iter != mapNextTx.end() && iter->first->GetTxId() == txid; ++iter) { const TxId &childTxId = iter->second->GetId(); txiter childIter = mapTx.find(childTxId); assert(childIter != mapTx.end()); // We can skip updating entries we've encountered before or that // are in the block (which are already accounted for). if (!visited(childIter) && !setAlreadyIncluded.count(childTxId)) { UpdateChild(it, childIter, true); UpdateParent(childIter, it, true); } } } // release epoch guard for UpdateForDescendants UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded); } } bool CTxMemPool::CalculateMemPoolAncestors( const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */) const { setEntries parentHashes; const CTransaction &tx = entry.GetTx(); if (fSearchForParents) { // Get parents of this transaction that are in the mempool // GetMemPoolParents() is only valid for entries in the mempool, so we // iterate mapTx to find parents. for (const CTxIn &in : tx.vin) { std::optional piter = GetIter(in.prevout.GetTxId()); if (!piter) { continue; } parentHashes.insert(*piter); if (parentHashes.size() + 1 > limitAncestorCount) { errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount); return false; } } } else { // If we're not searching for parents, we require this to be an entry in // the mempool already. txiter it = mapTx.iterator_to(entry); parentHashes = GetMemPoolParents(it); } size_t totalSizeWithAncestors = entry.GetTxSize(); while (!parentHashes.empty()) { txiter stageit = *parentHashes.begin(); setAncestors.insert(stageit); parentHashes.erase(stageit); totalSizeWithAncestors += stageit->GetTxSize(); if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > limitDescendantSize) { errString = strprintf( "exceeds descendant size limit for tx %s [limit: %u]", stageit->GetTx().GetId().ToString(), limitDescendantSize); return false; } if (stageit->GetCountWithDescendants() + 1 > limitDescendantCount) { errString = strprintf("too many descendants for tx %s [limit: %u]", stageit->GetTx().GetId().ToString(), limitDescendantCount); return false; } if (totalSizeWithAncestors > limitAncestorSize) { errString = strprintf("exceeds ancestor size limit [limit: %u]", limitAncestorSize); return false; } const setEntries &setMemPoolParents = GetMemPoolParents(stageit); for (txiter phash : setMemPoolParents) { // If this is a new ancestor, add it. if (setAncestors.count(phash) == 0) { parentHashes.insert(phash); } if (parentHashes.size() + setAncestors.size() + 1 > limitAncestorCount) { errString = strprintf("too many unconfirmed ancestors [limit: %u]", limitAncestorCount); return false; } } } return true; } void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors) { setEntries parentIters = GetMemPoolParents(it); // add or remove this tx as a child of each parent for (txiter piter : parentIters) { UpdateChild(piter, it, add); } const int64_t updateCount = (add ? 1 : -1); const int64_t updateSize = updateCount * it->GetTxSize(); const int64_t updateSigOpCount = updateCount * it->GetSigOpCount(); const Amount updateFee = updateCount * it->GetModifiedFee(); for (txiter ancestorIt : setAncestors) { mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount, updateSigOpCount)); } } void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncestors) { int64_t updateCount = setAncestors.size(); int64_t updateSize = 0; int64_t updateSigOpsCount = 0; Amount updateFee = Amount::zero(); for (txiter ancestorIt : setAncestors) { updateSize += ancestorIt->GetTxSize(); updateFee += ancestorIt->GetModifiedFee(); updateSigOpsCount += ancestorIt->GetSigOpCount(); } mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOpsCount)); } void CTxMemPool::UpdateChildrenForRemoval(txiter it) { const setEntries &setMemPoolChildren = GetMemPoolChildren(it); for (txiter updateIt : setMemPoolChildren) { UpdateParent(updateIt, it, false); } } void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants) { // For each entry, walk back all ancestors and decrement size associated // with this transaction. const uint64_t nNoLimit = std::numeric_limits::max(); if (updateDescendants) { // updateDescendants should be true whenever we're not recursively // removing a tx and all its descendants, eg when a transaction is // confirmed in a block. Here we only update statistics and not data in // mapLinks (which we need to preserve until we're finished with all // operations that need to traverse the mempool). for (txiter removeIt : entriesToRemove) { setEntries setDescendants; CalculateDescendants(removeIt, setDescendants); setDescendants.erase(removeIt); // don't update state for self int64_t modifySize = -int64_t(removeIt->GetTxSize()); Amount modifyFee = -1 * removeIt->GetModifiedFee(); int modifySigOps = -removeIt->GetSigOpCount(); for (txiter dit : setDescendants) { mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps)); } } } for (txiter removeIt : entriesToRemove) { setEntries setAncestors; const CTxMemPoolEntry &entry = *removeIt; std::string dummy; // Since this is a tx that is already in the mempool, we can call CMPA // with fSearchForParents = false. If the mempool is in a consistent // state, then using true or false should both be correct, though false // should be a bit faster. // However, if we happen to be in the middle of processing a reorg, then // the mempool can be in an inconsistent state. In this case, the set of // ancestors reachable via mapLinks will be the same as the set of // ancestors whose packages include this transaction, because when we // add a new transaction to the mempool in addUnchecked(), we assume it // has no children, and in the case of a reorg where that assumption is // false, the in-mempool children aren't linked to the in-block tx's // until UpdateTransactionsFromBlock() is called. So if we're being // called during a reorg, ie before UpdateTransactionsFromBlock() has // been called, then mapLinks[] will differ from the set of mempool // parents we'd calculate by searching, and it's important that we use // the mapLinks[] notion of ancestor transactions as the set of things // to update for removal. CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false); // Note that UpdateAncestorsOf severs the child links that point to // removeIt in the entries for the parents of removeIt. UpdateAncestorsOf(false, removeIt, setAncestors); } // After updating all the ancestor sizes, we can now sever the link between // each transaction being removed and any mempool children (ie, update // setMemPoolParents for each direct child of a transaction being removed). for (txiter removeIt : entriesToRemove) { UpdateChildrenForRemoval(removeIt); } } void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, Amount modifyFee, int64_t modifyCount, int64_t modifySigOpCount) { nSizeWithDescendants += modifySize; assert(int64_t(nSizeWithDescendants) > 0); nModFeesWithDescendants += modifyFee; nCountWithDescendants += modifyCount; assert(int64_t(nCountWithDescendants) > 0); nSigOpCountWithDescendants += modifySigOpCount; assert(int64_t(nSigOpCountWithDescendants) >= 0); } void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, Amount modifyFee, int64_t modifyCount, int64_t modifySigOps) { nSizeWithAncestors += modifySize; assert(int64_t(nSizeWithAncestors) > 0); nModFeesWithAncestors += modifyFee; nCountWithAncestors += modifyCount; assert(int64_t(nCountWithAncestors) > 0); nSigOpCountWithAncestors += modifySigOps; assert(int(nSigOpCountWithAncestors) >= 0); } CTxMemPool::CTxMemPool() : nTransactionsUpdated(0), m_epoch(0), m_has_epoch_guard(false) { // lock free clear _clear(); // Sanity checks off by default for performance, because otherwise accepting // transactions becomes O(N^2) where N is the number of transactions in the // pool nCheckFrequency = 0; } CTxMemPool::~CTxMemPool() {} bool CTxMemPool::isSpent(const COutPoint &outpoint) const { LOCK(cs); return mapNextTx.count(outpoint); } unsigned int CTxMemPool::GetTransactionsUpdated() const { return nTransactionsUpdated; } void CTxMemPool::AddTransactionsUpdated(unsigned int n) { nTransactionsUpdated += n; } void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors) { // Add to memory pool without checking anything. // Used by AcceptToMemoryPool(), which DOES do all the appropriate checks. indexed_transaction_set::iterator newit = mapTx.insert(entry).first; mapLinks.insert(make_pair(newit, TxLinks())); // Update transaction for any feeDelta created by PrioritiseTransaction // TODO: refactor so that the fee delta is calculated before inserting into // mapTx. Amount feeDelta = Amount::zero(); ApplyDelta(entry.GetTx().GetId(), feeDelta); if (feeDelta != Amount::zero()) { mapTx.modify(newit, update_fee_delta(feeDelta)); } // Update cachedInnerUsage to include contained transaction's usage. // (When we update the entry for in-mempool parents, memory usage will be // further updated.) cachedInnerUsage += entry.DynamicMemoryUsage(); const CTransaction &tx = newit->GetTx(); std::set setParentTransactions; for (const CTxIn &in : tx.vin) { mapNextTx.insert(std::make_pair(&in.prevout, &tx)); setParentTransactions.insert(in.prevout.GetTxId()); } // Don't bother worrying about child transactions of this one. Normal case // of a new transaction arriving is that there can't be any children, // because such children would be orphans. An exception to that is if a // transaction enters that used to be in a block. In that case, our // disconnect block logic will call UpdateTransactionsFromBlock to clean up // the mess we're leaving here. // Update ancestors with information about this tx for (const auto &pit : GetIterSet(setParentTransactions)) { UpdateParent(newit, pit, true); } UpdateAncestorsOf(true, newit, setAncestors); UpdateEntryForAncestors(newit, setAncestors); nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); vTxHashes.emplace_back(tx.GetHash(), newit); newit->vTxHashesIdx = vTxHashes.size() - 1; } void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { if (reason != MemPoolRemovalReason::BLOCK) { // Notify clients that a transaction has been removed from the mempool // for any reason except being included in a block. Clients interested // in transactions included in blocks can subscribe to the // BlockConnected notification. - GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx()); + GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), + reason); } for (const CTxIn &txin : it->GetTx().vin) { mapNextTx.erase(txin.prevout); } /* add logging because unchecked */ RemoveUnbroadcastTx(it->GetTx().GetId(), true); if (vTxHashes.size() > 1) { vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back()); vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx; vTxHashes.pop_back(); if (vTxHashes.size() * 2 < vTxHashes.capacity()) { vTxHashes.shrink_to_fit(); } } else { vTxHashes.clear(); } totalTxSize -= it->GetTxSize(); cachedInnerUsage -= it->DynamicMemoryUsage(); cachedInnerUsage -= memusage::DynamicUsage(mapLinks[it].parents) + memusage::DynamicUsage(mapLinks[it].children); mapLinks.erase(it); mapTx.erase(it); nTransactionsUpdated++; } // Calculates descendants of entry that are not already in setDescendants, and // adds to setDescendants. Assumes entryit is already a tx in the mempool and // setMemPoolChildren is correct for tx and all descendants. Also assumes that // if an entry is in setDescendants already, then all in-mempool descendants of // it are already in setDescendants as well, so that we can save time by not // iterating over those entries. void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants) const { setEntries stage; if (setDescendants.count(entryit) == 0) { stage.insert(entryit); } // Traverse down the children of entry, only adding children that are not // accounted for in setDescendants already (because those children have // either already been walked, or will be walked in this iteration). while (!stage.empty()) { txiter it = *stage.begin(); setDescendants.insert(it); stage.erase(it); const setEntries &setChildren = GetMemPoolChildren(it); for (txiter childiter : setChildren) { if (!setDescendants.count(childiter)) { stage.insert(childiter); } } } } void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason) { // Remove transaction from memory pool. AssertLockHeld(cs); setEntries txToRemove; txiter origit = mapTx.find(origTx.GetId()); if (origit != mapTx.end()) { txToRemove.insert(origit); } else { // When recursively removing but origTx isn't in the mempool be sure to // remove any children that are in the pool. This can happen during // chain re-orgs if origTx isn't re-accepted into the mempool for any // reason. for (size_t i = 0; i < origTx.vout.size(); i++) { auto it = mapNextTx.find(COutPoint(origTx.GetId(), i)); if (it == mapNextTx.end()) { continue; } txiter nextit = mapTx.find(it->second->GetId()); assert(nextit != mapTx.end()); txToRemove.insert(nextit); } } setEntries setAllRemoves; for (txiter it : txToRemove) { CalculateDescendants(it, setAllRemoves); } RemoveStaged(setAllRemoves, false, reason); } void CTxMemPool::removeForReorg(const Config &config, const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) { // Remove transactions spending a coinbase which are now immature and // no-longer-final transactions. AssertLockHeld(cs); setEntries txToRemove; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { const CTransaction &tx = it->GetTx(); LockPoints lp = it->GetLockPoints(); bool validLP = TestLockPointValidity(&lp); TxValidationState state; if (!ContextualCheckTransactionForCurrentBlock( config.GetChainParams().GetConsensus(), tx, state, flags) || !CheckSequenceLocks(*this, tx, flags, &lp, validLP)) { // Note if CheckSequenceLocks fails the LockPoints may still be // invalid. So it's critical that we remove the tx and not depend on // the LockPoints. txToRemove.insert(it); } else if (it->GetSpendsCoinbase()) { for (const CTxIn &txin : tx.vin) { indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.GetTxId()); if (it2 != mapTx.end()) { continue; } const Coin &coin = pcoins->AccessCoin(txin.prevout); if (nCheckFrequency != 0) { assert(!coin.IsSpent()); } if (coin.IsSpent() || (coin.IsCoinBase() && int64_t(nMemPoolHeight) - coin.GetHeight() < COINBASE_MATURITY)) { txToRemove.insert(it); break; } } } if (!validLP) { mapTx.modify(it, update_lock_points(lp)); } } setEntries setAllRemoves; for (txiter it : txToRemove) { CalculateDescendants(it, setAllRemoves); } RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG); } void CTxMemPool::removeConflicts(const CTransaction &tx) { // Remove transactions which depend on inputs of tx, recursively AssertLockHeld(cs); for (const CTxIn &txin : tx.vin) { auto it = mapNextTx.find(txin.prevout); if (it != mapNextTx.end()) { const CTransaction &txConflict = *it->second; if (txConflict != tx) { ClearPrioritisation(txConflict.GetId()); removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT); } } } } /** * Called when a block is connected. Removes from mempool and updates the miner * fee estimator. */ void CTxMemPool::removeForBlock(const std::vector &vtx, unsigned int nBlockHeight) { AssertLockHeld(cs); DisconnectedBlockTransactions disconnectpool; disconnectpool.addForBlock(vtx, *this); std::vector entries; for (const CTransactionRef &tx : reverse_iterate(disconnectpool.GetQueuedTx().get())) { const TxId &txid = tx->GetId(); indexed_transaction_set::iterator i = mapTx.find(txid); if (i != mapTx.end()) { entries.push_back(&*i); } } for (const CTransactionRef &tx : reverse_iterate(disconnectpool.GetQueuedTx().get())) { txiter it = mapTx.find(tx->GetId()); if (it != mapTx.end()) { setEntries stage; stage.insert(it); RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); } removeConflicts(*tx); ClearPrioritisation(tx->GetId()); } disconnectpool.clear(); lastRollingFeeUpdate = GetTime(); blockSinceLastRollingFeeBump = true; } void CTxMemPool::_clear() { mapLinks.clear(); mapTx.clear(); mapNextTx.clear(); vTxHashes.clear(); totalTxSize = 0; cachedInnerUsage = 0; lastRollingFeeUpdate = GetTime(); blockSinceLastRollingFeeBump = false; rollingMinimumFeeRate = 0; ++nTransactionsUpdated; } void CTxMemPool::clear() { LOCK(cs); _clear(); } static void CheckInputsAndUpdateCoins(const CTransaction &tx, CCoinsViewCache &mempoolDuplicate, const int64_t spendheight) { // Not used. CheckTxInputs() should always pass TxValidationState dummy_state; Amount txfee = Amount::zero(); bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee); assert(fCheckResult); UpdateCoins(mempoolDuplicate, tx, std::numeric_limits::max()); } void CTxMemPool::check(const CCoinsViewCache *pcoins) const { LOCK(cs); if (nCheckFrequency == 0) { return; } if (GetRand(std::numeric_limits::max()) >= nCheckFrequency) { return; } LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); uint64_t checkTotal = 0; uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast(pcoins)); const int64_t spendheight = GetSpendHeight(mempoolDuplicate); std::list waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; checkTotal += it->GetTxSize(); innerUsage += it->DynamicMemoryUsage(); const CTransaction &tx = it->GetTx(); txlinksMap::const_iterator linksiter = mapLinks.find(it); assert(linksiter != mapLinks.end()); const TxLinks &links = linksiter->second; innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children); bool fDependsWait = false; setEntries setParentCheck; for (const CTxIn &txin : tx.vin) { // Check that every mempool transaction's inputs refer to available // coins, or other mempool tx's. indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.GetTxId()); if (it2 != mapTx.end()) { const CTransaction &tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.GetN() && !tx2.vout[txin.prevout.GetN()].IsNull()); fDependsWait = true; setParentCheck.insert(it2); } else { assert(pcoins->HaveCoin(txin.prevout)); } // Check whether its inputs are marked in mapNextTx. auto it3 = mapNextTx.find(txin.prevout); assert(it3 != mapNextTx.end()); assert(it3->first == &txin.prevout); assert(it3->second == &tx); i++; } assert(setParentCheck == GetMemPoolParents(it)); // Verify ancestor state is correct. setEntries setAncestors; uint64_t nNoLimit = std::numeric_limits::max(); std::string dummy; CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy); uint64_t nCountCheck = setAncestors.size() + 1; uint64_t nSizeCheck = it->GetTxSize(); Amount nFeesCheck = it->GetModifiedFee(); int64_t nSigOpCheck = it->GetSigOpCount(); for (txiter ancestorIt : setAncestors) { nSizeCheck += ancestorIt->GetTxSize(); nFeesCheck += ancestorIt->GetModifiedFee(); nSigOpCheck += ancestorIt->GetSigOpCount(); } assert(it->GetCountWithAncestors() == nCountCheck); assert(it->GetSizeWithAncestors() == nSizeCheck); assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck); // Check children against mapNextTx CTxMemPool::setEntries setChildrenCheck; auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetId(), 0)); uint64_t child_sizes = 0; int64_t child_sigop_counts = 0; for (; iter != mapNextTx.end() && iter->first->GetTxId() == it->GetTx().GetId(); ++iter) { txiter childit = mapTx.find(iter->second->GetId()); // mapNextTx points to in-mempool transactions assert(childit != mapTx.end()); if (setChildrenCheck.insert(childit).second) { child_sizes += childit->GetTxSize(); child_sigop_counts += childit->GetSigOpCount(); } } assert(setChildrenCheck == GetMemPoolChildren(it)); // Also check to make sure size is greater than sum with immediate // children. Just a sanity check, not definitive that this calc is // correct... assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); assert(it->GetSigOpCountWithDescendants() >= child_sigop_counts + it->GetSigOpCount()); if (fDependsWait) { waitingOnDependants.push_back(&(*it)); } else { CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); } } unsigned int stepsSinceLastRemove = 0; while (!waitingOnDependants.empty()) { const CTxMemPoolEntry *entry = waitingOnDependants.front(); waitingOnDependants.pop_front(); if (!mempoolDuplicate.HaveInputs(entry->GetTx())) { waitingOnDependants.push_back(entry); stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight); stepsSinceLastRemove = 0; } } for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { const TxId &txid = it->second->GetId(); indexed_transaction_set::const_iterator it2 = mapTx.find(txid); const CTransaction &tx = it2->GetTx(); assert(it2 != mapTx.end()); assert(&tx == it->second); } assert(totalTxSize == checkTotal); assert(innerUsage == cachedInnerUsage); } bool CTxMemPool::CompareDepthAndScore(const TxId &txida, const TxId &txidb) { LOCK(cs); indexed_transaction_set::const_iterator i = mapTx.find(txida); if (i == mapTx.end()) { return false; } indexed_transaction_set::const_iterator j = mapTx.find(txidb); if (j == mapTx.end()) { return true; } uint64_t counta = i->GetCountWithAncestors(); uint64_t countb = j->GetCountWithAncestors(); if (counta == countb) { return CompareTxMemPoolEntryByScore()(*i, *j); } return counta < countb; } namespace { class DepthAndScoreComparator { public: bool operator()(const CTxMemPool::indexed_transaction_set::const_iterator &a, const CTxMemPool::indexed_transaction_set::const_iterator &b) { uint64_t counta = a->GetCountWithAncestors(); uint64_t countb = b->GetCountWithAncestors(); if (counta == countb) { return CompareTxMemPoolEntryByScore()(*a, *b); } return counta < countb; } }; } // namespace std::vector CTxMemPool::GetSortedDepthAndScore() const { std::vector iters; AssertLockHeld(cs); iters.reserve(mapTx.size()); for (indexed_transaction_set::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) { iters.push_back(mi); } std::sort(iters.begin(), iters.end(), DepthAndScoreComparator()); return iters; } void CTxMemPool::queryHashes(std::vector &vtxid) const { LOCK(cs); auto iters = GetSortedDepthAndScore(); vtxid.clear(); vtxid.reserve(mapTx.size()); for (auto it : iters) { vtxid.push_back(it->GetTx().GetId()); } } static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) { return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee()}; } std::vector CTxMemPool::infoAll() const { LOCK(cs); auto iters = GetSortedDepthAndScore(); std::vector ret; ret.reserve(mapTx.size()); for (auto it : iters) { ret.push_back(GetInfo(it)); } return ret; } CTransactionRef CTxMemPool::get(const TxId &txid) const { LOCK(cs); indexed_transaction_set::const_iterator i = mapTx.find(txid); if (i == mapTx.end()) { return nullptr; } return i->GetSharedTx(); } TxMempoolInfo CTxMemPool::info(const TxId &txid) const { LOCK(cs); indexed_transaction_set::const_iterator i = mapTx.find(txid); if (i == mapTx.end()) { return TxMempoolInfo(); } return GetInfo(i); } CFeeRate CTxMemPool::estimateFee() const { LOCK(cs); uint64_t maxMempoolSize = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; // minerPolicy uses recent blocks to figure out a reasonable fee. This // may disagree with the rollingMinimumFeerate under certain scenarios // where the mempool increases rapidly, or blocks are being mined which // do not contain propagated transactions. return std::max(::minRelayTxFee, GetMinFee(maxMempoolSize)); } void CTxMemPool::PrioritiseTransaction(const TxId &txid, const Amount nFeeDelta) { { LOCK(cs); Amount &delta = mapDeltas[txid]; delta += nFeeDelta; txiter it = mapTx.find(txid); if (it != mapTx.end()) { mapTx.modify(it, update_fee_delta(delta)); // Now update all ancestors' modified fees with descendants setEntries setAncestors; uint64_t nNoLimit = std::numeric_limits::max(); std::string dummy; CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false); for (txiter ancestorIt : setAncestors) { mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0, 0)); } // Now update all descendants' modified fees with ancestors setEntries setDescendants; CalculateDescendants(it, setDescendants); setDescendants.erase(it); for (txiter descendantIt : setDescendants) { mapTx.modify(descendantIt, update_ancestor_state(0, nFeeDelta, 0, 0)); } ++nTransactionsUpdated; } } LogPrintf("PrioritiseTransaction: %s fee += %s\n", txid.ToString(), FormatMoney(nFeeDelta)); } void CTxMemPool::ApplyDelta(const TxId &txid, Amount &nFeeDelta) const { LOCK(cs); std::map::const_iterator pos = mapDeltas.find(txid); if (pos == mapDeltas.end()) { return; } nFeeDelta += pos->second; } void CTxMemPool::ClearPrioritisation(const TxId &txid) { LOCK(cs); mapDeltas.erase(txid); } const CTransaction *CTxMemPool::GetConflictTx(const COutPoint &prevout) const { const auto it = mapNextTx.find(prevout); return it == mapNextTx.end() ? nullptr : it->second; } std::optional CTxMemPool::GetIter(const TxId &txid) const { auto it = mapTx.find(txid); if (it != mapTx.end()) { return it; } return std::optional{std::nullopt}; } CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set &txids) const { CTxMemPool::setEntries ret; for (const auto &txid : txids) { const auto mi = GetIter(txid); if (mi) { ret.insert(*mi); } } return ret; } bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const { for (const CTxIn &in : tx.vin) { if (exists(in.prevout.GetTxId())) { return false; } } return true; } CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, const CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) {} bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { // If an entry in the mempool exists, always return that one, as it's // guaranteed to never conflict with the underlying cache, and it cannot // have pruned entries (as it contains full) transactions. First checking // the underlying cache risks returning a pruned entry instead. CTransactionRef ptx = mempool.get(outpoint.GetTxId()); if (ptx) { if (outpoint.GetN() < ptx->vout.size()) { coin = Coin(ptx->vout[outpoint.GetN()], MEMPOOL_HEIGHT, false); return true; } return false; } return base->GetCoin(outpoint, coin); } size_t CTxMemPool::DynamicMemoryUsage() const { LOCK(cs); // Estimate the overhead of mapTx to be 12 pointers + an allocation, as no // exact formula for boost::multi_index_contained is implemented. return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void *)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage; } void CTxMemPool::RemoveUnbroadcastTx(const TxId &txid, const bool unchecked) { LOCK(cs); if (m_unbroadcast_txids.erase(txid)) { LogPrint( BCLog::MEMPOOL, "Removed %i from set of unbroadcast txns%s\n", txid.GetHex(), (unchecked ? " before confirmation that txn was sent out" : "")); } } void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) { AssertLockHeld(cs); UpdateForRemoveFromMempool(stage, updateDescendants); for (txiter it : stage) { removeUnchecked(it, reason); } } int CTxMemPool::Expire(std::chrono::seconds time) { AssertLockHeld(cs); indexed_transaction_set::index::type::iterator it = mapTx.get().begin(); setEntries toremove; while (it != mapTx.get().end() && it->GetTime() < time) { toremove.insert(mapTx.project<0>(it)); it++; } setEntries stage; for (txiter removeit : toremove) { CalculateDescendants(removeit, stage); } RemoveStaged(stage, false, MemPoolRemovalReason::EXPIRY); return stage.size(); } void CTxMemPool::LimitSize(size_t limit, std::chrono::seconds age) { int expired = Expire(GetTime() - age); if (expired != 0) { LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired); } std::vector vNoSpendsRemaining; TrimToSize(limit, &vNoSpendsRemaining); for (const COutPoint &removed : vNoSpendsRemaining) { ::ChainstateActive().CoinsTip().Uncache(removed); } } void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry) { setEntries setAncestors; uint64_t nNoLimit = std::numeric_limits::max(); std::string dummy; CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy); return addUnchecked(entry, setAncestors); } void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) { setEntries s; if (add && mapLinks[entry].children.insert(child).second) { cachedInnerUsage += memusage::IncrementalDynamicUsage(s); } else if (!add && mapLinks[entry].children.erase(child)) { cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); } } void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add) { setEntries s; if (add && mapLinks[entry].parents.insert(parent).second) { cachedInnerUsage += memusage::IncrementalDynamicUsage(s); } else if (!add && mapLinks[entry].parents.erase(parent)) { cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); } } const CTxMemPool::setEntries & CTxMemPool::GetMemPoolParents(txiter entry) const { assert(entry != mapTx.end()); txlinksMap::const_iterator it = mapLinks.find(entry); assert(it != mapLinks.end()); return it->second.parents; } const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) const { assert(entry != mapTx.end()); txlinksMap::const_iterator it = mapLinks.find(entry); assert(it != mapLinks.end()); return it->second.children; } CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { LOCK(cs); if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) { return CFeeRate(int64_t(ceill(rollingMinimumFeeRate)) * SATOSHI); } int64_t time = GetTime(); if (time > lastRollingFeeUpdate + 10) { double halflife = ROLLING_FEE_HALFLIFE; if (DynamicMemoryUsage() < sizelimit / 4) { halflife /= 4; } else if (DynamicMemoryUsage() < sizelimit / 2) { halflife /= 2; } rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife); lastRollingFeeUpdate = time; } return CFeeRate(int64_t(ceill(rollingMinimumFeeRate)) * SATOSHI); } void CTxMemPool::trackPackageRemoved(const CFeeRate &rate) { AssertLockHeld(cs); if ((rate.GetFeePerK() / SATOSHI) > rollingMinimumFeeRate) { rollingMinimumFeeRate = rate.GetFeePerK() / SATOSHI; blockSinceLastRollingFeeBump = false; } } void CTxMemPool::TrimToSize(size_t sizelimit, std::vector *pvNoSpendsRemaining) { AssertLockHeld(cs); unsigned nTxnRemoved = 0; CFeeRate maxFeeRateRemoved(Amount::zero()); while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) { indexed_transaction_set::index::type::iterator it = mapTx.get().begin(); // We set the new mempool min fee to the feerate of the removed set, // plus the "minimum reasonable fee rate" (ie some value under which we // consider txn to have 0 fee). This way, we don't allow txn to enter // mempool with feerate equal to txn which were removed with no block in // between. CFeeRate removed(it->GetModFeesWithDescendants(), it->GetVirtualSizeWithDescendants()); removed += MEMPOOL_FULL_FEE_INCREMENT; trackPackageRemoved(removed); maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); setEntries stage; CalculateDescendants(mapTx.project<0>(it), stage); nTxnRemoved += stage.size(); std::vector txn; if (pvNoSpendsRemaining) { txn.reserve(stage.size()); for (txiter iter : stage) { txn.push_back(iter->GetTx()); } } RemoveStaged(stage, false, MemPoolRemovalReason::SIZELIMIT); if (pvNoSpendsRemaining) { for (const CTransaction &tx : txn) { for (const CTxIn &txin : tx.vin) { if (exists(txin.prevout.GetTxId())) { continue; } pvNoSpendsRemaining->push_back(txin.prevout); } } } } if (maxFeeRateRemoved > CFeeRate(Amount::zero())) { LogPrint(BCLog::MEMPOOL, "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString()); } } uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const { // find parent with highest descendant count std::vector candidates; setEntries counted; candidates.push_back(entry); uint64_t maximum = 0; while (candidates.size()) { txiter candidate = candidates.back(); candidates.pop_back(); if (!counted.insert(candidate).second) { continue; } const setEntries &parents = GetMemPoolParents(candidate); if (parents.size() == 0) { maximum = std::max(maximum, candidate->GetCountWithDescendants()); } else { for (txiter i : parents) { candidates.push_back(i); } } } return maximum; } void CTxMemPool::GetTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants) const { LOCK(cs); auto it = mapTx.find(txid); ancestors = descendants = 0; if (it != mapTx.end()) { ancestors = it->GetCountWithAncestors(); descendants = CalculateDescendantMaximum(it); } } bool CTxMemPool::IsLoaded() const { LOCK(cs); return m_is_loaded; } void CTxMemPool::SetIsLoaded(bool loaded) { LOCK(cs); m_is_loaded = loaded; } /** Maximum bytes for transactions to store for processing during reorg */ static const size_t MAX_DISCONNECTED_TX_POOL_SIZE = 20 * DEFAULT_MAX_BLOCK_SIZE; void DisconnectedBlockTransactions::addForBlock( const std::vector &vtx, CTxMemPool &pool) { AssertLockHeld(pool.cs); for (const auto &tx : reverse_iterate(vtx)) { // If we already added it, just skip. auto it = queuedTx.find(tx->GetId()); if (it != queuedTx.end()) { continue; } // Insert the transaction into the pool. addTransaction(tx); // Fill in the set of parents. std::unordered_set parents; for (const CTxIn &in : tx->vin) { parents.insert(in.prevout.GetTxId()); } // In order to make sure we keep things in topological order, we check // if we already know of the parent of the current transaction. If so, // we remove them from the set and then add them back. while (parents.size() > 0) { std::unordered_set worklist( std::move(parents)); parents.clear(); for (const TxId &txid : worklist) { // If we do not have that txid in the set, nothing needs to be // done. auto pit = queuedTx.find(txid); if (pit == queuedTx.end()) { continue; } // We have parent in our set, we reinsert them at the right // position. const CTransactionRef ptx = *pit; queuedTx.erase(pit); queuedTx.insert(ptx); // And we make sure ancestors are covered. for (const CTxIn &in : ptx->vin) { parents.insert(in.prevout.GetTxId()); } } } } // Keep the size under control. while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE) { // Drop the earliest entry, and remove its children from the // mempool. auto it = queuedTx.get().begin(); pool.removeRecursive(**it, MemPoolRemovalReason::REORG); removeEntry(it); } } void DisconnectedBlockTransactions::importMempool(CTxMemPool &pool) { AssertLockHeld(pool.cs); // addForBlock's algorithm sorts a vector of transactions back into // topological order. We use it in a separate object to create a valid // ordering of all mempool transactions, which we then splice in front of // the current queuedTx. This results in a valid sequence of transactions to // be reprocessed in updateMempoolForReorg. // We create vtx in order of the entry_time index to facilitate for // addForBlocks (which iterates in reverse order), as vtx probably end in // the correct ordering for queuedTx. std::vector vtx; vtx.reserve(pool.mapTx.size()); for (const CTxMemPoolEntry &e : pool.mapTx.get()) { vtx.push_back(e.GetSharedTx()); } pool.clear(); // Use addForBlocks to sort the transactions and then splice them in front // of queuedTx DisconnectedBlockTransactions orderedTxnPool; orderedTxnPool.addForBlock(vtx, pool); cachedInnerUsage += orderedTxnPool.cachedInnerUsage; queuedTx.get().splice( queuedTx.get().begin(), orderedTxnPool.queuedTx.get()); // We limit memory usage because we can't know if more blocks will be // disconnected while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE) { // Drop the earliest entry which, by definition, has no children removeEntry(queuedTx.get().begin()); } } void DisconnectedBlockTransactions::updateMempoolForReorg(const Config &config, bool fAddToMempool, CTxMemPool &pool) { AssertLockHeld(cs_main); AssertLockHeld(pool.cs); std::vector txidsUpdate; // disconnectpool's insertion_order index sorts the entries from oldest to // newest, but the oldest entry will be the last tx from the latest mined // block that was disconnected. // Iterate disconnectpool in reverse, so that we add transactions back to // the mempool starting with the earliest transaction that had been // previously seen in a block. for (const CTransactionRef &tx : reverse_iterate(queuedTx.get())) { // ignore validation errors in resurrected transactions TxValidationState stateDummy; if (!fAddToMempool || tx->IsCoinBase() || !AcceptToMemoryPool(config, pool, stateDummy, tx, true /* bypass_limits */, Amount::zero() /* nAbsurdFee */)) { // If the transaction doesn't make it in to the mempool, remove any // transactions that depend on it (which would now be orphans). pool.removeRecursive(*tx, MemPoolRemovalReason::REORG); } else if (pool.exists(tx->GetId())) { txidsUpdate.push_back(tx->GetId()); } } queuedTx.clear(); // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have // no in-mempool children, which is generally not true when adding // previously-confirmed transactions back to the mempool. // UpdateTransactionsFromBlock finds descendants of any transactions in the // disconnectpool that were added back and cleans up the mempool state. pool.UpdateTransactionsFromBlock(txidsUpdate); // We also need to remove any now-immature transactions pool.removeForReorg(config, &::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); // Re-limit mempool size, in case we added any transactions pool.LimitSize(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{ gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); } CTxMemPool::EpochGuard CTxMemPool::GetFreshEpoch() const { return EpochGuard(*this); } CTxMemPool::EpochGuard::EpochGuard(const CTxMemPool &in) : pool(in) { assert(!pool.m_has_epoch_guard); ++pool.m_epoch; pool.m_has_epoch_guard = true; } CTxMemPool::EpochGuard::~EpochGuard() { // prevents stale results being used ++pool.m_epoch; pool.m_has_epoch_guard = false; } diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index dc53ae525..a3e4787b6 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -1,274 +1,275 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include //! The MainSignalsInstance manages a list of shared_ptr //! callbacks. //! //! A std::unordered_map is used to track what callbacks are currently //! registered, and a std::list is to used to store the callbacks that are //! currently registered as well as any callbacks that are just unregistered //! and about to be deleted when they are done executing. struct MainSignalsInstance { private: Mutex m_mutex; //! List entries consist of a callback pointer and reference count. The //! count is equal to the number of current executions of that entry, plus 1 //! if it's registered. It cannot be 0 because that would imply it is //! unregistered and also not being executed (so shouldn't exist). struct ListEntry { std::shared_ptr callbacks; int count = 1; }; std::list m_list GUARDED_BY(m_mutex); std::unordered_map::iterator> m_map GUARDED_BY(m_mutex); public: // We are not allowed to assume the scheduler only runs in one thread, // but must ensure all callbacks happen in-order, so we end up creating // our own queue here :( SingleThreadedSchedulerClient m_schedulerClient; explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {} void Register(std::shared_ptr callbacks) { LOCK(m_mutex); auto inserted = m_map.emplace(callbacks.get(), m_list.end()); if (inserted.second) { inserted.first->second = m_list.emplace(m_list.end()); } inserted.first->second->callbacks = std::move(callbacks); } void Unregister(CValidationInterface *callbacks) { LOCK(m_mutex); auto it = m_map.find(callbacks); if (it != m_map.end()) { if (!--it->second->count) { m_list.erase(it->second); } m_map.erase(it); } } //! Clear unregisters every previously registered callback, erasing every //! map entry. After this call, the list may still contain callbacks that //! are currently executing, but it will be cleared when they are done //! executing. void Clear() { LOCK(m_mutex); for (const auto &entry : m_map) { if (!--entry.second->count) { m_list.erase(entry.second); } } m_map.clear(); } template void Iterate(F &&f) { WAIT_LOCK(m_mutex, lock); for (auto it = m_list.begin(); it != m_list.end();) { ++it->count; { REVERSE_LOCK(lock); f(*it->callbacks); } it = --it->count ? std::next(it) : m_list.erase(it); } } }; static CMainSignals g_signals; void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler &scheduler) { assert(!m_internals); m_internals.reset(new MainSignalsInstance(&scheduler)); } void CMainSignals::UnregisterBackgroundSignalScheduler() { m_internals.reset(nullptr); } void CMainSignals::FlushBackgroundCallbacks() { if (m_internals) { m_internals->m_schedulerClient.EmptyQueue(); } } size_t CMainSignals::CallbacksPending() { if (!m_internals) { return 0; } return m_internals->m_schedulerClient.CallbacksPending(); } CMainSignals &GetMainSignals() { return g_signals; } void RegisterSharedValidationInterface( std::shared_ptr callbacks) { // Each connection captures the shared_ptr to ensure that each callback is // executed before the subscriber is destroyed. For more details see #18338. g_signals.m_internals->Register(std::move(callbacks)); } void RegisterValidationInterface(CValidationInterface *callbacks) { // Create a shared_ptr with a no-op deleter - CValidationInterface lifecycle // is managed by the caller. RegisterSharedValidationInterface( {callbacks, [](CValidationInterface *) {}}); } void UnregisterSharedValidationInterface( std::shared_ptr callbacks) { UnregisterValidationInterface(callbacks.get()); } void UnregisterValidationInterface(CValidationInterface *callbacks) { if (g_signals.m_internals) { g_signals.m_internals->Unregister(callbacks); } } void UnregisterAllValidationInterfaces() { if (!g_signals.m_internals) { return; } g_signals.m_internals->Clear(); } void CallFunctionInValidationInterfaceQueue(std::function func) { g_signals.m_internals->m_schedulerClient.AddToProcessQueue(std::move(func)); } void SyncWithValidationInterfaceQueue() { AssertLockNotHeld(cs_main); // Block until the validation queue drains std::promise promise; CallFunctionInValidationInterfaceQueue([&promise] { promise.set_value(); }); promise.get_future().wait(); } // Use a macro instead of a function for conditional logging to prevent // evaluating arguments when logging is not enabled. // // NOTE: The lambda captures all local variables by value. #define ENQUEUE_AND_LOG_EVENT(event, fmt, name, ...) \ do { \ auto local_name = (name); \ LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__); \ m_internals->m_schedulerClient.AddToProcessQueue([=] { \ LOG_EVENT(fmt, local_name, __VA_ARGS__); \ event(); \ }); \ } while (0) #define LOG_EVENT(fmt, ...) LogPrint(BCLog::VALIDATION, fmt "\n", __VA_ARGS__) void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { // Dependencies exist that require UpdatedBlockTip events to be delivered in // the order in which the chain actually updates. One way to ensure this is // for the caller to invoke this signal in the same critical section where // the chain is updated auto event = [pindexNew, pindexFork, fInitialDownload, this] { m_internals->Iterate([&](CValidationInterface &callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); }); }; ENQUEUE_AND_LOG_EVENT( event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__, pindexNew->GetBlockHash().ToString(), pindexFork ? pindexFork->GetBlockHash().ToString() : "null", fInitialDownload); } void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) { auto event = [ptx, this] { m_internals->Iterate([&](CValidationInterface &callbacks) { callbacks.TransactionAddedToMempool(ptx); }); }; ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s", __func__, ptx->GetHash().ToString()); } -void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) { - auto event = [ptx, this] { +void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx, + MemPoolRemovalReason reason) { + auto event = [ptx, reason, this] { m_internals->Iterate([&](CValidationInterface &callbacks) { - callbacks.TransactionRemovedFromMempool(ptx); + callbacks.TransactionRemovedFromMempool(ptx, reason); }); }; ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s", __func__, ptx->GetHash().ToString()); } void CMainSignals::BlockConnected(const std::shared_ptr &pblock, const CBlockIndex *pindex) { auto event = [pblock, pindex, this] { m_internals->Iterate([&](CValidationInterface &callbacks) { callbacks.BlockConnected(pblock, pindex); }); }; ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__, pblock->GetHash().ToString(), pindex->nHeight); } void CMainSignals::BlockDisconnected( const std::shared_ptr &pblock, const CBlockIndex *pindex) { auto event = [pblock, pindex, this] { m_internals->Iterate([&](CValidationInterface &callbacks) { callbacks.BlockDisconnected(pblock, pindex); }); }; ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__, pblock->GetHash().ToString()); } void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) { auto event = [locator, this] { m_internals->Iterate([&](CValidationInterface &callbacks) { callbacks.ChainStateFlushed(locator); }); }; ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__, locator.IsNull() ? "null" : locator.vHave.front().ToString()); } void CMainSignals::BlockChecked(const CBlock &block, const BlockValidationState &state) { LOG_EVENT("%s: block hash=%s state=%s", __func__, block.GetHash().ToString(), state.ToString()); m_internals->Iterate([&](CValidationInterface &callbacks) { callbacks.BlockChecked(block, state); }); } void CMainSignals::NewPoWValidBlock( const CBlockIndex *pindex, const std::shared_ptr &block) { LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString()); m_internals->Iterate([&](CValidationInterface &callbacks) { callbacks.NewPoWValidBlock(pindex, block); }); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 1b0ebbaed..65b779bf6 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -1,234 +1,237 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_VALIDATIONINTERFACE_H #define BITCOIN_VALIDATIONINTERFACE_H #include // CTransaction(Ref) #include #include #include extern RecursiveMutex cs_main; class BlockValidationState; class CBlock; class CBlockIndex; struct CBlockLocator; class CConnman; class CValidationInterface; class uint256; class CScheduler; +enum class MemPoolRemovalReason; /** Register subscriber */ void RegisterValidationInterface(CValidationInterface *callbacks); /** * Unregister subscriber. DEPRECATED. This is not safe to use when the RPC * server or main message handler thread is running. */ void UnregisterValidationInterface(CValidationInterface *callbacks); /** Unregister all subscribers */ void UnregisterAllValidationInterfaces(); // Alternate registration functions that release a shared_ptr after the last // notification is sent. These are useful for race-free cleanup, since // unregistration is nonblocking and can return before the last notification is // processed. /** Register subscriber */ void RegisterSharedValidationInterface( std::shared_ptr callbacks); /** Unregister subscriber */ void UnregisterSharedValidationInterface( std::shared_ptr callbacks); /** * Pushes a function to callback onto the notification queue, guaranteeing any * callbacks generated prior to now are finished when the function is called. * * Be very careful blocking on func to be called if any locks are held - * validation interface clients may not be able to make progress as they often * wait for things like cs_main, so blocking until func is called with cs_main * will result in a deadlock (that DEBUG_LOCKORDER will miss). */ void CallFunctionInValidationInterfaceQueue(std::function func); /** * This is a synonym for the following, which asserts certain locks are not * held: * std::promise promise; * CallFunctionInValidationInterfaceQueue([&promise] { * promise.set_value(); * }); * promise.get_future().wait(); */ void SyncWithValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main); /** * Implement this to subscribe to events generated in validation * * Each CValidationInterface() subscriber will receive event callbacks * in the order in which the events were generated by validation. * Furthermore, each ValidationInterface() subscriber may assume that * callbacks effectively run in a single thread with single-threaded * memory consistency. That is, for a given ValidationInterface() * instantiation, each callback will complete before the next one is * invoked. This means, for example when a block is connected that the * UpdatedBlockTip() callback may depend on an operation performed in * the BlockConnected() callback without worrying about explicit * synchronization. No ordering should be assumed across * ValidationInterface() subscribers. */ class CValidationInterface { protected: /** * Protected destructor so that instances can only be deleted by derived * classes. If that restriction is no longer desired, this should be made * public and virtual. */ ~CValidationInterface() = default; /** * Notifies listeners when the block chain tip advances. * * When multiple blocks are connected at once, UpdatedBlockTip will be * called on the final tip but may not be called on every intermediate tip. * If the latter behavior is desired, subscribe to BlockConnected() instead. * * Called on a background thread. */ virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {} /** * Notifies listeners of a transaction having been added to mempool. * * Called on a background thread. */ virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {} /** * Notifies listeners of a transaction leaving mempool. * * This notification fires for transactions that are removed from the * mempool for the following reasons: * * - EXPIRY (expired from mempool after -mempoolexpiry hours) * - SIZELIMIT (removed in size limiting if the mempool exceeds -maxmempool * megabytes) * - REORG (removed during a reorg) * - CONFLICT (removed because it conflicts with in-block transaction) * * This does not fire for transactions that are removed from the mempool * because they have been included in a block. Any client that is interested * in transactions removed from the mempool for inclusion in a block can * learn about those transactions from the BlockConnected notification. * * Transactions that are removed from the mempool because they conflict * with a transaction in the new block will have * TransactionRemovedFromMempool events fired *before* the BlockConnected * event is fired. If multiple blocks are connected in one step, then the * ordering could be: * * - TransactionRemovedFromMempool(tx1 from block A) * - TransactionRemovedFromMempool(tx2 from block A) * - TransactionRemovedFromMempool(tx1 from block B) * - TransactionRemovedFromMempool(tx2 from block B) * - BlockConnected(A) * - BlockConnected(B) * * Called on a background thread. */ - virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx) {} + virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx, + MemPoolRemovalReason reason) {} /** * Notifies listeners of a block being connected. * Provides a vector of transactions evicted from the mempool as a result. * * Called on a background thread. */ virtual void BlockConnected(const std::shared_ptr &block, const CBlockIndex *pindex) {} /** * Notifies listeners of a block being disconnected * * Called on a background thread. */ virtual void BlockDisconnected(const std::shared_ptr &block, const CBlockIndex *pindex) {} /** * Notifies listeners of the new active block chain on-disk. * * Prior to this callback, any updates are not guaranteed to persist on disk * (ie clients need to handle shutdown/restart safety by being able to * understand when some updates were lost due to unclean shutdown). * * When this callback is invoked, the validation changes done by any prior * callback are guaranteed to exist on disk and survive a restart, including * an unclean shutdown. * * Provides a locator describing the best chain, which is likely useful for * storing current state on disk in client DBs. * * Called on a background thread. */ virtual void ChainStateFlushed(const CBlockLocator &locator) {} /** * Notifies listeners of a block validation result. * If the provided BlockValidationState IsValid, the provided block * is guaranteed to be the current best block at the time the * callback was generated (not necessarily now) */ virtual void BlockChecked(const CBlock &, const BlockValidationState &) {} /** * Notifies listeners that a block which builds directly on our current tip * has been received and connected to the headers tree, though not validated * yet. */ virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr &block){}; friend class CMainSignals; }; struct MainSignalsInstance; class CMainSignals { private: std::unique_ptr m_internals; friend void ::RegisterSharedValidationInterface( std::shared_ptr); friend void ::UnregisterValidationInterface(CValidationInterface *); friend void ::UnregisterAllValidationInterfaces(); friend void ::CallFunctionInValidationInterfaceQueue( std::function func); public: /** * Register a CScheduler to give callbacks which should run in the * background (may only be called once) */ void RegisterBackgroundSignalScheduler(CScheduler &scheduler); /** * Unregister a CScheduler to give callbacks which should run in the * background - these callbacks will now be dropped! */ void UnregisterBackgroundSignalScheduler(); /** Call any remaining callbacks on the calling thread */ void FlushBackgroundCallbacks(); size_t CallbacksPending(); void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload); void TransactionAddedToMempool(const CTransactionRef &); - void TransactionRemovedFromMempool(const CTransactionRef &); + void TransactionRemovedFromMempool(const CTransactionRef &, + MemPoolRemovalReason); void BlockConnected(const std::shared_ptr &, const CBlockIndex *pindex); void BlockDisconnected(const std::shared_ptr &, const CBlockIndex *pindex); void ChainStateFlushed(const CBlockLocator &); void BlockChecked(const CBlock &, const BlockValidationState &); void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr &); }; CMainSignals &GetMainSignals(); #endif // BITCOIN_VALIDATIONINTERFACE_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b66afafa4..001963df6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1,5078 +1,5113 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include