2016-12-09 15:45:27 -03:00
#!/usr/bin/env python3
2020-04-16 13:14:08 -04:00
# Copyright (c) 2016-2020 The Bitcoin Core developers
2016-12-09 15:45:27 -03:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-03-20 18:17:27 -03:00
""" Test the bumpfee RPC.
Verifies that the bumpfee RPC creates replacement transactions successfully when
its preconditions are met , and returns appropriate errors in other cases .
This module consists of around a dozen individual test cases implemented in the
top - level functions named as test_ < test_case_description > . The test functions
can be disabled or reordered if needed for debugging . If new test cases are
2017-06-19 18:57:31 -04:00
added in the future , they should try to follow the same convention and not
2017-03-20 18:17:27 -03:00
make assumptions about execution order .
"""
2018-07-06 18:10:35 -04:00
from decimal import Decimal
2018-09-23 09:50:47 -03:00
import io
2018-07-06 18:10:35 -04:00
from test_framework . blocktools import add_witness_commitment , create_block , create_coinbase , send_to_witness
from test_framework . messages import BIP125_SEQUENCE_NUMBER , CTransaction
2016-12-09 15:45:27 -03:00
from test_framework . test_framework import BitcoinTestFramework
2019-04-06 19:38:51 -03:00
from test_framework . util import (
assert_equal ,
assert_greater_than ,
assert_raises_rpc_error ,
hex_str_to_bytes ,
)
2016-12-09 15:45:27 -03:00
WALLET_PASSPHRASE = " test "
WALLET_PASSPHRASE_TIMEOUT = 3600
2020-03-13 19:11:55 -03:00
# Fee rates (in BTC per 1000 vbytes)
INSUFFICIENT = 0.00001000
ECONOMICAL = 0.00050000
NORMAL = 0.00100000
HIGH = 0.00500000
TOO_HIGH = 1.00000000
2020-04-25 13:00:28 -04:00
2016-12-09 15:45:27 -03:00
class BumpFeeTest ( BitcoinTestFramework ) :
2017-06-09 18:21:21 -04:00
def set_test_params ( self ) :
2016-12-09 15:45:27 -03:00
self . num_nodes = 2
self . setup_clean_chain = True
2018-09-09 14:32:37 -03:00
self . extra_args = [ [
" -walletrbf= {} " . format ( i ) ,
" -mintxfee=0.00002 " ,
2019-10-19 21:03:13 -03:00
" -addresstype=bech32 " ,
2018-09-09 14:32:37 -03:00
] for i in range ( self . num_nodes ) ]
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2016-12-09 15:45:27 -03:00
2020-08-21 12:23:06 -04:00
def clear_mempool ( self ) :
# Clear mempool between subtests. The subtests may only depend on chainstate (utxos)
self . nodes [ 1 ] . generate ( 1 )
self . sync_all ( )
2017-06-09 16:35:17 -04:00
def run_test ( self ) :
2016-12-09 15:45:27 -03:00
# Encrypt wallet for test_locked_wallet_fails test
2018-02-20 18:09:51 -03:00
self . nodes [ 1 ] . encryptwallet ( WALLET_PASSPHRASE )
2016-12-09 15:45:27 -03:00
self . nodes [ 1 ] . walletpassphrase ( WALLET_PASSPHRASE , WALLET_PASSPHRASE_TIMEOUT )
peer_node , rbf_node = self . nodes
rbf_node_address = rbf_node . getnewaddress ( )
# fund rbf node with 10 coins of 0.001 btc (100,000 satoshis)
2017-03-07 20:46:17 -03:00
self . log . info ( " Mining blocks... " )
2016-12-09 15:45:27 -03:00
peer_node . generate ( 110 )
self . sync_all ( )
2020-08-02 19:10:56 -04:00
for _ in range ( 25 ) :
2016-12-09 15:45:27 -03:00
peer_node . sendtoaddress ( rbf_node_address , 0.001 )
self . sync_all ( )
peer_node . generate ( 1 )
self . sync_all ( )
assert_equal ( rbf_node . getbalance ( ) , Decimal ( " 0.025 " ) )
2017-03-07 20:46:17 -03:00
self . log . info ( " Running tests " )
2016-12-09 15:45:27 -03:00
dest_address = peer_node . getnewaddress ( )
2020-08-21 12:23:06 -04:00
self . test_invalid_parameters ( rbf_node , dest_address )
2019-07-29 19:59:23 -04:00
test_simple_bumpfee_succeeds ( self , " default " , rbf_node , peer_node , dest_address )
test_simple_bumpfee_succeeds ( self , " fee_rate " , rbf_node , peer_node , dest_address )
test_feerate_args ( self , rbf_node , peer_node , dest_address )
2020-03-10 13:30:57 -03:00
test_segwit_bumpfee_succeeds ( self , rbf_node , dest_address )
test_nonrbf_bumpfee_fails ( self , peer_node , dest_address )
test_notmine_bumpfee_fails ( self , rbf_node , peer_node , dest_address )
test_bumpfee_with_descendant_fails ( self , rbf_node , rbf_node_address , dest_address )
test_dust_to_fee ( self , rbf_node , dest_address )
2019-11-28 09:02:58 -03:00
test_watchonly_psbt ( self , peer_node , rbf_node , dest_address )
2020-03-10 13:30:57 -03:00
test_rebumping ( self , rbf_node , dest_address )
test_rebumping_not_replaceable ( self , rbf_node , dest_address )
test_unconfirmed_not_spendable ( self , rbf_node , rbf_node_address )
test_bumpfee_metadata ( self , rbf_node , dest_address )
test_locked_wallet_fails ( self , rbf_node , dest_address )
test_change_script_match ( self , rbf_node , dest_address )
2020-03-30 16:59:04 -03:00
test_settxfee ( self , rbf_node , dest_address )
2019-07-02 10:16:36 -04:00
test_maxtxfee_fails ( self , rbf_node , dest_address )
2019-03-08 13:30:53 -03:00
# These tests wipe out a number of utxos that are expected in other tests
2020-03-10 13:30:57 -03:00
test_small_output_with_feerate_succeeds ( self , rbf_node , dest_address )
test_no_more_inputs_fails ( self , rbf_node , dest_address )
2016-12-09 15:45:27 -03:00
2020-08-21 12:23:06 -04:00
def test_invalid_parameters ( self , node , dest_address ) :
txid = spend_one_input ( node , dest_address )
# invalid estimate mode
assert_raises_rpc_error ( - 8 , " Invalid estimate_mode parameter " , node . bumpfee , txid , {
" estimate_mode " : " moo " ,
} )
assert_raises_rpc_error ( - 3 , " Expected type string " , node . bumpfee , txid , {
" estimate_mode " : 38 ,
} )
assert_raises_rpc_error ( - 3 , " Expected type string " , node . bumpfee , txid , {
" estimate_mode " : {
" foo " : " bar " ,
} ,
} )
assert_raises_rpc_error ( - 8 , " Invalid estimate_mode parameter " , node . bumpfee , txid , {
" estimate_mode " : Decimal ( " 3.141592 " ) ,
} )
# confTarget and conf_target
assert_raises_rpc_error ( - 8 , " confTarget and conf_target options should not both be set " , node . bumpfee , txid , {
" confTarget " : 123 ,
" conf_target " : 456 ,
} )
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2019-07-29 19:59:23 -04:00
def test_simple_bumpfee_succeeds ( self , mode , rbf_node , peer_node , dest_address ) :
2020-03-13 19:11:55 -03:00
self . log . info ( ' Test simple bumpfee: {} ' . format ( mode ) )
2017-02-06 20:27:24 -03:00
rbfid = spend_one_input ( rbf_node , dest_address )
2016-12-09 15:45:27 -03:00
rbftx = rbf_node . gettransaction ( rbfid )
2019-04-06 19:19:45 -03:00
self . sync_mempools ( ( rbf_node , peer_node ) )
2016-12-09 15:45:27 -03:00
assert rbfid in rbf_node . getrawmempool ( ) and rbfid in peer_node . getrawmempool ( )
2019-07-29 19:59:23 -04:00
if mode == " fee_rate " :
2020-04-13 14:48:03 -04:00
bumped_psbt = rbf_node . psbtbumpfee ( rbfid , { " fee_rate " : NORMAL } )
2020-03-13 19:11:55 -03:00
bumped_tx = rbf_node . bumpfee ( rbfid , { " fee_rate " : NORMAL } )
2019-07-29 19:59:23 -04:00
else :
2020-04-13 14:48:03 -04:00
bumped_psbt = rbf_node . psbtbumpfee ( rbfid )
2019-07-29 19:59:23 -04:00
bumped_tx = rbf_node . bumpfee ( rbfid )
2017-05-24 16:15:26 -04:00
assert_equal ( bumped_tx [ " errors " ] , [ ] )
2019-12-01 11:46:44 -03:00
assert bumped_tx [ " fee " ] > - rbftx [ " fee " ]
assert_equal ( bumped_tx [ " origfee " ] , - rbftx [ " fee " ] )
2019-11-28 09:02:58 -03:00
assert " psbt " not in bumped_tx
2020-04-13 14:48:03 -04:00
assert_equal ( bumped_psbt [ " errors " ] , [ ] )
assert bumped_psbt [ " fee " ] > - rbftx [ " fee " ]
assert_equal ( bumped_psbt [ " origfee " ] , - rbftx [ " fee " ] )
assert " psbt " in bumped_psbt
2017-08-15 19:24:39 -03:00
# check that bumped_tx propagates, original tx was evicted and has a wallet conflict
2019-04-06 19:19:45 -03:00
self . sync_mempools ( ( rbf_node , peer_node ) )
2016-12-09 15:45:27 -03:00
assert bumped_tx [ " txid " ] in rbf_node . getrawmempool ( )
assert bumped_tx [ " txid " ] in peer_node . getrawmempool ( )
assert rbfid not in rbf_node . getrawmempool ( )
assert rbfid not in peer_node . getrawmempool ( )
oldwtx = rbf_node . gettransaction ( rbfid )
assert len ( oldwtx [ " walletconflicts " ] ) > 0
# check wallet transaction replaces and replaced_by values
bumpedwtx = rbf_node . gettransaction ( bumped_tx [ " txid " ] )
assert_equal ( oldwtx [ " replaced_by_txid " ] , bumped_tx [ " txid " ] )
assert_equal ( bumpedwtx [ " replaces_txid " ] , rbfid )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-04-25 13:00:28 -04:00
2019-07-29 19:59:23 -04:00
def test_feerate_args ( self , rbf_node , peer_node , dest_address ) :
2020-03-13 19:11:55 -03:00
self . log . info ( ' Test fee_rate args ' )
2019-07-29 19:59:23 -04:00
rbfid = spend_one_input ( rbf_node , dest_address )
self . sync_mempools ( ( rbf_node , peer_node ) )
assert rbfid in rbf_node . getrawmempool ( ) and rbfid in peer_node . getrawmempool ( )
2019-08-28 23:31:00 -04:00
assert_raises_rpc_error ( - 8 , " conf_target can ' t be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate. " , rbf_node . bumpfee , rbfid , { " fee_rate " : NORMAL , " confTarget " : 1 } )
2020-03-10 14:40:23 -03:00
assert_raises_rpc_error ( - 3 , " Unexpected key totalFee " , rbf_node . bumpfee , rbfid , { " totalFee " : NORMAL } )
2019-08-28 23:31:00 -04:00
assert_raises_rpc_error ( - 8 , " conf_target can ' t be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate. " , rbf_node . bumpfee , rbfid , { " fee_rate " : 0.00001 , " confTarget " : 1 } )
2019-07-29 19:59:23 -04:00
# Bumping to just above minrelay should fail to increase total fee enough, at least
2020-03-13 19:11:55 -03:00
assert_raises_rpc_error ( - 8 , " Insufficient total fee " , rbf_node . bumpfee , rbfid , { " fee_rate " : INSUFFICIENT } )
2019-07-29 19:59:23 -04:00
2020-04-25 13:00:28 -04:00
assert_raises_rpc_error ( - 3 , " Amount out of range " , rbf_node . bumpfee , rbfid , { " fee_rate " : - 1 } )
2019-07-29 19:59:23 -04:00
2020-03-13 19:11:55 -03:00
assert_raises_rpc_error ( - 4 , " is too high (cannot be higher than " , rbf_node . bumpfee , rbfid , { " fee_rate " : TOO_HIGH } )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2019-07-29 19:59:23 -04:00
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_segwit_bumpfee_succeeds ( self , rbf_node , dest_address ) :
self . log . info ( ' Test that segwit-sourcing bumpfee works ' )
2016-12-09 15:45:27 -03:00
# Create a transaction with segwit output, then create an RBF transaction
# which spends it, and make sure bumpfee can be called on it.
segwit_in = next ( u for u in rbf_node . listunspent ( ) if u [ " amount " ] == Decimal ( " 0.001 " ) )
2018-09-23 00:01:32 -03:00
segwit_out = rbf_node . getaddressinfo ( rbf_node . getnewaddress ( address_type = ' p2sh-segwit ' ) )
2016-12-09 15:45:27 -03:00
segwitid = send_to_witness (
2017-03-10 13:21:20 -03:00
use_p2wsh = False ,
2016-12-09 15:45:27 -03:00
node = rbf_node ,
utxo = segwit_in ,
pubkey = segwit_out [ " pubkey " ] ,
encode_p2sh = False ,
amount = Decimal ( " 0.0009 " ) ,
sign = True )
rbfraw = rbf_node . createrawtransaction ( [ {
' txid ' : segwitid ,
' vout ' : 0 ,
" sequence " : BIP125_SEQUENCE_NUMBER
} ] , { dest_address : Decimal ( " 0.0005 " ) ,
2017-02-06 15:50:08 -03:00
rbf_node . getrawchangeaddress ( ) : Decimal ( " 0.0003 " ) } )
2017-09-05 20:49:18 -03:00
rbfsigned = rbf_node . signrawtransactionwithwallet ( rbfraw )
2016-12-09 15:45:27 -03:00
rbfid = rbf_node . sendrawtransaction ( rbfsigned [ " hex " ] )
assert rbfid in rbf_node . getrawmempool ( )
bumped_tx = rbf_node . bumpfee ( rbfid )
assert bumped_tx [ " txid " ] in rbf_node . getrawmempool ( )
assert rbfid not in rbf_node . getrawmempool ( )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_nonrbf_bumpfee_fails ( self , peer_node , dest_address ) :
self . log . info ( ' Test that we cannot replace a non RBF transaction ' )
2017-02-06 20:27:24 -03:00
not_rbfid = peer_node . sendtoaddress ( dest_address , Decimal ( " 0.00090000 " ) )
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 4 , " not BIP 125 replaceable " , peer_node . bumpfee , not_rbfid )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_notmine_bumpfee_fails ( self , rbf_node , peer_node , dest_address ) :
self . log . info ( ' Test that it cannot bump fee if non-owned inputs are included ' )
2016-12-09 15:45:27 -03:00
# here, the rbftx has a peer_node coin and then adds a rbf_node input
# Note that this test depends upon the RPC code checking input ownership prior to change outputs
# (since it can't use fundrawtransaction, it lacks a proper change output)
2020-08-24 07:52:17 -04:00
fee = Decimal ( " 0.001 " )
utxos = [ node . listunspent ( query_options = { ' minimumAmount ' : fee } ) [ - 1 ] for node in ( rbf_node , peer_node ) ]
2016-12-09 15:45:27 -03:00
inputs = [ {
" txid " : utxo [ " txid " ] ,
" vout " : utxo [ " vout " ] ,
" address " : utxo [ " address " ] ,
" sequence " : BIP125_SEQUENCE_NUMBER
} for utxo in utxos ]
2020-08-24 07:52:17 -04:00
output_val = sum ( utxo [ " amount " ] for utxo in utxos ) - fee
2016-12-09 15:45:27 -03:00
rawtx = rbf_node . createrawtransaction ( inputs , { dest_address : output_val } )
2017-09-05 20:49:18 -03:00
signedtx = rbf_node . signrawtransactionwithwallet ( rawtx )
signedtx = peer_node . signrawtransactionwithwallet ( signedtx [ " hex " ] )
2016-12-09 15:45:27 -03:00
rbfid = rbf_node . sendrawtransaction ( signedtx [ " hex " ] )
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 4 , " Transaction contains inputs that don ' t belong to this wallet " ,
2018-09-23 09:50:47 -03:00
rbf_node . bumpfee , rbfid )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_bumpfee_with_descendant_fails ( self , rbf_node , rbf_node_address , dest_address ) :
self . log . info ( ' Test that fee cannot be bumped when it has descendant ' )
2016-12-09 15:45:27 -03:00
# parent is send-to-self, so we don't have to check which output is change when creating the child tx
2017-02-06 20:27:24 -03:00
parent_id = spend_one_input ( rbf_node , rbf_node_address )
2016-12-09 15:45:27 -03:00
tx = rbf_node . createrawtransaction ( [ { " txid " : parent_id , " vout " : 0 } ] , { dest_address : 0.00020000 } )
2017-09-05 20:49:18 -03:00
tx = rbf_node . signrawtransactionwithwallet ( tx )
2017-07-20 17:10:37 -04:00
rbf_node . sendrawtransaction ( tx [ " hex " ] )
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 8 , " Transaction has descendants in the wallet " , rbf_node . bumpfee , parent_id )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-04-25 13:00:28 -04:00
2020-03-10 13:30:57 -03:00
def test_small_output_with_feerate_succeeds ( self , rbf_node , dest_address ) :
self . log . info ( ' Testing small output with feerate bump succeeds ' )
2019-03-08 13:30:53 -03:00
# Make sure additional inputs exist
rbf_node . generatetoaddress ( 101 , rbf_node . getnewaddress ( ) )
rbfid = spend_one_input ( rbf_node , dest_address )
2019-10-30 13:59:50 -03:00
input_list = rbf_node . getrawtransaction ( rbfid , 1 ) [ " vin " ]
assert_equal ( len ( input_list ) , 1 )
original_txin = input_list [ 0 ]
2020-03-10 13:33:50 -03:00
self . log . info ( ' Keep bumping until transaction fee out-spends non-destination value ' )
2019-03-08 13:30:53 -03:00
tx_fee = 0
2020-03-10 13:33:50 -03:00
while True :
2019-10-30 13:59:50 -03:00
input_list = rbf_node . getrawtransaction ( rbfid , 1 ) [ " vin " ]
new_item = list ( input_list ) [ 0 ]
assert_equal ( len ( input_list ) , 1 )
2019-03-08 13:30:53 -03:00
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
2020-03-10 13:33:50 -03:00
tx_fee = rbfid_new_details [ " fee " ]
# Total value from input not going to destination
if tx_fee > Decimal ( ' 0.00050000 ' ) :
break
2019-03-08 13:30:53 -03:00
# 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 )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-04-25 13:00:28 -04:00
2020-03-10 13:30:57 -03:00
def test_dust_to_fee ( self , rbf_node , dest_address ) :
self . log . info ( ' Test that bumped output that is dust is dropped to fee ' )
2017-02-06 20:27:24 -03:00
rbfid = spend_one_input ( rbf_node , dest_address )
2016-12-09 15:45:27 -03:00
fulltx = rbf_node . getrawtransaction ( rbfid , 1 )
2020-04-03 12:12:08 -03:00
# The DER formatting used by Bitcoin to serialize ECDSA signatures means that signatures can have a
# variable size of 70-72 bytes (or possibly even less), with most being 71 or 72 bytes. The signature
# in the witness is divided by 4 for the vsize, so this variance can take the weight across a 4-byte
# boundary. Thus expected transaction size (p2wpkh, 1 input, 2 outputs) is 140-141 vbytes, usually 141.
if not 140 < = fulltx [ " vsize " ] < = 141 :
2020-04-06 12:37:50 -04:00
raise AssertionError ( " Invalid tx vsize of {} (140-141 expected), full tx: {} " . format ( fulltx [ " vsize " ] , fulltx ) )
2020-04-03 12:12:08 -03:00
# Bump with fee_rate of 0.00350250 BTC per 1000 vbytes to create dust.
# Expected fee is 141 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049385 BTC.
# or occasionally 140 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049035 BTC.
# Dust should be dropped to the fee, so actual bump fee is 0.00050000 BTC.
bumped_tx = rbf_node . bumpfee ( rbfid , { " fee_rate " : 0.00350250 } )
2016-12-09 15:45:27 -03:00
full_bumped_tx = rbf_node . getrawtransaction ( bumped_tx [ " txid " ] , 1 )
2017-02-06 20:27:24 -03:00
assert_equal ( bumped_tx [ " fee " ] , Decimal ( " 0.00050000 " ) )
2016-12-09 15:45:27 -03:00
assert_equal ( len ( fulltx [ " vout " ] ) , 2 )
2018-09-23 09:50:47 -03:00
assert_equal ( len ( full_bumped_tx [ " vout " ] ) , 1 ) # change output is eliminated
2020-03-13 19:11:55 -03:00
assert_equal ( full_bumped_tx [ " vout " ] [ 0 ] [ ' value ' ] , Decimal ( " 0.00050000 " ) )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_settxfee ( self , rbf_node , dest_address ) :
self . log . info ( ' Test settxfee ' )
2018-08-20 14:34:39 -03:00
assert_raises_rpc_error ( - 8 , " txfee cannot be less than min relay tx fee " , rbf_node . settxfee , Decimal ( ' 0.000005 ' ) )
assert_raises_rpc_error ( - 8 , " txfee cannot be less than wallet min fee " , rbf_node . settxfee , Decimal ( ' 0.000015 ' ) )
2016-12-09 15:45:27 -03:00
# check that bumpfee reacts correctly to the use of settxfee (paytxfee)
2017-02-06 20:27:24 -03:00
rbfid = spend_one_input ( rbf_node , dest_address )
requested_feerate = Decimal ( " 0.00025000 " )
rbf_node . settxfee ( requested_feerate )
2016-12-09 15:45:27 -03:00
bumped_tx = rbf_node . bumpfee ( rbfid )
2017-11-30 21:49:11 -03:00
actual_feerate = bumped_tx [ " fee " ] * 1000 / rbf_node . getrawtransaction ( bumped_tx [ " txid " ] , True ) [ " vsize " ]
2017-02-06 20:27:24 -03:00
# Assert that the difference between the requested feerate and the actual
# feerate of the bumped transaction is small.
assert_greater_than ( Decimal ( " 0.00001000 " ) , abs ( requested_feerate - actual_feerate ) )
2016-12-09 15:45:27 -03:00
rbf_node . settxfee ( Decimal ( " 0.00000000 " ) ) # unset paytxfee
2020-03-30 16:59:04 -03:00
# check that settxfee respects -maxtxfee
self . restart_node ( 1 , [ ' -maxtxfee=0.000025 ' ] + self . extra_args [ 1 ] )
assert_raises_rpc_error ( - 8 , " txfee cannot be more than wallet max tx fee " , rbf_node . settxfee , Decimal ( ' 0.00003 ' ) )
self . restart_node ( 1 , self . extra_args [ 1 ] )
rbf_node . walletpassphrase ( WALLET_PASSPHRASE , WALLET_PASSPHRASE_TIMEOUT )
2020-08-21 12:23:06 -04:00
self . connect_nodes ( 1 , 0 )
self . clear_mempool ( )
2020-03-30 16:59:04 -03:00
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_maxtxfee_fails ( self , rbf_node , dest_address ) :
2020-03-30 16:59:04 -03:00
self . log . info ( ' Test that bumpfee fails when it hits -maxtxfee ' )
2019-10-19 21:03:13 -03:00
# size of bumped transaction (p2wpkh, 1 input, 2 outputs): 141 vbytes
2020-03-13 19:11:55 -03:00
# expected bump fee of 141 vbytes * 0.00200000 BTC / 1000 vbytes = 0.00002820 BTC
# which exceeds maxtxfee and is expected to raise
2020-03-10 13:30:57 -03:00
self . restart_node ( 1 , [ ' -maxtxfee=0.000025 ' ] + self . extra_args [ 1 ] )
2019-07-02 10:16:36 -04:00
rbf_node . walletpassphrase ( WALLET_PASSPHRASE , WALLET_PASSPHRASE_TIMEOUT )
rbfid = spend_one_input ( rbf_node , dest_address )
2020-08-25 10:00:38 -04:00
assert_raises_rpc_error ( - 4 , " Unable to create transaction. Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate) " , rbf_node . bumpfee , rbfid )
2020-03-10 13:30:57 -03:00
self . restart_node ( 1 , self . extra_args [ 1 ] )
2019-07-02 10:16:36 -04:00
rbf_node . walletpassphrase ( WALLET_PASSPHRASE , WALLET_PASSPHRASE_TIMEOUT )
2020-08-21 12:23:06 -04:00
self . connect_nodes ( 1 , 0 )
self . clear_mempool ( )
2019-07-02 10:16:36 -04:00
2020-04-25 13:00:28 -04:00
2020-03-10 13:30:57 -03:00
def test_watchonly_psbt ( self , peer_node , rbf_node , dest_address ) :
self . log . info ( ' Test that PSBT is returned for bumpfee in watchonly wallets ' )
2019-11-28 09:02:58 -03:00
priv_rec_desc = " wpkh([00000001/84 ' /1 ' /0 ' ]tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#rweraev0 "
pub_rec_desc = rbf_node . getdescriptorinfo ( priv_rec_desc ) [ " descriptor " ]
priv_change_desc = " wpkh([00000001/84 ' /1 ' /0 ' ]tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#j6uzqvuh "
pub_change_desc = rbf_node . getdescriptorinfo ( priv_change_desc ) [ " descriptor " ]
# Create a wallet with private keys that can sign PSBTs
rbf_node . createwallet ( wallet_name = " signer " , disable_private_keys = False , blank = True )
signer = rbf_node . get_wallet_rpc ( " signer " )
assert signer . getwalletinfo ( ) [ ' private_keys_enabled ' ]
result = signer . importmulti ( [ {
" desc " : priv_rec_desc ,
" timestamp " : 0 ,
" range " : [ 0 , 1 ] ,
" internal " : False ,
" keypool " : False # Keys can only be imported to the keypool when private keys are disabled
} ,
{
" desc " : priv_change_desc ,
" timestamp " : 0 ,
" range " : [ 0 , 0 ] ,
" internal " : True ,
" keypool " : False
} ] )
assert_equal ( result , [ { ' success ' : True } , { ' success ' : True } ] )
# Create another wallet with just the public keys, which creates PSBTs
rbf_node . createwallet ( wallet_name = " watcher " , disable_private_keys = True , blank = True )
watcher = rbf_node . get_wallet_rpc ( " watcher " )
assert not watcher . getwalletinfo ( ) [ ' private_keys_enabled ' ]
result = watcher . importmulti ( [ {
" desc " : pub_rec_desc ,
" timestamp " : 0 ,
2020-04-25 13:00:28 -04:00
" range " : [ 0 , 10 ] ,
2019-11-28 09:02:58 -03:00
" internal " : False ,
" keypool " : True ,
" watchonly " : True
2020-04-25 13:00:28 -04:00
} , {
2019-11-28 09:02:58 -03:00
" desc " : pub_change_desc ,
" timestamp " : 0 ,
" range " : [ 0 , 10 ] ,
" internal " : True ,
" keypool " : True ,
" watchonly " : True
} ] )
assert_equal ( result , [ { ' success ' : True } , { ' success ' : True } ] )
funding_address1 = watcher . getnewaddress ( address_type = ' bech32 ' )
funding_address2 = watcher . getnewaddress ( address_type = ' bech32 ' )
peer_node . sendmany ( " " , { funding_address1 : 0.001 , funding_address2 : 0.001 } )
peer_node . generate ( 1 )
2020-03-10 13:30:57 -03:00
self . sync_all ( )
2019-11-28 09:02:58 -03:00
# Create single-input PSBT for transaction to be bumped
2020-04-25 13:00:28 -04:00
psbt = watcher . walletcreatefundedpsbt ( [ ] , { dest_address : 0.0005 } , 0 , { " feeRate " : 0.00001 } , True ) [ ' psbt ' ]
2019-11-28 09:02:58 -03:00
psbt_signed = signer . walletprocesspsbt ( psbt = psbt , sign = True , sighashtype = " ALL " , bip32derivs = True )
psbt_final = watcher . finalizepsbt ( psbt_signed [ " psbt " ] )
original_txid = watcher . sendrawtransaction ( psbt_final [ " hex " ] )
assert_equal ( len ( watcher . decodepsbt ( psbt ) [ " tx " ] [ " vin " ] ) , 1 )
# Bump fee, obnoxiously high to add additional watchonly input
2020-04-13 14:48:03 -04:00
bumped_psbt = watcher . psbtbumpfee ( original_txid , { " fee_rate " : HIGH } )
2019-11-28 09:02:58 -03:00
assert_greater_than ( len ( watcher . decodepsbt ( bumped_psbt [ ' psbt ' ] ) [ " tx " ] [ " vin " ] ) , 1 )
assert " txid " not in bumped_psbt
assert_equal ( bumped_psbt [ " origfee " ] , - watcher . gettransaction ( original_txid ) [ " fee " ] )
assert not watcher . finalizepsbt ( bumped_psbt [ " psbt " ] ) [ " complete " ]
# Sign bumped transaction
bumped_psbt_signed = signer . walletprocesspsbt ( psbt = bumped_psbt [ " psbt " ] , sign = True , sighashtype = " ALL " , bip32derivs = True )
bumped_psbt_final = watcher . finalizepsbt ( bumped_psbt_signed [ " psbt " ] )
assert bumped_psbt_final [ " complete " ]
# Broadcast bumped transaction
bumped_txid = watcher . sendrawtransaction ( bumped_psbt_final [ " hex " ] )
assert bumped_txid in rbf_node . getrawmempool ( )
assert original_txid not in rbf_node . getrawmempool ( )
rbf_node . unloadwallet ( " watcher " )
rbf_node . unloadwallet ( " signer " )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2019-07-02 10:16:36 -04:00
2020-04-25 13:00:28 -04:00
2020-03-10 13:30:57 -03:00
def test_rebumping ( self , rbf_node , dest_address ) :
self . log . info ( ' Test that re-bumping the original tx fails, but bumping successor works ' )
2017-02-06 20:27:24 -03:00
rbfid = spend_one_input ( rbf_node , dest_address )
2020-03-13 19:11:55 -03:00
bumped = rbf_node . bumpfee ( rbfid , { " fee_rate " : ECONOMICAL } )
assert_raises_rpc_error ( - 4 , " already bumped " , rbf_node . bumpfee , rbfid , { " fee_rate " : NORMAL } )
rbf_node . bumpfee ( bumped [ " txid " ] , { " fee_rate " : NORMAL } )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_rebumping_not_replaceable ( self , rbf_node , dest_address ) :
self . log . info ( ' Test that re-bumping non-replaceable fails ' )
2017-02-06 20:27:24 -03:00
rbfid = spend_one_input ( rbf_node , dest_address )
2020-03-13 19:11:55 -03:00
bumped = rbf_node . bumpfee ( rbfid , { " fee_rate " : ECONOMICAL , " replaceable " : False } )
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 4 , " Transaction is not BIP 125 replaceable " , rbf_node . bumpfee , bumped [ " txid " ] ,
2020-03-13 19:11:55 -03:00
{ " fee_rate " : NORMAL } )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_unconfirmed_not_spendable ( self , rbf_node , rbf_node_address ) :
self . log . info ( ' Test that unconfirmed outputs from bumped txns are not spendable ' )
2017-02-06 20:27:24 -03:00
rbfid = spend_one_input ( rbf_node , rbf_node_address )
2016-12-09 15:45:27 -03:00
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,
2018-01-27 19:45:32 -03:00
# 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
2016-12-09 15:45:27 -03:00
# spendable before the rbf tx is confirmed.
block = submit_block_with_tx ( rbf_node , rbftx )
2018-01-27 19:45:32 -03:00
# Can not abandon conflicted tx
assert_raises_rpc_error ( - 5 , ' Transaction not eligible for abandonment ' , lambda : rbf_node . abandontransaction ( txid = bumpid ) )
2016-12-09 15:45:27 -03:00
rbf_node . invalidateblock ( block . hash )
2018-01-27 19:45:32 -03:00
# Call abandon to make sure the wallet doesn't attempt to resubmit
# the bump tx and hope the wallet does not rebroadcast before we call.
rbf_node . abandontransaction ( bumpid )
2016-12-09 15:45:27 -03:00
assert bumpid not in rbf_node . getrawmempool ( )
assert rbfid in rbf_node . getrawmempool ( )
# check that outputs from the rbf tx are not spendable before the
# transaction is confirmed, due to the replaced_by_txid check in
# CWallet::AvailableCoins
assert_equal ( [ t for t in rbf_node . listunspent ( minconf = 0 , include_unsafe = False ) if t [ " txid " ] == rbfid ] , [ ] )
# check that the main output from the rbf tx is spendable after confirmed
rbf_node . generate ( 1 )
assert_equal (
sum ( 1 for t in rbf_node . listunspent ( minconf = 0 , include_unsafe = False )
if t [ " txid " ] == rbfid and t [ " address " ] == rbf_node_address and t [ " spendable " ] ) , 1 )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-10 13:30:57 -03:00
def test_bumpfee_metadata ( self , rbf_node , dest_address ) :
self . log . info ( ' Test that bumped txn metadata persists to new txn record ' )
2019-03-05 13:04:32 -03:00
assert ( rbf_node . getbalance ( ) < 49 )
rbf_node . generatetoaddress ( 101 , rbf_node . getnewaddress ( ) )
rbfid = rbf_node . sendtoaddress ( dest_address , 49 , " comment value " , " to value " )
2017-02-02 19:34:22 -03:00
bumped_tx = rbf_node . bumpfee ( rbfid )
bumped_wtx = rbf_node . gettransaction ( bumped_tx [ " txid " ] )
assert_equal ( bumped_wtx [ " comment " ] , " comment value " )
assert_equal ( bumped_wtx [ " to " ] , " to value " )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2017-02-02 19:34:22 -03:00
2020-03-10 13:30:57 -03:00
def test_locked_wallet_fails ( self , rbf_node , dest_address ) :
self . log . info ( ' Test that locked wallet cannot bump txn ' )
2017-02-06 20:27:24 -03:00
rbfid = spend_one_input ( rbf_node , dest_address )
2016-12-09 15:45:27 -03:00
rbf_node . walletlock ( )
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 13 , " Please enter the wallet passphrase with walletpassphrase first. " ,
2018-09-23 09:50:47 -03:00
rbf_node . bumpfee , rbfid )
2019-03-08 13:30:53 -03:00
rbf_node . walletpassphrase ( WALLET_PASSPHRASE , WALLET_PASSPHRASE_TIMEOUT )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-04-25 13:00:28 -04:00
2020-03-10 13:30:57 -03:00
def test_change_script_match ( self , rbf_node , dest_address ) :
2020-03-13 19:11:55 -03:00
self . log . info ( ' Test that the same change addresses is used for the replacement transaction when possible ' )
2020-03-10 13:30:57 -03:00
2019-03-08 18:43:40 -03:00
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 " ] ]
2016-12-09 15:45:27 -03:00
2019-03-08 18:43:40 -03:00
# 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
2020-03-13 19:11:55 -03:00
bumped_total_tx = rbf_node . bumpfee ( rbfid , { " fee_rate " : ECONOMICAL } )
2019-03-08 18:43:40 -03:00
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 ' ] ) )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-04-25 13:00:28 -04:00
2019-03-08 13:30:53 -03:00
def spend_one_input ( node , dest_address , change_size = Decimal ( " 0.00049000 " ) ) :
2017-03-16 11:49:42 -03:00
tx_input = dict (
2017-02-06 20:27:24 -03:00
sequence = BIP125_SEQUENCE_NUMBER , * * next ( u for u in node . listunspent ( ) if u [ " amount " ] == Decimal ( " 0.00100000 " ) ) )
2019-03-08 13:30:53 -03:00
destinations = { dest_address : Decimal ( " 0.00050000 " ) }
if change_size > 0 :
destinations [ node . getrawchangeaddress ( ) ] = change_size
rawtx = node . createrawtransaction ( [ tx_input ] , destinations )
2017-09-05 20:49:18 -03:00
signedtx = node . signrawtransactionwithwallet ( rawtx )
2016-12-09 15:45:27 -03:00
txid = node . sendrawtransaction ( signedtx [ " hex " ] )
return txid
2020-04-25 13:00:28 -04:00
2016-12-09 15:45:27 -03:00
def submit_block_with_tx ( node , tx ) :
ctx = CTransaction ( )
ctx . deserialize ( io . BytesIO ( hex_str_to_bytes ( tx ) ) )
tip = node . getbestblockhash ( )
height = node . getblockcount ( ) + 1
block_time = node . getblockheader ( tip ) [ " mediantime " ] + 1
2018-07-06 18:10:35 -04:00
block = create_block ( int ( tip , 16 ) , create_coinbase ( height ) , block_time )
2016-12-09 15:45:27 -03:00
block . vtx . append ( ctx )
block . rehash ( )
block . hashMerkleRoot = block . calc_merkle_root ( )
2018-07-06 18:10:35 -04:00
add_witness_commitment ( block )
2016-12-09 15:45:27 -03:00
block . solve ( )
2019-05-08 10:03:06 -04:00
node . submitblock ( block . serialize ( ) . hex ( ) )
2016-12-09 15:45:27 -03:00
return block
2020-04-25 13:00:28 -04:00
2020-03-10 13:30:57 -03:00
def test_no_more_inputs_fails ( self , rbf_node , dest_address ) :
self . log . info ( ' Test that bumpfee fails when there are no available confirmed outputs ' )
2019-03-08 13:30:53 -03:00
# 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 )
2020-04-21 07:53:24 -04:00
assert_raises_rpc_error ( - 4 , " Unable to create transaction. Insufficient funds " , rbf_node . bumpfee , rbfid )
2020-08-21 12:23:06 -04:00
self . clear_mempool ( )
2016-12-09 15:45:27 -03:00
2020-03-13 19:11:55 -03:00
2016-12-09 15:45:27 -03:00
if __name__ == " __main__ " :
BumpFeeTest ( ) . main ( )