mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-20 14:05:23 +01:00
RPC: listunspent, add "include immature coinbase" flag
so we can return the immature coinbase UTXOs as well.
This commit is contained in:
parent
4f270d2b63
commit
f0f6a3577b
5 changed files with 32 additions and 7 deletions
6
doc/release-notes-25730.md
Normal file
6
doc/release-notes-25730.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
RPC Wallet
|
||||
----------
|
||||
|
||||
- RPC `listunspent` now has a new argument `include_immature_coinbase`
|
||||
to include coinbase UTXOs that don't meet the minimum spendability
|
||||
depth requirement (which before were silently skipped). (#25730)
|
|
@ -515,6 +515,7 @@ RPCHelpMan listunspent()
|
|||
{"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
|
||||
{"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
|
||||
{"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
|
||||
{"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase UTXOs"}
|
||||
},
|
||||
RPCArgOptions{.oneline_description="query_options"}},
|
||||
},
|
||||
|
@ -594,6 +595,7 @@ RPCHelpMan listunspent()
|
|||
CAmount nMaximumAmount = MAX_MONEY;
|
||||
CAmount nMinimumSumAmount = MAX_MONEY;
|
||||
uint64_t nMaximumCount = 0;
|
||||
bool include_immature_coinbase{false};
|
||||
|
||||
if (!request.params[4].isNull()) {
|
||||
const UniValue& options = request.params[4].get_obj();
|
||||
|
@ -604,6 +606,7 @@ RPCHelpMan listunspent()
|
|||
{"maximumAmount", UniValueType()},
|
||||
{"minimumSumAmount", UniValueType()},
|
||||
{"maximumCount", UniValueType(UniValue::VNUM)},
|
||||
{"include_immature_coinbase", UniValueType(UniValue::VBOOL)}
|
||||
},
|
||||
true, true);
|
||||
|
||||
|
@ -618,6 +621,10 @@ RPCHelpMan listunspent()
|
|||
|
||||
if (options.exists("maximumCount"))
|
||||
nMaximumCount = options["maximumCount"].getInt<int64_t>();
|
||||
|
||||
if (options.exists("include_immature_coinbase")) {
|
||||
include_immature_coinbase = options["include_immature_coinbase"].get_bool();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the results are valid at least up to the most recent block
|
||||
|
@ -633,7 +640,7 @@ RPCHelpMan listunspent()
|
|||
cctl.m_max_depth = nMaxDepth;
|
||||
cctl.m_include_unsafe_inputs = include_unsafe;
|
||||
LOCK(pwallet->cs_wallet);
|
||||
vecOutputs = AvailableCoinsListUnspent(*pwallet, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount).All();
|
||||
vecOutputs = AvailableCoinsListUnspent(*pwallet, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, include_immature_coinbase).All();
|
||||
}
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
|
|
@ -195,7 +195,8 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
|||
const CAmount& nMaximumAmount,
|
||||
const CAmount& nMinimumSumAmount,
|
||||
const uint64_t nMaximumCount,
|
||||
bool only_spendable)
|
||||
bool only_spendable,
|
||||
bool include_immature_coinbase)
|
||||
{
|
||||
AssertLockHeld(wallet.cs_wallet);
|
||||
|
||||
|
@ -213,7 +214,7 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
|||
const uint256& wtxid = entry.first;
|
||||
const CWalletTx& wtx = entry.second;
|
||||
|
||||
if (wallet.IsTxImmatureCoinBase(wtx))
|
||||
if (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)
|
||||
continue;
|
||||
|
||||
int nDepth = wallet.GetTxDepthInMainChain(wtx);
|
||||
|
@ -344,9 +345,9 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
|||
return result;
|
||||
}
|
||||
|
||||
CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
|
||||
CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount, bool include_immature_coinbase)
|
||||
{
|
||||
return AvailableCoins(wallet, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, /*only_spendable=*/false);
|
||||
return AvailableCoins(wallet, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, /*only_spendable=*/false, include_immature_coinbase);
|
||||
}
|
||||
|
||||
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
|
||||
|
|
|
@ -65,13 +65,14 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
|||
const CAmount& nMaximumAmount = MAX_MONEY,
|
||||
const CAmount& nMinimumSumAmount = MAX_MONEY,
|
||||
const uint64_t nMaximumCount = 0,
|
||||
bool only_spendable = true) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
bool only_spendable = true,
|
||||
bool include_immature_coinbase = false) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
|
||||
/**
|
||||
* Wrapper function for AvailableCoins which skips the `feerate` parameter. Use this function
|
||||
* to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction.
|
||||
*/
|
||||
CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, bool include_immature_coinbase = false) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
|
||||
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);
|
||||
|
||||
|
|
|
@ -77,8 +77,18 @@ class WalletTest(BitcoinTestFramework):
|
|||
self.log.info("Mining blocks ...")
|
||||
self.generate(self.nodes[0], 1)
|
||||
self.generate(self.nodes[1], 1)
|
||||
|
||||
# Verify listunspent returns immature coinbase if 'include_immature_coinbase' is set
|
||||
assert_equal(len(self.nodes[0].listunspent(query_options={'include_immature_coinbase': True})), 1)
|
||||
assert_equal(len(self.nodes[0].listunspent(query_options={'include_immature_coinbase': False})), 0)
|
||||
|
||||
self.generatetoaddress(self.nodes[1], COINBASE_MATURITY + 1, ADDRESS_WATCHONLY)
|
||||
|
||||
# Verify listunspent returns all immature coinbases if 'include_immature_coinbase' is set
|
||||
# For now, only the legacy wallet will see the coinbases going to the imported 'ADDRESS_WATCHONLY'
|
||||
assert_equal(len(self.nodes[0].listunspent(query_options={'include_immature_coinbase': False})), 1 if self.options.descriptors else 2)
|
||||
assert_equal(len(self.nodes[0].listunspent(query_options={'include_immature_coinbase': True})), 1 if self.options.descriptors else COINBASE_MATURITY + 2)
|
||||
|
||||
if not self.options.descriptors:
|
||||
# Tests legacy watchonly behavior which is not present (and does not need to be tested) in descriptor wallets
|
||||
assert_equal(self.nodes[0].getbalances()['mine']['trusted'], 50)
|
||||
|
|
Loading…
Add table
Reference in a new issue