Post
Topic
Board Development & Technical Discussion
Re: Need help reversing bitcoinutils "tagged_hash" to generate Taproot addresses
by
BTCW
on 17/01/2024, 16:14:38 UTC
Think I nailed it!

I don't think such concrete Python code has been published elsewhere; correct me if I'm wrong. Anyways, here we go.

Let's start with some imports and two functions:

Code:
import hashlib, base58, bitcoinutils.bech32
from fastecdsa import keys, curve, ecdsa, point
curve = curve.secp256k1

def privkey_to_pubkey_hex(privkey, compressed=False):
    #Accept 64 char HEX, WIF and arbitrary lengt integers as inputs
   
    try: #Is it WIF?
        privkey_hex = base58.b58decode_check(privkey).hex()
        privkey_hex = privkey_hex.replace('0x','')
        if len(privkey_hex) == 68:
            privkey_hex = privkey_hex[2:-2]
        if len(privkey_hex) == 66:
            privkey_hex = privkey_hex[2:]
    except:
        pass
    try: #Is it HEX?
        if int(str(privkey),16):
            privkey_hex = privkey
            privkey_hex = privkey_hex.replace('0x','')
            if len(privkey_hex) == 68:
                privkey_hex = privkey_hex[2:-2]
            if len(privkey_hex) == 66:
                privkey_hex = privkey_hex[2:]
    except:
        pass
    try: #Is it INT?
        if isinstance(privkey, int):
            privkey_hex = hex(privkey)[2:].zfill(64)
    except:
        pass
           
    try:
        privkey_int = int(privkey_hex,16)
        pub_key = keys.get_public_key(privkey_int, curve)
        uncompressed_public_key = '04'+str(hex(pub_key.x)[2:]).zfill(64)+str(hex(pub_key.y)[2:]).zfill(64)
        if compressed==False:
            return uncompressed_public_key
        else:
            if int(str(pub_key.y)) % 2 == 0:
                compressed_public_key = '02'
            else:
                compressed_public_key = '03'
           
            compressed_public_key += str(uncompressed_public_key[2:66])
           
            return compressed_public_key
    except:
        pass

def decompress_point(x, curve):
    y_squared = (x**3 + curve.a * x + curve.b) % curve.p
    y = pow(y_squared, (curve.p + 1) // 4, curve.p)
    if y % 2 == 0:
        return point.Point(x, y, curve=curve)
    else:
        return point.Point(x, curve.p - y, curve=curve)

And in action:

Code:
seed = '0000000000000000000000000000000000000000000000000000000000000001'
short_x = privkey_to_pubkey_hex(seed, compressed=True)[2:]
tag_hash = hashlib.sha256('TapTweak'.encode()).hexdigest()
tagged_hash = hashlib.sha256(bytes.fromhex(tag_hash + tag_hash + short_x)).hexdigest()
tagged_hash_pubx = privkey_to_pubkey_hex(tagged_hash, compressed=True)[2:]

point1 = decompress_point(int(short_x, 16), curve)
point2 = decompress_point(int(tagged_hash_pubx, 16), curve)

point_add = point1 + point2
tweaked_witness = hex(point_add.x)[2:]
print(bitcoinutils.bech32.encode('bc', 1, bytes.fromhex(tweaked_witness)))

Tweaked Taproot demystified - mission accomplished - the output is the desired Taproot public address!

Code:
bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9

Yay.

Thank you for all your support. This was the least user-friendly address generation code thus far Smiley