aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcoFalke <falke.marco@gmail.com>2019-07-29 09:36:49 -0400
committerMarcoFalke <falke.marco@gmail.com>2019-07-29 09:36:55 -0400
commit74ea1f3b0f268b0272f8b3548c736dc60f442f78 (patch)
tree315763a81ece963b7f0a76e0818353134a1caedd
parent29220250c10ece3f61581225701fe1334d6a6b43 (diff)
parente967cae8fac84ec7a89a3a853a83d8193ac3308e (diff)
Merge #16399: wallet: Improve wallet creation
e967cae8fac84ec7a89a3a853a83d8193ac3308e Use switch on status in RpcWallet (Fabian Jahr) ba1f128d6c117a63d5d904b3956551bd83405ec9 Return error for ignored passphrase through disable private keys option (Fabian Jahr) d6649d16b57e20b05075f1c80d0de7ff32cca1a4 Use strong enum for WalletCreationStatus (Fabian Jahr) 3199610ad3b93b849f2cb55a8ed3a39a32bbdffc 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` (https://github.com/bitcoin/bitcoin/pull/16244#discussion_r296434142) - Placing out args at the end convention (https://github.com/bitcoin/bitcoin/pull/16244#discussion_r296434172) - Return error when passphrase would be ignored because of disabled private keys (including functional test) (https://github.com/bitcoin/bitcoin/pull/16244#pullrequestreview-252015195) - Make `status` return variable of `CreateWallet` (https://github.com/bitcoin/bitcoin/pull/16244#discussion_r302107394) - Using a `switch` statement instead of `if/else` in `RpcWallet` (https://github.com/bitcoin/bitcoin/pull/16244#discussion_r302112502) Not included was: - "new create wallet function [could take] separate option arguments instead of wallet flags" (https://github.com/bitcoin/bitcoin/pull/16244#pullrequestreview-252015195) - "blank wallet and disable private keys options could be combined into a single option" (https://github.com/bitcoin/bitcoin/pull/16244#pullrequestreview-252015195) 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 e967cae8fac84ec7a89a3a853a83d8193ac3308e MarcoFalke: ACK e967cae8fa Tree-SHA512: 3d12880ff95add9e4a5702afa26ef38080b57b216a608c113a4d0a08ba2d61142c027ba0071c6402add45db90383eee0bada12dc42820dc0d602721d7175edd5
-rw-r--r--src/wallet/rpcwallet.cpp18
-rw-r--r--src/wallet/wallet.cpp27
-rw-r--r--src/wallet/wallet.h4
-rwxr-xr-xtest/functional/wallet_createwallet.py3
4 files changed, 29 insertions, 23 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7af009f430..f95d025815 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2690,14 +2690,16 @@ static UniValue createwallet(const JSONRPCRequest& request)
std::string error;
std::string warning;
- WalletCreationStatus status;
- std::shared_ptr<CWallet> wallet = CreateWallet(*g_rpc_interfaces->chain, request.params[0].get_str(), error, warning, status, passphrase, flags);
- if (status == WalletCreationStatus::CREATION_FAILED) {
- throw JSONRPCError(RPC_WALLET_ERROR, error);
- } else if (status == WalletCreationStatus::ENCRYPTION_FAILED) {
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error);
- } else if (status != WalletCreationStatus::SUCCESS) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed");
+ std::shared_ptr<CWallet> wallet;
+ WalletCreationStatus status = CreateWallet(*g_rpc_interfaces->chain, passphrase, flags, request.params[0].get_str(), error, warning, wallet);
+ switch (status) {
+ case WalletCreationStatus::CREATION_FAILED:
+ throw JSONRPCError(RPC_WALLET_ERROR, error);
+ case WalletCreationStatus::ENCRYPTION_FAILED:
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error);
+ case WalletCreationStatus::SUCCESS:
+ break;
+ // no default case, so the compiler can warn about missing cases
}
UniValue obj(UniValue::VOBJ);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index b9eb0d3eed..18915aad54 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -161,7 +161,7 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string&
return LoadWallet(chain, WalletLocation(name), error, warning);
}
-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)
+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)
{
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
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
WalletLocation location(name);
if (location.Exists()) {
error = "Wallet " + location.GetName() + " already exists.";
- status = WalletCreationStatus::CREATION_FAILED;
- return nullptr;
+ return WalletCreationStatus::CREATION_FAILED;
}
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
std::string wallet_error;
if (!CWallet::Verify(chain, location, false, wallet_error, warning)) {
error = "Wallet file verification failed: " + wallet_error;
- status = WalletCreationStatus::CREATION_FAILED;
- return nullptr;
+ return WalletCreationStatus::CREATION_FAILED;
+ }
+
+ // Do not allow a passphrase when private keys are disabled
+ if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ 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.";
+ return WalletCreationStatus::CREATION_FAILED;
}
// Make the wallet
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, wallet_creation_flags);
if (!wallet) {
error = "Wallet creation failed";
- status = WalletCreationStatus::CREATION_FAILED;
- return nullptr;
+ return WalletCreationStatus::CREATION_FAILED;
}
// Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) {
error = "Error: Wallet created but failed to encrypt.";
- status = WalletCreationStatus::ENCRYPTION_FAILED;
- return nullptr;
+ return WalletCreationStatus::ENCRYPTION_FAILED;
}
if (!create_blank) {
// Unlock the wallet
if (!wallet->Unlock(passphrase)) {
error = "Error: Wallet was encrypted but could not be unlocked";
- status = WalletCreationStatus::ENCRYPTION_FAILED;
- return nullptr;
+ return WalletCreationStatus::ENCRYPTION_FAILED;
}
// Set a seed for the wallet
@@ -221,8 +222,8 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin
}
AddWallet(wallet);
wallet->postInitProcess();
- status = WalletCreationStatus::SUCCESS;
- return wallet;
+ result = wallet;
+ return WalletCreationStatus::SUCCESS;
}
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 8ed27c3cc8..25dcae58bd 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -50,13 +50,13 @@ std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning);
-enum WalletCreationStatus {
+enum class WalletCreationStatus {
SUCCESS,
CREATION_FAILED,
ENCRYPTION_FAILED
};
-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);
+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);
//! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index c17949a2f6..294f90a0fa 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -119,5 +119,8 @@ class CreateWalletTest(BitcoinTestFramework):
# Empty passphrase, error
assert_raises_rpc_error(-16, 'Cannot encrypt a wallet with a blank password', self.nodes[0].createwallet, 'w7', False, False, '')
+ self.log.info('Using a passphrase with private keys disabled returns error')
+ 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')
+
if __name__ == '__main__':
CreateWalletTest().main()