From ad57a6405d9cbd7046e0ae7e818fb019b29ce92d Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Mar 2026 15:01:05 +0700 Subject: [PATCH 01/26] fix: make regression miner_tests works correctly with InstantSend enabled (part I) --- src/test/miner_tests.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 2ac2bc6d6698..ca35fa31841e 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,8 @@ struct MinerTestingSetup : public TestingSetup { } BlockAssembler AssemblerForTest(const CChainParams& params); }; + +static constexpr int WAIT_FOR_ISLOCK_TIMEOUT{601}; } // namespace miner_tests BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup) @@ -235,8 +238,11 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C { tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); bool spendsCoinbase = i == 0; // only first tx spends coinbase // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails + m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } @@ -281,6 +287,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C // orphan in mempool, template creation fails hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); @@ -307,6 +315,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); // give it a fee so it'll get mined m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(false).FromTx(tx)); // Should throw bad-cb-multiple @@ -322,6 +332,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); @@ -361,11 +373,15 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C CScript script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script)); hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(false).FromTx(tx)); // Should throw block-validation-failed BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); From 27437f66c2d933826af463fa39b2e31305e5bbd7 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Mar 2026 15:37:04 +0700 Subject: [PATCH 02/26] fix: make regression miner_tests works correctly with InstantSend enabled (part II) --- src/test/miner_tests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index ca35fa31841e..db5a12eb222e 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -398,6 +398,7 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C // non-final txs in mempool SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1); + int64_t mocked_time_for_is = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() - WAIT_FOR_ISLOCK_TIMEOUT; const int flags{LOCKTIME_VERIFY_SEQUENCE}; // height map std::vector prevheights; @@ -416,6 +417,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = 0; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail @@ -430,6 +433,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block prevheights[0] = baseheight + 2; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Time(Now()).FromTx(tx)); BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail @@ -453,6 +458,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C prevheights[0] = baseheight + 3; tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Time(Now()).FromTx(tx)); BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass @@ -464,6 +471,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C prevheights.resize(1); prevheights[0] = baseheight + 4; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Time(Now()).FromTx(tx)); BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass From e91cce86847e28416696c753faef05dda8b4f543 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Mar 2026 16:33:00 +0700 Subject: [PATCH 03/26] fix: make even more miner_tests friendly to InstantSend enabled (TestPackageSelection, final part) --- src/test/miner_tests.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index db5a12eb222e..1562c679c088 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -135,6 +135,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co uint256 hashHighFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(50000).Time(Now()).SpendsCoinbase(false).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashParentTx, hashMediumFeeTx, hashHighFeeTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); std::unique_ptr pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 4U); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx); @@ -145,6 +147,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vin[0].prevout.hash = hashHighFeeTx; tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee uint256 hashFreeTx = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashFreeTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx)); size_t freeTxSize = GetVirtualTransactionSize(CTransaction(tx)); @@ -170,6 +174,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashLowFeeTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx); @@ -184,6 +190,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[1].nValue = 100000000; // 1BTC output uint256 hashFreeTx2 = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({tx.GetHash()}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); // This tx can't be mined by itself tx.vin[0].prevout.hash = hashFreeTx2; @@ -192,6 +200,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({tx.GetHash()}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); // Verify that this tx isn't selected. @@ -205,6 +215,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vin[0].prevout.n = 1; tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({tx.GetHash()}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 9U); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); @@ -258,6 +270,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C hash = tx.GetHash(); bool spendsCoinbase = i == 0; // only first tx spends coinbase // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } @@ -566,6 +580,8 @@ void MinerTestingSetup::TestPrioritisedMining(const CChainParams& chainparams, c uint256 hashFreeGrandchild = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashMediumFeeTx, hashPrioritsedChild, hashParentTx, hashFreeParent, hashFreeChild, hashFreeGrandchild, hashFreePrioritisedTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); auto pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashFreeParent); From 13442a772433ec2bc942956a50bb13a65de6c7ae Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Mar 2026 16:40:28 +0700 Subject: [PATCH 04/26] refactor: drop dependency of InstantSend on CMasternodeSync --- src/instantsend/instantsend.cpp | 10 ++-------- src/instantsend/instantsend.h | 5 +---- src/llmq/context.cpp | 7 +++---- src/llmq/context.h | 6 ++---- src/node/chainstate.cpp | 2 +- 5 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 26d0e3f90559..615669121ab0 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -15,11 +14,9 @@ using node::fImporting; using node::fReindex; namespace llmq { -CInstantSendManager::CInstantSendManager(CSporkManager& sporkman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params) : +CInstantSendManager::CInstantSendManager(CSporkManager& sporkman, const util::DbWrapperParams& db_params) : db{db_params}, - spork_manager{sporkman}, - m_mn_sync{mn_sync} + spork_manager{sporkman} { } @@ -471,9 +468,6 @@ bool CInstantSendManager::IsInstantSendEnabled() const bool CInstantSendManager::RejectConflictingBlocks() const { - if (!m_mn_sync.IsBlockchainSynced()) { - return false; - } if (!spork_manager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) { LogPrint(BCLog::INSTANTSEND, "%s: spork3 is off, skipping transaction locking checks\n", __func__); return false; diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index a9667ab4384c..4bdfc8d2d4a4 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -20,7 +20,6 @@ class CBlockIndex; class CDataStream; -class CMasternodeSync; class CSporkManager; namespace Consensus { struct LLMQParams; @@ -54,7 +53,6 @@ class CInstantSendManager private: instantsend::CInstantSendDb db; CSporkManager& spork_manager; - const CMasternodeSync& m_mn_sync; mutable Mutex cs_pendingLocks; // Incoming and not verified yet @@ -90,8 +88,7 @@ class CInstantSendManager CInstantSendManager() = delete; CInstantSendManager(const CInstantSendManager&) = delete; CInstantSendManager& operator=(const CInstantSendManager&) = delete; - explicit CInstantSendManager(CSporkManager& sporkman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params); + explicit CInstantSendManager(CSporkManager& sporkman, const util::DbWrapperParams& db_params); ~CInstantSendManager(); void AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index 3e24e34d01fa..3d28a364fa5b 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -13,9 +13,8 @@ #include LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - ChainstateManager& chainman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, - int64_t max_recsigs_age) : + ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, + int16_t worker_count, int64_t max_recsigs_age) : bls_worker{std::make_shared()}, qsnapman{std::make_unique(evo_db)}, quorum_block_processor{std::make_unique(chainman.ActiveChainstate(), dmnman, evo_db, @@ -23,7 +22,7 @@ LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSpork qman{std::make_unique(*bls_worker, dmnman, evo_db, *quorum_block_processor, *qsnapman, chainman, db_params)}, sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, - isman{std::make_unique(sporkman, mn_sync, db_params)} + isman{std::make_unique(sporkman, db_params)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(worker_count); diff --git a/src/llmq/context.h b/src/llmq/context.h index 4f3e5e984e3f..8ffc38dfa529 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -13,7 +13,6 @@ class CBLSWorker; class ChainstateManager; class CDeterministicMNManager; class CEvoDB; -class CMasternodeSync; class CSporkManager; class PeerManager; @@ -34,9 +33,8 @@ struct LLMQContext { LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - ChainstateManager& chainman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, - int64_t max_recsigs_age); + ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, + int16_t worker_count, int64_t max_recsigs_age); ~LLMQContext(); /** Guaranteed if LLMQContext is initialized then all members are valid too diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 98d4dc21d199..d5211c6a36bb 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -230,7 +230,7 @@ void DashChainstateSetup(ChainstateManager& chainman, dmnman = std::make_unique(evodb, mn_metaman); llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainman, mn_sync, + llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainman, util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, bls_threads, worker_count, max_recsigs_age); mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); From caefa9fcb6639b1f45df98724e9f20a5243aef63 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 11 Mar 2026 17:38:46 +0700 Subject: [PATCH 05/26] refactor: drop dependency of CChainState on CMasternodeSync There's new helper IsValidAndSynced in CGovernanceManager that incapsulate usages of CMasternodeSync in CMNPayment --- src/evo/chainhelper.cpp | 7 +++---- src/evo/chainhelper.h | 6 ++---- src/governance/governance.cpp | 2 ++ src/governance/governance.h | 1 + src/masternode/payments.cpp | 6 +++--- src/masternode/payments.h | 16 ++++++++++------ src/node/chainstate.cpp | 2 +- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/evo/chainhelper.cpp b/src/evo/chainhelper.cpp index 5c1f19a508d0..8794fceabbc7 100644 --- a/src/evo/chainhelper.cpp +++ b/src/evo/chainhelper.cpp @@ -17,14 +17,13 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmnman, CGovernanceManager& govman, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync, - const CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, - const llmq::CQuorumManager& qman) : + const Consensus::Params& consensus_params, const CSporkManager& sporkman, + const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman) : isman{isman}, credit_pool_manager{std::make_unique(evodb, chainman)}, m_chainlocks{chainlocks}, ehf_manager{std::make_unique(evodb, chainman, qman)}, - mn_payments{std::make_unique(dmnman, govman, chainman, consensus_params, mn_sync, sporkman)}, + mn_payments{std::make_unique(dmnman, govman, chainman, consensus_params, sporkman)}, special_tx{std::make_unique(*credit_pool_manager, dmnman, *ehf_manager, qblockman, qsnapman, chainman, consensus_params, chainlocks, qman)} {} diff --git a/src/evo/chainhelper.h b/src/evo/chainhelper.h index 69a229d93cad..eb69e8cfd679 100644 --- a/src/evo/chainhelper.h +++ b/src/evo/chainhelper.h @@ -15,7 +15,6 @@ class CDeterministicMNManager; class CEvoDB; class CGovernanceManager; class ChainstateManager; -class CMasternodeSync; class CMNHFManager; class CMNPaymentsProcessor; class CSpecialTxProcessor; @@ -60,9 +59,8 @@ class CChainstateHelper explicit CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmnman, CGovernanceManager& govman, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync, - const CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, - const llmq::CQuorumManager& qman); + const Consensus::Params& consensus_params, const CSporkManager& sporkman, + const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman); ~CChainstateHelper(); /** Passthrough functions to chainlock::Chainlocks */ diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index f72c8d837846..530524188f1c 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -96,6 +96,8 @@ bool CGovernanceManager::LoadCache(bool load_cache) return is_valid; } +bool CGovernanceManager::IsValidAndSynced() const { return is_valid && m_mn_sync.IsSynced(); } + void CGovernanceManager::RelayObject(const CGovernanceObject& obj) { AssertLockNotHeld(cs_relay); diff --git a/src/governance/governance.h b/src/governance/governance.h index fa048b40d9d2..58addde58e7b 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -273,6 +273,7 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent // Basic initialization and querying bool IsValid() const override { return is_valid; } + bool IsValidAndSynced() const; bool LoadCache(bool load_cache) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional); diff --git a/src/masternode/payments.cpp b/src/masternode/payments.cpp index 72fb0cff9b84..933a1295cfc1 100644 --- a/src/masternode/payments.cpp +++ b/src/masternode/payments.cpp @@ -160,7 +160,7 @@ CAmount PlatformShare(const CAmount reward) int nOffset = nBlockHeight % m_consensus_params.nBudgetPaymentsCycleBlocks; if (nOffset < m_consensus_params.nBudgetPaymentsWindowBlocks) { // NOTE: old budget system is disabled since 12.1 - if(m_mn_sync.IsSynced()) { + if (m_govman.IsValidAndSynced()) { // no old budget blocks should be accepted here on mainnet, // testnet/devnet/regtest should produce regular blocks only LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- WARNING! Client synced but old budget system is disabled, checking block value against block reward\n", __func__); @@ -232,7 +232,7 @@ bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const int nBlo return false; } - if (!m_mn_sync.IsSynced() || !m_govman.IsValid()) { + if (!m_govman.IsValidAndSynced()) { LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- WARNING! Not enough data, checked superblock max bounds only\n", __func__); // not enough data for full checks but at least we know that the superblock limits were honored. // We rely on the network to have followed the correct chain in this case @@ -291,7 +291,7 @@ bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CB return false; } - if (!m_mn_sync.IsSynced() || !m_govman.IsValid()) { + if (!m_govman.IsValidAndSynced()) { // governance data is either incomplete or non-existent LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- WARNING! Not enough data, skipping superblock payee checks\n", __func__); return true; // not an error diff --git a/src/masternode/payments.h b/src/masternode/payments.h index a4df891c8c1a..4c7c640afbc2 100644 --- a/src/masternode/payments.h +++ b/src/masternode/payments.h @@ -15,7 +15,6 @@ class CBlockIndex; class CDeterministicMNManager; class CGovernanceManager; class ChainstateManager; -class CMasternodeSync; class CTransaction; class CSporkManager; class CTxOut; @@ -37,7 +36,6 @@ class CMNPaymentsProcessor CGovernanceManager& m_govman; const ChainstateManager& m_chainman; const Consensus::Params& m_consensus_params; - const CMasternodeSync& m_mn_sync; const CSporkManager& m_sporkman; private: @@ -50,10 +48,16 @@ class CMNPaymentsProcessor [[nodiscard]] bool IsOldBudgetBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet); public: - explicit CMNPaymentsProcessor(CDeterministicMNManager& dmnman, CGovernanceManager& govman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync, const CSporkManager& sporkman) : - m_dmnman{dmnman}, m_govman{govman}, m_chainman{chainman}, m_consensus_params{consensus_params}, m_mn_sync{mn_sync}, - m_sporkman{sporkman} {} + explicit CMNPaymentsProcessor(CDeterministicMNManager& dmnman, CGovernanceManager& govman, + const ChainstateManager& chainman, const Consensus::Params& consensus_params, + const CSporkManager& sporkman) : + m_dmnman{dmnman}, + m_govman{govman}, + m_chainman{chainman}, + m_consensus_params{consensus_params}, + m_sporkman{sporkman} + { + } bool IsBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock); bool IsBlockPayeeValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, const bool check_superblock); diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index d5211c6a36bb..fed510763de9 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -236,7 +236,7 @@ void DashChainstateSetup(ChainstateManager& chainman, mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); chain_helper.reset(); chain_helper = std::make_unique(evodb, *dmnman, govman, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), - *(llmq_ctx->qsnapman), chainman, consensus_params, mn_sync, sporkman, chainlocks, + *(llmq_ctx->qsnapman), chainman, consensus_params, sporkman, chainlocks, *(llmq_ctx->qman)); } From 2b65525738de89a28db1267a493c1b7cd92de7d3 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Mar 2026 19:47:05 +0700 Subject: [PATCH 06/26] refactor: remove usages of CMasternodeSync from chainstate --- src/init.cpp | 1 - src/node/chainstate.cpp | 4 +--- src/node/chainstate.h | 3 --- src/test/util/setup_common.cpp | 3 +-- 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 703c50646cc3..80264f04c765 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1992,7 +1992,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) chainman, *node.govman, *node.mn_metaman, - *node.mn_sync, *node.sporkman, *node.chainlocks, node.chain_helper, diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index fed510763de9..f9919d81ffdc 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -37,7 +37,6 @@ std::optional LoadChainstate(bool fReset, ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, @@ -83,7 +82,7 @@ std::optional LoadChainstate(bool fReset, pblocktree.reset(); pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, block_tree_db_in_memory, fReset)); - DashChainstateSetup(chainman, govman, mn_metaman, mn_sync, sporkman, chainlocks, chain_helper, + DashChainstateSetup(chainman, govman, mn_metaman, sporkman, chainlocks, chain_helper, dmnman, *evodb, llmq_ctx, mempool, data_dir, dash_dbs_in_memory, /*llmq_dbs_wipe=*/fReset || fReindexChainState, bls_threads, worker_count, max_recsigs_age, consensus_params); @@ -209,7 +208,6 @@ std::optional LoadChainstate(bool fReset, void DashChainstateSetup(ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, diff --git a/src/node/chainstate.h b/src/node/chainstate.h index 09adc7cdf214..ac6b3f9235da 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -17,7 +17,6 @@ class CEvoDB; class CGovernanceManager; class ChainstateManager; class CMasternodeMetaMan; -class CMasternodeSync; class CSporkManager; class CTxMemPool; struct LLMQContext; @@ -80,7 +79,6 @@ std::optional LoadChainstate(bool fReset, ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, @@ -111,7 +109,6 @@ std::optional LoadChainstate(bool fReset, void DashChainstateSetup(ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 9847e4b2f913..4eb5e3f45f07 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -143,7 +143,7 @@ void DashChainstateSetup(ChainstateManager& chainman, bool llmq_dbs_wipe, const Consensus::Params& consensus_params) { - DashChainstateSetup(chainman, *Assert(node.govman.get()), *Assert(node.mn_metaman.get()), *Assert(node.mn_sync.get()), + DashChainstateSetup(chainman, *Assert(node.govman.get()), *Assert(node.mn_metaman.get()), *Assert(node.sporkman.get()), *Assert(node.chainlocks), node.chain_helper, node.dmnman, *node.evodb, node.llmq_ctx, Assert(node.mempool.get()), node.args->GetDataDirNet(), llmq_dbs_in_memory, llmq_dbs_wipe, llmq::DEFAULT_BLSCHECK_THREADS, llmq::DEFAULT_WORKER_COUNT, llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE, @@ -323,7 +323,6 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector Date: Wed, 11 Mar 2026 17:38:46 +0700 Subject: [PATCH 07/26] refactor: new stub implementation (NullNodeSyncNotifier) of NodeSyncNotifier is introduced to use it for CChainState --- src/masternode/sync.cpp | 5 +++++ src/masternode/sync.h | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 2b69c71cb47d..e367a24e3e27 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -9,6 +9,11 @@ #include #include +#include + +void NullNodeSyncNotifier::SyncReset() { assert(false); } +void NullNodeSyncNotifier::SyncFinished() { assert(false); } + CMasternodeSync::CMasternodeSync(std::unique_ptr&& sync_notifier) : nTimeAssetSyncStarted{GetTime()}, nTimeLastBumped{GetTime()}, diff --git a/src/masternode/sync.h b/src/masternode/sync.h index 247d8d0ff889..98401982727e 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -32,6 +32,20 @@ class NodeSyncNotifier virtual ~NodeSyncNotifier() = default; }; +/** Stub implementation for use in chainstate-only (non-network) contexts. + * CMasternodeSync constructed with this notifier permanently returns + * IsBlockchainSynced()=false and IsSynced()=false, which correctly disables + * network-dependent validation paths. + * + * Asserts on any call — if sync state is being advanced, a real notifier + * (NodeSyncNotifierImpl) must be used instead. */ +class NullNodeSyncNotifier final : public NodeSyncNotifier +{ +public: + void SyncReset() override; + void SyncFinished() override; +}; + // // CMasternodeSync : Sync masternode assets in stages // From 3472a90583714a4426969508ec18f5ae8d6bf475 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 13 Mar 2026 13:11:41 +0700 Subject: [PATCH 08/26] refactor: move VerifyChainLock to clsig.h to avoid building up handler.cpp for chain-state --- src/chainlock/clsig.cpp | 12 ++++++++++++ src/chainlock/clsig.h | 17 +++++++++++++++-- src/chainlock/handler.cpp | 20 +++++--------------- src/chainlock/handler.h | 9 --------- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/chainlock/clsig.cpp b/src/chainlock/clsig.cpp index 573331bba610..16438a0c76c8 100644 --- a/src/chainlock/clsig.cpp +++ b/src/chainlock/clsig.cpp @@ -4,6 +4,8 @@ #include +#include +#include #include #include @@ -30,4 +32,14 @@ uint256 GenSigRequestId(const int32_t nHeight) { return ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, nHeight)); } + +llmq::VerifyRecSigStatus VerifyChainLock(const Consensus::Params& params, const CChain& chain, + const llmq::CQuorumManager& qman, const chainlock::ChainLockSig& clsig) +{ + const auto llmqType = params.llmqTypeChainLocks; + const uint256 request_id = GenSigRequestId(clsig.getHeight()); + + return llmq::VerifyRecoveredSig(llmqType, chain, qman, clsig.getHeight(), request_id, clsig.getBlockHash(), + clsig.getSig()); +} } // namespace chainlock diff --git a/src/chainlock/clsig.h b/src/chainlock/clsig.h index 3800f2f813cb..62f86db68e67 100644 --- a/src/chainlock/clsig.h +++ b/src/chainlock/clsig.h @@ -5,13 +5,23 @@ #ifndef BITCOIN_CHAINLOCK_CLSIG_H #define BITCOIN_CHAINLOCK_CLSIG_H +#include #include #include -#include - #include +class CChain; + +namespace Consensus { +struct Params; +} // namespace Consensus + +namespace llmq { +class CQuorumManager; +enum class VerifyRecSigStatus : uint8_t; +} // namespace llmq + namespace chainlock { struct ChainLockSig { private: @@ -39,6 +49,9 @@ struct ChainLockSig { //! Generate clsig request ID with block height uint256 GenSigRequestId(const int32_t nHeight); + +llmq::VerifyRecSigStatus VerifyChainLock(const Consensus::Params& params, const CChain& chain, + const llmq::CQuorumManager& qman, const ChainLockSig& clsig); } // namespace chainlock #endif // BITCOIN_CHAINLOCK_CLSIG_H diff --git a/src/chainlock/handler.cpp b/src/chainlock/handler.cpp index ddb7bf1ff59a..d1075a8fc7f6 100644 --- a/src/chainlock/handler.cpp +++ b/src/chainlock/handler.cpp @@ -4,19 +4,18 @@ #include +#include #include +#include +#include #include #include #include -#include -#include - -#include -#include -#include #include #include +#include #include +#include #include #include #include @@ -324,13 +323,4 @@ void ChainlockHandler::Cleanup() } } -llmq::VerifyRecSigStatus VerifyChainLock(const Consensus::Params& params, const CChain& chain, - const llmq::CQuorumManager& qman, const chainlock::ChainLockSig& clsig) -{ - const auto llmqType = params.llmqTypeChainLocks; - const uint256 request_id = chainlock::GenSigRequestId(clsig.getHeight()); - - return llmq::VerifyRecoveredSig(llmqType, chain, qman, clsig.getHeight(), request_id, clsig.getBlockHash(), - clsig.getSig()); -} } // namespace chainlock diff --git a/src/chainlock/handler.h b/src/chainlock/handler.h index e024ca539876..140325c007c6 100644 --- a/src/chainlock/handler.h +++ b/src/chainlock/handler.h @@ -24,20 +24,14 @@ class CBlock; class CBlockIndex; -class CChain; class CMasternodeSync; class ChainstateManager; class CScheduler; class CTxMemPool; struct MessageProcessingResult; -namespace Consensus { -struct Params; -} // namespace Consensus - namespace llmq { class CQuorumManager; -enum class VerifyRecSigStatus : uint8_t; } // namespace llmq namespace chainlock { @@ -107,9 +101,6 @@ class ChainlockHandler final : public CValidationInterface private: void Cleanup() EXCLUSIVE_LOCKS_REQUIRED(!cs); }; - -llmq::VerifyRecSigStatus VerifyChainLock(const Consensus::Params& params, const CChain& chain, - const llmq::CQuorumManager& qman, const chainlock::ChainLockSig& clsig); } // namespace chainlock #endif // BITCOIN_CHAINLOCK_HANDLER_H From 547ca66b7145f1c95ef42721a947e970c5aba0a4 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 13 Mar 2026 14:16:18 +0700 Subject: [PATCH 09/26] refactor: drop dependency of governance/object on core_write (ScriptToAsmStr) --- src/governance/object.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/governance/object.cpp b/src/governance/object.cpp index d05e5b021e99..86431439196f 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -12,11 +12,11 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -491,12 +491,12 @@ bool CGovernanceObject::IsCollateralValid(const ChainstateManager& chainman, std CAmount nMinFee = GetMinCollateralFee(); LogPrint(BCLog::GOBJECT, "CGovernanceObject::IsCollateralValid -- txCollateral->vout.size() = %s, findScript = %s, nMinFee = %lld\n", - txCollateral->vout.size(), ScriptToAsmStr(findScript, false), nMinFee); + txCollateral->vout.size(), HexStr(findScript), nMinFee); bool foundOpReturn = false; for (const auto& output : txCollateral->vout) { LogPrint(BCLog::GOBJECT, "CGovernanceObject::IsCollateralValid -- txout = %s, output.nValue = %lld, output.scriptPubKey = %s\n", - output.ToString(), output.nValue, ScriptToAsmStr(output.scriptPubKey, false)); + output.ToString(), output.nValue, HexStr(output.scriptPubKey)); if (!output.scriptPubKey.IsPayToPublicKeyHash() && !output.scriptPubKey.IsUnspendable()) { strError = strprintf("Invalid Script %s", txCollateral->ToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); From 963a5f01fa4a7985e0b49be13b260e742ca951b4 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 13 Mar 2026 13:47:43 +0700 Subject: [PATCH 10/26] refactor: drop pfrom from CGovernanceManager::ProcessVote --- src/governance/governance.cpp | 4 ++-- src/governance/governance.h | 2 +- src/governance/net_governance.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 530524188f1c..83b0459e4363 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -753,14 +753,14 @@ bool CGovernanceManager::ProcessVoteAndRelay(const CGovernanceVote& vote, CGover AssertLockNotHeld(cs_store); AssertLockNotHeld(cs_relay); uint256 hashToRequest; // Ignored for local votes (no peer to request from) - bool fOK = ProcessVote(/*pfrom=*/nullptr, vote, exception, hashToRequest); + bool fOK = ProcessVote(vote, exception, hashToRequest); if (fOK) { RelayVote(vote); } return fOK; } -bool CGovernanceManager::ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception, +bool CGovernanceManager::ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception, uint256& hashToRequest) { AssertLockNotHeld(cs_store); diff --git a/src/governance/governance.h b/src/governance/governance.h index 58addde58e7b..223a3d0db5d0 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -383,7 +383,7 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent CDeterministicMNManager& GetMNManager(); /** Process a governance vote. Returns true on success. * If the vote is for an unknown object (orphan), hashToRequest is set to the object hash. */ - bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception, uint256& hashToRequest) + bool ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception, uint256& hashToRequest) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 7849ff91544e..c9314c9cc1d5 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -169,7 +169,7 @@ void NetGovernance::ProcessMessage(CNode& peer, const std::string& msg_type, CDa CGovernanceException exception; uint256 hashToRequest; - if (m_gov_manager.ProcessVote(&peer, vote, exception, hashToRequest)) { + if (m_gov_manager.ProcessVote(vote, exception, hashToRequest)) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- %s new\n", strHash); m_node_sync.BumpAssetLastTime("MNGOVERNANCEOBJECTVOTE"); From 3658db6fc079f5c332b2b2eb2ca5e7e69b02af18 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 13 Mar 2026 22:01:15 +0700 Subject: [PATCH 11/26] fix: add guards if CTxMempool is nullptr in chainstate --- src/node/chainstate.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index f9919d81ffdc..c97cf9c61f2d 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -231,7 +231,9 @@ void DashChainstateSetup(ChainstateManager& chainman, llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainman, util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, bls_threads, worker_count, max_recsigs_age); - mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); + if (mempool) { + mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); + } chain_helper.reset(); chain_helper = std::make_unique(evodb, *dmnman, govman, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), *(llmq_ctx->qsnapman), chainman, consensus_params, sporkman, chainlocks, @@ -246,7 +248,9 @@ void DashChainstateSetupClose(std::unique_ptr& chain_helper, { chain_helper.reset(); llmq_ctx.reset(); - mempool->DisconnectManagers(); + if (mempool) { + mempool->DisconnectManagers(); + } dmnman.reset(); } From 5e5f4a0f1c31d181ebb8c5e428ca3e1d2c501578 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Thu, 3 Mar 2022 19:31:14 +0000 Subject: [PATCH 12/26] Merge bitcoin/bitcoin#24304: [kernel 0/n] Introduce `bitcoin-chainstate` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2c03cec2ff8cdbfd5da92bfb507d218e5c6435b0 ci: Build bitcoin-chainstate (Carl Dong) 095aa6ca37bf0bd5c5e221bab779978a99b2a34c build: Add example bitcoin-chainstate executable (Carl Dong) Pull request description: Part of: #24303 This PR introduces an example/demo `bitcoin-chainstate` executable using said library which can print out information about a datadir and take in new blocks on stdin. Please read the commit messages for more details. ----- #### You may ask: WTF?! Why is `index/*.cpp`, etc. being linked in? This PR is meant only to capture the state of dependencies in our consensus engine as of right now. There are many things to decouple from consensus, which will be done in subsequent PRs. Listing the files out right now in `bitcoin_chainstate_SOURCES` is purely to give us a clear picture of the task at hand, it is **not** to say that these dependencies _belongs_ there in any way. ### TODO 1. Clean up `bitcoin-chainstate.cpp` It is quite ugly, with a lot of comments I've left for myself, I should clean it up to the best of my abilities (the ugliness of our init/shutdown might be the upper bound on cleanliness here...) ACKs for top commit: ajtowns: ACK 2c03cec2ff8cdbfd5da92bfb507d218e5c6435b0 ryanofsky: Code review ACK 2c03cec2ff8cdbfd5da92bfb507d218e5c6435b0. Just rebase, comments, formatting change since last review MarcoFalke: re-ACK 2c03cec2ff8cdbfd5da92bfb507d218e5c6435b0 🏔 Tree-SHA512: 86e7fb5718caa577df8abc8288c754f4a590650d974df9d2f6476c87ed25c70f923c4db651c6963f33498fc7a3a31f6692b9a75cbc996bf4888c5dac2f34a13b --- .gitignore | 1 + ci/dash/matrix.sh | 2 +- ...p_env_native_nowallet_libbitcoinkernel.sh} | 4 +- configure.ac | 14 + src/Makefile.am | 99 +++++++ src/bitcoin-chainstate.cpp | 262 ++++++++++++++++++ 6 files changed, 379 insertions(+), 3 deletions(-) rename ci/test/{00_setup_env_native_nowallet.sh => 00_setup_env_native_nowallet_libbitcoinkernel.sh} (81%) create mode 100644 src/bitcoin-chainstate.cpp diff --git a/.gitignore b/.gitignore index be9ddf9c6069..eab9f3f83ec1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ src/dash-gui src/dash-node src/dash-tx src/dash-util +src/dash-chainstate src/dash-wallet src/test/fuzz/fuzz src/test/test_dash diff --git a/ci/dash/matrix.sh b/ci/dash/matrix.sh index 6e249fc476d1..de01eaf6d6c7 100755 --- a/ci/dash/matrix.sh +++ b/ci/dash/matrix.sh @@ -27,7 +27,7 @@ elif [ "$BUILD_TARGET" = "linux64_fuzz" ]; then elif [ "$BUILD_TARGET" = "linux64_multiprocess" ]; then source ./ci/test/00_setup_env_native_multiprocess.sh elif [ "$BUILD_TARGET" = "linux64_nowallet" ]; then - source ./ci/test/00_setup_env_native_nowallet.sh + source ./ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh elif [ "$BUILD_TARGET" = "linux64_sqlite" ]; then source ./ci/test/00_setup_env_native_sqlite.sh elif [ "$BUILD_TARGET" = "linux64_tsan" ]; then diff --git a/ci/test/00_setup_env_native_nowallet.sh b/ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh similarity index 81% rename from ci/test/00_setup_env_native_nowallet.sh rename to ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh index 3e083c3d4d62..ef228c677fef 100755 --- a/ci/test/00_setup_env_native_nowallet.sh +++ b/ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh @@ -6,9 +6,9 @@ export LC_ALL=C.UTF-8 -export CONTAINER_NAME=ci_native_nowallet +export CONTAINER_NAME=ci_native_nowallet_libbitcoinkernel export HOST=x86_64-pc-linux-gnu export PACKAGES="python3-zmq" export DEP_OPTS="NO_WALLET=1 CC=gcc-14 CXX=g++-14" export GOAL="install" -export BITCOIN_CONFIG="--enable-reduce-exports CC=gcc-14 CXX=g++-14" +export BITCOIN_CONFIG="--enable-reduce-exports CC=gcc-14 CXX=g++-14 --enable-experimental-util-chainstate" diff --git a/configure.ac b/configure.ac index 215b797f544c..873e777101f7 100644 --- a/configure.ac +++ b/configure.ac @@ -35,6 +35,7 @@ BITCOIN_TEST_NAME=test_dash BITCOIN_CLI_NAME=dash-cli BITCOIN_TX_NAME=dash-tx BITCOIN_UTIL_NAME=dash-util +BITCOIN_CHAINSTATE_NAME=dash-chainstate BITCOIN_WALLET_TOOL_NAME=dash-wallet dnl Multi Process BITCOIN_MP_NODE_NAME=dash-node @@ -769,6 +770,13 @@ AC_ARG_ENABLE([util-util], [build_bitcoin_util=$enableval], [build_bitcoin_util=$build_bitcoin_utils]) +AC_ARG_ENABLE([experimental-util-chainstate], + [AS_HELP_STRING([--enable-experimental-util-chainstate], + [build experimental bitcoin-chainstate executable (default=no)])], + [build_bitcoin_chainstate=$enableval], + [build_bitcoin_chainstate=no]) + + AC_ARG_WITH([libs], [AS_HELP_STRING([--with-libs], [build libraries (default=yes)])], @@ -1443,6 +1451,7 @@ if test "$enable_fuzz" = "yes"; then build_bitcoin_cli=no build_bitcoin_tx=no build_bitcoin_util=no + build_bitcoin_chainstate=no build_bitcoin_wallet=no build_bitcoind=no build_bitcoin_libs=no @@ -1781,6 +1790,10 @@ AC_MSG_CHECKING([whether to build dash-util]) AM_CONDITIONAL([BUILD_BITCOIN_UTIL], [test $build_bitcoin_util = "yes"]) AC_MSG_RESULT($build_bitcoin_util) +AC_MSG_CHECKING([whether to build experimental dash-chainstate]) +AM_CONDITIONAL([BUILD_BITCOIN_CHAINSTATE], [test $build_bitcoin_chainstate = "yes"]) +AC_MSG_RESULT($build_bitcoin_chainstate) + AC_MSG_CHECKING([whether to build libraries]) AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test $build_bitcoin_libs = "yes"]) if test "$build_bitcoin_libs" = "yes"; then @@ -1995,6 +2008,7 @@ AC_SUBST(BITCOIN_TEST_NAME) AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(BITCOIN_UTIL_NAME) +AC_SUBST(BITCOIN_CHAINSTATE_NAME) AC_SUBST(BITCOIN_WALLET_TOOL_NAME) AC_SUBST(BITCOIN_MP_NODE_NAME) AC_SUBST(BITCOIN_MP_GUI_NAME) diff --git a/src/Makefile.am b/src/Makefile.am index a36f08be79c5..a9a4ebd3fe3b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,6 +148,10 @@ if BUILD_BITCOIN_UTIL bin_PROGRAMS += dash-util endif +if BUILD_BITCOIN_CHAINSTATE + bin_PROGRAMS += bitcoin-chainstate +endif + .PHONY: FORCE check-symbols check-security # dash core # BITCOIN_CORE_H = \ @@ -1157,6 +1161,101 @@ dash_util_LDADD = \ $(BACKTRACE_LIBS) dash_util_LDADD += $(BOOST_LIBS) + +# bitcoin-chainstate binary # +bitcoin_chainstate_SOURCES = \ + bitcoin-chainstate.cpp \ + arith_uint256.cpp \ + blockfilter.cpp \ + chain.cpp \ + chainparamsbase.cpp \ + chainparams.cpp \ + clientversion.cpp \ + coins.cpp \ + compat/glibcxx_sanity.cpp \ + compressor.cpp \ + consensus/merkle.cpp \ + consensus/tx_check.cpp \ + consensus/tx_verify.cpp \ + core_read.cpp \ + dbwrapper.cpp \ + deploymentinfo.cpp \ + deploymentstatus.cpp \ + flatfile.cpp \ + fs.cpp \ + hash.cpp \ + index/base.cpp \ + index/blockfilterindex.cpp \ + index/coinstatsindex.cpp \ + init/common.cpp \ + key.cpp \ + logging.cpp \ + netaddress.cpp \ + node/blockstorage.cpp \ + node/chainstate.cpp \ + node/coinstats.cpp \ + node/ui_interface.cpp \ + policy/feerate.cpp \ + policy/fees.cpp \ + policy/packages.cpp \ + policy/policy.cpp \ + policy/rbf.cpp \ + policy/settings.cpp \ + pow.cpp \ + primitives/block.cpp \ + primitives/transaction.cpp \ + pubkey.cpp \ + random.cpp \ + randomenv.cpp \ + scheduler.cpp \ + script/interpreter.cpp \ + script/script.cpp \ + script/script_error.cpp \ + script/sigcache.cpp \ + script/standard.cpp \ + shutdown.cpp \ + signet.cpp \ + support/cleanse.cpp \ + support/lockedpool.cpp \ + sync.cpp \ + threadinterrupt.cpp \ + timedata.cpp \ + txdb.cpp \ + txmempool.cpp \ + uint256.cpp \ + util/asmap.cpp \ + util/bytevectorhash.cpp \ + util/getuniquepath.cpp \ + util/hasher.cpp \ + util/moneystr.cpp \ + util/rbf.cpp \ + util/serfloat.cpp \ + util/settings.cpp \ + util/strencodings.cpp \ + util/syscall_sandbox.cpp \ + util/system.cpp \ + util/thread.cpp \ + util/threadnames.cpp \ + util/time.cpp \ + util/tokenpipe.cpp \ + validation.cpp \ + validationinterface.cpp \ + versionbits.cpp \ + warnings.cpp +bitcoin_chainstate_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +bitcoin_chainstate_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +bitcoin_chainstate_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) +bitcoin_chainstate_LDADD = \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBUNIVALUE) \ + $(LIBSECP256K1) \ + $(LIBLEVELDB) \ + $(LIBLEVELDB_SSE42) \ + $(LIBMEMENV) + +# Required for obj/build.h to be generated first. +# More details: https://www.gnu.org/software/automake/manual/html_node/Built-Sources-Example.html +bitcoin_chainstate-clientversion.$(OBJEXT): obj/build.h # # dashconsensus library # diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp new file mode 100644 index 000000000000..f93197350d01 --- /dev/null +++ b/src/bitcoin-chainstate.cpp @@ -0,0 +1,262 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +// The bitcoin-chainstate executable serves to surface the dependencies required +// by a program wishing to use Bitcoin Core's consensus engine as it is right +// now. +// +// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable, +// it may diverge from Bitcoin Core's coding style. +// +// It is part of the libbitcoinkernel project. + +#include +#include +#include +#include +#include +#include +#include +#include