From d900ab3dd4485d9c76bce0a772811caad1f75e19 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 10 Oct 2024 16:03:30 +0700 Subject: [PATCH 01/13] fix: assertion in Credit Pool validation during connecting blocks It can happen because now order of activation of hardforks v20, mn_rr, dip3 can be changed by using testactivationheight on Regtest and no more guarantee about this assertions --- src/evo/specialtxman.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 5ed1e08c10ba0..969efa2703055 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -267,16 +267,20 @@ bool CSpecialTxProcessor::CheckCreditPoolDiffForBlock(const CBlock& block, const try { if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0003)) return true; + if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0008)) return true; if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20)) return true; auto creditPoolDiff = GetCreditPoolDiffForBlock(m_cpoolman, m_chainman.m_blockman, m_qman, block, pindex->pprev, m_consensus_params, blockSubsidy, state); if (!creditPoolDiff.has_value()) return false; // If we get there we have v20 activated and credit pool amount must be included in block CbTx + if (block.vtx.empty()) { + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-missing-cbtx"); + } const auto& tx = *block.vtx[0]; - assert(tx.IsCoinBase()); - assert(tx.IsSpecialTxVersion()); - assert(tx.nType == TRANSACTION_COINBASE); + if (!tx.IsCoinBase() || !tx.IsSpecialTxVersion() || tx.nType != TRANSACTION_COINBASE) { + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-type"); + } const auto opt_cbTx = GetTxPayload(tx); if (!opt_cbTx) { From 1730ac36c2eba47a8cd1bd135d9337d6c35f913e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 21 Nov 2024 16:05:27 +0700 Subject: [PATCH 02/13] fix: add check that DIP0003 activated before retrieving CbTx for CreditPool --- src/evo/creditpool.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index abcf72650a412..e1b0bb3756694 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -114,17 +114,20 @@ void CCreditPoolManager::AddToCache(const uint256& block_hash, int height, const static std::optional GetBlockForCreditPool(const CBlockIndex* const block_index, const Consensus::Params& consensusParams) { + // There's no CbTx before DIP0003 activation + if (!DeploymentActiveAt(*block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) { + return std::nullopt; + } + CBlock block; if (!ReadBlockFromDisk(block, block_index, consensusParams)) { throw std::runtime_error("failed-getcbforblock-read"); } - assert(!block.vtx.empty()); - - // Should not fail if V20 (DIP0027) is active but it happens for RegChain (unit tests) - if (!block.vtx[0]->IsSpecialTxVersion()) return std::nullopt; - - assert(!block.vtx[0]->vExtraPayload.empty()); + if (block.vtx.empty() || block.vtx[0]->vExtraPayload.empty() || !block.vtx[0]->IsSpecialTxVersion()) { + LogPrintf("%s: ERROR: empty CbTx for CreditPool at height=%d\n", __func__, block_index->nHeight); + return std::nullopt; + } return block; } From 7ece22df94c1b083cb426ffe6d27745e5679a7a1 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 10 Oct 2024 15:33:54 +0700 Subject: [PATCH 03/13] docs: updated comment for DIP0003 activation on RegTest --- src/chainparams.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index eeab97c2c479f..2cc25399a6805 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -788,7 +788,8 @@ class CRegTestParams : public CChainParams { consensus.BIP147Height = 1; // Always active unless overridden consensus.CSVHeight = 1; // Always active unless overridden consensus.DIP0001Height = 1; // Always active unless overridden - consensus.DIP0003Height = 432; + consensus.DIP0003Height = 432; // Always active for DashTestFramework in functional tests (see dip3params) + // For unit tests and for BitcoinTestFramework is disabled due to missing quorum commitment for blocks created by helpers such as create_blocks consensus.DIP0003EnforcementHeight = 500; consensus.DIP0003EnforcementHash = uint256(); consensus.DIP0008Height = 1; // Always active unless overridden From 0ea0c2796e8c960c31b5f396e53e01569250aed1 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 9 Aug 2024 00:59:32 +0700 Subject: [PATCH 04/13] refactor: remove command line argument -bip147height, -dip8params bip147height is superseeded by -testactivationheight=bip147@height dip8params is superseeded by -testactivationheight=dip0008@height --- doc/release-notes-6325.md | 4 ++++ src/chainparams.cpp | 46 --------------------------------------- src/chainparamsbase.cpp | 2 -- 3 files changed, 4 insertions(+), 48 deletions(-) create mode 100644 doc/release-notes-6325.md diff --git a/doc/release-notes-6325.md b/doc/release-notes-6325.md new file mode 100644 index 0000000000000..84c02201d48b0 --- /dev/null +++ b/doc/release-notes-6325.md @@ -0,0 +1,4 @@ +Tests +----- + +- Command line arguments -dip8params, -bip147height are removed in favour of -testactivationheight. (dash#6325) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2cc25399a6805..d7f31a576259d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -844,8 +844,6 @@ class CRegTestParams : public CChainParams { UpdateActivationParametersFromArgs(args); UpdateDIP3ParametersFromArgs(args); - UpdateDIP8ParametersFromArgs(args); - UpdateBIP147ParametersFromArgs(args); UpdateBudgetParametersFromArgs(args); genesis = CreateGenesisBlock(1417713337, 1096447, 0x207fffff, 1, 50 * COIN); @@ -964,21 +962,6 @@ class CRegTestParams : public CChainParams { } void UpdateDIP3ParametersFromArgs(const ArgsManager& args); - /** - * Allows modifying the DIP8 activation height - */ - void UpdateDIP8Parameters(int nActivationHeight) - { - consensus.DIP0008Height = nActivationHeight; - } - void UpdateDIP8ParametersFromArgs(const ArgsManager& args); - - void UpdateBIP147Parameters(int nActivationHeight) - { - consensus.BIP147Height = nActivationHeight; - } - void UpdateBIP147ParametersFromArgs(const ArgsManager& args); - /** * Allows modifying the budget regtest parameters. */ @@ -1135,35 +1118,6 @@ void CRegTestParams::UpdateDIP3ParametersFromArgs(const ArgsManager& args) UpdateDIP3Parameters(nDIP3ActivationHeight, nDIP3EnforcementHeight); } -void CRegTestParams::UpdateDIP8ParametersFromArgs(const ArgsManager& args) -{ - if (!args.IsArgSet("-dip8params")) return; - - std::string strParams = args.GetArg("-dip8params", ""); - std::vector vParams = SplitString(strParams, ':'); - if (vParams.size() != 1) { - throw std::runtime_error("DIP8 parameters malformed, expecting "); - } - int nDIP8ActivationHeight; - if (!ParseInt32(vParams[0], &nDIP8ActivationHeight)) { - throw std::runtime_error(strprintf("Invalid activation height (%s)", vParams[0])); - } - LogPrintf("Setting DIP8 parameters to activation=%ld\n", nDIP8ActivationHeight); - UpdateDIP8Parameters(nDIP8ActivationHeight); -} - -void CRegTestParams::UpdateBIP147ParametersFromArgs(const ArgsManager& args) -{ - if (!args.IsArgSet("-bip147height")) return; - int nBIP147Height; - const std::string strParams = args.GetArg("-bip147height", ""); - if (!ParseInt32(strParams, &nBIP147Height)) { - throw std::runtime_error(strprintf("Invalid activation height (%s)", strParams)); - } - LogPrintf("Setting BIP147 parameters to activation=%lld\n", nBIP147Height); - UpdateBIP147Parameters(nBIP147Height); -} - void CRegTestParams::UpdateBudgetParametersFromArgs(const ArgsManager& args) { if (!args.IsArgSet("-budgetparams")) return; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index d7ef237c0eee8..755025c17e369 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -20,8 +20,6 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-budgetparams=::", "Override masternode, budget and superblock start heights (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-devnet=", "Use devnet chain with provided name", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-dip3params=:", "Override DIP3 activation and enforcement heights (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-dip8params=", "Override DIP8 activation height (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-bip147height=", "Override BIP147 activation height (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (bip147, bip34, dersig, cltv, csv, brr, dip0001, dip0008, v20, mn_rr). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-highsubsidyblocks=", "The number of blocks with a higher than normal subsidy to mine at the start of a chain. Block after that height will have fixed subsidy base. (default: 0, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-highsubsidyfactor=", "The factor to multiply the normal block subsidy by while in the highsubsidyblocks window of a chain (default: 1, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); From 03a532760dd32ef8fdbcb1ea3769003560d57047 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 22 Nov 2024 21:57:56 +0700 Subject: [PATCH 05/13] fix: intermittent error in feature_index_prune due to DKG influence --- test/functional/feature_index_prune.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py index dcb649362f820..7fa3a56c5c19f 100755 --- a/test/functional/feature_index_prune.py +++ b/test/functional/feature_index_prune.py @@ -11,16 +11,17 @@ ) from test_framework.governance import EXPECTED_STDERR_NO_GOV_PRUNE -DEPLOYMENT_ARG = "-testactivationheight=v20@3000" +# TODO: remove testactivationheight=v20@3000 when it will be activated from block 1 +DEPLOYMENT_ARGS = ["-testactivationheight=v20@3000", "-dip3params=3000:3000"] class FeatureIndexPruneTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 self.extra_args = [ - ["-fastprune", "-prune=1", "-blockfilterindex=1", DEPLOYMENT_ARG], - ["-fastprune", "-prune=1", "-coinstatsindex=1", DEPLOYMENT_ARG], - ["-fastprune", "-prune=1", "-blockfilterindex=1", "-coinstatsindex=1", DEPLOYMENT_ARG], - [DEPLOYMENT_ARG] + ["-fastprune", "-prune=1", "-blockfilterindex=1"] + DEPLOYMENT_ARGS, + ["-fastprune", "-prune=1", "-coinstatsindex=1"] + DEPLOYMENT_ARGS, + ["-fastprune", "-prune=1", "-blockfilterindex=1", "-coinstatsindex=1"] + DEPLOYMENT_ARGS, + [] + DEPLOYMENT_ARGS, ] def sync_index(self, height): @@ -51,7 +52,7 @@ def mine_batches(self, blocks): def restart_without_indices(self): for i in range(3): - self.restart_node(i, extra_args=["-fastprune", "-prune=1", DEPLOYMENT_ARG], expected_stderr=EXPECTED_STDERR_NO_GOV_PRUNE) + self.restart_node(i, extra_args=["-fastprune", "-prune=1"] + DEPLOYMENT_ARGS, expected_stderr=EXPECTED_STDERR_NO_GOV_PRUNE) self.reconnect_nodes() def run_test(self): @@ -110,7 +111,7 @@ def run_test(self): self.log.info("prune exactly up to the indices best blocks while the indices are disabled") for i in range(3): pruneheight_2 = self.nodes[i].pruneblockchain(1000) - assert_equal(pruneheight_2, 932) + assert_equal(pruneheight_2, 732) # Restart the nodes again with the indices activated self.restart_node(i, extra_args=self.extra_args[i], expected_stderr=EXPECTED_STDERR_NO_GOV_PRUNE) @@ -145,7 +146,7 @@ def run_test(self): for node in self.nodes[:2]: with node.assert_debug_log(['limited pruning to height 2489']): pruneheight_new = node.pruneblockchain(2500) - assert_equal(pruneheight_new, 2197) + assert_equal(pruneheight_new, 2125) self.log.info("ensure that prune locks don't prevent indices from failing in a reorg scenario") with self.nodes[0].assert_debug_log(['basic block filter index prune lock moved back to 2480']): From 31abe4463a996fbd9220f28a43c58a73499b7a9e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 27 Aug 2024 18:10:39 +0700 Subject: [PATCH 06/13] feat: re-bury DIP0024 with new height when quorums actually appeared For current implementation it is not important when exactly fork happened yet important to know when first rotating quorum formed --- src/chainparams.cpp | 6 +----- src/consensus/params.h | 4 +--- src/llmq/options.cpp | 14 +++++++------- src/llmq/options.h | 3 ++- src/test/evo_utils_tests.cpp | 20 ++++++++------------ 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d7f31a576259d..25ebd0815a09e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -189,7 +189,6 @@ class CMainParams : public CChainParams { consensus.DIP0008Height = 1088640; // 00000000000000112e41e4b3afda8b233b8cc07c532d2eac5de097b68358c43e consensus.BRRHeight = 1374912; // 000000000000000c5a124f3eccfbe6e17876dca79cec9e63dfa70d269113c926 consensus.DIP0020Height = 1516032; // 000000000000000f64ed3bd9af1078177ac026f6aa2677aa4d8beeae43be56cc - consensus.DIP0024Height = 1737792; // 0000000000000001342be9c0b75ad40c276beaad91616423c4d9cb101b3db438 consensus.DIP0024QuorumsHeight = 1738698; // 000000000000001aa25181e4c466e593992c98f9eb21c69ee757b8bb0af50244 consensus.V19Height = 1899072; // 0000000000000015e32e73052d663626327004c81c5c22cb8b42c361015c0eae consensus.V20Height = 1987776; // 000000000000001bf41cff06b76780050682ca29e61a91c391893d4745579777 @@ -386,7 +385,6 @@ class CTestNetParams : public CChainParams { consensus.DIP0008Height = 78800; // 000000000e9329d964d80e7dab2e704b43b6bd2b91fea1e9315d38932e55fb55 consensus.BRRHeight = 387500; // 0000001537dbfd09dea69f61c1f8b2afa27c8dc91c934e144797761c9f10367b consensus.DIP0020Height = 414100; // 000000cf961868662fbfbb5d1af6f1caa1809f6a4e390efe5f8cd3031adea668 - consensus.DIP0024Height = 769700; // 0000008d84e4efd890ae95c70a7a6126a70a80e5c19e4cb264a5b3469aeef172 consensus.DIP0024QuorumsHeight = 770730; // 0000003c43b3ae7fffe61278ca5537a0e256ebf4d709d45f0ab040271074d51e consensus.V19Height = 850100; // 000004728b8ff2a16b9d4eebb0fd61eeffadc9c7fe4b0ec0b5a739869401ab5b consensus.V20Height = 905100; // 0000020c5e0f86f385cbf8e90210de9a9fd63633f01433bf47a6b3227a2851fd @@ -558,12 +556,11 @@ class CDevNetParams : public CChainParams { consensus.DIP0008Height = 2; // DIP0008 activated immediately on devnet consensus.BRRHeight = 2; // BRR (realloc) activated immediately on devnet consensus.DIP0020Height = 2; // DIP0020 activated immediately on devnet - consensus.DIP0024Height = 2; // DIP0024 activated immediately on devnet consensus.DIP0024QuorumsHeight = 2; // DIP0024 activated immediately on devnet consensus.V19Height = 2; // V19 activated immediately on devnet consensus.V20Height = 2; // V20 activated immediately on devnet consensus.MN_RRHeight = 2; // MN_RR activated immediately on devnet - consensus.MinBIP9WarningHeight = 2 + 2016; // v19 activation height + miner confirmation window + consensus.MinBIP9WarningHeight = 2 + 2016; // mn_rr activation height + miner confirmation window consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1 consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes @@ -795,7 +792,6 @@ class CRegTestParams : public CChainParams { consensus.DIP0008Height = 1; // Always active unless overridden consensus.BRRHeight = 1; // Always active unless overridden consensus.DIP0020Height = 1; // Always active unless overridden - consensus.DIP0024Height = 900; consensus.DIP0024QuorumsHeight = 900; consensus.V19Height = 900; consensus.V20Height = 900; diff --git a/src/consensus/params.h b/src/consensus/params.h index 508d477c54f43..74a0c035d4ba7 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -137,8 +137,6 @@ struct Params { int BRRHeight; /** Block height at which DIP0020, DIP0021 and LLMQ_100_67 quorums become active */ int DIP0020Height; - /** Block height at which DIP0024 (Quorum Rotation) and decreased governance proposal fee becomes active */ - int DIP0024Height; /** Block height at which the first DIP0024 quorum was mined */ int DIP0024QuorumsHeight; /** Block height at which V19 (Basic BLS and EvoNodes) becomes active */ @@ -205,7 +203,7 @@ struct Params { case DEPLOYMENT_DIP0020: return DIP0020Height; case DEPLOYMENT_DIP0024: - return DIP0024Height; + return DIP0024QuorumsHeight; case DEPLOYMENT_BRR: return BRRHeight; case DEPLOYMENT_V19: diff --git a/src/llmq/options.cpp b/src/llmq/options.cpp index c1956e8a67a1b..d0723f7f12f29 100644 --- a/src/llmq/options.cpp +++ b/src/llmq/options.cpp @@ -116,26 +116,26 @@ std::map GetEnabledQuorumVvecSyncEntries() bool IsQuorumTypeEnabled(Consensus::LLMQType llmqType, gsl::not_null pindexPrev) { - return IsQuorumTypeEnabledInternal(llmqType, pindexPrev, std::nullopt, std::nullopt); + return IsQuorumTypeEnabledInternal(llmqType, pindexPrev, std::nullopt); } bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, gsl::not_null pindexPrev, - std::optional optDIP0024IsActive, std::optional optHaveDIP0024Quorums) + std::optional optIsDIP0024Active) { const Consensus::Params& consensusParams = Params().GetConsensus(); - const bool fDIP0024IsActive{optDIP0024IsActive.value_or(DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DIP0024))}; - const bool fHaveDIP0024Quorums{optHaveDIP0024Quorums.value_or(pindexPrev->nHeight >= consensusParams.DIP0024QuorumsHeight)}; + const bool fDIP0024IsActive{ + optIsDIP0024Active.value_or(DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DIP0024))}; switch (llmqType) { case Consensus::LLMQType::LLMQ_DEVNET: return true; case Consensus::LLMQType::LLMQ_50_60: - return !fDIP0024IsActive || !fHaveDIP0024Quorums || Params().NetworkIDString() == CBaseChainParams::TESTNET || + return !fDIP0024IsActive || Params().NetworkIDString() == CBaseChainParams::TESTNET || Params().NetworkIDString() == CBaseChainParams::DEVNET; case Consensus::LLMQType::LLMQ_TEST_INSTANTSEND: - return !fDIP0024IsActive || !fHaveDIP0024Quorums || - consensusParams.llmqTypeDIP0024InstantSend == Consensus::LLMQType::LLMQ_TEST_INSTANTSEND; + return !fDIP0024IsActive || + consensusParams.llmqTypeDIP0024InstantSend == Consensus::LLMQType::LLMQ_TEST_INSTANTSEND; case Consensus::LLMQType::LLMQ_TEST: case Consensus::LLMQType::LLMQ_TEST_PLATFORM: case Consensus::LLMQType::LLMQ_400_60: diff --git a/src/llmq/options.h b/src/llmq/options.h index f78f2362fdfc0..de220f499c014 100644 --- a/src/llmq/options.h +++ b/src/llmq/options.h @@ -44,7 +44,8 @@ bool IsWatchQuorumsEnabled(); std::map GetEnabledQuorumVvecSyncEntries(); bool IsQuorumTypeEnabled(Consensus::LLMQType llmqType, gsl::not_null pindexPrev); -bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, gsl::not_null pindexPrev, std::optional optDIP0024IsActive, std::optional optHaveDIP0024Quorums); +bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, gsl::not_null pindexPrev, + std::optional optHaveDIP0024Quorums); std::vector GetEnabledQuorumTypes(gsl::not_null pindex); std::vector> GetEnabledQuorumParams(gsl::not_null pindex); diff --git a/src/test/evo_utils_tests.cpp b/src/test/evo_utils_tests.cpp index 4d555492469b4..65c405f9bda22 100644 --- a/src/test/evo_utils_tests.cpp +++ b/src/test/evo_utils_tests.cpp @@ -21,18 +21,14 @@ void Test(NodeContext& node) using namespace llmq; auto tip = node.chainman->ActiveTip(); const auto& consensus_params = Params().GetConsensus(); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeDIP0024InstantSend, tip, false, false), false); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeDIP0024InstantSend, tip, true, false), true); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeDIP0024InstantSend, tip, true, true), true); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, tip, false, false), true); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, tip, true, false), true); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, tip, true, true), true); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, tip, false, false), Params().IsTestChain()); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, tip, true, false), Params().IsTestChain()); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, tip, true, true), Params().IsTestChain()); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, tip, false, false), true); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, tip, true, false), true); - BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, tip, true, true), true); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeDIP0024InstantSend, tip, false), false); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeDIP0024InstantSend, tip, true), true); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, tip, false), true); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, tip, true), true); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, tip, false), Params().IsTestChain()); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, tip, true), Params().IsTestChain()); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, tip, false), true); + BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, tip, true), true); } BOOST_FIXTURE_TEST_CASE(utils_IsQuorumTypeEnabled_tests_regtest, RegTestingSetup) From f3a96ce70d361e5e7558dc403e00798db2521edb Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 9 Oct 2024 14:42:14 +0700 Subject: [PATCH 07/13] feat: put DIP0024 activation to block 1 on RegTest --- src/chainparams.cpp | 4 +- src/chainparamsbase.cpp | 2 +- src/test/util/setup_common.cpp | 4 +- .../feature_dip4_coinbasemerkleroots.py | 3 +- test/functional/feature_llmq_rotation.py | 7 +-- test/functional/feature_llmq_signing.py | 59 ++++++++++--------- test/functional/rpc_blockchain.py | 2 +- 7 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 25ebd0815a09e..0dff08db58f21 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -792,7 +792,7 @@ class CRegTestParams : public CChainParams { consensus.DIP0008Height = 1; // Always active unless overridden consensus.BRRHeight = 1; // Always active unless overridden consensus.DIP0020Height = 1; // Always active unless overridden - consensus.DIP0024QuorumsHeight = 900; + consensus.DIP0024QuorumsHeight = 1; // Always have dip0024 quorums unless overridden consensus.V19Height = 900; consensus.V20Height = 900; consensus.MN_RRHeight = 900; @@ -1023,6 +1023,8 @@ static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& conse consensus.DIP0001Height = int{height}; } else if (name == "dip0008") { consensus.DIP0008Height = int{height}; + } else if (name == "dip0024") { + consensus.DIP0024QuorumsHeight = int{height}; } else if (name == "v20") { consensus.V20Height = int{height}; } else if (name == "mn_rr") { diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 755025c17e369..1a05491f065ba 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -20,7 +20,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-budgetparams=::", "Override masternode, budget and superblock start heights (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-devnet=", "Use devnet chain with provided name", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-dip3params=:", "Override DIP3 activation and enforcement heights (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (bip147, bip34, dersig, cltv, csv, brr, dip0001, dip0008, v20, mn_rr). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (bip147, bip34, dersig, cltv, csv, brr, dip0001, dip0008, dip0024, v20, mn_rr). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-highsubsidyblocks=", "The number of blocks with a higher than normal subsidy to mine at the start of a chain. Block after that height will have fixed subsidy base. (default: 0, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-highsubsidyfactor=", "The factor to multiply the normal block subsidy by while in the highsubsidyblocks window of a chain (default: 1, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-llmqchainlocks=", "Override the default LLMQ type used for ChainLocks. Allows using ChainLocks with smaller LLMQs. (default: llmq_devnet, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 487bb1bb565aa..6057d89174378 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -353,9 +353,9 @@ TestChainSetup::TestChainSetup(int num_blocks, const std::vector& e /* TestChainDIP3BeforeActivationSetup */ { 430, uint256S("0x0bcefaa33fec56cd84d05d0e76cd6a78badcc20f627d91903646de6a07930a14") }, /* TestChainBRRBeforeActivationSetup */ - { 497, uint256S("0x23c31820ec5160b7181bfdf328e2b76cd12c9fa4544d892b7f01e74dd6220849") }, + { 497, uint256S("0x3c71d807d28b9b813434eb0679ec3d5bcf424c20088cf578f3757521c3e3eded") }, /* TestChainV19BeforeActivationSetup */ - { 894, uint256S("0x2885cf0fe8fdf29803b6c65002ba2570ff011531d8ea92be312a85d655e00c51") }, + { 894, uint256S("0x3f031e5cceade15bdfa559ddecb2ccb2b8d17083bdfd871a9d23b17d04b15292") }, } }; diff --git a/test/functional/feature_dip4_coinbasemerkleroots.py b/test/functional/feature_dip4_coinbasemerkleroots.py index ac45de11763de..d0756230f0f00 100755 --- a/test/functional/feature_dip4_coinbasemerkleroots.py +++ b/test/functional/feature_dip4_coinbasemerkleroots.py @@ -18,6 +18,7 @@ from test_framework.util import assert_equal DIP0008_HEIGHT = 432 +DIP0024_HEIGHT = 900 # TODO: this helper used in many tests, find a new home for it class TestP2PConn(P2PInterface): @@ -43,7 +44,7 @@ def getmnlistdiff(self, baseBlockHash, blockHash): class LLMQCoinbaseCommitmentsTest(DashTestFramework): def set_test_params(self): - self.extra_args = [[ f'-testactivationheight=dip0008@{DIP0008_HEIGHT}', "-vbparams=testdummy:999999999999:999999999999" ]] * 4 + self.extra_args = [[ f'-testactivationheight=dip0008@{DIP0008_HEIGHT}', f'-testactivationheight=dip0024@{DIP0024_HEIGHT}', "-vbparams=testdummy:999999999999:999999999999" ]] * 4 self.set_dash_test_params(4, 3, extra_args = self.extra_args) def run_test(self): # No IS or Chainlocks in this test diff --git a/test/functional/feature_llmq_rotation.py b/test/functional/feature_llmq_rotation.py index 3d933eb8e4076..e537c7500bd7b 100755 --- a/test/functional/feature_llmq_rotation.py +++ b/test/functional/feature_llmq_rotation.py @@ -81,11 +81,9 @@ def run_test(self): h_0 = self.mine_quorum() h_100_0 = QuorumId(100, int(h_0, 16)) h_106_0 = QuorumId(106, int(h_0, 16)) - h_104_0 = QuorumId(104, int(h_0, 16)) h_1 = self.mine_quorum() h_100_1 = QuorumId(100, int(h_1, 16)) h_106_1 = QuorumId(106, int(h_1, 16)) - h_104_1 = QuorumId(104, int(h_1, 16)) self.log.info("Mine single block, wait for chainlock") self.generate(self.nodes[0], 1, sync_fun=self.no_op) @@ -103,10 +101,10 @@ def run_test(self): assert_equal(dkg_info['active_dkgs'], 0) nonzero_dkgs += dkg_info['active_dkgs'] assert_equal(dkg_info['next_dkg'], next_dkg) - assert_equal(nonzero_dkgs, 11) # 2 quorums 4 nodes each and 1 quorum of 3 nodes + assert_equal(nonzero_dkgs, 7) # 1 quorums 4 nodes and 1 quorum of 3 nodes expectedDeleted = [] - expectedNew = [h_100_0, h_106_0, h_104_0, h_100_1, h_106_1, h_104_1] + expectedNew = [h_100_0, h_106_0, h_100_1, h_106_1] quorumList = self.test_getmnlistdiff_quorums(b_h_0, b_h_1, {}, expectedDeleted, expectedNew, testQuorumsCLSigs=False) projected_activation_height = 900 @@ -363,7 +361,6 @@ def get_llmq_size(self, llmq_type): 100: 4, # In this test size for llmqType 100 is overwritten to 4 102: 3, 103: 4, - 104: 4, # In this test size for llmqType 104 is overwritten to 4 106: 3 }.get(llmq_type, -1) diff --git a/test/functional/feature_llmq_signing.py b/test/functional/feature_llmq_signing.py index 8206e7dde41d3..88c941ad81b28 100755 --- a/test/functional/feature_llmq_signing.py +++ b/test/functional/feature_llmq_signing.py @@ -16,6 +16,7 @@ from test_framework.util import assert_equal, assert_raises_rpc_error, force_finish_mnsync, wait_until_helper +q_type=100 class LLMQSigningTest(DashTestFramework): def set_test_params(self): self.set_dash_test_params(6, 5) @@ -43,11 +44,11 @@ def run_test(self): def check_sigs(hasrecsigs, isconflicting1, isconflicting2): for mn in self.mninfo: - if mn.node.quorum("hasrecsig", 104, id, msgHash) != hasrecsigs: + if mn.node.quorum("hasrecsig", q_type, id, msgHash) != hasrecsigs: return False - if mn.node.quorum("isconflicting", 104, id, msgHash) != isconflicting1: + if mn.node.quorum("isconflicting", q_type, id, msgHash) != isconflicting1: return False - if mn.node.quorum("isconflicting", 104, id, msgHashConflict) != isconflicting2: + if mn.node.quorum("isconflicting", q_type, id, msgHashConflict) != isconflicting2: return False return True @@ -61,24 +62,24 @@ def assert_sigs_nochange(hasrecsigs, isconflicting1, isconflicting2, timeout): wait_for_sigs(False, False, False, 1) # Sign first share without any optional parameter, should not result in recovered sig - self.mninfo[0].node.quorum("sign", 104, id, msgHash) + self.mninfo[0].node.quorum("sign", q_type, id, msgHash) assert_sigs_nochange(False, False, False, 3) # Sign second share and test optional quorumHash parameter, should not result in recovered sig # 1. Providing an invalid quorum hash should fail and cause no changes for sigs - assert not self.mninfo[1].node.quorum("sign", 104, id, msgHash, msgHash) + assert not self.mninfo[1].node.quorum("sign", q_type, id, msgHash, msgHash) assert_sigs_nochange(False, False, False, 3) # 2. Providing a valid quorum hash should succeed and cause no changes for sigss - quorumHash = self.mninfo[1].node.quorum("selectquorum", 104, id)["quorumHash"] - assert self.mninfo[1].node.quorum("sign", 104, id, msgHash, quorumHash) + quorumHash = self.mninfo[1].node.quorum("selectquorum", q_type, id)["quorumHash"] + assert self.mninfo[1].node.quorum("sign", q_type, id, msgHash, quorumHash) assert_sigs_nochange(False, False, False, 3) # Sign third share and test optional submit parameter if spork21 is enabled, should result in recovered sig # and conflict for msgHashConflict if self.options.spork21: # 1. Providing an invalid quorum hash and set submit=false, should throw an error - assert_raises_rpc_error(-8, 'quorum not found', self.mninfo[2].node.quorum, "sign", 104, id, msgHash, id, False) + assert_raises_rpc_error(-8, 'quorum not found', self.mninfo[2].node.quorum, "sign", q_type, id, msgHash, id, False) # 2. Providing a valid quorum hash and set submit=false, should return a valid sigShare object - sig_share_rpc_1 = self.mninfo[2].node.quorum("sign", 104, id, msgHash, quorumHash, False) - sig_share_rpc_2 = self.mninfo[2].node.quorum("sign", 104, id, msgHash, "", False) + sig_share_rpc_1 = self.mninfo[2].node.quorum("sign", q_type, id, msgHash, quorumHash, False) + sig_share_rpc_2 = self.mninfo[2].node.quorum("sign", q_type, id, msgHash, "", False) assert_equal(sig_share_rpc_1, sig_share_rpc_2) assert_sigs_nochange(False, False, False, 3) # 3. Sending the sig share received from RPC to the recovery member through P2P interface, should result @@ -93,7 +94,7 @@ def assert_sigs_nochange(hasrecsigs, isconflicting1, isconflicting2, timeout): for mn in self.mninfo: assert mn.node.getconnectioncount() == self.llmq_size # Get the current recovery member of the quorum - q = self.nodes[0].quorum('selectquorum', 104, id) + q = self.nodes[0].quorum('selectquorum', q_type, id) mn = self.get_mninfo(q['recoveryMembers'][0]) # Open a P2P connection to it p2p_interface = mn.node.add_p2p_connection(P2PInterface()) @@ -101,7 +102,7 @@ def assert_sigs_nochange(hasrecsigs, isconflicting1, isconflicting2, timeout): p2p_interface.send_message(msg_qsigshare([sig_share])) else: # If spork21 is not enabled just sign regularly - self.mninfo[2].node.quorum("sign", 104, id, msgHash) + self.mninfo[2].node.quorum("sign", q_type, id, msgHash) wait_for_sigs(True, False, True, 15) @@ -110,19 +111,19 @@ def assert_sigs_nochange(hasrecsigs, isconflicting1, isconflicting2, timeout): # Test `quorum verify` rpc node = self.mninfo[0].node - recsig = node.quorum("getrecsig", 104, id, msgHash) + recsig = node.quorum("getrecsig", q_type, id, msgHash) # Find quorum automatically height = node.getblockcount() height_bad = node.getblockheader(recsig["quorumHash"])["height"] hash_bad = node.getblockhash(0) - assert node.quorum("verify", 104, id, msgHash, recsig["sig"]) - assert node.quorum("verify", 104, id, msgHash, recsig["sig"], "", height) - assert not node.quorum("verify", 104, id, msgHashConflict, recsig["sig"]) - assert not node.quorum("verify", 104, id, msgHash, recsig["sig"], "", height_bad) + assert node.quorum("verify", q_type, id, msgHash, recsig["sig"]) + assert node.quorum("verify", q_type, id, msgHash, recsig["sig"], "", height) + assert not node.quorum("verify", q_type, id, msgHashConflict, recsig["sig"]) + assert not node.quorum("verify", q_type, id, msgHash, recsig["sig"], "", height_bad) # Use specific quorum - assert node.quorum("verify", 104, id, msgHash, recsig["sig"], recsig["quorumHash"]) - assert not node.quorum("verify", 104, id, msgHashConflict, recsig["sig"], recsig["quorumHash"]) - assert_raises_rpc_error(-8, "quorum not found", node.quorum, "verify", 104, id, msgHash, recsig["sig"], hash_bad) + assert node.quorum("verify", q_type, id, msgHash, recsig["sig"], recsig["quorumHash"]) + assert not node.quorum("verify", q_type, id, msgHashConflict, recsig["sig"], recsig["quorumHash"]) + assert_raises_rpc_error(-8, "quorum not found", node.quorum, "verify", q_type, id, msgHash, recsig["sig"], hash_bad) # Mine one more quorum, so that we have 2 active ones, nothing should change self.mine_quorum() @@ -131,10 +132,10 @@ def assert_sigs_nochange(hasrecsigs, isconflicting1, isconflicting2, timeout): # Create a recovered sig for the oldest quorum i.e. the active quorum which will be moved # out of the active set when a new quorum appears request_id = 2 - oldest_quorum_hash = node.quorum("list")["llmq_test_instantsend"][-1] + oldest_quorum_hash = node.quorum("list")["llmq_test"][-1] # Search for a request id which selects the last active quorum while True: - selected_hash = node.quorum('selectquorum', 104, uint256_to_string(request_id))["quorumHash"] + selected_hash = node.quorum('selectquorum', q_type, uint256_to_string(request_id))["quorumHash"] if selected_hash == oldest_quorum_hash: break else: @@ -142,12 +143,12 @@ def assert_sigs_nochange(hasrecsigs, isconflicting1, isconflicting2, timeout): # Produce the recovered signature id = uint256_to_string(request_id) for mn in self.mninfo: - mn.node.quorum("sign", 104, id, msgHash) + mn.node.quorum("sign", q_type, id, msgHash) # And mine a quorum to move the quorum which signed out of the active set self.mine_quorum() # Verify the recovered sig. This triggers the "signHeight + dkgInterval" verification - recsig = node.quorum("getrecsig", 104, id, msgHash) - assert node.quorum("verify", 104, id, msgHash, recsig["sig"], "", node.getblockcount()) + recsig = node.quorum("getrecsig", q_type, id, msgHash) + assert node.quorum("verify", q_type, id, msgHash, recsig["sig"], "", node.getblockcount()) recsig_time = self.mocktime @@ -166,21 +167,21 @@ def assert_sigs_nochange(hasrecsigs, isconflicting1, isconflicting2, timeout): wait_for_sigs(False, False, False, 15) for i in range(2): - self.mninfo[i].node.quorum("sign", 104, id, msgHashConflict) + self.mninfo[i].node.quorum("sign", q_type, id, msgHashConflict) for i in range(2, 5): - self.mninfo[i].node.quorum("sign", 104, id, msgHash) + self.mninfo[i].node.quorum("sign", q_type, id, msgHash) wait_for_sigs(True, False, True, 15) if self.options.spork21: id = uint256_to_string(request_id + 1) # Isolate the node that is responsible for the recovery of a signature and assert that recovery fails - q = self.nodes[0].quorum('selectquorum', 104, id) + q = self.nodes[0].quorum('selectquorum', q_type, id) mn = self.get_mninfo(q['recoveryMembers'][0]) mn.node.setnetworkactive(False) self.wait_until(lambda: mn.node.getconnectioncount() == 0) for i in range(4): - self.mninfo[i].node.quorum("sign", 104, id, msgHash) + self.mninfo[i].node.quorum("sign", q_type, id, msgHash) assert_sigs_nochange(False, False, False, 3) # Need to re-connect so that it later gets the recovered sig mn.node.setnetworkactive(True) diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 38eb7a7fb021a..287641142d684 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -168,7 +168,7 @@ def _test_getblockchaininfo(self): 'dip0003': { 'type': 'buried', 'active': False, 'height': 432}, 'dip0008': { 'type': 'buried', 'active': True, 'height': 1}, 'dip0020': { 'type': 'buried', 'active': True, 'height': 1}, - 'dip0024': { 'type': 'buried', 'active': False, 'height': 900}, + 'dip0024': { 'type': 'buried', 'active': True, 'height': 1}, 'realloc': { 'type': 'buried', 'active': True, 'height': 1}, 'v19': { 'type': 'buried', 'active': False, 'height': 900}, 'v20': { 'type': 'buried', 'active': False, 'height': 900}, From 629d0e6ad63f109b99fa3288b395e46f1ba3a552 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 21 Nov 2024 16:48:40 +0700 Subject: [PATCH 08/13] fix: intermittent error for feature_llmq_simplepose due to rotating quorums --- test/functional/feature_llmq_simplepose.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/feature_llmq_simplepose.py b/test/functional/feature_llmq_simplepose.py index ea4d51d381e2e..3d5c382249d86 100755 --- a/test/functional/feature_llmq_simplepose.py +++ b/test/functional/feature_llmq_simplepose.py @@ -18,8 +18,10 @@ class LLMQSimplePoSeTest(DashTestFramework): def set_test_params(self): + self.extra_args = [[ f'-testactivationheight=dip0024@9999' ]] * 6 self.set_dash_test_params(6, 5) self.set_dash_llmq_test_params(5, 3) + # rotating quorums add instability for this functional tests def run_test(self): From f0ff94297753987cc4eebe903d18011315016eee Mon Sep 17 00:00:00 2001 From: merge-script Date: Sat, 25 Sep 2021 09:39:42 +0200 Subject: [PATCH 09/13] Merge bitcoin/bitcoin#23086: test: Add -testactivationheight tests to rpc_blockchain fa4ca8d5797adb9ad6aa0f150a5f187ebfb714c7 test: Add -testactivationheight tests to rpc_blockchain (MarcoFalke) Pull request description: Suggested: https://github.com/bitcoin/bitcoin/pull/22818#discussion_r712513991 ACKs for top commit: laanwj: Code review ACK fa4ca8d5797adb9ad6aa0f150a5f187ebfb714c7 theStack: Concept and code-review ACK fa4ca8d5797adb9ad6aa0f150a5f187ebfb714c7 Tree-SHA512: 41304db1a15c0c705a9cc2808c9f1d7831a321a8a7948a28ec5d3ee1ed3da6a0ce67cd50c99a33aaed86830c59608eb6ffadbeaba67d95245c490f9b6c277912 --- test/functional/rpc_blockchain.py | 56 ++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 287641142d684..14e481f4328ba 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -147,7 +147,37 @@ def _test_getblockchaininfo(self): # should have exact keys assert_equal(sorted(res.keys()), keys) - self.restart_node(0, ['-stopatheight=207', '-prune=550', '-txindex=0']) + self.stop_node(0) + self.nodes[0].assert_start_raises_init_error( + extra_args=['-testactivationheight=name@2'], + expected_msg='Error: Invalid name (name@2) for -testactivationheight=name@height.', + ) + self.nodes[0].assert_start_raises_init_error( + extra_args=['-testactivationheight=bip34@-2'], + expected_msg='Error: Invalid height value (bip34@-2) for -testactivationheight=name@height.', + ) + self.nodes[0].assert_start_raises_init_error( + extra_args=['-testactivationheight='], + expected_msg='Error: Invalid format () for -testactivationheight=name@height.', + ) + self.start_node(0, extra_args=[ + '-stopatheight=207', + '-prune=550', + '-txindex=0', + '-testactivationheight=bip34@2', + '-testactivationheight=dersig@3', + '-testactivationheight=cltv@4', + '-testactivationheight=csv@5', + '-testactivationheight=bip147@6', + '-testactivationheight=dip0001@10', + '-dip3params=411:511', + '-testactivationheight=dip0008@12', + '-testactivationheight=dip0024@13', + '-testactivationheight=brr@14', + '-testactivationheight=v20@901', + '-testactivationheight=mn_rr@902', + ]) + res = self.nodes[0].getblockchaininfo() # result should have these additional pruning keys if prune=550 assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys)) @@ -159,20 +189,20 @@ def _test_getblockchaininfo(self): assert_equal(res['prune_target_size'], 576716800) assert_greater_than(res['size_on_disk'], 0) assert_equal(res['softforks'], { - 'bip34': {'type': 'buried', 'active': True, 'height': 1}, - 'bip66': {'type': 'buried', 'active': True, 'height': 1}, - 'bip65': {'type': 'buried', 'active': True, 'height': 1}, - 'bip147': { 'type': 'buried', 'active': True, 'height': 1}, - 'csv': {'type': 'buried', 'active': True, 'height': 1}, - 'dip0001': { 'type': 'buried', 'active': True, 'height': 1}, - 'dip0003': { 'type': 'buried', 'active': False, 'height': 432}, - 'dip0008': { 'type': 'buried', 'active': True, 'height': 1}, + 'bip34': {'type': 'buried', 'active': True, 'height': 2}, + 'bip66': {'type': 'buried', 'active': True, 'height': 3}, + 'bip65': {'type': 'buried', 'active': True, 'height': 4}, + 'csv': {'type': 'buried', 'active': True, 'height': 5}, + 'bip147': {'type': 'buried', 'active': True, 'height': 6}, + 'dip0001': { 'type': 'buried', 'active': True, 'height': 10}, + 'dip0003': { 'type': 'buried', 'active': False, 'height': 411}, + 'dip0008': { 'type': 'buried', 'active': True, 'height': 12}, 'dip0020': { 'type': 'buried', 'active': True, 'height': 1}, - 'dip0024': { 'type': 'buried', 'active': True, 'height': 1}, - 'realloc': { 'type': 'buried', 'active': True, 'height': 1}, + 'dip0024': { 'type': 'buried', 'active': True, 'height': 13}, + 'realloc': { 'type': 'buried', 'active': True, 'height': 14}, 'v19': { 'type': 'buried', 'active': False, 'height': 900}, - 'v20': { 'type': 'buried', 'active': False, 'height': 900}, - 'mn_rr': { 'type': 'buried', 'active': False, 'height': 900}, + 'v20': { 'type': 'buried', 'active': False, 'height': 901}, + 'mn_rr': { 'type': 'buried', 'active': False, 'height': 902}, 'withdrawals': { 'type': 'bip9', 'bip9': { From 5e6a58a9c5440b1e2b2be13e11094030c3c24418 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 13 Mar 2022 10:20:10 +0100 Subject: [PATCH 10/13] Merge bitcoin/bitcoin#24527: test: set segwit height back to 0 on regtest 5ce3057c8e8f192921fd5e4bdb95bb15e3f7dbad test: set segwit height back to 0 on regtest (Martin Zumsande) Pull request description: The change of `consensus.SegwitHeight` from 0 to 1 for regtest in #22818 had the effect that if I create a regtest enviroment with current master (or 23.x), and then try to load this chain with an older version (22.x), I get an InitError `Witness data for blocks after height 0 requires validation. Please restart with -reindex` and have to reindex because `BLOCK_OPT_WITNESS` is no longer set for the Genesis block and `NeedsRedownload()` in validation returns `true` with an older version. That might be a bit annoying for tests that use a shared regtest dir with different versions. If people think this is enough of an issue to be worth fixing, I think it should also make it into 23.x ACKs for top commit: theStack: Concept and code-review ACK 5ce3057c8e8f192921fd5e4bdb95bb15e3f7dbad Tree-SHA512: b0e89ff7fc953bc0ae929d2da44cde7149321d987fb4763934f6c9635d00d807129a50b459cc5e69e86bb1819e4b063b969486e8016a1cb8db8f905fa315653d --- src/chainparams.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0dff08db58f21..4e0ad9d84c7f0 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -782,7 +782,7 @@ class CRegTestParams : public CChainParams { consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1; // Always active unless overridden consensus.BIP66Height = 1; // Always active unless overridden - consensus.BIP147Height = 1; // Always active unless overridden + consensus.BIP147Height = 0; // Always active unless overridden consensus.CSVHeight = 1; // Always active unless overridden consensus.DIP0001Height = 1; // Always active unless overridden consensus.DIP0003Height = 432; // Always active for DashTestFramework in functional tests (see dip3params) From bb609413a1824301b1d69861ef43d5d9df374aa8 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 21 Nov 2024 21:51:37 +0700 Subject: [PATCH 11/13] fix: bump time for all nodes during mine_quorum in tests --- .../test_framework/test_framework.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 3ccf819fd9cf7..278aed4bc3dd0 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1850,7 +1850,7 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec # move forward to next DKG skip_count = 24 - (self.nodes[0].getblockcount() % 24) if skip_count != 0: - self.bump_mocktime(1, nodes=nodes) + self.bump_mocktime(1) self.generate(self.nodes[0], skip_count, sync_fun=self.no_op) self.sync_blocks(nodes) @@ -1858,9 +1858,9 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec self.log.info("Expected quorum_hash:"+str(q)) self.log.info("Waiting for phase 1 (init)") self.wait_for_quorum_phase(q, 1, expected_members, None, 0, mninfos_online, llmq_type_name=llmq_type_name) - self.wait_for_quorum_connections(q, expected_connections, mninfos_online, wait_proc=lambda: self.bump_mocktime(1, nodes=nodes), llmq_type_name=llmq_type_name) + self.wait_for_quorum_connections(q, expected_connections, mninfos_online, wait_proc=lambda: self.bump_mocktime(1), llmq_type_name=llmq_type_name) if spork23_active: - self.wait_for_masternode_probes(q, mninfos_online, wait_proc=lambda: self.bump_mocktime(1, nodes=nodes)) + self.wait_for_masternode_probes(q, mninfos_online, wait_proc=lambda: self.bump_mocktime(1)) self.move_blocks(nodes, 2) @@ -1891,7 +1891,7 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec self.wait_for_quorum_commitment(q, nodes, llmq_type=llmq_type) self.log.info("Mining final commitment") - self.bump_mocktime(1, nodes=nodes) + self.bump_mocktime(1) self.nodes[0].getblocktemplate() # this calls CreateNewBlock self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_blocks(nodes)) @@ -1941,9 +1941,9 @@ def mine_cycle_quorum(self, llmq_type_name="llmq_test_dip0024", llmq_type=103, self.log.info("quorumIndex 0: Waiting for phase 1 (init)") self.wait_for_quorum_phase(q_0, 1, expected_members, None, 0, mninfos_online, llmq_type_name) self.log.info("quorumIndex 0: Waiting for quorum connections (init)") - self.wait_for_quorum_connections(q_0, expected_connections, mninfos_online, llmq_type_name, wait_proc=lambda: self.bump_mocktime(1, nodes=nodes)) + self.wait_for_quorum_connections(q_0, expected_connections, mninfos_online, llmq_type_name, wait_proc=lambda: self.bump_mocktime(1)) if spork23_active: - self.wait_for_masternode_probes(q_0, mninfos_online, wait_proc=lambda: self.bump_mocktime(1, nodes=nodes), llmq_type_name=llmq_type_name) + self.wait_for_masternode_probes(q_0, mninfos_online, wait_proc=lambda: self.bump_mocktime(1), llmq_type_name=llmq_type_name) self.move_blocks(nodes, 1) @@ -1953,9 +1953,9 @@ def mine_cycle_quorum(self, llmq_type_name="llmq_test_dip0024", llmq_type=103, self.log.info("quorumIndex 1: Waiting for phase 1 (init)") self.wait_for_quorum_phase(q_1, 1, expected_members, None, 0, mninfos_online, llmq_type_name) self.log.info("quorumIndex 1: Waiting for quorum connections (init)") - self.wait_for_quorum_connections(q_1, expected_connections, mninfos_online, llmq_type_name, wait_proc=lambda: self.bump_mocktime(1, nodes=nodes)) + self.wait_for_quorum_connections(q_1, expected_connections, mninfos_online, llmq_type_name, wait_proc=lambda: self.bump_mocktime(1)) if spork23_active: - self.wait_for_masternode_probes(q_1, mninfos_online, wait_proc=lambda: self.bump_mocktime(1, nodes=nodes), llmq_type_name=llmq_type_name) + self.wait_for_masternode_probes(q_1, mninfos_online, wait_proc=lambda: self.bump_mocktime(1), llmq_type_name=llmq_type_name) self.move_blocks(nodes, 1) @@ -2007,7 +2007,7 @@ def mine_cycle_quorum(self, llmq_type_name="llmq_test_dip0024", llmq_type=103, self.log.info("quorumIndex 1: Waiting for phase 6 (finalization)") self.wait_for_quorum_phase(q_1, 6, expected_members, None, 0, mninfos_online, llmq_type_name) self.log.info("Mining final commitments") - self.bump_mocktime(1, nodes=nodes) + self.bump_mocktime(1) self.nodes[0].getblocktemplate() # this calls CreateNewBlock self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_blocks(nodes)) @@ -2041,7 +2041,7 @@ def move_to_next_cycle(self): # move forward to next DKG skip_count = cycle_length - (cur_block % cycle_length) if skip_count != 0: - self.bump_mocktime(1, nodes=nodes) + self.bump_mocktime(1) self.generate(self.nodes[0], skip_count, sync_fun=self.no_op) self.sync_blocks(nodes) self.log.info('Moved from block %d to %d' % (cur_block, self.nodes[0].getblockcount())) From 0b3ce03becb630d14803cd11e3d27502a29b1d71 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 21 Nov 2024 22:12:34 +0700 Subject: [PATCH 12/13] fix: add real-time delay 1 second to ensure other quorum validity --- .../test_framework/test_framework.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 278aed4bc3dd0..88663f6a6041c 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1857,6 +1857,12 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec q = self.nodes[0].getbestblockhash() self.log.info("Expected quorum_hash:"+str(q)) self.log.info("Waiting for phase 1 (init)") + # extra sleep is needed to be sure that we have *all* quorum mined, not only this one + # is is important in case of spork23 is active, because otherwise node can get a ban unexpectable + # that's a silly workaround but it increase stability of functional tests on both localhost and CI + # TODO: replace it to proper validation for each quorum instead just one + if spork23_active: + time.sleep(1) self.wait_for_quorum_phase(q, 1, expected_members, None, 0, mninfos_online, llmq_type_name=llmq_type_name) self.wait_for_quorum_connections(q, expected_connections, mninfos_online, wait_proc=lambda: self.bump_mocktime(1), llmq_type_name=llmq_type_name) if spork23_active: @@ -1865,32 +1871,46 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec self.move_blocks(nodes, 2) self.log.info("Waiting for phase 2 (contribute)") + if spork23_active: + time.sleep(1) # see comment above self.wait_for_quorum_phase(q, 2, expected_members, "receivedContributions", expected_contributions, mninfos_online, llmq_type_name=llmq_type_name) self.move_blocks(nodes, 2) self.log.info("Waiting for phase 3 (complain)") + if spork23_active: + time.sleep(1) # see comment above self.wait_for_quorum_phase(q, 3, expected_members, "receivedComplaints", expected_complaints, mninfos_online, llmq_type_name=llmq_type_name) self.move_blocks(nodes, 2) self.log.info("Waiting for phase 4 (justify)") + if spork23_active: + time.sleep(1) # see comment above self.wait_for_quorum_phase(q, 4, expected_members, "receivedJustifications", expected_justifications, mninfos_online, llmq_type_name=llmq_type_name) self.move_blocks(nodes, 2) self.log.info("Waiting for phase 5 (commit)") + if spork23_active: + time.sleep(1) # see comment above self.wait_for_quorum_phase(q, 5, expected_members, "receivedPrematureCommitments", expected_commitments, mninfos_online, llmq_type_name=llmq_type_name) self.move_blocks(nodes, 2) self.log.info("Waiting for phase 6 (mining)") + if spork23_active: + time.sleep(1) # see comment above self.wait_for_quorum_phase(q, 6, expected_members, None, 0, mninfos_online, llmq_type_name=llmq_type_name) self.log.info("Waiting final commitment") + if spork23_active: + time.sleep(1) # see comment above self.wait_for_quorum_commitment(q, nodes, llmq_type=llmq_type) self.log.info("Mining final commitment") + if spork23_active: + time.sleep(1) # see comment above self.bump_mocktime(1) self.nodes[0].getblocktemplate() # this calls CreateNewBlock self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_blocks(nodes)) @@ -1939,6 +1959,12 @@ def mine_cycle_quorum(self, llmq_type_name="llmq_test_dip0024", llmq_type=103, self.log.info("Expected quorum_0 at:" + str(self.nodes[0].getblockcount())) self.log.info("Expected quorum_0 hash:" + str(q_0)) self.log.info("quorumIndex 0: Waiting for phase 1 (init)") + # extra sleep is needed to be sure that we have *all* quorum mined, not only this one + # is is important in case of spork23 is active, because otherwise node can get a ban unexpectable + # that's a silly workaround but it increase stability of functional tests on both localhost and CI + # TODO: replace it to proper validation for each quorum instead just one + if spork23_active: + time.sleep(1) self.wait_for_quorum_phase(q_0, 1, expected_members, None, 0, mninfos_online, llmq_type_name) self.log.info("quorumIndex 0: Waiting for quorum connections (init)") self.wait_for_quorum_connections(q_0, expected_connections, mninfos_online, llmq_type_name, wait_proc=lambda: self.bump_mocktime(1)) @@ -1951,6 +1977,8 @@ def mine_cycle_quorum(self, llmq_type_name="llmq_test_dip0024", llmq_type=103, self.log.info("Expected quorum_1 at:" + str(self.nodes[0].getblockcount())) self.log.info("Expected quorum_1 hash:" + str(q_1)) self.log.info("quorumIndex 1: Waiting for phase 1 (init)") + if spork23_active: + time.sleep(1) # see comment above self.wait_for_quorum_phase(q_1, 1, expected_members, None, 0, mninfos_online, llmq_type_name) self.log.info("quorumIndex 1: Waiting for quorum connections (init)") self.wait_for_quorum_connections(q_1, expected_connections, mninfos_online, llmq_type_name, wait_proc=lambda: self.bump_mocktime(1)) From cd2f39a1cd574f88f65cafb3c36d0085e399e80c Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 21 Nov 2024 22:22:38 +0700 Subject: [PATCH 13/13] fix: bump mocktime 10 minutes for feature_llmq_simplepose to increase stability of test --- test/functional/feature_llmq_simplepose.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/feature_llmq_simplepose.py b/test/functional/feature_llmq_simplepose.py index 3d5c382249d86..d0722a1c2d368 100755 --- a/test/functional/feature_llmq_simplepose.py +++ b/test/functional/feature_llmq_simplepose.py @@ -93,6 +93,7 @@ def force_old_mn_proto(self, mn): return False, True def test_no_banning(self, expected_connections=None): + self.bump_mocktime(10 * 60 + 1) for i in range(3): self.log.info(f"Testing no PoSe banning in normal conditions {i + 1}/3") self.mine_quorum(expected_connections=expected_connections) @@ -163,6 +164,7 @@ def test_banning(self, invalidate_proc, expected_connections=None): mninfos_online = self.mninfo.copy() mninfos_valid = self.mninfo.copy() expected_contributors = len(mninfos_online) + self.bump_mocktime(10 * 60 + 1) for i in range(2): self.log.info(f"Testing PoSe banning due to {invalidate_proc.__name__} {i + 1}/2") mn = mninfos_valid.pop()