Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ runs:
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: "16.18.x"
node-version: "20.x"
cache: npm
- name: Install packages
run: npm install
shell: bash
- name: Generate verifiers
run: npx hardhat zkit verifiers
shell: bash
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ generated-types

# Hardhat migrate
.storage.json

contracts/verifiers
43 changes: 43 additions & 0 deletions circuits/ElGamal.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
pragma circom 2.1.6;

include "./babyjubjub/babyjub.circom";
include "./babyjubjub/escalarmulany.circom";

template ElGamal() {
signal input pk[2];
signal input M[2];
signal input nonce;

signal output C[2];
signal output D[2];

component pbk = BabyPbk();
pbk.in <== nonce;

C[0] <== pbk.Ax;
C[1] <== pbk.Ay;

signal rP[2];

component nonceBits = Num2Bits(253);
nonceBits.in <== nonce;

component mul = EscalarMulAny(253);
mul.p <== pk;

var i;
for (i = 0; i < 253; i++) {
mul.e[i] <== nonceBits.out[i];
}

rP <== mul.out;

component add = BabyAdd();
add.x1 <== M[0];
add.y1 <== M[1];
add.x2 <== rP[0];
add.y2 <== rP[1];

D[0] <== add.xout;
D[1] <== add.yout;
}
34 changes: 34 additions & 0 deletions circuits/IsGOrInf.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
pragma circom 2.1.6;

include "../node_modules/@solarity/circom-lib/bitify/comparators.circom";

template IsGOrInf() {
var Gx = 5299619240641551281634865583518297030282874472190772894086521144482721001553;
var Gy = 16950150798460657717958625567821834550301663161624707787222815936182638968203;

signal input x;
signal input y;

component diffGx = IsZero();
diffGx.in <== x - Gx;

component diffGy = IsZero();
diffGy.in <== y - Gy;

signal isG;
isG <== diffGx.out * diffGy.out;

component diffInfX = IsZero();
diffInfX.in <== x - 0;

component diffInfY = IsZero();
diffInfY.in <== y - 1;

signal isInf;
isInf <== diffInfX.out * diffInfY.out;

signal isValid;
isValid <== isG + isInf;

isValid === 1;
}
48 changes: 48 additions & 0 deletions circuits/ProposalCreation.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
pragma circom 2.1.6;

include "./babyjubjub/babyjub.circom";
include "../node_modules/@solarity/circom-lib/data-structures/CartesianMerkleTree.circom";
include "../node_modules/@solarity/circom-lib/hasher/poseidon/poseidon.circom";

template ProposalCreation(proofSize) {
signal input cmtRoot;
signal input proposalId;

signal input sk;

signal input siblings[proofSize];
signal input siblingsLength[proofSize/2];
signal input directionBits[proofSize/2];
signal input nonExistenceKey;

component pbk = BabyPbk();
pbk.in <== sk;

signal publicKey[2];
publicKey[0] <== pbk.Ax;
publicKey[1] <== pbk.Ay;

component keyHash = Poseidon(3);
keyHash.in[0] <== publicKey[0];
keyHash.in[1] <== publicKey[1];
keyHash.in[2] <== 1;
keyHash.dummy <== 0;

signal key;
key <== keyHash.out;

component cmt = CartesianMerkleTree(proofSize);
cmt.root <== cmtRoot;
cmt.siblings <== siblings;
cmt.siblingsLength <== siblingsLength;
cmt.directionBits <== directionBits;
cmt.key <== key;
cmt.nonExistenceKey <== nonExistenceKey;
cmt.isExclusion <== 0;
cmt.dummy <== 0;

signal proposalConstraint;
proposalConstraint <== proposalId * key;
}

component main {public [cmtRoot, proposalId]} = ProposalCreation(20);
180 changes: 180 additions & 0 deletions circuits/Voting.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
pragma circom 2.1.6;

include "./IsGOrInf.circom";
include "./ElGamal.circom";
include "./babyjubjub/babyjub.circom";
include "./babyjubjub/escalarmulany.circom";
include "../node_modules/@solarity/circom-lib/data-structures/CartesianMerkleTree.circom";
include "../node_modules/@solarity/circom-lib/hasher/poseidon/poseidon.circom";

