Merge #6271: feat: simplify and speedup feature_governance.py test by generating less blocks

42dffe8541 chore: improve logging of functional tests feature_governance.py (Konstantin Akimov)
922b796800 feat: simplify and speedup feature_governance.py test by generating less blocks (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  Too many blocks are generated in a functional tests `feature_governance.py`

  ## What was done?
  1. Activate v20 faster for `feature_governance.py`, drop generation of useless blocks, simplify code.

  2. Added extra logging for this test by converting comments to logs.

  ## How Has This Been Tested?
  Run `test/functional/feature_governance.py`
  Locally improvement of performance is less than 5 seconds.
  `tsan` jobs on CI:
  ```
  204/264 - feature_governance.py --legacy-wallet passed, Duration: 243 s
  208/264 - feature_governance.py --descriptors passed, Duration: 270 s
  ```
  ↑ [old version](https://gitlab.com/dashpay/dash/-/jobs/7805625816) vs [new version](https://gitlab.com/dashpay/dash/-/jobs/7819897536) ↓:
  ```
  193/264 - feature_governance.py --legacy-wallet passed, Duration: 220 s
  199/264 - feature_governance.py --descriptors passed, Duration: 240 s
  ```

  Most of the time is spent in `feature_governance.py` on stage "let all fulfilled requests expire for re-sync to work correctly" so that's a very minor optimization.

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] 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

ACKs for top commit:
  UdjinM6:
    utACK 42dffe8541
  PastaPastaPasta:
    utACK 42dffe8541

Tree-SHA512: 9257301043efca8b3775fff64e598b153e00bc48779212e6237ab63f06a043a66764d72e38d9e6071b82477e55884b9bce410f4d3fabeb13da631505113e86bc
This commit is contained in:
pasta 2024-09-16 21:15:28 -05:00
commit 05740e8264
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984

View File

@ -17,16 +17,16 @@ class DashGovernanceTest (DashTestFramework):
def set_test_params(self):
self.set_dash_test_params(6, 5, [[
"-budgetparams=10:10:10",
'-testactivationheight=v20@240',
'-testactivationheight=v20@160',
]] * 6, fast_dip3_enforcement=True)
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)
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(120), self.expected_old_budget)
assert_equal(self.nodes[0].getsuperblockbudget(140), self.expected_old_budget)
assert_equal(self.nodes[0].getsuperblockbudget(160), self.expected_v20_budget)
assert_equal(self.nodes[0].getsuperblockbudget(180), self.expected_v20_budget)
def check_superblock(self):
# Make sure Superblock has only payments that fit into the budget
@ -53,6 +53,7 @@ class DashGovernanceTest (DashTestFramework):
assert_equal(payments_found, 2)
def run_test(self):
self.log.info("Start testing...")
governance_info = self.nodes[0].getgovernanceinfo()
assert_equal(governance_info['governanceminquorum'], 1)
assert_equal(governance_info['proposalfee'], 1)
@ -78,7 +79,7 @@ class DashGovernanceTest (DashTestFramework):
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_old_budget = satoshi_round("1000")
self.expected_v20_budget = satoshi_round("18.57142860")
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 4070908800)
@ -87,26 +88,23 @@ class DashGovernanceTest (DashTestFramework):
assert_equal(len(self.nodes[0].gobject("list-prepared")), 0)
# TODO: drop these extra 80 blocks - doesn't work without them
for _ in range(8):
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()
self.log.info("Check 1st superblock before v20")
self.nodes[0].generate(3)
self.bump_mocktime(3)
self.sync_blocks()
assert_equal(self.nodes[0].getblockcount(), 210)
assert_equal(self.nodes[0].getblockcount(), 130)
assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["active"], False)
self.check_superblockbudget(False)
self.log.info("Check 2nd superblock before v20")
self.nodes[0].generate(10)
self.bump_mocktime(10)
self.sync_blocks()
assert_equal(self.nodes[0].getblockcount(), 220)
assert_equal(self.nodes[0].getblockcount(), 140)
assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["active"], False)
self.check_superblockbudget(False)
self.log.info("Prepare proposals")
proposal_time = self.mocktime
self.p0_payout_address = self.nodes[0].getnewaddress()
self.p1_payout_address = self.nodes[0].getnewaddress()
@ -162,10 +160,10 @@ class DashGovernanceTest (DashTestFramework):
block_count = self.nodes[0].getblockcount()
# Move until 1 block before the Superblock maturity window starts
self.log.info("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
self.log.info("v20 is expected to be activate since block 160")
assert block_count + n < 160
for _ in range(n - 1):
self.nodes[0].generate(1)
self.bump_mocktime(1)
@ -174,7 +172,7 @@ class DashGovernanceTest (DashTestFramework):
assert_equal(len(self.nodes[0].gobject("list", "valid", "triggers")), 0)
# Detect payee node
self.log.info("Detect payee node")
mn_list = self.nodes[0].protx("list", "registered", True)
height_protx_list = []
for mn in mn_list:
@ -190,54 +188,54 @@ class DashGovernanceTest (DashTestFramework):
break
assert payee_idx is not None
# Isolate payee node and create a trigger
self.log.info("Isolate payee node and create a trigger")
self.isolate_node(payee_idx)
isolated = self.nodes[payee_idx]
# Move 1 block inside the Superblock maturity window on the isolated node
self.log.info("Move 1 block inside the Superblock maturity window on the isolated node")
isolated.generate(1)
self.bump_mocktime(1)
# The isolated "winner" should submit new trigger and vote for it
self.log.info("The isolated 'winner' should submit new trigger and vote for it")
self.wait_until(lambda: len(isolated.gobject("list", "valid", "triggers")) == 1, timeout=5)
isolated_trigger_hash = list(isolated.gobject("list", "valid", "triggers").keys())[0]
self.wait_until(lambda: list(isolated.gobject("list", "valid", "triggers").values())[0]['YesCount'] == 1, timeout=5)
more_votes = wait_until_helper(lambda: list(isolated.gobject("list", "valid", "triggers").values())[0]['YesCount'] > 1, timeout=5, do_assert=False)
assert_equal(more_votes, False)
# Move 1 block enabling the Superblock maturity window on non-isolated nodes
self.log.info("Move 1 block enabling the Superblock maturity window on non-isolated nodes")
self.nodes[0].generate(1)
self.bump_mocktime(1)
assert_equal(self.nodes[0].getblockcount(), 230)
assert_equal(self.nodes[0].getblockcount(), 150)
assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["active"], False)
self.check_superblockbudget(False)
# The "winner" should submit new trigger and vote for it, but it's isolated so no triggers should be found
self.log.info("The 'winner' should submit new trigger and vote for it, but it's isolated so no triggers should be found")
has_trigger = wait_until_helper(lambda: len(self.nodes[0].gobject("list", "valid", "triggers")) >= 1, timeout=5, do_assert=False)
assert_equal(has_trigger, False)
# Move 1 block inside the Superblock maturity window on non-isolated nodes
self.log.info("Move 1 block inside the Superblock maturity window on non-isolated nodes")
self.nodes[0].generate(1)
self.bump_mocktime(1)
# There is now new "winner" who should submit new trigger and vote for it
self.log.info("There is now new 'winner' who should submit new trigger and vote for it")
self.wait_until(lambda: len(self.nodes[0].gobject("list", "valid", "triggers")) == 1, timeout=5)
winning_trigger_hash = list(self.nodes[0].gobject("list", "valid", "triggers").keys())[0]
self.wait_until(lambda: list(self.nodes[0].gobject("list", "valid", "triggers").values())[0]['YesCount'] == 1, timeout=5)
more_votes = wait_until_helper(lambda: list(self.nodes[0].gobject("list", "valid", "triggers").values())[0]['YesCount'] > 1, timeout=5, do_assert=False)
assert_equal(more_votes, False)
# Make sure amounts aren't trimmed
self.log.info("Make sure amounts aren't trimmed")
payment_amounts_expected = [str(satoshi_round(str(self.p0_amount))), str(satoshi_round(str(self.p1_amount))), str(satoshi_round(str(self.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 another block inside the Superblock maturity window on non-isolated nodes
self.log.info("Move another block inside the Superblock maturity window on non-isolated nodes")
self.nodes[0].generate(1)
self.bump_mocktime(1)
# Every non-isolated MN should vote for the same trigger now, no new triggers should be created
self.log.info("Every non-isolated MN should vote for the same trigger now, no new triggers should be created")
self.wait_until(lambda: list(self.nodes[0].gobject("list", "valid", "triggers").values())[0]['YesCount'] == self.mn_count - 1, timeout=5)
more_triggers = wait_until_helper(lambda: len(self.nodes[0].gobject("list", "valid", "triggers")) > 1, timeout=5, do_assert=False)
assert_equal(more_triggers, False)
@ -251,9 +249,9 @@ class DashGovernanceTest (DashTestFramework):
self.bump_mocktime(1)
return node.mnsync("status")["IsSynced"]
# make sure isolated node is fully synced at this point
self.log.info("make sure isolated node is fully synced at this point")
self.wait_until(lambda: sync_gov(isolated))
# let all fulfilled requests expire for re-sync to work correctly
self.log.info("let all fulfilled requests expire for re-sync to work correctly")
self.bump_mocktime(5 * 60)
for node in self.nodes:
@ -263,53 +261,53 @@ class DashGovernanceTest (DashTestFramework):
node.mnsync("next")
self.wait_until(lambda: sync_gov(node))
# Should see two triggers now
self.log.info("Should see two triggers now")
self.wait_until(lambda: len(isolated.gobject("list", "valid", "triggers")) == 2, timeout=5)
self.wait_until(lambda: len(self.nodes[0].gobject("list", "valid", "triggers")) == 2, timeout=5)
more_triggers = wait_until_helper(lambda: len(self.nodes[0].gobject("list", "valid", "triggers")) > 2, timeout=5, do_assert=False)
assert_equal(more_triggers, False)
# Move another block inside the Superblock maturity window
self.log.info("Move another block inside the Superblock maturity window")
self.nodes[0].generate(1)
self.bump_mocktime(1)
self.sync_blocks()
# Should see NO votes on both triggers now
self.log.info("Should see NO votes on both triggers now")
self.wait_until(lambda: self.nodes[0].gobject("list", "valid", "triggers")[winning_trigger_hash]['NoCount'] == 1, timeout=5)
self.wait_until(lambda: self.nodes[0].gobject("list", "valid", "triggers")[isolated_trigger_hash]['NoCount'] == self.mn_count - 1, timeout=5)
# Remember vote count
self.log.info("Remember vote count")
before = self.nodes[1].gobject("count")["votes"]
# Bump mocktime to let MNs vote again
self.log.info("Bump mocktime to let MNs vote again")
self.bump_mocktime(GOVERNANCE_UPDATE_MIN + 1, update_schedulers=False)
# Move another block inside the Superblock maturity window
self.log.info("Move another block inside the Superblock maturity window")
with self.nodes[1].assert_debug_log(["CGovernanceManager::VoteGovernanceTriggers"]):
self.nodes[0].generate(1)
self.bump_mocktime(1)
self.sync_blocks()
# Vote count should not change even though MNs are allowed to vote again
self.log.info("Vote count should not change even though MNs are allowed to vote again")
assert_equal(before, self.nodes[1].gobject("count")["votes"])
# Revert mocktime back to avoid issues in tests below
self.log.info("Revert mocktime back to avoid issues in tests below")
self.bump_mocktime(GOVERNANCE_UPDATE_MIN * -1, update_schedulers=False)
block_count = self.nodes[0].getblockcount()
n = sb_cycle - block_count % sb_cycle
# Move remaining n blocks until actual Superblock
self.log.info("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)
# comparing to 159 because bip9 forks are active when the tip is one block behind the activation height
self.check_superblockbudget(block_count + i + 1 >= 159)
self.check_superblockbudget(True)
self.check_superblock()
# Move a few block past the recent superblock height and make sure we have no new votes
self.log.info("Move a few block past the recent superblock height and make sure we have no new votes")
for _ in range(5):
with self.nodes[1].assert_debug_log("", [f"Voting NO-FUNDING for trigger:{winning_trigger_hash} success"]):
self.nodes[0].generate(1)
@ -322,28 +320,28 @@ class DashGovernanceTest (DashTestFramework):
block_count = self.nodes[0].getblockcount()
n = sb_cycle - block_count % sb_cycle
# Move remaining n blocks until the next Superblock
self.log.info("Move remaining n blocks until the next Superblock")
for _ in range(n - 1):
self.nodes[0].generate(1)
self.bump_mocktime(1)
self.sync_blocks()
# Wait for new trigger and votes
self.wait_until(lambda: have_trigger_for_height(self.nodes, 260))
# Mine superblock
self.log.info("Wait for new trigger and votes")
self.wait_until(lambda: have_trigger_for_height(self.nodes, 180))
self.log.info("Mine superblock")
self.nodes[0].generate(1)
self.bump_mocktime(1)
self.sync_blocks()
assert_equal(self.nodes[0].getblockcount(), 260)
assert_equal(self.nodes[0].getblockcount(), 180)
assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["active"], True)
# Mine and check a couple more superblocks
self.log.info("Mine and check a couple more superblocks")
for i in range(2):
for _ in range(sb_cycle - 1):
self.nodes[0].generate(1)
self.bump_mocktime(1)
self.sync_blocks()
# Wait for new trigger and votes
sb_block_height = 260 + (i + 1) * sb_cycle
sb_block_height = 180 + (i + 1) * sb_cycle
self.wait_until(lambda: have_trigger_for_height(self.nodes, sb_block_height))
# Mine superblock
self.nodes[0].generate(1)