Skip to content
Merged
4 changes: 2 additions & 2 deletions test/functional/feature_asset_locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def create_assetunlock(self, index, withdrawal, pubkey=None, fee=tiny_amount):

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

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

self.log.info("Test no IS for asset unlock...")
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0)
Expand Down
137 changes: 56 additions & 81 deletions test/functional/feature_dip3_deterministicmns.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@
#

from decimal import Decimal
from typing import List

from test_framework.blocktools import create_block_with_mnpayments
from test_framework.messages import tx_from_hex
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_framework import (
MASTERNODE_COLLATERAL,
BitcoinTestFramework,
MasternodeInfo,
)
from test_framework.util import assert_equal, force_finish_mnsync, p2p_port, softfork_active

class Masternode(object):
pass

class DIP3Test(BitcoinTestFramework):
def set_test_params(self):
self.num_initial_mn = 11 # Should be >= 11 to make sure quorums are not always the same MNs
Expand Down Expand Up @@ -48,19 +50,19 @@ def start_controller_node(self):

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

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

mns = []
mns: List[MasternodeInfo] = []

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

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

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

# start a few MNs before they are registered and a few after they are registered
Expand All @@ -93,12 +95,12 @@ def run_test(self):
# let a few of the protx MNs refer to the existing collaterals
fund = (i % 2) == 0
if fund:
self.log.info("register_fund %s" % mn.alias)
self.log.info(f"register_fund {mn.friendlyName}")
self.register_fund_mn(self.nodes[0], mn)
else:
self.log.info("create_collateral %s" % mn.alias)
self.log.info(f"create_collateral {mn.friendlyName}")
self.create_mn_collateral(self.nodes[0], mn)
self.log.info("register %s" % mn.alias)
self.log.info(f"register {mn.friendlyName}")
self.register_mn(self.nodes[0], mn)

self.generate(self.nodes[0], 1, sync_fun=self.no_op)
Expand All @@ -117,7 +119,7 @@ def run_test(self):
old_tip = self.nodes[0].getblockcount()
old_listdiff = self.nodes[0].protx("listdiff", 1, old_tip)
for i in range(spend_mns_count):
old_protx_hash = mns[i].protx_hash
old_protx_hash = mns[i].proTxHash
old_collateral_address = mns[i].collateral_address
old_blockhash = self.nodes[0].getbestblockhash()
old_rpc_info = self.nodes[0].protx("info", old_protx_hash)
Expand Down Expand Up @@ -187,7 +189,7 @@ def run_test(self):
mn = mns[i]
# a few of these will actually refer to old ProRegTx internal collaterals,
# which should work the same as external collaterals
new_mn = self.prepare_mn(self.nodes[0], mn.idx, mn.alias)
new_mn: MasternodeInfo = self.prepare_mn(self.nodes[0], mn.nodeIdx, mn.friendlyName)
new_mn.collateral_address = mn.collateral_address
new_mn.collateral_txid = mn.collateral_txid
new_mn.collateral_vout = mn.collateral_vout
Expand All @@ -196,115 +198,88 @@ def run_test(self):
mns[i] = new_mn
self.generate(self.nodes[0], 1)
self.assert_mnlists(mns)
self.log.info("restarting MN %s" % new_mn.alias)
self.stop_node(new_mn.idx)
self.log.info(f"restarting MN {mn.friendlyName}")
self.stop_node(new_mn.nodeIdx)
self.start_mn(new_mn)
self.sync_all()

self.log.info("testing masternode status updates")
# change voting address and see if changes are reflected in `masternode status` rpc output
mn = mns[0]
node = self.nodes[0]
old_dmnState = mn.node.masternode("status")["dmnState"]
old_dmnState = mn.get_node(self).masternode("status")["dmnState"]
old_voting_address = old_dmnState["votingAddress"]
new_voting_address = node.getnewaddress()
assert old_voting_address != new_voting_address
# also check if funds from payout address are used when no fee source address is specified
node.sendtoaddress(mn.rewards_address, 0.001)
node.protx('update_registrar' if softfork_active(node, 'v19') else 'update_registrar_legacy', mn.protx_hash, "", new_voting_address, "")
node.protx('update_registrar' if softfork_active(node, 'v19') else 'update_registrar_legacy', mn.proTxHash, "", new_voting_address, "")
self.generate(node, 1)
new_dmnState = mn.node.masternode("status")["dmnState"]
new_dmnState = mn.get_node(self).masternode("status")["dmnState"]
new_voting_address_from_rpc = new_dmnState["votingAddress"]
assert new_voting_address_from_rpc == new_voting_address
# make sure payoutAddress is the same as before
assert old_dmnState["payoutAddress"] == new_dmnState["payoutAddress"]

def prepare_mn(self, node, idx, alias):
mn = Masternode()
mn.idx = idx
mn.alias = alias
mn.p2p_port = p2p_port(mn.idx)
mn.operator_reward = (mn.idx % self.num_initial_mn)

