"""Claim/proof receipts for ANNEX-2.3 R2 governed claim audit read-back. A receipt is a minimal, tamper-evident record generated alongside a governed claim. Its digest is sha256(canonical_json(payload)) so the payload can be re-verified on read-back, consistent with the evidence and event chain. """ from __future__ import annotations import time import uuid from dataclasses import dataclass from typing import Any from .models import compute_sha256_hex def receipt_digest(payload: dict[str, Any]) -> str: return compute_sha256_hex(payload) @dataclass class ClaimReceipt: receipt_id: str kind: str task_id: str worker_id: str claim_id: str | None proof_id: str | None digest: str payload: dict[str, Any] created_at: float def to_dict(self) -> dict[str, Any]: return { "receipt_id": self.receipt_id, "kind": self.kind, "task_id": self.task_id, "worker_id": self.worker_id, "claim_id": self.claim_id, "proof_id": self.proof_id, "digest": self.digest, "payload": self.payload, "created_at": self.created_at, } def build_receipt( *, kind: str, task_id: str, worker_id: str, claim_id: str | None = None, proof_id: str | None = None, extra: dict[str, Any] | None = None, created_at: float | None = None, ) -> ClaimReceipt: """Build a ClaimReceipt whose digest covers the full digestable payload.""" extra = extra or {} payload = { "kind": kind, "task_id": task_id, "worker_id": worker_id, "claim_id": claim_id, "proof_id": proof_id, **extra, } return ClaimReceipt( receipt_id=f"rct-{uuid.uuid4()}", kind=kind, task_id=task_id, worker_id=worker_id, claim_id=claim_id, proof_id=proof_id, digest=receipt_digest(payload), payload=payload, created_at=created_at if created_at is not None else time.time(), )