2016-05-06 11:23:48 +02:00
#!/usr/bin/env python3
2023-08-16 19:27:31 +02:00
# Copyright (c) 2014-2020 The Bitcoin Core developers
2015-04-25 03:26:30 +02:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2019-01-07 10:55:35 +01:00
""" Test the fundrawtransaction RPC. """
2015-04-25 03:26:30 +02:00
2018-05-09 10:12:45 +02:00
from decimal import Decimal
2020-11-02 16:54:06 +01:00
from test_framework . descriptors import descsum_create
2017-09-06 20:02:08 +02:00
from test_framework . test_framework import BitcoinTestFramework
2018-05-09 10:12:45 +02:00
from test_framework . util import (
assert_equal ,
assert_fee_amount ,
assert_greater_than ,
assert_greater_than_or_equal ,
assert_raises_rpc_error ,
count_bytes ,
find_vout_for_address ,
2023-11-03 16:05:37 +01:00
satoshi_round ,
2018-05-09 10:12:45 +02:00
)
2015-04-25 03:26:30 +02:00
2016-06-21 10:24:09 +02: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-25 03:26:30 +02:00
class RawTransactionsTest ( BitcoinTestFramework ) :
2017-09-01 18:47:13 +02:00
def set_test_params ( self ) :
2016-05-20 15:16:51 +02:00
self . num_nodes = 4
2017-09-01 18:47:13 +02:00
self . setup_clean_chain = True
2019-11-06 21:18:34 +01:00
# This test isn't testing tx relay. Set whitelist on the peers for
# instant tx relay.
2024-02-28 10:02:21 +01:00
self . extra_args = [ [ f ' -usehd= { not self . options . nohd } ' , ' -whitelist=noban@127.0.0.1 ' ] ] * self . num_nodes
2015-04-25 03:26:30 +02:00
2018-09-13 12:33:15 +02:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2023-05-24 19:38:33 +02:00
def add_options ( self , parser ) :
2024-02-28 10:02:21 +01:00
parser . add_argument ( " --nohd " , dest = " nohd " , default = False , action = " store_true " ,
help = " Test with -nohd enabled " )
2023-05-24 19:38:33 +02:00
2018-12-29 20:18:43 +01:00
def setup_network ( self ) :
2019-06-20 18:37:09 +02:00
self . setup_nodes ( )
2015-04-25 03:26:30 +02:00
2022-09-24 14:36:35 +02:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
self . connect_nodes ( 0 , 3 )
2015-04-25 03:26:30 +02:00
def run_test ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Connect nodes, set fees, generate blocks, and sync " )
2019-08-01 00:03:43 +02:00
self . min_relay_tx_fee = self . nodes [ 0 ] . getnetworkinfo ( ) [ ' relayfee ' ]
2016-01-05 19:10:19 +01: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-08-01 00:03:43 +02:00
node . settxfee ( self . min_relay_tx_fee )
2016-01-05 19:10:19 +01:00
2015-10-14 14:27:03 +02: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-08-01 00:03:43 +02:00
self . fee_tolerance = 2 * self . min_relay_tx_fee / 1000
2015-04-25 03:26:30 +02:00
self . nodes [ 2 ] . generate ( 1 )
2015-07-10 20:11:44 +02:00
self . sync_all ( )
2015-04-24 06:42:49 +02:00
self . nodes [ 0 ] . generate ( 121 )
2015-04-25 03:26:30 +02:00
self . sync_all ( )
2015-04-24 06:42:49 +02:00
2019-08-01 00:03:43 +02: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_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-12-01 17:56:52 +01:00
self . test_subtract_fee_with_presets ( )
2024-06-23 14:37:45 +02:00
self . test_include_unsafe ( )
2019-08-01 00:03:43 +02:00
def test_change_position ( self ) :
2019-11-01 16:06:12 +01:00
""" Ensure setting changePosition in fundraw with an exact match is handled properly. """
self . log . info ( " Test fundrawtxn changePosition option " )
2018-01-25 22:07:35 +01:00
rawmatch = self . nodes [ 2 ] . createrawtransaction ( [ ] , { self . nodes [ 2 ] . getnewaddress ( ) : 500 } )
2017-05-01 15:19:47 +02:00
rawmatch = self . nodes [ 2 ] . fundrawtransaction ( rawmatch , { " changePosition " : 1 , " subtractFeeFromOutputs " : [ 0 ] } )
assert_equal ( rawmatch [ " changepos " ] , - 1 )
2020-11-02 16:54:06 +01:00
self . nodes [ 3 ] . createwallet ( wallet_name = " wwatch " , disable_private_keys = True )
wwatch = self . nodes [ 3 ] . get_wallet_rpc ( ' wwatch ' )
2015-04-24 06:42:49 +02:00
watchonly_address = self . nodes [ 0 ] . getnewaddress ( )
2020-12-17 13:46:20 +01:00
watchonly_pubkey = self . nodes [ 0 ] . getaddressinfo ( watchonly_address ) [ " pubkey " ]
2019-08-01 00:03:43 +02:00
self . watchonly_amount = Decimal ( 2000 )
2020-11-02 16:54:06 +01:00
wwatch . importpubkey ( watchonly_pubkey , " " , True )
2019-08-01 00:03:43 +02:00
self . watchonly_txid = self . nodes [ 0 ] . sendtoaddress ( watchonly_address , self . watchonly_amount )
2018-05-09 10:12:45 +02:00
# Lock UTXO so nodes[0] doesn't accidentally spend it
2019-08-01 00:03:43 +02:00
self . watchonly_vout = find_vout_for_address ( self . nodes [ 0 ] , self . watchonly_txid , watchonly_address )
self . nodes [ 0 ] . lockunspent ( False , [ { " txid " : self . watchonly_txid , " vout " : self . watchonly_vout } ] )
2018-05-09 10:12:45 +02:00
2020-11-02 16:54:06 +01:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 3 ] . get_wallet_rpc ( self . default_wallet_name ) . getnewaddress ( ) , self . watchonly_amount / 10 )
2015-04-24 06:42:49 +02:00
2016-03-06 16:14:39 +01:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 15 )
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 10 )
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 50 )
2015-04-24 06:42:49 +02:00
2015-04-25 03:26:30 +02:00
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
2020-11-02 16:54:06 +01:00
wwatch . unloadwallet ( )
2019-08-01 00:03:43 +02:00
def test_simple ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn " )
2015-04-25 03:26:30 +02:00
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 10 }
2015-04-25 03:26:30 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
2021-08-27 21:03:02 +02:00
assert len ( dec_tx [ ' vin ' ] ) > 0 #test that we have enough inputs
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_simple_two_coins ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with 2 coins " )
2015-04-25 03:26:30 +02:00
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 22 }
2015-04-25 03:26:30 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
2021-08-27 21:03:02 +02:00
assert len ( dec_tx [ ' vin ' ] ) > 0 #test if we have enough inputs
2015-04-25 03:26:30 +02:00
assert_equal ( dec_tx [ ' vin ' ] [ 0 ] [ ' scriptSig ' ] [ ' hex ' ] , ' ' )
2019-08-01 00:03:43 +02:00
def test_simple_two_outputs ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with 2 outputs " )
2015-04-25 03:26:30 +02:00
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 26 , self . nodes [ 1 ] . getnewaddress ( ) : 25 }
2015-04-25 03:26:30 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
totalOut = 0
for out in dec_tx [ ' vout ' ] :
totalOut + = out [ ' value ' ]
2024-06-23 17:51:21 +02:00
address = out [ ' scriptPubKey ' ] [ ' address ' ]
2023-11-03 16:05:37 +01:00
if address in outputs . keys ( ) :
assert_equal ( satoshi_round ( outputs [ address ] ) , out [ ' value ' ] )
2015-04-25 03:26:30 +02:00
2021-08-27 21:03:02 +02:00
assert len ( dec_tx [ ' vin ' ] ) > 0
2015-04-25 03:26:30 +02:00
assert_equal ( dec_tx [ ' vin ' ] [ 0 ] [ ' scriptSig ' ] [ ' hex ' ] , ' ' )
2019-08-01 00:03:43 +02:00
def test_change ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with a vin > required amount " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 50 )
2015-04-25 03:26:30 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 10 }
2015-04-25 03:26:30 +02: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 ' ]
2019-08-01 00:03:43 +02:00
self . test_no_change_fee = fee # Use the same fee for the next tx
2015-04-25 03:26:30 +02: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-08-01 00:03:43 +02:00
def test_no_change ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn not having a change output " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 50 )
2015-04-25 03:26:30 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
2019-08-01 00:03:43 +02:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 50 ) - self . test_no_change_fee - self . fee_tolerance }
2015-04-25 03:26:30 +02: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-08-01 00:03:43 +02:00
def test_invalid_option ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with an invalid option " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 50 )
2016-04-15 16:23:16 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
2017-12-20 21:43:27 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 40 ) }
2016-04-15 16:23:16 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 3 , " Unexpected key foo " , self . nodes [ 2 ] . fundrawtransaction , rawtx , { ' foo ' : ' bar ' } )
2016-04-15 16:23:16 +02:00
2018-02-14 16:42:40 +01:00
# reserveChangeKey was deprecated and is now removed
assert_raises_rpc_error ( - 3 , " Unexpected key reserveChangeKey " , lambda : self . nodes [ 2 ] . fundrawtransaction ( hexstring = rawtx , options = { ' reserveChangeKey ' : True } ) )
2019-08-01 00:03:43 +02:00
def test_invalid_change_address ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with an invalid change address " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 50 )
2016-04-15 16:23:16 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
2017-12-20 21:43:27 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 40 ) }
2016-04-15 16:23:16 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2024-03-07 09:19:48 +01:00
assert_raises_rpc_error ( - 5 , " Change address must be a valid Dash address " , self . nodes [ 2 ] . fundrawtransaction , rawtx , { ' changeAddress ' : ' foobar ' } )
2016-04-15 16:23:16 +02:00
2019-08-01 00:03:43 +02:00
def test_valid_change_address ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with a provided change address " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 50 )
2016-04-15 16:23:16 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
2017-12-20 21:43:27 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( 40 ) }
2016-04-15 16:23:16 +02:00
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 ( )
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 8 , " changePosition out of bounds " , self . nodes [ 2 ] . fundrawtransaction , rawtx , { ' changeAddress ' : change , ' changePosition ' : 2 } )
2016-04-15 16:23:16 +02:00
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx , { ' changeAddress ' : change , ' changePosition ' : 0 } )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
2017-01-23 13:22:32 +01:00
out = dec_tx [ ' vout ' ] [ 0 ]
2024-06-23 17:51:21 +02:00
assert_equal ( change , out [ ' scriptPubKey ' ] [ ' address ' ] )
2016-04-15 16:23:16 +02:00
2019-08-01 00:03:43 +02:00
def test_coin_selection ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with a vin < required amount " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 10 )
2015-04-25 03:26:30 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 10 }
2015-04-25 03:26:30 +02:00
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 ' ] )
2024-02-06 16:41:26 +01:00
# Should fail without add_inputs:
assert_raises_rpc_error ( - 4 , " Insufficient funds " , self . nodes [ 2 ] . fundrawtransaction , rawtx , { " add_inputs " : False } )
# add_inputs is enabled by default
2015-04-25 03:26:30 +02:00
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx )
2024-02-06 16:41:26 +01:00
2015-04-25 03:26:30 +02:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
totalOut = 0
matchingOuts = 0
for i , out in enumerate ( dec_tx [ ' vout ' ] ) :
totalOut + = out [ ' value ' ]
2024-06-23 17:51:21 +02:00
if out [ ' scriptPubKey ' ] [ ' address ' ] in outputs :
2015-04-25 03:26:30 +02: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-08-01 00:03:43 +02:00
def test_two_vin ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with 2 vins " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 10 )
utx2 = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 50 )
2015-04-25 03:26:30 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } , { ' txid ' : utx2 [ ' txid ' ] , ' vout ' : utx2 [ ' vout ' ] } ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 60 }
2015-04-25 03:26:30 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2024-02-06 16:41:26 +01:00
# Should fail without add_inputs:
assert_raises_rpc_error ( - 4 , " Insufficient funds " , self . nodes [ 2 ] . fundrawtransaction , rawtx , { " add_inputs " : False } )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx , { " add_inputs " : True } )
2015-04-25 03:26:30 +02:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
totalOut = 0
matchingOuts = 0
for out in dec_tx [ ' vout ' ] :
totalOut + = out [ ' value ' ]
2024-06-23 17:51:21 +02:00
if out [ ' scriptPubKey ' ] [ ' address ' ] in outputs :
2015-04-25 03:26:30 +02: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-08-01 00:03:43 +02:00
def test_two_vin_two_vout ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with 2 vins and 2 vouts " )
2016-06-21 10:24:09 +02:00
utx = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 10 )
utx2 = get_unspent ( self . nodes [ 2 ] . listunspent ( ) , 50 )
2015-04-25 03:26:30 +02:00
inputs = [ { ' txid ' : utx [ ' txid ' ] , ' vout ' : utx [ ' vout ' ] } , { ' txid ' : utx2 [ ' txid ' ] , ' vout ' : utx2 [ ' vout ' ] } ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 60 , self . nodes [ 0 ] . getnewaddress ( ) : 10 }
2015-04-25 03:26:30 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtx )
assert_equal ( utx [ ' txid ' ] , dec_tx [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2024-02-06 16:41:26 +01:00
# Should fail without add_inputs:
assert_raises_rpc_error ( - 4 , " Insufficient funds " , self . nodes [ 2 ] . fundrawtransaction , rawtx , { " add_inputs " : False } )
rawtxfund = self . nodes [ 2 ] . fundrawtransaction ( rawtx , { " add_inputs " : True } )
2015-04-25 03:26:30 +02:00
dec_tx = self . nodes [ 2 ] . decoderawtransaction ( rawtxfund [ ' hex ' ] )
totalOut = 0
matchingOuts = 0
for out in dec_tx [ ' vout ' ] :
totalOut + = out [ ' value ' ]
2024-06-23 17:51:21 +02:00
if out [ ' scriptPubKey ' ] [ ' address ' ] in outputs :
2015-04-25 03:26:30 +02:00
matchingOuts + = 1
assert_equal ( matchingOuts , 2 )
assert_equal ( len ( dec_tx [ ' vout ' ] ) , 3 )
2019-08-01 00:03:43 +02:00
def test_invalid_input ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with an invalid vin " )
2015-04-25 03:26:30 +02:00
inputs = [ { ' txid ' : " 1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1 " , ' vout ' : 0 } ] #invalid vin!
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 10 }
2015-04-25 03:26:30 +02:00
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 4 , " Insufficient funds " , self . nodes [ 2 ] . fundrawtransaction , rawtx )
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_fee_p2pkh ( self ) :
2019-11-01 16:06:12 +01:00
""" Compare fee of a standard pubkeyhash transaction. """
self . log . info ( " Test fundrawtxn p2pkh fee " )
2015-04-25 03:26:30 +02:00
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 11 }
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Create same transaction over sendtoaddress.
2016-03-06 16:14:39 +01:00
txId = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , 11 )
2021-08-20 17:38:38 +02:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fee ' ]
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Compare fee.
2015-12-02 18:12:23 +01:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-08-01 00:03:43 +02:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_fee_p2pkh_multi_out ( self ) :
2019-11-01 16:06:12 +01:00
""" Compare fee of a standard pubkeyhash transaction with multiple outputs. """
self . log . info ( " Test fundrawtxn p2pkh fee with multiple outputs " )
2015-04-25 03:26:30 +02:00
inputs = [ ]
2019-11-01 16:06:12 +01:00
outputs = {
self . nodes [ 1 ] . getnewaddress ( ) : 11 ,
self . nodes [ 1 ] . getnewaddress ( ) : 12 ,
self . nodes [ 1 ] . getnewaddress ( ) : 1 ,
self . nodes [ 1 ] . getnewaddress ( ) : 13 ,
self . nodes [ 1 ] . getnewaddress ( ) : 2 ,
self . nodes [ 1 ] . getnewaddress ( ) : 3 ,
}
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2019-11-01 16:06:12 +01:00
# Create same transaction over sendtoaddress.
2015-12-02 18:12:23 +01:00
txId = self . nodes [ 0 ] . sendmany ( " " , outputs )
2021-08-20 17:38:38 +02:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fee ' ]
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Compare fee.
2015-12-02 18:12:23 +01:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-08-01 00:03:43 +02:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_fee_p2sh ( self ) :
2019-11-01 16:06:12 +01:00
""" Compare fee of a 2-of-2 multisig p2sh transaction. """
# Create 2-of-2 addr.
2015-04-25 03:26:30 +02:00
addr1 = self . nodes [ 1 ] . getnewaddress ( )
addr2 = self . nodes [ 1 ] . getnewaddress ( )
2020-12-17 13:46:20 +01:00
addr1Obj = self . nodes [ 1 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 1 ] . getaddressinfo ( addr2 )
2015-04-25 03:26:30 +02:00
2020-11-02 16:54:06 +01:00
mSigObj = self . nodes [ 3 ] . createmultisig ( 2 , [ addr1Obj [ ' pubkey ' ] , addr2Obj [ ' pubkey ' ] ] ) [ ' address ' ]
2015-04-25 03:26:30 +02:00
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { mSigObj : 11 }
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Create same transaction over sendtoaddress.
2016-03-06 16:14:39 +01:00
txId = self . nodes [ 0 ] . sendtoaddress ( mSigObj , 11 )
2021-08-20 17:38:38 +02:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fee ' ]
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Compare fee.
2015-12-02 18:12:23 +01:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-08-01 00:03:43 +02:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_fee_4of5 ( self ) :
2019-11-01 16:06:12 +01:00
""" Compare fee of a standard pubkeyhash transaction. """
self . log . info ( " Test fundrawtxn fee with 4-of-5 addresses " )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Create 4-of-5 addr.
2015-04-25 03:26:30 +02: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 ( )
2020-12-17 13:46:20 +01: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-25 03:26:30 +02:00
2020-11-02 16:54:06 +01:00
mSigObj = self . nodes [ 1 ] . createmultisig (
2019-11-01 16:06:12 +01:00
4 ,
[
addr1Obj [ ' pubkey ' ] ,
addr2Obj [ ' pubkey ' ] ,
addr3Obj [ ' pubkey ' ] ,
addr4Obj [ ' pubkey ' ] ,
addr5Obj [ ' pubkey ' ] ,
]
) [ ' address ' ]
2015-04-25 03:26:30 +02:00
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { mSigObj : 11 }
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 0 ] . fundrawtransaction ( rawtx )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Create same transaction over sendtoaddress.
2016-03-06 16:14:39 +01:00
txId = self . nodes [ 0 ] . sendtoaddress ( mSigObj , 11 )
2021-08-20 17:38:38 +02:00
signedFee = self . nodes [ 0 ] . getmempoolentry ( txId ) [ ' fee ' ]
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Compare fee.
2015-12-02 18:12:23 +01:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-08-01 00:03:43 +02:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_spend_2of2 ( self ) :
2019-11-01 16:06:12 +01:00
""" Spend a 2-of-2 multisig transaction over fundraw. """
2020-11-02 16:54:06 +01:00
self . log . info ( " Test fundpsbt spending 2-of-2 multisig " )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Create 2-of-2 addr.
2015-04-25 03:26:30 +02:00
addr1 = self . nodes [ 2 ] . getnewaddress ( )
addr2 = self . nodes [ 2 ] . getnewaddress ( )
2020-12-17 13:46:20 +01:00
addr1Obj = self . nodes [ 2 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 2 ] . getaddressinfo ( addr2 )
2015-04-25 03:26:30 +02:00
2020-11-02 16:54:06 +01: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-11-01 16:06:12 +01:00
2 ,
[
addr1Obj [ ' pubkey ' ] ,
addr2Obj [ ' pubkey ' ] ,
]
) [ ' address ' ]
2020-11-02 16:54:06 +01:00
if not self . options . descriptors :
wmulti . importaddress ( mSigObj )
2015-04-25 03:26:30 +02:00
2016-03-06 16:14:39 +01:00
# send 12 DASH to msig addr
2019-08-01 00:03:43 +02:00
self . nodes [ 0 ] . sendtoaddress ( mSigObj , 12 )
2019-11-06 21:18:34 +01:00
self . nodes [ 0 ] . generate ( 1 )
2015-04-25 03:26:30 +02:00
self . sync_all ( )
oldBalance = self . nodes [ 1 ] . getbalance ( )
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 11 }
2020-11-02 16:54:06 +01:00
funded_psbt = wmulti . walletcreatefundedpsbt ( inputs = inputs , outputs = outputs , options = { ' changeAddress ' : w2 . getrawchangeaddress ( ) } ) [ ' psbt ' ]
2015-04-25 03:26:30 +02:00
2020-11-02 16:54:06 +01:00
signed_psbt = w2 . walletprocesspsbt ( funded_psbt )
final_psbt = w2 . finalizepsbt ( signed_psbt [ ' psbt ' ] )
self . nodes [ 2 ] . sendrawtransaction ( final_psbt [ ' hex ' ] )
2019-11-06 21:18:34 +01:00
self . nodes [ 2 ] . generate ( 1 )
2015-04-25 03:26:30 +02:00
self . sync_all ( )
2019-11-01 16:06:12 +01:00
# Make sure funds are received at node1.
2016-03-06 16:14:39 +01:00
assert_equal ( oldBalance + Decimal ( ' 11.0000000 ' ) , self . nodes [ 1 ] . getbalance ( ) )
2015-04-25 03:26:30 +02:00
2020-11-02 16:54:06 +01:00
wmulti . unloadwallet ( )
2019-08-01 00:03:43 +02:00
def test_locked_wallet ( self ) :
2020-11-02 16:54:06 +01:00
self . log . info ( " Test fundrawtxn with locked wallet and hardened derivation " )
2019-11-01 16:06:12 +01:00
2018-09-14 10:28:27 +02:00
self . nodes [ 1 ] . encryptwallet ( " test " )
2015-04-25 03:26:30 +02:00
2020-11-02 16:54:06 +01:00
if self . options . descriptors :
self . nodes [ 1 ] . walletpassphrase ( ' test ' , 10 )
self . nodes [ 1 ] . importdescriptors ( [ {
' desc ' : descsum_create ( ' pkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h) ' ) ,
' timestamp ' : ' now ' ,
' active ' : True
} ,
{
' desc ' : descsum_create ( ' pkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h) ' ) ,
' timestamp ' : ' now ' ,
' active ' : True ,
' internal ' : True
} ] )
self . nodes [ 1 ] . walletlock ( )
2019-11-01 16:06:12 +01:00
# Drain the keypool.
2017-04-11 12:53:54 +02:00
self . nodes [ 1 ] . getnewaddress ( )
2020-04-18 11:37:20 +02:00
inputs = self . nodes [ 1 ] . listunspent ( )
# Deduce fee to produce a changeless transaction
# bnb doesn't work same way as in bitcoin, so, `value` is also calculated by different way
value = sum ( input_it [ " amount " ] for input_it in inputs ) - Decimal ( " 0.00002200 " )
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : value }
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs )
2020-04-18 11:37:20 +02:00
# fund a transaction that does not require a new key for the change output
self . nodes [ 1 ] . fundrawtransaction ( rawtx )
2017-04-11 12:53:54 +02:00
# fund a transaction that requires a new key for the change output
# creating the key must be impossible because the wallet is locked
2020-04-18 11:37:20 +02:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1.1 }
rawtx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs )
assert_raises_rpc_error ( - 4 , " Transaction needs a change address, but we can ' t generate it. Please call keypoolrefill first. " , self . nodes [ 1 ] . fundrawtransaction , rawtx )
2017-04-11 12:53:54 +02:00
2019-11-01 16:06:12 +01:00
# Refill the keypool.
2017-04-11 12:53:54 +02:00
self . nodes [ 1 ] . walletpassphrase ( " test " , 100 )
self . nodes [ 1 ] . walletlock ( )
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 13 , " walletpassphrase " , self . nodes [ 1 ] . sendtoaddress , self . nodes [ 0 ] . getnewaddress ( ) , 12 )
2015-04-25 03:26:30 +02:00
oldBalance = self . nodes [ 0 ] . getbalance ( )
inputs = [ ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 11 }
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 1 ] . fundrawtransaction ( rawtx )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Now we need to unlock.
2016-12-10 18:47:33 +01:00
self . nodes [ 1 ] . walletpassphrase ( " test " , 600 )
2018-02-20 03:29:22 +01:00
signedTx = self . nodes [ 1 ] . signrawtransactionwithwallet ( fundedTx [ ' hex ' ] )
2019-08-01 00:03:43 +02:00
self . nodes [ 1 ] . sendrawtransaction ( signedTx [ ' hex ' ] )
2015-04-25 03:26:30 +02:00
self . nodes [ 1 ] . generate ( 1 )
self . sync_all ( )
2019-11-01 16:06:12 +01:00
# Make sure funds are received at node1.
2016-03-06 16:14:39 +01:00
assert_equal ( oldBalance + Decimal ( ' 511.0000000 ' ) , self . nodes [ 0 ] . getbalance ( ) )
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_many_inputs_fee ( self ) :
2019-11-01 16:06:12 +01:00
""" Multiple (~19) inputs tx test | Compare fee. """
self . log . info ( " Test fundrawtxn fee with many inputs " )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Empty node1, send some small coins from node0 to node1.
2015-12-02 18:12:23 +01:00
self . nodes [ 1 ] . sendtoaddress ( self . nodes [ 0 ] . getnewaddress ( ) , self . nodes [ 1 ] . getbalance ( ) , " " , " " , True )
2019-11-06 21:18:34 +01:00
self . nodes [ 1 ] . generate ( 1 )
2015-04-25 03:26:30 +02:00
self . sync_all ( )
2020-08-11 02:50:34 +02:00
for _ in range ( 20 ) :
2015-12-02 18:12:23 +01:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , 0.01 )
2015-04-25 03:26:30 +02:00
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
2019-11-01 16:06:12 +01:00
# Fund a tx with ~20 small inputs.
2015-04-25 03:26:30 +02:00
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 0.15 , self . nodes [ 0 ] . getnewaddress ( ) : 0.04 }
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 1 ] . fundrawtransaction ( rawtx )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Create same transaction over sendtoaddress.
2015-12-02 18:12:23 +01:00
txId = self . nodes [ 1 ] . sendmany ( " " , outputs )
2021-08-20 17:38:38 +02:00
signedFee = self . nodes [ 1 ] . getmempoolentry ( txId ) [ ' fee ' ]
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Compare fee.
2015-12-02 18:12:23 +01:00
feeDelta = Decimal ( fundedTx [ ' fee ' ] ) - Decimal ( signedFee )
2019-08-01 00:03:43 +02:00
assert feeDelta > = 0 and feeDelta < = self . fee_tolerance * 19 #~19 inputs
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_many_inputs_send ( self ) :
2019-11-01 16:06:12 +01:00
""" Multiple (~19) inputs tx test | sign/send. """
self . log . info ( " Test fundrawtxn sign+send with many inputs " )
2015-04-25 03:26:30 +02:00
2019-11-01 16:06:12 +01:00
# Again, empty node1, send some small coins from node0 to node1.
2015-12-02 18:12:23 +01:00
self . nodes [ 1 ] . sendtoaddress ( self . nodes [ 0 ] . getnewaddress ( ) , self . nodes [ 1 ] . getbalance ( ) , " " , " " , True )
2019-11-06 21:18:34 +01:00
self . nodes [ 1 ] . generate ( 1 )
2015-04-25 03:26:30 +02:00
self . sync_all ( )
2020-08-11 02:50:34 +02:00
for _ in range ( 20 ) :
2015-12-02 18:12:23 +01:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , 0.01 )
2015-04-25 03:26:30 +02:00
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
2019-11-01 16:06:12 +01:00
# Fund a tx with ~20 small inputs.
2015-04-25 03:26:30 +02:00
oldBalance = self . nodes [ 0 ] . getbalance ( )
inputs = [ ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 0.15 , self . nodes [ 0 ] . getnewaddress ( ) : 0.04 }
2017-03-27 09:10:37 +02:00
rawtx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs )
fundedTx = self . nodes [ 1 ] . fundrawtransaction ( rawtx )
2018-02-20 03:29:22 +01:00
fundedAndSignedTx = self . nodes [ 1 ] . signrawtransactionwithwallet ( fundedTx [ ' hex ' ] )
2019-08-01 00:03:43 +02:00
self . nodes [ 1 ] . sendrawtransaction ( fundedAndSignedTx [ ' hex ' ] )
2019-11-06 21:18:34 +01:00
self . nodes [ 1 ] . generate ( 1 )
2015-04-25 03:26:30 +02:00
self . sync_all ( )
2016-03-06 16:14:39 +01:00
assert_equal ( oldBalance + Decimal ( ' 500.19000000 ' ) , self . nodes [ 0 ] . getbalance ( ) ) #0.19+block reward
2015-04-25 03:26:30 +02:00
2019-08-01 00:03:43 +02:00
def test_op_return ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn with OP_RETURN and no vin " )
2015-03-25 10:04:02 +01: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-08-01 00:03:43 +02:00
def test_watchonly ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn using only watchonly " )
2015-04-24 06:42:49 +02:00
inputs = [ ]
2019-08-01 00:03:43 +02:00
outputs = { self . nodes [ 2 ] . getnewaddress ( ) : self . watchonly_amount / 2 }
2015-04-24 06:42:49 +02:00
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
2020-11-02 16:54:06 +01: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 ( " pkh(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 06:42:49 +02:00
res_dec = self . nodes [ 0 ] . decoderawtransaction ( result [ " hex " ] )
assert_equal ( len ( res_dec [ " vin " ] ) , 1 )
2019-08-01 00:03:43 +02:00
assert_equal ( res_dec [ " vin " ] [ 0 ] [ " txid " ] , self . watchonly_txid )
2015-04-24 06:42:49 +02:00
2021-08-27 21:03:02 +02:00
assert " fee " in result . keys ( )
2015-04-24 06:42:49 +02:00
assert_greater_than ( result [ " changepos " ] , - 1 )
2020-11-02 16:54:06 +01:00
wwatch . unloadwallet ( )
2019-08-01 00:03:43 +02:00
def test_all_watched_funds ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn using entirety of watched funds " )
2015-04-24 06:42:49 +02:00
inputs = [ ]
2019-08-01 00:03:43 +02:00
outputs = { self . nodes [ 2 ] . getnewaddress ( ) : self . watchonly_amount }
2015-04-24 06:42:49 +02:00
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
2020-11-02 16:54:06 +01: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 )
result = wwatch . fundrawtransaction ( rawtx , { ' includeWatching ' : True , ' changeAddress ' : w3 . getrawchangeaddress ( ) , ' subtractFeeFromOutputs ' : [ 0 ] } )
2015-04-24 06:42:49 +02:00
res_dec = self . nodes [ 0 ] . decoderawtransaction ( result [ " hex " ] )
2020-11-02 16:54:06 +01:00
assert_equal ( len ( res_dec [ " vin " ] ) , 1 )
assert res_dec [ " vin " ] [ 0 ] [ " txid " ] == self . watchonly_txid
2015-04-24 06:42:49 +02:00
assert_greater_than ( result [ " fee " ] , 0 )
2020-11-02 16:54:06 +01:00
assert_equal ( result [ " changepos " ] , - 1 )
assert_equal ( result [ " fee " ] + res_dec [ " vout " ] [ 0 ] [ " value " ] , self . watchonly_amount )
2015-04-24 06:42:49 +02:00
2020-11-02 16:54:06 +01:00
signedtx = wwatch . signrawtransactionwithwallet ( result [ " hex " ] )
2021-08-27 21:03:02 +02:00
assert not signedtx [ " complete " ]
2018-02-20 03:29:22 +01:00
signedtx = self . nodes [ 0 ] . signrawtransactionwithwallet ( signedtx [ " hex " ] )
2021-08-27 21:03:02 +02:00
assert signedtx [ " complete " ]
2015-04-24 06:42:49 +02:00
self . nodes [ 0 ] . sendrawtransaction ( signedtx [ " hex " ] )
2016-06-14 08:23:31 +02:00
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
2020-11-02 16:54:06 +01:00
wwatch . unloadwallet ( )
2019-08-01 00:03:43 +02:00
def test_option_feerate ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn feeRate option " )
2016-06-14 08:23:31 +02:00
2019-11-01 16:06:12 +01:00
# Make sure there is exactly one input so coin selection can't skew the result.
2016-06-14 08:23:31 +02:00
assert_equal ( len ( self . nodes [ 3 ] . listunspent ( 1 ) ) , 1 )
2015-04-24 06:42:49 +02:00
2016-06-03 15:31:49 +02:00
inputs = [ ]
2017-01-20 14:32:09 +01:00
outputs = { self . nodes [ 3 ] . getnewaddress ( ) : 1 }
2016-06-03 15:31:49 +02:00
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
2019-08-01 00:03:43 +02:00
result = self . nodes [ 3 ] . fundrawtransaction ( rawtx ) # uses self.min_relay_tx_fee (set by settxfee)
result2 = self . nodes [ 3 ] . fundrawtransaction ( rawtx , { " feeRate " : 2 * self . min_relay_tx_fee } )
result3 = self . nodes [ 3 ] . fundrawtransaction ( rawtx , { " feeRate " : 10 * self . min_relay_tx_fee } )
2024-01-25 07:53:18 +01:00
assert_raises_rpc_error ( - 4 , " Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate) " , self . nodes [ 3 ] . fundrawtransaction , rawtx , { " feeRate " : 1 } )
2016-06-14 08:23:31 +02:00
result_fee_rate = result [ ' fee ' ] * 1000 / count_bytes ( result [ ' hex ' ] )
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 )
2015-04-24 06:42:49 +02:00
2019-08-01 00:03:43 +02:00
def test_address_reuse ( self ) :
2019-11-01 16:06:12 +01:00
""" Test no address reuse occurs. """
self . log . info ( " Test fundrawtxn does not reuse addresses " )
2017-01-20 14:32:09 +01:00
2019-08-01 00:03:43 +02:00
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs = [ ] , outputs = { self . nodes [ 3 ] . getnewaddress ( ) : 1 } )
2017-01-20 14:32:09 +01: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 :
2024-06-23 17:51:21 +02:00
changeaddress + = out [ ' scriptPubKey ' ] [ ' address ' ]
2021-08-27 21:03:02 +02:00
assert changeaddress != " "
2017-01-20 14:32:09 +01:00
nextaddr = self . nodes [ 3 ] . getnewaddress ( )
2019-11-01 16:06:12 +01:00
# Now the change address key should be removed from the keypool.
2021-08-27 21:03:02 +02:00
assert changeaddress != nextaddr
2017-01-20 14:32:09 +01:00
2019-08-01 00:03:43 +02:00
def test_option_subtract_fee_from_outputs ( self ) :
2019-11-01 16:06:12 +01:00
self . log . info ( " Test fundrawtxn subtractFeeFromOutputs option " )
2017-01-12 12:49:06 +01:00
2019-11-01 16:06:12 +01:00
# Make sure there is exactly one input so coin selection can't skew the result.
2017-01-12 12:49:06 +01:00
assert_equal ( len ( self . nodes [ 3 ] . listunspent ( 1 ) ) , 1 )
inputs = [ ]
outputs = { self . nodes [ 2 ] . getnewaddress ( ) : 1 }
rawtx = self . nodes [ 3 ] . createrawtransaction ( inputs , outputs )
2019-08-01 00:03:43 +02:00
result = [ self . nodes [ 3 ] . fundrawtransaction ( rawtx ) , # uses self.min_relay_tx_fee (set by settxfee)
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 ] } ) , ]
2017-01-12 12:49:06 +01:00
2018-02-08 15:52:01 +01:00
dec_tx = [ self . nodes [ 3 ] . decoderawtransaction ( tx_ [ ' hex ' ] ) for tx_ in result ]
2017-01-12 12:49:06 +01: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 )
2019-09-22 22:47:17 +02:00
# Add changePosition=4 to circumvent BIP69 input/output sorting
result = [ self . nodes [ 3 ] . fundrawtransaction ( rawtx , { " changePosition " : 4 } ) ,
2019-11-01 16:06:12 +01:00
# Split the fee between outputs 0, 2, and 3, but not output 1.
2019-09-22 22:47:17 +02:00
self . nodes [ 3 ] . fundrawtransaction ( rawtx , { " subtractFeeFromOutputs " : [ 0 , 2 , 3 ] , " changePosition " : 4 } ) ]
2017-01-12 12:49:06 +01:00
dec_tx = [ self . nodes [ 3 ] . decoderawtransaction ( result [ 0 ] [ ' hex ' ] ) ,
self . nodes [ 3 ] . decoderawtransaction ( result [ 1 ] [ ' hex ' ] ) ]
2019-11-01 16:06:12 +01:00
# Nested list of non-change output amounts for each transaction.
2017-01-12 12:49:06 +01: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 16:06:12 +01:00
# List of differences in output amounts between normal and subtractFee transactions.
2017-01-12 12:49:06 +01:00
share = [ o0 - o1 for o0 , o1 in zip ( output [ 0 ] , output [ 1 ] ) ]
2019-11-01 16:06:12 +01:00
# Output 1 is the same in both transactions.
2017-01-12 12:49:06 +01:00
assert_equal ( share [ 1 ] , 0 )
2019-11-01 16:06:12 +01:00
# The other 3 outputs are smaller as a result of subtractFeeFromOutputs.
2017-01-12 12:49:06 +01:00
assert_greater_than ( share [ 0 ] , 0 )
assert_greater_than ( share [ 2 ] , 0 )
assert_greater_than ( share [ 3 ] , 0 )
2019-11-01 16:06:12 +01:00
# Outputs 2 and 3 take the same share of the fee.
2017-01-12 12:49:06 +01:00
assert_equal ( share [ 2 ] , share [ 3 ] )
2019-11-01 16:06:12 +01:00
# Output 0 takes at least as much share of the fee, and no more than 2
# satoshis more, than outputs 2 and 3.
2017-01-12 12:49:06 +01: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 16:06:12 +01:00
# The fee is the same in both transactions.
2017-01-12 12:49:06 +01:00
assert_equal ( result [ 0 ] [ ' fee ' ] , result [ 1 ] [ ' fee ' ] )
2019-11-01 16:06:12 +01:00
# The total subtracted from the outputs is equal to the fee.
2017-01-12 12:49:06 +01:00
assert_equal ( share [ 0 ] + share [ 2 ] + share [ 3 ] , result [ 0 ] [ ' fee ' ] )
2019-12-01 17:56:52 +01: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 ( )
txid = self . nodes [ 0 ] . sendtoaddress ( addr , 10 )
vout = find_vout_for_address ( self . nodes [ 0 ] , txid , addr )
rawtx = self . nodes [ 0 ] . createrawtransaction ( [ { ' txid ' : txid , ' vout ' : vout } ] , [ { self . nodes [ 0 ] . getnewaddress ( ) : 5 } ] )
fundedtx = self . nodes [ 0 ] . fundrawtransaction ( rawtx , { ' subtractFeeFromOutputs ' : [ 0 ] } )
signedtx = self . nodes [ 0 ] . signrawtransactionwithwallet ( fundedtx [ ' hex ' ] )
self . nodes [ 0 ] . sendrawtransaction ( signedtx [ ' hex ' ] )
2024-06-23 14:37:45 +02: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 ( )
txid1 = self . nodes [ 2 ] . sendtoaddress ( addr , 6 )
txid2 = self . nodes [ 2 ] . sendtoaddress ( addr , 4 )
self . sync_all ( )
vout1 = find_vout_for_address ( wallet , txid1 , addr )
vout2 = find_vout_for_address ( wallet , txid2 , addr )
# Unsafe inputs are ignored by default.
rawtx = wallet . createrawtransaction ( [ ] , [ { self . nodes [ 2 ] . getnewaddress ( ) : 5 } ] )
assert_raises_rpc_error ( - 4 , " Insufficient funds " , wallet . fundrawtransaction , rawtx )
# But we can opt-in to use them for funding.
fundedtx = wallet . fundrawtransaction ( rawtx , { " include_unsafe " : True } )
tx_dec = wallet . decoderawtransaction ( fundedtx [ ' hex ' ] )
assert any ( [ txin [ ' txid ' ] == txid1 and txin [ ' vout ' ] == vout1 for txin in tx_dec [ ' vin ' ] ] )
signedtx = wallet . signrawtransactionwithwallet ( fundedtx [ ' hex ' ] )
wallet . sendrawtransaction ( signedtx [ ' hex ' ] )
# And we can also use them once they're confirmed.
self . nodes [ 0 ] . generate ( 1 )
rawtx = wallet . createrawtransaction ( [ ] , [ { self . nodes [ 2 ] . getnewaddress ( ) : 3 } ] )
fundedtx = wallet . fundrawtransaction ( rawtx , { " include_unsafe " : True } )
tx_dec = wallet . decoderawtransaction ( fundedtx [ ' hex ' ] )
assert any ( [ txin [ ' txid ' ] == txid2 and txin [ ' vout ' ] == vout2 for txin in tx_dec [ ' vin ' ] ] )
signedtx = wallet . signrawtransactionwithwallet ( fundedtx [ ' hex ' ] )
wallet . sendrawtransaction ( signedtx [ ' hex ' ] )
2015-04-25 03:26:30 +02:00
if __name__ == ' __main__ ' :
RawTransactionsTest ( ) . main ( )