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

Code before:
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"]])

        # Create sweep that doesn't spend conflicting sponsor coin
        sweep_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=dusty_tx["new_utxos"], version=3)

        # Can resweep
        self.nodes[0].sendrawtransaction(sweep_tx["hex"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"], sweep_tx["tx"]])

        self.generate(self.nodes[0], 1)
        assert_mempool_contents(self, self.nodes[0], expected=[])

    def test_reorgs(self):
        self.log.info("Test that reorgs breaking the truc topology doesn't cause issues")

        assert_equal(self.nodes[0].getrawmempool(), [])

        # Many shallow re-orgs confuse block gossiping making test less reliable otherwise
        self.disconnect_nodes(0, 1)

        # Get dusty tx mined, then check that it makes it back into mempool on reorg
        # due to bypass_limits allowing 0-fee individually
        dusty_tx, _ = self.create_ephemeral_dust_package(tx_version=3)
        assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, dusty_tx["hex"])

        block_res = self.nodes[0].rpc.generateblock(self.wallet.get_address(), [dusty_tx["hex"]])
        self.nodes[0].invalidateblock(block_res["hash"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"]], sync=False)

        # Create a sweep that has dust of its own and leaves dusty_tx's dust unspent
        sweep_tx = self.wallet.create_self_transfer_multi(fee_per_output=0, utxos_to_spend=[dusty_tx["new_utxos"][0]], version=3)
        self.add_output_to_create_multi_result(sweep_tx)
        assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, sweep_tx["hex"])

        # Mine the sweep then re-org, the sweep will not make it back in due to spend checks
        block_res = self.nodes[0].rpc.generateblock(self.wallet.get_address(), [dusty_tx["hex"], sweep_tx["hex"]])
        self.nodes[0].invalidateblock(block_res["hash"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"]], sync=False)

        # Should re-enter if dust is swept
        sweep_tx_2 = self.wallet.create_self_transfer_multi(fee_per_output=0, utxos_to_spend=dusty_tx["new_utxos"], version=3)
        self.add_output_to_create_multi_result(sweep_tx_2)
        assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, sweep_tx_2["hex"])

        reconsider_block_res = self.nodes[0].rpc.generateblock(self.wallet.get_address(), [dusty_tx["hex"], sweep_tx_2["hex"]])
        self.nodes[0].invalidateblock(reconsider_block_res["hash"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"], sweep_tx_2["tx"]], sync=False)

        # TRUC transactions restriction for ephemeral dust disallows further spends of ancestor chains
        child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=sweep_tx_2["new_utxos"], version=3)
        assert_raises_rpc_error(-26, "TRUC-violation", self.nodes[0].sendrawtransaction, child_tx["hex"])

Review comment (suggestion):
```
sync_fun=self.no_op
```

Note for myself and other reviewers: Syncing is not done in this test because the nodes are disconnected on line 321 and hence, syncing all the nodes would result in assertion errors.

```
  File "/Users/rkrux/projects/bitcoin/test/functional/test_framework/test_framework.py", line 769, in sync_blocks
    assert (all([len(x.getpeerinfo()) for x in rpc_connections]))
            ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
```

Code after:
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"]])

        # Create sweep that doesn't spend conflicting sponsor coin
        sweep_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=dusty_tx["new_utxos"], version=3)

        # Can resweep
        self.nodes[0].sendrawtransaction(sweep_tx["hex"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"], sweep_tx["tx"]])

        self.generate(self.nodes[0], 1)
        assert_mempool_contents(self, self.nodes[0], expected=[])

    def test_reorgs(self):
        self.log.info("Test that reorgs breaking the truc topology doesn't cause issues")

        assert_equal(self.nodes[0].getrawmempool(), [])

        # Many shallow re-orgs confuse block gossiping making test less reliable otherwise
        self.disconnect_nodes(0, 1)

        # Get dusty tx mined, then check that it makes it back into mempool on reorg
        # due to bypass_limits allowing 0-fee individually
        dusty_tx, _ = self.create_ephemeral_dust_package(tx_version=3)
        assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, dusty_tx["hex"])

        block_res = self.generateblock(self.nodes[0], self.wallet.get_address(), [dusty_tx["hex"]], sync_fun=self.no_op)
        self.nodes[0].invalidateblock(block_res["hash"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"]], sync=False)

        # Create a sweep that has dust of its own and leaves dusty_tx's dust unspent
        sweep_tx = self.wallet.create_self_transfer_multi(fee_per_output=0, utxos_to_spend=[dusty_tx["new_utxos"][0]], version=3)
        self.add_output_to_create_multi_result(sweep_tx)
        assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, sweep_tx["hex"])

        # Mine the sweep then re-org, the sweep will not make it back in due to spend checks
        block_res = self.generateblock(self.nodes[0], self.wallet.get_address(), [dusty_tx["hex"], sweep_tx["hex"]], sync_fun=self.no_op)
        self.nodes[0].invalidateblock(block_res["hash"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"]], sync=False)

        # Should re-enter if dust is swept
        sweep_tx_2 = self.wallet.create_self_transfer_multi(fee_per_output=0, utxos_to_spend=dusty_tx["new_utxos"], version=3)
        self.add_output_to_create_multi_result(sweep_tx_2)
        assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, sweep_tx_2["hex"])

        reconsider_block_res = self.generateblock(self.nodes[0], self.wallet.get_address(), [dusty_tx["hex"], sweep_tx_2["hex"]], sync_fun=self.no_op)
        self.nodes[0].invalidateblock(reconsider_block_res["hash"])
        assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"], sweep_tx_2["tx"]], sync=False)

        # TRUC transactions restriction for ephemeral dust disallows further spends of ancestor chains
        child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=sweep_tx_2["new_utxos"], version=3)
        assert_raises_rpc_error(-26, "TRUC-violation", self.nodes[0].sendrawtransaction, child_tx["hex"])