From 965f5b2063845cab40708d9b91b62f98ba2306f7 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 30 Oct 2023 18:12:07 +0300 Subject: [PATCH] fix: adjust GetPaymentsLimit to work correctly with historical blocks, adjust sb params on regtest, tweak tests (#5641) ## Issue being fixed or feature implemented Noticed a couple of things while I was trying to figure out if an [issue](https://github.com/dashpay/dash/pull/5627#discussion_r1367153099) @knst mentioned in #5627 could actually exist: 1. `GetPaymentsLimit()` won't work correctly with historical blocks rn. We don't use it that way internally but it could be done via rpc and it should provide correct results. 2. superblock params on regtest are too small to test them properly 3. because of (2) and a huge v20 activation window (comparing to sb params) `feature_governance.py` doesn't test v20 switching states. There's also no "sb on v20 activation block" test. ~NOTE: based on #5639 atm~ ## What was done? fix it, pls see individual commits ## How Has This Been Tested? run tests ## Breaking Changes n/a ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/chainparams.cpp | 5 +- src/governance/classes.cpp | 10 +- src/test/block_reward_reallocation_tests.cpp | 16 +- test/functional/feature_governance.py | 212 ++++++++++++------- 4 files changed, 151 insertions(+), 92 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 12f4ab953b6a9..8097633d35bae 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -219,6 +219,7 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 19999999999; // TODO: To be determined later consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + // NOTE: nWindowSize for MN_RR __MUST__ be greater than or equal to nSuperblockMaturityWindow for CSuperblock::GetPaymentsLimit() to work correctly consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 4032; // TODO to be determined before v20 release: choose nWindowSize/nThresholdStart/nThresholdMin consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 3226; // 80% of 4032 consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 2420; // 60% of 4032 @@ -794,8 +795,8 @@ class CRegTestParams : public CChainParams { consensus.nBudgetPaymentsWindowBlocks = 10; consensus.nSuperblockStartBlock = 1500; consensus.nSuperblockStartHash = uint256(); // do not check this on regtest - consensus.nSuperblockCycle = 10; - consensus.nSuperblockMaturityWindow = 2; + consensus.nSuperblockCycle = 20; + consensus.nSuperblockMaturityWindow = 10; consensus.nGovernanceMinQuorum = 1; consensus.nGovernanceFilterElements = 100; consensus.nMasternodeMinimumConfirmations = 1; diff --git a/src/governance/classes.cpp b/src/governance/classes.cpp index c0efbc901d18b..d3444a90f06be 100644 --- a/src/governance/classes.cpp +++ b/src/governance/classes.cpp @@ -494,14 +494,16 @@ CAmount CSuperblock::GetPaymentsLimit(int nBlockHeight) return 0; } - const CBlockIndex* tipIndex = ::ChainActive().Tip(); - const auto v20_state = llmq::utils::GetV20State(tipIndex); + const CBlockIndex* pindex = ::ChainActive().Tip(); + if (pindex->nHeight > nBlockHeight) pindex = pindex->GetAncestor(nBlockHeight); + + const auto v20_state = llmq::utils::GetV20State(pindex); bool fV20Active{v20_state == ThresholdState::ACTIVE}; - if (!fV20Active && nBlockHeight > tipIndex->nHeight) { + if (!fV20Active && nBlockHeight > pindex->nHeight) { // If fV20Active isn't active yet and nBlockHeight refers to a future SuperBlock // then we need to check if the fork is locked_in and see if it will be active by the time of the future SuperBlock if (v20_state == ThresholdState::LOCKED_IN) { - int activation_height = llmq::utils::GetV20Since(tipIndex) + static_cast(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize); + int activation_height = llmq::utils::GetV20Since(pindex) + static_cast(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize); if (nBlockHeight >= activation_height) { fV20Active = true; } diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp index 4a3a77acfced5..ec4cef54aa920 100644 --- a/src/test/block_reward_reallocation_tests.cpp +++ b/src/test/block_reward_reallocation_tests.cpp @@ -214,7 +214,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); } - for ([[maybe_unused]] auto _ : irange::range(9)) { + for ([[maybe_unused]] auto _ : irange::range(consensus_params.nSuperblockCycle - 1)) { CreateAndProcessBlock({}, coinbaseKey); } @@ -234,7 +234,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS // there will be 19 adjustments, 3 superblocks long each for ([[maybe_unused]] auto i : irange::range(19)) { for ([[maybe_unused]] auto j : irange::range(3)) { - for ([[maybe_unused]] auto k : irange::range(10)) { + for ([[maybe_unused]] auto k : irange::range(consensus_params.nSuperblockCycle)) { CreateAndProcessBlock({}, coinbaseKey); } LOCK(cs_main); @@ -256,15 +256,15 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS const CAmount block_subsidy = GetBlockSubsidyInner(tip->nBits, tip->nHeight, consensus_params, isV20Active); const CAmount block_subsidy_sb = GetSuperblockSubsidyInner(tip->nBits, tip->nHeight, consensus_params, isV20Active); CAmount block_subsidy_potential = block_subsidy + block_subsidy_sb; - BOOST_CHECK_EQUAL(block_subsidy_potential, 113573330); + BOOST_CHECK_EQUAL(block_subsidy_potential, 84437941); CAmount expected_block_reward = block_subsidy_potential - block_subsidy_potential / 5; const CAmount masternode_payment = GetMasternodePayment(tip->nHeight, block_subsidy, isV20Active); const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), expected_block_reward); - BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 90858664); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 67550353); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); - BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 68143998); // 0.75 + BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 50662764); // 0.75 } BOOST_CHECK(!llmq::utils::IsMNRewardReallocationActive(m_node.chainman->ActiveChain().Tip())); @@ -274,7 +274,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS // Reward split should stay ~75/25 after reallocation is done, // check 10 next superblocks for ([[maybe_unused]] auto i : irange::range(10)) { - for ([[maybe_unused]] auto k : irange::range(10)) { + for ([[maybe_unused]] auto k : irange::range(consensus_params.nSuperblockCycle)) { CreateAndProcessBlock({}, coinbaseKey); } LOCK(cs_main); @@ -307,9 +307,9 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS masternode_payment -= platform_payment; const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); - // At this height (3178) the block subsidy is 105460950. CAmount block_subsidy_potential = block_subsidy + block_subsidy_sb; - BOOST_CHECK_EQUAL(block_subsidy_potential, 105460950); + BOOST_CHECK_EQUAL(tip->nHeight, 3858); + BOOST_CHECK_EQUAL(block_subsidy_potential, 78406660); // Treasury is 20% since MNRewardReallocation CAmount expected_block_reward = block_subsidy_potential - block_subsidy_potential / 5; // Since MNRewardReallocation, MN reward share is 75% of the block reward diff --git a/test/functional/feature_governance.py b/test/functional/feature_governance.py index 78417578e60cb..7456a59d329cc 100755 --- a/test/functional/feature_governance.py +++ b/test/functional/feature_governance.py @@ -5,15 +5,16 @@ """Tests around dash governance.""" import json -import time from test_framework.messages import uint256_to_string from test_framework.test_framework import DashTestFramework -from test_framework.util import assert_equal, satoshi_round +from test_framework.util import assert_equal, satoshi_round, set_node_times, wait_until class DashGovernanceTest (DashTestFramework): def set_test_params(self): - self.set_dash_test_params(6, 5, [["-budgetparams=10:10:10"]] * 6) + self.v20_start_time = 1417713500 + # using adjusted v20 deployment params to test an edge case where superblock maturity window is equal to deployment window size + self.set_dash_test_params(6, 5, [["-budgetparams=10:10:10", f"-vbparams=v20:{self.v20_start_time}:999999999999:10:8:6:5:-1"]] * 6, fast_dip3_enforcement=True) def prepare_object(self, object_type, parent_hash, creation_time, revision, name, amount, payment_address): proposal_rev = revision @@ -23,7 +24,7 @@ def prepare_object(self, object_type, parent_hash, creation_time, revision, name "name": name, "start_epoch": proposal_time, "end_epoch": proposal_time + 24 * 60 * 60, - "payment_amount": amount, + "payment_amount": float(amount), "payment_address": payment_address, "url": "https://dash.org" } @@ -38,6 +39,44 @@ def prepare_object(self, object_type, parent_hash, creation_time, revision, name "data": proposal_template, } + def check_superblockbudget(self, v20_active): + v20_state = self.nodes[0].getblockchaininfo()["softforks"]["v20"] + assert_equal(v20_state["active"], v20_active) + assert_equal(self.nodes[0].getsuperblockbudget(200), self.expected_old_budget) + assert_equal(self.nodes[0].getsuperblockbudget(220), self.expected_old_budget) + if v20_state["bip9"]["status"] == "locked_in" or v20_state["bip9"]["status"] == "active": + assert_equal(self.nodes[0].getsuperblockbudget(240), self.expected_v20_budget) + assert_equal(self.nodes[0].getsuperblockbudget(260), self.expected_v20_budget) + assert_equal(self.nodes[0].getsuperblockbudget(280), self.expected_v20_budget) + else: + assert_equal(self.nodes[0].getsuperblockbudget(240), self.expected_old_budget) + assert_equal(self.nodes[0].getsuperblockbudget(260), self.expected_old_budget) + assert_equal(self.nodes[0].getsuperblockbudget(280), self.expected_old_budget) + + def check_superblock(self): + # Make sure Superblock has only payments that fit into the budget + # p0 must always be included because it has most votes + # p1 and p2 have equal number of votes (but less votes than p0) + # so only one of them can be included (depends on proposal hashes). + + coinbase_outputs = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 2)["tx"][0]["vout"] + payments_found = 0 + for txout in coinbase_outputs: + if txout["value"] == self.p0_amount and txout["scriptPubKey"]["addresses"][0] == self.p0_payout_address: + payments_found += 1 + if txout["value"] == self.p1_amount and txout["scriptPubKey"]["addresses"][0] == self.p1_payout_address: + if self.p1_hash > self.p2_hash: + payments_found += 1 + else: + assert False + if txout["value"] == self.p2_amount and txout["scriptPubKey"]["addresses"][0] == self.p2_payout_address: + if self.p2_hash > self.p1_hash: + payments_found += 1 + else: + assert False + + assert_equal(payments_found, 2) + def run_test(self): map_vote_outcomes = { 0: "none", @@ -52,6 +91,13 @@ def run_test(self): 3: "delete", 4: "endorsed" } + sb_cycle = 20 + sb_maturity_window = 10 + sb_immaturity_window = sb_cycle - sb_maturity_window + self.expected_old_budget = satoshi_round("928.57142840") + self.expected_v20_budget = satoshi_round("18.57142860") + + self.activate_dip8() self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 4070908800) self.nodes[0].sporkupdate("SPORK_9_SUPERBLOCKS_ENABLED", 0) @@ -59,82 +105,101 @@ def run_test(self): assert_equal(len(self.nodes[0].gobject("list-prepared")), 0) + self.nodes[0].generate(3) + self.bump_mocktime(3) + self.sync_blocks() + assert_equal(self.nodes[0].getblockcount(), 210) + assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["bip9"]["status"], "defined") + self.check_superblockbudget(False) + + assert self.mocktime < self.v20_start_time + self.mocktime = self.v20_start_time + set_node_times(self.nodes, self.mocktime) + + self.nodes[0].generate(10) + self.bump_mocktime(10) + self.sync_blocks() + assert_equal(self.nodes[0].getblockcount(), 220) + assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["bip9"]["status"], "started") + self.check_superblockbudget(False) + proposal_time = self.mocktime - sb_block_height = self.nodes[0].getblockcount() + 10 - self.nodes[0].getblockcount() % 10 - p0_payout_address = self.nodes[0].getnewaddress() - p1_payout_address = self.nodes[0].getnewaddress() - p2_payout_address = self.nodes[0].getnewaddress() - p0_amount = float(1.1) - p1_amount = float(3.3) - p2_amount = float(self.nodes[0].getsuperblockbudget(sb_block_height)) - p1_amount - - p0_collateral_prepare = self.prepare_object(1, uint256_to_string(0), proposal_time, 1, "Proposal_0", p0_amount, p0_payout_address) - p1_collateral_prepare = self.prepare_object(1, uint256_to_string(0), proposal_time, 1, "Proposal_1", p1_amount, p1_payout_address) - p2_collateral_prepare = self.prepare_object(1, uint256_to_string(0), proposal_time, 1, "Proposal_2", p2_amount, p2_payout_address) + self.p0_payout_address = self.nodes[0].getnewaddress() + self.p1_payout_address = self.nodes[0].getnewaddress() + self.p2_payout_address = self.nodes[0].getnewaddress() + self.p0_amount = satoshi_round("1.1") + self.p1_amount = satoshi_round("3.3") + self.p2_amount = self.expected_v20_budget - self.p1_amount + + p0_collateral_prepare = self.prepare_object(1, uint256_to_string(0), proposal_time, 1, "Proposal_0", self.p0_amount, self.p0_payout_address) + p1_collateral_prepare = self.prepare_object(1, uint256_to_string(0), proposal_time, 1, "Proposal_1", self.p1_amount, self.p1_payout_address) + p2_collateral_prepare = self.prepare_object(1, uint256_to_string(0), proposal_time, 1, "Proposal_2", self.p2_amount, self.p2_payout_address) self.nodes[0].generate(6) + self.bump_mocktime(6) self.sync_blocks() assert_equal(len(self.nodes[0].gobject("list-prepared")), 3) assert_equal(len(self.nodes[0].gobject("list")), 0) - p0_hash = self.nodes[0].gobject("submit", "0", 1, proposal_time, p0_collateral_prepare["hex"], p0_collateral_prepare["collateralHash"]) - p1_hash = self.nodes[0].gobject("submit", "0", 1, proposal_time, p1_collateral_prepare["hex"], p1_collateral_prepare["collateralHash"]) - p2_hash = self.nodes[0].gobject("submit", "0", 1, proposal_time, p2_collateral_prepare["hex"], p2_collateral_prepare["collateralHash"]) + self.p0_hash = self.nodes[0].gobject("submit", "0", 1, proposal_time, p0_collateral_prepare["hex"], p0_collateral_prepare["collateralHash"]) + self.p1_hash = self.nodes[0].gobject("submit", "0", 1, proposal_time, p1_collateral_prepare["hex"], p1_collateral_prepare["collateralHash"]) + self.p2_hash = self.nodes[0].gobject("submit", "0", 1, proposal_time, p2_collateral_prepare["hex"], p2_collateral_prepare["collateralHash"]) assert_equal(len(self.nodes[0].gobject("list")), 3) - assert_equal(self.nodes[0].gobject("get", p0_hash)["FundingResult"]["YesCount"], 0) - assert_equal(self.nodes[0].gobject("get", p0_hash)["FundingResult"]["NoCount"], 0) + assert_equal(self.nodes[0].gobject("get", self.p0_hash)["FundingResult"]["YesCount"], 0) + assert_equal(self.nodes[0].gobject("get", self.p0_hash)["FundingResult"]["NoCount"], 0) - assert_equal(self.nodes[0].gobject("get", p1_hash)["FundingResult"]["YesCount"], 0) - assert_equal(self.nodes[0].gobject("get", p1_hash)["FundingResult"]["NoCount"], 0) + assert_equal(self.nodes[0].gobject("get", self.p1_hash)["FundingResult"]["YesCount"], 0) + assert_equal(self.nodes[0].gobject("get", self.p1_hash)["FundingResult"]["NoCount"], 0) - assert_equal(self.nodes[0].gobject("get", p2_hash)["FundingResult"]["YesCount"], 0) - assert_equal(self.nodes[0].gobject("get", p2_hash)["FundingResult"]["NoCount"], 0) + assert_equal(self.nodes[0].gobject("get", self.p2_hash)["FundingResult"]["YesCount"], 0) + assert_equal(self.nodes[0].gobject("get", self.p2_hash)["FundingResult"]["NoCount"], 0) - self.nodes[0].gobject("vote-alias", p0_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[0].proTxHash) - self.nodes[0].gobject("vote-many", p0_hash, map_vote_signals[1], map_vote_outcomes[1]) - assert_equal(self.nodes[0].gobject("get", p0_hash)["FundingResult"]["YesCount"], self.mn_count - 1) - assert_equal(self.nodes[0].gobject("get", p0_hash)["FundingResult"]["NoCount"], 1) + self.nodes[0].gobject("vote-alias", self.p0_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[0].proTxHash) + self.nodes[0].gobject("vote-many", self.p0_hash, map_vote_signals[1], map_vote_outcomes[1]) + assert_equal(self.nodes[0].gobject("get", self.p0_hash)["FundingResult"]["YesCount"], self.mn_count - 1) + assert_equal(self.nodes[0].gobject("get", self.p0_hash)["FundingResult"]["NoCount"], 1) - self.nodes[0].gobject("vote-alias", p1_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[0].proTxHash) - self.nodes[0].gobject("vote-alias", p1_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[1].proTxHash) - self.nodes[0].gobject("vote-many", p1_hash, map_vote_signals[1], map_vote_outcomes[1]) - assert_equal(self.nodes[0].gobject("get", p1_hash)["FundingResult"]["YesCount"], self.mn_count - 2) - assert_equal(self.nodes[0].gobject("get", p1_hash)["FundingResult"]["NoCount"], 2) + self.nodes[0].gobject("vote-alias", self.p1_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[0].proTxHash) + self.nodes[0].gobject("vote-alias", self.p1_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[1].proTxHash) + self.nodes[0].gobject("vote-many", self.p1_hash, map_vote_signals[1], map_vote_outcomes[1]) + assert_equal(self.nodes[0].gobject("get", self.p1_hash)["FundingResult"]["YesCount"], self.mn_count - 2) + assert_equal(self.nodes[0].gobject("get", self.p1_hash)["FundingResult"]["NoCount"], 2) - self.nodes[0].gobject("vote-alias", p2_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[0].proTxHash) - self.nodes[0].gobject("vote-alias", p2_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[1].proTxHash) - self.nodes[0].gobject("vote-many", p2_hash, map_vote_signals[1], map_vote_outcomes[1]) - assert_equal(self.nodes[0].gobject("get", p2_hash)["FundingResult"]["YesCount"], self.mn_count - 2) - assert_equal(self.nodes[0].gobject("get", p2_hash)["FundingResult"]["NoCount"], 2) + self.nodes[0].gobject("vote-alias", self.p2_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[0].proTxHash) + self.nodes[0].gobject("vote-alias", self.p2_hash, map_vote_signals[1], map_vote_outcomes[2], self.mninfo[1].proTxHash) + self.nodes[0].gobject("vote-many", self.p2_hash, map_vote_signals[1], map_vote_outcomes[1]) + assert_equal(self.nodes[0].gobject("get", self.p2_hash)["FundingResult"]["YesCount"], self.mn_count - 2) + assert_equal(self.nodes[0].gobject("get", self.p2_hash)["FundingResult"]["NoCount"], 2) assert_equal(len(self.nodes[0].gobject("list", "valid", "triggers")), 0) block_count = self.nodes[0].getblockcount() - sb_cycle = 10 - sb_maturity_window = 2 - sb_maturity_cycle = sb_cycle - sb_maturity_window # Move until 1 block before the Superblock maturity window starts - n = sb_maturity_cycle - block_count % sb_cycle - self.nodes[0].generate(n - 1) - self.sync_blocks() - time.sleep(1) + n = sb_immaturity_window - block_count % sb_cycle + # v20 is expected to be activate since block 240 + assert block_count + n < 240 + for _ in range(n - 1): + self.nodes[0].generate(1) + self.bump_mocktime(1) + self.sync_blocks() + self.check_superblockbudget(False) assert_equal(len(self.nodes[0].gobject("list", "valid", "triggers")), 0) # Move 1 block enabling the Superblock maturity window self.nodes[0].generate(1) + self.bump_mocktime(1) self.sync_blocks() - time.sleep(1) + assert_equal(self.nodes[0].getblockcount(), 230) + assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["bip9"]["status"], "locked_in") + self.check_superblockbudget(False) # The "winner" should submit new trigger and vote for it, no one else should vote yet - valid_triggers = self.nodes[0].gobject("list", "valid", "triggers") - assert_equal(len(valid_triggers), 1) - trigger_data = list(valid_triggers.values())[0] - assert_equal(trigger_data['YesCount'], 1) + wait_until(lambda: len(self.nodes[0].gobject("list", "valid", "triggers")) == 1, timeout=5) # Make sure amounts aren't trimmed payment_amounts_expected = [str(satoshi_round(str(p0_amount))), str(satoshi_round(str(p1_amount))), str(satoshi_round(str(p2_amount)))] @@ -145,45 +210,36 @@ def run_test(self): # Move 1 block inside the Superblock maturity window self.nodes[0].generate(1) + self.bump_mocktime(1) self.sync_blocks() - time.sleep(1) # Every MN should vote for the same trigger now, no new triggers should be created - triggers_rpc = self.nodes[0].gobject("list", "valid", "triggers") - assert_equal(len(triggers_rpc), 1) - trigger_data = list(triggers_rpc.values())[0] - assert_equal(trigger_data['YesCount'], self.mn_count) + wait_until(lambda: list(self.nodes[0].gobject("list", "valid", "triggers").values())[0]['YesCount'] == self.mn_count, timeout=5) block_count = self.nodes[0].getblockcount() n = sb_cycle - block_count % sb_cycle # Move remaining n blocks until actual Superblock for i in range(n): - time.sleep(1) self.nodes[0].generate(1) + self.bump_mocktime(1) self.sync_blocks() - - # Make sure Superblock has only payments that fit into the budget - # p0 must always be included because it has most votes - # p1 and p2 have equal number of votes (but less votes than p0) - # so only one of them can be included (depends on proposal hashes). - coinbase_outputs = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 2)["tx"][0]["vout"] - payments_found = 0 - for txout in coinbase_outputs: - if txout["value"] == satoshi_round(str(p0_amount)) and txout["scriptPubKey"]["addresses"][0] == p0_payout_address: - payments_found += 1 - if txout["value"] == satoshi_round(str(p1_amount)) and txout["scriptPubKey"]["addresses"][0] == p1_payout_address: - if p1_hash > p2_hash: - payments_found += 1 - else: - assert False - if txout["value"] == satoshi_round(str(p2_amount)) and txout["scriptPubKey"]["addresses"][0] == p2_payout_address: - if p2_hash > p1_hash: - payments_found += 1 - else: - assert False - - assert_equal(payments_found, 2) + # comparing to 239 because bip9 forks are active when the tip is one block behind the activation height + self.check_superblockbudget(block_count + i + 1 >= 239) + + self.check_superblockbudget(True) + self.check_superblock() + + # Mine and check a couple more superblocks + for i in range(2): + for _ in range(20): + self.nodes[0].generate(1) + self.bump_mocktime(1) + self.sync_blocks() + assert_equal(self.nodes[0].getblockcount(), 240 + (i + 1) * 20) + assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["bip9"]["status"], "active") + self.check_superblockbudget(True) + self.check_superblock() if __name__ == '__main__':