Threat Model
threat-model.md
Boundary
This route preserves legacy markdown access inside the Next.js surface. The raw repository file remains authoritative.
Open raw fileThreat Model
1. System Overview
PrivateDAO is a Solana governance protocol implemented as an Anchor program. It governs proposal creation, hidden voting, proposal finalization, and treasury execution.
The core lifecycle is:
- `create_proposal`
- `commit_vote` or `commit_delegated_vote`
- `reveal_vote`
- `finalize_proposal`
- `execute_proposal`
The commit-reveal flow is intentional:
- votes are committed as `sha256(vote_byte || salt_32 || proposal_pubkey_32 || voter_pubkey_32)`
- the proposal tally remains hidden during the commit phase
- tally updates only after valid reveal
- execution is separated from finalization by a timelock
Treasury execution supports:
- `SendSol`
- `SendToken`
- `CustomCPI` as a reserved action type that is rejected rather than exposed as arbitrary on-chain CPI execution
Delegation is proposal-scoped:
- a delegator can delegate voting weight to a delegatee for one proposal
- the delegatee commits and reveals using combined weight
- delegation is one-shot through `is_used`
On-chain boundaries:
- DAO, proposal, voter record, delegation, treasury, and voter-weight accounts
- instruction-phase enforcement
- signer enforcement
- PDA and account binding enforcement
- treasury movement
Off-chain boundaries:
- CLI scripts
- GitHub Pages frontend
- Android-native client
- RPC availability
- wallet signing
- zk witness generation
- zk proving
- zk verification artifacts
The protocol is on-chain for state transition and treasury safety. Operator surfaces are off-chain wrappers around that protocol.
The current zk layer is additive:
- it does not change the deployed on-chain lifecycle
- it adds a real off-chain proof path
- it introduces new proof-system assumptions that must be reviewed separately
2. Actor Model
Malicious voter
Capabilities:
- holds governance tokens
- can sign transactions
- can attempt invalid commit/reveal orderings
- can attempt replay, wrong salt, wrong vote payload, or late reveal
- can attempt voter-record substitution
Malicious delegate
Capabilities:
- acts as delegatee on a proposal
- can attempt to consume delegated rights outside intended binding
- can attempt stale or cross-proposal delegation use
- can attempt overlap with direct commit paths
Malicious treasury participant
Capabilities:
- attempts treasury account substitution
- attempts recipient substitution
- attempts wrong mint, wrong token source, wrong token destination, or duplicate token account wiring
- attempts to trigger execution with valid-looking but semantically wrong accounts
Compromised signer
Capabilities:
- controls a wallet with governance tokens or authority privileges
- can submit otherwise valid instructions
- can attempt to finalize or execute in unexpected order
- can exploit any missing signer/account binding checks
Account substitution attacker
Capabilities:
- supplies initialized accounts that are structurally valid
- swaps proposal, voter record, delegation, treasury, or token accounts
- attempts semantic confusion without breaking serialization
Replay attacker
Capabilities:
- resubmits prior instructions
- reuses signatures only at the transaction layer if possible
- attempts repeated commit, reveal, finalize, or execute
- attempts reordered or slightly altered account sets against old state
Confused-deputy attacker
Capabilities:
- uses a permissionless surface such as finalize or execute
- supplies valid signer but mismatched account context
- relies on protocol accepting approximate authority or account relations
External transaction manipulator
Capabilities:
- observes transaction timing
- front-runs or back-runs around timing windows
- cannot forge signatures, but can exploit timing or account assumptions if they exist
ZK artifact attacker
Capabilities:
- attempts to substitute public signals
- attempts to rely on reviewer confusion between current on-chain logic and future zk integration
- attempts to exploit incorrect setup, verification key, or proof-handling assumptions
3. Asset Model
Treasury balances
Treasury SOL and SPL token balances must not move except through correctly finalized and correctly wired execution paths.
Governance decision correctness
A proposal outcome must reflect valid committed and revealed votes, not invalid reveals, stale delegation, or state confusion.
Proposal lifecycle integrity
A proposal must move only through valid lifecycle states and must not skip, regress, or duplicate critical transitions.
Voter rights
Only valid governance token holders should be able to propose or vote with the weight they legitimately control.
Delegation rights
Delegation must remain proposal-scoped, delegatee-bound, one-shot, and non-replayable in practical operation.
Execution correctness
Execution must move the intended asset to the intended recipient only after the intended proposal has passed and unlocked.
Proposal status invariants
`Voting`, `Passed`, `Failed`, `Cancelled`, and `Vetoed` statuses must remain consistent with the lifecycle and must not be entered through partial or confused paths.
ZK proof integrity
Proof artifacts, verification keys, and public signals must correspond to the intended circuit semantics. Otherwise the zk layer would create false assurance rather than stronger privacy guarantees.
4. Attack Surfaces
Create proposal
Misuse risks:
- proposer without token ownership
- malformed treasury action
- wrong proposer token mint
Commit vote
Misuse risks:
- vote from zero-balance or insufficient-balance account
- double commit
- commit after voting close
- wrong voter record or wrong proposal binding
Commit delegated vote
Misuse risks:
- non-delegatee consuming delegated rights
- delegation from another proposal
- replaying a used delegation
- cross-proposal account confusion
Reveal vote
Misuse risks:
- reveal before commit
- reveal with wrong salt
- reveal with wrong vote payload
- reveal by wrong signer
- reveal after reveal window
- reveal against wrong proposal or wrong voter record
Finalize proposal
Misuse risks:
- finalize before reveal window ends
- finalize with wrong DAO / proposal pairing
- finalize after proposal is already finalized
- confused-deputy use of permissionless finalize surface
Execute proposal
Misuse risks:
- execute before finalization
- execute before timelock unlock
- execute twice
- execute with wrong treasury PDA
- execute with wrong recipient
- execute with wrong token mint or token ownership relations
- partial mutation before CPI failure
Treasury transfer logic
Misuse risks:
- SOL recipient substitution
- token mint mismatch
- source account not controlled by treasury PDA
- destination account not owned by intended recipient
- duplicate token source/destination
Delegation logic
Misuse risks:
- self-delegation
- stale delegation
- cross-proposal reuse
- overlap between delegated and direct voting paths
Timing window transitions
Misuse risks:
- off-by-one mistakes at commit end
- off-by-one mistakes at reveal start/end
- premature finalization
- premature execution
ZK companion-proof path
Misuse risks:
- treating zk as if it already changes the deployed protocol when it does not
- mismatching proof public signals with the intended DAO or proposal context
- stale proof artifact reuse in review or verification flows
- verification-key confusion
5. Threat Classes
Lifecycle bypass
Attempting to finalize or execute from the wrong phase or from a proposal state that is not eligible for that transition.
Replay attacks
Attempting to repeat a previously valid lifecycle step to duplicate effects or re-enter a phase.
Duplicate execution
Attempting to move treasury funds more than once from the same proposal.
Signer confusion
Using a signer that is valid for transaction submission but not valid for the protocol role intended by the account set.
Authority misuse
Attempting to use authority-only paths or authority-related account expectations with the wrong signer or wrong DAO context.
PDA substitution
Supplying a valid account that is not the PDA derived for the intended proposal, vote record, delegation, or treasury.
Account misbinding
Supplying accounts that are individually valid but belong to a different proposal, DAO, delegation, or recipient relationship.
Treasury miswiring
Supplying incorrect treasury, recipient, token source, or token destination accounts to extract funds or redirect them.
Commit-reveal mismatch
Attempting to reveal a vote that does not match the stored commitment or stored voter identity.
Invalid reveal injection
Attempting to reveal before commit, after the reveal window, or using a wrong signer or keeper.
Timing boundary attacks
Attempting to exploit off-by-one mistakes around `voting_end`, `reveal_end`, or `execution_unlocks_at`.
Delegation misuse
Attempting to reuse delegation state, consume it from the wrong signer, or apply it to the wrong proposal.
Partial state mutation
Attempting to trigger a failure after a lifecycle field or balance changes partially, leaving the protocol in an inconsistent state.
State regression
Attempting to push a proposal back into an earlier state or create impossible state combinations after failure paths.
Semantic account confusion
Supplying initialized, owner-correct, or funded accounts that still do not belong to the exact intended governance relationship.
ZK proof confusion
Treating a valid proof as if it were sufficient for a different DAO, proposal, or verifier context than the one encoded in its public signals.
6. Mitigations
Lifecycle bypass
Mitigation:
- `commit_vote` requires `status == Voting`
- `reveal_vote` requires `status == Voting` and timing checks
- `finalize_proposal` requires `status == Voting` and `now >= reveal_end`
- `execute_proposal` requires `status == Passed`, `!is_executed`, and `now >= execution_unlocks_at`
Protocol enforcement:
- `programs/private-dao/src/lib.rs`
Tests:
- `tests/full-flow-test.ts`
- `tests/private-dao.ts`
Replay and duplicate execution
Mitigation:
- voter records reject `AlreadyCommitted`
- reveals reject `AlreadyRevealed`
- finalize rejects `AlreadyFinalized`
- execute rejects `AlreadyExecuted`
Protocol enforcement:
- `programs/private-dao/src/lib.rs`
Tests:
- `tests/private-dao.ts`
- `tests/full-flow-test.ts`
Signer confusion and authority misuse
Mitigation:
- authority-only paths use `has_one = authority`
- reveal requires `revealer == voter || revealer == voter_reveal_authority`
- delegated commit requires `delegation.delegatee == delegatee.key()`
- finalize and execute remain permissionless, but account bindings remain strict
Protocol enforcement:
- `CancelProposal`
- `VetoProposal`
- `RevealVote`
- `CommitDelegatedVote`
- `FinalizeProposal`
- `ExecuteProposal`
Tests:
- `tests/private-dao.ts`
- `tests/full-flow-test.ts`
PDA substitution and account misbinding
Mitigation:
- proposal accounts use proposal PDA seeds
- voter records use vote PDA seeds bound to proposal + voter
- delegation records use delegation PDA seeds bound to proposal + delegator
- treasury PDA is derived from DAO
- `has_one = dao` and seed constraints enforce exact relations
Protocol enforcement:
- account validation in instruction contexts
Tests:
- `tests/private-dao.ts`
- `tests/full-flow-test.ts`
Treasury miswiring
Mitigation:
- `SendSol` checks `treasury_recipient == action.recipient`
- `SendToken` checks:
- token program ownership
- token account length sanity
- treasury token owner equals treasury PDA
- source mint equals action mint
- destination mint equals action mint
- destination owner equals configured recipient
- source and destination are not the same account
Protocol enforcement:
- `execute_proposal`
Tests:
- `tests/full-flow-test.ts`
Commit-reveal mismatch and invalid reveal injection
Mitigation:
- reveal recomputes `sha256(vote || salt || proposal_pubkey || voter_pubkey)`
- reveal requires committed state
- reveal rejects wrong signer and wrong timing
Protocol enforcement:
- `reveal_vote`
Tests:
- `tests/private-dao.ts`
- `tests/full-flow-test.ts`
Timing boundary attacks
Mitigation:
- explicit `< voting_end`, `>= voting_end`, `< reveal_end`, `>= execution_unlocks_at` checks
- tests assert before/at/after transition behavior
Protocol enforcement:
- `commit_vote`
- `commit_delegated_vote`
- `reveal_vote`
- `finalize_proposal`
- `execute_proposal`
Tests:
- `tests/full-flow-test.ts`
Delegation misuse
Mitigation:
- self-delegation rejected
- delegation tied to one proposal
- delegated commit checks delegatee equality
- `is_used` blocks reuse after successful delegated commit
- direct and delegated paths reject overlap on-chain through proposal-bound marker accounts
Protocol enforcement:
- `delegate_vote`
- `commit_delegated_vote`
Tests:
- `tests/private-dao.ts`
Partial state mutation and state regression
Mitigation:
- tests verify failed finalize and execute leave critical fields unchanged
- failed treasury execution paths are asserted not to set `is_executed`
- proposal status remains stable after failed execute attempts
Protocol enforcement:
- Anchor transaction atomicity
- explicit state checks around lifecycle fields
Tests:
- `tests/full-flow-test.ts`
ZK companion-proof integrity
Mitigation:
- public signals bind proof semantics to DAO and proposal context
- proof verification is exposed through repository commands rather than vague claims
- reviewer-facing docs explain the difference between current commit-reveal and zk-augmented governance
- zk artifacts are gated through `verify-zk-surface.sh`
Protocol and repository enforcement:
- `zk/circuits/private_dao_vote_overlay.circom`
- `scripts/zk/build-zk.sh`
- `scripts/zk/prove-sample.sh`
- `scripts/zk/verify-sample.sh`
- `scripts/verify-zk-surface.sh`
Tests / verification references:
- `docs/zk-layer.md`
- `docs/zk-upgrade.md`
- `docs/zk-evidence.md`
7. Residual Risks
- commit-reveal hides vote content, not transaction timing or participation timing
- the zk layer is off-chain today and is not yet an on-chain verifier integration
- `CustomCPI` is intentionally unsupported as a live execution path
- external RPC availability and local validator behavior affect verification environments
- no external audit is claimed
- mainnet release operations still require release discipline, monitoring, and wallet hygiene outside the program itself
8. Trust Assumptions
Trusted or assumed components:
- Solana runtime transaction atomicity
- Anchor account validation and signer enforcement
- SPL Token program correctness
- honest wallet signature semantics
- RPC responses are sufficiently correct for off-chain observation
- Circom circuit correctness for the current companion stack
- Groth16 setup artifact integrity
- verification key integrity for the committed zk artifacts
Timing assumptions:
- cluster time and block time progress enough for lifecycle windows to become reachable
- users and operators do not assume sub-second exactness beyond the enforced timestamp boundaries
Operational assumptions:
- wallets sign only intended transactions
- off-chain surfaces preserve account sets and user intent
- deployment and governance operations use the intended program ID and cluster