Skip to content

Commit 93e8453

Browse files
committed
[Tests] Update deterministicmns test checking collateral locking
1 parent cc132a8 commit 93e8453

File tree

4 files changed

+57
-34
lines changed

4 files changed

+57
-34
lines changed

test/functional/test_framework/messages.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ def deserialize_uniqueness(self, f):
305305
def __repr__(self):
306306
return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n)
307307

308+
def to_json(self):
309+
return {"txid": "%064x" % self.hash, "vout": self.n}
310+
308311
NullOutPoint = COutPoint(0, 0xffffffff)
309312

310313
class CTxIn():

test/functional/test_framework/test_framework.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@
4848
connect_nodes_clique,
4949
disconnect_nodes,
5050
get_collateral_vout,
51-
lock_utxo,
5251
Decimal,
5352
DEFAULT_FEE,
5453
get_datadir_path,
5554
hex_str_to_bytes,
5655
bytes_to_hex_str,
5756
initialize_datadir,
57+
is_coin_locked_by,
5858
create_new_dmn,
5959
p2p_port,
6060
set_node_times,
@@ -1193,7 +1193,7 @@ def not_found():
11931193
"""
11941194
Create a ProReg tx, which has the collateral as one of its outputs
11951195
"""
1196-
def protx_register_fund(self, miner, controller, dmn, collateral_addr, lock=True, op_rew=None):
1196+
def protx_register_fund(self, miner, controller, dmn, collateral_addr, op_rew=None):
11971197
# send to the owner the collateral tx + some dust for the ProReg and fee
11981198
funding_txid = miner.sendtoaddress(collateral_addr, Decimal('101'))
11991199
# confirm and verify reception
@@ -1210,14 +1210,12 @@ def protx_register_fund(self, miner, controller, dmn, collateral_addr, lock=True
12101210
op_rew["reward"], op_rew["address"])
12111211
dmn.collateral = COutPoint(int(dmn.proTx, 16),
12121212
get_collateral_vout(controller.getrawtransaction(dmn.proTx, True)))
1213-
if lock:
1214-
lock_utxo(controller, dmn.collateral)
12151213

12161214
"""
12171215
Create a ProReg tx, which references an 100 PIV UTXO as collateral.
12181216
The controller node owns the collateral and creates the ProReg tx.
12191217
"""
1220-
def protx_register(self, miner, controller, dmn, collateral_addr, fLock):
1218+
def protx_register(self, miner, controller, dmn, collateral_addr):
12211219
# send to the owner the exact collateral tx amount
12221220
funding_txid = miner.sendtoaddress(collateral_addr, Decimal('100'))
12231221
# send another output to be used for the fee of the proReg tx
@@ -1231,14 +1229,12 @@ def protx_register(self, miner, controller, dmn, collateral_addr, fLock):
12311229
dmn.collateral = COutPoint(int(funding_txid, 16), get_collateral_vout(json_tx))
12321230
dmn.proTx = controller.protx_register(funding_txid, dmn.collateral.n, dmn.ipport, dmn.owner,
12331231
dmn.operator, dmn.voting, dmn.payee)
1234-
if fLock:
1235-
lock_utxo(controller, dmn.collateral)
12361232

