aboutsummaryrefslogtreecommitdiff
path: root/test/functional/test_framework
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/test_framework')
-rw-r--r--test/functional/test_framework/script.py1
-rwxr-xr-xtest/functional/test_framework/test_framework.py11
-rwxr-xr-xtest/functional/test_framework/test_node.py3
-rw-r--r--test/functional/test_framework/util.py16
-rw-r--r--test/functional/test_framework/wallet.py63
5 files changed, 78 insertions, 16 deletions
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 7791ae5392..2b70eab4e4 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -27,6 +27,7 @@ from .messages import (
from .ripemd160 import ripemd160
MAX_SCRIPT_ELEMENT_SIZE = 520
+MAX_PUBKEYS_PER_MULTI_A = 999
LOCKTIME_THRESHOLD = 500000000
ANNEX_TAG = 0x50
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 165c5b8d83..2fb9ec0942 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -448,11 +448,15 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def get_bin_from_version(version, bin_name, bin_default):
if not version:
return bin_default
+ if version > 219999:
+ # Starting at client version 220000 the first two digits represent
+ # the major version, e.g. v22.0 instead of v0.22.0.
+ version *= 100
return os.path.join(
self.options.previous_releases_path,
re.sub(
- r'\.0$',
- '', # remove trailing .0 for point releases
+ r'\.0$' if version <= 219999 else r'(\.0){1,2}$',
+ '', # Remove trailing dot for point releases, after 22.0 also remove double trailing dot.
'v{}.{}.{}.{}'.format(
(version % 100000000) // 1000000,
(version % 1000000) // 10000,
@@ -474,7 +478,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
versions = [None] * num_nodes
if self.is_syscall_sandbox_compiled() and not self.disable_syscall_sandbox:
for i in range(len(extra_args)):
- if versions[i] is None or versions[i] >= 219900:
+ # The -sandbox argument is not present in the v22.0 release.
+ if versions[i] is None or versions[i] >= 229900:
extra_args[i] = extra_args[i] + ["-sandbox=log-and-abort"]
if binary is None:
binary = [get_bin_from_version(v, 'bitcoind', self.options.bitcoind) for v in versions]
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 289e83579b..e56d4aa492 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -743,6 +743,9 @@ class RPCOverloadWrapper():
def __getattr__(self, name):
return getattr(self.rpc, name)
+ def createwallet_passthrough(self, *args, **kwargs):
+ return self.__getattr__("createwallet")(*args, **kwargs)
+
def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None, external_signer=None):
if descriptors is None:
descriptors = self.descriptors
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 210025104e..8651bcf636 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -573,17 +573,17 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
return txids
-def mine_large_block(test_framework, node, utxos=None):
+def mine_large_block(test_framework, mini_wallet, node):
# generate a 66k transaction,
# and 14 of them is close to the 1MB block limit
- num = 14
txouts = gen_return_txouts()
- utxos = utxos if utxos is not None else []
- if len(utxos) < num:
- utxos.clear()
- utxos.extend(node.listunspent())
- fee = 100 * node.getnetworkinfo()["relayfee"]
- create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee)
+ from .messages import COIN
+ fee = 100 * int(node.getnetworkinfo()["relayfee"] * COIN)
+ for _ in range(14):
+ tx = mini_wallet.create_self_transfer(from_node=node, fee_rate=0, mempool_valid=False)['tx']
+ tx.vout[0].nValue -= fee
+ tx.vout.extend(txouts)
+ mini_wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
test_framework.generate(node, 1)
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index dd41a740ae..e86f365f11 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 = []
@@ -131,24 +137,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,6 +191,47 @@ 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."""
from_node = from_node or self._test_node