#!/usr/bin/env python3
"""Verify BIP84 address derivation independently of bip-utils."""
from mnemonic import Mnemonic
import hashlib
import hmac
from ecdsa import SECP256k1, SigningKey

mn = Mnemonic("english")
mnemonic_str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
seed = mn.to_seed(mnemonic_str)

print("Seed:", seed.hex())

def hmac_sha512(key, message):
    return hmac.new(key, message, hashlib.sha512).digest()

def sha256(data):
    return hashlib.sha256(data).digest()

def ripemd160(data):
    h = hashlib.new("ripemd160")
    h.update(data)
    return h.digest()

def hash160(data):
    return ripemd160(sha256(data))

def ckdf(seed):
    I = hmac_sha512(b"Bitcoin seed", seed)
    return I[:32], I[32:]

def derive_child(parent_key_bytes, parent_chain, index, hardened=True):
    if hardened:
        index = index + 0x80000000
    data = b"\x00" + parent_key_bytes + index.to_bytes(4, "big")
    I = hmac_sha512(parent_chain, data)
    chain_code = I[32:]
    n = SECP256k1.order
    key_int = (int.from_bytes(I[:32], "big") + int.from_bytes(parent_key_bytes, "big")) % n
    return key_int.to_bytes(32, "big"), chain_code

def secp256k1_pubkey(privkey_bytes):
    sk = SigningKey.from_string(privkey_bytes, curve=SECP256k1)
    return sk.verifying_key.to_string()

def bech32_encode(hrp, data_bytes):
    charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"

    def convertbits(data, frombits, tobits, pad=True):
        acc = 0
        bits = 0
        ret = []
        maxv = (1 << tobits) - 1
        for v in data:
            acc = (acc << frombits) | v
            bits += frombits
            while bits >= tobits:
                bits -= tobits
                ret.append((acc >> bits) & maxv)
        if pad and bits:
            ret.append((acc << (tobits - bits)) & maxv)
        return ret

    def polymod(values):
        GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
        res = 1
        for v in values:
            b = res >> 25
            res = ((res & 0x1ffffff) << 5) ^ v
            for i in range(5):
                res ^= GEN[i] if ((b >> i) & 1) else 0
        return res ^ 1

    data5 = convertbits([0x00] + list(data_bytes), 8, 5)
    payload = [0x00] + data5
    checksum = polymod([0] + [ord(c) for c in hrp] + [0] + payload + [0]*6)
    for i in range(6):
        payload.append((checksum >> (5 * (5 - i))) & 0x1f)
    return hrp + "1" + "".join(charset[d] for d in payload)

# Derive master
master_key, master_chain = ckdf(seed)
print("Master key (hex):", master_key.hex())

# Derive m/84'/0'/0'/0/0
path_indices = [84, 0, 0, 0, 0]
key = master_key
chain = master_chain
for idx in path_indices:
    key, chain = derive_child(key, chain, idx, hardened=True)

# Get public key
pub_uncompressed = secp256k1_pubkey(key)
# Compress: 02/03 prefix + x coordinate
y = int.from_bytes(pub_uncompressed, "big")
prefix = 0x02 if y % 2 == 0 else 0x03
pub_compressed = bytes([prefix]) + pub_uncompressed[:32]

print("PubKey compressed:", pub_compressed.hex())

h160 = hash160(pub_compressed)
print("H160:", h160.hex())

addr = bech32_encode("bc", h160)
print()
print("Independently computed BIP84 addr 0:", addr)
print("bip-utils says:                       bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu")
print("Commonly-cited test vector:           bc1qcr8te4kr609gcawutmrza0j4xv80jy8z3z67nr")
print()

if addr == "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu":
    print("MATCH: bip-utils derivation is correct.")
elif addr == "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z3z67nr":
    print("MATCH: the commonly-cited test vector is correct.")
else:
    print("NO MATCH - something is wrong with my derivation.")
