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:
Wladimir J. van der Laan 2017-05-17 09:08:04 +02:00
commit 9390845c53
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
4 changed files with 81 additions and 18 deletions

View file

@ -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" },

View file

@ -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"} },

View file

@ -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;
}
} }
} }
} }

View file

@ -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