#!/usr/bin/env python3
"""
verify_model_receipt.py — verify a Hive model-call receipt offline.

No network. No account. No Hive code. Two dependencies you already trust:
the Python standard library, and `cryptography` for Ed25519.

    pip install cryptography

Usage:
    # 1. get a receipt (this is the only step that touches the network)
    curl -s -X POST https://receipts.thehiveryiq.com/v1/model-receipts/run \
      -H 'Content-Type: application/json' \
      -d '{"provider":"openrouter","model":"anthropic/claude-sonnet-4",
           "prompt":"Reply with exactly: routed receipt test."}' > receipt.json

    # 2. get the public key once, then keep it forever
    curl -s https://receipts.thehiveryiq.com/v1/prov/pubkey > pubkey.json

    # 3. verify with the network off, now or in ten years
    python3 verify_model_receipt.py receipt.json pubkey.json

What this checks
----------------
1. CONTENT ADDRESS. The receipt carries `payload_sha256`. This script
   re-derives it from `signed_body` and compares. If any byte of the
   recorded run changed, this fails.

2. SIGNATURE. The signer signs the ASCII string

       hive-receipt <receipt_id> <payload_sha256> <ts>

   with Ed25519. This script verifies that signature against the published
   public key. If the receipt id, the content address, or the timestamp was
   altered, this fails.

3. ROUTING DISCLOSURE. Prints the model that was requested and the model the
   provider reported for the call, as two separate fields, plus the custody
   path and the evidence basis for each kind of field.

What this does NOT check
------------------------
That the model output is correct. That the provider ran the weights it named.
A receipt records what was sent and returned, by hash, and when. The claim
about which weights served the call is the provider's claim, relayed and
signed. That distinction is the point of the evidence basis printed below.

It also cannot tell you about a receipt that was never issued. Detecting a
missing receipt needs an append-only inclusion log, which is designed and
not serving. See the honest limits on the page you got this from.
"""

import hashlib
import json
import sys

try:
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
except ImportError:
    sys.exit("Install the dependency first:  pip install cryptography")


GREEN = "\033[32m"
RED = "\033[31m"
DIM = "\033[2m"
OFF = "\033[0m"


def canonical_body(signed_body: dict) -> str:
    """The exact serialisation the signer hashed: sorted keys, no whitespace."""
    return json.dumps(signed_body, sort_keys=True, separators=(",", ":"))


def b64u_decode(s: str) -> bytes:
    import base64

    return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))


def main() -> int:
    if len(sys.argv) != 3:
        sys.exit(f"usage: {sys.argv[0]} <receipt.json> <pubkey.json>")

    with open(sys.argv[1], encoding="utf-8") as fh:
        doc = json.load(fh)
    with open(sys.argv[2], encoding="utf-8") as fh:
        keydoc = json.load(fh)

    receipt = doc.get("receipt", doc)
    body = receipt["signed_body"]

    print(f"\n{DIM}receipt{OFF} {receipt['receipt_id']}")
    print(f"{DIM}key    {OFF} {receipt['key_id']}  ({receipt['algorithm']})\n")

    ok = True

    # ---- 1. content address -------------------------------------------------
    recomputed = hashlib.sha256(canonical_body(body).encode("utf-8")).hexdigest()
    if recomputed == receipt["payload_sha256"]:
        print(f"{GREEN}PASS{OFF}  content address  sha256 of the recorded run matches")
    else:
        ok = False
        print(f"{RED}FAIL{OFF}  content address  the recorded run does not hash to payload_sha256")
        print(f"      expected {receipt['payload_sha256']}")
        print(f"      got      {recomputed}")

    # ---- 2. signature -------------------------------------------------------
    canonical = (
        f"hive-receipt {receipt['receipt_id']} "
        f"{receipt['payload_sha256']} {receipt['ts']}"
    ).encode("utf-8")

    pubkey_hex = keydoc.get("pubkey_hex") or keydoc.get("public_key_hex")
    pk = Ed25519PublicKey.from_public_bytes(bytes.fromhex(pubkey_hex))
    try:
        pk.verify(b64u_decode(receipt["sig_b64u"]), canonical)
        print(f"{GREEN}PASS{OFF}  signature        Ed25519 verified against the published key")
    except Exception:
        ok = False
        print(f"{RED}FAIL{OFF}  signature        does not verify against the published key")

    # ---- 3. routing disclosure ---------------------------------------------
    print()
    requested = body.get("api_model", "n.a.")
    reported = body.get("model", "n.a.")
    print(f"  model requested   {requested}")
    print(f"  model reported    {reported}", end="")
    print("   <- DIFFERS from the request" if reported != requested else "")
    print(f"  custody           {body.get('custody', doc.get('custody', 'n.a.'))}")
    print(f"  route             {body.get('route', 'n.a.')}")
    print(f"  prompt sha256     {body.get('prompt_sha256', 'n.a.')}")
    print(f"  response sha256   {body.get('response_sha256', 'n.a.')}")
    print(f"  finish reason     {body.get('finish_reason', 'n.a.')}")

    print(f"\n{DIM}  Evidence basis. The hashes above are computed by the signer over bytes it")
    print("  handled, so they are witnessed. The model identity is what the provider")
    print("  reported for this call, relayed and signed, not a hardware attestation.")
    print(f"  The receipt records which of the two each field is.{OFF}")

    print(("\n" + GREEN + "RECEIPT VERIFIED" + OFF) if ok else ("\n" + RED + "RECEIPT INVALID" + OFF))
    print(f"{DIM}Change one byte of signed_body and run this again. It goes red.{OFF}\n")
    return 0 if ok else 1


if __name__ == "__main__":
    raise SystemExit(main())
