diff options
author | MacroFake <falke.marco@gmail.com> | 2022-08-03 11:11:44 +0200 |
---|---|---|
committer | MacroFake <falke.marco@gmail.com> | 2022-08-03 11:12:05 +0200 |
commit | 9155f9b7af22a9c97cbeee461f5bbd9217adb56d (patch) | |
tree | 47cd25c4e4acecb393e23039ca946ddbc56f6ec0 /test | |
parent | f6fdedf850d10d877316871aacfd5b6656178e70 (diff) | |
parent | f2f6068b69b1b532db92b276f024c89b56f38294 (diff) | |
download | bitcoin-9155f9b7af22a9c97cbeee461f5bbd9217adb56d.tar.xz |
Merge bitcoin/bitcoin#25379: test: use MiniWallet to simplify mempool_package_limits.py tests
f2f6068b69b1b532db92b276f024c89b56f38294 test: MiniWallet: add `send_self_transfer_chain` to create chain of txns (Andreas Kouloumos)
1d6b438ef0ccd05e1522ac38b44f847c1d93e72f test: use MiniWallet to simplify mempool_package_limits.py tests (Andreas Kouloumos)
Pull request description:
While `wallet.py` includes the MiniWallet class and some helper methods, it also includes some methods that have been moved there without having any direct relation with the MiniWallet class. Specifically `make_chain`, `create_child_with_parents` and `create_raw_chain` methods that were extracted from `rpc_packages.py` at f8253d69d6f02850995a11eeb71fedc22e6f6575 in order to be used on both `mempool_package_limits.py` and `rpc_packages.py`.
Since that change, due to the introduction of additional methods in MiniWallet, the functionality of those methods can now be replicated with the existing MiniWallet methods and simultaneously simplify those tests by using the MiniWallet.
This PR's goals are
- to simplify the `mempool_package_limits.py` functional tests with usage of the MiniWallet.
- to make progress towards the removal of the `make_chain`, `create_child_with_parents` and `create_raw_chain` methods of `wallet.py`.
For the purpose of the aforementioned goals, a helper method `MiniWallet.send_self_transfer_chain` is introduced and method `bulk_transaction` has been integrated in `create_self_transfer*` methods using an optional `target_weight` option.
ACKs for top commit:
MarcoFalke:
ACK f2f6068b69b1b532db92b276f024c89b56f38294 👜
Tree-SHA512: 3ddfa0046168cbf7904ec6b1ca233b3fdd4f30db6aefae108b6d7fb69f34ef6fb2cf4fa7cef9473ce1434a0cc8149d236441a685352fef35359a2b7ba0d951eb
Diffstat (limited to 'test')
-rwxr-xr-x | test/functional/mempool_package_limits.py | 312 | ||||
-rw-r--r-- | test/functional/test_framework/wallet.py | 55 |
2 files changed, 101 insertions, 266 deletions
diff --git a/test/functional/mempool_package_limits.py b/test/functional/mempool_package_limits.py index 89a5c83826..3712f66a65 100755 --- a/test/functional/mempool_package_limits.py +++ b/test/functional/mempool_package_limits.py @@ -6,28 +6,16 @@ from decimal import Decimal -from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE +from test_framework.blocktools import COINBASE_MATURITY from test_framework.test_framework import BitcoinTestFramework from test_framework.messages import ( COIN, - CTransaction, - CTxInWitness, - tx_from_hex, WITNESS_SCALE_FACTOR, ) -from test_framework.script import ( - CScript, - OP_TRUE, -) from test_framework.util import ( assert_equal, ) -from test_framework.wallet import ( - bulk_transaction, - create_child_with_parents, - make_chain, - DEFAULT_FEE, -) +from test_framework.wallet import MiniWallet class MempoolPackageLimitsTest(BitcoinTestFramework): def set_test_params(self): @@ -35,19 +23,10 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): self.setup_clean_chain = True def run_test(self): - self.log.info("Generate blocks to create UTXOs") - node = self.nodes[0] - self.privkeys = [node.get_deterministic_priv_key().key] - self.address = node.get_deterministic_priv_key().address - self.coins = [] - # The last 100 coinbase transactions are premature - for b in self.generatetoaddress(node, 200, self.address)[:100]: - coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0] - self.coins.append({ - "txid": coinbase["txid"], - "amount": coinbase["vout"][0]["value"], - "scriptPubKey": coinbase["vout"][0]["scriptPubKey"], - }) + self.wallet = MiniWallet(self.nodes[0]) + # Add enough mature utxos to the wallet so that all txs spend confirmed coins. + self.generate(self.wallet, 35) + self.generate(self.nodes[0], COINBASE_MATURITY) self.test_chain_limits() self.test_desc_count_limits() @@ -64,22 +43,14 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): def test_chain_limits_helper(self, mempool_count, package_count): node = self.nodes[0] assert_equal(0, node.getmempoolinfo()["size"]) - first_coin = self.coins.pop() - spk = None - txid = first_coin["txid"] chain_hex = [] - chain_txns = [] - value = first_coin["amount"] - - for i in range(mempool_count + package_count): - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk) - txid = tx.rehash() - if i < mempool_count: - node.sendrawtransaction(txhex) - assert_equal(node.getmempoolentry(txid)["ancestorcount"], i + 1) - else: - chain_hex.append(txhex) - chain_txns.append(tx) + + chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=mempool_count) + # in-package transactions + for _ in range(package_count): + tx = self.wallet.create_self_transfer(utxo_to_spend=chaintip_utxo) + chaintip_utxo = tx["new_utxo"] + chain_hex.append(tx["hex"]) testres_too_long = node.testmempoolaccept(rawtxs=chain_hex) for txres in testres_too_long: assert_equal(txres["package-error"], "package-mempool-limits") @@ -125,49 +96,20 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): assert_equal(0, node.getmempoolinfo()["size"]) self.log.info("Check that in-mempool and in-package descendants are calculated properly in packages") # Top parent in mempool, M1 - first_coin = self.coins.pop() - parent_value = (first_coin["amount"] - Decimal("0.0002")) / 2 # Deduct reasonable fee and make 2 outputs - inputs = [{"txid": first_coin["txid"], "vout": 0}] - outputs = [{self.address : parent_value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE : parent_value}] - rawtx = node.createrawtransaction(inputs, outputs) - - parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys) - assert parent_signed["complete"] - parent_tx = tx_from_hex(parent_signed["hex"]) - parent_txid = parent_tx.rehash() - node.sendrawtransaction(parent_signed["hex"]) + m1_utxos = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2)['new_utxos'] package_hex = [] - - # Chain A - spk = parent_tx.vout[0].scriptPubKey.hex() - value = parent_value - txid = parent_txid - for i in range(12): - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk) - txid = tx.rehash() - if i < 11: # M2a... M12a - node.sendrawtransaction(txhex) - else: # Pa - package_hex.append(txhex) - - # Chain B - value = parent_value - Decimal("0.0001") - rawtx_b = node.createrawtransaction([{"txid": parent_txid, "vout": 1}], {self.address : value}) - tx_child_b = tx_from_hex(rawtx_b) # M2b - tx_child_b.wit.vtxinwit = [CTxInWitness()] - tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] - tx_child_b_hex = tx_child_b.serialize().hex() - node.sendrawtransaction(tx_child_b_hex) - spk = tx_child_b.vout[0].scriptPubKey.hex() - txid = tx_child_b.rehash() - for i in range(12): - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk) - txid = tx.rehash() - if i < 11: # M3b... M13b - node.sendrawtransaction(txhex) - else: # Pb - package_hex.append(txhex) + # Chain A (M2a... M12a) + chain_a_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=11, utxo_to_spend=m1_utxos[0]) + # Pa + pa_hex = self.wallet.create_self_transfer(utxo_to_spend=chain_a_tip_utxo)["hex"] + package_hex.append(pa_hex) + + # Chain B (M2b... M13b) + chain_b_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12, utxo_to_spend=m1_utxos[1]) + # Pb + pb_hex = self.wallet.create_self_transfer(utxo_to_spend=chain_b_tip_utxo)["hex"] + package_hex.append(pb_hex) assert_equal(24, node.getmempoolinfo()["size"]) assert_equal(2, len(package_hex)) @@ -200,41 +142,18 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): node = self.nodes[0] package_hex = [] # M1 - first_coin_a = self.coins.pop() - parent_value = (first_coin_a["amount"] - DEFAULT_FEE) / 2 # Deduct reasonable fee and make 2 outputs - inputs = [{"txid": first_coin_a["txid"], "vout": 0}] - outputs = [{self.address : parent_value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE : parent_value}] - rawtx = node.createrawtransaction(inputs, outputs) - - parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys) - assert parent_signed["complete"] - parent_tx = tx_from_hex(parent_signed["hex"]) - parent_txid = parent_tx.rehash() - node.sendrawtransaction(parent_signed["hex"]) + m1_utxos = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2)['new_utxos'] # Chain M2...M24 - spk = parent_tx.vout[0].scriptPubKey.hex() - value = parent_value - txid = parent_txid - for i in range(23): # M2...M24 - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk) - txid = tx.rehash() - node.sendrawtransaction(txhex) + self.wallet.send_self_transfer_chain(from_node=node, chain_length=23, utxo_to_spend=m1_utxos[0]) # P1 - value_p1 = (parent_value - DEFAULT_FEE) - rawtx_p1 = node.createrawtransaction([{"txid": parent_txid, "vout": 1}], [{self.address : value_p1}]) - tx_child_p1 = tx_from_hex(rawtx_p1) - tx_child_p1.wit.vtxinwit = [CTxInWitness()] - tx_child_p1.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] - tx_child_p1_hex = tx_child_p1.serialize().hex() - txid_child_p1 = tx_child_p1.rehash() - package_hex.append(tx_child_p1_hex) - tx_child_p1_spk = tx_child_p1.vout[0].scriptPubKey.hex() + p1_tx = self.wallet.create_self_transfer(utxo_to_spend=m1_utxos[1]) + package_hex.append(p1_tx["hex"]) # P2 - (_, tx_child_p2_hex, _, _) = make_chain(node, self.address, self.privkeys, txid_child_p1, value_p1, 0, tx_child_p1_spk) - package_hex.append(tx_child_p2_hex) + p2_tx = self.wallet.create_self_transfer(utxo_to_spend=p1_tx["new_utxo"]) + package_hex.append(p2_tx["hex"]) assert_equal(24, node.getmempoolinfo()["size"]) assert_equal(2, len(package_hex)) @@ -266,32 +185,21 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): node = self.nodes[0] assert_equal(0, node.getmempoolinfo()["size"]) package_hex = [] - parents_tx = [] - values = [] - scripts = [] + pc_parent_utxos = [] self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages") # Two chains of 13 transactions each for _ in range(2): - spk = None - top_coin = self.coins.pop() - txid = top_coin["txid"] - value = top_coin["amount"] - for i in range(13): - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk) - txid = tx.rehash() - if i < 12: - node.sendrawtransaction(txhex) - else: # Save the 13th transaction for the package - package_hex.append(txhex) - parents_tx.append(tx) - scripts.append(spk) - values.append(value) + chain_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12) + # Save the 13th transaction for the package + tx = self.wallet.create_self_transfer(utxo_to_spend=chain_tip_utxo) + package_hex.append(tx["hex"]) + pc_parent_utxos.append(tx["new_utxo"]) # Child Pc - child_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts) - package_hex.append(child_hex) + pc_hex = self.wallet.create_self_transfer_multi(utxos_to_spend=pc_parent_utxos)["hex"] + package_hex.append(pc_hex) assert_equal(24, node.getmempoolinfo()["size"]) assert_equal(3, len(package_hex)) @@ -321,45 +229,29 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): """ node = self.nodes[0] assert_equal(0, node.getmempoolinfo()["size"]) - parents_tx = [] - values = [] - scripts = [] + pc_parent_utxos = [] self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages") # Two chains of 12 transactions each for _ in range(2): - spk = None - top_coin = self.coins.pop() - txid = top_coin["txid"] - value = top_coin["amount"] - for i in range(12): - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk) - txid = tx.rehash() - value -= Decimal("0.0001") - node.sendrawtransaction(txhex) - if i == 11: - # last 2 transactions will be the parents of Pc - parents_tx.append(tx) - values.append(value) - scripts.append(spk) + chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12) + # last 2 transactions will be the parents of Pc + pc_parent_utxos.append(chaintip_utxo) # Child Pc - pc_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts) - pc_tx = tx_from_hex(pc_hex) - pc_value = sum(values) - Decimal("0.0002") - pc_spk = pc_tx.vout[0].scriptPubKey.hex() + pc_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=pc_parent_utxos) # Child Pd - (_, pd_hex, _, _) = make_chain(node, self.address, self.privkeys, pc_tx.rehash(), pc_value, 0, pc_spk) + pd_tx = self.wallet.create_self_transfer(utxo_to_spend=pc_tx["new_utxos"][0]) assert_equal(24, node.getmempoolinfo()["size"]) - testres_too_long = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex]) + testres_too_long = node.testmempoolaccept(rawtxs=[pc_tx["hex"], pd_tx["hex"]]) for txres in testres_too_long: assert_equal(txres["package-error"], "package-mempool-limits") # Clear mempool and check that the package passes now self.generate(node, 1) - assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])]) + assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_tx["hex"], pd_tx["hex"]])]) def test_anc_count_limits_bushy(self): """Create a tree with 20 transactions in the mempool and 6 in the package: @@ -375,31 +267,18 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): node = self.nodes[0] assert_equal(0, node.getmempoolinfo()["size"]) package_hex = [] - parent_txns = [] - parent_values = [] - scripts = [] + pc_parent_utxos = [] for _ in range(5): # Make package transactions P0 ... P4 - gp_tx = [] - gp_values = [] - gp_scripts = [] + pc_grandparent_utxos = [] for _ in range(4): # Make mempool transactions M(4i+1)...M(4i+4) - parent_coin = self.coins.pop() - value = parent_coin["amount"] - txid = parent_coin["txid"] - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value) - gp_tx.append(tx) - gp_values.append(value) - gp_scripts.append(spk) - node.sendrawtransaction(txhex) + pc_grandparent_utxos.append(self.wallet.send_self_transfer(from_node=node)["new_utxo"]) # Package transaction Pi - pi_hex = create_child_with_parents(node, self.address, self.privkeys, gp_tx, gp_values, gp_scripts) - package_hex.append(pi_hex) - pi_tx = tx_from_hex(pi_hex) - parent_txns.append(pi_tx) - parent_values.append(Decimal(pi_tx.vout[0].nValue) / COIN) - scripts.append(pi_tx.vout[0].scriptPubKey.hex()) + pi_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=pc_grandparent_utxos) + package_hex.append(pi_tx["hex"]) + pc_parent_utxos.append(pi_tx["new_utxos"][0]) # Package transaction PC - package_hex.append(create_child_with_parents(node, self.address, self.privkeys, parent_txns, parent_values, scripts)) + pc_hex = self.wallet.create_self_transfer_multi(utxos_to_spend=pc_parent_utxos)["hex"] + package_hex.append(pc_hex) assert_equal(20, node.getmempoolinfo()["size"]) assert_equal(6, len(package_hex)) @@ -424,51 +303,30 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): """ node = self.nodes[0] assert_equal(0, node.getmempoolinfo()["size"]) - parents_tx = [] - values = [] - scripts = [] + parent_utxos = [] target_weight = WITNESS_SCALE_FACTOR * 1000 * 30 # 30KvB high_fee = Decimal("0.003") # 10 sats/vB self.log.info("Check that in-mempool and in-package ancestor size limits are calculated properly in packages") # Mempool transactions A and B for _ in range(2): - spk = None - top_coin = self.coins.pop() - txid = top_coin["txid"] - value = top_coin["amount"] - (tx, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk, high_fee) - bulked_tx = bulk_transaction(tx, node, target_weight, self.privkeys) - node.sendrawtransaction(bulked_tx.serialize().hex()) - parents_tx.append(bulked_tx) - values.append(Decimal(bulked_tx.vout[0].nValue) / COIN) - scripts.append(bulked_tx.vout[0].scriptPubKey.hex()) + bulked_tx = self.wallet.create_self_transfer(target_weight=target_weight) + self.wallet.sendrawtransaction(from_node=node, tx_hex=bulked_tx["hex"]) + parent_utxos.append(bulked_tx["new_utxo"]) # Package transaction C - small_pc_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts, high_fee) - pc_tx = bulk_transaction(tx_from_hex(small_pc_hex), node, target_weight, self.privkeys) - pc_value = Decimal(pc_tx.vout[0].nValue) / COIN - pc_spk = pc_tx.vout[0].scriptPubKey.hex() - pc_hex = pc_tx.serialize().hex() + pc_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_utxos, fee_per_output=int(high_fee * COIN), target_weight=target_weight) # Package transaction D - (small_pd, _, val, spk) = make_chain(node, self.address, self.privkeys, pc_tx.rehash(), pc_value, 0, pc_spk, high_fee) - prevtxs = [{ - "txid": pc_tx.rehash(), - "vout": 0, - "scriptPubKey": spk, - "amount": val, - }] - pd_tx = bulk_transaction(small_pd, node, target_weight, self.privkeys, prevtxs) - pd_hex = pd_tx.serialize().hex() + pd_tx = self.wallet.create_self_transfer(utxo_to_spend=pc_tx["new_utxos"][0], target_weight=target_weight) assert_equal(2, node.getmempoolinfo()["size"]) - testres_too_heavy = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex]) + testres_too_heavy = node.testmempoolaccept(rawtxs=[pc_tx["hex"], pd_tx["hex"]]) for txres in testres_too_heavy: assert_equal(txres["package-error"], "package-mempool-limits") # Clear mempool and check that the package passes now self.generate(node, 1) - assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])]) + assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_tx["hex"], pd_tx["hex"]])]) def test_desc_size_limits(self): """Create 3 mempool transactions and 2 package transactions (25KvB each): @@ -486,50 +344,18 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): high_fee = Decimal("0.0021") # 10 sats/vB self.log.info("Check that in-mempool and in-package descendant sizes are calculated properly in packages") # Top parent in mempool, Ma - first_coin = self.coins.pop() - parent_value = (first_coin["amount"] - high_fee) / 2 # Deduct fee and make 2 outputs - inputs = [{"txid": first_coin["txid"], "vout": 0}] - outputs = [{self.address : parent_value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE: parent_value}] - rawtx = node.createrawtransaction(inputs, outputs) - parent_tx = bulk_transaction(tx_from_hex(rawtx), node, target_weight, self.privkeys) - node.sendrawtransaction(parent_tx.serialize().hex()) + ma_tx = self.wallet.create_self_transfer_multi(num_outputs=2, fee_per_output=int(high_fee / 2 * COIN), target_weight=target_weight) + self.wallet.sendrawtransaction(from_node=node, tx_hex=ma_tx["hex"]) package_hex = [] for j in range(2): # Two legs (left and right) # Mempool transaction (Mb and Mc) - mempool_tx = CTransaction() - spk = parent_tx.vout[j].scriptPubKey.hex() - value = Decimal(parent_tx.vout[j].nValue) / COIN - txid = parent_tx.rehash() - prevtxs = [{ - "txid": txid, - "vout": j, - "scriptPubKey": spk, - "amount": value, - }] - if j == 0: # normal key - (tx_small, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, j, spk, high_fee) - mempool_tx = bulk_transaction(tx_small, node, target_weight, self.privkeys, prevtxs) - else: # OP_TRUE - inputs = [{"txid": txid, "vout": 1}] - outputs = {self.address: value - high_fee} - small_tx = tx_from_hex(node.createrawtransaction(inputs, outputs)) - mempool_tx = bulk_transaction(small_tx, node, target_weight, None, prevtxs) - node.sendrawtransaction(mempool_tx.serialize().hex()) + mempool_tx = self.wallet.create_self_transfer(utxo_to_spend=ma_tx["new_utxos"][j], target_weight=target_weight) + self.wallet.sendrawtransaction(from_node=node, tx_hex=mempool_tx["hex"]) # Package transaction (Pd and Pe) - spk = mempool_tx.vout[0].scriptPubKey.hex() - value = Decimal(mempool_tx.vout[0].nValue) / COIN - txid = mempool_tx.rehash() - (tx_small, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk, high_fee) - prevtxs = [{ - "txid": txid, - "vout": 0, - "scriptPubKey": spk, - "amount": value, - }] - package_tx = bulk_transaction(tx_small, node, target_weight, self.privkeys, prevtxs) - package_hex.append(package_tx.serialize().hex()) + package_tx = self.wallet.create_self_transfer(utxo_to_spend=mempool_tx["new_utxo"], target_weight=target_weight) + package_hex.append(package_tx["hex"]) assert_equal(3, node.getmempoolinfo()["size"]) assert_equal(2, len(package_hex)) diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 2164627781..b671ecbaf2 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -7,7 +7,6 @@ from copy import deepcopy from decimal import Decimal from enum import Enum -from random import choice from typing import ( Any, List, @@ -104,6 +103,16 @@ class MiniWallet: def _create_utxo(self, *, txid, vout, value, height): return {"txid": txid, "vout": vout, "value": value, "height": height} + def _bulk_tx(self, tx, target_weight): + """Pad a transaction with extra outputs until it reaches a target weight (or higher). + returns the tx + """ + assert_greater_than_or_equal(target_weight, tx.get_weight()) + while tx.get_weight() < target_weight: + script_pubkey = ( b"6a4d0200" # OP_RETURN OP_PUSH2 512 bytes + + b"01" * 512 ) + tx.vout.append(CTxOut(0, script_pubkey)) + def get_balance(self): return sum(u['value'] for u in self._utxos) @@ -235,6 +244,7 @@ class MiniWallet: amount_per_output=0, sequence=0, fee_per_output=1000, + target_weight=0 ): """ Create and return a transaction that spends the given UTXOs and creates a @@ -265,6 +275,10 @@ class MiniWallet: outputs_value_total = inputs_value_total - fee_per_output * num_outputs for o in tx.vout: o.nValue = amount_per_output or (outputs_value_total // num_outputs) + + if target_weight: + self._bulk_tx(tx, target_weight) + txid = tx.rehash() return { "new_utxos": [self._create_utxo( @@ -278,7 +292,7 @@ class MiniWallet: "tx": tx, } - def create_self_transfer(self, *, fee_rate=Decimal("0.003"), fee=Decimal("0"), utxo_to_spend=None, locktime=0, sequence=0): + def create_self_transfer(self, *, fee_rate=Decimal("0.003"), fee=Decimal("0"), utxo_to_spend=None, locktime=0, sequence=0, target_weight=0): """Create and return a tx with the specified fee. If fee is 0, use fee_rate, where the resulting fee may be exact or at most one satoshi higher than needed.""" utxo_to_spend = utxo_to_spend or self.get_utxo() assert fee_rate >= 0 @@ -305,9 +319,13 @@ class MiniWallet: tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key] else: assert False - tx_hex = tx.serialize().hex() assert_equal(tx.get_vsize(), vsize) + + if target_weight: + self._bulk_tx(tx, target_weight) + + tx_hex = tx.serialize().hex() new_utxo = self._create_utxo(txid=tx.rehash(), vout=0, value=send_value, height=0) return {"txid": new_utxo["txid"], "wtxid": tx.getwtxid(), "hex": tx_hex, "tx": tx, "new_utxo": new_utxo} @@ -317,6 +335,17 @@ class MiniWallet: self.scan_tx(from_node.decoderawtransaction(tx_hex)) return txid + def send_self_transfer_chain(self, *, from_node, chain_length, utxo_to_spend=None): + """Create and send a "chain" of chain_length transactions. The nth transaction in + the chain is a child of the n-1th transaction and parent of the n+1th transaction. + + Returns the chaintip (nth) utxo + """ + chaintip_utxo = utxo_to_spend or self.get_utxo() + for _ in range(chain_length): + chaintip_utxo = self.send_self_transfer(utxo_to_spend=chaintip_utxo, from_node=from_node)["new_utxo"] + return chaintip_utxo + def getnewdestination(address_type='bech32m'): """Generate a random destination of the specified type and return the @@ -409,23 +438,3 @@ def create_raw_chain(node, first_coin, address, privkeys, chain_length=25): chain_txns.append(tx) return (chain_hex, chain_txns) - -def bulk_transaction(tx, node, target_weight, privkeys, prevtxs=None): - """Pad a transaction with extra outputs until it reaches a target weight (or higher). - returns CTransaction object - """ - tx_heavy = deepcopy(tx) - assert_greater_than_or_equal(target_weight, tx_heavy.get_weight()) - while tx_heavy.get_weight() < target_weight: - random_spk = "6a4d0200" # OP_RETURN OP_PUSH2 512 bytes - for _ in range(512*2): - random_spk += choice("0123456789ABCDEF") - tx_heavy.vout.append(CTxOut(0, bytes.fromhex(random_spk))) - # Re-sign the transaction - if privkeys: - signed = node.signrawtransactionwithkey(tx_heavy.serialize().hex(), privkeys, prevtxs) - return tx_from_hex(signed["hex"]) - # OP_TRUE - tx_heavy.wit.vtxinwit = [CTxInWitness()] - tx_heavy.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] - return tx_heavy |