
Dans ce tutoriel, nous implémentons un système de vote privé basé sur ZK, Privote, dans lequel personne ne peut voir les choix des votants avant la fin du vote.

Imaginez que vous êtes un électeur inscrit, appelé à voter sur une question particulière ou pour un candidat. Une partie externe souhaite vous acheter votre vote, autrement dit, elle veut que vous votiez selon son choix et non le vôtre, en échange de certaines contreparties. Si vous cédez à sa demande, elle aura besoin d'une preuve de votre vote — souvent sous la forme d'un reçu de transaction blockchain. Même si cela peut sembler une opportunité intéressante si vous êtes enclin à vendre votre vote, cela compromet l'intégrité de l'ensemble du processus électoral.
Comment permettre aux électeurs de voter pour les candidats ou les questions de leur choix tout en dissimulant leurs préférences afin que personne d'autre ne puisse les voir ? La réponse tient en un seul mot : le chiffrement.
Dans ce tutoriel, nous allons explorer comment développer une application de vote simple — Privote — qui utilise le chiffrement sur la blockchain. Le moment venu, les votes sont déchiffrés et comptabilisés, et les résultats sont publiés sur la blockchain tout en préservant l'anonymat des électeurs. Les résultats sont vérifiés grâce à une preuve à divulgation nulle de connaissance (ZK). Bien qu'imparfait, cet exemple illustre les concepts sous-jacents.
Veuillez consulter le billet de blog de Vitalik Buterin pour une compréhension approfondie du vote sur blockchain.
Avant de poursuivre, voici une présentation des principaux composants de la dapp Privote :
Smart Contract : Ce système stocke plusieurs votes (bulletins) et les données associées, telles que les titres, les choix disponibles et les données chiffrées.
Partie de confiance : Un serveur hors chaîne qui déchiffre les votes, calcule les résultats et les publie sur la blockchain. Ce serveur est appelé « Partie de confiance » pour une raison précise : il ne peut pas publier des résultats incorrects, que ce soit en calculant de manière inexacte ou en censurant des messages.
Frontend : Une plateforme où les utilisateurs connectent leurs wallets et soumettent leurs votes. Il y aura également une page distincte pour l'« administrateur », lui permettant de consulter le nombre de votes exprimés et de publier manuellement les résultats.
Pour construire Privote avec succès, nous devons relever deux défis :
Permettre à n'importe quel électeur de chiffrer son vote tout en permettant à la partie de confiance de le déchiffrer.
S'assurer que la partie de confiance est fiable et capable de fournir une preuve ZK du calcul, mais que prouvons-nous exactement ?
Pour résoudre ce problème, nous pouvons adopter le chiffrement asymétrique. Contrairement au chiffrement symétrique, qui utilise la même clé pour chiffrer et déchiffrer les données, le chiffrement asymétrique utilise deux clés différentes : l'une pour chiffrer et l'autre pour déchiffrer. Il existe plusieurs algorithmes de chiffrement asymétrique, notamment ElGamal, RSA, DSA et les techniques à courbes elliptiques. Pour ce tutoriel, nous utiliserons les techniques à courbes elliptiques, plus précisément l'échange de clés Diffie-Hellman(ECDH)](https://www.upguard.com/blog/diffie-hellman)(ECDH)).
Dans ECDH, la partie de confiance et chaque électeur disposent de leurs propres paires de clés (une clé publique et une clé privée). Le choix de la courbe elliptique, comme la courbe Baby Jubjub utilisée ici pour son adéquation aux preuves à divulgation nulle de connaissance, influe sur la sécurité et l'efficacité des opérations cryptographiques. La courbe Baby Jubjub est définie par l'équation y^2 = x^3 + 168698 x 2 + x, et les paires de clés sont générées sous forme de paires de points spécifiques sur cette courbe :
Dans la fonction generateKeyPair, je commence par créer une clé privée aléatoire sécurisée, puis je calcule la clé publique correspondante en effectuant une opération mathématique spécifique (multiplication scalaire) sur un point prédéfini de la courbe, souvent appelé point de base ou point générateur. Dans le code, ce point est nommé Base8. Le résultat de cette multiplication est un autre point sur la courbe, désigné par ses coordonnées X et Y. Ce point est ensuite formaté en bigint pour une utilisation pratique avec la fonction packPoint.
L'électeur crée un cipher en combinant sa clé privée avec la clé publique de la partie de confiance. Ce cipher est utilisé pour chiffrer le vote. Le smart contract enregistre ensuite le vote chiffré et la clé publique de l'électeur ensemble.
La partie de confiance peut créer un cipher en utilisant sa clé privée et la clé publique de chaque électeur pour déchiffrer les données.
Dans la fonction generateKeyPair, nous avons formaté la clé publique pour une utilisation facile avec la fonction packPoint. Dans le code ci-dessus, avec unpackPoint, nous la reconvertissons dans sa forme originale, car nous devons créer un secret partagé en combinant la clé privée et la clé publique de l'électeur. Ce secret partagé est utilisé pour déchiffrer le vote.
Après avoir déchiffré chaque vote, le calcul des résultats est simple ! Il suffit de parcourir le tableau des votes et d'accumuler les votes pour un choix donné.
Si vous n'êtes pas familier avec les preuves ZK, il s'agit d'une technologie assez complexe mais fascinante. Les preuves ZK sont une méthode par laquelle une partie peut prouver à une autre qu'elle connaît une valeur sans révéler aucune information sur cette valeur elle-même. En substance, c'est un moyen de valider la véracité de quelque chose sans en divulguer les détails. De plus, n'importe qui peut vérifier si cette preuve est correcte.
Pour notre application, idéalement, la partie de confiance devrait prouver qu'elle peut prendre les votes chiffrés, les déchiffrer et calculer le nombre de votes reçus par chaque choix. Dans ce cas, la clé « secrète » est la clé privée de la partie de confiance. Par souci de simplicité, nous prouvons simplement la possession d'une clé privée correspondant à la clé publique enregistrée dans le smart contract. Comme mentionné précédemment, les votes utilisent cette clé pour générer un cipher afin de chiffrer le vote.
À cette fin, nous utiliserons ZoKrates, une boîte à outils pour les zkSNARKs sur Ethereum. Elle vous aide à utiliser le calcul vérifiable dans votre dapp, depuis la spécification de votre programme dans un langage de haut niveau jusqu'à la génération de preuves de calcul et leur vérification dans Solidity.
Voici le programme ZK simple que j'utilise :
Comme vous pouvez le voir dans la liste des arguments, pk est un argument public et sk est un argument privé. context est également public. Seul le prouveur — la partie de confiance — connaît sk, mais pk est connu de tous car il figure dans le smart contract. context représente les paramètres liés à la courbe que nous avons choisie pour générer une paire de clés.
Dans l'extrait ci-dessus, j'utilise le SDK JS de ZoKrates pour générer une preuve. Le flux est similaire à ce qui est documenté dans la documentation ZoKrates. Notre objectif est d'utiliser le programme ZoKrates pour générer une preuve ; nous passons donc trois arguments à la fonction computeWitness : pk (un argument public), sk (un argument privé) et context. Ceux-ci sont utilisés pour créer un témoin (witness), qui est ensuite utilisé pour générer la preuve.
Vous vous interrogez peut-être sur cette partie de l'argument :
Le context fournit les données relatives à la courbe Baby JubJub spécifique que nous avons utilisée tout au long de l'application, car de nombreuses variantes de courbes peuvent être employées. Les fichiers tels que la clé de vérification, la clé de preuve et le programme ZoKrates sont conservés dans un répertoire séparé. J'utilise la fonction ci-dessous pour les récupérer :
L'extrait ci-dessous est tiré du contrat dans lequel celui-ci accepte les résultats calculés par la partie de confiance. Le contrat n'accepte le résultat que si la vérification est réussie. Les parties externes peuvent vérifier cela en passant la pk (qui est publique et figure dans le contrat) et la preuve à la fonction de vérification :
Dans ce billet de blog, nous avons développé Privote, un système de vote privé basé sur ZK qui préserve la confidentialité des choix des électeurs jusqu'à la fin de la période de vote. Nous avons utilisé des techniques à courbes elliptiques pour chiffrer les votes et démontré comment les déchiffrer et les comptabiliser de manière sécurisée. Nous avons également mis en œuvre des preuves à divulgation nulle de connaissance à l'aide de ZoKrates pour vérifier l'intégrité du processus de vote sans révéler d'informations privées.
Ce tutoriel offre une compréhension de base de la création de systèmes de vote sécurisés et privés sur la blockchain. Dans la deuxième partie, nous construirons le frontend et vous montrerons comment les utilisateurs peuvent se connecter et voter en utilisant le MetaMask SDK.
Traduit par IA. Peut contenir des erreurs. Veuillez toujours vérifier les informations.