Skip to content

feat: implement v2 reveal phase and commitment verification - #84

Merged
collinsezedike merged 1 commit into
mainfrom
feat/v2-reveal-phase
Aug 12, 2026
Merged

feat: implement v2 reveal phase and commitment verification#84
collinsezedike merged 1 commit into
mainfrom
feat/v2-reveal-phase

Conversation

@collinsezedike

Copy link
Copy Markdown
Collaborator

Summary

Implements #67: the reveal phase, where third-party positions disclose the side they committed to during registration.

  • reveal(voter, id, choice, salt): looks up the voter's Position, verifies (choice, salt) against its stored commitment, and adds the position's weight to Resolution.agree_weight or disagree_weight.
  • Commitment verification uses a VoteCommitmentPreimage struct hashed via ToXdr, the same canonical-encoding approach policy_hash already uses (see [Feature] V2: implement PolicySnapshot and versioned assertion state #64), rather than a hand-rolled byte concatenation. Matches V2_RESOLUTION.md's H(canonical_encode("THOLOS_V2_VOTE", network_id, contract_address, policy_hash, assertion_id, round, voter, choice, salt)) spec exactly, with round fixed at 0 (this design has exactly one weighted round).
  • The Registration -> Reveal transition is lazy: the first reveal call after registration_deadline triggers it, matching V2_RESOLUTION.md's "a caller may advance an expired phase permissionlessly" principle rather than adding a separate entrypoint. That same transition auto-counts and auto-marks-revealed the asserter's and disputer's fixed positions, since their sides are already public and they never call reveal themselves; a Fixed voter calling reveal is naturally rejected with AlreadyRevealed since their position is already marked revealed at that point, no separate error variant needed.
  • register's existing phase check already refuses new deposits once phase leaves Registration, so W freezes as a side effect of the transition with no extra code.
  • New RevealOpened event for the phase transition itself, alongside Revealed per reveal.

One ordering fix worth calling out: reveal checks the assertion's phase before fetching its Resolution, not after. An uncontested assertion that reached Resolved via finalize never has a Resolution at all (only dispute creates one), so fetching it first would've surfaced a misleading AssertionNotFound instead of NotReveal for that case.

Closes #67

Test plan

  • cargo fmt --check, cargo clippy --workspace --all-targets --locked -- -D warnings, cargo test --workspace --locked all pass (67 v1 + 60 v2 + 2 integration-example tests)
  • cargo build -p tholos-v2 --target wasm32v1-none --release --locked succeeds
  • mdbook build succeeds
  • Every new Error variant has a dedicated test triggering it
  • Commitment verification tested both for correct reveals (weight counted on the right side) and tampered ones (wrong salt, wrong choice)
  • Fixed-position auto-reveal tested directly (weight counted, marked revealed, can't be revealed again)

@collinsezedike
collinsezedike merged commit e4ef4c6 into main Aug 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] V2: implement reveal phase and commitment verification

1 participant