2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2019-02-21 02:03:13 +01:00
# Copyright (c) 2014-2019 The Bitcoin Core developers
2014-10-23 03:48:19 +02:00
# Distributed under the MIT software license, see the accompanying
2014-06-16 14:45:32 +02:00
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-18 00:34:40 +01:00
""" Test the wallet keypool and interaction with wallet encryption/locking. """
2014-06-16 14:45:32 +02:00
2018-07-07 00:10:35 +02:00
import time
2019-10-23 15:21:50 +02:00
from decimal import Decimal
2018-07-07 00:10:35 +02:00
2015-11-15 17:58:01 +01:00
from test_framework . test_framework import BitcoinTestFramework
2018-07-07 00:10:35 +02:00
from test_framework . util import assert_equal , assert_raises_rpc_error
2014-06-16 14:45:32 +02:00
2015-11-15 17:58:01 +01:00
class KeyPoolTest ( BitcoinTestFramework ) :
2017-06-10 00:21:21 +02:00
def set_test_params ( self ) :
self . num_nodes = 1
2015-11-15 17:58:01 +01:00
2018-09-09 19:32:37 +02:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2015-11-15 17:58:01 +01:00
def run_test ( self ) :
nodes = self . nodes
2016-07-21 21:19:02 +02:00
addr_before_encrypting = nodes [ 0 ] . getnewaddress ( )
2018-02-09 17:12:27 +01:00
addr_before_encrypting_data = nodes [ 0 ] . getaddressinfo ( addr_before_encrypting )
2016-07-21 21:19:02 +02:00
wallet_info_old = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 21:33:35 +02:00
if not self . options . descriptors :
assert addr_before_encrypting_data [ ' hdseedid ' ] == wallet_info_old [ ' hdseedid ' ]
2018-04-16 11:13:07 +02:00
2015-11-15 18:48:18 +01:00
# Encrypt wallet and wait to terminate
2018-02-20 22:09:51 +01:00
nodes [ 0 ] . encryptwallet ( ' test ' )
2019-07-16 21:33:35 +02: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 18:48:18 +01:00
# Keep creating keys
2014-06-16 14:45:32 +02:00
addr = nodes [ 0 ] . getnewaddress ( )
2018-02-09 17:12:27 +01:00
addr_data = nodes [ 0 ] . getaddressinfo ( addr )
2016-07-21 21:19:02 +02:00
wallet_info = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 21:33:35 +02: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 16:33:46 +02:00
assert_raises_rpc_error ( - 12 , " Error: Keypool ran out, please call keypoolrefill first " , nodes [ 0 ] . getnewaddress )
2015-11-15 18:48:18 +01:00
2017-01-17 08:55:30 +01:00
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
2015-11-15 18:48:18 +01:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 12000 )
2017-01-10 16:45:30 +01:00
nodes [ 0 ] . keypoolrefill ( 6 )
2015-11-15 18:48:18 +01:00
nodes [ 0 ] . walletlock ( )
2017-01-10 16:45:30 +01:00
wi = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 21:33:35 +02: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 18:48:18 +01:00
2017-01-10 16:45:30 +01:00
# drain the internal keys
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
2017-01-17 08:55:30 +01:00
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
2015-11-15 18:48:18 +01:00
addr = set ( )
# the next one should fail
2017-07-12 16:33:46 +02:00
assert_raises_rpc_error ( - 12 , " Keypool ran out " , nodes [ 0 ] . getrawchangeaddress )
2015-11-15 18:48:18 +01:00
2017-01-10 16:45:30 +01:00
# drain the external keys
2019-10-23 15:21:50 +02: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 23:43:44 +01:00
assert len ( addr ) == 6
2017-01-10 16:45:30 +01:00
# the next one should fail
2017-07-12 16:33:46 +02:00
assert_raises_rpc_error ( - 12 , " Error: Keypool ran out, please call keypoolrefill first " , nodes [ 0 ] . getnewaddress )
2017-01-10 16:45:30 +01:00
2015-11-15 18:48:18 +01:00
# refill keypool with three new addresses
2016-01-08 13:12:16 +01:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 1 )
2015-11-15 18:48:18 +01:00
nodes [ 0 ] . keypoolrefill ( 3 )
2017-01-10 16:45:30 +01:00
2016-01-08 13:12:16 +01:00
# test walletpassphrase timeout
time . sleep ( 1.1 )
assert_equal ( nodes [ 0 ] . getwalletinfo ( ) [ " unlocked_until " ] , 0 )
2015-11-15 18:48:18 +01:00
2018-10-11 17:04:06 +02: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 18:48:18 +01:00
2017-01-10 16:45:30 +01:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 100 )
nodes [ 0 ] . keypoolrefill ( 100 )
wi = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 21:33:35 +02: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 16:45:30 +01:00
2019-10-23 15:21:50 +02:00
# create a blank wallet
2019-07-16 21:33:35 +02:00
nodes [ 0 ] . createwallet ( wallet_name = ' w2 ' , blank = True , disable_private_keys = True )
2019-10-23 15:21:50 +02:00
w2 = nodes [ 0 ] . get_wallet_rpc ( ' w2 ' )
# refer to initial wallet as w1
w1 = nodes [ 0 ] . get_wallet_rpc ( ' ' )
# import private key and fund it
address = addr . pop ( )
2019-07-16 21:33:35 +02: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 15:21:50 +02: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 14:45:32 +02:00
if __name__ == ' __main__ ' :
2015-11-15 17:58:01 +01:00
KeyPoolTest ( ) . main ( )