Repository: bitcoin/bitcoin
File: test/functional/mempool_accept_wtxid.py (Python)

Code before:
from test_framework.script_util import build_malleated_tx_package
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
    assert_not_equal,
    assert_equal,
)
from test_framework.wallet import (
    MiniWallet,
)

class MempoolWtxidTest(BitcoinTestFramework):
    def set_test_params(self):
        self.num_nodes = 1

    def run_test(self):
        node = self.nodes[0]
        mini_wallet = MiniWallet(node)
        self.log.info('Start with pre-generated blocks')

        assert_equal(node.getmempoolinfo()['size'], 0)

        self.log.info("Submit parent with multiple script branches to mempool")

        parent = mini_wallet.create_self_transfer()["tx"]
        parent_amount = parent.vout[0].nValue - 10000
        child_amount = parent_amount - 10000
        parent, child_one, child_two = build_malleated_tx_package(
            parent=parent,
            rebalance_parent_output_amount=parent_amount,
            child_amount=child_amount
        )

        mini_wallet.sendrawtransaction(from_node=node, tx_hex=parent.serialize().hex())

        self.generate(node, 1)

        peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore())
        child_one_wtxid = child_one.wtxid_hex
        child_one_txid = child_one.txid_hex
        child_two_wtxid = child_two.wtxid_hex
        child_two_txid = child_two.txid_hex

        assert_equal(child_one_txid, child_two_txid)
        assert_not_equal(child_one_wtxid, child_two_wtxid)

        self.log.info("Submit child_one to the mempool")
        txid_submitted = node.sendrawtransaction(child_one.serialize().hex())
        assert_equal(node.getmempoolentry(txid_submitted)['wtxid'], child_one_wtxid)
        peer_wtxid_relay.wait_for_broadcast([child_one_wtxid])
        assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)


Review comment (suggestion):
I believe it's unlikely this'd be flaky but it's quite odd to calculate the parent amount based on the first output that ultimately gets subtracted from the last output in case there are multiple outputs in the transaction. I'd prefer to only use the last output for these calculations.

Also, the `C = A - B` phrase reads fine for the child amount because the reader can realise that `B` is going away in fees when `A` is spent, but using the same phrase for calculating the parent amount that's only going to be rebalanced can be confusing for the reader - can just split the last output amount into two.

```diff
diff --git a/test/functional/mempool_accept_wtxid.py b/test/functional/mempool_accept_wtxid.py
index a9edea6d64..c44b6b0544 100755
--- a/test/functional/mempool_accept_wtxid.py
+++ b/test/functional/mempool_accept_wtxid.py
@@ -32,8 +32,8 @@ class MempoolWtxidTest(BitcoinTestFramework):
         self.log.info("Submit parent with multiple script branches to mempool")
 
         parent = mini_wallet.create_self_transfer()["tx"]
-        parent_amount = parent.vout[0].nValue - 10000
-        child_amount = parent_amount - 10000
+        parent_amount = int(parent.vout[len(parent.vout) - 1].nValue / 2)
+        child_amount = parent_amount - 10000 # less fees
         parent, child_one, child_two = build_malleated_tx_package(
             parent=parent,
             rebalance_parent_output_amount=parent_amount,

Code after:
from test_framework.script_util import build_malleated_tx_package
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
    assert_not_equal,
    assert_equal,
)
from test_framework.wallet import (
    MiniWallet,
)

class MempoolWtxidTest(BitcoinTestFramework):
    def set_test_params(self):
        self.num_nodes = 1

    def run_test(self):
        node = self.nodes[0]
        mini_wallet = MiniWallet(node)
        self.log.info('Start with pre-generated blocks')

        assert_equal(node.getmempoolinfo()['size'], 0)

        self.log.info("Submit parent with multiple script branches to mempool")

        parent = mini_wallet.create_self_transfer()["tx"]
        parent_amount = parent.vout[0].nValue - 10000
        child_amount = parent_amount - 10000
        parent, child_one, child_two = build_malleated_tx_package(
            parent=parent,
            rebalance_parent_output_amount=parent_amount,
            child_amount=child_amount
        )

        mini_wallet.sendrawtransaction(from_node=node, tx_hex=parent.serialize().hex())

        self.generate(node, 1)

        peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore())

        assert_equal(child_one.txid_hex, child_two.txid_hex)
        assert_not_equal(child_one.wtxid_hex, child_two.wtxid_hex)

        self.log.info("Submit child_one to the mempool")
        txid_submitted = node.sendrawtransaction(child_one.serialize().hex())
        assert_equal(node.getmempoolentry(txid_submitted)['wtxid'], child_one.wtxid_hex)
        peer_wtxid_relay.wait_for_broadcast([child_one.wtxid_hex])
        assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)

        # testmempoolaccept reports the "already in mempool" error
        assert_equal(node.testmempoolaccept([child_one.serialize().hex()]), [{
            "txid": child_one.txid_hex,
            "wtxid": child_one.wtxid_hex,