""" Sovereign Circuit — Dual Attestation Protocol Reference implementation, version 1.0 (draft) Normative dependencies: SHA-256 FIPS 180-4 JSON canonicalization RFC 8785 (JCS) subset, see canon() Nonce generation NIST SP 800-90A class DRBG via os.urandom Signatures Ed25519, RFC 8032 (optional layer, see sign.py) This file is deliberately dependency-free so it can be audited by reading it. All money is handled as integer cents. No floats anywhere in the settlement path. """ from __future__ import annotations import hashlib import hmac import json import os from dataclasses import dataclass, field from decimal import ROUND_HALF_UP, Decimal from typing import Optional PROTOCOL_ID = "sovereign-circuit/dual-attestation/1" # Domain separation tags. Every hash in the protocol is prefixed with exactly # one of these so that a digest computed for one purpose can never be replayed # as a digest for another purpose. TAG_COMMIT = b"SCDA1|commit|" TAG_RECORD = b"SCDA1|record|" TAG_ROUND = b"SCDA1|round|" NONCE_BYTES = 32 GENESIS = "0" * 64 # --------------------------------------------------------------------------- # Canonicalization # --------------------------------------------------------------------------- def canon(obj) -> bytes: """RFC 8785-compatible serialization for the subset of JSON this protocol uses: objects, arrays, strings, integers, booleans, null. Floats are rejected outright rather than canonicalized, because no protocol field is permitted to be a float.""" _reject_floats(obj) return json.dumps( obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False ).encode("utf-8") def _reject_floats(obj, path="$"): if isinstance(obj, float): raise TypeError(f"float at {path}: protocol fields must be int or str") if isinstance(obj, dict): for k, v in obj.items(): if not isinstance(k, str): raise TypeError(f"non-string key at {path}") _reject_floats(v, f"{path}.{k}") elif isinstance(obj, (list, tuple)): for i, v in enumerate(obj): _reject_floats(v, f"{path}[{i}]") def h(tag: bytes, payload: bytes) -> str: return hashlib.sha256(tag + payload).hexdigest() def new_nonce() -> str: return os.urandom(NONCE_BYTES).hex() # --------------------------------------------------------------------------- # Commitment # --------------------------------------------------------------------------- def commitment( *, round_id: str, asset_id: str, party_id: str, role: str, amount_cents: int, grade: str, nonce_hex: str, ) -> str: """C = SHA256( TAG_COMMIT || JCS(commit_body) ) Binding: the commitment covers round_id, asset_id, party_id and role, so a commitment published in one round for one unit by one party cannot be replayed in another round, for another unit, or attributed to the other side. The nonce provides hiding: without it, an adversary could brute-force the amount, since the plausible price space for a used laptop is small enough to enumerate exhaustively. """ if not isinstance(amount_cents, int) or isinstance(amount_cents, bool): raise TypeError("amount_cents must be int") if amount_cents < 0: raise ValueError("amount_cents must be non-negative") if role not in ("holder", "inspector"): raise ValueError("role must be holder or inspector") if len(bytes.fromhex(nonce_hex)) != NONCE_BYTES: raise ValueError(f"nonce must be {NONCE_BYTES} bytes") body = { "protocol": PROTOCOL_ID, "round_id": round_id, "asset_id": asset_id, "party_id": party_id, "role": role, "amount_cents": amount_cents, "grade": grade, "nonce": nonce_hex, } return h(TAG_COMMIT, canon(body)) def verify_reveal(commit_hash: str, reveal: dict) -> bool: """Constant-comparison check that a reveal matches its published commitment.""" try: recomputed = commitment( round_id=reveal["round_id"], asset_id=reveal["asset_id"], party_id=reveal["party_id"], role=reveal["role"], amount_cents=reveal["amount_cents"], grade=reveal["grade"], nonce_hex=reveal["nonce"], ) except (KeyError, TypeError, ValueError): return False return _ct_eq(recomputed, commit_hash) def _ct_eq(a: str, b: str) -> bool: return hmac.compare_digest(a.encode(), b.encode()) # --------------------------------------------------------------------------- # Settlement # --------------------------------------------------------------------------- @dataclass(frozen=True) class Policy: """Published settlement policy. A round is only valid against a policy whose hash was published before the commit window opened.""" tolerance_bp: int = 1000 # 1000 basis points = 10.00% floor_cents: int = 2500 # below this, spread math is noise absolute_floor_bp_exempt_cents: int = 5000 bonus_bp: int = 250 # verified-condition bonus, 2.50% bonus_inner_bp: int = 300 # bonus band, 3.00% bonus_cap_cents: int = 7500 late_reveal_penalty: str = "counterparty_amount" index_name: str = "SC-COMP-A" def _as_dict(p: Policy) -> dict: return { "tolerance_bp": p.tolerance_bp, "floor_cents": p.floor_cents, "absolute_floor_bp_exempt_cents": p.absolute_floor_bp_exempt_cents, "bonus_bp": p.bonus_bp, "bonus_inner_bp": p.bonus_inner_bp, "bonus_cap_cents": p.bonus_cap_cents, "late_reveal_penalty": p.late_reveal_penalty, "index_name": p.index_name, } def half_up(numerator: int, denominator: int) -> int: """Deterministic rounding. Banker's rounding is rejected because two independent implementations must agree to the cent, and half-up is the rule a non-technical participant expects when reading the arithmetic.""" return int( (Decimal(numerator) / Decimal(denominator)).quantize( Decimal(1), rounding=ROUND_HALF_UP ) ) def spread_bp(a_cents: int, b_cents: int) -> int: """Spread in basis points, measured against the LOWER of the two values. Measuring against the lower value is the conservative choice: it makes the band harder to satisfy than measuring against the midpoint or the higher value, so a round that auto-settles under this definition would also auto-settle under the looser ones. Stated plainly: the gap is expressed as a percentage of the smaller number. """ lo, hi = min(a_cents, b_cents), max(a_cents, b_cents) if lo == 0: return 0 if hi == 0 else 10**9 return half_up((hi - lo) * 10000, lo) @dataclass class Settlement: outcome: str # auto_settled | escalated | defaulted amount_cents: int spread_bp: int bonus_cents: int = 0 total_cents: int = 0 reason: str = "" index_amount_cents: Optional[int] = None def as_dict(self) -> dict: d = { "outcome": self.outcome, "amount_cents": self.amount_cents, "spread_bp": self.spread_bp, "bonus_cents": self.bonus_cents, "total_cents": self.total_cents, "reason": self.reason, } if self.index_amount_cents is not None: d["index_amount_cents"] = self.index_amount_cents return d def settle( holder_cents: int, inspector_cents: int, policy: Policy = Policy(), index_cents: Optional[int] = None, ) -> Settlement: """Deterministic settlement. Given the same two revealed amounts and the same policy, every conforming implementation MUST return the same result. Ladder: 1. Inside the tolerance band -> settle at the midpoint, half-up. 2. Outside the band -> escalate to the published index, and clamp the index into the revealed range so escalation can never land outside what both parties actually claimed. 3. Bonus -> if the holder's own self-grade landed inside the tighter inner band, pay a bonus. This is the line item that buys honest self-assessment. """ s = spread_bp(holder_cents, inspector_cents) lo, hi = min(holder_cents, inspector_cents), max(holder_cents, inspector_cents) small = hi <= policy.absolute_floor_bp_exempt_cents within = s <= policy.tolerance_bp or (small and (hi - lo) <= policy.floor_cents) if within: amount = half_up(holder_cents + inspector_cents, 2) reason = ( f"spread {s} bp within {policy.tolerance_bp} bp band; midpoint settlement" if s <= policy.tolerance_bp else f"low-value exemption: absolute gap {hi - lo}c within {policy.floor_cents}c" ) st = Settlement("auto_settled", amount, s, reason=reason) else: if index_cents is None: raise ValueError("spread exceeds band and no index value supplied") clamped = max(lo, min(hi, index_cents)) note = "" if clamped == index_cents else " (clamped into revealed range)" st = Settlement( "escalated", clamped, s, reason=( f"spread {s} bp exceeds {policy.tolerance_bp} bp band; " f"settled at {policy.index_name} index{note}" ), index_amount_cents=index_cents, ) if s <= policy.bonus_inner_bp: bonus = min( half_up(st.amount_cents * policy.bonus_bp, 10000), policy.bonus_cap_cents ) st.bonus_cents = bonus st.reason += ( f"; verified-condition bonus {policy.bonus_bp} bp " f"(self-grade inside {policy.bonus_inner_bp} bp)" ) st.total_cents = st.amount_cents + st.bonus_cents return st def settle_default(present_cents: int, policy: Policy = Policy()) -> Settlement: """One side committed and never revealed. The revealing side does NOT get to name the price unilaterally -- that would create an incentive to induce a non-reveal. Settlement is the counterparty's amount, meaning the party who stayed silent is treated as having agreed to the other side's figure, and no bonus is paid.""" return Settlement( "defaulted", present_cents, 0, total_cents=present_cents, reason="counterparty failed to reveal before deadline; " "settled at revealing party's amount, no bonus", ) # --------------------------------------------------------------------------- # Sealed record / hash chain # --------------------------------------------------------------------------- @dataclass class Chain: entries: list = field(default_factory=list) @property def head(self) -> str: return self.entries[-1]["entry_hash"] if self.entries else GENESIS def append(self, body: dict) -> dict: prev = self.head payload = {"prev": prev, "seq": len(self.entries), "body": body} entry_hash = h(TAG_RECORD, canon(payload)) entry = {**payload, "entry_hash": entry_hash} self.entries.append(entry) return entry def verify(self) -> bool: prev = GENESIS for i, e in enumerate(self.entries): if e["prev"] != prev or e["seq"] != i: return False expect = h( TAG_RECORD, canon({"prev": e["prev"], "seq": e["seq"], "body": e["body"]}) ) if not _ct_eq(expect, e["entry_hash"]): return False prev = e["entry_hash"] return True def round_record( *, round_id: str, asset_id: str, policy: Policy, holder_commit: str, inspector_commit: str, holder_reveal: Optional[dict], inspector_reveal: Optional[dict], settlement: Settlement, timestamps: dict, ) -> dict: """The sealed record. Note that it retains BOTH revealed amounts, not just the settled figure. The holder's claim stays visible as an assertion and the inspector's finding stays visible as an observation; the record shows both rather than collapsing them into one price. That distinction is the whole reason this protocol exists.""" return { "protocol": PROTOCOL_ID, "round_id": round_id, "asset_id": asset_id, "policy_hash": policy_hash(policy), "commitments": {"holder": holder_commit, "inspector": inspector_commit}, "reveals": { "holder": _public_reveal(holder_reveal), "inspector": _public_reveal(inspector_reveal), }, "settlement": settlement.as_dict(), "timestamps": timestamps, } def policy_hash(p: Policy) -> str: return h(TAG_ROUND, canon(_as_dict(p))) def _public_reveal(r: Optional[dict]) -> Optional[dict]: if r is None: return None return { "party_id": r["party_id"], "role": r["role"], "amount_cents": r["amount_cents"], "grade": r["grade"], "nonce": r["nonce"], }