Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rest: Reject truncated hex txid early in getutxos parsing #30482

Merged
merged 7 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: Replace ParseHashStr with FromHex
No need to have two functions with different names that achieve the
exact same thing.
  • Loading branch information
MarcoFalke committed Jul 24, 2024
commit fa7b57e5f5a6dafbbadc361ffd27b58afff1ed59
12 changes: 6 additions & 6 deletions src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
throw std::runtime_error("TX input missing separator");

// extract and validate TXID
uint256 txid;
if (!ParseHashStr(vStrInputParts[0], txid)) {
auto txid{Txid::FromHex(vStrInputParts[0])};
if (!txid) {
throw std::runtime_error("invalid TX input txid");
}

Expand All @@ -285,7 +285,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
}

// append to transaction input list
CTxIn txin(Txid::FromUint256(txid), vout, CScript(), nSequenceIn);
CTxIn txin(*txid, vout, CScript(), nSequenceIn);
maflcko marked this conversation as resolved.
Show resolved Hide resolved
tx.vin.push_back(txin);
}

Expand Down Expand Up @@ -625,16 +625,16 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (!prevOut.checkObject(types))
throw std::runtime_error("prevtxs internal object typecheck fail");

uint256 txid;
if (!ParseHashStr(prevOut["txid"].get_str(), txid)) {
auto txid{Txid::FromHex(prevOut["txid"].get_str())};
if (!txid) {
throw std::runtime_error("txid must be hexadecimal string (not '" + prevOut["txid"].get_str() + "')");
}

const int nOut = prevOut["vout"].getInt<int>();
if (nOut < 0)
throw std::runtime_error("vout cannot be negative");

COutPoint out(Txid::FromUint256(txid), nOut);
COutPoint out(*txid, nOut);
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());

Expand Down
9 changes: 0 additions & 9 deletions src/core_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,6 @@ std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDeco
[[nodiscard]] bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);

/**
* Parse a hex string into 256 bits
* @param[in] strHex a hex-formatted, 64-character string
* @param[out] result the result of the parsing
* @returns true if successful, false if not
*
* @see ParseHashV for an RPC-oriented version of this
*/
bool ParseHashStr(const std::string& strHex, uint256& result);
[[nodiscard]] util::Result<int> SighashFromStr(const std::string& sighash);

// core_write.cpp
Expand Down
9 changes: 0 additions & 9 deletions src/core_read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,15 +234,6 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
return true;
}

bool ParseHashStr(const std::string& strHex, uint256& result)
{
if ((strHex.size() != 64) || !IsHex(strHex))
return false;

result.SetHexDeprecated(strHex);
return true;
}