template Voting(proofSize) {
signal input decryptionKeyShare;
signal input encryptionKey[2];
signal input challenge;
signal input proposalId;
signal input cmtRoot;

signal input sk1;
signal input sk2;

signal input vote[2];
signal input k;

// CMT inclusion proof
signal input siblings[2][proofSize];
signal input siblingsLength[2][proofSize/2];
signal input directionBits[2][proofSize/2];
signal input nonExistenceKey[2];

signal output blinder;
signal output nullifier;
signal output C1[2];
signal output C2[2];

// pk = skG
component pbk1 = BabyPbk();
pbk1.in <== sk1;

signal publicKey1[2];
publicKey1[0] <== pbk1.Ax;
publicKey1[1] <== pbk1.Ay;

component pbk2 = BabyPbk();
pbk2.in <== sk2;

signal publicKey2[2];
publicKey2[0] <== pbk2.Ax;
publicKey2[1] <== pbk2.Ay;

component keyHash1 = Poseidon(3);
keyHash1.in[0] <== publicKey1[0];
keyHash1.in[1] <== publicKey1[1];
keyHash1.in[2] <== 1;
keyHash1.dummy <== 0;

signal key1;
key1 <== keyHash1.out;

component cmt1 = CartesianMerkleTree(proofSize);
cmt1.root <== cmtRoot;
cmt1.siblings <== siblings[0];
cmt1.siblingsLength <== siblingsLength[0];
cmt1.directionBits <== directionBits[0];
cmt1.key <== key1;
cmt1.nonExistenceKey <== nonExistenceKey[0];
cmt1.isExclusion <== 0;
cmt1.dummy <== 0;

component keyHash2 = Poseidon(3);
keyHash2.in[0] <== publicKey2[0];
keyHash2.in[1] <== publicKey2[1];
keyHash2.in[2] <== 2;
keyHash2.dummy <== 0;

signal key2;
key2 <== keyHash2.out;

component cmt2 = CartesianMerkleTree(proofSize);
cmt2.root <== cmtRoot;
cmt2.siblings <== siblings[1];
cmt2.siblingsLength <== siblingsLength[1];
cmt2.directionBits <== directionBits[1];
cmt2.key <== key2;
cmt2.nonExistenceKey <== nonExistenceKey[1];
cmt2.isExclusion <== 0;
cmt2.dummy <== 0;

// blinder check
component blinderHash = Poseidon(2);
blinderHash.in[0] <== sk1;
blinderHash.in[1] <== proposalId;
blinderHash.dummy <== 0;

blinder <== blinderHash.out;

// sk2 nullifier check
component nullifierHash = Poseidon(1);
nullifierHash.in[0] <== sk2;
nullifierHash.dummy <== 0;

nullifier <== nullifierHash.out;

// decryption key share calculation
component h1Hash = Poseidon(1);
h1Hash.in[0] <== challenge;
h1Hash.dummy <== 0;

signal h1;
h1 <== h1Hash.out;

component h2Hash = Poseidon(1);
h2Hash.in[0] <== h1;
h2Hash.dummy <== 0;

signal h2;
h2 <== h2Hash.out;

signal hpk1[2];
signal hpk2[2];

component h1Bits = Num2Bits(254);
h1Bits.in <== h1;

component pk1Mul = EscalarMulAny(254);
pk1Mul.p <== publicKey1;

var i;
for (i = 0; i < 254; i++) {
pk1Mul.e[i] <== h1Bits.out[i];
}

hpk1 <== pk1Mul.out;

component h2Bits = Num2Bits(254);
h2Bits.in <== h2;

component pk2Mul = EscalarMulAny(254);
pk2Mul.p <== publicKey2;

for (i = 0; i < 254; i++) {
pk2Mul.e[i] <== h2Bits.out[i];
}

hpk2 <== pk2Mul.out;

signal expectedEncryptionKeyShare[2];
component add = BabyAdd();
add.x1 <== hpk1[0];
add.y1 <== hpk1[1];
add.x2 <== hpk2[0];
add.y2 <== hpk2[1];

expectedEncryptionKeyShare[0] <== add.xout;
expectedEncryptionKeyShare[1] <== add.yout;

signal encryptionKeyShare[2];
component encKey = BabyPbk();
encKey.in <== decryptionKeyShare;

encryptionKeyShare[0] <== encKey.Ax;
encryptionKeyShare[1] <== encKey.Ay;

encryptionKeyShare[0] === expectedEncryptionKeyShare[0];
encryptionKeyShare[1] === expectedEncryptionKeyShare[1];

// check for the vote to be either G or inf
component voteChecker = IsGOrInf();
voteChecker.x <== vote[0];
voteChecker.y <== vote[1];

// vote ElGamal encryption
component elgamal = ElGamal();
elgamal.pk <== encryptionKey;
elgamal.M <== vote;
elgamal.nonce <== k;

C1 <== elgamal.C;
C2 <== elgamal.D;
}

component main {public [decryptionKeyShare, encryptionKey, challenge, proposalId, cmtRoot, decryptionKeyShare]} = Voting(20);
Loading