Enrich zcash-cli arg conversion

Instead of storing the indices of args to convert from string, store two
`vector<bool>` (per operation), the first containing an entry for each required
parameter (`true` if we should convert it), and the second containing an entry
for each optional parameter.

This allows us to check a few more things on the client side:
- does the operation exist
- have enough arguments been passed
- have too many arguments been passed

This is ostensibly a fix for `zcash-cli` to be able to use `asOfHeight` where
available, but it also caught a few bugs in the old implementation:
- `submitblock` didn’t convert its optional (but ignored) second arg;
- `z_getpaymentdisclosure` docs claimed all the args were strings, but two are
  actually ints;`
- `listreceivedbyaddress` didn’t convert the optional `includeImmatureCodebase`;
- `listsinceblock` didn’t convert the optional `includeRemoved` and
  `includeChange`;`
- `gettransaction` didn’t convert `verbose`;
- `listunspent` didn’t convert `includeUnsafe` or `queryOptions`;
- `z_getbalanceforviewingkey` didn’t convert minconf; and
- a minor non-bug – `z_getbalanceforaddress` had a handler even though the
  operation has been removed.

`getblockdeltas` also incorrectly tries to convert its required string argument,
but correcting that would be a breaking API change. Instead, it is deferred to

Fixes #6429.
This commit is contained in:
Greg Pfeil 2023-02-15 23:46:14 -07:00
parent d1ba9c423e
commit 2435b974ee
No known key found for this signature in database
GPG Key ID: 1193ACD196ED61F2
6 changed files with 300 additions and 120 deletions

View File

