diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp index ebe30c3c1aa..8bc0460bb8b 100644 --- a/src/test/fuzz/hex.cpp +++ b/src/test/fuzz/hex.cpp @@ -32,6 +32,10 @@ FUZZ_TARGET(hex) assert(random_hex_string.length() == 64); assert(Txid::FromHex(random_hex_string)); assert(Wtxid::FromHex(random_hex_string)); + assert(uint256::FromUserHex(random_hex_string)); + } + if (const auto result{uint256::FromUserHex(random_hex_string)}) { + assert(uint256::FromHex(result->ToString())); } (void)uint256S(random_hex_string); try { diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index 04f8682b69d..ce17ceca6ec 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -386,6 +386,35 @@ BOOST_AUTO_TEST_CASE(from_hex) TestFromHex(); } +BOOST_AUTO_TEST_CASE(from_user_hex) +{ + BOOST_CHECK_EQUAL(uint256::FromUserHex("").value(), uint256::ZERO); + BOOST_CHECK_EQUAL(uint256::FromUserHex("0x").value(), uint256::ZERO); + BOOST_CHECK_EQUAL(uint256::FromUserHex("0").value(), uint256::ZERO); + BOOST_CHECK_EQUAL(uint256::FromUserHex("00").value(), uint256::ZERO); + BOOST_CHECK_EQUAL(uint256::FromUserHex("1").value(), uint256::ONE); + BOOST_CHECK_EQUAL(uint256::FromUserHex("0x10").value(), uint256{0x10}); + BOOST_CHECK_EQUAL(uint256::FromUserHex("10").value(), uint256{0x10}); + BOOST_CHECK_EQUAL(uint256::FromUserHex("0xFf").value(), uint256{0xff}); + BOOST_CHECK_EQUAL(uint256::FromUserHex("Ff").value(), uint256{0xff}); + const std::string valid_hex_64{"0x0123456789abcdef0123456789abcdef0123456789ABDCEF0123456789ABCDEF"}; + BOOST_REQUIRE_EQUAL(valid_hex_64.size(), 2 + 64); // 0x prefix and 64 hex digits + BOOST_CHECK_EQUAL(uint256::FromUserHex(valid_hex_64.substr(2)).value().ToString(), ToLower(valid_hex_64.substr(2))); + BOOST_CHECK_EQUAL(uint256::FromUserHex(valid_hex_64.substr(0)).value().ToString(), ToLower(valid_hex_64.substr(2))); + + BOOST_CHECK(!uint256::FromUserHex("0x0 ")); // no spaces at end, + BOOST_CHECK(!uint256::FromUserHex(" 0x0")); // or beginning, + BOOST_CHECK(!uint256::FromUserHex("0x 0")); // or middle, + BOOST_CHECK(!uint256::FromUserHex(" ")); // etc. + BOOST_CHECK(!uint256::FromUserHex("0x0ga")); // invalid character + BOOST_CHECK(!uint256::FromUserHex("x0")); // broken prefix + BOOST_CHECK(!uint256::FromUserHex("0x0x00")); // two prefixes not allowed + BOOST_CHECK(!uint256::FromUserHex(valid_hex_64.substr(2) + "0")); // 1 hex digit too many + BOOST_CHECK(!uint256::FromUserHex(valid_hex_64 + "a")); // 1 hex digit too many + BOOST_CHECK(!uint256::FromUserHex(valid_hex_64 + " ")); // whitespace after max length + BOOST_CHECK(!uint256::FromUserHex(valid_hex_64 + "z")); // invalid character after max length +} + BOOST_AUTO_TEST_CASE( check_ONE ) { uint256 one = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); diff --git a/src/uint256.h b/src/uint256.h index f8e78b820f3..759837ff103 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include /** Template base class for fixed-sized opaque blobs. */ template @@ -106,6 +108,25 @@ std::optional FromHex(std::string_view str) rv.SetHexDeprecated(str); return rv; } +/** + * @brief Like FromHex(std::string_view str), but allows an "0x" prefix + * and pads the input with leading zeroes if it is shorter than + * the expected length of uintN_t::size()*2. + * + * Designed to be used when dealing with user input. + */ +template +std::optional FromUserHex(std::string_view input) +{ + input = util::RemovePrefixView(input, "0x"); + constexpr auto expected_size{uintN_t::size() * 2}; + if (input.size() < expected_size) { + auto padded = std::string(expected_size, '0'); + std::copy(input.begin(), input.end(), padded.begin() + expected_size - input.size()); + return FromHex(padded); + } + return FromHex(input); +} } // namespace detail /** 160-bit opaque blob. @@ -127,6 +148,7 @@ public: class uint256 : public base_blob<256> { public: static std::optional FromHex(std::string_view str) { return detail::FromHex(str); } + static std::optional FromUserHex(std::string_view str) { return detail::FromUserHex(str); } constexpr uint256() = default; constexpr explicit uint256(uint8_t v) : base_blob<256>(v) {} constexpr explicit uint256(Span vch) : base_blob<256>(vch) {}