-
Notifications
You must be signed in to change notification settings - Fork 1.2k
test: consolidate masternode info tracking (MasternodeInfo
) in functional tests, implement helpers, add type annotations
#6718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
WalkthroughThe changes refactor the functional test suite for Dash by standardizing how masternode node objects are accessed and enhancing type safety. The 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (19)
🚧 Files skipped from review as they are similar to previous changes (12)
🧰 Additional context used🧠 Learnings (1)📓 Common learnings
🧬 Code Graph Analysis (3)test/functional/feature_governance.py (1)
test/functional/feature_llmq_singlenode.py (2)
test/functional/feature_mnehf.py (2)
🪛 Ruff (0.11.9)test/functional/feature_governance.py12-12: Remove unused import: (F401) test/functional/feature_llmq_connections.py17-17: Remove unused import: (F401) 59-59: Function definition does not bind loop variable (B023) 64-64: Function definition does not bind loop variable (B023) 69-69: Function definition does not bind loop variable (B023) 89-90: Use a single (SIM117) 119-119: Function definition does not bind loop variable (B023) test/functional/feature_llmq_simplepose.py230-230: Function definition does not bind loop variable (B023) test/functional/feature_llmq_singlenode.py19-19: Remove unused import: (F401) test/functional/feature_mnehf.py21-21: Remove unused import: (F401) test/functional/interface_zmq_dash.py17-17: Remove unused import: (F401) test/functional/test_framework/test_framework.py2102-2102: Do not Replace (B011) 🪛 Pylint (3.3.7)test/functional/interface_zmq_dash.py[error] 224-224: Instance of 'DashZMQTest' has no 'quorum_hash' member (E1101) [error] 225-225: Instance of 'DashZMQTest' has no 'quorum_type' member (E1101) test/functional/test_framework/test_framework.py[refactor] 1183-1186: Unnecessary "else" after "raise", remove the "else" and de-indent the code inside it (R1720) [refactor] 1192-1192: Too many arguments (6/5) (R0913) [refactor] 1192-1192: Too many positional arguments (6/5) (R0917) [refactor] 1379-1379: Too many local variables (16/15) (R0914) [refactor] 2117-2117: Too many arguments (6/5) (R0913) [refactor] 2117-2117: Too many positional arguments (6/5) (R0917) ⏰ Context from checks skipped due to timeout of 90000ms (9)
🔇 Additional comments (31)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (2)
test/functional/feature_llmq_connections.py (1)
89-90
: Consider simplifying nested with statements.The nested
with
statements can be combined for better readability.- with mn.get_node(self).assert_debug_log(['removing masternodes quorum connections']): - with mn.get_node(self).assert_debug_log(['keeping mn quorum connections']): + with (mn.get_node(self).assert_debug_log(['removing masternodes quorum connections']), + mn.get_node(self).assert_debug_log(['keeping mn quorum connections'])):🧰 Tools
🪛 Ruff (0.11.9)
89-90: Use a single
with
statement with multiple contexts instead of nestedwith
statements(SIM117)
test/functional/feature_llmq_simplepose.py (1)
230-230
: Address potential loop variable binding issue.The lambda function captures the loop variable
mn
, which could potentially cause issues if the lambda were executed after the loop completes. However, sincewait_until
calls the lambda immediately within the same iteration, this is likely not a practical problem in this context.For better clarity and to avoid static analysis warnings, consider capturing the variable explicitly:
- self.wait_until(lambda: mn.get_node(self).getconnectioncount() == 0) + self.wait_until(lambda mn=mn: mn.get_node(self).getconnectioncount() == 0)🧰 Tools
🪛 Ruff (0.11.9)
230-230: Function definition does not bind loop variable
mn
(B023)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
test/functional/feature_asset_locks.py
(2 hunks)test/functional/feature_dip3_deterministicmns.py
(7 hunks)test/functional/feature_dip3_v19.py
(4 hunks)test/functional/feature_dip4_coinbasemerkleroots.py
(3 hunks)test/functional/feature_governance.py
(2 hunks)test/functional/feature_llmq_connections.py
(6 hunks)test/functional/feature_llmq_data_recovery.py
(5 hunks)test/functional/feature_llmq_dkgerrors.py
(1 hunks)test/functional/feature_llmq_evo.py
(5 hunks)test/functional/feature_llmq_rotation.py
(3 hunks)test/functional/feature_llmq_signing.py
(6 hunks)test/functional/feature_llmq_simplepose.py
(6 hunks)test/functional/feature_llmq_singlenode.py
(4 hunks)test/functional/feature_mnehf.py
(4 hunks)test/functional/interface_zmq_dash.py
(4 hunks)test/functional/p2p_quorum_data.py
(10 hunks)test/functional/rpc_mnauth.py
(2 hunks)test/functional/rpc_quorum.py
(2 hunks)test/functional/test_framework/test_framework.py
(21 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (11)
test/functional/feature_dip3_v19.py (1)
test/functional/test_framework/test_framework.py (4)
DashTestFramework
(1214-2155)MasternodeInfo
(1138-1212)dynamically_add_masternode
(1339-1375)dynamically_evo_update_service
(1410-1434)
test/functional/feature_llmq_connections.py (1)
test/functional/test_framework/test_framework.py (9)
DashTestFramework
(1214-2155)MasternodeInfo
(1138-1212)get_node
(1209-1212)wait_for_mnauth
(2147-2155)mine_quorum
(1865-1951)get_quorum_masternodes
(2102-2107)wait_until
(900-901)bump_mocktime
(873-890)mine_cycle_quorum
(1953-2075)
test/functional/feature_llmq_rotation.py (1)
test/functional/test_framework/test_framework.py (1)
get_node
(1209-1212)
test/functional/feature_governance.py (1)
test/functional/test_framework/test_framework.py (1)
MasternodeInfo
(1138-1212)
test/functional/feature_dip4_coinbasemerkleroots.py (1)
test/functional/test_framework/test_framework.py (2)
DashTestFramework
(1214-2155)MasternodeInfo
(1138-1212)
test/functional/feature_asset_locks.py (2)
test/functional/test_framework/test_framework.py (1)
get_node
(1209-1212)test/functional/test_framework/util.py (1)
assert_equal
(64-69)
test/functional/feature_mnehf.py (2)
test/functional/test_framework/test_framework.py (2)
MasternodeInfo
(1138-1212)get_node
(1209-1212)test/functional/test_framework/util.py (1)
assert_equal
(64-69)
test/functional/feature_llmq_evo.py (1)
test/functional/test_framework/test_framework.py (2)
MasternodeInfo
(1138-1212)dynamically_add_masternode
(1339-1375)
test/functional/feature_llmq_simplepose.py (3)
test/functional/test_framework/test_framework.py (8)
DashTestFramework
(1214-2155)MasternodeInfo
(1138-1212)get_node
(1209-1212)wait_until
(900-901)stop_node
(673-675)start_masternode
(1522-1527)connect_nodes
(695-735)connect_nodes
(1239-1246)test/functional/test_framework/util.py (2)
assert_equal
(64-69)force_finish_mnsync
(525-531)test/functional/test_framework/masternodes.py (2)
check_banned
(11-13)check_punished
(7-9)
test/functional/rpc_quorum.py (2)
test/functional/test_framework/test_framework.py (1)
MasternodeInfo
(1138-1212)test/functional/test_framework/util.py (1)
assert_equal
(64-69)
test/functional/feature_llmq_data_recovery.py (3)
test/functional/test_framework/test_framework.py (6)
MasternodeInfo
(1138-1212)get_node
(1209-1212)restart_node
(687-690)wait_until
(900-901)connect_nodes
(695-735)connect_nodes
(1239-1246)test/functional/p2p_quorum_data.py (1)
restart_mn
(126-133)test/functional/test_framework/util.py (1)
force_finish_mnsync
(525-531)
🪛 Ruff (0.11.9)
test/functional/feature_llmq_connections.py
17-17: test_framework.test_framework.MasternodeInfo
imported but unused
Remove unused import: test_framework.test_framework.MasternodeInfo
(F401)
59-59: Function definition does not bind loop variable mn
(B023)
64-64: Function definition does not bind loop variable mn
(B023)
69-69: Function definition does not bind loop variable mn
(B023)
89-90: Use a single with
statement with multiple contexts instead of nested with
statements
(SIM117)
119-119: Function definition does not bind loop variable mn
(B023)
test/functional/feature_governance.py
12-12: test_framework.test_framework.MasternodeInfo
imported but unused
Remove unused import: test_framework.test_framework.MasternodeInfo
(F401)
test/functional/feature_mnehf.py
21-21: test_framework.test_framework.MasternodeInfo
imported but unused
Remove unused import: test_framework.test_framework.MasternodeInfo
(F401)
test/functional/interface_zmq_dash.py
17-17: test_framework.test_framework.MasternodeInfo
imported but unused
Remove unused import: test_framework.test_framework.MasternodeInfo
(F401)
test/functional/feature_llmq_singlenode.py
19-19: test_framework.test_framework.MasternodeInfo
imported but unused
Remove unused import: test_framework.test_framework.MasternodeInfo
(F401)
test/functional/feature_llmq_simplepose.py
230-230: Function definition does not bind loop variable mn
(B023)
test/functional/test_framework/test_framework.py
1184-1184: Do not assert False
(python -O
removes these calls), raise AssertionError()
Replace assert False
(B011)
2100-2100: Do not assert False
(python -O
removes these calls), raise AssertionError()
Replace assert False
(B011)
🪛 Pylint (3.3.7)
test/functional/interface_zmq_dash.py
[error] 224-224: Instance of 'DashZMQTest' has no 'quorum_hash' member
(E1101)
[error] 225-225: Instance of 'DashZMQTest' has no 'quorum_type' member
(E1101)
test/functional/test_framework/test_framework.py
[refactor] 1192-1192: Too many arguments (6/5)
(R0913)
[refactor] 1192-1192: Too many positional arguments (6/5)
(R0917)
[refactor] 1377-1377: Too many local variables (16/15)
(R0914)
[refactor] 2115-2115: Too many arguments (6/5)
(R0913)
[refactor] 2115-2115: Too many positional arguments (6/5)
(R0917)
⏰ Context from checks skipped due to timeout of 90000ms (7)
- GitHub Check: linux64_tsan-test / Test source
- GitHub Check: linux64_ubsan-test / Test source
- GitHub Check: linux64_sqlite-test / Test source
- GitHub Check: linux64_nowallet-test / Test source
- GitHub Check: linux64-test / Test source
- GitHub Check: linux64_multiprocess-build / Build source
- GitHub Check: linux64_fuzz-build / Build source
🔇 Additional comments (54)
test/functional/feature_governance.py (2)
10-13
: LGTM! Import consolidation for better type safety.Adding
MasternodeInfo
to the import statement supports the type annotation added later in the file.🧰 Tools
🪛 Ruff (0.11.9)
12-12:
test_framework.test_framework.MasternodeInfo
imported but unusedRemove unused import:
test_framework.test_framework.MasternodeInfo
(F401)
193-193
: Good addition of type annotation for clarity.The type annotation comment helps with code readability and IDE support. Note that the static analysis tool incorrectly flags
MasternodeInfo
as unused - this is a false positive since type annotation comments are valid usage.test/functional/feature_llmq_dkgerrors.py (1)
30-30
: Excellent refactoring to improve encapsulation.Replacing direct
.node
attribute access with.get_node(self)
method calls improves encapsulation and consistency across the codebase. The logic remains unchanged while providing better abstraction for node access.Also applies to: 35-36, 41-41, 49-50, 55-57, 62-63, 68-69
test/functional/feature_dip4_coinbasemerkleroots.py (2)
17-20
: LGTM! Added MasternodeInfo import for type annotations.The explicit import supports the type annotations added in the method signatures below.
56-56
: Good addition of type annotations for better code clarity.Adding explicit type annotations for
MasternodeInfo
variables improves code readability and enables better static type checking.Also applies to: 86-86
test/functional/feature_llmq_singlenode.py (2)
17-20
: LGTM! Import consolidation for type safety.Adding
MasternodeInfo
to the import statement supports the type annotation used later in the file.🧰 Tools
🪛 Ruff (0.11.9)
19-19:
test_framework.test_framework.MasternodeInfo
imported but unusedRemove unused import:
test_framework.test_framework.MasternodeInfo
(F401)
61-67
: Excellent encapsulation improvement with get_node() method.Replacing direct
.node
attribute access with.get_node(self)
method calls improves encapsulation and follows the established pattern across the codebase. The type annotation comment on line 61 also enhances code clarity. Note that the static analysis tool incorrectly flagsMasternodeInfo
as unused - this is a false positive since it's used for the type annotation comment.Also applies to: 118-118, 121-121, 123-123, 128-128, 133-134, 143-143
test/functional/rpc_quorum.py (3)
5-9
: LGTM: Import statement properly updated for type annotations.The explicit import of
MasternodeInfo
enables proper type annotations and aligns with the refactoring effort.
28-28
: Good addition of type annotation.Adding
MasternodeInfo
type annotation improves code clarity and enables better static analysis support.
31-31
: Excellent improvement: Direct attribute access over dynamic calculation.Replacing
p2p_port(mn.node.index)
withmn.nodePort
eliminates the need for dynamic port calculation and directly uses the encapsulated port information from theMasternodeInfo
object. This is cleaner and more efficient.test/functional/feature_llmq_rotation.py (3)
143-143
: LGTM: Consistent node access pattern.The change from
mn.node
tomn.get_node(self)
follows the standardized node access pattern established in this refactoring effort.
196-196
: LGTM: Proper node access method usage.Consistent application of the
get_node(self)
method for accessing masternode nodes.
401-401
: LGTM: Standardized node retrieval.The node access pattern is correctly updated to use the encapsulated
get_node(self)
method.test/functional/feature_asset_locks.py (2)
106-106
: LGTM: Consistent node access refactoring.The change from
mninfo[0].node.quorum(...)
tomninfo[0].get_node(self).quorum(...)
properly follows the new standardized node access pattern while maintaining identical functionality.
371-371
: LGTM: Proper node method access.Correctly updated to use the
get_node(self)
method for accessing the quorum RPC, consistent with the refactoring approach.test/functional/rpc_mnauth.py (3)
8-11
: LGTM: Proper import for type annotations.Explicitly importing
MasternodeInfo
enables type annotations and maintains consistency with the refactoring effort.
26-27
: Excellent approach: Extract node once and reuse.Adding the type annotation and extracting the node using
get_node(self)
once is more efficient than calling it multiple times throughout the method. This is a clean implementation of the new access pattern.
28-55
: LGTM: Consistent use of extracted node variable.All node method calls correctly use the extracted
node
variable instead of accessing throughmasternode.node
. This maintains the same functionality while following the standardized access pattern.test/functional/feature_dip3_v19.py (2)
17-20
: LGTM! Import statement properly updated.The addition of
MasternodeInfo
to the import statement is consistent with its usage in type annotations throughout the file.
84-94
: LGTM! Type annotations improve code clarity.The explicit type annotations for
MasternodeInfo
variables enhance code readability and provide better static analysis support. All annotations are consistent with the return types ofdynamically_add_masternode
.Also applies to: 107-107, 135-135
test/functional/feature_llmq_signing.py (3)
15-18
: LGTM! Import statement properly updated.The addition of
MasternodeInfo
to the import statement supports the type annotations used throughout the file.
42-42
: LGTM! Consistent refactoring to use get_node() method.The replacement of direct
.node
attribute access with.get_node(self)
method calls is consistent with theMasternodeInfo
class design and improves encapsulation. This change standardizes how test nodes are accessed from masternode info objects.Also applies to: 50-50, 52-52, 54-54, 68-68, 72-72, 75-76, 82-82, 84-85, 98-98, 103-103, 108-108, 113-113, 116-116, 149-149, 173-175, 184-198
49-49
: LGTM! Type annotations enhance code clarity.The explicit type annotations for
MasternodeInfo
variables improve code readability and static analysis support.Also applies to: 97-97, 101-101, 148-148, 183-183
test/functional/interface_zmq_dash.py (3)
15-18
: LGTM! Import statement properly updated.The addition of
MasternodeInfo
to the import statement supports the type annotations used throughout the file.🧰 Tools
🪛 Ruff (0.11.9)
17-17:
test_framework.test_framework.MasternodeInfo
imported but unusedRemove unused import:
test_framework.test_framework.MasternodeInfo
(F401)
172-172
: LGTM! Consistent refactoring to use get_node() method.The replacement of direct
.node
attribute access with.get_node(self)
method calls follows the same pattern as other files in this PR and improves encapsulation.Also applies to: 195-195, 225-225
224-224
: Fix type annotation consistency.The type annotation is correct for the loop variable.
🧰 Tools
🪛 Pylint (3.3.7)
[error] 224-224: Instance of 'DashZMQTest' has no 'quorum_hash' member
(E1101)
test/functional/feature_llmq_connections.py (3)
15-18
: LGTM! Import statement properly updated.The addition of
MasternodeInfo
to the import statement supports the type annotations used throughout the file.🧰 Tools
🪛 Ruff (0.11.9)
17-17:
test_framework.test_framework.MasternodeInfo
imported but unusedRemove unused import:
test_framework.test_framework.MasternodeInfo
(F401)
38-38
: LGTM! Consistent refactoring to use get_node() method.The replacement of direct
.node
attribute access with.get_node(self)
method calls is consistent with the broader refactoring and improves encapsulation.Also applies to: 52-52, 87-87, 89-92, 103-105, 117-117, 121-121, 127-127
86-86
: LGTM! Type annotations enhance code clarity.The explicit type annotations for
MasternodeInfo
variables improve code readability and static analysis support.Also applies to: 102-102, 116-118, 120-120
test/functional/feature_mnehf.py (4)
19-22
: LGTM! Import statement properly updated for MasternodeInfo.The import has been correctly updated to include
MasternodeInfo
alongsideDashTestFramework
, aligning with the refactoring to use the enhancedMasternodeInfo
class.🧰 Tools
🪛 Ruff (0.11.9)
21-21:
test_framework.test_framework.MasternodeInfo
imported but unusedRemove unused import:
test_framework.test_framework.MasternodeInfo
(F401)
44-45
: LGTM! Proper migration to new MasternodeInfo API.The code correctly:
- Adds type annotation for the loop variable
- Replaces
mn.node.index
withmn.nodeIdx
for node indexingThis aligns with the enhanced
MasternodeInfo
class design.
57-57
: LGTM! Node access properly updated.The direct node access
self.mninfo[0].node.quorum(...)
has been correctly replaced withself.mninfo[0].get_node(self).quorum(...)
, following the new encapsulated approach.
143-143
: LGTM! Consistent node access pattern.This line correctly follows the same pattern as line 57, ensuring consistent usage of the
get_node(self)
method throughout the file.test/functional/feature_llmq_evo.py (5)
13-13
: LGTM! Optional import added for type annotations.The
Optional
import is properly added to support the return type annotation in theget_mn_payee_for_block
method.
18-21
: LGTM! Import statement correctly updated.The import has been properly updated to include
MasternodeInfo
for use in type annotations throughout the file.
79-79
: LGTM! Excellent type annotation additions.The type annotations for
evo_info
,current_evo
, andpayee
variables significantly improve code clarity and enable better static analysis. All annotations correctly useMasternodeInfo
.Also applies to: 118-118, 122-122
161-161
: LGTM! Method return type properly annotated.The
get_mn_payee_for_block
method now correctly declares its return type asOptional[MasternodeInfo]
, which matches the implementation that can return either aMasternodeInfo
instance orNone
.
166-166
: LGTM! Loop variable type annotations improve clarity.The inline type comments for loop variables help clarify that the iterations are over
MasternodeInfo
instances, enhancing code readability and static analysis support.Also applies to: 177-177
test/functional/p2p_quorum_data.py (4)
13-16
: LGTM! Import statement properly updated.The import has been correctly updated to include
MasternodeInfo
, supporting the comprehensive refactoring throughout this file.
126-133
: LGTM! Method signature and implementation properly updated.The
restart_mn
method correctly:
- Adds type annotation for the
mn
parameter asMasternodeInfo
- Replaces
mn.node.index
withmn.nodeIdx
for node indexing- Uses
mn.get_node(self)
for node access- Maintains the same functionality with improved encapsulation
147-188
: LGTM! Consistent node access pattern throughout test_basics.All direct node accesses have been properly replaced with the
get_node(self)
method, maintaining consistency with the newMasternodeInfo
API. The changes preserve the original test logic while improving encapsulation.
414-416
: LGTM! Local variable type annotations improve clarity.The type annotations for
mn1
,mn2
, andmn3
variables clearly indicate they areMasternodeInfo
instances, enhancing code readability and enabling better static analysis.test/functional/feature_llmq_simplepose.py (3)
16-20
: LGTM! Import statement properly updated.The import has been correctly updated to include
MasternodeInfo
, supporting the type annotations and method calls throughout the file.
71-74
: LGTM! Method signatures and implementations properly updated.All three methods (
isolate_mn
,close_mn_port
,force_old_mn_proto
) correctly:
- Add type annotations for the
mn
parameter asMasternodeInfo
- Replace direct node access with
mn.get_node(self)
method calls- Use
mn.nodeIdx
for node indexing- Maintain the same functionality with improved encapsulation
Also applies to: 76-86, 88-93
211-211
: LGTM! Direct port access improves consistency.Using
mn.nodePort
directly instead of a utility function withmn.nodeIdx
is more consistent with the encapsulatedMasternodeInfo
design and eliminates unnecessary indirection.test/functional/feature_llmq_data_recovery.py (3)
7-12
: Good addition of type annotations and explicit imports.The explicit import of
MasternodeInfo
and addition of type annotations improves code clarity and enables better IDE support.
35-50
: Consistent refactoring to use MasternodeInfo methods.The changes properly utilize the new
MasternodeInfo
structure by:
- Using
mn.nodeIdx
instead ofmn.node.index
- Calling
mn.get_node(self)
instead of directmn.node
accessThis improves encapsulation and maintains consistency with the framework refactoring.
80-94
: Well-structured return type annotations.The addition of return type annotations (
Optional[MasternodeInfo]
,List[Optional[MasternodeInfo]]
,List[MasternodeInfo]
) enhances code documentation and type safety.test/functional/feature_dip3_deterministicmns.py (3)
11-20
: Good use of constants and explicit imports.Importing
MASTERNODE_COLLATERAL
eliminates magic numbers and improves maintainability. The explicit import ofMasternodeInfo
clarifies dependencies.
224-229
: Well-structured masternode preparation with MasternodeInfo.The
prepare_mn
method now properly creates and initializes aMasternodeInfo
instance with:
- Correct evo/legacy flags based on softfork status
- Generated addresses
- Proper parameters including operator reward and node port
This encapsulation makes the code more maintainable and type-safe.
231-262
: Excellent refactoring of masternode lifecycle methods.The refactoring of
create_mn_collateral
,register_fund_mn
,register_mn
,start_mn
, andspend_mn_collateral
to useMasternodeInfo
methods and attributes:
- Improves code organization
- Ensures consistent collateral value calculation
- Properly encapsulates masternode state
The assertion in
start_mn
(line 252) is a good defensive programming practice.test/functional/test_framework/test_framework.py (3)
1138-1187
: Excellent refactoring of MasternodeInfo class.The new
MasternodeInfo
class is well-structured with:
- Comprehensive type annotations for all attributes
- Clear separation between evo and regular masternodes
- Useful helper methods for address generation and collateral management
- Proper encapsulation of masternode state
This significantly improves code maintainability and type safety.
🧰 Tools
🪛 Ruff (0.11.9)
1184-1184: Do not
assert False
(python -O
removes these calls), raiseAssertionError()
Replace
assert False
(B011)
🪛 Pylint (3.3.7)
[refactor] 1138-1138: Too many instance attributes (16/7)
(R0902)
1209-1212
: Well-designed node access method.The
get_node
method with proper assertions ensures:
nodeIdx
is set before accessing the node- The node exists in the test framework
- Clear error messages for debugging
This defensive programming approach prevents common errors.
1239-1246
: Good encapsulation in connect_nodes method.The refactoring to use
mn.get_node(self)
instead of directmn.node
access maintains consistency with the new MasternodeInfo encapsulation pattern throughout the codebase.
for mn in self.get_quorum_masternodes(self.quorum_hash): # type: MasternodeInfo | ||
mn.get_node(self).quorum("sign", self.quorum_type, sign_id, sign_msg_hash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify missing attributes: quorum_hash and quorum_type.
The code references self.quorum_hash
and self.quorum_type
but these attributes don't appear to be defined in the DashZMQTest
class.
🏁 Script executed:
#!/bin/bash
# Description: Search for quorum_hash and quorum_type attribute definitions in DashZMQTest class
# Expected: Find where these attributes should be defined or initialized
# Search for quorum_hash and quorum_type in the test file
rg -A 3 -B 3 "quorum_hash|quorum_type" test/functional/interface_zmq_dash.py
# Search for these attributes in other test files to understand the pattern
rg -A 2 -B 2 "self\.quorum_hash|self\.quorum_type" test/functional/
# Check the DashTestFramework base class for these attributes
rg -A 3 -B 3 "quorum_hash|quorum_type" test/functional/test_framework/test_framework.py
Length of output: 11281
🏁 Script executed:
#!/bin/bash
# Inspect the DashZMQTest class definition and its initial attributes
rg -n "^class " test/functional/interface_zmq_dash.py
sed -n '1,200p' test/functional/interface_zmq_dash.py
Length of output: 7384
🏁 Script executed:
#!/bin/bash
# Locate set_dash_llmq_test_params definition
rg -n "def set_dash_llmq_test_params" test/functional/test_framework/test_framework.py
# Locate mine_cycle_quorum definition
rg -n "def mine_cycle_quorum" -n test/functional/test_framework/test_framework.py
rg -A 20 -B 0 "def mine_cycle_quorum" test/functional/test_framework/test_framework.py
# Search for assignments to self.quorum_hash and self.quorum_type in the framework
rg -n "self\.quorum_hash" test/functional/test_framework/test_framework.py
rg -n "self\.quorum_type" test/functional/test_framework/test_framework.py
Length of output: 1597
Initialize missing quorum attributes before use
The test_recovered_signature_publishers
method references self.quorum_hash
and self.quorum_type
(lines 224–225) but neither is ever initialized in DashZMQTest
, leading to an AttributeError
when this code runs. You should set these attributes before invoking get_quorum_masternodes
and the quorum("sign", …)
call. For example:
• After generating the quorum in run_test
, capture its hash and type:
# in run_test, instead of:
self.mine_cycle_quorum()
# do:
self.quorum_hash, self.quorum_type = self.mine_cycle_quorum()
• Or explicitly initialize them in set_test_params
(or right before the loop):
self.quorum_type = 103 # llmq_test_dip0024
self.quorum_hash = self.nodes[0].quorum("list")[“llmq_test_dip0024”][-1]
Then update the loop to use the initialized values:
- for mn in self.get_quorum_masternodes(self.quorum_hash):
+ for mn in self.get_quorum_masternodes(self.quorum_hash):
mn.get_node(self).quorum("sign", self.quorum_type, sign_id, sign_msg_hash)
• File: test/functional/interface_zmq_dash.py
Lines: 224–225
🧰 Tools
🪛 Pylint (3.3.7)
[error] 224-224: Instance of 'DashZMQTest' has no 'quorum_hash' member
(E1101)
[error] 225-225: Instance of 'DashZMQTest' has no 'quorum_type' member
(E1101)
🤖 Prompt for AI Agents
In test/functional/interface_zmq_dash.py around lines 224 to 225, the attributes
self.quorum_hash and self.quorum_type are used but never initialized, causing an
AttributeError. To fix this, initialize these attributes before the loop in
test_recovered_signature_publishers or in run_test by assigning self.quorum_hash
and self.quorum_type from the result of self.mine_cycle_quorum() or by setting
them explicitly in set_test_params or just before the loop. This ensures the
quorum hash and type are defined before calling get_quorum_masternodes and
quorum("sign", ...).
|
||
self.log.info("mine a new quorum and verify that all members connect to each other") | ||
q = self.mine_quorum() | ||
|
||
self.log.info("checking that all MNs got probed") | ||
for mn in self.get_quorum_masternodes(q): | ||
self.wait_until(lambda: self.get_mn_probe_count(mn.node, q, False) == 4) | ||
self.wait_until(lambda: self.get_mn_probe_count(mn.get_node(self), q, False) == 4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix lambda function variable capture issue.
The lambda functions are capturing the loop variable mn
by reference, which could lead to incorrect behavior when the lambda is executed after the loop iteration completes.
Apply this fix to properly capture the loop variable:
- for mn in self.get_quorum_masternodes(q):
- self.wait_until(lambda: self.get_mn_probe_count(mn.get_node(self), q, False) == 4)
+ for mn in self.get_quorum_masternodes(q):
+ self.wait_until(lambda mn=mn: self.get_mn_probe_count(mn.get_node(self), q, False) == 4)
Apply the same pattern to lines 64 and 69.
Also applies to: 64-64, 69-69
🧰 Tools
🪛 Ruff (0.11.9)
59-59: Function definition does not bind loop variable mn
(B023)
🤖 Prompt for AI Agents
In test/functional/feature_llmq_connections.py at lines 59, 64, and 69, the
lambda functions capture the loop variable mn by reference, causing potential
incorrect behavior after the loop ends. To fix this, modify each lambda to
capture the current value of mn as a default argument in the lambda definition,
ensuring the correct mn is used when the lambda executes.
for mn in self.mninfo: # type: MasternodeInfo | ||
mn.get_node(self).setnetworkactive(False) | ||
for mn in self.mninfo: # type: MasternodeInfo | ||
self.wait_until(lambda: len(mn.get_node(self).getpeerinfo()) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix lambda function variable capture issue.
Similar issue with lambda function capturing loop variable by reference.
Apply this fix:
- for mn in self.mninfo: # type: MasternodeInfo
- self.wait_until(lambda: len(mn.get_node(self).getpeerinfo()) == 0)
+ for mn in self.mninfo: # type: MasternodeInfo
+ self.wait_until(lambda mn=mn: len(mn.get_node(self).getpeerinfo()) == 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.
self.wait_until(lambda: len(mn.get_node(self).getpeerinfo()) == 0) | |
for mn in self.mninfo: # type: MasternodeInfo | |
self.wait_until(lambda mn=mn: len(mn.get_node(self).getpeerinfo()) == 0) |
🧰 Tools
🪛 Ruff (0.11.9)
119-119: Function definition does not bind loop variable mn
(B023)
🤖 Prompt for AI Agents
In test/functional/feature_llmq_connections.py at line 119, the lambda function
captures the loop variable by reference, causing incorrect behavior. Fix this by
binding the current value of the loop variable as a default argument in the
lambda function to ensure it captures the intended value at each iteration.
MasternodeInfo
) in functional tests, add type annotations, implement helpersMasternodeInfo
) in functional tests, implement helpers, add type annotations
MasternodeInfo is used a lot in multiple tests and without annotations, the linter is running blind. Let's try to do something about that. Note, PEP 526 prohibits annotating types in loops, we are using PEP 484 comments there instead.
Relevant when we migrate feature_dip3_deterministicmns away from their own bespoke Masternode class
This makes crafting invalid addresses as part of testing easier, a valid entry will always use the loopback address (127.0.0.1), it's the port that differentiates. So let's only store what we need. Relevant when we migrate feature_dip3_deterministicmns away from their own bespoke Masternode class
node is self.nodes[nodeIdx], so node.index will always be nodeIdx, just fetch nodeIdx directly instead.
We don't register nodeIdx and node at the same time. This creates a problem that we aren't entirely sure if the node is online when we access node since we don't really own the TestNode instance, it is always in the control of BitcoinTestFramework. So let's try to be more explicit about that and add some belt-and-suspenders assertions.
Harmonize with the rest of the codebase
In order to generate BLS keypairs properly, we need to also record if we are running a legacy node, so we store that now too. set_params() is meant to be as flexible as possible, to allow ensuring that details are registered across different steps or all at once.
Motivation
While working on functional tests for dash#6666 (which in turn, relies on the foundation built by dash#6674, see
rpc_netinfo.py
, source), the existing test infrastructure, while well optimized for testing interactions between masternodes, didn't offer enough flexibility for test creation of masternodes.The tests that need to be implemented for extended addresses mimic
feature_dip3_deterministicmns.py
(source) in objective and while taking cues from it, it was found that instead of theMasternodeInfo
used in most of the codebase (source),feature_dip3_deterministicmns.py
implements its own object,Masternode
(source).In a similar vein,
rpc_netinfo.py
, as implemented in c788531, implemented its own variant,Node
(source) to address the testing needs for extended addresses. It became clear that without additional intervention, the test suite would have three different ways of representing masternode information (MasternodeInfo
,Masternode
,Node
) and that it would be more beneficial to consolidate all three approaches together.So, taking cue from
Node
(fromrpc_netinfo.py
, not included indevelop
as of this writing, source), this pull request aims to clean up, consolidate and improve uponMasternodeInfo
in an attempt to remove the need forNode
.Additional Information
PEP 526 ("Syntax for Variable Annotations") prohibit annotation of variables defined by a
for
orwith
statement (source), to get around that, the syntax for type comments (source) as defined in PEP 484 ("Type Hints") have been used instead.The decision to remove
node
fromMasternodeInfo
despite its convenience was rooted in the observation that there are cases wherenodeIdx
andnode
are assigned separately (source, source), which highlighted the fact thatMasternodeInfo
has no control over the lifetime of theTestNode
instance as it is ultimately controlled byBitcoinTestFramework
.To avoid potential error and to make it syntactically clear that ownership is vests with
BitcoinTestFramework
,node
has been removed and replaced withget_node()
, which alongside some basic sanity checks, is just an alias forself.nodes[mn.nodeIdx]
but is still less tedious to type or read.The reason why functions like
generate_addresses()
accept a separateTestNode
(source) is due to the practice of using a single node to instantiate all masternodes instead of keeping them as self-contained instances that mine their own blocks and fund their own collaterals (for instance,DashTestFramework
usesnodes[0]
to setup all the masternodes even if the masternodes themselves had a different index).The decision to replace
addr
withnodePort
has been inspired by the need to register masternodes with non-loopback addresses (like infeature_dip3_deterministicmns.py
orrpc_netinfo.py
). As a node that is intended to be used will always need the same standardized loopback address, we can safely resort to only storing the port number and save a fewp2p_port()
calls along the way.Breaking Changes
None expected. Affects only functional tests.
Checklist