Skip to content

Commit fafeddf

Browse files
author
MarcoFalke
committed
rpc: Throw more user friendly arg type check error
1 parent eebc24b commit fafeddf

File tree

3 files changed

+51
-33
lines changed

3 files changed

+51
-33
lines changed

src/rpc/util.cpp

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@ std::string GetAllOutputTypes()
3131
return Join(ret, ", ");
3232
}
3333

34-
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
35-
{
36-
if (!typeExpected.typeAny && value.type() != typeExpected.type) {
37-
throw JSONRPCError(RPC_TYPE_ERROR,
38-
strprintf("JSON value of type %s is not of expected type %s", uvTypeName(value.type()), uvTypeName(typeExpected.type)));
39-
}
40-
}
41-
4234
void RPCTypeCheckObj(const UniValue& o,
4335
const std::map<std::string, UniValueType>& typesExpected,
4436
bool fAllowNull,
@@ -564,8 +556,16 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
564556
if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
565557
throw std::runtime_error(ToString());
566558
}
559+
UniValue arg_mismatch{UniValue::VOBJ};
567560
for (size_t i{0}; i < m_args.size(); ++i) {
568-
m_args.at(i).MatchesType(request.params[i]);
561+
const auto& arg{m_args.at(i)};
562+
UniValue match{arg.MatchesType(request.params[i])};
563+
if (!match.isTrue()) {
564+
arg_mismatch.pushKV(strprintf("Position %s (%s)", i + 1, arg.m_names), std::move(match));
565+
}
566+
}
567+
if (!arg_mismatch.empty()) {
568+
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Wrong type passed:\n%s", arg_mismatch.write(4)));
569569
}
570570
UniValue ret = m_fun(*this, request);
571571
if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {
@@ -684,42 +684,50 @@ UniValue RPCHelpMan::GetArgMap() const
684684
return arr;
685685
}
686686

687-
void RPCArg::MatchesType(const UniValue& request) const
687+
static std::optional<UniValue::VType> ExpectedType(RPCArg::Type type)
688688
{
689-
if (m_opts.skip_type_check) return;
690-
if (IsOptional() && request.isNull()) return;
691-
switch (m_type) {
689+
using Type = RPCArg::Type;
690+
switch (type) {
692691
case Type::STR_HEX:
693692
case Type::STR: {
694-
RPCTypeCheckArgument(request, UniValue::VSTR);
695-
return;
693+
return UniValue::VSTR;
696694
}
697695
case Type::NUM: {
698-
RPCTypeCheckArgument(request, UniValue::VNUM);
699-
return;
696+
return UniValue::VNUM;
700697
}
701698
case Type::AMOUNT: {
702699
// VNUM or VSTR, checked inside AmountFromValue()
703-
return;
700+
return std::nullopt;
704701
}
705702
case Type::RANGE: {
706703
// VNUM or VARR, checked inside ParseRange()
707-
return;
704+
return std::nullopt;
708705
}
709706
case Type::BOOL: {
710-
RPCTypeCheckArgument(request, UniValue::VBOOL);
711-
return;
707+
return UniValue::VBOOL;
712708
}
713709
case Type::OBJ:
714710
case Type::OBJ_USER_KEYS: {
715-
RPCTypeCheckArgument(request, UniValue::VOBJ);
716-
return;
711+
return UniValue::VOBJ;
717712
}
718713
case Type::ARR: {
719-
RPCTypeCheckArgument(request, UniValue::VARR);
720-
return;
714+
return UniValue::VARR;
721715
}
722716
} // no default case, so the compiler can warn about missing cases
717+
NONFATAL_UNREACHABLE();
718+
}
719+
720+
UniValue RPCArg::MatchesType(const UniValue& request) const
721+
{
722+
if (m_opts.skip_type_check) return true;
723+
if (IsOptional() && request.isNull()) return true;
724+
const auto exp_type{ExpectedType(m_type)};
725+
if (!exp_type) return true; // nothing to check
726+
727+
if (*exp_type != request.getType()) {
728+
return strprintf("JSON value of type %s is not of expected type %s", uvTypeName(request.getType()), uvTypeName(*exp_type));
729+
}
730+
return true;
723731
}
724732

725733
std::string RPCArg::GetFirstName() const
@@ -903,7 +911,7 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
903911
NONFATAL_UNREACHABLE();
904912
}
905913

906-
static const std::optional<UniValue::VType> ExpectedType(RPCResult::Type type)
914+
static std::optional<UniValue::VType> ExpectedType(RPCResult::Type type)
907915
{
908916
using Type = RPCResult::Type;
909917
switch (type) {

src/rpc/util.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,6 @@ struct UniValueType {
6262
UniValue::VType type;
6363
};
6464

65-
/**
66-
* Type-check one argument; throws JSONRPCError if wrong type given.
67-
*/
68-
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
69-
7065
/*
7166
Check for expected keys/value types in an Object.
7267
*/
@@ -214,8 +209,11 @@ struct RPCArg {
214209

215210
bool IsOptional() const;
216211

217-
/** Check whether the request JSON type matches. */
218-
void MatchesType(const UniValue& request) const;
212+
/**
213+
* Check whether the request JSON type matches.
214+
* Returns true if type matches, or object describing error(s) if not.
215+
*/
216+
UniValue MatchesType(const UniValue& request) const;
219217

220218
/** Return the first of all aliases */
221219
std::string GetFirstName() const;

test/functional/rpc_blockchain.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import http.client
2626
import os
2727
import subprocess
28+
import textwrap
2829

2930
from test_framework.blocktools import (
3031
MAX_FUTURE_BLOCK_TIME,
@@ -429,6 +430,17 @@ def _test_getdifficulty(self):
429430
def _test_getnetworkhashps(self):
430431
self.log.info("Test getnetworkhashps")
431432
hashes_per_second = self.nodes[0].getnetworkhashps()
433+
assert_raises_rpc_error(
434+
-3,
435+
textwrap.dedent("""
436+
Wrong type passed:
437+
{
438+
"Position 1 (nblocks)": "JSON value of type string is not of expected type number",
439+
"Position 2 (height)": "JSON value of type array is not of expected type number"
440+
}
441+
""").strip(),
442+
lambda: self.nodes[0].getnetworkhashps("a", []),
443+
)
432444
# This should be 2 hashes every 10 minutes or 1/300
433445
assert abs(hashes_per_second * 300 - 1) < 0.0001
434446

0 commit comments

Comments
 (0)