Skip to content

Threat Model

What Accord defends against, where each defense lives, what proves it, and — explicitly — what residual risk remains. Companion to the Trust Profile: the trust profile names who is trusted; this page enumerates which attacks were considered and how each is answered. If an attack is not in this table, we have not considered it — say so and it gets a row.

The discipline this table follows (learned from the most honest design docs in the space, e.g. Kourt's GAMETHEORY.md): every entry is either defended (with code + test pointers) or accepted (with the price named). Nothing is waved away.

Attack ledger

# Attack Vector Defense Where Proven by Residual
1 Bribery of drawn jurors Pay jurors off-chain to vote a chosen option Vote secrecy until reveal (commit-reveal, per-juror salt) makes vote-buying unverifiable at commit time; appeals multiply the panel (3→7→15→31) and the bribe bill; incoherent jurors are slashed commit/reveal, appeal (ADR-0004) voting.spec.ts, appeal.spec.ts Off-chain bribery is undetectable on-chain. Priced by the security-value ceiling — do not file disputes worth more than it
2 Stake-majority capture One coalition holds >50% of pool stake ⇒ majority draw probability per seat Honest-majority-stake assumption; appeals raise cost but not the majority requirement — this is priced, not prevented whole design (ADR-0001) — Accepted: majority capture is possible; cost ≈ majority stake acquisition. Ceiling formula in the trust profile
3 Draw manipulation / fabricated juror set Post a fake root; inflate a colluding juror's selection range No posted root exists: the accumulator is maintained live by the protocol on every stake/unstake; sums are hash-bound; the root is frozen atomically with the committed VRF ADR-0012 (supersedes 0003/0008/0009 snapshot layer) accumulator.spec.ts, accumulator_litesvm.rs, host MST tests (src/tests.rs) None on correctness; see #8 for liveness
4 Freeze-window stake top-up ("rented draw weight") Watch for a filed dispute, top up stake before request_vrf lands — the root freezes at the VRF callback, not at filing — buying draw probability inside the window (a) The filer can close the window itself by bundling create_dispute + request_vrf in one transaction; (b) stake buys draw probability, not seats; (c) drawn seats are locked (active_draws) through settlement and must vote coherently or be slashed; (d) post-freeze stake/unstake cannot alter frozen_root, and the draw-time inflation guard rejects a leaf heavier than live stake commit_vrf_callback freeze, draw_seat inflation guard, active_draws gate draw.spec.ts, accumulator_litesvm.rs A filer that leaves a gap exposes its own dispute to a better-funded watcher. Priced (capital + coherence risk), not excluded. Compare Kourt's rented-weight treatment
5 VRF provider withholds or biases randomness Oracle refuses the callback, or grinds values Randomness is committed once and the callback is identity-constrained and Created-state-gated; the result is on-chain and deterministic given the seed — the provider cannot bias which juror a given seed selects ADR-0013, SR3-L-3 draw.spec.ts Availability is provider-dependent: a stalling oracle blocks new draws, never in-flight ones (trust profile #4)
6 Apathy / low turnout forces a wrong or arbitrary ruling Jurors no-show; a tie exists; nobody cranks Reveal quorum (reveal_threshold_bps, default 2/3) and a decisive tally are required; a shortfall or a Plurality top-count tie ⇒ same-size redraw ladder (draw_attempt, cap max_draw_attempts); no-shows are slashed into stake_delta; exhaustion ⇒ Failed with the filer refund exactly the booked filing fee. Who wins when nothing happens: nobody — funds return. ADR-0021, 0026, 0014, 0033 quorum-redraw.spec.ts, dispute.spec.ts A coalition holding > (1 − threshold) can force Failed (veto-by-abstention) — priced by no-show slashing, bounded to refunds, never a wrong ruling
7 Same-mint dominance (fee_token == staking_token) Jurors self-deal fees from a mint they also stake, breaking coherence incentives Dominance gate at pool creation and on every param update: α·min_stake/10_000 ≥ 2·fee_per_juror; split-mint pools are deliberately un-gated ADR-0029 lifecycle.subaccord.spec.ts, update_litesvm.rs None — the gate is structural for same-mint pools
8 Indexer withholds Merkle paths Staking/drawing stalls because no one serves paths Root is canonical on-chain; any auditor can rebuild the tree from JurorStake via getProgramAccounts and serve paths; competing indexers are stateless mirrors ADR-0012 root-rebuild path exercised in accumulator.spec.ts Liveness dependency only: at least one honest indexer must serve each pool (trust profile #3). SNARK-proven root is the v2 destination
9 Custody drain / fee theft An instruction moves vault tokens without exact accounting Fail-closed exact custody: every custody point measures the vault delta and requires equality with the nominal terms (SR3-M-2); stake and fee economics live in separate vaults; no fee moves before finality ADR-0020, 0029 appeal.spec.ts, staking.spec.ts, full-lifecycle.spec.ts None known; the fail-closed check is the invariant (see I2)
10 Governance abuse (hostile pool params, panic freeze) Subaccord authority retunes a pool mid-dispute; multisig pauses to strand funds Param updates on a 48h on-chain timelock; CaseTerms are frozen per dispute at filing so governance cannot retune a live dispute; pause gates new exposure (create_dispute/stake/appeal) and never adjudication; unpause is itself timelocked so a freeze is recoverable ADR-0005, 0016, 0007 lifecycle.update.spec.ts, lifecycle.pause.timelock.spec.ts, pause_litesvm.rs A watcher must observe the 48h window. Until the post-audit freeze, upgrade-multisig members are trusted with capital-bearing code
11 Re-initialization / account confusion Re-run an initializer to wipe state; pass a look-alike account Anchor discriminator + init/init_if_needed constraints; every instruction's LiteSVM contract includes the reinit and wrong-authority cases (the safe-solana-builder checklist pattern) all instructions/*.rs *_litesvm.rs auth/reinit cases per instruction None known
12 Party-juror conflict A party to the dispute is staked in the pool and drawn onto its own panel Priced, not excluded: a drawn party votes its side and is slashed if incoherent; on minimum panels one seat is cheap to outvote; appeals are open to anyone design decision (ADR-0004) — Accepted (trust profile, Synod note). Compare Kourt's hard-exclusion alternative on the prior-art page
13 Evidence operator leaks or selectively serves Operator sees plaintext evidence; withholds from honest jurors On-chain evidence hash commits the bundle; delivery crypto (ECIES/AES-GCM, registered delivery keys) is protocol-fixed in the SDK, not operator-discretionary; operator identity is per-Subaccord and public ADR-0006, 0011, 0015, 0034 evidence.spec.ts, SDK evidence/*.test.ts Accepted: the operator sees plaintext and is trusted not to leak (trust profile #7). Threshold-PRE is the v2+ path
14 Expired-credential juror stalls a gated pool An attestation-gated pool keeps a dead leaf that blocks draws prune_juror: a permissionless crank evicts expired-credential jurors (full amount into pending_withdrawal) so the tree stays drawable ADR-0024 attestation.spec.ts, attestation_litesvm.rs None — eviction is permissionless
15 Sybil admission One human, many keys, each at min_stake Stake is the anti-sybil unit and selection weight; optional per-Subaccord SAS credential gate (juror_credential/juror_schema) raises admission to a verifiable credential ADR-0001, 0024 staking.spec.ts, attestation.spec.ts Accepted for ungated pools: admission is key-level pseudonymous, not identity-verified humans (trust profile #9)

Invariants

The properties the protocol promises to hold always, with the test that would fail if one broke. These are the rows a reviewer should try to falsify.

# Invariant Statement Enforced in Proven by
I1 Stake vault invariant Slashing and redistribution are ledger-only (stake_delta); the stake_vault SPL balance is unchanged by slash + redistribute settle_round (ADR-0020) staking.spec.ts, full-lifecycle.spec.ts
I2 Fail-closed exact custody At every custody point the measured vault delta must equal the full nominal terms — unequal ⇒ revert, funds never stuck half-moved appeal, settlement paths (SR3-M-2, review 2026-09-23) appeal.spec.ts, dispute.spec.ts
I3 No arbitrary tie winner A Plurality top-count tie is a non-decisive round → redraw; a tie never crowns an option finalize_round (ADR-0026) quorum-redraw.spec.ts
I4 No ruling, no pay Failed disputes pay zero participation; the filer refund is exactly the booked filing fee, independent of reveal counts cancel_dispute, redraw, claim_appeal_refund (ADR-0033) dispute.spec.ts, synod.full-lifecycle.spec.ts
I5 Fees settle against the final ruling only No juror fee is earned on a vote that ends up incoherent; every round settles against final_ruling settle_round/finalize_dispute (ADR-0029, 0018) appeal.spec.ts, scalar.spec.ts
I6 Governance never moves vaults No pause or governance instruction transfers vault balances; pause gates new exposure only; in-flight disputes always resolve or refund pause/unpause/update paths (ADR-0016) lifecycle.pause.timelock.spec.ts, pause_litesvm.rs
I7 Root canonicality The juror-set root is protocol-maintained, not posted; frozen atomically with the committed VRF; post-freeze stake changes cannot alter a draw stake/unstake/commit_vrf_callback (ADR-0012) accumulator.spec.ts, draw.spec.ts, host MST tests
I8 Appeal fee is never the appellant's back claim_appeal_refund returns the bond portion (+ bounty reward); the consumed per-juror fee never refunds on the Final path; on Failed the whole deposit returns because its only destination earned nothing claim_appeal_refund (ADR-0033, bean accord-xftx) appeal.spec.ts

Standing review discipline

  • Every entry above names its evidence. An ADR without a test pointer, or a test without a row here, is a doc bug.
  • The "who wins when nothing happens" audit (attack #6's discipline): every gate — window expiry, crank lapse, pause, Failed exhaustion — must answer "which party benefits if this simply never fires?" If the answer is anyone other than "nobody / refunds," it is a finding.
  • New attack surfaces (new instructions, new roles) get a row here in the same change — this page is part of the definition of done, in the same sense as the SPEC.

See also: Trust Profile (who is trusted, the security-value ceiling) · Stake Accumulator (why no fraud proofs) · Sortition & VRF (the draw trust chain) · Prior Art (how other systems answer the same attacks).