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

Code before:
    assert original_txid not in rbf_node.getrawmempool()

    rbf_node.unloadwallet("watcher")
    rbf_node.unloadwallet("signer")
    self.clear_mempool()


def test_rebumping(self, rbf_node, dest_address):
    self.log.info('Test that re-bumping the original tx fails, but bumping successor works')
    rbfid = spend_one_input(rbf_node, dest_address)
    bumped = rbf_node.bumpfee(rbfid, fee_rate=ECONOMICAL)
    assert_raises_rpc_error(-4, f"Cannot bump transaction {rbfid} which was already bumped by transaction {bumped['txid']}",
                            rbf_node.bumpfee, rbfid, fee_rate=NORMAL)
    rbf_node.bumpfee(bumped["txid"], fee_rate=NORMAL)
    self.clear_mempool()


def test_rebumping_not_replaceable(self, rbf_node, dest_address):
    self.log.info('Test that re-bumping non-replaceable fails')
    rbfid = spend_one_input(rbf_node, dest_address)
    bumped = rbf_node.bumpfee(rbfid, fee_rate=ECONOMICAL, replaceable=False)
    assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
                            {"fee_rate": NORMAL})
    self.clear_mempool()


def test_bumpfee_already_spent(self, rbf_node, dest_address):
    self.log.info('Test that bumping tx with already spent coin fails')
    txid = spend_one_input(rbf_node, dest_address)
    self.generate(rbf_node, 1)  # spend coin simply by mining block with tx
    spent_input = rbf_node.gettransaction(txid=txid, verbose=True)['decoded']['vin'][0]
    assert_raises_rpc_error(-1, f"{spent_input['txid']}:{spent_input['vout']} is already spent",
                            rbf_node.bumpfee, txid, fee_rate=NORMAL)


def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address):
    self.log.info('Test that unconfirmed outputs from bumped txns are not spendable')
    rbfid = spend_one_input(rbf_node, rbf_node_address)
    rbftx = rbf_node.gettransaction(rbfid)["hex"]
    assert rbfid in rbf_node.getrawmempool()
    bumpid = rbf_node.bumpfee(rbfid)["txid"]
    assert bumpid in rbf_node.getrawmempool()
    assert rbfid not in rbf_node.getrawmempool()

    # check that outputs from the bump transaction are not spendable
    # due to the replaces_txid check in CWallet::AvailableCoins
    assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == bumpid], [])

    # submit a block with the rbf tx to clear the bump tx out of the mempool,
    # then invalidate the block so the rbf tx will be put back in the mempool.
    # This makes it possible to check whether the rbf tx outputs are

Review comment (suggestion):
Not a fan of looping here and explicitly creating an array of just 1 element `[seq_in]` only for assertion because the test explicitly spends 1 input, but okay I guess.

Code after:
    assert original_txid not in rbf_node.getrawmempool()

    rbf_node.unloadwallet("watcher")
    rbf_node.unloadwallet("signer")
    self.clear_mempool()


def test_rebumping(self, rbf_node, dest_address):
    self.log.info('Test that re-bumping the original tx fails, but bumping successor works')
    rbfid = spend_one_input(rbf_node, dest_address)
    bumped = rbf_node.bumpfee(rbfid, fee_rate=ECONOMICAL)
    assert_raises_rpc_error(-4, f"Cannot bump transaction {rbfid} which was already bumped by transaction {bumped['txid']}",
                            rbf_node.bumpfee, rbfid, fee_rate=NORMAL)
    rbf_node.bumpfee(bumped["txid"], fee_rate=NORMAL)
    self.clear_mempool()


def test_rebumping_not_replaceable(self, rbf_node, dest_address):
    self.log.info("Test that re-bumping non-replaceable passes")
    rbfid = spend_one_input(rbf_node, dest_address)

    def check_sequence(tx, seq_in):
        tx = rbf_node.getrawtransaction(tx["txid"])
        tx = rbf_node.decoderawtransaction(tx)
        seq = [i["sequence"] for i in tx["vin"]]
        assert_equal(seq, [seq_in])

    bumped = rbf_node.bumpfee(rbfid, fee_rate=ECONOMICAL, replaceable=False)
    check_sequence(bumped, MAX_SEQUENCE_NONFINAL)
    bumped = rbf_node.bumpfee(bumped["txid"], {"fee_rate": NORMAL})
    check_sequence(bumped, MAX_BIP125_RBF_SEQUENCE)

    self.clear_mempool()


def test_bumpfee_already_spent(self, rbf_node, dest_address):
    self.log.info('Test that bumping tx with already spent coin fails')
    txid = spend_one_input(rbf_node, dest_address)
    self.generate(rbf_node, 1)  # spend coin simply by mining block with tx
    spent_input = rbf_node.gettransaction(txid=txid, verbose=True)['decoded']['vin'][0]
    assert_raises_rpc_error(-1, f"{spent_input['txid']}:{spent_input['vout']} is already spent",
                            rbf_node.bumpfee, txid, fee_rate=NORMAL)


def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address):
    self.log.info('Test that unconfirmed outputs from bumped txns are not spendable')
    rbfid = spend_one_input(rbf_node, rbf_node_address)
    rbftx = rbf_node.gettransaction(rbfid)["hex"]
    assert rbfid in rbf_node.getrawmempool()
    bumpid = rbf_node.bumpfee(rbfid)["txid"]
    assert bumpid in rbf_node.getrawmempool()