Merge #15930: rpc: Add balances RPC

facfb4111d14a3b06c46690a2cca7ca91cea8a96 rpc: Deprecate getunconfirmedbalance and getwalletinfo balances (MarcoFalke)
999931cf8f167c7547f1015cdf05437a460c27f0 rpc: Add getbalances RPC (MarcoFalke)
fad13e925e197163a942f3f0d1ba2c95a2b65a56 rpcwallet: Make helper methods const on CWallet (MarcoFalke)
fad40ec9151248c6e8225e14980424f581d23e02 wallet: Use IsValidNumArgs in getwalletinfo rpc (MarcoFalke)

Pull request description:

  This exposes the `CWallet::GetBalance()` struct over RPC.

  In the future, incorrectly named rpcs such as `getunconfirmedbalance` or rpcs redundant to this such as `getbalance` could be removed.

ACKs for commit facfb4:
  jnewbery:
    utACK facfb4111d14a3b06c46690a2cca7ca91cea8a96

Tree-SHA512: 1f54fedce55df9a8ea82d2b6265354b39a956072621876ebaee2355aac0e23c7b64340c3279502415598c095858529e18b50789be956250aafda1cd3a8d948a5
This commit is contained in:
Wladimir J. van der Laan 2019-05-06 10:12:20 +02:00 committed by PastaPastaPasta
parent ad68327f38
commit 078094b2aa
3 changed files with 86 additions and 10 deletions

View File

