AUTHOR: witcher_sense
Around that kind one week or so, I come decide say na d perfect time for me to start to de learn ho to take program and after i don do some research, i come conclude say na Phyton go be better programming language for me to take start with. For my own opinion, the best way to take learn something na to de practice and many many times, dis na d reason y I think say to de write programs na d best way to take learn how to de write better program. By say I combine my theoretical interest for Bitcoin come join practical interest for programming, I believe say I fit create things way dem fit use ( way relate to Bitcoin and other fields) way I dey read about before and make them dey "real."
For under here na program way Dey take private key for hex format, come convert am to WIF (d one way dem compress and and d one way dem no compress), com calculate public keys (d one way dem compress and and d one way dem no compress) and the address dem way follow am . I collect Functions way Dey do elliptic curve calculation (modulo inverse, point addition, doubling and multiplication) and elliptic curve parameters from here: https://github.com/wobine/blackboard101
import base58
class PublicKey:
"""Calculating public key from private key"""
def __init__(self, private_key):
"""Initialize parameters of an elliptic curve"""
self.private_key = private_key
self.p_curve = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1 # The proven prime
self.n_curve = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 # Number of points in the field
self.a_curve, self.b_curve = 0, 7 # These two defines the elliptic curve. y^2 = x^3 + Acurve * x + Bcurve
self.gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
self.gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
self.gpoint = (self.gx, self.gy) # This is generator point.
def hash(self, key):
"""Function performing SHA-256 hashing"""
sha = hashlib.sha256()
sha.update(bytes.fromhex(key))
return sha.hexdigest()
def public_address(self, key):
"""Function calculating address"""
ripemd = hashlib.new('ripemd160')
sha = hashlib.sha256()
sha.update(bytes.fromhex(key))
ripemd.update(sha.digest())
address = f"00{ripemd.hexdigest()}"
checksum = self.hash(self.hash(address))[:8]
address = f"{address}{checksum}"
address = base58.b58encode(bytes.fromhex(address)).decode("UTF-8")
return address
def privatewif(self):
"""Converting a private key to WIF"""
private_wif = f"80{hex(self.private_key)[2:]}"
private_wif_comp = f"80{hex(self.private_key)[2:]}01"
checksum = self.hash(self.hash(private_wif))[:8]
checksum_comp = self.hash(self.hash(private_wif_comp))[:8]
private_wif = f"{private_wif}{checksum}"
private_wif_comp = f"{private_wif_comp}{checksum_comp}"
private_wif = base58.b58encode(bytes.fromhex(private_wif)).decode("UTF-8")
private_wif_comp = base58.b58encode(bytes.fromhex(private_wif_comp)).decode("UTF-8")
print(f"\nWIF - private key\n{private_wif}")
print(f"Length: {len(private_wif)}\n")
print(f"WIF compressed - private key\n{private_wif_comp}")
print(f"Length: {len(private_wif_comp)}\n")
def modinv(self, a, n):
"""Extended Euclidean Algorithm"""
lm, hm = 1, 0
low, high = a%n, n
while low > 1:
ratio = int(high/low)
nm, new = int(hm-lm*ratio), int(high-low*ratio)
lm, low, hm, high = nm, new, lm, low
return lm % n
def ec_add(self, a, b):
"""Point addition"""
lam_add = ((b[1]-a[1]) * self.modinv(b[0]-a[0], self.p_curve)) % self.p_curve
x = (pow(lam_add, 2)-a[0]-b[0]) % self.p_curve
y = (lam_add*(a[0]-x)-a[1]) % self.p_curve
return x, y
def ec_double(self, a):
"""EC point doubling"""
lam = ((3*a[0]*a[0]+self.a_curve) * self.modinv((2*a[1]), self.p_curve)) % self.p_curve
x = int((lam*lam-2*a[0]) % self.p_curve)
y = int((lam*(a[0]-x)-a[1]) % self.p_curve)
return x, y
def ec_multiply(self, genpoint, scalarhex):
"""EC point multiplication"""
if scalarhex == 0 or scalarhex >= self.n_curve: raise Exception("Invalid Scalar/Private Key")
scalarbin = str(bin(scalarhex))[2:]
q = genpoint
for i in range (1, len(scalarbin)): #Double and add to multiply point
q = self.ec_double(q)
if scalarbin[i] == "1":
q = self.ec_add(q, genpoint)
return q
def public_calc(self):
"""Calculating a public key"""
public_key = self.ec_multiply(self.gpoint, self.private_key)
print("\nThe uncompressed public key (not address):")
print(public_key)
print("\nThe uncompressed public key (HEX):")
message = f"04{(hex(public_key[0])[2:]):0>64}{(hex(public_key[1])[2:]):0>64}"
print(message)
print(f"Length: {len(message)}\n")
message = self.public_address(message)
print(f"Address from uncompressed key\n{message}")
print("\nThe official Public Key - compressed:")
if public_key[1] % 2 == 1: # If the Y value for the Public Key is odd.
message = f"03{hex(public_key[0])[2:]:0>64}"
print(message)
print(f"Length: {len(message)}\n")
message = self.public_address(message)
print(f"Address from compressed key\n{message}")
else: # Or else, if the Y value is even.
message = f"02{hex(public_key[0])[2:]:0>64}"
print(message)
print(f"Length: {len(message)}\n")
message = self.public_address(message)
print(f"Address from compressed key\n{message}")
prompt = input(f"Please insert your private key in HEX format (0x): ")
try:
prompt = int(prompt, 16) # interpret the input as a base-16 number, a hexadecimal.
except ValueError:
print("You did not enter a hexadecimal number!")
my_key = PublicKey(prompt)
my_key.public_calc()
my_key.privatewif()
Source code also dey available for here: https://github.com/witcher-sense/learning_python/blob/main/PrivateToPublic.py
E go only work with Python 3.6 or d one way pass am , and you go need to make some kind changes to openssl.conf to take make sure say RIPEMD160 hashing go work well.
If you de use Linux, then you fit do the things way De under here:
To take find where configuration file de , u go need to put the command way follow so:
You go Copy the path come change the directory way you de work with, for example:
Open openssl.conf for inside any text editor. You go need super user right to take edit this file. If you nor get am , ask your administrator for help:
Add the lines way follow so join the configuration file (some of them don already dey there and some dey wait for "uncommenting"):
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1
After you don do all these manipulation, RIPEDMD suppose start to dey work.