@ -340,13 +340,20 @@ int CommandLineRPC(int argc, char *argv[])
if (args.size() < 1)
throw std::runtime_error("too few parameters (need at least command)");
std::string strMethod = args[0];
UniValue params = RPCConvertValues(strMethod, std::vector<std::string>(args.begin()+1, args.end()));
auto params =
RPCConvertValues(strMethod, std::vector<std::string>(args.begin()+1, args.end()));
if (!params.has_value()) {
params.map_error([&](auto failure) {
throw std::runtime_error(FormatConversionFailure(strMethod, failure));
});
}
// Execute and handle connection failures with -rpcwait
const bool fWait = GetBoolArg("-rpcwait", false);
do {
try {
const UniValue reply = CallRPC(strMethod, params);
const UniValue reply = CallRPC(strMethod, params.value());
// Parse reply
const UniValue& result = find_value(reply, "result");

View File

@ -6,6 +6,7 @@
#include "rpc/client.h"
#include "rpc/protocol.h"
#include "util/match.h"
#include "util/system.h"
#include <set>
@ -13,118 +14,245 @@
#include <univalue.h>
typedef std::map<std::string, std::set<int>> CRPCConvertTable;
typedef std::map<std::string, std::pair<std::vector<bool>, std::vector<bool>>> CRPCConvertTable;
/** A string parameter, should not be converted. */
static const bool s = false;
/** Something other than a string parameter, should be converted. */
static const bool o = true;
static const CRPCConvertTable rpcCvtTable =
{
{ "stop", {0} },
{ "setmocktime", {0} },
{ "getaddednodeinfo", {0} },
{ "setgenerate", {0, 1} },
{ "generate", {0} },
{ "getnetworkhashps", {0, 1} },
{ "getnetworksolps", {0, 1} },
{ "sendtoaddress", {1, 4} },
{ "settxfee", {0} },
{ "getreceivedbyaddress", {1, 2} },
{ "listreceivedbyaddress", {0, 1, 2} },
{ "getbalance", {1, 2, 3} },
{ "getblockhash", {0} },
{ "listtransactions", {1, 2, 3} },
{ "walletpassphrase", {1} },
{ "getblocktemplate", {0} },
{ "listsinceblock", {1, 2} },
{ "sendmany", {1, 2, 4} },
{ "addmultisigaddress", {0, 1} },
{ "createmultisig", {0, 1} },
{ "listunspent", {0, 1, 2} },
{ "getblock", {1} },
{ "getblockheader", {1} },
{ "gettransaction", {1} },
{ "getrawtransaction", {1} },
{ "createrawtransaction", {0, 1, 2, 3} },
{ "signrawtransaction", {1, 2} },
{ "sendrawtransaction", {1} },
{ "fundrawtransaction", {1} },
{ "gettxout", {1, 2} },
{ "gettxoutproof", {0} },
{ "lockunspent", {0, 1} },
{ "importprivkey", {2} },
{ "importaddress", {2, 3} },
{ "importpubkey", {2} },
{ "verifychain", {0, 1} },
{ "keypoolrefill", {0} },
{ "getrawmempool", {0} },
{ "estimatefee", {0} },
{ "estimatepriority", {0} },
{ "prioritisetransaction", {1, 2} },
{ "setban", {2, 3} },
{ "getspentinfo", {0} },
{ "getaddresstxids", {0} },
{ "getaddressbalance", {0} },
{ "getaddressdeltas", {0} },
{ "getaddressutxos", {0} },
{ "getaddressmempool", {0} },
{ "getblockhashes", {0, 1, 2} },
{ "getblockdeltas", {0} },
{ "zcbenchmark", {1, 2} },
{ "getblocksubsidy", {0} },
{ "z_listaddresses", {0} },
{ "z_listreceivedbyaddress", {1} },
{ "z_listunspent", {0, 1, 2, 3} },
{ "z_getaddressforaccount", {0, 1, 2} },
{ "z_getbalance", {1, 2} },
{ "z_getbalanceforaccount", {0, 1} },
{ "z_getbalanceforaddress", {1} },
{ "z_gettotalbalance", {0, 1, 2} },
{ "z_mergetoaddress", {0, 2, 3, 4} },
{ "z_sendmany", {1, 2, 3} },
{ "z_shieldcoinbase", {2, 3} },
{ "z_getoperationstatus", {0} },
{ "z_getoperationresult", {0} },
{ "z_importkey", {2} },
{ "z_importviewingkey", {2} },
{ "z_getpaymentdisclosure", {1, 2} },
{ "z_setmigration", {0} },
{ "z_getnotescount", {0} },
// operation {required params, optional params}
// blockchain
{ "getblockcount", {{}, {}} },
{ "getbestblockhash", {{}, {}} },
{ "getdifficulty", {{}, {}} },
{ "getrawmempool", {{}, {o}} },
{ "getblockdeltas", {{o}, {}} },
{ "getblockhashes", {{o, o}, {o}} },
{ "getblockhash", {{o}, {}} },
{ "getblockheader", {{s}, {o}} },
{ "getblock", {{s}, {o}} },
{ "gettxoutsetinfo", {{}, {}} },
{ "gettxout", {{s, o}, {o}} },
{ "verifychain", {{}, {o, o}} },
{ "getblockchaininfo", {{}, {}} },
{ "getchaintips", {{}, {}} },
{ "z_gettreestate", {{s}, {}} },
{ "getmempoolinfo", {{}, {}} },
{ "invalidateblock", {{s}, {}} },
{ "reconsiderblock", {{s}, {}} },
// mining
{ "getlocalsolps", {{}, {}} },
{ "getnetworksolps", {{}, {o, o}} },
{ "getnetworkhashps", {{}, {o, o}} },
{ "getgenerate", {{}, {}} },
{ "generate", {{o}, {}} },
{ "setgenerate", {{o}, {o}} },
{ "getmininginfo", {{}, {}} },
{ "prioritisetransaction", {{s, o, o}, {}} },
{ "getblocktemplate", {{}, {o}} },
// NB: The second argument _should_ be an object, but upstream treats it as a string, so we
// preserve that here.
{ "submitblock", {{s}, {s}} },
{ "estimatefee", {{o}, {}} },
{ "estimatepriority", {{o}, {}} },
{ "getblocksubsidy", {{o}, {}} },
// misc
{ "getinfo", {{}, {}} },
{ "validateaddress", {{s}, {}} },
{ "z_validateaddress", {{s}, {}} },
{ "createmultisig", {{o, o}, {}} },
{ "verifymessage", {{s, s ,s}, {}} },
{ "setmocktime", {{o}, {}} },
{ "getexperimentalfeatures", {{}, {}} },
{ "getaddressmempool", {{o}, {}} },
{ "getaddressutxos", {{o}, {}} },
{ "getaddressdeltas", {{o}, {}} },
{ "getaddressbalance", {{o}, {}} },
{ "getaddresstxids", {{o}, {}} },
{ "getspentinfo", {{o}, {}} },
{ "getmemoryinfo", {{}, {}} },
// net
{ "getconnectioncount", {{}, {}} },
{ "ping", {{}, {}} },
{ "getpeerinfo", {{}, {}} },
{ "addnode", {{s, s}, {}} },
{ "disconnectnode", {{s}, {}} },
{ "getaddednodeinfo", {{o}, {s}} },
{ "getnettotals", {{}, {}} },
{ "getdeprecationinfo", {{}, {}} },
{ "getnetworkinfo", {{}, {}} },
{ "setban", {{s, s}, {o, o}} },
{ "listbanned", {{}, {}} },
{ "clearbanned", {{}, {}} },
// rawtransaction
{ "getrawtransaction", {{s}, {o, s}} },
{ "gettxoutproof", {{o}, {s}} },
{ "verifytxoutproof", {{s}, {}} },
{ "createrawtransaction", {{o, o}, {o, o}} },
{ "decoderawtransaction", {{s}, {}} },
{ "decodescript", {{s}, {}} },
{ "signrawtransaction", {{s}, {o, o, s, s}} },
{ "sendrawtransaction", {{s}, {o}} },
// rpcdisclosure
{ "z_getpaymentdisclosure", {{s, o, o}, {s}} },
{ "z_validatepaymentdisclosure", {{s}, {}} },
// rpcdump
{ "importprivkey", {{s}, {s, o}} },
{ "importaddress", {{s}, {s, o, o}} },
{ "importpubkey", {{s}, {s, o}} },
{ "z_importwallet", {{s}, {}} },
{ "importwallet", {{s}, {}} },
{ "dumpprivkey", {{s}, {}} },
{ "z_exportwallet", {{s}, {}} },
{ "z_importkey", {{s}, {s, o}} },
{ "z_importviewingkey", {{s}, {s, o}} },
{ "z_exportkey", {{s}, {}} },
{ "z_exportviewingkey", {{s}, {}} },
// rpcwallet
{ "getnewaddress", {{}, {s}} },
{ "getrawchangeaddress", {{}, {}} },
{ "sendtoaddress", {{s, o}, {s, s, o}} },
{ "listaddresses", {{}, {}} },
{ "listaddressgroupings", {{}, {o}} },
{ "signmessage", {{s, s}, {}} },
{ "getreceivedbyaddress", {{s}, {o, o, o}} },
{ "getbalance", {{}, {s, o, o, o, o}} },
{ "sendmany", {{s, o}, {o, s, o}} },
{ "addmultisigaddress", {{o, o}, {s}} },
{ "listreceivedbyaddress", {{}, {o, o, o, s, o, o}} },
{ "listtransactions", {{}, {s, o, o, o, o}} },
{ "listsinceblock", {{}, {s, o, o, o, o, o}} },
{ "gettransaction", {{s}, {o, o, o}} },
{ "backupwallet", {{s}, {}} },
{ "keypoolrefill", {{}, {o}} },
{ "walletpassphrase", {{s, o}, {}} },
{ "walletpassphrasechange", {{s, s}, {}} },
{ "walletconfirmbackup", {{s}, {}} },
{ "walletlock", {{}, {}} },
{ "encryptwallet", {{s}, {}} },
{ "lockunspent", {{o, o}, {}} },
{ "listlockunspent", {{}, {}} },
{ "settxfee", {{o}, {}} },
{ "getwalletinfo", {{}, {o}} },
{ "resendwallettransactions", {{}, {}} },
{ "listunspent", {{}, {o, o, o, o, o, o}} },
{ "z_listunspent", {{}, {o, o, o, o, o}} },
{ "fundrawtransaction", {{s}, {o}} },
{ "zcsamplejoinsplit", {{}, {}} },
{ "zcbenchmark", {{s, o}, {o}} },
{ "z_getnewaddress", {{}, {s}} },
{ "z_getnewaccount", {{}, {}} },
{ "z_getaddressforaccount", {{o}, {o, o}} },
{ "z_listaccounts", {{}, {}} },
{ "z_listaddresses", {{}, {o}} },
{ "z_listunifiedreceivers", {{s}, {}} },
{ "z_listreceivedbyaddress", {{s}, {o, o}} },
{ "z_getbalance", {{s}, {o, o}} },
{ "z_getbalanceforviewingkey", {{s}, {o, o}} },
{ "z_getbalanceforaccount", {{o}, {o, o}} },
{ "z_gettotalbalance", {{}, {o, o}} },
{ "z_viewtransaction", {{s}, {}} },
{ "z_getoperationresult", {{}, {o}} },
{ "z_getoperationstatus", {{}, {o}} },
{ "z_sendmany", {{s, o}, {o, o, s}} },
{ "z_setmigration", {{o}, {}} },
{ "z_getmigrationstatus", {{}, {o}} },
{ "z_shieldcoinbase", {{s, s}, {o, o}} },
{ "z_mergetoaddress", {{o, s}, {o, o, o, s}} },
{ "z_listoperationids", {{}, {s}} },
{ "z_getnotescount", {{}, {o, o}} },
// server
{ "help", {{}, {s}} },
{ "setlogfilter", {{s}, {}} },
{ "stop", {{}, {o}} },
};
std::set<int> ParamsToConvertFor(const std::string& method) {
std::string FormatConversionFailure(const std::string& method, const ConversionFailure& failure) {
return std::visit(match {
[&](const UnknownRPCMethod&) {
return tinyformat::format("Unknown RPC method, %s", method);
},
[&](const WrongNumberOfArguments& err) {
return tinyformat::format(
"%s for method, %s. Needed between %u and %u, but received %u",
err.providedArgs < err.requiredParams
? "Not enough arguments"
: "Too many arguments",
method,
err.requiredParams,
err.requiredParams + err.optionalParams,
err.providedArgs);
},
[](const UnparseableArgument& err) {
return std::string("Error parsing JSON:") + err.unparsedArg;
}
}, failure);
}
std::optional<std::pair<std::vector<bool>, std::vector<bool>>>
ParamsToConvertFor(const std::string& method) {
auto search = rpcCvtTable.find(method);
if (search != rpcCvtTable.end()) {
return search->second;
} else {
return std::set<int>();
return std::nullopt;
}
}
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
* as well as objects and arrays.
*/
UniValue ParseNonRFCJSONValue(const std::string& strVal)
std::optional<UniValue> ParseNonRFCJSONValue(const std::string& strVal)
{
UniValue jVal;
if (!jVal.read(std::string("[")+strVal+std::string("]")) ||
!jVal.isArray() || jVal.size()!=1)
throw std::runtime_error(std::string("Error parsing JSON:")+strVal);
return jVal[0];
if (jVal.read(std::string("[")+strVal+std::string("]")) && jVal.isArray() && jVal.size() == 1) {
return jVal[0];
} else {
return std::nullopt;
}
}
/** Convert strings to command-specific RPC representation */
UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
tl::expected<UniValue, ConversionFailure>
RPCConvertValues(const std::string &method, const std::vector<std::string> &strArgs)
{
UniValue params(UniValue::VARR);
auto paramsToConvert = ParamsToConvertFor(method);
std::vector<bool> requiredParams{}, optionalParams{};
if (paramsToConvert.has_value()) {
std::tie(requiredParams, optionalParams) = paramsToConvert.value();
} else {
return tl::expected<UniValue, ConversionFailure>(tl::unexpect, UnknownRPCMethod());
}
auto paramsToConvert = ParamsToConvertFor(strMethod);
for (std::vector<std::string>::size_type idx = 0; idx < strParams.size(); idx++) {
const std::string& strVal = strParams[idx];
if (strArgs.size() < requiredParams.size()
|| requiredParams.size() + optionalParams.size() < strArgs.size()) {
return tl::expected<UniValue, ConversionFailure>(
tl::unexpect,
WrongNumberOfArguments(requiredParams.size(), optionalParams.size(), strArgs.size()));
}
if (paramsToConvert.count(idx) != 0) {
std::vector<bool> allParams(requiredParams.begin(), requiredParams.end());
allParams.reserve(requiredParams.size() + optionalParams.size());
allParams.insert(allParams.end(), optionalParams.begin(), optionalParams.end());
for (std::vector<std::string>::size_type idx = 0; idx < strArgs.size(); idx++) {
const bool shouldConvert = allParams[idx];
const std::string& strVal = strArgs[idx];
if (!shouldConvert) {
// insert string value directly
params.push_back(strVal);
} else {
// parse string as JSON, insert bool/number/object/etc. value
params.push_back(ParseNonRFCJSONValue(strVal));
auto parsedArg = ParseNonRFCJSONValue(strVal);
if (parsedArg.has_value()) {
params.push_back(parsedArg.value());
} else {
return tl::expected<UniValue, ConversionFailure>(
tl::unexpect,
UnparseableArgument(strVal));
}
}
}

View File

@ -7,12 +7,54 @@
#ifndef BITCOIN_RPC_CLIENT_H
#define BITCOIN_RPC_CLIENT_H
#include <tl/expected.hpp>
#include <univalue.h>
UniValue RPCConvertValues(const std::string& strMethod, const std::vector<std::string>& strParams);
class UnknownRPCMethod {
public:
UnknownRPCMethod() {};
};
class WrongNumberOfArguments {
public:
std::vector<bool>::size_type requiredParams;
std::vector<bool>::size_type optionalParams;
std::vector<std::string>::size_type providedArgs;
WrongNumberOfArguments(
std::vector<bool>::size_type requiredParams,
std::vector<bool>::size_type optionalParams,
std::vector<std::string>::size_type providedArgs) :
requiredParams(requiredParams),
optionalParams(optionalParams),
providedArgs(providedArgs) {}
};
class UnparseableArgument {
public:
std::string unparsedArg;
UnparseableArgument(std::string unparsedArg) :
unparsedArg(unparsedArg) {}
};
typedef std::variant<
UnknownRPCMethod,
WrongNumberOfArguments,
UnparseableArgument
> ConversionFailure;
// TODO: This should be closer to the leaves, but dont have a good place for it, since its
// currently shared by bitcoin-cli and tests.
std::string FormatConversionFailure(const std::string& method, const ConversionFailure& failure);
/** Convert strings to command-specific RPC representation */
tl::expected<UniValue, ConversionFailure>
RPCConvertValues(const std::string& method, const std::vector<std::string>& strArgs);
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
* as well as objects and arrays.
* as well as objects and arrays. Returns `std::nullopt` if `strVal` couldnt be parsed.
*/
UniValue ParseNonRFCJSONValue(const std::string& strVal);
std::optional<UniValue> ParseNonRFCJSONValue(const std::string& strVal);
#endif // BITCOIN_RPC_CLIENT_H

View File

@ -166,28 +166,28 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values)
BOOST_AUTO_TEST_CASE(json_parse_errors)
{
// Valid
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0").get_real(), 1.0);
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0").value().get_real(), 1.0);
// Valid, with leading or trailing whitespace
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").get_real(), 1.0);
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").get_real(), 1.0);
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").value().get_real(), 1.0);
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").value().get_real(), 1.0);
BOOST_CHECK_THROW(AmountFromValue(ParseNonRFCJSONValue(".19e-6")), std::runtime_error); //should fail, missing leading 0, therefore invalid JSON
BOOST_CHECK_EQUAL(AmountFromValue(ParseNonRFCJSONValue("0.00000000000000000000000000000000000001e+30 ")), 1);
BOOST_CHECK(!ParseNonRFCJSONValue(".19e-6").has_value()); //should fail, missing leading 0, therefore invalid JSON
BOOST_CHECK_EQUAL(AmountFromValue(ParseNonRFCJSONValue("0.00000000000000000000000000000000000001e+30 ").value()), 1);
// Invalid, initial garbage
BOOST_CHECK_THROW(ParseNonRFCJSONValue("[1.0"), std::runtime_error);
BOOST_CHECK_THROW(ParseNonRFCJSONValue("a1.0"), std::runtime_error);
BOOST_CHECK(!ParseNonRFCJSONValue("[1.0").has_value());
BOOST_CHECK(!ParseNonRFCJSONValue("a1.0").has_value());
// Invalid, trailing garbage
BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0sds"), std::runtime_error);
BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0]"), std::runtime_error);
BOOST_CHECK(!ParseNonRFCJSONValue("1.0sds").has_value());
BOOST_CHECK(!ParseNonRFCJSONValue("1.0]").has_value());
// BTC addresses should fail parsing
BOOST_CHECK_THROW(ParseNonRFCJSONValue("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"), std::runtime_error);
BOOST_CHECK_THROW(ParseNonRFCJSONValue("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL"), std::runtime_error);
BOOST_CHECK(!ParseNonRFCJSONValue("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W").has_value());
BOOST_CHECK(!ParseNonRFCJSONValue("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL").has_value());
}
BOOST_AUTO_TEST_CASE(rpc_ban)
{
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
UniValue r;
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0 add")));
BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.0:8334")), runtime_error); //portnumber for setban not allowed
@ -353,7 +353,7 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer)
const string addr = "t1T3G72ToPuCDTiCEytrU1VUBRHsNupEBut";
BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool \"" + addr + "\""));
BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool {\"addresses\":[\"" + addr + "\"]}"));
BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool {\"addresses\":[\"" + addr + "\",\"" + addr + "\"]}"));
BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool {\"addresses\":[\"" + addr + "\",\"" + addr + "\"]}"));
BOOST_CHECK_NO_THROW(CallRPC("getaddressutxos {\"addresses\":[],\"chainInfo\":true}"));
CheckRPCThrows("getaddressutxos {}",
@ -385,9 +385,9 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer)
// transaction does not exist:
CheckRPCThrows("getspentinfo {\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\",\"index\":0}",
"Unable to get spent info");
"Unable to get spent info");
CheckRPCThrows("getspentinfo {\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\"}",
"Invalid index, must be an integer");
"Invalid index, must be an integer");
CheckRPCThrows("getspentinfo {\"txid\":\"hello\",\"index\":0}",
"txid must be hexadecimal string (not 'hello')");

