Skip to content

Commit ee34525

Browse files
Merge #6718: test: consolidate masternode info tracking (MasternodeInfo) in functional tests, implement helpers, add type annotations
0532baf refactor: add helper for finding collateral vout (Kittywhiskers Van Gogh) 89b2548 refactor: add helper for transaction burial (Kittywhiskers Van Gogh) f3777c5 refactor: introduce address generation helper, make constructor minimal (Kittywhiskers Van Gogh) fa56126 refactor: add type annotations for `MasternodeInfo` members (Kittywhiskers Van Gogh) dfca0ee refactor: use `MasternodeInfo` in feature_dip3_deterministicmns.py (Kittywhiskers Van Gogh) cd7ffcf refactor: store only the node index in `MasternodeInfo` (Kittywhiskers Van Gogh) 07c7319 refactor: use `nodeIdx` instead of `node.index` with `MasternodeInfo` (Kittywhiskers Van Gogh) 08e81d8 refactor: store P2P port instead of full address in `MasternodeInfo` (Kittywhiskers Van Gogh) 3e25b30 refactor: store the funding address in `MasternodeInfo` (Kittywhiskers Van Gogh) 08d18d7 refactor: annotate usage of `MasternodeInfo` (Kittywhiskers Van Gogh) Pull request description: ## Motivation While working on functional tests for [dash#6666](#6666) (which in turn, relies on the foundation built by [dash#6674](#6674), see `rpc_netinfo.py`, [source](https://github.com/dashpay/dash/blob/c788531d35c48820af951799ff8b67ee2dadb8b3/test/functional/rpc_netinfo.py)), the existing test infrastructure, while well optimized for testing interactions _between_ masternodes, didn't offer enough flexibility for testing _creation_ of masternodes. The tests that need to be implemented for extended addresses mimic `feature_dip3_deterministicmns.py` ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/feature_dip3_deterministicmns.py)) in objective and while taking cues from it, it was found that instead of the `MasternodeInfo` used in most of the codebase ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/test_framework.py#L1138-L1153)), `feature_dip3_deterministicmns.py` implements its own object, `Masternode` ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/feature_dip3_deterministicmns.py#L223-L227)). In a similar vein, `rpc_netinfo.py`, as implemented in c788531, implemented its own variant, `Node` ([source](https://github.com/dashpay/dash/blob/c788531d35c48820af951799ff8b67ee2dadb8b3/test/functional/rpc_netinfo.py#L31-L194)) 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` (from `rpc_netinfo.py`, not included in `develop` as of this writing, [source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/rpc_netinfo.py)), this pull request aims to clean up, consolidate and improve upon `MasternodeInfo` in an attempt to remove the need for `Node`. ## Additional Information * PEP 526 ("Syntax for Variable Annotations") prohibit annotation of variables defined by a `for` or `with` statement ([source](https://peps.python.org/pep-0526/#where-annotations-aren-t-allowed)), to get around that, the syntax for type comments ([source](https://peps.python.org/pep-0484)) as defined in PEP 484 ("Type Hints") have been used instead. * The decision to remove `node` from `MasternodeInfo` despite its convenience was rooted in the observation that there are cases where `nodeIdx` and `node` are assigned separately ([source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/test_framework.py#L1480), [source](https://github.com/dashpay/dash/blob/09aa42ef0f20dc94ea63bdc8e7e064d494c9ae8f/test/functional/test_framework/test_framework.py#L1499)), which highlighted the fact that `MasternodeInfo` has no control over the lifetime of the `TestNode` instance as it is ultimately controlled by `BitcoinTestFramework`. To avoid potential error and to make it syntactically clear that ownership is vests with `BitcoinTestFramework`, `node` has been removed and replaced with `get_node()`, which alongside some basic sanity checks, is just an alias for `self.nodes[mn.nodeIdx]` but is still less tedious to type or read. * The reason why functions like `generate_addresses()` accept a separate `TestNode` ([source](https://github.com/dashpay/dash/blob/c5444b3f7c06aa5b060fd542773df6dd5a9bae1f/test/functional/test_framework/test_framework.py#L1160-L1174)) 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` uses `nodes[0]` to setup all the masternodes even if the masternodes themselves had a different index). * The decision to replace `addr` with `nodePort` has been inspired by the need to register masternodes with non-loopback addresses (like in `feature_dip3_deterministicmns.py` or `rpc_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 few `p2p_port()` calls along the way. ## Breaking Changes None expected. Affects only functional tests. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: PastaPastaPasta: utACK 0532baf UdjinM6: utACK 0532baf Tree-SHA512: c2b5b0f9200c2714159a5e0f6f52174e38eae426c01620cf15e9adb8b8e67bbb078d658edb21b052a1fc9896ba098cffa02248aaa980be5d5cb145e9929c4568
2 parents 55fe954 + 0532baf commit ee34525

