mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 04:22:55 +01:00
965f5b2063
## 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)_
247 lines
12 KiB
Python
Executable File
247 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2018-2020 The Dash Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
"""Tests around dash governance."""
|
|
|
|
import json
|
|
|
|
from test_framework.messages import uint256_to_string
|
|
from test_framework.test_framework import DashTestFramework
|
|
from test_framework.util import assert_equal, satoshi_round, set_node_times, wait_until
|
|
|
|
class DashGovernanceTest (DashTestFramework):
|
|
def set_test_params(self):
|
|
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
|
|
proposal_time = int(creation_time)
|
|
proposal_template = {
|
|
"type": object_type,
|
|
"name": name,
|
|
"start_epoch": proposal_time,
|
|
"end_epoch": proposal_time + 24 * 60 * 60,
|
|
"payment_amount": float(amount),
|
|
"payment_address": payment_address,
|
|
"url": "https://dash.org"
|
|
}
|
|
proposal_hex = ''.join(format(x, '02x') for x in json.dumps(proposal_template).encode())
|
|
collateral_hash = self.nodes[0].gobject("prepare", parent_hash, proposal_rev, proposal_time, proposal_hex)
|
|
return {
|
|
"parentHash": parent_hash,
|
|
"collateralHash": collateral_hash,
|
|
"createdAt": proposal_time,
|
|
"revision": proposal_rev,
|
|
"hex": proposal_hex,
|
|
"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",
|
|
1: "yes",
|
|
2: "no",
|
|
3: "abstain"
|
|
}
|
|
map_vote_signals = {
|
|
0: "none",
|
|
1: "funding",
|
|
2: "valid",
|
|
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)
|
|
self.wait_for_sporks_same()
|
|
|
|
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
|
|
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)
|
|
|
|
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", 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", 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", 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", 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", 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", 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()
|
|
|
|
# Move until 1 block before the Superblock maturity window starts
|
|
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()
|
|
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
|
|
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)))]
|
|
data_string = list(self.nodes[0].gobject("list", "valid", "triggers").values())[0]["DataString"]
|
|
payment_amounts_trigger = json.loads(data_string)["payment_amounts"].split("|")
|
|
for amount_str in payment_amounts_trigger:
|
|
assert(amount_str in payment_amounts_expected)
|
|
|
|
# Move 1 block inside the Superblock maturity window
|
|
self.nodes[0].generate(1)
|
|
self.bump_mocktime(1)
|
|
self.sync_blocks()
|
|
|
|
# Every MN should vote for the same trigger now, no new triggers should be created
|
|
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):
|
|
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)
|
|
|
|
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__':
|
|
DashGovernanceTest().main()
|