Reference · Threat model
Adversaries, defenses, and what's still open
docs/threat-model.md in the repository.Adversaries modeled
20
Fixed / mitigated
16
each with regression tests
Partial
3
trust assumptions stated
Open / deferred
1
MEV (out of scope)
The adversary table
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
#
Threat
Defense
Status
| # | Threat | Defense | Status |
|---|---|---|---|
| 1 | One malicious or faulty resolver | Quorum needs quorumThreshold byte-identical answers; one resolver can't propose alone. | mitigated |
| 2 | Colluding resolver majority | Anyone can dispute; committees are drawn from the whole pool; Tier 2 then VOID, never a token vote. Residual: if the colluders ARE the pool they also sit on committees; authorization is owner-gated; flat bonds, no stake. | partial |
| 3 | Late or stale observation | Rejected after observationDeadline; price-at abstains on rounds older than maxStaleness. | fixed |
| 4 | Dispute spam / griefing | A wrong disputer loses the dispute bond; one dispute case per event. Residual: bonds are flat, not scaled to stakes. | mitigated |
| 5 | Event Bus bypass by a consumer | Consumers hold only Settlement / IEventBus — enforced by a guard test per consumer. | mitigated |
| 6 | Composite griefing (unknown operands, huge or cyclic graphs, dead operands) | Operands must exist; ≤10 children, depth ≤8; cycles impossible (proof below); voided operands propagate. | mitigated |
| 7 | Non-deterministic composition | Uses committed finalizedAt / occurredAt, never block time at resolution; result cached forever. | mitigated |
| 8 | Front-running market creation / settlement (MEV) | Out of MVP scope. | deferred |
| 9 | Careless outcome decoding | bool stays the first ABI word in v2; every decode site audited; Registry rejects malformed v2 payloads. | fixed |
| 10 | Committee-selection manipulation | Commit-reveal among resolvers: committee drawn from the XOR of revealed salts; withholders excluded. Residual: last revealer can drop themselves; block-data fallback only if nobody reveals. | mitigated |
| 11 | Losers exit after the outcome is public | tradingClosesAt + 'outcome known' checks on every deposit/withdraw/trade. | fixed |
| 12 | Voided event locks market funds | getAvailability returns Voided; Market refunds; range markets pay 1/N. | fixed |
| 13 | Observations before the event happens | Rejected before openTimestamp; resolvers also wait. | fixed |
| 14 | Reverting recipient freezes a dispute | Pull payments (pendingWithdrawals + withdraw()). | fixed |
| 15 | Spoofed position ledger | Only the wired Market can record positions. | fixed |
| 16 | Sequencer / timestamp trust | Windows are minutes-to-hours; no sequencer-uptime feed on Robinhood Chain, so price-at relies on maxStaleness. | partial |
| 17 | Wrong or compromised source data | Dispute ladder is the backstop; raw evidence served at /evidence/:hash. | partial |
| 18 | LMSR market-maker loss | Bounded at b·ln N (the price of liquidity); reserve ≥ C(q) re-checked every trade; no trading on known outcomes. | by design |
| 19 | Ladder manipulation / inconsistency | Each boundary is disputable; an inconsistent ladder voids the market (1/N) instead of picking a range. | mitigated |
| 20 | Fee attribution games | depositFees only adds rewards; only correct resolvers / voters are paid; insurance top-up capped at one third. | mitigated |
Committee randomness: commit-reveal
prevrandao is the constant 1 and blockhash is weak, and Chainlink VRF isn't available — so committees aren't drawn from block data. When the pool is larger than the committee, resolvers commit to secret salts, reveal them, and the draw uses their XOR: unpredictable unless every revealer colludes. A committer who withholds is excluded, so the most anyone can do is remove themselves.Cycle-impossibility proof
A composite's ID is keccak256(op, canonicalOperands, window) — a hash of its own definition, including its operand list. For a composite to reference itself, its operands would have to contain its own ID before that ID exists, which requires inverting keccak256. And EventComposer requires every operand to already exist in storage, so every edge points to a node created strictly earlier. No cycle of any length can be built — not bounded, impossible.
Terminal-state propagation
A voided primitive used to leave every composite above it reporting “not yet resolved” forever. The rule now applied to every operator, in order:
Precedence
Rule
Precedence
Rule
Precedence
Rule
Precedence
Rule
| Precedence | Rule |
|---|---|
| 1 | A decisive short-circuit wins regardless of siblings (AND / BEFORE / WITHIN on any False; OR on any True). |
| 2 | Otherwise a genuinely pending sibling keeps the composite Unresolved — normal openness, not a stall. |
| 3 | Otherwise a Voided (or Expired) sibling makes the composite Voided. |
| 4 | Otherwise every operand is decided and the normal comparison applies. |
Out of scope for the MVP
Cross-chain events, ZK proofs, a permissionless resolver marketplace with staking tokens, MEV protection, and full stake-weighted / VRF-selected arbitration. The bonded committee ladder is real progress toward the last one — not the final design.