-
Notifications
You must be signed in to change notification settings - Fork 173
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2384 from jamescowens/upstream_port_base58
util: Port of Bitcoin upstream base58.h/cpp at a85442f62bf157b07849accd495c55c73535dc73
- Loading branch information
Showing
3 changed files
with
228 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// Copyright (c) 2014-2020 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 <assert.h> | ||
#include <string.h> | ||
|
||
#include <limits> | ||
|
||
/** 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(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; | ||
} | ||
|
||
std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) | ||
{ | ||
return EncodeBase58(Span<const unsigned char>(pbegin, pend)); | ||
} | ||
|
||
bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len) | ||
{ | ||
if (!ValidAsCString(str)) { | ||
return false; | ||
} | ||
return DecodeBase58(str.c_str(), vchRet, max_ret_len); | ||
} | ||
|
||
std::string EncodeBase58Check(Span<const unsigned char> input) | ||
{ | ||
// add 4-byte hash check to the end | ||
std::vector<unsigned char> vch(input.begin(), input.end()); | ||
// Can't use spans here until we teach hash.h about them. | ||
uint256 hash = Hash(vch.begin(), vch.end()); | ||
vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); | ||
return EncodeBase58(vch); | ||
} | ||
|
||
std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn) | ||
{ | ||
return EncodeBase58Check(Span<const unsigned char>(vchIn)); | ||
} | ||
|
||
[[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 | ||
// Can't use spans here until we teach hash.h about them. | ||
uint256 hash = Hash(vchRet.begin(), vchRet.end() - 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 (!ValidAsCString(str)) { | ||
return false; | ||
} | ||
return DecodeBase58Check(str.c_str(), vchRet, max_ret); | ||
} |
Oops, something went wrong.