Awesome
Zero Knowledge Message Board
A public message board, hosted on a solidity contract, that uses ZK-SNARKS to allow a user to register their identity as a member of a group and post messages on behalf of the group without revealing their identity. Currently, this implementation is only semi-decentralized as it uses a central server to send the transactions on behalf of the user, but there are ways to change this with a little more effort.
Setup
Use Node v14. If you don't have it, do:
nvm install 14.15.3
npm -v # 14.15.3
Install dependencis
yarn
Run
Compile circuits and contracts
Get and link ptau files
curl https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_15.ptau --output ./pot15_final.ptau
curl https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_20.ptau --output ./pot20_final.ptau
mkdir ./circuits/pots/
ln ./pot15_final.ptau ./circuits/pots/pot15_final.ptau
ln ./pot20_final.ptau ./circuits/pots/pot20_final.ptau
Compile the circuits to create the witness and keys
yarn compile:dev hash-check 15
yarn compile:dev hash-check-bits 20
yarn compile:dev sig-check 20
Create the Verifier
library with the SNARK verification keys
yarn compile:verifier
Compile the solidity contracts
yarn compile:solidity
Deploy Contracts and run the client
Local
Deploy the contracts
yarn deploy:local
Run the client watcher and http server
yarn dev
Ropsten
Rebuild contracts with compile:circom instead of compile:dev
echo `beacon="<YOUR RANDOM STRING HERE>"` >> .env
yarn compile:circom hash-check 15
yarn compile:circom hash-check-bits 20
yarn compile:circom sig-check 20
Deploy the contracts
yarn deploy:ropsten
Run the client watcher and http server
yarn prod
View localhost:8080
Circuits
Circuit Name | Private Inputs | Public Inputs | Outputs | Description |
---|---|---|---|---|
hash-check | x | hash | out | Checks if MiMC(x) = hash ; outputs MiMC(x) |
hash-check-bits | x (256-bit list) | hash | out | Checks if MiMC(x) = hash ; outputs MiMC(x) |
sig-check | publicKey | hashes , sig , message | None | Checks eddsa_verify(publicKey, sig, message) == true ; checks MiMIC(publicKey) is in list hashes |
'hash-check'
Inputs | Private | Type | Description |
---|---|---|---|
'x' | Yes | 256-bit integer | MiMC hash pre-image |
'hash' | No | 256-bit integer | MiMC hash |
'hash-check-bits'
Inputs | Private | Type | Description |
---|---|---|---|
'x' | Yes | 256-bit array | MiMC hash pre-image |
'hash' | No | 256-bit integer | MiMC hash |
'sig-check'
Inputs | Private | Type | Description |
---|---|---|---|
'publicKey' | Yes | 256-bit | EdDSA public key |
'hashes' | No | [] | Registered public key hashes |
'sig' | No | 2-by-256-bit array | EdDSA signature |
'message' | No | 312-bit array | binary representation of the MiMC hash of the message |
Example use
import { babyJub, eddsa } from 'circomlib';
import mimc from 'client/utils/mimc';
function buffer2bits(buff) {
const res = [];
for (let i=0; i<buff.length; i++) {
for (let j=0; j<8; j++) {
if ((buff[i]>>j)&1) {
res.push('1');
} else {
res.push('0');
}
}
}
return res;
}
const message = mimc(1234).toString().padStart(78, '0');
const msg = Buffer.from(message, "hex");
const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex");
const pubKey = eddsa.prv2pub(prvKey);
const pPubKey = babyJub.packPoint(pubKey);
const signature = eddsa.sign(prvKey, msg);
const pSignature = eddsa.packSignature(signature);
const aBits = buffer2bits(pPubKey);
const rBits = buffer2bits(pSignature.slice(0, 32));
const sBits = buffer2bits(pSignature.slice(32, 64));
const msgBits = buffer2bits(msg);
const sig = [rBits, sBits];
const hash = mimc(...aBits).toString();
const inputs = { publicKey: aBits, hashes: [hash], signature: sig, message: msgBits };
Add a circuit
Make a new directory in /circuits/
with the name of the circuit.
Copy the pot15_final.ptau
file from /circuits/hash
into the new directory.
In the new directory, create circuit.circom
and input.json
with the test inputs.
Run npm run compile CIRCUIT_NAME
, if that doesn't work npm run compile CIRCUIT_NAME 20
. If it complains about an env file in development, use compile-dev
instead of compile
.
If the circuit and input produce a valid proof you should see OK
.
The compiled circuit.wasm
file will be in /circuits/circuits-compiled/CIRCUIT_NAME
.
The proof key circuit_final.zkey
and the verification key verification_key.json
will be found in /circuits/keys/CIRCUIT_NAME
.
An example of creating and verifying a new proof in Node can be found in /client/prover.js
.
Run ./solbuilder.js
to generate Solidity from the contracts.
How it works
- User generates EdDSA key pair
(pk, sk)
and sends the MiMC hash to the serverH(pk)
. - To vote, the user first proves they're registered to the poll by sending a snark proving that they have a public key
pk
to one of the recorded MiMC hashes. - Then, the user sends an EdDSA signature of the vote and a snark proving that the signature was produced by the private key associated with the public key they just verified.
Valid sig = EdDSA(sk, msg)
where pk = private2public(sk)
and H(pk)
is a recorded hash