util::Result<int> SighashFromStr(const std::string& sighash)
{
static const std::map<std::string, int> map_sighash_values = {
Expand Down
39 changes: 21 additions & 18 deletions src/rest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,10 @@ static bool rest_headers(const std::any& context,
return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
}

uint256 hash;
if (!ParseHashStr(hashStr, hash))
auto hash{uint256::FromHex(hashStr)};
if (!hash) {
maflcko marked this conversation as resolved.
Show resolved Hide resolved
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
}

const CBlockIndex* tip = nullptr;
std::vector<const CBlockIndex*> headers;
Expand All @@ -231,7 +232,7 @@ static bool rest_headers(const std::any& context,
LOCK(cs_main);
CChain& active_chain = chainman.ActiveChain();
tip = active_chain.Tip();
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
const CBlockIndex* pindex{chainman.m_blockman.LookupBlockIndex(*hash)};
while (pindex != nullptr && active_chain.Contains(pindex)) {
headers.push_back(pindex);
if (headers.size() == *parsed_count) {
Expand Down Expand Up @@ -290,9 +291,10 @@ static bool rest_block(const std::any& context,
std::string hashStr;
const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);

uint256 hash;
if (!ParseHashStr(hashStr, hash))
auto hash{uint256::FromHex(hashStr)};
if (!hash) {
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
}

FlatFilePos pos{};
const CBlockIndex* pblockindex = nullptr;
Expand All @@ -303,7 +305,7 @@ static bool rest_block(const std::any& context,
{
LOCK(cs_main);
tip = chainman.ActiveChain().Tip();
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
pblockindex = chainman.m_blockman.LookupBlockIndex(*hash);
if (!pblockindex) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
Expand Down Expand Up @@ -390,8 +392,8 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
}

uint256 block_hash;
if (!ParseHashStr(raw_blockhash, block_hash)) {
auto block_hash{uint256::FromHex(raw_blockhash)};
if (!block_hash) {
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash);
}

Expand All @@ -413,7 +415,7 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
CChain& active_chain = chainman.ActiveChain();
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block_hash);
const CBlockIndex* pindex{chainman.m_blockman.LookupBlockIndex(*block_hash)};
while (pindex != nullptr && active_chain.Contains(pindex)) {
headers.push_back(pindex);
if (headers.size() == *parsed_count)
Expand Down Expand Up @@ -494,8 +496,8 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilter/<filtertype>/<blockhash>");
}

uint256 block_hash;
if (!ParseHashStr(uri_parts[1], block_hash)) {
auto block_hash{uint256::FromHex(uri_parts[1])};
if (!block_hash) {
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[1]);
}

Expand All @@ -516,7 +518,7 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
if (!maybe_chainman) return false;
ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
block_index = chainman.m_blockman.LookupBlockIndex(*block_hash);
if (!block_index) {
return RESTERR(req, HTTP_NOT_FOUND, uri_parts[1] + " not found");
}
Expand Down Expand Up @@ -616,14 +618,14 @@ static bool rest_deploymentinfo(const std::any& context, HTTPRequest* req, const
jsonRequest.params = UniValue(UniValue::VARR);

if (!hash_str.empty()) {
uint256 hash;
if (!ParseHashStr(hash_str, hash)) {
auto hash{uint256::FromHex(hash_str)};
if (!hash) {
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hash_str);
}

const ChainstateManager* chainman = GetChainman(context, req);
if (!chainman) return false;
if (!WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(ParseHashV(hash_str, "blockhash")))) {
if (!WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(*hash))) {
return RESTERR(req, HTTP_BAD_REQUEST, "Block not found");
}

Expand Down Expand Up @@ -704,9 +706,10 @@ static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string
std::string hashStr;
const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);

uint256 hash;
if (!ParseHashStr(hashStr, hash))
auto hash{uint256::FromHex(hashStr)};
if (!hash) {
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
}

if (g_txindex) {
g_txindex->BlockUntilSyncedToCurrentChain();
Expand All @@ -715,7 +718,7 @@ static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string
const NodeContext* const node = GetNodeContext(context, req);
if (!node) return false;
uint256 hashBlock = uint256();
const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, node->mempool.get(), hash, hashBlock, node->chainman->m_blockman);
const CTransactionRef tx{GetTransaction(/*block_index=*/nullptr, node->mempool.get(), *hash, hashBlock, node->chainman->m_blockman)};
if (!tx) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
Expand Down
8 changes: 3 additions & 5 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,11 @@ static RPCHelpMan generateblock()
std::vector<CTransactionRef> txs;
const auto raw_txs_or_txids = request.params[1].get_array();
for (size_t i = 0; i < raw_txs_or_txids.size(); i++) {
const auto str(raw_txs_or_txids[i].get_str());
const auto& str{raw_txs_or_txids[i].get_str()};

uint256 hash;
CMutableTransaction mtx;
if (ParseHashStr(str, hash)) {

const auto tx = mempool.get(hash);
if (auto hash{uint256::FromHex(str)}) {
const auto tx{mempool.get(*hash)};
if (!tx) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Transaction %s not in mempool.", str));
}
Expand Down
9 changes: 3 additions & 6 deletions src/test/blockfilter_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)

unsigned int pos = 0;
/*int block_height =*/ test[pos++].getInt<int>();
uint256 block_hash;
BOOST_CHECK(ParseHashStr(test[pos++].get_str(), block_hash));
BOOST_CHECK(uint256::FromHex(test[pos++].get_str()));

CBlock block;
BOOST_REQUIRE(DecodeHexBlk(block, test[pos++].get_str()));
Expand All @@ -165,11 +164,9 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
tx_undo.vprevout.emplace_back(txout, 0, false);
}

uint256 prev_filter_header_basic;
BOOST_CHECK(ParseHashStr(test[pos++].get_str(), prev_filter_header_basic));
uint256 prev_filter_header_basic{*Assert(uint256::FromHex(test[pos++].get_str()))};
maflcko marked this conversation as resolved.
Show resolved Hide resolved
std::vector<unsigned char> filter_basic = ParseHex(test[pos++].get_str());
maflcko marked this conversation as resolved.
Show resolved Hide resolved
uint256 filter_header_basic;
BOOST_CHECK(ParseHashStr(test[pos++].get_str(), filter_header_basic));
uint256 filter_header_basic{*Assert(uint256::FromHex(test[pos++].get_str()))};

BlockFilter computed_filter_basic(BlockFilterType::BASIC, block, block_undo);
BOOST_CHECK(computed_filter_basic.GetFilter().GetEncoded() == filter_basic);
Expand Down
3 changes: 1 addition & 2 deletions src/test/fuzz/hex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ FUZZ_TARGET(hex)
assert(ToLower(random_hex_string) == hex_data);
}
(void)IsHexNumber(random_hex_string);
uint256 result;
(void)ParseHashStr(random_hex_string, result);
(void)uint256::FromHex(random_hex_string);
(void)uint256S(random_hex_string);
try {
(void)HexToPubKey(random_hex_string);
Expand Down