@ -1,5 +1,5 @@
// Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers // Copyright (c) 2009-2019 The Bitcoin Core developers
// Copyright (c) 2014-2022 The Dash Core developers // Copyright (c) 2014-2022 The Dash Core developers
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -20,10 +20,10 @@
#include <util/bip32.h> #include <util/bip32.h>
#include <util/fees.h> #include <util/fees.h>
#include <util/message.h> // For MessageSign() #include <util/message.h> // For MessageSign()
#include <util/system.h>
#include <util/moneystr.h> #include <util/moneystr.h>
#include <util/ref.h> #include <util/ref.h>
#include <util/string.h> #include <util/string.h>
#include <util/system.h>
#include <util/translation.h> #include <util/translation.h>
#include <util/url.h> #include <util/url.h>
#include <util/vector.h> #include <util/vector.h>
@ -123,7 +123,7 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path)."); "Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
} }
void EnsureWalletIsUnlocked(CWallet * const pwallet) void EnsureWalletIsUnlocked(const CWallet* pwallet)
{ {
if (pwallet->IsLocked()) { if (pwallet->IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
@ -801,7 +801,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
static UniValue getunconfirmedbalance(const JSONRPCRequest &request) static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
{ {
RPCHelpMan{"getunconfirmedbalance", RPCHelpMan{"getunconfirmedbalance",
"Returns the server's total unconfirmed balance\n", "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{}, {},
RPCResult{RPCResult::Type::NUM, "", "The balance"}, RPCResult{RPCResult::Type::NUM, "", "The balance"},
RPCExamples{""}, RPCExamples{""},
@ -2381,6 +2381,68 @@ static UniValue setcoinjoinamount(const JSONRPCRequest& request)
return NullUniValue; return NullUniValue;
} }
static UniValue getbalances(const JSONRPCRequest& request)
{
RPCHelpMan{"getbalances",
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
{},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
{
{RPCResult::Type::STR_AMOUNT, "trusted", " trusted balance (outputs created by the wallet or confirmed outputs)"},
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", " untrusted pending balance (outputs created by others that are in the mempool)"},
{RPCResult::Type::STR_AMOUNT, "immature", " balance from immature coinbase outputs"},
{RPCResult::Type::STR_AMOUNT, "coinjoin", " CoinJoin balance (outputs with enough rounds created by the wallet via mixing)"},
}},
{RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
{
{RPCResult::Type::STR_AMOUNT, "trusted", " trusted balance (outputs created by the wallet or confirmed outputs)"},
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", " untrusted pending balance (outputs created by others that are in the mempool)"},
{RPCResult::Type::STR_AMOUNT, "immature", " balance from immature coinbase outputs"},
}},
},
},
RPCExamples{
HelpExampleCli("getbalances", "") +
HelpExampleRpc("getbalances", "")
},
}.Check(request);
std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
if (!rpc_wallet) return NullUniValue;
CWallet& wallet = *rpc_wallet;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
LOCK(wallet.cs_wallet);
UniValue obj(UniValue::VOBJ);
const auto bal = wallet.GetBalance();
UniValue balances{UniValue::VOBJ};
{
UniValue balances_mine{UniValue::VOBJ};
balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
balances_mine.pushKV("coinjoin", ValueFromAmount(bal.m_anonymized));
balances.pushKV("mine", balances_mine);
}
auto spk_man = wallet.GetLegacyScriptPubKeyMan();
if (spk_man && spk_man->HaveWatchOnly()) {
UniValue balances_watchonly{UniValue::VOBJ};
balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
balances.pushKV("watchonly", balances_watchonly);
}
return balances;
}
static UniValue getwalletinfo(const JSONRPCRequest& request) static UniValue getwalletinfo(const JSONRPCRequest& request)
{ {
RPCHelpMan{"getwalletinfo", RPCHelpMan{"getwalletinfo",
@ -2391,10 +2453,10 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
{ {
{RPCResult::Type::STR, "walletname", "the wallet name"}, {RPCResult::Type::STR, "walletname", "the wallet name"},
{RPCResult::Type::NUM, "walletversion", "the wallet version"}, {RPCResult::Type::NUM, "walletversion", "the wallet version"},
{RPCResult::Type::NUM, "balance", "the total confirmed balance of the wallet in " + CURRENCY_UNIT}, {RPCResult::Type::NUM, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
{RPCResult::Type::NUM, "coinjoin_balance", "the CoinJoin balance in " + CURRENCY_UNIT}, {RPCResult::Type::NUM, "coinjoin_balance", "DEPRECATED. Identical to getbalances().mine.coinjoin"},
{RPCResult::Type::NUM, "unconfirmed_balance", "the total unconfirmed balance of the wallet in " + CURRENCY_UNIT}, {RPCResult::Type::NUM, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
{RPCResult::Type::NUM, "immature_balance", "the total immature balance of the wallet in " + CURRENCY_UNIT}, {RPCResult::Type::NUM, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
{RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"}, {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
{RPCResult::Type::NUM_TIME, "timefirstkey", "the " + UNIX_EPOCH_TIME + " of the oldest known key in the wallet"}, {RPCResult::Type::NUM_TIME, "timefirstkey", "the " + UNIX_EPOCH_TIME + " of the oldest known key in the wallet"},
{RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool"}, {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool"},
@ -3985,6 +4047,7 @@ static const CRPCCommand commands[] =
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf","addlocked"} }, { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf","addlocked"} },
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
{ "wallet", "getbalances", &getbalances, {} },
{ "wallet", "getwalletinfo", &getwalletinfo, {} }, { "wallet", "getwalletinfo", &getwalletinfo, {} },
{ "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} },
{ "wallet", "importelectrumwallet", &importelectrumwallet, {"filename", "index"} }, { "wallet", "importelectrumwallet", &importelectrumwallet, {"filename", "index"} },

View File

@ -35,7 +35,7 @@ Span<const CRPCCommand> GetWalletRPCCommands();
*/ */
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request); std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
void EnsureWalletIsUnlocked(CWallet *); void EnsureWalletIsUnlocked(const CWallet*);
WalletContext& EnsureWalletContext(const util::Ref& context); WalletContext& EnsureWalletContext(const util::Ref& context);
UniValue getaddressinfo(const JSONRPCRequest& request); UniValue getaddressinfo(const JSONRPCRequest& request);

View File

@ -62,14 +62,24 @@ class WalletTest(BitcoinTestFramework):
assert_equal(len(self.nodes[0].listunspent()), 0) assert_equal(len(self.nodes[0].listunspent()), 0)
assert_equal(len(self.nodes[1].listunspent()), 0) assert_equal(len(self.nodes[1].listunspent()), 0)
self.log.info("Mining blocks ...") self.log.info("Check that only node 0 is watching an address")
assert 'watchonly' in self.nodes[0].getbalances()
assert 'watchonly' not in self.nodes[1].getbalances()
self.log.info("Mining blocks ...")
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all() self.sync_all()
self.nodes[1].generate(1) self.nodes[1].generate(1)
self.nodes[1].generatetoaddress(101, ADDRESS_WATCHONLY) self.nodes[1].generatetoaddress(101, ADDRESS_WATCHONLY)
self.sync_all() self.sync_all()
assert_equal(self.nodes[0].getbalances()['mine']['trusted'], 500)
assert_equal(self.nodes[0].getwalletinfo()['balance'], 500)
assert_equal(self.nodes[1].getbalances()['mine']['trusted'], 500)
assert_equal(self.nodes[0].getbalances()['watchonly']['immature'], 50000)
assert 'watchonly' not in self.nodes[1].getbalances()
assert_equal(self.nodes[0].getbalance(), 500) assert_equal(self.nodes[0].getbalance(), 500)
assert_equal(self.nodes[1].getbalance(), 500) assert_equal(self.nodes[1].getbalance(), 500)
@ -113,8 +123,11 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(minconf=1), Decimal('0')) assert_equal(self.nodes[1].getbalance(minconf=1), Decimal('0'))
# getunconfirmedbalance # getunconfirmedbalance
assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('960')) # output of node 1's spend assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('960')) # output of node 1's spend
assert_equal(self.nodes[0].getbalances()['mine']['untrusted_pending'], Decimal('960'))
assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], Decimal('960')) assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], Decimal('960'))
assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) # Doesn't include output of node 0's send since it was spent assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) # Doesn't include output of node 0's send since it was spent
assert_equal(self.nodes[1].getbalances()['mine']['untrusted_pending'], Decimal('0'))
assert_equal(self.nodes[1].getwalletinfo()["unconfirmed_balance"], Decimal('0')) assert_equal(self.nodes[1].getwalletinfo()["unconfirmed_balance"], Decimal('0'))
test_balances(fee_node_1=Decimal('0.01')) test_balances(fee_node_1=Decimal('0.01'))