Skip to content

Commit 858a64f

Browse files
jonasschnelliPastaPastaPasta
authored andcommitted
Merge bitcoin#13058: [wallet] createwallet RPC - create new wallet at runtime
f7e153e [wallets] [docs] Add release notes for createwallet RPC. (John Newbery) 32167e8 [wallet] [tests] Add tests for `createwallet` RPC. (John Newbery) 9421317 [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
1 parent 160061d commit 858a64f

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

src/wallet/rpcwallet.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2962,6 +2962,53 @@ UniValue loadwallet(const JSONRPCRequest& request)
29622962
return obj;
29632963
}
29642964

2965+
UniValue createwallet(const JSONRPCRequest& request)
2966+
{
2967+
if (request.fHelp || request.params.size() != 1) {
2968+
throw std::runtime_error(
2969+
"createwallet \"wallet_name\"\n"
2970+
"\nCreates and loads a new wallet.\n"
2971+
"\nArguments:\n"
2972+
"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"
2973+
"\nResult:\n"
2974+
"{\n"
2975+
" \"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"
2976+
" \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
2977+
"}\n"
2978+
"\nExamples:\n"
2979+
+ HelpExampleCli("createwallet", "\"testwallet\"")
2980+
+ HelpExampleRpc("createwallet", "\"testwallet\"")
2981+
);
2982+
}
2983+
std::string wallet_name = request.params[0].get_str();
2984+
std::string error;
2985+
std::string warning;
2986+
2987+
fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
2988+
if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
2989+
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists.");
2990+
}
2991+
2992+
// Wallet::Verify will check if we're trying to create a wallet with a duplication name.
2993+
if (!CWallet::Verify(wallet_name, false, error, warning)) {
2994+
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
2995+
}
2996+
2997+
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()));
2998+
if (!wallet) {
2999+
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
3000+
}
3001+
AddWallet(wallet);
3002+
3003+
wallet->postInitProcess();
3004+
3005+
UniValue obj(UniValue::VOBJ);
3006+
obj.pushKV("name", wallet->GetName());
3007+
obj.pushKV("warning", warning);
3008+
3009+
return obj;
3010+
}
3011+
29653012
UniValue resendwallettransactions(const JSONRPCRequest& request)
29663013
{
29673014
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
@@ -3593,6 +3640,7 @@ static const CRPCCommand commands[] =
35933640
{ "wallet", "abortrescan", &abortrescan, {} },
35943641
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} },
35953642
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
3643+
{ "wallet", "createwallet", &createwallet, {"wallet_name"} },
35963644
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
35973645
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
35983646
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },

test/functional/wallet_multiwallet.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,29 @@ def run_test(self):
205205
# Fail to load if wallet file is a symlink
206206
assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink')
207207

208+
self.log.info("Test dynamic wallet creation.")
209+
210+
# Fail to create a wallet if it already exists.
211+
assert_raises_rpc_error(-4, "Wallet w2 already exists.", self.nodes[0].createwallet, 'w2')
212+
213+
# Successfully create a wallet with a new name
214+
loadwallet_name = self.nodes[0].createwallet('w9')
215+
assert_equal(loadwallet_name['name'], 'w9')
216+
w9 = node.get_wallet_rpc('w9')
217+
assert_equal(w9.getwalletinfo()['walletname'], 'w9')
218+
219+
assert 'w9' in self.nodes[0].listwallets()
220+
221+
# Successfully create a wallet using a full path
222+
new_wallet_dir = os.path.join(self.options.tmpdir, 'new_walletdir')
223+
new_wallet_name = os.path.join(new_wallet_dir, 'w10')
224+
loadwallet_name = self.nodes[0].createwallet(new_wallet_name)
225+
assert_equal(loadwallet_name['name'], new_wallet_name)
226+
w10 = node.get_wallet_rpc(new_wallet_name)
227+
assert_equal(w10.getwalletinfo()['walletname'], new_wallet_name)
228+
229+
assert new_wallet_name in self.nodes[0].listwallets()
230+
208231
# Fail to load if a directory is specified that doesn't contain a wallet
209232
os.mkdir(wallet_dir('empty_wallet_dir'))
210233
assert_raises_rpc_error(-18, "Directory empty_wallet_dir does not contain a wallet.dat file", self.nodes[0].loadwallet, 'empty_wallet_dir')

0 commit comments

Comments
 (0)