blsKey = node.bls('generate') if softfork_active(node, 'v19') else node.bls('generate', True)
mn.fundsAddr = node.getnewaddress()
mn.ownerAddr = node.getnewaddress()
mn.operatorAddr = blsKey['public']
mn.votingAddr = mn.ownerAddr
mn.blsMnkey = blsKey['secret']

def prepare_mn(self, node, idx, alias) -> MasternodeInfo:
mn = MasternodeInfo(evo=False, legacy=(not softfork_active(node, 'v19')))
mn.generate_addresses(node)
mn.set_params(operator_reward=(idx % self.num_initial_mn), nodePort=p2p_port(idx))
mn.set_node(idx, alias)
return mn

def create_mn_collateral(self, node, mn):
mn.collateral_address = node.getnewaddress()
mn.collateral_txid = node.sendtoaddress(mn.collateral_address, 1000)
mn.collateral_vout = None
def create_mn_collateral(self, node, mn: MasternodeInfo):
txid = node.sendtoaddress(mn.collateral_address, mn.get_collateral_value())
self.generate(node, 1, sync_fun=self.no_op)

rawtx = node.getrawtransaction(mn.collateral_txid, 1)
for txout in rawtx['vout']:
if txout['value'] == Decimal(1000):
mn.collateral_vout = txout['n']
break
assert mn.collateral_vout is not None
vout = mn.get_collateral_vout(node, txid)
mn.set_params(collateral_txid=txid, collateral_vout=vout)

# register a protx MN and also fund it (using collateral inside ProRegTx)
def register_fund_mn(self, node, mn):
node.sendtoaddress(mn.fundsAddr, 1000.001)
mn.collateral_address = node.getnewaddress()
mn.rewards_address = node.getnewaddress()

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)
mn.collateral_txid = mn.protx_hash
mn.collateral_vout = None

rawtx = node.getrawtransaction(mn.collateral_txid, 1)
for txout in rawtx['vout']:
if txout['value'] == Decimal(1000):
mn.collateral_vout = txout['n']
break
assert mn.collateral_vout is not None
def register_fund_mn(self, node, mn: MasternodeInfo):
node.sendtoaddress(mn.fundsAddr, mn.get_collateral_value() + 0.001)
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)
vout = mn.get_collateral_vout(node, txid)
mn.set_params(proTxHash=txid, collateral_txid=txid, collateral_vout=vout)

# create a protx MN which refers to an existing collateral
def register_mn(self, node, mn):
def register_mn(self, node, mn: MasternodeInfo):
node.sendtoaddress(mn.fundsAddr, 0.001)
mn.rewards_address = node.getnewaddress()

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)
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)
mn.set_params(proTxHash=proTxHash)
self.generate(node, 1, sync_fun=self.no_op)

def start_mn(self, mn):
if len(self.nodes) <= mn.idx:
self.add_nodes(mn.idx - len(self.nodes) + 1)
assert len(self.nodes) == mn.idx + 1
self.start_node(mn.idx, extra_args = self.extra_args + ['-masternodeblsprivkey=%s' % mn.blsMnkey])
force_finish_mnsync(self.nodes[mn.idx])
mn.node = self.nodes[mn.idx]
self.connect_nodes(mn.idx, 0)
def start_mn(self, mn: MasternodeInfo):
assert mn.nodeIdx is not None, "nodeIdx must be set before starting masternode"
if len(self.nodes) <= mn.nodeIdx:
self.add_nodes(mn.nodeIdx - len(self.nodes) + 1)
assert len(self.nodes) == mn.nodeIdx + 1
self.start_node(mn.nodeIdx, extra_args = self.extra_args + ['-masternodeblsprivkey=%s' % mn.keyOperator])
force_finish_mnsync(mn.get_node(self))
self.connect_nodes(mn.nodeIdx, 0)
self.sync_all()

def spend_mn_collateral(self, mn, with_dummy_input_output=False):
return self.spend_input(mn.collateral_txid, mn.collateral_vout, 1000, with_dummy_input_output)
def spend_mn_collateral(self, mn: MasternodeInfo, with_dummy_input_output=False):
return self.spend_input(mn.collateral_txid, mn.collateral_vout, mn.get_collateral_value(), with_dummy_input_output)

def update_mn_payee(self, mn, payee):
def update_mn_payee(self, mn: MasternodeInfo, payee):
self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001)
self.nodes[0].protx('update_registrar' if softfork_active(self.nodes[0], 'v19') else 'update_registrar_legacy', mn.protx_hash, '', '', payee, mn.fundsAddr)
self.nodes[0].protx('update_registrar' if softfork_active(self.nodes[0], 'v19') else 'update_registrar_legacy', mn.proTxHash, '', '', payee, mn.fundsAddr)
self.generate(self.nodes[0], 1)
info = self.nodes[0].protx('info', mn.protx_hash)
info = self.nodes[0].protx('info', mn.proTxHash)
assert info['state']['payoutAddress'] == payee

