Skip to content

Conversation

knst
Copy link
Collaborator

@knst knst commented Aug 29, 2025

Issue being fixed or feature implemented

These functional tests are one of the slowest to run with tsan on CI:

feature_llmq_chainlocks.py                             | ✓ Passed  | 301 s
feature_llmq_evo.py                                    | ✓ Passed  | 198 s
feature_llmq_rotation.py                               | ✓ Passed  | 315 s

What was done?

feature_llmq_chainlocks.py

It waits only once for all nodes to check missing chainlock; instead of waiting 5 second for each node. Used to be: wait->check->wait->check... Become: wait->check->check->check...

feature_llmq_evo.py

feature_llmq_rotation.py

test_framework

Minor improvements and refactorings:

  • simplified activate_mn_rr
  • simplified wait_for_chainlocked_block_all_nodes by removing flag expected
  • disabled excessive logging in mine_cycle_quorum

How Has This Been Tested?

Run functional tests locally multiple times. Median time is improved:

  • feature_llmq_chainlocks.py: 120s -> 75s (localhost), 301s -> 105s (CI)
  • feature_llmq_evo.py: 108s -> 74s (localhost), 198s -> 118s (CI)
  • feature_llmq_rotation.py: 154s -> 93s (localhost), 315s -> 141s (CI)

Breaking Changes

N/A

Checklist:

  • 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
  • I have assigned this pull request to a milestone

@knst knst added this to the 23 milestone Aug 29, 2025
Copy link

github-actions bot commented Aug 29, 2025

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

Copy link

coderabbitai bot commented Aug 29, 2025

Walkthrough

  • activate_mn_rr was unified to take no arguments in framework and tests; tests now rely on the framework's mn_rr_height attribute.
  • wait_for_chainlocked_block_all_nodes dropped the expected parameter; per-node waits no longer pass an expected flag.
  • Added sleep_and_assert_no_cl(block_hash, sleep_time=5) and an import time to the LLMQ chainlocks test; replaced a poll-based negative chainlock check with a fixed sleep-and-assert.
  • LLMQ rotation: per-node vbparams added, mn_rr_height moved to a dynamic value (300), removed 102-type quorums and their size mapping, and updated expected quorum lists and logs.
  • LLMQ evo: test node/evo counts and mn_rr_height adjusted, InstantSend spork toggles removed, iteration and masternode-list assertions tightened, and logging modernized.
  • mine_cycle_quorum gained an extra_debug_rotation_info flag controlling additional debug logs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title references the relevant test files but remains overly generic by using “various improvements” and listing filenames instead of concisely summarizing the core changes in a single clear sentence. Consider rephrasing the title to highlight the primary change and its impact, for example: “test: reduce runtime and fix instabilities in LLMQ chainlocks, Evo, and rotation functional tests.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed The description directly correlates with the changeset by outlining the specific modifications in each test file, performance improvements observed, and additional context such as testing methodology and checklist status.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
test/functional/feature_llmq_chainlocks.py (1)

26-34: Tighten helper and fix flake8 E265; reduce log spam.

  • Replace per-node log lines with a single log.
  • Remove/comment-space the “confirmations” commented assert to satisfy E265 and avoid confusion (not required and may be false).
  • Allow adjustable delay.

Apply:

-def sleep_and_assert_no_cl(self, block_hash):
-    time.sleep(5)
-
-    for node in self.nodes:
-        self.log.info(f"Expecting no ChainLock for {block_hash}")
-        block = node.getblock(block_hash)
-        #assert_equal(block["confirmations"], 0)
-        assert not block["chainlock"]
+def sleep_and_assert_no_cl(self, block_hash, delay=5):
+    time.sleep(delay)
+    self.log.info(f"Expecting no ChainLock for {block_hash} on all nodes")
+    for node in self.nodes:
+        block = node.getblock(block_hash)
+        # assert_equal(block["confirmations"], 0)  # not required; CL status alone is checked
+        assert not block["chainlock"]
test/functional/feature_llmq_evo.py (1)

