Ok, I have gone through both the linked comments now.
The main issue that I see in the current implementation is that both the MiniWallet and the node are required to be passed in this function, which unnecessarily ties the two of them together in an utility function that only needs to manipulate & create the related transaction.
From the #32385 (comment):
one has to create a new dedicated parent transaction, get it in the mempool and then broadcast the two children. Also, one has to handle the fees manually.
The diff that I suggested earlier helps in avoiding creating a dedicated parent transaction within the utility function, and even doesn’t broadcast to the mempool. The following diff below, slightly altered from the previous implementation, doesn’t even accept a fee and let the caller handle. Although the final diff suggested in the 32385 linked comment suggests a completely different approach that malleates only one transaction instead of creating a transaction package - if that approach is preferred, I’m fine with that as well.
IMO the diff above doesnt ease the burden of the caller
I don’t think it’s a burden for the caller but rather provides flexibility to the caller to create a starting parent transaction and broadcast it whenever it wishes.
0diff --git a/test/functional/mempool_accept_wtxid.py b/test/functional/mempool_accept_wtxid.py
1index a49a886d3a..179ddb0d38 100755
2--- a/test/functional/mempool_accept_wtxid.py
3+++ b/test/functional/mempool_accept_wtxid.py
4@@ -11,7 +11,7 @@ from test_framework.messages import (
5 COIN,
6 )
7 from test_framework.p2p import P2PTxInvStore
8-from test_framework.script_util import valid_witness_malleate_tx
9+from test_framework.script_util import build_malleated_tx_package
10 from test_framework.test_framework import BitcoinTestFramework
11 from test_framework.util import (
12 assert_not_equal,
13@@ -34,9 +34,10 @@ class MempoolWtxidTest(BitcoinTestFramework):
14 assert_equal(node.getmempoolinfo()['size'], 0)
15
16 self.log.info("Submit parent with multiple script branches to mempool")
17- parent_amount = int(9.99998 * COIN)
18- child_amount = int(9.99996 * COIN)
19- _, child_one, child_two = valid_witness_malleate_tx(mini_wallet, node, parent_amount, child_amount)
20+ parent, child_one, child_two = build_malleated_tx_package(parent=mini_wallet.create_self_transfer()["tx"],
21+ rebalance_parent_output_amount=int(9.99998 * COIN),
22+ malleated_child_amount=int(9.99996 * COIN))
23+ mini_wallet.sendrawtransaction(from_node=node, tx_hex=parent.serialize().hex())
24
25 self.generate(node, 1)
26
27diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py
28index f3b3c803ed..5ab97d6753 100755
29--- a/test/functional/test_framework/script_util.py
30+++ b/test/functional/test_framework/script_util.py
31@@ -6,6 +6,9 @@
32 import unittest
33
34 from copy import deepcopy
35+from test_framework.util import (
36+ assert_greater_than_or_equal,
37+)
38
39 from test_framework.messages import (
40 COutPoint,
41@@ -150,19 +153,23 @@ def check_script(script):
42 assert False
43
44
45-def valid_witness_malleate_tx(mini_wallet, node, parent_amount, child_amount):
46+def build_malleated_tx_package(*, parent: CTransaction, rebalance_parent_output_amount, malleated_child_amount):
47 """
48- Creates a valid witness malleation transaction:
49- - Parent transaction with a script supporting 2 branches
50- - 2 child transactions with the same txid but different wtxids
51+ Returns a transaction package with valid witness:
52+ - Parent transaction whose last output contains a script that has two spending conditions
53+ - Two malleated child transactions with same txid but different wtxids because of different witnesses
54 """
55 hashlock = hash160(b'Preimage')
56 witness_script = CScript([OP_IF, OP_HASH160, hashlock, OP_EQUAL, OP_ELSE, OP_TRUE, OP_ENDIF])
57 witness_program = sha256(witness_script)
58 script_pubkey = CScript([OP_0, witness_program])
59
60- # Create parent transaction with a script supporting 2 branches
61- parent = mini_wallet.send_to(from_node=node, scriptPubKey=script_pubkey, amount=parent_amount, fee=1000)
62+ # Append to the transaction the vout containing the script supporting two spending conditions
63+ assert_greater_than_or_equal(len(parent.vout), 1)
64+ last_output = parent.vout[len(parent.vout) - 1]
65+ assert_greater_than_or_equal(last_output.nValue, rebalance_parent_output_amount)
66+ last_output.nValue -= rebalance_parent_output_amount
67+ parent.vout.append(CTxOut(rebalance_parent_output_amount, script_pubkey))
68
69 # Create 2 valid children that differ only in witness data.
70 # 1. Create a new transaction with witness solving first branch
71@@ -171,13 +178,15 @@ def valid_witness_malleate_tx(mini_wallet, node, parent_amount, child_amount):
72 child_script_pubkey = CScript([OP_0, child_witness_program])
73 child_one = CTransaction()
74
75- child_one.vin.append(CTxIn(COutPoint(int(parent['txid'], 16), parent['sent_vout']), b""))
76- child_one.vout.append(CTxOut(child_amount, child_script_pubkey))
77+ child_one.vin.append(CTxIn(COutPoint(int(parent.txid_hex, 16), len(parent.vout) - 1), b""))
78+ child_one.vout.append(CTxOut(malleated_child_amount, child_script_pubkey))
79 child_one.wit.vtxinwit.append(CTxInWitness())
80 child_one.wit.vtxinwit[0].scriptWitness.stack = [b'Preimage', b'\x01', witness_script]
81+
82 # 2. Create another identical transaction with witness solving second branch
83 child_two = deepcopy(child_one)
84 child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', witness_script]
85+
86 return parent, child_one, child_two
Having said this, if other reviewers prefer the current approach in this PR, I don’t see a strong reason to block (n-a-c-k) the PR based on this but I don’t find the function generic enough to treat it as a utility function.