aboutsummaryrefslogtreecommitdiff
path: root/test/functional/test_framework/wallet.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/test_framework/wallet.py')
-rw-r--r--test/functional/test_framework/wallet.py74
1 files changed, 65 insertions, 9 deletions
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index dd41a740ae..6901bcfe66 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -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,
@@ -93,6 +96,9 @@ class MiniWallet:
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
+ def get_balance(self):
+ return sum(u['value'] for u in self._utxos)
+
def rescan_utxos(self):
"""Drop all utxos and rescan the utxo set"""
self._utxos = []
@@ -121,6 +127,7 @@ class MiniWallet:
if not fixed_length:
break
tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
+ tx.rehash()
def generate(self, num_blocks, **kwargs):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
@@ -131,24 +138,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:
@@ -179,8 +192,50 @@ class MiniWallet:
txid = self.sendrawtransaction(from_node=from_node, tx_hex=tx.serialize().hex())
return txid, 1
+ def send_self_transfer_multi(self, **kwargs):
+ """
+ Create and send a transaction that spends the given UTXOs and creates a
+ certain number of outputs with equal amounts.
+
+ Returns a dictionary with
+ - txid
+ - serialized transaction in hex format
+ - transaction as CTransaction instance
+ - list of newly created UTXOs, ordered by vout index
+ """
+ tx = self.create_self_transfer_multi(**kwargs)
+ txid = self.sendrawtransaction(from_node=kwargs['from_node'], tx_hex=tx.serialize().hex())
+ return {'new_utxos': [self.get_utxo(txid=txid, vout=vout) for vout in range(len(tx.vout))],
+ 'txid': txid, 'hex': tx.serialize().hex(), 'tx': tx}
+
+ def create_self_transfer_multi(self, *, from_node, utxos_to_spend=None, num_outputs=1, fee_per_output=1000):
+ """
+ Create and return a transaction that spends the given UTXOs and creates a
+ certain number of outputs with equal amounts.
+ """
+ utxos_to_spend = utxos_to_spend or [self.get_utxo()]
+ # create simple tx template (1 input, 1 output)
+ tx = self.create_self_transfer(fee_rate=0, from_node=from_node, utxo_to_spend=utxos_to_spend[0], mempool_valid=False)['tx']
+
+ # duplicate inputs, witnesses and outputs
+ tx.vin = [deepcopy(tx.vin[0]) for _ in range(len(utxos_to_spend))]
+ tx.wit.vtxinwit = [deepcopy(tx.wit.vtxinwit[0]) for _ in range(len(utxos_to_spend))]
+ tx.vout = [deepcopy(tx.vout[0]) for _ in range(num_outputs)]
+
+ # adapt input prevouts
+ for i, utxo in enumerate(utxos_to_spend):
+ tx.vin[i] = CTxIn(COutPoint(int(utxo['txid'], 16), utxo['vout']))
+
+ # adapt output amounts (use fixed fee per output)
+ inputs_value_total = sum([int(COIN * utxo['value']) for utxo in utxos_to_spend])
+ outputs_value_total = inputs_value_total - fee_per_output * num_outputs
+ for o in tx.vout:
+ o.nValue = outputs_value_total // num_outputs
+ return tx
+
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node=None, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0):
- """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
+ """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed.
+ Checking mempool validity via the testmempoolaccept RPC can be skipped by setting mempool_valid to False."""
from_node = from_node or self._test_node
utxo_to_spend = utxo_to_spend or self.get_utxo()
if self._priv_key is None:
@@ -207,12 +262,13 @@ class MiniWallet:
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
tx_hex = tx.serialize().hex()
- tx_info = from_node.testmempoolaccept([tx_hex])[0]
- assert_equal(mempool_valid, tx_info['allowed'])
if mempool_valid:
+ tx_info = from_node.testmempoolaccept([tx_hex])[0]
+ assert_equal(tx_info['allowed'], True)
assert_equal(tx_info['vsize'], vsize)
assert_equal(tx_info['fees']['base'], utxo_to_spend['value'] - Decimal(send_value) / COIN)
- return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx}
+
+ return {'txid': tx.rehash(), 'wtxid': tx.getwtxid(), 'hex': tx_hex, 'tx': tx}
def sendrawtransaction(self, *, from_node, tx_hex, **kwargs):
txid = from_node.sendrawtransaction(hexstring=tx_hex, **kwargs)