diff --git a/README.md b/README.md index 9919b8a..8e893cf 100644 --- a/README.md +++ b/README.md @@ -29,18 +29,18 @@ make test ### Installing prerequirements If the above doesn't work, you probably need to install some -prerequirements. For example, on a fresh Debian Stretch system: +prerequirements. For example, on a fresh Debian 10 ("buster") system: ``` -$ sudo apt-get install make gcc g++ +sudo apt-get install make gcc g++ ``` It is worth getting the latest cmake, so install that the hard way: ``` -wget https://cmake.org/files/v3.13/cmake-3.13.2.tar.gz -tar xzf cmake-3.13.2.tar.gz -cd cmake-3.13.2 +wget https://cmake.org/files/v3.19/cmake-3.19.4.tar.gz +tar xzf cmake-3.19.4.tar.gz +cd cmake-3.19.4 ./configure make sudo make install @@ -48,6 +48,8 @@ sudo make install Now you can again try to build libbech32. +## Example Code + ### C++ Usage Example ```cpp @@ -63,7 +65,7 @@ int main() { // encode std::string bstr = bech32::encode(hrp, data); - // prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum + // prints "hello1w0rldjn365x" : "hello" + Bech32.separator + encoded data + 6 char checksum std::cout << bstr << std::endl; // decode @@ -71,9 +73,12 @@ int main() { assert(hrp == hd.hrp); assert(data == hd.dp); + assert(bech32::Encoding::Bech32m == hd.encoding); } ``` +For more C++ examples, see `examples/cpp_example.cpp` + ### C Usage Example ```C @@ -87,24 +92,68 @@ int main() { char hrp[] = "hello"; unsigned char dp[] = {14, 15, 3, 31, 13}; - // create output array for bech32 string - char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; + // create output for bech32 string + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); // encode - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); - // prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum + // prints "hello1w0rldjn365x" : "hello" + Bech32.separator + encoded data + 6 char checksum printf("bech32 encoding of human-readable part \'hello\' and data part \'[14, 15, 3, 31, 13]\' is:\n"); - printf("%s\n", bstr); + printf("%s\n", bstring->string); // allocate memory for decoded data - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstring->string); // decode - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); - assert(strcmp(hrpdp->hrp, "hello") == 0); + assert(bech32_decode(hrpdp, bstring->string) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, hrp) == 0); + assert(hrpdp->dp[0] == dp[0]); + assert(hrpdp->dp[4] == dp[4]); + assert(ENCODING_BECH32M == hrpdp->encoding); // free memory - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); } ``` + +For more C examples, see `examples/c_example.cpp` + + +## Regarding bech32 checksums + +The Bech32 data encoding format was first proposed by Pieter Wuille in early 2017 in +[BIP 0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). Later, in November 2019, Pieter published +some research regarding that an exponent used in the bech32 checksum algorithm (value = 1) may not be +optimal for the error detecting properties of bech32. In February 2021, Pieter published +[BIP 0350](http://www.example.com) reporting that "exhaustive analysis" showed the best possible exponent value is +0x2bc830a3. This improved variant of Bech32 is called "Bech32m". + +When decoding a possible bech32 encoded string, libbech32 returns an enum value showing whether bech32m or bech32 +was used to encode. This can be seen in the exaples above. + +When encoding data, libbech32 defaults to using the new exponent value of 0x2bc830a3. If the original exponent value +of 1 is desired, then the following functions may be used: + +### C++ Usage Example + +```cpp + /// ... as above ... + + // encode + std::string bstr = bech32::encodeUsingOriginalConstant(hrp, data); + + /// ... as above ... +``` + +### C Usage Example + +```C + /// ... as above ... + + // encode + assert(bech32_encode_using_original_constant(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); + + /// ... as above ... +``` diff --git a/examples/c_example.c b/examples/c_example.c index 58f4d04..ff0401d 100644 --- a/examples/c_example.c +++ b/examples/c_example.c @@ -12,55 +12,62 @@ void encodeAndDecode() { char hrp[] = "example"; unsigned char dp[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; - // create output array for bech32 string - char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; + // create output for bech32 string + bech32_bstring *bstring = bech32_create_bstring(sizeof(hrp), sizeof(dp)); // expected bech32 string output - char expected[] = "example1qpzry9x8gnylnjs"; + char expected[] = "example1qpzry9x8ge8sqgv"; // encode - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); - assert(strcmp(expected, bstr) == 0); - - printf("bech32 encoding of human-readable part \'example\' and data part \'[0, 1, 2, 3, 4, 5, 6, 7, 8]\' is:\n"); - printf("%s\n", bstr); + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); + assert(strcmp(expected, bstring->string) == 0); // allocate memory for decoded data - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstring->string); // decode - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); + assert(bech32_decode(hrpdp, bstring->string) == E_BECH32_SUCCESS); assert(strcmp(hrpdp->hrp, "example") == 0); assert(hrpdp->dp[0] == 0); assert(hrpdp->dp[8] == 8); + assert(ENCODING_BECH32M == hrpdp->encoding); // free memory - free_HrpAndDp_storage(hrpdp); - + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); } void decodeAndEncode() { // bech32 string with extra invalid characters - char bstr[] = " example1:qpz!r--y9#x8&%&%gn-y-lnjs "; + char bstr[] = " example1:qpz!r--y9#x8&%&%ge-8-sqgv "; // expected bech32 string output - char expected[] = "example1qpzry9x8gnylnjs"; + char expected[] = "example1qpzry9x8ge8sqgv"; - // strip unwanted chars from bstr - bech32_stripUnknownChars(bstr, sizeof(bstr), bstr, sizeof(bstr)); + // make sure to strip invalid characters before allocating storage and trying to decode + assert(bech32_stripUnknownChars(bstr, sizeof(bstr), bstr, sizeof(bstr)) == E_BECH32_SUCCESS); // allocate memory for decoded data - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); // decode - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); assert(strcmp(hrpdp->hrp, "example") == 0); assert(hrpdp->dp[0] == 0); assert(hrpdp->dp[8] == 8); - // free memory - free_HrpAndDp_storage(hrpdp); + // create output array for bech32 string + bech32_bstring *bstring = bech32_create_bstring_from_HrpAndDp(hrpdp); + // encode + assert(bech32_encode(bstring, hrpdp->hrp, hrpdp->dp, hrpdp->dplen) == E_BECH32_SUCCESS); + + // encoding of "cleaned" decoded data should match expected string + assert(strcmp(expected, bstring->string) == 0); + + // free memory + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); } void badEncoding() { @@ -69,35 +76,54 @@ void badEncoding() { char hrp[] = "example"; unsigned char dp[] = {0, 1, 2, 3, 4, 5, 6, 7, 33}; - // create output array for bech32 string - char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; + // create output for bech32 string + bech32_bstring *bstring = bech32_create_bstring(sizeof(hrp), sizeof(dp)); // encode - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR); + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR); + // free memory + bech32_free_bstring(bstring); } -void badDecoding() { +void badDecoding_corruptData() { - // bech32 string with extra invalid characters - char bstr[] = "example1qpzry9x8gnylnjs"; + // bech32 string + char bstr[] = "example1qpzry9x8ge8sqgv"; // simulate corrupted data--checksum verification will fail bstr[10] = 'x'; // allocate memory for decoded data - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); // decode - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_INVALID_CHECKSUM); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_INVALID_CHECKSUM); // free memory - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); +} + +void badDecoding_corruptChecksum() { + + // bech32 string + char bstr[] = "example1qpzry9x8ge8sqgv"; + // simulate corrupted checksum--verification will fail + bstr[19] = 'q'; + // allocate memory for decoded data + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); + + // decode + assert(bech32_decode(hrpdp, bstr) == E_BECH32_INVALID_CHECKSUM); + + // free memory + bech32_free_HrpAndDp(hrpdp); } int main() { encodeAndDecode(); decodeAndEncode(); badEncoding(); - badDecoding(); + badDecoding_corruptData(); + badDecoding_corruptChecksum(); } diff --git a/examples/c_usage_example.c b/examples/c_usage_example.c index e4ba431..f720835 100644 --- a/examples/c_usage_example.c +++ b/examples/c_usage_example.c @@ -1,3 +1,4 @@ +// This small example is mainly meant for easy copy/paste into the README.md #include "libbech32.h" #include #include @@ -11,23 +12,27 @@ int main() { char hrp[] = "hello"; unsigned char dp[] = {14, 15, 3, 31, 13}; - // create output array for bech32 string - char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; + // create output for bech32 string + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); // encode - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); - // prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum + // prints "hello1w0rldjn365x" : "hello" + Bech32.separator + encoded data + 6 char checksum printf("bech32 encoding of human-readable part \'hello\' and data part \'[14, 15, 3, 31, 13]\' is:\n"); - printf("%s\n", bstr); + printf("%s\n", bstring->string); // allocate memory for decoded data - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstring->string); // decode - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); - assert(strcmp(hrpdp->hrp, "hello") == 0); + assert(bech32_decode(hrpdp, bstring->string) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, hrp) == 0); + assert(hrpdp->dp[0] == dp[0]); + assert(hrpdp->dp[4] == dp[4]); + assert(ENCODING_BECH32M == hrpdp->encoding); // free memory - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); } diff --git a/examples/cpp_example.cpp b/examples/cpp_example.cpp index a5c62eb..c5fe020 100644 --- a/examples/cpp_example.cpp +++ b/examples/cpp_example.cpp @@ -12,30 +12,32 @@ void encodeAndDecode() { // data values can be 0-31 std::vector data = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + // expected bech32 string output + char expected[] = "example1qpzry9x8ge8sqgv"; + // encode std::string bstr = bech32::encode(hrp, data); - - // will print "example1qpzry9x8gnylnjs" ... last 6 characters are the checksum - std::cout << R"(bech32 encoding of human-readable part 'example' and data part '[0, 1, 2, 3, 4, 5, 6, 7, 8]' is:)" << std::endl; - std::cout << bstr << std::endl; + assert(expected == bstr); // decode bech32::HrpAndDp hd = bech32::decode(bstr); assert(hrp == hd.hrp); assert(data == hd.dp); + assert(bech32::Encoding::Bech32m == hd.encoding); } void decodeAndEncode() { // bech32 string with extra invalid characters - std::string bstr = " example1:qpz!r--y9#x8&%&%gn-y-lnjs "; - std::string expected = "example1qpzry9x8gnylnjs"; + std::string bstr = " example1:qpz!r--y9#x8&%&%ge-8-sqgv "; + std::string expected = "example1qpzry9x8ge8sqgv"; // decode - make sure to strip invalid characters before trying to decode bech32::HrpAndDp hd = bech32::decode(bech32::stripUnknownChars(bstr)); // verify decoding assert(!hd.hrp.empty() && !hd.dp.empty()); + assert(bech32::Encoding::Bech32m == hd.encoding); // encode bstr = bech32::encode(hd.hrp, hd.dp); @@ -43,22 +45,13 @@ void decodeAndEncode() { // encoding of "cleaned" decoded data should match expected string assert(bstr == expected); - // simulate corrupted data--checksum verification will fail - bstr[10] = 'x'; - - // decode - make sure to strip invalid characters before trying to decode - hd = bech32::decode(bech32::stripUnknownChars(bstr)); - - // verify decoding failed - assert(hd.hrp.empty() && hd.dp.empty()); - } void badEncoding() { // human-readable part std::string hrp = "example"; - // data values can be 0-31 + // data values can be 0-31. "33" is invalid std::vector data = {0, 1, 2, 3, 4, 5, 6, 7, 33}; // encode @@ -70,10 +63,10 @@ void badEncoding() { } } -void badDecoding() { +void badDecoding_corruptData() { // valid bech32 string - std::string bstr = "example1qpzry9x8gnylnjs"; + std::string bstr = "example1qpzry9x8ge8sqgv"; // simulate corrupted data--checksum verification will fail bstr[10] = 'x'; @@ -82,6 +75,23 @@ void badDecoding() { // verify decoding failed assert(hd.hrp.empty() && hd.dp.empty()); + assert(bech32::Encoding::None == hd.encoding); + +} + +void badDecoding_corruptChecksum() { + + // valid bech32 string + std::string bstr = "example1qpzry9x8ge8sqgv"; + // simulate corrupted checksum--verification will fail + bstr[19] = 'q'; + + // decode + bech32::HrpAndDp hd = bech32::decode(bstr); + + // verify decoding failed + assert(hd.hrp.empty() && hd.dp.empty()); + assert(bech32::Encoding::None == hd.encoding); } @@ -89,5 +99,6 @@ int main() { encodeAndDecode(); decodeAndEncode(); badEncoding(); - badDecoding(); + badDecoding_corruptData(); + badDecoding_corruptChecksum(); } diff --git a/examples/cpp_usage_example.cpp b/examples/cpp_usage_example.cpp index 99cf672..63671da 100644 --- a/examples/cpp_usage_example.cpp +++ b/examples/cpp_usage_example.cpp @@ -1,3 +1,4 @@ +// This small example is mainly meant for easy copy/paste into the README.md #include "libbech32.h" #include @@ -13,7 +14,7 @@ int main() { // encode std::string bstr = bech32::encode(hrp, data); - // prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum + // prints "hello1w0rldjn365x" : "hello" + Bech32.separator + encoded data + 6 char checksum std::cout << R"(bech32 encoding of human-readable part 'hello' and data part '[14, 15, 3, 31, 13]' is:)" << std::endl; std::cout << bstr << std::endl; @@ -22,4 +23,5 @@ int main() { assert(hrp == hd.hrp); assert(data == hd.dp); + assert(bech32::Encoding::Bech32m == hd.encoding); } diff --git a/include/libbech32/libbech32.h b/include/libbech32/libbech32.h index 9957c32..2a61df9 100644 --- a/include/libbech32/libbech32.h +++ b/include/libbech32/libbech32.h @@ -9,6 +9,14 @@ namespace bech32 { + // Encoding: denotes which encoding was used for a bech32 string + enum Encoding { + None, // no encoding was detected + Unknown, // encoding has not yet been determined + Bech32, // encoding uses original checksum constant (1) + Bech32m // encoding used default checksum constant (M = 0x2bc830a3) + }; + // The Bech32 separator character static const char separator = '1'; @@ -16,6 +24,7 @@ namespace bech32 { // hrp: the human-readable part // dp: the data part struct HrpAndDp { + Encoding encoding; std::string hrp; std::vector dp; }; @@ -24,9 +33,12 @@ namespace bech32 { // the separator character, which is '1' std::string stripUnknownChars(const std::string & bstring); - // encode a "human-readable part" and a "data part", returning a bech32 string + // encode a "human-readable part" and a "data part", returning a bech32m string std::string encode(const std::string & hrp, const std::vector & dp); + // encode a "human-readable part" and a "data part", returning a bech32 string + std::string encodeUsingOriginalConstant(const std::string & hrp, const std::vector & dp); + // decode a bech32 string, returning the "human-readable part" and a "data part" HrpAndDp decode(const std::string & bstring); @@ -45,6 +57,9 @@ namespace bech32 { const int MIN_HRP_LENGTH = 1; const int MAX_HRP_LENGTH = 83; + // separator character is always 1 char long + const int SEPARATOR_LENGTH = 1; + // checksum is always 6 chars long const int CHECKSUM_LENGTH = 6; @@ -67,13 +82,35 @@ namespace bech32 { extern "C" { #endif +/** +* Represents a bech32 encoded string. +*/ +typedef struct bech32_bstring_s { + char * string; + size_t length; +} bech32_bstring; + +/** + * Encoding: denotes which encoding was used for a bech32 string + */ +typedef enum bech32_encoding_e { + ENCODING_NONE, // no encoding was detected + ENCODING_UNKNOWN, // encoding has not yet been determined + ENCODING_BECH32, // encoding uses original checksum constant (1) + ENCODING_BECH32M // encoding used default checksum constant (M = 0x2bc830a3) +} bech32_encoding; + /** * Represents the payload within a bech32 string. * + * encoding: which encoding is used for this bech32 string (see: bech32_encoding enum) * hrp: the human-readable part + * hrplen: length of the human-readable part (not including trailing NULL char) * dp: the data part + * dplen: length of the data part */ typedef struct bech32_HrpAndDp_s { + bech32_encoding encoding; char * hrp; size_t hrplen; unsigned char * dp; @@ -99,29 +136,71 @@ typedef enum bech32_error_e extern const char *bech32_errordesc[]; /** - * Returns error message corresponding to the error code + * Returns error message string corresponding to the error code * * @param error_code the error code to convert * - * @return error message corresponding to the error code + * @return error message string corresponding to the error code */ extern const char * bech32_strerror(bech32_error error_code); /** * Allocates memory for a bech32_HrpAndDp struct based on the size of the bech32 string passed in. * - * This memory must be freed using the free_HrpAndDp_storage function. + * This memory must be freed using the bech32_free_HrpAndDp function. * - * @param bstr the bech32 string to be decoded by bech32_decode() + * @param str the bech32 string to be decoded by bech32_decode() * * @return a pointer to a new bech32_HrpAndDp struct, or NULL if error */ -extern bech32_HrpAndDp * create_HrpAndDp_storage(const char *bstr); +extern bech32_HrpAndDp * bech32_create_HrpAndDp(const char *str); + +/** + * Frees memory for a bech32_HrpAndDp struct. + * + * @param hrpAndDp pointer to a bech32_HrpAndDp struct + */ +extern void bech32_free_HrpAndDp(bech32_HrpAndDp *hrpAndDp); + +/** + * Computes final length for a to-be-encoded bech32 string + * + * @param hrplen the length of the "human-readable part" string. must be > 0 + * @param dplen the length of the "data part" array + * + * @return length of to-be-encoded bech32 string + */ +extern size_t bech32_compute_encoded_string_length(size_t hrplen, size_t dplen); + +/** + * Allocates memory for a to-be-encoded bech32 string + * + * This memory must be freed using the bech32_free_bstring function. + * + * @param hrplen the length of the "human-readable part" string. must be > 0 + * @param dplen the length of the "data part" array + * + * @return a pointer to a new bech32_bstring struct, or NULL if error + */ +extern bech32_bstring * bech32_create_bstring(size_t hrplen, size_t dplen); + +/** + * Allocates memory for a to-be-encoded bech32 string based on the size of the bech32_HrpAndDp struct + * + * This memory must be freed using the bech32_free_bstring function. + * + * @param hrpAndDp a pointer to a bech32_HrpAndDp struct + * + * @return a pointer to a new bech32_bstring struct, or NULL if error + */ +extern bech32_bstring * bech32_create_bstring_from_HrpAndDp(bech32_HrpAndDp *hrpAndDp); /** - * Frees memory for a txref_LocationData struct. + * Frees memory for a bech32 string. + * + * @param bstring pointer to a bech32 string */ -extern void free_HrpAndDp_storage(bech32_HrpAndDp *hrpAndDp); +extern void bech32_free_bstring(bech32_bstring *bstring); /** * clean a bech32 string of any stray characters not in the allowed charset, except for the @@ -141,36 +220,50 @@ extern bech32_error bech32_stripUnknownChars( /** * encode a "human-readable part" (ex: "xyz") and a "data part" (ex: {1,2,3}), returning a - * bech32 string + * bech32m string * - * @param bstr pointer to memory to copy the output encoded bech32 string. - * @param bstrlen number of bytes allocated at bstr + * @param bstring pointer to a bech32_bstring struct to store the encoded bech32 string. * @param hrp pointer to the "human-readable part" - * @param hrplen the length of the "human-readable part" string * @param dp pointer to the "data part" * @param dplen the length of the "data part" array * - * @return E_BECH32_SUCCESS on success, others on error (hrp/dp/bstr is NULL, bstr not + * @return E_BECH32_SUCCESS on success, others on error (i.e., hrp/dp/bstring is NULL, bstring not * long enough for bech32 string) */ extern bech32_error bech32_encode( - char *bstr, size_t bstrlen, - const char *hrp, size_t hrplen, + bech32_bstring *bstring, + const char *hrp, + const unsigned char *dp, size_t dplen); + +/** + * encode a "human-readable part" (ex: "xyz") and a "data part" (ex: {1,2,3}), returning a + * bech32 string + * + * @param bstring pointer to a bech32_bstring struct to store the encoded bech32 string. + * @param hrp pointer to the "human-readable part" + * @param dp pointer to the "data part" + * @param dplen the length of the "data part" array + * + * @return E_BECH32_SUCCESS on success, others on error (i.e., hrp/dp/bstring is NULL, bstring not + * long enough for bech32 string) + */ +extern bech32_error bech32_encode_using_original_constant( + bech32_bstring *bstring, + const char *hrp, const unsigned char *dp, size_t dplen); /** * decode a bech32 string, returning the "human-readable part" and a "data part" * * @param output pointer to struct to copy the decoded "human-readable part" and "data part" - * @param bstr the bech32 string to decode - * @param bstrlen the length of the bech32 string + * @param str the bech32 string to decode * - * @return E_BECH32_SUCCESS on success, others on error (hrp/dp/bstr is NULL, hrp/dp not + * @return E_BECH32_SUCCESS on success, others on error (i.e., output is NULL, hrp/dp not * long enough for decoded bech32 data) */ extern bech32_error bech32_decode( bech32_HrpAndDp *output, - const char *bstr, size_t bstrlen); + const char *str); #ifdef __cplusplus } diff --git a/libbech32/bech32.cpp b/libbech32/bech32.cpp index b8a5429..4db1810 100644 --- a/libbech32/bech32.cpp +++ b/libbech32/bech32.cpp @@ -7,15 +7,15 @@ namespace { using namespace bech32::limits; // exponent used in checksum generation, taken from recommendation - // in: https://gist.github.com/sipa/a9845b37c1b298a7301c33a04090b2eb - const unsigned int M = 0x3FFFFFFF; + // in: https://github.com/sipa/bips/blob/bip-bech32m/bip-bech32m.mediawiki + const unsigned int M = 0x2bc830a3; /** The Bech32 character set for encoding. The index into this string gives the char * each value is mapped to, i.e., 0 -> 'q', 10 -> '2', etc. This comes from the table * in BIP-0173 */ const char charset[VALID_CHARSET_SIZE] = { - 'q', 'p', 'z', 'r', 'y', '9', 'x', '8', 'g', 'f', '2', 't', 'v', 'd', 'w', '0', - 's', '3', 'j', 'n', '5', '4', 'k', 'h', 'c', 'e', '6', 'm', 'u', 'a', '7', 'l' + 'q', 'p', 'z', 'r', 'y', '9', 'x', '8', 'g', 'f', '2', 't', 'v', 'd', 'w', '0', // indexes 0 - F + 's', '3', 'j', 'n', '5', '4', 'k', 'h', 'c', 'e', '6', 'm', 'u', 'a', '7', 'l' // indexes 10 - 1F }; /** The Bech32 character set for decoding. This comes from the table in BIP-0173 @@ -24,7 +24,7 @@ namespace { * into the above charset). For instance, 'Q' (ascii 81) and 'q' (ascii 113) * are both set to index 0 in this table. Invalid chars are set to -1 */ const int REVERSE_CHARSET_SIZE = 128; - const int8_t charset_rev[REVERSE_CHARSET_SIZE] = { + const int8_t reverse_charset[REVERSE_CHARSET_SIZE] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -96,19 +96,19 @@ namespace { for(std::string::size_type i = 0; i < dpstr.size(); ++i) { dp[i] = static_cast(dpstr[i]); } - return {hrp, dp}; + return {bech32::Encoding::Unknown, hrp, dp}; } void convertToLowercase(std::string & str) { std::transform(str.begin(), str.end(), str.begin(), &::tolower); } - // dp needs to be mapped using the charset_rev table + // dp needs to be mapped using the reverse_charset table void mapDP(std::vector &dp) { for(unsigned char &c : dp) { if(c > REVERSE_CHARSET_SIZE - 1) throw std::runtime_error("data part contains character value out of range"); - int8_t d = charset_rev[c]; + int8_t d = reverse_charset[c]; if(d == -1) throw std::runtime_error("data part contains invalid character"); c = static_cast(d); @@ -172,6 +172,10 @@ namespace { return polymod(cat(expandHrp(hrp), dp)) == M; } + bool verifyChecksumUsingOriginalConstant(const std::string &hrp, const std::vector &dp) { + return polymod(cat(expandHrp(hrp), dp)) == 1; + } + void stripChecksum(std::vector &dp) { dp.erase(dp.end() - CHECKSUM_LENGTH, dp.end()); } @@ -188,6 +192,18 @@ namespace { return ret; } + std::vector + createChecksumUsingOriginalConstant(const std::string &hrp, const std::vector &dp) { + std::vector c = cat(expandHrp(hrp), dp); + c.resize(c.size() + CHECKSUM_LENGTH); + uint32_t mod = polymod(c) ^ 1; + std::vector ret(CHECKSUM_LENGTH); + for(std::vector::size_type i = 0; i < CHECKSUM_LENGTH; ++i) { + ret[i] = static_cast((mod >> (5 * (5 - i))) & 31u); + } + return ret; + } + void rejectHRPTooShort(const std::string &hrp) { if(hrp.size() < MIN_HRP_LENGTH) throw std::runtime_error("HRP must be at least one character"); @@ -242,7 +258,8 @@ namespace bech32 { } // encode a "human-readable part" and a "data part", returning a bech32 string - std::string encode(const std::string &hrp, const std::vector &dp) { + std::string encodeBasis(const std::string &hrp, const std::vector &dp, + std::vector (*checksumFunc)(const std::string &, const std::vector &)) { rejectHRPTooShort(hrp); rejectHRPTooLong(hrp); rejectBothPartsTooLong(hrp, dp); @@ -250,7 +267,7 @@ namespace bech32 { std::string hrpCopy = hrp; convertToLowercase(hrpCopy); - std::vector checksum = createChecksum(hrpCopy, dp); + std::vector checksum = checksumFunc(hrpCopy, dp); std::string ret = hrpCopy + '1'; std::vector combined = cat(dp, checksum); ret.reserve(ret.size() + combined.size()); @@ -262,6 +279,16 @@ namespace bech32 { return ret; } + // encode a "human-readable part" and a "data part", returning a bech32 string + std::string encode(const std::string &hrp, const std::vector &dp) { + return encodeBasis(hrp, dp, &createChecksum); + } + + // encode a "human-readable part" and a "data part", returning a bech32 string + std::string encodeUsingOriginalConstant(const std::string &hrp, const std::vector &dp) { + return encodeBasis(hrp, dp, &createChecksumUsingOriginalConstant); + } + // decode a bech32 string, returning the "human-readable part" and a "data part" HrpAndDp decode(const std::string & bstring) { rejectBStringThatIsntWellFormed(bstring); @@ -273,6 +300,12 @@ namespace bech32 { mapDP(b.dp); if (verifyChecksum(b.hrp, b.dp)) { stripChecksum(b.dp); + b.encoding = bech32::Encoding::Bech32m; + return b; + } + else if (verifyChecksumUsingOriginalConstant(b.hrp, b.dp)) { + stripChecksum(b.dp); + b.encoding = bech32::Encoding::Bech32; return b; } else { @@ -314,32 +347,44 @@ const char * bech32_strerror(bech32_error error_code) { /** * Allocates memory for a bech32_HrpAndDp struct based on the size of the bech32 string passed in. * - * This memory must be freed using the free_HrpAndDp_storage function. + * This memory must be freed using the bech32_free_HrpAndDp function. * - * @param bstr the bech32 string to be decoded by bech32_decode() + * @param str the bech32 string to be decoded by bech32_decode() * * @return a pointer to a new bech32_HrpAndDp struct, or NULL if error */ extern "C" -bech32_HrpAndDp * create_HrpAndDp_storage(const char *bstr) { +bech32_HrpAndDp * bech32_create_HrpAndDp(const char *str) { + if(str == nullptr) + return nullptr; // the storage needed for a decoded bech32 string can be easily determined by the // length of the input string - std::string inputStr(bstr); - size_t index_of_separator = inputStr.find_first_of('1'); + std::string inputStr(str); + if(inputStr.size() < MIN_BECH32_LENGTH) + return nullptr; + size_t index_of_separator = inputStr.find_first_of(bech32::separator); + if(index_of_separator == std::string::npos) + return nullptr; size_t number_of_hrp_characters = index_of_separator; + if(inputStr.length() - number_of_hrp_characters - 1 < bech32::limits::CHECKSUM_LENGTH) + // not enough data characters + return nullptr; size_t number_of_data_characters = - inputStr.length() - (index_of_separator+1) - bech32::limits::CHECKSUM_LENGTH; // +1 for zero-based index + inputStr.length() + - number_of_hrp_characters + - 1 // for separator character + - bech32::limits::CHECKSUM_LENGTH; auto hrpdp = static_cast(malloc(sizeof (bech32_HrpAndDp))); if(hrpdp == nullptr) return nullptr; - hrpdp->hrplen = number_of_hrp_characters + 1; // +1 for '\0' - hrpdp->hrp = static_cast(calloc(hrpdp->hrplen, 1)); + hrpdp->hrplen = number_of_hrp_characters; + hrpdp->hrp = static_cast(calloc(hrpdp->hrplen+1, 1)); // +1 for '\0' if(hrpdp->hrp == nullptr) { free(hrpdp); return nullptr; } - hrpdp->dplen = number_of_data_characters + 1; // +1 for '\0' + hrpdp->dplen = number_of_data_characters; hrpdp->dp = static_cast(calloc(hrpdp->dplen, 1)); if(hrpdp->dp == nullptr) { free(hrpdp->hrp); @@ -347,17 +392,87 @@ bech32_HrpAndDp * create_HrpAndDp_storage(const char *bstr) { return nullptr; } + hrpdp->encoding = ENCODING_UNKNOWN; + return hrpdp; } /** - * Frees memory for a txref_LocationData struct. + * Frees memory for a bech32_HrpAndDp struct. + * + * @param hrpAndDp a pointer to a bech32_HrpAndDp struct */ extern "C" -void free_HrpAndDp_storage(bech32_HrpAndDp *hrpdp) { - free(hrpdp->dp); - free(hrpdp->hrp); - free(hrpdp); +void bech32_free_HrpAndDp(bech32_HrpAndDp *hrpAndDp) { + if(hrpAndDp == nullptr) + return; + free(hrpAndDp->dp); + free(hrpAndDp->hrp); + free(hrpAndDp); +} + +/** + * Computes final length for a to-be-encoded bech32 string + * + * @param hrplen the length of the "human-readable part" string. must be > 0 + * @param dplen the length of the "data part" array + * + * @return length of to-be-encoded bech32 string + */ +extern "C" +size_t bech32_compute_encoded_string_length(size_t hrplen, size_t dplen) { + return hrplen + SEPARATOR_LENGTH + dplen + CHECKSUM_LENGTH; +} + +/** + * Allocates memory for a to-be-encoded bech32 string + * + * This memory must be freed using the bech32_free_bstring function. + * + * @param hrplen the length of the "human-readable part" string. must be > 0 + * @param dplen the length of the "data part" array + * + * @return a pointer to a new bech32_bstring struct, or NULL if error + */ +extern "C" +bech32_bstring * bech32_create_bstring(size_t hrplen, size_t dplen) { + if(hrplen < 1) + return nullptr; + auto *bstring = static_cast(malloc(sizeof(bech32_bstring))); + bstring->length = bech32_compute_encoded_string_length(hrplen, dplen); + bstring->string = static_cast(calloc(bstring->length + 1, 1)); // +1 for '\0' + return bstring; +} + +/** + * Allocates memory for a to-be-encoded bech32 string based on the size of the bech32_HrpAndDp struct + * + * This memory must be freed using the bech32_free_bstring function. + * + * @param hrpAndDp a pointer to a bech32_HrpAndDp struct + * + * @return a pointer to a new bech32_bstring struct, or NULL if error + */ +extern "C" +bech32_bstring * bech32_create_bstring_from_HrpAndDp(bech32_HrpAndDp *hrpAndDp) { + if(hrpAndDp == nullptr) + return nullptr; + if(hrpAndDp->hrplen < 1) + return nullptr; + return bech32_create_bstring(hrpAndDp->hrplen, hrpAndDp->dplen); +} + +/** + * Frees memory for a bech32 string. + * + * @param bstring pointer to a bech32 string + */ +extern "C" +void bech32_free_bstring(bech32_bstring *bstring) { + if(bstring == nullptr) + return; + free(bstring->string); + free(bstring); } /** @@ -397,25 +512,23 @@ bech32_error bech32_stripUnknownChars( /** * encode a "human-readable part" (ex: "xyz") and a "data part" (ex: {1,2,3}), returning a - * bech32 string + * bech32m string * - * @param bstr pointer to memory to copy the output encoded bech32 string. - * @param bstrlen number of bytes allocated at bstr + * @param bstring pointer to a bech32_bstring struct to store the encoded bech32 string. * @param hrp pointer to the "human-readable part" - * @param hrplen the length of the "human-readable part" string * @param dp pointer to the "data part" * @param dplen the length of the "data part" array * - * @return E_BECH32_SUCCESS on success, others on error (hrp/dp/bstr is NULL, bstr not + * @return E_BECH32_SUCCESS on success, others on error (i.e., hrp/dp/bstring is NULL, bstring not * long enough for bech32 string) */ extern "C" bech32_error bech32_encode( - char *bstr, size_t bstrlen, - const char *hrp, size_t hrplen, + bech32_bstring *bstring, + const char *hrp, const unsigned char *dp, size_t dplen) { - if(bstr == nullptr) + if(bstring == nullptr) return E_BECH32_NULL_ARGUMENT; if(hrp == nullptr) return E_BECH32_NULL_ARGUMENT; @@ -423,8 +536,6 @@ bech32_error bech32_encode( return E_BECH32_NULL_ARGUMENT; std::string hrpStr(hrp); - if(hrpStr.size() > hrplen-1) - return E_BECH32_LENGTH_TOO_SHORT; std::vector dpVec(dp, dp + dplen); std::string b; @@ -435,11 +546,56 @@ bech32_error bech32_encode( // todo: convert exception message return E_BECH32_UNKNOWN_ERROR; } - if(b.size() > bstrlen-1) + if(b.size() > bstring->length) + return E_BECH32_LENGTH_TOO_SHORT; + + std::copy_n(b.begin(), b.size(), bstring->string); + bstring->string[b.size()] = '\0'; + + return E_BECH32_SUCCESS; +} + +/** + * encode a "human-readable part" (ex: "xyz") and a "data part" (ex: {1,2,3}), returning a + * bech32 string + * + * @param bstring pointer to a bech32_bstring struct to store the encoded bech32 string. + * @param hrp pointer to the "human-readable part" + * @param dp pointer to the "data part" + * @param dplen the length of the "data part" array + * + * @return E_BECH32_SUCCESS on success, others on error (i.e., hrp/dp/sbtring is NULL, bstring not + * long enough for bech32 string) + */ +extern "C" +bech32_error bech32_encode_using_original_constant( + bech32_bstring *bstring, + const char *hrp, + const unsigned char *dp, size_t dplen) { + + if(bstring == nullptr) + return E_BECH32_NULL_ARGUMENT; + if(hrp == nullptr) + return E_BECH32_NULL_ARGUMENT; + if(dp == nullptr) + return E_BECH32_NULL_ARGUMENT; + + std::string hrpStr(hrp); + std::vector dpVec(dp, dp + dplen); + + std::string b; + try { + b = bech32::encodeUsingOriginalConstant(hrpStr, dpVec); + } + catch (std::exception &) { + // todo: convert exception message + return E_BECH32_UNKNOWN_ERROR; + } + if(b.size() > bstring->length) return E_BECH32_LENGTH_TOO_SHORT; - std::copy_n(b.begin(), b.size(), bstr); - bstr[b.size()] = '\0'; + std::copy_n(b.begin(), b.size(), bstring->string); + bstring->string[b.size()] = '\0'; return E_BECH32_SUCCESS; } @@ -448,14 +604,13 @@ bech32_error bech32_encode( * decode a bech32 string, returning the "human-readable part" and a "data part" * * @param output pointer to struct to copy the decoded "human-readable part" and "data part" - * @param bstr the bech32 string to decode - * @param bstrlen the length of the bech32 string + * @param str the bech32 string to decode * - * @return E_BECH32_SUCCESS on success, others on error (hrp/dp/bstr is NULL, hrp/dp not + * @return E_BECH32_SUCCESS on success, others on error (i.e., output is NULL, hrp/dp not * long enough for decoded bech32 data) */ extern "C" -bech32_error bech32_decode(bech32_HrpAndDp *output, char const *bstr, size_t bstrlen) { +bech32_error bech32_decode(bech32_HrpAndDp *output, char const *str) { if(output == nullptr) return E_BECH32_NULL_ARGUMENT; @@ -463,12 +618,10 @@ bech32_error bech32_decode(bech32_HrpAndDp *output, char const *bstr, size_t bst return E_BECH32_NULL_ARGUMENT; if(output->dp == nullptr) return E_BECH32_NULL_ARGUMENT; - if(bstr == nullptr) + if(str == nullptr) return E_BECH32_NULL_ARGUMENT; - std::string inputStr(bstr); - if(inputStr.size() > bstrlen-1) - return E_BECH32_LENGTH_TOO_SHORT; + std::string inputStr(str); bech32::HrpAndDp hrpAndDp; try { @@ -481,11 +634,12 @@ bech32_error bech32_decode(bech32_HrpAndDp *output, char const *bstr, size_t bst if(hrpAndDp.hrp.empty() && hrpAndDp.dp.empty()) return E_BECH32_INVALID_CHECKSUM; - if(hrpAndDp.hrp.size() > output->hrplen-1) + if(hrpAndDp.hrp.size() > output->hrplen) return E_BECH32_LENGTH_TOO_SHORT; if(hrpAndDp.dp.size() > output->dplen) return E_BECH32_LENGTH_TOO_SHORT; + output->encoding = static_cast(hrpAndDp.encoding); std::copy_n(hrpAndDp.hrp.begin(), hrpAndDp.hrp.size(), output->hrp); output->hrp[hrpAndDp.hrp.size()] = '\0'; std::copy_n(hrpAndDp.dp.begin(), hrpAndDp.dp.size(), output->dp); diff --git a/test/testbech32/CMakeLists.txt b/test/testbech32/CMakeLists.txt index 95380ea..f991922 100644 --- a/test/testbech32/CMakeLists.txt +++ b/test/testbech32/CMakeLists.txt @@ -27,3 +27,17 @@ target_link_libraries(bech32_c_api_tests PUBLIC bech32) add_test(NAME UnitTests_C_api_bech32 COMMAND bech32_c_api_tests) + + +add_executable(bech32_api_tests + bech32_api_tests.cpp + ) + +target_compile_features(bech32_api_tests PRIVATE cxx_std_11) +target_compile_options(bech32_api_tests PRIVATE ${DCD_CXX_FLAGS}) +set_target_properties(bech32_api_tests PROPERTIES CXX_EXTENSIONS OFF) + +target_link_libraries(bech32_api_tests PUBLIC bech32) + +add_test(NAME UnitTests_api_bech32 + COMMAND bech32_api_tests) diff --git a/test/testbech32/bech32_api_tests.cpp b/test/testbech32/bech32_api_tests.cpp new file mode 100644 index 0000000..4ca04e5 --- /dev/null +++ b/test/testbech32/bech32_api_tests.cpp @@ -0,0 +1,287 @@ +// test program calling bech32 library from C++ + +#include "libbech32.h" +#include + +// make sure we can run these tests even when building a release version +#undef NDEBUG +#include + + +void stripUnknownChars_withSimpleString_returnsSameString() { + std::string src = "ffff"; + assert(src == bech32::stripUnknownChars(src)); +} + +void stripUnknownChars_withComplexString_returnsStrippedString() { + std::string src = "foobar"; + std::string expected = "far"; + assert(expected == bech32::stripUnknownChars(src)); +} + +void stripUnknownChars_withFunkyString_returnsStrippedString() { + std::string src = "tx1!rjk0\\u5ng*4jsf^^mc"; + std::string expected = "tx1rjk0u5ng4jsfmc"; + assert(expected == bech32::stripUnknownChars(src)); +} + +// ----- tests using default checksum constant = M (0x2bc830a3) ------ + +void decode_minimalExample_isSuccessful() { + std::string bstr = "a1lqfn3a"; + std::string expectedHrp = "a"; + + bech32::HrpAndDp hd = bech32::decode(bstr); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32m == hd.encoding); +} + +void decode_longExample_isSuccessful() { + std::string bstr = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"; + std::string expectedHrp = "abcdef"; + + bech32::HrpAndDp hd = bech32::decode(bstr); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32m == hd.encoding); + + assert(hd.dp[0] == '\x1f'); // first 'l' in above dp part + assert(hd.dp[31] == '\0'); // last 'q' in above dp part +} + +void decode_minimalExampleBadChecksum_isUnsuccessful() { + std::string bstr = "a1lqfn3q"; // last 'q' should be a 'a' + std::string expectedHrp = "a"; + + bech32::HrpAndDp hd = bech32::decode(bstr); + + assert(hd.hrp.empty()); + assert(hd.dp.empty()); +} + +void decode_whenMethodThrowsException_isUnsuccessful() { + // bech32 string can only have HRPs that are 83 chars or less. Attempt to decode a string + // with more than 83 chars and make sure that the exception thrown in the C++ code is caught + std::string bstr = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + std::string expectedHrp = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + try { + bech32::HrpAndDp hd = bech32::decode(bstr); + } + catch (std::runtime_error &e) { + assert(std::string(e.what()) == "bech32 string too long"); + } +} + +void encode_emptyExample_isUnsuccessful() { + std::string hrp; + std::vector dp = {}; + std::string expected = "a1lqfn3a"; + + try { + bech32::encode(hrp, dp); + } + catch (std::runtime_error &e) { + assert(std::string(e.what()) == "HRP must be at least one character"); + } +} + +void encode_minimalExample_isSuccessful() { + std::string hrp = "a"; + std::vector dp = {}; + std::string expected = "a1lqfn3a"; + + assert(expected == bech32::encode(hrp, dp)); +} + +void encode_smallExample_isSuccessful() { + std::string hrp = "xyz"; + std::vector dp = {1,2,3}; + std::string expected = "xyz1pzrs3usye"; + + assert(expected == bech32::encode(hrp, dp)); +} + +void encode_whenMethodThrowsException_isUnsuccessful() { + // bech32 string can only have HRPs that are 83 chars or less. Attempt to encode an HRP string + // with more than 83 chars and make sure that the exception thrown in the C++ code is caught + // and returns an error code + std::string hrp = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + std::vector dp = {1,2,3}; + + try { + bech32::encode(hrp, dp); + } + catch (std::runtime_error &e) { + assert(std::string(e.what()) == "HRP must be less than 84 characters"); + } +} + +void decode_and_encode_minimalExample_producesSameResult() { + std::string bstr1 = "a1lqfn3a"; + std::string expectedHrp = "a"; + + bech32::HrpAndDp hd = bech32::decode(bstr1); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32m == hd.encoding); + + std::string bstr2 = bech32::encode(hd.hrp, hd.dp); + + assert(bstr1 == bstr2); +} + +void decode_and_encode_smallExample_producesSameResult() { + std::string bstr1 = "xyz1pzrs3usye"; + std::string expectedHrp = "xyz"; + + bech32::HrpAndDp hd = bech32::decode(bstr1); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32m == hd.encoding); + + std::string bstr2 = bech32::encode(hd.hrp, hd.dp); + + assert(bstr1 == bstr2); +} + +void decode_and_encode_longExample_producesSameResult() { + std::string bstr1 = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"; + std::string expectedHrp = "abcdef"; + + bech32::HrpAndDp hd = bech32::decode(bstr1); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32m == hd.encoding); + + std::string bstr2 = bech32::encode(hd.hrp, hd.dp); + + assert(bstr1 == bstr2); +} + +// ---------- tests using original checksum constant = 1 ------------ + +void decode_c1_minimalExample_isSuccessful() { + std::string bstr = "a12uel5l"; + std::string expectedHrp = "a"; + + bech32::HrpAndDp hd = bech32::decode(bstr); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32 == hd.encoding); +} + +void decode_c1_longExample_isSuccessful() { + std::string bstr = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + std::string expectedHrp = "abcdef"; + + bech32::HrpAndDp hd = bech32::decode(bstr); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32 == hd.encoding); + + assert(hd.dp[0] == '\0'); // first 'q' in above dp part + assert(hd.dp[31] == '\x1f'); // last 'l' in above dp part +} + +void encode_c1_minimalExample_isSuccessful() { + std::string hrp = "a"; + std::vector dp = {}; + std::string expected = "a12uel5l"; + + assert(expected == bech32::encodeUsingOriginalConstant(hrp, dp)); +} + +void encode_c1_smallExample_isSuccessful() { + std::string hrp = "xyz"; + std::vector dp = {1,2,3}; + std::string expected = "xyz1pzr9dvupm"; + + assert(expected == bech32::encodeUsingOriginalConstant(hrp, dp)); +} + +void decode_and_encode_c1_minimalExample_producesSameResult() { + std::string bstr1 = "a12uel5l"; + std::string expectedHrp = "a"; + + bech32::HrpAndDp hd = bech32::decode(bstr1); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32 == hd.encoding); + + std::string bstr2 = bech32::encodeUsingOriginalConstant(hd.hrp, hd.dp); + + assert(bstr1 == bstr2); +} + +void decode_and_encode_c1_smallExample_producesSameResult() { + std::string bstr1 = "xyz1pzr9dvupm"; + std::string expectedHrp = "xyz"; + + bech32::HrpAndDp hd = bech32::decode(bstr1); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32 == hd.encoding); + + std::string bstr2 = bech32::encodeUsingOriginalConstant(hd.hrp, hd.dp); + + assert(bstr1 == bstr2); +} + +void decode_and_encode_c1_longExample_producesSameResult() { + std::string bstr1 = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + std::string expectedHrp = "abcdef"; + + bech32::HrpAndDp hd = bech32::decode(bstr1); + + assert(expectedHrp == hd.hrp); + assert(bech32::Encoding::Bech32 == hd.encoding); + + std::string bstr2 = bech32::encodeUsingOriginalConstant(hd.hrp, hd.dp); + + assert(bstr1 == bstr2); +} + + +void tests_using_default_checksum_constant() { + stripUnknownChars_withSimpleString_returnsSameString(); + stripUnknownChars_withComplexString_returnsStrippedString(); + stripUnknownChars_withFunkyString_returnsStrippedString(); + + decode_whenMethodThrowsException_isUnsuccessful(); + decode_minimalExample_isSuccessful(); + decode_longExample_isSuccessful(); + decode_minimalExampleBadChecksum_isUnsuccessful(); + + encode_whenMethodThrowsException_isUnsuccessful(); + encode_emptyExample_isUnsuccessful(); + encode_minimalExample_isSuccessful(); + encode_smallExample_isSuccessful(); + + decode_and_encode_minimalExample_producesSameResult(); + decode_and_encode_smallExample_producesSameResult(); + decode_and_encode_longExample_producesSameResult(); +} + +void tests_using_original_checksum_constant() { + decode_c1_minimalExample_isSuccessful(); + decode_c1_longExample_isSuccessful(); + + encode_c1_minimalExample_isSuccessful(); + encode_c1_smallExample_isSuccessful(); + + decode_and_encode_c1_minimalExample_producesSameResult(); + decode_and_encode_c1_smallExample_producesSameResult(); + decode_and_encode_c1_longExample_producesSameResult(); +} + +int main() { + + tests_using_default_checksum_constant(); + tests_using_original_checksum_constant(); + + return 0; +} + + diff --git a/test/testbech32/bech32_c_api_tests.c b/test/testbech32/bech32_c_api_tests.c index e51e87c..36d6223 100644 --- a/test/testbech32/bech32_c_api_tests.c +++ b/test/testbech32/bech32_c_api_tests.c @@ -72,6 +72,8 @@ void stripUnknownChars_withFunkyString_returnsStrippedString() { assert(strcmp(expected, dst) == 0); } +// ----- tests using default checksum constant = M (0x2bc830a3) ------ + void decode_withBadArgs_isUnsuccessful() { { // input is null @@ -83,7 +85,7 @@ void decode_withBadArgs_isUnsuccessful() { hrpdp->dplen = 10; hrpdp->dp = (unsigned char *) calloc(hrpdp->dplen, 1); - assert(bech32_decode(hrpdp, bstr, 0) == E_BECH32_NULL_ARGUMENT); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_NULL_ARGUMENT); free(hrpdp->dp); free(hrpdp->hrp); @@ -91,43 +93,43 @@ void decode_withBadArgs_isUnsuccessful() { } { // output is null - char bstr[] = "xyz1pzr6jnr79"; + char bstr[] = "xyz1pzrs3usye"; bech32_HrpAndDp *hrpdp = NULL; - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_NULL_ARGUMENT); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_NULL_ARGUMENT); } { // hrp is null - char bstr[] = "xyz1pzr6jnr79"; + char bstr[] = "xyz1pzrs3usye"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->dplen = 10; hrpdp->dp = (unsigned char *) calloc(hrpdp->dplen, 1); hrpdp->hrp = NULL; - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_NULL_ARGUMENT); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_NULL_ARGUMENT); free(hrpdp->dp); free(hrpdp); } { // dp is null - char bstr[] = "xyz1pzr6jnr79"; + char bstr[] = "xyz1pzrs3usye"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->hrplen = 1; hrpdp->hrp = (char *) calloc(hrpdp->hrplen, 1); hrpdp->dp = NULL; - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_NULL_ARGUMENT); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_NULL_ARGUMENT); free(hrpdp->hrp); free(hrpdp); } { // allocated hrp is too short - char bstr[] = "xyz1pzr6jnr79"; + char bstr[] = "xyz1pzrs3usye"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->hrplen = 1; @@ -135,7 +137,7 @@ void decode_withBadArgs_isUnsuccessful() { hrpdp->dplen = 10; hrpdp->dp = (unsigned char *) calloc(hrpdp->dplen, 1); - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_LENGTH_TOO_SHORT); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_LENGTH_TOO_SHORT); free(hrpdp->dp); free(hrpdp->hrp); @@ -143,7 +145,7 @@ void decode_withBadArgs_isUnsuccessful() { } { // allocated dp is too short - char bstr[] = "xyz1pzr6jnr79"; + char bstr[] = "xyz1pzrs3usye"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->hrplen = 10; @@ -151,7 +153,7 @@ void decode_withBadArgs_isUnsuccessful() { hrpdp->dplen = 1; hrpdp->dp = (unsigned char *) calloc(hrpdp->dplen, 1); - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_LENGTH_TOO_SHORT); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_LENGTH_TOO_SHORT); free(hrpdp->dp); free(hrpdp->hrp); @@ -160,54 +162,54 @@ void decode_withBadArgs_isUnsuccessful() { } void decode_minimalExample_isSuccessful() { - char bstr[] = "a14rxqtp"; + char bstr[] = "a1lqfn3a"; char expectedHrp[] = "a"; - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(ENCODING_BECH32M == hrpdp->encoding); - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); } void decode_longExample_isSuccessful() { - char bstr[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; + char bstr[] = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"; char expectedHrp[] = "abcdef"; - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); assert(strcmp(hrpdp->hrp, expectedHrp) == 0); - assert(hrpdp->dp[0] == '\0'); // 'q' in above dp part - assert(hrpdp->dp[31] == '\x1f'); // 'l' in above dp part + assert(hrpdp->dp[0] == '\x1f'); // first 'l' in above dp part + assert(hrpdp->dp[31] == '\0'); // last 'q' in above dp part + assert(ENCODING_BECH32M == hrpdp->encoding); - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); } void decode_minimalExampleBadChecksum_isUnsuccessful() { - char bstr[] = "a14rxqtq"; // last 'q' should be a 'p' - char expectedHrp[] = "a"; + char bstr[] = "a1lqfn3q"; // last 'q' should be a 'a' - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_INVALID_CHECKSUM); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_INVALID_CHECKSUM); - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); } -void decode_whenMethodThrowsException_isUnsuccessful() { +void decode_whenCppMethodThrowsException_isUnsuccessful() { // bech32 string can only have HRPs that are 83 chars or less. Attempt to decode a string // with more than 83 chars and make sure that the exception thrown in the C++ code is caught // and returns an error code char bstr[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; - char expectedHrp[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); - assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_UNKNOWN_ERROR); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_UNKNOWN_ERROR); - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); } void encode_withBadArgs_isUnsuccessful() { @@ -215,33 +217,37 @@ void encode_withBadArgs_isUnsuccessful() { { // output is null char hrp[] = "a"; unsigned char dp[] = {}; - char * bstr = NULL; + bech32_bstring * bstring = NULL; - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_NULL_ARGUMENT); + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_NULL_ARGUMENT); } - { // dp is null + { // hrp is null char *hrp = NULL; unsigned char dp[] = {}; - char bstr[] = {}; + bech32_bstring bstring; - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_NULL_ARGUMENT); + assert(bech32_encode(&bstring, hrp, dp, sizeof(dp)) == E_BECH32_NULL_ARGUMENT); } - { // hrp is null + { // dp is null char hrp[] = {}; unsigned char *dp = NULL; - char bstr[] = {}; + bech32_bstring bstring; - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_NULL_ARGUMENT); + assert(bech32_encode(&bstring, hrp, dp, sizeof(dp)) == E_BECH32_NULL_ARGUMENT); } - { // allocated bstr is too small + { // allocated string is too small char hrp[] = "xyz"; unsigned char dp[] = {1,2,3}; - char bstr[sizeof(hrp) + 1 + sizeof(dp) + 5] = {0}; // should be sizeof(hrp) + 1 + sizeof(dp) + 6 + bech32_bstring bstring; + bstring.length = 12; // length should (strlen(hrp) + 1 + sizeof(dp) + 6) = 13, but here we will mistakenly set it to 12 + bstring.string = (char *)calloc(bstring.length + 1, 1); //string size should be = string.length + 1 for '\0' + // Could also use bech32_create_bstring() to avoid this problem. - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_LENGTH_TOO_SHORT); + assert(bech32_encode(&bstring, hrp, dp, sizeof(dp)) == E_BECH32_LENGTH_TOO_SHORT); + free(bstring.string); } } @@ -249,123 +255,370 @@ void encode_withBadArgs_isUnsuccessful() { void encode_emptyExample_isUnsuccessful() { char hrp[] = ""; unsigned char dp[] = {}; - char expected[] = "a14rxqtp"; - char bstr[sizeof(hrp) + 1 + 6] = {0}; + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR); + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_NULL_ARGUMENT); + + bech32_free_bstring(bstring); } void encode_minimalExample_isSuccessful() { char hrp[] = "a"; unsigned char dp[] = {}; - char expected[] = "a14rxqtp"; - char bstr[sizeof(hrp) + 1 + 6] = {0}; + char expected[] = "a1lqfn3a"; + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); + + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); + assert(strcmp(expected, bstring->string) == 0); - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); - assert(strcmp(expected, bstr) == 0); + bech32_free_bstring(bstring); } void encode_smallExample_isSuccessful() { char hrp[] = "xyz"; unsigned char dp[] = {1,2,3}; - char expected[] = "xyz1pzr6jnr79"; - char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; + char expected[] = "xyz1pzrs3usye"; + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); - assert(strcmp(expected, bstr) == 0); + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); + assert(strcmp(expected, bstring->string) == 0); + + bech32_free_bstring(bstring); } -void encode_whenMethodThrowsException_isUnsuccessful() { +void encode_whenCppMethodThrowsException_isUnsuccessful() { // bech32 string can only have HRPs that are 83 chars or less. Attempt to encode an HRP string // with more than 83 chars and make sure that the exception thrown in the C++ code is caught // and returns an error code char hrp[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; unsigned char dp[] = {1,2,3}; - char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); + + assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR); - assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR); + bech32_free_bstring(bstring); } void decode_and_encode_minimalExample_producesSameResult() { - char bstr1[] = "a14rxqtp"; + char bstr[] = "a1lqfn3a"; char expectedHrp[] = "a"; const size_t expectedDpSize = 0; // 0 = num chars after '1', minus 6 for checksum chars - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr1); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); - assert(bech32_decode(hrpdp, bstr1, sizeof(bstr1)) == E_BECH32_SUCCESS); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(hrpdp->dplen == expectedDpSize); + assert(ENCODING_BECH32M == hrpdp->encoding); - size_t bstr2len = sizeof(expectedHrp) + 1 + expectedDpSize + 6; - char bstr2[bstr2len]; - memset(bstr2, 0, bstr2len); + bech32_bstring *bstring = bech32_create_bstring(hrpdp->hrplen, hrpdp->dplen); - assert(bech32_encode(bstr2, sizeof(bstr2), hrpdp->hrp, sizeof(expectedHrp), hrpdp->dp, expectedDpSize) == E_BECH32_SUCCESS); - assert(strcmp(bstr1, bstr2) == 0); + assert(bech32_encode(bstring, hrpdp->hrp, hrpdp->dp, hrpdp->dplen) == E_BECH32_SUCCESS); + assert(strcmp(bstr, bstring->string) == 0); - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); } void decode_and_encode_smallExample_producesSameResult() { - char bstr1[] = "xyz1pzr6jnr79"; + char bstr[] = "xyz1pzrs3usye"; char expectedHrp[] = "xyz"; const size_t expectedDpSize = 3; // 3 = num chars after '1', minus 6 for checksum chars - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr1); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); - assert(bech32_decode(hrpdp, bstr1, sizeof(bstr1)) == E_BECH32_SUCCESS); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(hrpdp->dplen == expectedDpSize); + assert(ENCODING_BECH32M == hrpdp->encoding); - size_t bstr2len = sizeof(expectedHrp) + 1 + expectedDpSize + 6; - char bstr2[bstr2len]; - memset(bstr2, 0, bstr2len); + bech32_bstring *bstring = bech32_create_bstring(hrpdp->hrplen, hrpdp->dplen); - assert(bech32_encode(bstr2, sizeof(bstr2), hrpdp->hrp, sizeof(expectedHrp), hrpdp->dp, expectedDpSize) == E_BECH32_SUCCESS); - assert(strcmp(bstr1, bstr2) == 0); + assert(bech32_encode(bstring, hrpdp->hrp, hrpdp->dp, hrpdp->dplen) == E_BECH32_SUCCESS); + assert(strcmp(bstr, bstring->string) == 0); - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); } void decode_and_encode_longExample_producesSameResult() { - char bstr1[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; + char bstr[] = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"; char expectedHrp[] = "abcdef"; const size_t expectedDpSize = 32; // 32 = num chars after '1', minus 6 for checksum chars - bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr1); + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); - assert(bech32_decode(hrpdp, bstr1, sizeof(bstr1)) == E_BECH32_SUCCESS); + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(hrpdp->dplen == expectedDpSize); + assert(ENCODING_BECH32M == hrpdp->encoding); - size_t bstr2len = sizeof(expectedHrp) + 1 + expectedDpSize + 6; - char bstr2[bstr2len]; - memset(bstr2, 0, bstr2len); + bech32_bstring *bstring = bech32_create_bstring(hrpdp->hrplen, hrpdp->dplen); - assert(bech32_encode(bstr2, sizeof(bstr2), hrpdp->hrp, sizeof(expectedHrp), hrpdp->dp, expectedDpSize) == E_BECH32_SUCCESS); - assert(strcmp(bstr1, bstr2) == 0); + assert(bech32_encode(bstring, hrpdp->hrp, hrpdp->dp, expectedDpSize) == E_BECH32_SUCCESS); + assert(strcmp(bstr, bstring->string) == 0); - free_HrpAndDp_storage(hrpdp); + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); } -int main() { +// ---------- tests using original checksum constant = 1 ------------ + +void decode_c1_minimalExample_isSuccessful() { + char bstr[] = "a12uel5l"; + char expectedHrp[] = "a"; + + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); + + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(ENCODING_BECH32 == hrpdp->encoding); + + bech32_free_HrpAndDp(hrpdp); +} + +void decode_c1_longExample_isSuccessful() { + char bstr[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + char expectedHrp[] = "abcdef"; + + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); + + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(hrpdp->dp[0] == '\0'); // first 'q' in above dp part + assert(hrpdp->dp[31] == '\x1f'); // last 'l' in above dp part + assert(ENCODING_BECH32 == hrpdp->encoding); + + bech32_free_HrpAndDp(hrpdp); +} + +void encode_c1_minimalExample_isSuccessful() { + char hrp[] = "a"; + unsigned char dp[] = {}; + char expected[] = "a12uel5l"; + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); + + assert(bech32_encode_using_original_constant(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); + assert(strcmp(expected, bstring->string) == 0); + + bech32_free_bstring(bstring); +} + +void encode_c1_smallExample_isSuccessful() { + char hrp[] = "xyz"; + unsigned char dp[] = {1,2,3}; + char expected[] = "xyz1pzr9dvupm"; + bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp)); + + assert(bech32_encode_using_original_constant(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS); + assert(strcmp(expected, bstring->string) == 0); + + bech32_free_bstring(bstring); +} + +void decode_and_encode_c1_minimalExample_producesSameResult() { + char bstr[] = "a12uel5l"; + char expectedHrp[] = "a"; + const size_t expectedDpSize = 0; // 0 = num chars after '1', minus 6 for checksum chars + + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); + + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(hrpdp->dplen == expectedDpSize); + assert(ENCODING_BECH32 == hrpdp->encoding); + + bech32_bstring *bstring = bech32_create_bstring(hrpdp->hrplen, hrpdp->dplen); + + assert(bech32_encode_using_original_constant(bstring, hrpdp->hrp, hrpdp->dp, hrpdp->dplen) == E_BECH32_SUCCESS); + assert(strcmp(bstr, bstring->string) == 0); + + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); +} + +void decode_and_encode_c1_smallExample_producesSameResult() { + char bstr[] = "xyz1pzr9dvupm"; + char expectedHrp[] = "xyz"; + const size_t expectedDpSize = 3; // 3 = num chars after '1', minus 6 for checksum chars + + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); + + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(hrpdp->dplen == expectedDpSize); + assert(ENCODING_BECH32 == hrpdp->encoding); + + bech32_bstring *bstring = bech32_create_bstring(hrpdp->hrplen, hrpdp->dplen); + + assert(bech32_encode_using_original_constant(bstring, hrpdp->hrp, hrpdp->dp, hrpdp->dplen) == E_BECH32_SUCCESS); + assert(strcmp(bstr, bstring->string) == 0); + + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); +} +void decode_and_encode_c1_longExample_producesSameResult() { + char bstr[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + char expectedHrp[] = "abcdef"; + const size_t expectedDpSize = 32; // 32 = num chars after '1', minus 6 for checksum chars + + bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr); + + assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, expectedHrp) == 0); + assert(hrpdp->dplen == expectedDpSize); + assert(ENCODING_BECH32 == hrpdp->encoding); + + bech32_bstring *bstring = bech32_create_bstring(hrpdp->hrplen, hrpdp->dplen); + + assert(bech32_encode_using_original_constant(bstring, hrpdp->hrp, hrpdp->dp, hrpdp->dplen) == E_BECH32_SUCCESS); + assert(strcmp(bstr, bstring->string) == 0); + + bech32_free_HrpAndDp(hrpdp); + bech32_free_bstring(bstring); +} + + +void test_strerror() { strerror_withValidErrorCode_returnsErrorMessage(); strerror_withInvalidErrorCode_returnsUnknownErrorMessage(); +} +void test_stripUnknownChars() { stripUnknownChars_withNullInput_returnsError(); stripUnknownChars_withNullOutput_returnsError(); stripUnknownChars_withInsufficientOutputStorage_returnsError(); stripUnknownChars_withSimpleString_returnsSameString(); stripUnknownChars_withComplexString_returnsStrippedString(); stripUnknownChars_withFunkyString_returnsStrippedString(); +} + +void create_HrpAndDp_storage_withNullInput_returnsNull() { + bech32_HrpAndDp *p = bech32_create_HrpAndDp(NULL); + assert(p == NULL); +} + +void create_HrpAndDp_storage_withZeroLengthInput_returnsNull() { + char empty[] = ""; + bech32_HrpAndDp *p = bech32_create_HrpAndDp(empty); + assert(p == NULL); +} + +void create_HrpAndDp_storage_withMalformedInput_returnsNull() { + char tooShort[] = "a1a"; + bech32_HrpAndDp *p = bech32_create_HrpAndDp(tooShort); + assert(p == NULL); + + char noSeparator[] = "aaaaaaaa"; + p = bech32_create_HrpAndDp(noSeparator); + assert(p == NULL); + + char checksumTooShort[] = "aa1qqqqq"; + p = bech32_create_HrpAndDp(checksumTooShort); + assert(p == NULL); +} + +void create_HrpAndDp_storage_minimalExample_isSuccessful() { + char bstr[] = "a12uel5l"; + bech32_HrpAndDp *p = bech32_create_HrpAndDp(bstr); + assert(p != NULL); + bech32_free_HrpAndDp(p); +} + +void create_HrpAndDp_storage_smallExample_isSuccessful() { + char bstr[] = "xyz1pzr9dvupm"; + bech32_HrpAndDp *p = bech32_create_HrpAndDp(bstr); + assert(p != NULL); + bech32_free_HrpAndDp(p); +} + +void create_encoded_string_storage_withMalformedInput_returnsNull() { + // hrp too short + bech32_bstring *bstring = bech32_create_bstring(0, 3); + assert(bstring == NULL); +} + +void create_encoded_string_storage_minimalExample_isSuccessful() { + bech32_bstring *bstring = bech32_create_bstring(1, 0); + assert(bstring != NULL); + bech32_free_bstring(bstring); +} + +void create_encoded_string_storage_smallExample_isSuccessful() { + bech32_bstring *bstring = bech32_create_bstring(3, 3); + assert(bstring != NULL); + bech32_free_bstring(bstring); +} + +void create_encoded_string_storage_from_HrpAndDp_withNullInput_returnsNull() { + bech32_bstring *bstring = bech32_create_bstring_from_HrpAndDp(NULL); + assert(bstring == NULL); +} + +void create_encoded_string_storage_from_HrpAndDp_withMalformedInput_returnsNull() { + // create a valid bech32_HrpAndDp, then corrupt it for testing + char bstr[] = "xyz1pzr9dvupm"; + bech32_HrpAndDp *hd = bech32_create_HrpAndDp(bstr); + + // hrp too short + hd->hrplen = 0; + bech32_bstring *bstring = bech32_create_bstring_from_HrpAndDp(hd); + assert(bstring == NULL); + + bech32_free_HrpAndDp(hd); +} + +void create_encoded_string_storage_from_HrpAndDp_minimalExample_isSuccessful() { + char bstr[] = "a12uel5l"; + bech32_HrpAndDp *hd = bech32_create_HrpAndDp(bstr); + assert(hd != NULL); + + bech32_bstring *bstring = bech32_create_bstring_from_HrpAndDp(hd); + assert(bstring != NULL); + + bech32_free_HrpAndDp(hd); + bech32_free_bstring(bstring); +} + +void create_encoded_string_storage_from_HrpAndDp_smallExample_isSuccessful() { + char bstr[] = "xyz1pzr9dvupm"; + bech32_HrpAndDp *hd = bech32_create_HrpAndDp(bstr); + assert(hd != NULL); + bech32_bstring *bstring = bech32_create_bstring_from_HrpAndDp(hd); + assert(bstring != NULL); + + bech32_free_HrpAndDp(hd); + bech32_free_bstring(bstring); +} + +void test_memoryAllocation() { + create_HrpAndDp_storage_withNullInput_returnsNull(); + create_HrpAndDp_storage_withZeroLengthInput_returnsNull(); + create_HrpAndDp_storage_withMalformedInput_returnsNull(); + create_HrpAndDp_storage_minimalExample_isSuccessful(); + create_HrpAndDp_storage_smallExample_isSuccessful(); + + create_encoded_string_storage_withMalformedInput_returnsNull(); + create_encoded_string_storage_minimalExample_isSuccessful(); + create_encoded_string_storage_smallExample_isSuccessful(); + + create_encoded_string_storage_from_HrpAndDp_withNullInput_returnsNull(); + create_encoded_string_storage_from_HrpAndDp_withMalformedInput_returnsNull(); + create_encoded_string_storage_from_HrpAndDp_minimalExample_isSuccessful(); + create_encoded_string_storage_from_HrpAndDp_smallExample_isSuccessful(); +} + +void tests_using_default_checksum_constant() { decode_withBadArgs_isUnsuccessful(); - decode_whenMethodThrowsException_isUnsuccessful(); + decode_whenCppMethodThrowsException_isUnsuccessful(); decode_minimalExample_isSuccessful(); decode_longExample_isSuccessful(); decode_minimalExampleBadChecksum_isUnsuccessful(); encode_withBadArgs_isUnsuccessful(); - encode_whenMethodThrowsException_isUnsuccessful(); + encode_whenCppMethodThrowsException_isUnsuccessful(); encode_emptyExample_isUnsuccessful(); encode_minimalExample_isSuccessful(); encode_smallExample_isSuccessful(); @@ -373,6 +626,28 @@ int main() { decode_and_encode_minimalExample_producesSameResult(); decode_and_encode_smallExample_producesSameResult(); decode_and_encode_longExample_producesSameResult(); +} + +void tests_using_original_checksum_constant() { + decode_c1_minimalExample_isSuccessful(); + decode_c1_longExample_isSuccessful(); + + encode_c1_minimalExample_isSuccessful(); + encode_c1_smallExample_isSuccessful(); + + decode_and_encode_c1_minimalExample_producesSameResult(); + decode_and_encode_c1_smallExample_producesSameResult(); + decode_and_encode_c1_longExample_producesSameResult(); +} + +int main() { + + test_strerror(); + test_stripUnknownChars(); + test_memoryAllocation(); + + tests_using_default_checksum_constant(); + tests_using_original_checksum_constant(); return 0; } diff --git a/test/testbech32/test_Bech32.cpp b/test/testbech32/test_Bech32.cpp index 4d34add..9c66c25 100644 --- a/test/testbech32/test_Bech32.cpp +++ b/test/testbech32/test_Bech32.cpp @@ -436,37 +436,37 @@ TEST(Bech32Test, polymod) { // check the verifyChecksum method TEST(Bech32Test, verifyChecksum_good) { - std::string data("a14rxqtp"); + std::string data("a1lqfn3a"); bech32::HrpAndDp b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "A14RXQTP"; + data = "A1LQFN3A"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; + data = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "split1checkupstagehandshakeupstreamerranterredcaperred4m6xws"; + data = "split1checkupstagehandshakeupstreamerranterredcaperredlc445v"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio155t5hw"; + data = "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8c42pv"; + data = "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); @@ -477,37 +477,37 @@ TEST(Bech32Test, verifyChecksum_good) { // check the verifyChecksum method // these are simply the "good" tests from above with a single character changed TEST(Bech32Test, verifyChecksum_bad) { - std::string data("a14rxqtm"); + std::string data("a1lqfn33"); bech32::HrpAndDp b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "C14RXQTP"; + data = "A1LQFN33"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "abcdefg1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; + data = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryy"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "split1dheckupstagehandshakeupstreamerranterredcaperred4m6xws"; + data = "split1checkupstagehandshakeupstreamerranterredcaperredlc445s"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "an83characterlonghumanreadablepartthatcontainsthenumber1andhheexcludedcharactersbio155t5hw"; + data = "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg7"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8c42pc"; + data = "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsrc"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); @@ -516,23 +516,25 @@ TEST(Bech32Test, verifyChecksum_bad) { // check the main bech32 decode method TEST(Bech32Test, decode_good) { - std::string data("a14rxqtp"); + std::string data("a1lqfn3a"); bech32::HrpAndDp b = bech32::decode(data); + ASSERT_EQ(b.encoding, bech32::Encoding::Bech32m); ASSERT_EQ(b.hrp, "a"); ASSERT_TRUE(b.dp.empty()); - data = "A14RXQTP"; + data = "A1LQFN3A"; b = bech32::decode(data); + ASSERT_EQ(b.encoding, bech32::Encoding::Bech32m); ASSERT_EQ(b.hrp, "a"); ASSERT_TRUE(b.dp.empty()); - data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; + data = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"; b = bech32::decode(data); + ASSERT_EQ(b.encoding, bech32::Encoding::Bech32m); ASSERT_EQ(b.hrp, "abcdef"); ASSERT_EQ(b.dp.size(), 32); - ASSERT_EQ(b.dp[0], '\0'); // 'q' in above dp part - ASSERT_EQ(b.dp[31], '\x1f'); // 'l' in above dp part - + ASSERT_EQ(b.dp[0], '\x1f'); // first 'l' in above dp part + ASSERT_EQ(b.dp[31], '\0'); // last 'q' in above dp part } TEST(Bech32Test, create_checksum) { @@ -540,36 +542,36 @@ TEST(Bech32Test, create_checksum) { std::vector data; std::vector checksum = createChecksum(hrp, data); - ASSERT_EQ(checksum[0], '\x15'); - ASSERT_EQ(checksum[1], '\x03'); - ASSERT_EQ(checksum[2], '\x06'); - ASSERT_EQ(checksum[3], '\x00'); - ASSERT_EQ(checksum[4], '\x0B'); - ASSERT_EQ(checksum[5], '\x01'); + ASSERT_EQ(checksum[0], '\x1F'); + ASSERT_EQ(checksum[1], '\x00'); + ASSERT_EQ(checksum[2], '\x09'); + ASSERT_EQ(checksum[3], '\x13'); + ASSERT_EQ(checksum[4], '\x11'); + ASSERT_EQ(checksum[5], '\x1D'); std::string mapped = mapToCharset(checksum); - ASSERT_EQ(mapped, "4rxqtp"); + ASSERT_EQ(mapped, "lqfn3a"); //// hrp = "abcdef"; - data = {'q','p','z','r','y','9','x','8','g','f','2','t','v','d','w','0','s','3','j', - 'n','5','4','k','h','c','e','6','m','u','a','7','l'}; + data = {'l','7','a','u','m','6','e','c','h','k','4','5','n','j','3','s','0','w','d', + 'v','t','2','f','g','8','x','9','y','r','z','p','q'}; mapDP(data); checksum = createChecksum(hrp, data); - ASSERT_EQ(checksum[0], '\x04'); - ASSERT_EQ(checksum[1], '\x1F'); - ASSERT_EQ(checksum[2], '\x1F'); - ASSERT_EQ(checksum[3], '\x1F'); - ASSERT_EQ(checksum[4], '\x19'); - ASSERT_EQ(checksum[5], '\x10'); + ASSERT_EQ(checksum[0], '\x02'); + ASSERT_EQ(checksum[1], '\x0D'); + ASSERT_EQ(checksum[2], '\x11'); + ASSERT_EQ(checksum[3], '\x03'); + ASSERT_EQ(checksum[4], '\x04'); + ASSERT_EQ(checksum[5], '\x06'); mapped = mapToCharset(checksum); - ASSERT_EQ(mapped, "yllles"); + ASSERT_EQ(mapped, "zd3ryx"); //// @@ -581,58 +583,58 @@ TEST(Bech32Test, create_checksum) { mapDP(data); checksum = createChecksum(hrp, data); - ASSERT_EQ(checksum[0], '\x15'); - ASSERT_EQ(checksum[1], '\x1B'); - ASSERT_EQ(checksum[2], '\x1A'); - ASSERT_EQ(checksum[3], '\x06'); - ASSERT_EQ(checksum[4], '\x0E'); - ASSERT_EQ(checksum[5], '\x10'); + ASSERT_EQ(checksum[0], '\x1F'); + ASSERT_EQ(checksum[1], '\x18'); + ASSERT_EQ(checksum[2], '\x15'); + ASSERT_EQ(checksum[3], '\x15'); + ASSERT_EQ(checksum[4], '\x14'); + ASSERT_EQ(checksum[5], '\x0C'); mapped = mapToCharset(checksum); - ASSERT_EQ(mapped, "4m6xws"); + ASSERT_EQ(mapped, "lc445v"); //// - hrp = "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio"; + hrp = "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber1"; data = {}; mapDP(data); checksum = createChecksum(hrp, data); - ASSERT_EQ(checksum[0], '\x14'); - ASSERT_EQ(checksum[1], '\x14'); - ASSERT_EQ(checksum[2], '\x0B'); - ASSERT_EQ(checksum[3], '\x14'); - ASSERT_EQ(checksum[4], '\x17'); - ASSERT_EQ(checksum[5], '\x0E'); + ASSERT_EQ(checksum[0], '\x10'); + ASSERT_EQ(checksum[1], '\x08'); + ASSERT_EQ(checksum[2], '\x1E'); + ASSERT_EQ(checksum[3], '\x17'); + ASSERT_EQ(checksum[4], '\x08'); + ASSERT_EQ(checksum[5], '\x1A'); mapped = mapToCharset(checksum); - ASSERT_EQ(mapped, "55t5hw"); + ASSERT_EQ(mapped, "sg7hg6"); //// hrp = "1"; - data = {'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', - 'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', - 'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', - 'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', - 'q','q','q','q','q','q'}; + data = {'l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l', + 'l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l', + 'l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l', + 'l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l', + 'l','l','l','l','l','l'}; mapDP(data); checksum = createChecksum(hrp, data); - ASSERT_EQ(checksum[0], '\x07'); - ASSERT_EQ(checksum[1], '\x18'); - ASSERT_EQ(checksum[2], '\x15'); - ASSERT_EQ(checksum[3], '\x0A'); - ASSERT_EQ(checksum[4], '\x01'); - ASSERT_EQ(checksum[5], '\x0C'); + ASSERT_EQ(checksum[0], '\x1F'); + ASSERT_EQ(checksum[1], '\x1C'); + ASSERT_EQ(checksum[2], '\x0D'); + ASSERT_EQ(checksum[3], '\x10'); + ASSERT_EQ(checksum[4], '\x03'); + ASSERT_EQ(checksum[5], '\x07'); mapped = mapToCharset(checksum); - ASSERT_EQ(mapped, "8c42pv"); + ASSERT_EQ(mapped, "ludsr8"); } @@ -641,30 +643,33 @@ TEST(Bech32Test, encode_good) { std::string hrp = "a"; std::vector data; std::string b = bech32::encode(hrp, data); - ASSERT_EQ(b, "a14rxqtp"); + ASSERT_EQ(b, "a1lqfn3a"); hrp = "A"; b = bech32::encode(hrp, data); - ASSERT_EQ(b, "a14rxqtp"); + ASSERT_EQ(b, "a1lqfn3a"); } // check that we can decode and then encode back to the original TEST(Bech32Test, check_decode_encode) { - std::string data("a14rxqtp"); + std::string data("a1lqfn3a"); bech32::HrpAndDp bs = bech32::decode(data); + ASSERT_EQ(bs.encoding, bech32::Encoding::Bech32m); ASSERT_EQ(bs.hrp, "a"); ASSERT_TRUE(bs.dp.empty()); std::string enc = bech32::encode(bs.hrp, bs.dp); ASSERT_EQ(enc, data); - data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; + data = "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"; bs = bech32::decode(data); + ASSERT_EQ(bs.encoding, bech32::Encoding::Bech32m); ASSERT_EQ(bs.hrp, "abcdef"); enc = bech32::encode(bs.hrp, bs.dp); ASSERT_EQ(enc, data); - data = "split1checkupstagehandshakeupstreamerranterredcaperred4m6xws"; + data = "split1checkupstagehandshakeupstreamerranterredcaperredlc445v"; bs = bech32::decode(data); + ASSERT_EQ(bs.encoding, bech32::Encoding::Bech32m); ASSERT_EQ(bs.hrp, "split"); enc = bech32::encode(bs.hrp, bs.dp); ASSERT_EQ(enc, data);