19 files changed

+496
-447
lines changed

test/functional/feature_asset_locks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def create_assetunlock(self, index, withdrawal, pubkey=None, fee=tiny_amount):
103103

104104
height = node_wallet.getblockcount()
105105
self.log.info(f"Creating asset unlock: index={index} {request_id}")
106-
quorumHash = mninfo[0].node.quorum("selectquorum", llmq_type_test, request_id)["quorumHash"]
106+
quorumHash = mninfo[0].get_node(self).quorum("selectquorum", llmq_type_test, request_id)["quorumHash"]
107107
self.log.info(f"Used quorum hash: {quorumHash}")
108108
unlockTx_payload = CAssetUnlockTx(
109109
version = 1,
@@ -368,7 +368,7 @@ def test_asset_unlocks(self, node_wallet, node, pubkey):
368368
asset_unlock_tx_payload = CAssetUnlockTx()
369369
asset_unlock_tx_payload.deserialize(BytesIO(asset_unlock_tx.vExtraPayload))
370370

371-
assert_equal(asset_unlock_tx_payload.quorumHash, int(self.mninfo[0].node.quorum("selectquorum", llmq_type_test, 'e6c7a809d79f78ea85b72d5df7e9bd592aecf151e679d6e976b74f053a7f9056')["quorumHash"], 16))
371+
assert_equal(asset_unlock_tx_payload.quorumHash, int(self.mninfo[0].get_node(self).quorum("selectquorum", llmq_type_test, 'e6c7a809d79f78ea85b72d5df7e9bd592aecf151e679d6e976b74f053a7f9056')["quorumHash"], 16))
372372

373373
self.log.info("Test no IS for asset unlock...")
374374
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0)

test/functional/feature_dip3_deterministicmns.py

Lines changed: 56 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88
#
99

1010
from decimal import Decimal
11+
from typing import List
1112

1213
from test_framework.blocktools import create_block_with_mnpayments
1314
from test_framework.messages import tx_from_hex
14-
from test_framework.test_framework import BitcoinTestFramework
15+
from test_framework.test_framework import (
16+
MASTERNODE_COLLATERAL,
17+
BitcoinTestFramework,
18+
MasternodeInfo,
19+
)
1520
from test_framework.util import assert_equal, force_finish_mnsync, p2p_port, softfork_active
1621

17-
class Masternode(object):
18-
pass
19-
2022
class DIP3Test(BitcoinTestFramework):
2123
def set_test_params(self):
2224
self.num_initial_mn = 11 # Should be >= 11 to make sure quorums are not always the same MNs
@@ -48,19 +50,19 @@ def start_controller_node(self):
4850

4951
def run_test(self):
5052
self.log.info("funding controller node")
51-
while self.nodes[0].getbalance() < (self.num_initial_mn + 3) * 1000:
53+
while self.nodes[0].getbalance() < (self.num_initial_mn + 3) * MASTERNODE_COLLATERAL:
5254
self.generate(self.nodes[0], 10, sync_fun=self.no_op) # generate enough for collaterals
5355
self.log.info("controller node has {} dash".format(self.nodes[0].getbalance()))
5456

