Author

Topic: Private to WIF to public to address (Read 482 times)

legendary
Activity: 2604
Merit: 2353
December 18, 2022, 06:42:58 AM
#20
Since I didn't find any projects like that on Github, I asked OpenAI's chatGPT to write a hex to base58 C++ function. I'm licensing the resulting code under the MIT license.
Code:
...

[...]

Since Bitcoin uses Base58Check which adds a checksum at the end of the Base58 string, and not just plain Base58, we need to modify this function as follows (also MIT licensed / AI generated):
Code:
...

It's using OpenSSL by the way, but I have a standalone SHA256 class file (that is not AI-generated) that does an equivalent job if you want it.

Damn, now I'm worried that I'm going to be obsoleted by a robot  Cheesy

[...]
PS: Since most people will be dealing with binary data when converting the HASH160 to base58(check), I decided to ask chatGPT to make binary to hex conversions as well:
Code:
...

I am so glad I didn't waste $100 on Copilot, this is so much better and more useful (and free!).
What you manage to do with this tool is very impressive, to be honest. Congratulations. Each time I tried to ask it something, about various matters, I got very incomplete/unsatisfactory results, with mistakes and erroneous claims unfortunately.
Which kind of questions did you ask it to get those results precisely?
legendary
Activity: 2464
Merit: 4415
🔐BitcoinMessage.Tools🔑
December 16, 2022, 05:17:46 AM
#19
Are you linking to both libssl and libcrypto? -lssl -lcrypto
Thanks! Now it works but after conducting some tests of hex-base58 function, it doesn't seem to generate a right result:

Code:
std::string hex = "0B4ECCD4BC14DD";
std::string base58 = hex_to_base58(hex);

Expected: RrXWAxKqn
Actual: 19NvXJ9YRCv

std::string hex = "ABF539E673FC2EB49D09B5319D638";
std::string base58 = hex_to_base58(hex);

Expected: JSUumzgQCd8NedR439AF
Actual: dd3ka1QEMZN6UheU7Tg

std::string hex = "0114527a0fdd1c71";
std::string base58 = hex_to_base58(hex);

Expected: BULhLPjcrU
Actual: 1KvLT7KeJaja

The same is true for base58check because it is based on the same algorithm...
Am I missing something?
legendary
Activity: 1568
Merit: 6660
bitcoincleanup.com / bitmixlist.org
December 16, 2022, 02:34:52 AM
#18
It's using OpenSSL by the way, but I have a standalone SHA256 class file (that is not AI-generated) that does an equivalent job if you want it.
Can you please share the code because I don't think I ever figure out how to include openssl library or any other third-party library in cpp projects. Cheesy

Certainly.

Code:
// //////////////////////////////////////////////////////////
// sha256.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//

#pragma once

//#include "hash.h"
#include

// define fixed size integer types
#ifdef _WIN32
// Windows
typedef unsigned __int8  uint8_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include
#endif

// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN
#ifndef _WIN32
#include
#endif

//#define SHA2_224_SEED_VECTOR


  inline uint32_t SHA256_rotate(uint32_t a, uint32_t c)
  {
    return (a >> c) | (a << (32 - c));
  }

  inline uint32_t SHA256_swap(uint32_t x)
  {
#if defined(__GNUC__) || defined(__clang__)
    return __builtin_bswap32(x);
#endif
#ifdef MSC_VER
    return _byteSHA256_swap_ulong(x);
#endif

    return (x >> 24) |
          ((x >>  8) & 0x0000FF00) |
          ((x <<  8) & 0x00FF0000) |
           (x << 24);
  }

  // mix functions for processBlock()
  inline uint32_t SHA256_f1(uint32_t e, uint32_t f, uint32_t g)
  {
    uint32_t term1 = SHA256_rotate(e, 6) ^ SHA256_rotate(e, 11) ^ SHA256_rotate(e, 25);
    uint32_t term2 = (e & f) ^ (~e & g); //(g ^ (e & (f ^ g)))
    return term1 + term2;
  }

  inline uint32_t SHA256_f2(uint32_t a, uint32_t b, uint32_t c)
  {
    uint32_t term1 = SHA256_rotate(a, 2) ^ SHA256_rotate(a, 13) ^ SHA256_rotate(a, 22);
    uint32_t term2 = ((a | b) & c) | (a & b); //(a & (b ^ c)) ^ (b & c);
    return term1 + term2;
  }

