Skip to content

Commit

Permalink
Merge pull request #5 from EYBlockchain/westlad/verify
Browse files Browse the repository at this point in the history
feat: add verify capability and update zokrates
  • Loading branch information
Westlad authored Mar 2, 2021
2 parents 2669495 + 2f5eec4 commit 148de54
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 45 deletions.
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
FROM docker.pkg.github.com/eyblockchain/zokrates-zexe/zokrates_zexe:a6bfd3bf3fa81fc4b4ced9d6b4a998cb240b21fe as builder
FROM zokrates/zokrates:0.6.3 as builder

# Actual application (for testing purposes)
FROM node:12
RUN mkdir /app
WORKDIR /app
COPY ./package.json ./package-lock.json ./
COPY --from=builder /home/zokrates/zokrates /app/zokrates
COPY --from=builder /home/zokrates/.zokrates* /app/stdlib
COPY --from=builder /home/zokrates/.zokrates/bin/zokrates /app/
COPY --from=builder /home/zokrates/.zokrates/stdlib/ /app/
ENV ZOKRATES_HOME='/app'
RUN npm ci
RUN npm install jest --g
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ const computeWitness = require('./lib/compute-witness');
const extractVk = require('./lib/extract-vk');
const generateProof = require('./lib/generate-proof');
const setup = require('./lib/setup');
const verify = require('./lib/verify');

module.exports = {
compile,
computeWitness,
extractVk,
generateProof,
setup,
verify,
};
67 changes: 32 additions & 35 deletions lib/__tests__/ordered-tests.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const fs = require('fs');
const deleteFile = require('../utils/utils');
const jsonfile = require('jsonfile');

const deleteFile = require('../utils/utils');
const compile = require('../compile');
const setup = require('../setup');
const computeWitness = require('../compute-witness');
const generateProof = require('../generate-proof');
const extractVk = require('../extract-vk');
const verify = require('../verify');

