Skip to content

Commit 42b66a6

Browse files
author
MarcoFalke
committed
Merge #20186: wallet: Make -wallet setting not create wallets
01476a8 wallet: Make -wallet setting not create wallets (Russell Yanofsky) Pull request description: This changes `-wallet` setting to only load existing wallets, not create new ones. - Fixes settings.json corner cases reported by sjors & promag: bitcoin-core/gui#95, #19754 (comment), #19754 (comment) - Prevents accidental creation of wallets reported most recently by jb55 http://www.erisian.com.au/bitcoin-core-dev/log-2020-09-14.html#l-355 - Simplifies behavior after #15454. #15454 took the big step of disabling creation of the default wallet. This PR extends it to avoid creating other wallets as well. With this change, new wallets just aren't created on startup, instead of sometimes being created, sometimes not. #15454 release notes are updated here and are simpler. This change should be targeted for 0.21.0. It's a bug fix and simplifies behavior of the #15937 / #19754 / #15454 features added in 0.21.0. --- This PR is implementing the simplest, most basic alternative listed in bitcoin-core/gui#95 (comment). Other improvements mentioned there can build on top of this. ACKs for top commit: achow101: ACK 01476a8 hebasto: re-ACK 01476a8 MarcoFalke: review ACK 01476a8 🏂 Tree-SHA512: 0d50f4e5dfbd04a2efd9fd66c02085a0ed705807bdec1cf5770d0ae8cb6af07080fb81306349937bf66acdb713d03fb35636f6442b650d0820e66cbae09c2f87
2 parents 8e9e190 + 01476a8 commit 42b66a6

File tree

10 files changed

+77
-41
lines changed

10 files changed

+77
-41
lines changed

doc/release-notes.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -292,15 +292,18 @@ Wallet
292292
changed from `-32601` (method not found) to `-18` (wallet not found).
293293
(#20101)
294294

295-
### Default Wallet
296-
297-
Bitcoin Core will no longer create an unnamed `""` wallet by default when no
298-
wallet is specified on the command line or in the configuration files. For
299-
backwards compatibility, if an unnamed `""` wallet already exists and would
300-
have been loaded previously, then it will still be loaded. Users without an
301-
unnamed `""` wallet and without any other wallets to be loaded on startup will
302-
be prompted to either choose a wallet to load, or to create a new wallet.
303-
(#15454)
295+
### Automatic wallet creation removed
296+
297+
Bitcoin Core will no longer automatically create new wallets on startup. It will
298+
load existing wallets specified by `-wallet` options on the command line or in
299+
`bitcoin.conf` or `settings.json` files. And by default it will also load a
300+
top-level unnamed ("") wallet. However, if specified wallets don't exist,
301+
Bitcoin Core will now just log warnings instead of creating new wallets with
302+
new keys and addresses like previous releases did.
303+
304+
New wallets can be created through the GUI (which has a more prominent create
305+
wallet option), through the `bitcoin-cli createwallet` or `bitcoin-wallet
306+
create` commands, or the `createwallet` RPC. (#15454)
304307

305308
### Experimental Descriptor Wallets
306309

src/wallet/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
6060
argsman.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
6161
argsman.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
6262
argsman.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
63-
argsman.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
63+
argsman.AddArg("-wallet=<path>", "Specify wallet path to load at startup. Can be used multiple times to load multiple wallets. Path is to a directory containing wallet data and log files. If the path is not absolute, it is interpreted relative to <walletdir>. This only loads existing wallets and does not create new ones. For backwards compatibility this also accepts names of existing top-level data files in <walletdir>.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
6464
argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
6565
argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
6666
#if HAVE_SYSTEM

src/wallet/load.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,16 @@ bool VerifyWallets(interfaces::Chain& chain)
7171

7272
DatabaseOptions options;
7373
DatabaseStatus status;
74+
options.require_existing = true;
7475
options.verify = true;
7576
bilingual_str error_string;
7677
if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
77-
chain.initError(error_string);
78-
return false;
78+
if (status == DatabaseStatus::FAILED_NOT_FOUND) {
79+
chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s\n", error_string.original)));
80+
} else {
81+
chain.initError(error_string);
82+
return false;
83+
}
7984
}
8085
}
8186

@@ -88,10 +93,14 @@ bool LoadWallets(interfaces::Chain& chain)
8893
for (const std::string& name : gArgs.GetArgs("-wallet")) {
8994
DatabaseOptions options;
9095
DatabaseStatus status;
96+
options.require_existing = true;
9197
options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
9298
bilingual_str error;
9399
std::vector<bilingual_str> warnings;
94100
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
101+
if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
102+
continue;
103+
}
95104
std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings) : nullptr;
96105
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
97106
if (!pwallet) {

test/functional/feature_config_args.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,19 +179,15 @@ def run_test(self):
179179

180180
# Create the directory and ensure the config file now works
181181
os.mkdir(new_data_dir)
182-
self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
182+
self.start_node(0, ['-conf='+conf_file])
183183
self.stop_node(0)
184184
assert os.path.exists(os.path.join(new_data_dir, self.chain, 'blocks'))
185-
if self.is_wallet_compiled():
186-
assert os.path.exists(os.path.join(new_data_dir, self.chain, 'wallets', 'w1'))
187185

188186
# Ensure command line argument overrides datadir in conf
189187
os.mkdir(new_data_dir_2)
190188
self.nodes[0].datadir = new_data_dir_2
191-
self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file, '-wallet=w2'])
189+
self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file])
192190
assert os.path.exists(os.path.join(new_data_dir_2, self.chain, 'blocks'))
193-
if self.is_wallet_compiled():
194-
assert os.path.exists(os.path.join(new_data_dir_2, self.chain, 'wallets', 'w2'))
195191

