SC-26 : Projet Final - DApp Complete

Navigation : Sommaire | << Précédent


Objectifs d’apprentissage

Ce projet capstone integre toutes les competences acquises dans la serie : 1. Ecrire un smart contract de vote en Solidity 2. Chiffrer les votes avec Paillier (chiffrement homomorphique) 3. Construire une preuve ZKP de validite du bulletin 4. Deployer et tester le système sur anvil puis testnet 5. Ecrire des tests Foundry pour le contrat

Prerequis

  • SC-0 a SC-25 (toute la serie)
  • En particulier : SC-9 (Governance), SC-15 (ZKP), SC-16-17 (HE/Voting), SC-24 (Testnet)

Duree estimee : 90 minutes


Sujet : Système de Vote Decentralise

Construire un système de vote complet avec : 1. Smart contract Solidity pour la gouvernance (SC-9) 2. Chiffrement des votes avec Paillier (SC-16, SC-17) 3. Preuve ZKP de validite du bulletin (SC-15) 4. Deploiement sur testnet (SC-24) 5. Tests avec Foundry (SC-12)

Architecture

[Electeur] --> [Chiffre vote (Paillier)] --> [Prouve validite (ZKP)]
     |                                            |
     v                                            v
[Smart Contract: enregistre bulletin chiffre + preuve]
     |                                            |
     v                                            v
[Decompte homomorphique] --> [Dechiffrement résultat]
     |                                            |
     v                                            v
[Verification publique: tout le monde peut verifier]

Partie 1 : Smart Contract de Vote (Solidity)

Ecrivez un contrat VotingSystem qui : - Enregistre les electeurs autorises - Accepte des bulletins chiffres - Stocke les preuves de validite - Permet le decompte final

# Partie 1: Contrat Solidity
# Utilisez compile_and_deploy() de SC-2

try:
    from web3 import Web3
    import solcx
    WEB3_AVAILABLE = True
except ImportError:
    WEB3_AVAILABLE = False
    print("web3 ou py-solc-x non installe. Installez avec: pip install web3 py-solc-x")

