aboutsummaryrefslogtreecommitdiff
path: root/test/functional/wallet_bumpfee.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/wallet_bumpfee.py')
-rwxr-xr-xtest/functional/wallet_bumpfee.py88
1 files changed, 78 insertions, 10 deletions
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index fc752e5ac0..568b1f28d8 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -19,7 +19,13 @@ import io
from test_framework.blocktools import add_witness_commitment, create_block, create_coinbase, send_to_witness
from test_framework.messages import BIP125_SEQUENCE_NUMBER, CTransaction
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, connect_nodes_bi, hex_str_to_bytes, sync_mempools
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+ hex_str_to_bytes,
+)
WALLET_PASSPHRASE = "test"
WALLET_PASSPHRASE_TIMEOUT = 3600
@@ -60,7 +66,7 @@ class BumpFeeTest(BitcoinTestFramework):
self.log.info("Running tests")
dest_address = peer_node.getnewaddress()
- test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address)
+ test_simple_bumpfee_succeeds(self, rbf_node, peer_node, dest_address)
test_segwit_bumpfee_succeeds(rbf_node, dest_address)
test_nonrbf_bumpfee_fails(peer_node, dest_address)
test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address)
@@ -73,19 +79,23 @@ class BumpFeeTest(BitcoinTestFramework):
test_unconfirmed_not_spendable(rbf_node, rbf_node_address)
test_bumpfee_metadata(rbf_node, dest_address)
test_locked_wallet_fails(rbf_node, dest_address)
+ test_change_script_match(rbf_node, dest_address)
+ # These tests wipe out a number of utxos that are expected in other tests
+ test_small_output_with_feerate_succeeds(rbf_node, dest_address)
+ test_no_more_inputs_fails(rbf_node, dest_address)
self.log.info("Success")
-def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address):
+def test_simple_bumpfee_succeeds(self, rbf_node, peer_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
rbftx = rbf_node.gettransaction(rbfid)
- sync_mempools((rbf_node, peer_node))
+ self.sync_mempools((rbf_node, peer_node))
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
bumped_tx = rbf_node.bumpfee(rbfid)
assert_equal(bumped_tx["errors"], [])
assert bumped_tx["fee"] - abs(rbftx["fee"]) > 0
# check that bumped_tx propagates, original tx was evicted and has a wallet conflict
- sync_mempools((rbf_node, peer_node))
+ self.sync_mempools((rbf_node, peer_node))
assert bumped_tx["txid"] in rbf_node.getrawmempool()
assert bumped_tx["txid"] in peer_node.getrawmempool()
assert rbfid not in rbf_node.getrawmempool()
@@ -173,6 +183,40 @@ def test_small_output_fails(rbf_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
+def test_small_output_with_feerate_succeeds(rbf_node, dest_address):
+
+ # Make sure additional inputs exist
+ rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
+ rbfid = spend_one_input(rbf_node, dest_address)
+ original_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
+ assert_equal(len(original_input_list), 1)
+ original_txin = original_input_list[0]
+ # Keep bumping until we out-spend change output
+ tx_fee = 0
+ while tx_fee < Decimal("0.0005"):
+ new_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
+ new_item = list(new_input_list)[0]
+ assert_equal(len(original_input_list), 1)
+ assert_equal(original_txin["txid"], new_item["txid"])
+ assert_equal(original_txin["vout"], new_item["vout"])
+ rbfid_new_details = rbf_node.bumpfee(rbfid)
+ rbfid_new = rbfid_new_details["txid"]
+ raw_pool = rbf_node.getrawmempool()
+ assert rbfid not in raw_pool
+ assert rbfid_new in raw_pool
+ rbfid = rbfid_new
+ tx_fee = rbfid_new_details["origfee"]
+
+ # input(s) have been added
+ final_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
+ assert_greater_than(len(final_input_list), 1)
+ # Original input is in final set
+ assert [txin for txin in final_input_list
+ if txin["txid"] == original_txin["txid"]
+ and txin["vout"] == original_txin["vout"]]
+
+ rbf_node.generatetoaddress(1, rbf_node.getnewaddress())
+ assert_equal(rbf_node.gettransaction(rbfid)["confirmations"], 1)
def test_dust_to_fee(rbf_node, dest_address):
# check that if output is reduced to dust, it will be converted to fee
@@ -272,19 +316,37 @@ def test_locked_wallet_fails(rbf_node, dest_address):
rbf_node.walletlock()
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
rbf_node.bumpfee, rbfid)
+ rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+
+def test_change_script_match(rbf_node, dest_address):
+ """Test that the same change addresses is used for the replacement transaction when possible."""
+ def get_change_address(tx):
+ tx_details = rbf_node.getrawtransaction(tx, 1)
+ txout_addresses = [txout['scriptPubKey']['addresses'][0] for txout in tx_details["vout"]]
+ return [address for address in txout_addresses if rbf_node.getaddressinfo(address)["ischange"]]
+ # Check that there is only one change output
+ rbfid = spend_one_input(rbf_node, dest_address)
+ change_addresses = get_change_address(rbfid)
+ assert_equal(len(change_addresses), 1)
+
+ # Now find that address in each subsequent tx, and no other change
+ bumped_total_tx = rbf_node.bumpfee(rbfid, {"totalFee": 2000})
+ assert_equal(change_addresses, get_change_address(bumped_total_tx['txid']))
+ bumped_rate_tx = rbf_node.bumpfee(bumped_total_tx["txid"])
+ assert_equal(change_addresses, get_change_address(bumped_rate_tx['txid']))
-def spend_one_input(node, dest_address):
+def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")):
tx_input = dict(
sequence=BIP125_SEQUENCE_NUMBER, **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000")))
- rawtx = node.createrawtransaction(
- [tx_input], {dest_address: Decimal("0.00050000"),
- node.getrawchangeaddress(): Decimal("0.00049000")})
+ destinations = {dest_address: Decimal("0.00050000")}
+ if change_size > 0:
+ destinations[node.getrawchangeaddress()] = change_size
+ rawtx = node.createrawtransaction([tx_input], destinations)
signedtx = node.signrawtransactionwithwallet(rawtx)
txid = node.sendrawtransaction(signedtx["hex"])
return txid
-
def submit_block_with_tx(node, tx):
ctx = CTransaction()
ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx)))
@@ -301,6 +363,12 @@ def submit_block_with_tx(node, tx):
node.submitblock(block.serialize(True).hex())
return block
+def test_no_more_inputs_fails(rbf_node, dest_address):
+ # feerate rbf requires confirmed outputs when change output doesn't exist or is insufficient
+ rbf_node.generatetoaddress(1, dest_address)
+ # spend all funds, no change output
+ rbfid = rbf_node.sendtoaddress(rbf_node.getnewaddress(), rbf_node.getbalance(), "", "", True)
+ assert_raises_rpc_error(-4, "Unable to create transaction: Insufficient funds", rbf_node.bumpfee, rbfid)
if __name__ == "__main__":
BumpFeeTest().main()