Skip to content

Commit 8fd2267

Browse files
committed
Import Bech32 C++ reference code & tests
This includes a reformatted version of the Bech32 reference code (see https://github.com/sipa/bech32/tree/master/ref/c%2B%2B), with extra documentation.
1 parent 1e46ebd commit 8fd2267

File tree

5 files changed

+286
-0
lines changed

5 files changed

+286
-0
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ BITCOIN_CORE_H = \
7878
addrdb.h \
7979
addrman.h \
8080
base58.h \
81+
bech32.h \
8182
bloom.h \
8283
blockencodings.h \
8384
chain.h \
@@ -316,6 +317,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
316317
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
317318
libbitcoin_common_a_SOURCES = \
318319
base58.cpp \
320+
bech32.cpp \
319321
chainparams.cpp \
320322
coins.cpp \
321323
compressor.cpp \

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ BITCOIN_TESTS =\
3131
test/base32_tests.cpp \
3232
test/base58_tests.cpp \
3333
test/base64_tests.cpp \
34+
test/bech32_tests.cpp \
3435
test/bip32_tests.cpp \
3536
test/blockencodings_tests.cpp \
3637
test/bloom_tests.cpp \

src/bech32.cpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Copyright (c) 2017 Pieter Wuille
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "bech32.h"
6+
7+
namespace
8+
{
9+
10+
typedef std::vector<uint8_t> data;
11+
12+
/** The Bech32 character set for encoding. */
13+
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
14+
15+
/** The Bech32 character set for decoding. */
16+
const int8_t CHARSET_REV[128] = {
17+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
18+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
19+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
20+
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
21+
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
22+
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
23+
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
24+
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
25+
};
26+
27+
/** Concatenate two byte arrays. */
28+
data Cat(data x, const data& y)
29+
{
30+
x.insert(x.end(), y.begin(), y.end());
31+
return x;
32+
}
33+
34+
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
35+
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
36+
* bits correspond to earlier values. */
37+
uint32_t PolyMod(const data& v)
38+
{
39+
// The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
40+
// implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
41+
// 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
42+
// [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
43+
44+
// The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
45+
// v(x) mod g(x), where g(x) is the Bech32 generator,
46+
// x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
47+
// that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
48+
// window of 1023 characters. Among the various possible BCH codes, one was selected to in
49+
// fact guarantee detection of up to 4 errors within a window of 89 characters.
50+
51+
// Note that the coefficients are elements of GF(32), here represented as decimal numbers
52+
// between {}. In this finite field, addition is just XOR of the corresponding numbers. For
53+
// example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
54+
// treating the bits of values themselves as coefficients of a polynomial over a smaller field,
55+
// GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
56+
// (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
57+
// = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
58+
59+
// During the course of the loop below, `c` contains the bitpacked coefficients of the
60+
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
61+
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
62+
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
63+
// for `c`.
64+
uint32_t c = 1;
65+
for (auto v_i : v) {
66+
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
67+
// value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
68+
// correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
69+
// process. Simplifying:
70+
// c'(x) = (f(x) * x + v_i) mod g(x)
71+
// ((f(x) mod g(x)) * x + v_i) mod g(x)
72+
// (c(x) * x + v_i) mod g(x)
73+
// If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
74+
// c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
75+
// = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
76+
// = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
77+
// If we call (x^6 mod g(x)) = k(x), this can be written as
78+
// c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
79+
80+
// First, determine the value of c0:
81+
uint8_t c0 = c >> 25;
82+
83+
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
84+
c = ((c & 0x1ffffff) << 5) ^ v_i;
85+
86+
// Finally, for each set bit n in c0, conditionally add {2^n}k(x):
87+
if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
88+
if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
89+
if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
90+
if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
91+
if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
92+
}
93+
return c;
94+
}
95+
96+
/** Convert to lower case. */
97+
inline unsigned char LowerCase(unsigned char c)
98+
{
99+
return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
100+
}
101+
102+
/** Expand a HRP for use in checksum computation. */
103+
data ExpandHRP(const std::string& hrp)
104+
{
105+
data ret;
106+
ret.reserve(hrp.size() + 90);
107+
ret.resize(hrp.size() * 2 + 1);
108+
for (size_t i = 0; i < hrp.size(); ++i) {
109+
unsigned char c = hrp[i];
110+
ret[i] = c >> 5;
111+
ret[i + hrp.size() + 1] = c & 0x1f;
112+
}
113+
ret[hrp.size()] = 0;
114+
return ret;
115+
}
116+
117+
/** Verify a checksum. */
118+
bool VerifyChecksum(const std::string& hrp, const data& values)
119+
{
120+
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
121+
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
122+
// list of values would result in a new valid list. For that reason, Bech32 requires the
123+
// resulting checksum to be 1 instead.
124+
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
125+
}
126+
127+
/** Create a checksum. */
128+
data CreateChecksum(const std::string& hrp, const data& values)
129+
{
130+
data enc = Cat(ExpandHRP(hrp), values);
131+
enc.resize(enc.size() + 6); // Append 6 zeroes
132+
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
133+
data ret(6);
134+
for (size_t i = 0; i < 6; ++i) {
135+
// Convert the 5-bit groups in mod to checksum values.
136+
ret[i] = (mod >> (5 * (5 - i))) & 31;
137+
}
138+
return ret;
139+
}
140+
141+
} // namespace
142+
143+
namespace bech32
144+
{
145+
146+
/** Encode a Bech32 string. */
147+
std::string Encode(const std::string& hrp, const data& values) {
148+
data checksum = CreateChecksum(hrp, values);
149+
data combined = Cat(values, checksum);
150+
std::string ret = hrp + '1';
151+
ret.reserve(ret.size() + combined.size());
152+
for (auto c : combined) {
153+
ret += CHARSET[c];
154+
}
155+
return ret;
156+
}
157+
158+
/** Decode a Bech32 string. */
159+
std::pair<std::string, data> Decode(const std::string& str) {
160+
bool lower = false, upper = false;
161+
for (size_t i = 0; i < str.size(); ++i) {
162+
unsigned char c = str[i];
163+
if (c < 33 || c > 126) return {};
164+
if (c >= 'a' && c <= 'z') lower = true;
165+
if (c >= 'A' && c <= 'Z') upper = true;
166+
}
167+
if (lower && upper) return {};
168+
size_t pos = str.rfind('1');
169+
if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
170+
return {};
171+
}
172+
data values(str.size() - 1 - pos);
173+
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
174+
unsigned char c = str[i + pos + 1];
175+
int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c];
176+
if (rev == -1) {
177+
return {};
178+
}
179+
values[i] = rev;
180+
}
181+
std::string hrp;
182+
for (size_t i = 0; i < pos; ++i) {
183+
hrp += LowerCase(str[i]);
184+
}
185+
if (!VerifyChecksum(hrp, values)) {
186+
return {};
187+
}
188+
return {hrp, data(values.begin(), values.end() - 6)};
189+
}
190+
191+
} // namespace bech32

