diff --git a/doc/JSON-RPC-interface.md b/doc/JSON-RPC-interface.md index e384fa041335..9b36ad5d56d3 100644 --- a/doc/JSON-RPC-interface.md +++ b/doc/JSON-RPC-interface.md @@ -5,6 +5,28 @@ The headless daemon `dashd` has the JSON-RPC API enabled by default, the GUI option. In the GUI it is possible to execute RPC methods in the Debug Console Dialog. +## Parameter passing + +The JSON-RPC server supports both _by-position_ and _by-name_ [parameter +structures](https://www.jsonrpc.org/specification#parameter_structures) +described in the JSON-RPC specification. For extra convenience, to avoid the +need to name every parameter value, all RPC methods accept a named parameter +called `args`, which can be set to an array of initial positional values that +are combined with named values. + +Examples: + +```sh +# "params": ["mywallet", false, false, "", false, false, true] +dash-cli createwallet mywallet false false "" false false true + +# "params": {"wallet_name": "mywallet", "load_on_startup": true} +dash-cli -named createwallet wallet_name=mywallet load_on_startup=true + +# "params": {"args": ["mywallet"], "load_on_startup": true} +dash-cli -named createwallet mywallet load_on_startup=true +``` + ## Versioning The RPC interface might change from one major version of Dash Core to the diff --git a/doc/release-notes-19550.md b/doc/release-notes-19550.md new file mode 100644 index 000000000000..6903ec2cdf3e --- /dev/null +++ b/doc/release-notes-19550.md @@ -0,0 +1,6 @@ +New RPCs +-------- + +- The `getindexinfo` RPC returns the actively running indices of the node, + including their current sync status and height. It also accepts an `index_name` + to specify returning only the status of that index. diff --git a/doc/release-notes-19762.md b/doc/release-notes-19762.md new file mode 100644 index 000000000000..9078a08da9c6 --- /dev/null +++ b/doc/release-notes-19762.md @@ -0,0 +1,19 @@ +JSON-RPC +--- + +All JSON-RPC methods accept a new [named +parameter](JSON-RPC-interface.md#parameter-passing) called `args` that can +contain positional parameter values. This is a convenience to allow some +parameter values to be passed by name without having to name every value. The +python test framework and `dash-cli` tool both take advantage of this, so +for example: + +```sh +dash-cli -named createwallet wallet_name=mywallet load_on_startup=1 +``` + +Can now be shortened to: + +```sh +dash-cli -named createwallet mywallet load_on_startup=1 +``` diff --git a/src/Makefile.am b/src/Makefile.am index a72d1d758d26..fb897c43f9d1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -247,6 +247,7 @@ BITCOIN_CORE_H = \ netbase.h \ netfulfilledman.h \ netmessagemaker.h \ + node/blockstorage.h \ node/coin.h \ node/coinstats.h \ node/context.h \ @@ -441,6 +442,7 @@ libbitcoin_server_a_SOURCES = \ net.cpp \ netfulfilledman.cpp \ net_processing.cpp \ + node/blockstorage.cpp \ node/coin.cpp \ node/coinstats.cpp \ node/context.cpp \ diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 97b2b67531ef..cb2a86ff1f35 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -30,15 +30,6 @@ const std::function G_TRANSLATION_FUN = nullptr; -static void WaitForShutdown(NodeContext& node) -{ - while (!ShutdownRequested()) - { - UninterruptibleSleep(std::chrono::milliseconds{200}); - } - Interrupt(node); -} - ////////////////////////////////////////////////////////////////////////////// // // Start @@ -155,12 +146,10 @@ static bool AppInit(int argc, char* argv[]) PrintExceptionContinue(std::current_exception(), "AppInit()"); } - if (!fRet) - { - Interrupt(node); - } else { - WaitForShutdown(node); + if (fRet) { + WaitForShutdown(); } + Interrupt(node); Shutdown(node); return fRet; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 523f38c8f858..963a3be634b1 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -780,7 +780,7 @@ class CRegTestParams : public CChainParams { nDefaultPort = 19899; nDefaultPlatformP2PPort = 22200; nDefaultPlatformHTTPPort = 22201; - nPruneAfterHeight = 1000; + nPruneAfterHeight = gArgs.GetBoolArg("-fastprune", false) ? 100 : 1000; m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; diff --git a/src/context.h b/src/context.h index a2d0c3ac7b90..6570cdf0befd 100644 --- a/src/context.h +++ b/src/context.h @@ -24,10 +24,10 @@ using CoreContext = std::variant>; template -T* GetContext(const CoreContext& ctx) noexcept +T* GetContext(const CoreContext& context) noexcept { - return std::holds_alternative>(ctx) - ? &std::get>(ctx).get() + return std::holds_alternative>(context) + ? &std::get>(context).get() : nullptr; } diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp index 34797da3331c..42df4fee37f9 100644 --- a/src/evo/cbtx.cpp +++ b/src/evo/cbtx.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index dda04ad8f112..14f7609a77c1 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 481f77a077fb..e17f071ccd1f 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 025ffccd6a71..a81becfe8b39 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -555,7 +555,7 @@ bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv) return false; } - const auto& [_, inserted] = setHash->insert(inv.hash); + const auto& [_itr, inserted] = setHash->insert(inv.hash); if (inserted) { LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest added inv to requested set\n"); diff --git a/src/index/base.cpp b/src/index/base.cpp index c8fd962a186c..623b4f438def 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -4,11 +4,12 @@ #include #include +#include #include #include #include #include -#include +#include // For g_chainman #include constexpr char DB_BEST_BLOCK = 'B'; @@ -64,6 +65,43 @@ bool BaseIndex::Init() m_best_block_index = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); } m_synced = m_best_block_index.load() == ::ChainActive().Tip(); + if (!m_synced) { + bool prune_violation = false; + if (!m_best_block_index) { + // index is not built yet + // make sure we have all block data back to the genesis + const CBlockIndex* block = ::ChainActive().Tip(); + while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { + block = block->pprev; + } + prune_violation = block != ::ChainActive().Genesis(); + } + // in case the index has a best block set and is not fully synced + // check if we have the required blocks to continue building the index + else { + const CBlockIndex* block_to_test = m_best_block_index.load(); + if (!ChainActive().Contains(block_to_test)) { + // if the bestblock is not part of the mainchain, find the fork + // and make sure we have all data down to the fork + block_to_test = ::ChainActive().FindFork(block_to_test); + } + const CBlockIndex* block = ::ChainActive().Tip(); + prune_violation = true; + // check backwards from the tip if we have all block data until we reach the indexes bestblock + while (block_to_test && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { + if (block_to_test == block) { + prune_violation = false; + break; + } + block = block->pprev; + } + } + if (prune_violation) { + // throw error and graceful shutdown if we can't build the index + FatalError("%s: %s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)", __func__, GetName()); + return false; + } + } return true; } @@ -176,6 +214,10 @@ bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_ti assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip); // In the case of a reorg, ensure persisted block locator is not stale. + // Pruning has a minimum of 288 blocks-to-keep and getting the index + // out of sync may be possible but a users fault. + // In case we reorg beyond the pruned depth, ReadBlockFromDisk would + // throw and lead to a graceful shutdown m_best_block_index = new_tip; if (!Commit()) { // If commit fails, revert the best block index to avoid corruption. @@ -318,3 +360,12 @@ void BaseIndex::Stop() m_thread_sync.join(); } } + +IndexSummary BaseIndex::GetSummary() const +{ + IndexSummary summary{}; + summary.name = GetName(); + summary.synced = m_synced; + summary.best_block_height = m_best_block_index ? m_best_block_index.load()->nHeight : 0; + return summary; +} diff --git a/src/index/base.h b/src/index/base.h index 68f72c16635d..aa25a5265391 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -13,6 +13,12 @@ class CBlockIndex; +struct IndexSummary { + std::string name; + bool synced{false}; + int best_block_height{0}; +}; + /** * Base class for indices of blockchain data. This implements * CValidationInterface and ensures blocks are indexed sequentially according @@ -113,6 +119,9 @@ class BaseIndex : public CValidationInterface /// Stops the instance from staying in sync with blockchain updates. void Stop(); + + /// Get a summary of the index and its state. + IndexSummary GetSummary() const; }; #endif // BITCOIN_INDEX_BASE_H diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp index 2b6e4b7ef796..4152dfee5a52 100644 --- a/src/index/blockfilterindex.cpp +++ b/src/index/blockfilterindex.cpp @@ -6,9 +6,8 @@ #include #include +#include #include -#include -#include /* The index database stores three items for each block: the disk location of the encoded filter, * its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by diff --git a/src/init.cpp b/src/init.cpp index 6e6df4d466d1..abf2645e08b3 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -123,7 +123,6 @@ static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; -static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; static CDSNotificationInterface* pdsNotificationInterface = nullptr; @@ -189,8 +188,6 @@ static fs::path GetPidFile(const ArgsManager& args) static std::unique_ptr globalVerifyHandle; -static std::thread g_load_block; - void Interrupt(NodeContext& node) { InterruptHTTPServer(); @@ -266,7 +263,7 @@ void PrepareShutdown(NodeContext& node) // After everything has been shut down, but before things get flushed, stop the // CScheduler/checkqueue, threadGroup and load block thread. if (node.scheduler) node.scheduler->stop(); - if (g_load_block.joinable()) g_load_block.join(); + if (node.chainman && node.chainman->m_load_block.joinable()) node.chainman->m_load_block.join(); StopScriptCheckWorkerThreads(); // After there are no more peers/RPC left to give us new data which may generate @@ -518,6 +515,7 @@ void SetupServerArgs(NodeContext& node) #endif argsman.AddArg("-assumevalid=", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-blocksdir=", "Specify directory to hold blocks subdirectory for *.dat files (default: )", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-fastprune", "Use smaller block files and lower minimum prune height for testing purposes", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); #if HAVE_SYSTEM argsman.AddArg("-blocknotify=", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #endif @@ -824,20 +822,6 @@ static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) } } -struct CImportingNow -{ - CImportingNow() { - assert(fImporting == false); - fImporting = true; - } - - ~CImportingNow() { - assert(fImporting == true); - fImporting = false; - } -}; - - // If we're using -prune with -reindex, then delete block files that will be ignored by the // reindex. Since reindexing works by starting at block file 0 and looping until a blockfile // is missing, do the same here to delete any later block files after a gap. Also delete all @@ -879,103 +863,6 @@ static void CleanupBlockRevFiles() } } -static void ThreadImport(ChainstateManager& chainman, std::vector vImportFiles, const ArgsManager& args) -{ - ScheduleBatchPriority(); - - { - CImportingNow imp; - - // -reindex - if (fReindex) { - int nFile = 0; - while (true) { - FlatFilePos pos(nFile, 0); - if (!fs::exists(GetBlockPosFilename(pos))) - break; // No block files left to reindex - FILE *file = OpenBlockFile(pos, true); - if (!file) - break; // This error is logged in OpenBlockFile - LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); - ::ChainstateActive().LoadExternalBlockFile(file, &pos); - if (ShutdownRequested()) { - LogPrintf("Shutdown requested. Exit %s\n", __func__); - return; - } - nFile++; - } - pblocktree->WriteReindexing(false); - fReindex = false; - LogPrintf("Reindexing finished\n"); - // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): - ::ChainstateActive().LoadGenesisBlock(); - } - - // -loadblock= - for (const fs::path& path : vImportFiles) { - FILE *file = fsbridge::fopen(path, "rb"); - if (file) { - LogPrintf("Importing blocks file %s...\n", path.string()); - ::ChainstateActive().LoadExternalBlockFile(file); - if (ShutdownRequested()) { - LogPrintf("Shutdown requested. Exit %s\n", __func__); - return; - } - } else { - LogPrintf("Warning: Could not open blocks file %s\n", path.string()); - } - } - - // scan for better chains in the block chain database, that are not yet connected in the active best chain - - // We can't hold cs_main during ActivateBestChain even though we're accessing - // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve - // the relevant pointers before the ABC call. - for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) { - BlockValidationState state; - if (!chainstate->ActivateBestChain(state, nullptr)) { - LogPrintf("Failed to connect best block (%s)\n", state.ToString()); - StartShutdown(); - return; - } - } - - if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { - LogPrintf("Stopping after block import\n"); - StartShutdown(); - return; - } - } // End scope of CImportingNow - - // force UpdatedBlockTip to initialize nCachedBlockHeight for DS, MN payments and budgets - // but don't call it directly to prevent triggering of other listeners like zmq etc. - // GetMainSignals().UpdatedBlockTip(::ChainActive().Tip()); - pdsNotificationInterface->InitializeCurrentBlockTip(); - - { - // Get all UTXOs for each MN collateral in one go so that we can fill coin cache early - // and reduce further locking overhead for cs_main in other parts of code including GUI - LogPrintf("Filling coin cache with masternode UTXOs...\n"); - LOCK(cs_main); - int64_t nStart = GetTimeMillis(); - auto mnList = deterministicMNManager->GetListAtChainTip(); - mnList.ForEachMN(false, [&](auto& dmn) { - Coin coin; - GetUTXOCoin(dmn.collateralOutpoint, coin); - }); - LogPrintf("Filling coin cache with masternode UTXOs: done in %dms\n", GetTimeMillis() - nStart); - } - - if (fMasternodeMode) { - assert(activeMasternodeManager); - activeMasternodeManager->Init(::ChainActive().Tip()); - } - - g_wallet_init_interface.AutoLockMasternodeCollaterals(); - - chainman.ActiveChainstate().LoadMempool(args); -} - static void PeriodicStats(ArgsManager& args, const CTxMemPool& mempool) { assert(args.GetBoolArg("-statsenabled", DEFAULT_STATSD_ENABLE)); @@ -1228,6 +1115,9 @@ bool AppInitBasicSetup(const ArgsManager& args) // Enable heap terminate-on-corruption HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0); #endif + if (!InitShutdownState()) { + return InitError(Untranslated("Initializing wait-for-shutdown state failed.")); + } if (!SetupNetworking()) { return InitError(Untranslated("Initializing networking failed.")); @@ -1321,9 +1211,6 @@ bool AppInitParameterInteraction(const ArgsManager& args) if (!args.GetBoolArg("-disablegovernance", false)) { return InitError(_("Prune mode is incompatible with -disablegovernance=false.")); } - if (!g_enabled_filter_types.empty()) { - return InitError(_("Prune mode is incompatible with -blockfilterindex.")); - } } if (args.IsArgSet("-devnet")) { @@ -2467,7 +2354,9 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc vImportFiles.push_back(strFile); } - g_load_block = std::thread(&TraceThread>, "loadblk", [=, &chainman, &args]{ ThreadImport(chainman, vImportFiles, args); }); + chainman.m_load_block = std::thread(&TraceThread>, "loadblk", [=, &chainman, &args] { + ThreadImport(chainman, *pdsNotificationInterface ,vImportFiles, args); + }); // Wait for genesis block to be processed { diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 4a4a2c34b2c6..8ad2c704258b 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/src/llmq/chainlocks.cpp b/src/llmq/chainlocks.cpp index d6c9f147adc0..f6ce0e61d303 100644 --- a/src/llmq/chainlocks.cpp +++ b/src/llmq/chainlocks.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/net_processing.cpp b/src/net_processing.cpp index a53d9ee91725..78344eaeecdf 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/src/node/README.md b/src/node/README.md index e99a7175344b..ab5979594d48 100644 --- a/src/node/README.md +++ b/src/node/README.md @@ -15,8 +15,7 @@ As a rule of thumb, code in one of the [`src/node/`](./), calling code in the other directories directly, and only invoke it indirectly through the more limited [`src/interfaces/`](../interfaces/) classes. -The [`src/node/`](./) directory is a new directory introduced in -[#14978](https://github.com/bitcoin/bitcoin/pull/14978) and at the moment is +This directory is at the moment sparsely populated. Eventually more substantial files like [`src/validation.cpp`](../validation.cpp) and [`src/txmempool.cpp`](../txmempool.cpp) might be moved there. diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp new file mode 100644 index 000000000000..9d92daf199eb --- /dev/null +++ b/src/node/blockstorage.cpp @@ -0,0 +1,223 @@ +// Copyright (c) 2011-2021 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 + +// From validation. TODO move here +bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false); + +static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart) +{ + // Open history file to append + CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) { + return error("WriteBlockToDisk: OpenBlockFile failed"); + } + + // Write index header + unsigned int nSize = GetSerializeSize(block, fileout.GetVersion()); + fileout << messageStart << nSize; + + // Write block + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) { + return error("WriteBlockToDisk: ftell failed"); + } + pos.nPos = (unsigned int)fileOutPos; + fileout << block; + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams) +{ + block.SetNull(); + + // Open history file to read + CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) { + return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); + } + + // Read block + try { + filein >> block; + } catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); + } + + // Check the header + if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) { + return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); + } + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + FlatFilePos blockPos; + { + LOCK(cs_main); + blockPos = pindex->GetBlockPos(); + } + + if (!ReadBlockFromDisk(block, blockPos, consensusParams)) { + return false; + } + if (block.GetHash() != pindex->GetBlockHash()) { + return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", + pindex->ToString(), pindex->GetBlockPos().ToString()); + } + return true; +} + +/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ +FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp) +{ + unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION); + FlatFilePos blockPos; + if (dbp != nullptr) { + blockPos = *dbp; + } + if (!FindBlockPos(blockPos, nBlockSize + 8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) { + error("%s: FindBlockPos failed", __func__); + return FlatFilePos(); + } + if (dbp == nullptr) { + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) { + AbortNode("Failed to write block"); + return FlatFilePos(); + } + } + return blockPos; +} + +struct CImportingNow { + CImportingNow() + { + assert(fImporting == false); + fImporting = true; + } + + ~CImportingNow() + { + assert(fImporting == true); + fImporting = false; + } +}; + +void ThreadImport(ChainstateManager& chainman, CDSNotificationInterface& dsnfi, std::vector vImportFiles, const ArgsManager& args) +{ + ScheduleBatchPriority(); + + { + CImportingNow imp; + + // -reindex + if (fReindex) { + int nFile = 0; + while (true) { + FlatFilePos pos(nFile, 0); + if (!fs::exists(GetBlockPosFilename(pos))) { + break; // No block files left to reindex + } + FILE* file = OpenBlockFile(pos, true); + if (!file) { + break; // This error is logged in OpenBlockFile + } + LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); + chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos); + if (ShutdownRequested()) { + LogPrintf("Shutdown requested. Exit %s\n", __func__); + return; + } + nFile++; + } + pblocktree->WriteReindexing(false); + fReindex = false; + LogPrintf("Reindexing finished\n"); + // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): + chainman.ActiveChainstate().LoadGenesisBlock(); + } + + // -loadblock= + for (const fs::path& path : vImportFiles) { + FILE *file = fsbridge::fopen(path, "rb"); + if (file) { + LogPrintf("Importing blocks file %s...\n", path.string()); + chainman.ActiveChainstate().LoadExternalBlockFile(file); + if (ShutdownRequested()) { + LogPrintf("Shutdown requested. Exit %s\n", __func__); + return; + } + } else { + LogPrintf("Warning: Could not open blocks file %s\n", path.string()); + } + } + + // scan for better chains in the block chain database, that are not yet connected in the active best chain + + // We can't hold cs_main during ActivateBestChain even though we're accessing + // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve + // the relevant pointers before the ABC call. + for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) { + BlockValidationState state; + if (!chainstate->ActivateBestChain(state, nullptr)) { + LogPrintf("Failed to connect best block (%s)\n", state.ToString()); + StartShutdown(); + return; + } + } + + if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { + LogPrintf("Stopping after block import\n"); + StartShutdown(); + return; + } + } // End scope of CImportingNow + + // force UpdatedBlockTip to initialize nCachedBlockHeight for DS, MN payments and budgets + // but don't call it directly to prevent triggering of other listeners like zmq etc. + // GetMainSignals().UpdatedBlockTip(::ChainActive().Tip()); + dsnfi.InitializeCurrentBlockTip(); + + { + // Get all UTXOs for each MN collateral in one go so that we can fill coin cache early + // and reduce further locking overhead for cs_main in other parts of code including GUI + LogPrintf("Filling coin cache with masternode UTXOs...\n"); + LOCK(cs_main); + int64_t nStart = GetTimeMillis(); + auto mnList = deterministicMNManager->GetListAtChainTip(); + mnList.ForEachMN(false, [&](auto& dmn) { + Coin coin; + GetUTXOCoin(dmn.collateralOutpoint, coin); + }); + LogPrintf("Filling coin cache with masternode UTXOs: done in %dms\n", GetTimeMillis() - nStart); + } + + if (fMasternodeMode) { + assert(activeMasternodeManager); + activeMasternodeManager->Init(::ChainActive().Tip()); + } + + g_wallet_init_interface.AutoLockMasternodeCollaterals(); + + chainman.ActiveChainstate().LoadMempool(args); +} diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h new file mode 100644 index 000000000000..19b75d9c3ff4 --- /dev/null +++ b/src/node/blockstorage.h @@ -0,0 +1,40 @@ +// Copyright (c) 2011-2021 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_NODE_BLOCKSTORAGE_H +#define BITCOIN_NODE_BLOCKSTORAGE_H + +#include +#include // For CMessageHeader::MessageStartChars + +#include +#include +#include + +class ArgsManager; +class CBlock; +class CBlockIndex; +class CBlockUndo; +class CChain; +class CChainParams; +class CDSNotificationInterface; +class ChainstateManager; +struct FlatFilePos; +namespace Consensus { +struct Params; +} + +static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false}; + +/** Functions for disk access for blocks */ +bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams); +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); + +bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex); + +FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp); + +void ThreadImport(ChainstateManager& chainman, CDSNotificationInterface& dsnfi, std::vector vImportFiles, const ArgsManager& args); + +#endif // BITCOIN_NODE_BLOCKSTORAGE_H diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index b44329a95b7d..f7e205689501 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rest.cpp b/src/rest.cpp index 2fa766b4763e..a69f4605eea8 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -178,14 +179,16 @@ static bool rest_headers(const CoreContext& context, std::vector headers; headers.reserve(count); { + ChainstateManager& chainman = EnsureAnyChainman(context); LOCK(cs_main); - tip = ::ChainActive().Tip(); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); - while (pindex != nullptr && ::ChainActive().Contains(pindex)) { + CChain& active_chain = chainman.ActiveChain(); + tip = active_chain.Tip(); + const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash); + while (pindex != nullptr && active_chain.Contains(pindex)) { headers.push_back(pindex); if (headers.size() == (unsigned long)count) break; - pindex = ::ChainActive().Next(pindex); + pindex = active_chain.Next(pindex); } } @@ -229,7 +232,8 @@ static bool rest_headers(const CoreContext& context, } } -static bool rest_block(HTTPRequest* req, +static bool rest_block(const CoreContext& context, + HTTPRequest* req, const std::string& strURIPart, bool showTxDetails) { @@ -246,9 +250,10 @@ static bool rest_block(HTTPRequest* req, CBlockIndex* pblockindex = nullptr; CBlockIndex* tip = nullptr; { + ChainstateManager& chainman = EnsureAnyChainman(context); LOCK(cs_main); - tip = ::ChainActive().Tip(); - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); + tip = chainman.ActiveChain().Tip(); + pblockindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } @@ -295,12 +300,12 @@ static bool rest_block(HTTPRequest* req, static bool rest_block_extended(const CoreContext& context, HTTPRequest* req, const std::string& strURIPart) { - return rest_block(req, strURIPart, true); + return rest_block(context, req, strURIPart, true); } static bool rest_block_notxdetails(const CoreContext& context, HTTPRequest* req, const std::string& strURIPart) { - return rest_block(req, strURIPart, false); + return rest_block(context, req, strURIPart, false); } // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp @@ -533,6 +538,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st std::string bitmapStringRepresentation; std::vector hits; bitmap.resize((vOutPoints.size() + 7) / 8); + ChainstateManager& chainman = EnsureAnyChainman(context); { auto process_utxos = [&vOutPoints, &outs, &hits](const CCoinsView& view, const CTxMemPool& mempool) { for (const COutPoint& vOutPoint : vOutPoints) { @@ -548,12 +554,12 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st if (!mempool) return false; // use db+mempool as cache backend in case user likes to query mempool LOCK2(cs_main, mempool->cs); - CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip(); + CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip(); CCoinsViewMemPool viewMempool(&viewChain, *mempool); process_utxos(viewMempool, *mempool); } else { LOCK(cs_main); // no need to lock mempool! - process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool()); + process_utxos(chainman.ActiveChainstate().CoinsTip(), CTxMemPool()); } for (size_t i = 0; i < hits.size(); ++i) { @@ -568,7 +574,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st // serialize data // use exact same output as mentioned in Bip64 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); - ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs; + ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs; std::string ssGetUTXOResponseString = ssGetUTXOResponse.str(); req->WriteHeader("Content-Type", "application/octet-stream"); @@ -578,7 +584,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st case RetFormat::HEX: { CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); - ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs; + ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs; std::string strHex = HexStr(ssGetUTXOResponse) + "\n"; req->WriteHeader("Content-Type", "text/plain"); @@ -591,8 +597,8 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st // pack in some essentials // use more or less the same output as mentioned in Bip64 - objGetUTXOResponse.pushKV("chainHeight", ::ChainActive().Height()); - objGetUTXOResponse.pushKV("chaintipHash", ::ChainActive().Tip()->GetBlockHash().GetHex()); + objGetUTXOResponse.pushKV("chainHeight", chainman.ActiveChain().Height()); + objGetUTXOResponse.pushKV("chaintipHash", chainman.ActiveChain().Tip()->GetBlockHash().GetHex()); objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); UniValue utxos(UniValue::VARR); @@ -635,11 +641,13 @@ static bool rest_blockhash_by_height(const CoreContext& context, HTTPRequest* re CBlockIndex* pblockindex = nullptr; { + ChainstateManager& chainman = EnsureAnyChainman(context); LOCK(cs_main); - if (blockheight > ::ChainActive().Height()) { + const CChain& active_chain = chainman.ActiveChain(); + if (blockheight > active_chain.Height()) { return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range"); } - pblockindex = ::ChainActive()[blockheight]; + pblockindex = active_chain[blockheight]; } switch (rf) { case RetFormat::BINARY: { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 28da203cdf6b..8346c7500c4c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -62,9 +63,9 @@ static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange); -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempool, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry); +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempool, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry); -NodeContext& EnsureNodeContext(const CoreContext& context) +NodeContext& EnsureAnyNodeContext(const CoreContext& context) { auto* node_context = GetContext(context); if (!node_context) { @@ -73,42 +74,59 @@ NodeContext& EnsureNodeContext(const CoreContext& context) return *node_context; } -CTxMemPool& EnsureMemPool(const CoreContext& context) +CTxMemPool& EnsureMemPool(const NodeContext& node) { - NodeContext& node = EnsureNodeContext(context); if (!node.mempool) { throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); } return *node.mempool; } -ChainstateManager& EnsureChainman(const CoreContext& context) +CTxMemPool& EnsureAnyMemPool(const CoreContext& context) +{ + return EnsureMemPool(EnsureAnyNodeContext(context)); +} + +ChainstateManager& EnsureChainman(const NodeContext& node) { - NodeContext& node = EnsureNodeContext(context); if (!node.chainman) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found"); } + WITH_LOCK(::cs_main, CHECK_NONFATAL(std::addressof(g_chainman) == std::addressof(*node.chainman))); return *node.chainman; } -CBlockPolicyEstimator& EnsureFeeEstimator(const CoreContext& context) +ChainstateManager& EnsureAnyChainman(const CoreContext& context) +{ + return EnsureChainman(EnsureAnyNodeContext(context)); +} + +CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node) { - NodeContext& node = EnsureNodeContext(context); if (!node.fee_estimator) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled"); } return *node.fee_estimator; } -LLMQContext& EnsureLLMQContext(const CoreContext& context) +CBlockPolicyEstimator& EnsureAnyFeeEstimator(const CoreContext& context) +{ + return EnsureFeeEstimator(EnsureAnyNodeContext(context)); +} + +LLMQContext& EnsureLLMQContext(const NodeContext& node) { - NodeContext& node = EnsureNodeContext(context); if (!node.llmq_ctx) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Node LLMQ context not found"); } return *node.llmq_ctx; } +LLMQContext& EnsureAnyLLMQContext(const CoreContext& context) +{ + return EnsureLLMQContext(EnsureAnyNodeContext(context)); +} + /* Calculate the difficulty for a given block index. */ double GetDifficulty(const CBlockIndex* blockindex) @@ -232,8 +250,9 @@ static UniValue getblockcount(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - return ::ChainActive().Height(); + return chainman.ActiveChain().Height(); } static UniValue getbestblockhash(const JSONRPCRequest& request) @@ -249,8 +268,9 @@ static UniValue getbestblockhash(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - return ::ChainActive().Tip()->GetBlockHash().GetHex(); + return chainman.ActiveChain().Tip()->GetBlockHash().GetHex(); } static UniValue getbestchainlock(const JSONRPCRequest& request) @@ -273,7 +293,9 @@ static UniValue getbestchainlock(const JSONRPCRequest& request) }.Check(request); UniValue result(UniValue::VOBJ); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + LLMQContext& llmq_ctx = EnsureLLMQContext(node); llmq::CChainLockSig clsig = llmq_ctx.clhandler->GetBestChainLock(); if (clsig.IsNull()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to find any chainlock"); @@ -282,8 +304,9 @@ static UniValue getbestchainlock(const JSONRPCRequest& request) result.pushKV("height", clsig.getHeight()); result.pushKV("signature", clsig.getSig().ToString()); + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); - result.pushKV("known_block", g_chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr); + result.pushKV("known_block", chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr); return result; } @@ -451,8 +474,9 @@ static UniValue getdifficulty(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - return GetDifficulty(::ChainActive().Tip()); + return GetDifficulty(chainman.ActiveChain().Tip()); } static std::vector MempoolEntryDescription() { return { @@ -589,7 +613,10 @@ static UniValue getrawmempool(const JSONRPCRequest& request) if (!request.params[0].isNull()) fVerbose = request.params[0].get_bool(); - return MempoolToJSON(EnsureMemPool(request.context), EnsureLLMQContext(request.context).isman, fVerbose); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); + return MempoolToJSON(mempool, llmq_ctx.isman, fVerbose); } static UniValue getmempoolancestors(const JSONRPCRequest& request) @@ -620,7 +647,9 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) uint256 hash(ParseHashV(request.params[0], "parameter 1")); - const CTxMemPool& mempool = EnsureMemPool(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + const CTxMemPool& mempool = EnsureMemPool(node); LOCK(mempool.cs); CTxMemPool::txiter it = mempool.mapTx.find(hash); @@ -642,7 +671,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) return o; } else { UniValue o(UniValue::VOBJ); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); for (CTxMemPool::txiter ancestorIt : setAncestors) { const CTxMemPoolEntry &e = *ancestorIt; const uint256& _hash = e.GetTx().GetHash(); @@ -684,7 +713,9 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) uint256 hash(ParseHashV(request.params[0], "parameter 1")); - const CTxMemPool& mempool = EnsureMemPool(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + const CTxMemPool& mempool = EnsureMemPool(node); LOCK(mempool.cs); CTxMemPool::txiter it = mempool.mapTx.find(hash); @@ -706,7 +737,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) return o; } else { UniValue o(UniValue::VOBJ); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); for (CTxMemPool::txiter descendantIt : setDescendants) { const CTxMemPoolEntry &e = *descendantIt; const uint256& _hash = e.GetTx().GetHash(); @@ -735,7 +766,9 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) uint256 hash(ParseHashV(request.params[0], "parameter 1")); - const CTxMemPool& mempool = EnsureMemPool(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + const CTxMemPool& mempool = EnsureMemPool(node); LOCK(mempool.cs); CTxMemPool::txiter it = mempool.mapTx.find(hash); @@ -745,7 +778,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) const CTxMemPoolEntry &e = *it; UniValue info(UniValue::VOBJ); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); entryToJSON(mempool, info, e, llmq_ctx.isman); return info; } @@ -798,13 +831,15 @@ static UniValue getblockhash(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); + const CChain& active_chain = chainman.ActiveChain(); int nHeight = request.params[0].get_int(); - if (nHeight < 0 || nHeight > ::ChainActive().Height()) + if (nHeight < 0 || nHeight > active_chain.Height()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - CBlockIndex* pblockindex = ::ChainActive()[nHeight]; + CBlockIndex* pblockindex = active_chain[nHeight]; return pblockindex->GetBlockHash().GetHex(); } @@ -847,6 +882,7 @@ static UniValue getblockheader(const JSONRPCRequest& request) }.Check(request); uint256 hash(ParseHashV(request.params[0], "hash")); + const NodeContext& node = EnsureAnyNodeContext(request.context); bool fVerbose = true; if (!request.params[1].isNull()) @@ -855,9 +891,10 @@ static UniValue getblockheader(const JSONRPCRequest& request) const CBlockIndex* pblockindex; const CBlockIndex* tip; { + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); - tip = ::ChainActive().Tip(); + pblockindex = chainman.m_blockman.LookupBlockIndex(hash); + tip = chainman.ActiveChain().Tip(); } if (!pblockindex) { @@ -872,7 +909,7 @@ static UniValue getblockheader(const JSONRPCRequest& request) return strHex; } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); return blockheaderToJSON(tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman); } @@ -921,12 +958,19 @@ static UniValue getblockheaders(const JSONRPCRequest& request) uint256 hash(ParseHashV(request.params[0], "blockhash")); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + ChainstateManager& chainman = EnsureChainman(node); + + CChainState& active_chainstate = chainman.ActiveChainstate(); + CChain& active_chain = active_chainstate.m_chain; + const CBlockIndex* pblockindex; const CBlockIndex* tip; { LOCK(cs_main); - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); - tip = ::ChainActive().Tip(); + pblockindex = chainman.m_blockman.LookupBlockIndex(hash); + tip = active_chain.Tip(); } if (!pblockindex) { @@ -948,7 +992,7 @@ static UniValue getblockheaders(const JSONRPCRequest& request) if (!fVerbose) { - for (; pblockindex; pblockindex = ::ChainActive().Next(pblockindex)) + for (; pblockindex; pblockindex = active_chain.Next(pblockindex)) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); ssBlock << pblockindex->GetBlockHeader(); @@ -960,8 +1004,8 @@ static UniValue getblockheaders(const JSONRPCRequest& request) return arrHeaders; } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - for (; pblockindex; pblockindex = ::ChainActive().Next(pblockindex)) + LLMQContext& llmq_ctx = EnsureLLMQContext(node); + for (; pblockindex; pblockindex = active_chain.Next(pblockindex)) { arrHeaders.push_back(blockheaderToJSON(tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman)); if (--nCount <= 0) @@ -1020,6 +1064,7 @@ static UniValue getmerkleblocks(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); CBloomFilter filter; @@ -1032,7 +1077,7 @@ static UniValue getmerkleblocks(const JSONRPCRequest& request) uint256 hash(ParseHashV(request.params[1], "blockhash")); - const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); + const CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -1049,7 +1094,7 @@ static UniValue getmerkleblocks(const JSONRPCRequest& request) UniValue arrMerkleBlocks(UniValue::VARR); - for (; pblockindex; pblockindex = ::ChainActive().Next(pblockindex)) + for (; pblockindex; pblockindex = chainman.ActiveChain().Next(pblockindex)) { if (--nCount < 0) { break; @@ -1145,13 +1190,16 @@ static UniValue getblock(const JSONRPCRequest& request) verbosity = request.params[1].get_bool() ? 1 : 0; } + const NodeContext& node = EnsureAnyNodeContext(request.context); + CBlock block; const CBlockIndex* pblockindex; const CBlockIndex* tip; { + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); - tip = ::ChainActive().Tip(); + pblockindex = chainman.m_blockman.LookupBlockIndex(hash); + tip = chainman.ActiveChain().Tip(); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -1168,7 +1216,7 @@ static UniValue getblock(const JSONRPCRequest& request) return strHex; } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); return blockToJSON(block, tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, verbosity >= 2); } @@ -1190,7 +1238,10 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) if (!fPruneMode) throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode."); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); + CChainState& active_chainstate = chainman.ActiveChainstate(); + CChain& active_chain = active_chainstate.m_chain; int heightParam = request.params[0].get_int(); if (heightParam < 0) @@ -1200,7 +1251,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) // too low to be a block time (corresponds to timestamp from Sep 2001). if (heightParam > 1000000000) { // Add a 2 hour buffer to include blocks which might have had old timestamps - CBlockIndex* pindex = ::ChainActive().FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0); + CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0); if (!pindex) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp."); } @@ -1208,7 +1259,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) } unsigned int height = (unsigned int) heightParam; - unsigned int chainHeight = (unsigned int) ::ChainActive().Height(); + unsigned int chainHeight = (unsigned int) active_chain.Height(); if (chainHeight < Params().PruneAfterHeight()) throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning."); else if (height > chainHeight) @@ -1218,8 +1269,8 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) height = chainHeight - MIN_BLOCKS_TO_KEEP; } - PruneBlockFilesManual(::ChainstateActive(), height); - const CBlockIndex* block = ::ChainActive().Tip(); + PruneBlockFilesManual(active_chainstate, height); + const CBlockIndex* block = active_chain.Tip(); CHECK_NONFATAL(block); while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { block = block->pprev; @@ -1270,13 +1321,21 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); CCoinsStats stats; - ::ChainstateActive().ForceFlushStateToDisk(); + const NodeContext& node = EnsureAnyNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); + CChainState& active_chainstate = chainman.ActiveChainstate(); + active_chainstate.ForceFlushStateToDisk(); const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())}; - CCoinsView* coins_view = WITH_LOCK(::cs_main, return &::ChainstateActive().CoinsDB()); - NodeContext& node = EnsureNodeContext(request.context); - if (GetUTXOStats(coins_view, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, hash_type, node.rpc_interruption_point)) { + CCoinsView* coins_view; + BlockManager* blockman; + { + LOCK(::cs_main); + coins_view = &active_chainstate.CoinsDB(); + blockman = &active_chainstate.m_blockman; + } + if (GetUTXOStats(coins_view, *blockman, stats, hash_type, node.rpc_interruption_point)) { ret.pushKV("height", (int64_t)stats.nHeight); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("transactions", (int64_t)stats.nTransactions); @@ -1332,8 +1391,13 @@ static UniValue gettxout(const JSONRPCRequest& request) }, }.Check(request); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); + CChainState& active_chainstate = chainman.ActiveChainstate(); + UniValue ret(UniValue::VOBJ); uint256 hash(ParseHashV(request.params[0], "txid")); @@ -1344,10 +1408,10 @@ static UniValue gettxout(const JSONRPCRequest& request) fMempool = request.params[2].get_bool(); Coin coin; - CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip(); + CCoinsViewCache* coins_view = &active_chainstate.CoinsTip(); if (fMempool) { - const CTxMemPool& mempool = EnsureMemPool(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); LOCK(mempool.cs); CCoinsViewMemPool view(coins_view, mempool); if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { @@ -1359,7 +1423,7 @@ static UniValue gettxout(const JSONRPCRequest& request) } } - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(coins_view->GetBestBlock()); + const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(coins_view->GetBestBlock()); ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.nHeight == MEMPOOL_HEIGHT) { ret.pushKV("confirmations", 0); @@ -1395,33 +1459,35 @@ static UniValue verifychain(const JSONRPCRequest& request) const int check_level(request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].get_int()); const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].get_int()}; - LOCK(cs_main); + const NodeContext& node = EnsureAnyNodeContext(request.context); - const NodeContext& node_context = EnsureNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); + LOCK(cs_main); + CChainState& active_chainstate = chainman.ActiveChainstate(); return CVerifyDB().VerifyDB( - ::ChainstateActive(), Params(), ::ChainstateActive().CoinsTip(), *node_context.evodb, check_level, check_depth); + active_chainstate, Params(), active_chainstate.CoinsTip(), *node.evodb, check_level, check_depth); } -static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int softfork_height, int tip_height) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { // For buried deployments. // A buried deployment is one where the height of the activation has been hardcoded into // the client implementation long after the consensus change has activated. See BIP 90. // Buried deployments with activation height value of // std::numeric_limits::max() are disabled and thus hidden. - if (height == std::numeric_limits::max()) return; + if (softfork_height == std::numeric_limits::max()) return; UniValue rv(UniValue::VOBJ); rv.pushKV("type", "buried"); // getblockchaininfo reports the softfork as active from when the chain height is // one below the activation height - rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height); - rv.pushKV("height", height); + rv.pushKV("active", tip_height + 1 >= softfork_height); + rv.pushKV("height", softfork_height); softforks.pushKV(name, rv); } -static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { // For BIP9 deployments. // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden. @@ -1430,7 +1496,7 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return; UniValue bip9(UniValue::VOBJ); - const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache); + const ThresholdState thresholdState = VersionBitsState(active_chain_tip, consensusParams, id, versionbitscache); switch (thresholdState) { case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break; case ThresholdState::STARTED: bip9.pushKV("status", "started"); break; @@ -1444,12 +1510,12 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam } bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); - int64_t since_height = VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache); + int64_t since_height = VersionBitsStateSinceHeight(active_chain_tip, consensusParams, id, versionbitscache); bip9.pushKV("since", since_height); if (ThresholdState::STARTED == thresholdState) { UniValue statsUV(UniValue::VOBJ); - BIP9Stats statsStruct = VersionBitsStatistics(::ChainActive().Tip(), consensusParams, id, versionbitscache); + BIP9Stats statsStruct = VersionBitsStatistics(active_chain_tip, consensusParams, id, versionbitscache); statsUV.pushKV("period", statsStruct.period); statsUV.pushKV("threshold", statsStruct.threshold); statsUV.pushKV("elapsed", statsStruct.elapsed); @@ -1522,20 +1588,24 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); + CChainState& active_chainstate = chainman.ActiveChainstate(); std::string strChainName = gArgs.IsArgSet("-devnet") ? gArgs.GetDevNetName() : Params().NetworkIDString(); - const CBlockIndex* tip = ::ChainActive().Tip(); + const CBlockIndex* tip = active_chainstate.m_chain.Tip(); + CHECK_NONFATAL(tip); + const int height = tip->nHeight; UniValue obj(UniValue::VOBJ); obj.pushKV("chain", strChainName); - obj.pushKV("blocks", (int)::ChainActive().Height()); + obj.pushKV("blocks", height); obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); obj.pushKV("difficulty", (double)GetDifficulty(tip)); obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast()); obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); - obj.pushKV("initialblockdownload", ::ChainstateActive().IsInitialBlockDownload()); + obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload()); obj.pushKV("chainwork", tip->nChainWork.GetHex()); obj.pushKV("size_on_disk", CalculateCurrentUsage()); obj.pushKV("pruned", fPruneMode); @@ -1559,20 +1629,20 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) const Consensus::Params& consensusParams = Params().GetConsensus(); UniValue softforks(UniValue::VOBJ); // sorted by activation block - BuriedForkDescPushBack(softforks,"bip34", consensusParams.BIP34Height); - BuriedForkDescPushBack(softforks,"bip66", consensusParams.BIP66Height); - BuriedForkDescPushBack(softforks,"bip65", consensusParams.BIP65Height); - BuriedForkDescPushBack(softforks,"bip147", consensusParams.BIP147Height); - BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight); - BuriedForkDescPushBack(softforks, "dip0001", consensusParams.DIP0001Height); - BuriedForkDescPushBack(softforks, "dip0003", consensusParams.DIP0003Height); - BuriedForkDescPushBack(softforks, "dip0008", consensusParams.DIP0008Height); - BuriedForkDescPushBack(softforks, "dip0020", consensusParams.DIP0020Height); - BuriedForkDescPushBack(softforks, "dip0024", consensusParams.DIP0024Height); - BuriedForkDescPushBack(softforks, "realloc", consensusParams.BRRHeight); - BuriedForkDescPushBack(softforks, "v19", consensusParams.V19Height); - BIP9SoftForkDescPushBack(softforks, "v20", consensusParams, Consensus::DEPLOYMENT_V20); - BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); + BuriedForkDescPushBack(softforks,"bip34", consensusParams.BIP34Height, height); + BuriedForkDescPushBack(softforks,"bip66", consensusParams.BIP66Height, height); + BuriedForkDescPushBack(softforks,"bip65", consensusParams.BIP65Height, height); + BuriedForkDescPushBack(softforks,"bip147", consensusParams.BIP147Height, height); + BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight, height); + BuriedForkDescPushBack(softforks, "dip0001", consensusParams.DIP0001Height, height); + BuriedForkDescPushBack(softforks, "dip0003", consensusParams.DIP0003Height, height); + BuriedForkDescPushBack(softforks, "dip0008", consensusParams.DIP0008Height, height); + BuriedForkDescPushBack(softforks, "dip0020", consensusParams.DIP0020Height, height); + BuriedForkDescPushBack(softforks, "dip0024", consensusParams.DIP0024Height, height); + BuriedForkDescPushBack(softforks, "realloc", consensusParams.BRRHeight, height); + BuriedForkDescPushBack(softforks, "v19", consensusParams.V19Height, height); + BIP9SoftForkDescPushBack(tip, softforks, "v20", consensusParams, Consensus::DEPLOYMENT_V20); + BIP9SoftForkDescPushBack(tip, softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); obj.pushKV("softforks", softforks); @@ -1629,8 +1699,9 @@ static UniValue getchaintips(const JSONRPCRequest& request) }, }.Check(request); - ChainstateManager& chainman = EnsureChainman(request.context); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); + CChain& active_chain = chainman.ActiveChain(); /* * Idea: The set of chain tips is the active chain tip, plus orphan blocks which do not have another orphan building off of them. @@ -1644,7 +1715,7 @@ static UniValue getchaintips(const JSONRPCRequest& request) std::set setPrevs; for (const std::pair& item : chainman.BlockIndex()) { - if (!chainman.ActiveChain().Contains(item.second)) { + if (!active_chain.Contains(item.second)) { setOrphans.insert(item.second); setPrevs.insert(item.second->pprev); } @@ -1657,7 +1728,7 @@ static UniValue getchaintips(const JSONRPCRequest& request) } // Always report the currently active tip. - setTips.insert(chainman.ActiveChain().Tip()); + setTips.insert(active_chain.Tip()); int nBranchMin = -1; int nCountMax = INT_MAX; @@ -1672,7 +1743,7 @@ static UniValue getchaintips(const JSONRPCRequest& request) UniValue res(UniValue::VARR); for (const CBlockIndex* block : setTips) { - const CBlockIndex* pindexFork = chainman.ActiveChain().FindFork(block); + const CBlockIndex* pindexFork = active_chain.FindFork(block); const int branchLen = block->nHeight - pindexFork->nHeight; if(branchLen < nBranchMin) continue; @@ -1687,7 +1758,7 @@ static UniValue getchaintips(const JSONRPCRequest& request) obj.pushKV("forkpoint", pindexFork->phashBlock->GetHex()); std::string status; - if (chainman.ActiveChain().Contains(block)) { + if (active_chain.Contains(block)) { // This block is part of the currently active chain. status = "active"; } else if (block->nStatus & BLOCK_FAILED_MASK) { @@ -1759,7 +1830,10 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) }, }.Check(request); - return MempoolInfoToJSON(EnsureMemPool(request.context), *(EnsureLLMQContext(request.context).isman)); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); + return MempoolInfoToJSON(mempool, *llmq_ctx.isman); } static UniValue preciousblock(const JSONRPCRequest& request) @@ -1781,16 +1855,17 @@ static UniValue preciousblock(const JSONRPCRequest& request) uint256 hash(ParseHashV(request.params[0], "blockhash")); CBlockIndex* pblockindex; + ChainstateManager& chainman = EnsureAnyChainman(request.context); { LOCK(cs_main); - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); + pblockindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } BlockValidationState state; - ::ChainstateActive().PreciousBlock(state, pblockindex); + chainman.ActiveChainstate().PreciousBlock(state, pblockindex); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); @@ -1817,17 +1892,20 @@ static UniValue invalidateblock(const JSONRPCRequest& request) BlockValidationState state; CBlockIndex* pblockindex; + ChainstateManager& chainman = EnsureAnyChainman(request.context); { LOCK(cs_main); - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); + pblockindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } - ::ChainstateActive().InvalidateBlock(state, pblockindex); + + CChainState& active_chainstate = chainman.ActiveChainstate(); + active_chainstate.InvalidateBlock(state, pblockindex); if (state.IsValid()) { - ::ChainstateActive().ActivateBestChain(state); + active_chainstate.ActivateBestChain(state); } if (!state.IsValid()) { @@ -1852,20 +1930,23 @@ static UniValue reconsiderblock(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CChainState& active_chainstate = chainman.ActiveChainstate(); + uint256 hash(ParseHashV(request.params[0], "blockhash")); { LOCK(cs_main); - CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); + CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - ::ChainstateActive().ResetBlockFailureFlags(pblockindex); + active_chainstate.ResetBlockFailureFlags(pblockindex); } BlockValidationState state; - ::ChainstateActive().ActivateBestChain(state); + active_chainstate.ActivateBestChain(state); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); @@ -1900,20 +1981,23 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); + + CChain& active_chain = chainman.ActiveChain(); const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month if (request.params[1].isNull()) { LOCK(cs_main); - pindex = ::ChainActive().Tip(); + pindex = active_chain.Tip(); } else { uint256 hash(ParseHashV(request.params[1], "blockhash")); LOCK(cs_main); - pindex = g_chainman.m_blockman.LookupBlockIndex(hash); + pindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - if (!::ChainActive().Contains(pindex)) { + if (!active_chain.Contains(pindex)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); } } @@ -2069,12 +2153,14 @@ static UniValue getblockstats(const JSONRPCRequest& request) g_txindex->BlockUntilSyncedToCurrentChain(); } + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); + CChain& active_chain = chainman.ActiveChain(); CBlockIndex* pindex; if (request.params[0].isNum()) { const int height = request.params[0].get_int(); - const int current_tip = ::ChainActive().Height(); + const int current_tip = active_chain.Height(); if (height < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height)); } @@ -2082,14 +2168,14 @@ static UniValue getblockstats(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip)); } - pindex = ::ChainActive()[height]; + pindex = active_chain[height]; } else { const uint256 hash(ParseHashV(request.params[0], "hash_or_height")); - pindex = g_chainman.m_blockman.LookupBlockIndex(hash); + pindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - if (!::ChainActive().Contains(pindex)) { + if (!active_chain.Contains(pindex)) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Block is not in chain %s", Params().NetworkIDString())); } } @@ -2274,8 +2360,14 @@ static UniValue getspecialtxes(const JSONRPCRequest& request) }, }.Check(request); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); + CTxMemPool& mempool = EnsureMemPool(node); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); + uint256 hash(ParseHashV(request.params[0], "blockhash")); int nTxType = -1; @@ -2304,7 +2396,8 @@ static UniValue getspecialtxes(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2"); } } - const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); + + const CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2313,8 +2406,6 @@ static UniValue getspecialtxes(const JSONRPCRequest& request) int nTxNum = 0; UniValue result(UniValue::VARR); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - CTxMemPool& mempool = EnsureMemPool(request.context); for(const auto& tx : block.vtx) { @@ -2334,7 +2425,7 @@ static UniValue getspecialtxes(const JSONRPCRequest& request) case 2 : { UniValue objTx(UniValue::VOBJ); - TxToJSON(*tx, uint256(), mempool, *llmq_ctx.clhandler, *llmq_ctx.isman, objTx); + TxToJSON(*tx, uint256(), mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, objTx); result.push_back(objTx); break; } @@ -2357,7 +2448,7 @@ static UniValue savemempool(const JSONRPCRequest& request) }, }.Check(request); - const CTxMemPool& mempool = EnsureMemPool(request.context); + const CTxMemPool& mempool = EnsureAnyMemPool(request.context); if (!mempool.IsLoaded()) { throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet"); @@ -2545,15 +2636,17 @@ UniValue scantxoutset(const JSONRPCRequest& request) int64_t count = 0; std::unique_ptr pcursor; CBlockIndex* tip; + NodeContext& node = EnsureAnyNodeContext(request.context); { + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); - ::ChainstateActive().ForceFlushStateToDisk(); - pcursor = std::unique_ptr(::ChainstateActive().CoinsDB().Cursor()); + CChainState& active_chainstate = chainman.ActiveChainstate(); + active_chainstate.ForceFlushStateToDisk(); + pcursor = std::unique_ptr(active_chainstate.CoinsDB().Cursor()); CHECK_NONFATAL(pcursor); - tip = ::ChainActive().Tip(); + tip = active_chainstate.m_chain.Tip(); CHECK_NONFATAL(tip); } - NodeContext& node = EnsureNodeContext(request.context); bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point); result.pushKV("success", res); result.pushKV("txouts", count); @@ -2624,8 +2717,9 @@ static UniValue getblockfilter(const JSONRPCRequest& request) const CBlockIndex* block_index; bool block_was_connected; { + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - block_index = g_chainman.m_blockman.LookupBlockIndex(block_hash); + block_index = chainman.m_blockman.LookupBlockIndex(block_hash); if (!block_index) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2702,7 +2796,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request) FILE* file{fsbridge::fopen(temppath, "wb")}; CAutoFile afile{file, SER_DISK, CLIENT_VERSION}; - NodeContext& node = EnsureNodeContext(request.context); + NodeContext& node = EnsureAnyNodeContext(request.context); UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), afile); fs::rename(temppath, path); @@ -2738,7 +2832,7 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil } pcursor = std::unique_ptr(chainstate.CoinsDB().Cursor()); - tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock); + tip = chainstate.m_blockman.LookupBlockIndex(stats.hashBlock); CHECK_NONFATAL(tip); } diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 2f51e1d963d7..808324dd8a29 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -57,11 +57,15 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex /** Used by getblockstats to get feerates at different percentiles by weight */ void CalculatePercentilesBySize(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector>& scores, int64_t total_size); -NodeContext& EnsureNodeContext(const CoreContext& context); -CTxMemPool& EnsureMemPool(const CoreContext& context); -ChainstateManager& EnsureChainman(const CoreContext& context); -CBlockPolicyEstimator& EnsureFeeEstimator(const CoreContext& context); -LLMQContext& EnsureLLMQContext(const CoreContext& context); +NodeContext& EnsureAnyNodeContext(const CoreContext& context); +CTxMemPool& EnsureMemPool(const NodeContext& node); +CTxMemPool& EnsureAnyMemPool(const CoreContext& context); +ChainstateManager& EnsureChainman(const NodeContext& node); +ChainstateManager& EnsureAnyChainman(const CoreContext& context); +CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node); +CBlockPolicyEstimator& EnsureAnyFeeEstimator(const CoreContext& context); +LLMQContext& EnsureLLMQContext(const NodeContext& node); +LLMQContext& EnsureAnyLLMQContext(const CoreContext& context); /** * Helper to create UTXO snapshots given a chainstate and a file handle. diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 5a2f7b97c90d..c0f36b530ad1 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -277,11 +277,13 @@ UniValue RPCConvertValues(const std::string &strMethod, const std::vector &strParams) { UniValue params(UniValue::VOBJ); + UniValue positional_args{UniValue::VARR}; for (const std::string &s: strParams) { size_t pos = s.find('='); if (pos == std::string::npos) { - throw(std::runtime_error("No '=' in named argument '"+s+"', this needs to be present for every argument (even if it is empty)")); + positional_args.push_back(rpcCvtTable.convert(strMethod, positional_args.size()) ? ParseNonRFCJSONValue(s) : s); + continue; } std::string name = s.substr(0, pos); @@ -296,5 +298,9 @@ UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vectorsecond->DoAutomaticDenominating(*node.connman, *node.fee_estimator, mempool); + const NodeContext& node = EnsureAnyNodeContext(request.context); + CTxMemPool& mempool = EnsureMemPool(node); + CBlockPolicyEstimator& fee_estimator = EnsureFeeEstimator(node); + bool result = it->second->DoAutomaticDenominating(*node.connman, fee_estimator, mempool); return "Mixing " + (result ? "started successfully" : ("start failed: " + it->second->GetStatuses().original + ", will retry")); } diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 5eb6aa30eae2..56d27c8d8feb 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -324,15 +324,15 @@ static void SignSpecialTxPayloadByHash(const CMutableTransaction& tx, SpecialTxP payload.sig = key.Sign(hash); } -static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, const CMutableTransaction& tx, bool fSubmit = true) +static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, const ChainstateManager& chainman, const CMutableTransaction& tx, bool fSubmit = true) { { LOCK(cs_main); - CCreditPool creditPool = creditPoolManager->GetCreditPool(::ChainActive().Tip(), Params().GetConsensus()); + CCreditPool creditPool = creditPoolManager->GetCreditPool(chainman.ActiveChain().Tip(), Params().GetConsensus()); TxValidationState state; - if (!CheckSpecialTx(CTransaction(tx), ::ChainActive().Tip(), ::ChainstateActive().CoinsTip(), creditPool, true, state)) { + if (!CheckSpecialTx(CTransaction(tx), chainman.ActiveChain().Tip(), chainman.ActiveChainstate().CoinsTip(), creditPool, true, state)) { throw std::runtime_error(state.ToString()); } } // cs_main @@ -581,6 +581,7 @@ static void protx_register_prepare_hpmn_help(const JSONRPCRequest& request) } static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, + const ChainstateManager& chainman, const bool specific_legacy_bls_scheme, const bool isExternalRegister, const bool isFundRegister, @@ -613,7 +614,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, EnsureWalletIsUnlocked(wallet.get()); } - bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); + bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();)); if (isHPMNrequested && !isV19active) { throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); } @@ -744,7 +745,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, ptx.collateralOutpoint.n = collateralIndex; SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx, fSubmit); + return SignAndSendSpecialTx(request, chainman, tx, fSubmit); } else { // referencing external collateral @@ -782,36 +783,36 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, } SignSpecialTxPayloadByString(tx, ptx, key); SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx, fSubmit); + return SignAndSendSpecialTx(request, chainman, tx, fSubmit); } } } -static UniValue protx_register_hpmn(const JSONRPCRequest& request) +static UniValue protx_register_hpmn(const JSONRPCRequest& request, const ChainstateManager& chainman) { bool isExternalRegister = request.strMethod == "protxregister_hpmn"; bool isFundRegister = request.strMethod == "protxregister_fund_hpmn"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_hpmn"; - return protx_register_common_wrapper(request, false, isExternalRegister, isFundRegister, isPrepareRegister, MnType::HighPerformance); + return protx_register_common_wrapper(request, chainman, false, isExternalRegister, isFundRegister, isPrepareRegister, MnType::HighPerformance); } -static UniValue protx_register(const JSONRPCRequest& request) +static UniValue protx_register(const JSONRPCRequest& request, const ChainstateManager& chainman) { bool isExternalRegister = request.strMethod == "protxregister"; bool isFundRegister = request.strMethod == "protxregister_fund"; bool isPrepareRegister = request.strMethod == "protxregister_prepare"; - return protx_register_common_wrapper(request, false, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); + return protx_register_common_wrapper(request, chainman, false, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); } -static UniValue protx_register_legacy(const JSONRPCRequest& request) +static UniValue protx_register_legacy(const JSONRPCRequest& request, const ChainstateManager& chainman) { bool isExternalRegister = request.strMethod == "protxregister_legacy"; bool isFundRegister = request.strMethod == "protxregister_fund_legacy"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_legacy"; - return protx_register_common_wrapper(request, true, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); + return protx_register_common_wrapper(request, chainman, true, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); } -static UniValue protx_register_submit(const JSONRPCRequest& request) +static UniValue protx_register_submit(const JSONRPCRequest& request, const ChainstateManager& chainman) { protx_register_submit_help(request); @@ -838,7 +839,7 @@ static UniValue protx_register_submit(const JSONRPCRequest& request) ptx.vchSig = DecodeBase64(request.params[1].get_str().c_str()); SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx); + return SignAndSendSpecialTx(request, chainman, tx); } static void protx_update_service_help(const JSONRPCRequest& request) @@ -889,7 +890,7 @@ static void protx_update_service_hpmn_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& request, const MnType mnType) +static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& request, const ChainstateManager& chainman, const MnType mnType) { const bool isHPMNrequested = mnType == MnType::HighPerformance; if (isHPMNrequested) { @@ -903,7 +904,7 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques EnsureWalletIsUnlocked(wallet.get()); - const bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); + const bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();)); const bool is_bls_legacy = !isV19active; if (isHPMNrequested && !isV19active) { throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); @@ -995,7 +996,7 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx); + return SignAndSendSpecialTx(request, chainman, tx); } static void protx_update_registrar_help(const JSONRPCRequest& request, bool legacy) @@ -1025,7 +1026,7 @@ static void protx_update_registrar_help(const JSONRPCRequest& request, bool lega }.Check(request); } -static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, const bool specific_legacy_bls_scheme) +static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, const ChainstateManager& chainman, const bool specific_legacy_bls_scheme) { protx_update_registrar_help(request, specific_legacy_bls_scheme); @@ -1044,7 +1045,7 @@ static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, co ptx.keyIDVoting = dmn->pdmnState->keyIDVoting; ptx.scriptPayout = dmn->pdmnState->scriptPayout; - const bool use_legacy = llmq::utils::IsV19Active(::ChainActive().Tip()) ? specific_legacy_bls_scheme : true; + const bool use_legacy = llmq::utils::IsV19Active(chainman.ActiveChain().Tip()) ? specific_legacy_bls_scheme : true; if (request.params[1].get_str() != "") { // new pubkey @@ -1099,17 +1100,17 @@ static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, co SignSpecialTxPayloadByHash(tx, ptx, keyOwner); SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx); + return SignAndSendSpecialTx(request, chainman, tx); } -static UniValue protx_update_registrar(const JSONRPCRequest& request) +static UniValue protx_update_registrar(const JSONRPCRequest& request, const ChainstateManager& chainman) { - return protx_update_registrar_wrapper(request, false); + return protx_update_registrar_wrapper(request, chainman, false); } -static UniValue protx_update_registrar_legacy(const JSONRPCRequest& request) +static UniValue protx_update_registrar_legacy(const JSONRPCRequest& request, const ChainstateManager& chainman) { - return protx_update_registrar_wrapper(request, true); + return protx_update_registrar_wrapper(request, chainman, true); } static void protx_revoke_help(const JSONRPCRequest& request) @@ -1135,7 +1136,7 @@ static void protx_revoke_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue protx_revoke(const JSONRPCRequest& request) +static UniValue protx_revoke(const JSONRPCRequest& request, const ChainstateManager& chainman) { protx_revoke_help(request); @@ -1144,7 +1145,7 @@ static UniValue protx_revoke(const JSONRPCRequest& request) EnsureWalletIsUnlocked(wallet.get()); - const bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); + const bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();)); const bool is_bls_legacy = !isV19active; CProUpRevTx ptx; ptx.nVersion = CProUpRevTx::GetVersion(isV19active); @@ -1195,7 +1196,7 @@ static UniValue protx_revoke(const JSONRPCRequest& request) SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx); + return SignAndSendSpecialTx(request, chainman, tx); } #endif//ENABLE_WALLET @@ -1297,7 +1298,7 @@ static UniValue BuildDMNListEntry(CWallet* pwallet, const CDeterministicMN& dmn, return o; } -static UniValue protx_list(const JSONRPCRequest& request) +static UniValue protx_list(const JSONRPCRequest& request, const ChainstateManager& chainman) { protx_list_help(request); @@ -1333,8 +1334,8 @@ static UniValue protx_list(const JSONRPCRequest& request) bool detailed = !request.params[1].isNull() ? ParseBoolV(request.params[1], "detailed") : false; - int height = !request.params[2].isNull() ? ParseInt32V(request.params[2], "height") : ::ChainActive().Height(); - if (height < 1 || height > ::ChainActive().Height()) { + int height = !request.params[2].isNull() ? ParseInt32V(request.params[2], "height") : chainman.ActiveChain().Height(); + if (height < 1 || height > chainman.ActiveChain().Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid height specified"); } @@ -1345,7 +1346,7 @@ static UniValue protx_list(const JSONRPCRequest& request) setOutpts.emplace(outpt); } - CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(::ChainActive()[height]); + CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(chainman.ActiveChain()[height]); mnList.ForEachMN(false, [&](const auto& dmn) { if (setOutpts.count(dmn.collateralOutpoint) || CheckWalletOwnsKey(wallet.get(), dmn.pdmnState->keyIDOwner) || @@ -1365,12 +1366,12 @@ static UniValue protx_list(const JSONRPCRequest& request) bool detailed = !request.params[1].isNull() ? ParseBoolV(request.params[1], "detailed") : false; - int height = !request.params[2].isNull() ? ParseInt32V(request.params[2], "height") : ::ChainActive().Height(); - if (height < 1 || height > ::ChainActive().Height()) { + int height = !request.params[2].isNull() ? ParseInt32V(request.params[2], "height") : chainman.ActiveChain().Height(); + if (height < 1 || height > chainman.ActiveChain().Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid height specified"); } - CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(::ChainActive()[height]); + CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(chainman.ActiveChain()[height]); bool onlyValid = type == "valid"; bool onlyHPMN = type == "hpmn"; mnList.ForEachMN(onlyValid, [&](const auto& dmn) { @@ -1442,7 +1443,7 @@ static void protx_diff_help(const JSONRPCRequest& request) }.Check(request); } -static uint256 ParseBlock(const UniValue& v, std::string strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static uint256 ParseBlock(const UniValue& v, const ChainstateManager& chainman, std::string strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); @@ -1450,19 +1451,19 @@ static uint256 ParseBlock(const UniValue& v, std::string strName) EXCLUSIVE_LOCK return ParseHashV(v, strName); } catch (...) { int h = ParseInt32V(v, strName); - if (h < 1 || h > ::ChainActive().Height()) + if (h < 1 || h > chainman.ActiveChain().Height()) throw std::runtime_error(strprintf("%s must be a block hash or chain height and not %s", strName, v.getValStr())); - return *::ChainActive()[h]->phashBlock; + return *chainman.ActiveChain()[h]->phashBlock; } } -static UniValue protx_diff(const JSONRPCRequest& request) +static UniValue protx_diff(const JSONRPCRequest& request, const ChainstateManager& chainman) { protx_diff_help(request); LOCK(cs_main); - uint256 baseBlockHash = ParseBlock(request.params[0], "baseBlock"); - uint256 blockHash = ParseBlock(request.params[1], "block"); + uint256 baseBlockHash = ParseBlock(request.params[0], chainman, "baseBlock"); + uint256 blockHash = ParseBlock(request.params[1], chainman, "block"); bool extended = false; if (!request.params[2].isNull()) { extended = ParseBoolV(request.params[2], "extended"); @@ -1470,7 +1471,7 @@ static UniValue protx_diff(const JSONRPCRequest& request) CSimplifiedMNListDiff mnListDiff; std::string strError; - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureAnyLLMQContext(request.context); if (!BuildSimplifiedMNListDiff(baseBlockHash, blockHash, mnListDiff, *llmq_ctx.quorum_block_processor, strError, extended)) { throw std::runtime_error(strError); @@ -1494,33 +1495,33 @@ static void protx_listdiff_help(const JSONRPCRequest& request) }.Check(request); } -static const CBlockIndex* ParseBlockIndex(const UniValue& v, std::string strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static const CBlockIndex* ParseBlockIndex(const UniValue& v, const ChainstateManager& chainman, std::string strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); try { uint256 hash = ParseHashV(v, strName); - const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); + const CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) throw std::runtime_error(strprintf("Block %s with hash %s not found", strName, v.getValStr())); return pblockindex; } catch (...) { int h = ParseInt32V(v, strName); - if (h < 1 || h > ::ChainActive().Height()) + if (h < 1 || h > chainman.ActiveChain().Height()) throw std::runtime_error(strprintf("%s must be a chain height and not %s", strName, v.getValStr())); - return ::ChainActive()[h]; + return chainman.ActiveChain()[h]; } } -static UniValue protx_listdiff(const JSONRPCRequest& request) +static UniValue protx_listdiff(const JSONRPCRequest& request, const ChainstateManager& chainman) { protx_listdiff_help(request); LOCK(cs_main); UniValue ret(UniValue::VOBJ); - const CBlockIndex* pBaseBlockIndex = ParseBlockIndex(request.params[0], "baseBlock"); - const CBlockIndex* pTargetBlockIndex = ParseBlockIndex(request.params[1], "block"); + const CBlockIndex* pBaseBlockIndex = ParseBlockIndex(request.params[0], chainman, "baseBlock"); + const CBlockIndex* pTargetBlockIndex = ParseBlockIndex(request.params[1], chainman, "block"); if (pBaseBlockIndex == nullptr) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Base block not found"); @@ -1610,35 +1611,37 @@ static UniValue protx(const JSONRPCRequest& request) const JSONRPCRequest new_request{request.strMethod == "protx" ? request.squashed() : request}; const std::string command{new_request.strMethod}; + const ChainstateManager& chainman = EnsureAnyChainman(request.context); + #ifdef ENABLE_WALLET if (command == "protxregister" || command == "protxregister_fund" || command == "protxregister_prepare") { - return protx_register(new_request); + return protx_register(new_request, chainman); } else if (command == "protxregister_hpmn" || command == "protxregister_fund_hpmn" || command == "protxregister_prepare_hpmn") { - return protx_register_hpmn(new_request); + return protx_register_hpmn(new_request, chainman); } else if (command == "protxregister_legacy" || command == "protxregister_fund_legacy" || command == "protxregister_prepare_legacy") { - return protx_register_legacy(new_request); + return protx_register_legacy(new_request, chainman); } else if (command == "protxregister_submit") { - return protx_register_submit(new_request); + return protx_register_submit(new_request, chainman); } else if (command == "protxupdate_service") { - return protx_update_service_common_wrapper(new_request, MnType::Regular); + return protx_update_service_common_wrapper(new_request, chainman, MnType::Regular); } else if (command == "protxupdate_service_hpmn") { - return protx_update_service_common_wrapper(new_request, MnType::HighPerformance); + return protx_update_service_common_wrapper(new_request, chainman, MnType::HighPerformance); } else if (command == "protxupdate_registrar") { - return protx_update_registrar(new_request); + return protx_update_registrar(new_request, chainman); } else if (command == "protxupdate_registrar_legacy") { - return protx_update_registrar_legacy(new_request); + return protx_update_registrar_legacy(new_request, chainman); } else if (command == "protxrevoke") { - return protx_revoke(new_request); + return protx_revoke(new_request, chainman); } else #endif if (command == "protxlist") { - return protx_list(new_request); + return protx_list(new_request, chainman); } else if (command == "protxinfo") { return protx_info(new_request); } else if (command == "protxdiff") { - return protx_diff(new_request); + return protx_diff(new_request, chainman); } else if (command == "protxlistdiff") { - return protx_listdiff(new_request); + return protx_listdiff(new_request, chainman); } else { protx_help(); @@ -1665,13 +1668,13 @@ static void bls_generate_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue bls_generate(const JSONRPCRequest& request) +static UniValue bls_generate(const JSONRPCRequest& request, const ChainstateManager& chainman) { bls_generate_help(request); CBLSSecretKey sk; sk.MakeNewKey(); - bool bls_legacy_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip()); + bool bls_legacy_scheme = !llmq::utils::IsV19Active(chainman.ActiveChain().Tip()); if (!request.params[0].isNull()) { bls_legacy_scheme = ParseBoolV(request.params[0], "bls_legacy_scheme"); } @@ -1704,11 +1707,11 @@ static void bls_fromsecret_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue bls_fromsecret(const JSONRPCRequest& request) +static UniValue bls_fromsecret(const JSONRPCRequest& request, const ChainstateManager& chainman) { bls_fromsecret_help(request); - bool bls_legacy_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip()); + bool bls_legacy_scheme = !llmq::utils::IsV19Active(chainman.ActiveChain().Tip()); if (!request.params[1].isNull()) { bls_legacy_scheme = ParseBoolV(request.params[1], "bls_legacy_scheme"); } @@ -1742,10 +1745,12 @@ static UniValue _bls(const JSONRPCRequest& request) const JSONRPCRequest new_request{request.strMethod == "bls" ? request.squashed() : request}; const std::string command{new_request.strMethod}; + const ChainstateManager& chainman = EnsureAnyChainman(request.context); + if (command == "blsgenerate") { - return bls_generate(new_request); + return bls_generate(new_request, chainman); } else if (command == "blsfromsecret") { - return bls_fromsecret(new_request); + return bls_fromsecret(new_request, chainman); } else { bls_help(); } diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 3096bd0d3210..cab85368d244 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -366,7 +366,8 @@ static UniValue gobject_submit(const JSONRPCRequest& request) std::string strHash = govobj.GetHash().ToString(); - const CTxMemPool& mempool = EnsureMemPool(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); bool fMissingConfirmations; { if (g_txindex) { @@ -390,7 +391,6 @@ static UniValue gobject_submit(const JSONRPCRequest& request) LogPrintf("gobject(submit) -- Adding locally created governance object - %s\n", strHash); - const NodeContext& node = EnsureNodeContext(request.context); if (fMissingConfirmations) { governance->AddPostponedObject(govobj); govobj.Relay(*node.connman); @@ -488,7 +488,7 @@ static UniValue gobject_vote_conf(const JSONRPCRequest& request) } CGovernanceException exception; - const NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (governance->ProcessVoteAndRelay(vote, exception, *node.connman)) { nSuccessful++; statusObj.pushKV("result", "success"); @@ -510,7 +510,7 @@ static UniValue VoteWithMasternodes(const JSONRPCRequest& request, const std::ma const uint256& hash, vote_signal_enum_t eVoteSignal, vote_outcome_enum_t eVoteOutcome) { - const NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); { LOCK(governance->cs); CGovernanceObject *pGovObj = governance->FindGovernanceObject(hash); @@ -1119,7 +1119,7 @@ static UniValue voteraw(const JSONRPCRequest& request) } CGovernanceException exception; - const NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (governance->ProcessVoteAndRelay(vote, exception, *node.connman)) { return "Voted successfully"; } else { @@ -1150,7 +1150,9 @@ static UniValue getgovernanceinfo(const JSONRPCRequest& request) int nLastSuperblock = 0, nNextSuperblock = 0; - const auto* pindex = WITH_LOCK(cs_main, return ::ChainActive().Tip()); + + const ChainstateManager& chainman = EnsureAnyChainman(request.context); + const auto* pindex = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); int nBlockHeight = pindex->nHeight; CSuperblock::GetNearestSuperblocksHeights(nBlockHeight, nLastSuperblock, nNextSuperblock); diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 8fca2a15c6ce..7840e089aff8 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,7 @@ #include #include -static UniValue masternodelist(const JSONRPCRequest& request); +static UniValue masternodelist(const JSONRPCRequest& request, ChainstateManager& chainman); static void masternode_list_help(const JSONRPCRequest& request) { @@ -83,7 +84,7 @@ static UniValue masternode_connect(const JSONRPCRequest& request) if (!Lookup(strAddress, addr, 0, false)) throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Incorrect masternode address %s", strAddress)); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); node.connman->OpenMasternodeConnection(CAddress(addr, NODE_NETWORK)); if (!node.connman->IsConnected(CAddress(addr, NODE_NETWORK), CConnman::AllNodes)) throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to masternode %s", strAddress)); @@ -326,14 +327,14 @@ static void masternode_winners_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue masternode_winners(const JSONRPCRequest& request) +static UniValue masternode_winners(const JSONRPCRequest& request, const ChainstateManager& chainman) { masternode_winners_help(request); const CBlockIndex* pindexTip{nullptr}; { LOCK(cs_main); - pindexTip = ::ChainActive().Tip(); + pindexTip = chainman.ActiveChain().Tip(); if (!pindexTip) return NullUniValue; } @@ -405,7 +406,7 @@ static void masternode_payments_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue masternode_payments(const JSONRPCRequest& request) +static UniValue masternode_payments(const JSONRPCRequest& request, const ChainstateManager& chainman) { masternode_payments_help(request); @@ -417,11 +418,11 @@ static UniValue masternode_payments(const JSONRPCRequest& request) if (request.params[0].isNull()) { LOCK(cs_main); - pindex = ::ChainActive().Tip(); + pindex = chainman.ActiveChain().Tip(); } else { LOCK(cs_main); uint256 blockHash(ParseHashV(request.params[0], "blockhash")); - pindex = g_chainman.m_blockman.LookupBlockIndex(blockHash); + pindex = chainman.m_blockman.LookupBlockIndex(blockHash); if (pindex == nullptr) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -442,7 +443,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request) // Note: we have to actually calculate block reward from scratch instead of simply querying coinbase vout // because miners might collect less coins than they potentially could and this would break our calculations. CAmount nBlockFees{0}; - const CTxMemPool& mempool = EnsureMemPool(request.context); + const CTxMemPool& mempool = EnsureAnyMemPool(request.context); for (const auto& tx : block.vtx) { if (tx->IsCoinBase()) { continue; @@ -496,7 +497,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request) if (nCount > 0) { LOCK(cs_main); - pindex = ::ChainActive().Next(pindex); + pindex = chainman.ActiveChain().Next(pindex); } else { pindex = pindex->pprev; } @@ -542,6 +543,8 @@ static UniValue masternode(const JSONRPCRequest& request) const JSONRPCRequest new_request{request.strMethod == "masternode" ? request.squashed() : request}; const std::string command{new_request.strMethod}; + ChainstateManager& chainman = EnsureAnyChainman(request.context); + if (command == "masternodeconnect") { return masternode_connect(new_request); } else if (command == "masternodecount") { @@ -557,17 +560,17 @@ static UniValue masternode(const JSONRPCRequest& request) } else if (command == "masternodestatus") { return masternode_status(new_request); } else if (command == "masternodepayments") { - return masternode_payments(new_request); + return masternode_payments(new_request, chainman); } else if (command == "masternodewinners") { - return masternode_winners(new_request); + return masternode_winners(new_request, chainman); } else if (command == "masternodelist") { - return masternodelist(new_request); + return masternodelist(new_request, chainman); } else { masternode_help(); } } -static UniValue masternodelist(const JSONRPCRequest& request) +static UniValue masternodelist(const JSONRPCRequest& request, ChainstateManager& chainman) { std::string strMode = "json"; std::string strFilter = ""; @@ -605,13 +608,13 @@ static UniValue masternodelist(const JSONRPCRequest& request) } LOCK(cs_main); - const CBlockIndex* pindex = ::ChainActive()[dmn.pdmnState->nLastPaidHeight]; + const CBlockIndex* pindex = chainman.ActiveChain()[dmn.pdmnState->nLastPaidHeight]; return (int)pindex->nTime; }; bool showRecentMnsOnly = strMode == "recent"; bool showHPMNsOnly = strMode == "hpmn"; - int tipHeight = WITH_LOCK(cs_main, return ::ChainActive().Tip()->nHeight); + int tipHeight = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()->nHeight); mnList.ForEachMN(false, [&](auto& dmn) { if (showRecentMnsOnly && mnList.IsMNPoSeBanned(dmn)) { if (tipHeight - dmn.pdmnState->GetBannedHeight() > Params().GetConsensus().nSuperblockCycle) { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 1e0797cd3268..ca8c5d2263ef 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -54,11 +54,12 @@ * or from the last difficulty change if 'lookup' is nonpositive. * If 'height' is nonnegative, compute the estimate at the time when a given block was found. */ -static UniValue GetNetworkHashPS(int lookup, int height) { - CBlockIndex *pb = ::ChainActive().Tip(); +static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) { + const CBlockIndex* pb = active_chain.Tip(); - if (height >= 0 && height < ::ChainActive().Height()) - pb = ::ChainActive()[height]; + if (height >= 0 && height < active_chain.Height()) { + pb = active_chain[height]; + } if (pb == nullptr || !pb->nHeight) return 0; @@ -71,7 +72,7 @@ static UniValue GetNetworkHashPS(int lookup, int height) { if (lookup > pb->nHeight) lookup = pb->nHeight; - CBlockIndex *pb0 = pb; + const CBlockIndex* pb0 = pb; int64_t minTime = pb0->GetBlockTime(); int64_t maxTime = minTime; for (int i = 0; i < lookup; i++) { @@ -109,8 +110,9 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request) }, }.Check(request); + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1); + return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1, chainman.ActiveChain()); } #if ENABLE_MINER @@ -120,7 +122,8 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& { LOCK(cs_main); - IncrementExtraNonce(&block, ::ChainActive().Tip(), extra_nonce); + CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain())); + IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce); } CChainParams chainparams(Params()); @@ -154,14 +157,15 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me { // Don't keep cs_main locked LOCK(cs_main); - nHeight = ::ChainActive().Height(); + CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain())); + nHeight = chainman.ActiveChain().Height(); nHeightEnd = nHeight+nGenerate; } unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd && !ShutdownRequested()) { - std::unique_ptr pblocktemplate(BlockAssembler(*sporkManager, *governance, quorum_block_processor, clhandler, isman, evodb, ::ChainstateActive(), mempool, Params()).CreateNewBlock(coinbase_script)); + std::unique_ptr pblocktemplate(BlockAssembler(*sporkManager, *governance, quorum_block_processor, clhandler, isman, evodb, chainman.ActiveChainstate(), mempool, Params()).CreateNewBlock(coinbase_script)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -243,12 +247,12 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } - const NodeContext& node_context = EnsureNodeContext(request.context); - const CTxMemPool& mempool = EnsureMemPool(request.context); - ChainstateManager& chainman = EnsureChainman(request.context); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); + ChainstateManager& chainman = EnsureChainman(node); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); - return generateBlocks(chainman, mempool, *node_context.evodb, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, coinbase_script, num_blocks, max_tries); + return generateBlocks(chainman, mempool, *node.evodb, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, coinbase_script, num_blocks, max_tries); } static UniValue generatetoaddress(const JSONRPCRequest& request) @@ -280,14 +284,14 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); } - const NodeContext& node_context = EnsureNodeContext(request.context); - const CTxMemPool& mempool = EnsureMemPool(request.context); - ChainstateManager& chainman = EnsureChainman(request.context); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); + ChainstateManager& chainman = EnsureChainman(node); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); CScript coinbase_script = GetScriptForDestination(destination); - return generateBlocks(chainman, mempool, *node_context.evodb, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, coinbase_script, num_blocks, max_tries); + return generateBlocks(chainman, mempool, *node.evodb, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, coinbase_script, num_blocks, max_tries); } static UniValue generateblock(const JSONRPCRequest& request) @@ -329,8 +333,8 @@ static UniValue generateblock(const JSONRPCRequest& request) coinbase_script = GetScriptForDestination(destination); } - const NodeContext& node_context = EnsureNodeContext(request.context); - const CTxMemPool& mempool = EnsureMemPool(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); std::vector txs; const auto raw_txs_or_txids = request.params[1].get_array(); @@ -357,14 +361,17 @@ static UniValue generateblock(const JSONRPCRequest& request) } CChainParams chainparams(Params()); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); CBlock block; + + ChainstateManager& chainman = EnsureChainman(node); + CChainState& active_chainstate = chainman.ActiveChainstate(); { LOCK(cs_main); CTxMemPool empty_mempool; - std::unique_ptr blocktemplate(BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, ::ChainstateActive(), empty_mempool, chainparams).CreateNewBlock(coinbase_script)); + std::unique_ptr blocktemplate(BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node.evodb, active_chainstate, empty_mempool, chainparams).CreateNewBlock(coinbase_script)); if (!blocktemplate) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); } @@ -381,7 +388,7 @@ static UniValue generateblock(const JSONRPCRequest& request) LOCK(cs_main); BlockValidationState state; - if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, chainparams, ::ChainstateActive(), block, g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { + if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node.evodb, chainparams, active_chainstate, block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.GetRejectReason())); } } @@ -390,7 +397,7 @@ static UniValue generateblock(const JSONRPCRequest& request) uint64_t max_tries{DEFAULT_MAX_TRIES}; unsigned int extra_nonce{0}; - if (!GenerateBlock(EnsureChainman(request.context), block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) { + if (!GenerateBlock(chainman, block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) { throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block."); } @@ -436,14 +443,19 @@ static UniValue getmininginfo(const JSONRPCRequest& request) }, }.Check(request); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); - const CTxMemPool& mempool = EnsureMemPool(request.context); + + const CTxMemPool& mempool = EnsureMemPool(node); + const CChain& active_chain = chainman.ActiveChain(); UniValue obj(UniValue::VOBJ); - obj.pushKV("blocks", (int)::ChainActive().Height()); + obj.pushKV("blocks", active_chain.Height()); if (BlockAssembler::m_last_block_size) obj.pushKV("currentblocksize", *BlockAssembler::m_last_block_size); if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs); - obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip())); + obj.pushKV("difficulty", (double)GetDifficulty(active_chain.Tip())); obj.pushKV("networkhashps", getnetworkhashps(request)); obj.pushKV("pooledtx", (uint64_t)mempool.size()); obj.pushKV("chain", Params().NetworkIDString()); @@ -477,7 +489,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) uint256 hash(ParseHashV(request.params[0].get_str(), "txid")); CAmount nAmount = request.params[1].get_int64(); - EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount); + EnsureAnyMemPool(request.context).PrioritiseTransaction(hash, nAmount); return true; } @@ -618,14 +630,17 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) }, }.Check(request); - LOCK(cs_main); + const NodeContext& node = EnsureAnyNodeContext(request.context); - const NodeContext& node_context = EnsureNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); + LOCK(cs_main); std::string strMode = "template"; UniValue lpval = NullUniValue; std::set setClientRules; int64_t nMaxVersionPreVB = -1; + CChainState& active_chainstate = chainman.ActiveChainstate(); + CChain& active_chain = active_chainstate.m_chain; if (!request.params[0].isNull()) { const UniValue& oparam = request.params[0].get_obj(); @@ -651,7 +666,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); uint256 hash = block.GetHash(); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); + const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash); if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) return "duplicate"; @@ -660,14 +675,14 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) return "duplicate-inconclusive"; } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); - CBlockIndex* const pindexPrev = ::ChainActive().Tip(); + CBlockIndex* const pindexPrev = active_chain.Tip(); // TestBlockValidity only supports blocks built on the current Tip if (block.hashPrevBlock != pindexPrev->GetBlockHash()) return "inconclusive-not-best-prevblk"; BlockValidationState state; - TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, Params(), ::ChainstateActive(), block, pindexPrev, false, true); + TestBlockValidity(state, *llmq_ctx.clhandler, *node.evodb, Params(), active_chainstate, block, pindexPrev, false, true); return BIP22ValidationResult(state); } @@ -689,24 +704,23 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) if (strMode != "template") throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - NodeContext& node = EnsureNodeContext(request.context); if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); - if (::ChainstateActive().IsInitialBlockDownload()) + if (active_chainstate.IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); // next bock is a superblock and we need governance info to correctly construct it if (AreSuperblocksEnabled(*sporkManager) && !::masternodeSync->IsSynced() - && CSuperblock::IsValidBlockHeight(::ChainActive().Height() + 1)) + && CSuperblock::IsValidBlockHeight(active_chain.Height() + 1)) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is syncing with network..."); static unsigned int nTransactionsUpdatedLast; - const CTxMemPool& mempool = EnsureMemPool(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); if (!lpval.isNull()) { @@ -726,7 +740,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) else { // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier - hashWatchedChain = ::ChainActive().Tip()->GetBlockHash(); + hashWatchedChain = active_chain.Tip()->GetBlockHash(); nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; } @@ -759,7 +773,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) static CBlockIndex* pindexPrev; static int64_t nStart; static std::unique_ptr pblocktemplate; - if (pindexPrev != ::ChainActive().Tip() || + if (pindexPrev != active_chain.Tip() || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on @@ -767,13 +781,13 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // Store the ::ChainActive().Tip() used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); - CBlockIndex* pindexPrevNew = ::ChainActive().Tip(); + CBlockIndex* pindexPrevNew = active_chain.Tip(); nStart = GetTime(); // Create new block CScript scriptDummy = CScript() << OP_TRUE; - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - pblocktemplate = BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, ::ChainstateActive(), mempool, Params()).CreateNewBlock(scriptDummy); + LLMQContext& llmq_ctx = EnsureAnyLLMQContext(request.context); + pblocktemplate = BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node.evodb, active_chainstate, mempool, Params()).CreateNewBlock(scriptDummy); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -893,7 +907,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) result.pushKV("transactions", transactions); result.pushKV("coinbaseaux", aux); result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->GetValueOut()); - result.pushKV("longpollid", ::ChainActive().Tip()->GetBlockHash().GetHex() + ToString(nTransactionsUpdatedLast)); + result.pushKV("longpollid", active_chain.Tip()->GetBlockHash().GetHex() + ToString(nTransactionsUpdatedLast)); result.pushKV("target", hashTarget.GetHex()); result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); result.pushKV("mutable", aMutable); @@ -987,10 +1001,11 @@ static UniValue submitblock(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block does not start with a coinbase"); } + ChainstateManager& chainman = EnsureAnyChainman(request.context); uint256 hash = block.GetHash(); { LOCK(cs_main); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); + const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash); if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) { return "duplicate"; @@ -1004,7 +1019,7 @@ static UniValue submitblock(const JSONRPCRequest& request) bool new_block; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool accepted = EnsureChainman(request.context).ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block); + bool accepted = chainman.ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block); UnregisterValidationInterface(&sc); if (!new_block && accepted) { return "duplicate"; @@ -1035,15 +1050,16 @@ static UniValue submitheader(const JSONRPCRequest& request) if (!DecodeHexBlockHeader(h, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed"); } + ChainstateManager& chainman = EnsureAnyChainman(request.context); { LOCK(cs_main); - if (!g_chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) { + if (!chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) { throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first"); } } BlockValidationState state; - EnsureChainman(request.context).ProcessNewBlockHeaders({h}, state, Params()); + chainman.ProcessNewBlockHeaders({h}, state, Params()); if (state.IsValid()) return NullUniValue; if (state.IsError()) { throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString()); @@ -1091,7 +1107,7 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request) RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR}); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); - CBlockPolicyEstimator& fee_estimator = EnsureFeeEstimator(request.context); + CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context); unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target); @@ -1176,7 +1192,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); - CBlockPolicyEstimator& fee_estimator = EnsureFeeEstimator(request.context); + CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context); unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 6bec4dfec898..f020e92f4f81 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -194,7 +196,7 @@ static UniValue sporkupdate(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid spork name"); } - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (!node.connman) { throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } @@ -571,14 +573,17 @@ static UniValue mnauth(const JSONRPCRequest& request) if (proTxHash.IsNull()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "proTxHash invalid"); } + + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CBLSPublicKey publicKey; - bool bls_legacy_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip()); + bool bls_legacy_scheme = !llmq::utils::IsV19Active(chainman.ActiveChain().Tip()); publicKey.SetHexStr(request.params[2].get_str(), bls_legacy_scheme); if (!publicKey.IsValid()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "publicKey invalid"); } - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); bool fSuccess = node.connman->ForNode(nodeId, CConnman::AllNodes, [&](CNode* pNode){ pNode->SetVerifiedProRegTxHash(proTxHash); pNode->SetVerifiedPubKeyHash(publicKey.GetHash()); @@ -697,7 +702,7 @@ static UniValue getaddressmempool(const JSONRPCRequest& request) std::vector > indexes; - CTxMemPool& mempool = EnsureMemPool(request.context); + CTxMemPool& mempool = EnsureAnyMemPool(request.context); if (!mempool.getAddressIndex(addresses, indexes)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); } @@ -922,7 +927,8 @@ static UniValue getaddressbalance(const JSONRPCRequest& request) } } - int nHeight = WITH_LOCK(cs_main, return ::ChainActive().Height()); + ChainstateManager& chainman = EnsureAnyChainman(request.context); + int nHeight = WITH_LOCK(cs_main, return chainman.ActiveChain().Height()); CAmount balance = 0; CAmount balance_spendable = 0; @@ -1066,7 +1072,7 @@ static UniValue getspentinfo(const JSONRPCRequest& request) CSpentIndexKey key(txid, outputIndex); CSpentIndexValue value; - CTxMemPool& mempool = EnsureMemPool(request.context); + CTxMemPool& mempool = EnsureAnyMemPool(request.context); if (!GetSpentIndex(mempool, key, value)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get spent info"); } @@ -1302,6 +1308,60 @@ static UniValue echo(const JSONRPCRequest& request) return request.params; } +static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name) +{ + UniValue ret_summary(UniValue::VOBJ); + if (!index_name.empty() && index_name != summary.name) return ret_summary; + + UniValue entry(UniValue::VOBJ); + entry.pushKV("synced", summary.synced); + entry.pushKV("best_block_height", summary.best_block_height); + ret_summary.pushKV(summary.name, entry); + return ret_summary; +} + +static RPCHelpMan getindexinfo() +{ + return RPCHelpMan{"getindexinfo", + "\nReturns the status of one or all available indices currently running in the node.\n", + { + {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Filter results for an index with a specific name."}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", { + { + RPCResult::Type::OBJ, "name", "The name of the index", + { + {RPCResult::Type::BOOL, "synced", "Whether the index is synced or not"}, + {RPCResult::Type::NUM, "best_block_height", "The block height to which the index is synced"}, + } + }, + }, + }, + RPCExamples{ + HelpExampleCli("getindexinfo", "") + + HelpExampleRpc("getindexinfo", "") + + HelpExampleCli("getindexinfo", "txindex") + + HelpExampleRpc("getindexinfo", "txindex") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + UniValue result(UniValue::VOBJ); + const std::string index_name = request.params[0].isNull() ? "" : request.params[0].get_str(); + + if (g_txindex) { + result.pushKVs(SummaryToJSON(g_txindex->GetSummary(), index_name)); + } + + ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) { + result.pushKVs(SummaryToJSON(index.GetSummary(), index_name)); + }); + + return result; +}, + }; +} + // clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames @@ -1315,6 +1375,7 @@ static const CRPCCommand commands[] = { "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} }, { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, + { "util", "getindexinfo", &getindexinfo, {"index_name"} }, { "blockchain", "getspentinfo", &getspentinfo, {"json"} }, /* Address index */ diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 17da32632560..e972f7a94206 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -45,7 +45,7 @@ static UniValue getconnectioncount(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -66,7 +66,7 @@ static UniValue ping(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -149,7 +149,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.connman || !node.peerman) { throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } @@ -266,7 +266,7 @@ static UniValue addnode(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -312,7 +312,7 @@ static UniValue disconnectnode(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -370,7 +370,7 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -438,7 +438,7 @@ static UniValue getnettotals(const JSONRPCRequest& request) + HelpExampleRpc("getnettotals", "") }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -541,7 +541,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) obj.pushKV("buildversion", FormatFullVersion()); obj.pushKV("subversion", strSubVersion); obj.pushKV("protocolversion",PROTOCOL_VERSION); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (node.connman) { ServiceFlags services = node.connman->GetLocalServices(); obj.pushKV("localservices", strprintf("%016x", services)); @@ -621,7 +621,7 @@ static UniValue setban(const JSONRPCRequest& request) if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) { throw std::runtime_error(help.ToString()); } - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (!node.banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } @@ -699,7 +699,7 @@ static UniValue listbanned(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if(!node.banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } @@ -733,7 +733,7 @@ static UniValue clearbanned(const JSONRPCRequest& request) + HelpExampleRpc("clearbanned", "") }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (!node.banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } @@ -754,7 +754,7 @@ static UniValue cleardiscouraged(const JSONRPCRequest& request) + HelpExampleRpc("cleardiscouraged", "") }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (!node.banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } @@ -775,7 +775,7 @@ static UniValue setnetworkactive(const JSONRPCRequest& request) RPCExamples{""}, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (!node.connman) { throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } @@ -809,7 +809,7 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) + HelpExampleRpc("getnodeaddresses", "8") }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (!node.connman) { throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } @@ -856,7 +856,7 @@ static UniValue addpeeraddress(const JSONRPCRequest& request) }, }.Check(request); - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); if (!node.addrman) { throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled"); } diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index 4b8e6a35782a..592d73d0daa2 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -53,7 +53,7 @@ static void quorum_list_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_list(const JSONRPCRequest& request) +static UniValue quorum_list(const JSONRPCRequest& request, const ChainstateManager& chainman, const LLMQContext& llmq_ctx) { quorum_list_help(request); @@ -67,8 +67,7 @@ static UniValue quorum_list(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - CBlockIndex* pindexTip = WITH_LOCK(cs_main, return ::ChainActive().Tip()); + CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pindexTip)) { const auto& llmq_params_opt = llmq::GetLLMQParams(type); @@ -119,22 +118,21 @@ static void quorum_list_extended_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_list_extended(const JSONRPCRequest& request) +static UniValue quorum_list_extended(const JSONRPCRequest& request, const ChainstateManager& chainman, const LLMQContext& llmq_ctx) { quorum_list_extended_help(request); int nHeight = -1; if (!request.params[0].isNull()) { nHeight = ParseInt32V(request.params[0], "height"); - if (nHeight < 0 || nHeight > WITH_LOCK(cs_main, return ::ChainActive().Height())) { + if (nHeight < 0 || nHeight > WITH_LOCK(cs_main, return chainman.ActiveChain().Height())) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } } UniValue ret(UniValue::VOBJ); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - CBlockIndex* pblockindex = nHeight != -1 ? WITH_LOCK(cs_main, return ::ChainActive()[nHeight]) : WITH_LOCK(cs_main, return ::ChainActive().Tip()); + CBlockIndex* pblockindex = nHeight != -1 ? WITH_LOCK(cs_main, return chainman.ActiveChain()[nHeight]) : WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pblockindex)) { const auto& llmq_params_opt = llmq::GetLLMQParams(type); @@ -232,7 +230,7 @@ static UniValue BuildQuorumInfo(const llmq::CQuorumCPtr& quorum, bool includeMem return ret; } -static UniValue quorum_info(const JSONRPCRequest& request) +static UniValue quorum_info(const JSONRPCRequest& request, const LLMQContext& llmq_ctx) { quorum_info_help(request); @@ -247,7 +245,6 @@ static UniValue quorum_info(const JSONRPCRequest& request) includeSkShare = ParseBoolV(request.params[2], "includeSkShare"); } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); auto quorum = llmq_ctx.qman->GetQuorum(llmqType, quorumHash); if (!quorum) { throw JSONRPCError(RPC_INVALID_PARAMETER, "quorum not found"); @@ -271,7 +268,7 @@ static void quorum_dkgstatus_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_dkgstatus(const JSONRPCRequest& request) +static UniValue quorum_dkgstatus(const JSONRPCRequest& request, const ChainstateManager& chainman, const LLMQContext& llmq_ctx) { quorum_dkgstatus_help(request); @@ -284,12 +281,11 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request) } llmq::CDKGDebugStatus status; - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); llmq_ctx.dkg_debugman->GetLocalDebugStatus(status); auto ret = status.ToJson(detailLevel); - CBlockIndex* pindexTip = WITH_LOCK(cs_main, return ::ChainActive().Tip()); + CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); int tipHeight = pindexTip->nHeight; auto proTxHash = WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.proTxHash); @@ -311,7 +307,7 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request) if (fMasternodeMode) { int quorumHeight = tipHeight - (tipHeight % llmq_params.dkgInterval) + quorumIndex; if (quorumHeight <= tipHeight) { - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return ::ChainActive()[quorumHeight]); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return chainman.ActiveChain()[quorumHeight]); obj.pushKV("pQuorumBaseBlockIndex", pQuorumBaseBlockIndex->nHeight); obj.pushKV("quorumHash", pQuorumBaseBlockIndex->GetBlockHash().ToString()); obj.pushKV("pindexTip", pindexTip->nHeight); @@ -322,7 +318,7 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request) pQuorumBaseBlockIndex, proTxHash, true); std::map foundConnections; - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); node.connman->ForEachNode([&](const CNode* pnode) { auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash(); if (!verifiedProRegTxHash.IsNull() && allConnections.count(verifiedProRegTxHash)) { @@ -378,7 +374,7 @@ static void quorum_memberof_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_memberof(const JSONRPCRequest& request) +static UniValue quorum_memberof(const JSONRPCRequest& request, const ChainstateManager& chainman, const LLMQContext& llmq_ctx) { quorum_memberof_help(request); @@ -391,7 +387,7 @@ static UniValue quorum_memberof(const JSONRPCRequest& request) } } - const CBlockIndex* pindexTip = WITH_LOCK(cs_main, return ::ChainActive().Tip()); + const CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); auto mnList = deterministicMNManager->GetListForBlock(pindexTip); auto dmn = mnList.GetMN(protxHash); @@ -399,8 +395,6 @@ static UniValue quorum_memberof(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "masternode not found"); } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - UniValue result(UniValue::VARR); for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pindexTip)) { const auto& llmq_params_opt = llmq::GetLLMQParams(type); @@ -503,7 +497,7 @@ static void quorum_isconflicting_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_sigs_cmd(const JSONRPCRequest& request) +static UniValue quorum_sigs_cmd(const JSONRPCRequest& request, const LLMQContext& llmq_ctx) { auto cmd = request.strMethod; if (request.fHelp || (request.params.size() != 3)) { @@ -523,7 +517,6 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request) } } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[0], "llmqType"); const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); @@ -634,7 +627,7 @@ static void quorum_selectquorum_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_selectquorum(const JSONRPCRequest& request) +static UniValue quorum_selectquorum(const JSONRPCRequest& request, const LLMQContext& llmq_ctx) { quorum_selectquorum_help(request); @@ -648,7 +641,6 @@ static UniValue quorum_selectquorum(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); auto quorum = llmq_ctx.sigman->SelectQuorumForSigning(llmq_params_opt.value(), *llmq_ctx.qman, id); if (!quorum) { throw JSONRPCError(RPC_MISC_ERROR, "no quorums active"); @@ -719,7 +711,7 @@ static void quorum_getdata_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_getdata(const JSONRPCRequest& request) +static UniValue quorum_getdata(const JSONRPCRequest& request, const LLMQContext& llmq_ctx, const ChainstateManager& chainman) { quorum_getdata_help(request); @@ -741,9 +733,9 @@ static UniValue quorum_getdata(const JSONRPCRequest& request) } } - NodeContext& node = EnsureNodeContext(request.context); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash)); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(quorumHash)); + + const NodeContext& node = EnsureAnyNodeContext(request.context); return node.connman->ForNode(nodeId, [&](CNode* pNode) { return llmq_ctx.qman->RequestQuorumData(pNode, llmqType, pQuorumBaseBlockIndex, nDataMask, proTxHash); }); @@ -764,7 +756,7 @@ static void quorum_rotationinfo_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue quorum_rotationinfo(const JSONRPCRequest& request) +static UniValue quorum_rotationinfo(const JSONRPCRequest& request, const LLMQContext& llmq_ctx) { quorum_rotationinfo_help(request); @@ -783,7 +775,6 @@ static UniValue quorum_rotationinfo(const JSONRPCRequest& request) LOCK(cs_main); - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); if (!BuildQuorumRotationInfo(cmd, quorumRotationInfoRet, *llmq_ctx.qman, *llmq_ctx.quorum_block_processor, strError)) { throw JSONRPCError(RPC_INVALID_REQUEST, strError); } @@ -830,26 +821,30 @@ static UniValue _quorum(const JSONRPCRequest& request) const JSONRPCRequest new_request{request.strMethod == "quorum" ? request.squashed() : request}; const std::string command{new_request.strMethod}; + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + if (command == "quorumlist") { - return quorum_list(new_request); + return quorum_list(new_request, chainman, llmq_ctx); } else if (command == "quorumlistextended") { - return quorum_list_extended(new_request); + return quorum_list_extended(new_request, chainman, llmq_ctx); } else if (command == "quoruminfo") { - return quorum_info(new_request); + return quorum_info(new_request, llmq_ctx); } else if (command == "quorumdkgstatus") { - return quorum_dkgstatus(new_request); + return quorum_dkgstatus(new_request, chainman, llmq_ctx); } else if (command == "quorummemberof") { - return quorum_memberof(new_request); + return quorum_memberof(new_request, chainman, llmq_ctx); } else if (command == "quorumsign" || command == "quorumverify" || command == "quorumhasrecsig" || command == "quorumgetrecsig" || command == "quorumisconflicting") { - return quorum_sigs_cmd(new_request); + return quorum_sigs_cmd(new_request, llmq_ctx); } else if (command == "quorumselectquorum") { - return quorum_selectquorum(new_request); + return quorum_selectquorum(new_request, llmq_ctx); } else if (command == "quorumdkgsimerror") { return quorum_dkgsimerror(new_request); } else if (command == "quorumgetdata") { - return quorum_getdata(new_request); + return quorum_getdata(new_request, llmq_ctx, chainman); } else if (command == "quorumrotationinfo") { - return quorum_rotationinfo(new_request); + return quorum_rotationinfo(new_request, llmq_ctx); } else { quorum_help(); } @@ -875,10 +870,13 @@ static UniValue verifychainlock(const JSONRPCRequest& request) const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash")); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + int nBlockHeight; CBlockIndex* pIndex{nullptr}; if (request.params[2].isNull()) { - pIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(nBlockHash)); + pIndex = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(nBlockHash)); if (pIndex == nullptr) { throw JSONRPCError(RPC_INTERNAL_ERROR, "blockHash not found"); } @@ -889,8 +887,8 @@ static UniValue verifychainlock(const JSONRPCRequest& request) if (nBlockHeight < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } - if (nBlockHeight <= ::ChainActive().Height()) { - pIndex = ::ChainActive()[nBlockHeight]; + if (nBlockHeight <= chainman.ActiveChain().Height()) { + pIndex = chainman.ActiveChain()[nBlockHeight]; } } @@ -908,8 +906,7 @@ static UniValue verifychainlock(const JSONRPCRequest& request) } } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - + const LLMQContext& llmq_ctx = EnsureLLMQContext(node); return llmq_ctx.clhandler->VerifyChainLock(llmq::CChainLockSig(nBlockHeight, nBlockHash, sig)); } @@ -935,6 +932,9 @@ static UniValue verifyislock(const JSONRPCRequest& request) uint256 id(ParseHashV(request.params[0], "id")); uint256 txid(ParseHashV(request.params[1], "txid")); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + if (g_txindex) { g_txindex->BlockUntilSyncedToCurrentChain(); } @@ -945,7 +945,7 @@ static UniValue verifyislock(const JSONRPCRequest& request) uint256 hash_block; CTransactionRef tx = GetTransaction(/* block_index */ nullptr, /* mempool */ nullptr, txid, Params().GetConsensus(), hash_block); if (tx && !hash_block.IsNull()) { - pindexMined = g_chainman.m_blockman.LookupBlockIndex(hash_block); + pindexMined = chainman.m_blockman.LookupBlockIndex(hash_block); } } @@ -965,9 +965,9 @@ static UniValue verifyislock(const JSONRPCRequest& request) { LOCK(cs_main); if (signHeight == -1) { - pBlockIndex = ::ChainActive().Tip(); + pBlockIndex = chainman.ActiveChain().Tip(); } else { - pBlockIndex = ::ChainActive()[signHeight]; + pBlockIndex = chainman.ActiveChain()[signHeight]; } } @@ -979,7 +979,7 @@ static UniValue verifyislock(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature format"); } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); + const LLMQContext& llmq_ctx = EnsureLLMQContext(node); auto llmqType = llmq::utils::GetInstantSendLLMQType(*llmq_ctx.qman, pBlockIndex); // First check against the current active set, if it fails check against the last active set diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 5d2f8334a129..07482192e2b1 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ */ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10}; -void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempool, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry) +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempool, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry) { // Call into TxToUniv() in bitcoin-common to decode the transaction hex. // @@ -91,11 +92,11 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempo LOCK(cs_main); entry.pushKV("blockhash", hashBlock.GetHex()); - CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); + CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock); if (pindex) { - if (::ChainActive().Contains(pindex)) { + if (active_chainstate.m_chain.Contains(pindex)) { entry.pushKV("height", pindex->nHeight); - entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight); + entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); entry.pushKV("time", pindex->GetBlockTime()); entry.pushKV("blocktime", pindex->GetBlockTime()); chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); @@ -200,7 +201,8 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) }, }.Check(request); - const NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); bool in_active_chain = true; uint256 hash = ParseHashV(request.params[0], "parameter 1"); @@ -221,11 +223,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) LOCK(cs_main); uint256 blockhash = ParseHashV(request.params[2], "parameter 3"); - blockindex = g_chainman.m_blockman.LookupBlockIndex(blockhash); + blockindex = chainman.m_blockman.LookupBlockIndex(blockhash); if (!blockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found"); } - in_active_chain = ::ChainActive().Contains(blockindex); + in_active_chain = chainman.ActiveChain().Contains(blockindex); } bool f_txindex_ready = false; @@ -256,12 +258,12 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) return EncodeHexTx(*tx); } - LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - CTxMemPool& mempool = EnsureMemPool(request.context); + LLMQContext& llmq_ctx = EnsureLLMQContext(node); + CTxMemPool& mempool = EnsureMemPool(node); UniValue result(UniValue::VOBJ); if (blockindex) result.pushKV("in_active_chain", in_active_chain); - TxToJSON(*tx, hash_block, mempool, *llmq_ctx.clhandler, *llmq_ctx.isman, result); + TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); return result; } @@ -306,21 +308,23 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) CBlockIndex* pblockindex = nullptr; uint256 hashBlock; + ChainstateManager& chainman = EnsureAnyChainman(request.context); if (!request.params[1].isNull()) { LOCK(cs_main); hashBlock = ParseHashV(request.params[1], "blockhash"); - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); + pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } else { LOCK(cs_main); + CChainState& active_chainstate = chainman.ActiveChainstate(); // Loop through txids and try to find which block they're in. Exit loop once a block is found. for (const auto& tx : setTxids) { - const Coin& coin = AccessByTxid(::ChainstateActive().CoinsTip(), tx); + const Coin& coin = AccessByTxid(active_chainstate.CoinsTip(), tx); if (!coin.IsSpent()) { - pblockindex = ::ChainActive()[coin.nHeight]; + pblockindex = active_chainstate.m_chain[coin.nHeight]; break; } } @@ -339,7 +343,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) if (!tx || hashBlock.IsNull()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); } - pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); + pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); } @@ -398,10 +402,11 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) return res; + ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash()); - if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) { + const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash()); + if (!pindex || !chainman.ActiveChain().Contains(pindex) || pindex->nTx == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } @@ -652,10 +657,11 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { - const CTxMemPool& mempool = EnsureMemPool(request.context); - LOCK(cs_main); - LOCK(mempool.cs); - CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); + ChainstateManager& chainman = EnsureChainman(node); + LOCK2(cs_main, mempool.cs); + CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip(); CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view @@ -776,7 +782,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) for (const CTxIn& txin : mtx.vin) { coins[txin.prevout]; // Create empty map entry keyed by prevout. } - NodeContext& node = EnsureNodeContext(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); FindCoins(node, coins); // Parse the prevtxs array @@ -840,7 +846,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) if (!request.params[3].isNull()) bypass_limits = request.params[3].get_bool(); std::string err_string; AssertLockNotHeld(cs_main); - NodeContext& node = EnsureNodeContext(request.context); + NodeContext& node = EnsureAnyNodeContext(request.context); const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /* relay */ true, /* wait_callback */ true, bypass_limits); if (TransactionError::OK != err) { throw JSONRPCTransactionError(err, err_string); @@ -907,12 +913,12 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) DEFAULT_MAX_RAW_TX_FEE_RATE : CFeeRate(AmountFromValue(request.params[1])); + const NodeContext& node = EnsureAnyNodeContext(request.context); + + CTxMemPool& mempool = EnsureMemPool(node); int64_t virtual_size = GetVirtualTransactionSize(*tx); CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); - - CTxMemPool& mempool = EnsureMemPool(request.context); - UniValue result(UniValue::VARR); UniValue result_0(UniValue::VOBJ); result_0.pushKV("txid", tx_hash.GetHex()); @@ -920,8 +926,9 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) TxValidationState state; bool test_accept_res; { + ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); - test_accept_res = AcceptToMemoryPool(::ChainstateActive(), mempool, state, std::move(tx), + test_accept_res = AcceptToMemoryPool(chainman.ActiveChainstate(), mempool, state, std::move(tx), false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true); } result_0.pushKV("allowed", test_accept_res); @@ -1461,9 +1468,11 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request) CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { - const CTxMemPool& mempool = EnsureMemPool(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); + ChainstateManager& chainman = EnsureChainman(node); LOCK2(cs_main, mempool.cs); - CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); + CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip(); CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index d9a872a7bf60..8f3c778ae928 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -420,8 +420,16 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c for (size_t i=0; i vargNames = SplitString(argNamePattern, '|'); auto fr = argsIn.end(); @@ -443,6 +451,24 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c argsIn.erase(fr); } else { hole += 1; + if (out.params.empty()) initial_hole_size = hole; + } + } + // If leftover "args" param was found, use it as a source of positional + // arguments and add named arguments after. This is a convenience for + // clients that want to pass a combination of named and positional + // arguments as described in doc/JSON-RPC-interface.md#parameter-passing + auto positional_args{argsIn.extract("args")}; + if (positional_args && positional_args.mapped()->isArray()) { + const bool has_named_arguments{initial_hole_size < (int)argNames.size()}; + if (initial_hole_size < (int)positional_args.mapped()->size() && has_named_arguments) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + argNames[initial_hole_size] + " specified twice both as positional and named argument"); + } + // Assign positional_args to out.params and append named_args after. + UniValue named_args{std::move(out.params)}; + out.params = *positional_args.mapped(); + for (size_t i{out.params.size()}; i < named_args.size(); ++i) { + out.params.push_back(named_args[i]); } } // If there are still arguments in the argsIn map, this is an error. diff --git a/src/shutdown.cpp b/src/shutdown.cpp index e40353e258f8..f835833f388b 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -5,28 +5,136 @@ #include +#include +#include +#include + +#include + +#include #include +#ifdef WIN32 +#include +#else +#include +#include +#include +#endif + +bool AbortNode(const std::string& strMessage, bilingual_str user_message) +{ + SetMiscWarning(strMessage); + LogPrintf("*** %s\n", strMessage); + if (user_message.empty()) { + user_message = _("A fatal internal error occurred, see debug.log for details"); + } + AbortError(user_message); + StartShutdown(); + return false; +} static std::atomic fRequestShutdown(false); static std::atomic fRequestRestart(false); +#ifdef WIN32 +/** On windows it is possible to simply use a condition variable. */ +std::mutex g_shutdown_mutex; +std::condition_variable g_shutdown_cv; +#else +/** On UNIX-like operating systems use the self-pipe trick. + * Index 0 will be the read end of the pipe, index 1 the write end. + */ +static int g_shutdown_pipe[2] = {-1, -1}; +#endif + +bool InitShutdownState() +{ +#ifndef WIN32 +#if HAVE_O_CLOEXEC + // If we can, make sure that the file descriptors are closed on exec() + // to prevent interference. + if (pipe2(g_shutdown_pipe, O_CLOEXEC) != 0) { + return false; + } +#else + if (pipe(g_shutdown_pipe) != 0) { + return false; + } +#endif +#endif + return true; +} + void StartShutdown() { +#ifdef WIN32 + std::unique_lock lk(g_shutdown_mutex); fRequestShutdown = true; + g_shutdown_cv.notify_one(); +#else + // This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe. + // Make sure that the token is only written once even if multiple threads call this concurrently or in + // case of a reentrant signal. + if (!fRequestShutdown.exchange(true)) { + // Write an arbitrary byte to the write end of the shutdown pipe. + const char token = 'x'; + while (true) { + int result = write(g_shutdown_pipe[1], &token, 1); + if (result < 0) { + // Failure. It's possible that the write was interrupted by another signal. + // Other errors are unexpected here. + assert(errno == EINTR); + } else { + assert(result == 1); + break; + } + } + } +#endif } void StartRestart() { - fRequestShutdown = fRequestRestart = true; + fRequestRestart = true; + StartShutdown(); } + void AbortShutdown() { + if (fRequestShutdown) { + // Cancel existing shutdown by waiting for it, this will reset condition flags and remove + // the shutdown token from the pipe. + WaitForShutdown(); + } fRequestShutdown = false; } + bool ShutdownRequested() { return fRequestShutdown; } + bool RestartRequested() { return fRequestRestart; } + +void WaitForShutdown() +{ +#ifdef WIN32 + std::unique_lock lk(g_shutdown_mutex); + g_shutdown_cv.wait(lk, [] { return fRequestShutdown.load(); }); +#else + char token; + while (true) { + int result = read(g_shutdown_pipe[0], &token, 1); + if (result < 0) { + // Failure. Check if the read was interrupted by a signal. + // Other errors are unexpected here. + assert(errno == EINTR); + } else { + assert(result == 1); + break; + } + } +#endif +} diff --git a/src/shutdown.h b/src/shutdown.h index a5f1f1f4b550..3970484657e8 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -6,10 +6,34 @@ #ifndef BITCOIN_SHUTDOWN_H #define BITCOIN_SHUTDOWN_H +#include // For bilingual_str + +/** Abort with a message */ +bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str{}); + +/** Initialize shutdown state. This must be called before using either StartShutdown(), + * AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe. + */ +bool InitShutdownState(); + +/** Request shutdown of the application. */ void StartShutdown(); + +/** Request restart of the application. */ void StartRestart(); + +/** Clear shutdown flag. Only use this during init (before calling WaitForShutdown in any + * thread), or in the unit tests. Calling it in other circumstances will cause a race condition. + */ void AbortShutdown(); + +/** Returns true if a shutdown is requested, false otherwise. */ bool ShutdownRequested(); bool RestartRequested(); +/** Wait for StartShutdown to be called in any thread. This can only be used + * from a single thread. + */ +void WaitForShutdown(); + #endif diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 95bc95a33f7c..908ae7857a61 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -16,12 +16,50 @@ #include +static UniValue JSON(std::string_view json) +{ + UniValue value; + BOOST_CHECK(value.read(json.data(), json.size())); + return value; +} + +class HasJSON +{ +public: + explicit HasJSON(std::string json) : m_json(std::move(json)) {} + bool operator()(const UniValue& value) const + { + std::string json{value.write()}; + BOOST_CHECK_EQUAL(json, m_json); + return json == m_json; + }; + +private: + const std::string m_json; +}; + class RPCTestingSetup : public TestingSetup { public: + UniValue TransformParams(const UniValue& params, std::vector arg_names); UniValue CallRPC(std::string args); }; +UniValue RPCTestingSetup::TransformParams(const UniValue& params, std::vector arg_names) +{ + UniValue transformed_params; + CRPCTable table; + CRPCCommand command{"category", "method", [&](const JSONRPCRequest& request, UniValue&, bool) -> bool { transformed_params = request.params; return true; }, arg_names, /*unique_id=*/0}; + table.appendCommand("method", &command); + CoreContext context{m_node}; + JSONRPCRequest request(context); + request.strMethod = "method"; + request.params = params; + if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished(); + table.execute(request); + return transformed_params; +} + UniValue RPCTestingSetup::CallRPC(std::string args) { std::vector vArgs{SplitString(args, ' ')}; @@ -45,6 +83,29 @@ UniValue RPCTestingSetup::CallRPC(std::string args) BOOST_FIXTURE_TEST_SUITE(rpc_tests, RPCTestingSetup) +BOOST_AUTO_TEST_CASE(rpc_namedparams) +{ + const std::vector arg_names{{"arg1", "arg2", "arg3", "arg4", "arg5"}}; + + // Make sure named arguments are transformed into positional arguments in correct places separated by nulls + BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg2": 2, "arg4": 4})"), arg_names).write(), "[null,2,null,4]"); + + // Make sure named and positional arguments can be combined. + BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg5": 5, "args": [1, 2], "arg4": 4})"), arg_names).write(), "[1,2,null,4,5]"); + + // Make sure a unknown named argument raises an exception + BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"arg2": 2, "unknown": 6})"), arg_names), UniValue, + HasJSON(R"({"code":-8,"message":"Unknown named parameter unknown"})")); + + // Make sure an overlap between a named argument and positional argument raises an exception + BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"args": [1,2,3], "arg4": 4, "arg2": 2})"), arg_names), UniValue, + HasJSON(R"({"code":-8,"message":"Parameter arg2 specified twice both as positional and named argument"})")); + + // Make sure extra positional arguments can be passed through to the method implemenation, as long as they don't overlap with named arguments. + BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"args": [1,2,3,4,5,6,7,8,9,10]})"), arg_names).write(), "[1,2,3,4,5,6,7,8,9,10]"); + BOOST_CHECK_EQUAL(TransformParams(JSON(R"([1,2,3,4,5,6,7,8,9,10])"), arg_names).write(), "[1,2,3,4,5,6,7,8,9,10]"); +} + BOOST_AUTO_TEST_CASE(rpc_rawparams) { // Test raw transaction API argument handling diff --git a/src/test/util/blockfilter.cpp b/src/test/util/blockfilter.cpp index cbc95d8bdc54..d1f0cad7cc8e 100644 --- a/src/test/util/blockfilter.cpp +++ b/src/test/util/blockfilter.cpp @@ -5,6 +5,7 @@ #include #include +#include #include bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index, BlockFilter& filter) diff --git a/src/validation.cpp b/src/validation.cpp index 53edb09e03ed..add1602cc54e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -18,9 +18,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -1086,72 +1088,6 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe return nullptr; } -////////////////////////////////////////////////////////////////////////////// -// -// CBlock and CBlockIndex -// - -static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart) -{ - // Open history file to append - CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("WriteBlockToDisk: OpenBlockFile failed"); - - // Write index header - unsigned int nSize = GetSerializeSize(block, fileout.GetVersion()); - fileout << messageStart << nSize; - - // Write block - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("WriteBlockToDisk: ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << block; - - return true; -} - -bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams) -{ - block.SetNull(); - - // Open history file to read - CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); - - // Read block - try { - filein >> block; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); - } - - // Check the header - if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) - return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); - - return true; -} - -bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) -{ - FlatFilePos blockPos; - { - LOCK(cs_main); - blockPos = pindex->GetBlockPos(); - } - - if (!ReadBlockFromDisk(block, blockPos, consensusParams)) - return false; - if (block.GetHash() != pindex->GetBlockHash()) - return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", - pindex->ToString(), pindex->GetBlockPos().ToString()); - return true; -} - double ConvertBitsToDouble(unsigned int nBits) { int nShift = (nBits >> 24) & 0xff; @@ -1675,19 +1611,6 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex) return true; } -/** Abort with a message */ -static bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str()) -{ - SetMiscWarning(strMessage); - LogPrintf("*** %s\n", strMessage); - if (user_message.empty()) { - user_message = _("A fatal internal error occurred, see debug.log for details"); - } - AbortError(user_message); - StartShutdown(); - return false; -} - static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str()) { AbortNode(strMessage, userMessage); @@ -2615,14 +2538,21 @@ bool CChainState::FlushStateToDisk( CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(); LOCK(cs_LastBlockFile); if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { + // make sure we don't prune above the blockfilterindexes bestblocks + // pruning is height-based + int last_prune = m_chain.Height(); // last height we can prune + ForEachBlockFilterIndex([&](BlockFilterIndex& index) { + last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height)); + }); + if (nManualPruneHeight > 0) { LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCHMARK); - m_blockman.FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight, m_chain.Height()); + m_blockman.FindFilesToPruneManual(setFilesToPrune, std::min(last_prune, nManualPruneHeight), m_chain.Height()); } else { LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCHMARK); - m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), IsInitialBlockDownload()); + m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload()); fCheckForPruning = false; } if (!setFilesToPrune.empty()) { @@ -3751,7 +3681,8 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi } } -static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false) +// TODO move to blockstorage +bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false) { LOCK(cs_LastBlockFile); @@ -3762,7 +3693,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n bool finalize_undo = false; if (!fKnown) { - while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + while (vinfoBlockFile[nFile].nSize + nAddSize >= (gArgs.GetBoolArg("-fastprune", false) ? 0x10000 /* 64kb */ : MAX_BLOCKFILE_SIZE)) { // when the undo file is keeping up with the block file, we want to flush it explicitly // when it is lagging behind (more blocks arrive than are being connected), we let the // undo block write case handle it @@ -4216,25 +4147,6 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& return true; } -/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ -static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp) { - unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION); - FlatFilePos blockPos; - if (dbp != nullptr) - blockPos = *dbp; - if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) { - error("%s: FindBlockPos failed", __func__); - return FlatFilePos(); - } - if (dbp == nullptr) { - if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) { - AbortNode("Failed to write block"); - return FlatFilePos(); - } - } - return blockPos; -} - /** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ bool CChainState::AcceptBlock(const std::shared_ptr& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) { @@ -4512,7 +4424,7 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh } } -void BlockManager::FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd) +void BlockManager::FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd) { LOCK2(cs_main, cs_LastBlockFile); if (chain_tip_height < 0 || nPruneTarget == 0) { @@ -4522,7 +4434,7 @@ void BlockManager::FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPr return; } - unsigned int nLastBlockWeCanPrune = chain_tip_height - MIN_BLOCKS_TO_KEEP; + unsigned int nLastBlockWeCanPrune = std::min(prune_height, chain_tip_height - static_cast(MIN_BLOCKS_TO_KEEP)); uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files // So we should leave a buffer under our target to account for another allocation @@ -4573,7 +4485,7 @@ void BlockManager::FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPr static FlatFileSeq BlockFileSeq() { - return FlatFileSeq(GetBlocksDir(), "blk", BLOCKFILE_CHUNK_SIZE); + return FlatFileSeq(GetBlocksDir(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE); } static FlatFileSeq UndoFileSeq() diff --git a/src/validation.h b/src/validation.h index fb063fc811d3..6c80cb75189e 100644 --- a/src/validation.h +++ b/src/validation.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -312,13 +313,6 @@ bool GetAddressUnspent(uint160 addressHash, int type, /** Initializes the script-execution cache */ void InitScriptExecutionCache(); - -/** Functions for disk access for blocks */ -bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams); -bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); - -bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex); - /** Functions for validating blocks and updating the block tree */ /** Context-independent validity checks */ @@ -400,7 +394,7 @@ class BlockManager * * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned */ - void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd); + void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd); public: BlockMap m_block_index GUARDED_BY(cs_main); @@ -921,6 +915,7 @@ class ChainstateManager friend CChain& ChainActive(); public: + std::thread m_load_block; //! A single BlockManager instance is shared across each constructed //! chainstate to avoid duplicating block metadata. BlockManager m_blockman GUARDED_BY(::cs_main); diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp index 2724f8b17ba5..0f4444c53508 100644 --- a/src/zmq/zmqpublishnotifier.cpp +++ b/src/zmq/zmqpublishnotifier.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/test/functional/feature_blockfilterindex_prune.py b/test/functional/feature_blockfilterindex_prune.py new file mode 100755 index 000000000000..fab4b8677c7a --- /dev/null +++ b/test/functional/feature_blockfilterindex_prune.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test blockfilterindex in conjunction with prune.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_greater_than, + assert_raises_rpc_error, + wait_until +) + + +class FeatureBlockfilterindexPruneTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.extra_args = [["-fastprune", "-prune=1", "-blockfilterindex=1"]] + + def sync_index(self, height): + expected = {'basic block filter index': {'synced': True, 'best_block_height': height}} + wait_until(lambda: self.nodes[0].getindexinfo() == expected) + + def run_test(self): + self.log.info("check if we can access a blockfilter when pruning is enabled but no blocks are actually pruned") + self.sync_index(height=200) + assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0) + # Mine two batches of blocks to avoid hitting NODE_NETWORK_LIMITED_MIN_BLOCKS disconnection + self.nodes[0].generate(250) + self.sync_all() + self.nodes[0].generate(250) + self.sync_all() + self.sync_index(height=700) + + self.log.info("prune some blocks") + pruneheight = self.nodes[0].pruneblockchain(400) + assert_equal(pruneheight, 366) + + self.log.info("check if we can access the tips blockfilter when we have pruned some blocks") + assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0) + + self.log.info("check if we can access the blockfilter of a pruned block") + assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getblockhash(2))['filter']), 0) + + self.log.info("start node without blockfilterindex") + self.restart_node(0, extra_args=["-fastprune", "-prune=1"], expected_stderr='Warning: You are starting with governance validation disabled. This is expected because you are running a pruned node.') + + self.log.info("make sure accessing the blockfilters throws an error") + assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic", self.nodes[0].getblockfilter, self.nodes[0].getblockhash(2)) + self.nodes[0].generate(1000) + + self.log.info("prune below the blockfilterindexes best block while blockfilters are disabled") + pruneheight_new = self.nodes[0].pruneblockchain(1000) + assert_greater_than(pruneheight_new, pruneheight) + self.stop_node(0, expected_stderr='Warning: You are starting with governance validation disabled. This is expected because you are running a pruned node.') + + self.log.info("make sure we get an init error when starting the node again with block filters") + with self.nodes[0].assert_debug_log(["basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"]): + self.nodes[0].assert_start_raises_init_error(extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"]) + + self.log.info("make sure the node starts again with the -reindex arg") + self.start_node(0, extra_args = ["-fastprune", "-prune=1", "-blockfilterindex", "-reindex"]) + self.stop_nodes(expected_stderr='Warning: You are starting with governance validation disabled. This is expected because you are running a pruned node.') + + +if __name__ == '__main__': + FeatureBlockfilterindexPruneTest().main() diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index cf3bbaa9f9aa..79de5f030158 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -44,6 +44,11 @@ def run_test(self): rpc_response = self.nodes[0].getblockchaininfo() assert_equal(cli_response, rpc_response) + self.log.info("Test named arguments") + assert_equal(self.nodes[0].cli.echo(0, 1, arg3=3, arg5=5), ['0', '1', None, '3', None, '5']) + assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, 1, arg1=1) + assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, None, 2, arg1=1) + user, password = get_auth_cookie(self.nodes[0].datadir, self.chain) self.log.info("Test -stdinrpcpass option") diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index 9215022b8ecf..f40d0a921ae1 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -11,6 +11,7 @@ assert_equal, assert_greater_than, assert_greater_than_or_equal, + wait_until ) from test_framework.authproxy import JSONRPCException @@ -61,6 +62,35 @@ def run_test(self): node.logging(include=['qt']) assert_equal(node.logging()['qt'], True) + self.log.info("test getindexinfo") + self.restart_node(0, ["-txindex=0"]) + # Without any indices running the RPC returns an empty object + assert_equal(node.getindexinfo(), {}) + + # Restart the node with indices and wait for them to sync + self.restart_node(0, ["-txindex", "-blockfilterindex"]) + wait_until(lambda: all(i["synced"] for i in node.getindexinfo().values())) + + # Returns a list of all running indices by default + assert_equal( + node.getindexinfo(), + { + "txindex": {"synced": True, "best_block_height": 200}, + "basic block filter index": {"synced": True, "best_block_height": 200} + } + ) + + # Specifying an index by name returns only the status of that index + assert_equal( + node.getindexinfo("txindex"), + { + "txindex": {"synced": True, "best_block_height": 200}, + } + ) + + # Specifying an unknown index name returns an empty result + assert_equal(node.getindexinfo("foo"), {}) + if __name__ == '__main__': RpcMiscTest().main() diff --git a/test/functional/rpc_named_arguments.py b/test/functional/rpc_named_arguments.py index 13e571250c04..c14de0fbd794 100755 --- a/test/functional/rpc_named_arguments.py +++ b/test/functional/rpc_named_arguments.py @@ -30,6 +30,9 @@ def run_test(self): assert_equal(node.echo(arg1=1), [None, 1]) assert_equal(node.echo(arg9=None), [None]*10) assert_equal(node.echo(arg0=0,arg3=3,arg9=9), [0] + [None]*2 + [3] + [None]*5 + [9]) + assert_equal(node.echo(0, 1, arg3=3, arg5=5), [0, 1, None, 3, None, 5]) + assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", node.echo, 0, 1, arg1=1) + assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", node.echo, 0, None, 2, arg1=1) if __name__ == '__main__': NamedArgumentTest().main() diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index 3252780e1484..40df17e4e718 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -131,10 +131,12 @@ def get_request(self, *args, **argsn): json.dumps(args or argsn, default=EncodeDecimal, ensure_ascii=self.ensure_ascii), )) if args and argsn: - raise ValueError('Cannot handle both named and positional arguments') + params = dict(args=args, **argsn) + else: + params = args or argsn return {'version': '1.1', 'method': self._service_name, - 'params': args or argsn, + 'params': params, 'id': AuthServiceProxy.__id_count} def __call__(self, *args, **argsn): diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 589a9dec5223..2a6ef84d2c91 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -629,7 +629,6 @@ def send_cli(self, command=None, *args, **kwargs): """Run dash-cli command. Deserializes returned string as python object.""" pos_args = [arg_to_cli(arg) for arg in args] named_args = [str(key) + "=" + arg_to_cli(value) for (key, value) in kwargs.items()] - assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same dash-cli call" p_args = [self.binary, "-datadir=" + self.datadir] + self.options if named_args: p_args += ["-named"] diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index e0a8278d357e..db2a5abc90e6 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -274,6 +274,7 @@ 'rpc_getaddressinfo_label_deprecation.py', 'rpc_help.py', 'feature_help.py', + 'feature_blockfilterindex_prune.py' # Don't append tests at the end to avoid merge conflicts # Put them in a random line within the section that fits their approximate run-time ] diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index de882d3f98ae..88b2d1d2c222 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -11,6 +11,9 @@ export LC_ALL=C EXPECTED_CIRCULAR_DEPENDENCIES=( "chainparamsbase -> util/system -> chainparamsbase" "index/txindex -> validation -> index/txindex" + "node/blockstorage -> validation -> node/blockstorage" + "index/blockfilterindex -> node/blockstorage -> validation -> index/blockfilterindex" + "index/base -> validation -> index/blockfilterindex -> index/base" "policy/fees -> txmempool -> policy/fees" "qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel" "qt/bitcoingui -> qt/walletframe -> qt/bitcoingui" @@ -22,6 +25,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "node/coinstats -> validation -> node/coinstats" # Dash "coinjoin/server -> net_processing -> coinjoin/server" + "dsnotificationinterface -> llmq/chainlocks -> node/blockstorage -> dsnotificationinterface" "evo/cbtx -> evo/simplifiedmns -> evo/cbtx" "evo/deterministicmns -> llmq/commitment -> evo/deterministicmns" "evo/deterministicmns -> llmq/utils -> evo/deterministicmns"