View File

@ -94,11 +94,15 @@ UniValue CallRPC(std::string args)
vArgs[i] = "";
}
}
UniValue params = RPCConvertValues(strMethod, vArgs);
auto params = RPCConvertValues(strMethod, vArgs);
params.map_error([&](auto failure) {
throw std::runtime_error(FormatConversionFailure(strMethod, failure));
});
rpcfn_type method = tableRPC[strMethod]->actor;
try {
UniValue result = (*method)(params, false);
UniValue result = (*method)(params.value(), false);
return result;
}
catch (const UniValue& objError) {
@ -117,4 +121,3 @@ void CheckRPCThrows(std::string rpcString, std::string expectedErrorMessage) {
BOOST_FAIL(std::string("Unexpected exception: ") + typeid(e).name() + ", message=\"" + e.what() + "\"");
}
}

View File

@ -51,14 +51,14 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp)
if (fHelp || params.size() < 3 || params.size() > 4 )
throw runtime_error(
"z_getpaymentdisclosure \"txid\" \"js_index\" \"output_index\" (\"message\") \n"
"z_getpaymentdisclosure \"txid\" js_index output_index (\"message\") \n"
"\nGenerate a payment disclosure for a given joinsplit output.\n"
"\nEXPERIMENTAL FEATURE\n"
+ disabledMsg +
"\nArguments:\n"
"1. \"txid\" (string, required) \n"
"2. \"js_index\" (string, required) \n"
"3. \"output_index\" (string, required) \n"
"2. js_index (numeric, required) \n"
"3. output_index (numeric, required) \n"
"4. \"message\" (string, optional) \n"
"\nResult:\n"
"\"paymentdisclosure\" (string) Hex data string, with \"zpd:\" prefix.\n"
@ -101,7 +101,7 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp)
// Check if shielded tx
if (wtx.vJoinSplit.empty()) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction");
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction");
}
// Check js_index
@ -195,7 +195,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp)
// too much data is ignored, but if not enough data, exception of type ios_base::failure is thrown,
// CBaseDataStream::read(): end of data: iostream error
} catch (const std::exception &e) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure data is malformed.");
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure data is malformed.");
}
if (pd.payload.marker != PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES) {
@ -221,7 +221,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp)
// Check if shielded tx
if (tx.vJoinSplit.empty()) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction");
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction");
}
UniValue errs(UniValue::VARR);
@ -259,9 +259,9 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp)
dataToBeSigned.begin(), 32);
o.pushKV("signatureVerified", sigVerified);
if (!sigVerified) {
errs.push_back("Payment disclosure signature does not match transaction signature");
errs.push_back("Payment disclosure signature does not match transaction signature");
}
KeyIO keyIO(Params());
// Check the payment address is valid
@ -289,7 +289,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp)
string memoHexString = HexStr(npt.memo().data(), npt.memo().data() + npt.memo().size());
o.pushKV("memo", memoHexString);
o.pushKV("value", ValueFromAmount(npt.value()));
// Check the blockchain commitment matches decrypted note commitment
uint256 cm_blockchain = jsdesc.commitments[pd.payload.n];
SproutNote note = npt.note(zaddr);