How to encode and decode a string in base58 using C++

1 Answer

0 votes
// 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

*/

 



answered May 1 by avibootz
...