forked from decenomy/DSW
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmining_pos_reorg.py
More file actions
executable file
·193 lines (165 loc) · 8.44 KB
/
mining_pos_reorg.py
File metadata and controls
executable file
·193 lines (165 loc) · 8.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env python3
# Copyright (c) 2019-2020 The PIVX developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import PivxTestFramework
from test_framework.util import (
sync_blocks,
assert_equal,
assert_raises_rpc_error,
connect_nodes,
connect_nodes_clique,
disconnect_nodes,
set_node_times,
DecimalAmt,
)
class ReorgStakeTest(PivxTestFramework):
def set_test_params(self):
self.num_nodes = 3
# node 0 and 1 stake the blocks
def setup_chain(self):
# Start with PoS cache: 330 blocks
self._initialize_chain(toPosPhase=True)
self.enable_mocktime()
def setup_network(self):
# connect all nodes between each other
self.setup_nodes()
connect_nodes_clique(self.nodes)
self.sync_all()
def log_title(self):
title = "*** Starting %s ***" % self.__class__.__name__
underline = "-" * len(title)
description = "Tests reorganisation for PoS blocks."
self.log.info("\n\n%s\n%s\n%s\n", title, underline, description)
def disconnect_all(self):
self.log.info("Disconnecting nodes...")
for i in range(self.num_nodes):
for j in range(self.num_nodes):
if j != i:
disconnect_nodes(self.nodes[i], j)
self.log.info("Nodes disconnected")
def get_tot_balance(self, nodeid):
wi = self.nodes[nodeid].getwalletinfo()
return wi['balance'] + wi['immature_balance']
def check_money_supply(self, expected_piv):
g_info = [self.nodes[i].getinfo() for i in range(self.num_nodes)]
# verify that nodes have the expected __DSW__ supply
for node in g_info:
assert_equal(node['moneysupply'], DecimalAmt(expected_piv))
def run_test(self):
def findUtxoInList(txid, vout, utxo_list):
for x in utxo_list:
if x["txid"] == txid and x["vout"] == vout:
return True, x
return False, None
# Check __DSW__ supply at the beginning
# ------------------------------------------
# __DSW__ supply: block rewards minus burned fees for minting
expected_money_supply = 250.0 * 330 - 16 * 0.01
self.check_money_supply(expected_money_supply)
# Stake with node 0 and node 1 up to public spend activation (400)
# 70 blocks: 5 blocks each (x7)
self.log.info("Staking 70 blocks to reach public spends activation...")
set_node_times(self.nodes, self.mocktime)
for i in range(7):
for peer in range(2):
for nblock in range(5):
self.mocktime = self.generate_pos(peer, self.mocktime)
sync_blocks(self.nodes)
block_time_0 = block_time_1 = self.mocktime
self.log.info("Blocks staked.")
# Check balances
self.log.info("Checking balances...")
initial_balance = [self.get_tot_balance(i) for i in range(self.num_nodes)]
# --nodes 0, 1: 62 pow blocks + 55 pos blocks
assert_equal(initial_balance[0], DecimalAmt(250.0 * (62 + 55)))
assert_equal(initial_balance[1], DecimalAmt(250.0 * (62 + 55)))
self.log.info("Balances ok.")
# Disconnect nodes
minted_amount = mints[0]["denomination"] + mints[1]["denomination"]
self.disconnect_all()
# Stake one block with node-0 and save the stake input
self.log.info("Staking 1 block with node 0...")
initial_unspent_0 = self.nodes[0].listunspent()
self.nodes[0].generate(1)
block_time_0 += 60
set_node_times(self.nodes, block_time_0)
last_block = self.nodes[0].getblock(self.nodes[0].getbestblockhash())
assert(len(last_block["tx"]) > 1) # a PoS block has at least two txes
coinstake_txid = last_block["tx"][1]
coinstake_tx = self.nodes[0].getrawtransaction(coinstake_txid, True)
assert (coinstake_tx["vout"][0]["scriptPubKey"]["hex"] == "") # first output of coinstake is empty
stakeinput = coinstake_tx["vin"][0]
# The stake input was unspent 1 block ago, now it's not
res, utxo = findUtxoInList(stakeinput["txid"], stakeinput["vout"], initial_unspent_0)
assert (res and utxo["spendable"])
res, utxo = findUtxoInList(stakeinput["txid"], stakeinput["vout"], self.nodes[0].listunspent())
assert (not res or not utxo["spendable"])
self.log.info("Coinstake input %s...%s-%d is no longer spendable." % (
stakeinput["txid"][:9], stakeinput["txid"][-4:], stakeinput["vout"]))
# Stake 10 more blocks with node-0 and check balances
self.log.info("Staking 10 more blocks with node 0...")
for i in range(10):
block_time_0 = self.generate_pos(0, block_time_0)
expected_balance_0 = initial_balance[0] + DecimalAmt(11 * 250.0)
assert_equal(self.get_tot_balance(0), expected_balance_0)
self.log.info("Balance for node 0 checks out.")
# verify that the stakeinput can't be spent
stakeinput_tx_json = self.nodes[0].getrawtransaction(stakeinput["txid"], True)
stakeinput_amount = float(stakeinput_tx_json["vout"][int(stakeinput["vout"])]["value"])
rawtx_unsigned = self.nodes[0].createrawtransaction(
[{"txid": stakeinput["txid"], "vout": int(stakeinput["vout"])}],
{"xxncEuJK27ygNh7imNfaX8JV6ZQUnoBqzN": (stakeinput_amount-0.01)})
rawtx = self.nodes[0].signrawtransaction(rawtx_unsigned)
assert(rawtx["complete"])
try:
self.nodes[0].sendrawtransaction(rawtx["hex"])
except JSONRPCException as e:
# JSONRPCException was thrown as expected. Check the code and message values are correct.
if e.error["code"] not in [-26, -25]:
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
if ([x for x in ["bad-txns-inputs-spent", "Missing inputs"] if x in e.error['message']] == []):
raise e
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
self.log.info("GOOD: v2 spend was not possible.")
# Spend tx_B0 and tx_B1 on the other chain
self.nodes[1].sendrawtransaction(tx_B0)
self.nodes[1].sendrawtransaction(tx_B1)
# Stake 12 blocks with node-1
set_node_times(self.nodes, block_time_1)
self.log.info("Staking 12 blocks with node 1...")
for i in range(12):
block_time_1 = self.generate_pos(1, block_time_1)
expected_balance_1 = initial_balance[1] + DecimalAmt(12 * 250.0)
assert_equal(self.get_tot_balance(1), expected_balance_1)
self.log.info("Balance for node 1 checks out.")
# re-connect and sync nodes and check that node-0 and node-2 get on the other chain
new_best_hash = self.nodes[1].getbestblockhash()
self.log.info("Connecting and syncing nodes...")
set_node_times(self.nodes, block_time_1)
connect_nodes_clique(self.nodes)
sync_blocks(self.nodes)
for i in [0, 2]:
assert_equal(self.nodes[i].getbestblockhash(), new_best_hash)
# check balance of node-0
assert_equal(self.get_tot_balance(0), initial_balance[0])
self.log.info("Balance for node 0 checks out.")
# check that NOW the original stakeinput is present and spendable
res, utxo = findUtxoInList(stakeinput["txid"], stakeinput["vout"], self.nodes[0].listunspent())
assert (res and utxo["spendable"])
self.log.info("Coinstake input %s...%s-%d is spendable again." % (
stakeinput["txid"][:9], stakeinput["txid"][-4:], stakeinput["vout"]))
self.nodes[0].sendrawtransaction(rawtx["hex"])
self.nodes[1].generate(1)
sync_blocks(self.nodes)
res, utxo = findUtxoInList(stakeinput["txid"], stakeinput["vout"], self.nodes[0].listunspent())
assert (not res or not utxo["spendable"])
# Verify that __DSW__ supplies were properly updated after the spends and reorgs
self.log.info("Check __DSW__ supply...")
expected_money_supply += 250.0 * (self.nodes[1].getblockcount() - 330)
self.check_money_supply(expected_money_supply)
self.log.info("Supply checks out.")
if __name__ == '__main__':
ReorgStakeTest().main()