Skip to content

A secure, scalable voting application built on Solana using the Anchor framework. Implements advanced D21 protocol with duplicate vote prevention, flexible poll management, and efficient bitmap-based vote tracking.

Notifications You must be signed in to change notification settings

mapfumo/d21voting-anchor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

1 Commit
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ—ณ๏ธ D21Voting - A Solana Voting Program

Live Demo Status Solana License Anchor Rust

A secure, scalable voting application built on Solana using the Anchor framework. Implements advanced D21 protocol with duplicate vote prevention, flexible poll management, and efficient bitmap-based vote tracking.

๐Ÿš€ Live Demo

Experience D21Voting in action: https://d21voting.vercel.app/


โœจ Features

  • ๐Ÿ”’ Secure Voting - Prevents duplicate votes using advanced bitmap tracking
  • ๐ŸŽฏ Flexible Polls - Configurable candidate limits and voting quotas
  • ๐Ÿ‘‘ Authority Control - Poll creators maintain full control over their polls
  • โšก Efficient Storage - Optimized account sizing and memory usage
  • ๐Ÿง  D21 Protocol - Advanced voting with multiple votes per voter
  • ๐Ÿš€ Production Ready - Fully tested and deployed on Solana

๐Ÿ—๏ธ Architecture

Program ID

2CNQMKvkPPfgiZFKJar6gyWc6bquTV2jW7NEHMfynLBs

Core Components

graph TD
    A[Poll Creator] --> B[Create Poll]
    B --> C[Poll Account]
    A --> D[Add Candidates]
    D --> E[Candidate Accounts]
    F[Voter] --> G[Init Vote Record]
    G --> H[Vote Record Account]
    F --> I[Cast Votes]
    I --> C
    I --> H
    A --> J[Close Poll]
    J --> C
Loading

๐Ÿ“ Project Structure

d21voting-backend/
โ”œโ”€โ”€ ๐Ÿ“‹ Anchor.toml              # Project configuration
โ”œโ”€โ”€ ๐Ÿ“ฆ Cargo.toml               # Rust dependencies
โ”œโ”€โ”€ ๐Ÿ“ฑ package.json             # Node.js dependencies
โ”œโ”€โ”€ โš™๏ธ tsconfig.json            # TypeScript configuration
โ”œโ”€โ”€ ๐Ÿ—๏ธ programs/
โ”‚   โ””โ”€โ”€ d21voting/
โ”‚       โ”œโ”€โ”€ ๐Ÿ“ฆ Cargo.toml      # Program-specific dependencies
โ”‚       โ””โ”€โ”€ ๐Ÿ“ src/
โ”‚           โ”œโ”€โ”€ ๐Ÿš€ lib.rs       # Main program entry point
โ”‚           โ”œโ”€โ”€ ๐Ÿ›๏ธ state.rs     # Account structures
โ”‚           โ”œโ”€โ”€ ๐Ÿ” contexts.rs  # Account validation
โ”‚           โ”œโ”€โ”€ โŒ errors.rs     # Custom error types
โ”‚           โ”œโ”€โ”€ ๐Ÿ› ๏ธ utils.rs     # Utility functions
โ”‚           โ””โ”€โ”€ ๐Ÿ“ instructions/ # Instruction handlers
โ”‚               โ”œโ”€โ”€ mod.rs
โ”‚               โ”œโ”€โ”€ create_poll.rs
โ”‚               โ”œโ”€โ”€ add_candidate.rs
โ”‚               โ”œโ”€โ”€ init_vote_record.rs
โ”‚               โ”œโ”€โ”€ cast_votes.rs
โ”‚               โ””โ”€โ”€ close_poll.rs
โ”œโ”€โ”€ ๐Ÿงช tests/                   # Integration tests
โ”œโ”€โ”€ ๐Ÿš€ migrations/              # Deployment scripts
โ””โ”€โ”€ ๐ŸŽฏ target/                  # Build artifacts

๐ŸŽฏ Core Instructions

1. Create Poll

Creates a new voting poll with specified parameters.

pub fn create_poll(
    ctx: Context<CreatePoll>,
    poll_id: u64,
    title: String,
    max_candidates: u16,
    max_votes_per_voter: u16,
    max_title_bytes: u32,
) -> Result<()>

Parameters:

  • poll_id - Unique identifier for the poll
  • title - Poll title (with size validation)
  • max_candidates - Maximum number of candidates allowed
  • max_votes_per_voter - Maximum votes each voter can cast
  • max_title_bytes - Reserved space for title

2. Add Candidate

Adds a candidate to an existing poll.

