@@ -19,6 +19,9 @@ namespace
19
19
20
20
typedef std::vector<uint8_t > data;
21
21
22
+ /* * The Bech32 and Bech32m checksum size */
23
+ constexpr size_t CHECKSUM_SIZE = 6 ;
24
+
22
25
/* * The Bech32 and Bech32m character set for encoding. */
23
26
const char * CHARSET = " qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
24
27
@@ -308,18 +311,18 @@ bool CheckCharacters(const std::string& str, std::vector<int>& errors)
308
311
return errors.empty ();
309
312
}
310
313
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)
313
315
{
314
316
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
+
323
326
return ret;
324
327
}
325
328
@@ -331,7 +334,8 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
331
334
// list of values would result in a new valid list. For that reason, Bech32 requires the
332
335
// resulting checksum to be 1 instead. In Bech32m, this constant was amended. See
333
336
// 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);
335
339
if (check == EncodingConstant (Encoding::BECH32)) return Encoding::BECH32;
336
340
if (check == EncodingConstant (Encoding::BECH32M)) return Encoding::BECH32M;
337
341
return Encoding::INVALID;
@@ -340,11 +344,11 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
340
344
/* * Create a checksum. */
341
345
data CreateChecksum (Encoding encoding, const std::string& hrp, const data& values)
342
346
{
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 );
345
349
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) {
348
352
// Convert the 5-bit groups in mod to checksum values.
349
353
ret[i] = (mod >> (5 * (5 - i))) & 31 ;
350
354
}
@@ -375,7 +379,7 @@ DecodeResult Decode(const std::string& str, CharLimit limit) {
375
379
if (!CheckCharacters (str, errors)) return {};
376
380
size_t pos = str.rfind (' 1' );
377
381
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 ()) {
379
383
return {};
380
384
}
381
385
data values (str.size () - 1 - pos);
@@ -394,7 +398,7 @@ DecodeResult Decode(const std::string& str, CharLimit limit) {
394
398
}
395
399
Encoding result = VerifyChecksum (hrp, values);
396
400
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 )};
398
402
}
399
403
400
404
/* * 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
415
419
if (pos == str.npos ) {
416
420
return std::make_pair (" Missing separator" , std::vector<int >{});
417
421
}
418
- if (pos == 0 || pos + 7 > str.size ()) {
422
+ if (pos == 0 || pos + CHECKSUM_SIZE >= str.size ()) {
419
423
error_locations.push_back (pos);
420
424
return std::make_pair (" Invalid separator position" , std::move (error_locations));
421
425
}
@@ -442,9 +446,10 @@ std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, Ch
442
446
std::optional<Encoding> error_encoding;
443
447
for (Encoding encoding : {Encoding::BECH32, Encoding::BECH32M}) {
444
448
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
446
450
// 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);
448
453
449
454
// All valid codewords should be multiples of G(x), so this remainder (after XORing with the encoding
450
455
// constant) should be 0 - hence 0 indicates there are no errors present.
0 commit comments