diff options
author | MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> | 2024-07-18 22:38:14 +0200 |
---|---|---|
committer | MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> | 2024-07-24 17:38:06 +0200 |
commit | fad2991ba073de0bd1f12e42bf0fbaca4a265508 (patch) | |
tree | 0889ecf0db4868751eed32460dfd808ab33cf407 | |
parent | fa103db2bb736bce4440f0bde564e6671e36311d (diff) |
refactor: Implement strict uint256::FromHex()
This is a safe replacement of the previous SetHex, which now returns an
optional to indicate success or failure.
The code is similar to the ParseHashStr helper, which will be removed in
a later commit.
-rw-r--r-- | src/test/uint256_tests.cpp | 14 | ||||
-rw-r--r-- | src/uint256.h | 29 |
2 files changed, 32 insertions, 11 deletions
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index 5929069271..d280126cde 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE( comparison ) // <= >= < > uint256S("1000000000000000000000000000000000000000000000000000000000000002")); } -BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHexDeprecated begin() end() size() GetLow64 GetSerializeSize, Serialize, Unserialize +BOOST_AUTO_TEST_CASE(methods) // GetHex SetHexDeprecated FromHex begin() end() size() GetLow64 GetSerializeSize, Serialize, Unserialize { BOOST_CHECK_EQUAL(R1L.GetHex(), R1L.ToString()); BOOST_CHECK_EQUAL(R2L.GetHex(), R2L.ToString()); @@ -168,10 +168,10 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHexDeprecated begin() end() size() // Verify previous values don't persist when setting to truncated string. TmpL.SetHexDeprecated("21"); BOOST_CHECK_EQUAL(TmpL.ToString(), "0000000000000000000000000000000000000000000000000000000000000021"); - TmpL.SetHexDeprecated(R2L.ToString()); BOOST_CHECK_EQUAL(TmpL, R2L); - TmpL.SetHexDeprecated(ZeroL.ToString()); BOOST_CHECK_EQUAL(TmpL, uint256()); + BOOST_CHECK_EQUAL(uint256::FromHex(R2L.ToString()).value(), R2L); + BOOST_CHECK_EQUAL(uint256::FromHex(ZeroL.ToString()).value(), uint256()); - TmpL.SetHexDeprecated(R1L.ToString()); + TmpL = uint256::FromHex(R1L.ToString()).value(); BOOST_CHECK_EQUAL_COLLECTIONS(R1L.begin(), R1L.end(), R1Array, R1Array + R1L.size()); BOOST_CHECK_EQUAL_COLLECTIONS(TmpL.begin(), TmpL.end(), R1Array, R1Array + TmpL.size()); BOOST_CHECK_EQUAL_COLLECTIONS(R2L.begin(), R2L.end(), R2Array, R2Array + R2L.size()); @@ -214,10 +214,10 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHexDeprecated begin() end() size() BOOST_CHECK_EQUAL(MaxS.GetHex(), MaxS.ToString()); uint160 TmpS(R1S); BOOST_CHECK_EQUAL(TmpS, R1S); - TmpS.SetHexDeprecated(R2S.ToString()); BOOST_CHECK_EQUAL(TmpS, R2S); - TmpS.SetHexDeprecated(ZeroS.ToString()); BOOST_CHECK_EQUAL(TmpS, uint160()); + BOOST_CHECK_EQUAL(uint160::FromHex(R2S.ToString()).value(), R2S); + BOOST_CHECK_EQUAL(uint160::FromHex(ZeroS.ToString()).value(), uint160()); - TmpS.SetHexDeprecated(R1S.ToString()); + TmpS = uint160::FromHex(R1S.ToString()).value(); BOOST_CHECK_EQUAL_COLLECTIONS(R1S.begin(), R1S.end(), R1Array, R1Array + R1S.size()); BOOST_CHECK_EQUAL_COLLECTIONS(TmpS.begin(), TmpS.end(), R1Array, R1Array + TmpS.size()); BOOST_CHECK_EQUAL_COLLECTIONS(R2S.begin(), R2S.end(), R2Array, R2Array + R2S.size()); diff --git a/src/uint256.h b/src/uint256.h index 3f7dce6983..f8e78b820f 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2022 The Bitcoin Core developers +// Copyright (c) 2009-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,12 +8,14 @@ #include <crypto/common.h> #include <span.h> +#include <util/strencodings.h> #include <algorithm> #include <array> #include <cassert> +#include <cstdint> #include <cstring> -#include <stdint.h> +#include <optional> #include <string> /** Template base class for fixed-sized opaque blobs. */ @@ -59,6 +61,7 @@ public: // Hex string representations are little-endian. std::string GetHex() const; + /** Unlike FromHex this accepts any invalid input, thus it is fragile and deprecated */ void SetHexDeprecated(std::string_view str); std::string ToString() const; @@ -88,12 +91,30 @@ public: } }; +namespace detail { +/** + * Writes the hex string (treated as little-endian) into a new uintN_t object + * and only returns a value iff all of the checks pass: + * - Input length is uintN_t::size()*2 + * - All characters are hex + */ +template <class uintN_t> +std::optional<uintN_t> FromHex(std::string_view str) +{ + if (uintN_t::size() * 2 != str.size() || !IsHex(str)) return std::nullopt; + uintN_t rv; + rv.SetHexDeprecated(str); + return rv; +} +} // namespace detail + /** 160-bit opaque blob. * @note This type is called uint160 for historical reasons only. It is an opaque * blob of 160 bits and has no integer operations. */ class uint160 : public base_blob<160> { public: + static std::optional<uint160> FromHex(std::string_view str) { return detail::FromHex<uint160>(str); } constexpr uint160() = default; constexpr explicit uint160(Span<const unsigned char> vch) : base_blob<160>(vch) {} }; @@ -105,6 +126,7 @@ public: */ class uint256 : public base_blob<256> { public: + static std::optional<uint256> FromHex(std::string_view str) { return detail::FromHex<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) {} @@ -113,8 +135,7 @@ public: }; /* uint256 from std::string_view, treated as little-endian. - * This is not a uint256 constructor because of historical fears of uint256(0) - * resolving to a NULL string and crashing. + * DEPRECATED. Unlike FromHex this accepts any invalid input, thus it is fragile and deprecated! */ inline uint256 uint256S(std::string_view str) { |