aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfanquake <fanquake@gmail.com>2022-07-19 11:19:12 +0100
committerfanquake <fanquake@gmail.com>2022-07-19 11:24:53 +0100
commit47dad42833d535aee6071526bec9df1b2497bad3 (patch)
tree46ac07305f7c3a3dda2ea86762d5e0f73e14f534
parent6d8707b21d3a5b4abb76b773a69f2bfac22bcd93 (diff)
parentfae5ce8795080018875227aee8613677f92e99ce (diff)
downloadbitcoin-47dad42833d535aee6071526bec9df1b2497bad3.tar.xz
Merge bitcoin/bitcoin#25629: univalue: Return more detailed type check error messages
fae5ce8795080018875227aee8613677f92e99ce univalue: Return more detailed type check error messages (MacroFake) fafab147e7ff41ab1b961349f20a364f6bf847d2 move-only: Move UniValue::getInt definition to keep class with definitions only (MacroFake) Pull request description: Print the current type and the expected type ACKs for top commit: aureleoules: ACK fae5ce8795080018875227aee8613677f92e99ce. Tree-SHA512: 4ae720a012ff8245baf5cd7f844f93b946c58feebe62de6dfd84ebc5c8afb988295a94de7c01aef98aaf4c6228f7184ed622f37079c738924617e0f336ac5b6e
-rw-r--r--src/univalue/include/univalue.h30
-rw-r--r--src/univalue/lib/univalue.cpp19
-rw-r--r--src/univalue/lib/univalue_get.cpp19
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py4
-rwxr-xr-xtest/functional/rpc_blockchain.py6
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py2
-rwxr-xr-xtest/functional/rpc_help.py2
-rwxr-xr-xtest/functional/rpc_rawtransaction.py14
-rwxr-xr-xtest/functional/wallet_basic.py2
-rwxr-xr-xtest/functional/wallet_hd.py4
-rwxr-xr-xtest/functional/wallet_multiwallet.py2
11 files changed, 54 insertions, 50 deletions
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index 5cb8268472..dff544f96f 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -106,6 +106,7 @@ private:
std::vector<std::string> keys;
std::vector<UniValue> values;
+ void checkType(const VType& expected) const;
bool findKey(const std::string& key, size_t& retIdx) const;
void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
@@ -116,19 +117,7 @@ public:
const std::vector<std::string>& getKeys() const;
const std::vector<UniValue>& getValues() const;
template <typename Int>
- auto getInt() const
- {
- static_assert(std::is_integral<Int>::value);
- if (typ != VNUM) {
- throw std::runtime_error("JSON value is not an integer as expected");
- }
- Int result;
- const auto [first_nonmatching, error_condition] = std::from_chars(val.data(), val.data() + val.size(), result);
- if (first_nonmatching != val.data() + val.size() || error_condition != std::errc{}) {
- throw std::runtime_error("JSON integer out of range");
- }
- return result;
- }
+ Int getInt() const;
bool get_bool() const;
const std::string& get_str() const;
double get_real() const;
@@ -142,10 +131,23 @@ public:
template <class It>
void UniValue::push_backV(It first, It last)
{
- if (typ != VARR) throw std::runtime_error{"JSON value is not an array as expected"};
+ checkType(VARR);
values.insert(values.end(), first, last);
}
+template <typename Int>
+Int UniValue::getInt() const
+{
+ static_assert(std::is_integral<Int>::value);
+ checkType(VNUM);
+ Int result;
+ const auto [first_nonmatching, error_condition] = std::from_chars(val.data(), val.data() + val.size(), result);
+ if (first_nonmatching != val.data() + val.size() || error_condition != std::errc{}) {
+ throw std::runtime_error("JSON integer out of range");
+ }
+ return result;
+}
+
enum jtokentype {
JTOK_ERR = -1,
JTOK_NONE = 0, // eof
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
index ac6f31a5a9..051bc8eba6 100644
--- a/src/univalue/lib/univalue.cpp
+++ b/src/univalue/lib/univalue.cpp
@@ -110,21 +110,21 @@ bool UniValue::setObject()
void UniValue::push_back(const UniValue& val_)
{
- if (typ != VARR) throw std::runtime_error{"JSON value is not an array as expected"};
+ checkType(VARR);
values.push_back(val_);
}
void UniValue::push_backV(const std::vector<UniValue>& vec)
{
- if (typ != VARR) throw std::runtime_error{"JSON value is not an array as expected"};
+ checkType(VARR);
values.insert(values.end(), vec.begin(), vec.end());
}
void UniValue::__pushKV(const std::string& key, const UniValue& val_)
{
- if (typ != VOBJ) throw std::runtime_error{"JSON value is not an object as expected"};
+ checkType(VOBJ);
keys.push_back(key);
values.push_back(val_);
@@ -132,7 +132,7 @@ void UniValue::__pushKV(const std::string& key, const UniValue& val_)
void UniValue::pushKV(const std::string& key, const UniValue& val_)
{
- if (typ != VOBJ) throw std::runtime_error{"JSON value is not an object as expected"};
+ checkType(VOBJ);
size_t idx;
if (findKey(key, idx))
@@ -143,7 +143,8 @@ void UniValue::pushKV(const std::string& key, const UniValue& val_)
void UniValue::pushKVs(const UniValue& obj)
{
- if (typ != VOBJ || obj.typ != VOBJ) throw std::runtime_error{"JSON value is not an object as expected"};
+ checkType(VOBJ);
+ obj.checkType(VOBJ);
for (size_t i = 0; i < obj.keys.size(); i++)
__pushKV(obj.keys[i], obj.values.at(i));
@@ -213,6 +214,14 @@ const UniValue& UniValue::operator[](size_t index) const
return values.at(index);
}
+void UniValue::checkType(const VType& expected) const
+{
+ if (typ != expected) {
+ throw std::runtime_error{"JSON value of type " + std::string{uvTypeName(typ)} + " is not of expected type " +
+ std::string{uvTypeName(expected)}};
+ }
+}
+
const char *uvTypeName(UniValue::VType t)
{
switch (t) {
diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp
index 9bbdb1fe69..5c58f388dd 100644
--- a/src/univalue/lib/univalue_get.cpp
+++ b/src/univalue/lib/univalue_get.cpp
@@ -46,8 +46,7 @@ bool ParseDouble(const std::string& str, double *out)
const std::vector<std::string>& UniValue::getKeys() const
{
- if (typ != VOBJ)
- throw std::runtime_error("JSON value is not an object as expected");
+ checkType(VOBJ);
return keys;
}
@@ -60,22 +59,19 @@ const std::vector<UniValue>& UniValue::getValues() const
bool UniValue::get_bool() const
{
- if (typ != VBOOL)
- throw std::runtime_error("JSON value is not a boolean as expected");
+ checkType(VBOOL);
return getBool();
}
const std::string& UniValue::get_str() const
{
- if (typ != VSTR)
- throw std::runtime_error("JSON value is not a string as expected");
+ checkType(VSTR);
return getValStr();
}
double UniValue::get_real() const
{
- if (typ != VNUM)
- throw std::runtime_error("JSON value is not a number as expected");
+ checkType(VNUM);
double retval;
if (!ParseDouble(getValStr(), &retval))
throw std::runtime_error("JSON double out of range");
@@ -84,15 +80,12 @@ double UniValue::get_real() const
const UniValue& UniValue::get_obj() const
{
- if (typ != VOBJ)
- throw std::runtime_error("JSON value is not an object as expected");
+ checkType(VOBJ);
return *this;
}
const UniValue& UniValue::get_array() const
{
- if (typ != VARR)
- throw std::runtime_error("JSON value is not an array as expected");
+ checkType(VARR);
return *this;
}
-
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index 64e66ac30a..3b75b2bc2d 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -122,11 +122,11 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# Test `prioritisetransaction` invalid `dummy`
txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000'
- assert_raises_rpc_error(-1, "JSON value is not a number as expected", self.nodes[0].prioritisetransaction, txid, 'foo', 0)
+ assert_raises_rpc_error(-1, "JSON value of type string is not of expected type number", self.nodes[0].prioritisetransaction, txid, 'foo', 0)
assert_raises_rpc_error(-8, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.", self.nodes[0].prioritisetransaction, txid, 1, 0)
# Test `prioritisetransaction` invalid `fee_delta`
- assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo')
+ assert_raises_rpc_error(-1, "JSON value of type string is not of expected type number", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo')
self.test_diamond()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 193bd3f1cd..d07b144905 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -261,12 +261,12 @@ class BlockchainTest(BitcoinTestFramework):
assert_raises_rpc_error(-1, 'getchaintxstats', self.nodes[0].getchaintxstats, 0, '', 0)
# Test `getchaintxstats` invalid `nblocks`
- assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].getchaintxstats, '')
+ assert_raises_rpc_error(-1, "JSON value of type string is not of expected type number", self.nodes[0].getchaintxstats, '')
assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, -1)
assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, self.nodes[0].getblockcount())
# Test `getchaintxstats` invalid `blockhash`
- assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getchaintxstats, blockhash=0)
+ assert_raises_rpc_error(-1, "JSON value of type number is not of expected type string", self.nodes[0].getchaintxstats, blockhash=0)
assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 1, for '0')", self.nodes[0].getchaintxstats, blockhash='0')
assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getchaintxstats, blockhash='ZZZ0000000000000000000000000000000000000000000000000000000000000')
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0000000000000000000000000000000000000000000000000000000000000000')
@@ -536,7 +536,7 @@ class BlockchainTest(BitcoinTestFramework):
datadir = get_datadir_path(self.options.tmpdir, 0)
self.log.info("Test getblock with invalid verbosity type returns proper error message")
- assert_raises_rpc_error(-1, "JSON value is not an integer as expected", node.getblock, blockhash, "2")
+ assert_raises_rpc_error(-1, "JSON value of type string is not of expected type number", node.getblock, blockhash, "2")
def move_block_file(old, new):
old_path = os.path.join(datadir, self.chain, 'blocks', old)
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 948deaaec4..cf9ad3f458 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -301,7 +301,7 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None})
+ assert_raises_rpc_error(-1, "JSON value of type null is not of expected type string", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None})
assert_raises_rpc_error(-5, "Unknown change type ''", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'})
dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index 3b6413d4a6..5b7e724728 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -92,7 +92,7 @@ class HelpRpcTest(BitcoinTestFramework):
assert_raises_rpc_error(-1, 'help', node.help, 'foo', 'bar')
# invalid argument
- assert_raises_rpc_error(-1, 'JSON value is not a string as expected', node.help, 0)
+ assert_raises_rpc_error(-1, "JSON value of type number is not of expected type string", node.help, 0)
# help of unknown command
assert_equal(node.help('foo'), 'help: unknown command: foo')
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index bbf1f022d7..a858292dd4 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -124,13 +124,13 @@ class RawTransactionsTest(BitcoinTestFramework):
# 6. invalid parameters - supply txid and invalid boolean values (strings) for verbose
for value in ["True", "False"]:
- assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txid=txId, verbose=value)
+ assert_raises_rpc_error(-1, "not of expected type bool", self.nodes[n].getrawtransaction, txid=txId, verbose=value)
# 7. invalid parameters - supply txid and empty array
- assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txId, [])
+ assert_raises_rpc_error(-1, "not of expected type bool", self.nodes[n].getrawtransaction, txId, [])
# 8. invalid parameters - supply txid and empty dict
- assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txId, {})
+ assert_raises_rpc_error(-1, "not of expected type bool", self.nodes[n].getrawtransaction, txId, {})
# Make a tx by sending, then generate 2 blocks; block1 has the tx in it
tx = self.wallet.send_self_transfer(from_node=self.nodes[2])['txid']
@@ -152,7 +152,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# We should not get the tx if we provide an unrelated block
assert_raises_rpc_error(-5, "No such transaction found", self.nodes[n].getrawtransaction, txid=tx, blockhash=block2)
# An invalid block hash should raise the correct errors
- assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[n].getrawtransaction, txid=tx, blockhash=True)
+ assert_raises_rpc_error(-1, "JSON value of type bool is not of expected type string", self.nodes[n].getrawtransaction, txid=tx, blockhash=True)
assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[n].getrawtransaction, txid=tx, blockhash="foobar")
assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[n].getrawtransaction, txid=tx, blockhash="abcd1234")
foo = "ZZZ0000000000000000000000000000000000000000000000000000000000000"
@@ -181,8 +181,8 @@ class RawTransactionsTest(BitcoinTestFramework):
# Test `createrawtransaction` invalid `inputs`
assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {})
- assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {})
- assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].createrawtransaction, [{}], {})
+ assert_raises_rpc_error(-1, "JSON value of type string is not of expected type object", self.nodes[0].createrawtransaction, ['foo'], {})
+ assert_raises_rpc_error(-1, "JSON value of type null is not of expected type string", self.nodes[0].createrawtransaction, [{}], {})
assert_raises_rpc_error(-8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {})
txid = "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844"
assert_raises_rpc_error(-8, f"txid must be hexadecimal string (not '{txid}')", self.nodes[0].createrawtransaction, [{'txid': txid}], {})
@@ -207,7 +207,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# Test `createrawtransaction` invalid `outputs`
address = getnewdestination()[2]
- assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo')
+ assert_raises_rpc_error(-1, "JSON value of type string is not of expected type array", self.nodes[0].createrawtransaction, [], 'foo')
self.nodes[0].createrawtransaction(inputs=[], outputs={}) # Should not throw for backwards compatibility
self.nodes[0].createrawtransaction(inputs=[], outputs=[])
assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'})
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index f66fab19ac..9cf1b3d2c4 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -414,7 +414,7 @@ class WalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")
# This will raise an exception since generate does not accept a string
- assert_raises_rpc_error(-1, "not an integer", self.generate, self.nodes[0], "2")
+ assert_raises_rpc_error(-1, "not of expected type number", self.generate, self.nodes[0], "2")
if not self.options.descriptors:
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index ac878ea0aa..686a365584 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -173,8 +173,8 @@ class WalletHDTest(BitcoinTestFramework):
# Sethdseed parameter validity
assert_raises_rpc_error(-1, 'sethdseed', self.nodes[0].sethdseed, False, new_seed, 0)
assert_raises_rpc_error(-5, "Invalid private key", self.nodes[1].sethdseed, False, "not_wif")
- assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", self.nodes[1].sethdseed, "Not_bool")
- assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[1].sethdseed, False, True)
+ assert_raises_rpc_error(-1, "JSON value of type string is not of expected type bool", self.nodes[1].sethdseed, "Not_bool")
+ assert_raises_rpc_error(-1, "JSON value of type bool is not of expected type string", self.nodes[1].sethdseed, False, True)
assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed)
assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress()))
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index dcb82bbbe9..99e472a7b4 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -356,7 +356,7 @@ class MultiWalletTest(BitcoinTestFramework):
self.log.info("Test dynamic wallet unloading")
# Test `unloadwallet` errors
- assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].unloadwallet)
+ assert_raises_rpc_error(-1, "JSON value of type null is not of expected type string", self.nodes[0].unloadwallet)
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", self.nodes[0].unloadwallet, "dummy")
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", node.get_wallet_rpc("dummy").unloadwallet)
assert_raises_rpc_error(-8, "RPC endpoint wallet and wallet_name parameter specify different wallets", w1.unloadwallet, "w2"),