# Blockchain Voting Flow - Complete Documentation ## Overview When a user votes through the web interface, the entire flow is: ``` User selects candidate ↓ Vote encrypted (ElGamal) on client ↓ Vote submitted to API (/api/votes/submit) ↓ Backend validates voter & election ↓ Vote recorded in database ↓ Vote added to blockchain (immutable) ↓ User sees success with transaction ID ↓ Vote visible in blockchain viewer ``` ## Detailed Flow ### 1. Frontend: Vote Selection & Encryption **File**: `frontend/components/voting-interface.tsx` ```typescript // Step 1: Get voter profile and public keys const voterResponse = await fetch("/api/auth/profile") const voterId = voterData.id // Step 2: Get election's public keys for encryption const keysResponse = await fetch(`/api/votes/public-keys?election_id=${electionId}`) const publicKeys = keysResponse.data // Step 3: Create signed ballot with client-side encryption const ballot = createSignedBallot( voteValue, // 1 for selected candidate voterId, // Voter ID publicKeys.elgamal_pubkey, // ElGamal public key "" // Private key for signing ) // Result includes: // - encrypted_vote: Base64 encoded ElGamal ciphertext // - zkp_proof: Zero-knowledge proof of validity // - signature: RSA-PSS signature ``` ### 2. Frontend: Vote Submission **File**: `frontend/components/voting-interface.tsx` (lines 116-130) ```typescript const submitResponse = await fetch("/api/votes/submit", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, body: JSON.stringify({ election_id: electionId, candidate_id: selectedCandidate, encrypted_vote: ballot.encrypted_vote, zkp_proof: ballot.zkp_proof, signature: ballot.signature, timestamp: ballot.timestamp }) }) ``` ### 3. Backend: Vote Validation & Recording **File**: `backend/routes/votes.py` (lines 99-210) ```python @router.post("/submit") async def submit_vote( vote_bulletin: schemas.VoteBulletin, current_voter: Voter = Depends(get_current_voter), db: Session = Depends(get_db), request: Request = None ): # 1. Verify voter hasn't already voted if services.VoteService.has_voter_voted(db, current_voter.id, election_id): raise HTTPException(detail="Already voted") # 2. Verify election exists election = services.ElectionService.get_election(db, election_id) # 3. Verify candidate exists candidate = db.query(Candidate).filter(...).first() # 4. Decode encrypted vote (from base64) encrypted_vote_bytes = base64.b64decode(vote_bulletin.encrypted_vote) # 5. Generate ballot hash (immutable record) ballot_hash = SecureHash.hash_bulletin( vote_id=current_voter.id, candidate_id=candidate_id, timestamp=int(time.time()) ) # 6. Record vote in database vote = services.VoteService.record_vote( db=db, voter_id=current_voter.id, election_id=election_id, candidate_id=candidate_id, encrypted_vote=encrypted_vote_bytes, ballot_hash=ballot_hash, ip_address=request.client.host ) ``` ### 4. Backend: Blockchain Recording **File**: `backend/routes/votes.py` (lines 181-210) ```python # Generate unique transaction ID (anonymized) transaction_id = f"tx-{uuid.uuid4().hex[:12]}" # Add vote to blockchain blockchain = blockchain_manager.get_or_create_blockchain(election_id) block = blockchain.add_block( encrypted_vote=vote_bulletin.encrypted_vote, transaction_id=transaction_id ) # Mark voter as voted services.VoterService.mark_as_voted(db, current_voter.id) # Return success with blockchain info return { "id": vote.id, "transaction_id": transaction_id, "block_index": block.index, "ballot_hash": ballot_hash, "timestamp": vote.timestamp } ``` ### 5. Backend: Blockchain Structure **File**: `backend/blockchain.py` ```python class Block: """Immutable voting block""" index: int prev_hash: str # Hash of previous block (chain integrity) timestamp: int encrypted_vote: str # Base64 encrypted vote transaction_id: str # Unique identifier for this vote block_hash: str # SHA-256 hash of this block signature: str # Digital signature class Blockchain: """Election blockchain""" blocks: List[Block] # All blocks for election def add_block(self, encrypted_vote: str, transaction_id: str) -> Block: """Add new vote block and compute hash chain""" new_block = Block( index=len(self.blocks), prev_hash=self.blocks[-1].block_hash if self.blocks else "0"*64, timestamp=int(time.time()), encrypted_vote=encrypted_vote, transaction_id=transaction_id ) # Compute SHA-256 hash of block new_block.block_hash = sha256(json.dumps({...}).encode()).hexdigest() self.blocks.append(new_block) return new_block def verify_chain_integrity(self) -> bool: """Verify no blocks have been tampered with""" for i, block in enumerate(self.blocks): # Check hash chain continuity if i > 0 and block.prev_hash != self.blocks[i-1].block_hash: return False # Recompute hash and verify if block.block_hash != compute_block_hash(block): return False return True ``` ### 6. Frontend: Blockchain Viewer **File**: `frontend/app/dashboard/blockchain/page.tsx` The blockchain page fetches and displays the blockchain: ```typescript // Fetch blockchain for selected election const response = await fetch( `/api/votes/blockchain?election_id=${selectedElection}`, { headers: { Authorization: `Bearer ${token}` } } ) const data = await response.json() // Returns: // { // blocks: [ // { // index: 0, // prev_hash: "0000...", // timestamp: 1699328400, // encrypted_vote: "aGVsbG8...", // Base64 // transaction_id: "tx-abc123...", // block_hash: "e3b0c4...", // signature: "d2d2d2..." // }, // ... // ], // verification: { // chain_valid: true, // total_blocks: 3, // total_votes: 2 // } // } ``` **File**: `frontend/components/blockchain-visualizer.tsx` Displays blockchain with: - Statistics dashboard (blocks, votes, status, security) - Expandable block cards showing all fields - Copy-to-clipboard for hashes - Visual integrity check - Staggered animations ### 7. Backend: Blockchain Retrieval **File**: `backend/routes/votes.py` (lines 351-370) ```python @router.get("/blockchain") async def get_blockchain( election_id: int = Query(...), db: Session = Depends(get_db) ): """Retrieve complete blockchain for election""" blockchain = blockchain_manager.get_or_create_blockchain(election_id) return blockchain.get_blockchain_data() # Returns: {blocks: [...], verification: {...}} ``` ## Data Flow Diagram ``` ┌─────────────────────────────────────────────────────────────┐ │ Frontend (Next.js) │ │ │ │ 1. User votes on /dashboard/votes/active/1 │ │ 2. VotingInterface encrypts vote (ElGamal) │ │ 3. Submits to POST /api/votes/submit │ │ 4. Shows success with transaction_id │ │ 5. User views blockchain on /dashboard/blockchain │ │ 6. BlockchainVisualizer displays all blocks │ └─────────────────────────────────────────────────────────────┘ ↑↓ HTTP API ┌─────────────────────────────────────────────────────────────┐ │ Backend (FastAPI) │ │ │ │ POST /api/votes/submit: │ │ - Validate voter & election │ │ - Verify candidate exists │ │ - Record vote in database │ │ - ADD TO BLOCKCHAIN ← New block created │ │ - Return transaction_id & block_index │ │ │ │ GET /api/votes/blockchain: │ │ - Retrieve all blocks for election │ │ - Calculate chain verification status │ │ - Return blocks + verification data │ │ │ │ POST /api/votes/verify-blockchain: │ │ - Verify chain integrity (no tampering) │ │ - Check all hashes are correct │ │ - Return verification result │ └─────────────────────────────────────────────────────────────┘ ↑↓ Database ┌─────────────────────────────────────────────────────────────┐ │ MariaDB Database │ │ │ │ votes table: │ │ - voter_id, election_id, candidate_id │ │ - encrypted_vote (stored encrypted) │ │ - ballot_hash, timestamp │ │ - ip_address │ │ │ │ blockchain (in-memory BlockchainManager): │ │ - All blocks for each election │ │ - Chain integrity verified on each access │ └─────────────────────────────────────────────────────────────┘ ``` ## Security Features ### 1. Encryption - **ElGamal Homomorphic**: Vote encrypted client-side - **Cannot decrypt individual votes** (only aggregate counts) - **Public key available** at `/api/votes/public-keys` ### 2. Signatures - **RSA-PSS**: Each ballot digitally signed - **Ballot Hash**: SHA-256 hash of vote metadata - **Transaction ID**: Anonymized identifier (not tied to voter) ### 3. Blockchain Integrity - **Hash Chain**: Each block references previous hash - **Tamper Detection**: Any change breaks chain verification - **Immutable**: Blocks cannot be modified (verified on GET) - **Voter Anonymity**: Transaction ID is anonymous UUID ### 4. Vote Validation - **One vote per person**: `has_voter_voted()` check - **Valid election**: Election must exist and be active - **Valid candidate**: Candidate must be in election - **Authentication**: Vote requires valid JWT token ## Testing the Flow ### 1. Via Web Interface ``` 1. Go to http://localhost:3000/dashboard/votes/active 2. Click "Participer" on election 3. Select candidate and confirm 4. See "Vote enregistré avec succès" 5. Click "Voir la blockchain" 6. See your vote block added to chain ``` ### 2. Via API (cURL) ```bash # 1. Register user curl -X POST http://localhost:8000/api/auth/register \ -H "Content-Type: application/json" \ -d '{ "email": "voter@test.fr", "password": "Test@12345", "first_name": "Jean", "last_name": "Dupont", "citizen_id": "12345ABC" }' # Response: {"access_token": "eyJ0eXA..."} # 2. Get public keys curl http://localhost:8000/api/votes/public-keys?election_id=1 # 3. Submit vote (encrypted client-side in real scenario) curl -X POST http://localhost:8000/api/votes/submit \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJ0eXA..." \ -d '{ "election_id": 1, "candidate_id": 2, "encrypted_vote": "aGVsbG8gd29ybGQ=", "zero_knowledge_proof": "...", "signature": "..." }' # Response: # { # "id": 1, # "transaction_id": "tx-abc123def456", # "block_index": 1, # "ballot_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" # } # 4. Fetch blockchain curl http://localhost:8000/api/votes/blockchain?election_id=1 # 5. Verify blockchain integrity curl -X POST http://localhost:8000/api/votes/verify-blockchain \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJ0eXA..." \ -d '{"election_id": 1}' # Response: {"chain_valid": true} ``` ## Key Endpoints | Endpoint | Method | Purpose | |----------|--------|---------| | `/api/votes/submit` | POST | Submit encrypted vote (adds to blockchain) | | `/api/votes/blockchain` | GET | Retrieve all blocks for election | | `/api/votes/verify-blockchain` | POST | Verify chain integrity | | `/api/votes/results` | GET | Get election results with verification | | `/api/votes/public-keys` | GET | Get public keys for encryption | | `/api/elections/active` | GET | List active elections | | `/api/elections/{id}` | GET | Get election details with candidates | ## Files Involved ### Frontend - `frontend/components/voting-interface.tsx` - Vote submission form - `frontend/app/dashboard/votes/active/page.tsx` - Elections list - `frontend/app/dashboard/votes/active/[id]/page.tsx` - Vote detail page - `frontend/app/dashboard/blockchain/page.tsx` - Blockchain viewer - `frontend/components/blockchain-visualizer.tsx` - Blockchain visualization - `frontend/lib/crypto-client.ts` - Encryption & signing ### Backend - `backend/routes/votes.py` - Vote endpoints - `backend/blockchain.py` - Blockchain implementation - `backend/crypto/elgamal.py` - ElGamal encryption - `backend/crypto/signatures.py` - Digital signatures - `backend/crypto/hashing.py` - SHA-256 hashing - `backend/services/vote_service.py` - Vote business logic ## Success Indicators ✓ User can select candidate and vote ✓ Vote encrypted before transmission ✓ Vote recorded in database ✓ Vote added to blockchain ✓ Transaction ID returned to user ✓ Blockchain viewer shows new block ✓ All hashes verify correctly ✓ Chain integrity: valid ✓ ✓ One vote per person enforced ✓ Only active elections can be voted on