Skip to content

Commit 7f3f6c6

Browse files
Lőrincl0rinc
authored andcommitted
refactor: replace hardcoded numbers
Replace ExpandHRP with a PreparePolynomialCoefficients function. Instead of using a hardcoded value for the size of the array (90 in this case) and a hardcoded value for the checksum, use the actual values vector and define checksum size as a constexpr. Use the new CHECKSUM_SIZE throughout instead 6. Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
1 parent 5676aec commit 7f3f6c6

File tree

1 file changed

+25
-20
lines changed

1 file changed

+25
-20
lines changed

src/bech32.cpp

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ namespace
1919

2020
typedef std::vector<uint8_t> data;
2121

22+
/** The Bech32 and Bech32m checksum size */
23+
constexpr size_t CHECKSUM_SIZE = 6;
24+
2225
/** The Bech32 and Bech32m character set for encoding. */
2326
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
2427

@@ -308,18 +311,18 @@ bool CheckCharacters(const std::string& str, std::vector<int>& errors)
308311
return errors.empty();
309312
}
310313

311-
/** Expand a HRP for use in checksum computation. */
312-
data ExpandHRP(const std::string& hrp)
314+
std::vector<unsigned char> PreparePolynomialCoefficients(const std::string& hrp, const data& values)
313315
{
314316
data ret;
315-
ret.reserve(hrp.size() + 90);
316-
ret.resize(hrp.size() * 2 + 1);
317-
for (size_t i = 0; i < hrp.size(); ++i) {
318-
unsigned char c = hrp[i];
319-
ret[i] = c >> 5;
320-
ret[i + hrp.size() + 1] = c & 0x1f;
321-
}
322-
ret[hrp.size()] = 0;
317+
ret.reserve(hrp.size() + 1 + hrp.size() + values.size() + CHECKSUM_SIZE);
318+
319+
/** Expand a HRP for use in checksum computation. */
320+
for (size_t i = 0; i < hrp.size(); ++i) ret.push_back(hrp[i] >> 5);
321+
ret.push_back(0);
322+
for (size_t i = 0; i < hrp.size(); ++i) ret.push_back(hrp[i] & 0x1f);
323+
324+
ret.insert(ret.end(), values.begin(), values.end());
325+
323326
return ret;
324327
}
325328

@@ -331,7 +334,8 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
331334
// list of values would result in a new valid list. For that reason, Bech32 requires the
332335
// resulting checksum to be 1 instead. In Bech32m, this constant was amended. See
333336
// https://gist.github.com/sipa/14c248c288c3880a3b191f978a34508e for details.
334-
const uint32_t check = PolyMod(Cat(ExpandHRP(hrp), values));
337+
auto enc = PreparePolynomialCoefficients(hrp, values);
338+
const uint32_t check = PolyMod(enc);
335339
if (check == EncodingConstant(Encoding::BECH32)) return Encoding::BECH32;
336340
if (check == EncodingConstant(Encoding::BECH32M)) return Encoding::BECH32M;
337341
return Encoding::INVALID;
@@ -340,11 +344,11 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
340344
/** Create a checksum. */
341345
data CreateChecksum(Encoding encoding, const std::string& hrp, const data& values)
342346
{
343-
data enc = Cat(ExpandHRP(hrp), values);
344-
enc.resize(enc.size() + 6); // Append 6 zeroes
347+
auto enc = PreparePolynomialCoefficients(hrp, values);
348+
enc.insert(enc.end(), CHECKSUM_SIZE, 0x00);
345349
uint32_t mod = PolyMod(enc) ^ EncodingConstant(encoding); // Determine what to XOR into those 6 zeroes.
346-
data ret(6);
347-
for (size_t i = 0; i < 6; ++i) {
350+
data ret(CHECKSUM_SIZE);
351+
for (size_t i = 0; i < CHECKSUM_SIZE; ++i) {
348352
// Convert the 5-bit groups in mod to checksum values.
349353
ret[i] = (mod >> (5 * (5 - i))) & 31;
350354
}
@@ -375,7 +379,7 @@ DecodeResult Decode(const std::string& str, CharLimit limit) {
375379
if (!CheckCharacters(str, errors)) return {};
376380
size_t pos = str.rfind('1');
377381
if (str.size() > limit) return {};
378-
if (pos == str.npos || pos == 0 || pos + 7 > str.size()) {
382+
if (pos == str.npos || pos == 0 || pos + CHECKSUM_SIZE >= str.size()) {
379383
return {};
380384
}
381385
data values(str.size() - 1 - pos);
@@ -394,7 +398,7 @@ DecodeResult Decode(const std::string& str, CharLimit limit) {
394398
}
395399
Encoding result = VerifyChecksum(hrp, values);
396400
if (result == Encoding::INVALID) return {};
397-
return {result, std::move(hrp), data(values.begin(), values.end() - 6)};
401+
return {result, std::move(hrp), data(values.begin(), values.end() - CHECKSUM_SIZE)};
398402
}
399403

400404
/** Find index of an incorrect character in a Bech32 string. */
@@ -415,7 +419,7 @@ std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, Ch
415419
if (pos == str.npos) {
416420
return std::make_pair("Missing separator", std::vector<int>{});
417421
}
418-
if (pos == 0 || pos + 7 > str.size()) {
422+
if (pos == 0 || pos + CHECKSUM_SIZE >= str.size()) {
419423
error_locations.push_back(pos);
420424
return std::make_pair("Invalid separator position", std::move(error_locations));
421425
}
@@ -442,9 +446,10 @@ std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, Ch
442446
std::optional<Encoding> error_encoding;
443447
for (Encoding encoding : {Encoding::BECH32, Encoding::BECH32M}) {
444448
std::vector<int> possible_errors;
445-
// Recall that (ExpandHRP(hrp) ++ values) is interpreted as a list of coefficients of a polynomial
449+
// Recall that (expanded hrp + values) is interpreted as a list of coefficients of a polynomial
446450
// over GF(32). PolyMod computes the "remainder" of this polynomial modulo the generator G(x).
447-
uint32_t residue = PolyMod(Cat(ExpandHRP(hrp), values)) ^ EncodingConstant(encoding);
451+
auto enc = PreparePolynomialCoefficients(hrp, values);
452+
uint32_t residue = PolyMod(enc) ^ EncodingConstant(encoding);
448453

449454
// All valid codewords should be multiples of G(x), so this remainder (after XORing with the encoding
450455
// constant) should be 0 - hence 0 indicates there are no errors present.

0 commit comments

Comments
 (0)