5557
# Make sure we're below block 135 (which activates dip3)
5658
self.log.info("testing rejection of ProTx before dip3 activation")
5759
assert self.nodes[0].getblockchaininfo()['blocks'] < 135
5860

59-
mns = []
61+
mns: List[MasternodeInfo] = []
6062

6163
# prepare mn which should still be accepted later when dip3 activates
6264
self.log.info("creating collateral for mn-before-dip3")
63-
before_dip3_mn = self.prepare_mn(self.nodes[0], 1, 'mn-before-dip3')
65+
before_dip3_mn: MasternodeInfo = self.prepare_mn(self.nodes[0], 1, 'mn-before-dip3')
6466
self.create_mn_collateral(self.nodes[0], before_dip3_mn)
6567
mns.append(before_dip3_mn)
6668

@@ -82,7 +84,7 @@ def run_test(self):
8284

8385
self.log.info("registering MNs")
8486
for i in range(self.num_initial_mn):
85-
mn = self.prepare_mn(self.nodes[0], i + 2, "mn-%d" % i)
87+
mn: MasternodeInfo = self.prepare_mn(self.nodes[0], i + 2, "mn-%d" % i)
8688
mns.append(mn)
8789

8890
# start a few MNs before they are registered and a few after they are registered
@@ -93,12 +95,12 @@ def run_test(self):
9395
# let a few of the protx MNs refer to the existing collaterals
9496
fund = (i % 2) == 0
9597
if fund:
96-
self.log.info("register_fund %s" % mn.alias)
98+
self.log.info(f"register_fund {mn.friendlyName}")
9799
self.register_fund_mn(self.nodes[0], mn)
98100
else:
99-
self.log.info("create_collateral %s" % mn.alias)
101+
self.log.info(f"create_collateral {mn.friendlyName}")
100102
self.create_mn_collateral(self.nodes[0], mn)
101-
self.log.info("register %s" % mn.alias)
103+
self.log.info(f"register {mn.friendlyName}")
102104
self.register_mn(self.nodes[0], mn)
103105

104106
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
@@ -117,7 +119,7 @@ def run_test(self):
117119
old_tip = self.nodes[0].getblockcount()
118120
old_listdiff = self.nodes[0].protx("listdiff", 1, old_tip)
119121
for i in range(spend_mns_count):
120-
old_protx_hash = mns[i].protx_hash
122+
old_protx_hash = mns[i].proTxHash
121123
old_collateral_address = mns[i].collateral_address
122124
old_blockhash = self.nodes[0].getbestblockhash()
123125
old_rpc_info = self.nodes[0].protx("info", old_protx_hash)
@@ -187,7 +189,7 @@ def run_test(self):
187189
mn = mns[i]
188190
# a few of these will actually refer to old ProRegTx internal collaterals,
189191
# which should work the same as external collaterals
190-
new_mn = self.prepare_mn(self.nodes[0], mn.idx, mn.alias)
192+
new_mn: MasternodeInfo = self.prepare_mn(self.nodes[0], mn.nodeIdx, mn.friendlyName)
191193
new_mn.collateral_address = mn.collateral_address
192194
new_mn.collateral_txid = mn.collateral_txid
193195
new_mn.collateral_vout = mn.collateral_vout
@@ -196,115 +198,88 @@ def run_test(self):
196198
mns[i] = new_mn
197199
self.generate(self.nodes[0], 1)
198200
self.assert_mnlists(mns)
199-
self.log.info("restarting MN %s" % new_mn.alias)
200-
self.stop_node(new_mn.idx)
201+
self.log.info(f"restarting MN {mn.friendlyName}")
202+
self.stop_node(new_mn.nodeIdx)
201203
self.start_mn(new_mn)
202204
self.sync_all()
203205

