Skip to content

Conversation

@knst
Copy link
Collaborator

@knst knst commented Jul 11, 2025

Issue being fixed or feature implemented

Dash Core sends transactions after random delay for privacy reason.

 tx_relay->m_next_inv_send_time = GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL)

This expential random delay could be long as 2s * 10 = 20 seconds or even longer sometimes.

What was done?

Prior work: #6631

To be sure that helper wait_for_instantsend is reliable need to bump mocktime for 20+ second, or even better 30 seconds.

It fixes potential instability in functional tests and make them more deterministic and faster to run.
Decreased delay for ChainLocks also.

How Has This Been Tested?

Run functional tests several times; no new failures are noticed.

Minor performance improvement for functional tests that use wait_for_instantlock:

TEST                                  | STATUS    | DURATION

feature_asset_locks.py                | ✓ Passed  | 114 s
feature_llmq_is_cl_conflicts.py       | ✓ Passed  | 47 s
feature_llmq_is_retroactive.py        | ✓ Passed  | 135 s
feature_llmq_singlenode.py            | ✓ Passed  | 56 s
feature_notifications.py              | ✓ Passed  | 74 s
interface_zmq_dash.py --legacy-wallet | ✓ Passed  | 45 s
p2p_instantsend.py                    | ✓ Passed  | 49 s
rpc_verifyislock.py                   | ✓ Passed  | 39 s

ALL                                   | ✓ Passed  | 559 s (accumulated)
Runtime: 135 s

--->

TEST                                  | STATUS    | DURATION

feature_asset_locks.py                | ✓ Passed  | 113 s
feature_llmq_is_cl_conflicts.py       | ✓ Passed  | 33 s
feature_llmq_is_retroactive.py        | ✓ Passed  | 126 s
feature_llmq_singlenode.py            | ✓ Passed  | 37 s
feature_notifications.py              | ✓ Passed  | 29 s
interface_zmq_dash.py --legacy-wallet | ✓ Passed  | 41 s
p2p_instantsend.py                    | ✓ Passed  | 44 s
rpc_verifyislock.py                   | ✓ Passed  | 37 s

ALL                                   | ✓ Passed  | 460 s (accumulated)
Runtime: 126 s 

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 added 4 commits July 10, 2025 00:15
It bumps mocktime properly for all instantsend lock

Dash Core sends transactions after random delay for privacy reason.

     tx_relay->m_next_inv_send_time = GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL)

This expential random delay once in 1000 tries is longer than 2s * 10 = 20 seconds
To be sure that helper wait_for_instantsend is reliable need to bump mocktime for 20+ second,
or even better 30 seconds
@coderabbitai
Copy link

coderabbitai bot commented Jul 11, 2025

Walkthrough

This set of changes updates several functional test files to modify the handling of simulated time progression during test execution. Across multiple test cases, calls to bump_mocktime(30) were added or replaced shorter increments, advancing the mock time by 30 seconds at key points such as after sending transactions and before waiting for InstantSend locks or transaction propagation. In the test framework, the wait_for_tx method was removed, and the wait_for_instantlock method no longer bumps mock time internally, with a new comment advising manual mock time advancement before waiting for instant locks. One test file introduces its own wait_for_tx helper. No changes were made to public interfaces except for the removal of the framework's wait_for_tx method and the addition of a similarly named method in a specific test class.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3bac0a4 and 1a5cee6.

📒 Files selected for processing (9)
  • test/functional/feature_asset_locks.py (1 hunks)
  • test/functional/feature_llmq_is_cl_conflicts.py (2 hunks)
  • test/functional/feature_llmq_is_retroactive.py (7 hunks)
  • test/functional/feature_llmq_singlenode.py (2 hunks)
  • test/functional/feature_notifications.py (1 hunks)
  • test/functional/interface_zmq_dash.py (3 hunks)
  • test/functional/p2p_instantsend.py (4 hunks)
  • test/functional/rpc_verifyislock.py (1 hunks)
  • test/functional/test_framework/test_framework.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
