// Base58 is an encoding scheme used heavily in Bitcoin and other crypto systems. It uses a
// 58‑character alphabet that avoids visually confusing characters such as 0, O, I, l.
// The standard alphabet is: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
#include <iostream>
#include <vector>
#include <string>
#include <cstdint>
// ------------------------------------------------------------
// Base58 alphabet (Bitcoin standard)
// ------------------------------------------------------------
static const char* BASE58_ALPHABET =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
// Reverse lookup table for decoding
// Index = ASCII code, Value = Base58 value or -1 if invalid
static const int8_t BASE58_INDEXES[128] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-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,44,-1,45,46,
47,48,49,50,51,52,53,54,55,56,57,-1,-1,-1,-1,-1
};
// ------------------------------------------------------------
// Base58 ENCODE
// ------------------------------------------------------------
std::string base58_encode(const std::vector<uint8_t>& bytes)
{
// Count leading zeros (they become '1' in Base58)
int zeros = 0;
while (zeros < bytes.size() && bytes[zeros] == 0)
zeros++;
// Copy input because we will modify it
std::vector<uint8_t> input(bytes.begin(), bytes.end());
std::vector<char> encoded;
// Main encoding loop: repeatedly divide by 58
int start = zeros;
while (start < input.size()) {
int carry = 0;
// Divide the number by 58
for (int i = start; i < input.size(); i++) {
int value = input[i] + carry * 256;
input[i] = value / 58;
carry = value % 58;
}
// Remainder becomes next Base58 digit
encoded.push_back(BASE58_ALPHABET[carry]);
// Skip leading zeros in the modified array
while (start < input.size() && input[start] == 0)
start++;
}
// Add '1' for each leading zero byte
while (zeros--)
encoded.push_back('1');
// Reverse result (Base58 digits are produced backwards)
return std::string(encoded.rbegin(), encoded.rend());
}
// ------------------------------------------------------------
// Base58 DECODE
// ------------------------------------------------------------
std::vector<uint8_t> base58_decode(const std::string& s)
{
// Count leading '1's (they become zero bytes)
int zeros = 0;
while (zeros < s.size() && s[zeros] == '1')
zeros++;
// Convert Base58 characters to integers
std::vector<uint8_t> input;
input.reserve(s.size());
for (char c : s) {
if (c & 0x80 || BASE58_INDEXES[(int)c] == -1)
throw std::runtime_error("Invalid Base58 character");
input.push_back(BASE58_INDEXES[(int)c]);
}
std::vector<uint8_t> decoded;
// Main decoding loop: repeatedly divide by 256
int start = zeros;
while (start < input.size()) {
int carry = 0;
// Divide the number by 256
for (int i = start; i < input.size(); i++) {
int value = input[i] + carry * 58;
input[i] = value / 256;
carry = value % 256;
}
decoded.push_back(carry);
// Skip leading zeros in the modified array
while (start < input.size() && input[start] == 0)
start++;
}
// Add zero bytes for each leading '1'
while (zeros--)
decoded.push_back(0);
// Reverse result
return std::vector<uint8_t>(decoded.rbegin(), decoded.rend());
}
// ------------------------------------------------------------
// MAIN PROGRAM (demo)
// ------------------------------------------------------------
int main()
{
std::string text = "hello world";
// Convert string to bytes
std::vector<uint8_t> data(text.begin(), text.end());
// Encode
std::string encoded = base58_encode(data);
std::cout << "Encoded: " << encoded << "\n";
// Decode
std::vector<uint8_t> decoded = base58_decode(encoded);
std::string decoded_text(decoded.begin(), decoded.end());
std::cout << "Decoded: " << decoded_text << "\n";
}
/*
run:
Encoded: StV1DL6CwTryKyV
Decoded: hello world
*/