Merge #13058: [wallet] createwallet
RPC - create new wallet at runtime
f7e153e95
[wallets] [docs] Add release notes for createwallet RPC. (John Newbery)32167e830
[wallet] [tests] Add tests for `createwallet` RPC. (John Newbery)942131774
[wallet] [rpc] Add `createwallet` RPC (John Newbery) Pull request description: Adds a `createwallet` RPC to dynamically create a new wallet at runtime. Includes tests and release notes. Tree-SHA512: e0d89e3ae498234e9db5b827c56804cbab64f18a1875e2b5e676172c110278ea1b9e93a8a61b8dd80e2f2a691490bf229e923e4ccb284a1d3e420b8317815866
This commit is contained in:
commit
343d4e44ef
3 changed files with 77 additions and 5 deletions
|
@ -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.
|
||||||
|
|
|
@ -3114,6 +3114,53 @@ UniValue loadwallet(const JSONRPCRequest& request)
|
||||||
return obj;
|
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. If this is a path, the wallet will be created at the path location.\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"name\" : <wallet_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\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
|
||||||
|
"}\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("createwallet", "\"testwallet\"")
|
||||||
|
+ HelpExampleRpc("createwallet", "\"testwallet\"")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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<CWallet> 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)
|
static UniValue resendwallettransactions(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||||
|
@ -4315,6 +4362,7 @@ static const CRPCCommand commands[] =
|
||||||
{ "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
|
{ "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
|
||||||
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
||||||
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
|
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
|
||||||
|
{ "wallet", "createwallet", &createwallet, {"wallet_name"} },
|
||||||
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
||||||
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
|
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
|
||||||
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
|
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
|
||||||
|
|
|
@ -211,5 +211,28 @@ class MultiWalletTest(BitcoinTestFramework):
|
||||||
# Fail to load if wallet file is a symlink
|
# 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')
|
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__':
|
if __name__ == '__main__':
|
||||||
MultiWalletTest().main()
|
MultiWalletTest().main()
|
||||||
|
|
Loading…
Reference in a new issue