| // Copyright (c) 2014-present The Bitcoin Core developers | |
| // Distributed under the MIT software license, see the accompanying | |
| // file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
| | |
| #include <base58.h> | |
| | |
| #include <hash.h> | |
| #include <uint256.h> | |
| #include <util/strencodings.h> | |
| #include <util/string.h> | |
| | |
| #include <cassert> | |
| #include <cstring> | |
| | |
| #include <limits> | |
| | |
| using util::ContainsNoNUL; | |
| | |
| /** All alphanumeric characters except for "0", "I", "O", and "l" */ | |
| static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; | |
| static const int8_t mapBase58[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, | |
| }; | |
| | |
| [[nodiscard]] static bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len) | |
| { | |
| // Skip leading spaces. | |
| while (*psz && IsSpace(*psz)) | |
| psz++; | |
| // Skip and count leading '1's. | |
| int zeroes = 0; | |
| int length = 0; | |
| while (*psz == '1') { | |
| zeroes++; | |
| if (zeroes > max_ret_len) return false; | |
| psz++; | |
| } | |
| // Allocate enough space in big-endian base256 representation. | |
| int size = strlen(psz) * 733 /1000 + 1; // log(58) / log(256), rounded up. | |
| std::vector<unsigned char> b256(size); | |
| // Process the characters. | |
| static_assert(std::size(mapBase58) == 256, "mapBase58.size() should be 256"); // guarantee not out of range | |
| while (*psz && !IsSpace(*psz)) { | |
| // Decode base58 character | |
| int carry = mapBase58[(uint8_t)*psz]; | |
| if (carry == -1) // Invalid b58 character | |
| return false; | |
| int i = 0; | |
| for (std::vector<unsigned char>::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; | |
| if (length + zeroes > max_ret_len) return false; | |
| psz++; | |
| } | |
| // Skip trailing spaces. | |
| while (IsSpace(*psz)) | |
| psz++; | |
| if (*psz != 0) | |
| return false; | |
| // Skip leading zeroes in b256. | |
| std::vector<unsigned char>::iterator it = b256.begin() + (size - length); | |
| // Copy result into output vector. | |
| vch.reserve(zeroes + (b256.end() - it)); | |
| vch.assign(zeroes, 0x00); | |
| while (it != b256.end()) | |
| vch.push_back(*(it++)); | |
| return true; | |
| } | |
| | |
| std::string EncodeBase58(std::span<const unsigned char> input) | |
| { | |
| // Skip & count leading zeroes. | |
| int zeroes = 0; | |
| int length = 0; | |
| while (input.size() > 0 && input[0] == 0) { | |
| input = input.subspan(1); | |
| zeroes++; | |
| } | |
| // Allocate enough space in big-endian base58 representation. | |
| int size = input.size() * 138 / 100 + 1; // log(256) / log(58), rounded up. | |
| std::vector<unsigned char> b58(size); | |
| // Process the bytes. | |
| while (input.size() > 0) { | |
| int carry = input[0]; | |
| int i = 0; | |
| // Apply "b58 = b58 * 256 + ch". | |
| for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++) { | |
| carry += 256 * (*it); | |
| *it = carry % 58; | |
| carry /= 58; | |
| } | |
| | |
| assert(carry == 0); | |
| length = i; | |
| input = input.subspan(1); | |
| } | |
| // Skip leading zeroes in base58 result. | |
| std::vector<unsigned char>::iterator it = b58.begin() + (size - length); | |
| while (it != b58.end() && *it == 0) | |
| it++; | |
| // Translate the result into a string. | |
| std::string str; | |
| str.reserve(zeroes + (b58.end() - it)); | |
| str.assign(zeroes, '1'); | |
| while (it != b58.end()) | |
| str += pszBase58[*(it++)]; | |
| return str; | |
| } | |
| | |
| bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len) | |
| { | |
| if (!ContainsNoNUL(str)) { | |
| return false; | |
| } | |
| return DecodeBase58(str.c_str(), vchRet, max_ret_len); | |
| } | |
| | |
| std::string EncodeBase58Check(std::span<const unsigned char> input) | |
| { | |
| // add 4-byte hash check to the end | |
| std::vector<unsigned char> vch(input.begin(), input.end()); | |
| uint256 hash = Hash(vch); | |
| vch.insert(vch.end(), hash.data(), hash.data() + 4); | |
| return EncodeBase58(vch); | |
| } | |
| | |
| [[nodiscard]] static bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len) | |
| { | |
| if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits<int>::max() - 4 ? std::numeric_limits<int>::max() : max_ret_len + 4) || | |
| (vchRet.size() < 4)) { | |
| vchRet.clear(); | |
| return false; | |
| } | |
| // re-calculate the checksum, ensure it matches the included 4-byte checksum | |
| uint256 hash = Hash(std::span{vchRet}.first(vchRet.size() - 4)); | |
| if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) { | |
| vchRet.clear(); | |
| return false; | |
| } | |
| vchRet.resize(vchRet.size() - 4); | |
| return true; | |
| } | |
| | |
| bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret) | |
| { | |
| if (!ContainsNoNUL(str)) { | |
| return false; | |
| } | |
| return DecodeBase58Check(str.c_str(), vchRet, max_ret); | |
| } | |