VOTING_CONTRACT = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract VotingSystem {
    // TODO: Definir la structure du systeme de vote
    // - Liste des electeurs autorises
    // - Mapping des bulletins chiffres (bytes)
    // - Phase de vote (enum: Registration, Voting, Tallying, Ended)
    // - Fonction registerVoter(address)
    // - Fonction submitBallot(bytes calldata encryptedBallot, bytes calldata proof)
    // - Fonction tally() qui declenche le decompte
    // - Events pour chaque action
    
    // Indices:
    // - enum Phase { Registration, Voting, Tallying, Ended }
    // - mapping(address => bool) public registeredVoters;
    // - mapping(address => bytes) public encryptedBallots;
    // - modifier onlyPhase(Phase _phase)
}
"""

print("Completez le contrat VotingSystem ci-dessus")
print("Inspirez-vous de SC-9 (DAO Governance) et SC-17 (E2E Voting)")
web3 ou py-solc-x non installe. Installez avec: pip install web3 py-solc-x
Completez le contrat VotingSystem ci-dessus
Inspirez-vous de SC-9 (DAO Governance) et SC-17 (E2E Voting)

Transition : Du smart contract au chiffrement client

La partie 1 a défini la structure on-chain du système de vote. Maintenant, nous devons chiffrer les votes cote client avant de les soumettre.

Pourquoi le chiffrement homomorphique ? - Le smart contract stocke les bulletins sans connaitre le contenu (privacy) - Le decompte peut se faire sur les données chiffrees (verifiability) - Seule l’autorite de decompte possede la cle privee pour dechiffrer le résultat

Prochaine étape : Implementer le chiffrement Paillier en Python (Partie 2).

Interpretation : Structure du contrat VotingSystem

Sortie obtenue : Squelette Solidity pour le contrat de vote decentralise.

Élément Objectif
enum Phase Machine a etats pour gerer le cycle de vie du vote
mapping(address => bool) Suivi des electeurs autorises
mapping(address => bytes) Stockage des bulletins chiffres et preuves
modifier onlyPhase Contrôle d’acces selon la phase courante

Points cles : 1. Le contrat suit un pattern de state machine (4 phases distinctes) 2. Les bulletins sont stockes sous forme de bytes (données chiffrees opaques) 3. Les events permettent de tracer toutes les actions on-chain

Note technique : Le chiffrement Paillier et les ZKP se font cote client (Python). Le contrat stocke et verifie uniquement les résultats de ces opérations. Voir SC-17 pour un exemple complet d’implementation.

Indice : Structure du contrat VotingSystem

Rappel de SC-9 (DAO Governance) et SC-17 (E2E Voting) :

Éléments essentiels du contrat : 1. Enum Phase : Registration, Voting, Tallying, Ended pour gerer le cycle de vie 2. Modifiers : onlyPhase(Phase _phase) pour restreindre l’acces selon la phase 3. Events : VoterRegistered, BallotSubmitted, PhaseChanged pour la traçabilite 4. Mappings : - mapping(address => bool) public registeredVoters : Liste des electeurs autorises - mapping(address => bytes) public encryptedBallots : Bulletins chiffres par electeur - mapping(address => bytes) public proofs : Preuves ZKP par electeur

Fonctions a implementer : - registerVoter(address voter) : Admin uniquement, phase Registration - submitBallot(bytes calldata encryptedBallot, bytes calldata proof) : Phase Voting - tally() : n’importe qui peut declencher, phase Tallying - verifyProof(bytes calldata proof) : Verifie la ZKP avant d’accepter le bulletin

Propriete de securite : Un electeur ne peut voter qu’une seule fois (verifier encryptedBallots[voter] == bytes(0)).

Vocabulaire technique : - State machine : Le contrat est une machine a etats (enum Phase) - Access control : Restreindre qui peut appeler quelle fonction - Events : Logs EVM pour la traçabilite on-chain

Schema : la machine a etats du contrat VotingSystem

Le contrat de vote est une machine a etats (enum Phase) : chaque fonction n’est appelable que dans la phase courante (modifier onlyPhase), et l’admin fait progresser le cycle de vie en emettant l’event PhaseChanged. Le diagramme rend explicite ce cycle decrit ci-dessus :

stateDiagram-v2
    direction LR
    [*] --> Registration
    Registration --> Voting : PhaseChanged (admin)
    Voting --> Tallying : PhaseChanged (admin)
    Tallying --> Ended : PhaseChanged (admin)
    Ended --> [*]
    Registration : Registration -- registerVoter(address) [admin]
    Voting : Voting -- submitBallot(ballot, proof) + verifyProof
    Tallying : Tallying -- tally() decompte homomorphique Paillier
    Ended : Ended -- résultats verifiables on-chain

Lecture. Le contrat avance strictement de gauche a droite : on ne peut voter (submitBallot) qu’en phase Voting, ni decompter (tally) avant Tallying. Cette discipline d’etats est ce qui garantit, au niveau du protocole, qu’aucun bulletin ne peut etre soumis après la cloture ni decompte avant la fin du vote – une propriete de securite structurelle, imposee par le code et non par la confiance dans l’administrateur.


Partie 2 : Chiffrement des votes (Python)

Utilisez Paillier (SC-16) pour chiffrer les votes cote client.

# Partie 2: Chiffrement Paillier
try:
    from phe import paillier
    PHE_AVAILABLE = True
except ImportError:
    PHE_AVAILABLE = False

if PHE_AVAILABLE:
    # Etape: Implementer le chiffrement des votes
    # 1. Generer les cles Paillier
    # 2. Definir les candidats
    # 3. Chiffrer un vote (1 pour le candidat choisi, 0 pour les autres)
    # 4. Verifier que l'addition homomorphique fonctionne

    # Indice: voir SC-16 et SC-17 pour le pattern
    # public_key, private_key = paillier.generate_paillier_keypair()
    # encrypted_vote = [public_key.encrypt(1), public_key.encrypt(0), public_key.encrypt(0)]

    pass  # Etape: Implementez le chiffrement des votes
else:
    print("Exercice a completer (phe non installe)")
Exercice a completer (phe non installe)

Transition : Du chiffrement a la preuve de validite

La partie 2 permet de chiffrer les votes, mais le contrat doit verifier que chaque bulletin est valide (exactement un candidat choisi). C’est le rôle de la ZKP.

Pourquoi une preuve de validite ? - Le chiffrement protege le contenu du vote (privacy) - La ZKP prouve que le vote est correct (validity) - Sans ZKP, un electeur pourrait soumettre [E(5), E(3), E(2)] (bulletin invalide)

Prochaine étape : Implementer une preuve que le bulletin chiffre contient exactement un 1 (Partie 3).

Interpretation : Structure du chiffrement client

Sortie obtenue : Squelette Python pour le chiffrement des votes avec Paillier.

Élément Rôle
phe.paillier Bibliotheque Python pour le chiffrement Paillier
generate_paillier_keypair() Genere les cles publique et privee
public_key.encrypt(1) Chiffre la valeur 1 (vote pour un candidat)
public_key.encrypt(0) Chiffre la valeur 0 (vote contre un candidat)

Points cles : 1. Chaque vote est un tableau de valeurs chiffrees (une par candidat) 2. Seule une valeur est 1, les autres sont 0 (exactement un candidat choisi) 3. L’addition homomorphique permet d’additionner les votes chiffres sans dechiffrement

Note technique : La bibliotheque phe (Python Homomorphic Encryption) implemente le schema de Paillier. Verifiez que pip install phe est execute avant utilisation.

Indice : Chiffrement homomorphique avec Paillier

Rappel de SC-16 et SC-17 : - Paillier est un chiffrement additivement homomorphique : E(a) * E(b) = E(a+b) - Permet de compter les votes sans les dechiffrer individuellement - La cle publique sert a chiffrer, la cle privee a dechiffrer le résultat final

Workflow pour un vote : 1. Generer public_key, private_key = paillier.generate_paillier_keypair() 2. Pour 3 candidats, un vote pour le candidat 0 : [E(1), E(0), E(0)] 3. Soumettre le tableau de votes chiffres au contrat 4. Le decompte final : sum_votes = [sum(E(vote_i)) for i in candidates] 5. Dechiffrer uniquement sum_votes avec la cle privee

Propriete critique : Le contrat voit des bytes (votes chiffres) mais ne peut pas connaitre le contenu sans la cle privee.

Vocabulaire technique : - Homomorphique : Opération sur les données chiffrees equivalent a l’opération sur les données en clair - Cle publique : Utilisee pour chiffrer, partagee a tous - Cle privee : Utilisee pour dechiffrer, connue uniquement de l’autorite de decompte


Partie 3 : Preuve de validite (ZKP)

Chaque electeur doit prouver que son bulletin est valide sans reveler son vote.

# Partie 3: Preuve ZKP simplifiee
import hashlib
import secrets

# Etape: Implementer une preuve que le bulletin chiffre contient
# exactement un 1 et le reste des 0
# 
# Approche simplifiee (commitment scheme):
# 1. L'electeur genere un nonce r
# 2. Commitment = hash(vote || r)
# 3. La preuve montre que sum(votes) == 1 sans reveler les votes individuels
#
# Voir SC-15 pour les protocoles Sigma et SC-17 pour l'application au vote

pass  # Etape: Implementez la preuve de validite
print("Exercice a completer")
Exercice a completer

Transition : De la ZKP a l’integration complete

Les parties 1-3 ont défini les composants individuels (smart contract, chiffrement, ZKP). La partie 4 consiste a les assembler en un système fonctionnel.

Workflow d’integration : 1. Deployer le contrat sur anvil (blockchain locale) 2. Enregistrer les electeurs autorises 3. Chiffrer les votes (Paillier) + generer les preuves (ZKP) 4. Soumettre les bulletins chiffres au contrat 5. Decompter les votes (addition homomorphique) 6. Dechiffrer le résultat final avec la cle privee

Prochaine étape : Assembler le pipeline complet et tester (Partie 4).

Interpretation : Structure de la preuve ZKP

Sortie obtenue : Squelette Python pour implementer une preuve de validite de bulletin.

Élément Rôle
hashlib Fonctions de hachage pour le commitment
secrets Generation de nonces cryptographiquement securises
hash(vote \|\| r) Commitment qui lie le vote au nonce

Points cles : 1. La preuve doit demontrer que sum(votes) == 1 (exactement un candidat choisi) 2. Le nonce r assure que la preuve est non-interactive (Fiat-Shamir heuristic) 3. Le contrat doit verifier la preuve avant d’accepter le bulletin chiffre

Note technique : Cette approche simplifiee utilise un commitment scheme. Une ZKP complete (comme zk-SNARK) serait plus complexe mais plus succincte. Voir SC-15 pour les concepts avances.

Indice : Preuve de validite sans revelation

Objectif : Prouver que le bulletin chiffre contient exactement un vote pour un candidat (1) et des zeros pour les autres, sans reveler pour qui.

Approche suggeree : 1. Commitment scheme : L’electeur calcule C = hash(votes || r) où r est un nonce aleatoire 2. Challenge-response : Le contrat peut demander une preuve que sum(votes) == 1 3. ZKP simplifie : Utiliser le fait que le chiffrement Paillier est additivement homomorphique

Rappel de SC-15 et SC-17 : - Protocol Sigma : engagement → challenge → reponse - Preuve de connaissance : “Je connais un vote tel que sum=1” - Pas besoin de reveler le vote lui-même, seulement sa validite

Vocabulaire technique : - Commitment : Valeur qui engage l’electeur a son vote sans le reveler - Zero-knowledge : La preuve ne reve rien d’autre que la validite - Soundness : Impossible de prouver un bulletin invalide - Zero-knowledge : La preuve ne revele rien d’autre que la validite


Partie 4 : Integration et deploiement

Assemblez les 3 parties et deployez sur anvil (local) puis sur Sepolia (testnet).

# Partie 4: Integration complete

# TODO:
# 1. Deployer VotingSystem sur anvil
# 2. Enregistrer 3 electeurs
# 3. Chaque electeur chiffre et soumet son vote
# 4. Effectuer le decompte homomorphique
# 5. Verifier les resultats
# 6. (Bonus) Deployer sur Sepolia

# Workflow:
# ANVIL_URL = "http://127.0.0.1:8545"
# w3 = Web3(Web3.HTTPProvider(ANVIL_URL))
# contract, receipt = compile_and_deploy(w3, VOTING_CONTRACT, deployer)
# ... enregistrer, voter, decompter

pass  # Etape: Assemblez et deployez le systeme complet
print("Exercice a completer")
Exercice a completer

Transition : De l’integration aux tests

La partie 4 permet de deployer et tester manuellement le système. La partie 5 consiste a ecrire des tests automatises avec Foundry.

Pourquoi des tests Foundry ? - Tests reproductibles et automatisables - Couverture de tous les cas d’erreur (revert, access control) - Integration dans CI/CD pour développement continu - Les cheatcodes Foundry permettent de simuler n’importe quel scénario

Prochaine étape : Ecrire des tests Foundry pour valider le contrat (Partie 5, bonus).

Interpretation : Workflow d’integration

Sortie obtenue : Squelette du workflow complet d’integration des 3 composants.

Étape Composant Objectif
1 Anvil Blockchain locale pour développement
2 Web3.py Interface Python pour interagir avec le contrat
3 Chiffrement Votes transformes en bytes via Paillier
4 Soumission contract.functions.submitBallot().transact()
5 Decompte Addition homomorphique des votes chiffres

Points cles : 1. Anvil (http://127.0.0.1:8545) fournit un environnement Ethereum local instantane 2. Chaque electeur doit etre enregistre avant de pouvoir voter 3. Le decompte homomorphique permet d’additionner les votes chiffres sans les dechiffrer individuellement

Note technique : Pour Sepolia, remplacez ANVIL_URL par votre RPC provider (Infura/Alchemy) et ajoutez une cle privee avec des fonds Sepolia dans .env.


Partie 5 : Tests Foundry (Bonus)

Ecrivez des tests Foundry pour le contrat VotingSystem.

# Partie 5: Tests Foundry

VOTING_TEST = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "forge-std/Test.sol";
// import "../src/VotingSystem.sol";

// TODO: Ecrivez les tests pour VotingSystem
// - test_RegisterVoter()
// - test_SubmitBallot()
// - test_OnlyRegisteredCanVote()
// - test_CannotVoteTwice()
// - test_Tally()
// - test_PhaseTransitions()

// Utilisez les cheatcodes de SC-12:
// - vm.prank(voter) pour simuler un electeur
// - vm.expectRevert() pour les cas d'erreur
// - vm.warp() si vous avez des deadlines
"""

