]>
Commit | Line | Data |
---|---|---|
2d931e90 S |
1 | #!/usr/bin/env python2 |
2 | # Copyright (c) 2016 The Zcash developers | |
3 | # Distributed under the MIT software license, see the accompanying | |
4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
5 | ||
6 | ||
7 | from test_framework.test_framework import BitcoinTestFramework | |
aff0bf7f DH |
8 | from test_framework.util import assert_equal, initialize_chain_clean, \ |
9 | start_nodes, connect_nodes_bi | |
2d931e90 | 10 | |
aff0bf7f DH |
11 | import time |
12 | from decimal import Decimal | |
2d931e90 S |
13 | |
14 | class WalletTreeStateTest (BitcoinTestFramework): | |
15 | ||
16 | def setup_chain(self): | |
17 | print("Initializing test directory "+self.options.tmpdir) | |
18 | initialize_chain_clean(self.options.tmpdir, 4) | |
19 | ||
20 | # Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true. | |
21 | def setup_network(self, split=False): | |
22 | self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase','-debug=zrpc']] * 3 ) | |
23 | connect_nodes_bi(self.nodes,0,1) | |
24 | connect_nodes_bi(self.nodes,1,2) | |
25 | connect_nodes_bi(self.nodes,0,2) | |
26 | self.is_network_split=False | |
27 | self.sync_all() | |
28 | ||
29 | def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): | |
30 | print('waiting for async operation {}'.format(myopid)) | |
31 | opids = [] | |
32 | opids.append(myopid) | |
33 | timeout = 300 | |
34 | status = None | |
35 | errormsg = None | |
36 | for x in xrange(1, timeout): | |
37 | results = self.nodes[0].z_getoperationresult(opids) | |
38 | if len(results)==0: | |
aff0bf7f | 39 | time.sleep(1) |
2d931e90 S |
40 | else: |
41 | status = results[0]["status"] | |
42 | if status == "failed": | |
43 | errormsg = results[0]['error']['message'] | |
44 | break | |
45 | print('...returned status: {}'.format(status)) | |
46 | print('...error msg: {}'.format(errormsg)) | |
47 | assert_equal(in_status, status) | |
48 | if errormsg is not None: | |
49 | assert(in_errormsg is not None) | |
50 | assert_equal(in_errormsg in errormsg, True) | |
51 | print('...returned error: {}'.format(errormsg)) | |
52 | ||
53 | def run_test (self): | |
54 | print "Mining blocks..." | |
55 | ||
56 | self.nodes[0].generate(100) | |
57 | self.sync_all() | |
58 | self.nodes[1].generate(101) | |
59 | self.sync_all() | |
60 | ||
61 | mytaddr = self.nodes[0].getnewaddress() # where coins were mined | |
62 | myzaddr = self.nodes[0].z_getnewaddress() | |
63 | ||
64 | # Spend coinbase utxos to create three notes of 9.99990000 each | |
65 | recipients = [] | |
66 | recipients.append({"address":myzaddr, "amount":Decimal('10.0') - Decimal('0.0001')}) | |
67 | myopid = self.nodes[0].z_sendmany(mytaddr, recipients) | |
68 | self.wait_and_assert_operationid_status(myopid) | |
69 | self.sync_all() | |
70 | self.nodes[1].generate(1) | |
71 | self.sync_all() | |
72 | myopid = self.nodes[0].z_sendmany(mytaddr, recipients) | |
73 | self.wait_and_assert_operationid_status(myopid) | |
74 | self.sync_all() | |
75 | self.nodes[1].generate(1) | |
76 | self.sync_all() | |
77 | myopid = self.nodes[0].z_sendmany(mytaddr, recipients) | |
78 | self.wait_and_assert_operationid_status(myopid) | |
79 | self.sync_all() | |
80 | self.nodes[1].generate(1) | |
81 | self.sync_all() | |
82 | ||
83 | # Check balance | |
84 | resp = self.nodes[0].z_getbalance(myzaddr) | |
85 | assert_equal(Decimal(resp), Decimal('9.9999') * 3 ) | |
86 | ||
87 | # We want to test a real-world situation where during the time spent creating a transaction | |
88 | # with joinsplits, other transactions containing joinsplits have been mined into new blocks, | |
89 | # which result in the treestate changing whilst creating the transaction. | |
90 | ||
91 | # Tx 1 will change the treestate while Tx 2 containing chained joinsplits is still being generated | |
92 | recipients = [] | |
93 | recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('10.0') - Decimal('0.0001')}) | |
94 | myopid = self.nodes[0].z_sendmany(mytaddr, recipients) | |
95 | self.wait_and_assert_operationid_status(myopid) | |
96 | ||
97 | # Tx 2 will consume all three notes, which must take at least two joinsplits. This is regardless of | |
98 | # the z_sendmany implementation because there are only two inputs per joinsplit. | |
99 | recipients = [] | |
100 | recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('18')}) | |
101 | recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('11.9997') - Decimal('0.0001')}) | |
102 | myopid = self.nodes[0].z_sendmany(myzaddr, recipients) | |
103 | ||
104 | # Wait for Tx 2 to begin executing... | |
105 | for x in xrange(1, 60): | |
106 | results = self.nodes[0].z_getoperationstatus([myopid]) | |
107 | status = results[0]["status"] | |
108 | if status == "executing": | |
109 | break | |
aff0bf7f | 110 | time.sleep(1) |
2d931e90 S |
111 | |
112 | # Now mine Tx 1 which will change global treestate before Tx 2's second joinsplit begins processing | |
113 | self.sync_all() | |
114 | self.nodes[1].generate(1) | |
115 | self.sync_all() | |
116 | ||
117 | # Wait for Tx 2 to be created | |
118 | self.wait_and_assert_operationid_status(myopid) | |
119 | ||
120 | # Note that a bug existed in v1.0.0-1.0.3 where Tx 2 creation would fail with an error: | |
121 | # "Witness for spendable note does not have same anchor as change input" | |
122 | ||
123 | # Check balance | |
124 | resp = self.nodes[0].z_getbalance(myzaddr) | |
125 | assert_equal(Decimal(resp), Decimal('0.0')) | |
126 | ||
127 | ||
128 | if __name__ == '__main__': | |
129 | WalletTreeStateTest().main() |