0

its been a while after i done with my python bitcoin miner

And i realize that python is slower to do mining stuff, so i decided to rebuild it in c++ as you guys know c++ the faster than other programming language

But i have a problem to use base58 decode to hexadecimal in c++

Python code

import base58

BtcAddr: str = "1Htcz9y5sVuztuwTnfpWR4dhqirhLMcK7r"

base58.b58decode_check(BtcAddr).hex()

And the output will be this

00b9454405a39efcb023127cd6daacf9c133b84df3

But i need that in c++ code, i already search online but i didnt find anything helpful

Can you guys help me? I really need the source code in c++

1 Answer 1

1

This solution is used by Bitcoin Core

base58.h

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2023 Loopite.

#ifndef CRYPTO_BASE58_H
#define CRYPTO_BASE58_H

#include <string>
#include <vector>

/**
 * Encode a byte sequence as a base58-encoded string.
 * pbegin and pend cannot be NULL, unless both are.
 */
std::string encodeBase58(const unsigned char* pbegin, const unsigned char* pend);

/**
 * Encode a byte vector as a base58-encoded string
 */
std::string encodeBase58(const std::vector<unsigned char>& vch);

/**
 * Decode a base58-encoded string (psz) into a byte vector (vchRet).
 * return true if decoding is successful.
 * psz cannot be NULL.
 */
bool decodeBase58(const char* psz, std::vector<unsigned char>& vchRet);

/**
 * Decode a base58-encoded string (str) into a byte vector (vchRet).
 * return true if decoding is successful.
 */
bool decodeBase58(const std::string& str, std::vector<unsigned char>& vchRet);

#endif // CRYPTO_BASE58_H

base58.cpp

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2023 Loopite

#include "base58.h"

#include <iterator>
#include <cstring>

#include <cassert>

/** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 { "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" };

std::string encodeBase58(const unsigned char* pbegin, const unsigned char* pend)
{
    // Skip & count leading zeroes.
    int zeroes = 0;
    int length = 0;
    while (pbegin != pend && *pbegin == 0) {
        pbegin++;
        zeroes++;
    }
    // Allocate enough space in big-endian base58 representation.
    int size = (pend - pbegin) * 138 / 100 + 1; // log(256) / log(58), rounded up.
    std::vector<unsigned char> b58(size);
    // Process the bytes.
    while (pbegin != pend) {
        int carry = *pbegin;
        int i = 0;
        // Apply "b58 = b58 * 256 + ch".
        for (auto 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;
        pbegin++;
    }
    // Skip leading zeroes in base58 result.
    auto 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 std::vector<unsigned char>& vch)
{
    return encodeBase58(&vch[0], &vch[0] + vch.size());
}

bool decodeBase58(const char* psz, std::vector<unsigned char>& vch)
{
    // Skip leading spaces.
    while (*psz && isspace(*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<unsigned char> b256(size);
    // Process the characters.
    while (*psz && !isspace(*psz)) {
        // Decode base58 character
        const char* ch = strchr(pszBase58, *psz);
        if (ch == nullptr)
            return false;
        // Apply "b256 = b256 * 58 + ch".
        int carry = ch - pszBase58;
        int i = 0;
        for (auto 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 (isspace(*psz))
        psz++;
    if (*psz != 0)
        return false;
    // Skip leading zeroes in b256.
    auto it = b256.begin() + (size - length);
    while (it != b256.end() && *it == 0)
        it++;
    // 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;
}

bool decodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
{
    return decodeBase58(str.c_str(), vchRet);
}

Tests

[ RUN      ] App.base58_Basics
00b9454405a39efcb023127cd6daacf9c133b84df30ce3172d
[       OK ] App.base58_Basics (0 ms)

From C++ code:

/**
 * @Author: Loopite
 * @Description: Test for base58 encodings
 */

#include <gtest/gtest.h>

#include "crypto/base58.h"

#include <set>

TEST(App, base58_Basics)
{
    /*
     * Decode
     */
    const std::string data
    {
        "1Htcz9y5sVuztuwTnfpWR4dhqirhLMcK7r" // P2PKH address
    };

    std::vector<unsigned char> decodedContent;
    decodeBase58(data, decodedContent);

    // ...Print content of decoded data...
    for (unsigned char i : decodedContent)
        std::cout << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(i);

    std::cout << std::endl;
}

NOTE

The output is 00b9454405a39efcb023127cd6daacf9c133b84df30ce3172d NOT 00b9454405a39efcb023127cd6daacf9c133b84df3 for the string 1Htcz9y5sVuztuwTnfpWR4dhqirhLMcK7r. See https://appdevtools.com/base58-encoder-decoder.

10
  • I see but that includes with hexadecimal value + checksum right? Well i dont want the checksum value, i only want the hexadecimal value Aug 16, 2023 at 9:52
  • The thing is P2PKH has this structure: networkFlag (1 byte) + address (20 bytes since it has been hashed by RIPEMD-160) + checksum (4 bytes). Therefore the checksum is required. However if you WANT the hexadecimal value WITHOUT checksum you can process like this: 1Htcz9y5sVuztuwTnfpWR4dhqirhLMcK7r <=> 00b9454405a39efcb023127cd6daacf9c133b84df30ce3172d (With checksum) <=> 00b9454405a39efcb023127cd6daacf9c133b84df3 (Without checksum) <=> 13ahvXA4uoeMuX4iKJW1mQkYjCWbt (Encoded in base58) You just have to parse output to get networkFlag and address separatly by using substr.
    – Loopite
    Aug 16, 2023 at 10:22
  • /!\. You should know that the checksum is REQUIRED. So you'll have to keep it from LEGACY addresses. It's part of the protocol. Functions you see above allow you to encode/decode base58 sequence. That's it.
    – Loopite
    Aug 16, 2023 at 10:27
  • So what youre trying to say is that i need to include the checksum in order to make a raw transactions? Aug 16, 2023 at 11:10
  • In a raw transaction this is different. You'll have just to provide the address. This field can be determined from the Base58Check (1Htcz9y5sVuztuwTnfpWR4dhqirhLMcK7r). To sum up: Base58Check requires a checksum. But raw txs need address only.
    – Loopite
    Aug 16, 2023 at 11:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.