204206
self.log.info("testing masternode status updates")
205207
# change voting address and see if changes are reflected in `masternode status` rpc output
206208
mn = mns[0]
207209
node = self.nodes[0]
208-
old_dmnState = mn.node.masternode("status")["dmnState"]
210+
old_dmnState = mn.get_node(self).masternode("status")["dmnState"]
209211
old_voting_address = old_dmnState["votingAddress"]
210212
new_voting_address = node.getnewaddress()
211213
assert old_voting_address != new_voting_address
212214
# also check if funds from payout address are used when no fee source address is specified
213215
node.sendtoaddress(mn.rewards_address, 0.001)
214-
node.protx('update_registrar' if softfork_active(node, 'v19') else 'update_registrar_legacy', mn.protx_hash, "", new_voting_address, "")
216+
node.protx('update_registrar' if softfork_active(node, 'v19') else 'update_registrar_legacy', mn.proTxHash, "", new_voting_address, "")
215217
self.generate(node, 1)
216-
new_dmnState = mn.node.masternode("status")["dmnState"]
218+
new_dmnState = mn.get_node(self).masternode("status")["dmnState"]
217219
new_voting_address_from_rpc = new_dmnState["votingAddress"]
218220
assert new_voting_address_from_rpc == new_voting_address
219221
# make sure payoutAddress is the same as before
220222
assert old_dmnState["payoutAddress"] == new_dmnState["payoutAddress"]
221223

222-
def prepare_mn(self, node, idx, alias):
223-
mn = Masternode()
224-
mn.idx = idx
225-
mn.alias = alias
226-
mn.p2p_port = p2p_port(mn.idx)
227-
mn.operator_reward = (mn.idx % self.num_initial_mn)
228-
229-
blsKey = node.bls('generate') if softfork_active(node, 'v19') else node.bls('generate', True)
230-
mn.fundsAddr = node.getnewaddress()
231-
mn.ownerAddr = node.getnewaddress()
232-
mn.operatorAddr = blsKey['public']
233-
mn.votingAddr = mn.ownerAddr
234-
mn.blsMnkey = blsKey['secret']
235-
224+
def prepare_mn(self, node, idx, alias) -> MasternodeInfo:
225+
mn = MasternodeInfo(evo=False, legacy=(not softfork_active(node, 'v19')))
226+
mn.generate_addresses(node)
227+
mn.set_params(operator_reward=(idx % self.num_initial_mn), nodePort=p2p_port(idx))
228+
mn.set_node(idx, alias)
236229
return mn
237230

238-
def create_mn_collateral(self, node, mn):
239-
mn.collateral_address = node.getnewaddress()
240-
mn.collateral_txid = node.sendtoaddress(mn.collateral_address, 1000)
241-
mn.collateral_vout = None
231+
def create_mn_collateral(self, node, mn: MasternodeInfo):
232+
txid = node.sendtoaddress(mn.collateral_address, mn.get_collateral_value())
242233
self.generate(node, 1, sync_fun=self.no_op)
243-
244-
rawtx = node.getrawtransaction(mn.collateral_txid, 1)
245-
for txout in rawtx['vout']:
246-
if txout['value'] == Decimal(1000):
247-
mn.collateral_vout = txout['n']
248-
break
249-
assert mn.collateral_vout is not None
234+
vout = mn.get_collateral_vout(node, txid)
235+
mn.set_params(collateral_txid=txid, collateral_vout=vout)
250236

