2016-05-06 11:23:48 +02:00
#!/usr/bin/env python3
2019-02-01 15:35:08 +01:00
# Copyright (c) 2014-2019 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.
2019-01-07 10:55:35 +01:00
""" Test the wallet. """
2021-04-12 16:58:49 +02:00
from decimal import Decimal
2019-08-09 01:21:11 +02:00
2015-05-02 12:53:35 +02:00
from test_framework . test_framework import BitcoinTestFramework
2021-04-12 16:58:49 +02:00
from test_framework . util import (
assert_array_result ,
assert_equal ,
assert_fee_amount ,
assert_raises_rpc_error ,
count_bytes ,
wait_until ,
)
2020-01-07 23:23:58 +01:00
from test_framework . wallet_util import test_address
2014-11-19 21:55:40 +01:00
2019-02-01 15:35:08 +01:00
2017-04-29 14:47:00 +02:00
class WalletTest ( 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
2019-07-16 22:07:14 +02:00
self . extra_args = [ [
" -acceptnonstdtxn=1 " ,
' -usehd= {:d} ' . format ( i % 2 == 0 ) ,
] for i in range ( self . num_nodes ) ]
2017-09-01 18:47:13 +02:00
self . setup_clean_chain = True
2019-12-09 19:52:38 +01:00
self . supports_cli = False
2014-11-19 21:55:40 +01:00
2018-09-13 12:33:15 +02:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2017-05-02 20:02:55 +02:00
def setup_network ( self ) :
2018-11-02 16:41:29 +01:00
self . setup_nodes ( )
# Only need nodes 0-2 running at start of test
self . stop_node ( 3 )
2022-09-24 14:36:35 +02:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2014-11-19 21:55:40 +01:00
2017-09-01 18:47:13 +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:22:11 +02:00
2017-09-01 18:47:13 +02:00
def run_test ( self ) :
2016-04-19 13:22:11 +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-09 21:16:20 +01:00
self . log . info ( " Mining blocks... " )
2014-11-19 21:55:40 +01:00
2015-04-01 05:28:28 +02:00
self . nodes [ 0 ] . generate ( 1 )
2014-11-19 21:55:40 +01:00
2014-12-25 08:57:16 +01:00
walletinfo = self . nodes [ 0 ] . getwalletinfo ( )
2016-03-06 16:14:39 +01:00
assert_equal ( walletinfo [ ' immature_balance ' ] , 500 )
2014-12-25 08:57:16 +01:00
assert_equal ( walletinfo [ ' balance ' ] , 0 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2015-04-01 05:28:28 +02:00
self . nodes [ 1 ] . generate ( 101 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2014-11-19 21:55:40 +01:00
2016-03-06 16:14:39 +01:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , 500 )
assert_equal ( self . nodes [ 1 ] . getbalance ( ) , 500 )
2014-11-19 21:55:40 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , 0 )
2016-04-19 13:22:11 +02:00
# Check that only first and second nodes have UTXOs
2017-04-29 14:47:00 +02:00
utxos = self . nodes [ 0 ] . listunspent ( )
assert_equal ( len ( utxos ) , 1 )
2016-04-19 13:22:11 +02:00
assert_equal ( len ( self . nodes [ 1 ] . listunspent ( ) ) , 1 )
assert_equal ( len ( self . nodes [ 2 ] . listunspent ( ) ) , 0 )
2017-08-29 01:45:11 +02:00
self . log . info ( " test gettxout " )
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 ' ] , 500 )
txout = self . nodes [ 0 ] . gettxout ( txid = confirmed_txid , n = confirmed_index , include_mempool = True )
assert_equal ( txout [ ' value ' ] , 500 )
2020-07-07 19:37:53 +02:00
2016-03-06 16:14:39 +01:00
# Send 210 DASH from 0 to 2 using sendtoaddress call.
2014-11-19 21:55:40 +01:00
# Second transaction will be child of first, and will require a fee
2016-03-06 16:14:39 +01:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 110 )
2017-04-29 14:47:00 +02:00
mempool_txid = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 100 )
2017-08-29 01:45:11 +02:00
self . log . info ( " test gettxout (second part) " )
2017-04-29 14:47:00 +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 ' ] , 500 )
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 ( [ 100 , balance ] ) )
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 ' ] , 0 )
2015-04-28 16:48:28 +02:00
# Have node0 mine a block, thus it will collect its own fee.
2015-04-01 05:28:28 +02:00
self . nodes [ 0 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
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 " ] }
2017-11-16 12:23:55 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, expected locked output " , self . nodes [ 2 ] . lockunspent , True , [ unspent_0 ] )
2016-03-06 18:30:51 +01:00
self . nodes [ 2 ] . lockunspent ( False , [ unspent_0 ] )
2017-11-16 12:23:55 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, output already locked " , self . nodes [ 2 ] . lockunspent , False , [ unspent_0 ] )
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 4 , " Insufficient funds " , self . nodes [ 2 ] . sendtoaddress , self . nodes [ 2 ] . getnewaddress ( ) , 200 )
2016-03-06 18:30:51 +01:00
assert_equal ( [ unspent_0 ] , self . nodes [ 2 ] . listlockunspent ( ) )
self . nodes [ 2 ] . lockunspent ( True , [ unspent_0 ] )
assert_equal ( len ( self . nodes [ 2 ] . listlockunspent ( ) ) , 0 )
2018-09-24 20:54:10 +02:00
assert_raises_rpc_error ( - 8 , " txid must be of length 64 (not 34, for ' 0000000000000000000000000000000000 ' ) " ,
2021-04-12 16:58:49 +02:00
self . nodes [ 2 ] . lockunspent , False ,
[ { " txid " : " 0000000000000000000000000000000000 " , " vout " : 0 } ] )
2018-09-24 20:54:10 +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-11-16 12:23:55 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, vout index out of bounds " ,
2021-04-12 16:58:49 +02:00
self . nodes [ 2 ] . lockunspent , False ,
[ { " txid " : unspent_0 [ " txid " ] , " vout " : 999 } ] )
2016-03-06 18:30:51 +01:00
2018-06-24 18:52:27 +02:00
# An output should be unlocked when spent
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 } )
tx = self . nodes [ 1 ] . fundrawtransaction ( tx ) [ ' hex ' ]
2020-12-11 03:25:55 +01:00
tx = self . nodes [ 1 ] . signrawtransactionwithwallet ( tx ) [ " hex " ]
2018-06-24 18:52:27 +02:00
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)
2015-04-01 05:28:28 +02:00
self . nodes [ 1 ] . generate ( 100 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2014-11-19 21:55:40 +01:00
2016-03-06 16:14:39 +01:00
# node0 should end up with 1000 DASH in block rewards plus fees, but
# minus the 210 plus fees sent to node2
2021-04-12 16:58:49 +02:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , 1000 - 210 )
2016-03-06 16:14:39 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , 210 )
2014-11-19 21:55:40 +01:00
# 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 )
2019-03-15 07:18:16 +01:00
fee_per_input = Decimal ( ' 0.00001 ' )
totalfee = 0
2014-11-19 21:55:40 +01:00
# 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 = { }
2021-04-12 16:58:49 +02:00
inputs . append ( { " txid " : utxo [ " txid " ] , " vout " : utxo [ " vout " ] } )
2018-05-11 00:08:57 +02:00
outputs [ self . nodes [ 2 ] . getnewaddress ( ) ] = utxo [ " amount " ] - fee_per_input
2014-11-19 21:55:40 +01:00
raw_tx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
2018-02-20 03:29:22 +01:00
txns_to_send . append ( self . nodes [ 0 ] . signrawtransactionwithwallet ( raw_tx ) )
2019-03-15 07:18:16 +01:00
totalfee + = fee_per_input
2014-11-19 21:55:40 +01:00
# Have node 1 (miner) send the transactions
2019-09-25 11:51:26 +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:
2015-04-01 05:28:28 +02:00
self . nodes [ 1 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2014-11-19 21:55:40 +01:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , 0 )
2019-03-15 07:18:16 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , 1000 - totalfee )
2014-11-19 21:55:40 +01:00
2017-11-16 12:23:55 +01: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 ] )
2016-03-06 16:14:39 +01:00
# Send 100 DASH normal
2014-07-23 14:34:36 +02:00
address = self . nodes [ 0 ] . getnewaddress ( " test " )
2017-12-07 10:43:23 +01:00
fee_per_byte = Decimal ( ' 0.00001 ' ) / 1000
2015-09-21 12:49:13 +02:00
self . nodes [ 2 ] . settxfee ( fee_per_byte * 1000 )
2016-03-06 16:14:39 +01:00
txid = self . nodes [ 2 ] . sendtoaddress ( address , 100 , " " , " " , False )
2015-04-01 05:28:28 +02:00
self . nodes [ 2 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2019-02-01 15:35:08 +01:00
node_2_bal = self . check_fee_amount ( self . nodes [ 2 ] . getbalance ( ) , Decimal ( ' 900 ' ) - totalfee , fee_per_byte , count_bytes ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2016-03-06 16:14:39 +01:00
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , Decimal ( ' 100 ' ) )
2014-07-23 14:34:36 +02:00
2016-03-06 16:14:39 +01:00
# Send 100 DASH with subtract fee from amount
txid = self . nodes [ 2 ] . sendtoaddress ( address , 100 , " " , " " , True )
2015-04-01 05:28:28 +02:00
self . nodes [ 2 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2016-03-06 16:14:39 +01:00
node_2_bal - = Decimal ( ' 100 ' )
2015-09-21 12:49:13 +02:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2019-02-01 15:35:08 +01:00
node_0_bal = self . check_fee_amount ( self . nodes [ 0 ] . getbalance ( ) , Decimal ( ' 200 ' ) , fee_per_byte , count_bytes ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2014-11-19 21:55:40 +01:00
2016-03-06 16:14:39 +01:00
# Sendmany 100 DASH
2018-05-11 00:08:57 +02:00
txid = self . nodes [ 2 ] . sendmany ( ' ' , { address : 100 } , 0 , False , " " , [ ] )
2015-04-01 05:28:28 +02:00
self . nodes [ 2 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2016-03-06 16:14:39 +01:00
node_0_bal + = Decimal ( ' 100 ' )
2019-02-01 15:35:08 +01:00
node_2_bal = self . check_fee_amount ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal - Decimal ( ' 100 ' ) , fee_per_byte , count_bytes ( 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
2016-03-06 16:14:39 +01:00
# Sendmany 100 DASH with subtract fee from amount
2018-05-11 00:08:57 +02:00
txid = self . nodes [ 2 ] . sendmany ( ' ' , { address : 100 } , 0 , False , " " , [ address ] )
2015-04-01 05:28:28 +02:00
self . nodes [ 2 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2016-03-06 16:14:39 +01:00
node_2_bal - = Decimal ( ' 100 ' )
2015-09-21 12:49:13 +02:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2019-02-01 15:35:08 +01:00
node_0_bal = self . check_fee_amount ( self . nodes [ 0 ] . getbalance ( ) , node_0_bal + Decimal ( ' 100 ' ) , fee_per_byte , count_bytes ( self . nodes [ 2 ] . gettransaction ( txid ) [ ' hex ' ] ) )
2015-03-05 14:59:19 +01:00
2017-09-01 18:47:13 +02:00
self . start_node ( 3 )
2022-09-24 14:36:35 +02:00
self . connect_nodes ( 0 , 3 )
2021-12-12 14:38:12 +01:00
self . sync_all ( )
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +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
2020-07-07 19:37:53 +02:00
usp = self . nodes [ 1 ] . listunspent ( query_options = { ' minimumAmount ' : ' 499.998 ' } ) [ 0 ]
2021-04-12 16:58:49 +02:00
inputs = [ { " txid " : usp [ ' txid ' ] , " vout " : usp [ ' vout ' ] } ]
2016-03-06 16:14:39 +01:00
outputs = { self . nodes [ 1 ] . getnewaddress ( ) : 499.998 , self . nodes [ 0 ] . getnewaddress ( ) : 11.11 }
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +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-04-12 16:58:49 +02:00
self . nodes [ 1 ] . generate ( 1 ) # mine a block
2015-04-21 10:27:35 +02:00
self . sync_all ( )
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +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
2021-04-12 16:58:49 +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-02-01 15:35:08 +01:00
assert found
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +02:00
# do some -walletbroadcast tests
2017-06-02 12:08:48 +02:00
self . stop_nodes ( )
2017-09-01 18:47:13 +02:00
self . start_node ( 0 , [ " -walletbroadcast=0 " ] )
self . start_node ( 1 , [ " -walletbroadcast=0 " ] )
self . start_node ( 2 , [ " -walletbroadcast=0 " ] )
2022-09-24 14:36:35 +02:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2015-03-23 18:47:18 +01:00
2021-04-12 16:58:49 +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 )
self . nodes [ 1 ] . generate ( 1 ) # mine a block, tx should not be in there
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2021-04-12 16:58:49 +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
2021-04-12 16:58:49 +02:00
# now broadcast from another node, mine a block, sync, and check the balance
self . nodes [ 1 ] . sendrawtransaction ( tx_obj_not_broadcast [ ' hex ' ] )
2015-04-09 17:36:10 +02:00
self . nodes [ 1 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2015-12-15 17:15:13 +01:00
node_2_bal + = 2
2021-04-12 16:58:49 +02:00
tx_obj_not_broadcast = self . nodes [ 0 ] . gettransaction ( txid_not_broadcast )
2015-12-15 17:15:13 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +02:00
# create another tx
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 2 )
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +02:00
# restart the nodes with -walletbroadcast=1
2017-06-02 12:08:48 +02:00
self . stop_nodes ( )
2017-09-01 18:47:13 +02:00
self . start_node ( 0 )
self . start_node ( 1 )
self . start_node ( 2 )
2022-09-24 14:36:35 +02:00
self . connect_nodes ( 0 , 1 )
self . connect_nodes ( 1 , 2 )
self . connect_nodes ( 0 , 2 )
2020-04-14 12:00:16 +02:00
self . sync_blocks ( self . nodes [ 0 : 3 ] )
2015-07-14 21:13:16 +02:00
2015-04-09 17:36:10 +02:00
self . nodes [ 0 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_blocks ( self . nodes [ 0 : 3 ] )
2015-12-15 17:15:13 +01:00
node_2_bal + = 2
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +02:00
# tx should be added to balance because after restarting the nodes tx should be broadcast
2015-12-15 17:15:13 +01:00
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , node_2_bal )
2015-07-14 21:13:16 +02:00
2021-04-12 16:58:49 +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
2021-04-12 16:58:49 +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
2021-04-12 16:58:49 +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 21:27:13 +01:00
# General checks for errors from incorrect inputs
2017-03-16 11:57:09 +01:00
# This will raise an exception because the amount type is wrong
2019-09-25 11:34:51 +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-16 11:57:09 +01:00
# This will raise an exception since generate does not accept a string
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 1 , " not an integer " , self . nodes [ 0 ] . generate , " 2 " )
2015-07-14 21:13:16 +02:00
2019-02-12 21:27:13 +01: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 " )
# This will raise an exception for importing an address with the PS2H flag
temp_address = self . nodes [ 1 ] . getnewaddress ( )
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 )
# This will raise an exception for attempting to dump the private key of an address you do not own
2021-07-01 01:12:37 +02:00
assert_raises_rpc_error ( - 4 , " Private key for address %s is not known " % temp_address , self . nodes [ 0 ] . dumpprivkey , temp_address )
2019-02-12 21:27:13 +01:00
# This will raise an exception for attempting to get the private key of an invalid Dash address
assert_raises_rpc_error ( - 5 , " Invalid Dash address " , self . nodes [ 0 ] . dumpprivkey , " invalid " )
# This will raise an exception for attempting to set a label for an invalid Dash address
assert_raises_rpc_error ( - 5 , " Invalid Dash address " , self . nodes [ 0 ] . setlabel , " invalid address " , " label " )
# This will raise an exception for importing an invalid address
assert_raises_rpc_error ( - 5 , " Invalid Dash address or script " , self . nodes [ 0 ] . importaddress , " invalid " )
# 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 " )
# 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 " )
2016-04-19 13:22:11 +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 )
self . nodes [ 0 ] . generate ( 1 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2016-04-19 13:22:11 +02:00
# 2. Import address from node2 to node1
self . nodes [ 1 ] . importaddress ( address_to_import )
# 3. Validate that the imported address is watch-only on node1
2019-02-01 15:35:08 +01:00
assert self . nodes [ 1 ] . getaddressinfo ( address_to_import ) [ " iswatchonly " ]
2015-07-14 21:13:16 +02:00
2016-04-19 13:22:11 +02:00
# 4. Check that the unspents after import are not spendable
assert_array_result ( self . nodes [ 1 ] . listunspent ( ) ,
2021-04-12 16:58:49 +02:00
{ " address " : address_to_import } ,
{ " spendable " : False } )
2016-04-19 13:22:11 +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 )
# 6. Check that the unspents are now spendable on node1
assert_array_result ( self . nodes [ 1 ] . listunspent ( ) ,
2021-04-12 16:58:49 +02:00
{ " address " : address_to_import } ,
{ " spendable " : True } )
2015-12-15 17:15:13 +01:00
2016-03-23 13:24:34 +01:00
# Mine a block from node0 to an address from node1
2021-04-12 16:58:49 +02:00
coinbase_addr = self . nodes [ 1 ] . getnewaddress ( )
block_hash = self . nodes [ 0 ] . generatetoaddress ( 1 , coinbase_addr ) [ 0 ]
coinbase_txid = self . nodes [ 0 ] . getblock ( block_hash ) [ ' tx ' ] [ 0 ]
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2016-03-23 13:24:34 +01:00
# Check that the txid and balance is found by node1
2021-04-12 16:58:49 +02:00
self . nodes [ 1 ] . gettransaction ( coinbase_txid )
2016-03-23 13:24:34 +01:00
2016-05-17 09:26:42 +02:00
# check if wallet or blockchain maintenance changes the balance
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2016-03-06 18:30:51 +01:00
blocks = self . nodes [ 0 ] . generate ( 2 )
2020-04-14 12:00:16 +02:00
self . sync_all ( self . nodes [ 0 : 3 ] )
2015-12-15 17:15:13 +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-15 17:15:13 +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-05-11 00:08:57 +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-05-11 00:08:57 +02:00
for label in [ u ' рыба ' , u ' 𝅘𝅥𝅯 ' ] :
addr = self . nodes [ 0 ] . getnewaddress ( )
self . nodes [ 0 ] . setlabel ( addr , label )
2020-01-07 23:23:58 +01:00
test_address ( self . nodes [ 0 ] , addr , label = label , labels = [ label ] )
2019-02-01 15:35:08 +01:00
assert label in self . nodes [ 0 ] . listlabels ( )
2018-05-11 00:08:57 +02:00
self . nodes [ 0 ] . rpc . ensure_ascii = True # restore to default
2016-04-28 15:18:01 +02:00
# maintenance tests
2015-12-15 17:15:13 +01:00
maintenance = [
' -rescan ' ,
' -reindex ' ,
]
2016-12-20 13:12:46 +01:00
chainlimit = 6
2015-12-15 17:15:13 +01:00
for m in maintenance :
2017-03-09 21:16:20 +01:00
self . log . info ( " check " + m )
2017-06-02 12:08:48 +02:00
self . stop_nodes ( )
2016-12-20 13:12:46 +01:00
# set lower ancestor limit for later
2018-05-11 00:08:57 +02:00
self . start_node ( 0 , [ m , " -limitancestorcount= " + str ( chainlimit ) ] )
self . start_node ( 1 , [ m , " -limitancestorcount= " + str ( chainlimit ) ] )
self . start_node ( 2 , [ m , " -limitancestorcount= " + str ( chainlimit ) ] )
2018-03-19 17:10:29 +01:00
if m == ' -reindex ' :
2016-03-28 21:47:13 +02:00
# reindex will leave rpc warm up "early"; Wait for it to finish
2018-03-19 17:10:29 +01:00
wait_until ( lambda : [ block_count ] * 3 == [ self . nodes [ i ] . getblockcount ( ) for i in range ( 3 ) ] )
2015-12-15 17:15:13 +01:00
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-20 13:12:46 +01:00
# ==Check that wallet prefers to use coins that don't exceed mempool limits =====
# Get all non-zero utxos together
chain_addrs = [ self . nodes [ 0 ] . getnewaddress ( ) , self . nodes [ 0 ] . getnewaddress ( ) ]
singletxid = self . nodes [ 0 ] . sendtoaddress ( chain_addrs [ 0 ] , self . nodes [ 0 ] . getbalance ( ) , " " , " " , True )
self . nodes [ 0 ] . generate ( 1 )
node0_balance = self . nodes [ 0 ] . getbalance ( )
# Split into two chains
2021-04-12 16:58:49 +02:00
rawtx = self . nodes [ 0 ] . createrawtransaction ( [ { " txid " : singletxid , " vout " : 0 } ] , { chain_addrs [ 0 ] : node0_balance / 2 - Decimal ( ' 0.01 ' ) , chain_addrs [ 1 ] : node0_balance / 2 - Decimal ( ' 0.01 ' ) } )
2018-02-20 03:29:22 +01:00
signedtx = self . nodes [ 0 ] . signrawtransactionwithwallet ( rawtx )
2019-09-25 11:51:26 +02:00
singletxid = self . nodes [ 0 ] . sendrawtransaction ( hexstring = signedtx [ " hex " ] , maxfeerate = 0 )
2016-12-20 13:12:46 +01:00
self . nodes [ 0 ] . generate ( 1 )
# Make a long chain of unconfirmed payments without hitting mempool limit
2017-01-04 20:48:14 +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-20 13:12:46 +01:00
txid_list = [ ]
2021-04-12 16:58:49 +02:00
for i in range ( chainlimit * 2 ) :
2017-01-04 20:48:14 +01:00
txid_list . append ( self . nodes [ 0 ] . sendtoaddress ( sending_addr , Decimal ( ' 0.0001 ' ) ) )
2021-04-12 16:58:49 +02:00
assert_equal ( self . nodes [ 0 ] . getmempoolinfo ( ) [ ' size ' ] , chainlimit * 2 )
assert_equal ( len ( txid_list ) , chainlimit * 2 )
2016-12-20 13:12:46 +01:00
2017-01-04 20:48:14 +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-02-01 15:35:08 +01:00
assert extra_txid not in self . nodes [ 0 ] . getrawmempool ( )
assert extra_txid in [ tx [ " txid " ] for tx in self . nodes [ 0 ] . listtransactions ( ) ]
2017-01-04 20:48:14 +01:00
self . nodes [ 0 ] . abandontransaction ( extra_txid )
2021-04-12 16:58:49 +02:00
total_txs = len ( self . nodes [ 0 ] . listtransactions ( " * " , 99999 ) )
2017-01-04 20:48:14 +01:00
# Try with walletrejectlongchains
# Double chain limit but require combining inputs, so we pass SelectCoinsMinConf
2020-06-22 18:24:41 +02:00
self . restart_node ( 0 , extra_args = [ " -walletrejectlongchains " , " -limitancestorcount= " + str ( 2 * chainlimit ) ] )
2017-01-04 20:48:14 +01:00
2022-09-07 20:48:44 +02:00
# wait until the wallet has submitted all transactions to the mempool
wait_until ( lambda : len ( self . nodes [ 0 ] . getrawmempool ( ) ) == chainlimit * 2 )
2017-01-04 20:48:14 +01:00
2020-09-06 13:03:39 +02:00
# Prevent potential race condition when calling wallet RPCs right after restart
self . nodes [ 0 ] . syncwithvalidationinterfacequeue ( )
2017-01-04 20:48:14 +01:00
node0_balance = self . nodes [ 0 ] . getbalance ( )
# With walletrejectlongchains we will not create the tx and store it in our wallet.
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 4 , " Transaction has too long of a mempool chain " , self . nodes [ 0 ] . sendtoaddress , sending_addr , node0_balance - Decimal ( ' 0.01 ' ) )
2017-01-04 20:48:14 +01:00
# Verify nothing new in wallet
2021-04-12 16:58:49 +02:00
assert_equal ( total_txs , len ( self . nodes [ 0 ] . listtransactions ( " * " , 99999 ) ) )
2017-01-04 20:48:14 +01:00
2018-11-04 23:22:35 +01:00
# Test getaddressinfo on external address. Note that these addresses are taken from disablewallet.py
2020-12-17 13:46:20 +01:00
assert_raises_rpc_error ( - 5 , " Invalid address " , self . nodes [ 0 ] . getaddressinfo , " 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy " )
address_info = self . nodes [ 0 ] . getaddressinfo ( " yjQ5gLvGRtmq1cwc4kePLCrzQ8GVCh9Gaz " )
assert_equal ( address_info [ ' address ' ] , " yjQ5gLvGRtmq1cwc4kePLCrzQ8GVCh9Gaz " )
assert_equal ( address_info [ " scriptPubKey " ] , " 76a914fd2b4d101724a76374fccbc5b6df7670a75d7cd088ac " )
assert not address_info [ " ismine " ]
assert not address_info [ " iswatchonly " ]
assert not address_info [ " isscript " ]
2018-11-04 23:22:35 +01:00
assert not address_info [ " ischange " ]
# Test getaddressinfo 'ischange' field on change address.
self . nodes [ 0 ] . generate ( 1 )
destination = self . nodes [ 1 ] . getnewaddress ( )
txid = self . nodes [ 0 ] . sendtoaddress ( destination , 0.123 )
2019-02-01 15:35:08 +01:00
tx = self . nodes [ 0 ] . decoderawtransaction ( self . nodes [ 0 ] . gettransaction ( txid ) [ ' hex ' ] )
2018-11-04 23:22:35 +01:00
output_addresses = [ vout [ ' scriptPubKey ' ] [ ' addresses ' ] [ 0 ] for vout in tx [ " vout " ] ]
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 )
2020-12-17 13:46:20 +01:00
Merge #16873: rpc: fix regression in gettransaction
1b41c2c8a126ef4be183e1d800a17d85cab8837b test: improve gettransaction test coverage (Jon Atack)
0f34f54888f680bfbe7a29ac278636d7178a99bb rpc: fix regression in gettransaction (Jon Atack)
Pull request description:
Closes #16872.
PR #16866 renamed the `decode` argument in gettransaction to `verbose` to make it more consistent with other RPC calls like getrawtransaction. However, it inadvertently overloaded the "details" field when `verbose` is passed. The result is that the original "details" field is no longer returned correctly, which seems to be a breaking API change.
This PR:
- takes the simplest path to restoring the "details" field by renaming the decoded one back to "decoded" while leaving the `verbose` argument for API consistency, which was the main intent of #16866,
- addresses [this comment](https://github.com/bitcoin/bitcoin/pull/16185#discussion_r320740413) by mentioning in the RPC help that the new decoded field is equivalent to decoderawtransaction, and
- updates the help, functional test, and release note.
Reviewers, to test this manually, build and run `bitcoin-cli help gettransaction` and `bitcoin-cli gettransaction <wallet txid> false true`, and verify that the command returns both `details` and `decoded` fields.
ACKs for top commit:
jnewbery:
tACK 1b41c2c8a126ef4be183e1d800a17d85cab8837b
Tree-SHA512: 287edd5db7ed58fe8b548975aba58628bd45ed708b28f40174f10a35a455d89f796fbf27430aa881fc376f47aabda8803f74d4d100683bd86577a02279091cf3
2019-09-15 23:44:06 +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 ' , ' chainlock ' , ' confirmations ' , ' details ' , ' fee ' ,
' instantlock ' , ' instantlock_internal ' ,
' hex ' , ' timereceived ' , ' time ' , ' trusted ' , ' txid ' , ' walletconflicts ' } )
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-14 11:42:34 +02:00
tx = self . nodes [ 0 ] . gettransaction ( txid = txid , verbose = True )
Merge #16873: rpc: fix regression in gettransaction
1b41c2c8a126ef4be183e1d800a17d85cab8837b test: improve gettransaction test coverage (Jon Atack)
0f34f54888f680bfbe7a29ac278636d7178a99bb rpc: fix regression in gettransaction (Jon Atack)
Pull request description:
Closes #16872.
PR #16866 renamed the `decode` argument in gettransaction to `verbose` to make it more consistent with other RPC calls like getrawtransaction. However, it inadvertently overloaded the "details" field when `verbose` is passed. The result is that the original "details" field is no longer returned correctly, which seems to be a breaking API change.
This PR:
- takes the simplest path to restoring the "details" field by renaming the decoded one back to "decoded" while leaving the `verbose` argument for API consistency, which was the main intent of #16866,
- addresses [this comment](https://github.com/bitcoin/bitcoin/pull/16185#discussion_r320740413) by mentioning in the RPC help that the new decoded field is equivalent to decoderawtransaction, and
- updates the help, functional test, and release note.
Reviewers, to test this manually, build and run `bitcoin-cli help gettransaction` and `bitcoin-cli gettransaction <wallet txid> false true`, and verify that the command returns both `details` and `decoded` fields.
ACKs for top commit:
jnewbery:
tACK 1b41c2c8a126ef4be183e1d800a17d85cab8837b
Tree-SHA512: 287edd5db7ed58fe8b548975aba58628bd45ed708b28f40174f10a35a455d89f796fbf27430aa881fc376f47aabda8803f74d4d100683bd86577a02279091cf3
2019-09-15 23:44:06 +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-09-02 13:29:42 +02:00
2019-02-01 15:35:08 +01:00
2014-11-19 21:55:40 +01:00
if __name__ == ' __main__ ' :
2016-05-17 09:26:42 +02:00
WalletTest ( ) . main ( )