/// compute SHA256 hash
/** Usage:
    SHA256 sha256;
    std::string myHash  = sha256("Hello World");     // std::string
    std::string myHash2 = sha256("How are you", 11); // arbitrary data, 11 bytes

    // or in a streaming fashion:

    SHA256 sha256;
    while (more data available)
      sha256.add(pointer to fresh data, number of new bytes);
    std::string myHash3 = sha256.getHash();
  */
class SHA256 //: public Hash
{
public:
  /// split into 64 byte blocks (=> 512 bits), hash is 32 bytes long
  enum { BlockSize = 512 / 8, HashBytes = 32 };
private:
  /// size of processed data in bytes
  uint64_t m_numBytes;
  /// valid bytes in m_buffer
  size_t   m_bufferSize;
  /// bytes not processed yet
  uint8_t  m_buffer[BlockSize];

  enum { HashValues = HashBytes / 4 };
  /// hash, stored as integers
  uint32_t m_hash[HashValues];

  /// process 64 bytes
  void processBlock(const void* data)
{
  // get last hash
  uint32_t a = m_hash[0];
  uint32_t b = m_hash[1];
  uint32_t c = m_hash[2];
  uint32_t d = m_hash[3];
  uint32_t e = m_hash[4];
  uint32_t f = m_hash[5];
  uint32_t g = m_hash[6];
  uint32_t h = m_hash[7];

  // data represented as 16x 32-bit words
  const uint32_t* input = (uint32_t*) data;
  // convert to big endian
  uint32_t words[64];
  int i;
  for (i = 0; i < 16; i++)
#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
    words[i] =      input[i];
#else
    words[i] = SHA256_swap(input[i]);
#endif

  uint32_t x,y; // temporaries

  // first round
  x = h + SHA256_f1(e,f,g) + 0x428a2f98 + words[ 0]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0x71374491 + words[ 1]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0xb5c0fbcf + words[ 2]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0xe9b5dba5 + words[ 3]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0x3956c25b + words[ 4]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0x59f111f1 + words[ 5]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0x923f82a4 + words[ 6]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0xab1c5ed5 + words[ 7]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // secound round
  x = h + SHA256_f1(e,f,g) + 0xd807aa98 + words[ 8]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0x12835b01 + words[ 9]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0x243185be + words[10]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0x550c7dc3 + words[11]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0x72be5d74 + words[12]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0x80deb1fe + words[13]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0x9bdc06a7 + words[14]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0xc19bf174 + words[15]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // extend to 24 words
  for (; i < 24; i++)
    words[i] = words[i-16] +
               (SHA256_rotate(words[i-15],  7) ^ SHA256_rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
               words[i-7] +
               (SHA256_rotate(words[i- 2], 17) ^ SHA256_rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

  // third round
  x = h + SHA256_f1(e,f,g) + 0xe49b69c1 + words[16]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0xefbe4786 + words[17]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0x0fc19dc6 + words[18]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0x240ca1cc + words[19]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0x2de92c6f + words[20]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0x4a7484aa + words[21]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0x5cb0a9dc + words[22]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0x76f988da + words[23]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // extend to 32 words
  for (; i < 32; i++)
    words[i] = words[i-16] +
               (SHA256_rotate(words[i-15],  7) ^ SHA256_rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
               words[i-7] +
               (SHA256_rotate(words[i- 2], 17) ^ SHA256_rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

  // fourth round
  x = h + SHA256_f1(e,f,g) + 0x983e5152 + words[24]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0xa831c66d + words[25]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0xb00327c8 + words[26]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0xbf597fc7 + words[27]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0xc6e00bf3 + words[28]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0xd5a79147 + words[29]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0x06ca6351 + words[30]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0x14292967 + words[31]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // extend to 40 words
  for (; i < 40; i++)
    words[i] = words[i-16] +
               (SHA256_rotate(words[i-15],  7) ^ SHA256_rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
               words[i-7] +
               (SHA256_rotate(words[i- 2], 17) ^ SHA256_rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

  // fifth round
  x = h + SHA256_f1(e,f,g) + 0x27b70a85 + words[32]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0x2e1b2138 + words[33]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0x4d2c6dfc + words[34]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0x53380d13 + words[35]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0x650a7354 + words[36]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0x766a0abb + words[37]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0x81c2c92e + words[38]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0x92722c85 + words[39]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // extend to 48 words
  for (; i < 48; i++)
    words[i] = words[i-16] +
               (SHA256_rotate(words[i-15],  7) ^ SHA256_rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
               words[i-7] +
               (SHA256_rotate(words[i- 2], 17) ^ SHA256_rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

  // sixth round
  x = h + SHA256_f1(e,f,g) + 0xa2bfe8a1 + words[40]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0xa81a664b + words[41]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0xc24b8b70 + words[42]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0xc76c51a3 + words[43]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0xd192e819 + words[44]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0xd6990624 + words[45]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0xf40e3585 + words[46]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0x106aa070 + words[47]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // extend to 56 words
  for (; i < 56; i++)
    words[i] = words[i-16] +
               (SHA256_rotate(words[i-15],  7) ^ SHA256_rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
               words[i-7] +
               (SHA256_rotate(words[i- 2], 17) ^ SHA256_rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

  // seventh round
  x = h + SHA256_f1(e,f,g) + 0x19a4c116 + words[48]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0x1e376c08 + words[49]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0x2748774c + words[50]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0x34b0bcb5 + words[51]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0x391c0cb3 + words[52]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0x4ed8aa4a + words[53]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0x5b9cca4f + words[54]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0x682e6ff3 + words[55]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // extend to 64 words
  for (; i < 64; i++)
    words[i] = words[i-16] +
               (SHA256_rotate(words[i-15],  7) ^ SHA256_rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
               words[i-7] +
               (SHA256_rotate(words[i- 2], 17) ^ SHA256_rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));

  // eigth round
  x = h + SHA256_f1(e,f,g) + 0x748f82ee + words[56]; y = SHA256_f2(a,b,c); d += x; h = x + y;
  x = g + SHA256_f1(d,e,f) + 0x78a5636f + words[57]; y = SHA256_f2(h,a,b); c += x; g = x + y;
  x = f + SHA256_f1(c,d,e) + 0x84c87814 + words[58]; y = SHA256_f2(g,h,a); b += x; f = x + y;
  x = e + SHA256_f1(b,c,d) + 0x8cc70208 + words[59]; y = SHA256_f2(f,g,h); a += x; e = x + y;
  x = d + SHA256_f1(a,b,c) + 0x90befffa + words[60]; y = SHA256_f2(e,f,g); h += x; d = x + y;
  x = c + SHA256_f1(h,a,b) + 0xa4506ceb + words[61]; y = SHA256_f2(d,e,f); g += x; c = x + y;
  x = b + SHA256_f1(g,h,a) + 0xbef9a3f7 + words[62]; y = SHA256_f2(c,d,e); f += x; b = x + y;
  x = a + SHA256_f1(f,g,h) + 0xc67178f2 + words[63]; y = SHA256_f2(b,c,d); e += x; a = x + y;

  // update hash
  m_hash[0] += a;
  m_hash[1] += b;
  m_hash[2] += c;
  m_hash[3] += d;
  m_hash[4] += e;
  m_hash[5] += f;
  m_hash[6] += g;
  m_hash[7] += h;
}
  /// process everything left in the internal buffer
  void processBuffer()
{
  // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte

  // - append "1" bit to message
  // - append "0" bits until message length in bit mod 512 is 448
  // - append length as 64 bit integer

  // number of bits
  size_t paddedLength = m_bufferSize * 8;

  // plus one bit set to 1 (always appended)
  paddedLength++;

  // number of bits must be (numBits % 512) = 448
  size_t lower11Bits = paddedLength & 511;
  if (lower11Bits <= 448)
    paddedLength +=       448 - lower11Bits;
  else
    paddedLength += 512 + 448 - lower11Bits;
  // convert from bits to bytes
  paddedLength /= 8;

  // only needed if additional data flows over into a second block
  unsigned char extra[BlockSize];

  // append a "1" bit, 128 => binary 10000000
  if (m_bufferSize < BlockSize)
    m_buffer[m_bufferSize] = 128;
  else
    extra[0] = 128;

  size_t i;
  for (i = m_bufferSize + 1; i < BlockSize; i++)
    m_buffer[i] = 0;
  for (; i < paddedLength; i++)
    extra[i - BlockSize] = 0;

  // add message length in bits as 64 bit number
  uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);
  // find right position
  unsigned char* addLength;
  if (paddedLength < BlockSize)
    addLength = m_buffer + paddedLength;
  else
    addLength = extra + paddedLength - BlockSize;

  // must be big endian
  *addLength++ = (unsigned char)((msgBits >> 56) & 0xFF);
  *addLength++ = (unsigned char)((msgBits >> 48) & 0xFF);
  *addLength++ = (unsigned char)((msgBits >> 40) & 0xFF);
  *addLength++ = (unsigned char)((msgBits >> 32) & 0xFF);
  *addLength++ = (unsigned char)((msgBits >> 24) & 0xFF);
  *addLength++ = (unsigned char)((msgBits >> 16) & 0xFF);
  *addLength++ = (unsigned char)((msgBits >>  8) & 0xFF);
  *addLength   = (unsigned char)( msgBits        & 0xFF);

  // process blocks
  processBlock(m_buffer);
  // flowed over into a second block ?
  if (paddedLength > BlockSize)
    processBlock(extra);
}

public:

  /// same as reset()
  SHA256()
{
  reset();
}

  /// compute SHA256 of a memory block
  std::string operator()(const void* data, size_t numBytes)
{
  reset();
  add(data, numBytes);
  return getHash();
}
  /// compute SHA256 of a string, excluding final zero
  std::string operator()(const std::string& text)
{
  reset();
  add(text.c_str(), text.size());
  return getHash();
}

  /// add arbitrary number of bytes
  void add(const void* data, size_t numBytes)
{
  const uint8_t* current = (const uint8_t*) data;

  if (m_bufferSize > 0)
  {
    while (numBytes > 0 && m_bufferSize < BlockSize)
    {
      m_buffer[m_bufferSize++] = *current++;
      numBytes--;
    }
  }

  // full buffer
  if (m_bufferSize == BlockSize)
  {
    processBlock(m_buffer);
    m_numBytes  += BlockSize;
    m_bufferSize = 0;
  }

  // no more data ?
  if (numBytes == 0)
    return;

  // process full blocks
  while (numBytes >= BlockSize)
  {
    processBlock(current);
    current    += BlockSize;
    m_numBytes += BlockSize;
    numBytes   -= BlockSize;
  }

  // keep remaining bytes in buffer
  while (numBytes > 0)
  {
    m_buffer[m_bufferSize++] = *current++;
    numBytes--;
  }
}

  /// return latest hash as 64 hex characters
  std::string getHash()
{
  // compute hash (as raw bytes)
  unsigned char rawHash[HashBytes];
  getHash(rawHash);

  // convert to hex string
  std::string result;
  result.reserve(2 * HashBytes);
  for (int i = 0; i < HashBytes; i++)
  {
    static const char dec2hex[16+1] = "0123456789abcdef";
    result += dec2hex[(rawHash[i] >> 4) & 15];
    result += dec2hex[ rawHash[i]       & 15];
  }

  return result;
}
  /// return latest hash as bytes
  void        getHash(unsigned char buffer[HashBytes])
{
  // save old hash if buffer is partially filled
  uint32_t oldHash[HashValues];
  for (int i = 0; i < HashValues; i++)
    oldHash[i] = m_hash[i];

  // process remaining bytes
  processBuffer();

  unsigned char* current = buffer;
  for (int i = 0; i < HashValues; i++)
  {
    *current++ = (m_hash[i] >> 24) & 0xFF;
    *current++ = (m_hash[i] >> 16) & 0xFF;
    *current++ = (m_hash[i] >>  8) & 0xFF;
    *current++ =  m_hash[i]        & 0xFF;

    // restore old hash
    m_hash[i] = oldHash[i];
  }
}

  /// restart
  void reset()
{
  m_numBytes   = 0;
  m_bufferSize = 0;

  // according to RFC 1321
  // "These words were obtained by taking the first thirty-two bits of the
  //  fractional parts of the square roots of the first eight prime numbers"
  m_hash[0] = 0x6a09e667;
  m_hash[1] = 0xbb67ae85;
  m_hash[2] = 0x3c6ef372;
  m_hash[3] = 0xa54ff53a;
  m_hash[4] = 0x510e527f;
  m_hash[5] = 0x9b05688c;
  m_hash[6] = 0x1f83d9ab;
  m_hash[7] = 0x5be0cd19;

#ifdef SHA2_224_SEED_VECTOR
  // if you want SHA2-224 instead then use these seeds
  // and throw away the last 32 bits of getHash
  m_hash[0] = 0xc1059ed8;
  m_hash[1] = 0x367cd507;
  m_hash[2] = 0x3070dd17;
  m_hash[3] = 0xf70e5939;
  m_hash[4] = 0xffc00b31;
  m_hash[5] = 0x68581511;
  m_hash[6] = 0x64f98fa7;
  m_hash[7] = 0xbefa4fa4;
#endif
}
};

As the code mentions, I did not write this code, I just adapted it to a single header file.

He has more open-source hashing functions, which you can download for free at https://create.stephan-brumme.com/hash-library/

PS: Since most people will be dealing with binary data when converting the HASH160 to base58(check), I decided to ask chatGPT to make binary to hex conversions as well:

Update:
I installed openssl using this command:

Code:
sudo apt-get install libssl-dev
But now I am seeing another error:
Quote
/main.cpp:24: undefined reference to `SHA256' collect2: error: ld returned 1 exit status
How to make it work?

Are you linking to both libssl and libcrypto? -lssl -lcrypto
legendary
Activity: 2464
Merit: 4415
🔐BitcoinMessage.Tools🔑
December 16, 2022, 01:57:52 AM
#17
It's using OpenSSL by the way, but I have a standalone SHA256 class file (that is not AI-generated) that does an equivalent job if you want it.
Can you please share the code because I don't think I ever figure out how to include openssl library or any other third-party library in cpp projects. Cheesy

Update:
I installed openssl using this command:

Code:
sudo apt-get install libssl-dev
But now I am seeing another error:
Quote
/main.cpp:24: undefined reference to `SHA256' collect2: error: ld returned 1 exit status
How to make it work?
legendary
Activity: 1568
Merit: 6660
bitcoincleanup.com / bitmixlist.org
December 16, 2022, 12:25:43 AM
#16
Since I didn't find any projects like that on Github, I asked OpenAI's chatGPT to write a hex to base58 C++ function. I'm licensing the resulting code under the MIT license.

Quote
Code:
#include 
#include
#include

// Base58 alphabet used for encoding
const std::string base58_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

std::string hex_to_base58(const std::string& hex) {
  // Convert hex string to big-endian integer
  std::vector hex_bytes(hex.size() / 2);
  for (size_t i = 0; i < hex_bytes.size(); ++i) {
    sscanf(hex.substr(i * 2, 2).c_str(), "%2hhx", &hex_bytes[i]);
  }
  std::reverse(hex_bytes.begin(), hex_bytes.end());

  // Convert big-endian integer to base58 string
  std::string base58;
  while (hex_bytes.size() > 0) {
    int carry = 0;
    for (size_t i = 0; i < hex_bytes.size(); ++i) {
      carry = carry * 256 + hex_bytes[i];
      hex_bytes[i] = carry / 58;
      carry %= 58;
    }
    base58 += base58_alphabet[carry];
    while (hex_bytes.size() > 0 && hex_bytes.back() == 0) {
      hex_bytes.pop_back();
    }
  }

  // Add leading zeros
  for (size_t i = 0; i < hex.size() && hex[i] == '0'; ++i) {
    base58 += base58_alphabet[0];
  }

  std::reverse(base58.begin(), base58.end());
  return base58;
}

int main() {
  std::string hex = "6f7574707574";
  std::string base58 = hex_to_base58(hex);
  std::cout << "Hex: " << hex << std::endl;
  std::cout << "Base58: " << base58 << std::endl;
  return 0;
}

This function first converts the hexadecimal string to a big-endian integer by parsing each pair of hexadecimal digits as a byte and storing them in a vector. It then converts the big-endian integer to a base58 string using the following algorithm:

Initialize an empty base58 string and an integer carry to 0.
For each byte b in the integer, set carry to carry * 256 + b, and set b to the quotient of carry divided by 58.
Append the base58 alphabet character at the index of the remainder of carry divided by 58 to the base58 string.
If there are any non-zero bytes remaining in the integer, go back to step 2. Otherwise, the base58 string is complete.
Finally, the function adds leading zeros to the base58 string if the hexadecimal string had leading zeros. It then reverses the base58 string and returns it.

Since Bitcoin uses Base58Check which adds a checksum at the end of the Base58 string, and not just plain Base58, we need to modify this function as follows (also MIT licensed / AI generated):

Quote
Code:
#include 
#include
#include
#include

#include

// Base58 alphabet used for encoding
const std::string base58_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

std::string hex_to_base58check(const std::string& hex) {
  // Convert hex string to big-endian integer
  std::vector hex_bytes(hex.size() / 2);
  for (size_t i = 0; i < hex_bytes.size(); ++i) {
    sscanf(hex.substr(i * 2, 2).c_str(), "%2hhx", &hex_bytes[i]);
  }
  std::reverse(hex_bytes.begin(), hex_bytes.end());

  // Compute checksum
  std::vector checksum(SHA256_DIGEST_LENGTH);
  SHA256(hex_bytes.data(), hex_bytes.size(), checksum.data());
  SHA256(checksum.data(), checksum.size(), checksum.data());

  // Append checksum to data
  hex_bytes.insert(hex_bytes.end(), checksum.begin(), checksum.begin() + 4);

  // Convert big-endian integer to base58 string
  std::string base58;
  while (hex_bytes.size() > 0) {
    int carry = 0;
    for (size_t i = 0; i < hex_bytes.size(); ++i) {
      carry = carry * 256 + hex_bytes[i];
      hex_bytes[i] = carry / 58;
      carry %= 58;
    }
    base58 += base58_alphabet[carry];
    while (hex_bytes.size() > 0 && hex_bytes.back() == 0) {
      hex_bytes.pop_back();
    }
  }

  // Add leading zeros
  for (size_t i = 0; i < hex.size() && hex[i] == '0'; ++i) {
    base58 += base58_alphabet[0];
  }

  std::reverse(base58.begin(), base58.end());
  return base58;
}

int main() {
  std::string hex = "6f7574707574";
  std::string base58 = hex_to_base58check(hex);
  std::cout << "Hex: " << hex << std::endl;
  std::cout << "Base58check: " << base58 << std::endl;
  return 0;
}

It's using OpenSSL by the way, but I have a standalone SHA256 class file (that is not AI-generated) that does an equivalent job if you want it.

Damn, now I'm worried that I'm going to be obsoleted by a robot  Cheesy
member
Activity: 143
Merit: 82
December 15, 2022, 07:18:41 AM
#15
Here are two links which show each individual step, which should help you understand what is going on. You can enter your own strings and go back and forth along the steps.

Converting private key from hex to WIF and vice versa: https://gobittest.appspot.com/PrivateKey
Turning a hex private key in to an uncompressed public key and then an address: https://gobittest.appspot.com/Address

On the page about addresses the first three line items are
0 - Private ECDSA Key
1 - Public ECDSA Key
2 - SHA-256 hash of 1

For item 2 it indicates to create it via a hash from 1.   But I don't recognize anything that states how to get from 0 to 1.  What is that transformation?

There is the explanation: https://www.oreilly.com/library/view/mastering-bitcoin-2nd/9781491954379/ch04.html

Besides, there are:
- the private_key_into_bitcoin_wif.sh shell script (in Bash so no Python or additional frameworks installation required to run it): https://gist.github.com/GregTonoski/438992249df6e4bd613f9758421ff38a
- step-by-step computations: https://bitcoiner.guide/seed/
legendary
Activity: 2464
Merit: 4415
🔐BitcoinMessage.Tools🔑
December 12, 2022, 02:43:05 AM
#14
Although you have created a bunch of similar threads where you have already been given a comprehensive list of advice, suggestions, links to useful resources, and code snippets written in multiple programming languages, you still keep repeating the same question: how do you get from a private key to public key. The answer is simple; take your time, read and try to understand the information that people kindly gave you. Only when you read everything you have been presented, can you ask follow-up questions about parts you can't comprehend on your own... Please. In addition to everything that has already been told and written, I would like to suggest another bunch of useful links where people explain how the math behind public key generation actually works. Take your time and read the information.

https://hackernoon.com/what-is-the-math-behind-elliptic-curve-cryptography-f61b25253da3
https://bitcoin.stackexchange.com/questions/25024/how-do-you-get-a-bitcoin-public-key-from-a-private-key
https://bitcoin.stackexchange.com/questions/95081/produce-a-public-key-from-private-key-with-ecc-with-clear-description-about-k-k
https://bitcoin.stackexchange.com/questions/114766/fail-at-coding-my-private-to-public-key-converter-pyhon
https://bitcoin.stackexchange.com/questions/25382/bitcoin-private-key-location-on-ecc-curve
https://www.freecodecamp.org/news/how-to-create-a-bitcoin-wallet-address-from-a-private-key-eca3ddd9c05f/
https://engineering.purdue.edu/kak/compsec/NewLectures/Lecture14.pdf
https://www.imperialviolet.org/2010/12/04/ecc.html
hero member
Activity: 910
Merit: 5935
not your keys, not your coins!
December 11, 2022, 08:04:42 PM
#13
For item 2 it indicates to create it via a hash from 1.   But I don't recognize anything that states how to get from 0 to 1.  What is that transformation?
I had actually answered that days ago:

(1) Private Key > Public Key: Multiply pk with generator of the elliptic curve. (result: (x,y) point on the curve)

It's an elliptic-curve multiplication. You can read more about elliptic-curve cryptography here.
The security is based on the computationally hard DLOG problem.
legendary
Activity: 2268
Merit: 18771
December 11, 2022, 03:30:43 AM
#12
What is that transformation?
As pooya87 has said, it is an elliptic curve multiplication.

Your private key is simply a random number. Your public key is a point on the secp256k1 curve which bitcoin uses. To get from the private key to the public key, you multiply the private key by what is known as the generator point, which is (we think) an arbitrarily chosen point on the curve. This point is the same for everyone, for every private key, and for every wallet. Once you've multiplied the generator point by your private key, using elliptic curve multiplication, you reach your public key. The public key (uncompressed) is comprised of a 0x04 byte (which tells us it is uncompressed), followed by the 32 byte x coordinate and then the 32 byte y coordinate.
legendary
Activity: 3472
Merit: 10611
December 10, 2022, 11:52:01 PM
#11
But I don't recognize anything that states how to get from 0 to 1.  What is that transformation?
It is all about using Elliptic Curve Cryptography and the part where you compute public key from private key is using a process called Elliptic curve point multiplication you can read about it on wikipedia https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication
Here is another page explaining elliptic curve cryptography in general: https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/
member
Activity: 76
Merit: 35
December 10, 2022, 11:30:17 PM
#10
Here are two links which show each individual step, which should help you understand what is going on. You can enter your own strings and go back and forth along the steps.

Converting private key from hex to WIF and vice versa: https://gobittest.appspot.com/PrivateKey
Turning a hex private key in to an uncompressed public key and then an address: https://gobittest.appspot.com/Address

On the page about addresses the first three line items are
0 - Private ECDSA Key
1 - Public ECDSA Key
2 - SHA-256 hash of 1

For item 2 it indicates to create it via a hash from 1.   But I don't recognize anything that states how to get from 0 to 1.  What is that transformation?
member
Activity: 76
Merit: 35
December 10, 2022, 12:52:49 PM
#9
Here are two links which show each individual step, which should help you understand what is going on. You can enter your own strings and go back and forth along the steps.

Converting private key from hex to WIF and vice versa: https://gobittest.appspot.com/PrivateKey
Turning a hex private key in to an uncompressed public key and then an address: https://gobittest.appspot.com/Address

My apologies for not replying sooner, but, the two recommended pages are far better than anything else I have found.  I am just not good at arranging my search string and found neither of them.

Thank you so much for your time and patience.
legendary
Activity: 1512
Merit: 7340
Farewell, Leo
December 05, 2022, 05:03:42 AM
#8
Thanks for the code.  176,091,844 can be handled easily by 32 bit integers, but 256 bit arithmetic adds a bit of complexity.
Indeed. The C program was just an attempt to represent the pseudo-code in a real, translatable code you can play with. While possible, I wouldn't recommend using 256-bit arithmetic in C for educational purposes, because it's low-level and can turn this simple, easy to read program in a chaos. Generally, big integers in low-level programming languages come with complex code. That's because you need to break the big integer into small chunks.
member
Activity: 76
Merit: 35
December 04, 2022, 08:51:36 PM
#7
Pooya87: “The double hash is only for computation of the checksum but it doesn't affect the data part.”
I did not catch that on the first reading.  Thanks.

BlackHatCoiner
Thanks for the code.  176,091,844 can be handled easily by 32 bit integers, but 256 bit arithmetic adds a bit of complexity.
legendary
Activity: 1512
Merit: 7340
Farewell, Leo
December 04, 2022, 03:53:02 AM
#6
I understand what Base58 is. I don't know how to implement.
Think of a number. For the sake of simplicity, let's take a relatively small one: 176,091,844. That's the number in decimal, and I'll convert it to base58 as follows.

Code:
176091844 mod 58 = 16
176091844 div 16 = 3036066
3036066 mod 58 = 56
3036066 div 56 = 52345
52345 mod 58 = 29
52345 div 29 = 902
902 mod 58 = 32
902 div 32 = 15
15 mod 58 = 15
output = 15th character of base58 + 32nd character of base58 + 29th character of base58 + 56th character of base58 + 16th character of base58 = [15][32][29][56][16] = GZWyH

So the algorithm in pseudo-code is:
Code:
X = 176091844
WHILE X MOD 58 IS NOT X, DO:
    OUTPUT = OUTPUT + (X MOD 58 + 1)th character of base58
    X = X DIV 58

OUTPUT = OUTPUT + (X+1)th character of base58
PRINT OUTPUT BACKWARDS

I just wrote it as a simple C program, if you're familiar with.
Code: (base58.c)
#include 

int main(){
    unsigned int x = 176091844;
    int i = 0;
    int j = 0;
    char base58_chars[58] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
    char output[100];
    while(x % 58 != x){
        output[i] = base58_chars[x % 58];
        printf("%u mod 58 = %d\n", x, x%58);
        printf("%u div %d = %d\n", x, x%58, x/58);
        x /= 58;
        i++;
    }
    output[i] = base58_chars[x];
    printf("%u mod 58 = %u\n", x, x);

    for(j=i; j>=0; j--){
        printf("%c", output[j]);
    }
    printf("\n");
}

In Linux, compile with:
Code:
gcc -o base58 base58.c

And run with:
Code:
./base58

It does the base10(x) -> base58(x) conversion for x∈[0, 2^32-1].
legendary
Activity: 2268
Merit: 18771
December 04, 2022, 02:38:31 AM
#5
Here are two links which show each individual step, which should help you understand what is going on. You can enter your own strings and go back and forth along the steps.

Converting private key from hex to WIF and vice versa: https://gobittest.appspot.com/PrivateKey
Turning a hex private key in to an uncompressed public key and then an address: https://gobittest.appspot.com/Address

legendary
Activity: 3472
Merit: 10611
December 03, 2022, 11:30:46 PM
#4
I understand that private key is 256 random bits, chosen very carefully.  Then we go to WIF, compressed and uncompressed.
WIF is not one of the steps (priv > pub > address) it is the same as a private key just the encoding to have a human readable string that is the representation of those bits.

I find references to projects in github, but they all, so far, are part of larger projects and reference Linux libraries that I don't have.
Base58 encoding is not really dependent on other parts of the library. It is a very simple encoding algorithm that only needs to use SHA256

Quote
Are the WIFs versions conversions such as hex or binary to Base58?  Or is there something else?
WIF is Base58 encoding and it is similar to Hex or Base16 encoding but with a checksum.
member
Activity: 76
Merit: 35
December 03, 2022, 08:58:44 PM
#3
I do want to look at the WIF, Wallet Input Format.
I understand what Base58 is. I don't know how to implement.  I find references to projects in github, but they all, so far, are part of larger projects and reference Linux libraries that I don't have.  I am running Windows 11 and Visual Studio.

Edit:
Are the WIFs versions conversions such as hex or binary to Base58?  Or is there something else?
Do you, dear reader, know of any code that does the conversions.  I have found several Java and Perl examples, but they call libraries that I cannot see into and I don't know either of those.
hero member
Activity: 910
Merit: 5935
not your keys, not your coins!
December 03, 2022, 08:25:43 PM
#2
There are a few very good explanations around.
I'd start here: Learnmeabitcoin > Beginners > Keys & Addresses
Then learn about public key: Learnmeabitcoin > Technical > Public Key
Finally going from public key to address: Learnmeabitcoin > Technical > Address

In short:
(1) Private Key > Public Key: Multiply pk with generator of the elliptic curve. (result: (x,y) point on the curve)
(2) Public Key > Compressed Public Key: 02/03 (depending if y even or odd), appending x
(2) Compressed Public Key > Address: ripemd160(sha256(compressed public key))

You don't need to worry about WIF. That's just short for 'Wallet Import Format'; as the name implies, lets you import / export keys. But it's not needed to create the WIF as an intermediary step for address generation.

Another resource here:
https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses#How_to_create_Bitcoin_Address
member
Activity: 76
Merit: 35
December 03, 2022, 07:50:29 PM
#1
I am trying to understand the various transformations to go from private key to public.  I understand that private key is 256 random bits, chosen very carefully.  Then we go to WIF, compressed and uncompressed.  Then to public key, then address.  But very unsure about the processes and results.

My searches find different explanations.  One site hashes it twice to get the WIF.  That seems like its no longer the private, but the public key or address.

What is a good web site that describes these transformations?  For someone who can write C/C++ code but is not a math major.

Thank you for your time.
Jump to: