wallet_keypool.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2014-present The Bitcoin Core developers 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 """Test the wallet keypool and interaction with wallet encryption/locking.""" 6 7 from decimal import Decimal 8 9 from test_framework.test_framework import BitcoinTestFramework 10 from test_framework.util import ( 11 assert_equal, 12 assert_not_equal, 13 assert_raises_rpc_error, 14 ) 15 from test_framework.wallet_util import WalletUnlock 16 17 class KeyPoolTest(BitcoinTestFramework): 18 def set_test_params(self): 19 self.num_nodes = 1 20 21 def skip_test_if_missing_module(self): 22 self.skip_if_no_wallet() 23 24 def run_test(self): 25 nodes = self.nodes 26 addr_before_encrypting = nodes[0].getnewaddress() 27 addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting) 28 29 # Encrypt wallet and wait to terminate 30 nodes[0].encryptwallet('test') 31 # Import hardened derivation only descriptors 32 nodes[0].walletpassphrase('test', 10) 33 nodes[0].importdescriptors([ 34 { 35 "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n", 36 "timestamp": "now", 37 "range": [0,0], 38 "active": True 39 }, 40 { 41 "desc": "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1h/*h)#a0nyvl0k", 42 "timestamp": "now", 43 "range": [0,0], 44 "active": True 45 }, 46 { 47 "desc": "sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/2h/*h))#lmeu2axg", 48 "timestamp": "now", 49 "range": [0,0], 50 "active": True 51 }, 52 { 53 "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/3h/*h)#jkl636gm", 54 "timestamp": "now", 55 "range": [0,0], 56 "active": True, 57 "internal": True 58 }, 59 { 60 "desc": "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/4h/*h)#l3crwaus", 61 "timestamp": "now", 62 "range": [0,0], 63 "active": True, 64 "internal": True 65 }, 66 { 67 "desc": "sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/5h/*h))#qg8wa75f", 68 "timestamp": "now", 69 "range": [0,0], 70 "active": True, 71 "internal": True 72 } 73 ]) 74 nodes[0].walletlock() 75 # Keep creating keys 76 addr = nodes[0].getnewaddress() 77 addr_data = nodes[0].getaddressinfo(addr) 78 assert_not_equal(addr_before_encrypting_data['hdmasterfingerprint'], addr_data['hdmasterfingerprint']) 79 assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) 80 81 # put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min) 82 with WalletUnlock(nodes[0], 'test'): 83 nodes[0].keypoolrefill(6) 84 wi = nodes[0].getwalletinfo() 85 assert_equal(wi['keypoolsize_hd_internal'], 24) 86 assert_equal(wi['keypoolsize'], 24) 87 88 # drain the internal keys 89 nodes[0].getrawchangeaddress() 90 nodes[0].getrawchangeaddress() 91 nodes[0].getrawchangeaddress() 92 nodes[0].getrawchangeaddress() 93 nodes[0].getrawchangeaddress() 94 nodes[0].getrawchangeaddress() 95 # remember keypool sizes 96 wi = nodes[0].getwalletinfo() 97 kp_size_before = [wi['keypoolsize_hd_internal'], wi['keypoolsize']] 98 # the next one should fail 99 assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getrawchangeaddress) 100 # check that keypool sizes did not change 101 wi = nodes[0].getwalletinfo() 102 kp_size_after = [wi['keypoolsize_hd_internal'], wi['keypoolsize']] 103 assert_equal(kp_size_before, kp_size_after) 104 105 # drain the external keys 106 addr = set() 107 addr.add(nodes[0].getnewaddress(address_type="bech32")) 108 addr.add(nodes[0].getnewaddress(address_type="bech32")) 109 addr.add(nodes[0].getnewaddress(address_type="bech32")) 110 addr.add(nodes[0].getnewaddress(address_type="bech32")) 111 addr.add(nodes[0].getnewaddress(address_type="bech32")) 112 addr.add(nodes[0].getnewaddress(address_type="bech32")) 113 assert len(addr) == 6 114 # remember keypool sizes 115 wi = nodes[0].getwalletinfo() 116 kp_size_before = [wi['keypoolsize_hd_internal'], wi['keypoolsize']] 117 # the next one should fail 118 assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) 119 # check that keypool sizes did not change 120 wi = nodes[0].getwalletinfo() 121 kp_size_after = [wi['keypoolsize_hd_internal'], wi['keypoolsize']] 122 assert_equal(kp_size_before, kp_size_after) 123 124 # refill keypool with three new addresses 125 nodes[0].walletpassphrase('test', 1) 126 nodes[0].keypoolrefill(3) 127 128 # test walletpassphrase timeout 129 # CScheduler relies on condition_variable::wait_until() which does not 130 # guarantee accurate timing. We'll wait up to 5 seconds to execute a 1 131 # second scheduled event. 132 nodes[0].wait_until(lambda: nodes[0].getwalletinfo()["unlocked_until"] == 0, timeout=5) 133 134 # drain the keypool 135 for _ in range(3): 136 nodes[0].getnewaddress() 137 assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getnewaddress) 138 139 with WalletUnlock(nodes[0], 'test'): 140 nodes[0].keypoolrefill(100) 141 wi = nodes[0].getwalletinfo() 142 assert_equal(wi['keypoolsize_hd_internal'], 400) 143 assert_equal(wi['keypoolsize'], 400) 144 145 # create a blank wallet 146 nodes[0].createwallet(wallet_name='w2', blank=True, disable_private_keys=True) 147 w2 = nodes[0].get_wallet_rpc('w2') 148 149 # refer to initial wallet as w1 150 w1 = nodes[0].get_wallet_rpc(self.default_wallet_name) 151 152 # import private key and fund it 153 address = addr.pop() 154 desc = w1.getaddressinfo(address)['desc'] 155 res = w2.importdescriptors([{'desc': desc, 'timestamp': 'now'}]) 156 assert_equal(res[0]['success'], True) 157 158 with WalletUnlock(w1, 'test'): 159 res = w1.sendtoaddress(address=address, amount=0.00010000) 160 self.generate(nodes[0], 1) 161 destination = addr.pop() 162 163 # Using a fee rate (10 sat / byte) well above the minimum relay rate 164 # creating a 5,000 sat transaction with change should not be possible 165 assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", w2.walletcreatefundedpsbt, inputs=[], outputs=[{addr.pop(): 0.00005000}], subtractFeeFromOutputs=[0], feeRate=0.00010) 166 167 # creating a 10,000 sat transaction without change, with a manual input, should still be possible 168 res = w2.walletcreatefundedpsbt(inputs=w2.listunspent(), outputs=[{destination: 0.00010000}], subtractFeeFromOutputs=[0], feeRate=0.00010) 169 assert_equal("psbt" in res, True) 170 171 # creating a 10,000 sat transaction without change should still be possible 172 res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{destination: 0.00010000}], subtractFeeFromOutputs=[0], feeRate=0.00010) 173 assert_equal("psbt" in res, True) 174 # should work without subtractFeeFromOutputs if the exact fee is subtracted from the amount 175 res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{destination: 0.00008900}], feeRate=0.00010) 176 assert_equal("psbt" in res, True) 177 178 # dust change should be removed 179 res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{destination: 0.00008800}], feeRate=0.00010) 180 assert_equal("psbt" in res, True) 181 182 # create a transaction without change at the maximum fee rate, such that the output is still spendable: 183 res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{destination: 0.00010000}], subtractFeeFromOutputs=[0], feeRate=0.0008823) 184 assert_equal("psbt" in res, True) 185 assert_equal(res["fee"], Decimal("0.00009706")) 186 187 # creating a 10,000 sat transaction with a manual change address should be possible 188 res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{destination: 0.00010000}], subtractFeeFromOutputs=[0], feeRate=0.00010, changeAddress=addr.pop()) 189 assert_equal("psbt" in res, True) 190 191 if __name__ == '__main__': 192 KeyPoolTest(__file__).main()