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 (
COIN,
COutPoint,
CTransaction,
CTxIn,
CTxOut,
)
from test_framework.script import (
@ -33,6 +30,11 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
from test_framework.wallet import (
MiniWallet,
getnewdestination,
)
class CoinStatsIndexTest(BitcoinTestFramework):
def set_test_params(self):
@ -40,16 +42,12 @@ class CoinStatsIndexTest(BitcoinTestFramework):
self.num_nodes = 2
self.supports_cli = False
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"]
]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
self._test_coin_stats_index()
self._test_use_index_option()
self._test_reorg_index()
@ -69,9 +67,8 @@ class CoinStatsIndexTest(BitcoinTestFramework):
index_hash_options = ['none', 'muhash']
# Generate a normal transaction and mine it
self.generate(node, COINBASE_MATURITY + 1)
address = self.nodes[0].get_deterministic_priv_key().address
node.sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
self.generate(self.wallet, COINBASE_MATURITY + 1)
self.wallet.send_self_transfer(from_node=node)
self.generate(node, 1)
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'], {
'unspendable': 0,
'prevout_spent': 50,
'new_outputs_ex_coinbase': Decimal('49.99995560'),
'coinbase': Decimal('50.00004440'),
'new_outputs_ex_coinbase': Decimal('49.99968800'),
'coinbase': Decimal('50.00031200'),
'unspendables': {
'genesis_block': 0,
'bip30': 0,
'scripts': 0,
'unclaimed_rewards': 0
'unclaimed_rewards': 0,
}
})
self.block_sanity_check(res5['block_info'])
# Generate and send a normal tx with two outputs
tx1_inputs = []
tx1_outputs = {self.nodes[0].getnewaddress(): 21, self.nodes[0].getnewaddress(): 42}
raw_tx1 = self.nodes[0].createrawtransaction(tx1_inputs, tx1_outputs)
funded_tx1 = self.nodes[0].fundrawtransaction(raw_tx1)
signed_tx1 = self.nodes[0].signrawtransactionwithwallet(funded_tx1['hex'])
tx1_txid = self.nodes[0].sendrawtransaction(signed_tx1['hex'])
tx1_txid, tx1_vout = self.wallet.send_to(
from_node=node,
scriptPubKey=self.wallet.get_scriptPubKey(),
amount=21 * COIN,
)
# Find the right position of the 21 BTC output
tx1_final = self.nodes[0].gettransaction(tx1_txid)
for output in tx1_final['details']:
if output['amount'] == Decimal('21.00000000') and output['category'] == 'receive':
n = output['vout']
tx1_out_21 = self.wallet.get_utxo(txid=tx1_txid, vout=tx1_vout)
# Generate and send another tx with an OP_RETURN output (which is unspendable)
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(int(tx1_txid, 16), n), b''))
tx2.vout.append(CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE]*30)))
tx2_hex = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())['hex']
tx2 = self.wallet.create_self_transfer(utxo_to_spend=tx1_out_21)['tx']
tx2.vout = [CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
tx2_hex = tx2.serialize().hex()
self.nodes[0].sendrawtransaction(tx2_hex)
# 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['block_info'], {
'unspendable': Decimal('20.99000000'),
'prevout_spent': 111,
'new_outputs_ex_coinbase': Decimal('89.99993620'),
'coinbase': Decimal('50.01006380'),
'prevout_spent': 71,
'new_outputs_ex_coinbase': Decimal('49.99999000'),
'coinbase': Decimal('50.01001000'),
'unspendables': {
'genesis_block': 0,
'bip30': 0,
'scripts': Decimal('20.99000000'),
'unclaimed_rewards': 0
'unclaimed_rewards': 0,
}
})
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
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]
res_invalid = index_node.gettxoutsetinfo('muhash')
index_node.invalidateblock(reorg_blocks[0])

@ -8,7 +8,10 @@ from copy import deepcopy
from decimal import Decimal
from enum import Enum
from random import choice
from typing import Optional
from typing import (
Any,
Optional,
)
from test_framework.address import (
base58_to_byte,
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']})
return blocks
def get_scriptPubKey(self):
return self._scriptPubKey
def get_descriptor(self):
return descsum_create(f'raw({self._scriptPubKey.hex()})')
def get_address(self):
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)
Args:
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
if txid:
utxo = next(filter(lambda utxo: txid == utxo['txid'], self._utxos))
index = self._utxos.index(utxo)
utxo_filter: Any = filter(lambda utxo: txid == utxo['txid'], self._utxos)
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:
return self._utxos.pop(index)
else:

Loading…
Cancel
Save