test/functional/**/*.py

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • CLAUDE.md
🧠 Learnings (8)
📓 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.
Learnt from: kwvg
PR: dashpay/dash#6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: CR
PR: dashpay/dash#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-10T16:26:00.331Z
Learning: Applies to src/{test,wallet/test,qt/test}/**/*.{cpp,h,cc,cxx,hpp} : Unit tests should be implemented in src/test/, src/wallet/test/, and src/qt/test/ using Boost::Test or Qt 5 for GUI tests
test/functional/rpc_verifyislock.py (1)
Learnt from: kwvg
PR: dashpay/dash#6726
File: test/functional/rpc_createmultisig.py:120-120
Timestamp: 2025-06-20T23:32:16.225Z
Learning: In the rpc_createmultisig.py test, the checkbalances() function correctly excludes 9 blocks from the balance calculation: 8 blocks from do_multisig() calls (2 blocks per call × 4 calls) plus 1 block from checkbalances() itself.
test/functional/feature_notifications.py (1)
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.
test/functional/feature_llmq_singlenode.py (1)
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.
test/functional/p2p_instantsend.py (3)
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.
Learnt from: kwvg
PR: dashpay/dash#6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: kwvg
PR: dashpay/dash#6726
File: test/functional/rpc_createmultisig.py:120-120
Timestamp: 2025-06-20T23:32:16.225Z
Learning: In the rpc_createmultisig.py test, the checkbalances() function correctly excludes 9 blocks from the balance calculation: 8 blocks from do_multisig() calls (2 blocks per call × 4 calls) plus 1 block from checkbalances() itself.
test/functional/feature_llmq_is_cl_conflicts.py (2)
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.
Learnt from: kwvg
PR: dashpay/dash#6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
test/functional/feature_llmq_is_retroactive.py (1)
Learnt from: kwvg
PR: dashpay/dash#6726
File: test/functional/rpc_createmultisig.py:120-120
Timestamp: 2025-06-20T23:32:16.225Z
Learning: In the rpc_createmultisig.py test, the checkbalances() function correctly excludes 9 blocks from the balance calculation: 8 blocks from do_multisig() calls (2 blocks per call × 4 calls) plus 1 block from checkbalances() itself.
test/functional/test_framework/test_framework.py (1)
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.
🧬 Code Graph Analysis (5)
test/functional/rpc_verifyislock.py (1)
test/functional/test_framework/test_framework.py (1)
  • bump_mocktime (872-889)
test/functional/feature_asset_locks.py (1)
test/functional/test_framework/test_framework.py (1)
  • bump_mocktime (872-889)
test/functional/feature_llmq_singlenode.py (1)
test/functional/test_framework/test_framework.py (1)
  • bump_mocktime (872-889)
test/functional/feature_llmq_is_cl_conflicts.py (1)
test/functional/test_framework/test_framework.py (1)
  • bump_mocktime (872-889)
test/functional/interface_zmq_dash.py (1)
test/functional/test_framework/test_framework.py (1)
  • bump_mocktime (872-889)
🪛 Ruff (0.11.9)
test/functional/feature_llmq_is_retroactive.py

32-32: Do not use bare except

(E722)

test/functional/test_framework/test_framework.py

1936-1936: Do not use bare except

(E722)

🪛 Flake8 (7.2.0)
test/functional/feature_llmq_is_retroactive.py

[error] 32-32: do not use bare 'except'

(E722)

test/functional/test_framework/test_framework.py

[error] 1936-1936: do not use bare 'except'

(E722)

🔇 Additional comments (14)
test/functional/test_framework/test_framework.py (3)

1928-1931: Good documentation of the timing behavior change.

The comment clearly explains the rationale behind the change and provides guidance on the recommended approach. This will help test developers understand why they need to manually bump mocktime before waiting for instant locks.


1943-1952: Approve the removal of sleep parameter from wait_for_chainlocked_block.

The change simplifies the method by using the default sleep interval from wait_until, which is consistent with the overall goal of standardizing wait behavior in the test framework.


1958-1959: Approve the removal of sleep parameter from wait_for_best_chainlock.

Similar to the other wait methods, this change standardizes the sleep behavior and removes the explicit sleep parameter, making the method more consistent with the framework's default behavior.

test/functional/rpc_verifyislock.py (1)

41-41: LGTM: Proper timing adjustment for InstantSend testing.

The addition of bump_mocktime(30) after sending the transaction and before waiting for instantlock aligns with the PR objective to reduce random delays and improve test reliability. This change ensures deterministic timing behavior for InstantSend lock verification tests.

