diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 133bc282ef..6e13fd173b 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -35,6 +35,7 @@ bench_bench_dash_SOURCES = \ bench/ccoins_caching.cpp \ bench/gcs_filter.cpp \ bench/hashpadding.cpp \ + bench/load_external.cpp \ bench/merkle_root.cpp \ bench/mempool_eviction.cpp \ bench/mempool_stress.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 839224e9a1..a9b24c0007 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -89,6 +89,7 @@ BITCOIN_TESTS =\ test/blockencodings_tests.cpp \ test/blockfilter_tests.cpp \ test/blockfilter_index_tests.cpp \ + test/blockmanager_tests.cpp \ test/bloom_tests.cpp \ test/bls_tests.cpp \ test/bswap_tests.cpp \ diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 56afa0132f..2d2c7c97ac 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -32,7 +32,7 @@ static void AssembleBlock(benchmark::Bench& bench) std::array txs; for (size_t b{0}; b < NUM_BLOCKS; ++b) { CMutableTransaction tx; - tx.vin.push_back(MineBlock(test_setup->m_node, P2SH_OP_TRUE)); + tx.vin.push_back(MineBlock(test_setup->m_node, SCRIPT_PUB)); tx.vin.back().scriptSig = scriptSig; tx.vout.emplace_back(1337, SCRIPT_PUB); if (NUM_BLOCKS - b >= COINBASE_MATURITY) @@ -48,7 +48,7 @@ static void AssembleBlock(benchmark::Bench& bench) } bench.minEpochIterations(700).run([&] { - PrepareBlock(test_setup->m_node, P2SH_OP_TRUE); + PrepareBlock(test_setup->m_node, SCRIPT_PUB); }); } diff --git a/src/bench/gcs_filter.cpp b/src/bench/gcs_filter.cpp index 3dd53bab1b..7f08cbec68 100644 --- a/src/bench/gcs_filter.cpp +++ b/src/bench/gcs_filter.cpp @@ -5,38 +5,84 @@ #include #include -static void ConstructGCSFilter(benchmark::Bench& bench) +static const GCSFilter::ElementSet GenerateGCSTestElements() { GCSFilter::ElementSet elements; - for (int i = 0; i < 10000; ++i) { + + // Testing the benchmarks with different number of elements show that a filter + // with at least 100,000 elements results in benchmarks that have the same + // ns/op. This makes it easy to reason about how long (in nanoseconds) a single + // filter element takes to process. + for (int i = 0; i < 100000; ++i) { GCSFilter::Element element(32); element[0] = static_cast(i); element[1] = static_cast(i >> 8); elements.insert(std::move(element)); } + return elements; +} + +static void GCSBlockFilterGetHash(benchmark::Bench& bench) +{ + auto elements = GenerateGCSTestElements(); + + GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements); + BlockFilter block_filter(BlockFilterType::BASIC_FILTER, {}, filter.GetEncoded(), /*skip_decode_check=*/false); + + bench.run([&] { + block_filter.GetHash(); + }); +} + +static void GCSFilterConstruct(benchmark::Bench& bench) +{ + auto elements = GenerateGCSTestElements(); + uint64_t siphash_k0 = 0; - bench.batch(elements.size()).unit("elem").run([&] { - GCSFilter filter({siphash_k0, 0, 20, 1 << 20}, elements); + bench.run([&]{ + GCSFilter filter({siphash_k0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements); + siphash_k0++; }); } -static void MatchGCSFilter(benchmark::Bench& bench) +static void GCSFilterDecode(benchmark::Bench& bench) { - GCSFilter::ElementSet elements; - for (int i = 0; i < 10000; ++i) { - GCSFilter::Element element(32); - element[0] = static_cast(i); - element[1] = static_cast(i >> 8); - elements.insert(std::move(element)); - } - GCSFilter filter({0, 0, 20, 1 << 20}, elements); + auto elements = GenerateGCSTestElements(); - bench.unit("elem").run([&] { - filter.Match(GCSFilter::Element()); + GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements); + auto encoded = filter.GetEncoded(); + + bench.run([&] { + GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, encoded, /*skip_decode_check=*/false); }); } -BENCHMARK(ConstructGCSFilter); -BENCHMARK(MatchGCSFilter); +static void GCSFilterDecodeSkipCheck(benchmark::Bench& bench) +{ + auto elements = GenerateGCSTestElements(); + + GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements); + auto encoded = filter.GetEncoded(); + + bench.run([&] { + GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, encoded, /*skip_decode_check=*/true); + }); +} + +static void GCSFilterMatch(benchmark::Bench& bench) +{ + auto elements = GenerateGCSTestElements(); + + GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements); + + bench.run([&] { + filter.Match(GCSFilter::Element()); + }); +} +BENCHMARK(GCSBlockFilterGetHash); +BENCHMARK(GCSFilterConstruct); +BENCHMARK(GCSFilterDecode); +BENCHMARK(GCSFilterDecodeSkipCheck); +BENCHMARK(GCSFilterMatch); diff --git a/src/bench/load_external.cpp b/src/bench/load_external.cpp new file mode 100644 index 0000000000..6bc359a408 --- /dev/null +++ b/src/bench/load_external.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include + +/** + * The LoadExternalBlockFile() function is used during -reindex and -loadblock. + * + * Create a test file that's similar to a datadir/blocks/blk?????.dat file, + * It contains around 134 copies of the same block (typical size of real block files). + * For each block in the file, LoadExternalBlockFile() won't find its parent, + * and so will skip the block. (In the real system, it will re-read the block + * from disk later when it encounters its parent.) + * + * This benchmark measures the performance of deserializing the block (or just + * its header, beginning with PR 16981). + */ +static void LoadExternalBlockFile(benchmark::Bench& bench) +{ + const auto testing_setup{MakeNoLogFileContext(CBaseChainParams::MAIN)}; + + // Create a single block as in the blocks files (magic bytes, block size, + // block data) as a stream object. + const fs::path blkfile{testing_setup.get()->m_path_root / "blk.dat"}; + CDataStream ss(SER_DISK, 0); + auto params{Params()}; + ss << params.MessageStart(); + ss << static_cast(benchmark::data::block813851.size()); + // We can't use the streaming serialization (ss << benchmark::data::block813851) + // because that first writes a compact size. + ss.write(MakeByteSpan(benchmark::data::block813851)); + + // Create the test file. + { + // "wb+" is "binary, O_RDWR | O_CREAT | O_TRUNC". + FILE* file{fsbridge::fopen(blkfile, "wb+")}; + // Make the test block file about 128 MB in length. + for (size_t i = 0; i < MAX_BLOCKFILE_SIZE / ss.size(); ++i) { + if (fwrite(ss.data(), 1, ss.size(), file) != ss.size()) { + throw std::runtime_error("write to test file failed\n"); + } + } + fclose(file); + } + + CChainState& chainstate{testing_setup->m_node.chainman->ActiveChainstate()}; + std::multimap blocks_with_unknown_parent; + FlatFilePos pos; + bench.run([&] { + // "rb" is "binary, O_RDONLY", positioned to the start of the file. + // The file will be closed by LoadExternalBlockFile(). + FILE* file{fsbridge::fopen(blkfile, "rb")}; + chainstate.LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent); + }); + fs::remove(blkfile); +} + +BENCHMARK(LoadExternalBlockFile); diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index 25ca14c002..e7cc29afda 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -47,7 +47,7 @@ GCSFilter::GCSFilter(const Params& params) : m_params(params), m_N(0), m_F(0), m_encoded{0} {} -GCSFilter::GCSFilter(const Params& params, std::vector encoded_filter) +GCSFilter::GCSFilter(const Params& params, std::vector encoded_filter, bool skip_decode_check) : m_params(params), m_encoded(std::move(encoded_filter)) { SpanReader stream{GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0}; @@ -59,6 +59,8 @@ GCSFilter::GCSFilter(const Params& params, std::vector encoded_fi } m_F = static_cast(m_N) * static_cast(m_params.m_M); + if (skip_decode_check) return; + // Verify that the encoded filter contains exactly N elements. If it has too much or too little // data, a std::ios_base::failure exception will be raised. BitStreamReader bitreader{stream}; @@ -219,14 +221,14 @@ static GCSFilter::ElementSet BasicFilterElements(const CBlock& block, } BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash, - std::vector filter) + std::vector filter, bool skip_decode_check) : m_filter_type(filter_type), m_block_hash(block_hash) { GCSFilter::Params params; if (!BuildParams(params)) { throw std::invalid_argument("unknown filter_type"); } - m_filter = GCSFilter(params, std::move(filter)); + m_filter = GCSFilter(params, std::move(filter), skip_decode_check); } BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo) diff --git a/src/blockfilter.h b/src/blockfilter.h index 8bdd789dd5..081e35ed9f 100644 --- a/src/blockfilter.h +++ b/src/blockfilter.h @@ -60,7 +60,7 @@ public: explicit GCSFilter(const Params& params = Params()); /** Reconstructs an already-created filter from an encoding. */ - GCSFilter(const Params& params, std::vector encoded_filter); + GCSFilter(const Params& params, std::vector encoded_filter, bool skip_decode_check); /** Builds a new filter from the params and set of elements. */ GCSFilter(const Params& params, const ElementSet& elements); @@ -123,7 +123,7 @@ public: //! Reconstruct a BlockFilter from parts. BlockFilter(BlockFilterType filter_type, const uint256& block_hash, - std::vector filter); + std::vector filter, bool skip_decode_check); //! Construct a new BlockFilter of the specified type from a block. BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo); @@ -165,7 +165,7 @@ public: if (!BuildParams(params)) { throw std::ios_base::failure("unknown filter_type"); } - m_filter = GCSFilter(params, std::move(encoded_filter)); + m_filter = GCSFilter(params, std::move(encoded_filter), /*skip_decode_check=*/false); } }; diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 49912b8880..0b969d1f25 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -1240,6 +1240,10 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() auto dbTx = m_evoDb.BeginTransaction(); m_evoDb.WriteBestBlock(m_chainstate.m_chain.Tip()->GetBlockHash()); dbTx->Commit(); + if (!m_evoDb.CommitRootTransaction()) { + LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__); + return false; + } return true; } @@ -1351,6 +1355,10 @@ bool CDeterministicMNManager::MigrateDBIfNeeded2() auto dbTx = m_evoDb.BeginTransaction(); m_evoDb.WriteBestBlock(m_chainstate.m_chain.Tip()->GetBlockHash()); dbTx->Commit(); + if (!m_evoDb.CommitRootTransaction()) { + LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__); + return false; + } return true; } diff --git a/src/index/base.cpp b/src/index/base.cpp index e62da821f1..e132e27ee5 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -62,9 +62,9 @@ bool BaseIndex::Init() LOCK(cs_main); CChain& active_chain = m_chainstate->m_chain; if (locator.IsNull()) { - m_best_block_index = nullptr; + SetBestBlockIndex(nullptr); } else { - m_best_block_index = m_chainstate->FindForkInGlobalIndex(locator); + SetBestBlockIndex(m_chainstate->FindForkInGlobalIndex(locator)); } // Note: this will latch to true immediately if the user starts up with an empty @@ -76,11 +76,7 @@ bool BaseIndex::Init() if (!m_best_block_index) { // index is not built yet // make sure we have all block data back to the genesis - const CBlockIndex* block = active_chain.Tip(); - while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { - block = block->pprev; - } - prune_violation = block != active_chain.Genesis(); + prune_violation = GetFirstStoredBlock(active_chain.Tip()) != active_chain.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 @@ -138,7 +134,7 @@ void BaseIndex::ThreadSync() std::chrono::steady_clock::time_point last_locator_write_time{0s}; while (true) { if (m_interrupt) { - m_best_block_index = pindex; + SetBestBlockIndex(pindex); // No need to handle errors in Commit. If it fails, the error will be already be // logged. The best way to recover is to continue, as index cannot be corrupted by // a missed commit to disk for an advanced index state. @@ -150,7 +146,7 @@ void BaseIndex::ThreadSync() LOCK(cs_main); const CBlockIndex* pindex_next = NextSyncBlock(pindex, m_chainstate->m_chain); if (!pindex_next) { - m_best_block_index = pindex; + SetBestBlockIndex(pindex); m_synced = true; // No need to handle errors in Commit. See rationale above. Commit(); @@ -172,7 +168,7 @@ void BaseIndex::ThreadSync() } if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) { - m_best_block_index = pindex; + SetBestBlockIndex(pindex->pprev); last_locator_write_time = current_time; // No need to handle errors in Commit. See rationale above. Commit(); @@ -230,10 +226,10 @@ bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_ti // 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; + SetBestBlockIndex(new_tip); if (!Commit()) { // If commit fails, revert the best block index to avoid corruption. - m_best_block_index = current_tip; + SetBestBlockIndex(current_tip); return false; } @@ -274,7 +270,11 @@ void BaseIndex::BlockConnected(const std::shared_ptr& block, const } if (WriteBlock(*block, pindex)) { - m_best_block_index = pindex; + // Setting the best block index is intentionally the last step of this + // function, so BlockUntilSyncedToCurrentChain callers waiting for the + // best block index to be updated can rely on the block being fully + // processed, and the index object being safe to delete. + SetBestBlockIndex(pindex); } else { FatalError("%s: Failed to write block %s to index", __func__, pindex->GetBlockHash().ToString()); @@ -381,3 +381,21 @@ IndexSummary BaseIndex::GetSummary() const summary.best_block_height = m_best_block_index ? m_best_block_index.load()->nHeight : 0; return summary; } + +void BaseIndex::SetBestBlockIndex(const CBlockIndex* block) { + assert(!fPruneMode || AllowPrune()); + + if (AllowPrune() && block) { + PruneLockInfo prune_lock; + prune_lock.height_first = block->nHeight; + WITH_LOCK(::cs_main, m_chainstate->m_blockman.UpdatePruneLock(GetName(), prune_lock)); + } + + // Intentionally set m_best_block_index as the last step in this function, + // after updating prune locks above, and after making any other references + // to *this, so the BlockUntilSyncedToCurrentChain function (which checks + // m_best_block_index as an optimization) can be used to wait for the last + // BlockConnected notification and safely assume that prune locks are + // updated and that the index object is safe to delete. + m_best_block_index = block; +} diff --git a/src/index/base.h b/src/index/base.h index e1f2a9b82d..a4e0dd77eb 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -81,6 +81,9 @@ private: /// to a chain reorganization), the index must halt until Commit succeeds or else it could end up /// getting corrupted. bool Commit(); + + virtual bool AllowPrune() const = 0; + protected: CChainState* m_chainstate{nullptr}; @@ -109,6 +112,9 @@ protected: /// Get the name of the index for display in logs. virtual const char* GetName() const = 0; + /// Update the internal best block index as well as the prune lock. + void SetBestBlockIndex(const CBlockIndex* block); + public: /// Destructor interrupts sync thread if running and blocks until it exits. virtual ~BaseIndex(); diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp index 75a190fdb8..2e84934eba 100644 --- a/src/index/blockfilterindex.cpp +++ b/src/index/blockfilterindex.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -146,18 +147,22 @@ bool BlockFilterIndex::CommitInternal(CDBBatch& batch) return BaseIndex::CommitInternal(batch); } -bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const +bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, const uint256& hash, BlockFilter& filter) const { CAutoFile filein(m_filter_fileseq->Open(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { return false; } + // Check that the hash of the encoded_filter matches the one stored in the db. uint256 block_hash; std::vector encoded_filter; try { filein >> block_hash >> encoded_filter; - filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter)); + uint256 result; + CHash256().Write(encoded_filter).Finalize(result); + if (result != hash) return error("Checksum mismatch in filter decode."); + filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter), /*skip_decode_check=*/true); } catch (const std::exception& e) { return error("%s: Failed to deserialize block filter from disk: %s", __func__, e.what()); @@ -384,7 +389,7 @@ bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter& return false; } - return ReadFilterFromDisk(entry.pos, filter_out); + return ReadFilterFromDisk(entry.pos, entry.hash, filter_out); } bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) @@ -428,7 +433,7 @@ bool BlockFilterIndex::LookupFilterRange(int start_height, const CBlockIndex* st filters_out.resize(entries.size()); auto filter_pos_it = filters_out.begin(); for (const auto& entry : entries) { - if (!ReadFilterFromDisk(entry.pos, *filter_pos_it)) { + if (!ReadFilterFromDisk(entry.pos, entry.hash, *filter_pos_it)) { return false; } ++filter_pos_it; diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h index 760a712349..38754ead04 100644 --- a/src/index/blockfilterindex.h +++ b/src/index/blockfilterindex.h @@ -32,13 +32,15 @@ private: FlatFilePos m_next_filter_pos; std::unique_ptr m_filter_fileseq; - bool ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const; + bool ReadFilterFromDisk(const FlatFilePos& pos, const uint256& hash, BlockFilter& filter) const; size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter); Mutex m_cs_headers_cache; /** cache of block hash to filter header, to avoid disk access when responding to getcfcheckpt. */ std::unordered_map m_headers_cache GUARDED_BY(m_cs_headers_cache); + bool AllowPrune() const override { return true; } + protected: bool Init() override; diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h index ed0c8673f8..8cfb87ee8b 100644 --- a/src/index/coinstatsindex.h +++ b/src/index/coinstatsindex.h @@ -36,6 +36,8 @@ private: bool ReverseBlock(const CBlock& block, const CBlockIndex* pindex); + bool AllowPrune() const override { return true; } + protected: bool Init() override; diff --git a/src/index/txindex.h b/src/index/txindex.h index 23a1aef1e4..3fe3628f0a 100644 --- a/src/index/txindex.h +++ b/src/index/txindex.h @@ -20,6 +20,8 @@ protected: private: const std::unique_ptr m_db; + bool AllowPrune() const override { return false; } + protected: bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; diff --git a/src/init.cpp b/src/init.cpp index 564a74f18c..e92742be1d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -538,7 +538,7 @@ void SetupServerArgs(ArgsManager& argsman) -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-pid=", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-prune=", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -coinstatsindex, -rescan and -disablegovernance=false. " + argsman.AddArg("-prune=", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -rescan and -disablegovernance=false. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-settings=", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -1162,12 +1162,12 @@ bool AppInitParameterInteraction(const ArgsManager& args) nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS); } - // if using block pruning, then disallow txindex, coinstatsindex and require disabling governance validation if (args.GetArg("-prune", 0)) { if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) return InitError(_("Prune mode is incompatible with -txindex.")); - if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) - return InitError(_("Prune mode is incompatible with -coinstatsindex.")); + if (args.GetBoolArg("-reindex-chainstate", false)) { + return InitError(_("Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead.")); + } if (!args.GetBoolArg("-disablegovernance", !DEFAULT_GOVERNANCE_ENABLE)) { return InitError(_("Prune mode is incompatible with -disablegovernance=false.")); } diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index d8e59b88ab..2c8cacccef 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -24,6 +24,7 @@ #include #include +#include std::atomic_bool fImporting(false); std::atomic_bool fReindex(false); @@ -249,6 +250,11 @@ void BlockManager::FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPr nLastBlockWeCanPrune, count); } +void BlockManager::UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) { + AssertLockHeld(::cs_main); + m_prune_locks[name] = lock_info; +} + CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash) { AssertLockHeld(cs_main); @@ -421,6 +427,16 @@ bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex) return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0); } +const CBlockIndex* GetFirstStoredBlock(const CBlockIndex* start_block) { + AssertLockHeld(::cs_main); + assert(start_block); + const CBlockIndex* last_block = start_block; + while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) { + last_block = last_block->pprev; + } + return last_block; +} + // 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 @@ -774,19 +790,24 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus return true; } -/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ FlatFilePos BlockManager::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) { + const auto position_known {dbp != nullptr}; + if (position_known) { blockPos = *dbp; + } else { + // when known, blockPos.nPos points at the offset of the block data in the blk file. that already accounts for + // the serialization header present in the file (the 4 magic message start bytes + the 4 length bytes = 8 bytes = BLOCK_SERIALIZATION_HEADER_SIZE). + // we add BLOCK_SERIALIZATION_HEADER_SIZE only for new blocks since they will have the serialization header added when written to disk. + nBlockSize += static_cast(BLOCK_SERIALIZATION_HEADER_SIZE); } - if (!FindBlockPos(blockPos, nBlockSize + 8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) { + if (!FindBlockPos(blockPos, nBlockSize, nHeight, active_chain, block.GetBlockTime(), position_known)) { error("%s: FindBlockPos failed", __func__); return FlatFilePos(); } - if (dbp == nullptr) { + if (!position_known) { if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) { AbortNode("Failed to write block"); return FlatFilePos(); diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index ae7319d5ea..1b919df908 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -12,6 +12,7 @@ #include #include +#include #include extern RecursiveMutex cs_main; @@ -46,6 +47,9 @@ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB /** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** Size of header written by WriteBlockToDisk before a serialized CBlock */ +static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = CMessageHeader::MESSAGE_START_SIZE + sizeof(unsigned int); + extern std::atomic_bool fImporting; extern std::atomic_bool fReindex; /** Pruning-related variables and constants */ @@ -77,6 +81,10 @@ struct CBlockIndexHeightOnlyComparator { bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const; }; +struct PruneLockInfo { + int height_first{std::numeric_limits::max()}; //! Height of earliest block that should be kept and not pruned +}; + /** * Maintains a tree of blocks (stored in `m_block_index`) which is consulted * to determine where the most-work tip is. @@ -137,6 +145,14 @@ private: /** Dirty block file entries. */ std::set m_dirty_fileinfo; + /** + * Map from external index name to oldest block that must not be pruned. + * + * @note Internally, only blocks at height (height_first - PRUNE_LOCK_BUFFER - 1) and + * below will be pruned, but callers should avoid assuming any particular buffer size. + */ + std::unordered_map m_prune_locks GUARDED_BY(::cs_main); + public: BlockMap m_block_index GUARDED_BY(cs_main); PrevBlockMap m_prev_block_index GUARDED_BY(cs_main); @@ -172,6 +188,7 @@ public: bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + /** Store block on disk. If dbp is not nullptr, then it provides the known position of the block within a block file on disk. */ FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp); /** Calculate the amount of disk space the block & undo files currently use */ @@ -185,8 +202,14 @@ public: //! Check whether the block associated with this index entry is pruned or not. bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + + //! Create or update a prune lock identified by its name + void UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); }; +//! Find the first block that is not pruned +const CBlockIndex* GetFirstStoredBlock(const CBlockIndex* start_block) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + void CleanupBlockRevFiles(); /** Open a block file (blk?????.dat) */ diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a47558255e..411d6cb75d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1359,12 +1360,10 @@ static RPCHelpMan pruneblockchain() } 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; - } - return uint64_t(block->nHeight); + const CBlockIndex* block = CHECK_NONFATAL(active_chain.Tip()); + const CBlockIndex* last_block = GetFirstStoredBlock(block); + + return static_cast(last_block->nHeight); }, }; } @@ -1635,14 +1634,13 @@ static RPCHelpMan verifychain() const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].get_int()}; const NodeContext& node = EnsureAnyNodeContext(request.context); - CHECK_NONFATAL(node.evodb); ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); CChainState& active_chainstate = chainman.ActiveChainstate(); return CVerifyDB().VerifyDB( - active_chainstate, Params(), active_chainstate.CoinsTip(), *node.evodb, check_level, check_depth); + active_chainstate, Params(), active_chainstate.CoinsTip(), *CHECK_NONFATAL(node.evodb), check_level, check_depth); }, }; } @@ -1782,13 +1780,11 @@ RPCHelpMan getblockchaininfo() LOCK(cs_main); CChainState& active_chainstate = chainman.ActiveChainstate(); - const CBlockIndex* tip = active_chainstate.m_chain.Tip(); - CHECK_NONFATAL(tip); + const CBlockIndex* tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip()); const int height = tip->nHeight; - CHECK_NONFATAL(node.mnhf_manager); - const auto ehfSignals = node.mnhf_manager->GetSignalsStage(tip); + const auto ehfSignals = CHECK_NONFATAL(node.mnhf_manager)->GetSignalsStage(tip); UniValue obj(UniValue::VOBJ); if (args.IsArgSet("-devnet")) { @@ -1808,13 +1804,8 @@ RPCHelpMan getblockchaininfo() obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage()); obj.pushKV("pruned", fPruneMode); if (fPruneMode) { - const CBlockIndex* block = tip; - CHECK_NONFATAL(block); - while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { - block = block->pprev; - } - - obj.pushKV("pruneheight", block->nHeight); + const CBlockIndex* block = CHECK_NONFATAL(tip); + obj.pushKV("pruneheight", GetFirstStoredBlock(block)->nHeight); // if 0, execution bypasses the whole if block. bool automatic_pruning{args.GetArg("-prune", 0) != 1}; @@ -2864,10 +2855,8 @@ static RPCHelpMan scantxoutset() LOCK(cs_main); CChainState& active_chainstate = chainman.ActiveChainstate(); active_chainstate.ForceFlushStateToDisk(); - pcursor = active_chainstate.CoinsDB().Cursor(); - CHECK_NONFATAL(pcursor); - tip = active_chainstate.m_chain.Tip(); - CHECK_NONFATAL(tip); + pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor()); + tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip()); } bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point); result.pushKV("success", res); @@ -3062,8 +3051,7 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil } pcursor = chainstate.CoinsDB().Cursor(); - tip = chainstate.m_blockman.LookupBlockIndex(stats.hashBlock); - CHECK_NONFATAL(tip); + tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock)); } SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx}; diff --git a/src/rpc/coinjoin.cpp b/src/rpc/coinjoin.cpp index 0ce1bffff9..8e477ce8ce 100644 --- a/src/rpc/coinjoin.cpp +++ b/src/rpc/coinjoin.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -83,11 +84,8 @@ static RPCHelpMan coinjoin_reset() ValidateCoinJoinArguments(); - CHECK_NONFATAL(node.coinjoin_loader); - auto cj_clientman = node.coinjoin_loader->walletman().Get(wallet->GetName()); - - CHECK_NONFATAL(cj_clientman); - cj_clientman->ResetPool(); + auto cj_clientman = CHECK_NONFATAL(node.coinjoin_loader)->walletman().Get(wallet->GetName()); + CHECK_NONFATAL(cj_clientman)->ResetPool(); return "Mixing was reset"; }, @@ -126,10 +124,7 @@ static RPCHelpMan coinjoin_start() throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please unlock wallet for mixing with walletpassphrase first."); } - CHECK_NONFATAL(node.coinjoin_loader); - auto cj_clientman = node.coinjoin_loader->walletman().Get(wallet->GetName()); - - CHECK_NONFATAL(cj_clientman); + auto cj_clientman = CHECK_NONFATAL(CHECK_NONFATAL(node.coinjoin_loader)->walletman().Get(wallet->GetName())); if (!cj_clientman->StartMixing()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already."); } @@ -450,8 +445,7 @@ static RPCHelpMan getcoinjoininfo() return obj; } - auto manager = node.coinjoin_loader->walletman().Get(wallet->GetName()); - CHECK_NONFATAL(manager != nullptr); + auto* manager = CHECK_NONFATAL(node.coinjoin_loader->walletman().Get(wallet->GetName())); manager->GetJsonInfo(obj); std::string warning_msg{""}; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 5fa7506cda..f6e96b58b5 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -629,8 +630,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.chain_helper); - CChainstateHelper& chain_helper = *node.chain_helper; + CChainstateHelper& chain_helper = *CHECK_NONFATAL(node.chain_helper); const bool isEvoRequested = mnType == MnType::Evo; @@ -856,8 +856,7 @@ static RPCHelpMan protx_register_submit() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.chain_helper); - CChainstateHelper& chain_helper = *node.chain_helper; + CChainstateHelper& chain_helper = *CHECK_NONFATAL(node.chain_helper); std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -955,11 +954,8 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; - - CHECK_NONFATAL(node.chain_helper); - CChainstateHelper& chain_helper = *node.chain_helper; + CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); + CChainstateHelper& chain_helper = *CHECK_NONFATAL(node.chain_helper); const bool isEvoRequested = mnType == MnType::Evo; std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); @@ -1093,11 +1089,8 @@ static RPCHelpMan protx_update_registrar_wrapper(bool specific_legacy_bls_scheme const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; - - CHECK_NONFATAL(node.chain_helper); - CChainstateHelper& chain_helper = *node.chain_helper; + CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); + CChainstateHelper& chain_helper = *CHECK_NONFATAL(node.chain_helper); std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -1208,12 +1201,8 @@ static RPCHelpMan protx_revoke() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; - - CHECK_NONFATAL(node.chain_helper); - CChainstateHelper& chain_helper = *node.chain_helper; - + CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); + CChainstateHelper& chain_helper = *CHECK_NONFATAL(node.chain_helper); std::shared_ptr const pwallet = GetWalletForJSONRPCRequest(request); if (!pwallet) return NullUniValue; @@ -1371,11 +1360,8 @@ static RPCHelpMan protx_list() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; - - CHECK_NONFATAL(node.mn_metaman); - CMasternodeMetaMan& mn_metaman = *node.mn_metaman; + CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); + CMasternodeMetaMan& mn_metaman = *CHECK_NONFATAL(node.mn_metaman); std::shared_ptr wallet{nullptr}; #ifdef ENABLE_WALLET @@ -1487,11 +1473,8 @@ static RPCHelpMan protx_info() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; - - CHECK_NONFATAL(node.mn_metaman); - CMasternodeMetaMan& mn_metaman = *node.mn_metaman; + CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); + CMasternodeMetaMan& mn_metaman = *CHECK_NONFATAL(node.mn_metaman); std::shared_ptr wallet{nullptr}; #ifdef ENABLE_WALLET @@ -1561,11 +1544,8 @@ static RPCHelpMan protx_diff() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; - - CHECK_NONFATAL(node.llmq_ctx); - const LLMQContext& llmq_ctx = *node.llmq_ctx; + CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); + const LLMQContext& llmq_ctx = *CHECK_NONFATAL(node.llmq_ctx); LOCK(cs_main); uint256 baseBlockHash = ParseBlock(request.params[0], chainman, "baseBlock"); @@ -1622,8 +1602,7 @@ static RPCHelpMan protx_listdiff() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; + CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); LOCK(cs_main); UniValue ret(UniValue::VOBJ); diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 2137e91a7b..5123945c3f 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -194,12 +195,11 @@ static RPCHelpMan gobject_prepare() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); { LOCK(cs_main); std::string strError = ""; - if (!govobj.IsValidLocally(node.dmnman->GetListAtChainTip(), chainman, strError, false)) + if (!govobj.IsValidLocally(CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(), chainman, strError, false)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Governance object is not valid - " + govobj.GetHash().ToString() + " - " + strError); } @@ -303,14 +303,12 @@ static RPCHelpMan gobject_submit() { const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CHECK_NONFATAL(node.govman); if(!node.mn_sync->IsBlockchainSynced()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Must wait for client to sync with masternode network. Try again in a minute or so."); } - auto mnList = node.dmnman->GetListAtChainTip(); + auto mnList = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); if (node.mn_activeman) { const bool fMnFound = mnList.HasValidMNByCollateral(node.mn_activeman->GetOutPoint()); @@ -373,7 +371,6 @@ static RPCHelpMan gobject_submit() } LOCK2(cs_main, mempool.cs); - CHECK_NONFATAL(node.dmnman); std::string strError; if (!govobj.IsValidLocally(node.dmnman->GetListAtChainTip(), chainman, strError, fMissingConfirmations, true) && !fMissingConfirmations) { @@ -384,7 +381,7 @@ static RPCHelpMan gobject_submit() // RELAY THIS OBJECT // Reject if rate check fails but don't update buffer - if (!node.govman->MasternodeRateCheck(govobj)) { + if (!CHECK_NONFATAL(node.govman)->MasternodeRateCheck(govobj)) { LogPrintf("gobject(submit) -- Object submission rejected because of rate check failure - hash = %s\n", strHash); throw JSONRPCError(RPC_INVALID_PARAMETER, "Object creation rate limit exceeded"); } @@ -393,9 +390,8 @@ static RPCHelpMan gobject_submit() PeerManager& peerman = EnsurePeerman(node); if (fMissingConfirmations) { - CHECK_NONFATAL(node.mn_sync); node.govman->AddPostponedObject(govobj); - govobj.Relay(peerman, *node.mn_sync); + govobj.Relay(peerman, *CHECK_NONFATAL(node.mn_sync)); } else { node.govman->AddGovernanceObject(govobj, peerman); } @@ -452,7 +448,7 @@ static UniValue VoteWithMasternodes(const JSONRPCRequest& request, const CWallet int nSuccessful = 0; int nFailed = 0; - auto mnList = node.dmnman->GetListAtChainTip(); + auto mnList = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); UniValue resultsObj(UniValue::VOBJ); @@ -529,7 +525,6 @@ static RPCHelpMan gobject_vote_many() if (!wallet) return NullUniValue; const NodeContext& node = EnsureAnyNodeContext(request.context); - CHECK_NONFATAL(node.dmnman); uint256 hash(ParseHashV(request.params[0], "Object hash")); std::string strVoteSignal = request.params[1].get_str(); @@ -551,7 +546,7 @@ static RPCHelpMan gobject_vote_many() std::map votingKeys; - auto mnList = node.dmnman->GetListAtChainTip(); + auto mnList = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); mnList.ForEachMN(true, [&](auto& dmn) { const bool is_mine = CheckWalletOwnsKey(*wallet, dmn.pdmnState->keyIDVoting); if (is_mine) { @@ -583,7 +578,6 @@ static RPCHelpMan gobject_vote_alias() if (!wallet) return NullUniValue; const NodeContext& node = EnsureAnyNodeContext(request.context); - CHECK_NONFATAL(node.dmnman); uint256 hash(ParseHashV(request.params[0], "Object hash")); std::string strVoteSignal = request.params[1].get_str(); @@ -604,7 +598,7 @@ static RPCHelpMan gobject_vote_alias() EnsureWalletIsUnlocked(*wallet); uint256 proTxHash(ParseHashV(request.params[3], "protx-hash")); - auto dmn = node.dmnman->GetListAtChainTip().GetValidMN(proTxHash); + auto dmn = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip().GetValidMN(proTxHash); if (!dmn) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unknown proTxHash"); } @@ -718,11 +712,10 @@ static RPCHelpMan gobject_list_helper(const bool make_a_diff) const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - CHECK_NONFATAL(node.govman); const int64_t last_time = make_a_diff ? node.govman->GetLastDiffTime() : 0; - return ListObjects(*node.govman, node.dmnman->GetListAtChainTip(), chainman, strCachedSignal, strType, last_time); + return ListObjects(*CHECK_NONFATAL(node.govman), CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(), chainman, + strCachedSignal, strType, last_time); }, }; } @@ -759,8 +752,8 @@ static RPCHelpMan gobject_get() // FIND THE GOVERNANCE OBJECT THE USER IS LOOKING FOR const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman && node.govman); + CHECK_NONFATAL(node.govman); LOCK2(cs_main, node.govman->cs); const CGovernanceObject* pGovObj = node.govman->FindConstGovernanceObject(hash); @@ -785,7 +778,7 @@ static RPCHelpMan gobject_get() // SHOW (MUCH MORE) INFORMATION ABOUT VOTES FOR GOVERNANCE OBJECT (THAN LIST/DIFF ABOVE) // -- FUNDING VOTING RESULTS - auto tip_mn_list = node.dmnman->GetListAtChainTip(); + auto tip_mn_list = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); UniValue objFundingResult(UniValue::VOBJ); objFundingResult.pushKV("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); @@ -858,8 +851,8 @@ static RPCHelpMan gobject_getcurrentvotes() // FIND OBJECT USER IS LOOKING FOR const NodeContext& node = EnsureAnyNodeContext(request.context); - CHECK_NONFATAL(node.govman); + CHECK_NONFATAL(node.govman); LOCK(node.govman->cs); const CGovernanceObject* pGovObj = node.govman->FindConstGovernanceObject(hash); @@ -872,10 +865,9 @@ static RPCHelpMan gobject_getcurrentvotes() UniValue bResult(UniValue::VOBJ); // GET MATCHING VOTES BY HASH, THEN SHOW USERS VOTE INFORMATION - CHECK_NONFATAL(node.dmnman); std::vector vecVotes = node.govman->GetCurrentVotes(hash, mnCollateralOutpoint); for (const auto& vote : vecVotes) { - bResult.pushKV(vote.GetHash().ToString(), vote.ToString(node.dmnman->GetListAtChainTip())); + bResult.pushKV(vote.GetHash().ToString(), vote.ToString(CHECK_NONFATAL(node.dmnman)->GetListAtChainTip())); } return bResult; @@ -975,8 +967,7 @@ static RPCHelpMan voteraw() throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); } - CHECK_NONFATAL(node.dmnman); - const auto tip_mn_list = node.dmnman->GetListAtChainTip(); + const auto tip_mn_list = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); auto dmn = tip_mn_list.GetValidMNByCollateral(outpoint); if (!dmn) { @@ -1034,7 +1025,6 @@ static RPCHelpMan getgovernanceinfo() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureAnyChainman(request.context); - CHECK_NONFATAL(node.dmnman); const auto* pindex = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); int nBlockHeight = pindex->nHeight; @@ -1048,7 +1038,7 @@ static RPCHelpMan getgovernanceinfo() obj.pushKV("superblockmaturitywindow", Params().GetConsensus().nSuperblockMaturityWindow); obj.pushKV("lastsuperblock", nLastSuperblock); obj.pushKV("nextsuperblock", nNextSuperblock); - obj.pushKV("fundingthreshold", int(node.dmnman->GetListAtChainTip().GetValidWeightedMNsCount() / 10)); + obj.pushKV("fundingthreshold", int(CHECK_NONFATAL(node.dmnman)->GetListAtChainTip().GetValidWeightedMNsCount() / 10)); obj.pushKV("governancebudget", ValueFromAmount(CSuperblock::GetPaymentsLimit(chainman.ActiveChain(), nNextSuperblock))); return obj; diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 5b30f0944b..ee5fcbdea0 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -138,8 +139,7 @@ static RPCHelpMan masternode_winner() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - return GetNextMasternodeForPayment(chainman.ActiveChain(), *node.dmnman, 10); + return GetNextMasternodeForPayment(chainman.ActiveChain(), *CHECK_NONFATAL(node.dmnman), 10); }, }; } @@ -159,8 +159,7 @@ static RPCHelpMan masternode_current() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); - return GetNextMasternodeForPayment(chainman.ActiveChain(), *node.dmnman, 1); + return GetNextMasternodeForPayment(chainman.ActiveChain(), *CHECK_NONFATAL(node.dmnman), 1); }, }; } @@ -212,7 +211,7 @@ static RPCHelpMan masternode_status() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { const NodeContext& node = EnsureAnyNodeContext(request.context); - CHECK_NONFATAL(node.dmnman); + if (!node.mn_activeman) { throw JSONRPCError(RPC_INTERNAL_ERROR, "This node does not run an active masternode."); } @@ -221,7 +220,7 @@ static RPCHelpMan masternode_status() // keep compatibility with legacy status for now (might get deprecated/removed later) mnObj.pushKV("outpoint", node.mn_activeman->GetOutPoint().ToStringShort()); mnObj.pushKV("service", node.mn_activeman->GetService().ToStringAddrPort()); - CDeterministicMNCPtr dmn = node.dmnman->GetListAtChainTip().GetMN(node.mn_activeman->GetProTxHash()); + CDeterministicMNCPtr dmn = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip().GetMN(node.mn_activeman->GetProTxHash()); if (dmn) { mnObj.pushKV("proTxHash", dmn->proTxHash.ToString()); mnObj.pushKV("type", std::string(GetMnType(dmn->nType).description)); @@ -243,12 +242,12 @@ static std::string GetRequiredPaymentsString(CGovernanceManager& govman, const C if (payee) { CTxDestination dest; if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) { - CHECK_NONFATAL(false); + NONFATAL_UNREACHABLE(); } strPayments = EncodeDestination(dest); if (payee->nOperatorReward != 0 && payee->pdmnState->scriptOperatorPayout != CScript()) { if (!ExtractDestination(payee->pdmnState->scriptOperatorPayout, dest)) { - CHECK_NONFATAL(false); + NONFATAL_UNREACHABLE(); } strPayments += ", " + EncodeDestination(dest); } @@ -310,14 +309,11 @@ static RPCHelpMan masternode_winners() int nChainTipHeight = pindexTip->nHeight; int nStartHeight = std::max(nChainTipHeight - nCount, 1); - CHECK_NONFATAL(node.dmnman); - CHECK_NONFATAL(node.govman); - - const auto tip_mn_list = node.dmnman->GetListAtChainTip(); + const auto tip_mn_list = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); for (int h = nStartHeight; h <= nChainTipHeight; h++) { const CBlockIndex* pIndex = pindexTip->GetAncestor(h - 1); auto payee = node.dmnman->GetListForBlock(pIndex).GetMNPayee(pIndex); - std::string strPayments = GetRequiredPaymentsString(*node.govman, tip_mn_list, h, payee); + std::string strPayments = GetRequiredPaymentsString(*CHECK_NONFATAL(node.govman), tip_mn_list, h, payee); if (strFilter != "" && strPayments.find(strFilter) == std::string::npos) continue; obj.pushKV(strprintf("%d", h), strPayments); } @@ -558,11 +554,10 @@ static RPCHelpMan masternodelist_helper(bool is_composite) const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.dmnman); UniValue obj(UniValue::VOBJ); - const auto mnList = node.dmnman->GetListAtChainTip(); + const auto mnList = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); const auto dmnToStatus = [&](const auto& dmn) { if (mnList.IsMNValid(dmn)) { return "ENABLED"; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index af51ecc9f8..0444fc5a91 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -391,12 +392,11 @@ static RPCHelpMan generateblock() block.vtx.insert(block.vtx.end(), txs.begin(), txs.end()); { - CHECK_NONFATAL(node.evodb); - LOCK(cs_main); BlockValidationState state; - if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node.evodb, chainparams, active_chainstate, block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { + if (!TestBlockValidity(state, *llmq_ctx.clhandler, *CHECK_NONFATAL(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())); } } @@ -710,14 +710,14 @@ static RPCHelpMan getblocktemplate() } LLMQContext& llmq_ctx = EnsureLLMQContext(node); - CHECK_NONFATAL(node.evodb); 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.evodb, Params(), active_chainstate, block, pindexPrev, false, true); + TestBlockValidity(state, *llmq_ctx.clhandler, *CHECK_NONFATAL(node.evodb), Params(), active_chainstate, + block, pindexPrev, false, true); return BIP22ValidationResult(state); } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 0250036d04..40ed64f821 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1194,9 +1194,8 @@ static RPCHelpMan mockscheduler() throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)"); } - auto* node_context = GetContext(request.context); + auto* node_context = CHECK_NONFATAL(GetContext(request.context)); // protect against null pointer dereference - CHECK_NONFATAL(node_context); CHECK_NONFATAL(node_context->scheduler); node_context->scheduler->MockForward(std::chrono::seconds(delta_seconds)); diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index f6c27f4c73..3aa5de1a5a 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -282,7 +283,6 @@ static RPCHelpMan quorum_dkgstatus() const ChainstateManager& chainman = EnsureChainman(node); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); const CConnman& connman = EnsureConnman(node); - CHECK_NONFATAL(node.dmnman); CHECK_NONFATAL(node.sporkman); int detailLevel = 0; @@ -296,7 +296,7 @@ static RPCHelpMan quorum_dkgstatus() llmq::CDKGDebugStatus status; llmq_ctx.dkg_debugman->GetLocalDebugStatus(status); - auto ret = status.ToJson(*node.dmnman, chainman, detailLevel); + auto ret = status.ToJson(*CHECK_NONFATAL(node.dmnman), chainman, detailLevel); CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); int tipHeight = pindexTip->nHeight; @@ -385,7 +385,6 @@ static RPCHelpMan quorum_memberof() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - CHECK_NONFATAL(node.dmnman); uint256 protxHash(ParseHashV(request.params[0], "proTxHash")); int scanQuorumsCount = -1; @@ -397,7 +396,7 @@ static RPCHelpMan quorum_memberof() } const CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); - auto mnList = node.dmnman->GetListForBlock(pindexTip); + auto mnList = CHECK_NONFATAL(node.dmnman)->GetListForBlock(pindexTip); auto dmn = mnList.GetMN(protxHash); if (!dmn) { throw JSONRPCError(RPC_INVALID_PARAMETER, "masternode not found"); @@ -842,7 +841,7 @@ static RPCHelpMan quorum_rotationinfo() const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - CHECK_NONFATAL(node.dmnman); + ; llmq::CGetQuorumRotationInfo cmd; llmq::CQuorumRotationInfo quorumRotationInfoRet; @@ -859,7 +858,8 @@ static RPCHelpMan quorum_rotationinfo() LOCK(cs_main); - if (!BuildQuorumRotationInfo(*node.dmnman, chainman, *llmq_ctx.qman, *llmq_ctx.quorum_block_processor, cmd, quorumRotationInfoRet, strError)) { + if (!BuildQuorumRotationInfo(*CHECK_NONFATAL(node.dmnman), chainman, *llmq_ctx.qman, *llmq_ctx.quorum_block_processor, + cmd, quorumRotationInfoRet, strError)) { throw JSONRPCError(RPC_INVALID_REQUEST, strError); } @@ -1073,7 +1073,8 @@ static RPCHelpMan verifyislock() auto llmqType = Params().GetConsensus().llmqTypeDIP0024InstantSend; const auto llmq_params_opt = Params().GetLLMQ(llmqType); CHECK_NONFATAL(llmq_params_opt.has_value()); - return VerifyRecoveredSigLatestQuorums(*llmq_params_opt, chainman.ActiveChain(), *llmq_ctx.qman, signHeight, id, txid, sig); + return VerifyRecoveredSigLatestQuorums(*llmq_params_opt, chainman.ActiveChain(), *CHECK_NONFATAL(llmq_ctx.qman), + signHeight, id, txid, sig); }, }; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 1b01eb3195..e06382711e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -495,8 +496,6 @@ static RPCHelpMan getassetunlockstatuses() throw JSONRPCError(RPC_INTERNAL_ERROR, "No blocks in chain"); } - CHECK_NONFATAL(node.cpoolman); - std::optional poolCL{std::nullopt}; std::optional poolOnTip{std::nullopt}; std::optional nSpecificCoreHeight{std::nullopt}; @@ -506,7 +505,7 @@ static RPCHelpMan getassetunlockstatuses() if (nSpecificCoreHeight.value() < 0 || nSpecificCoreHeight.value() > chainman.ActiveChain().Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } - poolCL = std::make_optional(node.cpoolman->GetCreditPool(chainman.ActiveChain()[nSpecificCoreHeight.value()], Params().GetConsensus())); + poolCL = std::make_optional(CHECK_NONFATAL(node.cpoolman)->GetCreditPool(chainman.ActiveChain()[nSpecificCoreHeight.value()], Params().GetConsensus())); } else { const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* { diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 727fa5e694..4a63378b84 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -10,6 +10,7 @@ #include