Merge bitcoin/bitcoin#24605: test: Use MiniWallet in feature_coinstatsindex

fa48ea3067 Use MiniWallet in feature_coinstatsindex (MarcoFalke)
fab61437f6 test: Refactor MiniWallet get_utxo helper (MarcoFalke)

Pull request description:

  Allows the test to be run even without a wallet compiled

ACKs for top commit:
  josibake:
    ACK fa48ea3067
  ayush933:
    tACK  fa48ea3 . The test runs successfully with the wallet disabled.
  willcl-ark:
    tACK fa48ea3067 both with and without wallet compiled in.

Tree-SHA512: e04e04ea0f236c062d6be68909ece2770130ce1d5343823893073d95aebc6eedb1ad1dc5bc41e5b0cb0bf2cd9018bb1d668f0e7f5f1101ed4e0b007ed6b00f69
pull/24635/head
MarcoFalke 3 years ago
commit e3206c9445
No known key found for this signature in database
GPG Key ID: CE2B75697E69A548

@ -18,9 +18,6 @@ from test_framework.blocktools import (
) )
from test_framework.messages import ( from test_framework.messages import (
COIN, COIN,
COutPoint,
CTransaction,
CTxIn,
CTxOut, CTxOut,
) )
from test_framework.script import ( from test_framework.script import (
@ -33,6 +30,11 @@ from test_framework.util import (
assert_equal, assert_equal,
assert_raises_rpc_error, assert_raises_rpc_error,
) )
from test_framework.wallet import (
MiniWallet,
getnewdestination,
)
class CoinStatsIndexTest(BitcoinTestFramework): class CoinStatsIndexTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
@ -40,16 +42,12 @@ class CoinStatsIndexTest(BitcoinTestFramework):
self.num_nodes = 2 self.num_nodes = 2
self.supports_cli = False self.supports_cli = False
self.extra_args = [ self.extra_args = [
# Explicitly set the output type in order to have consistent tx vsize / fees [],
# for both legacy and descriptor wallets (disables the change address type detection algorithm)
["-addresstype=bech32", "-changetype=bech32"],
["-coinstatsindex"] ["-coinstatsindex"]
] ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self): def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
self._test_coin_stats_index() self._test_coin_stats_index()
self._test_use_index_option() self._test_use_index_option()
self._test_reorg_index() self._test_reorg_index()
@ -69,9 +67,8 @@ class CoinStatsIndexTest(BitcoinTestFramework):
index_hash_options = ['none', 'muhash'] index_hash_options = ['none', 'muhash']
# Generate a normal transaction and mine it # Generate a normal transaction and mine it
self.generate(node, COINBASE_MATURITY + 1) self.generate(self.wallet, COINBASE_MATURITY + 1)
address = self.nodes[0].get_deterministic_priv_key().address self.wallet.send_self_transfer(from_node=node)
node.sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
self.generate(node, 1) self.generate(node, 1)
self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option") self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option")
@ -136,36 +133,31 @@ class CoinStatsIndexTest(BitcoinTestFramework):
assert_equal(res5['block_info'], { assert_equal(res5['block_info'], {
'unspendable': 0, 'unspendable': 0,
'prevout_spent': 50, 'prevout_spent': 50,
'new_outputs_ex_coinbase': Decimal('49.99995560'), 'new_outputs_ex_coinbase': Decimal('49.99968800'),
'coinbase': Decimal('50.00004440'), 'coinbase': Decimal('50.00031200'),
'unspendables': { 'unspendables': {
'genesis_block': 0, 'genesis_block': 0,
'bip30': 0, 'bip30': 0,
'scripts': 0, 'scripts': 0,
'unclaimed_rewards': 0 'unclaimed_rewards': 0,
} }
}) })
self.block_sanity_check(res5['block_info']) self.block_sanity_check(res5['block_info'])
# Generate and send a normal tx with two outputs # Generate and send a normal tx with two outputs
tx1_inputs = [] tx1_txid, tx1_vout = self.wallet.send_to(
tx1_outputs = {self.nodes[0].getnewaddress(): 21, self.nodes[0].getnewaddress(): 42} from_node=node,
raw_tx1 = self.nodes[0].createrawtransaction(tx1_inputs, tx1_outputs) scriptPubKey=self.wallet.get_scriptPubKey(),
funded_tx1 = self.nodes[0].fundrawtransaction(raw_tx1) amount=21 * COIN,
signed_tx1 = self.nodes[0].signrawtransactionwithwallet(funded_tx1['hex']) )
tx1_txid = self.nodes[0].sendrawtransaction(signed_tx1['hex'])
# Find the right position of the 21 BTC output # Find the right position of the 21 BTC output
tx1_final = self.nodes[0].gettransaction(tx1_txid) tx1_out_21 = self.wallet.get_utxo(txid=tx1_txid, vout=tx1_vout)
for output in tx1_final['details']:
if output['amount'] == Decimal('21.00000000') and output['category'] == 'receive':
n = output['vout']
# Generate and send another tx with an OP_RETURN output (which is unspendable) # Generate and send another tx with an OP_RETURN output (which is unspendable)
tx2 = CTransaction() tx2 = self.wallet.create_self_transfer(utxo_to_spend=tx1_out_21)['tx']
tx2.vin.append(CTxIn(COutPoint(int(tx1_txid, 16), n), b'')) tx2.vout = [CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
tx2.vout.append(CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE]*30))) tx2_hex = tx2.serialize().hex()
tx2_hex = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())['hex']
self.nodes[0].sendrawtransaction(tx2_hex) self.nodes[0].sendrawtransaction(tx2_hex)
# Include both txs in a block # Include both txs in a block
@ -177,14 +169,14 @@ class CoinStatsIndexTest(BitcoinTestFramework):
assert_equal(res6['total_unspendable_amount'], Decimal('70.99000000')) assert_equal(res6['total_unspendable_amount'], Decimal('70.99000000'))
assert_equal(res6['block_info'], { assert_equal(res6['block_info'], {
'unspendable': Decimal('20.99000000'), 'unspendable': Decimal('20.99000000'),
'prevout_spent': 111, 'prevout_spent': 71,
'new_outputs_ex_coinbase': Decimal('89.99993620'), 'new_outputs_ex_coinbase': Decimal('49.99999000'),
'coinbase': Decimal('50.01006380'), 'coinbase': Decimal('50.01001000'),
'unspendables': { 'unspendables': {
'genesis_block': 0, 'genesis_block': 0,
'bip30': 0, 'bip30': 0,
'scripts': Decimal('20.99000000'), 'scripts': Decimal('20.99000000'),
'unclaimed_rewards': 0 'unclaimed_rewards': 0,
} }
}) })
self.block_sanity_check(res6['block_info']) self.block_sanity_check(res6['block_info'])
@ -246,7 +238,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
# Generate two block, let the index catch up, then invalidate the blocks # Generate two block, let the index catch up, then invalidate the blocks
index_node = self.nodes[1] index_node = self.nodes[1]
reorg_blocks = self.generatetoaddress(index_node, 2, index_node.getnewaddress()) reorg_blocks = self.generatetoaddress(index_node, 2, getnewdestination()[2])
reorg_block = reorg_blocks[1] reorg_block = reorg_blocks[1]
res_invalid = index_node.gettxoutsetinfo('muhash') res_invalid = index_node.gettxoutsetinfo('muhash')
index_node.invalidateblock(reorg_blocks[0]) index_node.invalidateblock(reorg_blocks[0])

