diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -289,7 +289,7 @@ FillBlock(block2, block2_out, lock); } void findCoins(std::map &coins) override { - return FindCoins(coins); + return FindCoins(m_node, coins); } double guessVerificationProgress(const BlockHash &block_hash) override { LOCK(cs_main); diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -199,9 +199,12 @@ return m_context.connman ? m_context.connman->GetTotalBytesSent() : 0; } - size_t getMempoolSize() override { return g_mempool.size(); } + size_t getMempoolSize() override { + return m_context.mempool ? m_context.mempool->size() : 0; + } size_t getMempoolDynamicUsage() override { - return g_mempool.DynamicMemoryUsage(); + return m_context.mempool ? m_context.mempool->DynamicMemoryUsage() + : 0; } bool getHeaderTip(int &height, int64_t &block_time) override { LOCK(::cs_main); @@ -245,7 +248,10 @@ bool getNetworkActive() override { return m_context.connman && m_context.connman->GetNetworkActive(); } - CFeeRate estimateSmartFee() override { return g_mempool.estimateFee(); } + CFeeRate estimateSmartFee() override { + return m_context.mempool ? m_context.mempool->estimateFee() + : CFeeRate(); + } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } UniValue executeRpc(Config &config, const std::string &command, const UniValue ¶ms, diff --git a/src/node/coin.h b/src/node/coin.h --- a/src/node/coin.h +++ b/src/node/coin.h @@ -9,14 +9,16 @@ class COutPoint; class Coin; +struct NodeContext; /** * 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. * + * @param[in] node The node context to use for lookup * @param[in,out] coins map to fill */ -void FindCoins(std::map &coins); +void FindCoins(const NodeContext &node, std::map &coins); #endif // BITCOIN_NODE_COIN_H diff --git a/src/node/coin.cpp b/src/node/coin.cpp --- a/src/node/coin.cpp +++ b/src/node/coin.cpp @@ -4,13 +4,15 @@ #include +#include #include #include -void FindCoins(std::map &coins) { - LOCK2(cs_main, ::g_mempool.cs); +void FindCoins(const NodeContext &node, std::map &coins) { + assert(node.mempool); + LOCK2(cs_main, node.mempool->cs); CCoinsViewCache &chain_view = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool mempool_view(&chain_view, ::g_mempool); + CCoinsViewMemPool mempool_view(&chain_view, *node.mempool); for (auto &coin : coins) { if (!mempool_view.GetCoin(coin.first, coin.second)) { // Either the coin is not in the CCoinsViewCache or is spent. Clear diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -28,6 +28,7 @@ // before RPC server is accepting calls, and reset after chain clients and // RPC sever are stopped. node.connman should never be null here. assert(node.connman); + assert(node.mempool); std::promise promise; TxId txid = tx->GetId(); bool callback_set = false; @@ -47,10 +48,10 @@ } } - if (!g_mempool.exists(txid)) { + if (!node.mempool->exists(txid)) { // Transaction is not already in the mempool. Submit it. TxValidationState state; - if (!AcceptToMemoryPool(config, g_mempool, state, std::move(tx), + if (!AcceptToMemoryPool(config, *node.mempool, state, std::move(tx), false /* bypass_limits */, max_tx_fee)) { err_string = FormatStateMessage(state); if (state.IsInvalid()) { diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -113,6 +113,7 @@ {}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } node.context()->connman = std::move(test.m_node.connman); + node.context()->mempool = std::move(test.m_node.mempool); std::shared_ptr wallet = std::make_shared( Params(), node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock()); diff --git a/src/rest.cpp b/src/rest.cpp --- a/src/rest.cpp +++ b/src/rest.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,23 @@ return false; } +/** + * Get the node context mempool. + * + * Set the HTTP error and return nullptr if node context + * mempool is not found. + * + * @param[in] req the HTTP request + * return pointer to the mempool or nullptr if no mempool found + */ +static CTxMemPool *GetMemPool(HTTPRequest *req) { + if (!g_rpc_node || !g_rpc_node->mempool) { + RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found"); + return nullptr; + } + return g_rpc_node->mempool; +} + static RetFormat ParseDataFormat(std::string ¶m, const std::string &strReq) { const std::string::size_type pos = strReq.rfind('.'); @@ -328,12 +346,17 @@ return false; } + const CTxMemPool *mempool = GetMemPool(req); + if (!mempool) { + return false; + } + std::string param; const RetFormat rf = ParseDataFormat(param, strURIPart); switch (rf) { case RetFormat::JSON: { - UniValue mempoolInfoObject = MempoolInfoToJSON(::g_mempool); + UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool); std::string strJSON = mempoolInfoObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); @@ -353,12 +376,17 @@ return false; } + const CTxMemPool *mempool = GetMemPool(req); + if (!mempool) { + return false; + } + std::string param; const RetFormat rf = ParseDataFormat(param, strURIPart); switch (rf) { case RetFormat::JSON: { - UniValue mempoolObject = MempoolToJSON(::g_mempool, true); + UniValue mempoolObject = MempoolToJSON(*mempool, true); std::string strJSON = mempoolObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); @@ -571,12 +599,17 @@ }; if (fCheckMemPool) { + const CTxMemPool *mempool = GetMemPool(req); + if (!mempool) { + return false; + } + // use db+mempool as cache backend in case user likes to query // mempool - LOCK2(cs_main, g_mempool.cs); + LOCK2(cs_main, mempool->cs); CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool viewMempool(&viewChain, g_mempool); - process_utxos(viewMempool, g_mempool); + CCoinsViewMemPool viewMempool(&viewChain, *mempool); + process_utxos(viewMempool, *mempool); } else { // no need to lock mempool! LOCK(cs_main); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -595,7 +595,7 @@ fVerbose = request.params[0].get_bool(); } - return MempoolToJSON(::g_mempool, fVerbose); + return MempoolToJSON(EnsureMemPool(), fVerbose); } static UniValue getmempoolancestors(const Config &config, @@ -635,10 +635,11 @@ TxId txid(ParseHashV(request.params[0], "parameter 1")); - LOCK(g_mempool.cs); + const CTxMemPool &mempool = EnsureMemPool(); + LOCK(mempool.cs); - CTxMemPool::txiter it = g_mempool.mapTx.find(txid); - if (it == g_mempool.mapTx.end()) { + CTxMemPool::txiter it = mempool.mapTx.find(txid); + if (it == mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } @@ -646,8 +647,8 @@ CTxMemPool::setEntries setAncestors; uint64_t noLimit = std::numeric_limits::max(); std::string dummy; - g_mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, - noLimit, noLimit, dummy, false); + mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, + noLimit, noLimit, dummy, false); if (!fVerbose) { UniValue o(UniValue::VARR); @@ -662,7 +663,7 @@ const CTxMemPoolEntry &e = *ancestorIt; const TxId &_txid = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); - entryToJSON(::g_mempool, info, e); + entryToJSON(mempool, info, e); o.pushKV(_txid.ToString(), info); } return o; @@ -706,16 +707,17 @@ TxId txid(ParseHashV(request.params[0], "parameter 1")); - LOCK(g_mempool.cs); + const CTxMemPool &mempool = EnsureMemPool(); + LOCK(mempool.cs); - CTxMemPool::txiter it = g_mempool.mapTx.find(txid); - if (it == g_mempool.mapTx.end()) { + CTxMemPool::txiter it = mempool.mapTx.find(txid); + if (it == mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } CTxMemPool::setEntries setDescendants; - g_mempool.CalculateDescendants(it, setDescendants); + mempool.CalculateDescendants(it, setDescendants); // CTxMemPool::CalculateDescendants will include the given tx setDescendants.erase(it); @@ -732,7 +734,7 @@ const CTxMemPoolEntry &e = *descendantIt; const TxId &_txid = e.GetTx().GetId(); UniValue info(UniValue::VOBJ); - entryToJSON(::g_mempool, info, e); + entryToJSON(mempool, info, e); o.pushKV(_txid.ToString(), info); } return o; @@ -757,17 +759,18 @@ TxId txid(ParseHashV(request.params[0], "parameter 1")); - LOCK(g_mempool.cs); + const CTxMemPool &mempool = EnsureMemPool(); + LOCK(mempool.cs); - CTxMemPool::txiter it = g_mempool.mapTx.find(txid); - if (it == g_mempool.mapTx.end()) { + CTxMemPool::txiter it = mempool.mapTx.find(txid); + if (it == mempool.mapTx.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); } const CTxMemPoolEntry &e = *it; UniValue info(UniValue::VOBJ); - entryToJSON(::g_mempool, info, e); + entryToJSON(mempool, info, e); return info; } @@ -1216,9 +1219,10 @@ CCoinsViewCache *coins_view = &::ChainstateActive().CoinsTip(); if (fMempool) { - LOCK(g_mempool.cs); - CCoinsViewMemPool view(coins_view, g_mempool); - if (!view.GetCoin(out, coin) || g_mempool.isSpent(out)) { + const CTxMemPool &mempool = EnsureMemPool(); + LOCK(mempool.cs); + CCoinsViewMemPool view(coins_view, mempool); + if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { return NullUniValue; } } else { @@ -1653,7 +1657,7 @@ } .Check(request); - return MempoolInfoToJSON(::g_mempool); + return MempoolInfoToJSON(EnsureMemPool()); } static UniValue preciousblock(const Config &config, @@ -2319,11 +2323,13 @@ } .Check(request); - if (!::g_mempool.IsLoaded()) { + const CTxMemPool &mempool = EnsureMemPool(); + + if (!mempool.IsLoaded()) { throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet"); } - if (!DumpMempool(::g_mempool)) { + if (!DumpMempool(mempool)) { throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk"); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -131,11 +131,13 @@ const uint64_t nExcessiveBlockSize = config.GetMaxBlockSize(); + const CTxMemPool &mempool = EnsureMemPool(); + unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd && !ShutdownRequested()) { std::unique_ptr pblocktemplate( - BlockAssembler(config, g_mempool).CreateNewBlock(coinbase_script)); + BlockAssembler(config, mempool).CreateNewBlock(coinbase_script)); if (!pblocktemplate.get()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); @@ -252,6 +254,7 @@ .Check(request); LOCK(cs_main); + const CTxMemPool &mempool = EnsureMemPool(); UniValue obj(UniValue::VOBJ); obj.pushKV("blocks", int(::ChainActive().Height())); @@ -263,7 +266,7 @@ } obj.pushKV("difficulty", double(GetDifficulty(::ChainActive().Tip()))); obj.pushKV("networkhashps", getnetworkhashps(config, request)); - obj.pushKV("pooledtx", uint64_t(g_mempool.size())); + obj.pushKV("pooledtx", uint64_t(mempool.size())); obj.pushKV("chain", config.GetChainParams().NetworkIDString()); obj.pushKV("warnings", GetWarnings("statusbar")); @@ -310,7 +313,7 @@ "prioritisetransaction must be 0."); } - g_mempool.PrioritiseTransaction(txid, nAmount); + EnsureMemPool().PrioritiseTransaction(txid, nAmount); return true; } @@ -548,6 +551,7 @@ } static unsigned int nTransactionsUpdatedLast; + const CTxMemPool &mempool = EnsureMemPool(); if (!lpval.isNull()) { // Wait to respond until either the best block changes, OR a minute has @@ -580,8 +584,8 @@ if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update - // without holding ::mempool.cs to avoid deadlocks - if (g_mempool.GetTransactionsUpdated() != + // without holding the mempool look to avoid deadlocks + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) { break; } @@ -603,21 +607,21 @@ static int64_t nStart; static std::unique_ptr pblocktemplate; if (pindexPrev != ::ChainActive().Tip() || - (g_mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any // failures from here on pindexPrev = nullptr; // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = g_mempool.GetTransactionsUpdated(); + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex *pindexPrevNew = ::ChainActive().Tip(); nStart = GetTime(); // Create new block CScript scriptDummy = CScript() << OP_TRUE; pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptDummy); + BlockAssembler(config, mempool).CreateNewBlock(scriptDummy); if (!pblocktemplate) { throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); } @@ -870,7 +874,8 @@ } .Check(request); - return ValueFromAmount(g_mempool.estimateFee().GetFeePerK()); + const CTxMemPool &mempool = EnsureMemPool(); + return ValueFromAmount(mempool.estimateFee().GetFeePerK()); } // clang-format off diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -709,10 +709,11 @@ CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { + const CTxMemPool &mempool = EnsureMemPool(); LOCK(cs_main); - LOCK(g_mempool.cs); + LOCK(mempool.cs); CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool viewMempool(&viewChain, g_mempool); + CCoinsViewMemPool viewMempool(&viewChain, mempool); // temporarily switch cache backend to db+mempool view view.SetBackend(viewMempool); @@ -873,7 +874,7 @@ // Create empty map entry keyed by prevout. coins[txin.prevout]; } - FindCoins(coins); + FindCoins(*g_rpc_node, coins); // Parse the prevtxs array ParsePrevouts(request.params[2], &keystore, coins); @@ -1040,6 +1041,8 @@ max_raw_tx_fee = fr.GetFee(sz); } + CTxMemPool &mempool = EnsureMemPool(); + UniValue result(UniValue::VARR); UniValue result_0(UniValue::VOBJ); result_0.pushKV("txid", txid.GetHex()); @@ -1049,7 +1052,7 @@ { LOCK(cs_main); test_accept_res = AcceptToMemoryPool( - config, g_mempool, state, std::move(tx), false /* bypass_limits */, + config, mempool, state, std::move(tx), false /* bypass_limits */, max_raw_tx_fee, true /* test_accept */); } result_0.pushKV("allowed", test_accept_res); @@ -1725,9 +1728,10 @@ CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { - LOCK2(cs_main, g_mempool.cs); + const CTxMemPool &mempool = EnsureMemPool(); + LOCK2(cs_main, mempool.cs); CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool viewMempool(&viewChain, g_mempool); + CCoinsViewMemPool viewMempool(&viewChain, mempool); // temporarily switch cache backend to db+mempool view view.SetBackend(viewMempool); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -137,16 +137,12 @@ "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; - NodeContext node; - node.chain = interfaces::MakeChain(node, GetConfig().GetChainParams()); - g_rpc_node = &node; r = CallRPC(std::string("signrawtransactionwithkey ") + notsigned + " [] " + prevout); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); r = CallRPC(std::string("signrawtransactionwithkey ") + notsigned + " [" + privkey1 + "," + privkey2 + "] " + prevout); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); - g_rpc_node = nullptr; } BOOST_AUTO_TEST_CASE(rpc_rawsign_missing_amount) { @@ -177,10 +173,6 @@ bool exceptionThrownDueToMissingAmount = false, errorWasMissingAmount = false; - NodeContext node; - node.chain = interfaces::MakeChain(node, GetConfig().GetChainParams()); - g_rpc_node = &node; - try { r = CallRPC(std::string("signrawtransactionwithkey ") + notsigned + " [" + privkey1 + "," + privkey2 + "] " + prevout); @@ -192,8 +184,6 @@ } BOOST_CHECK(exceptionThrownDueToMissingAmount == true); BOOST_CHECK(errorWasMissingAmount == true); - - g_rpc_node = nullptr; } BOOST_AUTO_TEST_CASE(rpc_createraw_op_return) {