From da7812835e59a4be42641d05281212d97e303431 Mon Sep 17 00:00:00 2001 From: Alexis Bruneteau Date: Fri, 7 Nov 2025 02:50:49 +0100 Subject: [PATCH] docs: Add comprehensive blockchain voting flow documentation --- e-voting-system/BLOCKCHAIN_FLOW.md | 432 +++++++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 e-voting-system/BLOCKCHAIN_FLOW.md diff --git a/e-voting-system/BLOCKCHAIN_FLOW.md b/e-voting-system/BLOCKCHAIN_FLOW.md new file mode 100644 index 0000000..d67a571 --- /dev/null +++ b/e-voting-system/BLOCKCHAIN_FLOW.md @@ -0,0 +1,432 @@ +# 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