print("Squelette de test Foundry pour VotingSystem")
print(VOTING_TEST)
print("\nPour executer: forge test -vvv")
Squelette de test Foundry pour VotingSystem

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "forge-std/Test.sol";
// import "../src/VotingSystem.sol";

// TODO: Ecrivez les tests pour VotingSystem
// - test_RegisterVoter()
// - test_SubmitBallot()
// - test_OnlyRegisteredCanVote()
// - test_CannotVoteTwice()
// - test_Tally()
// - test_PhaseTransitions()

// Utilisez les cheatcodes de SC-12:
// - vm.prank(voter) pour simuler un electeur
// - vm.expectRevert() pour les cas d'erreur
// - vm.warp() si vous avez des deadlines


Pour executer: forge test -vvv

Interpretation : Structure de test Foundry

Sortie obtenue : Squelette de code Solidity pour les tests du contrat VotingSystem.

Élément Objectif
import "forge-std/Test.sol" Importe les utilitaires de test de Foundry
vm.prank(voter) Simule l’envoi d’une transaction depuis un compte spécifique
vm.expectRevert() Verifie qu’une transaction echoue avec un revert
vm.warp(timestamp) Modifie le timestamp du bloc (pour deadlines)

Points cles : 1. Les tests Foundry utilisent des “cheatcodes” (vm.*) pour manipuler l’environnement blockchain 2. Chaque fonction de test doit commencer par test_ pour etre decouverte par Forge 3. Les tests doivent couvrir les “happy paths” et les cas d’erreur

