From 052f97563f8bf2f0da6401cb3eff706bf6061f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 12 Jul 2017 15:35:10 +0100 Subject: [PATCH 1/2] Handle null values in RPCTypeCheckArgument --- src/rpc/server.cpp | 8 +++++++- src/rpc/server.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 63e4e9c630b..99afabcd83d 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -74,11 +74,17 @@ void RPCTypeCheck(const UniValue& params, } } -void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected) +bool RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected) { + if (value.isNull()) { + return false; + } + if (value.type() != typeExpected) { throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected), uvTypeName(value.type()))); } + + return true; } void RPCTypeCheckObj(const UniValue& o, diff --git a/src/rpc/server.h b/src/rpc/server.h index b20c8277274..ea2635e2849 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -75,7 +75,7 @@ void RPCTypeCheck(const UniValue& params, /** * Type-check one argument; throws JSONRPCError if wrong type given. */ -void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected); +bool RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected); /* Check for expected keys/value types in an Object. From b680b5b9cea69ace96c0472bcf3ed7cec098eedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 12 Jul 2017 16:29:59 +0100 Subject: [PATCH 2/2] [WIP] Add histunspent RPC --- src/rpc/client.cpp | 1 + src/wallet/rpcwallet.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 775ad4b6c9a..a394d687e52 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -77,6 +77,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "addmultisigaddress", 1, "keys" }, { "createmultisig", 0, "nrequired" }, { "createmultisig", 1, "keys" }, + { "histunspent", 0, "ranges" }, { "listunspent", 0, "minconf" }, { "listunspent", 1, "maxconf" }, { "listunspent", 2, "addresses" }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5f72e3b6f59..df9f487fff3 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2664,6 +2664,100 @@ UniValue listunspent(const JSONRPCRequest& request) return results; } +UniValue histunspent(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() > 5) + throw std::runtime_error( + "histunspent [ranges] ( min_depth max_depth )\n" + "\nReturns an histogram of unspent transaction output amounts\n" + "with between min_depth and max_depth (inclusive) confirmations.\n" + "\nArguments:\n" + "1. ranges (array) A json array of amounts that define the histogram bins\n" + "2. min_depth (numeric, optional, default=1) The minimum confirmations to filter\n" + "3. max_depth (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "\nResult\n" + "{\n" + " \"bins\" : \"[]\", (array) A json array of counts of amounts in each bin\n" + " \"ranges\" : \"[]\", (array) A json array of amounts that define the histogram bins\n" + "}\n" + + "\nExamples\n" + + HelpExampleCli("histunspent", "\"[0,1,10,100,1000,10000]\"") + ); + + std::vector bins; + std::map ranges; + CAmount min_amount = 0; + CAmount max_amount = MAX_MONEY; + + if (!request.params[0].isNull()) { + const std::vector& values = request.params[0].getValues(); + + if (values.size() < 2) { + // throw invalid range (minimum 2 amounts); + } + + CAmount last = 0; + for (unsigned int bin = 0; bin <= values.size()-1; ++bin) { + CAmount amount = AmountFromValue(values[bin]); + if (amount <= last) { + // throw invalid range (not sorted); + } + printf(" -- %" PRIu64 " -> %u\n", amount, bin); + histogram.ranges.insert(std::make_pair(amount, bin)); + } + + min_amount = histogram.ranges.begin()->first; + max_amount = histogram.ranges.rbegin()->first; + } + + int min_depth = 1; + if (RPCTypeCheckArgument(request.params[1], UniValue::VNUM)) { + min_depth = request.params[0].get_int(); + } + + int max_depth = 9999999; + if (RPCTypeCheckArgument(request.params[2], UniValue::VNUM)) { + max_depth = request.params[1].get_int(); + } + + assert(pwallet != NULL); + LOCK2(cs_main, pwallet->cs_wallet); + + std::vector outputs; + pwallet->AvailableCoins(outputs, false, NULL, min_amount, max_amount, MAX_MONEY, 0, min_depth, max_depth); + + histogram.bins.resize(histogram.ranges.size()-1, 0); + for (const COutput& out : outputs) { + CAmount amount = out.tx->tx->vout[out.i].nValue; + + std::map::const_iterator it = --histogram.ranges.lower_bound(amount); + if (it != histogram.ranges.end()) { + histogram.bins[it->second]++; + } + } + + UniValue bins(UniValue::VARR); + for (unsigned int bin : histogram.bins) { + bins.push_back(UniValue(bin)); + } + + UniValue ranges(UniValue::VARR); + for (const auto& range : histogram.ranges) { + ranges.push_back(ValueFromAmount(range.first)); + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("bins", bins)); + result.push_back(Pair("ranges", ranges)); + return result; +} + UniValue fundrawtransaction(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -3090,6 +3184,7 @@ static const CRPCCommand commands[] = { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, + { "wallet", "histunspent", &histunspent, false, {"ranges","min_depth","max_depth","include_unsafe"} }, { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },