Points are decompressed by solving for y
in the equation used for secp256k1's elliptic curve where x
is the last 32 bytes of your public key. The equation is y^2 = x^3 + 7
. You will get 2 possible y
values, one even and one odd. The correct one is indicated by the prefix byte of your public key which indicates whether y
is even or odd.
Note that all operations must be modulo p
which is defined by secp256k1's spec as being 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
.
This python script will decompress the two public keys that you have posted:
#! /usr/bin/env python3
import binascii
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
def decompress_pubkey(pk):
x = int.from_bytes(pk[1:33], byteorder='big')
y_sq = (pow(x, 3, p) + 7) % p
y = pow(y_sq, (p + 1) // 4, p)
if y % 2 != pk[0] % 2:
y = p - y
y = y.to_bytes(32, byteorder='big')
return b'\x04' + pk[1:33] + y
print(binascii.hexlify(decompress_pubkey(binascii.unhexlify('0229b3e0919adc41a316aad4f41444d9bf3a9b639550f2aa735676ffff25ba3898'))).decode())
print(binascii.hexlify(decompress_pubkey(binascii.unhexlify('02f15446771c5c585dd25d8d62df5195b77799aa8eac2f2196c54b73ca05f72f27'))).decode())
The uncompressed public keys are:
0429b3e0919adc41a316aad4f41444d9bf3a9b639550f2aa735676ffff25ba3898d6881e81d2e0163348ff07b3a9a3968401572aa79c79e7edb522f41addc8e6ce
04f15446771c5c585dd25d8d62df5195b77799aa8eac2f2196c54b73ca05f72f274d335b71c85e064f80191e1f7e2437afa676a3e2a5a5fafcf0d27940cd33e4b4
Pubkey decompression is completely different and unrelated to address generation. Addresses are generated by taking a public key as is (either compressed or uncompressed) hashing it, and then encoding it. The compression matters because the resulting serialized public will produce one hash (and thus one address) when it is compressed, and a different hash (and thus different address) when uncompressed.
Addresses are encoded by hashing the serialized public key with SHA256 and then with RIPEMD160. The resulting hash is encoded using Base58 Check encoding.
In the transaction you provided, the three public keys:
04ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28
0229b3e0919adc41a316aad4f41444d9bf3a9b639550f2aa735676ffff25ba3898
02f15446771c5c585dd25d8d62df5195b77799aa8eac2f2196c54b73ca05f72f27
have the following hashes respectively:
946cb2e08075bcbaf157e47bcb67eb2b2339d242
55af2ea3c45819c6c5ae710d29fcaaced5b00cc7
8b38a8d40e08362046dee55c1c94e7991d7dec75
Encoding those as version 0 addresses with Base 58 Check encoding results in the addresses you are expecting:
1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P
18p4JBHP3EAKCc4jqN8XzuEJtvq8G9NAot
1Dh8oSChJWZQx5sr7ePsBNtw7uKMsNnYNC
The code I used for this is:
#! /usr/bin/env python3
import binascii
import hashlib
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def sha256(s):
return hashlib.new('sha256', s).digest()
def ripemd160(s):
return hashlib.new('ripemd160', s).digest()
def hash256(s):
return sha256(sha256(s))
def hash160(s):
return ripemd160(sha256(s))
def encode(b):
# Convert big-endian bytes to integer
n = int('0x0' + binascii.hexlify(b).decode('utf8'), 16)
# Divide that integer into bas58
res = []
while n > 0:
n, r = divmod (n, 58)
res.append(b58_digits[r])
res = ''.join(res[::-1])
# Encode leading zeros as base58 zeros
import sys
czero = b'\x00'
if sys.version > '3':
# In Python3 indexing a bytes returns numbers, not characters.
czero = 0
pad = 0
for c in b:
if c == czero: pad += 1
else: break
return b58_digits[0] * pad + res
def to_address(b, version):
data = version + b
checksum = hash256(data)[0:4]
data += checksum
return encode(data)
pk1 = binascii.unhexlify("0229b3e0919adc41a316aad4f41444d9bf3a9b639550f2aa735676ffff25ba3898")
pk2 = binascii.unhexlify("02f15446771c5c585dd25d8d62df5195b77799aa8eac2f2196c54b73ca05f72f27")
pk3 = binascii.unhexlify("04ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28")
h1 = hash160(pk1)
h2 = hash160(pk2)
h3 = hash160(pk3)
print(to_address(h3, b'\x00'))
print(to_address(h1, b'\x00'))
print(to_address(h2, b'\x00'))
(Base58 encoding function from https://github.com/bitcoin-core/HWI/blob/master/hwilib/base58.py)
As you can see, this does not do any point compression or decompression. Rather it takes the public key as it is and encodes it.