176-191: Robust Evo presence checks in both lists — good.

  • Validates type and membership in masternodelist() and masternodelist(mode="evo").
  • Minor nit: consider early-break once found to avoid N^2 scan, albeit tiny here.
-        for _, mn in mn_list.items():
+        for _, mn in mn_list.items():
             if mn['proTxHash'] == evo_protx:
                 found_in_mns = True
                 assert_equal(mn['type'], "Evo")
-        ...
+                break
test/functional/test_framework/test_framework.py (2)

2312-2322: Gate extra rotation debug via env var to avoid code edits.

Keeps logs off by default but easy to enable in CI/dev.

-extra_debug_rotation_info = False
-if extra_debug_rotation_info:
+extra_debug_rotation_info = bool(int(os.getenv("EXTRA_DEBUG_ROTATION_INFO", "0")))
+if extra_debug_rotation_info:

1956-1959: Confirm removal of expected parameter support and optionally provide a no-chainlock helper

The wait_for_chainlocked_block_all_nodes helper no longer accepts expected=False and all tests have been updated to avoid negative waits. To simplify future tests and prevent per-test sleeps, consider adding an assert_no_chainlock_all_nodes(block_hash, timeout=…) helper that polls each node and raises if a chainlock appears.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 873dee0 and 2903c11.

📒 Files selected for processing (5)
  • test/functional/feature_asset_locks.py (1 hunks)
  • test/functional/feature_llmq_chainlocks.py (2 hunks)
  • test/functional/feature_llmq_evo.py (5 hunks)
  • test/functional/feature_llmq_rotation.py (4 hunks)
  • test/functional/test_framework/test_framework.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests should be written in Python and placed in test/functional/

Files:

  • test/functional/feature_asset_locks.py
  • test/functional/feature_llmq_chainlocks.py
  • test/functional/feature_llmq_evo.py
  • test/functional/test_framework/test_framework.py
  • test/functional/feature_llmq_rotation.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: kwvg
PR: dashpay/dash#6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
PR: dashpay/dash#6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Applied to files:

  • test/functional/feature_llmq_evo.py
🧬 Code graph analysis (3)
test/functional/feature_asset_locks.py (1)
test/functional/test_framework/test_framework.py (1)
  • activate_mn_rr (1581-1582)
test/functional/feature_llmq_evo.py (2)
test/functional/test_framework/test_framework.py (1)
  • set_dash_test_params (1508-1530)
test/functional/test_framework/util.py (1)
  • assert_equal (69-74)
test/functional/feature_llmq_rotation.py (1)
test/functional/test_framework/test_framework.py (4)
  • set_dash_test_params (1508-1530)
  • set_dash_llmq_test_params (1584-1590)
  • delay_v20_and_mn_rr (1533-1535)
  • activate_v20 (1578-1579)
🪛 Flake8 (7.2.0)
test/functional/feature_llmq_chainlocks.py

[error] 32-32: block comment should start with '# '

(E265)

🔇 Additional comments (12)
test/functional/feature_asset_locks.py (1)

627-627: No remaining activate_mn_rr calls with arguments—approved.
Grep only matched the method definition, confirming no legacy usages remain.

test/functional/feature_llmq_chainlocks.py (2)

19-19: Add time import — OK.

Used by the new helper; no issues.


97-97: Runtime improvement preserved while matching new framework API — good.

Single 5s wait for all nodes replaces 5s-per-node polling.

test/functional/feature_llmq_rotation.py (4)

54-57: Per-node vbparams and earlier activation — good.

  • testdummy vbparams set consistently on all 9 nodes.
  • v20/mn_rr delayed to height 300 via helper for clarity.

109-110: Use mn_rr_height consistently for v20 activation — good.

Avoids hard-coding; aligns tests with framework knobs.


184-185: Second rotation cycle expectations updated — good.

Matches removal of 102-type quorums.


163-165: Approve: No lingering LLMQ type-102 references
Refined search found no instances of llmq_type = 102, QuorumId(102), or q_102 in test/functional—dropping type-102 expectations is safe.