@ -8,7 +8,10 @@ from copy import deepcopy
from decimal import Decimal from decimal import Decimal
from enum import Enum from enum import Enum
from random import choice from random import choice
from typing import Optional from typing import (
Any,
Optional,
)
from test_framework.address import ( from test_framework.address import (
base58_to_byte, base58_to_byte,
create_deterministic_address_bcrt1_p2tr_op_true, create_deterministic_address_bcrt1_p2tr_op_true,
@ -131,24 +134,30 @@ class MiniWallet:
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value'], 'height': block_info['height']}) self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value'], 'height': block_info['height']})
return blocks return blocks
def get_scriptPubKey(self):
return self._scriptPubKey
def get_descriptor(self): def get_descriptor(self):
return descsum_create(f'raw({self._scriptPubKey.hex()})') return descsum_create(f'raw({self._scriptPubKey.hex()})')
def get_address(self): def get_address(self):
return self._address return self._address
def get_utxo(self, *, txid: Optional[str]='', mark_as_spent=True): def get_utxo(self, *, txid: str = '', vout: Optional[int] = None, mark_as_spent=True):
""" """
Returns a utxo and marks it as spent (pops it from the internal list) Returns a utxo and marks it as spent (pops it from the internal list)
Args: Args:
txid: get the first utxo we find from a specific transaction txid: get the first utxo we find from a specific transaction
""" """
index = -1 # by default the last utxo
self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height'])) # Put the largest utxo last self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height'])) # Put the largest utxo last
if txid: if txid:
utxo = next(filter(lambda utxo: txid == utxo['txid'], self._utxos)) utxo_filter: Any = filter(lambda utxo: txid == utxo['txid'], self._utxos)
index = self._utxos.index(utxo) else:
utxo_filter = reversed(self._utxos) # By default the largest utxo
if vout is not None:
utxo_filter = filter(lambda utxo: vout == utxo['vout'], utxo_filter)
index = self._utxos.index(next(utxo_filter))
if mark_as_spent: if mark_as_spent:
return self._utxos.pop(index) return self._utxos.pop(index)
else: else:

Loading…
Cancel
Save