Note technique : La commande forge test -vvv execute tous les tests avec une verbosite maximale (affiche les console.log et les traces d’exécution).


Critères d’evaluation

Critere Points Description
Smart Contract 30% Contrat compile, deploye, fonctionnel
Chiffrement 25% Votes chiffres, decompte homomorphique correct
ZKP 20% Preuve de validite du bulletin
Deploiement 15% Fonctionne sur anvil + testnet
Tests 10% Au moins 3 tests Foundry qui passent

Bonus

  • Vote sur mainnet L2 (+5%)
  • Interface web basique (+5%)
  • Multi-candidats (>2) (+5%)
  • Verification publique complete (+5%)

Conclusion : Synthese du projet

Ce projet capstone a permis d’integrer tous les concepts de la serie SmartContracts dans une application complete.

Composant Notebook d’origine Rôle dans le projet
Smart Contract SC-9 (Governance) Stockage on-chain, state machine, access control
Chiffrement Paillier SC-16 (HE) Privacy des votes, decompte homomorphique
ZKP SC-15 (ZKP) Preuve de validite sans revelation du vote
E2E Voting SC-17 (Voting) Pattern complet de vote verifiable
Deploiement SC-24 (Testnet) anvil local + Sepolia testnet
Tests SC-12 (Foundry) Validation automatisee du contrat