196192

197193
if __name__ == '__main__':

test/functional/rpc_scantxoutset.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def run_test(self):
5555
self.log.info("Stop node, remove wallet, mine again some blocks...")
5656
self.stop_node(0)
5757
shutil.rmtree(os.path.join(self.nodes[0].datadir, self.chain, 'wallets'))
58-
self.start_node(0)
58+
self.start_node(0, ['-nowallet'])
59+
self.import_deterministic_coinbase_privkeys()
5960
self.nodes[0].generate(110)
6061

6162
scan = self.nodes[0].scantxoutset("start", [])

test/functional/test_framework/test_framework.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def __init__(self):
111111
# are not imported.
112112
self.wallet_names = None
113113
self.set_test_params()
114+
assert self.wallet_names is None or len(self.wallet_names) <= self.num_nodes
114115
if self.options.timeout_factor == 0 :
115116
self.options.timeout_factor = 99999
116117
self.rpc_timeout = int(self.rpc_timeout * self.options.timeout_factor) # optionally, increase timeout by a factor
@@ -390,9 +391,13 @@ def setup_nodes(self):
390391
assert_equal(chain_info["initialblockdownload"], False)
391392

392393
def import_deterministic_coinbase_privkeys(self):
393-
wallet_names = [self.default_wallet_name] * len(self.nodes) if self.wallet_names is None else self.wallet_names
394-
assert len(wallet_names) <= len(self.nodes)
395-
for wallet_name, n in zip(wallet_names, self.nodes):
394+
for i in range(self.num_nodes):
395+
self.init_wallet(i)
396+
397+
def init_wallet(self, i):
398+
wallet_name = self.default_wallet_name if self.wallet_names is None else self.wallet_names[i] if i < len(self.wallet_names) else False
399+
if wallet_name is not False:
400+
n = self.nodes[i]
396401
if wallet_name is not None:
397402
n.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors, load_on_startup=True)
398403
n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase')

