9
9
the index.
10
10
"""
11
11
12
+ from decimal import Decimal
13
+
14
+ from test_framework .blocktools import (
15
+ create_block ,
16
+ create_coinbase ,
17
+ )
18
+ from test_framework .messages import (
19
+ COIN ,
20
+ COutPoint ,
21
+ CTransaction ,
22
+ CTxIn ,
23
+ CTxOut ,
24
+ ToHex ,
25
+ )
26
+ from test_framework .script import (
27
+ CScript ,
28
+ OP_FALSE ,
29
+ OP_RETURN ,
30
+ )
12
31
from test_framework .test_framework import BitcoinTestFramework
13
32
from test_framework .util import (
14
33
assert_equal ,
@@ -32,6 +51,13 @@ def skip_test_if_missing_module(self):
32
51
def run_test (self ):
33
52
self ._test_coin_stats_index ()
34
53
54
+ def block_sanity_check (self , block_info ):
55
+ block_subsidy = 50
56
+ assert_equal (
57
+ block_info ['prevout_spent' ] + block_subsidy ,
58
+ block_info ['new_outputs_ex_coinbase' ] + block_info ['coinbase' ] + block_info ['unspendable' ]
59
+ )
60
+
35
61
def _test_coin_stats_index (self ):
36
62
node = self .nodes [0 ]
37
63
index_node = self .nodes [1 ]
@@ -85,5 +111,118 @@ def _test_coin_stats_index(self):
85
111
# It does not work without coinstatsindex
86
112
assert_raises_rpc_error (- 8 , "Querying specific block heights requires coinstatsindex" , node .gettxoutsetinfo , hash_option , 102 )
87
113
114
+ self .log .info ("Test gettxoutsetinfo() with index and verbose flag" )
115
+
116
+ for hash_option in index_hash_options :
117
+ # Genesis block is unspendable
118
+ res4 = index_node .gettxoutsetinfo (hash_option , 0 )
119
+ assert_equal (res4 ['total_unspendable_amount' ], 50 )
120
+ assert_equal (res4 ['block_info' ], {
121
+ 'unspendable' : 50 ,
122
+ 'prevout_spent' : 0 ,
123
+ 'new_outputs_ex_coinbase' : 0 ,
124
+ 'coinbase' : 0 ,
125
+ 'unspendables' : {
126
+ 'genesis_block' : 50 ,
127
+ 'bip30' : 0 ,
128
+ 'scripts' : 0 ,
129
+ 'unclaimed_rewards' : 0
130
+ }
131
+ })
132
+ self .block_sanity_check (res4 ['block_info' ])
133
+
134
+ # Test an older block height that included a normal tx
135
+ res5 = index_node .gettxoutsetinfo (hash_option , 102 )
136
+ assert_equal (res5 ['total_unspendable_amount' ], 50 )
137
+ assert_equal (res5 ['block_info' ], {
138
+ 'unspendable' : 0 ,
139
+ 'prevout_spent' : 50 ,
140
+ 'new_outputs_ex_coinbase' : Decimal ('49.99995560' ),
141
+ 'coinbase' : Decimal ('50.00004440' ),
142
+ 'unspendables' : {
143
+ 'genesis_block' : 0 ,
144
+ 'bip30' : 0 ,
145
+ 'scripts' : 0 ,
146
+ 'unclaimed_rewards' : 0
147
+ }
148
+ })
149
+ self .block_sanity_check (res5 ['block_info' ])
150
+
151
+ # Generate and send a normal tx with two outputs
152
+ tx1_inputs = []
153
+ tx1_outputs = {self .nodes [0 ].getnewaddress (): 21 , self .nodes [0 ].getnewaddress (): 42 }
154
+ raw_tx1 = self .nodes [0 ].createrawtransaction (tx1_inputs , tx1_outputs )
155
+ funded_tx1 = self .nodes [0 ].fundrawtransaction (raw_tx1 )
156
+ signed_tx1 = self .nodes [0 ].signrawtransactionwithwallet (funded_tx1 ['hex' ])
157
+ tx1_txid = self .nodes [0 ].sendrawtransaction (signed_tx1 ['hex' ])
158
+
159
+ # Find the right position of the 21 BTC output
160
+ tx1_final = self .nodes [0 ].gettransaction (tx1_txid )
161
+ for output in tx1_final ['details' ]:
162
+ if output ['amount' ] == Decimal ('21.00000000' ) and output ['category' ] == 'receive' :
163
+ n = output ['vout' ]
164
+
165
+ # Generate and send another tx with an OP_RETURN output (which is unspendable)
166
+ tx2 = CTransaction ()
167
+ tx2 .vin .append (CTxIn (COutPoint (int (tx1_txid , 16 ), n ), b'' ))
168
+ tx2 .vout .append (CTxOut (int (20.99 * COIN ), CScript ([OP_RETURN ] + [OP_FALSE ]* 30 )))
169
+ tx2_hex = self .nodes [0 ].signrawtransactionwithwallet (ToHex (tx2 ))['hex' ]
170
+ self .nodes [0 ].sendrawtransaction (tx2_hex )
171
+
172
+ # Include both txs in a block
173
+ self .nodes [0 ].generate (1 )
174
+ self .sync_all ()
175
+
176
+ self .wait_until (lambda : not try_rpc (- 32603 , "Unable to read UTXO set" , index_node .gettxoutsetinfo , 'muhash' ))
177
+ for hash_option in index_hash_options :
178
+ # Check all amounts were registered correctly
179
+ res6 = index_node .gettxoutsetinfo (hash_option , 108 )
180
+ assert_equal (res6 ['total_unspendable_amount' ], Decimal ('70.98999999' ))
181
+ assert_equal (res6 ['block_info' ], {
182
+ 'unspendable' : Decimal ('20.98999999' ),
183
+ 'prevout_spent' : 111 ,
184
+ 'new_outputs_ex_coinbase' : Decimal ('89.99993620' ),
185
+ 'coinbase' : Decimal ('50.01006381' ),
186
+ 'unspendables' : {
187
+ 'genesis_block' : 0 ,
188
+ 'bip30' : 0 ,
189
+ 'scripts' : Decimal ('20.98999999' ),
190
+ 'unclaimed_rewards' : 0
191
+ }
192
+ })
193
+ self .block_sanity_check (res6 ['block_info' ])
194
+
195
+ # Create a coinbase that does not claim full subsidy and also
196
+ # has two outputs
197
+ cb = create_coinbase (109 , nValue = 35 )
198
+ cb .vout .append (CTxOut (5 * COIN , CScript ([OP_FALSE ])))
199
+ cb .rehash ()
200
+
201
+ # Generate a block that includes previous coinbase
202
+ tip = self .nodes [0 ].getbestblockhash ()
203
+ block_time = self .nodes [0 ].getblock (tip )['time' ] + 1
204
+ block = create_block (int (tip , 16 ), cb , block_time )
205
+ block .solve ()
206
+ self .nodes [0 ].submitblock (ToHex (block ))
207
+ self .sync_all ()
208
+
209
+ self .wait_until (lambda : not try_rpc (- 32603 , "Unable to read UTXO set" , index_node .gettxoutsetinfo , 'muhash' ))
210
+ for hash_option in index_hash_options :
211
+ res7 = index_node .gettxoutsetinfo (hash_option , 109 )
212
+ assert_equal (res7 ['total_unspendable_amount' ], Decimal ('80.98999999' ))
213
+ assert_equal (res7 ['block_info' ], {
214
+ 'unspendable' : 10 ,
215
+ 'prevout_spent' : 0 ,
216
+ 'new_outputs_ex_coinbase' : 0 ,
217
+ 'coinbase' : 40 ,
218
+ 'unspendables' : {
219
+ 'genesis_block' : 0 ,
220
+ 'bip30' : 0 ,
221
+ 'scripts' : 0 ,
222
+ 'unclaimed_rewards' : 10
223
+ }
224
+ })
225
+ self .block_sanity_check (res7 ['block_info' ])
226
+
88
227
if __name__ == '__main__' :
89
228
CoinStatsIndexTest ().main ()
0 commit comments