# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-equivocation-in-multi-agent-task-commitment-when-attestation-quorums-overlap.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-equivocation-in-multi-agent-task-commitment-when-attestation-quorums-overlap.warrant.json
# Markdown SHA-256:   e891c9b678d1232eccc84bcd93d2fc8dae9906f7e3dd0b8ca92b8e3c6ebb6190
# Code AST SHA-256:   a4a329ea419f7341af0a778ad3599497d0493e277138f674d22116c697813a45
# Verified Classes:   AttestationInvariantError, Commitment, Attestation
# Defensive Guards:   4 fail-closed invariants (zero external deps)

import hashlib
from dataclasses import dataclass


class AttestationInvariantError(RuntimeError):
    """Raised when an attestation certificate violates cross-phase binding."""
    pass


@dataclass(frozen=True)
class Commitment:
    task_id: str
    input_hash: str
    signers: frozenset


@dataclass(frozen=True)
class Attestation:
    task_id: str
    bound_input_hash: str
    result_hash: str
    signers: frozenset


def verify_attestation(
    att: Attestation,
    commitment: Commitment,
    f: int,
    committed_validators: frozenset,
    attest_validators: frozenset,
) -> None:
    # Condition 1: content binding — the attestation must reference the
    # exact input that was committed, not merely the same task id.
    if att.task_id != commitment.task_id:
        raise AttestationInvariantError(
            f"task id mismatch: attestation={att.task_id!r} "
            f"commitment={commitment.task_id!r}"
        )
    if att.bound_input_hash != commitment.input_hash:
        raise AttestationInvariantError(
            f"input hash mismatch for task {att.task_id!r}: "
            f"attested={att.bound_input_hash!r} "
            f"committed={commitment.input_hash!r}"
        )

    # Condition 2: quorum validity is a property of the intersection of the
    # commitment and attestation validator sets, not of either set alone.
    intersection = att.signers & commitment.signers
    if len(intersection) < f + 1:
        raise AttestationInvariantError(
            f"insufficient honest intersection for task {att.task_id!r}: "
            f"|Q_c ∩ Q_a|={len(intersection)} < f+1={f + 1}"
        )

    # Condition 3: every signer of the attestation must have been eligible
    # in both phases; a signer absent from the commitment set cannot have
    # validated the bound input locally.
    rogue = att.signers - committed_validators
    if rogue:
        raise AttestationInvariantError(
            f"attestation signers not in commitment pool: {sorted(rogue)}"
        )


def attest(
    att: Attestation,
    commitment: Commitment,
    f: int,
    committed_validators: frozenset,
    attest_validators: frozenset,
) -> str:
    verify_attestation(
        att, commitment, f, committed_validators, attest_validators
    )
    return hashlib.sha256(
        (att.task_id + att.result_hash + att.bound_input_hash).encode()
    ).hexdigest()
