From afcf3d148e64c6b0afd5eaf27963ef3ab8e838d1 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 9 Apr 2026 22:14:58 +0300 Subject: [PATCH 1/5] spork: remove SPORK_3_INSTANTSEND_BLOCK_FILTERING and SPORK_9_SUPERBLOCKS_ENABLED Both sporks have been effectively permanent fixtures of the network and their gating logic is no longer needed. Drop the enum entries, spork definitions, the AreSuperblocksEnabled() helper, and all associated branches in governance, InstantSend, masternode payments and mining RPC. getblocktemplate now always reports superblocks_enabled as true. Functional tests are updated to stop toggling the removed sporks; feature_multikeysporks.py now uses SPORK_19_CHAINLOCKS_ENABLED as its second spork under test. Co-Authored-By: Claude Opus 4.6 --- src/governance/governance.cpp | 5 --- src/governance/governance.h | 2 - src/instantsend/instantsend.cpp | 4 -- src/masternode/payments.cpp | 45 +++++++------------ src/rpc/mining.cpp | 12 +++-- src/spork.h | 6 +-- test/functional/feature_governance.py | 1 - test/functional/feature_governance_cl.py | 1 - test/functional/feature_mnehf.py | 1 - test/functional/feature_multikeysporks.py | 4 +- .../test_framework/test_framework.py | 1 - 11 files changed, 23 insertions(+), 59 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index f72c8d837846..c3e21269b650 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1490,8 +1490,3 @@ std::vector> CGovernanceManager::GetApp return ret; } - -bool AreSuperblocksEnabled(const CSporkManager& sporkman) -{ - return sporkman.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED); -} diff --git a/src/governance/governance.h b/src/governance/governance.h index fa048b40d9d2..0c1a025e8fcb 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -444,6 +444,4 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(cs_store); }; -bool AreSuperblocksEnabled(const CSporkManager& sporkman); - #endif // BITCOIN_GOVERNANCE_GOVERNANCE_H diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 26d0e3f90559..29afd31f9dda 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -474,10 +474,6 @@ 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; - } return true; } diff --git a/src/masternode/payments.cpp b/src/masternode/payments.cpp index 72fb0cff9b84..01a379b1d1b8 100644 --- a/src/masternode/payments.cpp +++ b/src/masternode/payments.cpp @@ -241,17 +241,6 @@ bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const int nBlo // we are synced and possibly on a superblock now - if (!AreSuperblocksEnabled(m_sporkman)) { - // should NOT allow superblocks at all, when superblocks are disabled - // revert to block reward limits in this case - LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Superblocks are disabled, no superblocks allowed\n", __func__); - if(!isBlockRewardValueMet) { - strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, superblocks are disabled", - nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); - } - return isBlockRewardValueMet; - } - if (!check_superblock) return true; const auto tip_mn_list = m_dmnman.GetListAtChainTip(); @@ -308,26 +297,23 @@ bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CB } // superblocks started + if (!check_superblock) return true; - if (AreSuperblocksEnabled(m_sporkman)) { - if (!check_superblock) return true; - const auto tip_mn_list = m_dmnman.GetListAtChainTip(); - if (m_govman.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) { - if (m_govman.IsValidSuperblock(m_chainman.ActiveChain(), tip_mn_list, txNew, nBlockHeight, - blockSubsidy + feeReward)) { - LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Valid superblock at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */ - // continue validation, should also pay MN - } else { - LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid superblock detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */ - // should NOT allow such superblocks, when superblocks are enabled - return false; - } + const auto tip_mn_list = m_dmnman.GetListAtChainTip(); + if (m_govman.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) { + if (m_govman.IsValidSuperblock(m_chainman.ActiveChain(), tip_mn_list, txNew, nBlockHeight, + blockSubsidy + feeReward)) { + LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Valid superblock at height %d: %s", /* Continued */ + __func__, nBlockHeight, txNew.ToString()); + // continue validation, should also pay MN } else { - LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- No triggered superblock detected at height %d\n", __func__, nBlockHeight); + LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid superblock detected at height %d: %s", /* Continued */ + __func__, nBlockHeight, txNew.ToString()); + return false; } } else { - // should NOT allow superblocks at all, when superblocks are disabled - LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Superblocks are disabled, no superblocks allowed\n", __func__); + LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- No triggered superblock detected at height %d\n", + __func__, nBlockHeight); } return true; @@ -338,10 +324,9 @@ void CMNPaymentsProcessor::FillBlockPayments(CMutableTransaction& txNew, const C { int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; - // only create superblocks if spork is enabled AND if superblock is actually triggered - // (height should be validated inside) + // Only create superblocks when one is actually triggered. const auto tip_mn_list = m_dmnman.GetListAtChainTip(); - if (AreSuperblocksEnabled(m_sporkman) && m_govman.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) { + if (m_govman.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) { LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Triggered superblock creation at height %d\n", __func__, nBlockHeight); m_govman.GetSuperblockPayments(tip_mn_list, nBlockHeight, voutSuperblockPaymentsRet); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index ea13095ed37f..bfff5ebee15f 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -731,12 +731,10 @@ static RPCHelpMan getblocktemplate() } } - // next bock is a superblock and we need governance info to correctly construct it - CHECK_NONFATAL(node.sporkman); - if (AreSuperblocksEnabled(*node.sporkman) - && !node.mn_sync->IsSynced() - && CSuperblock::IsValidBlockHeight(active_chain.Height() + 1)) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is syncing with network..."); + if (!node.mn_sync->IsSynced() && CSuperblock::IsValidBlockHeight(active_chain.Height() + 1)) { + // Next block is a superblock but we need governance info to correctly construct it. + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is syncing with network..."); + } static unsigned int nTransactionsUpdatedLast; const CTxMemPool& mempool = EnsureMemPool(node); @@ -962,7 +960,7 @@ static RPCHelpMan getblocktemplate() } result.pushKV("superblock", superblockObjArray); result.pushKV("superblocks_started", pindexPrev->nHeight + 1 > consensusParams.nSuperblockStartBlock); - result.pushKV("superblocks_enabled", AreSuperblocksEnabled(*node.sporkman)); + result.pushKV("superblocks_enabled", true); result.pushKV("coinbase_payload", HexStr(pblock->vtx[0]->vExtraPayload)); diff --git a/src/spork.h b/src/spork.h index b6e24619b9a1..1f9da2b74804 100644 --- a/src/spork.h +++ b/src/spork.h @@ -37,8 +37,6 @@ class CSporkManager; */ enum SporkId : int32_t { SPORK_2_INSTANTSEND_ENABLED = 10001, - SPORK_3_INSTANTSEND_BLOCK_FILTERING = 10002, - SPORK_9_SUPERBLOCKS_ENABLED = 10008, SPORK_17_QUORUM_DKG_ENABLED = 10016, SPORK_19_CHAINLOCKS_ENABLED = 10018, SPORK_21_QUORUM_ALL_CONNECTED = 10020, @@ -69,10 +67,8 @@ struct CSporkDef }; #define MAKE_SPORK_DEF(name, defaultValue) CSporkDef{name, defaultValue, #name} -[[maybe_unused]] static constexpr std::array sporkDefs = { +[[maybe_unused]] static constexpr std::array sporkDefs = { MAKE_SPORK_DEF(SPORK_2_INSTANTSEND_ENABLED, 4070908800ULL), // OFF - MAKE_SPORK_DEF(SPORK_3_INSTANTSEND_BLOCK_FILTERING, 4070908800ULL), // OFF - MAKE_SPORK_DEF(SPORK_9_SUPERBLOCKS_ENABLED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_17_QUORUM_DKG_ENABLED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_19_CHAINLOCKS_ENABLED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_21_QUORUM_ALL_CONNECTED, 4070908800ULL), // OFF diff --git a/test/functional/feature_governance.py b/test/functional/feature_governance.py index 925ac0b1ccc6..f76ede05088b 100755 --- a/test/functional/feature_governance.py +++ b/test/functional/feature_governance.py @@ -89,7 +89,6 @@ def run_test(self): self.expected_v20_budget = satoshi_round("18.57142860") self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 4070908800) - self.nodes[0].sporkupdate("SPORK_9_SUPERBLOCKS_ENABLED", 0) self.wait_for_sporks_same() assert_equal(len(self.nodes[0].gobject("list-prepared")), 0) diff --git a/test/functional/feature_governance_cl.py b/test/functional/feature_governance_cl.py index 9b6ec2bca904..239f9734f6d0 100755 --- a/test/functional/feature_governance_cl.py +++ b/test/functional/feature_governance_cl.py @@ -55,7 +55,6 @@ def run_test(self): self.sync_blocks() self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash()) - self.nodes[0].sporkupdate("SPORK_9_SUPERBLOCKS_ENABLED", 0) self.wait_for_sporks_same() # Move to the superblock cycle start block diff --git a/test/functional/feature_mnehf.py b/test/functional/feature_mnehf.py index 952ee95ca40e..40b9631f07de 100755 --- a/test/functional/feature_mnehf.py +++ b/test/functional/feature_mnehf.py @@ -88,7 +88,6 @@ def set_sporks(self): self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", spork_enabled) self.nodes[0].sporkupdate("SPORK_19_CHAINLOCKS_ENABLED", spork_disabled) - self.nodes[0].sporkupdate("SPORK_3_INSTANTSEND_BLOCK_FILTERING", spork_disabled) self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", spork_disabled) self.wait_for_sporks_same() diff --git a/test/functional/feature_multikeysporks.py b/test/functional/feature_multikeysporks.py index 999622b1e6d4..e65b99303408 100755 --- a/test/functional/feature_multikeysporks.py +++ b/test/functional/feature_multikeysporks.py @@ -124,10 +124,10 @@ def test_spork(self, spork_name, final_value): def run_test(self): self.test_spork('SPORK_2_INSTANTSEND_ENABLED', 2) - self.test_spork('SPORK_3_INSTANTSEND_BLOCK_FILTERING', 3) + self.test_spork('SPORK_19_CHAINLOCKS_ENABLED', 3) for node in self.nodes: assert self.get_test_spork_value(node, 'SPORK_2_INSTANTSEND_ENABLED') == 2 - assert self.get_test_spork_value(node, 'SPORK_3_INSTANTSEND_BLOCK_FILTERING') == 3 + assert self.get_test_spork_value(node, 'SPORK_19_CHAINLOCKS_ENABLED') == 3 if __name__ == '__main__': diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index de0e4ca70501..b1bef6cd952c 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1846,7 +1846,6 @@ def setup_network(self): # Enable InstantSend (including block filtering) and ChainLocks by default self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0) - self.nodes[0].sporkupdate("SPORK_3_INSTANTSEND_BLOCK_FILTERING", 0) self.nodes[0].sporkupdate("SPORK_19_CHAINLOCKS_ENABLED", 0) self.wait_for_sporks_same() self.bump_mocktime(1) From ee9087995038b180aa10dd9ea27680241b22e0ae Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 9 Apr 2026 22:15:11 +0300 Subject: [PATCH 2/5] doc: add release notes for SPORK_3/SPORK_9 removal Co-Authored-By: Claude Opus 4.6 --- doc/release-notes-7278.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 doc/release-notes-7278.md diff --git a/doc/release-notes-7278.md b/doc/release-notes-7278.md new file mode 100644 index 000000000000..eeaa06fd7167 --- /dev/null +++ b/doc/release-notes-7278.md @@ -0,0 +1,13 @@ +Removed Sporks +-------------- + +* `SPORK_3_INSTANTSEND_BLOCK_FILTERING` and `SPORK_9_SUPERBLOCKS_ENABLED` have + been removed. The behaviours they gated (InstantSend conflicting-block + rejection and superblock payments) are now always enabled. These sporks will + no longer appear in the output of the `spork` RPC. + +Updated RPCs +------------ + +* `getblocktemplate` now always reports `superblocks_enabled` as `true`. The + field is retained for backwards compatibility. From 3d9e80e56aa47cc311c445687254936abe8b696f Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 10 Apr 2026 11:06:56 +0700 Subject: [PATCH 3/5] refactor: cleanup unused members, includes and forward declaration after sporks removal --- src/evo/chainhelper.cpp | 5 ++--- src/evo/chainhelper.h | 4 +--- src/governance/governance.cpp | 3 ++- src/governance/governance.h | 1 - src/masternode/payments.h | 7 ++----- src/node/chainstate.cpp | 2 +- src/rpc/mining.cpp | 6 ++---- test/functional/feature_governance_cl.py | 2 -- 8 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/evo/chainhelper.cpp b/src/evo/chainhelper.cpp index 5c1f19a508d0..63c6fdafd2a7 100644 --- a/src/evo/chainhelper.cpp +++ b/src/evo/chainhelper.cpp @@ -18,13 +18,12 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmn 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 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, mn_sync)}, 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..13561012a98b 100644 --- a/src/evo/chainhelper.h +++ b/src/evo/chainhelper.h @@ -19,7 +19,6 @@ class CMasternodeSync; class CMNHFManager; class CMNPaymentsProcessor; class CSpecialTxProcessor; -class CSporkManager; class CTransaction; class uint256; struct CCreditPool; @@ -61,8 +60,7 @@ class CChainstateHelper 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 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 c3e21269b650..0123bba56b2b 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -11,16 +11,17 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include #include +#include #include #include #include diff --git a/src/governance/governance.h b/src/governance/governance.h index 0c1a025e8fcb..cb43b3bbd452 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -40,7 +40,6 @@ class CGovernanceObject; class CGovernanceVote; class CMasternodeMetaMan; class CMasternodeSync; -class CSporkManager; class CSuperblock; class UniValue; diff --git a/src/masternode/payments.h b/src/masternode/payments.h index a4df891c8c1a..358d58d00d44 100644 --- a/src/masternode/payments.h +++ b/src/masternode/payments.h @@ -17,7 +17,6 @@ class CGovernanceManager; class ChainstateManager; class CMasternodeSync; class CTransaction; -class CSporkManager; class CTxOut; struct CMutableTransaction; @@ -38,7 +37,6 @@ class CMNPaymentsProcessor const ChainstateManager& m_chainman; const Consensus::Params& m_consensus_params; const CMasternodeSync& m_mn_sync; - const CSporkManager& m_sporkman; private: [[nodiscard]] bool GetBlockTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, @@ -51,9 +49,8 @@ class CMNPaymentsProcessor 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} {} + const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync) : + m_dmnman{dmnman}, m_govman{govman}, m_chainman{chainman}, m_consensus_params{consensus_params}, m_mn_sync{mn_sync} {} 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 98d4dc21d199..289720d0949f 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, mn_sync, chainlocks, *(llmq_ctx->qman)); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index bfff5ebee15f..1143ccf852a4 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -14,10 +14,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -42,10 +44,6 @@ #include #include -#include -#include -#include - #include #include diff --git a/test/functional/feature_governance_cl.py b/test/functional/feature_governance_cl.py index 239f9734f6d0..169266178f20 100755 --- a/test/functional/feature_governance_cl.py +++ b/test/functional/feature_governance_cl.py @@ -55,8 +55,6 @@ def run_test(self): self.sync_blocks() self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash()) - self.wait_for_sporks_same() - # Move to the superblock cycle start block n = sb_cycle - self.nodes[0].getblockcount() % sb_cycle if n > 0: From b2cde07e62e55b9edee3d9d78ac0fcfce5bd0b66 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 10 Apr 2026 12:22:50 +0300 Subject: [PATCH 4/5] test, doc: address review suggestions for spork removal - Update release notes to clarify that SPORK_3 and SPORK_9 were already effectively always enabled on mainnet but continued to gate behavior on test networks. - Add explicit wait_until(IsSynced) in feature_dip3_deterministicmns before the getblocktemplate loop, since -budgetparams=10:10:10 sets superblock cycle to 10 blocks and getblocktemplate now unconditionally checks IsSynced() at superblock heights. - Fix stale comment in DashTestFramework that still referenced "block filtering" after SPORK_3 removal. Co-Authored-By: Claude Opus 4.6 --- doc/release-notes-7278.md | 8 +++++--- test/functional/feature_dip3_deterministicmns.py | 1 + test/functional/test_framework/test_framework.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/release-notes-7278.md b/doc/release-notes-7278.md index eeaa06fd7167..f5e967489dcc 100644 --- a/doc/release-notes-7278.md +++ b/doc/release-notes-7278.md @@ -2,9 +2,11 @@ Removed Sporks -------------- * `SPORK_3_INSTANTSEND_BLOCK_FILTERING` and `SPORK_9_SUPERBLOCKS_ENABLED` have - been removed. The behaviours they gated (InstantSend conflicting-block - rejection and superblock payments) are now always enabled. These sporks will - no longer appear in the output of the `spork` RPC. + been removed. These sporks were already effectively always enabled on mainnet + but continued to gate behavior on test networks (testnet, regtest, and devnet). + The associated functionality (InstantSend conflicting-block rejection and + superblock payments) is now permanently enabled across all networks, and the + sporks will no longer appear in the `spork` RPC output. Updated RPCs ------------ diff --git a/test/functional/feature_dip3_deterministicmns.py b/test/functional/feature_dip3_deterministicmns.py index 0d60ec282f63..74096321588b 100755 --- a/test/functional/feature_dip3_deterministicmns.py +++ b/test/functional/feature_dip3_deterministicmns.py @@ -173,6 +173,7 @@ def run_test(self): multisig = self.nodes[0].createmultisig(1, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] self.update_mn_payee(mns[0], multisig) + self.wait_until(lambda: self.nodes[0].mnsync("status")['IsSynced']) found_multisig_payee = False for _ in range(len(mns)): bt = self.nodes[0].getblocktemplate() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index b1bef6cd952c..f63d486717e4 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1844,7 +1844,7 @@ def setup_network(self): for i in range(1, num_simple_nodes): force_finish_mnsync(self.nodes[i]) - # Enable InstantSend (including block filtering) and ChainLocks by default + # Enable InstantSend and ChainLocks by default self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0) self.nodes[0].sporkupdate("SPORK_19_CHAINLOCKS_ENABLED", 0) self.wait_for_sporks_same() From 18b990ba750c705404b964c4639669e2f39771e5 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sat, 11 Apr 2026 13:27:59 +0300 Subject: [PATCH 5/5] refactor: move superblock sync check inside !IsTestChain() guard Move the superblock sync check in getblocktemplate inside the existing !IsTestChain() block, matching the pattern used by the IBD check. Previously this was gated by SPORK_9 which was off on test networks; after spork removal the check would run unconditionally, requiring full masternode sync on test networks at superblock heights. Co-Authored-By: Claude Opus 4.6 --- doc/release-notes-7278.md | 4 ++++ src/rpc/mining.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/release-notes-7278.md b/doc/release-notes-7278.md index f5e967489dcc..9adbdcd16591 100644 --- a/doc/release-notes-7278.md +++ b/doc/release-notes-7278.md @@ -13,3 +13,7 @@ Updated RPCs * `getblocktemplate` now always reports `superblocks_enabled` as `true`. The field is retained for backwards compatibility. + +* `getblocktemplate` now requires a fully synced node at superblock heights. + This check is skipped on test networks. Previously it was gated by + `SPORK_9_SUPERBLOCKS_ENABLED` which was already always active on mainnet. diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 1143ccf852a4..385240ee1e93 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -727,11 +727,11 @@ static RPCHelpMan getblocktemplate() if (active_chainstate.IsInitialBlockDownload()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); } - } - if (!node.mn_sync->IsSynced() && CSuperblock::IsValidBlockHeight(active_chain.Height() + 1)) { - // Next block is a superblock but we need governance info to correctly construct it. - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is syncing with network..."); + if (!node.mn_sync->IsSynced() && CSuperblock::IsValidBlockHeight(active_chain.Height() + 1)) { + // Next block is a superblock but we need governance info to correctly construct it. + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is syncing with network..."); + } } static unsigned int nTransactionsUpdatedLast;