Points cles retires : 1. Privacy + Verifiability : Le chiffrement protege le contenu, la ZKP prouve la validite 2. Homomorphic encryption : Permet le decompte sans dechiffrement individuel 3. State machine : Le contrat suit un cycle de vie strict (Registration → Voting → Tallying → Ended) 4. Tests automatises : Foundry permet de valider tous les scénarios

Prochaine étape : Deployer sur un testnet public (Sepolia) et, en bonus, sur mainnet L2 (Arbitrum/Optimism).


Ressources


Navigation : Sommaire | << Précédent

Retour au sommaire

Resume et perspectives

Ce projet capstone a permis de consolider l’ensemble des competences developpees tout au long de la serie SmartContracts. En construisant un système de vote decentralise complet, nous avons articule cinq piliers fondamentaux : la programmation de smart contracts en Solidity avec une machine a etats robuste (Registration, Voting, Tallying, Ended), le chiffrement homomorphique Paillier pour garantir la confidentialite des bulletins tout en permettant un decompte verifiable, les preuves a divulgation nulle (ZKP) pour prouver la validite de chaque bulletin sans reveler son contenu, le deploiement progressif d’anvil vers les testnets publics, et la validation automatisee via les tests Foundry avec cheatcodes.

L’integration de ces composants illustre un principe central de la securite blockchain : la separation entre privacy (protection du contenu individuel via chiffrement) et verifiability (preuve publique de correction via ZKP). Le modèle de state machine du contrat, combine au typage fort du chiffrement homomorphique et aux commitment schemes, forme une architecture ou chaque couche apporte une garantie complementaire. Ce pattern de composition de primitives cryptographiques se retrouve dans les systèmes de vote reels (comme les implementations basées sur MACI ou Semaphore) et dans les protocoles DeFi avances utilisant des preuves a divulgation nulle pour la conformite reglementaire.

Les perspectives d’approfondissement incluent l’utilisation de zk-SNARKs (Groth16, PLONK) pour des preuves plus succinctes et verificables on-chain a cout reduit, l’implementation d’un mécanisme de seuil de dechiffrement (threshold decryption) pour eliminer le point de confiance unique de l’autorite de decompte, et le deploiement sur un L2 (Base, Arbitrum) pour beneficier de couts de transaction negligeables tout en heritant de la securite d’Ethereum.

Retour au sommet