#!/usr/bin/env python3 """ pvgs_receipt_hash.py — Python companion for PVGSReceipt hash computation. This module provides canonical JSON serialization and SHA-256 hashing for PVGSReceipt structures generated by section7_master_receipt.lean. USAGE: from pvgs_receipt_hash import receipt_to_canonical, hash_receipt r = generate_receipt(...) # from Lean-generated JSON canonical = receipt_to_canonical(r) h = hash_receipt(r) # Or command-line: python pvgs_receipt_hash.py < receipt.json The canonical form sorts keys and removes whitespace to ensure deterministic hashing across Python versions and platforms. RECEIPT: section-7-python-hash-companion-2026-06-21 """ import hashlib import json from typing import Any # -------------------------------------------------------------------- # Canonical JSON Serialization # -------------------------------------------------------------------- def receipt_to_canonical(r: dict[str, Any]) -> str: """Convert a PVGSReceipt dictionary to a canonical JSON string. The canonical form: - Sorts all object keys alphabetically - Removes all whitespace (separators=(',',':')) - Converts rational numbers to strings (preserving exact values) - Flattens theoremStatus from list of pairs to a dict Args: r: A dictionary with the PVGSReceipt structure. Expected keys: version, stellarRank, classification, energy, sieveValue, rrcEvidence (dict with typeAdmissible, projectionAdmissible, mergeAdmissible), helstromBound, bakerBound, theoremStatus (list of [name, status] pairs), sha256. Returns: A deterministic JSON string suitable for cryptographic hashing. Example: >>> r = { ... "version": "PVGS_DQ_Bridge:v3", ... "stellarRank": 0, ... "classification": "Gaussian", ... "energy": 0, ... "sieveValue": "1/31", ... "rrcEvidence": { ... "typeAdmissible": True, ... "projectionAdmissible": True, ... "mergeAdmissible": True ... }, ... "helstromBound": "0.25", ... "bakerBound": "1/10", ... "theoremStatus": [ ... ["pvgs_energy_to_dq", "PROVEN"], ... ["variety_isomorphism", "PARTIAL"] ... ], ... "sha256": "TBD" ... } >>> receipt_to_canonical(r) '{"baker":"1/10","classification":"Gaussian","energy":0,"helstrom":"0.25","rrc":{"merge":true,"projection":true,"type":true},"sha256":"TBD","sieveValue":"1/31","stellarRank":0,"theorems":{"pvgs_energy_to_dq":"PROVEN","variety_isomorphism":"PARTIAL"},"version":"PVGS_DQ_Bridge:v3"}' """ # Extract RRC evidence sub-fields rrc = r.get("rrcEvidence", r.get("rrc", {})) theorems_raw = r.get("theoremStatus", r.get("theorems", [])) # Convert theoremStatus list of pairs to a dict theorems: dict[str, str] = {} if isinstance(theorems_raw, dict): theorems = theorems_raw elif isinstance(theorems_raw, list): for entry in theorems_raw: if isinstance(entry, (list, tuple)) and len(entry) == 2: theorems[entry[0]] = entry[1] elif isinstance(entry, str): # Handle "name:status" strings parts = entry.split(":", 1) if len(parts) == 2: theorems[parts[0]] = parts[1] # Build the canonical dictionary with sorted keys canonical: dict[str, Any] = { "baker": str(r.get("bakerBound", r.get("baker", "0"))), "classification": r.get("classification", ""), "energy": r.get("energy", 0), "helstrom": str(r.get("helstromBound", r.get("helstrom", "0"))), "rrc": { "merge": rrc.get("mergeAdmissible", rrc.get("merge", False)), "projection": rrc.get("projectionAdmissible", rrc.get("projection", False)), "type": rrc.get("typeAdmissible", rrc.get("type", False)), }, "sha256": r.get("sha256", "TBD"), "sieveValue": str(r.get("sieveValue", "0")), "stellarRank": r.get("stellarRank", r.get("stellar_rank", 0)), "theorems": theorems, "version": r.get("version", ""), } # Serialize to compact, sorted JSON return json.dumps(canonical, sort_keys=True, separators=(",", ":")) # -------------------------------------------------------------------- # SHA-256 Hash Computation # -------------------------------------------------------------------- def hash_receipt(r: dict[str, Any]) -> str: """Compute the SHA-256 hash of a receipt's canonical JSON form. Args: r: A PVGSReceipt dictionary (same format as receipt_to_canonical). Returns: A 64-character hex string representing the SHA-256 digest. Example: >>> r = {"version": "PVGS_DQ_Bridge:v3", ...} >>> h = hash_receipt(r) >>> len(h) 64 >>> all(c in '0123456789abcdef' for c in h) True """ canonical = receipt_to_canonical(r) return hashlib.sha256(canonical.encode("utf-8")).hexdigest() def hash_string(s: str) -> str: """Compute SHA-256 of an arbitrary string. Utility function for hashing canonical forms produced externally. """ return hashlib.sha256(s.encode("utf-8")).hexdigest() # -------------------------------------------------------------------- # Receipt Builder (convenience) # -------------------------------------------------------------------- def build_receipt( version: str = "PVGS_DQ_Bridge:v3", stellar_rank: int = 0, classification: str = "Gaussian", energy: int = 0, sieve_value: str = "0", rrc_type: bool = True, rrc_projection: bool = True, rrc_merge: bool = True, helstrom: str = "0", baker: str = "0", theorems: dict[str, str] | None = None, sha256: str = "TBD", ) -> dict[str, Any]: """Build a receipt dictionary from individual fields. Convenience function for constructing receipts without needing to remember the nested structure. Returns: A dictionary suitable for receipt_to_canonical and hash_receipt. """ if theorems is None: theorems = { "pvgs_energy_to_dq": "PROVEN", "hermite_sieve_isomorphism": "CONJECTURE", "variety_isomorphism": "PARTIAL", "pvgs_always_better": "PROVEN", "bms_exhaustive_only_known": "COMPUTATIONAL", } return { "version": version, "stellarRank": stellar_rank, "classification": classification, "energy": energy, "sieveValue": sieve_value, "rrcEvidence": { "typeAdmissible": rrc_type, "projectionAdmissible": rrc_projection, "mergeAdmissible": rrc_merge, }, "helstromBound": helstrom, "bakerBound": baker, "theoremStatus": [[k, v] for k, v in theorems.items()], "sha256": sha256, } # -------------------------------------------------------------------- # Verification helpers # -------------------------------------------------------------------- def verify_receipt_hash(r: dict[str, Any]) -> bool: """Verify that a receipt's sha256 matches its content. Returns True if the stored sha256 equals the computed hash of the canonical form (excluding the sha256 field itself). """ stored_hash = r.get("sha256", "TBD") if stored_hash == "TBD": return False # Hash not yet computed # Compute hash over canonical form with sha256 set to "TBD" r_copy = dict(r) r_copy["sha256"] = "TBD" computed = hash_receipt(r_copy) return computed == stored_hash def receipt_equality(r1: dict[str, Any], r2: dict[str, Any]) -> bool: """Check if two receipts are equal by comparing their hashes.""" return hash_receipt(r1) == hash_receipt(r2) # -------------------------------------------------------------------- # Command-line interface # -------------------------------------------------------------------- def main() -> None: """CLI: read receipt JSON from stdin, output canonical form and hash.""" import sys if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"): print("Usage: python pvgs_receipt_hash.py [receipt.json]") print("Reads receipt JSON and outputs canonical form + SHA-256.") sys.exit(0) if len(sys.argv) > 1: # Read from file with open(sys.argv[1], "r") as f: data = json.load(f) else: # Read from stdin data = json.load(sys.stdin) canonical = receipt_to_canonical(data) h = hash_receipt(data) print("=== Canonical JSON ===") print(canonical) print() print("=== SHA-256 ===") print(h) # -------------------------------------------------------------------- # Self-test # -------------------------------------------------------------------- def _self_test() -> None: """Run internal consistency checks.""" print("=== PVGS Receipt Hash Self-Test ===") # Test 1: Basic receipt r1 = build_receipt( stellar_rank=0, classification="Gaussian", energy=0, sieve_value="1/31", rrc_type=True, rrc_projection=True, rrc_merge=True, helstrom="0.25", baker="1/10", ) c1 = receipt_to_canonical(r1) h1 = hash_receipt(r1) print(f"Test 1 (Gaussian): hash={h1[:16]}...") assert len(h1) == 64, "Hash must be 64 hex chars" assert all(c in "0123456789abcdef" for c in h1), "Hash must be hex" # Test 2: Determinism h1b = hash_receipt(r1) assert h1 == h1b, "Hash must be deterministic" print("Test 2 (determinism): PASS") # Test 3: Different receipts → different hashes r2 = build_receipt( stellar_rank=1, classification="PAGS", energy=5, sieve_value="1/8191", ) h2 = hash_receipt(r2) assert h1 != h2, "Different receipts must have different hashes" print(f"Test 3 (PAGS): hash={h2[:16]}...") # Test 4: Verify hash of hash itself r1_hashed = dict(r1) r1_hashed["sha256"] = h1 # Verification should pass when sha256 matches assert verify_receipt_hash(r1_hashed), "Hash verification should pass" print("Test 4 (hash verification): PASS") # Test 5: Canonical form structure assert "version" in c1, "Canonical form must contain version" assert "stellarRank" in c1, "Canonical form must contain stellarRank" assert "rrc" in c1, "Canonical form must contain rrc" assert "theorems" in c1, "Canonical form must contain theorems" print("Test 5 (canonical structure): PASS") print("\nAll self-tests PASSED.") if __name__ == "__main__": # Run self-test when executed directly _self_test()