Merge #8952: Add query options to listunspent RPC call

bc63d0e Add query options to listunspent rpc call (Pedro Branco)

Tree-SHA512: 2d296eee8df4e7ac378206ac3003a300e6478502d4b814f1ed1a47614222b01cc35dba871345ced68629860c227aff2c9e4b7f0d4ed0aa7de8b04f26c983580f
Signed-off-by: Pasta <pasta@dashboost.org>

# Conflicts:
#	src/rpc/client.cpp
#	src/wallet/rpcwallet.cpp
#	src/wallet/wallet.cpp
#	src/wallet/wallet.h
This commit is contained in:
Wladimir J. van der Laan 2017-05-17 09:08:04 +02:00 committed by Pasta
parent e7d7ecb6e9
commit 30ad4b8e8c
4 changed files with 78 additions and 17 deletions

View File

@ -100,6 +100,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listunspent", 1, "maxconf" }, { "listunspent", 1, "maxconf" },
{ "listunspent", 2, "addresses" }, { "listunspent", 2, "addresses" },
{ "listunspent", 3, "include_unsafe" }, { "listunspent", 3, "include_unsafe" },
{ "listunspent", 4, "query_options" },
{ "getblock", 1, "verbosity" }, { "getblock", 1, "verbosity" },
{ "getblockheader", 1, "verbose" }, { "getblockheader", 1, "verbose" },
{ "getblockheaders", 1, "count" }, { "getblockheaders", 1, "count" },

View File

@ -2668,9 +2668,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"
@ -2684,6 +2684,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"
@ -2709,6 +2716,8 @@ UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "")
+ HelpExampleCli("listunspent", "6 9999999 \"[\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\",\\\"XuQQkwA4FYkq2XERzMY2CiAZhJTEDAbtcg\\\"]\"") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\",\\\"XuQQkwA4FYkq2XERzMY2CiAZhJTEDAbtcg\\\"]\"")
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\",\\\"XuQQkwA4FYkq2XERzMY2CiAZhJTEDAbtcg\\\"]\"") + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\",\\\"XuQQkwA4FYkq2XERzMY2CiAZhJTEDAbtcg\\\"]\"")
+ HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
+ HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
); );
int nMinDepth = 1; int nMinDepth = 1;
@ -2744,15 +2753,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);
@ -3024,7 +3052,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","addlocked","include_empty","include_watchonly"} }, { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","addlocked","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","addlocked","comment","comment_to"} }, { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","addlocked","comment","comment_to"} },

View File

@ -2540,7 +2540,7 @@ 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, AvailableCoinsType nCoinType, bool fUseInstantSend) 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, AvailableCoinsType nCoinType, bool fUseInstantSend) const
{ {
vCoins.clear(); vCoins.clear();
@ -2548,6 +2548,8 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
int nInstantSendConfirmationsRequired = Params().GetConsensus().nInstantSendConfirmationsRequired; int nInstantSendConfirmationsRequired = Params().GetConsensus().nInstantSendConfirmationsRequired;
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;
@ -2575,6 +2577,9 @@ 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++) {
bool found = false; bool found = false;
if(nCoinType == ONLY_DENOMINATED) { if(nCoinType == ONLY_DENOMINATED) {
@ -2591,15 +2596,42 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
} }
if(!found) continue; if(!found) continue;
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) || nCoinType == ONLY_1000)
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) || nCoinType == ONLY_1000) && if (mine == ISMINE_NO) {
(pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && continue;
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) }
vCoins.push_back(COutput(pcoin, i, nDepth,
((mine & ISMINE_SPENDABLE) != ISMINE_NO) || bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO);
(coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO;
(mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO, safeTx));
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

@ -885,7 +885,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, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = 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, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = false) const;
/** /**
* Shuffle and select coins until nTargetValue is reached while avoiding * Shuffle and select coins until nTargetValue is reached while avoiding