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

Code before:

        # Check listunspent before reorg - should have the truc output
        unspent_before = self.alice.listunspent()
        truc_output_txids = [u["txid"] for u in unspent_before]
        assert truc_txid in truc_output_txids

        # Trigger the reorg
        self.trigger_reorg(fork_blocks)

        # After reorg, both transactions should be back in mempool
        mempool = self.nodes[0].getrawmempool()
        assert v2_txid in mempool
        assert truc_txid in mempool

        # Check listunspent after reorg - the truc output should still appear
        # as unconfirmed since the transaction is in the mempool
        unspent_after = self.alice.listunspent(minconf=0)
        unspent_txids_after = [u["txid"] for u in unspent_after]
        assert truc_txid in unspent_txids_after

        # Now try to create a v2 transaction - this triggers AvailableCoins with
        # check_version_trucness=true. The v2 parent has truc_child_in_mempool set
        # because the truc child is in mempool.
        new_v2_outputs = {self.bob.getnewaddress(): 0.1}
        raw_tx = self.alice.createrawtransaction(inputs=[], outputs=new_v2_outputs, version=2)
        self.alice.fundrawtransaction(raw_tx, {'include_unsafe': True})

if __name__ == '__main__':
    WalletV3Test(__file__).main()


Review comment (suggestion):
Might be worth testing that you can also create + broadcast this transaction. But this doesn't mean all TRUC rules are out the window:

```diff
diff --git a/test/functional/wallet_v3_txs.py b/test/functional/wallet_v3_txs.py
index 85865c3e54f..4728416099b 100755
--- a/test/functional/wallet_v3_txs.py
+++ b/test/functional/wallet_v3_txs.py
@@ -676,8 +676,15 @@ class WalletV3Test(BitcoinTestFramework):
         self.generate(self.nodes[0], 1)
 
         # Verify both transactions are confirmed
-        assert_equal(self.alice.gettransaction(v2_txid)["confirmations"], 2)
-        assert_equal(self.alice.gettransaction(truc_txid)["confirmations"], 1)
+        wallet_tx_v2 = self.alice.gettransaction(v2_txid)
+        wallet_tx_truc = self.alice.gettransaction(truc_txid)
+
+        assert_equal(wallet_tx_v2["confirmations"], 2)
+        assert_equal(wallet_tx_truc["confirmations"], 1)
+
+        # Check that their versions are correct
+        assert_equal(self.alice.decoderawtransaction(wallet_tx_v2["hex"])["version"], 2)
+        assert_equal(self.alice.decoderawtransaction(wallet_tx_truc["hex"])["version"], 3)
 
         # Check listunspent before reorg - should have the truc output
         unspent_before = self.alice.listunspent()
@@ -686,6 +693,8 @@ class WalletV3Test(BitcoinTestFramework):
 
         # Trigger the reorg
         self.trigger_reorg(fork_blocks)
+        # The TRUC transaction is now in a cluster of size 3, which is only permitted in a reorg.
+        assert_equal(self.nodes[0].getmempoolcluster(truc_txid)["txcount"], 3)
 
         # After reorg, both transactions should be back in mempool
         mempool = self.nodes[0].getrawmempool()
@@ -698,12 +707,34 @@ class WalletV3Test(BitcoinTestFramework):
         unspent_txids_after = [u["txid"] for u in unspent_after]
         assert truc_txid in unspent_txids_after
 
+        total_unconfirmed_amount = sum([u["amount"] for u in self.alice.listunspent(minconf=0)])
+        # We cannot create a transaction spending both outputs, regardless of version.
+        output_too_high = {self.bob.getnewaddress(): total_unconfirmed_amount - Decimal("0.001")}
+        for version in [2, 3]:
+            raw_output_too_high = self.alice.createrawtransaction(inputs=[], outputs=output_too_high, version=version)
+            assert_raises_rpc_error(
+                    -4,
+                    "Insufficient funds",
+                    self.alice.fundrawtransaction,
+                    raw_output_too_high,
+                    {'include_unsafe' : True}
+            )
+
         # Now try to create a v2 transaction - this triggers AvailableCoins with
         # check_version_trucness=true. The v2 parent has truc_child_in_mempool set
         # because the truc child is in mempool.
         new_v2_outputs = {self.bob.getnewaddress(): 0.1}
-        raw_tx = self.alice.createrawtransaction(inputs=[], outputs=new_v2_outputs, version=2)
-        self.alice.fundrawtransaction(raw_tx, {'include_unsafe': True})
+        raw_v2_child = self.alice.createrawtransaction(inputs=[], outputs=new_v2_outputs, version=2)
+        raw_v2_with_v3_sibling = self.alice.fundrawtransaction(raw_v2_child, {'include_unsafe': True})
+
+        # See that this transaction can be added to mempool
+        signed_raw_v2_with_v3_sibling = self.alice.signrawtransactionwithwallet(raw_v2_with_v3_sibling["hex"])
+        self.alice.sendrawtransaction(signed_raw_v2_with_v3_sibling["hex"])
+
+        # This TRUC transaction is in a cluster of size 4
+        assert_equal(self.nodes[0].getmempoolcluster(truc_txid)["txcount"], 4)
+
+
 
 if __name__ == '__main__':
     WalletV3Test(__file__).main()

```

