From 1cfe953c266475e379010900f278e55d21f10418 Mon Sep 17 00:00:00 2001 From: josibake Date: Tue, 18 Jul 2023 22:22:26 +0200 Subject: [PATCH] refactor: Model the bech32 charlimit as an Enum Bech32(m) was defined with a 90 character limit so that certain guarantees for error detection could be made for segwit addresses. However, there is nothing about the encoding scheme itself that requires a limit and in practice bech32(m) has been used without the 90 char limit (e.g. lightning invoices). Further, increasing the character limit doesn't do away with error detection, it simply lessons the guarantees. Model charlimit as an Enum, so that if a different address scheme is using bech32(m), the character limit for that address scheme can be used, rather than always using the 90 charlimit defined for segwit addresses. --- src/bech32.cpp | 12 ++++++------ src/bech32.h | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/bech32.cpp b/src/bech32.cpp index ba3c419d8b6860..c17b3fe7ef322a 100644 --- a/src/bech32.cpp +++ b/src/bech32.cpp @@ -370,11 +370,11 @@ std::string Encode(Encoding encoding, const std::string& hrp, const data& values } /** Decode a Bech32 or Bech32m string. */ -DecodeResult Decode(const std::string& str) { +DecodeResult Decode(const std::string& str, CharLimit limit) { std::vector errors; if (!CheckCharacters(str, errors)) return {}; size_t pos = str.rfind('1'); - if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { + if (str.size() > limit || pos == str.npos || pos == 0 || pos + 7 > str.size()) { return {}; } data values(str.size() - 1 - pos); @@ -397,12 +397,12 @@ DecodeResult Decode(const std::string& str) { } /** Find index of an incorrect character in a Bech32 string. */ -std::pair> LocateErrors(const std::string& str) { +std::pair> LocateErrors(const std::string& str, CharLimit limit) { std::vector error_locations{}; - if (str.size() > 90) { - error_locations.resize(str.size() - 90); - std::iota(error_locations.begin(), error_locations.end(), 90); + if (str.size() > limit) { + error_locations.resize(str.size() - limit); + std::iota(error_locations.begin(), error_locations.end(), static_cast(limit)); return std::make_pair("Bech32 string too long", std::move(error_locations)); } diff --git a/src/bech32.h b/src/bech32.h index 5e89e6efdaa94b..638e9fb423c9df 100644 --- a/src/bech32.h +++ b/src/bech32.h @@ -28,6 +28,16 @@ enum class Encoding { BECH32M, //!< Bech32m encoding as defined in BIP350 }; +/** Character limits for bech32(m) encoded strings. Character limits are how we provide error location guarantees. + * These values should never exceed 2^31 - 1 (max value for a 32-bit int), since there are places where we may need to + * convert the CharLimit::VALUE to an int. In practice, this should never happen since this CharLimit applies to an address encoding + * and we would never encode an address with such a massive value */ +enum CharLimit : size_t { + NO_LIMIT = 0, //!< Allows for Bech32(m) encoded strings of arbitrary size. No guarantees on the number of errors detected + + SEGWIT = 90, //!< BIP173/350 imposed 90 character limit on Bech32(m) encoded addresses. This guarantees finding up to 4 errors +}; + /** Encode a Bech32 or Bech32m string. If hrp contains uppercase characters, this will cause an * assertion error. Encoding must be one of BECH32 or BECH32M. */ std::string Encode(Encoding encoding, const std::string& hrp, const std::vector& values); @@ -43,10 +53,10 @@ struct DecodeResult }; /** Decode a Bech32 or Bech32m string. */ -DecodeResult Decode(const std::string& str); +DecodeResult Decode(const std::string& str, CharLimit limit = CharLimit::SEGWIT); /** Return the positions of errors in a Bech32 string. */ -std::pair> LocateErrors(const std::string& str); +std::pair> LocateErrors(const std::string& str, CharLimit limit = CharLimit::SEGWIT); } // namespace bech32