# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-to-sign-a-cross-model-quorum-certificate-the-heterogeneous-agreement-trap.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-to-sign-a-cross-model-quorum-certificate-the-heterogeneous-agreement-trap.warrant.json
# Markdown SHA-256:   ae9549f5e5b4cd2e3f7161b22cd405b63c27af43daa23ad3a26675a6c68b6bbc
# Code AST SHA-256:   273979122e228c2b6c4eafe1c6a593f8c68eeaf155eafaad55ed5325279ba435
# Verified Classes:   DivergentCanonicalizationError
# Defensive Guards:   3 fail-closed invariants (zero external deps)

import hashlib


class DivergentCanonicalizationError(RuntimeError):
    """Raised when a voter cannot prove byte-identity with the received proposal."""
    pass


def verify_and_sign(
    received_canonical_bytes: bytes,
    received_hash_hex: str,
    sign_fn,
) -> bytes:
    """Voter-side gate: sign only the exact bytes received, never a reconstruction.

    sign_fn: callable(bytes) -> signature bytes.
    Returns the signature over the verified canonical bytes.
    """
    if not isinstance(received_canonical_bytes, (bytes, bytearray)):
        raise DivergentCanonicalizationError(
            "proposal arrived as a decoded object; byte-identity unprovable"
        )

    local_hash = hashlib.sha256(bytes(received_canonical_bytes)).hexdigest()

    if local_hash != received_hash_hex:
        raise DivergentCanonicalizationError(
            f"hash mismatch: local={local_hash} leader={received_hash_hex}; "
            "refusing to sign a value the node did not hold"
        )

    # No model, no parser, no normalizer between verification and signature.
    return sign_fn(bytes(received_canonical_bytes))


def cast_vote(proposal, sign_fn):
    """Entry point that enforces the boundary before any signing occurs."""
    if hasattr(proposal, "canonical_bytes") and hasattr(proposal, "hash_hex"):
        return verify_and_sign(proposal.canonical_bytes, proposal.hash_hex, sign_fn)

    # Proposal arrived as natural language or a live object: cannot certify bytes.
    raise DivergentCanonicalizationError(
        "proposal lacks canonical byte representation; "
        "heterogeneous voters would sign divergent hashes"
    )
