diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 63e4e9c630b..fdd05de1c7c 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -213,6 +213,12 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& std::string firstLetter = category.substr(0,1); boost::to_upper(firstLetter); strRet += "== " + firstLetter + category.substr(1) + " ==\n"; + if (category == "wallet") { + strRet += "\nWhen more than one wallet is loaded (multiple -`wallet=filename` options passed\n" + "to bitcoind), wallet RPCs must be called with an extra named JSON-RPC `wallet`\n" + "parameter containing the wallet filename to disambiguate which wallet file the\n" + "RPC is intended for. Failure to specify will result in method disabled errors.\n\n"; + } } } strRet += strHelp + "\n"; @@ -472,6 +478,11 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c hole += 1; } } + auto wallet = argsIn.find("wallet"); + if (wallet != argsIn.end() && wallet->second->isStr()) { + out.wallet = wallet->second->getValStr(); + argsIn.erase(wallet); + } // If there are still arguments in the argsIn map, this is an error. if (!argsIn.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first); diff --git a/src/rpc/server.h b/src/rpc/server.h index b20c8277274..48d9cb68b1d 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -42,11 +42,25 @@ class JSONRPCRequest public: UniValue id; std::string strMethod; + /** + * Parameters from JSON-RPC request. + * This will be either an object or an array when the JSONRPCRequest object is + * originally created and parsed. But it will be transformed into an + * array before being passed to the RPC method implementation (using the + * list of named arguments provided by the implementation). + */ UniValue params; bool fHelp; std::string URI; std::string authUser; + /** + * Optional wallet name, set for backwards compatibility if the RPC method + * was called with a named "wallet" parameter and the RPC method + * implementation doesn't handle it itself. + */ + std::string wallet; + JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {} void parse(const UniValue& valRequest); }; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e0c7ab9f0f9..0e6db892b37 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -32,8 +32,15 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request) { - // TODO: Some way to access secondary wallets - return vpwallets.empty() ? nullptr : vpwallets[0]; + if (!request.wallet.empty()) { + for (const auto& wallet : ::vpwallets) { + if (request.wallet == wallet->GetName()) { + return wallet; + } + } + throw JSONRPCError(RPC_INVALID_PARAMETER, "Requested wallet does not exist or is not loaded"); + } + return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet * const pwallet) diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py new file mode 100755 index 00000000000..2269864fd76 --- /dev/null +++ b/test/functional/multiwallet.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the wallet.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +class MultiWalletTest(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 + self.extra_args = [['-wallet=w1', '-wallet=w2','-wallet=w3']] + + def setup_network(self): + self.nodes = self.start_nodes(1, self.options.tmpdir, self.extra_args[:1]) + + def run_test(self): + self.nodes[0].generate(nblocks=1, wallet="w1") + + #check default wallet balance + assert_raises_jsonrpc(-32601, "Method not found (disabled)", self.nodes[0].getwalletinfo) + + #check w1 wallet balance + walletinfo = self.nodes[0].getwalletinfo(wallet="w1") + assert_equal(walletinfo['immature_balance'], 50) + + #check w1 wallet balance + walletinfo = self.nodes[0].getwalletinfo(wallet="w2") + assert_equal(walletinfo['immature_balance'], 0) + + self.nodes[0].generate(nblocks=101, wallet="w1") + assert_equal(self.nodes[0].getbalance(wallet="w1"), 100) + assert_equal(self.nodes[0].getbalance(wallet="w2"), 0) + assert_equal(self.nodes[0].getbalance(wallet="w3"), 0) + + huh=self.nodes[0].getnewaddress(wallet="w2") + self.nodes[0].sendtoaddress(address=huh, amount=1, wallet="w1") + self.nodes[0].generate(nblocks=1, wallet="w1") + assert_equal(self.nodes[0].getbalance(wallet="w2"), 1) + +if __name__ == '__main__': + MultiWalletTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 54f625514bd..a3085ca96e2 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -63,6 +63,7 @@ 'segwit.py', # vv Tests less than 2m vv 'wallet.py', + 'multiwallet.py', 'wallet-accounts.py', 'p2p-segwit.py', 'wallet-dump.py',