diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -294,7 +294,7 @@ int timeout = 0; - uint256 hash = uint256S(request.params[0].get_str()); + BlockHash hash(ParseHashV(request.params[0], "blockhash")); if (!request.params[1].isNull()) { timeout = request.params[1].get_int(); @@ -820,8 +820,7 @@ "\"")); } - std::string strHash = request.params[0].get_str(); - BlockHash hash(uint256S(strHash)); + BlockHash hash(ParseHashV(request.params[0], "hash")); bool fVerbose = true; if (!request.params[1].isNull()) { @@ -939,8 +938,7 @@ LOCK(cs_main); - std::string strHash = request.params[0].get_str(); - BlockHash hash(uint256S(strHash)); + BlockHash hash(ParseHashV(request.params[0], "blockhash")); int verbosity = 1; if (!request.params[1].isNull()) { @@ -1202,8 +1200,7 @@ UniValue ret(UniValue::VOBJ); - std::string strTxId = request.params[0].get_str(); - TxId txid(uint256S(strTxId)); + TxId txid(ParseHashV(request.params[0], "txid")); int n = request.params[1].get_int(); COutPoint out(txid, n); bool fMempool = true; @@ -1634,8 +1631,7 @@ HelpExampleRpc("preciousblock", "\"blockhash\"")); } - std::string strHash = request.params[0].get_str(); - BlockHash hash(uint256S(strHash)); + BlockHash hash(ParseHashV(request.params[0], "blockhash")); CBlockIndex *pblockindex; { @@ -1712,8 +1708,7 @@ HelpExampleRpc("invalidateblock", "\"blockhash\"")); } - const std::string strHash = request.params[0].get_str(); - const BlockHash hash(uint256S(strHash)); + const BlockHash hash(ParseHashV(request.params[0], "blockhash")); CValidationState state; CBlockIndex *pblockindex; @@ -1793,8 +1788,7 @@ HelpExampleRpc("reconsiderblock", "\"blockhash\"")); } - const std::string strHash = request.params[0].get_str(); - const BlockHash hash(uint256S(strHash)); + const BlockHash hash(ParseHashV(request.params[0], "blockhash")); { LOCK(cs_main); @@ -1902,7 +1896,7 @@ LOCK(cs_main); pindex = chainActive.Tip(); } else { - BlockHash hash(uint256S(request.params[1].get_str())); + BlockHash hash(ParseHashV(request.params[1], "blockhash")); LOCK(cs_main); pindex = LookupBlockIndex(hash); if (!pindex) { @@ -2077,8 +2071,7 @@ pindex = chainActive[height]; } else { - const std::string strHash = request.params[0].get_str(); - const BlockHash hash(uint256S(strHash)); + const BlockHash hash(ParseHashV(request.params[0], "hash_or_height")); pindex = LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -293,7 +293,7 @@ LOCK(cs_main); - TxId txid(ParseHashStr(request.params[0].get_str(), "txid")); + TxId txid(ParseHashV(request.params[0], "txid")); Amount nAmount = request.params[2].get_int64() * SATOSHI; if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) { @@ -533,7 +533,7 @@ // Format: std::string lpstr = lpval.get_str(); - hashWatchedChain.SetHex(lpstr.substr(0, 64)); + hashWatchedChain = ParseHashV(lpstr.substr(0, 64), "longpollid"); nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); } else { // NOTE: Spec does not specify behaviour for non-string longpollid, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -284,12 +284,7 @@ UniValue txids = request.params[0].get_array(); for (unsigned int idx = 0; idx < txids.size(); idx++) { const UniValue &utxid = txids[idx]; - if (utxid.get_str().length() != 64 || !IsHex(utxid.get_str())) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - std::string("Invalid txid ") + utxid.get_str()); - } - - TxId txid(uint256S(utxid.get_str())); + TxId txid(ParseHashV(utxid, "txid")); if (setTxIds.count(txid)) { throw JSONRPCError( RPC_INVALID_PARAMETER, @@ -306,7 +301,7 @@ BlockHash hashBlock; if (!request.params[1].isNull()) { LOCK(cs_main); - hashBlock = BlockHash::fromHex(request.params[1].get_str()); + hashBlock = BlockHash(ParseHashV(request.params[1], "blockhash")); pblockindex = LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -187,27 +187,20 @@ } uint256 ParseHashV(const UniValue &v, std::string strName) { - std::string strHex; - if (v.isStr()) { - strHex = v.get_str(); + std::string strHex(v.get_str()); + if (64 != strHex.length()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, + strHex.length(), strHex)); } - // Note: IsHex("") is false if (!IsHex(strHex)) { throw JSONRPCError(RPC_INVALID_PARAMETER, strName + " must be hexadecimal string (not '" + strHex + "')"); } - - if (strHex.length() != 64) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - strprintf("%s must be of length %d (not %d)", - strName, 64, strHex.length())); - } - - uint256 result; - result.SetHex(strHex); - return result; + return uint256S(strHex); } uint256 ParseHashO(const UniValue &o, std::string strKey) { return ParseHashV(find_value(o, strKey), strKey); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -509,8 +509,7 @@ auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); - TxId txid; - txid.SetHex(request.params[0].get_str()); + TxId txid(ParseHashV(request.params[0], "txid")); std::vector txIds; txIds.push_back(txid); std::vector txIdsOut; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2726,7 +2726,8 @@ isminefilter filter = ISMINE_SPENDABLE; if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { - BlockHash blockId(uint256S(request.params[0].get_str())); + BlockHash blockId(ParseHashV(request.params[0], "blockhash")); + paltindex = pindex = LookupBlockIndex(blockId); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -2908,8 +2909,7 @@ auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); - TxId txid; - txid.SetHex(request.params[0].get_str()); + TxId txid(ParseHashV(request.params[0], "txid")); isminefilter filter = ISMINE_SPENDABLE; if (!request.params[1].isNull() && request.params[1].get_bool()) { @@ -2987,8 +2987,7 @@ auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); - TxId txid; - txid.SetHex(request.params[0].get_str()); + TxId txid(ParseHashV(request.params[0], "txid")); if (!pwallet->mapWallet.count(txid)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, @@ -3493,19 +3492,13 @@ {"vout", UniValueType(UniValue::VNUM)}, }); - const std::string &strTxId = find_value(o, "txid").get_str(); - if (!IsHex(strTxId)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "Invalid parameter, expected hex txid"); - } - const int nOutput = find_value(o, "vout").get_int(); if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); } - const TxId txid(uint256S(strTxId)); + const TxId txid(ParseHashO(o, "txid")); const auto it = pwallet->mapWallet.find(txid); if (it == pwallet->mapWallet.end()) { throw JSONRPCError(RPC_INVALID_PARAMETER, diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -51,11 +51,17 @@ 0) # Test `prioritisetransaction` invalid `txid` - assert_raises_rpc_error(-1, - "txid must be hexadecimal string", + assert_raises_rpc_error(-8, + "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) + assert_raises_rpc_error( + -8, + "txid must be hexadecimal string (not 'Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000')", + self.nodes[0].prioritisetransaction, + txid='Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000', + fee_delta=0) # Test `prioritisetransaction` invalid `dummy` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -328,7 +328,7 @@ # Store the raw block in our internal format. block = FromHex(CBlock(), node.getblock( - "{:02x}".format(block_hash), False)) + "{:064x}".format(block_hash), False)) for tx in block.vtx: tx.calc_sha256() block.rehash() diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py --- a/test/functional/p2p_sendheaders.py +++ b/test/functional/p2p_sendheaders.py @@ -425,7 +425,7 @@ block_time += 9 - fork_point = self.nodes[0].getblock("{:02x}".format( + fork_point = self.nodes[0].getblock("{:064x}".format( new_block_hashes[0]))["previousblockhash"] fork_point = int(fork_point, 16) diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -200,8 +200,20 @@ # 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(-8, + "blockhash must be of length 64 (not 1, for '0')", + self.nodes[0].getchaintxstats, + blockhash='0') assert_raises_rpc_error( - -5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0') + -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') blockhash = self.nodes[0].getblockhash(200) self.nodes[0].invalidateblock(blockhash) assert_raises_rpc_error( @@ -286,8 +298,17 @@ def _test_getblockheader(self): node = self.nodes[0] - assert_raises_rpc_error(-5, "Block not found", - node.getblockheader, "nonsense") + assert_raises_rpc_error(-8, + "hash must be of length 64 (not 8, for 'nonsense')", + node.getblockheader, + "nonsense") + assert_raises_rpc_error( + -8, + "hash must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", + node.getblockheader, + "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844") + assert_raises_rpc_error(-5, "Block not found", node.getblockheader, + "0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844") besthash = node.getbestblockhash() secondbesthash = node.getblockhash(199) diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -102,10 +102,21 @@ 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(-8, "txid must be hexadecimal string", - self.nodes[0].createrawtransaction, [{}], {}) - assert_raises_rpc_error(-8, "txid must be hexadecimal string", - self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {}) + assert_raises_rpc_error(-1, + "JSON value is not a string as expected", + 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'}], + {}) + assert_raises_rpc_error(-8, + "txid must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", + self.nodes[0].createrawtransaction, + [{'txid': 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844'}], + {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout must be a number", @@ -223,12 +234,31 @@ assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) # An invalid block hash should raise the correct errors - assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", - self.nodes[0].getrawtransaction, tx, True, True) - assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", - self.nodes[0].getrawtransaction, tx, True, "foobar") - assert_raises_rpc_error(-8, "parameter 3 must be of length 64", - self.nodes[0].getrawtransaction, tx, True, "abcd1234") + assert_raises_rpc_error(-1, + "JSON value is not a string as expected", + self.nodes[0].getrawtransaction, + tx, + True, + True) + assert_raises_rpc_error(-8, + "parameter 3 must be of length 64 (not 6, for 'foobar')", + self.nodes[0].getrawtransaction, + tx, + True, + "foobar") + assert_raises_rpc_error(-8, + "parameter 3 must be of length 64 (not 8, for 'abcd1234')", + self.nodes[0].getrawtransaction, + tx, + True, + "abcd1234") + assert_raises_rpc_error( + -8, + "parameter 3 must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + self.nodes[0].getrawtransaction, + tx, + True, + "ZZZ0000000000000000000000000000000000000000000000000000000000000") assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") # Undo the blocks and check in_active_chain diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -82,6 +82,32 @@ txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 + # Invalid txids + assert_raises_rpc_error( + -8, + "txid must be of length 64 (not 32, for '00000000000000000000000000000000')", + self.nodes[2].gettxoutproof, + ["00000000000000000000000000000000"], + blockhash) + assert_raises_rpc_error( + -8, + "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + self.nodes[2].gettxoutproof, + ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], + blockhash) + # Invalid blockhashes + assert_raises_rpc_error( + -8, + "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", + self.nodes[2].gettxoutproof, + [txid_spent], + "00000000000000000000000000000000") + assert_raises_rpc_error( + -8, + "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + self.nodes[2].gettxoutproof, + [txid_spent], + "ZZZ0000000000000000000000000000000000000000000000000000000000000") # We can't find the block from a fully-spent tx assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent]) @@ -90,7 +116,7 @@ self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) # We can't get the proof if we specify a non-existent block assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [ - txid_spent], "00000000000000000000000000000000") + txid_spent], "0000000000000000000000000000000000000000000000000000000000000000") # We can get the proof if the transaction is unspent assert_equal(self.nodes[2].verifytxoutproof( self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent]) diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -144,8 +144,15 @@ assert_equal([unspent_0], self.nodes[2].listlockunspent()) self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) - assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", self.nodes[2].lockunspent, False, [ - {"txid": "0000000000000000000000000000000000", "vout": 0}]) + assert_raises_rpc_error(-8, "txid must be of length 64 (not 34, for '0000000000000000000000000000000000')", + self.nodes[2].lockunspent, False, + [{"txid": "0000000000000000000000000000000000", "vout": 0}]) + assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + self.nodes[2].lockunspent, False, + [{"txid": "ZZZ0000000000000000000000000000000000000000000000000000000000000", "vout": 0}]) + assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", + self.nodes[2].lockunspent, False, + [{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0}]) assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds", self.nodes[2].lockunspent, False, [{"txid": unspent_0["txid"], "vout": 999}]) diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -54,8 +54,10 @@ "42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4") assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock, "0000000000000000000000000000000000000000000000000000000000000000") - assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock, + assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 11, for 'invalid-hex')", self.nodes[0].listsinceblock, "invalid-hex") + assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'Z000000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].listsinceblock, + "Z000000000000000000000000000000000000000000000000000000000000000") def test_reorg(self): '''