describe('Compile tests', () => {
it('should throw an error if file path does not exists', async () => {
Expand All @@ -19,13 +21,13 @@ describe('Compile tests', () => {
});

it('should create the given output file', async () => {
await compile('./code/test.zok', './code/test/', 'test-compiled', 'bls12_377');
await compile('./code/test.zok', './code/test/', 'test-compiled', 'bn128');
expect(fs.existsSync('./code/test/test-compiled')).toBe(true);
deleteFile('./code/test/test-compiled');
});

it('should return a string given a verbose flag', async () => {
const output = await compile('./code/test.zok', './code/test/', 'test-compiled', 'bls12_377', {
const output = await compile('./code/test.zok', './code/test/', 'test-compiled', 'bn128', {
verbose: true,
});
expect(typeof output).toBe('string');
Expand All @@ -35,34 +37,34 @@ describe('Setup tests', () => {
it('should throw an error if file path does not exists', async () => {
expect.assertions(1);
await expect(
setup('./foo', './code/test/', 'gm17', 'zexe', 'test-vk', 'test-pk'),
setup('./foo', './code/test/', 'gm17', 'ark', 'test-vk', 'test-pk'),
).rejects.toThrow(Error);
});

it('should throw an error if input file ends with .zok', async () => {
expect.assertions(1);
await expect(
setup('./code/test/test-compiled.zok', './code/test/', 'gm17', 'zexe', 'test-vk', 'test-pk'),
setup('./code/test/test-compiled.zok', './code/test/', 'gm17', 'ark', 'test-vk', 'test-pk'),
).rejects.toThrow(Error);
});

it('should create the given output file with default names', async () => {
expect.assertions(2);
await setup('./code/test/test-compiled', './code/test/', 'gm17', 'zexe', undefined, undefined);
await setup('./code/test/test-compiled', './code/test/', 'gm17', 'ark', undefined, undefined);
expect(fs.existsSync('./code/test/verification.key')).toBe(true);
expect(fs.existsSync('./code/test/proving.key')).toBe(true);
deleteFile('./code/test/verification.key');
deleteFile('./code/test/proving.key');
});

it('should allow GM17 proving scheme with Zexe backend', async () => {
it('should allow GM17 proving scheme with ark backend', async () => {
expect.assertions(1);
const basePath = './code/test/';
const vkPath = `${basePath}test-vk.key`;
const pkPath = `${basePath}test-pk.key`;

// eslint-disable-next-line no-await-in-loop
await setup('./code/test/test-compiled', './code/test/', 'gm17', 'zexe', 'test-vk', 'test-pk');
await setup('./code/test/test-compiled', './code/test/', 'gm17', 'ark', 'test-vk', 'test-pk');
expect(fs.existsSync(vkPath) && fs.existsSync(pkPath)).toBe(true);
deleteFile('./code/test/test-vk.key');
deleteFile('./code/test/test-pk.key');
Expand All @@ -73,7 +75,7 @@ describe('Setup tests', () => {
'./code/test/test-compiled',
'./code/test/',
'gm17',
'zexe',
'ark',
'test-vk',
'test-pk',
{
Expand All @@ -85,24 +87,15 @@ describe('Setup tests', () => {
});
describe('verifier key extraction tests', () => {
it('should return a json version of the verifier key', async () => {
expect.assertions(8);
expect.assertions(7);
const vk = extractVk('./code/test/test-vk.key');
expect(Object.keys(vk)).toEqual([
'h',
'g_alpha',
'h_beta',
'g_gamma',
'h_gamma',
'query',
'raw',
]);
expect(Object.keys(vk)).toEqual(['h', 'g_alpha', 'h_beta', 'g_gamma', 'h_gamma', 'query']);
expect(vk.h).toEqual(expect.anything());
expect(vk.g_alpha).toEqual(expect.anything());
expect(vk.h_beta).toEqual(expect.anything());
expect(vk.g_gamma).toEqual(expect.anything());
expect(vk.h_gamma).toEqual(expect.anything());
expect(vk.query).toEqual(expect.anything());
expect(vk.raw).toEqual(expect.anything());
});
it('should handle unknown vk filenames gracefully', async () => {
const vk = extractVk('./code/test/non-existent-filename.key');
Expand Down Expand Up @@ -158,7 +151,7 @@ describe('Generate proof tests', () => {
'./code/test/test-compiled',
'./code/test/test-witness',
'gm17',
'zexe',
'ark',
{
createFile: true,
directory: './code/',
Expand All @@ -168,18 +161,11 @@ describe('Generate proof tests', () => {
).rejects.toThrow(Error);

await expect(
generateProof(
'./code/test/test-pk.key',
'./foo',
'./code/test/test-witness',
'gm17',
'zexe',
{
createFile: true,
directory: './code/',
fileName: 'test-proof.json',
},
),
generateProof('./code/test/test-pk.key', './foo', './code/test/test-witness', 'gm17', 'ark', {
createFile: true,
directory: './code/',
fileName: 'test-proof.json',
}),
).rejects.toThrow(Error);
});

Expand All @@ -206,7 +192,7 @@ describe('Generate proof tests', () => {
'./code/test/test-compiled',
'./code/test/test-witness',
'gm17',
'zexe',
'ark',
{
createFile: true,
directory: './code/test/',
Expand All @@ -222,7 +208,7 @@ describe('Generate proof tests', () => {
'./code/test/test-compiled',
'./code/test/test-witness',
'gm17',
'zexe',
'ark',
{
createFile: true,
directory: './code/test/',
Expand All @@ -233,3 +219,14 @@ describe('Generate proof tests', () => {
deleteFile('./code/test/proof.json');
});
});
describe('Verification tests', () => {
it('Should verify true, when the proof and verifier are correct', async () => {
// get data from existing files to act as inputs for the call
const [proof, vk] = await Promise.all([
jsonfile.readFile('./code/test/test-proof.json'),
jsonfile.readFile('./code/test/test-vk.key'),
]);
const response = await verify(vk, proof, 'gm17', 'ark', 'bn128');
expect(response).toBe(true);
});
});
4 changes: 2 additions & 2 deletions lib/generate-proof.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const { spawn } = childProcess;
* @param {String} provingKeyPath - Path to proving key
* @param {String} codePath - Path to code file (Result of compile that doesn't end in .code)
* @param {String} provingScheme - Available options are 'g16', 'pghr13', 'gm17'
* @param {String} backEnd - Available options are 'libsnark', 'bellman', 'zexe'
* @param {String} backEnd - Available options are 'libsnark', 'bellman', 'ark'
* @param {Object} [options] - Options for output
* @param {Boolean} options.createFile - Whether or not to output a json file
* @param {String} [options.directory=./] - Directory to output files in
Expand All @@ -35,7 +35,7 @@ async function generateProof(
codePath,
witnessPath,
provingScheme = 'gm17',
backend = 'zexe',
backend = 'ark',
options = {},
) {
if (!fs.existsSync(codePath)) {
Expand Down
4 changes: 2 additions & 2 deletions lib/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ const { spawn } = childProcess;
* @param {String} codePath - Path of code file to compile
* @param {String} outputPath - Directory to output, defaults to current directory
* @param {String} provingScheme - Available options are g16, pghr13, gm17
* @param {String} backEnd - Available options are 'libsnark', 'bellman', 'zexe'
* @param {String} backEnd - Available options are 'libsnark', 'bellman', 'ark'
* @param {String} vkName - name of verification key file, defaults to verification.key
* @param {String} pkName - name of proving key file, defaults to proving.key
*/
async function setup(
codePath,
outputPath = './',
provingScheme = 'gm17',
backend = 'zexe',
backend = 'ark',
vkName = 'verification.key',
pkName = 'proving.key',
options = {},
Expand Down
84 changes: 84 additions & 0 deletions lib/verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const childProcess = require('child_process');
const jsonfile = require('jsonfile');
const deleteFile = require('./utils/utils');

const { spawn } = childProcess;
const { writeFile } = jsonfile;

/**
* Takes in a proof and a verification key and determines if the proof verifies.
*
* @example
* generateProof('./code/ft-mint/ft-mint-pk.key',
* './code/ft-mint/ft-mint-compiled',
* 'gm17',
* {
* createFile: true,
* directory: './code/ft-mint',
* fileName: 'ft-mint-proof.json',
* },
* );
*
* @param {String} provingKeyPath - Path to proving key
* @param {String} codePath - Path to code file (Result of compile that doesn't end in .code)
* @param {String} provingScheme - Available options are 'g16', 'pghr13', 'gm17'
* @param {String} backEnd - Available options are 'libsnark', 'bellman', 'ark'
* @param {Object} [options] - Options for output
* @param {Boolean} options.createFile - Whether or not to output a json file
* @param {String} [options.directory=./] - Directory to output files in
* @param {String} [options.fileName=proof.json] - Name of JSON proof file ()
* @returns {Object} JSON of the proof.
*/
async function verify(vk, proof, provingScheme = 'gm17', backend = 'ark', curve = 'bn128') {
// we've provided a json proof and a verifying key but Zokrates needs to read
// these from a file. Thus we should write them to temporary files.
const proofTempFile = '/tmp/proof.json';
const vkTempFile = '/tmp/verify.key';
await Promise.all([writeFile(vkTempFile, vk), writeFile(proofTempFile, proof)]);

const args = [
'verify',
'-v',
vkTempFile,
'-j',
proofTempFile,
'--proving-scheme',
provingScheme,
'--backend',
backend,
'--curve',
curve,
];

return new Promise((resolve, reject) => {
const zokrates = spawn('/app/zokrates', args, {
stdio: ['ignore', 'pipe', 'pipe'],
env: {
ZOKRATES_HOME: '/app/stdlib',
},
});

let output = '';
zokrates.stdout.on('data', data => {
output += data.toString('utf8');
});

zokrates.stderr.on('data', err => {
reject(new Error(`Verify failed: ${err}`));
});

zokrates.on('close', () => {
// we no longer need the temporary files
deleteFile(proofTempFile);
deleteFile(vkTempFile);
// ZoKrates sometimes outputs error through stdout instead of stderr,
// so we need to catch those errors manually.
if (output.includes('panicked')) reject(new Error(output.slice(output.indexOf('panicked'))));

if (output.includes('PASS')) resolve(true);
else resolve(false);
});
});
}

module.exports = verify;
17 changes: 15 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
"jest": "^24.9.0",
"markdownlint-cli": "^0.18.0"
},
"dependencies": {},
"dependencies": {
"jsonfile": "^6.1.0"
},
"publishConfig": {
"registry": "https://npm.pkg.github.com"
},
Expand Down

0 comments on commit 148de54

Please sign in to comment.