99the index.
1010"""
1111
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+ )
1231from test_framework .test_framework import BitcoinTestFramework
1332from test_framework .util import (
1433 assert_equal ,
@@ -32,6 +51,13 @@ def skip_test_if_missing_module(self):
3251 def run_test (self ):
3352 self ._test_coin_stats_index ()
3453
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+
3561 def _test_coin_stats_index (self ):
3662 node = self .nodes [0 ]
3763 index_node = self .nodes [1 ]
@@ -85,5 +111,118 @@ def _test_coin_stats_index(self):
85111 # It does not work without coinstatsindex
86112 assert_raises_rpc_error (- 8 , "Querying specific block heights requires coinstatsindex" , node .gettxoutsetinfo , hash_option , 102 )
87113
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+
88227if __name__ == '__main__' :
89228 CoinStatsIndexTest ().main ()
0 commit comments