test/functional/tool_wallet.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ def test_getwalletinfo_on_different_wallet(self):
218218
def test_salvage(self):
219219
# TODO: Check salvage actually salvages and doesn't break things. https://github.com/bitcoin/bitcoin/issues/7463
220220
self.log.info('Check salvage')
221-
self.start_node(0, ['-wallet=salvage'])
221+
self.start_node(0)
222+
self.nodes[0].createwallet("salvage")
222223
self.stop_node(0)
223224

224225
self.assert_tool_output('', '-wallet=salvage', 'salvage')

test/functional/wallet_backup.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ def do_one_round(self):
9191
self.sync_blocks()
9292

9393
# As above, this mirrors the original bash test.
94-
def start_three(self):
95-
self.start_node(0)
96-
self.start_node(1)
97-
self.start_node(2)
94+
def start_three(self, args=()):
95+
self.start_node(0, self.extra_args[0] + list(args))
96+
self.start_node(1, self.extra_args[1] + list(args))
97+
self.start_node(2, self.extra_args[2] + list(args))
9898
self.connect_nodes(0, 3)
9999
self.connect_nodes(1, 3)
100100
self.connect_nodes(2, 3)
@@ -110,6 +110,11 @@ def erase_three(self):
110110
os.remove(os.path.join(self.nodes[1].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
111111
os.remove(os.path.join(self.nodes[2].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
112112

113+
def init_three(self):
114+
self.init_wallet(0)
115+
self.init_wallet(1)
116+
self.init_wallet(2)
117+
113118
def run_test(self):
114119
self.log.info("Generating initial blockchain")
115120
self.nodes[0].generate(1)
@@ -193,7 +198,8 @@ def run_test(self):
193198
shutil.rmtree(os.path.join(self.nodes[2].datadir, self.chain, 'blocks'))
194199
shutil.rmtree(os.path.join(self.nodes[2].datadir, self.chain, 'chainstate'))
195200

196-
self.start_three()
201+
self.start_three(["-nowallet"])
202+
self.init_three()
197203

198204
assert_equal(self.nodes[0].getbalance(), 0)
199205
assert_equal(self.nodes[1].getbalance(), 0)

test/functional/wallet_dump.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
9595
class WalletDumpTest(BitcoinTestFramework):
9696
def set_test_params(self):
9797
self.num_nodes = 1
98-
self.extra_args = [["-keypool=90", "-addresstype=legacy", "-wallet=dump"]]
98+
self.extra_args = [["-keypool=90", "-addresstype=legacy"]]
9999
self.rpc_timeout = 120
100100

101101
def skip_test_if_missing_module(self):
@@ -106,6 +106,8 @@ def setup_network(self):
106106
self.start_nodes()
107107

108108
def run_test(self):
109+
self.nodes[0].createwallet("dump")
110+
109111
wallet_unenc_dump = os.path.join(self.nodes[0].datadir, "wallet.unencrypted.dump")
110112
wallet_enc_dump = os.path.join(self.nodes[0].datadir, "wallet.encrypted.dump")
111113

@@ -190,7 +192,8 @@ def run_test(self):
190192
assert_raises_rpc_error(-8, "already exists", lambda: self.nodes[0].dumpwallet(wallet_enc_dump))
191193

192194
# Restart node with new wallet, and test importwallet
193-
self.restart_node(0, ['-wallet=w2'])
195+
self.restart_node(0)
196+
self.nodes[0].createwallet("w2")
194197

195198
# Make sure the address is not IsMine before import
196199
result = self.nodes[0].getaddressinfo(multisig_addr)

test/functional/wallet_multiwallet.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def set_test_params(self):
4141
self.setup_clean_chain = True
4242
self.num_nodes = 2
4343
self.rpc_timeout = 120
44+
self.extra_args = [["-nowallet"], []]
4445

4546
def skip_test_if_missing_module(self):
4647
self.skip_if_no_wallet()
@@ -80,7 +81,9 @@ def wallet_file(name):
8081
# rename wallet.dat to make sure plain wallet file paths (as opposed to
8182
# directory paths) can be loaded
8283
# create another dummy wallet for use in testing backups later
83-
self.start_node(0, ["-nowallet", "-wallet=empty", "-wallet=plain"])
84+
self.start_node(0)
85+
node.createwallet("empty", descriptors=False)
86+
node.createwallet("plain", descriptors=False)
8487
node.createwallet("created")
8588
self.stop_nodes()
8689
empty_wallet = os.path.join(self.options.tmpdir, 'empty.dat')
@@ -101,21 +104,23 @@ def wallet_file(name):
101104
# w8 - to verify existing wallet file is loaded correctly
102105
# '' - to verify default wallet file is created correctly
103106
wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', self.default_wallet_name]
104-
extra_args = ['-nowallet'] + ['-wallet={}'.format(n) for n in wallet_names]
105-
self.start_node(0, extra_args)
107+
self.start_node(0)
108+
for wallet_name in wallet_names[:-2]:
109+
self.nodes[0].createwallet(wallet_name, descriptors=False)
110+
for wallet_name in wallet_names[-2:]:
111+
self.nodes[0].loadwallet(wallet_name)
106112
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8'])
107113

108114
assert_equal(set(node.listwallets()), set(wallet_names))
109115

116+
# should raise rpc error if wallet path can't be created
117+
assert_raises_rpc_error(-1, "boost::filesystem::create_directory:", self.nodes[0].createwallet, "w8/bad", descriptors=False)
118+
110119
# check that all requested wallets were created
111120
self.stop_node(0)
112121
for wallet_name in wallet_names:
113122
assert_equal(os.path.isfile(wallet_file(wallet_name)), True)
114123

115-
# should not initialize if wallet path can't be created
116-
exp_stderr = "boost::filesystem::create_directory:"
117-
self.nodes[0].assert_start_raises_init_error(['-wallet=w8/bad'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
118-
119124
self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist')
120125
self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir())
121126
self.nodes[0].assert_start_raises_init_error(['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir())
@@ -142,26 +147,33 @@ def wallet_file(name):
142147
# if wallets/ doesn't exist, datadir should be the default wallet dir
143148
wallet_dir2 = data_dir('walletdir')
144149
os.rename(wallet_dir(), wallet_dir2)
145-
self.start_node(0, ['-nowallet', '-wallet=w4', '-wallet=w5'])
150+
self.start_node(0)
151+
self.nodes[0].createwallet("w4")
152+
self.nodes[0].createwallet("w5")
146153
assert_equal(set(node.listwallets()), {"w4", "w5"})
147154
w5 = wallet("w5")
148155
node.generatetoaddress(nblocks=1, address=w5.getnewaddress())
149156

150157
# now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
151158
os.rename(wallet_dir2, wallet_dir())
152-
self.restart_node(0, ['-nowallet', '-wallet=w4', '-wallet=w5', '-walletdir=' + data_dir()])
159+
self.restart_node(0, ['-nowallet', '-walletdir=' + data_dir()])
160+
self.nodes[0].loadwallet("w4")
161+
self.nodes[0].loadwallet("w5")
153162
assert_equal(set(node.listwallets()), {"w4", "w5"})
154163
w5 = wallet("w5")
155164
w5_info = w5.getwalletinfo()
156165
assert_equal(w5_info['immature_balance'], 50)
157166

158167
competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir')
159168
os.mkdir(competing_wallet_dir)
160-
self.restart_node(0, ['-walletdir=' + competing_wallet_dir])
169+
self.restart_node(0, ['-nowallet', '-walletdir=' + competing_wallet_dir])
170+
self.nodes[0].createwallet(self.default_wallet_name, descriptors=False)
161171
exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\S*\"!"
162172
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
163173

164-
self.restart_node(0, extra_args)
174+
self.restart_node(0)
175+
for wallet_name in wallet_names:
176+
self.nodes[0].loadwallet(wallet_name)
165177

166178
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy'])
167179

0 commit comments

Comments
 (0)