test/functional/feature_llmq_singlenode.py (2)

107-107: LGTM: Consistent timing adjustment for InstantSend.

The addition of bump_mocktime(30) after sending funds and before waiting for instantlock ensures deterministic timing behavior in the single node quorum test. This change is consistent with the PR's objective to standardize InstantSend timing in tests.


185-185: LGTM: Proper timing for chainlock and InstantSend test.

The bump_mocktime(30) addition in the chainlock and InstantSend test section maintains consistency with the timing adjustments made throughout the codebase. This ensures reliable test execution when testing both chainlocks and InstantSend together.

test/functional/feature_llmq_is_cl_conflicts.py (2)

99-99: LGTM: Proper timing for conflict handling test.

The bump_mocktime(30) addition after sending rawtx4 and before mempool synchronization ensures deterministic timing behavior in the chainlock override test. This change aligns with the PR's objective to standardize InstantSend timing across all test scenarios.


160-160: LGTM: Consistent timing adjustment for chainlock validation.

The addition of bump_mocktime(30) after sending rawtx5 maintains timing consistency in the chainlock override validation phase. This ensures reliable test execution when validating chainlock behavior with conflicting transactions.

test/functional/feature_notifications.py (2)

135-136: LGTM: Timing adjustment and node selection for notification test.

The addition of bump_mocktime(30) ensures deterministic timing for InstantSend processing. The change from self.nodes[1] to self.nodes[0] in the wait_for_instantlock call appears intentional - waiting on the sender node while the receiver (node1) is configured with instantsendnotify. This aligns with the notification test setup.


139-139: LGTM: Additional timing for notification file processing.

The bump_mocktime(30) before waiting for notification files ensures sufficient time passes for both InstantSend processing and file system notification operations. This improves test reliability by providing adequate time for the notification mechanism to complete.

test/functional/feature_asset_locks.py (1)

380-380: LGTM: Proper timing adjustment for asset unlock InstantSend test.

The addition of bump_mocktime(30) after sending the asset unlock transaction and before waiting for instantlock ensures deterministic timing behavior in the asset unlock test. This change is consistent with the PR's objective to standardize InstantSend timing across all test scenarios, including asset lock functionality.

test/functional/p2p_instantsend.py (1)

45-45: LGTM: Standardized mock time advances for InstantSend testing

The addition of bump_mocktime(30) calls before waiting for InstantLock confirmations aligns with the PR objective of making tests more deterministic and faster. This standardizes the simulation of network delays and ensures proper synchronization before assertions.

Also applies to: 61-61, 105-105, 128-128, 135-135

test/functional/interface_zmq_dash.py (1)

296-296: LGTM: Consistent mock time advances for ZMQ InstantSend testing

The 30-second mock time bumps before waiting for InstantLock confirmations are consistent with the broader pattern across the test suite. This improves test determinism and synchronization with network delays.

Also applies to: 349-349, 383-383

test/functional/feature_llmq_is_retroactive.py (1)

52-52: LGTM: Consistent mock time advances for retroactive signing tests

The 30-second mock time bumps before waiting for transactions and InstantLock confirmations improve test determinism and align with the PR objective of standardizing timing in InstantSend tests.

Also applies to: 73-73, 83-83, 116-116, 155-155, 195-195, 198-198

Comment on lines +1932 to 1942

def check_instantlock():
self.bump_mocktime(1)
try:
return node.getrawtransaction(txid, True)["instantlock"]
except:
return False

self.log.info(f"Expecting InstantLock for {txid}")
if self.wait_until(check_instantlock, timeout=timeout, sleep=1, do_assert=expected) and not expected:
if self.wait_until(check_instantlock, timeout=timeout, do_assert=expected) and not expected:
raise AssertionError("waiting unexpectedly succeeded")

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Approve the simplified wait_for_instantlock method with one improvement needed.

The removal of internal mocktime bumping aligns with the PR objective to make tests more deterministic. However, the bare except clause should be made more specific for better error handling.

Apply this diff to make the exception handling more specific:

-            except:
+            except Exception:

Or even better, catch the specific exception you expect:

