2016-03-19 16:58:06 -03:00
#!/usr/bin/env python3
2022-12-24 20:49:50 -03:00
# Copyright (c) 2014-2022 The Bitcoin Core developers
2015-04-24 22:26:30 -03:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-17 20:34:40 -03:00
""" Test the fundrawtransaction RPC. """
2015-04-24 22:26:30 -03:00
2021-10-05 17:45:10 -03:00
2018-01-25 07:03:05 -03:00
from decimal import Decimal
2020-12-04 07:28:47 -03:00
from itertools import product
2021-10-05 17:45:10 -03:00
from math import ceil
2023-12-22 13:47:03 -03:00
from test_framework . address import address_to_scriptpubkey
2020-12-04 07:28:47 -03:00
2020-04-03 21:46:23 -03:00
from test_framework . descriptors import descsum_create
2022-03-30 04:18:33 -03:00
from test_framework . messages import (
COIN ,
2023-12-22 13:47:03 -03:00
CTransaction ,
CTxOut ,
2022-03-30 04:18:33 -03:00
)
2017-08-16 12:52:24 -03:00
from test_framework . test_framework import BitcoinTestFramework
2018-01-25 07:03:05 -03:00
from test_framework . util import (
2020-10-19 06:35:10 -03:00
assert_approx ,
2018-01-25 07:03:05 -03:00
assert_equal ,
assert_fee_amount ,
assert_greater_than ,
assert_greater_than_or_equal ,
assert_raises_rpc_error ,
count_bytes ,
2023-08-04 22:53:00 -04:00
get_fee ,
2018-01-25 07:03:05 -03:00
)
2023-10-09 09:30:40 -03:00
from test_framework . wallet_util import generate_keypair , WalletUnlock
2015-04-24 22:26:30 -03:00
2022-12-07 14:35:46 -03:00
ERR_NOT_ENOUGH_PRESET_INPUTS = " The preselected coins total amount does not cover the transaction target. " \
" Please allow other inputs to be automatically selected or include more coins manually "
2016-04-16 13:36:39 -03:00
def get_unspent ( listunspent , amount ) :
for utx in listunspent :
if utx [ ' amount ' ] == amount :
return utx
raise AssertionError ( ' Could not find unspent with amount= {} ' . format ( amount ) )
2015-04-24 22:26:30 -03:00
class RawTransactionsTest ( BitcoinTestFramework ) :
2022-11-09 08:53:13 -03:00
def add_options ( self , parser ) :
self . add_wallet_options ( parser )
2017-06-09 18:21:21 -04:00
def set_test_params ( self ) :
2016-05-14 08:01:31 -03:00
self . num_nodes = 4
self . setup_clean_chain = True
2023-03-14 10:18:47 -03:00
# whitelist peers to speed up tx relay / mempool sync
self . noban_tx_relay = True
2021-03-10 17:45:29 -03:00
self . rpc_timeout = 90 # to prevent timeouts in `test_transaction_too_large`
2015-04-24 22:26:30 -03:00
2018-09-09 14:32:37 -03:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2018-12-22 13:19:50 -03:00
def setup_network ( self ) :
2017-04-03 10:34:04 -03:00
self . setup_nodes ( )
2015-04-24 22:26:30 -03:00
2020-09-17 04:46:07 -03:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
self . connect_nodes ( 0 , 3 )
2015-04-24 22:26:30 -03:00
2021-08-26 19:21:25 -04:00
def lock_outputs_type ( self , wallet , outputtype ) :
"""
Only allow UTXOs of the given type
"""
if outputtype in [ " legacy " , " p2pkh " , " pkh " ] :
prefixes = [ " pkh( " , " sh(multi( " ]
elif outputtype in [ " p2sh-segwit " , " sh_wpkh " ] :
prefixes = [ " sh(wpkh( " , " sh(wsh( " ]
elif outputtype in [ " bech32 " , " wpkh " ] :
prefixes = [ " wpkh( " , " wsh( " ]
else :
assert False , f " Unknown output type { outputtype } "
to_lock = [ ]
for utxo in wallet . listunspent ( ) :
if " desc " in utxo :
for prefix in prefixes :
if utxo [ " desc " ] . startswith ( prefix ) :
to_lock . append ( { " txid " : utxo [ " txid " ] , " vout " : utxo [ " vout " ] } )
wallet . lockunspent ( False , to_lock )
def unlock_utxos ( self , wallet ) :
"""
Unlock all UTXOs except the watchonly one
"""
to_keep = [ ]
2023-08-13 10:02:10 -04:00
if self . watchonly_utxo is not None :
to_keep . append ( self . watchonly_utxo )
2021-08-26 19:21:25 -04:00
wallet . lockunspent ( True )
wallet . lockunspent ( False , to_keep )
2015-04-24 22:26:30 -03:00
def run_test ( self ) :
2023-08-13 10:02:10 -04:00
self . watchonly_utxo = None
2019-10-31 07:46:57 -03:00
self . log . info ( " Connect nodes, set fees, generate blocks, and sync " )
2019-06-26 12:42:11 -04:00
self . min_relay_tx_fee = self . nodes [ 0 ] . getnetworkinfo ( ) [ ' relayfee ' ]
2016-01-05 19:47:04 -03:00
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self . nodes :
2019-06-26 12:42:11 -04:00
node . settxfee ( self . min_relay_tx_fee )
2016-01-05 19:47:04 -03:00
2015-10-14 09:27:03 -03:00
# if the fee's positive delta is higher than this value tests will fail,
# neg. delta always fail the tests.
# The size of the signature of every input may be at most 2 bytes larger
# than a minimum sized signature.
# = 2 bytes * minRelayTxFeePerByte
2019-06-26 12:42:11 -04:00
self . fee_tolerance = 2 * self . min_relay_tx_fee / 1000
2015-04-24 22:26:30 -03:00
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 2 ] , 1 )
self . generate ( self . nodes [ 0 ] , 121 )
2015-04-24 01:42:49 -03:00
2022-09-09 11:18:48 -04:00
self . test_add_inputs_default_value ( )
2022-11-23 10:22:50 -03:00
self . test_preset_inputs_selection ( )
2022-03-30 04:18:33 -03:00
self . test_weight_calculation ( )
2024-06-19 13:27:35 -04:00
self . test_weight_limits ( )
2019-06-26 13:49:11 -04:00
self . test_change_position ( )
self . test_simple ( )
self . test_simple_two_coins ( )
self . test_simple_two_outputs ( )
self . test_change ( )
self . test_no_change ( )
self . test_invalid_option ( )
self . test_invalid_change_address ( )
self . test_valid_change_address ( )
self . test_change_type ( )
self . test_coin_selection ( )
self . test_two_vin ( )
self . test_two_vin_two_vout ( )
self . test_invalid_input ( )
self . test_fee_p2pkh ( )
self . test_fee_p2pkh_multi_out ( )
self . test_fee_p2sh ( )
self . test_fee_4of5 ( )
self . test_spend_2of2 ( )
self . test_locked_wallet ( )
self . test_many_inputs_fee ( )
self . test_many_inputs_send ( )
self . test_op_return ( )
self . test_watchonly ( )
self . test_all_watched_funds ( )
self . test_option_feerate ( )
self . test_address_reuse ( )
self . test_option_subtract_fee_from_outputs ( )
2019-11-23 00:21:36 -03:00
self . test_subtract_fee_with_presets ( )
2020-11-30 17:32:06 -03:00
self . test_transaction_too_large ( )
2021-03-10 11:37:18 -03:00
self . test_include_unsafe ( )
2019-10-21 17:55:07 -03:00
self . test_external_inputs ( )
2021-08-11 22:07:15 -04:00
self . test_22670 ( )
2021-09-10 21:27:41 -03:00
self . test_feerate_rounding ( )
2021-05-24 09:22:10 -04:00
self . test_input_confs_control ( )
2023-12-22 13:47:03 -03:00
self . test_duplicate_outputs ( )
def test_duplicate_outputs ( self ) :
self . log . info ( " Test deserializing and funding a transaction with duplicate outputs " )
self . nodes [ 1 ] . createwallet ( " fundtx_duplicate_outputs " )
w = self . nodes [ 1 ] . get_wallet_rpc ( " fundtx_duplicate_outputs " )
addr = w . getnewaddress ( address_type = " bech32 " )
self . nodes [ 0 ] . sendtoaddress ( addr , 5 )
self . generate ( self . nodes [ 0 ] , 1 )
address = self . nodes [ 0 ] . getnewaddress ( " bech32 " )
tx = CTransaction ( )
tx . vin = [ ]
tx . vout = [ CTxOut ( 1 * COIN , bytearray ( address_to_scriptpubkey ( address ) ) ) ] * 2
tx . nLockTime = 0
tx_hex = tx . serialize ( ) . hex ( )
res = w . fundrawtransaction ( tx_hex , add_inputs = True )
signed_res = w . signrawtransactionwithwallet ( res [ " hex " ] )
txid = w . sendrawtransaction ( signed_res [ " hex " ] )
assert self . nodes [ 1 ] . getrawtransaction ( txid )
self . log . info ( " Test SFFO with duplicate outputs " )
res_sffo = w . fundrawtransaction ( tx_hex , add_inputs = True , subtractFeeFromOutputs = [ 0 , 1 ] )
signed_res_sffo = w . signrawtransactionwithwallet ( res_sffo [ " hex " ] )
txid_sffo = w . sendrawtransaction ( signed_res_sffo [ " hex " ] )
assert self . nodes [ 1 ] . getrawtransaction ( txid_sffo )
2019-06-26 13:49:11 -04:00
def test_change_position ( self ) :
2019-11-01 07:51:49 -03:00
""" Ensure setting changePosition in fundraw with an exact match is handled properly. """
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn changePosition option " )
2017-04-28 18:22:37 -03:00
rawmatch = self . nodes [ 2 ] . createrawtransaction ( [ ] , { self . nodes [ 2 ] . getnewaddress ( ) : 50 } )
2022-11-10 14:04:07 -03:00
rawmatch = self . nodes [ 2 ] . fundrawtransaction ( rawmatch , changePosition = 1 , subtractFeeFromOutputs = [ 0 ] )
2017-04-28 18:22:37 -03:00
assert_equal ( rawmatch [ " changepos " ] , - 1 )
2020-04-03 21:46:23 -03:00
self . nodes [ 3 ] . createwallet ( wallet_name = " wwatch " , disable_private_keys = True )
wwatch = self . nodes [ 3 ] . get_wallet_rpc ( ' wwatch ' )
2015-04-24 01:42:49 -03:00
watchonly_address = self . nodes [ 0 ] . getnewaddress ( )
2018-02-09 13:12:27 -03:00
watchonly_pubkey = self . nodes [ 0 ] . getaddressinfo ( watchonly_address ) [ " pubkey " ]
2019-06-26 12:42:11 -04:00
self . watchonly_amount = Decimal ( 200 )
2020-04-03 21:46:23 -03:00
wwatch . importpubkey ( watchonly_pubkey , " " , True )
2023-08-13 10:02:10 -04:00
self . watchonly_utxo = self . create_outpoints ( self . nodes [ 0 ] , outputs = [ { watchonly_address : self . watchonly_amount } ] ) [ 0 ]
2018-01-25 07:03:05 -03:00
# Lock UTXO so nodes[0] doesn't accidentally spend it
2023-08-13 10:02:10 -04:00
self . nodes [ 0 ] . lockunspent ( False , [ self . watchonly_utxo ] )
2018-01-25 07:03:05 -03:00
2020-04-03 21:46:23 -03:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 3 ] . get_wallet_rpc ( self . default_wallet_name ) . getnewaddress ( ) , self . watchonly_amount / 10 )
2015-04-24 01:42:49 -03:00
2015-12-02 14:12:23 -03:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 1.5 )
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 1.0 )
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 5.0 )
2015-04-24 01:42:49 -03:00
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2015-04-24 22:26:30 -03:00
2020-04-03 21:46:23 -03:00
wwatch . unloadwallet ( )
2019-06-26 13:49:11 -04:00
def test_simple ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn " )
2015-04-24 22:26:30 -03:00
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1.0 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
2019-02-19 19:43:44 -03:00
assert len ( dec_tx [ ' vin ' ] ) > 0 #test that we have enough inputs
2015-04-24 22:26:30 -03:00
2019-06-26 13:49:11 -04:00
def test_simple_two_coins ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with 2 coins " )
2015-04-24 22:26:30 -03:00
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 2.2 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
2019-02-19 19:43:44 -03:00
assert len ( dec_tx [ ' vin ' ] ) > 0 #test if we have enough inputs
2015-04-24 22:26:30 -03:00
assert_equal ( dec_tx [ ' vin ' ] [ 0 ] [ ' scriptSig ' ] [ ' hex ' ] , ' ' )
2019-06-26 13:49:11 -04:00
def test_simple_two_outputs ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with 2 outputs " )
2015-04-24 22:26:30 -03:00
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 2.6 , self . nodes [ 1 ] . getnewaddress ( ) : 2.5 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
2019-02-19 19:43:44 -03:00
assert len ( dec_tx [ ' vin ' ] ) > 0
2015-04-24 22:26:30 -03:00
assert_equal ( dec_tx [ ' vin ' ] [ 0 ] [ ' scriptSig ' ] [ ' hex ' ] , ' ' )
2019-06-26 13:49:11 -04:00
def test_change ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with a vin > required amount " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
2015-04-24 22:26:30 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1.0 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
fee = rawtxfund [ ' fee ' ]
2019-06-26 12:42:11 -04:00
self . test_no_change_fee = fee # Use the same fee for the next tx
2015-04-24 22:26:30 -03:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
totalOut = 0
for out in dec_tx [ ' vout ' ] :
totalOut + = out [ ' value ' ]
assert_equal ( fee + totalOut , utx [ ' amount ' ] ) #compare vin total and totalout+fee
2019-06-26 13:49:11 -04:00
def test_no_change ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn not having a change output " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
2015-04-24 22:26:30 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
2019-06-26 12:42:11 -04:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 5.0 ) - self . test_no_change_fee - self . fee_tolerance }
2015-04-24 22:26:30 -03:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
fee = rawtxfund [ ' fee ' ]
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
totalOut = 0
for out in dec_tx [ ' vout ' ] :
totalOut + = out [ ' value ' ]
assert_equal ( rawtxfund [ ' changepos ' ] , - 1 )
assert_equal ( fee + totalOut , utx [ ' amount ' ] ) #compare vin total and totalout+fee
2019-06-26 13:49:11 -04:00
def test_invalid_option ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with an invalid option " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
2016-03-29 22:04:22 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 4.0 ) }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 8 , " Unknown named parameter foo " , self . nodes [ 2 ] . fundrawtransaction , rawtx , foo = ' bar ' )
2016-03-29 22:04:22 -03:00
2018-02-11 00:12:43 -03:00
# reserveChangeKey was deprecated and is now removed
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 8 , " Unknown named parameter reserveChangeKey " , lambda : self . nodes [ 2 ] . fundrawtransaction ( hexstring = rawtx , reserveChangeKey = True ) )
2018-02-11 00:12:43 -03:00
2019-06-26 13:49:11 -04:00
def test_invalid_change_address ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with an invalid change address " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
2016-03-29 22:04:22 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 4.0 ) }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 5 , " Change address must be a valid bitcoin address " , self . nodes [ 2 ] . fundrawtransaction , rawtx , changeAddress = ' foobar ' )
2016-03-29 22:04:22 -03:00
2019-06-26 13:49:11 -04:00
def test_valid_change_address ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with a provided change address " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
2016-03-29 22:04:22 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 4.0 ) }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
change = self . nodes [ 2 ] . getnewaddress ( )
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 8 , " changePosition out of bounds " , self . nodes [ 2 ] . fundrawtransaction , rawtx , changeAddress = change , changePosition = 2 )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx , changeAddress = change , changePosition = 0 )
2016-03-29 22:04:22 -03:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
2017-01-20 18:55:26 -03:00
out = dec_tx [ ' vout ' ] [ 0 ]
2021-02-01 12:52:07 -03:00
assert_equal ( change , out [ ' scriptPubKey ' ] [ ' address ' ] )
2016-03-29 22:04:22 -03:00
2019-06-26 13:49:11 -04:00
def test_change_type ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with a provided change type " )
2018-01-24 12:28:15 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 4.0 ) }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 3 , " JSON value of type null is not of expected type string " , self . nodes [ 2 ] . fundrawtransaction , rawtx , change_type = None )
assert_raises_rpc_error ( - 5 , " Unknown change type ' ' " , self . nodes [ 2 ] . fundrawtransaction , rawtx , change_type = ' ' )
rawtx = self . nodes [ 2 ] . fundrawtransaction ( rawtx , change_type = ' bech32 ' )
2018-01-29 16:25:40 -03:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx [ ' hex ' ] )
assert_equal ( ' witness_v0_keyhash ' , dec_tx [ ' vout ' ] [ rawtx [ ' changepos ' ] ] [ ' scriptPubKey ' ] [ ' type ' ] )
2016-03-29 22:04:22 -03:00
2019-06-26 13:49:11 -04:00
def test_coin_selection ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with a vin < required amount " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 1 )
2015-04-24 22:26:30 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1.0 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
# 4-byte version + 1-byte vin count + 36-byte prevout then script_len
rawtx = rawtx [ : 82 ] + " 0100 " + rawtx [ 84 : ]
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
assert_equal ( " 00 " , dec_tx [ ' vin ' ] [ 0 ] [ ' scriptSig ' ] [ ' hex ' ] )
2020-02-20 12:19:59 -03:00
# Should fail without add_inputs:
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 4 , ERR_NOT_ENOUGH_PRESET_INPUTS , self . nodes [ 2 ] . fundrawtransaction , rawtx , add_inputs = False )
2020-02-20 12:19:59 -03:00
# add_inputs is enabled by default
2015-04-24 22:26:30 -03:00
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
2020-02-20 12:19:59 -03:00
2015-04-24 22:26:30 -03:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
matchingOuts = 0
for i , out in enumerate ( dec_tx [ ' vout ' ] ) :
2021-02-01 12:52:07 -03:00
if out [ ' scriptPubKey ' ] [ ' address ' ] in outputs :
2015-04-24 22:26:30 -03:00
matchingOuts + = 1
else :
assert_equal ( i , rawtxfund [ ' changepos ' ] )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
assert_equal ( " 00 " , dec_tx [ ' vin ' ] [ 0 ] [ ' scriptSig ' ] [ ' hex ' ] )
assert_equal ( matchingOuts , 1 )
assert_equal ( len ( dec_tx [ ' vout ' ] ) , 2 )
2019-06-26 13:49:11 -04:00
def test_two_vin ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with 2 vins " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 1 )
utx2 = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
2015-04-24 22:26:30 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } , { ' txid ' : utx2 [ ' txid ' ] , ' vout ' : utx2 [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 6.0 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2020-02-20 12:19:59 -03:00
# Should fail without add_inputs:
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 4 , ERR_NOT_ENOUGH_PRESET_INPUTS , self . nodes [ 2 ] . fundrawtransaction , rawtx , add_inputs = False )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx , add_inputs = True )
2015-04-24 22:26:30 -03:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
matchingOuts = 0
for out in dec_tx [ ' vout ' ] :
2021-02-01 12:52:07 -03:00
if out [ ' scriptPubKey ' ] [ ' address ' ] in outputs :
2015-04-24 22:26:30 -03:00
matchingOuts + = 1
assert_equal ( matchingOuts , 1 )
assert_equal ( len ( dec_tx [ ' vout ' ] ) , 2 )
matchingIns = 0
for vinOut in dec_tx [ ' vin ' ] :
for vinIn in inputs :
if vinIn [ ' txid ' ] == vinOut [ ' txid ' ] :
matchingIns + = 1
assert_equal ( matchingIns , 2 ) #we now must see two vins identical to vins given as params
2019-06-26 13:49:11 -04:00
def test_two_vin_two_vout ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with 2 vins and 2 vouts " )
2016-04-16 13:36:39 -03:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 1 )
utx2 = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 5 )
2015-04-24 22:26:30 -03:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } , { ' txid ' : utx2 [ ' txid ' ] , ' vout ' : utx2 [ ' vout ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 6.0 , self . nodes [ 0 ] . getnewaddress ( ) : 1.0 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2020-02-20 12:19:59 -03:00
# Should fail without add_inputs:
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 4 , ERR_NOT_ENOUGH_PRESET_INPUTS , self . nodes [ 2 ] . fundrawtransaction , rawtx , add_inputs = False )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx , add_inputs = True )
2020-02-20 12:19:59 -03:00
2015-04-24 22:26:30 -03:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
matchingOuts = 0
for out in dec_tx [ ' vout ' ] :
2021-02-01 12:52:07 -03:00
if out [ ' scriptPubKey ' ] [ ' address ' ] in outputs :
2015-04-24 22:26:30 -03:00
matchingOuts + = 1
assert_equal ( matchingOuts , 2 )
assert_equal ( len ( dec_tx [ ' vout ' ] ) , 3 )
2019-06-26 13:49:11 -04:00
def test_invalid_input ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with an invalid vin " )
2022-08-05 11:51:44 -04:00
txid = " 1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1 "
vout = 0
inputs = [ { ' txid ' : txid , ' vout ' : vout } ] #invalid vin!
2015-04-24 22:26:30 -03:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1.0 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
2022-07-22 15:30:10 -04:00
assert_raises_rpc_error ( - 4 , " Unable to find UTXO for external input " , self . nodes [ 2 ] . fundrawtransaction , rawtx )
2015-04-24 22:26:30 -03:00
2019-06-26 13:49:11 -04:00
def test_fee_p2pkh ( self ) :
2019-11-01 07:51:49 -03:00
""" Compare fee of a standard pubkeyhash transaction. """
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn p2pkh fee " )
2021-08-26 19:21:25 -04:00
self . lock_outputs_type ( self . nodes [ 0 ] , " p2pkh " )
2015-04-24 22:26:30 -03:00
inputs = [ ]
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 1.1 }
2017-03-26 02:47:27 -03:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Create same transaction over sendtoaddress.
2015-12-02 14:12:23 -03:00
txId = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , 1.1 )
2021-08-12 09:37:06 -04:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fees ' ] [ ' base ' ]
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Compare fee.
2015-12-02 14:12:23 -03:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-06-26 12:42:11 -04:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-24 22:26:30 -03:00
2021-08-26 19:21:25 -04:00
self . unlock_utxos ( self . nodes [ 0 ] )
2019-06-26 13:49:11 -04:00
def test_fee_p2pkh_multi_out ( self ) :
2019-11-01 07:51:49 -03:00
""" Compare fee of a standard pubkeyhash transaction with multiple outputs. """
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn p2pkh fee with multiple outputs " )
2021-08-26 19:21:25 -04:00
self . lock_outputs_type ( self . nodes [ 0 ] , " p2pkh " )
2015-04-24 22:26:30 -03:00
inputs = [ ]
2019-10-31 07:46:57 -03:00
outputs = {
self . nodes [ 1 ] . getnewaddress ( ) : 1.1 ,
self . nodes [ 1 ] . getnewaddress ( ) : 1.2 ,
self . nodes [ 1 ] . getnewaddress ( ) : 0.1 ,
self . nodes [ 1 ] . getnewaddress ( ) : 1.3 ,
self . nodes [ 1 ] . getnewaddress ( ) : 0.2 ,
self . nodes [ 1 ] . getnewaddress ( ) : 0.3 ,
}
2017-03-26 02:47:27 -03:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2019-11-01 07:51:49 -03:00
# Create same transaction over sendtoaddress.
2015-12-02 14:12:23 -03:00
txId = self . nodes [ 0 ] . sendmany ( " " , outputs )
2021-08-12 09:37:06 -04:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fees ' ] [ ' base ' ]
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Compare fee.
2015-12-02 14:12:23 -03:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-06-26 12:42:11 -04:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-24 22:26:30 -03:00
2021-08-26 19:21:25 -04:00
self . unlock_utxos ( self . nodes [ 0 ] )
2019-06-26 13:49:11 -04:00
def test_fee_p2sh ( self ) :
2019-11-01 07:51:49 -03:00
""" Compare fee of a 2-of-2 multisig p2sh transaction. """
2021-08-26 19:21:25 -04:00
self . lock_outputs_type ( self . nodes [ 0 ] , " p2pkh " )
2019-11-01 07:51:49 -03:00
# Create 2-of-2 addr.
2015-04-24 22:26:30 -03:00
addr1 = self . nodes [ 1 ] . getnewaddress ( )
addr2 = self . nodes [ 1 ] . getnewaddress ( )
2018-02-09 13:12:27 -03:00
addr1Obj = self . nodes [ 1 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 1 ] . getaddressinfo ( addr2 )
2015-04-24 22:26:30 -03:00
2020-04-03 21:46:23 -03:00
mSigObj = self . nodes [ 3 ] . createmultisig ( 2 , [ addr1Obj [ ' pubkey ' ] , addr2Obj [ ' pubkey ' ] ] ) [ ' address ' ]
2015-04-24 22:26:30 -03:00
inputs = [ ]
outputs = { mSigObj : 1.1 }
2017-03-26 02:47:27 -03:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Create same transaction over sendtoaddress.
2015-12-02 14:12:23 -03:00
txId = self . nodes [ 0 ] . sendtoaddress ( mSigObj , 1.1 )
2021-08-12 09:37:06 -04:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fees ' ] [ ' base ' ]
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Compare fee.
2015-12-02 14:12:23 -03:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-06-26 12:42:11 -04:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-24 22:26:30 -03:00
2021-08-26 19:21:25 -04:00
self . unlock_utxos ( self . nodes [ 0 ] )
2019-06-26 13:49:11 -04:00
def test_fee_4of5 ( self ) :
2019-11-01 07:51:49 -03:00
""" Compare fee of a standard pubkeyhash transaction. """
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn fee with 4-of-5 addresses " )
2021-08-26 19:21:25 -04:00
self . lock_outputs_type ( self . nodes [ 0 ] , " p2pkh " )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Create 4-of-5 addr.
2015-04-24 22:26:30 -03:00
addr1 = self . nodes [ 1 ] . getnewaddress ( )
addr2 = self . nodes [ 1 ] . getnewaddress ( )
addr3 = self . nodes [ 1 ] . getnewaddress ( )
addr4 = self . nodes [ 1 ] . getnewaddress ( )
addr5 = self . nodes [ 1 ] . getnewaddress ( )
2018-02-09 13:12:27 -03:00
addr1Obj = self . nodes [ 1 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 1 ] . getaddressinfo ( addr2 )
addr3Obj = self . nodes [ 1 ] . getaddressinfo ( addr3 )
addr4Obj = self . nodes [ 1 ] . getaddressinfo ( addr4 )
addr5Obj = self . nodes [ 1 ] . getaddressinfo ( addr5 )
2015-04-24 22:26:30 -03:00
2020-04-03 21:46:23 -03:00
mSigObj = self . nodes [ 1 ] . createmultisig (
2019-10-31 07:46:57 -03:00
4 ,
[
addr1Obj [ ' pubkey ' ] ,
addr2Obj [ ' pubkey ' ] ,
addr3Obj [ ' pubkey ' ] ,
addr4Obj [ ' pubkey ' ] ,
addr5Obj [ ' pubkey ' ] ,
]
) [ ' address ' ]
2015-04-24 22:26:30 -03:00
inputs = [ ]
outputs = { mSigObj : 1.1 }
2017-03-26 02:47:27 -03:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Create same transaction over sendtoaddress.
2015-12-02 14:12:23 -03:00
txId = self . nodes [ 0 ] . sendtoaddress ( mSigObj , 1.1 )
2021-08-12 09:37:06 -04:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fees ' ] [ ' base ' ]
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Compare fee.
2015-12-02 14:12:23 -03:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-06-26 12:42:11 -04:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-24 22:26:30 -03:00
2021-08-26 19:21:25 -04:00
self . unlock_utxos ( self . nodes [ 0 ] )
2019-06-26 13:49:11 -04:00
def test_spend_2of2 ( self ) :
2019-11-01 07:51:49 -03:00
""" Spend a 2-of-2 multisig transaction over fundraw. """
2020-04-03 21:46:23 -03:00
self . log . info ( " Test fundpsbt spending 2-of-2 multisig " )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Create 2-of-2 addr.
2015-04-24 22:26:30 -03:00
addr1 = self . nodes [ 2 ] . getnewaddress ( )
addr2 = self . nodes [ 2 ] . getnewaddress ( )
2018-02-09 13:12:27 -03:00
addr1Obj = self . nodes [ 2 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 2 ] . getaddressinfo ( addr2 )
2015-04-24 22:26:30 -03:00
2020-04-03 21:46:23 -03:00
self . nodes [ 2 ] . createwallet ( wallet_name = ' wmulti ' , disable_private_keys = True )
wmulti = self . nodes [ 2 ] . get_wallet_rpc ( ' wmulti ' )
w2 = self . nodes [ 2 ] . get_wallet_rpc ( self . default_wallet_name )
mSigObj = wmulti . addmultisigaddress (
2019-10-31 07:46:57 -03:00
2 ,
[
addr1Obj [ ' pubkey ' ] ,
addr2Obj [ ' pubkey ' ] ,
]
) [ ' address ' ]
2020-04-03 21:46:23 -03:00
if not self . options . descriptors :
wmulti . importaddress ( mSigObj )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Send 1.2 BTC to msig addr.
2019-06-26 13:49:11 -04:00
self . nodes [ 0 ] . sendtoaddress ( mSigObj , 1.2 )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2015-04-24 22:26:30 -03:00
oldBalance = self . nodes [ 1 ] . getbalance ( )
inputs = [ ]
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 1.1 }
2022-11-10 14:04:07 -03:00
funded_psbt = wmulti . walletcreatefundedpsbt ( inputs = inputs , outputs = outputs , changeAddress = w2 . getrawchangeaddress ( ) ) [ ' psbt ' ]
2015-04-24 22:26:30 -03:00
2020-04-03 21:46:23 -03:00
signed_psbt = w2 . walletprocesspsbt ( funded_psbt )
2023-09-05 13:10:21 -03:00
self . nodes [ 2 ] . sendrawtransaction ( signed_psbt [ ' hex ' ] )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 2 ] , 1 )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Make sure funds are received at node1.
2015-04-24 22:26:30 -03:00
assert_equal ( oldBalance + Decimal ( ' 1.10000000 ' ) , self . nodes [ 1 ] . getbalance ( ) )
2020-04-03 21:46:23 -03:00
wmulti . unloadwallet ( )
2019-06-26 13:49:11 -04:00
def test_locked_wallet ( self ) :
2020-04-03 21:46:23 -03:00
self . log . info ( " Test fundrawtxn with locked wallet and hardened derivation " )
2019-10-31 07:46:57 -03:00
2023-07-24 09:48:29 -04:00
df_wallet = self . nodes [ 1 ] . get_wallet_rpc ( self . default_wallet_name )
self . nodes [ 1 ] . createwallet ( wallet_name = " locked_wallet " , descriptors = self . options . descriptors )
wallet = self . nodes [ 1 ] . get_wallet_rpc ( " locked_wallet " )
2023-08-04 22:53:00 -04:00
# This test is not meant to exercise fee estimation. Making sure all txs are sent at a consistent fee rate.
wallet . settxfee ( self . min_relay_tx_fee )
2023-07-24 09:48:29 -04:00
# Add some balance to the wallet (this will be reverted at the end of the test)
df_wallet . sendall ( recipients = [ wallet . getnewaddress ( ) ] )
self . generate ( self . nodes [ 1 ] , 1 )
# Encrypt wallet and import descriptors
wallet . encryptwallet ( " test " )
2015-04-24 22:26:30 -03:00
2020-04-03 21:46:23 -03:00
if self . options . descriptors :
2023-10-09 09:30:40 -03:00
with WalletUnlock ( wallet , " test " ) :
wallet . importdescriptors ( [ {
' desc ' : descsum_create ( ' wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h) ' ) ,
' timestamp ' : ' now ' ,
' active ' : True
} ,
{
' desc ' : descsum_create ( ' wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h) ' ) ,
' timestamp ' : ' now ' ,
' active ' : True ,
' internal ' : True
} ] )
2020-04-03 21:46:23 -03:00
2019-11-01 07:51:49 -03:00
# Drain the keypool.
2023-07-24 09:48:29 -04:00
wallet . getnewaddress ( )
wallet . getrawchangeaddress ( )
2021-08-26 20:32:18 -04:00
2023-07-24 09:48:29 -04:00
# Choose input
inputs = wallet . listunspent ( )
2023-08-04 22:53:00 -04:00
# Deduce exact fee to produce a changeless transaction
tx_size = 110 # Total tx size: 110 vbytes, p2wpkh -> p2wpkh. Input 68 vbytes + rest of tx is 42 vbytes.
value = inputs [ 0 ] [ " amount " ] - get_fee ( tx_size , self . min_relay_tx_fee )
2021-08-26 20:32:18 -04:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : value }
2023-07-24 09:48:29 -04:00
rawtx = wallet . createrawtransaction ( inputs , outputs )
2019-10-23 10:21:50 -03:00
# fund a transaction that does not require a new key for the change output
2023-07-24 09:48:29 -04:00
funded_tx = wallet . fundrawtransaction ( rawtx )
assert_equal ( funded_tx [ " changepos " ] , - 1 )
2019-10-23 10:21:50 -03:00
2016-12-07 11:20:40 -03:00
# fund a transaction that requires a new key for the change output
# creating the key must be impossible because the wallet is locked
2021-08-26 20:32:18 -04:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : value - Decimal ( " 0.1 " ) }
2023-07-24 09:48:29 -04:00
rawtx = wallet . createrawtransaction ( inputs , outputs )
assert_raises_rpc_error ( - 4 , " Transaction needs a change address, but we can ' t generate it. " , wallet . fundrawtransaction , rawtx )
2016-12-07 11:20:40 -03:00
2019-11-01 07:51:49 -03:00
# Refill the keypool.
2023-10-09 09:30:40 -03:00
with WalletUnlock ( wallet , " test " ) :
wallet . keypoolrefill ( 8 ) #need to refill the keypool to get an internal change address
2016-12-07 11:20:40 -03:00
2023-07-24 09:48:29 -04:00
assert_raises_rpc_error ( - 13 , " walletpassphrase " , wallet . sendtoaddress , self . nodes [ 0 ] . getnewaddress ( ) , 1.2 )
2015-04-24 22:26:30 -03:00
oldBalance = self . nodes [ 0 ] . getbalance ( )
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1.1 }
2023-07-24 09:48:29 -04:00
rawtx = wallet . createrawtransaction ( inputs , outputs )
fundedTx = wallet . fundrawtransaction ( rawtx )
assert fundedTx [ " changepos " ] != - 1
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Now we need to unlock.
2023-10-09 09:30:40 -03:00
with WalletUnlock ( wallet , " test " ) :
signedTx = wallet . signrawtransactionwithwallet ( fundedTx [ ' hex ' ] )
wallet . sendrawtransaction ( signedTx [ ' hex ' ] )
self . generate ( self . nodes [ 1 ] , 1 )
2015-04-24 22:26:30 -03:00
2023-10-09 09:30:40 -03:00
# Make sure funds are received at node1.
assert_equal ( oldBalance + Decimal ( ' 51.10000000 ' ) , self . nodes [ 0 ] . getbalance ( ) )
2015-04-24 22:26:30 -03:00
2023-10-09 09:30:40 -03:00
# Restore pre-test wallet state
wallet . sendall ( recipients = [ df_wallet . getnewaddress ( ) , df_wallet . getnewaddress ( ) , df_wallet . getnewaddress ( ) ] )
2023-07-24 09:48:29 -04:00
wallet . unloadwallet ( )
self . generate ( self . nodes [ 1 ] , 1 )
2019-06-26 13:49:11 -04:00
def test_many_inputs_fee ( self ) :
2019-11-01 07:51:49 -03:00
""" Multiple (~19) inputs tx test | Compare fee. """
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn fee with many inputs " )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Empty node1, send some small coins from node0 to node1.
2022-08-29 13:33:16 -04:00
self . nodes [ 1 ] . sendall ( recipients = [ self . nodes [ 0 ] . getnewaddress ( ) ] )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 1 ] , 1 )
2015-04-24 22:26:30 -03:00
2020-08-02 19:10:56 -04:00
for _ in range ( 20 ) :
2015-12-02 14:12:23 -03:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , 0.01 )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Fund a tx with ~20 small inputs.
2015-04-24 22:26:30 -03:00
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 0.15 , self . nodes [ 0 ] . getnewaddress ( ) : 0.04 }
2017-03-26 02:47:27 -03:00
rawtx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 1 ] . fundrawtransaction ( rawtx )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Create same transaction over sendtoaddress.
2015-12-02 14:12:23 -03:00
txId = self . nodes [ 1 ] . sendmany ( " " , outputs )
2021-08-12 09:37:06 -04:00
signedFee = self . nodes [ 1 ] . getmempoolentry ( txId ) [ ' fees ' ] [ ' base ' ]
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Compare fee.
2015-12-02 14:12:23 -03:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-06-26 12:42:11 -04:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance * 19 #~19 inputs
2015-04-24 22:26:30 -03:00
2019-06-26 13:49:11 -04:00
def test_many_inputs_send ( self ) :
2019-11-01 07:51:49 -03:00
""" Multiple (~19) inputs tx test | sign/send. """
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn sign+send with many inputs " )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Again, empty node1, send some small coins from node0 to node1.
2022-08-29 13:33:16 -04:00
self . nodes [ 1 ] . sendall ( recipients = [ self . nodes [ 0 ] . getnewaddress ( ) ] )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 1 ] , 1 )
2015-04-24 22:26:30 -03:00
2020-08-02 19:10:56 -04:00
for _ in range ( 20 ) :
2015-12-02 14:12:23 -03:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , 0.01 )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2015-04-24 22:26:30 -03:00
2019-11-01 07:51:49 -03:00
# Fund a tx with ~20 small inputs.
2015-04-24 22:26:30 -03:00
oldBalance = self . nodes [ 0 ] . getbalance ( )
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 0.15 , self . nodes [ 0 ] . getnewaddress ( ) : 0.04 }
2017-03-26 02:47:27 -03:00
rawtx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 1 ] . fundrawtransaction ( rawtx )
2017-09-05 20:49:18 -03:00
fundedAndSignedTx = self . nodes [ 1 ] . signrawtransactionwithwallet ( fundedTx [ ' hex ' ] )
2019-06-26 13:49:11 -04:00
self . nodes [ 1 ] . sendrawtransaction ( fundedAndSignedTx [ ' hex ' ] )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 1 ] , 1 )
2015-04-24 22:26:30 -03:00
assert_equal ( oldBalance + Decimal ( ' 50.19000000 ' ) , self . nodes [ 0 ] . getbalance ( ) ) #0.19+block reward
2019-06-26 13:49:11 -04:00
def test_op_return ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn with OP_RETURN and no vin " )
2015-03-25 06:04:02 -03:00
rawtx = " 0100000000010000000000000000066a047465737400000000 "
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( len ( dec_tx [ ' vin ' ] ) , 0 )
assert_equal ( len ( dec_tx [ ' vout ' ] ) , 1 )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
assert_greater_than ( len ( dec_tx [ ' vin ' ] ) , 0 ) # at least one vin
assert_equal ( len ( dec_tx [ ' vout ' ] ) , 2 ) # one change output added
2019-06-26 13:49:11 -04:00
def test_watchonly ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn using only watchonly " )
2015-04-24 01:42:49 -03:00
inputs = [ ]
2019-06-26 12:42:11 -04:00
outputs = { self . nodes [ 2 ] . getnewaddress ( ) : self . watchonly_amount / 2 }
2015-04-24 01:42:49 -03:00
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
2020-04-03 21:46:23 -03:00
self . nodes [ 3 ] . loadwallet ( ' wwatch ' )
wwatch = self . nodes [ 3 ] . get_wallet_rpc ( ' wwatch ' )
# Setup change addresses for the watchonly wallet
desc_import = [ {
" desc " : descsum_create ( " wpkh(tpubD6NzVbkrYhZ4YNXVQbNhMK1WqguFsUXceaVJKbmno2aZ3B6QfbMeraaYvnBSGpV3vxLyTTK9DYT1yoEck4XUScMzXoQ2U2oSmE2JyMedq3H/1/*) " ) ,
" timestamp " : " now " ,
" internal " : True ,
" active " : True ,
" keypool " : True ,
" range " : [ 0 , 100 ] ,
" watchonly " : True ,
} ]
if self . options . descriptors :
wwatch . importdescriptors ( desc_import )
else :
wwatch . importmulti ( desc_import )
# Backward compatibility test (2nd params is includeWatching)
result = wwatch . fundrawtransaction ( rawtx , True )
2015-04-24 01:42:49 -03:00
res_dec = self . nodes [ 0 ] . decoderawtransaction ( result [ " hex " ] )
assert_equal ( len ( res_dec [ " vin " ] ) , 1 )
2023-08-13 10:02:10 -04:00
assert_equal ( res_dec [ " vin " ] [ 0 ] [ " txid " ] , self . watchonly_utxo [ ' txid ' ] )
2015-04-24 01:42:49 -03:00
2019-02-19 19:43:44 -03:00
assert " fee " in result . keys ( )
2015-04-24 01:42:49 -03:00
assert_greater_than ( result [ " changepos " ] , - 1 )
2020-04-03 21:46:23 -03:00
wwatch . unloadwallet ( )
2019-06-26 13:49:11 -04:00
def test_all_watched_funds ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn using entirety of watched funds " )
2015-04-24 01:42:49 -03:00
inputs = [ ]
2019-06-26 12:42:11 -04:00
outputs = { self . nodes [ 2 ] . getnewaddress ( ) : self . watchonly_amount }
2015-04-24 01:42:49 -03:00
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
2020-04-03 21:46:23 -03:00
self . nodes [ 3 ] . loadwallet ( ' wwatch ' )
wwatch = self . nodes [ 3 ] . get_wallet_rpc ( ' wwatch ' )
w3 = self . nodes [ 3 ] . get_wallet_rpc ( self . default_wallet_name )
2022-11-10 14:04:07 -03:00
result = wwatch . fundrawtransaction ( rawtx , includeWatching = True , changeAddress = w3 . getrawchangeaddress ( ) , subtractFeeFromOutputs = [ 0 ] )
2015-04-24 01:42:49 -03:00
res_dec = self . nodes [ 0 ] . decoderawtransaction ( result [ " hex " ] )
2020-04-03 21:46:23 -03:00
assert_equal ( len ( res_dec [ " vin " ] ) , 1 )
2023-08-13 10:02:10 -04:00
assert res_dec [ " vin " ] [ 0 ] [ " txid " ] == self . watchonly_utxo [ ' txid ' ]
2015-04-24 01:42:49 -03:00
assert_greater_than ( result [ " fee " ] , 0 )
2020-04-03 21:46:23 -03:00
assert_equal ( result [ " changepos " ] , - 1 )
assert_equal ( result [ " fee " ] + res_dec [ " vout " ] [ 0 ] [ " value " ] , self . watchonly_amount )
2015-04-24 01:42:49 -03:00
2020-04-03 21:46:23 -03:00
signedtx = wwatch . signrawtransactionwithwallet ( result [ " hex " ] )
2019-02-19 19:43:44 -03:00
assert not signedtx [ " complete " ]
2017-09-05 20:49:18 -03:00
signedtx = self . nodes [ 0 ] . signrawtransactionwithwallet ( signedtx [ " hex " ] )
2019-02-19 19:43:44 -03:00
assert signedtx [ " complete " ]
2015-04-24 01:42:49 -03:00
self . nodes [ 0 ] . sendrawtransaction ( signedtx [ " hex " ] )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2016-06-13 13:52:01 -04:00
2020-04-03 21:46:23 -03:00
wwatch . unloadwallet ( )
2019-06-26 13:49:11 -04:00
def test_option_feerate ( self ) :
2020-11-04 09:13:17 -03:00
self . log . info ( " Test fundrawtxn with explicit fee rates (fee_rate sat/vB and feeRate BTC/kvB) " )
2020-10-19 06:35:10 -03:00
node = self . nodes [ 3 ]
# Make sure there is exactly one input so coin selection can't skew the result.
2020-11-04 09:13:17 -03:00
assert_equal ( len ( self . nodes [ 3 ] . listunspent ( 1 ) ) , 1 )
2020-10-19 06:35:10 -03:00
inputs = [ ]
outputs = { node . getnewaddress ( ) : 1 }
rawtx = node . createrawtransaction ( inputs , outputs )
2020-11-04 09:13:17 -03:00
result = node . fundrawtransaction ( rawtx ) # uses self.min_relay_tx_fee (set by settxfee)
btc_kvb_to_sat_vb = 100000 # (1e5)
2022-11-10 14:04:07 -03:00
result1 = node . fundrawtransaction ( rawtx , fee_rate = str ( 2 * btc_kvb_to_sat_vb * self . min_relay_tx_fee ) )
result2 = node . fundrawtransaction ( rawtx , feeRate = 2 * self . min_relay_tx_fee )
result3 = node . fundrawtransaction ( rawtx , fee_rate = 10 * btc_kvb_to_sat_vb * self . min_relay_tx_fee )
result4 = node . fundrawtransaction ( rawtx , feeRate = str ( 10 * self . min_relay_tx_fee ) )
2020-11-19 14:38:00 -03:00
2020-11-04 09:13:17 -03:00
result_fee_rate = result [ ' fee ' ] * 1000 / count_bytes ( result [ ' hex ' ] )
2021-04-27 11:13:38 -04:00
assert_fee_amount ( result1 [ ' fee ' ] , count_bytes ( result1 [ ' hex ' ] ) , 2 * result_fee_rate )
2020-11-04 09:13:17 -03:00
assert_fee_amount ( result2 [ ' fee ' ] , count_bytes ( result2 [ ' hex ' ] ) , 2 * result_fee_rate )
assert_fee_amount ( result3 [ ' fee ' ] , count_bytes ( result3 [ ' hex ' ] ) , 10 * result_fee_rate )
2021-04-27 11:13:38 -04:00
assert_fee_amount ( result4 [ ' fee ' ] , count_bytes ( result4 [ ' hex ' ] ) , 10 * result_fee_rate )
2020-12-04 07:28:47 -03:00
# Test that funding non-standard "zero-fee" transactions is valid.
for param , zero_value in product ( [ " fee_rate " , " feeRate " ] , [ 0 , 0.000 , 0.00000000 , " 0 " , " 0.000 " , " 0.00000000 " ] ) :
assert_equal ( self . nodes [ 3 ] . fundrawtransaction ( rawtx , { param : zero_value } ) [ " fee " ] , 0 )
2020-10-19 06:35:10 -03:00
2021-12-09 14:28:19 -03:00
# With no arguments passed, expect fee of 141 satoshis.
assert_approx ( node . fundrawtransaction ( rawtx ) [ " fee " ] , vexp = 0.00000141 , vspan = 0.00000001 )
# Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.
2022-11-10 14:04:07 -03:00
result = node . fundrawtransaction ( rawtx , fee_rate = 10000 )
2021-12-09 14:28:19 -03:00
assert_approx ( result [ " fee " ] , vexp = 0.0141 , vspan = 0.0001 )
2020-10-19 06:35:10 -03:00
self . log . info ( " Test fundrawtxn with invalid estimate_mode settings " )
for k , v in { " number " : 42 , " object " : { " foo " : " bar " } } . items ( ) :
2022-10-04 13:51:24 -03:00
assert_raises_rpc_error ( - 3 , f " JSON value of type { k } for field estimate_mode is not of expected type string " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , estimate_mode = v , conf_target = 0.1 , add_inputs = True )
2020-11-04 09:13:17 -03:00
for mode in [ " " , " foo " , Decimal ( " 3.141592 " ) ] :
2020-11-10 08:29:01 -03:00
assert_raises_rpc_error ( - 8 , ' Invalid estimate_mode parameter, must be one of: " unset " , " economical " , " conservative " ' ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , estimate_mode = mode , conf_target = 0.1 , add_inputs = True )
2020-10-19 06:35:10 -03:00
self . log . info ( " Test fundrawtxn with invalid conf_target settings " )
2020-11-04 09:13:17 -03:00
for mode in [ " unset " , " economical " , " conservative " ] :
2020-10-19 06:35:10 -03:00
self . log . debug ( " {} " . format ( mode ) )
for k , v in { " string " : " " , " object " : { " foo " : " bar " } } . items ( ) :
2022-10-04 13:51:24 -03:00
assert_raises_rpc_error ( - 3 , f " JSON value of type { k } for field conf_target is not of expected type number " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , estimate_mode = mode , conf_target = v , add_inputs = True )
2020-11-04 09:13:17 -03:00
for n in [ - 1 , 0 , 1009 ] :
assert_raises_rpc_error ( - 8 , " Invalid conf_target, must be between 1 and 1008 " , # max value of 1008 per src/policy/fees.h
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , estimate_mode = mode , conf_target = n , add_inputs = True )
2020-11-04 09:13:17 -03:00
self . log . info ( " Test invalid fee rate settings " )
for param , value in { ( " fee_rate " , 100000 ) , ( " feeRate " , 1.000 ) } :
assert_raises_rpc_error ( - 4 , " Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate) " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , add_inputs = True , * * { param : value } )
2020-11-04 09:13:17 -03:00
assert_raises_rpc_error ( - 3 , " Amount out of range " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , add_inputs = True , * * { param : - 1 } )
2020-11-04 09:13:17 -03:00
assert_raises_rpc_error ( - 3 , " Amount is not a number or string " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , add_inputs = True , * * { param : { " foo " : " bar " } } )
2020-12-03 06:55:15 -03:00
# Test fee rate values that don't pass fixed-point parsing checks.
for invalid_value in [ " " , 0.000000001 , 1e-09 , 1.111111111 , 1111111111111111 , " 31.999999999999999999999 " ] :
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 3 , " Invalid amount " , node . fundrawtransaction , rawtx , add_inputs = True , * * { param : invalid_value } )
2020-12-02 09:55:26 -03:00
# Test fee_rate values that cannot be represented in sat/vB.
2023-07-26 21:14:19 -04:00
for invalid_value in [ 0.0001 , 0.00000001 , 0.00099999 , 31.99999999 ] :
2020-12-02 09:55:26 -03:00
assert_raises_rpc_error ( - 3 , " Invalid amount " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , fee_rate = invalid_value , add_inputs = True )
2020-11-04 09:13:17 -03:00
2020-11-12 07:16:48 -03:00
self . log . info ( " Test min fee rate checks are bypassed with fundrawtxn, e.g. a fee_rate under 1 sat/vB is allowed " )
2022-11-10 14:04:07 -03:00
node . fundrawtransaction ( rawtx , fee_rate = 0.999 , add_inputs = True )
node . fundrawtransaction ( rawtx , feeRate = 0.00000999 , add_inputs = True )
2020-11-04 09:13:17 -03:00
self . log . info ( " - raises RPC error if both feeRate and fee_rate are passed " )
assert_raises_rpc_error ( - 8 , " Cannot specify both fee_rate (sat/vB) and feeRate (BTC/kvB) " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , fee_rate = 0.1 , feeRate = 0.1 , add_inputs = True )
2020-11-04 09:13:17 -03:00
self . log . info ( " - raises RPC error if both feeRate and estimate_mode passed " )
assert_raises_rpc_error ( - 8 , " Cannot specify both estimate_mode and feeRate " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , estimate_mode = " economical " , feeRate = 0.1 , add_inputs = True )
2020-11-04 09:13:17 -03:00
for param in [ " feeRate " , " fee_rate " ] :
self . log . info ( " - raises RPC error if both {} and conf_target are passed " . format ( param ) )
assert_raises_rpc_error ( - 8 , " Cannot specify both conf_target and {} . Please provide either a confirmation "
" target in blocks for automatic fee estimation, or an explicit fee rate. " . format ( param ) ,
node . fundrawtransaction , rawtx , { param : 1 , " conf_target " : 1 , " add_inputs " : True } )
self . log . info ( " - raises RPC error if both fee_rate and estimate_mode are passed " )
assert_raises_rpc_error ( - 8 , " Cannot specify both estimate_mode and fee_rate " ,
2022-11-10 14:04:07 -03:00
node . fundrawtransaction , rawtx , fee_rate = 1 , estimate_mode = " economical " , add_inputs = True )
2020-10-19 06:35:10 -03:00
2019-06-26 13:49:11 -04:00
def test_address_reuse ( self ) :
2019-11-01 07:51:49 -03:00
""" Test no address reuse occurs. """
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn does not reuse addresses " )
2016-12-19 05:10:33 -03:00
2019-06-26 12:42:11 -04:00
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs = [ ] , outputs = { self . nodes [ 3 ] . getnewaddress ( ) : 1 } )
2016-12-19 05:10:33 -03:00
result3 = self . nodes [ 3 ] . fundrawtransaction ( rawtx )
res_dec = self . nodes [ 0 ] . decoderawtransaction ( result3 [ " hex " ] )
changeaddress = " "
for out in res_dec [ ' vout ' ] :
if out [ ' value ' ] > 1.0 :
2021-02-01 12:52:07 -03:00
changeaddress + = out [ ' scriptPubKey ' ] [ ' address ' ]
2019-02-19 19:43:44 -03:00
assert changeaddress != " "
2016-12-19 05:10:33 -03:00
nextaddr = self . nodes [ 3 ] . getnewaddress ( )
2019-11-01 07:51:49 -03:00
# Now the change address key should be removed from the keypool.
2019-02-19 19:43:44 -03:00
assert changeaddress != nextaddr
2016-12-19 05:10:33 -03:00
2019-06-26 13:49:11 -04:00
def test_option_subtract_fee_from_outputs ( self ) :
2019-10-31 07:46:57 -03:00
self . log . info ( " Test fundrawtxn subtractFeeFromOutputs option " )
2016-12-13 18:36:23 -03:00
2019-11-01 07:51:49 -03:00
# Make sure there is exactly one input so coin selection can't skew the result.
2016-12-13 18:36:23 -03:00
assert_equal ( len ( self . nodes [ 3 ] . listunspent ( 1 ) ) , 1 )
inputs = [ ]
outputs = { self . nodes [ 2 ] . getnewaddress ( ) : 1 }
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
2020-11-04 09:13:17 -03:00
# Test subtract fee from outputs with feeRate (BTC/kvB)
2019-06-26 12:42:11 -04:00
result = [ self . nodes [ 3 ] . fundrawtransaction ( rawtx ) , # uses self.min_relay_tx_fee (set by settxfee)
2022-11-10 14:04:07 -03:00
self . nodes [ 3 ] . fundrawtransaction ( rawtx , subtractFeeFromOutputs = [ ] ) , # empty subtraction list
self . nodes [ 3 ] . fundrawtransaction ( rawtx , subtractFeeFromOutputs = [ 0 ] ) , # uses self.min_relay_tx_fee (set by settxfee)
self . nodes [ 3 ] . fundrawtransaction ( rawtx , feeRate = 2 * self . min_relay_tx_fee ) ,
self . nodes [ 3 ] . fundrawtransaction ( rawtx , feeRate = 2 * self . min_relay_tx_fee , subtractFeeFromOutputs = [ 0 ] ) , ]
2020-11-04 09:13:17 -03:00
dec_tx = [ self . nodes [ 3 ] . decoderawtransaction ( tx_ [ ' hex ' ] ) for tx_ in result ]
output = [ d [ ' vout ' ] [ 1 - r [ ' changepos ' ] ] [ ' value ' ] for d , r in zip ( dec_tx , result ) ]
change = [ d [ ' vout ' ] [ r [ ' changepos ' ] ] [ ' value ' ] for d , r in zip ( dec_tx , result ) ]
2016-12-13 18:36:23 -03:00
2020-11-04 09:13:17 -03:00
assert_equal ( result [ 0 ] [ ' fee ' ] , result [ 1 ] [ ' fee ' ] , result [ 2 ] [ ' fee ' ] )
assert_equal ( result [ 3 ] [ ' fee ' ] , result [ 4 ] [ ' fee ' ] )
assert_equal ( change [ 0 ] , change [ 1 ] )
assert_equal ( output [ 0 ] , output [ 1 ] )
assert_equal ( output [ 0 ] , output [ 2 ] + result [ 2 ] [ ' fee ' ] )
assert_equal ( change [ 0 ] + result [ 0 ] [ ' fee ' ] , change [ 2 ] )
assert_equal ( output [ 3 ] , output [ 4 ] + result [ 4 ] [ ' fee ' ] )
assert_equal ( change [ 3 ] + result [ 3 ] [ ' fee ' ] , change [ 4 ] )
# Test subtract fee from outputs with fee_rate (sat/vB)
btc_kvb_to_sat_vb = 100000 # (1e5)
result = [ self . nodes [ 3 ] . fundrawtransaction ( rawtx ) , # uses self.min_relay_tx_fee (set by settxfee)
2022-11-10 14:04:07 -03:00
self . nodes [ 3 ] . fundrawtransaction ( rawtx , subtractFeeFromOutputs = [ ] ) , # empty subtraction list
self . nodes [ 3 ] . fundrawtransaction ( rawtx , subtractFeeFromOutputs = [ 0 ] ) , # uses self.min_relay_tx_fee (set by settxfee)
self . nodes [ 3 ] . fundrawtransaction ( rawtx , fee_rate = 2 * btc_kvb_to_sat_vb * self . min_relay_tx_fee ) ,
self . nodes [ 3 ] . fundrawtransaction ( rawtx , fee_rate = 2 * btc_kvb_to_sat_vb * self . min_relay_tx_fee , subtractFeeFromOutputs = [ 0 ] ) , ]
2018-01-29 16:25:40 -03:00
dec_tx = [ self . nodes [ 3 ] . decoderawtransaction ( tx_ [ ' hex ' ] ) for tx_ in result ]
2016-12-13 18:36:23 -03:00
output = [ d [ ' vout ' ] [ 1 - r [ ' changepos ' ] ] [ ' value ' ] for d , r in zip ( dec_tx , result ) ]
change = [ d [ ' vout ' ] [ r [ ' changepos ' ] ] [ ' value ' ] for d , r in zip ( dec_tx , result ) ]
assert_equal ( result [ 0 ] [ ' fee ' ] , result [ 1 ] [ ' fee ' ] , result [ 2 ] [ ' fee ' ] )
assert_equal ( result [ 3 ] [ ' fee ' ] , result [ 4 ] [ ' fee ' ] )
assert_equal ( change [ 0 ] , change [ 1 ] )
assert_equal ( output [ 0 ] , output [ 1 ] )
assert_equal ( output [ 0 ] , output [ 2 ] + result [ 2 ] [ ' fee ' ] )
assert_equal ( change [ 0 ] + result [ 0 ] [ ' fee ' ] , change [ 2 ] )
assert_equal ( output [ 3 ] , output [ 4 ] + result [ 4 ] [ ' fee ' ] )
assert_equal ( change [ 3 ] + result [ 3 ] [ ' fee ' ] , change [ 4 ] )
inputs = [ ]
outputs = { self . nodes [ 2 ] . getnewaddress ( ) : value for value in ( 1.0 , 1.1 , 1.2 , 1.3 ) }
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
result = [ self . nodes [ 3 ] . fundrawtransaction ( rawtx ) ,
2019-11-01 07:51:49 -03:00
# Split the fee between outputs 0, 2, and 3, but not output 1.
2022-11-10 14:04:07 -03:00
self . nodes [ 3 ] . fundrawtransaction ( rawtx , subtractFeeFromOutputs = [ 0 , 2 , 3 ] ) ]
2016-12-13 18:36:23 -03:00
dec_tx = [ self . nodes [ 3 ] . decoderawtransaction ( result [ 0 ] [ ' hex ' ] ) ,
self . nodes [ 3 ] . decoderawtransaction ( result [ 1 ] [ ' hex ' ] ) ]
2019-11-01 07:51:49 -03:00
# Nested list of non-change output amounts for each transaction.
2016-12-13 18:36:23 -03:00
output = [ [ out [ ' value ' ] for i , out in enumerate ( d [ ' vout ' ] ) if i != r [ ' changepos ' ] ]
for d , r in zip ( dec_tx , result ) ]
2019-11-01 07:51:49 -03:00
# List of differences in output amounts between normal and subtractFee transactions.
2016-12-13 18:36:23 -03:00
share = [ o0 - o1 for o0 , o1 in zip ( output [ 0 ] , output [ 1 ] ) ]
2019-11-01 07:51:49 -03:00
# Output 1 is the same in both transactions.
2016-12-13 18:36:23 -03:00
assert_equal ( share [ 1 ] , 0 )
2019-11-01 07:51:49 -03:00
# The other 3 outputs are smaller as a result of subtractFeeFromOutputs.
2016-12-13 18:36:23 -03:00
assert_greater_than ( share [ 0 ] , 0 )
assert_greater_than ( share [ 2 ] , 0 )
assert_greater_than ( share [ 3 ] , 0 )
2019-11-01 07:51:49 -03:00
# Outputs 2 and 3 take the same share of the fee.
2016-12-13 18:36:23 -03:00
assert_equal ( share [ 2 ] , share [ 3 ] )
2019-11-01 07:51:49 -03:00
# Output 0 takes at least as much share of the fee, and no more than 2
# satoshis more, than outputs 2 and 3.
2016-12-13 18:36:23 -03:00
assert_greater_than_or_equal ( share [ 0 ] , share [ 2 ] )
assert_greater_than_or_equal ( share [ 2 ] + Decimal ( 2e-8 ) , share [ 0 ] )
2019-11-01 07:51:49 -03:00
# The fee is the same in both transactions.
2016-12-13 18:36:23 -03:00
assert_equal ( result [ 0 ] [ ' fee ' ] , result [ 1 ] [ ' fee ' ] )
2019-11-01 07:51:49 -03:00
# The total subtracted from the outputs is equal to the fee.
2016-12-13 18:36:23 -03:00
assert_equal ( share [ 0 ] + share [ 2 ] + share [ 3 ] , result [ 0 ] [ ' fee ' ] )
2019-11-23 00:21:36 -03:00
def test_subtract_fee_with_presets ( self ) :
self . log . info ( " Test fundrawtxn subtract fee from outputs with preset inputs that are sufficient " )
addr = self . nodes [ 0 ] . getnewaddress ( )
2023-08-13 10:02:10 -04:00
utxo = self . create_outpoints ( self . nodes [ 0 ] , outputs = [ { addr : 10 } ] ) [ 0 ]
2019-11-23 00:21:36 -03:00
2023-08-13 10:02:10 -04:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( [ utxo ] , [ { self . nodes [ 0 ] . getnewaddress ( ) : 5 } ] )
2022-11-10 14:04:07 -03:00
fundedtx = self . nodes [ 0 ] . fundrawtransaction ( rawtx , subtractFeeFromOutputs = [ 0 ] )
2019-11-23 00:21:36 -03:00
signedtx = self . nodes [ 0 ] . signrawtransactionwithwallet ( fundedtx [ ' hex ' ] )
self . nodes [ 0 ] . sendrawtransaction ( signedtx [ ' hex ' ] )
2020-11-30 17:32:06 -03:00
def test_transaction_too_large ( self ) :
self . log . info ( " Test fundrawtx where BnB solution would result in a too large transaction, but Knapsack would not " )
self . nodes [ 0 ] . createwallet ( " large " )
wallet = self . nodes [ 0 ] . get_wallet_rpc ( self . default_wallet_name )
recipient = self . nodes [ 0 ] . get_wallet_rpc ( " large " )
outputs = { }
rawtx = recipient . createrawtransaction ( [ ] , { wallet . getnewaddress ( ) : 147.99899260 } )
2021-03-10 17:45:29 -03:00
# Make 1500 0.1 BTC outputs. The amount that we target for funding is in
# the BnB range when these outputs are used. However if these outputs
# are selected, the transaction will end up being too large, so it
# shouldn't use BnB and instead fall back to Knapsack but that behavior
# is not implemented yet. For now we just check that we get an error.
2022-12-16 23:13:59 -03:00
# First, force the wallet to bulk-generate the addresses we'll need.
recipient . keypoolrefill ( 1500 )
2021-03-10 17:45:29 -03:00
for _ in range ( 1500 ) :
2020-11-30 17:32:06 -03:00
outputs [ recipient . getnewaddress ( ) ] = 0.1
wallet . sendmany ( " " , outputs )
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 10 )
2022-12-07 14:35:46 -03:00
assert_raises_rpc_error ( - 4 , " The inputs size exceeds the maximum weight. "
" Please try sending a smaller amount or manually consolidating your wallet ' s UTXOs " ,
recipient . fundrawtransaction , rawtx )
2019-10-21 17:55:07 -03:00
self . nodes [ 0 ] . unloadwallet ( " large " )
def test_external_inputs ( self ) :
self . log . info ( " Test funding with external inputs " )
2023-05-23 17:38:31 -04:00
privkey , _ = generate_keypair ( wif = True )
2019-10-21 17:55:07 -03:00
self . nodes [ 2 ] . createwallet ( " extfund " )
wallet = self . nodes [ 2 ] . get_wallet_rpc ( " extfund " )
# Make a weird but signable script. sh(pkh()) descriptor accomplishes this
desc = descsum_create ( " sh(pkh( {} )) " . format ( privkey ) )
if self . options . descriptors :
res = self . nodes [ 0 ] . importdescriptors ( [ { " desc " : desc , " timestamp " : " now " } ] )
else :
res = self . nodes [ 0 ] . importmulti ( [ { " desc " : desc , " timestamp " : " now " } ] )
assert res [ 0 ] [ " success " ]
addr = self . nodes [ 0 ] . deriveaddresses ( desc ) [ 0 ]
addr_info = self . nodes [ 0 ] . getaddressinfo ( addr )
self . nodes [ 0 ] . sendtoaddress ( addr , 10 )
self . nodes [ 0 ] . sendtoaddress ( wallet . getnewaddress ( ) , 10 )
2021-10-05 20:18:33 -03:00
self . generate ( self . nodes [ 0 ] , 6 )
2019-10-21 17:55:07 -03:00
ext_utxo = self . nodes [ 0 ] . listunspent ( addresses = [ addr ] ) [ 0 ]
# An external input without solving data should result in an error
2021-10-05 17:45:10 -03:00
raw_tx = wallet . createrawtransaction ( [ ext_utxo ] , { self . nodes [ 0 ] . getnewaddress ( ) : ext_utxo [ " amount " ] / 2 } )
2022-08-05 11:51:44 -04:00
assert_raises_rpc_error ( - 4 , " Not solvable pre-selected input COutPoint( %s , %s ) " % ( ext_utxo [ " txid " ] [ 0 : 10 ] , ext_utxo [ " vout " ] ) , wallet . fundrawtransaction , raw_tx )
2019-10-21 17:55:07 -03:00
# Error conditions
2023-08-24 10:05:44 -04:00
assert_raises_rpc_error ( - 5 , ' Pubkey " not a pubkey " must be a hex string ' , wallet . fundrawtransaction , raw_tx , solving_data = { " pubkeys " : [ " not a pubkey " ] } )
assert_raises_rpc_error ( - 5 , ' Pubkey " 01234567890a0b0c0d0e0f " must have a length of either 33 or 65 bytes ' , wallet . fundrawtransaction , raw_tx , solving_data = { " pubkeys " : [ " 01234567890a0b0c0d0e0f " ] } )
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 5 , " ' not a script ' is not hex " , wallet . fundrawtransaction , raw_tx , solving_data = { " scripts " : [ " not a script " ] } )
assert_raises_rpc_error ( - 8 , " Unable to parse descriptor ' not a descriptor ' " , wallet . fundrawtransaction , raw_tx , solving_data = { " descriptors " : [ " not a descriptor " ] } )
assert_raises_rpc_error ( - 8 , " Invalid parameter, missing vout key " , wallet . fundrawtransaction , raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, vout cannot be negative " , wallet . fundrawtransaction , raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : - 1 } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, missing weight key " , wallet . fundrawtransaction , raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, weight cannot be less than 165 " , wallet . fundrawtransaction , raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : 164 } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, weight cannot be less than 165 " , wallet . fundrawtransaction , raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : - 1 } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, weight cannot be greater than " , wallet . fundrawtransaction , raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : 400001 } ] )
2019-10-21 17:55:07 -03:00
# But funding should work when the solving data is provided
2022-11-10 14:04:07 -03:00
funded_tx = wallet . fundrawtransaction ( raw_tx , solving_data = { " pubkeys " : [ addr_info [ ' pubkey ' ] ] , " scripts " : [ addr_info [ " embedded " ] [ " scriptPubKey " ] ] } )
2019-10-21 17:55:07 -03:00
signed_tx = wallet . signrawtransactionwithwallet ( funded_tx [ ' hex ' ] )
assert not signed_tx [ ' complete ' ]
signed_tx = self . nodes [ 0 ] . signrawtransactionwithwallet ( signed_tx [ ' hex ' ] )
assert signed_tx [ ' complete ' ]
2022-11-10 14:04:07 -03:00
funded_tx = wallet . fundrawtransaction ( raw_tx , solving_data = { " descriptors " : [ desc ] } )
2021-10-05 17:45:10 -03:00
signed_tx1 = wallet . signrawtransactionwithwallet ( funded_tx [ ' hex ' ] )
assert not signed_tx1 [ ' complete ' ]
signed_tx2 = self . nodes [ 0 ] . signrawtransactionwithwallet ( signed_tx1 [ ' hex ' ] )
assert signed_tx2 [ ' complete ' ]
unsigned_weight = self . nodes [ 0 ] . decoderawtransaction ( signed_tx1 [ " hex " ] ) [ " weight " ]
signed_weight = self . nodes [ 0 ] . decoderawtransaction ( signed_tx2 [ " hex " ] ) [ " weight " ]
# Input's weight is difference between weight of signed and unsigned,
# and the weight of stuff that didn't change (prevout, sequence, 1 byte of scriptSig)
input_weight = signed_weight - unsigned_weight + ( 41 * 4 )
low_input_weight = input_weight / / 2
high_input_weight = input_weight * 2
# Funding should also work if the input weight is provided
2023-10-05 20:20:30 -03:00
funded_tx = wallet . fundrawtransaction ( raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : input_weight } ] , fee_rate = 2 )
2021-10-05 17:45:10 -03:00
signed_tx = wallet . signrawtransactionwithwallet ( funded_tx [ " hex " ] )
signed_tx = self . nodes [ 0 ] . signrawtransactionwithwallet ( signed_tx [ " hex " ] )
assert_equal ( self . nodes [ 0 ] . testmempoolaccept ( [ signed_tx [ " hex " ] ] ) [ 0 ] [ " allowed " ] , True )
assert_equal ( signed_tx [ " complete " ] , True )
# Reducing the weight should have a lower fee
2023-10-05 20:20:30 -03:00
funded_tx2 = wallet . fundrawtransaction ( raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : low_input_weight } ] , fee_rate = 2 )
2021-10-05 17:45:10 -03:00
assert_greater_than ( funded_tx [ " fee " ] , funded_tx2 [ " fee " ] )
# Increasing the weight should have a higher fee
2023-10-05 20:20:30 -03:00
funded_tx2 = wallet . fundrawtransaction ( raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : high_input_weight } ] , fee_rate = 2 )
2021-10-05 17:45:10 -03:00
assert_greater_than ( funded_tx2 [ " fee " ] , funded_tx [ " fee " ] )
# The provided weight should override the calculated weight when solving data is provided
2023-10-05 20:20:30 -03:00
funded_tx3 = wallet . fundrawtransaction ( raw_tx , solving_data = { " descriptors " : [ desc ] } , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : high_input_weight } ] , fee_rate = 2 )
2021-10-05 17:45:10 -03:00
assert_equal ( funded_tx2 [ " fee " ] , funded_tx3 [ " fee " ] )
# The feerate should be met
2022-11-10 14:04:07 -03:00
funded_tx4 = wallet . fundrawtransaction ( raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : high_input_weight } ] , fee_rate = 10 )
2021-10-05 17:45:10 -03:00
input_add_weight = high_input_weight - ( 41 * 4 )
tx4_weight = wallet . decoderawtransaction ( funded_tx4 [ " hex " ] ) [ " weight " ] + input_add_weight
tx4_vsize = int ( ceil ( tx4_weight / 4 ) )
assert_fee_amount ( funded_tx4 [ " fee " ] , tx4_vsize , Decimal ( 0.0001 ) )
# Funding with weight at csuint boundaries should not cause problems
2023-10-05 20:20:30 -03:00
funded_tx = wallet . fundrawtransaction ( raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : 255 } ] , fee_rate = 2 )
funded_tx = wallet . fundrawtransaction ( raw_tx , input_weights = [ { " txid " : ext_utxo [ " txid " ] , " vout " : ext_utxo [ " vout " ] , " weight " : 65539 } ] , fee_rate = 2 )
2021-10-05 17:45:10 -03:00
2019-10-21 17:55:07 -03:00
self . nodes [ 2 ] . unloadwallet ( " extfund " )
2020-11-30 17:32:06 -03:00
2022-09-09 11:18:48 -04:00
def test_add_inputs_default_value ( self ) :
self . log . info ( " Test ' add_inputs ' default value " )
# Create and fund the wallet with 5 BTC
self . nodes [ 2 ] . createwallet ( " test_preset_inputs " )
wallet = self . nodes [ 2 ] . get_wallet_rpc ( " test_preset_inputs " )
addr1 = wallet . getnewaddress ( address_type = " bech32 " )
self . nodes [ 0 ] . sendtoaddress ( addr1 , 5 )
self . generate ( self . nodes [ 0 ] , 1 )
# Covered cases:
# 1. Default add_inputs value with no preset inputs (add_inputs=true):
# Expect: automatically add coins from the wallet to the tx.
# 2. Default add_inputs value with preset inputs (add_inputs=false):
# Expect: disallow automatic coin selection.
# 3. Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount).
# Expect: include inputs from the wallet.
# 4. Explicit add_inputs=true and preset inputs (with preset inputs covering the target amount).
# Expect: only preset inputs are used.
# 5. Explicit add_inputs=true, no preset inputs (same as (1) but with an explicit set):
# Expect: include inputs from the wallet.
2022-08-05 10:58:08 -04:00
# 6. Explicit add_inputs=false, no preset inputs:
# Expect: failure as we did not provide inputs and the process cannot automatically select coins.
2022-09-09 11:18:48 -04:00
# Case (1), 'send' command
# 'add_inputs' value is true unless "inputs" are specified, in such case, add_inputs=false.
# So, the wallet will automatically select coins and create the transaction if only the outputs are provided.
tx = wallet . send ( outputs = [ { addr1 : 3 } ] )
assert tx [ " complete " ]
# Case (2), 'send' command
# Select an input manually, which doesn't cover the entire output amount and
# verify that the dynamically set 'add_inputs=false' value works.
# Fund wallet with 2 outputs, 5 BTC each.
addr2 = wallet . getnewaddress ( address_type = " bech32 " )
2022-11-10 14:04:07 -03:00
source_tx = self . nodes [ 0 ] . send ( outputs = [ { addr1 : 5 } , { addr2 : 5 } ] , change_position = 0 )
2022-09-09 11:18:48 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
# Select only one input.
options = {
" inputs " : [
{
" txid " : source_tx [ " txid " ] ,
" vout " : 1 # change position was hardcoded to index 0
}
]
}
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 4 , ERR_NOT_ENOUGH_PRESET_INPUTS , wallet . send , outputs = [ { addr1 : 8 } ] , * * options )
2022-09-09 11:18:48 -04:00
# Case (3), Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount)
options [ " add_inputs " ] = True
options [ " add_to_wallet " ] = False
2022-11-10 14:04:07 -03:00
tx = wallet . send ( outputs = [ { addr1 : 8 } ] , * * options )
2022-09-09 11:18:48 -04:00
assert tx [ " complete " ]
# Case (4), Explicit add_inputs=true and preset inputs (with preset inputs covering the target amount)
options [ " inputs " ] . append ( {
" txid " : source_tx [ " txid " ] ,
" vout " : 2 # change position was hardcoded to index 0
} )
2022-11-10 14:04:07 -03:00
tx = wallet . send ( outputs = [ { addr1 : 8 } ] , * * options )
2022-09-09 11:18:48 -04:00
assert tx [ " complete " ]
# Check that only the preset inputs were added to the tx
decoded_psbt_inputs = self . nodes [ 0 ] . decodepsbt ( tx [ " psbt " ] ) [ ' tx ' ] [ ' vin ' ]
assert_equal ( len ( decoded_psbt_inputs ) , 2 )
for input in decoded_psbt_inputs :
assert_equal ( input [ " txid " ] , source_tx [ " txid " ] )
# Case (5), assert that inputs are added to the tx by explicitly setting add_inputs=true
options = { " add_inputs " : True , " add_to_wallet " : True }
2022-11-10 14:04:07 -03:00
tx = wallet . send ( outputs = [ { addr1 : 8 } ] , * * options )
2022-09-09 11:18:48 -04:00
assert tx [ " complete " ]
2022-08-05 10:58:08 -04:00
# 6. Explicit add_inputs=false, no preset inputs:
options = { " add_inputs " : False }
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 4 , ERR_NOT_ENOUGH_PRESET_INPUTS , wallet . send , outputs = [ { addr1 : 3 } ] , * * options )
2022-08-05 10:58:08 -04:00
2022-09-09 11:18:48 -04:00
################################################
# Case (1), 'walletcreatefundedpsbt' command
# Default add_inputs value with no preset inputs (add_inputs=true)
inputs = [ ]
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 8 }
assert " psbt " in wallet . walletcreatefundedpsbt ( inputs = inputs , outputs = outputs )
# Case (2), 'walletcreatefundedpsbt' command
# Default add_inputs value with preset inputs (add_inputs=false).
inputs = [ {
" txid " : source_tx [ " txid " ] ,
" vout " : 1 # change position was hardcoded to index 0
} ]
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 8 }
2022-12-07 14:35:46 -03:00
assert_raises_rpc_error ( - 4 , ERR_NOT_ENOUGH_PRESET_INPUTS , wallet . walletcreatefundedpsbt , inputs = inputs , outputs = outputs )
2022-09-09 11:18:48 -04:00
# Case (3), Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount)
options [ " add_inputs " ] = True
2022-11-10 14:04:07 -03:00
assert " psbt " in wallet . walletcreatefundedpsbt ( outputs = [ { addr1 : 8 } ] , inputs = inputs , * * options )
2022-09-09 11:18:48 -04:00
# Case (4), Explicit add_inputs=true and preset inputs (with preset inputs covering the target amount)
inputs . append ( {
" txid " : source_tx [ " txid " ] ,
" vout " : 2 # change position was hardcoded to index 0
} )
2022-11-10 14:04:07 -03:00
psbt_tx = wallet . walletcreatefundedpsbt ( outputs = [ { addr1 : 8 } ] , inputs = inputs , * * options )
2022-09-09 11:18:48 -04:00
# Check that only the preset inputs were added to the tx
decoded_psbt_inputs = self . nodes [ 0 ] . decodepsbt ( psbt_tx [ " psbt " ] ) [ ' tx ' ] [ ' vin ' ]
assert_equal ( len ( decoded_psbt_inputs ) , 2 )
for input in decoded_psbt_inputs :
assert_equal ( input [ " txid " ] , source_tx [ " txid " ] )
# Case (5), 'walletcreatefundedpsbt' command
# Explicit add_inputs=true, no preset inputs
options = {
" add_inputs " : True
}
2022-11-10 14:04:07 -03:00
assert " psbt " in wallet . walletcreatefundedpsbt ( inputs = [ ] , outputs = outputs , * * options )
2022-09-09 11:18:48 -04:00
2022-08-05 10:58:08 -04:00
# Case (6). Explicit add_inputs=false, no preset inputs:
options = { " add_inputs " : False }
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 4 , ERR_NOT_ENOUGH_PRESET_INPUTS , wallet . walletcreatefundedpsbt , inputs = [ ] , outputs = outputs , * * options )
2022-08-05 10:58:08 -04:00
2022-09-09 11:18:48 -04:00
self . nodes [ 2 ] . unloadwallet ( " test_preset_inputs " )
2022-11-23 10:22:50 -03:00
def test_preset_inputs_selection ( self ) :
self . log . info ( ' Test wallet preset inputs are not double-counted or reused in coin selection ' )
# Create and fund the wallet with 4 UTXO of 5 BTC each (20 BTC total)
self . nodes [ 2 ] . createwallet ( " test_preset_inputs_selection " )
wallet = self . nodes [ 2 ] . get_wallet_rpc ( " test_preset_inputs_selection " )
outputs = { }
for _ in range ( 4 ) :
outputs [ wallet . getnewaddress ( address_type = " bech32 " ) ] = 5
self . nodes [ 0 ] . sendmany ( " " , outputs )
self . generate ( self . nodes [ 0 ] , 1 )
# Select the preset inputs
coins = wallet . listunspent ( )
preset_inputs = [ coins [ 0 ] , coins [ 1 ] , coins [ 2 ] ]
# Now let's create the tx creation options
options = {
" inputs " : preset_inputs ,
" add_inputs " : True , # automatically add coins from the wallet to fulfill the target
" subtract_fee_from_outputs " : [ 0 ] , # deduct fee from first output
" add_to_wallet " : False
}
# Attempt to send 29 BTC from a wallet that only has 20 BTC. The wallet should exclude
# the preset inputs from the pool of available coins, realize that there is not enough
# money to fund the 29 BTC payment, and fail with "Insufficient funds".
#
# Even with SFFO, the wallet can only afford to send 20 BTC.
# If the wallet does not properly exclude preset inputs from the pool of available coins
# prior to coin selection, it may create a transaction that does not fund the full payment
# amount or, through SFFO, incorrectly reduce the recipient's amount by the difference
# between the original target and the wrongly counted inputs (in this case 9 BTC)
# so that the recipient's amount is no longer equal to the user's selected target of 29 BTC.
# First case, use 'subtract_fee_from_outputs = true'
assert_raises_rpc_error ( - 4 , " Insufficient funds " , wallet . send , outputs = [ { wallet . getnewaddress ( address_type = " bech32 " ) : 29 } ] , options = options )
# Second case, don't use 'subtract_fee_from_outputs'
del options [ " subtract_fee_from_outputs " ]
assert_raises_rpc_error ( - 4 , " Insufficient funds " , wallet . send , outputs = [ { wallet . getnewaddress ( address_type = " bech32 " ) : 29 } ] , options = options )
self . nodes [ 2 ] . unloadwallet ( " test_preset_inputs_selection " )
2022-03-30 04:18:33 -03:00
def test_weight_calculation ( self ) :
self . log . info ( " Test weight calculation with external inputs " )
self . nodes [ 2 ] . createwallet ( " test_weight_calculation " )
wallet = self . nodes [ 2 ] . get_wallet_rpc ( " test_weight_calculation " )
2022-07-22 15:26:40 -04:00
addr = wallet . getnewaddress ( address_type = " bech32 " )
ext_addr = self . nodes [ 0 ] . getnewaddress ( address_type = " bech32 " )
2023-08-13 10:02:10 -04:00
utxo , ext_utxo = self . create_outpoints ( self . nodes [ 0 ] , outputs = [ { addr : 5 } , { ext_addr : 5 } ] )
2022-03-30 04:18:33 -03:00
2022-07-22 15:26:40 -04:00
self . nodes [ 0 ] . sendtoaddress ( wallet . getnewaddress ( address_type = " bech32 " ) , 5 )
2022-03-30 04:18:33 -03:00
self . generate ( self . nodes [ 0 ] , 1 )
2023-08-13 10:02:10 -04:00
rawtx = wallet . createrawtransaction ( [ utxo ] , [ { self . nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) : 8 } ] )
2022-11-10 14:04:07 -03:00
fundedtx = wallet . fundrawtransaction ( rawtx , fee_rate = 10 , change_type = " bech32 " )
2022-03-30 04:18:33 -03:00
# with 71-byte signatures we should expect following tx size
2022-07-22 15:26:40 -04:00
# tx overhead (10) + 2 inputs (41 each) + 2 p2wpkh (31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 byte sig witnesses (107 each)) / witness scaling factor (4)
tx_size = ceil ( 10 + 41 * 2 + 31 * 2 + ( 2 + 107 * 2 ) / 4 )
assert_equal ( fundedtx [ ' fee ' ] * COIN , tx_size * 10 )
# Using the other output should have 72 byte sigs
2023-08-13 10:02:10 -04:00
rawtx = wallet . createrawtransaction ( [ ext_utxo ] , [ { self . nodes [ 0 ] . getnewaddress ( ) : 13 } ] )
2022-07-22 15:26:40 -04:00
ext_desc = self . nodes [ 0 ] . getaddressinfo ( ext_addr ) [ " desc " ]
2022-11-10 14:04:07 -03:00
fundedtx = wallet . fundrawtransaction ( rawtx , fee_rate = 10 , change_type = " bech32 " , solving_data = { " descriptors " : [ ext_desc ] } )
2022-07-22 15:26:40 -04:00
# tx overhead (10) + 3 inputs (41 each) + 2 p2wpkh(31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 bytes sig witnesses (107 each) + p2wpkh 72 byte sig witness (108)) / witness scaling factor (4)
tx_size = ceil ( 10 + 41 * 3 + 31 * 2 + ( 2 + 107 * 2 + 108 ) / 4 )
2022-03-30 04:18:33 -03:00
assert_equal ( fundedtx [ ' fee ' ] * COIN , tx_size * 10 )
self . nodes [ 2 ] . unloadwallet ( " test_weight_calculation " )
2024-06-19 13:27:35 -04:00
def test_weight_limits ( self ) :
self . log . info ( " Test weight limits " )
self . nodes [ 2 ] . createwallet ( " test_weight_limits " )
wallet = self . nodes [ 2 ] . get_wallet_rpc ( " test_weight_limits " )
outputs = [ ]
for _ in range ( 1472 ) :
outputs . append ( { wallet . getnewaddress ( address_type = " legacy " ) : 0.1 } )
2024-06-27 15:46:59 -04:00
txid = self . nodes [ 0 ] . send ( outputs = outputs , change_position = 0 ) [ " txid " ]
2024-06-19 13:27:35 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
# 272 WU per input (273 when high-s); picking 1471 inputs will exceed the max standard tx weight.
rawtx = wallet . createrawtransaction ( [ ] , [ { wallet . getnewaddress ( ) : 0.1 * 1471 } ] )
2024-06-27 15:46:59 -04:00
# 1) Try to fund transaction only using the preset inputs (pick all 1472 inputs to cover the fee)
2024-06-19 13:27:35 -04:00
input_weights = [ ]
2024-06-27 15:46:59 -04:00
for i in range ( 1 , 1473 ) : # skip first output as it is the parent tx change output
2024-06-19 13:27:35 -04:00
input_weights . append ( { " txid " : txid , " vout " : i , " weight " : 273 } )
assert_raises_rpc_error ( - 4 , " Transaction too large " , wallet . fundrawtransaction , hexstring = rawtx , input_weights = input_weights )
# 2) Let the wallet fund the transaction
assert_raises_rpc_error ( - 4 , " The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet ' s UTXOs " ,
wallet . fundrawtransaction , hexstring = rawtx )
# 3) Pre-select some inputs and let the wallet fill-up the remaining amount
inputs = input_weights [ 0 : 1000 ]
assert_raises_rpc_error ( - 4 , " The combination of the pre-selected inputs and the wallet automatic inputs selection exceeds the transaction maximum weight. Please try sending a smaller amount or manually consolidating your wallet ' s UTXOs " ,
wallet . fundrawtransaction , hexstring = rawtx , input_weights = inputs )
self . nodes [ 2 ] . unloadwallet ( " test_weight_limits " )
2021-03-10 11:37:18 -03:00
def test_include_unsafe ( self ) :
self . log . info ( " Test fundrawtxn with unsafe inputs " )
self . nodes [ 0 ] . createwallet ( " unsafe " )
wallet = self . nodes [ 0 ] . get_wallet_rpc ( " unsafe " )
# We receive unconfirmed funds from external keys (unsafe outputs).
addr = wallet . getnewaddress ( )
2021-08-26 20:33:22 -04:00
inputs = [ ]
for i in range ( 0 , 2 ) :
2023-08-13 10:02:10 -04:00
utxo = self . create_outpoints ( self . nodes [ 2 ] , outputs = [ { addr : 5 } ] ) [ 0 ]
inputs . append ( ( utxo [ ' txid ' ] , utxo [ ' vout ' ] ) )
self . sync_mempools ( )
2021-03-10 11:37:18 -03:00
# Unsafe inputs are ignored by default.
2021-08-26 20:33:22 -04:00
rawtx = wallet . createrawtransaction ( [ ] , [ { self . nodes [ 2 ] . getnewaddress ( ) : 7.5 } ] )
2021-03-10 11:37:18 -03:00
assert_raises_rpc_error ( - 4 , " Insufficient funds " , wallet . fundrawtransaction , rawtx )
# But we can opt-in to use them for funding.
2022-11-10 14:04:07 -03:00
fundedtx = wallet . fundrawtransaction ( rawtx , include_unsafe = True )
2021-03-10 11:37:18 -03:00
tx_dec = wallet . decoderawtransaction ( fundedtx [ ' hex ' ] )
2021-08-26 20:33:22 -04:00
assert all ( ( txin [ " txid " ] , txin [ " vout " ] ) in inputs for txin in tx_dec [ " vin " ] )
2021-03-10 11:37:18 -03:00
signedtx = wallet . signrawtransactionwithwallet ( fundedtx [ ' hex ' ] )
2021-08-26 20:33:22 -04:00
assert wallet . testmempoolaccept ( [ signedtx [ ' hex ' ] ] ) [ 0 ] [ " allowed " ]
2021-03-10 11:37:18 -03:00
# And we can also use them once they're confirmed.
2021-08-19 11:10:24 -04:00
self . generate ( self . nodes [ 0 ] , 1 )
2022-11-10 14:04:07 -03:00
fundedtx = wallet . fundrawtransaction ( rawtx , include_unsafe = False )
2021-03-10 11:37:18 -03:00
tx_dec = wallet . decoderawtransaction ( fundedtx [ ' hex ' ] )
2021-08-26 20:33:22 -04:00
assert all ( ( txin [ " txid " ] , txin [ " vout " ] ) in inputs for txin in tx_dec [ " vin " ] )
2021-03-10 11:37:18 -03:00
signedtx = wallet . signrawtransactionwithwallet ( fundedtx [ ' hex ' ] )
2021-08-26 20:33:22 -04:00
assert wallet . testmempoolaccept ( [ signedtx [ ' hex ' ] ] ) [ 0 ] [ " allowed " ]
2019-10-21 17:55:07 -03:00
self . nodes [ 0 ] . unloadwallet ( " unsafe " )
2021-03-10 11:37:18 -03:00
2021-08-11 22:07:15 -04:00
def test_22670 ( self ) :
# In issue #22670, it was observed that ApproximateBestSubset may
# choose enough value to cover the target amount but not enough to cover the transaction fees.
# This leads to a transaction whose actual transaction feerate is lower than expected.
# However at normal feerates, the difference between the effective value and the real value
# that this bug is not detected because the transaction fee must be at least 0.01 BTC (the minimum change value).
# Otherwise the targeted minimum change value will be enough to cover the transaction fees that were not
# being accounted for. So the minimum relay fee is set to 0.1 BTC/kvB in this test.
self . log . info ( " Test issue 22670 ApproximateBestSubset bug " )
# Make sure the default wallet will not be loaded when restarted with a high minrelaytxfee
self . nodes [ 0 ] . unloadwallet ( self . default_wallet_name , False )
feerate = Decimal ( " 0.1 " )
2021-12-09 14:28:19 -03:00
self . restart_node ( 0 , [ f " -minrelaytxfee= { feerate } " , " -discardfee=0 " ] ) # Set high minrelayfee, set discardfee to 0 for easier calculation
2021-08-11 22:07:15 -04:00
self . nodes [ 0 ] . loadwallet ( self . default_wallet_name , True )
funds = self . nodes [ 0 ] . get_wallet_rpc ( self . default_wallet_name )
self . nodes [ 0 ] . createwallet ( wallet_name = " tester " )
tester = self . nodes [ 0 ] . get_wallet_rpc ( " tester " )
# Because this test is specifically for ApproximateBestSubset, the target value must be greater
# than any single input available, and require more than 1 input. So we make 3 outputs
for i in range ( 0 , 3 ) :
funds . sendtoaddress ( tester . getnewaddress ( address_type = " bech32 " ) , 1 )
2020-11-10 14:02:31 -03:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = self . no_op )
2021-08-11 22:07:15 -04:00
# Create transactions in order to calculate fees for the target bounds that can trigger this bug
change_tx = tester . fundrawtransaction ( tester . createrawtransaction ( [ ] , [ { funds . getnewaddress ( ) : 1.5 } ] ) )
tx = tester . createrawtransaction ( [ ] , [ { funds . getnewaddress ( ) : 2 } ] )
2022-11-10 14:04:07 -03:00
no_change_tx = tester . fundrawtransaction ( tx , subtractFeeFromOutputs = [ 0 ] )
2021-08-11 22:07:15 -04:00
overhead_fees = feerate * len ( tx ) / 2 / 1000
cost_of_change = change_tx [ " fee " ] - no_change_tx [ " fee " ]
fees = no_change_tx [ " fee " ]
assert_greater_than ( fees , 0.01 )
def do_fund_send ( target ) :
2021-08-19 04:09:36 -04:00
create_tx = tester . createrawtransaction ( [ ] , [ { funds . getnewaddress ( ) : target } ] )
2021-08-11 22:07:15 -04:00
funded_tx = tester . fundrawtransaction ( create_tx )
signed_tx = tester . signrawtransactionwithwallet ( funded_tx [ " hex " ] )
assert signed_tx [ " complete " ]
decoded_tx = tester . decoderawtransaction ( signed_tx [ " hex " ] )
assert_equal ( len ( decoded_tx [ " vin " ] ) , 3 )
assert tester . testmempoolaccept ( [ signed_tx [ " hex " ] ] ) [ 0 ] [ " allowed " ]
# We want to choose more value than is available in 2 inputs when considering the fee,
# but not enough to need 3 inputs when not considering the fee.
# So the target value must be at least 2.00000001 - fee.
lower_bound = Decimal ( " 2.00000001 " ) - fees
# The target value must be at most 2 - cost_of_change - not_input_fees - min_change (these are all
# included in the target before ApproximateBestSubset).
upper_bound = Decimal ( " 2.0 " ) - cost_of_change - overhead_fees - Decimal ( " 0.01 " )
assert_greater_than_or_equal ( upper_bound , lower_bound )
do_fund_send ( lower_bound )
do_fund_send ( upper_bound )
self . restart_node ( 0 )
2021-09-10 21:27:41 -03:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 0 , 2 )
self . connect_nodes ( 0 , 3 )
def test_feerate_rounding ( self ) :
self . log . info ( " Test that rounding of GetFee does not result in an assertion " )
self . nodes [ 1 ] . createwallet ( " roundtest " )
w = self . nodes [ 1 ] . get_wallet_rpc ( " roundtest " )
addr = w . getnewaddress ( address_type = " bech32 " )
self . nodes [ 0 ] . sendtoaddress ( addr , 1 )
self . generate ( self . nodes [ 0 ] , 1 )
# A P2WPKH input costs 68 vbytes; With a single P2WPKH output, the rest of the tx is 42 vbytes for a total of 110 vbytes.
# At a feerate of 1.85 sat/vb, the input will need a fee of 125.8 sats and the rest 77.7 sats
# The entire tx fee should be 203.5 sats.
# Coin selection rounds the fee individually instead of at the end (due to how CFeeRate::GetFee works).
# If rounding down (which is the incorrect behavior), then the calculated fee will be 125 + 77 = 202.
# If rounding up, then the calculated fee will be 126 + 78 = 204.
# In the former case, the calculated needed fee is higher than the actual fee being paid, so an assertion is reached
# To test this does not happen, we subtract 202 sats from the input value. If working correctly, this should
# fail with insufficient funds rather than bitcoind asserting.
rawtx = w . createrawtransaction ( inputs = [ ] , outputs = [ { self . nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) : 1 - 0.00000202 } ] )
2022-11-10 14:04:07 -03:00
assert_raises_rpc_error ( - 4 , " Insufficient funds " , w . fundrawtransaction , rawtx , fee_rate = 1.85 )
2021-09-10 21:27:41 -03:00
2021-05-24 09:22:10 -04:00
def test_input_confs_control ( self ) :
self . nodes [ 0 ] . createwallet ( " minconf " )
wallet = self . nodes [ 0 ] . get_wallet_rpc ( " minconf " )
# Fund the wallet with different chain heights
for _ in range ( 2 ) :
self . nodes [ 2 ] . sendmany ( " " , { wallet . getnewaddress ( ) : 1 , wallet . getnewaddress ( ) : 1 } )
self . generate ( self . nodes [ 2 ] , 1 )
unconfirmed_txid = wallet . sendtoaddress ( wallet . getnewaddress ( ) , 0.5 )
self . log . info ( " Crafting TX using an unconfirmed input " )
target_address = self . nodes [ 2 ] . getnewaddress ( )
raw_tx1 = wallet . createrawtransaction ( [ ] , { target_address : 0.1 } , 0 , True )
funded_tx1 = wallet . fundrawtransaction ( raw_tx1 , { ' fee_rate ' : 1 , ' maxconf ' : 0 } ) [ ' hex ' ]
# Make sure we only had the one input
tx1_inputs = self . nodes [ 0 ] . decoderawtransaction ( funded_tx1 ) [ ' vin ' ]
assert_equal ( len ( tx1_inputs ) , 1 )
utxo1 = tx1_inputs [ 0 ]
assert unconfirmed_txid == utxo1 [ ' txid ' ]
final_tx1 = wallet . signrawtransactionwithwallet ( funded_tx1 ) [ ' hex ' ]
txid1 = self . nodes [ 0 ] . sendrawtransaction ( final_tx1 )
mempool = self . nodes [ 0 ] . getrawmempool ( )
assert txid1 in mempool
self . log . info ( " Fail to craft a new TX with minconf above highest one " )
# Create a replacement tx to 'final_tx1' that has 1 BTC target instead of 0.1.
raw_tx2 = wallet . createrawtransaction ( [ { ' txid ' : utxo1 [ ' txid ' ] , ' vout ' : utxo1 [ ' vout ' ] } ] , { target_address : 1 } )
assert_raises_rpc_error ( - 4 , " Insufficient funds " , wallet . fundrawtransaction , raw_tx2 , { ' add_inputs ' : True , ' minconf ' : 3 , ' fee_rate ' : 10 } )
self . log . info ( " Fail to broadcast a new TX with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs " )
# Now fund 'raw_tx2' to fulfill the total target (1 BTC) by using all the wallet unconfirmed outputs.
# As it was created with the first unconfirmed output, 'raw_tx2' only has 0.1 BTC covered (need to fund 0.9 BTC more).
# So, the selection process, to cover the amount, will pick up the 'final_tx1' output as well, which is an output of the tx that this
# new tx is replacing!. So, once we send it to the mempool, it will return a "bad-txns-spends-conflicting-tx"
# because the input will no longer exist once the first tx gets replaced by this new one).
funded_invalid = wallet . fundrawtransaction ( raw_tx2 , { ' add_inputs ' : True , ' maxconf ' : 0 , ' fee_rate ' : 10 } ) [ ' hex ' ]
final_invalid = wallet . signrawtransactionwithwallet ( funded_invalid ) [ ' hex ' ]
assert_raises_rpc_error ( - 26 , " bad-txns-spends-conflicting-tx " , self . nodes [ 0 ] . sendrawtransaction , final_invalid )
self . log . info ( " Craft a replacement adding inputs with highest depth possible " )
funded_tx2 = wallet . fundrawtransaction ( raw_tx2 , { ' add_inputs ' : True , ' minconf ' : 2 , ' fee_rate ' : 10 } ) [ ' hex ' ]
tx2_inputs = self . nodes [ 0 ] . decoderawtransaction ( funded_tx2 ) [ ' vin ' ]
assert_greater_than_or_equal ( len ( tx2_inputs ) , 2 )
for vin in tx2_inputs :
if vin [ ' txid ' ] != unconfirmed_txid :
assert_greater_than_or_equal ( self . nodes [ 0 ] . gettxout ( vin [ ' txid ' ] , vin [ ' vout ' ] ) [ ' confirmations ' ] , 2 )
final_tx2 = wallet . signrawtransactionwithwallet ( funded_tx2 ) [ ' hex ' ]
txid2 = self . nodes [ 0 ] . sendrawtransaction ( final_tx2 )
mempool = self . nodes [ 0 ] . getrawmempool ( )
assert txid1 not in mempool
assert txid2 in mempool
wallet . unloadwallet ( )
2021-03-10 17:45:29 -03:00
2015-04-24 22:26:30 -03:00
if __name__ == ' __main__ ' :
2024-07-16 17:05:14 -04:00
RawTransactionsTest ( __file__ ) . main ( )