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): def set_test_params(self):
self.set_dash_test_params(6, 5, [[ self.set_dash_test_params(6, 5, [[
"-budgetparams=10:10:10", "-budgetparams=10:10:10",
'-testactivationheight=v20@240', '-testactivationheight=v20@160',
]] * 6, fast_dip3_enforcement=True) ]] * 6, fast_dip3_enforcement=True)
def check_superblockbudget(self, v20_active): def check_superblockbudget(self, v20_active):
v20_state = self.nodes[0].getblockchaininfo()["softforks"]["v20"] v20_state = self.nodes[0].getblockchaininfo()["softforks"]["v20"]
assert_equal(v20_state["active"], v20_active) assert_equal(v20_state["active"], v20_active)
assert_equal(self.nodes[0].getsuperblockbudget(200), self.expected_old_budget) assert_equal(self.nodes[0].getsuperblockbudget(120), self.expected_old_budget)
assert_equal(self.nodes[0].getsuperblockbudget(220), self.expected_old_budget) assert_equal(self.nodes[0].getsuperblockbudget(140), self.expected_old_budget)
assert_equal(self.nodes[0].getsuperblockbudget(240), self.expected_v20_budget) assert_equal(self.nodes[0].getsuperblockbudget(160), self.expected_v20_budget)
assert_equal(self.nodes[0].getsuperblockbudget(260), self.expected_v20_budget) assert_equal(self.nodes[0].getsuperblockbudget(180), self.expected_v20_budget)
def check_superblock(self): def check_superblock(self):
# Make sure Superblock has only payments that fit into the budget # Make sure Superblock has only payments that fit into the budget
@ -53,6 +53,7 @@ class DashGovernanceTest (DashTestFramework):
assert_equal(payments_found, 2) assert_equal(payments_found, 2)
def run_test(self): def run_test(self):
self.log.info("Start testing...")
governance_info = self.nodes[0].getgovernanceinfo() governance_info = self.nodes[0].getgovernanceinfo()
assert_equal(governance_info['governanceminquorum'], 1) assert_equal(governance_info['governanceminquorum'], 1)
assert_equal(governance_info['proposalfee'], 1) assert_equal(governance_info['proposalfee'], 1)
@ -78,7 +79,7 @@ class DashGovernanceTest (DashTestFramework):
sb_cycle = 20 sb_cycle = 20
sb_maturity_window = 10 sb_maturity_window = 10
sb_immaturity_window = sb_cycle - sb_maturity_window 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.expected_v20_budget = satoshi_round("18.57142860")
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 4070908800) 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) assert_equal(len(self.nodes[0].gobject("list-prepared")), 0)
# TODO: drop these extra 80 blocks - doesn't work without them self.log.info("Check 1st superblock before v20")
for _ in range(8):
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()
self.nodes[0].generate(3) self.nodes[0].generate(3)
self.bump_mocktime(3) self.bump_mocktime(3)
self.sync_blocks() 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) assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["active"], False)
self.check_superblockbudget(False) self.check_superblockbudget(False)
self.log.info("Check 2nd superblock before v20")
self.nodes[0].generate(10) self.nodes[0].generate(10)
self.bump_mocktime(10) self.bump_mocktime(10)
self.sync_blocks() 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) assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["active"], False)
self.check_superblockbudget(False) self.check_superblockbudget(False)
self.log.info("Prepare proposals")
proposal_time = self.mocktime proposal_time = self.mocktime
self.p0_payout_address = self.nodes[0].getnewaddress() self.p0_payout_address = self.nodes[0].getnewaddress()
self.p1_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() 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 n = sb_immaturity_window - block_count % sb_cycle
# v20 is expected to be activate since block 240 self.log.info("v20 is expected to be activate since block 160")
assert block_count + n < 240 assert block_count + n < 160
for _ in range(n - 1): for _ in range(n - 1):
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.bump_mocktime(1) self.bump_mocktime(1)
@ -174,7 +172,7 @@ class DashGovernanceTest (DashTestFramework):
assert_equal(len(self.nodes[0].gobject("list", "valid", "triggers")), 0) 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) mn_list = self.nodes[0].protx("list", "registered", True)
height_protx_list = [] height_protx_list = []
for mn in mn_list: for mn in mn_list:
@ -190,54 +188,54 @@ class DashGovernanceTest (DashTestFramework):
break break
assert payee_idx is not None 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) self.isolate_node(payee_idx)
isolated = self.nodes[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) isolated.generate(1)
self.bump_mocktime(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) self.wait_until(lambda: len(isolated.gobject("list", "valid", "triggers")) == 1, timeout=5)
isolated_trigger_hash = list(isolated.gobject("list", "valid", "triggers").keys())[0] 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) 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) 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) 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.nodes[0].generate(1)
self.bump_mocktime(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) assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["v20"]["active"], False)
self.check_superblockbudget(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) 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) 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.nodes[0].generate(1)
self.bump_mocktime(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) 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] 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) 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) 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) 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)))] 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"] data_string = list(self.nodes[0].gobject("list", "valid", "triggers").values())[0]["DataString"]
payment_amounts_trigger = json.loads(data_string)["payment_amounts"].split("|") payment_amounts_trigger = json.loads(data_string)["payment_amounts"].split("|")
for amount_str in payment_amounts_trigger: for amount_str in payment_amounts_trigger:
assert(amount_str in payment_amounts_expected) 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.nodes[0].generate(1)
self.bump_mocktime(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) 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) 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) assert_equal(more_triggers, False)
@ -251,9 +249,9 @@ class DashGovernanceTest (DashTestFramework):
self.bump_mocktime(1) self.bump_mocktime(1)
return node.mnsync("status")["IsSynced"] 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)) 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) self.bump_mocktime(5 * 60)
for node in self.nodes: for node in self.nodes:
@ -263,53 +261,53 @@ class DashGovernanceTest (DashTestFramework):
node.mnsync("next") node.mnsync("next")
self.wait_until(lambda: sync_gov(node)) 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(isolated.gobject("list", "valid", "triggers")) == 2, timeout=5)
self.wait_until(lambda: len(self.nodes[0].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) 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) 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.nodes[0].generate(1)
self.bump_mocktime(1) self.bump_mocktime(1)
self.sync_blocks() 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")[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) 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"] 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) 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"]): with self.nodes[1].assert_debug_log(["CGovernanceManager::VoteGovernanceTriggers"]):
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.bump_mocktime(1) self.bump_mocktime(1)
self.sync_blocks() 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"]) 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) self.bump_mocktime(GOVERNANCE_UPDATE_MIN * -1, update_schedulers=False)
block_count = self.nodes[0].getblockcount() block_count = self.nodes[0].getblockcount()
n = sb_cycle - block_count % sb_cycle 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): for i in range(n):
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.bump_mocktime(1) self.bump_mocktime(1)
self.sync_blocks() self.sync_blocks()
# comparing to 239 because bip9 forks are active when the tip is one block behind the activation height # 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 >= 239) self.check_superblockbudget(block_count + i + 1 >= 159)
self.check_superblockbudget(True) self.check_superblockbudget(True)
self.check_superblock() 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): for _ in range(5):
with self.nodes[1].assert_debug_log("", [f"Voting NO-FUNDING for trigger:{winning_trigger_hash} success"]): with self.nodes[1].assert_debug_log("", [f"Voting NO-FUNDING for trigger:{winning_trigger_hash} success"]):
self.nodes[0].generate(1) self.nodes[0].generate(1)
@ -322,28 +320,28 @@ class DashGovernanceTest (DashTestFramework):
block_count = self.nodes[0].getblockcount() block_count = self.nodes[0].getblockcount()
n = sb_cycle - block_count % sb_cycle 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): for _ in range(n - 1):
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.bump_mocktime(1) self.bump_mocktime(1)
self.sync_blocks() self.sync_blocks()
# Wait for new trigger and votes self.log.info("Wait for new trigger and votes")
self.wait_until(lambda: have_trigger_for_height(self.nodes, 260)) self.wait_until(lambda: have_trigger_for_height(self.nodes, 180))
# Mine superblock self.log.info("Mine superblock")
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.bump_mocktime(1) self.bump_mocktime(1)
self.sync_blocks() 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) 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 i in range(2):
for _ in range(sb_cycle - 1): for _ in range(sb_cycle - 1):
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.bump_mocktime(1) self.bump_mocktime(1)
self.sync_blocks() self.sync_blocks()
# Wait for new trigger and votes # 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)) self.wait_until(lambda: have_trigger_for_height(self.nodes, sb_block_height))
# Mine superblock # Mine superblock
self.nodes[0].generate(1) self.nodes[0].generate(1)