-            except:
+            except JSONRPCException:
📝 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 check_instantlock():
self.bump_mocktime(1)
try:
return node.getrawtransaction(txid, True)["instantlock"]
except:
return False
self.log.info(f"Expecting InstantLock for {txid}")
if self.wait_until(check_instantlock, timeout=timeout, sleep=1, do_assert=expected) and not expected:
if self.wait_until(check_instantlock, timeout=timeout, do_assert=expected) and not expected:
raise AssertionError("waiting unexpectedly succeeded")
def check_instantlock():
try:
return node.getrawtransaction(txid, True)["instantlock"]
except Exception:
return False
🧰 Tools
🪛 Ruff (0.11.9)

1936-1936: Do not use bare except

(E722)

🪛 Flake8 (7.2.0)

[error] 1936-1936: do not use bare 'except'

(E722)

🤖 Prompt for AI Agents
In test/functional/test_framework/test_framework.py around lines 1932 to 1942,
the bare except clause in the check_instantlock function should be replaced with
a more specific exception to improve error handling. Identify the specific
exception type that node.getrawtransaction might raise when the transaction is
not found or invalid, and catch only that exception instead of a bare except.
This will make the code more robust and avoid masking unexpected errors.

Comment on lines +27 to +35
# random delay before tx is actually send by network could take up to 30 seconds
def wait_for_tx(self, txid, node, expected=True, timeout=60):
def check_tx():
try:
return node.getrawtransaction(txid)
except:
return False
if self.wait_until(check_tx, timeout=timeout, do_assert=expected) and not expected:
raise AssertionError("waiting unexpectedly succeeded")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix the bare except clause in the wait_for_tx helper method

The bare except clause should be replaced with a more specific exception to improve code quality and debugging.

Apply this diff to fix the exception handling:

-    def wait_for_tx(self, txid, node, expected=True, timeout=60):
-        def check_tx():
-            try:
-                return node.getrawtransaction(txid)
-            except:
-                return False
-        if self.wait_until(check_tx, timeout=timeout, do_assert=expected) and not expected:
-            raise AssertionError("waiting unexpectedly succeeded")
+    def wait_for_tx(self, txid, node, expected=True, timeout=60):
+        def check_tx():
+            try:
+                return node.getrawtransaction(txid)
+            except Exception:
+                return False
+        if self.wait_until(check_tx, timeout=timeout, do_assert=expected) and not expected:
+            raise AssertionError("waiting unexpectedly succeeded")
📝 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
# random delay before tx is actually send by network could take up to 30 seconds
def wait_for_tx(self, txid, node, expected=True, timeout=60):
def check_tx():
try:
return node.getrawtransaction(txid)
except:
return False
if self.wait_until(check_tx, timeout=timeout, do_assert=expected) and not expected:
raise AssertionError("waiting unexpectedly succeeded")
# random delay before tx is actually send by network could take up to 30 seconds
def wait_for_tx(self, txid, node, expected=True, timeout=60):
def check_tx():
try:
return node.getrawtransaction(txid)
except Exception:
return False
if self.wait_until(check_tx, timeout=timeout, do_assert=expected) and not expected:
raise AssertionError("waiting unexpectedly succeeded")
🧰 Tools
🪛 Ruff (0.11.9)

32-32: Do not use bare except

(E722)

🪛 Flake8 (7.2.0)

[error] 32-32: do not use bare 'except'

(E722)

🤖 Prompt for AI Agents
In test/functional/feature_llmq_is_retroactive.py around lines 27 to 35, replace
the bare except clause in the wait_for_tx method with a specific exception, such
as catching the exception type raised by node.getrawtransaction when the
transaction is not found (e.g., JSONRPCException or a relevant exception). This
improves error handling clarity and debugging by avoiding catching all
exceptions indiscriminately.

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 1a5cee6

@knst knst requested a review from PastaPastaPasta July 15, 2025 13:55
@UdjinM6 UdjinM6 added this to the 23 milestone Jul 16, 2025
@PastaPastaPasta
Copy link
Member

Very nice! I like this find.

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

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

utACK 1a5cee6

@PastaPastaPasta PastaPastaPasta merged commit e06a8b6 into dashpay:develop Jul 17, 2025
31 of 33 checks passed
@knst knst deleted the test-wait-is-cl-0.05 branch July 17, 2025 05:14
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