Merge #8952: Add query options to listunspent RPC call
bc63d0e
Add query options to listunspent rpc call (Pedro Branco)
Tree-SHA512: 2d296eee8df4e7ac378206ac3003a300e6478502d4b814f1ed1a47614222b01cc35dba871345ced68629860c227aff2c9e4b7f0d4ed0aa7de8b04f26c983580f
This commit is contained in:
commit
9390845c53
4 changed files with 81 additions and 18 deletions
|
@ -77,6 +77,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "listunspent", 0, "minconf" },
|
{ "listunspent", 0, "minconf" },
|
||||||
{ "listunspent", 1, "maxconf" },
|
{ "listunspent", 1, "maxconf" },
|
||||||
{ "listunspent", 2, "addresses" },
|
{ "listunspent", 2, "addresses" },
|
||||||
|
{ "listunspent", 4, "query_options" },
|
||||||
{ "getblock", 1, "verbosity" },
|
{ "getblock", 1, "verbosity" },
|
||||||
{ "getblockheader", 1, "verbose" },
|
{ "getblockheader", 1, "verbose" },
|
||||||
{ "getchaintxstats", 0, "nblocks" },
|
{ "getchaintxstats", 0, "nblocks" },
|
||||||
|
|
|
@ -2470,9 +2470,9 @@ UniValue listunspent(const JSONRPCRequest& request)
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.fHelp || request.params.size() > 4)
|
if (request.fHelp || request.params.size() > 5)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n"
|
"listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] [query_options])\n"
|
||||||
"\nReturns array of unspent transaction outputs\n"
|
"\nReturns array of unspent transaction outputs\n"
|
||||||
"with between minconf and maxconf (inclusive) confirmations.\n"
|
"with between minconf and maxconf (inclusive) confirmations.\n"
|
||||||
"Optionally filter to only include txouts paid to specified addresses.\n"
|
"Optionally filter to only include txouts paid to specified addresses.\n"
|
||||||
|
@ -2486,6 +2486,13 @@ UniValue listunspent(const JSONRPCRequest& request)
|
||||||
" ]\n"
|
" ]\n"
|
||||||
"4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
|
"4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
|
||||||
" See description of \"safe\" attribute below.\n"
|
" See description of \"safe\" attribute below.\n"
|
||||||
|
"5. query_options (json, optional) JSON with query options\n"
|
||||||
|
" {\n"
|
||||||
|
" \"minimumAmount\" (numeric or string, default=0) Minimum value of each UTXO in " + CURRENCY_UNIT + "\n"
|
||||||
|
" \"maximumAmount\" (numeric or string, default=unlimited) Maximum value of each UTXO in " + CURRENCY_UNIT + "\n"
|
||||||
|
" \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n"
|
||||||
|
" \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n"
|
||||||
|
" }\n"
|
||||||
"\nResult\n"
|
"\nResult\n"
|
||||||
"[ (array of json object)\n"
|
"[ (array of json object)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
|
@ -2510,6 +2517,8 @@ UniValue listunspent(const JSONRPCRequest& request)
|
||||||
+ HelpExampleCli("listunspent", "")
|
+ HelpExampleCli("listunspent", "")
|
||||||
+ HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
|
+ HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
|
||||||
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
|
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
|
||||||
|
+ HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
|
||||||
|
+ HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
|
||||||
);
|
);
|
||||||
|
|
||||||
int nMinDepth = 1;
|
int nMinDepth = 1;
|
||||||
|
@ -2545,15 +2554,34 @@ UniValue listunspent(const JSONRPCRequest& request)
|
||||||
include_unsafe = request.params[3].get_bool();
|
include_unsafe = request.params[3].get_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CAmount nMinimumAmount = 0;
|
||||||
|
CAmount nMaximumAmount = MAX_MONEY;
|
||||||
|
CAmount nMinimumSumAmount = MAX_MONEY;
|
||||||
|
uint64_t nMaximumCount = 0;
|
||||||
|
|
||||||
|
if (request.params.size() > 4) {
|
||||||
|
const UniValue& options = request.params[4].get_obj();
|
||||||
|
|
||||||
|
if (options.exists("minimumAmount"))
|
||||||
|
nMinimumAmount = AmountFromValue(options["minimumAmount"]);
|
||||||
|
|
||||||
|
if (options.exists("maximumAmount"))
|
||||||
|
nMaximumAmount = AmountFromValue(options["maximumAmount"]);
|
||||||
|
|
||||||
|
if (options.exists("minimumSumAmount"))
|
||||||
|
nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
|
||||||
|
|
||||||
|
if (options.exists("maximumCount"))
|
||||||
|
nMaximumCount = options["maximumCount"].get_int64();
|
||||||
|
}
|
||||||
|
|
||||||
UniValue results(UniValue::VARR);
|
UniValue results(UniValue::VARR);
|
||||||
std::vector<COutput> vecOutputs;
|
std::vector<COutput> vecOutputs;
|
||||||
assert(pwallet != NULL);
|
assert(pwallet != NULL);
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
pwallet->AvailableCoins(vecOutputs, !include_unsafe, NULL, true);
|
|
||||||
BOOST_FOREACH(const COutput& out, vecOutputs) {
|
|
||||||
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
pwallet->AvailableCoins(vecOutputs, !include_unsafe, NULL, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
|
||||||
|
BOOST_FOREACH(const COutput& out, vecOutputs) {
|
||||||
CTxDestination address;
|
CTxDestination address;
|
||||||
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
|
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
|
||||||
bool fValidAddress = ExtractDestination(scriptPubKey, address);
|
bool fValidAddress = ExtractDestination(scriptPubKey, address);
|
||||||
|
@ -2936,7 +2964,7 @@ static const CRPCCommand commands[] =
|
||||||
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} },
|
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} },
|
||||||
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
|
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
|
||||||
{ "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} },
|
{ "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} },
|
||||||
{ "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe"} },
|
{ "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
|
||||||
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
|
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
|
||||||
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} },
|
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} },
|
||||||
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
|
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
|
||||||
|
|
|
@ -1977,12 +1977,15 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons
|
||||||
return balance;
|
return balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const
|
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const
|
||||||
{
|
{
|
||||||
vCoins.clear();
|
vCoins.clear();
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, cs_wallet);
|
LOCK2(cs_main, cs_wallet);
|
||||||
|
|
||||||
|
CAmount nTotal = 0;
|
||||||
|
|
||||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||||||
{
|
{
|
||||||
const uint256& wtxid = it->first;
|
const uint256& wtxid = it->first;
|
||||||
|
@ -2040,15 +2043,46 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nDepth < nMinDepth || nDepth > nMaxDepth)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
|
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
|
||||||
|
if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IsLockedCoin((*it).first, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IsSpent(wtxid, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
isminetype mine = IsMine(pcoin->tx->vout[i]);
|
isminetype mine = IsMine(pcoin->tx->vout[i]);
|
||||||
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
|
|
||||||
!IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) &&
|
if (mine == ISMINE_NO) {
|
||||||
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i))))
|
continue;
|
||||||
vCoins.push_back(COutput(pcoin, i, nDepth,
|
}
|
||||||
((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
|
|
||||||
(coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO),
|
bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO);
|
||||||
(mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO, safeTx));
|
bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO;
|
||||||
|
|
||||||
|
vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx));
|
||||||
|
|
||||||
|
// Checks the sum amount of all UTXO's.
|
||||||
|
if (nMinimumSumAmount != MAX_MONEY) {
|
||||||
|
nTotal += pcoin->tx->vout[i].nValue;
|
||||||
|
|
||||||
|
if (nTotal >= nMinimumSumAmount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the maximum number of UTXO's.
|
||||||
|
if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -818,7 +818,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* populate vCoins with vector of available COutputs.
|
* populate vCoins with vector of available COutputs.
|
||||||
*/
|
*/
|
||||||
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const;
|
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 9999999) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuffle and select coins until nTargetValue is reached while avoiding
|
* Shuffle and select coins until nTargetValue is reached while avoiding
|
||||||
|
|
Loading…
Reference in a new issue