A Clarity smart contract implementing anonymous voting through a commitment-reveal scheme on the Stacks blockchain.
This contract enables privacy-preserving polls where voters commit to their choices cryptographically before revealing them, preventing vote manipulation and maintaining anonymity during the voting process.
- Commit-Reveal Scheme: Two-phase voting prevents vote observation during commit phase
- Cryptographic Commitments: SHA-256 hashing with salt prevents vote tampering
- Anonymous Voting: Vote choices hidden until reveal phase begins
- Registration Phase: Voters register for specific polls
- Commit Phase: Voters submit hashed votes (commitment)
- Reveal Phase: Voters prove their votes with salt verification
- Input validation for all user data
- Double-voting prevention
- Time-locked phases using block heights
- Registered voters only
- Emergency shutdown capability
Creates a new poll with multiple options.
(create-poll
(title (string-ascii 100))
(description (string-ascii 500))
(options (list 10 (string-ascii 200)))
(commit-duration uint)
(reveal-duration uint))Parameters:
title: Poll title (1-100 characters)description: Poll description (1-500 characters)options: List of 2-10 voting options (each 1-200 characters)commit-duration: Number of blocks for commit phasereveal-duration: Number of blocks for reveal phase
Returns: Poll ID
Register to participate in a poll.
(register-voter (poll-id uint))Requirements:
- Poll must be active
- Must register before commit phase ends
- Cannot register twice
Submit a cryptographic commitment of your vote.
(commit-vote (poll-id uint) (commitment-hash (buff 32)))Commitment Hash Calculation:
hash = SHA256(option-id + salt + voter-principal)
Requirements:
- Must be registered voter
- Only during commit phase
- Cannot commit twice
- Hash must be exactly 32 bytes
Reveal your vote with proof.
(reveal-vote
(poll-id uint)
(option-id uint)
(salt (buff 32)))Requirements:
- Must have committed in commit phase
- Only during reveal phase
- Hash must match commitment
- Cannot reveal twice
Close the poll after reveal phase ends (creator only).
(close-poll (poll-id uint))Emergency shutdown (contract owner only).
(emergency-close-poll (poll-id uint))get-poll (poll-id uint): Get poll detailsget-poll-option (poll-id uint) (option-id uint): Get option details and vote countget-commitment (poll-id uint) (voter principal): Get voter's commitmentis-registered (poll-id uint) (voter principal): Check registration statusget-vote-reveal (poll-id uint) (voter principal): Get revealed voteget-poll-counter: Get total number of pollsis-commit-phase (poll-id uint): Check if in commit phaseis-reveal-phase (poll-id uint): Check if in reveal phase
(contract-call? .voting-contract create-poll
"Best Programming Language"
"Vote for your favorite programming language"
(list "Clarity" "Solidity" "Rust" "JavaScript")
u144 ;; ~1 day commit phase
u144) ;; ~1 day reveal phase(contract-call? .voting-contract register-voter u1)Off-chain calculation:
const optionId = 2; // Voting for option 2
const salt = generateRandom32Bytes();
const voter = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
const commitment = sha256(optionId + salt + voter);(contract-call? .voting-contract commit-vote
u1
0x<commitment-hash>)(contract-call? .voting-contract reveal-vote
u1
u2
0x<original-salt>)(contract-call? .voting-contract get-poll-option u1 u2)
;; Returns: {description: "Solidity", vote-count: u5}| Code | Error | Description |
|---|---|---|
| u100 | err-owner-only | Only contract owner can perform action |
| u101 | err-not-authorized | Caller not authorized |
| u102 | err-poll-not-found | Poll does not exist |
| u103 | err-poll-closed | Poll is not active |
| u104 | err-already-voted | Already committed/revealed |
| u105 | err-invalid-option | Invalid option ID |
| u106 | err-reveal-phase-not-started | Still in commit phase |
| u107 | err-commitment-not-found | No commitment found |
| u108 | err-invalid-reveal | Hash verification failed |
| u109 | err-poll-active | Poll still active |
| u110 | err-already-registered | Already registered |
| u111 | err-invalid-options-count | Invalid options list |
| u112 | err-invalid-duration | Invalid duration value |
| u113 | err-invalid-input | Input validation failed |
- Input Validation: All user inputs validated before processing
- Cryptographic Verification: SHA-256 ensures vote integrity
- Time-Locking: Block-height-based phases prevent timing attacks
- Access Control: Registration and authorization checks
- State Management: Prevents double voting and unauthorized actions
- Salt Security: Keep salt secret until reveal phase
- Gas Costs: Revealing votes requires transaction fees
- Block Timing: Phase durations are in blocks (~10 min per block on Stacks)
- Commitment Storage: Store commitment details off-chain for reveal
- Deploy contract to Stacks blockchain
- Contract deployer becomes owner
- Create polls with appropriate durations
- Share poll ID with voters
- Test all three phases sequentially
- Verify commitment hash calculations
- Test error conditions (double voting, wrong phase, etc.)
- Validate time-lock enforcement
- Test emergency shutdown functionality