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)_
This commit is contained in:
UdjinM6 2023-10-30 18:12:07 +03:00 committed by GitHub
parent fa19c5ffee
commit 965f5b2063
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 149 additions and 90 deletions

View File

@ -219,6 +219,7 @@ public:
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 @@ public:
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;

View File

@ -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<int>(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize);
int activation_height = llmq::utils::GetV20Since(pindex) + static_cast<int>(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize);
if (nBlockHeight >= activation_height) {
fV20Active = true;
}

View File

@ -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

View File

@ -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 @@ class DashGovernanceTest (DashTestFramework):
"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 @@ class DashGovernanceTest (DashTestFramework):
"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 @@ class DashGovernanceTest (DashTestFramework):
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 @@ class DashGovernanceTest (DashTestFramework):
assert_equal(len(self.nodes[0].gobject("list-prepared")), 0)
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
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)
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)
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
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 @@ class DashGovernanceTest (DashTestFramework):
# 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()
# 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)
# 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
self.check_superblockbudget(True)
self.check_superblock()
assert_equal(payments_found, 2)
# 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__':