2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2021-07-28 13:57:16 +02:00
# Copyright (c) 2014-2021 The Bitcoin Core developers
2014-12-13 05:09:33 +01:00
# Distributed under the MIT software license, see the accompanying
2014-11-19 21:55:40 +01:00
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-18 00:34:40 +01:00
""" Test the wallet. """
2018-04-24 21:55:53 +02:00
from decimal import Decimal
2020-11-04 13:13:17 +01:00
from itertools import product
2018-04-24 21:55:53 +02:00
2021-05-17 16:38:19 +02:00
from test_framework . blocktools import COINBASE_MATURITY
2022-07-05 14:42:06 +02:00
from test_framework . descriptors import descsum_create
2015-05-02 12:53:35 +02:00
from test_framework . test_framework import BitcoinTestFramework
2018-04-24 21:55:53 +02:00
from test_framework . util import (
assert_array_result ,
assert_equal ,
assert_fee_amount ,
assert_raises_rpc_error ,
2021-09-02 21:25:13 +02:00
find_vout_for_address ,
2018-04-24 21:55:53 +02:00
)
2019-11-24 12:05:38 +01:00
from test_framework . wallet_util import test_address
2014-11-19 21:55:40 +01:00
2020-12-04 12:51:34 +01:00
NOT_A_NUMBER_OR_STRING = " Amount is not a number or string "
2020-11-04 13:13:17 +01:00
OUT_OF_RANGE = " Amount out of range "
2019-01-25 17:34:39 +01:00
2017-04-26 03:22:28 +02:00
class WalletTest ( BitcoinTestFramework ) :
2017-06-10 00:21:21 +02:00
def set_test_params ( self ) :
2016-05-14 13:01:31 +02:00
self . num_nodes = 4
2019-04-24 23:55:58 +02:00
self . extra_args = [ [
2022-08-18 00:15:21 +02:00
" -dustrelayfee=0 " , " -walletrejectlongchains=0 " , " -whitelist=noban@127.0.0.1 "
2019-04-24 23:55:58 +02:00
] ] * self . num_nodes
2016-05-14 13:01:31 +02:00
self . setup_clean_chain = True
2019-12-06 15:37:49 +01:00
self . supports_cli = False
2014-11-19 21:55:40 +01:00
2018-09-09 19:32:37 +02:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2017-04-03 15:34:04 +02:00
def setup_network ( self ) :
2018-10-16 17:21:07 +02:00
self . setup_nodes ( )
# Only need nodes 0-2 running at start of test
self . stop_node ( 3 )
2020-09-17 09:46:07 +02:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
2019-04-09 17:46:05 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2014-11-19 21:55:40 +01:00
2017-06-10 00:21:21 +02:00
def check_fee_amount ( self , curr_balance , balance_with_fee , fee_per_byte , tx_size ) :
""" Return curr_balance after asserting the fee was in range """
fee = balance_with_fee - curr_balance
assert_fee_amount ( fee , tx_size , fee_per_byte * 1000 )
return curr_balance
2016-04-19 13:28:37 +02:00
2017-12-01 01:49:11 +01:00
def get_vsize ( self , txn ) :
return self . nodes [ 0 ] . decoderawtransaction ( txn ) [ ' vsize ' ]
2017-06-10 00:21:21 +02:00
def run_test ( self ) :
2019-07-16 21:33:35 +02:00
2016-04-19 13:28:37 +02:00
# Check that there's no UTXO on none of the nodes
assert_equal ( len ( self . nodes [ 0 ] . listunspent ( ) ) , 0 )
assert_equal ( len ( self . nodes [ 1 ] . listunspent ( ) ) , 0 )
assert_equal ( len ( self . nodes [ 2 ] . listunspent ( ) ) , 0 )
2017-03-08 00:46:17 +01:00
self . log . info ( " Mining blocks... " )
2014-11-19 21:55:40 +01:00
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = self . no_op )
2014-11-19 21:55:40 +01:00
2014-12-25 08:57:16 +01:00
walletinfo = self . nodes [ 0 ] . getwalletinfo ( )
assert_equal ( walletinfo [ ' immature_balance ' ] , 50 )
assert_equal ( walletinfo [ ' balance ' ] , 0 )
2019-04-09 17:46:05 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 1 ] , COINBASE_MATURITY + 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2014-11-19 21:55:40 +01:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , 50 )
assert_equal ( self . nodes [ 1 ] . getbalance ( ) , 50 )
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , 0 )
2016-04-19 13:28:37 +02:00
# Check that only first and second nodes have UTXOs
2017-04-26 03:22:28 +02:00
utxos = self . nodes [ 0 ] . listunspent ( )
assert_equal ( len ( utxos ) , 1 )
2016-04-19 13:28:37 +02:00
assert_equal ( len ( self . nodes [ 1 ] . listunspent ( ) ) , 1 )
assert_equal ( len ( self . nodes [ 2 ] . listunspent ( ) ) , 0 )
2020-11-04 13:13:17 +01:00
self . log . info ( " Test gettxout " )
2017-07-18 02:19:21 +02:00
confirmed_txid , confirmed_index = utxos [ 0 ] [ " txid " ] , utxos [ 0 ] [ " vout " ]
# First, outputs that are unspent both in the chain and in the
# mempool should appear with or without include_mempool
txout = self . nodes [ 0 ] . gettxout ( txid = confirmed_txid , n = confirmed_index , include_mempool = False )
assert_equal ( txout [ ' value ' ] , 50 )
txout = self . nodes [ 0 ] . gettxout ( txid = confirmed_txid , n = confirmed_index , include_mempool = True )
assert_equal ( txout [ ' value ' ] , 50 )
2017-06-13 03:53:46 +02:00
2014-11-19 21:55:40 +01:00
# Send 21 BTC from 0 to 2 using sendtoaddress call.
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 11 )
2017-04-26 03:22:28 +02:00
mempool_txid = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 10 )
2020-11-04 13:13:17 +01:00
self . log . info ( " Test gettxout (second part) " )
2017-04-26 03:22:28 +02:00
# utxo spent in mempool should be visible if you exclude mempool
# but invisible if you include mempool
txout = self . nodes [ 0 ] . gettxout ( confirmed_txid , confirmed_index , False )
assert_equal ( txout [ ' value ' ] , 50 )
2021-04-17 00:34:40 +02:00
txout = self . nodes [ 0 ] . gettxout ( confirmed_txid , confirmed_index ) # by default include_mempool=True
assert txout is None
2017-04-26 03:22:28 +02:00
txout = self . nodes [ 0 ] . gettxout ( confirmed_txid , confirmed_index , True )
assert txout is None
# new utxo from mempool should be invisible if you exclude mempool
# but visible if you include mempool
txout = self . nodes [ 0 ] . gettxout ( mempool_txid , 0 , False )
assert txout is None
txout1 = self . nodes [ 0 ] . gettxout ( mempool_txid , 0 , True )
txout2 = self . nodes [ 0 ] . gettxout ( mempool_txid , 1 , True )
# note the mempool tx will have randomly assigned indices
# but 10 will go to node2 and the rest will go to node0
balance = self . nodes [ 0 ] . getbalance ( )
assert_equal ( set ( [ txout1 [ ' value ' ] , txout2 [ ' value ' ] ] ) , set ( [ 10 , balance ] ) )
2014-12-25 08:57:16 +01:00
walletinfo = self . nodes [ 0 ] . getwalletinfo ( )
assert_equal ( walletinfo [ ' immature_balance ' ] , 0 )
2015-04-28 16:48:28 +02:00
# Have node0 mine a block, thus it will collect its own fee.
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2014-11-19 21:55:40 +01:00
2016-03-06 18:30:51 +01:00
# Exercise locking of unspent outputs
unspent_0 = self . nodes [ 2 ] . listunspent ( ) [ 0 ]
unspent_0 = { " txid " : unspent_0 [ " txid " ] , " vout " : unspent_0 [ " vout " ] }
2021-09-22 14:19:05 +02:00
# Trying to unlock an output which isn't locked should error
2017-08-18 14:21:40 +02:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, expected locked output " , self . nodes [ 2 ] . lockunspent , True , [ unspent_0 ] )
2021-09-22 14:19:05 +02:00
# Locking an already-locked output should error
2016-03-06 18:30:51 +01:00
self . nodes [ 2 ] . lockunspent ( False , [ unspent_0 ] )
2017-08-18 14:21:40 +02:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, output already locked " , self . nodes [ 2 ] . lockunspent , False , [ unspent_0 ] )
2021-09-22 14:19:05 +02:00
# Restarting the node should clear the lock
self . restart_node ( 2 )
self . nodes [ 2 ] . lockunspent ( False , [ unspent_0 ] )
# Unloading and reloating the wallet should clear the lock
assert_equal ( self . nodes [ 0 ] . listwallets ( ) , [ self . default_wallet_name ] )
self . nodes [ 2 ] . unloadwallet ( self . default_wallet_name )
self . nodes [ 2 ] . loadwallet ( self . default_wallet_name )
assert_equal ( len ( self . nodes [ 2 ] . listlockunspent ( ) ) , 0 )
# Locking non-persistently, then re-locking persistently, is allowed
self . nodes [ 2 ] . lockunspent ( False , [ unspent_0 ] )
self . nodes [ 2 ] . lockunspent ( False , [ unspent_0 ] , True )
# Restarting the node with the lock written to the wallet should keep the lock
2022-03-08 12:07:39 +01:00
self . restart_node ( 2 , [ " -walletrejectlongchains=0 " ] )
2021-09-22 14:19:05 +02:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, output already locked " , self . nodes [ 2 ] . lockunspent , False , [ unspent_0 ] )
# Unloading and reloading the wallet with a persistent lock should keep the lock
self . nodes [ 2 ] . unloadwallet ( self . default_wallet_name )
self . nodes [ 2 ] . loadwallet ( self . default_wallet_name )
assert_raises_rpc_error ( - 8 , " Invalid parameter, output already locked " , self . nodes [ 2 ] . lockunspent , False , [ unspent_0 ] )
# Locked outputs should not be used, even if they are the only available funds
2020-02-24 20:12:50 +01:00
assert_raises_rpc_error ( - 6 , " Insufficient funds " , self . nodes [ 2 ] . sendtoaddress , self . nodes [ 2 ] . getnewaddress ( ) , 20 )
2016-03-06 18:30:51 +01:00
assert_equal ( [ unspent_0 ] , self . nodes [ 2 ] . listlockunspent ( ) )
2021-09-22 14:19:05 +02:00
# Unlocking should remove the persistent lock
2016-03-06 18:30:51 +01:00
self . nodes [ 2 ] . lockunspent ( True , [ unspent_0 ] )
2021-09-22 14:19:05 +02:00
self . restart_node ( 2 )
2016-03-06 18:30:51 +01:00
assert_equal ( len ( self . nodes [ 2 ] . listlockunspent ( ) ) , 0 )
2021-09-22 14:19:05 +02:00
# Reconnect node 2 after restarts
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
2018-06-08 20:16:07 +02:00
assert_raises_rpc_error ( - 8 , " txid must be of length 64 (not 34, for ' 0000000000000000000000000000000000 ' ) " ,
2018-04-24 21:55:53 +02:00
self . nodes [ 2 ] . lockunspent , False ,
[ { " txid " : " 0000000000000000000000000000000000 " , " vout " : 0 } ] )
2018-06-08 20:16:07 +02:00
assert_raises_rpc_error ( - 8 , " txid must be hexadecimal string (not ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' ) " ,
self . nodes [ 2 ] . lockunspent , False ,
[ { " txid " : " ZZZ0000000000000000000000000000000000000000000000000000000000000 " , " vout " : 0 } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, unknown transaction " ,
self . nodes [ 2 ] . lockunspent , False ,
[ { " txid " : " 0000000000000000000000000000000000000000000000000000000000000000 " , " vout " : 0 } ] )
2017-08-18 14:21:40 +02:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, vout index out of bounds " ,
2018-04-24 21:55:53 +02:00
self . nodes [ 2 ] . lockunspent , False ,
[ { " txid " : unspent_0 [ " txid " ] , " vout " : 999 } ] )
2016-03-06 18:30:51 +01:00
2020-08-07 13:53:51 +02:00
# The lock on a manually selected output is ignored
2018-05-25 15:28:37 +02:00
unspent_0 = self . nodes [ 1 ] . listunspent ( ) [ 0 ]
self . nodes [ 1 ] . lockunspent ( False , [ unspent_0 ] )
tx = self . nodes [ 1 ] . createrawtransaction ( [ unspent_0 ] , { self . nodes [ 1 ] . getnewaddress ( ) : 1 } )
2020-08-07 13:53:51 +02:00
self . nodes [ 1 ] . fundrawtransaction ( tx , { " lockUnspents " : True } )
# fundrawtransaction can lock an input
self . nodes [ 1 ] . lockunspent ( True , [ unspent_0 ] )
assert_equal ( len ( self . nodes [ 1 ] . listlockunspent ( ) ) , 0 )
tx = self . nodes [ 1 ] . fundrawtransaction ( tx , { " lockUnspents " : True } ) [ ' hex ' ]
assert_equal ( len ( self . nodes [ 1 ] . listlockunspent ( ) ) , 1 )
# Send transaction
2018-05-25 15:28:37 +02:00
tx = self . nodes [ 1 ] . signrawtransactionwithwallet ( tx ) [ " hex " ]
self . nodes [ 1 ] . sendrawtransaction ( tx )
assert_equal ( len ( self . nodes [ 1 ] . listlockunspent ( ) ) , 0 )
2014-11-19 21:55:40 +01:00
# Have node1 generate 100 blocks (so node0 can recover the fee)
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 1 ] , COINBASE_MATURITY , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2014-11-19 21:55:40 +01:00
# node0 should end up with 100 btc in block rewards plus fees, but
# minus the 21 plus fees sent to node2
2018-04-24 21:55:53 +02:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , 100 - 21 )
2014-11-19 21:55:40 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , 21 )
# Node0 should have two unspent outputs.
2015-07-14 21:13:16 +02:00
# Create a couple of transactions to send them to node2, submit them through
# node1, and make sure both node0 and node2 pick them up properly:
2014-11-19 21:55:40 +01:00
node0utxos = self . nodes [ 0 ] . listunspent ( 1 )
assert_equal ( len ( node0utxos ) , 2 )
# create both transactions
txns_to_send = [ ]
2015-07-14 21:13:16 +02:00
for utxo in node0utxos :
2014-11-19 21:55:40 +01:00
inputs = [ ]
outputs = { }
2018-04-24 21:55:53 +02:00
inputs . append ( { " txid " : utxo [ " txid " ] , " vout " : utxo [ " vout " ] } )
2018-04-24 21:50:00 +02:00
outputs [ self . nodes [ 2 ] . getnewaddress ( ) ] = utxo [ " amount " ] - 3
2014-11-19 21:55:40 +01:00
raw_tx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
2017-09-06 01:49:18 +02:00
txns_to_send . append ( self . nodes [ 0 ] . signrawtransactionwithwallet ( raw_tx ) )
2014-11-19 21:55:40 +01:00
# Have node 1 (miner) send the transactions
2019-09-21 16:03:28 +02:00
self . nodes [ 1 ] . sendrawtransaction ( hexstring = txns_to_send [ 0 ] [ " hex " ] , maxfeerate = 0 )
self . nodes [ 1 ] . sendrawtransaction ( hexstring = txns_to_send [ 1 ] [ " hex " ] , maxfeerate = 0 )
2014-11-19 21:55:40 +01:00
# Have node1 mine a block to confirm transactions:
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 1 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2014-11-19 21:55:40 +01:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , 0 )
2015-12-06 20:24:02 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , 94 )
2014-11-19 21:55:40 +01:00
2017-08-18 14:21:40 +02:00
# Verify that a spent output cannot be locked anymore
spent_0 = { " txid " : node0utxos [ 0 ] [ " txid " ] , " vout " : node0utxos [ 0 ] [ " vout " ] }
assert_raises_rpc_error ( - 8 , " Invalid parameter, expected unspent output " , self . nodes [ 0 ] . lockunspent , False , [ spent_0 ] )
2015-03-05 14:59:19 +01:00
# Send 10 BTC normal
2014-07-23 14:34:36 +02:00
address = self . nodes [ 0 ] . getnewaddress ( " test " )
2015-09-21 12:49:13 +02:00
fee_per_byte = Decimal ( ' 0.001 ' ) / 1000
self . nodes [ 2 ] . settxfee ( fee_per_byte * 1000 )
2014-07-23 14:34:36 +02:00
txid = self . nodes [ 2 ] . sendtoaddress ( address , 10 , " " , " " , False )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 2 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2019-01-25 00:52:29 +01:00
node_2_bal = self . check_fee_amount ( self . nodes [ 2 ] . getbalance ( ) , Decimal ( ' 84 ' ) , fee_per_byte , self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2015-09-21 12:49:13 +02:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , Decimal ( ' 10 ' ) )
2014-07-23 14:34:36 +02:00
2015-03-05 14:59:19 +01:00
# Send 10 BTC with subtract fee from amount
2014-07-23 14:34:36 +02:00
txid = self . nodes [ 2 ] . sendtoaddress ( address , 10 , " " , " " , True )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 2 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2015-09-21 12:49:13 +02:00
node_2_bal - = Decimal ( ' 10 ' )
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2019-01-25 00:52:29 +01:00
node_0_bal = self . check_fee_amount ( self . nodes [ 0 ] . getbalance ( ) , Decimal ( ' 20 ' ) , fee_per_byte , self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2014-11-19 21:55:40 +01:00
2020-10-25 20:52:17 +01:00
self . log . info ( " Test sendmany " )
2015-03-05 14:59:19 +01:00
# Sendmany 10 BTC
2018-04-24 21:50:00 +02:00
txid = self . nodes [ 2 ] . sendmany ( ' ' , { address : 10 } , 0 , " " , [ ] )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 2 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2015-09-21 12:49:13 +02:00
node_0_bal + = Decimal ( ' 10 ' )
2019-01-25 00:52:29 +01:00
node_2_bal = self . check_fee_amount ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal - Decimal ( ' 10 ' ) , fee_per_byte , self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2015-09-21 12:49:13 +02:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , node_0_bal )
2015-03-05 14:59:19 +01:00
# Sendmany 10 BTC with subtract fee from amount
2018-04-24 21:50:00 +02:00
txid = self . nodes [ 2 ] . sendmany ( ' ' , { address : 10 } , 0 , " " , [ address ] )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 2 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2015-09-21 12:49:13 +02:00
node_2_bal - = Decimal ( ' 10 ' )
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2019-01-25 00:52:29 +01:00
node_0_bal = self . check_fee_amount ( self . nodes [ 0 ] . getbalance ( ) , node_0_bal + Decimal ( ' 10 ' ) , fee_per_byte , self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2015-03-05 14:59:19 +01:00
2020-11-04 13:13:17 +01:00
self . log . info ( " Test sendmany with fee_rate param (explicit fee rate in sat/vB) " )
fee_rate_sat_vb = 2
fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
explicit_fee_rate_btc_kvb = Decimal ( fee_rate_btc_kvb ) / 1000
2019-08-29 05:31:00 +02:00
2020-12-04 11:22:34 +01:00
# Test passing fee_rate as a string
txid = self . nodes [ 2 ] . sendmany ( amounts = { address : 10 } , fee_rate = str ( fee_rate_sat_vb ) )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 2 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2019-08-29 05:31:00 +02:00
balance = self . nodes [ 2 ] . getbalance ( )
2020-11-04 13:13:17 +01:00
node_2_bal = self . check_fee_amount ( balance , node_2_bal - Decimal ( ' 10 ' ) , explicit_fee_rate_btc_kvb , self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2019-08-29 05:31:00 +02:00
assert_equal ( balance , node_2_bal )
node_0_bal + = Decimal ( ' 10 ' )
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , node_0_bal )
2020-12-04 11:22:34 +01:00
# Test passing fee_rate as an integer
amount = Decimal ( " 0.0001 " )
txid = self . nodes [ 2 ] . sendmany ( amounts = { address : amount } , fee_rate = fee_rate_sat_vb )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 2 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2020-12-04 11:22:34 +01:00
balance = self . nodes [ 2 ] . getbalance ( )
node_2_bal = self . check_fee_amount ( balance , node_2_bal - amount , explicit_fee_rate_btc_kvb , self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
assert_equal ( balance , node_2_bal )
node_0_bal + = amount
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , node_0_bal )
2020-11-04 13:13:17 +01:00
for key in [ " totalFee " , " feeRate " ] :
assert_raises_rpc_error ( - 8 , " Unknown named parameter key " , self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1 , fee_rate = 1 , key = 1 )
2020-10-25 20:52:17 +01:00
# Test setting explicit fee rate just below the minimum.
2020-11-04 13:13:17 +01:00
self . log . info ( " Test sendmany raises ' fee rate too low ' if fee_rate of 0.99999999 is passed " )
2020-11-08 19:57:35 +01:00
assert_raises_rpc_error ( - 6 , " Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB) " ,
2021-04-27 11:04:10 +02:00
self . nodes [ 2 ] . sendmany , amounts = { address : 10 } , fee_rate = 0.999 )
2020-11-04 13:13:17 +01:00
2020-12-03 10:55:15 +01:00
self . log . info ( " Test sendmany raises if an invalid fee_rate is passed " )
2020-12-04 11:28:47 +01:00
# Test fee_rate with zero values.
msg = " Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB) "
for zero_value in [ 0 , 0.000 , 0.00000000 , " 0 " , " 0.000 " , " 0.00000000 " ] :
assert_raises_rpc_error ( - 6 , msg , self . nodes [ 2 ] . sendmany , amounts = { address : 1 } , fee_rate = zero_value )
2020-12-03 10:55:15 +01:00
msg = " Invalid amount "
# 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 " ] :
assert_raises_rpc_error ( - 3 , msg , self . nodes [ 2 ] . sendmany , amounts = { address : 1.0 } , fee_rate = invalid_value )
2020-12-02 13:55:26 +01:00
# Test fee_rate values that cannot be represented in sat/vB.
for invalid_value in [ 0.0001 , 0.00000001 , 0.00099999 , 31.99999999 , " 0.0001 " , " 0.00000001 " , " 0.00099999 " , " 31.99999999 " ] :
assert_raises_rpc_error ( - 3 , msg , self . nodes [ 2 ] . sendmany , amounts = { address : 10 } , fee_rate = invalid_value )
2020-12-04 11:28:47 +01:00
# Test fee_rate out of range (negative number).
2020-11-04 13:13:17 +01:00
assert_raises_rpc_error ( - 3 , OUT_OF_RANGE , self . nodes [ 2 ] . sendmany , amounts = { address : 10 } , fee_rate = - 1 )
2020-12-04 12:51:34 +01:00
# Test type error.
for invalid_value in [ True , { " foo " : " bar " } ] :
assert_raises_rpc_error ( - 3 , NOT_A_NUMBER_OR_STRING , self . nodes [ 2 ] . sendmany , amounts = { address : 10 } , fee_rate = invalid_value )
2020-11-04 13:13:17 +01:00
self . log . info ( " Test sendmany raises if an invalid conf_target or estimate_mode is passed " )
for target , mode in product ( [ - 1 , 0 , 1009 ] , [ " economical " , " conservative " ] ) :
assert_raises_rpc_error ( - 8 , " Invalid conf_target, must be between 1 and 1008 " , # max value of 1008 per src/policy/fees.h
self . nodes [ 2 ] . sendmany , amounts = { address : 1 } , conf_target = target , estimate_mode = mode )
for target , mode in product ( [ - 1 , 0 ] , [ " btc/kb " , " sat/b " ] ) :
2020-11-10 12:29:01 +01:00
assert_raises_rpc_error ( - 8 , ' Invalid estimate_mode parameter, must be one of: " unset " , " economical " , " conservative " ' ,
2020-11-04 13:13:17 +01:00
self . nodes [ 2 ] . sendmany , amounts = { address : 1 } , conf_target = target , estimate_mode = mode )
2020-10-25 20:52:17 +01:00
2019-07-16 21:33:35 +02:00
self . start_node ( 3 , self . nodes [ 3 ] . extra_args )
2020-09-17 09:46:07 +02:00
self . connect_nodes ( 0 , 3 )
2019-03-22 19:14:32 +01:00
self . sync_all ( )
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
# check if we can list zero value tx as available coins
# 1. create raw_tx
# 2. hex-changed one output to 0.0
# 3. sign and send
# 4. check if recipient (node0) can list the zero value tx
2018-06-25 15:17:21 +02:00
usp = self . nodes [ 1 ] . listunspent ( query_options = { ' minimumAmount ' : ' 49.998 ' } ) [ 0 ]
inputs = [ { " txid " : usp [ ' txid ' ] , " vout " : usp [ ' vout ' ] } ]
2015-04-21 10:27:35 +02:00
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 49.998 , self . nodes [ 0 ] . getnewaddress ( ) : 11.11 }
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
raw_tx = self . nodes [ 1 ] . createrawtransaction ( inputs , outputs ) . replace ( " c0833842 " , " 00000000 " ) # replace 11.11 with 0.0 (int32)
signed_raw_tx = self . nodes [ 1 ] . signrawtransactionwithwallet ( raw_tx )
decoded_raw_tx = self . nodes [ 1 ] . decoderawtransaction ( signed_raw_tx [ ' hex ' ] )
zero_value_txid = decoded_raw_tx [ ' txid ' ]
self . nodes [ 1 ] . sendrawtransaction ( signed_raw_tx [ ' hex ' ] )
2015-07-14 21:13:16 +02:00
2015-04-21 10:27:35 +02:00
self . sync_all ( )
2021-08-19 17:10:24 +02:00
self . generate ( self . nodes [ 1 ] , 1 ) # mine a block
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
unspent_txs = self . nodes [ 0 ] . listunspent ( ) # zero value tx must be in listunspents output
2015-04-21 10:27:35 +02:00
found = False
2018-04-24 21:55:53 +02:00
for uTx in unspent_txs :
if uTx [ ' txid ' ] == zero_value_txid :
2015-04-21 10:27:35 +02:00
found = True
2015-12-15 17:15:13 +01:00
assert_equal ( uTx [ ' amount ' ] , Decimal ( ' 0 ' ) )
2019-01-25 17:34:39 +01:00
assert found
2015-07-14 21:13:16 +02:00
2020-11-04 13:13:17 +01:00
self . log . info ( " Test -walletbroadcast " )
2017-03-24 04:56:31 +01:00
self . stop_nodes ( )
2017-06-09 22:35:17 +02:00
self . start_node ( 0 , [ " -walletbroadcast=0 " ] )
self . start_node ( 1 , [ " -walletbroadcast=0 " ] )
self . start_node ( 2 , [ " -walletbroadcast=0 " ] )
2020-09-17 09:46:07 +02:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
2019-04-09 17:46:05 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2015-03-23 18:47:18 +01:00
2018-04-24 21:55:53 +02:00
txid_not_broadcast = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 2 )
tx_obj_not_broadcast = self . nodes [ 0 ] . gettransaction ( txid_not_broadcast )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 1 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) ) # mine a block, tx should not be in there
2018-04-24 21:55:53 +02:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal ) # should not be changed because tx was not broadcasted
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
# now broadcast from another node, mine a block, sync, and check the balance
self . nodes [ 1 ] . sendrawtransaction ( tx_obj_not_broadcast [ ' hex ' ] )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 1 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2015-12-18 12:35:50 +01:00
node_2_bal + = 2
2018-04-24 21:55:53 +02:00
tx_obj_not_broadcast = self . nodes [ 0 ] . gettransaction ( txid_not_broadcast )
2015-12-18 12:35:50 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
# create another tx
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 2 )
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
# restart the nodes with -walletbroadcast=1
2017-03-24 04:56:31 +01:00
self . stop_nodes ( )
2017-06-09 22:35:17 +02:00
self . start_node ( 0 )
self . start_node ( 1 )
self . start_node ( 2 )
2020-09-17 09:46:07 +02:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
2019-04-07 00:19:45 +02:00
self . sync_blocks ( self . nodes [ 0 : 3 ] )
2015-07-14 21:13:16 +02:00
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = lambda : self . sync_blocks ( self . nodes [ 0 : 3 ] ) )
2015-12-18 12:35:50 +01:00
node_2_bal + = 2
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
# tx should be added to balance because after restarting the nodes tx should be broadcast
2015-12-18 12:35:50 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
# send a tx with value in a string (PR#6380 +)
txid = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , " 2 " )
tx_obj = self . nodes [ 0 ] . gettransaction ( txid )
assert_equal ( tx_obj [ ' amount ' ] , Decimal ( ' -2 ' ) )
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
txid = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , " 0.0001 " )
tx_obj = self . nodes [ 0 ] . gettransaction ( txid )
assert_equal ( tx_obj [ ' amount ' ] , Decimal ( ' -0.0001 ' ) )
2015-07-14 21:13:16 +02:00
2018-04-24 21:55:53 +02:00
# check if JSON parser can handle scientific notation in strings
txid = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , " 1e-4 " )
tx_obj = self . nodes [ 0 ] . gettransaction ( txid )
assert_equal ( tx_obj [ ' amount ' ] , Decimal ( ' -0.0001 ' ) )
2015-07-14 21:13:16 +02:00
2019-02-12 01:19:22 +01:00
# General checks for errors from incorrect inputs
2020-02-24 20:12:50 +01:00
# This will raise an exception because the amount is negative
2020-11-04 13:13:17 +01:00
assert_raises_rpc_error ( - 3 , OUT_OF_RANGE , self . nodes [ 0 ] . sendtoaddress , self . nodes [ 2 ] . getnewaddress ( ) , " -1 " )
2020-02-24 20:12:50 +01:00
2017-03-07 20:08:59 +01:00
# This will raise an exception because the amount type is wrong
2017-07-12 16:33:46 +02:00
assert_raises_rpc_error ( - 3 , " Invalid amount " , self . nodes [ 0 ] . sendtoaddress , self . nodes [ 2 ] . getnewaddress ( ) , " 1f-4 " )
2015-07-14 21:13:16 +02:00
2017-03-07 20:08:59 +01:00
# This will raise an exception since generate does not accept a string
2022-09-14 17:13:58 +02:00
assert_raises_rpc_error ( - 3 , " not of expected type number " , self . generate , self . nodes [ 0 ] , " 2 " )
2015-07-14 21:13:16 +02:00
2019-07-16 21:33:35 +02:00
if not self . options . descriptors :
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for the invalid private key format
assert_raises_rpc_error ( - 5 , " Invalid private key encoding " , self . nodes [ 0 ] . importprivkey , " invalid " )
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for importing an address with the PS2H flag
temp_address = self . nodes [ 1 ] . getnewaddress ( " " , " p2sh-segwit " )
assert_raises_rpc_error ( - 5 , " Cannot use the p2sh flag with an address - use a script instead " , self . nodes [ 0 ] . importaddress , temp_address , " label " , False , True )
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for attempting to dump the private key of an address you do not own
assert_raises_rpc_error ( - 3 , " Address does not refer to a key " , self . nodes [ 0 ] . dumpprivkey , temp_address )
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for attempting to get the private key of an invalid Bitcoin address
assert_raises_rpc_error ( - 5 , " Invalid Bitcoin address " , self . nodes [ 0 ] . dumpprivkey , " invalid " )
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for attempting to set a label for an invalid Bitcoin address
assert_raises_rpc_error ( - 5 , " Invalid Bitcoin address " , self . nodes [ 0 ] . setlabel , " invalid address " , " label " )
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for importing an invalid address
assert_raises_rpc_error ( - 5 , " Invalid Bitcoin address or script " , self . nodes [ 0 ] . importaddress , " invalid " )
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for attempting to import a pubkey that isn't in hex
assert_raises_rpc_error ( - 5 , " Pubkey must be a hex string " , self . nodes [ 0 ] . importpubkey , " not hex " )
2019-02-12 01:19:22 +01:00
2019-07-16 21:33:35 +02:00
# This will raise an exception for importing an invalid pubkey
assert_raises_rpc_error ( - 5 , " Pubkey is not a valid public key " , self . nodes [ 0 ] . importpubkey , " 5361746f736869204e616b616d6f746f " )
2021-06-04 23:35:47 +02:00
# Bech32m addresses cannot be imported into a legacy wallet
assert_raises_rpc_error ( - 5 , " Bech32m addresses cannot be imported into legacy wallets " , self . nodes [ 0 ] . importaddress , " bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqc8gma6 " )
2019-07-16 21:33:35 +02:00
# Import address and private key to check correct behavior of spendable unspents
# 1. Send some coins to generate new UTXO
address_to_import = self . nodes [ 2 ] . getnewaddress ( )
txid = self . nodes [ 0 ] . sendtoaddress ( address_to_import , 1 )
2021-09-02 21:25:13 +02:00
self . sync_mempools ( self . nodes [ 0 : 3 ] )
vout = find_vout_for_address ( self . nodes [ 2 ] , txid , address_to_import )
self . nodes [ 2 ] . lockunspent ( False , [ { " txid " : txid , " vout " : vout } ] )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2016-04-19 13:28:37 +02:00
2020-11-04 13:13:17 +01:00
self . log . info ( " Test sendtoaddress with fee_rate param (explicit fee rate in sat/vB) " )
2019-08-29 05:31:00 +02:00
prebalance = self . nodes [ 2 ] . getbalance ( )
assert prebalance > 2
address = self . nodes [ 1 ] . getnewaddress ( )
2020-11-04 13:13:17 +01:00
amount = 3
fee_rate_sat_vb = 2
fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
2020-12-04 11:22:34 +01:00
# Test passing fee_rate as an integer
2020-11-17 20:08:30 +01:00
txid = self . nodes [ 2 ] . sendtoaddress ( address = address , amount = amount , fee_rate = fee_rate_sat_vb )
2019-08-29 05:31:00 +02:00
tx_size = self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2019-08-29 05:31:00 +02:00
postbalance = self . nodes [ 2 ] . getbalance ( )
2020-11-04 13:13:17 +01:00
fee = prebalance - postbalance - Decimal ( amount )
assert_fee_amount ( fee , tx_size , Decimal ( fee_rate_btc_kvb ) )
2020-12-04 11:22:34 +01:00
prebalance = self . nodes [ 2 ] . getbalance ( )
amount = Decimal ( " 0.001 " )
fee_rate_sat_vb = 1.23
fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
# Test passing fee_rate as a string
txid = self . nodes [ 2 ] . sendtoaddress ( address = address , amount = amount , fee_rate = str ( fee_rate_sat_vb ) )
tx_size = self . get_vsize ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2020-12-04 11:22:34 +01:00
postbalance = self . nodes [ 2 ] . getbalance ( )
fee = prebalance - postbalance - amount
assert_fee_amount ( fee , tx_size , Decimal ( fee_rate_btc_kvb ) )
2020-11-04 13:13:17 +01:00
for key in [ " totalFee " , " feeRate " ] :
assert_raises_rpc_error ( - 8 , " Unknown named parameter key " , self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1 , fee_rate = 1 , key = 1 )
2019-08-29 05:31:00 +02:00
2020-10-25 20:52:17 +01:00
# Test setting explicit fee rate just below the minimum.
2020-11-04 13:13:17 +01:00
self . log . info ( " Test sendtoaddress raises ' fee rate too low ' if fee_rate of 0.99999999 is passed " )
2020-11-08 19:57:35 +01:00
assert_raises_rpc_error ( - 6 , " Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB) " ,
2021-04-27 11:04:10 +02:00
self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1 , fee_rate = 0.999 )
2020-11-04 13:13:17 +01:00
2020-12-03 10:55:15 +01:00
self . log . info ( " Test sendtoaddress raises if an invalid fee_rate is passed " )
2020-12-04 11:28:47 +01:00
# Test fee_rate with zero values.
msg = " Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB) "
for zero_value in [ 0 , 0.000 , 0.00000000 , " 0 " , " 0.000 " , " 0.00000000 " ] :
assert_raises_rpc_error ( - 6 , msg , self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1 , fee_rate = zero_value )
2020-12-03 10:55:15 +01:00
msg = " Invalid amount "
# 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 " ] :
assert_raises_rpc_error ( - 3 , msg , self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1.0 , fee_rate = invalid_value )
2020-12-02 13:55:26 +01:00
# Test fee_rate values that cannot be represented in sat/vB.
for invalid_value in [ 0.0001 , 0.00000001 , 0.00099999 , 31.99999999 , " 0.0001 " , " 0.00000001 " , " 0.00099999 " , " 31.99999999 " ] :
assert_raises_rpc_error ( - 3 , msg , self . nodes [ 2 ] . sendtoaddress , address = address , amount = 10 , fee_rate = invalid_value )
2020-12-04 11:28:47 +01:00
# Test fee_rate out of range (negative number).
2020-11-04 13:13:17 +01:00
assert_raises_rpc_error ( - 3 , OUT_OF_RANGE , self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1.0 , fee_rate = - 1 )
2020-12-04 12:51:34 +01:00
# Test type error.
for invalid_value in [ True , { " foo " : " bar " } ] :
assert_raises_rpc_error ( - 3 , NOT_A_NUMBER_OR_STRING , self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1.0 , fee_rate = invalid_value )
2020-11-04 13:13:17 +01:00
self . log . info ( " Test sendtoaddress raises if an invalid conf_target or estimate_mode is passed " )
for target , mode in product ( [ - 1 , 0 , 1009 ] , [ " economical " , " conservative " ] ) :
assert_raises_rpc_error ( - 8 , " Invalid conf_target, must be between 1 and 1008 " , # max value of 1008 per src/policy/fees.h
self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1 , conf_target = target , estimate_mode = mode )
for target , mode in product ( [ - 1 , 0 ] , [ " btc/kb " , " sat/b " ] ) :
2020-11-10 12:29:01 +01:00
assert_raises_rpc_error ( - 8 , ' Invalid estimate_mode parameter, must be one of: " unset " , " economical " , " conservative " ' ,
2020-11-04 13:13:17 +01:00
self . nodes [ 2 ] . sendtoaddress , address = address , amount = 1 , conf_target = target , estimate_mode = mode )
2020-10-25 20:52:17 +01:00
2019-07-16 21:33:35 +02:00
# 2. Import address from node2 to node1
self . nodes [ 1 ] . importaddress ( address_to_import )
2016-04-19 13:28:37 +02:00
2019-07-16 21:33:35 +02:00
# 3. Validate that the imported address is watch-only on node1
assert self . nodes [ 1 ] . getaddressinfo ( address_to_import ) [ " iswatchonly " ]
2016-04-19 13:28:37 +02:00
2019-07-16 21:33:35 +02:00
# 4. Check that the unspents after import are not spendable
assert_array_result ( self . nodes [ 1 ] . listunspent ( ) ,
{ " address " : address_to_import } ,
{ " spendable " : False } )
2016-04-19 13:28:37 +02:00
2019-07-16 21:33:35 +02:00
# 5. Import private key of the previously imported address on node1
priv_key = self . nodes [ 2 ] . dumpprivkey ( address_to_import )
self . nodes [ 1 ] . importprivkey ( priv_key )
2016-04-19 13:28:37 +02:00
2019-07-16 21:33:35 +02:00
# 6. Check that the unspents are now spendable on node1
assert_array_result ( self . nodes [ 1 ] . listunspent ( ) ,
{ " address " : address_to_import } ,
{ " spendable " : True } )
2015-07-14 21:13:16 +02:00
2016-03-14 22:54:34 +01:00
# Mine a block from node0 to an address from node1
2018-04-24 21:55:53 +02:00
coinbase_addr = self . nodes [ 1 ] . getnewaddress ( )
2020-11-10 18:02:31 +01:00
block_hash = self . generatetoaddress ( self . nodes [ 0 ] , 1 , coinbase_addr , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) ) [ 0 ]
2018-04-24 21:55:53 +02:00
coinbase_txid = self . nodes [ 0 ] . getblock ( block_hash ) [ ' tx ' ] [ 0 ]
2016-03-14 22:54:34 +01:00
# Check that the txid and balance is found by node1
2018-04-24 21:55:53 +02:00
self . nodes [ 1 ] . gettransaction ( coinbase_txid )
2016-03-14 22:54:34 +01:00
2016-05-10 17:22:01 +02:00
# check if wallet or blockchain maintenance changes the balance
2019-04-09 17:46:05 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2020-11-10 18:02:31 +01:00
blocks = self . generate ( self . nodes [ 0 ] , 2 , sync_fun = lambda : self . sync_all ( self . nodes [ 0 : 3 ] ) )
2015-12-18 12:35:50 +01:00
balance_nodes = [ self . nodes [ i ] . getbalance ( ) for i in range ( 3 ) ]
2016-03-28 21:47:13 +02:00
block_count = self . nodes [ 0 ] . getblockcount ( )
2015-12-18 12:35:50 +01:00
2016-04-28 15:18:01 +02:00
# Check modes:
# - True: unicode escaped as \u....
# - False: unicode directly as UTF-8
for mode in [ True , False ] :
2018-04-24 21:50:00 +02:00
self . nodes [ 0 ] . rpc . ensure_ascii = mode
2016-04-28 15:18:01 +02:00
# unicode check: Basic Multilingual Plane, Supplementary Plane respectively
2018-04-24 21:50:00 +02:00
for label in [ u ' рыба ' , u ' 𝅘𝅥𝅯 ' ] :
addr = self . nodes [ 0 ] . getnewaddress ( )
self . nodes [ 0 ] . setlabel ( addr , label )
2019-11-25 01:31:38 +01:00
test_address ( self . nodes [ 0 ] , addr , labels = [ label ] )
2019-01-25 17:34:39 +01:00
assert label in self . nodes [ 0 ] . listlabels ( )
2018-04-24 21:50:00 +02:00
self . nodes [ 0 ] . rpc . ensure_ascii = True # restore to default
2016-04-28 15:18:01 +02:00
2021-09-29 05:32:07 +02:00
# -reindex tests
2016-12-02 18:20:29 +01:00
chainlimit = 6
2021-09-29 05:32:07 +02:00
self . log . info ( " Test -reindex " )
self . stop_nodes ( )
# set lower ancestor limit for later
2022-03-08 12:07:39 +01:00
self . start_node ( 0 , [ ' -reindex ' , " -walletrejectlongchains=0 " , " -limitancestorcount= " + str ( chainlimit ) ] )
2021-09-29 05:32:07 +02:00
self . start_node ( 1 , [ ' -reindex ' , " -limitancestorcount= " + str ( chainlimit ) ] )
self . start_node ( 2 , [ ' -reindex ' , " -limitancestorcount= " + str ( chainlimit ) ] )
# reindex will leave rpc warm up "early"; Wait for it to finish
self . wait_until ( lambda : [ block_count ] * 3 == [ self . nodes [ i ] . getblockcount ( ) for i in range ( 3 ) ] )
assert_equal ( balance_nodes , [ self . nodes [ i ] . getbalance ( ) for i in range ( 3 ) ] )
2015-07-14 21:13:16 +02:00
2016-03-06 18:30:51 +01:00
# Exercise listsinceblock with the last two blocks
coinbase_tx_1 = self . nodes [ 0 ] . listsinceblock ( blocks [ 0 ] )
assert_equal ( coinbase_tx_1 [ " lastblock " ] , blocks [ 1 ] )
assert_equal ( len ( coinbase_tx_1 [ " transactions " ] ) , 1 )
assert_equal ( coinbase_tx_1 [ " transactions " ] [ 0 ] [ " blockhash " ] , blocks [ 1 ] )
assert_equal ( len ( self . nodes [ 0 ] . listsinceblock ( blocks [ 1 ] ) [ " transactions " ] ) , 0 )
2015-07-14 21:13:16 +02:00
2016-12-02 18:20:29 +01:00
# ==Check that wallet prefers to use coins that don't exceed mempool limits =====
2022-08-30 20:55:33 +02:00
# Get all non-zero utxos together and split into two chains
2016-12-02 18:20:29 +01:00
chain_addrs = [ self . nodes [ 0 ] . getnewaddress ( ) , self . nodes [ 0 ] . getnewaddress ( ) ]
2022-08-30 20:55:33 +02:00
self . nodes [ 0 ] . sendall ( recipients = chain_addrs )
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = self . no_op )
2016-12-02 18:20:29 +01:00
# Make a long chain of unconfirmed payments without hitting mempool limit
2016-12-21 03:54:31 +01:00
# Each tx we make leaves only one output of change on a chain 1 longer
# Since the amount to send is always much less than the outputs, we only ever need one output
# So we should be able to generate exactly chainlimit txs for each original output
sending_addr = self . nodes [ 1 ] . getnewaddress ( )
2016-12-02 18:20:29 +01:00
txid_list = [ ]
2020-08-03 01:10:56 +02:00
for _ in range ( chainlimit * 2 ) :
2016-12-21 03:54:31 +01:00
txid_list . append ( self . nodes [ 0 ] . sendtoaddress ( sending_addr , Decimal ( ' 0.0001 ' ) ) )
2018-04-24 21:55:53 +02:00
assert_equal ( self . nodes [ 0 ] . getmempoolinfo ( ) [ ' size ' ] , chainlimit * 2 )
assert_equal ( len ( txid_list ) , chainlimit * 2 )
2016-12-02 18:20:29 +01:00
2016-12-21 03:54:31 +01:00
# Without walletrejectlongchains, we will still generate a txid
# The tx will be stored in the wallet but not accepted to the mempool
extra_txid = self . nodes [ 0 ] . sendtoaddress ( sending_addr , Decimal ( ' 0.0001 ' ) )
2019-01-25 17:34:39 +01:00
assert extra_txid not in self . nodes [ 0 ] . getrawmempool ( )
assert extra_txid in [ tx [ " txid " ] for tx in self . nodes [ 0 ] . listtransactions ( ) ]
2016-12-21 03:54:31 +01:00
self . nodes [ 0 ] . abandontransaction ( extra_txid )
2018-04-24 21:55:53 +02:00
total_txs = len ( self . nodes [ 0 ] . listtransactions ( " * " , 99999 ) )
2016-12-21 03:54:31 +01:00
# Try with walletrejectlongchains
2021-02-13 00:01:22 +01:00
# Double chain limit but require combining inputs, so we pass AttemptSelection
2017-03-24 04:56:31 +01:00
self . stop_node ( 0 )
2019-07-16 21:33:35 +02:00
extra_args = [ " -walletrejectlongchains " , " -limitancestorcount= " + str ( 2 * chainlimit ) ]
self . start_node ( 0 , extra_args = extra_args )
2016-12-21 03:54:31 +01:00
2020-05-05 02:06:38 +02:00
# wait until the wallet has submitted all transactions to the mempool
2020-08-15 14:04:52 +02:00
self . wait_until ( lambda : len ( self . nodes [ 0 ] . getrawmempool ( ) ) == chainlimit * 2 )
2016-12-21 03:54:31 +01:00
2020-09-05 19:55:37 +02:00
# Prevent potential race condition when calling wallet RPCs right after restart
self . nodes [ 0 ] . syncwithvalidationinterfacequeue ( )
2016-12-21 03:54:31 +01:00
node0_balance = self . nodes [ 0 ] . getbalance ( )
# With walletrejectlongchains we will not create the tx and store it in our wallet.
2020-02-24 20:12:50 +01:00
assert_raises_rpc_error ( - 6 , " Transaction has too long of a mempool chain " , self . nodes [ 0 ] . sendtoaddress , sending_addr , node0_balance - Decimal ( ' 0.01 ' ) )
2016-12-21 03:54:31 +01:00
# Verify nothing new in wallet
2018-04-24 21:55:53 +02:00
assert_equal ( total_txs , len ( self . nodes [ 0 ] . listtransactions ( " * " , 99999 ) ) )
2016-12-21 03:54:31 +01:00
2018-10-12 10:57:22 +02:00
# Test getaddressinfo on external address. Note that these addresses are taken from disablewallet.py
2021-01-03 01:45:25 +01:00
assert_raises_rpc_error ( - 5 , " Invalid prefix for Base58-encoded address " , self . nodes [ 0 ] . getaddressinfo , " 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy " )
2017-06-13 03:53:46 +02:00
address_info = self . nodes [ 0 ] . getaddressinfo ( " mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ " )
assert_equal ( address_info [ ' address ' ] , " mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ " )
assert_equal ( address_info [ " scriptPubKey " ] , " 76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac " )
assert not address_info [ " ismine " ]
assert not address_info [ " iswatchonly " ]
assert not address_info [ " isscript " ]
2018-10-12 10:57:22 +02:00
assert not address_info [ " ischange " ]
# Test getaddressinfo 'ischange' field on change address.
2020-11-10 18:02:31 +01:00
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = self . no_op )
2018-10-12 10:57:22 +02:00
destination = self . nodes [ 1 ] . getnewaddress ( )
txid = self . nodes [ 0 ] . sendtoaddress ( destination , 0.123 )
2021-10-15 16:11:30 +02:00
tx = self . nodes [ 0 ] . gettransaction ( txid = txid , verbose = True ) [ ' decoded ' ]
2021-02-01 16:52:07 +01:00
output_addresses = [ vout [ ' scriptPubKey ' ] [ ' address ' ] for vout in tx [ " vout " ] ]
2018-10-12 10:57:22 +02:00
assert len ( output_addresses ) > 1
for address in output_addresses :
ischange = self . nodes [ 0 ] . getaddressinfo ( address ) [ ' ischange ' ]
assert_equal ( ischange , address != destination )
if ischange :
change = address
self . nodes [ 0 ] . setlabel ( change , ' foobar ' )
assert_equal ( self . nodes [ 0 ] . getaddressinfo ( change ) [ ' ischange ' ] , False )
2017-06-13 03:53:46 +02:00
2019-09-15 13:27:15 +02:00
# Test gettransaction response with different arguments.
self . log . info ( " Testing gettransaction response with different arguments... " )
self . nodes [ 0 ] . setlabel ( change , ' baz ' )
baz = self . nodes [ 0 ] . listtransactions ( label = " baz " , count = 1 ) [ 0 ]
expected_receive_vout = { " label " : " baz " ,
" address " : baz [ " address " ] ,
" amount " : baz [ " amount " ] ,
" category " : baz [ " category " ] ,
" vout " : baz [ " vout " ] }
expected_fields = frozenset ( { ' amount ' , ' bip125-replaceable ' , ' confirmations ' , ' details ' , ' fee ' ,
2022-01-29 19:39:12 +01:00
' hex ' , ' time ' , ' timereceived ' , ' trusted ' , ' txid ' , ' wtxid ' , ' walletconflicts ' } )
2019-09-15 13:27:15 +02:00
verbose_field = " decoded "
expected_verbose_fields = expected_fields | { verbose_field }
self . log . debug ( " Testing gettransaction response without verbose " )
tx = self . nodes [ 0 ] . gettransaction ( txid = txid )
assert_equal ( set ( [ * tx ] ) , expected_fields )
assert_array_result ( tx [ " details " ] , { " category " : " receive " } , expected_receive_vout )
self . log . debug ( " Testing gettransaction response with verbose set to False " )
tx = self . nodes [ 0 ] . gettransaction ( txid = txid , verbose = False )
assert_equal ( set ( [ * tx ] ) , expected_fields )
assert_array_result ( tx [ " details " ] , { " category " : " receive " } , expected_receive_vout )
self . log . debug ( " Testing gettransaction response with verbose set to True " )
2019-09-13 21:31:11 +02:00
tx = self . nodes [ 0 ] . gettransaction ( txid = txid , verbose = True )
2019-09-15 13:27:15 +02:00
assert_equal ( set ( [ * tx ] ) , expected_verbose_fields )
assert_array_result ( tx [ " details " ] , { " category " : " receive " } , expected_receive_vout )
assert_equal ( tx [ verbose_field ] , self . nodes [ 0 ] . decoderawtransaction ( tx [ " hex " ] ) )
2019-06-13 00:11:27 +02:00
2020-09-27 02:58:56 +02:00
self . log . info ( " Test send* RPCs with verbose=True " )
address = self . nodes [ 0 ] . getnewaddress ( " test " )
2020-09-30 09:20:37 +02:00
txid_feeReason_one = self . nodes [ 2 ] . sendtoaddress ( address = address , amount = 5 , verbose = True )
2020-09-27 02:58:56 +02:00
assert_equal ( txid_feeReason_one [ " fee_reason " ] , " Fallback fee " )
2020-09-30 09:20:37 +02:00
txid_feeReason_two = self . nodes [ 2 ] . sendmany ( dummy = ' ' , amounts = { address : 5 } , verbose = True )
2020-09-27 02:58:56 +02:00
assert_equal ( txid_feeReason_two [ " fee_reason " ] , " Fallback fee " )
self . log . info ( " Test send* RPCs with verbose=False " )
2020-09-30 09:20:37 +02:00
txid_feeReason_three = self . nodes [ 2 ] . sendtoaddress ( address = address , amount = 5 , verbose = False )
2020-09-27 02:58:56 +02:00
assert_equal ( self . nodes [ 2 ] . gettransaction ( txid_feeReason_three ) [ ' txid ' ] , txid_feeReason_three )
2020-09-30 09:20:37 +02:00
txid_feeReason_four = self . nodes [ 2 ] . sendmany ( dummy = ' ' , amounts = { address : 5 } , verbose = False )
2020-09-27 02:58:56 +02:00
assert_equal ( self . nodes [ 2 ] . gettransaction ( txid_feeReason_four ) [ ' txid ' ] , txid_feeReason_four )
2022-09-07 19:28:18 +02:00
if self . options . descriptors :
self . log . info ( " Testing ' listunspent ' outputs the parent descriptor(s) of coins " )
# Create two multisig descriptors, and send a UTxO each.
multi_a = descsum_create ( " wsh(multi(1,tpubD6NzVbkrYhZ4YBNjUo96Jxd1u4XKWgnoc7LsA1jz3Yc2NiDbhtfBhaBtemB73n9V5vtJHwU6FVXwggTbeoJWQ1rzdz8ysDuQkpnaHyvnvzR/*,tpubD6NzVbkrYhZ4YHdDGMAYGaWxMSC1B6tPRTHuU5t3BcfcS3nrF523iFm5waFd1pP3ZvJt4Jr8XmCmsTBNx5suhcSgtzpGjGMASR3tau1hJz4/*)) " )
multi_b = descsum_create ( " wsh(multi(1,tpubD6NzVbkrYhZ4YHdDGMAYGaWxMSC1B6tPRTHuU5t3BcfcS3nrF523iFm5waFd1pP3ZvJt4Jr8XmCmsTBNx5suhcSgtzpGjGMASR3tau1hJz4/*,tpubD6NzVbkrYhZ4Y2RLiuEzNQkntjmsLpPYDm3LTRBYynUQtDtpzeUKAcb9sYthSFL3YR74cdFgF5mW8yKxv2W2CWuZDFR2dUpE5PF9kbrVXNZ/*)) " )
addr_a = self . nodes [ 0 ] . deriveaddresses ( multi_a , 0 ) [ 0 ]
addr_b = self . nodes [ 0 ] . deriveaddresses ( multi_b , 0 ) [ 0 ]
txid_a = self . nodes [ 0 ] . sendtoaddress ( addr_a , 0.01 )
txid_b = self . nodes [ 0 ] . sendtoaddress ( addr_b , 0.01 )
self . generate ( self . nodes [ 0 ] , 1 , sync_fun = self . no_op )
# Now import the descriptors, make sure we can identify on which descriptor each coin was received.
self . nodes [ 0 ] . createwallet ( wallet_name = " wo " , descriptors = True , disable_private_keys = True )
wo_wallet = self . nodes [ 0 ] . get_wallet_rpc ( " wo " )
wo_wallet . importdescriptors ( [
{
" desc " : multi_a ,
" active " : False ,
" timestamp " : " now " ,
} ,
{
" desc " : multi_b ,
" active " : False ,
" timestamp " : " now " ,
} ,
] )
coins = wo_wallet . listunspent ( minconf = 0 )
assert_equal ( len ( coins ) , 2 )
coin_a = next ( c for c in coins if c [ " txid " ] == txid_a )
assert_equal ( coin_a [ " parent_descs " ] [ 0 ] , multi_a )
coin_b = next ( c for c in coins if c [ " txid " ] == txid_b )
assert_equal ( coin_b [ " parent_descs " ] [ 0 ] , multi_b )
self . nodes [ 0 ] . unloadwallet ( " wo " )
2022-07-05 14:42:06 +02:00
2020-09-30 09:20:37 +02:00
2014-11-19 21:55:40 +01:00
if __name__ == ' __main__ ' :
2016-05-10 17:22:01 +02:00
WalletTest ( ) . main ( )