aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/rpcwallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r--src/wallet/rpcwallet.cpp164
1 files changed, 86 insertions, 78 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 3b7a5d2736..d24d92a178 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -40,6 +40,7 @@
using interfaces::FoundBlock;
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
+static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
@@ -79,9 +80,9 @@ bool HaveKey(const SigningProvider& wallet, const CKey& key)
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
{
- if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
+ if (URL_DECODE && request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
// wallet endpoint was used
- wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
+ wallet_name = URL_DECODE(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
return true;
}
return false;
@@ -100,13 +101,6 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr;
}
-std::string HelpRequiringPassphrase(const CWallet* pwallet)
-{
- return pwallet && pwallet->IsCrypted()
- ? "\nRequires wallet passphrase to be set with walletpassphrase call."
- : "";
-}
-
bool EnsureWalletIsAvailable(const CWallet* pwallet, bool avoidException)
{
if (pwallet) return true;
@@ -370,7 +364,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
- HelpRequiringPassphrase(pwallet) + "\n",
+ HELP_REQUIRING_PASSPHRASE,
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to send to."},
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
@@ -506,8 +500,9 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
addressInfo.push_back(EncodeDestination(address));
addressInfo.push_back(ValueFromAmount(balances[address]));
{
- if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) {
- addressInfo.push_back(pwallet->mapAddressBook.find(address)->second.name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
+ if (address_book_entry) {
+ addressInfo.push_back(address_book_entry->GetLabel());
}
}
jsonGrouping.push_back(addressInfo);
@@ -528,7 +523,7 @@ static UniValue signmessage(const JSONRPCRequest& request)
RPCHelpMan{"signmessage",
"\nSign a message with the private key of an address" +
- HelpRequiringPassphrase(pwallet) + "\n",
+ HELP_REQUIRING_PASSPHRASE,
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the private key."},
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
@@ -738,9 +733,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
},
RPCExamples{
- "\nThe total amount in the wallet with 1 or more confirmations\n"
+ "\nThe total amount in the wallet with 0 or more confirmations\n"
+ HelpExampleCli("getbalance", "") +
- "\nThe total amount in the wallet at least 6 blocks confirmed\n"
+ "\nThe total amount in the wallet with at least 6 confirmations\n"
+ HelpExampleCli("getbalance", "\"*\" 6") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getbalance", "\"*\", 6")
@@ -811,7 +806,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
RPCHelpMan{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
- HelpRequiringPassphrase(pwallet) + "\n",
+ HELP_REQUIRING_PASSPHRASE,
{
{"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
{"amounts", RPCArg::Type::OBJ, RPCArg::Optional::NO, "The addresses and amounts",
@@ -1099,13 +1094,13 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWalle
UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> label_tally;
- // Create mapAddressBook iterator
+ // Create m_address_book iterator
// If we aren't filtering, go from begin() to end()
- auto start = pwallet->mapAddressBook.begin();
- auto end = pwallet->mapAddressBook.end();
+ auto start = pwallet->m_address_book.begin();
+ auto end = pwallet->m_address_book.end();
// If we are filtering, find() the applicable entry
if (has_filtered_address) {
- start = pwallet->mapAddressBook.find(filtered_address);
+ start = pwallet->m_address_book.find(filtered_address);
if (start != end) {
end = std::next(start);
}
@@ -1113,8 +1108,9 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWalle
for (auto item_it = start; item_it != end; ++item_it)
{
+ if (item_it->second.IsChange()) continue;
const CTxDestination& address = item_it->first;
- const std::string& label = item_it->second.name;
+ const std::string& label = item_it->second.GetLabel();
auto it = mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty)
continue;
@@ -1314,8 +1310,9 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
MaybePushAddress(entry, s.destination);
entry.pushKV("category", "send");
entry.pushKV("amount", ValueFromAmount(-s.amount));
- if (pwallet->mapAddressBook.count(s.destination)) {
- entry.pushKV("label", pwallet->mapAddressBook.at(s.destination).name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(s.destination);
+ if (address_book_entry) {
+ entry.pushKV("label", address_book_entry->GetLabel());
}
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
@@ -1331,8 +1328,9 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
for (const COutputEntry& r : listReceived)
{
std::string label;
- if (pwallet->mapAddressBook.count(r.destination)) {
- label = pwallet->mapAddressBook.at(r.destination).name;
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(r.destination);
+ if (address_book_entry) {
+ label = address_book_entry->GetLabel();
}
if (filter_label && label != *filter_label) {
continue;
@@ -1356,7 +1354,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
entry.pushKV("category", "receive");
}
entry.pushKV("amount", ValueFromAmount(r.amount));
- if (pwallet->mapAddressBook.count(r.destination)) {
+ if (address_book_entry) {
entry.pushKV("label", label);
}
entry.pushKV("vout", r.vout);
@@ -1852,7 +1850,7 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
RPCHelpMan{"keypoolrefill",
"\nFills the keypool."+
- HelpRequiringPassphrase(pwallet) + "\n",
+ HELP_REQUIRING_PASSPHRASE,
{
{"newsize", RPCArg::Type::NUM, /* default */ "100", "The new keypool size"},
},
@@ -1919,44 +1917,52 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
- LOCK(pwallet->cs_wallet);
-
- if (!pwallet->IsCrypted()) {
- throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
- }
+ int64_t nSleepTime;
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
- SecureString strWalletPass;
- strWalletPass.reserve(100);
- // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make request.params[0] mlock()'d to begin with.
- strWalletPass = request.params[0].get_str().c_str();
+ if (!pwallet->IsCrypted()) {
+ throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
+ }
- // Get the timeout
- int64_t nSleepTime = request.params[1].get_int64();
- // Timeout cannot be negative, otherwise it will relock immediately
- if (nSleepTime < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
- }
- // Clamp timeout
- constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
- if (nSleepTime > MAX_SLEEP_TIME) {
- nSleepTime = MAX_SLEEP_TIME;
- }
+ // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
+ SecureString strWalletPass;
+ strWalletPass.reserve(100);
+ // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
+ // Alternately, find a way to make request.params[0] mlock()'d to begin with.
+ strWalletPass = request.params[0].get_str().c_str();
+
+ // Get the timeout
+ nSleepTime = request.params[1].get_int64();
+ // Timeout cannot be negative, otherwise it will relock immediately
+ if (nSleepTime < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
+ }
+ // Clamp timeout
+ constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
+ if (nSleepTime > MAX_SLEEP_TIME) {
+ nSleepTime = MAX_SLEEP_TIME;
+ }
- if (strWalletPass.empty()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
- }
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
- if (!pwallet->Unlock(strWalletPass)) {
- throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
- }
+ if (!pwallet->Unlock(strWalletPass)) {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
+ }
- pwallet->TopUpKeyPool();
+ pwallet->TopUpKeyPool();
- pwallet->nRelockTime = GetTime() + nSleepTime;
+ pwallet->nRelockTime = GetTime() + nSleepTime;
+ }
+ // rpcRunLater must be called without cs_wallet held otherwise a deadlock
+ // can occur. The deadlock would happen when RPCRunLater removes the
+ // previous timer (and waits for the callback to finish if already running)
+ // and the callback locks cs_wallet.
+ AssertLockNotHeld(wallet->cs_wallet);
// Keep a weak pointer to the wallet so that it is possible to unload the
// wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded.
@@ -2956,9 +2962,9 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (fValidAddress) {
entry.pushKV("address", EncodeDestination(address));
- auto i = pwallet->mapAddressBook.find(address);
- if (i != pwallet->mapAddressBook.end()) {
- entry.pushKV("label", i->second.name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
+ if (address_book_entry) {
+ entry.pushKV("label", address_book_entry->GetLabel());
}
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
@@ -3246,7 +3252,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain." +
- HelpRequiringPassphrase(pwallet) + "\n",
+ HELP_REQUIRING_PASSPHRASE,
{
{"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
{"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The previous dependent transaction outputs",
@@ -3696,7 +3702,7 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
{
UniValue ret(UniValue::VOBJ);
if (verbose) {
- ret.pushKV("name", data.name);
+ ret.pushKV("name", data.GetLabel());
}
ret.pushKV("purpose", data.purpose);
return ret;
@@ -3806,8 +3812,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// DEPRECATED: Return label field if existing. Currently only one label can
// be associated with an address, so the label should be equivalent to the
// value of the name key/value pair in the labels array below.
- if ((pwallet->chain().rpcEnableDeprecated("label")) && (pwallet->mapAddressBook.count(dest))) {
- ret.pushKV("label", pwallet->mapAddressBook.at(dest).name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
+ if (pwallet->chain().rpcEnableDeprecated("label") && address_book_entry) {
+ ret.pushKV("label", address_book_entry->GetLabel());
}
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
@@ -3830,14 +3837,13 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// stable if we allow multiple labels to be associated with an address in
// the future.
UniValue labels(UniValue::VARR);
- std::map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(dest);
- if (mi != pwallet->mapAddressBook.end()) {
+ if (address_book_entry) {
// DEPRECATED: The previous behavior of returning an array containing a
// JSON object of `name` and `purpose` key/value pairs is deprecated.
if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
- labels.push_back(AddressBookDataToJSON(mi->second, true));
+ labels.push_back(AddressBookDataToJSON(*address_book_entry, true));
} else {
- labels.push_back(mi->second.name);
+ labels.push_back(address_book_entry->GetLabel());
}
}
ret.pushKV("labels", std::move(labels));
@@ -3881,10 +3887,11 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
// Find all addresses that have the given label
UniValue ret(UniValue::VOBJ);
std::set<std::string> addresses;
- for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
- if (item.second.name == label) {
+ for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->m_address_book) {
+ if (item.second.IsChange()) continue;
+ if (item.second.GetLabel() == label) {
std::string address = EncodeDestination(item.first);
- // CWallet::mapAddressBook is not expected to contain duplicate
+ // CWallet::m_address_book is not expected to contain duplicate
// address strings, but build a separate set as a precaution just in
// case it does.
bool unique = addresses.emplace(address).second;
@@ -3945,9 +3952,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
// Add to a set to sort by label name, then insert into Univalue array
std::set<std::string> label_set;
- for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->mapAddressBook) {
+ for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->m_address_book) {
+ if (entry.second.IsChange()) continue;
if (purpose.empty() || entry.second.purpose == purpose) {
- label_set.insert(entry.second.name);
+ label_set.insert(entry.second.GetLabel());
}
}
@@ -3972,7 +3980,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
- HelpRequiringPassphrase(pwallet) + "\n",
+ HELP_REQUIRING_PASSPHRASE,
{
{"newkeypool", RPCArg::Type::BOOL, /* default */ "true", "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
" If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
@@ -4049,7 +4057,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
"that we can sign for." +
- HelpRequiringPassphrase(pwallet) + "\n",
+ HELP_REQUIRING_PASSPHRASE,
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
{"sign", RPCArg::Type::BOOL, /* default */ "true", "Also sign the transaction when updating"},
@@ -4240,6 +4248,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request);
UniValue removeprunedfunds(const JSONRPCRequest& request);
UniValue importmulti(const JSONRPCRequest& request);
+void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
+{
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -4303,8 +4313,6 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
-{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
handlers.emplace_back(chain.handleRpc(commands[vcidx]));
}