Skip to content

Commit

Permalink
Merge ExpandHRP + value usages into PreparePolynomialCoefficients
Browse files Browse the repository at this point in the history
Overall resulting in decode being ~22% faster, encode ~32% faster.

Before:
```
|             ns/byte |              byte/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|                8.67 |      115,400,002.88 |    0.1% |      1.10 | `Bech32Decode`
|               18.76 |       53,300,176.12 |    0.2% |      1.10 | `Bech32Encode`
```

After:
```
|             ns/byte |              byte/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|                7.10 |      140,922,648.34 |    0.1% |      1.10 | `Bech32Decode`
|               14.20 |       70,425,968.92 |    0.1% |      1.10 | `Bech32Encode`
```
  • Loading branch information
Lőrinc committed Mar 9, 2024
1 parent 4b80d1f commit 4f76265
Showing 1 changed file with 12 additions and 8 deletions.
20 changes: 12 additions & 8 deletions src/bech32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,18 @@ bool CheckCharacters(const std::string& str, std::vector<int>& errors)
return errors.empty();
}

data ExpandHRP(const std::string& hrp)
std::vector<unsigned char> PreparePolynomialCoefficients(const std::string& hrp, const data& values, const size_t extra)
{
data ret;
ret.reserve(hrp.size() + 90);
ret.reserve(hrp.size() + 1 + hrp.size() + values.size() + extra);

/** Expand a HRP for use in checksum computation. */
for (size_t i = 0; i < hrp.size(); ++i) ret.push_back(hrp[i] >> 5);
ret.push_back(0);
for (size_t i = 0; i < hrp.size(); ++i) ret.push_back(hrp[i] & 0x1f);

ret.insert(ret.end(), values.begin(), values.end());

return ret;
}

Expand All @@ -331,7 +333,8 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
// list of values would result in a new valid list. For that reason, Bech32 requires the
// resulting checksum to be 1 instead. In Bech32m, this constant was amended. See
// https://gist.github.com/sipa/14c248c288c3880a3b191f978a34508e for details.
const uint32_t check = PolyMod(Cat(ExpandHRP(hrp), values));
auto enc = PreparePolynomialCoefficients(hrp, values, 0);
const uint32_t check = PolyMod(enc);
if (check == EncodingConstant(Encoding::BECH32)) return Encoding::BECH32;
if (check == EncodingConstant(Encoding::BECH32M)) return Encoding::BECH32M;
return Encoding::INVALID;
Expand All @@ -340,8 +343,8 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
/** Create a checksum. */
data CreateChecksum(Encoding encoding, const std::string& hrp, const data& values)
{
data enc = Cat(ExpandHRP(hrp), values);
enc.resize(enc.size() + CHECKSUM_SIZE); // Append 6 zeroes
auto enc = PreparePolynomialCoefficients(hrp, values, CHECKSUM_SIZE);
enc.insert(enc.end(), CHECKSUM_SIZE, 0x00);
uint32_t mod = PolyMod(enc) ^ EncodingConstant(encoding); // Determine what to XOR into those 6 zeroes.

data ret;
Expand Down Expand Up @@ -396,7 +399,7 @@ DecodeResult Decode(const std::string& str) {
}
Encoding result = VerifyChecksum(hrp, values);
if (result == Encoding::INVALID) return {};
return {result, std::move(hrp), data(values.begin(), values.end() - 6)};
return {result, std::move(hrp), data(values.begin(), values.end() - CHECKSUM_SIZE)};
}

/** Find index of an incorrect character in a Bech32 string. */
Expand Down Expand Up @@ -445,9 +448,10 @@ std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str) {
std::optional<Encoding> error_encoding;
for (Encoding encoding : {Encoding::BECH32, Encoding::BECH32M}) {
std::vector<int> possible_errors;
// Recall that (ExpandHRP(hrp) ++ values) is interpreted as a list of coefficients of a polynomial
// Recall that (expanded hrp + values) is interpreted as a list of coefficients of a polynomial
// over GF(32). PolyMod computes the "remainder" of this polynomial modulo the generator G(x).
uint32_t residue = PolyMod(Cat(ExpandHRP(hrp), values)) ^ EncodingConstant(encoding);
auto enc = PreparePolynomialCoefficients(hrp, values, 0);
uint32_t residue = PolyMod(enc) ^ EncodingConstant(encoding);

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

0 comments on commit 4f76265

Please sign in to comment.