Code after:

        assert_equal(wallet_tx_v2["confirmations"], 2)
        assert_equal(wallet_tx_truc["confirmations"], 1)

        # Check that their versions are correct
        assert_equal(self.alice.decoderawtransaction(wallet_tx_v2["hex"])["version"], 2)
        assert_equal(self.alice.decoderawtransaction(wallet_tx_truc["hex"])["version"], 3)

        # Check listunspent before reorg - should have the truc output
        unspent_before = self.alice.listunspent()
        truc_output_txids = [u["txid"] for u in unspent_before]
        assert truc_txid in truc_output_txids

        # Trigger the reorg
        self.trigger_reorg(fork_blocks)
        # The TRUC transaction is now in a cluster of size 3, which is only permitted in a reorg.
        assert_equal(self.nodes[0].getmempoolcluster(truc_txid)["txcount"], 3)

        # After reorg, both transactions should be back in mempool
        mempool = self.nodes[0].getrawmempool()
        assert v2_txid in mempool
        assert truc_txid in mempool

        # Check listunspent after reorg - the truc output should still appear
        # as unconfirmed since the transaction is in the mempool
        unspent_after = self.alice.listunspent(minconf=0)
        unspent_txids_after = [u["txid"] for u in unspent_after]
        assert truc_txid in unspent_txids_after

        total_unconfirmed_amount = sum([u["amount"] for u in self.alice.listunspent(minconf=0)])
        # We cannot create a transaction spending both outputs, regardless of version.
        output_too_high = {self.bob.getnewaddress(): total_unconfirmed_amount - Decimal("1")}
        for version in [2, 3]:
            raw_output_too_high = self.alice.createrawtransaction(inputs=[], outputs=output_too_high, version=version)
            assert_raises_rpc_error(
                    -4,
                    "Insufficient funds",
                    self.alice.fundrawtransaction,
                    raw_output_too_high,
                    {'include_unsafe' : True}
            )

        # Now try to create a v2 transaction - this triggers AvailableCoins with
        # check_version_trucness=true. The v2 parent has truc_child_in_mempool set
        # because the truc child is in mempool.
        new_v2_outputs = {self.bob.getnewaddress(): 0.1}
        raw_v2_child = self.alice.createrawtransaction(inputs=[], outputs=new_v2_outputs, version=2)
        raw_v2_with_v3_sibling = self.alice.fundrawtransaction(raw_v2_child, {'include_unsafe': True})

        # See that this transaction can be added to mempool
        signed_raw_v2_with_v3_sibling = self.alice.signrawtransactionwithwallet(raw_v2_with_v3_sibling["hex"])