Skip to content

Commit e0938c2

Browse files
committed
test: Add tests for block_info in gettxoutsetinfo
This additional data will automatically be returned if the coinstats index is used.
1 parent 2501576 commit e0938c2

File tree

2 files changed

+145
-5
lines changed

2 files changed

+145
-5
lines changed

test/functional/feature_coinstatsindex.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@
99
the 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+
)
1231
from test_framework.test_framework import BitcoinTestFramework
1332
from 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+
88227
if __name__ == '__main__':
89228
CoinStatsIndexTest().main()

test/functional/test_framework/blocktools.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def script_BIP34_coinbase_height(height):
115115
return CScript([CScriptNum(height)])
116116

117117

118-
def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0):
118+
def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0, nValue=50):
119119
"""Create a coinbase transaction.
120120
121121
If pubkey is passed in, the coinbase output will be a P2PK output;
@@ -126,10 +126,11 @@ def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0):
126126
coinbase = CTransaction()
127127
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), script_BIP34_coinbase_height(height), 0xffffffff))
128128
coinbaseoutput = CTxOut()
129-
coinbaseoutput.nValue = 50 * COIN
130-
halvings = int(height / 150) # regtest
131-
coinbaseoutput.nValue >>= halvings
132-
coinbaseoutput.nValue += fees
129+
coinbaseoutput.nValue = nValue * COIN
130+
if nValue == 50:
131+
halvings = int(height / 150) # regtest
132+
coinbaseoutput.nValue >>= halvings
133+
coinbaseoutput.nValue += fees
133134
if pubkey is not None:
134135
coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG])
135136
else:

0 commit comments

Comments
 (0)