]>
Commit | Line | Data |
---|---|---|
68e174e2 LR |
1 | #!/usr/bin/env python |
2 | # Copyright (c) 2019 The Zcash developers | |
8b78a819 T |
3 | # Distributed under the MIT software license, see the accompanying |
4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
5 | ||
88d014d0 | 6 | # TODO: change addres formats to include in Verus tests |
7 | ||
68e174e2 LR |
8 | # |
9 | # Test addressindex generation and fetching for insightexplorer | |
10 | # | |
11 | # RPCs tested here: | |
8b78a819 | 12 | # |
68e174e2 LR |
13 | # getaddresstxids |
14 | # getaddressbalance | |
15 | # getaddressdeltas | |
16 | # getaddressutxos | |
17 | # getaddressmempool | |
8b78a819 T |
18 | # |
19 | ||
68e174e2 LR |
20 | import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." |
21 | ||
8b78a819 | 22 | from test_framework.test_framework import BitcoinTestFramework |
68e174e2 LR |
23 | |
24 | ||
25 | from test_framework.util import ( | |
26 | assert_equal, | |
27 | initialize_chain_clean, | |
28 | start_nodes, | |
29 | stop_nodes, | |
30 | connect_nodes, | |
31 | ) | |
32 | ||
33 | from test_framework.util import wait_bitcoinds | |
34 | ||
35 | from test_framework.script import ( | |
36 | CScript, | |
37 | OP_HASH160, | |
38 | OP_EQUAL, | |
39 | OP_DUP, | |
40 | OP_DROP, | |
41 | ) | |
42 | ||
43 | from test_framework.mininode import COIN, CTransaction | |
44 | from test_framework.mininode import CTxIn, CTxOut, COutPoint | |
45 | ||
46 | from binascii import hexlify | |
47 | ||
8b78a819 T |
48 | |
49 | class AddressIndexTest(BitcoinTestFramework): | |
50 | ||
51 | def setup_chain(self): | |
52 | print("Initializing test directory "+self.options.tmpdir) | |
68e174e2 | 53 | initialize_chain_clean(self.options.tmpdir, 3) |
8b78a819 T |
54 | |
55 | def setup_network(self): | |
68e174e2 LR |
56 | # -insightexplorer causes addressindex to be enabled (fAddressIndex = true) |
57 | args = ('-debug', '-txindex', '-experimentalfeatures', '-insightexplorer') | |
58 | self.nodes = start_nodes(3, self.options.tmpdir, [args] * 3) | |
8b78a819 T |
59 | connect_nodes(self.nodes[0], 1) |
60 | connect_nodes(self.nodes[0], 2) | |
8b78a819 T |
61 | |
62 | self.is_network_split = False | |
63 | self.sync_all() | |
64 | ||
65 | def run_test(self): | |
8b78a819 | 66 | |
68e174e2 LR |
67 | # helper functions |
68 | def getaddresstxids(node_index, addresses, start, end): | |
69 | return self.nodes[node_index].getaddresstxids({ | |
70 | 'addresses': addresses, | |
71 | 'start': start, | |
72 | 'end': end | |
73 | }) | |
74 | ||
75 | def getaddressdeltas(node_index, addresses, start, end, chainInfo=None): | |
76 | params = { | |
77 | 'addresses': addresses, | |
78 | 'start': start, | |
79 | 'end': end, | |
80 | } | |
81 | if chainInfo is not None: | |
82 | params.update({'chainInfo': chainInfo}) | |
83 | return self.nodes[node_index].getaddressdeltas(params) | |
84 | ||
85 | # default received value is the balance value | |
86 | def check_balance(node_index, address, expected_balance, expected_received=None): | |
87 | if isinstance(address, list): | |
88 | bal = self.nodes[node_index].getaddressbalance({'addresses': address}) | |
89 | else: | |
90 | bal = self.nodes[node_index].getaddressbalance(address) | |
91 | assert_equal(bal['balance'], expected_balance) | |
92 | if expected_received is None: | |
93 | expected_received = expected_balance | |
94 | assert_equal(bal['received'], expected_received) | |
95 | ||
96 | # begin test | |
8b78a819 | 97 | |
68e174e2 | 98 | self.nodes[0].generate(105) |
8b78a819 | 99 | self.sync_all() |
68e174e2 LR |
100 | assert_equal(self.nodes[0].getbalance(), 5 * 10) |
101 | assert_equal(self.nodes[1].getblockcount(), 105) | |
102 | assert_equal(self.nodes[1].getbalance(), 0) | |
8b78a819 | 103 | |
68e174e2 LR |
104 | # only the oldest 5; subsequent are not yet mature |
105 | unspent_txids = [ u['txid'] for u in self.nodes[0].listunspent() ] | |
106 | ||
107 | # Currently our only unspents are coinbase transactions, choose any one | |
108 | tx = self.nodes[0].getrawtransaction(unspent_txids[0], 1) | |
109 | ||
110 | # It just so happens that the first output is the mining reward, | |
111 | # which has type pay-to-public-key-hash, and the second output | |
112 | # is the founders' reward, which has type pay-to-script-hash. | |
113 | addr_p2pkh = tx['vout'][0]['scriptPubKey']['addresses'][0] | |
114 | addr_p2sh = tx['vout'][1]['scriptPubKey']['addresses'][0] | |
115 | ||
116 | # Check that balances from mining are correct (105 blocks mined); in | |
117 | # regtest, all mining rewards from a single call to generate() are sent | |
118 | # to the same pair of addresses. | |
119 | check_balance(1, addr_p2pkh, 105 * 10 * COIN) | |
120 | check_balance(1, addr_p2sh, 105 * 2.5 * COIN) | |
121 | ||
122 | # Multiple address arguments, results are the sum | |
123 | check_balance(1, [addr_p2sh, addr_p2pkh], 105 * 12.5 * COIN) | |
124 | ||
125 | assert_equal(len(self.nodes[1].getaddresstxids(addr_p2pkh)), 105) | |
126 | assert_equal(len(self.nodes[1].getaddresstxids(addr_p2sh)), 105) | |
127 | ||
128 | # only the oldest 5 transactions are in the unspent list, | |
129 | # dup addresses are ignored | |
130 | height_txids = getaddresstxids(1, [addr_p2pkh, addr_p2pkh], 1, 5) | |
131 | assert_equal(sorted(height_txids), sorted(unspent_txids)) | |
132 | ||
133 | height_txids = getaddresstxids(1, [addr_p2sh], 1, 5) | |
134 | assert_equal(sorted(height_txids), sorted(unspent_txids)) | |
135 | ||
136 | # each txid should appear only once | |
137 | height_txids = getaddresstxids(1, [addr_p2pkh, addr_p2sh], 1, 5) | |
138 | assert_equal(sorted(height_txids), sorted(unspent_txids)) | |
139 | ||
140 | # do some transfers, make sure balances are good | |
141 | txids_a1 = [] | |
142 | addr1 = self.nodes[1].getnewaddress() | |
143 | expected = 0 | |
144 | expected_deltas = [] # for checking getaddressdeltas (below) | |
145 | for i in range(5): | |
146 | # first transaction happens at height 105, mined in block 106 | |
147 | txid = self.nodes[0].sendtoaddress(addr1, i + 1) | |
148 | txids_a1.append(txid) | |
149 | self.nodes[0].generate(1) | |
150 | self.sync_all() | |
151 | expected += i + 1 | |
152 | expected_deltas.append({ | |
153 | 'height': 106 + i, | |
154 | 'satoshis': (i + 1) * COIN, | |
155 | 'txid': txid, | |
156 | }) | |
157 | check_balance(1, addr1, expected * COIN) | |
158 | assert_equal(sorted(self.nodes[0].getaddresstxids(addr1)), sorted(txids_a1)) | |
159 | assert_equal(sorted(self.nodes[1].getaddresstxids(addr1)), sorted(txids_a1)) | |
160 | ||
161 | # Restart all nodes to ensure indices are saved to disk and recovered | |
162 | stop_nodes(self.nodes) | |
163 | wait_bitcoinds() | |
164 | self.setup_network() | |
165 | ||
166 | bal = self.nodes[1].getaddressbalance(addr1) | |
167 | assert_equal(bal['balance'], expected * COIN) | |
168 | assert_equal(bal['received'], expected * COIN) | |
169 | assert_equal(sorted(self.nodes[0].getaddresstxids(addr1)), sorted(txids_a1)) | |
170 | assert_equal(sorted(self.nodes[1].getaddresstxids(addr1)), sorted(txids_a1)) | |
171 | ||
172 | # Send 3 from addr1, but -- subtlety alert! -- addr1 at this | |
173 | # time has 4 UTXOs, with values 1, 2, 3, 4. Sending value 3 requires | |
174 | # using up the value 4 UTXO, because of the tx fee | |
175 | # (the 3 UTXO isn't quite large enough). | |
176 | # | |
177 | # The txid from sending *from* addr1 is also added to the list of | |
178 | # txids associated with that address (test will verify below). | |
179 | ||
180 | addr2 = self.nodes[2].getnewaddress() | |
181 | txid = self.nodes[1].sendtoaddress(addr2, 3) | |
8b78a819 T |
182 | self.sync_all() |
183 | ||
68e174e2 LR |
184 | # the one tx in the mempool refers to addresses addr1 and addr2, |
185 | # check that duplicate addresses are processed correctly | |
186 | mempool = self.nodes[0].getaddressmempool({'addresses': [addr2, addr1, addr2]}) | |
187 | assert_equal(len(mempool), 3) | |
8b78a819 | 188 | |
68e174e2 LR |
189 | # addr2 (first arg) |
190 | assert_equal(mempool[0]['address'], addr2) | |
191 | assert_equal(mempool[0]['satoshis'], 3 * COIN) | |
192 | assert_equal(mempool[0]['txid'], txid) | |
193 | ||
194 | # addr1 (second arg) | |
195 | assert_equal(mempool[1]['address'], addr1) | |
196 | assert_equal(mempool[1]['satoshis'], (-4) * COIN) | |
197 | assert_equal(mempool[1]['txid'], txid) | |
198 | ||
199 | # addr2 (third arg) | |
200 | assert_equal(mempool[2]['address'], addr2) | |
201 | assert_equal(mempool[2]['satoshis'], 3 * COIN) | |
202 | assert_equal(mempool[2]['txid'], txid) | |
203 | ||
204 | # a single address can be specified as a string (not json object) | |
205 | assert_equal([mempool[1]], self.nodes[0].getaddressmempool(addr1)) | |
206 | ||
86b23f37 LR |
207 | tx = self.nodes[0].getrawtransaction(txid, 1) |
208 | assert_equal(tx['vin'][0]['address'], addr1) | |
209 | assert_equal(tx['vin'][0]['value'], 4) | |
210 | assert_equal(tx['vin'][0]['valueSat'], 4 * COIN) | |
211 | ||
68e174e2 LR |
212 | txids_a1.append(txid) |
213 | expected_deltas.append({ | |
214 | 'height': 111, | |
215 | 'satoshis': (-4) * COIN, | |
216 | 'txid': txid, | |
217 | }) | |
218 | self.sync_all() # ensure transaction is included in the next block | |
8b78a819 T |
219 | self.nodes[0].generate(1) |
220 | self.sync_all() | |
221 | ||
68e174e2 LR |
222 | # the send to addr2 tx is now in a mined block, no longer in the mempool |
223 | mempool = self.nodes[0].getaddressmempool({'addresses': [addr2, addr1]}) | |
224 | assert_equal(len(mempool), 0) | |
8b78a819 | 225 | |
68e174e2 LR |
226 | # Test DisconnectBlock() by invalidating the most recent mined block |
227 | tip = self.nodes[1].getchaintips()[0] | |
228 | for i in range(3): | |
229 | node = self.nodes[i] | |
230 | # the value 4 UTXO is no longer in our balance | |
231 | check_balance(i, addr1, (expected - 4) * COIN, expected * COIN) | |
232 | check_balance(i, addr2, 3 * COIN) | |
8b78a819 | 233 | |
68e174e2 LR |
234 | assert_equal(node.getblockcount(), 111) |
235 | node.invalidateblock(tip['hash']) | |
236 | assert_equal(node.getblockcount(), 110) | |
8b78a819 | 237 | |
68e174e2 LR |
238 | mempool = node.getaddressmempool({'addresses': [addr2, addr1]}) |
239 | assert_equal(len(mempool), 2) | |
8b78a819 | 240 | |
68e174e2 LR |
241 | check_balance(i, addr1, expected * COIN) |
242 | check_balance(i, addr2, 0) | |
8b78a819 | 243 | |
68e174e2 | 244 | # now re-mine the addr1 to addr2 send |
8b78a819 T |
245 | self.nodes[0].generate(1) |
246 | self.sync_all() | |
68e174e2 LR |
247 | for node in self.nodes: |
248 | assert_equal(node.getblockcount(), 111) | |
249 | ||
250 | mempool = self.nodes[0].getaddressmempool({'addresses': [addr2, addr1]}) | |
251 | assert_equal(len(mempool), 0) | |
252 | ||
253 | # the value 4 UTXO is no longer in our balance | |
254 | check_balance(2, addr1, (expected - 4) * COIN, expected * COIN) | |
255 | ||
256 | # Ensure the change from that transaction appears | |
257 | tx = self.nodes[0].getrawtransaction(txid, 1) | |
258 | change_vout = filter(lambda v: v['valueZat'] != 3 * COIN, tx['vout']) | |
259 | change = change_vout[0]['scriptPubKey']['addresses'][0] | |
260 | bal = self.nodes[2].getaddressbalance(change) | |
261 | assert(bal['received'] > 0) | |
262 | # the inequality is due to randomness in the tx fee | |
263 | assert(bal['received'] < (4 - 3) * COIN) | |
264 | assert_equal(bal['received'], bal['balance']) | |
265 | assert_equal(self.nodes[2].getaddresstxids(change), [txid]) | |
266 | ||
267 | # Further checks that limiting by height works | |
268 | ||
269 | # various ranges | |
270 | for i in range(5): | |
271 | height_txids = getaddresstxids(1, [addr1], 106, 106 + i) | |
272 | assert_equal(height_txids, txids_a1[0:i+1]) | |
273 | ||
274 | height_txids = getaddresstxids(1, [addr1], 1, 108) | |
275 | assert_equal(height_txids, txids_a1[0:3]) | |
276 | ||
277 | # Further check specifying multiple addresses | |
278 | txids_all = list(txids_a1) | |
279 | txids_all += self.nodes[1].getaddresstxids(addr_p2pkh) | |
280 | txids_all += self.nodes[1].getaddresstxids(addr_p2sh) | |
281 | multitxids = self.nodes[1].getaddresstxids({ | |
282 | 'addresses': [addr1, addr_p2sh, addr_p2pkh] | |
283 | }) | |
284 | # No dups in return list from getaddresstxids | |
285 | assert_equal(len(multitxids), len(set(multitxids))) | |
286 | ||
287 | # set(txids_all) removes its (expected) duplicates | |
288 | assert_equal(set(multitxids), set(txids_all)) | |
289 | ||
290 | deltas = self.nodes[1].getaddressdeltas({'addresses': [addr1]}) | |
291 | assert_equal(len(deltas), len(expected_deltas)) | |
292 | for i in range(len(deltas)): | |
293 | assert_equal(deltas[i]['address'], addr1) | |
294 | assert_equal(deltas[i]['height'], expected_deltas[i]['height']) | |
295 | assert_equal(deltas[i]['satoshis'], expected_deltas[i]['satoshis']) | |
296 | assert_equal(deltas[i]['txid'], expected_deltas[i]['txid']) | |
297 | ||
298 | # 106-111 is the full range (also the default) | |
299 | deltas_limited = getaddressdeltas(1, [addr1], 106, 111) | |
300 | assert_equal(deltas_limited, deltas) | |
301 | ||
302 | # only the first element missing | |
303 | deltas_limited = getaddressdeltas(1, [addr1], 107, 111) | |
304 | assert_equal(deltas_limited, deltas[1:]) | |
305 | ||
306 | deltas_limited = getaddressdeltas(1, [addr1], 109, 109) | |
307 | assert_equal(deltas_limited, deltas[3:4]) | |
308 | ||
309 | # the full range (also the default) | |
310 | deltas_info = getaddressdeltas(1, [addr1], 106, 111, chainInfo=True) | |
311 | assert_equal(deltas_info['deltas'], deltas) | |
312 | ||
313 | # check the additional items returned by chainInfo | |
314 | assert_equal(deltas_info['start']['height'], 106) | |
315 | block_hash = self.nodes[1].getblockhash(106) | |
316 | assert_equal(deltas_info['start']['hash'], block_hash) | |
317 | ||
318 | assert_equal(deltas_info['end']['height'], 111) | |
319 | block_hash = self.nodes[1].getblockhash(111) | |
320 | assert_equal(deltas_info['end']['hash'], block_hash) | |
321 | ||
322 | # Test getaddressutxos by comparing results with deltas | |
323 | utxos = self.nodes[1].getaddressutxos(addr1) | |
324 | ||
325 | # The value 4 note was spent, so won't show up in the utxo list, | |
326 | # so for comparison, remove the 4 (and -4 for output) from the | |
327 | # deltas list | |
328 | deltas = self.nodes[1].getaddressdeltas({'addresses': [addr1]}) | |
329 | deltas = filter(lambda d: abs(d['satoshis']) != 4 * COIN, deltas) | |
330 | assert_equal(len(utxos), len(deltas)) | |
331 | for i in range(len(utxos)): | |
332 | assert_equal(utxos[i]['address'], addr1) | |
333 | assert_equal(utxos[i]['height'], deltas[i]['height']) | |
334 | assert_equal(utxos[i]['satoshis'], deltas[i]['satoshis']) | |
335 | assert_equal(utxos[i]['txid'], deltas[i]['txid']) | |
336 | ||
337 | # Check that outputs with the same address in the same tx return one txid | |
338 | # (can't use createrawtransaction() as it combines duplicate addresses) | |
339 | addr = "t2LMJ6Arw9UWBMWvfUr2QLHM4Xd9w53FftS" | |
340 | addressHash = "97643ce74b188f4fb6bbbb285e067a969041caf2".decode('hex') | |
341 | scriptPubKey = CScript([OP_HASH160, addressHash, OP_EQUAL]) | |
342 | # Add an unrecognized script type to vout[], a legal script that pays, | |
343 | # but won't modify the addressindex (since the address can't be extracted). | |
344 | # (This extra output has no effect on the rest of the test.) | |
345 | scriptUnknown = CScript([OP_HASH160, OP_DUP, OP_DROP, addressHash, OP_EQUAL]) | |
346 | unspent = filter(lambda u: u['amount'] >= 4, self.nodes[0].listunspent()) | |
8b78a819 | 347 | tx = CTransaction() |
68e174e2 LR |
348 | tx.vin = [CTxIn(COutPoint(int(unspent[0]['txid'], 16), unspent[0]['vout']))] |
349 | tx.vout = [ | |
350 | CTxOut(1 * COIN, scriptPubKey), | |
351 | CTxOut(2 * COIN, scriptPubKey), | |
352 | CTxOut(7 * COIN, scriptUnknown), | |
8b78a819 | 353 | ] |
68e174e2 LR |
354 | tx = self.nodes[0].signrawtransaction(hexlify(tx.serialize()).decode('utf-8')) |
355 | txid = self.nodes[0].sendrawtransaction(tx['hex'], True) | |
356 | self.nodes[0].generate(1) | |
8b78a819 | 357 | self.sync_all() |
8b78a819 | 358 | |
68e174e2 LR |
359 | assert_equal(self.nodes[1].getaddresstxids(addr), [txid]) |
360 | check_balance(2, addr, 3 * COIN) | |
8b78a819 T |
361 | |
362 | ||
363 | if __name__ == '__main__': | |
68e174e2 | 364 | AddressIndexTest().main() |