# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-reject-a-winning-contract-net-bid-whose-settlement-proof-verifies-but-whose-reputation-stake-was-posted-after-b.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-reject-a-winning-contract-net-bid-whose-settlement-proof-verifies-but-whose-reputation-stake-was-posted-after-b.warrant.json
# Markdown SHA-256:   2756a117505f12d9a829e7a5fb40d263110cf4526bb0962f354371b1ff066863
# Code AST SHA-256:   c1bfab3d40264bffd6d1c53e01353cacedcf79cad6d9870f29553e46e96f9d1e
# Verified Classes:   LateStakeBidError, Bid, SettlementProof, ReputationStake, Auction
# Defensive Guards:   6 fail-closed invariants (zero external deps)

from dataclasses import dataclass


class LateStakeBidError(RuntimeError):
    """Raised when a winning bid's reputation stake was committed after bid closure."""
    pass


@dataclass(frozen=True)
class Bid:
    nonce: str
    contract_hash: str
    close_block: int


@dataclass(frozen=True)
class SettlementProof:
    contract_hash: str
    verified: bool


@dataclass(frozen=True)
class ReputationStake:
    bid_nonce: str
    amount: int
    required_bond: int
    commit_block: int
    commit_log_index: int
    lock_until_block: int


@dataclass(frozen=True)
class Auction:
    contract_hash: str
    close_block: int
    close_log_index: int
    delivery_deadline_block: int
    required_bond: int


def admit_for_award(
    bid: Bid,
    proof: SettlementProof,
    stake: ReputationStake,
    auction: Auction,
) -> None:
    if not proof.verified or proof.contract_hash != auction.contract_hash:
        raise LateStakeBidError(
            f"settlement proof invalid for contract {auction.contract_hash}"
        )

    if stake.bid_nonce != bid.nonce:
        raise LateStakeBidError(
            f"stake bound to nonce {stake.bid_nonce!r}, bid nonce is {bid.nonce!r}"
        )

    if stake.amount < auction.required_bond:
        raise LateStakeBidError(
            f"stake {stake.amount} below required bond {auction.required_bond}"
        )

    # The critical temporal invariant: stake must precede closure in ledger order.
    if stake.commit_block > auction.close_block:
        raise LateStakeBidError(
            f"stake committed at block {stake.commit_block} after closure "
            f"at block {auction.close_block} (bid-then-fund attack)"
        )

    if (
        stake.commit_block == auction.close_block
        and stake.commit_log_index >= auction.close_log_index
    ):
        raise LateStakeBidError(
            f"same-block stake at log index {stake.commit_log_index} does not "
            f"precede closure log index {auction.close_log_index}"
        )

    if stake.lock_until_block < auction.delivery_deadline_block:
        raise LateStakeBidError(
            f"stake unlocks at {stake.lock_until_block} before delivery "
            f"deadline {auction.delivery_deadline_block}"
        )
