aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorstickies-v <stickies-v@protonmail.com>2024-08-20 13:41:08 +0100
committerstickies-v <stickies-v@protonmail.com>2024-08-23 13:53:39 +0100
commit70e2c87737e77ee85812cc328c4ddfaea7147533 (patch)
treeaaf29dec8dab040ac508a64f8cc2b05fb61a2c98 /src
parent85b7cbfcbe3f94770bdf73dedd8bda0193a44627 (diff)
downloadbitcoin-70e2c87737e77ee85812cc328c4ddfaea7147533.tar.xz
refactor: add uint256::FromUserHex helper
FromUserHex will be used in future commits to construct uint256 instances from user hex input without being unnecessarily restrictive on formatting by allowing 0x-prefixed input that is shorter than 64 characters.
Diffstat (limited to 'src')
-rw-r--r--src/test/fuzz/hex.cpp4
-rw-r--r--src/test/uint256_tests.cpp29
-rw-r--r--src/uint256.h22
3 files changed, 55 insertions, 0 deletions
diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp
index ebe30c3c1a..8bc0460bb8 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 04f8682b69..ce17ceca6e 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -386,6 +386,35 @@ BOOST_AUTO_TEST_CASE(from_hex)
TestFromHex<Wtxid>();
}
+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 f8e78b820f..759837ff10 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -9,6 +9,7 @@
#include <crypto/common.h>
#include <span.h>
#include <util/strencodings.h>
+#include <util/string.h>
#include <algorithm>
#include <array>
@@ -17,6 +18,7 @@
#include <cstring>
#include <optional>
#include <string>
+#include <string_view>
/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS>
@@ -106,6 +108,25 @@ std::optional<uintN_t> 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 <class uintN_t>
+std::optional<uintN_t> 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<uintN_t>(padded);
+ }
+ return FromHex<uintN_t>(input);
+}
} // namespace detail
/** 160-bit opaque blob.
@@ -127,6 +148,7 @@ public:
class uint256 : public base_blob<256> {
public:
static std::optional<uint256> FromHex(std::string_view str) { return detail::FromHex<uint256>(str); }
+ static std::optional<uint256> FromUserHex(std::string_view str) { return detail::FromUserHex<uint256>(str); }
constexpr uint256() = default;
constexpr explicit uint256(uint8_t v) : base_blob<256>(v) {}
constexpr explicit uint256(Span<const unsigned char> vch) : base_blob<256>(vch) {}