pub fn add_candidate(
    ctx: Context<AddCandidate>,
    index: u16,
    name: String,
    max_name_bytes: u32,
) -> Result<()>

Constraints:

  • Poll must be open
  • Index must equal current candidate count
  • Candidate count must be below maximum

3. Initialize Vote Record

Creates a vote record for a voter in a specific poll.

pub fn init_vote_record(ctx: Context<InitVoteRecord>) -> Result<()>

Features:

  • One-time initialization per voter per poll
  • Automatic bitmap sizing based on poll configuration
  • Voter pays for account creation

4. Cast Votes

Allows voters to cast votes for candidates.

pub fn cast_votes(ctx: Context<CastVotes>, indices: Vec<u16>) -> Result<()>

Advanced Features:

  • Duplicate Prevention - Uses bitmap to prevent voting for same candidate twice
  • Quota Management - Respects max_votes_per_voter limit
  • Batch Voting - Multiple candidates in single transaction
  • Smart Deduplication - Handles duplicate indices within same call

5. Close Poll

Closes a poll to prevent further voting.

pub fn close_poll(ctx: Context<ClosePoll>) -> Result<()>

Effect: Sets is_open = false, blocking all future votes


๐Ÿ›๏ธ Account Structures

Poll Account

pub struct Poll {
    pub authority: Pubkey,        // Poll creator (32 bytes)
    pub poll_id: u64,            // Unique identifier (8 bytes)
    pub is_open: bool,           // Poll status (1 byte)
    pub max_votes_per_voter: u16, // Voting limit (2 bytes)
    pub max_candidates: u16,     // Candidate limit (2 bytes)
    pub candidate_count: u16,    // Current count (2 bytes)
    pub title: String,           // Poll title (4 + length bytes)
    pub vote_counts: Vec<u64>,   // Vote counts per candidate
}

Candidate Account

pub struct Candidate {
    pub poll: Pubkey,    // Associated poll (32 bytes)
    pub index: u16,      // Candidate index (2 bytes)
    pub name: String,    // Candidate name (4 + length bytes)
}

Vote Record Account

pub struct VoteRecord {
    pub poll: Pubkey,        // Associated poll (32 bytes)
    pub voter: Pubkey,       // Voter's public key (32 bytes)
    pub used_votes: u16,     // Votes already cast (2 bytes)
    pub voted_bitmap: Vec<u8>, // Bitmap tracking voted candidates
}

๐Ÿ”’ Security Features

PDA (Program Derived Address) Security

  • Poll Seeds: [b"poll", authority, poll_id]
  • Candidate Seeds: [b"candidate", poll, index]
  • Vote Record Seeds: [b"vote", poll, voter]
  • Deterministic - Same inputs always produce same addresses
  • Collision Resistant - Unique combinations prevent conflicts

Access Control

  • Authority Validation - Only poll creators can modify their polls
  • Poll Status Checks - Closed polls reject all modifications
  • Voter Verification - Vote records are tied to specific voters
  • Sequential Constraints - Candidates must be added in order

Data Integrity

  • Input Validation - All parameters checked before processing
  • Overflow Protection - Uses checked arithmetic operations
  • Size Constraints - Pre-allocates vectors to prevent stack issues
  • Duplicate Prevention - Bitmap-based vote tracking

๐Ÿงฎ Advanced Algorithms

Bitmap Vote Tracking

// Calculate bitmap size in bytes
fn bitset_len_bytes(max_candidates: u16) -> usize {
    ((max_candidates as usize) + 7) / 8
}

// Check if candidate was voted for
fn bit_is_set(bitmap: &Vec<u8>, index: u16) -> bool {
    let byte = index as usize / 8;
    let bit = index as usize % 8;
    (bitmap[byte] & (1u8 << bit)) != 0
}

// Mark candidate as voted for
fn set_bit(bitmap: &mut Vec<u8>, index: u16) {
    let byte = index as usize / 8;
    let bit = index as usize % 8;
    bitmap[byte] |= 1u8 << bit;
}

Vote Deduplication

  • Intra-transaction - Removes duplicate indices within same call
  • Cross-transaction - Bitmap prevents voting for same candidate twice
  • Efficient - O(n) complexity with BTreeSet for deduplication

โŒ Error Handling

Custom Error Types

