mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-24 18:23:26 -03:00
Compare commits
8 commits
a63e5a0a82
...
360def914b
Author | SHA1 | Date | |
---|---|---|---|
|
360def914b | ||
|
66aa6a47bd | ||
|
7c123c08dd | ||
|
5d0c836294 | ||
|
e00979167a | ||
|
6cfb73e0e3 | ||
|
1b03a82cff | ||
|
19a4892871 |
11 changed files with 59 additions and 15 deletions
9
doc/release-notes-31353.md
Normal file
9
doc/release-notes-31353.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
Updated RPCs
|
||||
------------
|
||||
|
||||
- RPC getbalances has a new `total` field that provides the sum of all wallet
|
||||
balances returned by the RPC. (#31353)
|
||||
|
||||
- CLI -getinfo now displays wallet balances from RPC getbalances `total` instead
|
||||
of `mine.trusted` in order to include watchonly, reused, untrusted pending, and
|
||||
immature coinbase outputs in the balance shown. (#31353)
|
|
@ -374,7 +374,7 @@ public:
|
|||
result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]);
|
||||
}
|
||||
if (!batch[ID_BALANCES]["result"].isNull()) {
|
||||
result.pushKV("balance", batch[ID_BALANCES]["result"]["mine"]["trusted"]);
|
||||
result.pushKV("balance", batch[ID_BALANCES]["result"]["total"]);
|
||||
}
|
||||
result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]);
|
||||
result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]);
|
||||
|
@ -1007,7 +1007,7 @@ static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
|
|||
|
||||
/**
|
||||
* GetWalletBalances calls listwallets; if more than one wallet is loaded, it then
|
||||
* fetches mine.trusted balances for each loaded wallet and pushes them to `result`.
|
||||
* fetches the total balance for each loaded wallet and pushes it to `result`.
|
||||
*
|
||||
* @param result Reference to UniValue object the wallet names and balances are pushed to.
|
||||
*/
|
||||
|
@ -1023,7 +1023,7 @@ static void GetWalletBalances(UniValue& result)
|
|||
for (const UniValue& wallet : wallets.getValues()) {
|
||||
const std::string& wallet_name = wallet.get_str();
|
||||
const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
|
||||
const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"];
|
||||
const UniValue& balance = getbalances.find_value("result")["total"];
|
||||
balances.pushKV(wallet_name, balance);
|
||||
}
|
||||
result.pushKV("balances", std::move(balances));
|
||||
|
|
|
@ -421,6 +421,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
|||
}
|
||||
|
||||
++nPackagesSelected;
|
||||
pblocktemplate->m_package_feerates.emplace_back(packageFees, static_cast<int32_t>(packageSize));
|
||||
|
||||
// Update transactions that depend on each of these
|
||||
nDescendantsUpdated += UpdatePackagesForAdded(mempool, ancestors, mapModifiedTx);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <primitives/block.h>
|
||||
#include <txmempool.h>
|
||||
#include <util/feefrac.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -39,6 +40,9 @@ struct CBlockTemplate
|
|||
std::vector<CAmount> vTxFees;
|
||||
std::vector<int64_t> vTxSigOpsCost;
|
||||
std::vector<unsigned char> vchCoinbaseCommitment;
|
||||
/* A vector of package fee rates, ordered by the sequence in which
|
||||
* packages are selected for inclusion in the block template.*/
|
||||
std::vector<FeeFrac> m_package_feerates;
|
||||
};
|
||||
|
||||
// Container for tracking updates to ancestor feerate as we include (parent)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <util/check.h>
|
||||
#include <util/feefrac.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/time.h>
|
||||
#include <util/translation.h>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
|
@ -123,19 +125,22 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||
tx.vout[0].nValue = 5000000000LL - 1000;
|
||||
// This tx has a low fee: 1000 satoshis
|
||||
Txid hashParentTx = tx.GetHash(); // save this txid for later use
|
||||
AddToMempool(tx_mempool, entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
||||
const auto parent_tx{entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, parent_tx);
|
||||
|
||||
// This tx has a medium fee: 10000 satoshis
|
||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx.vout[0].nValue = 5000000000LL - 10000;
|
||||
Txid hashMediumFeeTx = tx.GetHash();
|
||||
AddToMempool(tx_mempool, entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
||||
const auto medium_fee_tx{entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, medium_fee_tx);
|
||||
|
||||
// This tx has a high fee, but depends on the first transaction
|
||||
tx.vin[0].prevout.hash = hashParentTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
|
||||
Txid hashHighFeeTx = tx.GetHash();
|
||||
AddToMempool(tx_mempool, entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
|
||||
const auto high_fee_tx{entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, high_fee_tx);
|
||||
|
||||
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
||||
BOOST_REQUIRE(block_template);
|
||||
|
@ -145,6 +150,21 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||
BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx);
|
||||
BOOST_CHECK(block.vtx[3]->GetHash() == hashMediumFeeTx);
|
||||
|
||||
// Test the inclusion of package feerates in the block template and ensure they are sequential.
|
||||
const auto block_package_feerates = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock()->m_package_feerates;
|
||||
BOOST_CHECK(block_package_feerates.size() == 2);
|
||||
|
||||
// parent_tx and high_fee_tx are added to the block as a package.
|
||||
const auto combined_txs_fee = parent_tx.GetFee() + high_fee_tx.GetFee();
|
||||
const auto combined_txs_size = parent_tx.GetTxSize() + high_fee_tx.GetTxSize();
|
||||
FeeFrac package_feefrac{combined_txs_fee, combined_txs_size};
|
||||
// The package should be added first.
|
||||
BOOST_CHECK(block_package_feerates[0] == package_feefrac);
|
||||
|
||||
// The medium_fee_tx should be added next.
|
||||
FeeFrac medium_tx_feefrac{medium_fee_tx.GetFee(), medium_fee_tx.GetTxSize()};
|
||||
BOOST_CHECK(block_package_feerates[1] == medium_tx_feefrac);
|
||||
|
||||
// Test that a package below the block min tx fee doesn't get included
|
||||
tx.vin[0].prevout.hash = hashHighFeeTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
|
||||
|
|
|
@ -314,6 +314,7 @@ Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
|
|||
}
|
||||
ret.m_mine_immature += CachedTxGetImmatureCredit(wallet, wtx, ISMINE_SPENDABLE);
|
||||
ret.m_watchonly_immature += CachedTxGetImmatureCredit(wallet, wtx, ISMINE_WATCH_ONLY);
|
||||
ret.m_total = ret.m_mine_trusted + ret.m_mine_untrusted_pending + ret.m_mine_immature + ret.m_watchonly_trusted + ret.m_watchonly_untrusted_pending + ret.m_watchonly_immature;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
|
|
@ -55,6 +55,7 @@ struct Balance {
|
|||
CAmount m_watchonly_trusted{0};
|
||||
CAmount m_watchonly_untrusted_pending{0};
|
||||
CAmount m_watchonly_immature{0};
|
||||
CAmount m_total{0};
|
||||
};
|
||||
Balance GetBalance(const CWallet& wallet, int min_depth = 0, bool avoid_reuse = true);
|
||||
|
||||
|
|
|
@ -447,6 +447,7 @@ RPCHelpMan getbalances()
|
|||
{RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
|
||||
}},
|
||||
RESULT_LAST_PROCESSED_BLOCK,
|
||||
{RPCResult::Type::STR_AMOUNT, "total", "total of all balances returned by this RPC"},
|
||||
}
|
||||
},
|
||||
RPCExamples{
|
||||
|
@ -466,6 +467,7 @@ RPCHelpMan getbalances()
|
|||
|
||||
const auto bal = GetBalance(wallet);
|
||||
UniValue balances{UniValue::VOBJ};
|
||||
const Balance full_bal{GetBalance(wallet, /*min_depth=*/0, /*avoid_reuse=*/false)};
|
||||
{
|
||||
UniValue balances_mine{UniValue::VOBJ};
|
||||
balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
|
||||
|
@ -474,7 +476,6 @@ RPCHelpMan getbalances()
|
|||
if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
|
||||
// If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
|
||||
// the total balance, and then subtract bal to get the reused address balance.
|
||||
const auto full_bal = GetBalance(wallet, 0, false);
|
||||
balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending));
|
||||
}
|
||||
balances.pushKV("mine", std::move(balances_mine));
|
||||
|
@ -489,6 +490,7 @@ RPCHelpMan getbalances()
|
|||
}
|
||||
|
||||
AppendLastProcessedBlock(balances, wallet);
|
||||
balances.pushKV("total", ValueFromAmount(full_bal.m_total));
|
||||
return balances;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -21,10 +21,9 @@ from test_framework.util import (
|
|||
import time
|
||||
|
||||
# The block reward of coinbaseoutput.nValue (50) BTC/block matures after
|
||||
# COINBASE_MATURITY (100) blocks. Therefore, after mining 101 blocks we expect
|
||||
# node 0 to have a balance of (BLOCKS - COINBASE_MATURITY) * 50 BTC/block.
|
||||
# COINBASE_MATURITY (100) blocks.
|
||||
BLOCKS = COINBASE_MATURITY + 1
|
||||
BALANCE = (BLOCKS - 100) * 50
|
||||
BALANCE = BLOCKS * 50
|
||||
|
||||
JSON_PARSING_ERROR = 'error: Error parsing JSON: foo'
|
||||
BLOCKS_VALUE_OF_ZERO = 'error: the first argument (number of blocks to generate, default: 1) must be an integer value greater than zero'
|
||||
|
@ -222,7 +221,7 @@ class TestBitcoinCli(BitcoinTestFramework):
|
|||
|
||||
# Setup to test -getinfo, -generate, and -rpcwallet= with multiple wallets.
|
||||
wallets = [self.default_wallet_name, 'Encrypted', 'secret']
|
||||
amounts = [BALANCE + Decimal('9.999928'), Decimal(9), Decimal(31)]
|
||||
amounts = [BALANCE, 9, 31]
|
||||
self.nodes[0].createwallet(wallet_name=wallets[1])
|
||||
self.nodes[0].createwallet(wallet_name=wallets[2])
|
||||
w1 = self.nodes[0].get_wallet_rpc(wallets[0])
|
||||
|
@ -234,9 +233,11 @@ class TestBitcoinCli(BitcoinTestFramework):
|
|||
w2.encryptwallet(password)
|
||||
w1.sendtoaddress(w2.getnewaddress(), amounts[1])
|
||||
w1.sendtoaddress(w3.getnewaddress(), amounts[2])
|
||||
amounts[0] -= (amounts[1] + amounts[2])
|
||||
|
||||
# Mine a block to confirm; adds a block reward (50 BTC) to the default wallet.
|
||||
self.generate(self.nodes[0], 1)
|
||||
amounts[0] += 50
|
||||
|
||||
self.log.info("Test -getinfo with multiple wallets and -rpcwallet returns specified wallet balance")
|
||||
for i in range(len(wallets)):
|
||||
|
|
|
@ -59,9 +59,12 @@ def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None
|
|||
|
||||
def assert_balances(node, mine, margin=0.001):
|
||||
'''Make assertions about a node's getbalances output'''
|
||||
got = node.getbalances()["mine"]
|
||||
balances, total = node.getbalances(), 0
|
||||
for k,v in mine.items():
|
||||
assert_approx(got[k], v, margin)
|
||||
assert_approx(balances["mine"][k], v, margin)
|
||||
total += v
|
||||
assert_approx(balances["total"], total, margin)
|
||||
|
||||
|
||||
class AvoidReuseTest(BitcoinTestFramework):
|
||||
def add_options(self, parser):
|
||||
|
|
|
@ -176,10 +176,12 @@ class WalletTest(BitcoinTestFramework):
|
|||
'untrusted_pending': Decimal('60.0')},
|
||||
'watchonly': {'immature': Decimal('5000'),
|
||||
'trusted': Decimal('50.0'),
|
||||
'untrusted_pending': Decimal('0E-8')}}
|
||||
'untrusted_pending': Decimal('0E-8')},
|
||||
'total': Decimal('69.99' if self.options.descriptors else '5119.99')}
|
||||
expected_balances_1 = {'mine': {'immature': Decimal('0E-8'),
|
||||
'trusted': Decimal('0E-8'), # node 1's send had an unsafe input
|
||||
'untrusted_pending': Decimal('30.0') - fee_node_1}} # Doesn't include output of node 0's send since it was spent
|
||||
'untrusted_pending': Decimal('30.0') - fee_node_1}, # Doesn't include output of node 0's send since it was spent
|
||||
'total': Decimal('30.0') - fee_node_1}
|
||||
if self.options.descriptors:
|
||||
del expected_balances_0["watchonly"]
|
||||
balances_0 = self.nodes[0].getbalances()
|
||||
|
|
Loading…
Add table
Reference in a new issue