Skip to content

Commit 74ea1f3

Browse files
author
MarcoFalke
committed
Merge #16399: wallet: Improve wallet creation
e967cae Use switch on status in RpcWallet (Fabian Jahr) ba1f128 Return error for ignored passphrase through disable private keys option (Fabian Jahr) d6649d1 Use strong enum for WalletCreationStatus (Fabian Jahr) 3199610 Place out args at the end for CreateWallet (Fabian Jahr) Pull request description: This is a follow-up PR to #16244 The following suggestions are included: - Usage of `enum class` (bitcoin/bitcoin#16244 (comment)) - Placing out args at the end convention (bitcoin/bitcoin#16244 (comment)) - Return error when passphrase would be ignored because of disabled private keys (including functional test) (bitcoin/bitcoin#16244 (review)) - Make `status` return variable of `CreateWallet` (bitcoin/bitcoin#16244 (comment)) - Using a `switch` statement instead of `if/else` in `RpcWallet` (bitcoin/bitcoin#16244 (comment)) Not included was: - "new create wallet function [could take] separate option arguments instead of wallet flags" (bitcoin/bitcoin#16244 (review)) - "blank wallet and disable private keys options could be combined into a single option" (bitcoin/bitcoin#16244 (review)) For these last two changes, I was not sure what an ideal solution could look like and/or this might be of slightly larger scope than the other changes, but I would be happy to work on these as well in this PR or another follow-up if I get positive feedback on that. Is there a place in the codebase that handles flags like these in a better way that I can refer to? Nonetheless, I would prefer keeping it in a separate PR unless it is a really simple change. ACKs for top commit: jnewbery: Code review utACK e967cae MarcoFalke: ACK e967cae Tree-SHA512: 3d12880ff95add9e4a5702afa26ef38080b57b216a608c113a4d0a08ba2d61142c027ba0071c6402add45db90383eee0bada12dc42820dc0d602721d7175edd5
2 parents 2922025 + e967cae commit 74ea1f3

File tree

4 files changed

+29
-23
lines changed

4 files changed

+29
-23
lines changed

src/wallet/rpcwallet.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,14 +2690,16 @@ static UniValue createwallet(const JSONRPCRequest& request)
26902690

26912691
std::string error;
26922692
std::string warning;
2693-
WalletCreationStatus status;
2694-
std::shared_ptr<CWallet> wallet = CreateWallet(*g_rpc_interfaces->chain, request.params[0].get_str(), error, warning, status, passphrase, flags);
2695-
if (status == WalletCreationStatus::CREATION_FAILED) {
2696-
throw JSONRPCError(RPC_WALLET_ERROR, error);
2697-
} else if (status == WalletCreationStatus::ENCRYPTION_FAILED) {
2698-
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error);
2699-
} else if (status != WalletCreationStatus::SUCCESS) {
2700-
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed");
2693+
std::shared_ptr<CWallet> wallet;
2694+
WalletCreationStatus status = CreateWallet(*g_rpc_interfaces->chain, passphrase, flags, request.params[0].get_str(), error, warning, wallet);
2695+
switch (status) {
2696+
case WalletCreationStatus::CREATION_FAILED:
2697+
throw JSONRPCError(RPC_WALLET_ERROR, error);
2698+
case WalletCreationStatus::ENCRYPTION_FAILED:
2699+
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error);
2700+
case WalletCreationStatus::SUCCESS:
2701+
break;
2702+
// no default case, so the compiler can warn about missing cases
27012703
}
27022704

27032705
UniValue obj(UniValue::VOBJ);

src/wallet/wallet.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string&
161161
return LoadWallet(chain, WalletLocation(name), error, warning);
162162
}
163163

164-
std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning, WalletCreationStatus& status, const SecureString& passphrase, uint64_t wallet_creation_flags)
164+
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::shared_ptr<CWallet>& result)
165165
{
166166
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
167167
bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
@@ -175,39 +175,40 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin
175175
WalletLocation location(name);
176176
if (location.Exists()) {
177177
error = "Wallet " + location.GetName() + " already exists.";
178-
status = WalletCreationStatus::CREATION_FAILED;
179-
return nullptr;
178+
return WalletCreationStatus::CREATION_FAILED;
180179
}
181180

182181
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
183182
std::string wallet_error;
184183
if (!CWallet::Verify(chain, location, false, wallet_error, warning)) {
185184
error = "Wallet file verification failed: " + wallet_error;
186-
status = WalletCreationStatus::CREATION_FAILED;
187-
return nullptr;
185+
return WalletCreationStatus::CREATION_FAILED;
186+
}
187+
188+
// Do not allow a passphrase when private keys are disabled
189+
if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
190+
error = "Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.";
191+
return WalletCreationStatus::CREATION_FAILED;
188192
}
189193

190194
// Make the wallet
191195
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, wallet_creation_flags);
192196
if (!wallet) {
193197
error = "Wallet creation failed";
194-
status = WalletCreationStatus::CREATION_FAILED;
195-
return nullptr;
198+
return WalletCreationStatus::CREATION_FAILED;
196199
}
197200

198201
// Encrypt the wallet
199202
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
200203
if (!wallet->EncryptWallet(passphrase)) {
201204
error = "Error: Wallet created but failed to encrypt.";
202-
status = WalletCreationStatus::ENCRYPTION_FAILED;
203-
return nullptr;
205+
return WalletCreationStatus::ENCRYPTION_FAILED;
204206
}
205207
if (!create_blank) {
206208
// Unlock the wallet
207209
if (!wallet->Unlock(passphrase)) {
208210
error = "Error: Wallet was encrypted but could not be unlocked";
209-
status = WalletCreationStatus::ENCRYPTION_FAILED;
210-
return nullptr;
211+
return WalletCreationStatus::ENCRYPTION_FAILED;
211212
}
212213

213214
// Set a seed for the wallet
@@ -221,8 +222,8 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin
221222
}
222223
AddWallet(wallet);
223224
wallet->postInitProcess();
224-
status = WalletCreationStatus::SUCCESS;
225-
return wallet;
225+
result = wallet;
226+
return WalletCreationStatus::SUCCESS;
226227
}
227228

228229
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;

src/wallet/wallet.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ std::vector<std::shared_ptr<CWallet>> GetWallets();
5050
std::shared_ptr<CWallet> GetWallet(const std::string& name);
5151
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning);
5252

53-
enum WalletCreationStatus {
53+
enum class WalletCreationStatus {
5454
SUCCESS,
5555
CREATION_FAILED,
5656
ENCRYPTION_FAILED
5757
};
5858

59-
std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning, WalletCreationStatus& status, const SecureString& passphrase, uint64_t wallet_creation_flags);
59+
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::shared_ptr<CWallet>& result);
6060

6161
//! Default for -keypool
6262
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;

test/functional/wallet_createwallet.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,8 @@ def run_test(self):
119119
# Empty passphrase, error
120120
assert_raises_rpc_error(-16, 'Cannot encrypt a wallet with a blank password', self.nodes[0].createwallet, 'w7', False, False, '')
121121

122+
self.log.info('Using a passphrase with private keys disabled returns error')
123+
assert_raises_rpc_error(-4, 'Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.', self.nodes[0].createwallet, wallet_name='w8', disable_private_keys=True, passphrase='thisisapassphrase')
124+
122125
if __name__ == '__main__':
123126
CreateWalletTest().main()

0 commit comments

Comments
 (0)