12371233
"""
12381234
Create a ProReg tx, referencing a collateral signed externally (eg. HW wallets).
12391235
Here the controller node owns the collateral (and signs), but the miner creates the ProReg tx.
12401236
"""
1241-
def protx_register_ext(self, miner, controller, dmn, outpoint, fSubmit, fLock):
1237+
def protx_register_ext(self, miner, controller, dmn, outpoint, fSubmit):
12421238
# send to the owner the collateral tx if the outpoint is not specified
12431239
if outpoint is None:
12441240
funding_txid = miner.sendtoaddress(controller.getnewaddress("collateral"), Decimal('100'))
@@ -1254,8 +1250,6 @@ def protx_register_ext(self, miner, controller, dmn, outpoint, fSubmit, fLock):
12541250
dmn.operator, dmn.voting, dmn.payee)
12551251
sig = controller.signmessage(reg_tx["collateralAddress"], reg_tx["signMessage"])
12561252
if fSubmit:
1257-
if fLock:
1258-
lock_utxo(controller, dmn.collateral)
12591253
dmn.proTx = miner.protx_register_submit(reg_tx["tx"], sig)
12601254
else:
12611255
return reg_tx["tx"], sig
@@ -1271,11 +1265,10 @@ def protx_register_ext(self, miner, controller, dmn, outpoint, fSubmit, fLock):
12711265
If not provided, a new utxo is created, sending it from the miner.
12721266
op_addr_and_key: (list of strings) List with two entries, operator address (0) and private key (1).
12731267
If not provided, a new address-key pair is generated.
1274-
fLock: (boolean) lock the collateral output
12751268
:return: dmn: (Masternode) the deterministic masternode object
12761269
"""
12771270
def register_new_dmn(self, idx, miner_idx, controller_idx, strType,
1278-
payout_addr=None, outpoint=None, op_addr_and_key=None, fLock=True):
1271+
payout_addr=None, outpoint=None, op_addr_and_key=None):
12791272
# Prepare remote node
12801273
assert idx != miner_idx
12811274
assert idx != controller_idx
@@ -1293,11 +1286,11 @@ def register_new_dmn(self, idx, miner_idx, controller_idx, strType,
12931286
self.log.info("Creating%s proRegTx for deterministic masternode idx=%d..." % (
12941287
" and funding" if strType == "fund" else "", idx))
12951288
if strType == "fund":
1296-
self.protx_register_fund(miner_node, controller_node, dmn, collateral_addr, fLock)
1289+
self.protx_register_fund(miner_node, controller_node, dmn, collateral_addr)
12971290
elif strType == "internal":
1298-
self.protx_register(miner_node, controller_node, dmn, collateral_addr, fLock)
1291+
self.protx_register(miner_node, controller_node, dmn, collateral_addr)
12991292
elif strType == "external":
1300-
self.protx_register_ext(miner_node, controller_node, dmn, outpoint, True, fLock)
1293+
self.protx_register_ext(miner_node, controller_node, dmn, outpoint, True)
13011294
else:
13021295
raise Exception("Type %s not available" % strType)
13031296
time.sleep(1)
@@ -1308,6 +1301,10 @@ def register_new_dmn(self, idx, miner_idx, controller_idx, strType,
13081301
self.sync_blocks(self.nodes)
13091302
assert_greater_than(mn_node.getrawtransaction(dmn.proTx, 1)["confirmations"], 0)
13101303
assert dmn.proTx in mn_node.protx_list(False)
1304+
1305+
# check coin locking
1306+
assert is_coin_locked_by(controller_node, dmn.collateral)
1307+
13111308
return dmn
13121309

13131310
def check_mn_list_on_node(self, idx, mns):
@@ -1317,7 +1314,7 @@ def check_mn_list_on_node(self, idx, mns):
13171314
protxs = [x["proTxHash"] for x in mnlist]
13181315
for mn in mns:
13191316
if mn.proTx not in protxs:
1320-
raise Exception("ProTx for mn %d (%s) not found in the list of node %d", mn.idx, mn.proTx, idx)
1317+
raise Exception("ProTx for mn %d (%s) not found in the list of node %d" % (mn.idx, mn.proTx, idx))
13211318

13221319

13231320
### ------------------------------------------------------

test/functional/test_framework/util.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -583,9 +583,8 @@ def get_coinstake_address(node, expected_utxos=None):
583583
return addrs[0]
584584

585585
# Deterministic masternodes
586-
587-
def lock_utxo(node, outpoint):
588-
node.lockunspent(False, [{"txid": "%064x" % outpoint.hash, "vout": outpoint.n}])
586+
def is_coin_locked_by(node, outpoint):
587+
return outpoint.to_json() in node.listlockunspent()
589588

590589
def get_collateral_vout(json_tx):
591590
funding_txidn = -1
@@ -599,7 +598,8 @@ def get_collateral_vout(json_tx):
599598
# owner and voting keys are created from controller node.
600599
# operator key and address are created, if operator_addr_and_key is None.
601600
def create_new_dmn(idx, controller, payout_addr, operator_addr_and_key):
602-
ipport = "127.0.0.1:" + str(p2p_port(idx))
601+
port = p2p_port(idx) if idx <= MAX_NODES else p2p_port(MAX_NODES) + (idx - MAX_NODES)
602+
ipport = "127.0.0.1:" + str(port)
603603
owner_addr = controller.getnewaddress("mnowner-%d" % idx)
604604
voting_addr = controller.getnewaddress("mnvoting-%d" % idx)
605605
if operator_addr_and_key is None:
@@ -611,7 +611,7 @@ def create_new_dmn(idx, controller, payout_addr, operator_addr_and_key):
611611
return messages.Masternode(idx, owner_addr, operator_addr, voting_addr, ipport, payout_addr, operator_key)
612612

613613
def spend_mn_collateral(spender, dmn):
614-
inputs = [{"txid": "%064x" % dmn.collateral.hash, "vout": dmn.collateral.n}]
614+
inputs = [dmn.collateral.to_json()]
615615
outputs = {spender.getnewaddress(): Decimal('99.99')}
616616
sig_res = spender.signrawtransaction(spender.createrawtransaction(inputs, outputs))
617617
assert_equal(sig_res['complete'], True)

test/functional/tiertwo_deterministicmns.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
assert_raises_rpc_error,
2424
bytes_to_hex_str,
2525
create_new_dmn,
26+
connect_nodes,
2627
hex_str_to_bytes,
28+
is_coin_locked_by,
2729
spend_mn_collateral,
2830
)
2931

@@ -39,14 +41,13 @@ def set_test_params(self):
3941
self.extra_args = [["-nuparams=v5_shield:1", "-nuparams=v6_evo:130"]] * self.num_nodes
4042
self.extra_args[0].append("-sporkkey=932HEevBSujW2ud7RfB1YF91AFygbBRQj3de3LyaCRqNzKKgWXi")
4143

42-
def add_new_dmn(self, mns, strType, op_keys=None, from_out=None, lock=True):
44+
def add_new_dmn(self, mns, strType, op_keys=None, from_out=None):
4345
mns.append(self.register_new_dmn(2 + len(mns),
4446
self.minerPos,
4547
self.controllerPos,
4648
strType,
4749
outpoint=from_out,
48-
op_addr_and_key=op_keys,
49-
fLock=lock))
50+
op_addr_and_key=op_keys))
5051

5152
def check_mn_list(self, mns):
5253
for i in range(self.num_nodes):
@@ -61,12 +62,21 @@ def get_last_paid_mn(self):
6162
return next(x['proTxHash'] for x in self.nodes[0].listmasternodes()
6263
if x['dmnstate']['lastPaidHeight'] == self.nodes[0].getblockcount())
6364

64-
def create_block(self, mn_payee_script, height, prevhash):
65-
coinbase = create_coinbase(height)
65+
def create_block(self, mn_payee_script, prev_block):
66+
coinbase = create_coinbase(prev_block["height"] + 1)
6667
coinbase.vout[0].nValue -= 3 * COIN
6768
coinbase.vout.append(CTxOut(int(3 * COIN), hex_str_to_bytes(mn_payee_script)))
6869
coinbase.rehash()
69-
return create_block(int(prevhash, 16), coinbase, nVersion=10)
70+
return create_block(int(prev_block["hash"], 16),
71+
coinbase,
72+
hashFinalSaplingRoot=int(prev_block["finalsaplingroot"], 16),
73+
nVersion=10)
74+
75+
def restart_controller(self):
76+
self.restart_node(self.controllerPos, extra_args=self.extra_args[self.controllerPos])
77+
self.connect_to_all(self.controllerPos)
78+
connect_nodes(self.nodes[self.controllerPos], self.minerPos)
79+
self.sync_all()
7080

7181
def wait_until_mnsync_completed(self):
7282
SYNC_FINISHED = [999] * self.num_nodes
@@ -111,11 +121,14 @@ def run_test(self):
111121
assert_raises_rpc_error(-1, "Evo upgrade is not active yet", self.add_new_dmn, mns, "fund")
112122
# Can create the raw proReg
113123
dmn = create_new_dmn(2, controller, dummy_add, None)
114-
tx, sig = self.protx_register_ext(miner, controller, dmn, None, False, False)
124+
tx, sig = self.protx_register_ext(miner, controller, dmn, None, False)
115125
# but cannot send it
116126
assert_raises_rpc_error(-1, "Evo upgrade is not active yet", miner.protx_register_submit, tx, sig)
117127
self.log.info("Done. Now mine blocks till enforcement...")
118128

129+
# Check that no coin has been locked by the controller yet
130+
assert_equal(len(controller.listlockunspent()), 0)
131+
119132
# DIP3 activates at block 130.
120133
miner.generate(130 - miner.getblockcount())
121134
self.sync_blocks()
@@ -162,6 +175,17 @@ def run_test(self):
162175
["Ready"] * (self.num_nodes - 2))
163176
self.log.info("All masternodes ready.")
164177

178+
# Restart the controller and check that the collaterals are still locked
179+
self.log.info("Restarting controller...")
180+
self.restart_controller()
181+
time.sleep(1)
182+
for mn in mns:
183+
if not is_coin_locked_by(controller, mn.collateral):
184+
raise Exception(
185+
"Collateral %s of mn with idx=%d is not locked" % (mn.collateral, mn.idx)
186+
)
187+
self.log.info("Collaterals still locked.")
188+
165189
# Test collateral spending
166190
dmn = mns.pop(randrange(len(mns))) # pop one at random
167191
self.log.info("Spending collateral of mn with idx=%d..." % dmn.idx)
@@ -179,8 +203,7 @@ def run_test(self):
179203
dmn2_keys = [dmn2.operator, dmn2.operator_key]
180204
self.log.info("Reactivating node %d reusing the collateral of node %d..." % (dmn.idx, dmn2.idx))
181205
mns.append(self.register_new_dmn(dmn.idx, self.minerPos, self.controllerPos, "external",
182-
outpoint=dmn2.collateral, op_addr_and_key=dmn_keys,
183-
fLock=False))
206+
outpoint=dmn2.collateral, op_addr_and_key=dmn_keys))
184207
miner.generate(1)
185208
self.sync_blocks()
186209
self.check_mn_list(mns)
@@ -196,20 +219,20 @@ def run_test(self):
196219
self.log.info("Trying duplicate operator key...")
197220
dmn2b = create_new_dmn(dmn2.idx, controller, dummy_add, dmn_keys)
198221
assert_raises_rpc_error(-1, "bad-protx-dup-operator-key",
199-
self.protx_register_fund, miner, controller, dmn2b, dummy_add, False)
222+
self.protx_register_fund, miner, controller, dmn2b, dummy_add)
200223

201224
# Now try with duplicate owner key
202225
self.log.info("Trying duplicate owner key...")
203226
dmn2c = create_new_dmn(dmn2.idx, controller, dummy_add, dmn2_keys)
204227
dmn2c.owner = mns[randrange(len(mns))].owner
205228
assert_raises_rpc_error(-1, "bad-protx-dup-owner-key",
206-
self.protx_register_fund, miner, controller, dmn2c, dummy_add, False)
229+
self.protx_register_fund, miner, controller, dmn2c, dummy_add)
207230

208231
# Finally, register it properly. This time setting 10% of the reward for the operator
209232
op_rew = {"reward": 10.00, "address": self.nodes[dmn2.idx].getnewaddress()}
210233
self.log.info("Reactivating the node with a new registration (with operator reward)...")
211234
dmn2c = create_new_dmn(dmn2.idx, controller, dummy_add, dmn2_keys)
212-
self.protx_register_fund(miner, controller, dmn2c, dummy_add, True, op_rew)
235+
self.protx_register_fund(miner, controller, dmn2c, dummy_add, op_rew)
213236
mns.append(dmn2c)
214237
time.sleep(1)
215238
self.sync_mempools([miner, controller])
@@ -244,7 +267,7 @@ def run_test(self):
244267
self.wait_until_mnsync_completed() # just to be sure
245268
self.log.info("Testing invalid masternode payment...")
246269
mn_payee_script = miner.validateaddress(miner.getnewaddress())['scriptPubKey']
247-
block = self.create_block(mn_payee_script, miner.getblockcount(), miner.getbestblockhash())
270+
block = self.create_block(mn_payee_script, miner.getblock(miner.getbestblockhash(), True))
248271
block.solve()
249272
assert_equal(miner.submitblock(bytes_to_hex_str(block.serialize())), "bad-cb-payee")
250273

0 commit comments

Comments
 (0)