Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to Buses #116

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
32 changes: 31 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,35 @@
"yaml.format.singleQuote": false,
"prettier.enable": true,
"prettier.jsxSingleQuote": false,
"yaml.format.enable": true
"yaml.format.enable": true,
"cSpell.words": [
"babyjub",
"Babyjubjub",
"circom",
"circomlib",
"circomlibjs",
"Commonlib",
"ECDH",
"fflonk",
"Groth",
"iden",
"izeto",
"Jubjub",
"keypair",
"maci",
"merkletree",
"ptau",
"ptaus",
"rapidsnark",
"snarkjs",
"solidityverifier",
"tokenid",
"UTXO",
"UTXOs",
"UUPS",
"verificationkey",
"vkey",
"WTNS",
"zeto"
]
}
2 changes: 1 addition & 1 deletion zkp/circuits/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ When using the groth16 proving system, per-circuit set up ceremony must be condu

### Export the verification key

The verification key is used by verifier code (either offchain with a JS library or onchain with Solidity). This can be derived from the proving key above.
The verification key is used by verifier code (either off-chain with a JS library or onchain with Solidity). This can be derived from the proving key above.

```console
snarkjs zkey export verificationkey ~/proving-keys/CIRCUIT_FILE_NAME.zkey ~/proving-keys/CIRCUIT_FILE_NAME-vkey.json
Expand Down
16 changes: 6 additions & 10 deletions zkp/circuits/check_hashes_value.circom
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,22 @@
// limitations under the License.
pragma circom 2.2.1;

include "./lib/check-positive.circom";
include "./lib/check-hashes.circom";

template Zeto(nOutputs) {
template checkHashesValue(nOutputs) {
signal input outputCommitments[nOutputs];
signal input outputValues[nOutputs];
signal input outputSalts[nOutputs];
signal input outputOwnerPublicKeys[nOutputs][2];
input CommitmentInputs() outputCommitmentInputs[nOutputs];
signal output out;

CheckPositive(nOutputs)(outputValues <== outputValues);

CheckHashes(nOutputs)(commitments <== outputCommitments, values <== outputValues, salts <== outputSalts, ownerPublicKeys <== outputOwnerPublicKeys);
CheckPositiveValues(nOutputs)(commitmentInputs <== outputCommitmentInputs);
CheckHashes(nOutputs)(commitmentHashes <== outputCommitments, commitmentInputs <== outputCommitmentInputs);

// calculate the sum of output values and set to the output
var sumOutputs = 0;
for (var i = 0; i < nOutputs; i++) {
sumOutputs = sumOutputs + outputValues[i];
sumOutputs = sumOutputs + outputCommitmentInputs[i].value;
}
out <== sumOutputs;
}

component main {public [ outputCommitments ]} = Zeto(2);
component main {public [ outputCommitments ]} = checkHashesValue(2);
34 changes: 17 additions & 17 deletions zkp/circuits/gen-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,81 +4,81 @@
"anon": {
"ptau": "powersOfTau28_hez_final_13",
"batchPtau": "powersOfTau28_hez_final_15",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"anon_enc": {
"ptau": "powersOfTau28_hez_final_15",
"batchPtau": "powersOfTau28_hez_final_17",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"anon_nullifier": {
"ptau": "powersOfTau28_hez_final_17",
"batchPtau": "powersOfTau28_hez_final_19",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"anon_nullifier_kyc": {
"ptau": "powersOfTau28_hez_final_17",
"batchPtau": "powersOfTau28_hez_final_19",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"anon_enc_nullifier_non_repudiation": {
"ptau": "powersOfTau28_hez_final_17",
"batchPtau": "powersOfTau28_hez_final_19",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"anon_enc_nullifier": {
"ptau": "powersOfTau28_hez_final_17",
"batchPtau": "powersOfTau28_hez_final_19",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"anon_enc_nullifier_kyc": {
"ptau": "powersOfTau28_hez_final_17",
"batchPtau": "powersOfTau28_hez_final_20",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"nf_anon": {
"ptau": "powersOfTau28_hez_final_13",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"nf_anon_nullifier": {
"ptau": "powersOfTau28_hez_final_16",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_hashes_value": {
"ptau": "powersOfTau28_hez_final_11",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_inputs_outputs_value": {
"ptau": "powersOfTau28_hez_final_13",
"batchPtau": "powersOfTau28_hez_final_14",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_nullifiers_value": {
"ptau": "powersOfTau28_hez_final_17",
"batchPtau": "powersOfTau28_hez_final_19",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_utxos_owner": {
"ptau": "powersOfTau28_hez_final_13",
"batchPtau": "powersOfTau28_hez_final_14",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_utxos_nf_owner": {
"ptau": "powersOfTau28_hez_final_13",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_nullifiers_owner": {
"ptau": "powersOfTau28_hez_final_11",
"batchPtau": "powersOfTau28_hez_final_13",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_nullifiers_nf_owner": {
"ptau": "powersOfTau28_hez_final_11",
"skipSolidityGenaration": false
"skipSolidityGeneration": false
},
"check_nullifiers": {
"ptau": "powersOfTau28_hez_final_13",
"skipSolidityGenaration": true
"skipSolidityGeneration": true
}
}
}
14 changes: 7 additions & 7 deletions zkp/circuits/gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const ptauDownload = process.env.PTAU_DOWNLOAD_PATH || argv.ptauDownloadPath;
const specificCircuits = argv.c;
const verbose = argv.v;
const compileOnly = argv.compileOnly;
const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 4; // Default to compile 4 circuits in parallel
const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 4; // Default to compile 4 circuits in parallel

// check env vars
if (!circuitsRoot) {
Expand Down Expand Up @@ -105,7 +105,7 @@ const log = (circuit, message) => {
};

// main circuit process logic
const processCircuit = async (circuit, ptau, skipSolidityGenaration) => {
const processCircuit = async (circuit, ptau, skipSolidityGeneration) => {
const circomInput = path.join("./", `${circuit}.circom`);
const ptauFile = path.join(ptauDownload, `${ptau}.ptau`);
const zkeyOutput = path.join(provingKeysRoot, `${circuit}.zkey`);
Expand Down Expand Up @@ -197,7 +197,7 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => {
log(circuit, "verification key export error:\n" + vkErr);
}
}
if (skipSolidityGenaration) {
if (skipSolidityGeneration) {
log(circuit, `Skipping solidity verifier generation`);
return;
}
Expand Down Expand Up @@ -257,14 +257,14 @@ const run = async () => {

for (const [
circuit,
{ ptau, skipSolidityGenaration, batchPtau },
{ ptau, skipSolidityGeneration, batchPtau },
] of circuitsArray) {
if (onlyCircuits && !onlyCircuits.includes(circuit)) {
continue;
}

let snarkjsVersion;
// first check cirom version and snarkjs version matches the one in the package.json
// first check circom version and snarkjs version matches the one in the package.json
try {
const { stdout: circomVersion } = await execAsync("circom --version");
// Trigger error to get snarkjs version
Expand Down Expand Up @@ -316,7 +316,7 @@ const run = async () => {
process.exit(1);
}

const pcPromise = processCircuit(circuit, ptau, skipSolidityGenaration);
const pcPromise = processCircuit(circuit, ptau, skipSolidityGeneration);
activePromises.add(pcPromise);

if (activePromises.size >= parallelLimit) {
Expand All @@ -327,7 +327,7 @@ const run = async () => {
const pcBatchPromise = processCircuit(
circuit + "_batch",
batchPtau,
skipSolidityGenaration,
skipSolidityGeneration,
);
activePromises.add(pcBatchPromise);

Expand Down
29 changes: 22 additions & 7 deletions zkp/circuits/lib/check-hashes.circom
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,46 @@ pragma circom 2.2.1;
include "../node_modules/circomlib/circuits/poseidon.circom";
include "../node_modules/circomlib/circuits/comparators.circom";

bus CommitmentInputs(){
signal value;
signal salt;
signal ownerPublicKey[2];
}

template CheckPositiveValues(numInputs) {
input CommitmentInputs() commitmentInputs[numInputs];

// check that the output values are within the expected range. we don't allow negative values
for (var i = 0; i < numInputs; i++) {
var greaterEqThanZero;
greaterEqThanZero = GreaterEqThan(100)(in <== [commitmentInputs[i].value, 0]);
greaterEqThanZero === 1;
}
}

// CheckHashes is a circuit that checks the integrity of transactions of Fungible Tokens
// - check that the commitments are the hash of the values, salts and owner public keys
//
// commitment = hash(value, salt, owner public key)
//
template CheckHashes(numInputs) {
signal input commitments[numInputs];
signal input values[numInputs];
signal input salts[numInputs];
signal input ownerPublicKeys[numInputs][2];
signal input commitmentHashes[numInputs];
input CommitmentInputs() commitmentInputs[numInputs];

// hash the input values
for (var i = 0; i < numInputs; i++) {
// perform the hash calculation even though they are not needed when the input
// commitment at the current index is 0; this is because in zkp circuits we
// must always perform the same computation (have the the same constraints)
var calculatedHash;
calculatedHash = Poseidon(4)([values[i], salts[i], ownerPublicKeys[i][0], ownerPublicKeys[i][1]]);
calculatedHash = Poseidon(4)([commitmentInputs[i].value, commitmentInputs[i].salt, commitmentInputs[i].ownerPublicKey[0], commitmentInputs[i].ownerPublicKey[1]]);

// check that the input commitments match the calculated hashes
var isCommitmentZero;
isCommitmentZero = IsZero()(in <== commitments[i]);
isCommitmentZero = IsZero()(in <== commitmentHashes[i]);

var isHashEqual;
isHashEqual = IsEqual()(in <== [commitments[i], (1 - isCommitmentZero) * calculatedHash /* ensure when commitment is 0, compare with 0 */]);
isHashEqual = IsEqual()(in <== [commitmentHashes[i], (1 - isCommitmentZero) * calculatedHash /* ensure when commitment is 0, compare with 0 */]);

isHashEqual === 1;
}
Expand Down
63 changes: 45 additions & 18 deletions zkp/js/integration-test/check_nullifiers_owner.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const { expect } = require('chai');
const { groth16 } = require('snarkjs');
const { genKeypair, formatPrivKeyForBabyJub } = require('maci-crypto');
const { Poseidon, newSalt, loadCircuit } = require('../index.js');
const { loadProvingKeys } = require('./utils.js');
const { expect } = require("chai");
const { groth16 } = require("snarkjs");
const { genKeypair, formatPrivKeyForBabyJub } = require("maci-crypto");
const { Poseidon, newSalt, loadCircuit } = require("../index.js");
const { loadProvingKeys } = require("./utils.js");

const poseidonHash3 = Poseidon.poseidon3;

describe('check_nullifiers_owner circuit tests', () => {
describe("check_nullifiers_owner circuit tests", () => {
let circuit, provingKeyFile, verificationKey;

const Alice = {};
let senderPrivateKey;

before(async () => {
circuit = await loadCircuit('check_nullifiers_owner');
({ provingKeyFile, verificationKey } = loadProvingKeys('check_nullifiers_owner'));
circuit = await loadCircuit("check_nullifiers_owner");
({ provingKeyFile, verificationKey } = loadProvingKeys(
"check_nullifiers_owner",
));

let keypair = genKeypair();
Alice.privKey = keypair.privKey;
Alice.pubKey = keypair.pubKey;
senderPrivateKey = formatPrivKeyForBabyJub(Alice.privKey);
});

it('should generate a valid proof that can be verified successfully and fail when public signals are tampered', async () => {
it("should generate a valid proof that can be verified successfully and fail when public signals are tampered", async () => {
const values = [15, 100];

// create two input UTXOs, each has their own salt, but same owner
Expand All @@ -47,8 +49,16 @@ describe('check_nullifiers_owner circuit tests', () => {
const salt2 = newSalt();

// create the nullifiers for the input UTXOs
const nullifier1 = poseidonHash3([BigInt(values[0]), salt1, senderPrivateKey]);
const nullifier2 = poseidonHash3([BigInt(values[1]), salt2, senderPrivateKey]);
const nullifier1 = poseidonHash3([
BigInt(values[0]),
salt1,
senderPrivateKey,
]);
const nullifier2 = poseidonHash3([
BigInt(values[1]),
salt2,
senderPrivateKey,
]);
const nullifiers = [nullifier1, nullifier2];

const startTime = Date.now();
Expand All @@ -59,18 +69,35 @@ describe('check_nullifiers_owner circuit tests', () => {
salts: [salt1, salt2],
ownerPrivateKey: senderPrivateKey,
},
true
true,
);

const { proof, publicSignals } = await groth16.prove(provingKeyFile, witness);
console.log('Proving time: ', (Date.now() - startTime) / 1000, 's');
const { proof, publicSignals } = await groth16.prove(
provingKeyFile,
witness,
);
console.log("Proving time: ", (Date.now() - startTime) / 1000, "s");

let verifyResult = await groth16.verify(verificationKey, publicSignals, proof);
let verifyResult = await groth16.verify(
verificationKey,
publicSignals,
proof,
);
expect(verifyResult).to.be.true;
const tamperedNullifier = poseidonHash3([BigInt(values[0] + 1), salt1, senderPrivateKey]);
let tamperedPublicSignals = publicSignals.map((ps) => (ps.toString() === nullifiers[0].toString() ? tamperedNullifier : ps));
const tamperedNullifier = poseidonHash3([
BigInt(values[0] + 1),
salt1,
senderPrivateKey,
]);
let tamperedPublicSignals = publicSignals.map((ps) =>
ps.toString() === nullifiers[0].toString() ? tamperedNullifier : ps,
);

verifyResult = await groth16.verify(verificationKey, tamperedPublicSignals, proof);
verifyResult = await groth16.verify(
verificationKey,
tamperedPublicSignals,
proof,
);
expect(verifyResult).to.be.false;
}).timeout(600000);
});
Loading
Loading