src/bech32.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) 2017 Pieter Wuille
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
// Bech32 is a string encoding format used in newer address types.
6+
// The output consists of a human-readable part (alphanumeric), a
7+
// separator character (1), and a base32 data section, the last
8+
// 6 characters of which are a checksum.
9+
//
10+
// For more information, see BIP 173.
11+
12+
#include <stdint.h>
13+
#include <string>
14+
#include <vector>
15+
16+
namespace bech32
17+
{
18+
19+
/** Encode a Bech32 string. Returns the empty string in case of failure. */
20+
std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
21+
22+
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
23+
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
24+
25+
} // namespace bech32

src/test/bech32_tests.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) 2017 Pieter Wuille
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "bech32.h"
6+
#include "test/test_bitcoin.h"
7+
8+
#include <boost/test/unit_test.hpp>
9+
10+
BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
11+
12+
bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2)
13+
{
14+
if (s1.size() != s2.size()) return false;
15+
for (size_t i = 0; i < s1.size(); ++i) {
16+
char c1 = s1[i];
17+
if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a');
18+
char c2 = s2[i];
19+
if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a');
20+
if (c1 != c2) return false;
21+
}
22+
return true;
23+
}
24+
25+
BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
26+
{
27+
static const std::string CASES[] = {
28+
"A12UEL5L",
29+
"a12uel5l",
30+
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
31+
"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
32+
"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
33+
"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
34+
"?1ezyfcl",
35+
};
36+
for (const std::string& str : CASES) {
37+
auto ret = bech32::Decode(str);
38+
BOOST_CHECK(!ret.first.empty());
39+
std::string recode = bech32::Encode(ret.first, ret.second);
40+
BOOST_CHECK(!recode.empty());
41+
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
42+
}
43+
}
44+
45+
BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
46+
{
47+
static const std::string CASES[] = {
48+
" 1nwldj5",
49+
"\x7f""1axkwrx",
50+
"\x80""1eym55h",
51+
"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx",
52+
"pzry9x0s0muk",
53+
"1pzry9x0s0muk",
54+
"x1b4n0q5v",
55+
"li1dgmt3",
56+
"de1lg7wt\xff",
57+
"A1G7SGD8",
58+
"10a06t8",
59+
"1qzzfhee",
60+
};
61+
for (const std::string& str : CASES) {
62+
auto ret = bech32::Decode(str);
63+
BOOST_CHECK(ret.first.empty());
64+
}
65+
}
66+
67+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)