251237
# register a protx MN and also fund it (using collateral inside ProRegTx)
252-
def register_fund_mn(self, node, mn):
253-
node.sendtoaddress(mn.fundsAddr, 1000.001)
254-
mn.collateral_address = node.getnewaddress()
255-
mn.rewards_address = node.getnewaddress()
256-
257-
mn.protx_hash = node.protx('register_fund' if softfork_active(node, 'v19') else 'register_fund_legacy', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, mn.operator_reward, mn.rewards_address, mn.fundsAddr)
258-
mn.collateral_txid = mn.protx_hash
259-
mn.collateral_vout = None
260-
261-
rawtx = node.getrawtransaction(mn.collateral_txid, 1)
262-
for txout in rawtx['vout']:
263-
if txout['value'] == Decimal(1000):
264-
mn.collateral_vout = txout['n']
265-
break
266-
assert mn.collateral_vout is not None
238+
def register_fund_mn(self, node, mn: MasternodeInfo):
239+
node.sendtoaddress(mn.fundsAddr, mn.get_collateral_value() + 0.001)
240+
txid = node.protx('register_fund' if softfork_active(node, 'v19') else 'register_fund_legacy', mn.collateral_address, '127.0.0.1:%d' % mn.nodePort, mn.ownerAddr, mn.pubKeyOperator, mn.votingAddr, mn.operator_reward, mn.rewards_address, mn.fundsAddr)
241+
vout = mn.get_collateral_vout(node, txid)
242+
mn.set_params(proTxHash=txid, collateral_txid=txid, collateral_vout=vout)
267243

268244
# create a protx MN which refers to an existing collateral
269-
def register_mn(self, node, mn):
245+
def register_mn(self, node, mn: MasternodeInfo):
270246
node.sendtoaddress(mn.fundsAddr, 0.001)
271-
mn.rewards_address = node.getnewaddress()
272-
273-
mn.protx_hash = node.protx('register' if softfork_active(node, 'v19') else 'register_legacy', mn.collateral_txid, mn.collateral_vout, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, mn.operator_reward, mn.rewards_address, mn.fundsAddr)
247+
proTxHash = node.protx('register' if softfork_active(node, 'v19') else 'register_legacy', mn.collateral_txid, mn.collateral_vout, '127.0.0.1:%d' % mn.nodePort, mn.ownerAddr, mn.pubKeyOperator, mn.votingAddr, mn.operator_reward, mn.rewards_address, mn.fundsAddr)
248+
mn.set_params(proTxHash=proTxHash)
274249
self.generate(node, 1, sync_fun=self.no_op)
275250

276-
def start_mn(self, mn):
277-
if len(self.nodes) <= mn.idx:
278-
self.add_nodes(mn.idx - len(self.nodes) + 1)
279-
assert len(self.nodes) == mn.idx + 1
280-
self.start_node(mn.idx, extra_args = self.extra_args + ['-masternodeblsprivkey=%s' % mn.blsMnkey])
281-
force_finish_mnsync(self.nodes[mn.idx])
282-
mn.node = self.nodes[mn.idx]
283-
self.connect_nodes(mn.idx, 0)
251+
def start_mn(self, mn: MasternodeInfo):
252+
assert mn.nodeIdx is not None, "nodeIdx must be set before starting masternode"
253+
if len(self.nodes) <= mn.nodeIdx:
254+
self.add_nodes(mn.nodeIdx - len(self.nodes) + 1)
255+
assert len(self.nodes) == mn.nodeIdx + 1
256+
self.start_node(mn.nodeIdx, extra_args = self.extra_args + ['-masternodeblsprivkey=%s' % mn.keyOperator])
257+
force_finish_mnsync(mn.get_node(self))
258+
self.connect_nodes(mn.nodeIdx, 0)
284259
self.sync_all()
285260

286-
def spend_mn_collateral(self, mn, with_dummy_input_output=False):
287-
return self.spend_input(mn.collateral_txid, mn.collateral_vout, 1000, with_dummy_input_output)
261+
def spend_mn_collateral(self, mn: MasternodeInfo, with_dummy_input_output=False):
262+
return self.spend_input(mn.collateral_txid, mn.collateral_vout, mn.get_collateral_value(), with_dummy_input_output)
288263

289-
def update_mn_payee(self, mn, payee):
264+
def update_mn_payee(self, mn: MasternodeInfo, payee):
290265
self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001)
291-
self.nodes[0].protx('update_registrar' if softfork_active(self.nodes[0], 'v19') else 'update_registrar_legacy', mn.protx_hash, '', '', payee, mn.fundsAddr)
266+
self.nodes[0].protx('update_registrar' if softfork_active(self.nodes[0], 'v19') else 'update_registrar_legacy', mn.proTxHash, '', '', payee, mn.fundsAddr)
292267
self.generate(self.nodes[0], 1)
293-
info = self.nodes[0].protx('info', mn.protx_hash)
268+
info = self.nodes[0].protx('info', mn.proTxHash)
294269
assert info['state']['payoutAddress'] == payee
295270

296-
def test_protx_update_service(self, mn):
271+
def test_protx_update_service(self, mn: MasternodeInfo):
297272
self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001)
298-
self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.2:%d' % mn.p2p_port, mn.blsMnkey, "", mn.fundsAddr)
273+
self.nodes[0].protx('update_service', mn.proTxHash, '127.0.0.2:%d' % mn.nodePort, mn.keyOperator, "", mn.fundsAddr)
299274
self.generate(self.nodes[0], 1)
300275
for node in self.nodes:
301-
protx_info = node.protx('info', mn.protx_hash)
276+
protx_info = node.protx('info', mn.proTxHash)
302277
mn_list = node.masternode('list')
303-
assert_equal(protx_info['state']['service'], '127.0.0.2:%d' % mn.p2p_port)
304-
assert_equal(mn_list['%s-%d' % (mn.collateral_txid, mn.collateral_vout)]['address'], '127.0.0.2:%d' % mn.p2p_port)
278+
assert_equal(protx_info['state']['service'], '127.0.0.2:%d' % mn.nodePort)
279+
assert_equal(mn_list['%s-%d' % (mn.collateral_txid, mn.collateral_vout)]['address'], '127.0.0.2:%d' % mn.nodePort)
305280

306281
# undo
307-
self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.1:%d' % mn.p2p_port, mn.blsMnkey, "", mn.fundsAddr)
282+
self.nodes[0].protx('update_service', mn.proTxHash, '127.0.0.1:%d' % mn.nodePort, mn.keyOperator, "", mn.fundsAddr)
308283
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
309284

310285
def assert_mnlists(self, mns):

test/functional/feature_dip3_v19.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
from test_framework.p2p import P2PInterface
1515
from test_framework.messages import CBlock, CBlockHeader, CCbTx, CMerkleBlock, from_hex, hash256, msg_getmnlistd, \
1616
QuorumId, ser_uint256
17-
from test_framework.test_framework import DashTestFramework
17+
from test_framework.test_framework import (
18+
DashTestFramework,
19+
MasternodeInfo,
20+
)
1821
from test_framework.util import (
1922
assert_equal
2023
)
@@ -78,16 +81,16 @@ def run_test(self):
7881
self.log.info("pubkeyoperator should still be shown using legacy scheme")
7982
assert_equal(pubkeyoperator_list_before, pubkeyoperator_list_after)
8083

81-
evo_info_0 = self.dynamically_add_masternode(evo=True, rnd=7)
84+
evo_info_0: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=7)
8285
assert evo_info_0 is not None
8386

8487
self.log.info("Checking that protxs with duplicate EvoNodes fields are rejected")
85-
evo_info_1 = self.dynamically_add_masternode(evo=True, rnd=7, should_be_rejected=True)
88+
evo_info_1: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=7, should_be_rejected=True)
8689
assert evo_info_1 is None
8790
self.dynamically_evo_update_service(evo_info_0, 8)
88-
evo_info_2 = self.dynamically_add_masternode(evo=True, rnd=8, should_be_rejected=True)
91+
evo_info_2: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=8, should_be_rejected=True)
8992
assert evo_info_2 is None
90-
evo_info_3 = self.dynamically_add_masternode(evo=True, rnd=9)
93+
evo_info_3: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=9)
9194
assert evo_info_3 is not None
9295
self.dynamically_evo_update_service(evo_info_0, 9, should_be_rejected=True)
9396

@@ -101,7 +104,7 @@ def run_test(self):
101104
self.log.info("Checking that adding more regular MNs after v19 doesn't break DKGs and IS/CLs")
102105

103106
for i in range(6):
104-
new_mn = self.dynamically_add_masternode(evo=False, rnd=(10 + i))
107+
new_mn: MasternodeInfo = self.dynamically_add_masternode(evo=False, rnd=(10 + i))
105108
assert new_mn is not None
106109

107110
# mine more quorums and make sure everything still works
@@ -129,7 +132,7 @@ def test_revoke_protx(self, node_idx, revoke_protx, revoke_keyoperator):
129132
self.connect_nodes(node_idx, 0)
130133
self.sync_all()
131134
self.log.info(f"Successfully revoked={revoke_protx}")
132-
for mn in self.mninfo:
135+
for mn in self.mninfo: # type: MasternodeInfo
133136
if mn.proTxHash == revoke_protx:
134137
self.mninfo.remove(mn)
135138
return

test/functional/feature_dip4_coinbasemerkleroots.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414

1515
from test_framework.messages import CBlock, CBlockHeader, CCbTx, CMerkleBlock, from_hex, hash256, msg_getmnlistd, QuorumId, ser_uint256
1616
from test_framework.p2p import P2PInterface
17-
from test_framework.test_framework import DashTestFramework
17+
from test_framework.test_framework import (
18+
DashTestFramework,
19+
MasternodeInfo,
20+
)
1821
from test_framework.util import assert_equal
1922

2023
DIP0008_HEIGHT = 432
@@ -50,7 +53,7 @@ def set_test_params(self):
5053
self.delay_v20_and_mn_rr(height=V20_HEIGHT)
5154

5255
def remove_masternode(self, idx):
53-
mn = self.mninfo[idx]
56+
mn: MasternodeInfo = self.mninfo[idx]
5457
rawtx = self.nodes[0].createrawtransaction([{"txid": mn.collateral_txid, "vout": mn.collateral_vout}], {self.nodes[0].getnewaddress(): 999.9999})
5558
rawtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
5659
self.nodes[0].sendrawtransaction(rawtx["hex"])
@@ -80,7 +83,7 @@ def run_test(self):
8083
# Register one more MN, but don't start it (that would fail as DashTestFramework doesn't support this atm)
8184
baseBlockHash = self.nodes[0].getbestblockhash()
8285
self.prepare_masternode(self.mn_count)
83-
new_mn = self.mninfo[self.mn_count]
86+
new_mn: MasternodeInfo = self.mninfo[self.mn_count]
8487

8588
# Now test if that MN appears in a diff when the base block is the one just before MN registration
8689
expectedDeleted = []

test/functional/feature_governance.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
import json
88

99
from test_framework.messages import uint256_to_string
10-
from test_framework.test_framework import DashTestFramework
10+
from test_framework.test_framework import (
11+
DashTestFramework,
12+
MasternodeInfo,
13+
)
1114
from test_framework.governance import have_trigger_for_height, prepare_object
1215
from test_framework.util import assert_equal, satoshi_round
1316

@@ -187,7 +190,7 @@ def run_test(self):
187190
_, mn_payee_protx = height_protx_list[1]
188191

189192
payee_idx = None
190-
for mn in self.mninfo:
193+
for mn in self.mninfo: # type: MasternodeInfo
191194
if mn.proTxHash == mn_payee_protx:
192195
payee_idx = mn.nodeIdx
193196
break

0 commit comments

Comments
 (0)