forked from dtr-org/unit-e
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfinalization_admin_full_cycle.py
More file actions
executable file
·200 lines (155 loc) · 7 KB
/
Copy pathfinalization_admin_full_cycle.py
File metadata and controls
executable file
·200 lines (155 loc) · 7 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
194
195
196
197
198
199
200
#!/usr/bin/env python3
# Copyright (c) 2018-2019 The Unit-e developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.test_framework import UnitETestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
connect_nodes_bi,
json,
)
from test_framework.admin import Admin
MIN_DEPOSIT_SIZE = 1000
WITHDRAWAL_EPOCH_DELAY = 5
DYNASTY_LOGOUT_DELAY = 2
EPOCH_LENGTH = 10
def block_contains_txs_from(block, address):
for tx in block["tx"]:
for out in tx["vout"]:
script_pub_key = out["scriptPubKey"]
addresses = script_pub_key.get("addresses", None)
if addresses is None:
continue
if address in addresses:
return True
return False
def prev_n_blocks_have_txs_from(node, validator_address, n):
height = node.getblockcount()
for i in range(0, n):
block_hash = node.getblockhash(height - i)
block = node.getblock(block_hash, 2)
if block_contains_txs_from(block, validator_address):
return True
return False
# Checks whole administration workflow, from whitelisting/blacklisting to life
# after END_PERMISSIONING
class AdminFullCycle(UnitETestFramework):
class ValidatorWrapper:
def __init__(self, framework, node, required_amounts):
super().__init__()
self.node = node
self.address = node.getnewaddress("", "legacy")
self.pubkey = node.validateaddress(self.address)["pubkey"]
self.framework = framework
# Workaround for a bug: When validator tx is rejected - funds it
# used are locked for some time. Split the only 10k UTXO into
# several by sending to self
if len(required_amounts) == 0:
return
outputs = dict()
for amount in required_amounts:
outputs[node.getnewaddress()] = amount
tx = node.sendmany("", outputs)
framework.wait_for_transaction(tx, timeout=20)
def deposit_reject(self):
balance = self.node.getbalance()
assert_raises_rpc_error(None, "Cannot create deposit",
self.node.deposit, self.address,
MIN_DEPOSIT_SIZE)
# Make sure deposit doesn't disappear when rejected.
assert_equal(self.node.getbalance(), balance)
def deposit_ok(self, deposit_amount=MIN_DEPOSIT_SIZE):
tx = self.node.deposit(self.address, deposit_amount)
self.framework.wait_for_transaction(tx, timeout=20)
def logout_ok(self):
tx = self.node.logout()
self.framework.wait_for_transaction(tx, timeout=20)
def withdraw_ok(self):
tx = self.node.withdraw(self.address)
self.framework.wait_for_transaction(tx, timeout=20)
def set_test_params(self):
self.num_nodes = 4
params_data = {
'epochLength': EPOCH_LENGTH,
'minDepositSize': MIN_DEPOSIT_SIZE,
'dynastyLogoutDelay': DYNASTY_LOGOUT_DELAY,
'withdrawalEpochDelay': WITHDRAWAL_EPOCH_DELAY
}
json_params = json.dumps(params_data)
proposer_settings = ['-permissioning=1', '-debug=all',
'-whitelist=127.0.0.1',
'-esperanzaconfig=' + json_params]
validator_settings = ['-permissioning=1', '-validating=1', '-debug=all',
'-whitelist=127.0.0.1',
'-esperanzaconfig=' + json_params]
self.extra_args = [
proposer_settings,
validator_settings,
validator_settings,
validator_settings
]
for args in self.extra_args:
args.extend(['-deprecatedrpc=validateaddress'])
self.setup_clean_chain = True
def setup_network(self):
super().setup_network()
# Fully connected graph works faster
for i in range(self.num_nodes):
for j in range(i + 1, self.num_nodes):
connect_nodes_bi(self.nodes, i, j)
def run_test(self):
self.setup_stake_coins(*self.nodes)
# Exit IBD
self.generate_sync(self.nodes[0])
# introduce actors
proposer = self.nodes[0]
admin = Admin.authorize(self, proposer)
validator1 = AdminFullCycle.ValidatorWrapper(self, self.nodes[1],
[MIN_DEPOSIT_SIZE + 1] * 3)
validator2 = AdminFullCycle.ValidatorWrapper(self, self.nodes[2],
[MIN_DEPOSIT_SIZE + 1] * 3)
validator3 = AdminFullCycle.ValidatorWrapper(self, self.nodes[3], [])
# No validators are whitelisted any deposits should fail
validator1.deposit_reject()
validator2.deposit_reject()
# Whitelist v1
admin.whitelist([validator1.pubkey])
validator1.deposit_ok()
validator2.deposit_reject() # ensure only v1 is whitelisted
# Whitelist v2
admin.whitelist([validator2.pubkey])
validator2.deposit_ok()
# Whitelist v3
admin.whitelist([validator3.pubkey])
validator3.deposit_ok(MIN_DEPOSIT_SIZE * 2)
# Finalize deposits
self.generate_sync(proposer, EPOCH_LENGTH * 4)
# Generate some blocks and check that validators are voting
n_blocks = 2 * EPOCH_LENGTH
self.generate_sync(proposer, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator1.address, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator2.address, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator3.address, n_blocks)
# Blacklist v1
admin.blacklist([validator1.pubkey])
self.generate_sync(proposer, n_blocks)
assert not prev_n_blocks_have_txs_from(proposer, validator1.address, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator2.address, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator3.address, n_blocks)
# v1 should be able to logout even if blacklisted
validator1.logout_ok()
self.generate_sync(proposer, DYNASTY_LOGOUT_DELAY * EPOCH_LENGTH)
self.generate_sync(proposer, WITHDRAWAL_EPOCH_DELAY * EPOCH_LENGTH)
self.generate_sync(proposer, EPOCH_LENGTH)
validator1.withdraw_ok()
# End permissioning, v1 should be able to deposit again, v2 and v3
# should continue voting
admin.end_permissioning()
validator1.deposit_ok()
self.generate_sync(proposer, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator1.address, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator2.address, n_blocks)
assert prev_n_blocks_have_txs_from(proposer, validator3.address, n_blocks)
if __name__ == '__main__':
AdminFullCycle().main()