pub enum D21Error {
    ZeroCandidatesNotAllowed,    // max_candidates must be > 0
    ZeroVotesPerVoter,          // max_votes_per_voter must be > 0
    PollClosed,                 // Poll is not accepting votes
    Unauthorized,               // Insufficient permissions
    MaxCandidatesReached,       // Cannot add more candidates
    NameTooLongAtInit,          // Candidate name exceeds limit
    TitleTooLongAtInit,         // Poll title exceeds limit
    NoVotesSubmitted,           // Empty vote indices
    NoNewVotes,                 // All votes are duplicates/already cast
    VotesQuotaExceeded,         // Exceeds max_votes_per_voter
    InvalidCandidateIndex,      // Index out of bounds
    Overflow,                   // Arithmetic overflow
    MismatchedPollInRecord,     // Vote record links to wrong poll
}

๐Ÿš€ Quick Start

Prerequisites

Installation

# Clone the repository
git clone https://github.com/mapfumo/d21voting-anchor.git
cd d21voting-anchor

# Install dependencies
yarn install

# Build the program
anchor build

# Run tests
yarn test

Usage Examples

Creating a Poll

const pollId = new BN(Date.now());
const title = "Favorite Programming Language";
const maxCandidates = 5;
const maxVotesPerVoter = 3;
const maxTitleBytes = title.length + 10;

await program.methods
  .createPoll(pollId, title, maxCandidates, maxVotesPerVoter, maxTitleBytes)
  .accounts({
    authority: wallet.publicKey,
    poll: pollPda,
    systemProgram: SystemProgram.programId,
  })
  .rpc();

Adding Candidates

const candidateIndex = poll.account.candidateCount;
const candidateName = "Rust";
const maxNameBytes = candidateName.length + 5;

await program.methods
  .addCandidate(candidateIndex, candidateName, maxNameBytes)
  .accounts({
    poll: pollPda,
    candidate: candidatePda,
    authority: wallet.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .rpc();

Casting Votes

const voteIndices = [0, 2, 4]; // Vote for candidates 0, 2, and 4

await program.methods
  .castVotes(voteIndices)
  .accounts({
    voter: wallet.publicKey,
    poll: pollPda,
    voteRecord: voteRecordPda,
  })
  .rpc();

๐Ÿงช Testing

Run Tests

# Install dependencies
yarn install

# Run all tests
yarn test

# Run specific test file
yarn test tests/d21voting.ts

Test Coverage

  • Unit Tests - Individual instruction functionality
  • Integration Tests - End-to-end voting workflows
  • Error Cases - All error conditions and edge cases
  • Security Tests - Authority validation and access control

๐Ÿš€ Deployment

Build Program

anchor build

Deploy to Localnet

anchor deploy

Deploy to Devnet

# Update Anchor.toml cluster to "devnet"
anchor deploy --provider.cluster devnet

Verify Program

anchor verify <PROGRAM_ID>

๐Ÿ“Š Performance Characteristics

Gas Optimization

  • Efficient Storage - Pre-allocated vectors prevent reallocation
  • Bitmap Operations - O(1) vote checking and setting
  • Minimal Instructions - Optimized transaction structure

Scalability

  • Configurable Limits - Adjustable candidate and vote limits
  • Batch Operations - Multiple votes in single transaction
  • Memory Efficient - Dynamic sizing based on actual needs

๐Ÿ”ฎ Future Enhancements

Planned Features

  • Poll Templates - Reusable poll configurations
  • Advanced Voting - Weighted voting, ranked choice
  • Real-time Updates - WebSocket integration
  • Analytics - Voting patterns and statistics
  • Multi-signature - Collaborative poll management

Performance Improvements

  • Batch Operations - Multiple candidates in single transaction
  • Compression - Efficient data storage algorithms
  • Caching - Frequently accessed data optimization

๐Ÿค Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

Development Setup

  1. Fork the repository
  2. Clone your fork locally
  3. Install dependencies: yarn install
  4. Build the program: anchor build
  5. Run tests: yarn test
  6. Submit a pull request

Code Standards

  • Follow Rust formatting guidelines (cargo fmt)
  • Add comprehensive documentation
  • Include error handling
  • Write tests for new features
  • Update documentation as needed

๐Ÿ“š Resources

Documentation

Community


๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


๐Ÿ™ Acknowledgments

  • Anchor Team - For the excellent framework
  • Solana Team - For the blockchain platform
  • Community - For feedback and contributions

Program ID: 2CNQMKvkPPfgiZFKJar6gyWc6bquTV2jW7NEHMfynLBs
Version: 1.0.0
Status: โœ… Production Ready
Frontend: โœ… Live on Vercel
Last Updated: August 2025

Star on GitHub Fork on GitHub

About

A secure, scalable voting application built on Solana using the Anchor framework. Implements advanced D21 protocol with duplicate vote prevention, flexible poll management, and efficient bitmap-based vote tracking.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published