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

Code before:
        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"])

        self.nodes[0].reconsiderblock(reconsider_block_res["hash"])
        assert_equal(self.nodes[0].getrawmempool(), [])

        self.log.info("Test that ephemeral dust tx with fees or multi dust don't enter mempool via reorg")
        multi_dusty_tx, _ = self.create_ephemeral_dust_package(tx_version=3, num_dust_outputs=2)
        block_res = self.generateblock(self.nodes[0], self.wallet.get_address(), [multi_dusty_tx["hex"]], sync_fun=self.no_op)

Review comment (suggestion):
if fork length is always 10 because it's sufficient and easy, just make a constant like `FORK_LENGTH` with a quick comment to describe what it's for

Code after:
        # Dust tx is not mined
        self.generate(self.nodes[0], 1)
        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

        # Prep for fork with empty blocks
        fork_blocks = create_empty_fork(self.nodes[0])

        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"])

        self.generateblock(self.nodes[0], self.wallet.get_address(), [dusty_tx["hex"]], sync_fun=self.no_op)
        self.trigger_reorg(fork_blocks)
        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"])

        # Prep for fork with empty blocks
        fork_blocks = create_empty_fork(self.nodes[0])

        # Mine the sweep then re-org, the sweep will not make it back in due to spend checks
        self.generateblock(self.nodes[0], self.wallet.get_address(), [dusty_tx["hex"], sweep_tx["hex"]], sync_fun=self.no_op)
        self.trigger_reorg(fork_blocks)
        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)