test/functional/feature_llmq_evo.py (4)

52-55: Lean topology and explicit mn_rr_height — good.

3 regular nodes + 4 Evo nodes keeps quorum minimal; explicit 320 height is clear.


70-70: Accurate initial counts.

expected_mns_count=2, expected_evo_count=0 reflects the new topology.


81-81: Progressive Evo count checks — good.

Validates growth per iteration.


96-98: MN RR activation flow/logging — good.

Switch to framework’s parameterless activate_mn_rr and concise f-string log.

test/functional/test_framework/test_framework.py (1)

1581-1583: approve code changes
No callers of activate_mn_rr pass an argument—removing the parameter is safe.

UdjinM6
UdjinM6 previously approved these changes Sep 2, 2025
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 2903c11

@knst knst requested a review from PastaPastaPasta September 9, 2025 14:03
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/functional/feature_llmq_chainlocks.py (1)

26-34: Reduce log noise and clarify scope of the check.

Log once before the loop and downgrade per-node messages to debug.

Apply this diff:

-        for node in self.nodes:
-            self.log.info(f"Expecting no ChainLock for {block_hash}")
+        self.log.info(f"Expecting no ChainLock for {block_hash} on all nodes")
+        for idx, node in enumerate(self.nodes):
+            self.log.debug(f"Checking absence of ChainLock for {block_hash} on node[{idx}]")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2903c11 and 2024c65.

📒 Files selected for processing (1)
  • test/functional/feature_llmq_chainlocks.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests should be written in Python and placed in test/functional/

Files:

  • test/functional/feature_llmq_chainlocks.py
🧠 Learnings (1)
📓 Common learnings
Learnt from: kwvg
PR: dashpay/dash#6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
🪛 Flake8 (7.2.0)
test/functional/feature_llmq_chainlocks.py

[error] 32-32: block comment should start with '# '

(E265)

🔇 Additional comments (2)
test/functional/feature_llmq_chainlocks.py (2)

19-19: LGTM on adding time import.

Needed by the new helper; no issues.


97-97: Good swap: single sleep+assert avoids O(n) per-node waits.

This should cut runtime while preserving correctness in the negative-CL scenario.

Comment on lines +26 to +34
def sleep_and_assert_no_cl(self, block_hash, sleep_time=5):
time.sleep(sleep_time)

for node in self.nodes:
self.log.info(f"Expecting no ChainLock for {block_hash}")
block = node.getblock(block_hash)
#assert_equal(block["confirmations"], 0)
assert not block["chainlock"]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Flake8 E265 and remove stale commented assertion.

The commented line lacks a space after '#', tripping E265. It’s also misleading (confirmations can be >0). Remove it.

Apply this diff:

-            #assert_equal(block["confirmations"], 0)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def sleep_and_assert_no_cl(self, block_hash, sleep_time=5):
time.sleep(sleep_time)
for node in self.nodes:
self.log.info(f"Expecting no ChainLock for {block_hash}")
block = node.getblock(block_hash)
#assert_equal(block["confirmations"], 0)
assert not block["chainlock"]
def sleep_and_assert_no_cl(self, block_hash, sleep_time=5):
time.sleep(sleep_time)
for node in self.nodes:
self.log.info(f"Expecting no ChainLock for {block_hash}")
block = node.getblock(block_hash)
assert not block["chainlock"]
🧰 Tools
🪛 Flake8 (7.2.0)

[error] 32-32: block comment should start with '# '

(E265)

🤖 Prompt for AI Agents
In test/functional/feature_llmq_chainlocks.py around lines 26 to 34, remove the
stale commented assertion line "#assert_equal(block["confirmations"], 0)" to fix
Flake8 E265 (missing space after '#') and to avoid misleading code; leave the
time.sleep, logging, getblock call, and the assert not block["chainlock"] check
intact.

Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 2024c65

@PastaPastaPasta PastaPastaPasta merged commit 45d55e7 into dashpay:develop Sep 23, 2025
32 of 33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants