From 9421317740a59110eba1576e2ee28dc4fe30e3cb Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 23 Apr 2018 12:44:22 -0400 Subject: [wallet] [rpc] Add `createwallet` RPC Add a `createwallet` RPC to allow wallets to be created dynamically at runtime. This functionality is currently only available through RPC and newly created wallets will not be displayed in the GUI. --- src/wallet/rpcwallet.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3809eb3dd7..5fb0501c99 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3114,6 +3114,52 @@ UniValue loadwallet(const JSONRPCRequest& request) return obj; } +UniValue createwallet(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "createwallet \"wallet_name\"\n" + "\nCreates and loads a new wallet.\n" + "\nArguments:\n" + "1. \"wallet_name\" (string, required) The name for the new wallet.\n" + "\nResult:\n" + "{\n" + " \"name\" : , (string) The wallet name if created successfully.\n" + " \"warning\" : , (string) Warning message if wallet was not loaded cleanly.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("createwallet", "\"test.dat\"") + + HelpExampleRpc("createwallet", "\"test.dat\"") + ); + std::string wallet_name = request.params[0].get_str(); + std::string error; + std::string warning; + + fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir()); + if (fs::symlink_status(wallet_path).type() != fs::file_not_found) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists."); + } + + // Wallet::Verify will check if we're trying to create a wallet with a duplication name. + if (!CWallet::Verify(wallet_name, false, error, warning)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); + } + + std::shared_ptr const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir())); + if (!wallet) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed."); + } + AddWallet(wallet); + + wallet->postInitProcess(); + + UniValue obj(UniValue::VOBJ); + obj.pushKV("name", wallet->GetName()); + obj.pushKV("warning", warning); + + return obj; +} + static UniValue resendwallettransactions(const JSONRPCRequest& request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); @@ -4315,6 +4361,7 @@ static const CRPCCommand commands[] = { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, + { "wallet", "createwallet", &createwallet, {"filename"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, -- cgit v1.2.3 From 32167e8300894db700e8da2e066e3c0eeb1a293e Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 23 Apr 2018 13:11:53 -0400 Subject: [wallet] [tests] Add tests for `createwallet` RPC. --- test/functional/wallet_multiwallet.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 5671773528..53638615f6 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -211,5 +211,28 @@ class MultiWalletTest(BitcoinTestFramework): # Fail to load if wallet file is a symlink assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink') + self.log.info("Test dynamic wallet creation.") + + # Fail to create a wallet if it already exists. + assert_raises_rpc_error(-4, "Wallet w2 already exists.", self.nodes[0].createwallet, 'w2') + + # Successfully create a wallet with a new name + loadwallet_name = self.nodes[0].createwallet('w9') + assert_equal(loadwallet_name['name'], 'w9') + w9 = node.get_wallet_rpc('w9') + assert_equal(w9.getwalletinfo()['walletname'], 'w9') + + assert 'w9' in self.nodes[0].listwallets() + + # Successfully create a wallet using a full path + new_wallet_dir = os.path.join(self.options.tmpdir, 'new_walletdir') + new_wallet_name = os.path.join(new_wallet_dir, 'w10') + loadwallet_name = self.nodes[0].createwallet(new_wallet_name) + assert_equal(loadwallet_name['name'], new_wallet_name) + w10 = node.get_wallet_rpc(new_wallet_name) + assert_equal(w10.getwalletinfo()['walletname'], new_wallet_name) + + assert new_wallet_name in self.nodes[0].listwallets() + if __name__ == '__main__': MultiWalletTest().main() -- cgit v1.2.3 From f7e153e95f08e662427e8e4abcac8f53ab58281b Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 23 Apr 2018 13:14:34 -0400 Subject: [wallets] [docs] Add release notes for createwallet RPC. --- doc/release-notes-pr10740.md | 11 ++++++----- src/wallet/rpcwallet.cpp | 13 +++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/doc/release-notes-pr10740.md b/doc/release-notes-pr10740.md index f286216b6e..c81ea6a4db 100644 --- a/doc/release-notes-pr10740.md +++ b/doc/release-notes-pr10740.md @@ -1,8 +1,9 @@ -Dynamic loading of wallets --------------------------- +Dynamic loading and creation of wallets +--------------------------------------- -Previously, wallets could only be loaded at startup, by specifying `-wallet` parameters on the command line or in the bitcoin.conf file. It is now possible to load wallets dynamically at runtime by calling the `loadwallet` RPC. +Previously, wallets could only be loaded or created at startup, by specifying `-wallet` parameters on the command line or in the bitcoin.conf file. It is now possible to load and create wallets dynamically at runtime: -The wallet can be specified as file/directory basename (which must be located in the `walletdir` directory), or as an absolute path to a file/directory. +- Existing wallets can be loaded by calling the `loadwallet` RPC. The wallet can be specified as file/directory basename (which must be located in the `walletdir` directory), or as an absolute path to a file/directory. +- New wallets can be created (and loaded) by calling the `createwallet` RPC. The provided name must not match a wallet file in the `walletdir` directory or the name of a wallet that is currently loaded. -This feature is currently only available through the RPC interface. Wallets loaded in this way will display in the bitcoin-qt GUI. +This feature is currently only available through the RPC interface. diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5fb0501c99..456f08bc14 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3116,21 +3116,22 @@ UniValue loadwallet(const JSONRPCRequest& request) UniValue createwallet(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) + if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "createwallet \"wallet_name\"\n" "\nCreates and loads a new wallet.\n" "\nArguments:\n" - "1. \"wallet_name\" (string, required) The name for the new wallet.\n" + "1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n" "\nResult:\n" "{\n" - " \"name\" : , (string) The wallet name if created successfully.\n" + " \"name\" : , (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n" " \"warning\" : , (string) Warning message if wallet was not loaded cleanly.\n" "}\n" "\nExamples:\n" - + HelpExampleCli("createwallet", "\"test.dat\"") - + HelpExampleRpc("createwallet", "\"test.dat\"") + + HelpExampleCli("createwallet", "\"testwallet\"") + + HelpExampleRpc("createwallet", "\"testwallet\"") ); + } std::string wallet_name = request.params[0].get_str(); std::string error; std::string warning; @@ -4361,7 +4362,7 @@ static const CRPCCommand commands[] = { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, - { "wallet", "createwallet", &createwallet, {"filename"} }, + { "wallet", "createwallet", &createwallet, {"wallet_name"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, -- cgit v1.2.3