diff options
author | MarcoFalke <falke.marco@gmail.com> | 2019-07-29 09:36:49 -0400 |
---|---|---|
committer | MarcoFalke <falke.marco@gmail.com> | 2019-07-29 09:36:55 -0400 |
commit | 74ea1f3b0f268b0272f8b3548c736dc60f442f78 (patch) | |
tree | 315763a81ece963b7f0a76e0818353134a1caedd | |
parent | 29220250c10ece3f61581225701fe1334d6a6b43 (diff) | |
parent | e967cae8fac84ec7a89a3a853a83d8193ac3308e (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.cpp | 18 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 27 | ||||
-rw-r--r-- | src/wallet/wallet.h | 4 | ||||
-rwxr-xr-x | test/functional/wallet_createwallet.py | 3 |
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() |