#!/usr/bin/env python3
"""Verify a Quantum Pad seal with nothing but Python's hashlib (no Quantum Pad code, no elliptic curves).
usage: python3 quantumpad_verify.py <mint>   (fetches https://quantumpad.bond/api/coin/<mint>)
   or: python3 quantumpad_verify.py proof.json
"""
import hashlib, json, struct, sys, urllib.request
H = lambda *a: hashlib.sha256(b''.join(a)).digest()
def adrs(t, key=0, chain=0, step=0, height=0, index=0): return struct.pack('>6I', t, key, chain, step, height, index) + b'\0' * 8
arg = sys.argv[1]
import os
SITE = os.environ.get('QP_SITE', 'https://quantumpad.bond')
data = json.load(open(arg)) if arg.endswith('.json') else json.load(urllib.request.urlopen(f'{SITE}/api/coin/{arg}'))
if 'seals' in data and not data['seals']: sys.exit('no Quantum Pad seal for this mint yet')
seal = data['seals'][0] if 'seals' in data else data
text, p = seal['text'], seal['pq']
root, pub, sig = bytes.fromhex(p['root']), bytes.fromhex(p['pubSeed']), bytes.fromhex(p['sig'])
assert len(sig) == 2404, 'signature must be 2,404 bytes'
key = struct.unpack('>I', sig[:4])[0]
dg = H(text.encode())
d = [x for b in dg for x in (b >> 4, b & 15)]; c = sum(15 - x for x in d); d += [(c >> 8) & 15, (c >> 4) & 15, c & 15]
ends = []
for i in range(67):
    x = sig[4 + 32 * i: 36 + 32 * i]
    for j in range(d[i], 15): x = H(pub, adrs(1, key, i, j), x)
    ends.append(x)
node, j = H(pub, adrs(2, key), *ends), key
for h in range(8):
    sib = sig[4 + 67 * 32 + 32 * h: 4 + 67 * 32 + 32 * (h + 1)]
    node = H(pub, adrs(3, 0, 0, 0, h + 1, j >> 1), *((sib, node) if j & 1 else (node, sib))); j >>= 1
print('claim:\n' + text + '\n')
print('PQ signature:', 'VALID' if node == root else 'INVALID', f'(key #{key}, root {p["root"][:16]}...)')
sys.exit(0 if node == root else 1)
