# Pure-python secp256k1; computes NUT-00 unblinding + verification vectors (cashubtc/nuts #200)
import hashlib
P=2**256-2**32-977; N=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
G=(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)
def add(a,b):
    if a is None: return b
    if b is None: return a
    if a[0]==b[0] and (a[1]+b[1])%P==0: return None
    l=(3*a[0]*a[0]*pow(2*a[1],-1,P) if a==b else (b[1]-a[1])*pow(b[0]-a[0],-1,P))%P
    x=(l*l-a[0]-b[0])%P; return (x,(l*(a[0]-x)-a[1])%P)
def mul(k,p):
    r=None
    while k:
        if k&1: r=add(r,p)
        p=add(p,p); k>>=1
    return r
def neg(p): return (p[0],(-p[1])%P)
def ser(p): return ('02' if p[1]%2==0 else '03')+'%064x'%p[0]
def parse(h):
    x=int(h[2:],16); y=pow((x**3+7)%P,(P+1)//4,P)
    if (y%2==0)!=(h[:2]=='02'): y=P-y
    return (x,y)
def h2c(msg):
    mh=hashlib.sha256(b"Secp256k1_HashToCurve_Cashu_"+msg).digest()
    for c in range(2**16):
        h=hashlib.sha256(mh+c.to_bytes(4,'little')).digest()
        try: return parse('02'+h.hex())
        except Exception: pass
        # parse may return an off-curve point; check
def h2c(msg):
    mh=hashlib.sha256(b"Secp256k1_HashToCurve_Cashu_"+msg).digest()
    for c in range(2**16):
        h=hashlib.sha256(mh+c.to_bytes(4,'little')).digest()
        x=int.from_bytes(h,'big')
        if x>=P: continue
        y2=(x**3+7)%P; y=pow(y2,(P+1)//4,P)
        if y*y%P==y2: return (x,y if y%2==0 else P-y)
# self-check against published vectors
assert ser(h2c(bytes.fromhex('00'*32)))=='024cce997d3b518f739663b757deaec95bcd9473c30a14ac2fd04023a739d1a725'
assert ser(h2c(bytes.fromhex('00'*31+'01')))=='022e7158e11c9506f1aa4248bf531298daa7febd6194f003edcd9b93ade6253acf'
assert ser(h2c(bytes.fromhex('00'*31+'02')))=='026cdbe15362df59cd1dd3c9c11de8aedac2106eca69236ecd9fbe117af897be4f'
V=[('d341ee4871f1f889041e63cf0d3823c713eea6aff01e80f1719f08f9e5be98f6','99fce58439fc37412ab3468b73db0569322588f62fb3a49182d67e23d877824a','033b1a9737a40cc3fd9b6af4b723632b76a67a36782596304612a6c2bfb5197e6d'),
   ('f1aaf16c2239746f369572c0784d9dd3d032d952c2d992175873fb58fae31a60','f78476ea7cc9ade20f9e05e58a804cf19533f03ea805ece5fee88c8e2874ba50','029bdf2d716ee366eddf599ba252786c1033f47e230248a4612a5670ab931f1763')]
for x,r,B in V:
    assert ser(add(h2c(bytes.fromhex(x)),mul(int(r,16),G)))==B   # published B_ reproduced
print("self-checks passed (h2c x3, B_ x2)\n")
print("### Unblinding (C = C_ - r*K)\n```shell")
n=0
for k in (1,int('7f'*32,16)):
    K=mul(k,G)
    for x,r,B in V:
        n+=1; Bp=parse(B); Cp=mul(k,Bp); C=add(Cp,neg(mul(int(r,16),K)))
        assert C==mul(k,h2c(bytes.fromhex(x)))   # = k*Y, consistency
        print(f"# Test {n}\nC_: {ser(Cp)}\nr:  {r}\nK:  {ser(K)}\nC:  {ser(C)}\n")
print("```\n### Verification (C == k*hash_to_curve(x))\n```shell")
n=0
for k in (1,int('7f'*32,16)):
    for x,r,B in V:
        n+=1; C=mul(k,h2c(bytes.fromhex(x)))
        print(f"# Test {n}\nk: {'%064x'%k}\nx: {x}\nC: {ser(C)}\n")
print("```")
