
In diesem Tutorial implementieren wir ein ZK-basiertes privates Abstimmungssystem, Privote, bei dem niemand die Entscheidungen der Wähler sehen kann, bis die Abstimmung beendet ist.

Stellen Sie sich vor, Sie sind ein registrierter Wähler, der über ein bestimmtes Thema oder einen Kandidaten abstimmt. Eine externe Partei möchte, dass Sie Ihre Stimme verkaufen – mit anderen Worten: Sie soll für deren Wahl stimmen, nicht für Ihre eigene, im Austausch gegen bestimmte Anreize. Wenn Sie dieser Forderung nachgeben, benötigt die Partei einen Nachweis Ihrer Stimmabgabe – häufig in Form einer Blockchain-Transaktionsquittung. Auch wenn dies wie eine verlockende Gelegenheit erscheinen mag, wenn Sie geneigt sind, Ihre Stimme zu verkaufen, untergräbt es die Integrität des gesamten Wahlprozesses.
Wie können wir es Wählern ermöglichen, für ihre bevorzugten Kandidaten oder Themen zu stimmen und dabei ihre Entscheidungen zu verbergen, sodass niemand sonst sie einsehen kann? Die Antwort liegt in einem einzigen Wort: Verschlüsselung.
In diesem Tutorial erfahren Sie, wie Sie eine einfache Wahlapplikation – Privote – entwickeln, die Verschlüsselung auf der Blockchain nutzt. Zum gegebenen Zeitpunkt werden die Stimmen entschlüsselt und ausgezählt, und die Ergebnisse werden unter Wahrung der Anonymität der Wähler wieder auf der Blockchain veröffentlicht. Die Ergebnisse werden durch einen Zero-Knowledge (ZK) Proof verifiziert. Obwohl nicht perfekt, veranschaulicht dieses Beispiel die zugrundeliegenden Konzepte.
Für ein tiefgehendes Verständnis des Blockchain-Votings empfehlen wir Vitalik Buterins Blogbeitrag.
Bevor wir fortfahren, hier ein Überblick über die Hauptkomponenten der Privote-Dapp:
Smart Contract: Dieses System speichert mehrere Stimmen (Stimmzettel) und zugehörige Daten wie Titel, verfügbare Auswahlmöglichkeiten und verschlüsselte Daten.
Vertrauenswürdige Partei: Ein Off-Chain-Server, der die Stimmen entschlüsselt, die Ergebnisse berechnet und sie auf der Blockchain veröffentlicht. Dieser Server wird aus gutem Grund als „Vertrauenswürdige Partei" bezeichnet: Er kann keine falschen Ergebnisse veröffentlichen, weder durch fehlerhafte Berechnungen noch durch das Zensieren von Nachrichten.
Frontend: Eine Plattform, auf der Nutzer ihre Wallets verbinden und Stimmen abgeben. Es wird auch eine separate Seite für den „Admin" geben, um die Anzahl der abgegebenen Stimmen einzusehen und die Ergebnisse manuell freizugeben.
Um Privote erfolgreich aufzubauen, müssen wir zwei Herausforderungen bewältigen:
Wie können wir jedem Wähler ermöglichen, seine Stimme zu verschlüsseln, während die vertrauenswürdige Partei sie entschlüsseln kann?
Wie stellen wir sicher, dass die vertrauenswürdige Partei zuverlässig ist und einen ZK-Proof der Berechnung liefern kann – aber was genau beweisen wir dabei?
Um dies zu lösen, können wir asymmetrische Verschlüsselung einsetzen. Im Gegensatz zur symmetrischen Verschlüsselung, die denselben Schlüssel zum Ver- und Entschlüsseln von Daten verwendet, nutzt die asymmetrische Verschlüsselung zwei verschiedene Schlüssel: einen zum Verschlüsseln und einen zum Entschlüsseln. Es gibt mehrere asymmetrische Verschlüsselungsalgorithmen, darunter ElGamal, RSA, DSA und Elliptische-Kurven-Verfahren. Für dieses Tutorial verwenden wir die Elliptische-Kurven-Verfahren, konkret den Diffie-Hellman-Schlüsselaustausch(ECDH)](https://www.upguard.com/blog/diffie-hellman)(ECDH)).
Bei ECDH verfügen sowohl die vertrauenswürdige Partei als auch jeder Wähler über eigene Schlüsselpaare (einen öffentlichen und einen privaten Schlüssel). Die Wahl der elliptischen Kurve – wie die hier verwendete Baby Jubjub-Kurve, die aufgrund ihrer Eignung für Zero-Knowledge-Proofs gewählt wurde – beeinflusst die Sicherheit und Effizienz kryptografischer Operationen. Die Baby Jubjub-Kurve ist durch die Gleichung y^2 = x^3 + 168698 x 2 + x definiert, und Schlüsselpaare werden als spezifische Punktepaare auf dieser Kurve generiert:
In der Funktion generateKeyPair erstelle ich zunächst einen sicheren zufälligen privaten Schlüssel und berechne anschließend den entsprechenden öffentlichen Schlüssel durch eine spezifische mathematische Operation (Skalarmultiplikation) auf einem vordefinierten Punkt der Kurve, der häufig als Basispunkt oder Generatorpunkt bezeichnet wird. Im Code trägt dieser Punkt den Namen Base8. Das Ergebnis dieser Multiplikation ist ein weiterer Punkt auf der Kurve, dargestellt durch seine X- und Y-Koordinaten. Anschließend wird der Punkt mithilfe der Funktion packPoint zur bequemen Verwendung in ein bigint umgewandelt.
Der Wähler erstellt einen Cipher, indem er seinen privaten Schlüssel mit dem öffentlichen Schlüssel der vertrauenswürdigen Partei kombiniert. Dieser Cipher wird zur Verschlüsselung der Stimme verwendet. Der Smart Contract speichert anschließend die verschlüsselte Stimme zusammen mit dem öffentlichen Schlüssel des Wählers.
Die vertrauenswürdige Partei kann einen Cipher mithilfe ihres privaten Schlüssels und des öffentlichen Schlüssels jedes Wählers erstellen, um die Daten zu entschlüsseln.
In der Funktion generateKeyPair haben wir den öffentlichen Schlüssel für die einfache Verwendung mit der packPoint-Funktion formatiert. Im obigen Code konvertieren wir ihn mit unpackPoint zurück in seine ursprüngliche Form, da wir ein gemeinsames Geheimnis durch die Kombination des privaten Schlüssels mit dem öffentlichen Schlüssel des Wählers erstellen müssen. Dieses gemeinsame Geheimnis wird zur Entschlüsselung der Stimme verwendet.
Nach der Entschlüsselung jeder Stimme ist die Berechnung der Ergebnisse einfach! Iterieren Sie einfach durch das Stimmen-Array und summieren Sie die Stimmen für eine bestimmte Auswahl.
Falls Sie mit ZK-Proofs nicht vertraut sind: Es handelt sich um eine recht komplexe, aber faszinierende Technologie. ZK-Proofs sind eine Methode, bei der eine Partei einer anderen beweisen kann, dass sie einen Wert kennt, ohne dabei Informationen über den Wert selbst preiszugeben. Im Wesentlichen ist es eine Möglichkeit, die Wahrheit von etwas zu validieren, ohne die Details zu teilen. Darüber hinaus kann jeder überprüfen, ob dieser Proof korrekt ist.
Für unsere Applikation sollte die vertrauenswürdige Partei idealerweise beweisen, dass sie die verschlüsselten Stimmen entgegennehmen, entschlüsseln und die Anzahl der Stimmen für jede Auswahl berechnen kann. In diesem Fall ist der „geheime" Schlüssel der private Schlüssel der vertrauenswürdigen Partei. Der Einfachheit halber beweisen wir lediglich den Besitz eines privaten Schlüssels, der mit dem im Smart Contract hinterlegten öffentlichen Schlüssel übereinstimmt. Wie bereits erwähnt, verwenden die Stimmen diesen Schlüssel, um einen Cipher zur Verschlüsselung der Stimme zu generieren.
Für diesen Zweck verwenden wir ZoKrates, eine Toolbox für zkSNARKs auf Ethereum. Es hilft Ihnen, verifizierbare Berechnungen in Ihrer Dapp zu nutzen – von der Spezifikation Ihres Programms in einer Hochsprache bis hin zur Generierung von Berechnungsbeweisen und deren Verifizierung in Solidity.
Dies ist das einfache ZK-Programm, das ich verwende:
Wie Sie in der Argumentenliste sehen können, ist pk ein öffentliches Argument und sk ein privates Argument. context ist ebenfalls öffentlich. Nur der Prover – die vertrauenswürdige Partei – kennt den sk, während pk für jeden zugänglich ist, da er im Smart Contract hinterlegt ist. context enthält die Parameter der Kurve, die wir zur Generierung eines Schlüsselpaars ausgewählt haben.
Im obigen Codeausschnitt verwende ich das ZoKrates JS SDK, um einen Proof zu generieren. Der Ablauf ähnelt dem, was in der ZoKrates-Dokumentation beschrieben ist. Unser Ziel ist es, das ZoKrates-Programm zur Generierung eines Proofs zu verwenden. Daher übergeben wir der Funktion computeWitness drei Argumente: pk (ein öffentliches Argument), sk (ein privates Argument) und context. Diese werden verwendet, um einen Witness zu erstellen, der anschließend zur Generierung des Proofs genutzt wird.
Möglicherweise fragen Sie sich, was es mit diesem Teil des Arguments auf sich hat:
Der context liefert die Daten zur spezifischen Baby JubJub-Kurve, die wir in der gesamten Applikation verwendet haben, da viele Kurven-Variationen eingesetzt werden können. Dateien wie der Verifikationsschlüssel, der Proving Key und das ZoKrates-Programm werden in einem separaten Verzeichnis gespeichert. Ich verwende die folgende Funktion, um diese abzurufen:
Der folgende Ausschnitt stammt aus dem Contract, in dem der Contract die von der vertrauenswürdigen Partei berechneten Ergebnisse akzeptiert. Nur wenn die Verifizierung erfolgreich ist, akzeptiert der Contract das Ergebnis. Außenstehende können dies überprüfen, indem sie den pk (der öffentlich ist und im Contract zu finden ist) sowie den Proof an die Verifier-Funktion übergeben:
In diesem Blogbeitrag haben wir Privote entwickelt, ein ZK-basiertes privates Wahlsystem, das die Entscheidungen der Wähler bis zum Ende des Abstimmungszeitraums vertraulich hält. Wir haben Elliptische-Kurven-Verfahren zur Verschlüsselung von Stimmen eingesetzt und gezeigt, wie diese sicher entschlüsselt und ausgezählt werden können. Außerdem haben wir Zero-Knowledge-Proofs mithilfe von ZoKrates implementiert, um die Integrität des Wahlprozesses zu verifizieren, ohne private Informationen preiszugeben.
Dieses Tutorial vermittelt ein grundlegendes Verständnis für die Erstellung sicherer und privater Wahlsysteme auf der Blockchain. Im zweiten Teil werden wir das Frontend aufbauen und zeigen, wie Nutzer sich verbinden und Stimmen mit dem MetaMask SDK abgeben können.
KI-übersetzt. Kann Fehler enthalten. Bitte überprüfen Sie die Informationen stets.