Skip to content

Commit bebd6ba

Browse files
committed
Regtest for update backwards compatibility.
This adds a new regtest, custom_address_update.py. The test runs two masternodes, where we explicitly set the protocol version of one of the nodes to the "old" version not supporting custom reward scripts for masternodes. With this test, we ensure that all the masternode networking works fine between "old" and "new" nodes before the update is activated, at least as closely as we can without running a real old node.
1 parent 8b1dfa5 commit bebd6ba

File tree

3 files changed

+149
-1
lines changed

3 files changed

+149
-1
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2020-2021 The DIVI developers
3+
# Distributed under the MIT/X11 software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
# Tests that nodes with support for custom reward addresses are compatible
7+
# to nodes without it before the change actually activates. For this,
8+
# we run two masternodes (one with and one without support) and check
9+
# that they can both see each other's masternode.
10+
#
11+
# We use five nodes:
12+
# - node 0 is a masternode with default protocol (i.e. "new")
13+
# - node 1 is a masternode with previous protocol (i.e. "old")
14+
# - nodes 2-4 are just used to get above the "three full nodes" threshold
15+
16+
from test_framework import BitcoinTestFramework
17+
from util import *
18+
from masternode import *
19+
20+
import collections
21+
import copy
22+
import time
23+
24+
OLD_PROTOCOL = 70915
25+
NEW_PROTOCOL = 71000
26+
27+
28+
class CustomAddressUpdateTest (MnTestFramework):
29+
30+
def __init__ (self):
31+
super ().__init__ ()
32+
self.base_args = ["-debug", "-nolistenonion", "-activeversion=%d" % OLD_PROTOCOL]
33+
self.number_of_nodes = 5
34+
35+
def args_for (self, n):
36+
res = copy.deepcopy (self.base_args)
37+
if n == 1:
38+
res.extend (["-protocolversion=%d" % OLD_PROTOCOL])
39+
return res
40+
41+
def setup_network (self, config_line=None, extra_args=[]):
42+
self.nodes = [
43+
start_node (i, self.options.tmpdir, extra_args=self.args_for (i))
44+
for i in range (self.number_of_nodes)
45+
]
46+
self.setup = [None] * self.number_of_nodes
47+
48+
# We want to work with mock times that are beyond the genesis
49+
# block timestamp but before current time (so that nodes being
50+
# started up and before they get on mocktime aren't rejecting
51+
# the on-disk blockchain).
52+
self.time = 1580000000
53+
assert self.time < time.time ()
54+
set_node_times (self.nodes, self.time)
55+
56+
connect_nodes (self.nodes[0], 2)
57+
connect_nodes (self.nodes[1], 2)
58+
connect_nodes (self.nodes[2], 3)
59+
connect_nodes (self.nodes[2], 4)
60+
connect_nodes (self.nodes[3], 4)
61+
62+
self.is_network_split = False
63+
64+
def advance_time (self, dt=1):
65+
"""Advances mocktime by the given number of seconds."""
66+
67+
self.time += dt
68+
set_node_times (self.nodes, self.time)
69+
70+
def mine_blocks (self, n):
71+
"""Mines blocks with node 2."""
72+
73+
self.nodes[2].setgenerate(True, n)
74+
sync_blocks (self.nodes)
75+
76+
def run_test (self):
77+
assert_equal (self.nodes[0].getnetworkinfo ()["protocolversion"], NEW_PROTOCOL)
78+
assert_equal (self.nodes[1].getnetworkinfo ()["protocolversion"], OLD_PROTOCOL)
79+
80+
self.fund_masternodes ()
81+
self.start_masternodes ()
82+
83+
def fund_masternodes (self):
84+
print ("Funding masternodes...")
85+
86+
# The collateral needs 15 confirmations, and the masternode broadcast
87+
# signature must be later than that block's timestamp. Thus we start
88+
# with a very early timestamp.
89+
genesis = self.nodes[0].getblockhash (0)
90+
genesisTime = self.nodes[0].getblockheader (genesis)["time"]
91+
assert genesisTime < self.time
92+
set_node_times (self.nodes, genesisTime)
93+
94+
self.nodes[0].setgenerate (True, 1)
95+
sync_blocks (self.nodes)
96+
self.nodes[1].setgenerate (True, 1)
97+
sync_blocks (self.nodes)
98+
self.mine_blocks (25)
99+
assert_equal (self.nodes[0].getbalance (), 1250)
100+
assert_equal (self.nodes[1].getbalance (), 1250)
101+
102+
self.setup_masternode (0, 0, "mn1", "copper")
103+
self.setup_masternode (1, 1, "mn2", "copper")
104+
self.mine_blocks (15)
105+
set_node_times (self.nodes, self.time)
106+
self.mine_blocks (1)
107+
108+
def start_masternodes (self):
109+
print ("Starting masternodes...")
110+
111+
self.stop_masternode_daemons ()
112+
self.start_masternode_daemons ()
113+
self.connect_masternodes_to_peers ([2, 3, 4], updateMockTime=True)
114+
115+
# Start the masternodes after the nodes are back up and connected
116+
# (so they will receive each other's broadcast).
117+
assert_equal (self.broadcast_start ("mn1", True)["status"], "success")
118+
assert_equal (self.broadcast_start ("mn2", True)["status"], "success")
119+
120+
# Check status of the masternodes themselves.
121+
self.wait_for_masternodes_to_be_locally_active (updateMockTime=True)
122+
123+
# Both masternodes should see each other, independent of the
124+
# protocol version used.
125+
def sortByTxhash (entry):
126+
return entry["txhash"]
127+
lists = [
128+
sorted (self.wait_for_mn_list_to_sync (self.nodes[i], 2), key=sortByTxhash)
129+
for i in [0, 1]
130+
]
131+
assert_equal (lists[0], lists[1])
132+
133+
lst = lists[0]
134+
assert_equal (len (lst), 2)
135+
for val in lst:
136+
assert_equal (val["tier"], "COPPER")
137+
assert_equal (val["status"], "ENABLED")
138+
139+
140+
if __name__ == '__main__':
141+
CustomAddressUpdateTest ().main ()

divi/qa/rpc-tests/masternode.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from test_framework import BitcoinTestFramework
88
from util import *
9+
import copy
910
import time
1011

1112

@@ -164,6 +165,11 @@ def wait_for_mnsync_on_nodes (self, updateMockTime = False):
164165
else:
165166
break
166167

168+
def args_for(self, n):
169+
"""Returns the arguments to use for node n. Defaults to the base args
170+
but can be overridden as needed."""
171+
return copy.deepcopy(self.base_args)
172+
167173
def stop_masternode_daemons(self):
168174
def stop_masternode(nodeIndex):
169175
if self.nodes[nodeIndex] is not None:
@@ -178,7 +184,7 @@ def start_masternode(nodeIndex):
178184
if self.nodes[nodeIndex] is not None:
179185
assert_equal("WARNING -- overwriting connection","")
180186
else:
181-
args = self.base_args[:]
187+
args = self.args_for(nodeIndex)
182188
conf = self.setup[nodeIndex].cfg
183189
args.append ("-masternode=%s" % conf.alias)
184190
self.nodes[nodeIndex] = start_node (nodeIndex, self.options.tmpdir, extra_args=args, mn_config_lines=[conf.line])

divi/qa/rpc-tests/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
'StakingVaultDeactivation.py',
8484
'StakingVaultSpam.py',
8585
'StakingVaultRedundancy.py',
86+
'custom_address_update.py',
8687
'forknotify.py',
8788
'getchaintips.py',
8889
'httpbasics.py',

0 commit comments

Comments
 (0)