Skip to content

Commit d3165ed

Browse files
committed
Merge pull request bitcoin#4347
6c37f7f `getrawchangeaddress` should fail when keypool exhausted (Wladimir J. van der Laan)
2 parents 6ad2c24 + 6c37f7f commit d3165ed

File tree

4 files changed

+137
-6
lines changed

4 files changed

+137
-6
lines changed

qa/rpc-tests/keypool.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) 2014 The Bitcoin Core developers
3+
# Distributed under the MIT/X11 software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
# Exercise the wallet keypool, and interaction with wallet encryption/locking
7+
8+
# Add python-bitcoinrpc to module search path:
9+
import os
10+
import sys
11+
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
12+
13+
import json
14+
import shutil
15+
import subprocess
16+
import tempfile
17+
import traceback
18+
19+
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
20+
from util import *
21+
22+
23+
def check_array_result(object_array, to_match, expected):
24+
"""
25+
Pass in array of JSON objects, a dictionary with key/value pairs
26+
to match against, and another dictionary with expected key/value
27+
pairs.
28+
"""
29+
num_matched = 0
30+
for item in object_array:
31+
all_match = True
32+
for key,value in to_match.items():
33+
if item[key] != value:
34+
all_match = False
35+
if not all_match:
36+
continue
37+
for key,value in expected.items():
38+
if item[key] != value:
39+
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
40+
num_matched = num_matched+1
41+
if num_matched == 0:
42+
raise AssertionError("No objects matched %s"%(str(to_match)))
43+
44+
def run_test(nodes, tmpdir):
45+
# Encrypt wallet and wait to terminate
46+
nodes[0].encryptwallet('test')
47+
bitcoind_processes[0].wait()
48+
# Restart node 0
49+
nodes[0] = start_node(0, tmpdir)
50+
# Keep creating keys
51+
addr = nodes[0].getnewaddress()
52+
try:
53+
addr = nodes[0].getnewaddress()
54+
raise AssertionError('Keypool should be exhausted after one address')
55+
except JSONRPCException,e:
56+
assert(e.error['code']==-12)
57+
58+
# put three new keys in the keypool
59+
nodes[0].walletpassphrase('test', 12000)
60+
nodes[0].keypoolrefill(3)
61+
nodes[0].walletlock()
62+
63+
# drain the keys
64+
addr = set()
65+
addr.add(nodes[0].getrawchangeaddress())
66+
addr.add(nodes[0].getrawchangeaddress())
67+
addr.add(nodes[0].getrawchangeaddress())
68+
addr.add(nodes[0].getrawchangeaddress())
69+
# assert that four unique addresses were returned
70+
assert(len(addr) == 4)
71+
# the next one should fail
72+
try:
73+
addr = nodes[0].getrawchangeaddress()
74+
raise AssertionError('Keypool should be exhausted after three addresses')
75+
except JSONRPCException,e:
76+
assert(e.error['code']==-12)
77+
78+
79+
def main():
80+
import optparse
81+
82+
parser = optparse.OptionParser(usage="%prog [options]")
83+
parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
84+
help="Leave bitcoinds and test.* datadir on exit or error")
85+
parser.add_option("--srcdir", dest="srcdir", default="../../src",
86+
help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
87+
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
88+
help="Root directory for datadirs")
89+
(options, args) = parser.parse_args()
90+
91+
os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
92+
93+
check_json_precision()
94+
95+
success = False
96+
nodes = []
97+
try:
98+
print("Initializing test directory "+options.tmpdir)
99+
if not os.path.isdir(options.tmpdir):
100+
os.makedirs(options.tmpdir)
101+
initialize_chain(options.tmpdir)
102+
103+
nodes = start_nodes(1, options.tmpdir)
104+
105+
run_test(nodes, options.tmpdir)
106+
107+
success = True
108+
109+
except AssertionError as e:
110+
print("Assertion failed: "+e.message)
111+
except JSONRPCException as e:
112+
print("JSONRPC error: "+e.error['message'])
113+
traceback.print_tb(sys.exc_info()[2])
114+
except Exception as e:
115+
print("Unexpected exception caught during testing: "+str(sys.exc_info()[0]))
116+
traceback.print_tb(sys.exc_info()[2])
117+
118+
if not options.nocleanup:
119+
print("Cleaning up")
120+
stop_nodes(nodes)
121+
wait_bitcoinds()
122+
shutil.rmtree(options.tmpdir)
123+
124+
if success:
125+
print("Tests successful")
126+
sys.exit(0)
127+
else:
128+
print("Failed")
129+
sys.exit(1)
130+
131+
if __name__ == '__main__':
132+
main()

src/miner.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,10 @@ void static BitcoinMiner(CWallet *pwallet)
467467

468468
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
469469
if (!pblocktemplate.get())
470+
{
471+
LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
470472
return;
473+
}
471474
CBlock *pblock = &pblocktemplate->block;
472475
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
473476

src/rpcwallet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ Value getrawchangeaddress(const Array& params, bool fHelp)
201201
CReserveKey reservekey(pwalletMain);
202202
CPubKey vchPubKey;
203203
if (!reservekey.GetReservedKey(vchPubKey))
204-
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Unable to obtain key for change");
204+
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
205205

206206
reservekey.KeepKey();
207207

src/wallet.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,11 +2010,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey)
20102010
if (nIndex != -1)
20112011
vchPubKey = keypool.vchPubKey;
20122012
else {
2013-
if (pwallet->vchDefaultKey.IsValid()) {
2014-
LogPrintf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!");
2015-
vchPubKey = pwallet->vchDefaultKey;
2016-
} else
2017-
return false;
2013+
return false;
20182014
}
20192015
}
20202016
assert(vchPubKey.IsValid());

0 commit comments

Comments
 (0)