#!/usr/bin/env python3
"""Independent PNTMAP ledger proof verifier. Python 3 stdlib only.

Usage: python3 verify_pntmap.py <report_id> [base_url]

Checks, against the public API and with no PNTMAP code:
  1 leaf recompute      sha256(canonical_string) == leaf_hash
  2 merkle fold         leaf + path == block merkle_root
  3 block header        sha256(height|prev|root|count|created_at) == block_hash
  4 prev-hash link      previous block's block_hash == this block's prev_block_hash
"""
import hashlib, json, sys, urllib.request

def get(base, path):
    with urllib.request.urlopen(base + path, timeout=30) as r:
        return json.load(r)

def sha(b): return hashlib.sha256(b).hexdigest()
def pair(a, b): return sha(bytes.fromhex(a) + bytes.fromhex(b))

def step(n, name, got, want):
    ok = got == want
    print("%s  %d %-16s computed=%s expected=%s" % ("PASS" if ok else "FAIL", n, name, got, want))
    return ok

def main(report_id, base="https://pntmap.co.uk"):
    p = get(base, "/api/public/proof/%s" % report_id)
    b = p["block"]
    ok = step(1, "leaf recompute", sha(p["record"]["canonical_string"].encode()), p["record"]["leaf_hash"])

    acc = p["record"]["leaf_hash"]
    for s in p["merkle_path"]:
        acc = pair(s["hash"], acc) if s["position"] == "left" else pair(acc, s["hash"])
    ok &= step(2, "merkle fold", acc, b["merkle_root"])

    header = "|".join([str(b["height"]), b["prev_block_hash"], b["merkle_root"],
                       str(b["record_count"]), b["created_at"]])
    ok &= step(3, "block header", sha(header.encode()), b["block_hash"])

    if b["height"] == 0:
        print("PASS  4 prev-hash link  genesis block, no predecessor")
    else:
        prev = get(base, "/api/public/ledger/blocks?before_height=%d&limit=1" % b["height"])["blocks"][0]
        ok &= step(4, "prev-hash link", prev["block_hash"], b["prev_block_hash"])

    a = p["anchor"]
    print("INFO  5 bitcoin anchor  status=%s btc_block=%s" % (a["status"], a.get("bitcoin_block_height")))
    print("INFO    ots verify: curl -O %s/api/public/ledger/anchor/%d.ots && ots verify pntmap-block-%d.ots"
          % (base, b["height"], b["height"]))
    print("RESULT %s (report %s, block %d)" % ("PASS" if ok else "FAIL", report_id, b["height"]))
    return 0 if ok else 1

if __name__ == "__main__":
    sys.exit(main(*sys.argv[1:]))
