From ed59959b5b9ff878330996cba91e1ce5ebf3aa5c Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 9 Mar 2023 18:18:10 +0100 Subject: [PATCH] include/libeosio/base58.hpp: adding base58_decode functions. --- include/libeosio/base58.hpp | 7 ++++ src/base58.cpp | 80 +++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/base58/decode.cpp | 48 ++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 tests/base58/decode.cpp diff --git a/include/libeosio/base58.hpp b/include/libeosio/base58.hpp index 9bace9f..9a6f37c 100644 --- a/include/libeosio/base58.hpp +++ b/include/libeosio/base58.hpp @@ -36,6 +36,13 @@ std::string base58_encode(const std::string& str); std::string base58_encode(const std::vector& vch); std::string base58_encode(const unsigned char* pbegin, const unsigned char* pend); + +/** + * Base58 Decoding functions. + */ +bool base58_decode(const char* psz, std::string& out); +bool base58_decode(const std::string& str, std::string& out); + /** * Returns true if `ch` is a base58 character, false otherwise. */ diff --git a/src/base58.cpp b/src/base58.cpp index 616572a..6c2589d 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -27,11 +27,35 @@ #include #include #include +#include #include namespace libeosio { static const char charmap[59] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +static const int8_t table[256] = { + -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, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1, + -1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1, + 22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1, + -1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46, + 47,48,49,50,51,52,53,54, 55,56,57,-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,-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,-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, +}; + +bool is_space(char c) { + return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; +} + std::string base58_encode(const unsigned char* pbegin, const unsigned char* pend) { @@ -84,6 +108,62 @@ std::string base58_encode(const std::vector& vch) { return base58_encode(vch.data(), vch.data() + vch.size()); } +bool base58_decode(const char* psz, std::string& out) { + // Skip leading spaces. + while (*psz && is_space(*psz)) + psz++; + // Skip and count leading '1's. + int zeroes = 0; + int length = 0; + while (*psz == '1') { + zeroes++; + psz++; + } + // Allocate enough space in big-endian base256 representation. + int size = strlen(psz) * 733 /1000 + 1; // log(58) / log(256), rounded up. + std::vector b256(size); + // Process the characters. + + while (*psz && !is_space(*psz)) { + // Decode base58 character + int carry = table[(uint8_t)*psz]; + if (carry == -1) // Invalid b58 character + return false; + int i = 0; + for (std::vector::reverse_iterator it = b256.rbegin(); (carry != 0 || i < length) && (it != b256.rend()); ++it, ++i) { + carry += 58 * (*it); + *it = carry % 256; + carry /= 256; + } + assert(carry == 0); + length = i; + psz++; + } + + // Skip trailing spaces. + while (is_space(*psz)) + psz++; + + if (*psz != 0) + return false; + + // Skip leading zeroes in b256. + std::vector::iterator it = b256.begin() + (size - length); + while (it != b256.end() && *it == 0) + it++; + + // Copy result into output vector. + out.reserve(zeroes + (b256.end() - it)); + out.assign(zeroes, 0); + while (it != b256.end()) + out.push_back(*(it++)); + return true; +} + +bool base58_decode(const std::string& str, std::string& out) { + return base58_decode(str.c_str(), out); +} + bool is_base58(char ch) { for(unsigned int i=0; i < sizeof(charmap); i++) { if (ch == charmap[i]) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 973eeb9..efeffd0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,7 @@ set(TEST_SRC # Base58 base58/encode.cpp + base58/decode.cpp base58/is_base58.cpp # WIF diff --git a/tests/base58/decode.cpp b/tests/base58/decode.cpp new file mode 100644 index 0000000..6193f45 --- /dev/null +++ b/tests/base58/decode.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +typedef struct { + std::string name; + std::string in; + std::string expectedOut; + bool expectedReturn; +} test_t; + +typedef std::vector tests; + +TEST_CASE("base58_decode") { + tests input = { + test_t{"empty", "","", true}, + test_t{"invalid","OI","",false}, + test_t{ + "valid #1", + "2nPTv2DT874jRaYBN4uhM9mT2iRiwdJuCXuX5buUHyyvWUSu6cX62i8HYo8PsWqgs9DHbwhpSpV5SVUnCqyLcpxcuGanH68eXgzZTGq", + "Quisque ut ipsum lorem. Nullam ac justo elit. Sed gravida convallis mattis.", + true + }, + test_t{ + "valid #2", + "5yAgp6rBagDHQZ3GacZSeaEPF2jfuwVHM21aNfXETJgn3EkArxc5UWSq1RM", + "Cras fringilla, eros et imperdiet tincidunt", + true + }, + test_t{ + "valid #3", + "9P7SxYWTWMq5hHkri53b1CGvWKRXxq3uXWPs5RiVtYagFrsnTXDxvKnk1twkPmV7BuxcRhBHWSwFLXpXbmdfHwZrnDaTB3wrBhsjm2Dd7F95ixh5vQLxajmT8hd22yUbvXuAZci8vTgFWMUyQi5YzWwntQiK5KFDkx3oA7kxvdU5t1yJZur84a9aKTCihEWtvCJ6LoBCpxvyB16YaCKeBQWLbUqoaXvFoDM78BpKD8biYyWQhnzHonjdwAS4KNXs5ByBdBvvPK1Q2Knr8zuFZxKHEFmgZGFTt8SMSsTDjkanUjojbfpJt5gcrHh6UFrt45n7kT9sj9Xsf1UyXZG3E2H85jXSbVnKowz2VPq1TkLLUKG8CSfdH3fVRp2E3yL5cpbbFWngbMzsbBZDgr4kPPcazebvSZ8qm8taBcBmt1ry25ey9TfFbMzP4FR1q9yjvkqGusMtrrBFm8YEeRmoMugMQoXvUgpExh29j", + "Praesent massa nibh, feugiat ac aliquet sed, varius quis metus. Fusce auctor imperdiet purus. Vivamus elementum risus vel imperdiet condimentum. Nunc iaculis, sem eu sollicitudin tempus, nibh felis scelerisque orci, a tincidunt felis lectus in nulla. Vestibulum egestas eu elit id luctus. Vivamus eget ipsum neque. Fusce eleifend mauris a tempus vehicula.", + true + }, + }; + + for(tests::const_iterator it = input.begin(); it != input.end(); it++) { + + SUBCASE(it->name.c_str()) { + std::string result; + + CHECK( libeosio::base58_decode(it->in, result) == it->expectedReturn ); + CHECK( result == it->expectedOut ); + } + } +} \ No newline at end of file