2016-03-19 16:58:06 -03:00
#!/usr/bin/env python3
2020-12-31 05:48:25 -03:00
# Copyright (c) 2014-2020 The Bitcoin Core developers
2014-10-22 22:48:19 -03:00
# Distributed under the MIT software license, see the accompanying
2014-06-16 08:45:32 -04:00
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-17 20:34:40 -03:00
""" Test the wallet keypool and interaction with wallet encryption/locking. """
2014-06-16 08:45:32 -04:00
2018-07-06 18:10:35 -04:00
import time
2019-10-23 10:21:50 -03:00
from decimal import Decimal
2018-07-06 18:10:35 -04:00
2015-11-15 13:58:01 -03:00
from test_framework . test_framework import BitcoinTestFramework
2018-07-06 18:10:35 -04:00
from test_framework . util import assert_equal , assert_raises_rpc_error
2014-06-16 08:45:32 -04:00
2015-11-15 13:58:01 -03:00
class KeyPoolTest ( BitcoinTestFramework ) :
2017-06-09 18:21:21 -04:00
def set_test_params ( self ) :
self . num_nodes = 1
2015-11-15 13:58:01 -03:00
2018-09-09 14:32:37 -03:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2015-11-15 13:58:01 -03:00
def run_test ( self ) :
nodes = self . nodes
2016-07-21 15:19:02 -04:00
addr_before_encrypting = nodes [ 0 ] . getnewaddress ( )
2018-02-09 13:12:27 -03:00
addr_before_encrypting_data = nodes [ 0 ] . getaddressinfo ( addr_before_encrypting )
2016-07-21 15:19:02 -04:00
wallet_info_old = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
if not self . options . descriptors :
assert addr_before_encrypting_data [ ' hdseedid ' ] == wallet_info_old [ ' hdseedid ' ]
2018-04-16 06:13:07 -03:00
2015-11-15 14:48:18 -03:00
# Encrypt wallet and wait to terminate
2018-02-20 18:09:51 -03:00
nodes [ 0 ] . encryptwallet ( ' test ' )
2019-07-16 15:33:35 -04:00
if self . options . descriptors :
# Import hardened derivation only descriptors
nodes [ 0 ] . walletpassphrase ( ' test ' , 10 )
nodes [ 0 ] . importdescriptors ( [
{
" desc " : " wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True
} ,
{
" desc " : " pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1h/*h)#a0nyvl0k " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True
} ,
{
" desc " : " sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/2h/*h))#lmeu2axg " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True
} ,
{
" desc " : " wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/3h/*h)#jkl636gm " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True ,
" internal " : True
} ,
{
" desc " : " pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/4h/*h)#l3crwaus " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True ,
" internal " : True
} ,
{
" desc " : " sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/5h/*h))#qg8wa75f " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True ,
" internal " : True
}
] )
nodes [ 0 ] . walletlock ( )
2015-11-15 14:48:18 -03:00
# Keep creating keys
2014-06-16 08:45:32 -04:00
addr = nodes [ 0 ] . getnewaddress ( )
2018-02-09 13:12:27 -03:00
addr_data = nodes [ 0 ] . getaddressinfo ( addr )
2016-07-21 15:19:02 -04:00
wallet_info = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
assert addr_before_encrypting_data [ ' hdmasterfingerprint ' ] != addr_data [ ' hdmasterfingerprint ' ]
if not self . options . descriptors :
assert addr_data [ ' hdseedid ' ] == wallet_info [ ' hdseedid ' ]
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 12 , " Error: Keypool ran out, please call keypoolrefill first " , nodes [ 0 ] . getnewaddress )
2015-11-15 14:48:18 -03:00
2017-01-17 04:55:30 -03:00
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
2015-11-15 14:48:18 -03:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 12000 )
2017-01-10 12:45:30 -03:00
nodes [ 0 ] . keypoolrefill ( 6 )
2015-11-15 14:48:18 -03:00
nodes [ 0 ] . walletlock ( )
2017-01-10 12:45:30 -03:00
wi = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
if self . options . descriptors :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 18 )
assert_equal ( wi [ ' keypoolsize ' ] , 18 )
else :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 6 )
assert_equal ( wi [ ' keypoolsize ' ] , 6 )
2015-11-15 14:48:18 -03:00
2017-01-10 12:45:30 -03:00
# drain the internal keys
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
2017-01-17 04:55:30 -03:00
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
2015-11-15 14:48:18 -03:00
addr = set ( )
# the next one should fail
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 12 , " Keypool ran out " , nodes [ 0 ] . getrawchangeaddress )
2015-11-15 14:48:18 -03:00
2017-01-10 12:45:30 -03:00
# drain the external keys
2019-10-23 10:21:50 -03:00
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
2019-02-19 19:43:44 -03:00
assert len ( addr ) == 6
2017-01-10 12:45:30 -03:00
# the next one should fail
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 12 , " Error: Keypool ran out, please call keypoolrefill first " , nodes [ 0 ] . getnewaddress )
2017-01-10 12:45:30 -03:00
2015-11-15 14:48:18 -03:00
# refill keypool with three new addresses
2016-01-08 09:12:16 -03:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 1 )
2015-11-15 14:48:18 -03:00
nodes [ 0 ] . keypoolrefill ( 3 )
2017-01-10 12:45:30 -03:00
2016-01-08 09:12:16 -03:00
# test walletpassphrase timeout
time . sleep ( 1.1 )
assert_equal ( nodes [ 0 ] . getwalletinfo ( ) [ " unlocked_until " ] , 0 )
2015-11-15 14:48:18 -03:00
2018-10-11 12:04:06 -03:00
# drain the keypool
for _ in range ( 3 ) :
nodes [ 0 ] . getnewaddress ( )
assert_raises_rpc_error ( - 12 , " Keypool ran out " , nodes [ 0 ] . getnewaddress )
2015-11-15 14:48:18 -03:00
2017-01-10 12:45:30 -03:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 100 )
nodes [ 0 ] . keypoolrefill ( 100 )
wi = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
if self . options . descriptors :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 300 )
assert_equal ( wi [ ' keypoolsize ' ] , 300 )
else :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 100 )
assert_equal ( wi [ ' keypoolsize ' ] , 100 )
2017-01-10 12:45:30 -03:00
2019-10-23 10:21:50 -03:00
# create a blank wallet
2019-07-16 15:33:35 -04:00
nodes [ 0 ] . createwallet ( wallet_name = ' w2 ' , blank = True , disable_private_keys = True )
2019-10-23 10:21:50 -03:00
w2 = nodes [ 0 ] . get_wallet_rpc ( ' w2 ' )
# refer to initial wallet as w1
2020-09-28 21:24:06 -03:00
w1 = nodes [ 0 ] . get_wallet_rpc ( self . default_wallet_name )
2019-10-23 10:21:50 -03:00
# import private key and fund it
address = addr . pop ( )
2019-07-16 15:33:35 -04:00
desc = w1 . getaddressinfo ( address ) [ ' desc ' ]
if self . options . descriptors :
res = w2 . importdescriptors ( [ { ' desc ' : desc , ' timestamp ' : ' now ' } ] )
else :
res = w2 . importmulti ( [ { ' desc ' : desc , ' timestamp ' : ' now ' } ] )
2019-10-23 10:21:50 -03:00
assert_equal ( res [ 0 ] [ ' success ' ] , True )
w1 . walletpassphrase ( ' test ' , 100 )
res = w1 . sendtoaddress ( address = address , amount = 0.00010000 )
nodes [ 0 ] . generate ( 1 )
destination = addr . pop ( )
# Using a fee rate (10 sat / byte) well above the minimum relay rate
# creating a 5,000 sat transaction with change should not be possible
assert_raises_rpc_error ( - 4 , " Transaction needs a change address, but we can ' t generate it. Please call keypoolrefill first. " , w2 . walletcreatefundedpsbt , inputs = [ ] , outputs = [ { addr . pop ( ) : 0.00005000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 } )
# creating a 10,000 sat transaction without change, with a manual input, should still be possible
res = w2 . walletcreatefundedpsbt ( inputs = w2 . listunspent ( ) , outputs = [ { destination : 0.00010000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 } )
assert_equal ( " psbt " in res , True )
# creating a 10,000 sat transaction without change should still be possible
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00010000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 } )
assert_equal ( " psbt " in res , True )
# should work without subtractFeeFromOutputs if the exact fee is subtracted from the amount
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00008900 } ] , options = { " feeRate " : 0.00010 } )
assert_equal ( " psbt " in res , True )
# dust change should be removed
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00008800 } ] , options = { " feeRate " : 0.00010 } )
assert_equal ( " psbt " in res , True )
# create a transaction without change at the maximum fee rate, such that the output is still spendable:
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00010000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.0008824 } )
assert_equal ( " psbt " in res , True )
assert_equal ( res [ " fee " ] , Decimal ( " 0.00009706 " ) )
# creating a 10,000 sat transaction with a manual change address should be possible
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00010000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 , " changeAddress " : addr . pop ( ) } )
assert_equal ( " psbt " in res , True )
2014-06-16 08:45:32 -04:00
if __name__ == ' __main__ ' :
2015-11-15 13:58:01 -03:00
KeyPoolTest ( ) . main ( )