3

I was following the tutorial here : https://hackernoon.com/how-to-generate-bitcoin-addresses-technical-address-generation-explanation-rus3z9e

i imported base58 to my python project :

import base58

I tried to convert the private key at the tutorial into the base58 version

privatekey = "a4f228d49910e8ecb53ba6f23f33fbfd2bad442e902ea20b8cf89c473237bf9f"

# expected : C6t2iJ7AXA2X1KQVnqw3r7NKtKaERdLnaGmbDZNqxXjk
privatekey_base58 = base58.b58encode(privatekey)

But it didn't give me the expected value like in the tutorial , instead it gives me this enter image description here

How do i properly encode a string to base58 and decode base58 to original string ?

3 Answers 3

3

You need to convert string hex to bytes hex format before feeding it to b58encode to produce C6t2iJ7AXA2X1KQVnqw3r7NKtKaERdLnaGmbDZNqxXjk.

import base58
    
privatekey_base58 = base58.b58encode(bytes.fromhex(
    "a4f228d49910e8ecb53ba6f23f33fbfd2bad442e902ea20b8cf89c473237bf9f"
))

The tutorial you linked to is not accessible anymore, so I am not sure whether they talked about it there, but there are actually several steps between generation private key and conversion to Wallet Import Format (WIF). You can see more details about it at https://en.bitcoin.it/wiki/Wallet_import_format. According to the wiki page, you need to extend private key with 0x80 (for mainnet addresses). Extended private key is then passed through double sha256 to extract a checksum (first 4 bytes). Finally, extended private key and checksum are concatenated and Base58 check encoding is applied to produce WIF private key. Surprisingly enough, their tutorial does not use Base58 check encoding, but Base58 encoding.

1

Use b58encode_check instead of non-check version and convert your hex to binary before passing it to the library.

4
  • And how do i decode base58 string ? Should i turn the string to binary too before decoding ? Commented Oct 10, 2021 at 12:04
  • No, but to decode convert the decode result to hex because it'll be in binary
    – MCCCS
    Commented Oct 10, 2021 at 12:23
  • help it doesnt work , i tried base58.b58encode_check(bin(int("0x000c2c910a661178ef63e276dd0e239883b862f58cc3c0439f", 16))) but it doesnt gives 127NVqnjf8gB9BFAW2dnQeM6wqmy1gbGtv like in the tutorial Commented Oct 11, 2021 at 2:34
  • Does this give you any inspiration stackoverflow.com/a/23565829 ?
    – MCCCS
    Commented Oct 11, 2021 at 6:23
-1

As of Python 3.10.8

Here's how to derive the addresses from 3 randomly generated private keys from bitaddress.org.

Example Bitcoin addresses with private keys:

  1. Private Key: L5Mssn1ANiFXuWrYpSohHfbvZzuUNZ7Px8x4vCLCFthihhEVwowM
    Address: 1FbFJHvT3xE2i5TDqtyCUuCiAjVJMV9XPt
  2. Private Key: L3jg9jj7SBfoU5qiLunjuES1f6aoSjvQMYBXHqR4b7HBv92dnHsq
    Address: 1PSds68vNzCeYvJNB6axnN8VnrLSnJC6S1
  3. Private Key: KwmT38RYZe55mM1TjY8dy41VpzerunJMoLt4Z97tF33rxHUsejLT
    Address: 1GPqjz4qQxz7ZWnvSN5Gc2H5hkhd5va95i

In the shell:

pip install ecdsa base58 hashlib

In Python:

import ecdsa
import hashlib
import base58

# These are the 3 private keys in base58check encoding.
privkey_b58_1 = "L5Mssn1ANiFXuWrYpSohHfbvZzuUNZ7Px8x4vCLCFthihhEVwowM"
privkey_b58_2 = "L3jg9jj7SBfoU5qiLunjuES1f6aoSjvQMYBXHqR4b7HBv92dnHsq"
privkey_b58_3 = "KwmT38RYZe55mM1TjY8dy41VpzerunJMoLt4Z97tF33rxHUsejLT"

# Remove the \x80 version prefix and x\01 compression suffix.
privkey_bin_1 = base58.b58decode_check(privkey_b58_1.encode())[1:-1]
privkey_bin_2 = base58.b58decode_check(privkey_b58_2.encode())[1:-1]
privkey_bin_3 = base58.b58decode_check(privkey_b58_3.encode())[1:-1]

# Create signing keys using the SECP256k1 curve in ECDSA.
sk_bin_1 = ecdsa.SigningKey.from_string(privkey_bin_1, ecdsa.SECP256k1)
sk_bin_2 = ecdsa.SigningKey.from_string(privkey_bin_2, ecdsa.SECP256k1)
sk_bin_3 = ecdsa.SigningKey.from_string(privkey_bin_3, ecdsa.SECP256k1)

# Derive verifying keys from the signing keys.
vk_bin_1 = sk_bin_1.verifying_key.to_string()
vk_bin_2 = sk_bin_2.verifying_key.to_string()
vk_bin_3 = sk_bin_3.verifying_key.to_string()

# Determine if the Y value is even. This is required for compression.
vk_iseven_1 = int(vk_bin_1.hex(), base=16) % 2 == 0
vk_iseven_2 = int(vk_bin_2.hex(), base=16) % 2 == 0
vk_iseven_3 = int(vk_bin_3.hex(), base=16) % 2 == 0

# Determine the prefix for the X value based on whether the Y value is even.
vk_prefix_1 = b"\x02" if vk_iseven_1 else b"\x03"
vk_prefix_2 = b"\x02" if vk_iseven_2 else b"\x03"
vk_prefix_3 = b"\x02" if vk_iseven_3 else b"\x03"

# Extract the X value from the verifying key. X is the first 32 bytes.
vk_xval_1 = vk_bin_1[:32]
vk_xval_2 = vk_bin_2[:32]
vk_xval_3 = vk_bin_3[:32]

# Get the SHA256 hash of the X value with the even/odd prefix.
vk_sha256_1 = hashlib.sha256(vk_prefix_1 + vk_xval_1).digest()
vk_sha256_2 = hashlib.sha256(vk_prefix_2 + vk_xval_2).digest()
vk_sha256_3 = hashlib.sha256(vk_prefix_3 + vk_xval_3).digest()

# Get the RIPEMD-160 hash of the SHA256 hash, then add the address prefix.
ripemd160_1 = hashlib.new("ripemd160")
ripemd160_1.update(vk_sha256_1)
address_1 = b"\x00" + ripemd160_1.digest()

ripemd160_2 = hashlib.new("ripemd160")
ripemd160_2.update(vk_sha256_2)
address_2 = b"\x00" + ripemd160_2.digest()

ripemd160_3 = hashlib.new("ripemd160")
ripemd160_3.update(vk_sha256_3)
address_3 = b"\x00" + ripemd160_3.digest()

# Get the base58check encoding for each address.
address_b58_1 = base58.b58encode_check(address_1)
address_b58_2 = base58.b58encode_check(address_2)
address_b58_3 = base58.b58encode_check(address_3)

# Show the addresses.
for x in [address_b58_1, address_b58_2, address_b58_3]:
    print(x.decode())

# 1FbFJHvT3xE2i5TDqtyCUuCiAjVJMV9XPt
# 1PSds68vNzCeYvJNB6axnN8VnrLSnJC6S1
# 1GPqjz4qQxz7ZWnvSN5Gc2H5hkhd5va95i

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