def test_protx_update_service(self, mn):
def test_protx_update_service(self, mn: MasternodeInfo):
self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001)
self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.2:%d' % mn.p2p_port, mn.blsMnkey, "", mn.fundsAddr)
self.nodes[0].protx('update_service', mn.proTxHash, '127.0.0.2:%d' % mn.nodePort, mn.keyOperator, "", mn.fundsAddr)
self.generate(self.nodes[0], 1)
for node in self.nodes:
protx_info = node.protx('info', mn.protx_hash)
protx_info = node.protx('info', mn.proTxHash)
mn_list = node.masternode('list')
assert_equal(protx_info['state']['service'], '127.0.0.2:%d' % mn.p2p_port)
assert_equal(mn_list['%s-%d' % (mn.collateral_txid, mn.collateral_vout)]['address'], '127.0.0.2:%d' % mn.p2p_port)
assert_equal(protx_info['state']['service'], '127.0.0.2:%d' % mn.nodePort)
assert_equal(mn_list['%s-%d' % (mn.collateral_txid, mn.collateral_vout)]['address'], '127.0.0.2:%d' % mn.nodePort)

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

def assert_mnlists(self, mns):
Expand Down
17 changes: 10 additions & 7 deletions test/functional/feature_dip3_v19.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
from test_framework.p2p import P2PInterface
from test_framework.messages import CBlock, CBlockHeader, CCbTx, CMerkleBlock, from_hex, hash256, msg_getmnlistd, \
QuorumId, ser_uint256
from test_framework.test_framework import DashTestFramework
from test_framework.test_framework import (
DashTestFramework,
MasternodeInfo,
)
from test_framework.util import (
assert_equal
)
Expand Down Expand Up @@ -78,16 +81,16 @@ def run_test(self):
self.log.info("pubkeyoperator should still be shown using legacy scheme")
assert_equal(pubkeyoperator_list_before, pubkeyoperator_list_after)

evo_info_0 = self.dynamically_add_masternode(evo=True, rnd=7)
evo_info_0: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=7)
assert evo_info_0 is not None

self.log.info("Checking that protxs with duplicate EvoNodes fields are rejected")
evo_info_1 = self.dynamically_add_masternode(evo=True, rnd=7, should_be_rejected=True)
evo_info_1: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=7, should_be_rejected=True)
assert evo_info_1 is None
self.dynamically_evo_update_service(evo_info_0, 8)
evo_info_2 = self.dynamically_add_masternode(evo=True, rnd=8, should_be_rejected=True)
evo_info_2: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=8, should_be_rejected=True)
assert evo_info_2 is None
evo_info_3 = self.dynamically_add_masternode(evo=True, rnd=9)
evo_info_3: MasternodeInfo = self.dynamically_add_masternode(evo=True, rnd=9)
assert evo_info_3 is not None
self.dynamically_evo_update_service(evo_info_0, 9, should_be_rejected=True)

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

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

# mine more quorums and make sure everything still works
Expand Down Expand Up @@ -129,7 +132,7 @@ def test_revoke_protx(self, node_idx, revoke_protx, revoke_keyoperator):
self.connect_nodes(node_idx, 0)
self.sync_all()
self.log.info(f"Successfully revoked={revoke_protx}")
for mn in self.mninfo:
for mn in self.mninfo: # type: MasternodeInfo
if mn.proTxHash == revoke_protx:
self.mninfo.remove(mn)
return
Expand Down
9 changes: 6 additions & 3 deletions test/functional/feature_dip4_coinbasemerkleroots.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

from test_framework.messages import CBlock, CBlockHeader, CCbTx, CMerkleBlock, from_hex, hash256, msg_getmnlistd, QuorumId, ser_uint256
from test_framework.p2p import P2PInterface
from test_framework.test_framework import DashTestFramework
from test_framework.test_framework import (
DashTestFramework,
MasternodeInfo,
)
from test_framework.util import assert_equal

DIP0008_HEIGHT = 432
Expand Down Expand Up @@ -50,7 +53,7 @@ def set_test_params(self):
self.delay_v20_and_mn_rr(height=V20_HEIGHT)

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

# Now test if that MN appears in a diff when the base block is the one just before MN registration
expectedDeleted = []
Expand Down
7 changes: 5 additions & 2 deletions test/functional/feature_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import json

from test_framework.messages import uint256_to_string
from test_framework.test_framework import DashTestFramework
from test_framework.test_framework import (
DashTestFramework,
MasternodeInfo,
)
from test_framework.governance import have_trigger_for_height, prepare_object
from test_framework.util import assert_equal, satoshi_round

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

payee_idx = None
for mn in self.mninfo:
for mn in self.mninfo: # type: MasternodeInfo
if mn.proTxHash == mn_payee_protx:
payee_idx = mn.nodeIdx
break
Expand Down
Loading
Loading