├── .eslintrc.js ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── README.md ├── cli.ts ├── dist ├── cli.d.ts ├── cli.js ├── cli.js.map ├── index.d.ts ├── index.js ├── index.js.map └── src │ ├── checker.d.ts │ ├── checker.js │ ├── checker.js.map │ ├── crypto.d.ts │ ├── crypto.js │ ├── crypto.js.map │ ├── math.d.ts │ ├── math.js │ ├── math.js.map │ ├── utils.d.ts │ ├── utils.js │ ├── utils.js.map │ ├── witness_generator.d.ts │ ├── witness_generator.js │ └── witness_generator.js.map ├── index.ts ├── package-lock.json ├── package.json ├── src ├── checker.ts ├── crypto.ts ├── utils.ts └── witness_generator.ts ├── testdata ├── num2bits │ ├── circuit.circom │ └── data │ │ └── case01 │ │ ├── input.json │ │ └── output.json └── num2bits_err │ ├── circuit.circom │ └── data │ └── err_case01 │ └── input.json ├── tsconfig.json └── win32 ├── README.md └── runtime ├── .gitignore ├── README.md ├── fr.hpp └── main.cpp /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 5 | 'prettier', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier 6 | 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 7 | ], 8 | parserOptions: { 9 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 10 | sourceType: 'module', // Allows for the use of imports 11 | }, 12 | rules: { 13 | '@typescript-eslint/no-var-requires': 'off', 14 | '@typescript-eslint/no-use-before-define': 'off', 15 | '@typescript-eslint/explicit-function-return-type': 'off', 16 | '@typescript-eslint/no-explicit-any': 'off', 17 | '@typescript-eslint/explicit-module-boundary-types': 'off', 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | calcwit.cpp 3 | calcwit.hpp 4 | circom.hpp 5 | circuit 6 | circuit.c 7 | circuit.cpp 8 | circuit.fast 9 | circuit.fast.dat 10 | circuit.dat 11 | circuit.r1cs 12 | circuit.sym 13 | circuit.wasm 14 | circuit.wtns 15 | srcs.sum 16 | witness.wtns 17 | witness.json 18 | fr.asm 19 | fr.cpp 20 | fr.hpp 21 | fr.o 22 | main.cpp 23 | utils.cpp 24 | utils.hpp 25 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v15.11.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | dist/ 3 | 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "singleQuote": true, 4 | "printWidth": 140, 5 | "arrowParens": "avoid" 6 | } 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-buster 2 | WORKDIR /data 3 | RUN apt update && apt install -y libgmp-dev nlohmann-json3-dev nasm g++ 4 | RUN rm -rf /var/lib/apt/lists/* 5 | COPY package.json package-lock.json /data 6 | COPY dist /data/dist 7 | RUN npm install . 8 | ENTRYPOINT ["node", "./dist/cli.js"] 9 | CMD ["--version"] 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # snarkit 2 | 3 | A toolkit to compile and debug circom circuit. 4 | 5 | # Features 6 | 7 | ### Better support for huge circuits 8 | 9 | `snarkit` supports both wasm witness generator and native(cpp) witness generator. 10 | So compared to the `snarkjs` official tool, `snarkit` is more friendly for developing huge circuits by using native backend. 11 | 12 | ### Better error detection 13 | 14 | `snarkit` can print very helpful error messages when the circuit code goes wrong. It can display the code line number and the component/signals related to the error, so we can detect the reason for the error quickly and fix it. Example: 15 | 16 | ``` 17 | # display incorrect component and signals 18 | $ snarkit check ./testdata/num2bits_err/ 19 | 20 | Invalid constraint: 21 | 0 * 0 != ((-1)*signal1 + 1*signal2 + 2*signal3 + 4*signal4 + 8*signal5 + 16*signal6 + 32*signal7 + 64*signal8 + 128*signal9) 22 | Related signals: 23 | signal1: main.num, value: 3 24 | signal2: main.bits[0], value: 1 25 | signal3: main.bits[1], value: 1 26 | signal4: main.bits[2], value: 1 27 | signal5: main.bits[3], value: 0 28 | signal6: main.bits[4], value: 0 29 | signal7: main.bits[5], value: 0 30 | signal8: main.bits[6], value: 0 31 | signal9: main.bits[7], value: 0 32 | please check your circuit and input 33 | 34 | # display incorrect code line number 35 | $ snarkit check ./testdata/num2bits_err/ --sanity_check 36 | 37 | Constraint doesn't match, /home/ubuntu/repos/snarkit/testdata/num2bits_err/circuit.circom:11:4: 3 != 7circuit: /home/ubuntu/repos/snarkit/testdata/num2bits_err/calcwit.cpp:201: void Circom_CalcWit::checkConstraint(int, PFrElement, PFrElement, const char*): Assertion `false' failed. 38 | Aborted (core dumped) 39 | ``` 40 | 41 | # Example 42 | 43 | The following demos how to test a circuit with given inputs/outputs. 44 | 45 | ``` 46 | $ npm install snarkit 47 | 48 | # first, you should prepare the circuit and input/output as the following structure 49 | # all the input.json/output.json pair inside data/*** folder will be tested 50 | # output.json can be an empty json file if you don't need to test against any circuit outputs. 51 | $ find num2bits/ 52 | num2bits/ 53 | num2bits/data 54 | num2bits/data/case01 55 | num2bits/data/case01/output.json 56 | num2bits/data/case01/input.json 57 | num2bits/circuit.circom 58 | 59 | # Snarkit has two backend: wasm and native(cpp). Only native backend can process huge circuits, you have to install some dependencies first before using it. 60 | 61 | # use wasm backend 62 | # compile the circuit 63 | $ npx snarkit compile num2bits --backend wasm 64 | # test the circuit 65 | $ npx snarkit check num2bits --backend wasm 66 | 67 | # use native backend 68 | # install deps 69 | $ sudo apt install nlohmann-json3-dev nasm g++ libgmp-dev 70 | # compile the circuit 71 | $ npx snarkit compile num2bits --backend native 72 | # test the circuit 73 | $ npx snarkit check num2bits --backend native 74 | ``` 75 | 76 | # Misc 77 | If you encounter `FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory` you can try increasing `max-old-space-size` by setting `NODE_ARGS` ENV to fully utilize your RAM. For example, 78 | ``` 79 | $ export NODE_ARGS='--max-old-space-size=16384' 80 | $ npx snarkit compile num2bits --backend native -f 81 | ``` 82 | If still encountering error, then you should try compiling it in a machine with larger RAM. 83 | -------------------------------------------------------------------------------- /cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { compileCircuitDir, testCircuitDir } from './index'; 3 | import { Command } from 'commander'; 4 | 5 | async function main() { 6 | try { 7 | const program = new Command(); 8 | program.version('0.0.11'); 9 | 10 | program 11 | .command('compile ') 12 | .description('compile a circom circuit dir') 13 | .option('-f, --force_recompile', 'ignore compiled files', false) 14 | .option('-s, --sanity_check', 'check constraints when generate witness', false) 15 | .option('-v, --verbose', 'print verbose log', false) 16 | .option('-b, --backend ', 'native|wasm|auto', 'auto') 17 | .action(async (circuit_dir, options) => { 18 | await compileCircuitDir(circuit_dir, { 19 | alwaysRecompile: options.force_recompile, 20 | verbose: options.verbose, 21 | backend: options.backend, 22 | sanityCheck: options.sanity_check, 23 | }); 24 | }); 25 | 26 | program 27 | .command('check ') 28 | .alias('test') 29 | .option('-d, --data_dir ', 'all input.json/output.json inside this dir will be tested', '') 30 | .option('-f, --force_recompile', 'ignore compiled files', false) 31 | .option('-s, --sanity_check', 'check constraints when generate witness', false) 32 | .option('-v, --verbose', 'print verbose log', false) 33 | .option('-b, --backend ', 'native|wasm|auto', 'auto') 34 | .option('-w, --witness_type ', 'bin or text', 'text') 35 | .description('test a circom circuit with given inputs/outputs') 36 | .action(async (circuit_dir, options) => { 37 | await testCircuitDir(circuit_dir, options.data_dir, { 38 | alwaysRecompile: options.force_recompile, 39 | verbose: options.verbose, 40 | backend: options.backend, 41 | witnessFileType: options.witness_type, 42 | sanityCheck: options.sanity_check, 43 | }); 44 | }); 45 | 46 | await program.parseAsync(process.argv); 47 | } catch (error) { 48 | console.error('Error:', error); 49 | process.exit(1); 50 | } 51 | } 52 | main(); 53 | -------------------------------------------------------------------------------- /dist/cli.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | export {}; 3 | -------------------------------------------------------------------------------- /dist/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | const index_1 = require("./index"); 5 | const commander_1 = require("commander"); 6 | async function main() { 7 | try { 8 | const program = new commander_1.Command(); 9 | program.version('0.0.11'); 10 | program 11 | .command('compile ') 12 | .description('compile a circom circuit dir') 13 | .option('-f, --force_recompile', 'ignore compiled files', false) 14 | .option('-s, --sanity_check', 'check constraints when generate witness', false) 15 | .option('-v, --verbose', 'print verbose log', false) 16 | .option('-b, --backend ', 'native|wasm|auto', 'auto') 17 | .action(async (circuit_dir, options) => { 18 | await (0, index_1.compileCircuitDir)(circuit_dir, { 19 | alwaysRecompile: options.force_recompile, 20 | verbose: options.verbose, 21 | backend: options.backend, 22 | sanityCheck: options.sanity_check, 23 | }); 24 | }); 25 | program 26 | .command('check ') 27 | .alias('test') 28 | .option('-d, --data_dir ', 'all input.json/output.json inside this dir will be tested', '') 29 | .option('-f, --force_recompile', 'ignore compiled files', false) 30 | .option('-s, --sanity_check', 'check constraints when generate witness', false) 31 | .option('-v, --verbose', 'print verbose log', false) 32 | .option('-b, --backend ', 'native|wasm|auto', 'auto') 33 | .option('-w, --witness_type ', 'bin or text', 'text') 34 | .description('test a circom circuit with given inputs/outputs') 35 | .action(async (circuit_dir, options) => { 36 | await (0, index_1.testCircuitDir)(circuit_dir, options.data_dir, { 37 | alwaysRecompile: options.force_recompile, 38 | verbose: options.verbose, 39 | backend: options.backend, 40 | witnessFileType: options.witness_type, 41 | sanityCheck: options.sanity_check, 42 | }); 43 | }); 44 | await program.parseAsync(process.argv); 45 | } 46 | catch (error) { 47 | console.error('Error:', error); 48 | process.exit(1); 49 | } 50 | } 51 | main(); 52 | //# sourceMappingURL=cli.js.map -------------------------------------------------------------------------------- /dist/cli.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"cli.js","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";;;AACA,mCAA4D;AAC5D,yCAAoC;AAEpC,KAAK,UAAU,IAAI;IACjB,IAAI;QACF,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;QAC9B,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE1B,OAAO;aACJ,OAAO,CAAC,uBAAuB,CAAC;aAChC,WAAW,CAAC,8BAA8B,CAAC;aAC3C,MAAM,CAAC,uBAAuB,EAAE,uBAAuB,EAAE,KAAK,CAAC;aAC/D,MAAM,CAAC,oBAAoB,EAAE,yCAAyC,EAAE,KAAK,CAAC;aAC9E,MAAM,CAAC,eAAe,EAAE,mBAAmB,EAAE,KAAK,CAAC;aACnD,MAAM,CAAC,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,CAAC;aAC5D,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE;YACrC,MAAM,IAAA,yBAAiB,EAAC,WAAW,EAAE;gBACnC,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,WAAW,EAAE,OAAO,CAAC,YAAY;aAClC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEL,OAAO;aACJ,OAAO,CAAC,qBAAqB,CAAC;aAC9B,KAAK,CAAC,MAAM,CAAC;aACb,MAAM,CAAC,yBAAyB,EAAE,2DAA2D,EAAE,EAAE,CAAC;aAClG,MAAM,CAAC,uBAAuB,EAAE,uBAAuB,EAAE,KAAK,CAAC;aAC/D,MAAM,CAAC,oBAAoB,EAAE,yCAAyC,EAAE,KAAK,CAAC;aAC9E,MAAM,CAAC,eAAe,EAAE,mBAAmB,EAAE,KAAK,CAAC;aACnD,MAAM,CAAC,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,CAAC;aAC5D,MAAM,CAAC,6BAA6B,EAAE,aAAa,EAAE,MAAM,CAAC;aAC5D,WAAW,CAAC,iDAAiD,CAAC;aAC9D,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE;YACrC,MAAM,IAAA,sBAAc,EAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,EAAE;gBAClD,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,eAAe,EAAE,OAAO,CAAC,YAAY;gBACrC,WAAW,EAAE,OAAO,CAAC,YAAY;aAClC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEL,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KACxC;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;KACjB;AACH,CAAC;AACD,IAAI,EAAE,CAAC"} -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as utils from './src/utils'; 2 | declare function compileCircuitDir(circuitDir: any, options: any): Promise; 3 | declare function testCircuitDir(circuitDir: any, dataDir: any, options: any): Promise; 4 | export { compileCircuitDir, testCircuitDir, utils }; 5 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.utils = exports.testCircuitDir = exports.compileCircuitDir = void 0; 4 | const witness_generator_1 = require("./src/witness_generator"); 5 | const checker_1 = require("./src/checker"); 6 | const path = require("path"); 7 | const fs = require("fs"); 8 | const utils = require("./src/utils"); 9 | exports.utils = utils; 10 | const walkSync = require('walk-sync'); 11 | async function compileCircuitDir(circuitDir, options) { 12 | const circuitName = path.basename(circuitDir); 13 | let witnessGenerator = new witness_generator_1.WitnessGenerator(circuitName, options); 14 | await witnessGenerator.compile(circuitDir); 15 | } 16 | exports.compileCircuitDir = compileCircuitDir; 17 | async function testCircuitDir(circuitDir, dataDir, options) { 18 | // make sure the circuit is compiled 19 | const circuitName = path.basename(circuitDir); 20 | let witnessGenerator = new witness_generator_1.WitnessGenerator(circuitName, options); 21 | const { r1csFilepath, symFilepath } = await witnessGenerator.compile(circuitDir); 22 | const checker = new checker_1.Checker(r1csFilepath, symFilepath); 23 | if (dataDir == null || dataDir == '') { 24 | dataDir = circuitDir; 25 | } 26 | for (const input of walkSync(path.resolve(dataDir), { includeBasePath: true, globs: ['**/input.json'] })) { 27 | const testCaseDir = path.normalize(path.dirname(input)); 28 | //const testCaseName = path.basename(testCaseDir) 29 | console.log('\ntest', testCaseDir); 30 | const inputFile = path.join(testCaseDir, 'input.json'); 31 | const witnessFileType = options.witnessFileType == 'bin' || options.witnessFileType == 'wtns' ? 'wtns' : 'json'; 32 | const witnessFile = path.join(testCaseDir, 'witness.' + witnessFileType); 33 | const expectedOutputFile = path.join(testCaseDir, 'output.json'); 34 | await witnessGenerator.generateWitness(inputFile, witnessFile); 35 | if (!options.sanityCheck || fs.existsSync(expectedOutputFile)) { 36 | await checker.checkConstraintsAndOutput(witnessFile, expectedOutputFile); 37 | } 38 | console.log('\ntest', testCaseDir, 'done'); 39 | } 40 | } 41 | exports.testCircuitDir = testCircuitDir; 42 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;AAAA,+DAA2D;AAC3D,2CAAwC;AACxC,6BAA6B;AAC7B,yBAAyB;AACzB,qCAAqC;AAoCO,sBAAK;AAnCjD,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;AAEtC,KAAK,UAAU,iBAAiB,CAAC,UAAU,EAAE,OAAO;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,gBAAgB,GAAG,IAAI,oCAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAClE,MAAM,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC7C,CAAC;AA6BQ,8CAAiB;AA3B1B,KAAK,UAAU,cAAc,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO;IACxD,oCAAoC;IACpC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,gBAAgB,GAAG,IAAI,oCAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAClE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAEjF,MAAM,OAAO,GAAG,IAAI,iBAAO,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAEvD,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,IAAI,EAAE,EAAE;QACpC,OAAO,GAAG,UAAU,CAAC;KACtB;IACD,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE;QACxG,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QACxD,iDAAiD;QACjD,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QACvD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,KAAK,IAAI,OAAO,CAAC,eAAe,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAChH,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,eAAe,CAAC,CAAC;QACzE,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QACjE,MAAM,gBAAgB,CAAC,eAAe,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE;YAC7D,MAAM,OAAO,CAAC,yBAAyB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;SAC1E;QACD,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;KAC5C;AACH,CAAC;AAE2B,wCAAc"} -------------------------------------------------------------------------------- /dist/src/checker.d.ts: -------------------------------------------------------------------------------- 1 | declare class Checker { 2 | r1csFilepath: string; 3 | symFilepath: string; 4 | r1cs: any; 5 | symbols: any; 6 | signals: any; 7 | constructor(r1csFilepath: any, symFilepath: any); 8 | load(): Promise; 9 | checkConstraintsAndOutput(witnessFilePath: any, expectedOutputFile: any): Promise; 10 | } 11 | export { Checker }; 12 | -------------------------------------------------------------------------------- /dist/src/checker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.Checker = void 0; 4 | const readR1cs = require('r1csfile').load; 5 | const { ZqField, utils: ffutils } = require('ffjavascript'); 6 | const { assert } = require('chai'); 7 | const fs = require("fs"); 8 | const binFileUtils = require('@iden3/binfileutils'); 9 | const crypto_1 = require("./crypto"); 10 | // copyed from snarkjs/src/wtns_utils.js 11 | async function readWtnsHeader(fd, sections) { 12 | await binFileUtils.startReadUniqueSection(fd, sections, 1); 13 | const n8 = await fd.readULE32(); 14 | const q = await binFileUtils.readBigInt(fd, n8); 15 | const nWitness = await fd.readULE32(); 16 | await binFileUtils.endReadSection(fd); 17 | return { n8, q, nWitness }; 18 | } 19 | async function readWtns(fileName) { 20 | const { fd, sections } = await binFileUtils.readBinFile(fileName, 'wtns', 2); 21 | const { n8, nWitness } = await readWtnsHeader(fd, sections); 22 | await binFileUtils.startReadUniqueSection(fd, sections, 2); 23 | const res = []; 24 | for (let i = 0; i < nWitness; i++) { 25 | const v = await binFileUtils.readBigInt(fd, n8); 26 | res.push(v); 27 | } 28 | await binFileUtils.endReadSection(fd); 29 | await fd.close(); 30 | return res; 31 | } 32 | // TOOD: type 33 | async function checkConstraints(F, constraints, witness, signals) { 34 | if (!constraints) { 35 | throw new Error('empty constraints'); 36 | } 37 | for (let i = 0; i < constraints.length; i++) { 38 | checkConstraint(constraints[i]); 39 | } 40 | function checkConstraint(constraint) { 41 | const a = evalLC(constraint[0]); 42 | const b = evalLC(constraint[1]); 43 | const c = evalLC(constraint[2]); 44 | if (!F.isZero(F.sub(F.mul(a, b), c))) { 45 | function displayLc(lc) { 46 | const entries = Object.entries(lc); 47 | if (entries.length == 0) { 48 | return '0'; 49 | } 50 | function displayField(x) { 51 | const f = BigInt(x); 52 | // display some field element as negative int for better reading 53 | if (f >= crypto_1.groupOrderPrime - 200n) { 54 | return `(-${(crypto_1.groupOrderPrime - f).toString()})`; 55 | } 56 | return f.toString(); 57 | } 58 | function displayMonomial(coef, signalIdx) { 59 | return `${displayField(coef)}*signal${signalIdx}`; 60 | } 61 | return '(' + entries.map(kv => displayMonomial(kv[1], kv[0])).join(' + ') + ')'; 62 | } 63 | console.log('\nInvalid constraint:'); 64 | console.log(`${displayLc(constraint[0])} * ${displayLc(constraint[1])} != ${displayLc(constraint[2])}`); 65 | console.log('Related signals:'); 66 | let sigs = new Set(); 67 | for (const c of constraint) { 68 | for (const s in c) { 69 | sigs.add(Number(s)); 70 | } 71 | } 72 | for (const s of sigs) { 73 | // signal 0 is 'one' 74 | if (s != 0) { 75 | console.log(`signal${s}: ${signals[s].join(' ')}, value: ${witness[s]}`); 76 | } 77 | } 78 | console.log('please check your circuit and input\n'); 79 | throw new Error("Constraint doesn't match"); 80 | } 81 | } 82 | function evalLC(lc) { 83 | let v = F.zero; 84 | for (let w in lc) { 85 | v = F.add(v, F.mul(BigInt(lc[w]), BigInt(witness[w]))); 86 | } 87 | return v; 88 | } 89 | } 90 | // TOOD: type 91 | async function assertOut(symbols, actualOut, expectedOut) { 92 | if (!symbols) { 93 | throw new Error('empty symbols'); 94 | } 95 | checkObject('main', expectedOut); 96 | function checkObject(prefix, eOut) { 97 | if (Array.isArray(eOut)) { 98 | for (let i = 0; i < eOut.length; i++) { 99 | checkObject(prefix + '[' + i + ']', eOut[i]); 100 | } 101 | } 102 | else if (typeof eOut == 'object' && eOut.constructor.name == 'Object') { 103 | for (let k in eOut) { 104 | checkObject(prefix + '.' + k, eOut[k]); 105 | } 106 | } 107 | else { 108 | if (typeof symbols[prefix] == 'undefined') { 109 | assert(false, 'Output variable not defined: ' + prefix); 110 | } 111 | const ba = actualOut[symbols[prefix].varIdx].toString(); 112 | const be = eOut.toString(); 113 | assert.strictEqual(ba, be, prefix); 114 | } 115 | } 116 | } 117 | async function readSymbols(path) { 118 | let symbols = {}; 119 | let signals = {}; 120 | const symsStr = await fs.promises.readFile(path, 'utf8'); 121 | const lines = symsStr.split('\n'); 122 | for (let i = 0; i < lines.length; i++) { 123 | const arr = lines[i].split(','); 124 | if (arr.length != 4) 125 | continue; 126 | const symbol = arr[3]; 127 | const labelIdx = Number(arr[0]); 128 | const varIdx = Number(arr[1]); 129 | const componentIdx = Number(arr[2]); 130 | symbols[symbol] = { 131 | labelIdx, 132 | varIdx, 133 | componentIdx, 134 | }; 135 | if (signals[varIdx] == null) { 136 | signals[varIdx] = [symbol]; 137 | } 138 | else { 139 | signals[varIdx].push(symbol); 140 | } 141 | } 142 | return { symbols, signals }; 143 | } 144 | class Checker { 145 | constructor(r1csFilepath, symFilepath) { 146 | this.r1csFilepath = r1csFilepath; 147 | this.symFilepath = symFilepath; 148 | } 149 | async load() { 150 | this.r1cs = await readR1cs(this.r1csFilepath, true, false); 151 | const { symbols, signals } = await readSymbols(this.symFilepath); 152 | this.symbols = symbols; 153 | this.signals = signals; 154 | } 155 | async checkConstraintsAndOutput(witnessFilePath, expectedOutputFile) { 156 | // 0. load r1cs and witness 157 | if (!this.r1cs) { 158 | await this.load(); 159 | } 160 | let witness; 161 | if (witnessFilePath.endsWith('json')) { 162 | witness = JSON.parse(fs.readFileSync(witnessFilePath).toString()); 163 | } 164 | else { 165 | witness = await readWtns(witnessFilePath); 166 | } 167 | // 1. check constraints 168 | const F = new ZqField(this.r1cs.prime); 169 | const constraints = this.r1cs.constraints; 170 | await checkConstraints(F, constraints, witness, this.signals); 171 | // 2. check output 172 | if (expectedOutputFile) { 173 | if (fs.existsSync(expectedOutputFile)) { 174 | const expectedOutputJson = JSON.parse(fs.readFileSync(expectedOutputFile).toString()); 175 | await assertOut(this.symbols, witness, expectedOutputJson); 176 | } 177 | else { 178 | console.log('no output file, skip:', expectedOutputFile); 179 | } 180 | } 181 | return true; 182 | } 183 | } 184 | exports.Checker = Checker; 185 | //# sourceMappingURL=checker.js.map -------------------------------------------------------------------------------- /dist/src/checker.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"checker.js","sourceRoot":"","sources":["../../src/checker.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/src/crypto.d.ts: -------------------------------------------------------------------------------- 1 | declare const groupOrderPrimeStr = "21888242871839275222246405745257275088548364400416034343698204186575808495617"; 2 | declare const groupOrderPrime: bigint; 3 | export { groupOrderPrimeStr, groupOrderPrime }; 4 | -------------------------------------------------------------------------------- /dist/src/crypto.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.groupOrderPrime = exports.groupOrderPrimeStr = void 0; 4 | const groupOrderPrimeStr = '21888242871839275222246405745257275088548364400416034343698204186575808495617'; 5 | exports.groupOrderPrimeStr = groupOrderPrimeStr; 6 | const groupOrderPrime = BigInt(groupOrderPrimeStr); 7 | exports.groupOrderPrime = groupOrderPrime; 8 | //# sourceMappingURL=crypto.js.map -------------------------------------------------------------------------------- /dist/src/crypto.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":";;;AAAA,MAAM,kBAAkB,GAAG,+EAA+E,CAAC;AAElG,gDAAkB;AAD3B,MAAM,eAAe,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC;AACtB,0CAAe"} -------------------------------------------------------------------------------- /dist/src/math.d.ts: -------------------------------------------------------------------------------- 1 | declare const groupOrderPrimeStr = "21888242871839275222246405745257275088548364400416034343698204186575808495617"; 2 | declare const groupOrderPrime: bigint; 3 | export { groupOrderPrimeStr, groupOrderPrime }; 4 | -------------------------------------------------------------------------------- /dist/src/math.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.groupOrderPrime = exports.groupOrderPrimeStr = void 0; 4 | const groupOrderPrimeStr = '21888242871839275222246405745257275088548364400416034343698204186575808495617'; 5 | exports.groupOrderPrimeStr = groupOrderPrimeStr; 6 | const groupOrderPrime = BigInt(groupOrderPrimeStr); 7 | exports.groupOrderPrime = groupOrderPrime; 8 | //# sourceMappingURL=math.js.map -------------------------------------------------------------------------------- /dist/src/math.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/math.ts"],"names":[],"mappings":";;;AAAA,MAAM,kBAAkB,GAAG,+EAA+E,CAAC;AAElG,gDAAkB;AAD3B,MAAM,eAAe,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC;AACtB,0CAAe"} -------------------------------------------------------------------------------- /dist/src/utils.d.ts: -------------------------------------------------------------------------------- 1 | declare function writeJsonWithBigint(path: string, obj: Object): Promise; 2 | declare function writeCircuitIntoDir(circuitDir: any, component: any): Promise; 3 | declare function writeInputOutputIntoDir(dataDir: any, input: any, output: any): Promise; 4 | export { writeCircuitIntoDir, writeInputOutputIntoDir, writeJsonWithBigint }; 5 | -------------------------------------------------------------------------------- /dist/src/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.writeJsonWithBigint = exports.writeInputOutputIntoDir = exports.writeCircuitIntoDir = void 0; 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | async function generateMainTestCircom(fileName, { src, main }) { 7 | if (src == '' || main == '') { 8 | throw new Error('invalid component ' + src + ' ' + main); 9 | } 10 | let srcCode = `include "${src}"; 11 | component main = ${main};`; 12 | fs.writeFileSync(fileName, srcCode, 'utf8'); 13 | } 14 | async function writeJsonWithBigint(path, obj) { 15 | let text = JSON.stringify(obj, (key, value) => (typeof value === 'bigint' ? value.toString() : value), // return everything else unchanged 16 | 2); 17 | // maybe another implementation?: 18 | // let text = JSON.stringify(ffutils.stringifyBigInts(obj))); 19 | fs.writeFileSync(path, text, 'utf8'); 20 | } 21 | exports.writeJsonWithBigint = writeJsonWithBigint; 22 | async function writeCircuitIntoDir(circuitDir, component) { 23 | fs.mkdirSync(circuitDir, { recursive: true }); 24 | const circuitFilePath = path.join(circuitDir, 'circuit.circom'); 25 | await generateMainTestCircom(circuitFilePath, component); 26 | } 27 | exports.writeCircuitIntoDir = writeCircuitIntoDir; 28 | async function writeInputOutputIntoDir(dataDir, input, output) { 29 | fs.mkdirSync(dataDir, { recursive: true }); 30 | const inputFilePath = path.join(dataDir, 'input.json'); 31 | await writeJsonWithBigint(inputFilePath, input); 32 | const outputFilePath = path.join(dataDir, 'output.json'); 33 | await writeJsonWithBigint(outputFilePath, output); 34 | } 35 | exports.writeInputOutputIntoDir = writeInputOutputIntoDir; 36 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /dist/src/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":";;;AAAA,yBAAyB;AACzB,6BAA6B;AAE7B,KAAK,UAAU,sBAAsB,CAAC,QAAgB,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE;IACnE,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC;KAC1D;IACD,IAAI,OAAO,GAAG,YAAY,GAAG;uBACR,IAAI,GAAG,CAAC;IAC7B,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,GAAW;IAC1D,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CACvB,GAAG,EACH,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,mCAAmC;IAC3G,CAAC,CACF,CAAC;IACF,iCAAiC;IACjC,6DAA6D;IAC7D,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACvC,CAAC;AAgBsD,kDAAmB;AAd1E,KAAK,UAAU,mBAAmB,CAAC,UAAU,EAAE,SAAS;IACtD,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;IAChE,MAAM,sBAAsB,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;AAC3D,CAAC;AAUQ,kDAAmB;AAR5B,KAAK,UAAU,uBAAuB,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM;IAC3D,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACvD,MAAM,mBAAmB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACzD,MAAM,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC;AAE6B,0DAAuB"} -------------------------------------------------------------------------------- /dist/src/witness_generator.d.ts: -------------------------------------------------------------------------------- 1 | declare function compileCircuitDir(circuitDirName: any, { alwaysRecompile, verbose, backend, sanityCheck }: { 2 | alwaysRecompile: any; 3 | verbose: any; 4 | backend: any; 5 | sanityCheck: any; 6 | }): Promise<{ 7 | circuitFilePath: string; 8 | r1csFilepath: string; 9 | symFilepath: string; 10 | binaryFilePath: string; 11 | }>; 12 | declare class WitnessGenerator { 13 | circuit: any; 14 | component: any; 15 | name: string; 16 | circuitDirName: string; 17 | binaryFilePath: string; 18 | alwaysRecompile: boolean; 19 | sanityCheck: boolean; 20 | verbose: boolean; 21 | writeExpectedOutput: boolean; 22 | backend: string; 23 | constructor(name: any, { backend, alwaysRecompile, verbose, sanityCheck }?: { 24 | backend: string; 25 | alwaysRecompile: boolean; 26 | verbose: boolean; 27 | sanityCheck: boolean; 28 | }); 29 | chooseBackend(): Promise<"native" | "wasm">; 30 | compile(circuitDirName: any): Promise<{ 31 | r1csFilepath: string; 32 | symFilepath: string; 33 | }>; 34 | generateWitness(inputFilePath: string, witnessFilePath: string): Promise; 35 | } 36 | export { WitnessGenerator, compileCircuitDir }; 37 | -------------------------------------------------------------------------------- /dist/src/witness_generator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.compileCircuitDir = exports.WitnessGenerator = void 0; 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | const shelljs = require("shelljs"); 7 | const si = require('systeminformation'); 8 | const crypto = require('crypto'); 9 | const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils; 10 | const { wtns } = require('snarkjs'); 11 | const crypto_1 = require("./crypto"); 12 | //import {compiler} from "circom"; 13 | //const Scalar = require("ffjavascript").Scalar; 14 | const DEFAULT_NODE_ARGS = '--max-old-space-size=8192 --stack-size=65500'; 15 | const NODE_ARGS = process.env.NODE_ARGS || DEFAULT_NODE_ARGS; 16 | const NODE_CMD = `node ${NODE_ARGS}`; 17 | function isEmptyFile(filePath) { 18 | return !fs.existsSync(filePath) || fs.statSync(filePath).size == 0; 19 | } 20 | function shellExecFnBuilder(verbose) { 21 | function shellExec(cmd, options = {}) { 22 | if (verbose) { 23 | console.log(cmd); 24 | } 25 | return shelljs.exec(cmd, options); 26 | } 27 | return shellExec; 28 | } 29 | async function compileWasmBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, binaryFilePath, verbose }) { 30 | const shellExec = shellExecFnBuilder(verbose); 31 | const circomcliPath = process.env.CIRCOM_CLI || path.join(require.resolve('circom'), '..', 'cli.js'); 32 | const isWindowsShell = process.platform === 'win32'; 33 | let cmd; 34 | cmd = `${NODE_CMD} ${circomcliPath} ${circuitFilePath} -r ${r1csFilepath} -w ${binaryFilePath} -s ${symFilepath}`; 35 | if (verbose) { 36 | if (!isWindowsShell) { 37 | cmd = '/usr/bin/time ' + (process.platform === 'linux' ? '-v' : '-l') + ' ' + cmd; 38 | } 39 | else { 40 | console.warn('in windows we can not timing the compilation'); 41 | } 42 | cmd += ' -v'; 43 | } 44 | shellExec(cmd, { fatal: true }); 45 | if (isEmptyFile(binaryFilePath)) { 46 | throw new Error('compile failed. ' + cmd); 47 | } 48 | } 49 | async function generateSrcsForNativeBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, verbose, alwaysRecompile }) { 50 | const shellExec = shellExecFnBuilder(verbose); 51 | const circomRuntimePath = path.join(require.resolve('circom_runtime'), '..', '..', 'c'); 52 | const ffiasmPath = path.join(require.resolve('ffiasm'), '..'); 53 | const circomcliPath = process.env.CIRCOM_CLI || path.join(require.resolve('circom'), '..', 'cli.js'); 54 | const cFilepath = path.join(circuitDirName, 'circuit.cpp'); 55 | if (!alwaysRecompile && fs.existsSync(cFilepath) && fs.statSync(cFilepath).size > 0) { 56 | if (verbose) { 57 | console.log('skip generate c src', cFilepath); 58 | } 59 | return; 60 | } 61 | let cmd; 62 | const buildzqfield = path.join(ffiasmPath, 'src', 'buildzqfield.js'); 63 | cmd = `node ${buildzqfield} -q ${crypto_1.groupOrderPrimeStr} -n Fr`; 64 | shellExec(cmd, { cwd: circuitDirName }); 65 | if (process.arch !== 'x64') { 66 | throw 'Unsupported platform ' + process.arch + '. Try wasm backend as an alternative'; 67 | } 68 | if (process.platform === 'darwin') { 69 | cmd = `nasm -fmacho64 --prefix _ ${circuitDirName}/fr.asm`; 70 | } 71 | else if (process.platform === 'linux') { 72 | cmd = `nasm -felf64 ${circuitDirName}/fr.asm`; 73 | } 74 | else if (process.platform === 'win32') { 75 | cmd = `nasm -fwin64 ${circuitDirName}\\fr.asm -o ${circuitDirName}\\fr.o`; 76 | } 77 | else 78 | throw 'Unsupported platform'; 79 | shellExec(cmd); 80 | const isWindowsShell = process.platform === 'win32'; 81 | const circomRuntimePathNR = path.normalize(`${circomRuntimePath}/`); 82 | if (isWindowsShell) { 83 | cmd = `copy /Y ${circomRuntimePathNR}*.cpp ${circuitDirName}`; 84 | shellExec(cmd); 85 | cmd = `copy /Y ${circomRuntimePathNR}*.hpp ${circuitDirName}`; 86 | shellExec(cmd); 87 | //we need to copy some hacking stuff ... 88 | const hackingRuntimePath = path.join(path.resolve(__dirname), '..', '..', 'win32', 'runtime'); 89 | cmd = `copy /Y ${hackingRuntimePath}\\*.hpp ${circuitDirName}`; 90 | shellExec(cmd); 91 | cmd = `copy /Y ${hackingRuntimePath}\\*.cpp ${circuitDirName}`; 92 | shellExec(cmd); 93 | } 94 | else { 95 | cmd = `cp ${circomRuntimePathNR}*.cpp ${circuitDirName}`; 96 | shellExec(cmd); 97 | cmd = `cp ${circomRuntimePathNR}*.hpp ${circuitDirName}`; 98 | shellExec(cmd); 99 | } 100 | cmd = `${NODE_CMD} ${circomcliPath} ${circuitFilePath} -r ${r1csFilepath} -c ${cFilepath} -s ${symFilepath}`; 101 | if (verbose) { 102 | if (!isWindowsShell) { 103 | cmd = '/usr/bin/time ' + (process.platform === 'linux' ? '-v' : '-l') + ' ' + cmd; 104 | } 105 | else { 106 | console.warn('in windows we can not timing the compilation'); 107 | } 108 | cmd += ' -v'; 109 | } 110 | shellExec(cmd, { fatal: true }); 111 | if (isEmptyFile(cFilepath)) { 112 | throw new Error('compile failed. ' + cmd); 113 | } 114 | // the binary needs a $arg0.dat file, so we make a symbol link here 115 | // TODO: should we remove the fast.dat first or skip it in case of the 'force compiling' scheme 116 | if (!isWindowsShell) { 117 | cmd = `ln -s -f circuit.dat circuit.fast.dat`; 118 | shellExec(cmd, { cwd: circuitDirName }); 119 | } 120 | else { 121 | // under win32 the bin file has extra extensions ... 122 | cmd = 'mklink /H circuit.exe.dat circuit.dat '; 123 | shellExec(cmd, { cwd: circuitDirName }); 124 | cmd = 'mklink /H circuit.fast.exe.dat circuit.dat '; 125 | shellExec(cmd, { cwd: circuitDirName }); 126 | } 127 | } 128 | async function compileNativeBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, binaryFilePath, verbose, sanityCheck, alwaysRecompile, }) { 129 | await generateSrcsForNativeBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, verbose, alwaysRecompile }); 130 | const shellExec = shellExecFnBuilder(verbose); 131 | let compileCmd = `g++ main.cpp calcwit.cpp utils.cpp fr.cpp fr.o circuit.cpp -o ${binaryFilePath} -lgmp -std=c++14 -O3`; 132 | if (process.platform === 'darwin') { 133 | // do nothing 134 | } 135 | else if (process.platform === 'linux') { 136 | compileCmd += ' -pthread -fopenmp'; 137 | } 138 | else if (process.platform === 'win32') { 139 | compileCmd += ' -lmman'; 140 | } 141 | else { 142 | throw 'Unsupported platform'; 143 | } 144 | if (sanityCheck) { 145 | compileCmd += ' -DSANITY_CHECK'; 146 | } 147 | shellExec(compileCmd, { cwd: `${circuitDirName}` }); 148 | } 149 | // Calculate md5 checksum for given set of src files. 150 | function calculateSrcHashes(contents) { 151 | const hashes = new Map(); 152 | for (const entry of Array.from(contents.entries())) { 153 | const checksum = crypto.createHash('md5').update(entry[1], 'utf8').digest('hex'); 154 | hashes.set(entry[0], checksum); 155 | } 156 | return hashes; 157 | } 158 | // Load checksums of src files from last compile. 159 | function loadOldSrcHashes(src) { 160 | const hashesFile = path.join(src, '..', 'srcs.sum'); 161 | try { 162 | const jsonStr = fs.readFileSync(hashesFile, 'utf8'); 163 | const hashes = new Map(JSON.parse(jsonStr)); 164 | return hashes; 165 | } 166 | catch (err) { 167 | if (err.code !== 'ENOENT') { 168 | console.log('Load old src hash error:', err); 169 | } 170 | return null; 171 | } 172 | } 173 | // Write checksums of src files of this compile. 174 | function writeSrcHashes(hashes, src) { 175 | const jsonStr = JSON.stringify(Array.from(hashes.entries())); 176 | const hashesFile = path.join(src, '..', 'srcs.sum'); 177 | fs.writeFileSync(hashesFile, jsonStr, 'utf8'); 178 | } 179 | // Check whether the checksums are equal between current src files and last compile. 180 | function isSrcHashesEqual(srcHashes, oldSrcHashes) { 181 | let isEqual = true; 182 | for (const src of Array.from(srcHashes.keys())) { 183 | if (!oldSrcHashes.has(src)) { 184 | console.log('Added src file: ', src); 185 | isEqual = false; 186 | } 187 | else if (oldSrcHashes.get(src) !== srcHashes.get(src)) { 188 | console.log('Changed src file: ', src); 189 | isEqual = false; 190 | } 191 | } 192 | for (const src of Array.from(oldSrcHashes.keys())) { 193 | if (!srcHashes.has(src)) { 194 | console.log('Removed src file: ', src); 195 | isEqual = false; 196 | } 197 | } 198 | return isEqual; 199 | } 200 | // Check whether the src files are changed between this run and last compile. 201 | function checkSrcChanged(src) { 202 | const circomDir = require.resolve('circom'); 203 | const parser = require(path.join(circomDir, '..', 'parser/jaz.js')).parser; 204 | const srcContents = new Map(); 205 | traverse(src); 206 | const srcHashes = calculateSrcHashes(srcContents); 207 | const oldSrcHashes = loadOldSrcHashes(src); 208 | if (oldSrcHashes == null || !isSrcHashesEqual(srcHashes, oldSrcHashes)) { 209 | writeSrcHashes(srcHashes, src); 210 | return true; 211 | } 212 | else { 213 | return false; 214 | } 215 | function traverse(src) { 216 | const content = fs.readFileSync(src, 'utf8'); 217 | srcContents.set(src, content); 218 | const ast = parser.parse(content); 219 | for (const stat of ast.statements) { 220 | if (stat.type == 'INCLUDE') { 221 | let includedFile = stat.file; 222 | if (!path.isAbsolute(includedFile)) { 223 | includedFile = path.normalize(path.join(src, '..', includedFile)); 224 | } 225 | if (!srcContents.has(includedFile)) { 226 | traverse(includedFile); 227 | } 228 | } 229 | } 230 | } 231 | } 232 | async function compileCircuitDir(circuitDirName, { alwaysRecompile, verbose, backend, sanityCheck }) { 233 | // console.log('compiling dir', circuitDirName); 234 | const circuitFilePath = path.join(circuitDirName, 'circuit.circom'); 235 | const r1csFilepath = path.join(circuitDirName, 'circuit.r1cs'); 236 | const symFilepath = path.join(circuitDirName, 'circuit.sym'); 237 | let binaryFilePath; 238 | if (backend === 'native') { 239 | if (sanityCheck) { 240 | binaryFilePath = path.join(circuitDirName, 'circuit'); 241 | } 242 | else { 243 | binaryFilePath = path.join(circuitDirName, 'circuit.fast'); 244 | } 245 | if (process.platform === 'win32') { 246 | binaryFilePath += '.exe'; 247 | } 248 | } 249 | else { 250 | binaryFilePath = path.join(circuitDirName, 'circuit.wasm'); 251 | } 252 | if (!alwaysRecompile && 253 | fs.existsSync(binaryFilePath) && 254 | fs.statSync(binaryFilePath).size > 0 && 255 | checkSrcChanged(circuitFilePath) === false) { 256 | if (verbose) { 257 | console.log('skip compiling binary ', binaryFilePath); 258 | } 259 | return { circuitFilePath, r1csFilepath, symFilepath, binaryFilePath }; 260 | } 261 | console.log('compile', circuitDirName); 262 | if (backend === 'native') { 263 | await compileNativeBinary({ 264 | circuitDirName, 265 | r1csFilepath, 266 | circuitFilePath, 267 | symFilepath, 268 | binaryFilePath, 269 | verbose, 270 | sanityCheck, 271 | alwaysRecompile, 272 | }); 273 | } 274 | else { 275 | // sanity check is not supported for wasm backend now 276 | await compileWasmBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, binaryFilePath, verbose }); 277 | } 278 | return { circuitFilePath, r1csFilepath, symFilepath, binaryFilePath }; 279 | } 280 | exports.compileCircuitDir = compileCircuitDir; 281 | class WitnessGenerator { 282 | constructor(name, { backend, alwaysRecompile, verbose, sanityCheck } = { backend: 'native', alwaysRecompile: true, verbose: false, sanityCheck: true }) { 283 | this.name = name; 284 | // we can specify cached files to avoid compiling every time 285 | this.alwaysRecompile = alwaysRecompile; 286 | this.verbose = verbose; 287 | this.backend = backend; 288 | this.sanityCheck = sanityCheck; 289 | if (this.verbose) { 290 | if (this.backend == 'wasm' && this.sanityCheck) { 291 | console.log('WARN: sanity check is not supported for wasm backend now'); 292 | } 293 | console.log(`node: ${shelljs.which('node')} ${NODE_ARGS}`); 294 | } 295 | } 296 | async chooseBackend() { 297 | const needFeatures = ['bmi2', 'adx']; 298 | let cpuFeatures = (await si.cpu()).flags.split(/\s/); 299 | if (process.platform === 'darwin') { 300 | const stdout = shelljs.exec('sysctl machdep.cpu.leaf7_features', { silent: true }); 301 | const features = stdout.trim().toLowerCase().split(/\s/).slice(1); 302 | cpuFeatures.push(...features); 303 | } 304 | for (const f of needFeatures) { 305 | if (!cpuFeatures.includes(f)) { 306 | console.log(`cpu missing needed feature ${f} for native backend, fallback to wasm`); 307 | console.log(`cpus earlier than Intel Boradwell / AMD Ryzen are not supported for native backend`); 308 | return 'wasm'; 309 | } 310 | } 311 | return 'native'; 312 | } 313 | async compile(circuitDirName) { 314 | this.circuitDirName = path.resolve(circuitDirName); 315 | if (this.backend === 'auto') { 316 | this.backend = await this.chooseBackend(); 317 | } 318 | const { r1csFilepath, symFilepath, binaryFilePath } = await compileCircuitDir(this.circuitDirName, { 319 | alwaysRecompile: this.alwaysRecompile, 320 | verbose: this.verbose, 321 | backend: this.backend, 322 | sanityCheck: this.sanityCheck, 323 | }); 324 | this.binaryFilePath = binaryFilePath; 325 | return { r1csFilepath, symFilepath }; 326 | } 327 | async generateWitness(inputFilePath, witnessFilePath) { 328 | var cmd; 329 | if (this.binaryFilePath == '' || !fs.existsSync(this.binaryFilePath)) { 330 | throw new Error('invalid bin ' + this.binaryFilePath + '. Has the circuit been compiled?'); 331 | } 332 | if (!witnessFilePath.endsWith('.json') && !witnessFilePath.endsWith('.wtns')) { 333 | throw new Error('invalid witness file type'); 334 | } 335 | // gen witness 336 | if (this.backend === 'native') { 337 | cmd = `${this.binaryFilePath} ${inputFilePath} ${witnessFilePath}`; 338 | if (this.verbose) { 339 | console.log(cmd); 340 | } 341 | const genWtnsOut = shelljs.exec(cmd, { silent: !this.verbose }); 342 | if (genWtnsOut.stderr || genWtnsOut.code != 0) { 343 | if (!this.verbose) { 344 | // don't display stderr twice 345 | console.error('\n' + genWtnsOut.stderr); 346 | } 347 | throw new Error('Could not generate witness'); 348 | } 349 | } 350 | else { 351 | const snarkjsPath = path.join(require.resolve('snarkjs'), '..', 'cli.cjs'); 352 | let witnessBinFile; 353 | if (witnessFilePath.endsWith('.json')) { 354 | witnessBinFile = witnessFilePath.replace(/json$/, 'wtns'); 355 | } 356 | else { 357 | witnessBinFile = witnessFilePath; 358 | } 359 | // calculate witness bin file 360 | const sameProcess = process.platform !== 'win32'; 361 | if (sameProcess) { 362 | const input = unstringifyBigInts(JSON.parse(await fs.promises.readFile(inputFilePath, 'utf8'))); 363 | await wtns.calculate(input, this.binaryFilePath, witnessBinFile, defaultWitnessOption()); 364 | } 365 | else { 366 | cmd = `${NODE_CMD} ${snarkjsPath} wc ${this.binaryFilePath} ${inputFilePath} ${witnessBinFile}`; 367 | shelljs.exec(cmd); 368 | } 369 | // convert bin witness to json witness if needed 370 | if (witnessFilePath.endsWith('.json')) { 371 | cmd = `${NODE_CMD} ${snarkjsPath} wej ${witnessBinFile} ${witnessFilePath}`; 372 | shelljs.exec(cmd); 373 | } 374 | } 375 | } 376 | } 377 | exports.WitnessGenerator = WitnessGenerator; 378 | function defaultWitnessOption() { 379 | let logFn = console.log; 380 | let calculateWitnessOptions = { 381 | sanityCheck: true, 382 | logTrigger: logFn, 383 | logOutput: logFn, 384 | logStartComponent: logFn, 385 | logFinishComponent: logFn, 386 | logSetSignal: logFn, 387 | logGetSignal: logFn, 388 | }; 389 | return calculateWitnessOptions; 390 | } 391 | //# sourceMappingURL=witness_generator.js.map -------------------------------------------------------------------------------- /dist/src/witness_generator.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"witness_generator.js","sourceRoot":"","sources":["../../src/witness_generator.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { WitnessGenerator } from './src/witness_generator'; 2 | import { Checker } from './src/checker'; 3 | import * as path from 'path'; 4 | import * as fs from 'fs'; 5 | import * as utils from './src/utils'; 6 | const walkSync = require('walk-sync'); 7 | 8 | async function compileCircuitDir(circuitDir, options) { 9 | const circuitName = path.basename(circuitDir); 10 | let witnessGenerator = new WitnessGenerator(circuitName, options); 11 | await witnessGenerator.compile(circuitDir); 12 | } 13 | 14 | async function testCircuitDir(circuitDir, dataDir, options) { 15 | // make sure the circuit is compiled 16 | const circuitName = path.basename(circuitDir); 17 | let witnessGenerator = new WitnessGenerator(circuitName, options); 18 | const { r1csFilepath, symFilepath } = await witnessGenerator.compile(circuitDir); 19 | 20 | const checker = new Checker(r1csFilepath, symFilepath); 21 | 22 | if (dataDir == null || dataDir == '') { 23 | dataDir = circuitDir; 24 | } 25 | for (const input of walkSync(path.resolve(dataDir), { includeBasePath: true, globs: ['**/input.json'] })) { 26 | const testCaseDir = path.normalize(path.dirname(input)); 27 | //const testCaseName = path.basename(testCaseDir) 28 | console.log('\ntest', testCaseDir); 29 | const inputFile = path.join(testCaseDir, 'input.json'); 30 | const witnessFileType = options.witnessFileType == 'bin' || options.witnessFileType == 'wtns' ? 'wtns' : 'json'; 31 | const witnessFile = path.join(testCaseDir, 'witness.' + witnessFileType); 32 | const expectedOutputFile = path.join(testCaseDir, 'output.json'); 33 | await witnessGenerator.generateWitness(inputFile, witnessFile); 34 | if (!options.sanityCheck || fs.existsSync(expectedOutputFile)) { 35 | await checker.checkConstraintsAndOutput(witnessFile, expectedOutputFile); 36 | } 37 | console.log('\ntest', testCaseDir, 'done'); 38 | } 39 | } 40 | 41 | export { compileCircuitDir, testCircuitDir, utils }; 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snarkit", 3 | "version": "0.0.11", 4 | "description": "A command line tool to test circom circuit with given inputs/outputs", 5 | "main": "./dist/index.js", 6 | "bin": { 7 | "snarkit": "./dist/cli.js" 8 | }, 9 | "build": "npx tsc", 10 | "scripts": { 11 | "fmt": "npx prettier --write \"**/*.{js,ts}\"", 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "docker": "docker buildx build --platform linux/amd64 --load -t fluidex/snarkit ." 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/fluidex/snarkit.git" 18 | }, 19 | "keywords": [ 20 | "circom", 21 | "zk-snark" 22 | ], 23 | "author": "lispczz", 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/fluidex/snarkit/issues" 27 | }, 28 | "homepage": "https://github.com/fluidex/snarkit#readme", 29 | "dependencies": { 30 | "@iden3/binfileutils": "^0.0.8", 31 | "chai": "^4.3.4", 32 | "circom": "^0.5.42", 33 | "circom_runtime": "^0.1.12", 34 | "commander": "^7.2.0", 35 | "ffiasm": "^0.1.1", 36 | "ffjavascript": "^0.2.35", 37 | "r1csfile": "0.0.16", 38 | "shelljs": "^0.8.4", 39 | "snarkjs": "^0.3.60", 40 | "systeminformation": "^5.6.10", 41 | "tmp-promise": "^3.0.2", 42 | "walk-sync": "^2.2.0" 43 | }, 44 | "devDependencies": { 45 | "@types/node": "^14.14.35", 46 | "prettier": "^2.2.1", 47 | "typescript": "^4.2.3" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/checker.ts: -------------------------------------------------------------------------------- 1 | const readR1cs = require('r1csfile').load; 2 | const { ZqField, utils: ffutils } = require('ffjavascript'); 3 | const { assert } = require('chai'); 4 | import * as path from 'path'; 5 | import * as fs from 'fs'; 6 | const binFileUtils = require('@iden3/binfileutils'); 7 | 8 | import { groupOrderPrimeStr, groupOrderPrime } from './crypto'; 9 | 10 | // copyed from snarkjs/src/wtns_utils.js 11 | async function readWtnsHeader(fd, sections) { 12 | await binFileUtils.startReadUniqueSection(fd, sections, 1); 13 | const n8 = await fd.readULE32(); 14 | const q = await binFileUtils.readBigInt(fd, n8); 15 | const nWitness = await fd.readULE32(); 16 | await binFileUtils.endReadSection(fd); 17 | 18 | return { n8, q, nWitness }; 19 | } 20 | 21 | async function readWtns(fileName) { 22 | const { fd, sections } = await binFileUtils.readBinFile(fileName, 'wtns', 2); 23 | 24 | const { n8, nWitness } = await readWtnsHeader(fd, sections); 25 | 26 | await binFileUtils.startReadUniqueSection(fd, sections, 2); 27 | const res = []; 28 | for (let i = 0; i < nWitness; i++) { 29 | const v = await binFileUtils.readBigInt(fd, n8); 30 | res.push(v); 31 | } 32 | await binFileUtils.endReadSection(fd); 33 | 34 | await fd.close(); 35 | 36 | return res; 37 | } 38 | 39 | // TOOD: type 40 | async function checkConstraints(F, constraints, witness, signals) { 41 | if (!constraints) { 42 | throw new Error('empty constraints'); 43 | } 44 | for (let i = 0; i < constraints.length; i++) { 45 | checkConstraint(constraints[i]); 46 | } 47 | 48 | function checkConstraint(constraint) { 49 | const a = evalLC(constraint[0]); 50 | const b = evalLC(constraint[1]); 51 | const c = evalLC(constraint[2]); 52 | if (!F.isZero(F.sub(F.mul(a, b), c))) { 53 | function displayLc(lc) { 54 | const entries = Object.entries(lc); 55 | if (entries.length == 0) { 56 | return '0'; 57 | } 58 | function displayField(x) { 59 | const f = BigInt(x); 60 | // display some field element as negative int for better reading 61 | if (f >= groupOrderPrime - 200n) { 62 | return `(-${(groupOrderPrime - f).toString()})`; 63 | } 64 | return f.toString(); 65 | } 66 | function displayMonomial(coef, signalIdx) { 67 | return `${displayField(coef)}*signal${signalIdx}`; 68 | } 69 | return '(' + entries.map(kv => displayMonomial(kv[1], kv[0])).join(' + ') + ')'; 70 | } 71 | console.log('\nInvalid constraint:'); 72 | console.log(`${displayLc(constraint[0])} * ${displayLc(constraint[1])} != ${displayLc(constraint[2])}`); 73 | console.log('Related signals:'); 74 | let sigs = new Set(); 75 | for (const c of constraint) { 76 | for (const s in c) { 77 | sigs.add(Number(s)); 78 | } 79 | } 80 | for (const s of sigs) { 81 | // signal 0 is 'one' 82 | if (s != 0) { 83 | console.log(`signal${s}: ${signals[s].join(' ')}, value: ${witness[s]}`); 84 | } 85 | } 86 | console.log('please check your circuit and input\n'); 87 | 88 | throw new Error("Constraint doesn't match"); 89 | } 90 | } 91 | 92 | function evalLC(lc) { 93 | let v = F.zero; 94 | for (let w in lc) { 95 | v = F.add(v, F.mul(BigInt(lc[w]), BigInt(witness[w]))); 96 | } 97 | return v; 98 | } 99 | } 100 | 101 | // TOOD: type 102 | async function assertOut(symbols, actualOut, expectedOut) { 103 | if (!symbols) { 104 | throw new Error('empty symbols'); 105 | } 106 | 107 | checkObject('main', expectedOut); 108 | 109 | function checkObject(prefix, eOut) { 110 | if (Array.isArray(eOut)) { 111 | for (let i = 0; i < eOut.length; i++) { 112 | checkObject(prefix + '[' + i + ']', eOut[i]); 113 | } 114 | } else if (typeof eOut == 'object' && eOut.constructor.name == 'Object') { 115 | for (let k in eOut) { 116 | checkObject(prefix + '.' + k, eOut[k]); 117 | } 118 | } else { 119 | if (typeof symbols[prefix] == 'undefined') { 120 | assert(false, 'Output variable not defined: ' + prefix); 121 | } 122 | const ba = actualOut[symbols[prefix].varIdx].toString(); 123 | const be = eOut.toString(); 124 | assert.strictEqual(ba, be, prefix); 125 | } 126 | } 127 | } 128 | 129 | async function readSymbols(path: string) { 130 | let symbols = {}; 131 | let signals = {}; 132 | 133 | const symsStr = await fs.promises.readFile(path, 'utf8'); 134 | const lines = symsStr.split('\n'); 135 | for (let i = 0; i < lines.length; i++) { 136 | const arr = lines[i].split(','); 137 | if (arr.length != 4) continue; 138 | const symbol = arr[3]; 139 | const labelIdx = Number(arr[0]); 140 | const varIdx = Number(arr[1]); 141 | const componentIdx = Number(arr[2]); 142 | symbols[symbol] = { 143 | labelIdx, 144 | varIdx, 145 | componentIdx, 146 | }; 147 | if (signals[varIdx] == null) { 148 | signals[varIdx] = [symbol]; 149 | } else { 150 | signals[varIdx].push(symbol); 151 | } 152 | } 153 | return { symbols, signals }; 154 | } 155 | 156 | class Checker { 157 | r1csFilepath: string; 158 | symFilepath: string; 159 | r1cs: any; 160 | symbols: any; 161 | signals: any; 162 | constructor(r1csFilepath, symFilepath) { 163 | this.r1csFilepath = r1csFilepath; 164 | this.symFilepath = symFilepath; 165 | } 166 | async load() { 167 | this.r1cs = await readR1cs(this.r1csFilepath, true, false); 168 | const { symbols, signals } = await readSymbols(this.symFilepath); 169 | this.symbols = symbols; 170 | this.signals = signals; 171 | } 172 | 173 | async checkConstraintsAndOutput(witnessFilePath, expectedOutputFile) { 174 | // 0. load r1cs and witness 175 | if (!this.r1cs) { 176 | await this.load(); 177 | } 178 | let witness; 179 | if (witnessFilePath.endsWith('json')) { 180 | witness = JSON.parse(fs.readFileSync(witnessFilePath).toString()); 181 | } else { 182 | witness = await readWtns(witnessFilePath); 183 | } 184 | // 1. check constraints 185 | const F = new ZqField(this.r1cs.prime); 186 | const constraints = this.r1cs.constraints; 187 | await checkConstraints(F, constraints, witness, this.signals); 188 | // 2. check output 189 | if (expectedOutputFile) { 190 | if (fs.existsSync(expectedOutputFile)) { 191 | const expectedOutputJson = JSON.parse(fs.readFileSync(expectedOutputFile).toString()); 192 | await assertOut(this.symbols, witness, expectedOutputJson); 193 | } else { 194 | console.log('no output file, skip:', expectedOutputFile); 195 | } 196 | } 197 | return true; 198 | } 199 | } 200 | 201 | export { Checker }; 202 | -------------------------------------------------------------------------------- /src/crypto.ts: -------------------------------------------------------------------------------- 1 | const groupOrderPrimeStr = '21888242871839275222246405745257275088548364400416034343698204186575808495617'; 2 | const groupOrderPrime = BigInt(groupOrderPrimeStr); 3 | export { groupOrderPrimeStr, groupOrderPrime }; 4 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | 4 | async function generateMainTestCircom(fileName: string, { src, main }) { 5 | if (src == '' || main == '') { 6 | throw new Error('invalid component ' + src + ' ' + main); 7 | } 8 | let srcCode = `include "${src}"; 9 | component main = ${main};`; 10 | fs.writeFileSync(fileName, srcCode, 'utf8'); 11 | } 12 | 13 | async function writeJsonWithBigint(path: string, obj: Object) { 14 | let text = JSON.stringify( 15 | obj, 16 | (key, value) => (typeof value === 'bigint' ? value.toString() : value), // return everything else unchanged 17 | 2, 18 | ); 19 | // maybe another implementation?: 20 | // let text = JSON.stringify(ffutils.stringifyBigInts(obj))); 21 | fs.writeFileSync(path, text, 'utf8'); 22 | } 23 | 24 | async function writeCircuitIntoDir(circuitDir, component) { 25 | fs.mkdirSync(circuitDir, { recursive: true }); 26 | const circuitFilePath = path.join(circuitDir, 'circuit.circom'); 27 | await generateMainTestCircom(circuitFilePath, component); 28 | } 29 | 30 | async function writeInputOutputIntoDir(dataDir, input, output) { 31 | fs.mkdirSync(dataDir, { recursive: true }); 32 | const inputFilePath = path.join(dataDir, 'input.json'); 33 | await writeJsonWithBigint(inputFilePath, input); 34 | const outputFilePath = path.join(dataDir, 'output.json'); 35 | await writeJsonWithBigint(outputFilePath, output); 36 | } 37 | 38 | export { writeCircuitIntoDir, writeInputOutputIntoDir, writeJsonWithBigint }; 39 | -------------------------------------------------------------------------------- /src/witness_generator.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as shelljs from 'shelljs'; 4 | const si = require('systeminformation'); 5 | const crypto = require('crypto'); 6 | const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils; 7 | const { wtns } = require('snarkjs'); 8 | import { groupOrderPrimeStr, groupOrderPrime } from './crypto'; 9 | //import {compiler} from "circom"; 10 | //const Scalar = require("ffjavascript").Scalar; 11 | 12 | const DEFAULT_NODE_ARGS = '--max-old-space-size=8192 --stack-size=65500'; 13 | const NODE_ARGS = process.env.NODE_ARGS || DEFAULT_NODE_ARGS; 14 | const NODE_CMD = `node ${NODE_ARGS}`; 15 | 16 | function isEmptyFile(filePath) { 17 | return !fs.existsSync(filePath) || fs.statSync(filePath).size == 0; 18 | } 19 | 20 | function shellExecFnBuilder(verbose) { 21 | function shellExec(cmd, options = {}) { 22 | if (verbose) { 23 | console.log(cmd); 24 | } 25 | return shelljs.exec(cmd, options); 26 | } 27 | return shellExec; 28 | } 29 | 30 | async function compileWasmBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, binaryFilePath, verbose }) { 31 | const shellExec = shellExecFnBuilder(verbose); 32 | const circomcliPath = process.env.CIRCOM_CLI || path.join(require.resolve('circom'), '..', 'cli.js'); 33 | const isWindowsShell = process.platform === 'win32' 34 | let cmd: string; 35 | cmd = `${NODE_CMD} ${circomcliPath} ${circuitFilePath} -r ${r1csFilepath} -w ${binaryFilePath} -s ${symFilepath}`; 36 | if (verbose) { 37 | if (!isWindowsShell){ 38 | cmd = '/usr/bin/time ' + (process.platform === 'linux' ? '-v' : '-l') + ' ' + cmd; 39 | }else { 40 | console.warn('in windows we can not timing the compilation'); 41 | } 42 | cmd += ' -v'; 43 | } 44 | shellExec(cmd, { fatal: true }); 45 | if (isEmptyFile(binaryFilePath)) { 46 | throw new Error('compile failed. ' + cmd); 47 | } 48 | } 49 | 50 | async function generateSrcsForNativeBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, verbose, alwaysRecompile }) { 51 | const shellExec = shellExecFnBuilder(verbose); 52 | const circomRuntimePath = path.join(require.resolve('circom_runtime'), '..', '..','c'); 53 | const ffiasmPath = path.join(require.resolve('ffiasm'), '..'); 54 | const circomcliPath = process.env.CIRCOM_CLI || path.join(require.resolve('circom'), '..', 'cli.js'); 55 | const cFilepath = path.join(circuitDirName, 'circuit.cpp'); 56 | 57 | if (!alwaysRecompile && fs.existsSync(cFilepath) && fs.statSync(cFilepath).size > 0) { 58 | if (verbose) { 59 | console.log('skip generate c src', cFilepath); 60 | } 61 | return; 62 | } 63 | 64 | let cmd: string; 65 | const buildzqfield = path.join(ffiasmPath, 'src', 'buildzqfield.js') 66 | cmd = `node ${buildzqfield} -q ${groupOrderPrimeStr} -n Fr`; 67 | shellExec(cmd, { cwd: circuitDirName }); 68 | if (process.arch !== 'x64') { 69 | throw 'Unsupported platform ' + process.arch + '. Try wasm backend as an alternative'; 70 | } 71 | if (process.platform === 'darwin') { 72 | cmd = `nasm -fmacho64 --prefix _ ${circuitDirName}/fr.asm`; 73 | } else if (process.platform === 'linux') { 74 | cmd = `nasm -felf64 ${circuitDirName}/fr.asm`; 75 | } else if (process.platform === 'win32') { 76 | cmd = `nasm -fwin64 ${circuitDirName}\\fr.asm -o ${circuitDirName}\\fr.o`; 77 | } else throw 'Unsupported platform'; 78 | shellExec(cmd); 79 | 80 | const isWindowsShell = process.platform === 'win32' 81 | const circomRuntimePathNR = path.normalize(`${circomRuntimePath}/`); 82 | if (isWindowsShell){ 83 | cmd = `copy /Y ${circomRuntimePathNR}*.cpp ${circuitDirName}`; 84 | shellExec(cmd); 85 | cmd = `copy /Y ${circomRuntimePathNR}*.hpp ${circuitDirName}`; 86 | shellExec(cmd); 87 | //we need to copy some hacking stuff ... 88 | const hackingRuntimePath = path.join(path.resolve(__dirname), '..', '..', 'win32', 'runtime'); 89 | cmd = `copy /Y ${hackingRuntimePath}\\*.hpp ${circuitDirName}`; 90 | shellExec(cmd); 91 | cmd = `copy /Y ${hackingRuntimePath}\\*.cpp ${circuitDirName}`; 92 | shellExec(cmd); 93 | 94 | }else { 95 | cmd = `cp ${circomRuntimePathNR}*.cpp ${circuitDirName}`; 96 | shellExec(cmd); 97 | cmd = `cp ${circomRuntimePathNR}*.hpp ${circuitDirName}`; 98 | shellExec(cmd); 99 | } 100 | 101 | cmd = `${NODE_CMD} ${circomcliPath} ${circuitFilePath} -r ${r1csFilepath} -c ${cFilepath} -s ${symFilepath}`; 102 | if (verbose) { 103 | if (!isWindowsShell){ 104 | cmd = '/usr/bin/time ' + (process.platform === 'linux' ? '-v' : '-l') + ' ' + cmd; 105 | }else { 106 | console.warn('in windows we can not timing the compilation'); 107 | } 108 | cmd += ' -v'; 109 | } 110 | shellExec(cmd, { fatal: true }); 111 | if (isEmptyFile(cFilepath)) { 112 | throw new Error('compile failed. ' + cmd); 113 | } 114 | 115 | // the binary needs a $arg0.dat file, so we make a symbol link here 116 | // TODO: should we remove the fast.dat first or skip it in case of the 'force compiling' scheme 117 | if (!isWindowsShell){ 118 | cmd = `ln -s -f circuit.dat circuit.fast.dat`; 119 | shellExec(cmd, { cwd: circuitDirName }); 120 | }else { 121 | // under win32 the bin file has extra extensions ... 122 | cmd = 'mklink /H circuit.exe.dat circuit.dat ' 123 | shellExec(cmd, { cwd: circuitDirName }); 124 | cmd = 'mklink /H circuit.fast.exe.dat circuit.dat ' 125 | shellExec(cmd, { cwd: circuitDirName }); 126 | } 127 | 128 | } 129 | 130 | async function compileNativeBinary({ 131 | circuitDirName, 132 | r1csFilepath, 133 | circuitFilePath, 134 | symFilepath, 135 | binaryFilePath, 136 | verbose, 137 | sanityCheck, 138 | alwaysRecompile, 139 | }) { 140 | await generateSrcsForNativeBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, verbose, alwaysRecompile }); 141 | const shellExec = shellExecFnBuilder(verbose); 142 | let compileCmd = `g++ main.cpp calcwit.cpp utils.cpp fr.cpp fr.o circuit.cpp -o ${binaryFilePath} -lgmp -std=c++14 -O3`; 143 | if (process.platform === 'darwin') { 144 | // do nothing 145 | } else if (process.platform === 'linux') { 146 | compileCmd += ' -pthread -fopenmp'; 147 | } else if (process.platform === 'win32') { 148 | compileCmd += ' -lmman'; 149 | } else { 150 | throw 'Unsupported platform'; 151 | } 152 | if (sanityCheck) { 153 | compileCmd += ' -DSANITY_CHECK'; 154 | } 155 | shellExec(compileCmd, {cwd: `${circuitDirName}`}); 156 | } 157 | 158 | // Calculate md5 checksum for given set of src files. 159 | function calculateSrcHashes(contents) { 160 | const hashes = new Map(); 161 | for (const entry of Array.from(contents.entries())) { 162 | const checksum = crypto.createHash('md5').update(entry[1], 'utf8').digest('hex'); 163 | hashes.set(entry[0], checksum); 164 | } 165 | return hashes; 166 | } 167 | 168 | // Load checksums of src files from last compile. 169 | function loadOldSrcHashes(src) { 170 | const hashesFile = path.join(src, '..', 'srcs.sum'); 171 | try { 172 | const jsonStr = fs.readFileSync(hashesFile, 'utf8'); 173 | const hashes = new Map(JSON.parse(jsonStr)); 174 | return hashes; 175 | } catch (err) { 176 | if (err.code !== 'ENOENT') { 177 | console.log('Load old src hash error:', err); 178 | } 179 | return null; 180 | } 181 | } 182 | 183 | // Write checksums of src files of this compile. 184 | function writeSrcHashes(hashes, src) { 185 | const jsonStr = JSON.stringify(Array.from(hashes.entries())); 186 | const hashesFile = path.join(src, '..', 'srcs.sum'); 187 | fs.writeFileSync(hashesFile, jsonStr, 'utf8'); 188 | } 189 | 190 | // Check whether the checksums are equal between current src files and last compile. 191 | function isSrcHashesEqual(srcHashes, oldSrcHashes) { 192 | let isEqual = true; 193 | for (const src of Array.from(srcHashes.keys())) { 194 | if (!oldSrcHashes.has(src)) { 195 | console.log('Added src file: ', src); 196 | isEqual = false; 197 | } else if (oldSrcHashes.get(src) !== srcHashes.get(src)) { 198 | console.log('Changed src file: ', src); 199 | isEqual = false; 200 | } 201 | } 202 | for (const src of Array.from(oldSrcHashes.keys())) { 203 | if (!srcHashes.has(src)) { 204 | console.log('Removed src file: ', src); 205 | isEqual = false; 206 | } 207 | } 208 | return isEqual; 209 | } 210 | 211 | // Check whether the src files are changed between this run and last compile. 212 | function checkSrcChanged(src): boolean { 213 | const circomDir = require.resolve('circom'); 214 | const parser = require(path.join(circomDir, '..', 'parser/jaz.js')).parser; 215 | const srcContents = new Map(); 216 | traverse(src); 217 | const srcHashes = calculateSrcHashes(srcContents); 218 | const oldSrcHashes = loadOldSrcHashes(src); 219 | if (oldSrcHashes == null || !isSrcHashesEqual(srcHashes, oldSrcHashes)) { 220 | writeSrcHashes(srcHashes, src); 221 | return true; 222 | } else { 223 | return false; 224 | } 225 | function traverse(src) { 226 | const content = fs.readFileSync(src, 'utf8'); 227 | srcContents.set(src, content); 228 | const ast = parser.parse(content); 229 | for (const stat of ast.statements) { 230 | if (stat.type == 'INCLUDE') { 231 | let includedFile = stat.file; 232 | if (!path.isAbsolute(includedFile)) { 233 | includedFile = path.normalize(path.join(src, '..', includedFile)); 234 | } 235 | if (!srcContents.has(includedFile)) { 236 | traverse(includedFile); 237 | } 238 | } 239 | } 240 | } 241 | } 242 | 243 | async function compileCircuitDir(circuitDirName, { alwaysRecompile, verbose, backend, sanityCheck }) { 244 | // console.log('compiling dir', circuitDirName); 245 | const circuitFilePath = path.join(circuitDirName, 'circuit.circom'); 246 | const r1csFilepath = path.join(circuitDirName, 'circuit.r1cs'); 247 | const symFilepath = path.join(circuitDirName, 'circuit.sym'); 248 | let binaryFilePath: string; 249 | if (backend === 'native') { 250 | if (sanityCheck) { 251 | binaryFilePath = path.join(circuitDirName, 'circuit'); 252 | } else { 253 | binaryFilePath = path.join(circuitDirName, 'circuit.fast'); 254 | } 255 | 256 | if (process.platform === 'win32'){ 257 | binaryFilePath += '.exe' 258 | } 259 | 260 | } else { 261 | binaryFilePath = path.join(circuitDirName, 'circuit.wasm'); 262 | } 263 | if ( 264 | !alwaysRecompile && 265 | fs.existsSync(binaryFilePath) && 266 | fs.statSync(binaryFilePath).size > 0 && 267 | checkSrcChanged(circuitFilePath) === false 268 | ) { 269 | if (verbose) { 270 | console.log('skip compiling binary ', binaryFilePath); 271 | } 272 | return { circuitFilePath, r1csFilepath, symFilepath, binaryFilePath }; 273 | } 274 | console.log('compile', circuitDirName); 275 | 276 | if (backend === 'native') { 277 | await compileNativeBinary({ 278 | circuitDirName, 279 | r1csFilepath, 280 | circuitFilePath, 281 | symFilepath, 282 | binaryFilePath, 283 | verbose, 284 | sanityCheck, 285 | alwaysRecompile, 286 | }); 287 | } else { 288 | // sanity check is not supported for wasm backend now 289 | await compileWasmBinary({ circuitDirName, r1csFilepath, circuitFilePath, symFilepath, binaryFilePath, verbose }); 290 | } 291 | return { circuitFilePath, r1csFilepath, symFilepath, binaryFilePath }; 292 | } 293 | 294 | class WitnessGenerator { 295 | circuit: any; 296 | component: any; 297 | name: string; 298 | circuitDirName: string; 299 | binaryFilePath: string; 300 | alwaysRecompile: boolean; 301 | sanityCheck: boolean; 302 | verbose: boolean; 303 | writeExpectedOutput: boolean; 304 | backend: string; 305 | constructor( 306 | name, 307 | { backend, alwaysRecompile, verbose, sanityCheck } = { backend: 'native', alwaysRecompile: true, verbose: false, sanityCheck: true }, 308 | ) { 309 | this.name = name; 310 | // we can specify cached files to avoid compiling every time 311 | this.alwaysRecompile = alwaysRecompile; 312 | this.verbose = verbose; 313 | this.backend = backend; 314 | this.sanityCheck = sanityCheck; 315 | if (this.verbose) { 316 | if (this.backend == 'wasm' && this.sanityCheck) { 317 | console.log('WARN: sanity check is not supported for wasm backend now'); 318 | } 319 | console.log(`node: ${shelljs.which('node')} ${NODE_ARGS}`); 320 | } 321 | } 322 | 323 | async chooseBackend() { 324 | const needFeatures = ['bmi2', 'adx']; 325 | let cpuFeatures = (await si.cpu()).flags.split(/\s/); 326 | if (process.platform === 'darwin') { 327 | const stdout = shelljs.exec('sysctl machdep.cpu.leaf7_features', { silent: true }); 328 | const features = stdout.trim().toLowerCase().split(/\s/).slice(1); 329 | cpuFeatures.push(...features); 330 | } 331 | for (const f of needFeatures) { 332 | if (!cpuFeatures.includes(f)) { 333 | console.log(`cpu missing needed feature ${f} for native backend, fallback to wasm`); 334 | console.log(`cpus earlier than Intel Boradwell / AMD Ryzen are not supported for native backend`); 335 | return 'wasm'; 336 | } 337 | } 338 | return 'native'; 339 | } 340 | 341 | async compile(circuitDirName) { 342 | this.circuitDirName = path.resolve(circuitDirName); 343 | if (this.backend === 'auto') { 344 | this.backend = await this.chooseBackend(); 345 | } 346 | 347 | const { r1csFilepath, symFilepath, binaryFilePath } = await compileCircuitDir(this.circuitDirName, { 348 | alwaysRecompile: this.alwaysRecompile, 349 | verbose: this.verbose, 350 | backend: this.backend, 351 | sanityCheck: this.sanityCheck, 352 | }); 353 | this.binaryFilePath = binaryFilePath; 354 | return { r1csFilepath, symFilepath }; 355 | } 356 | 357 | async generateWitness(inputFilePath: string, witnessFilePath: string) { 358 | var cmd: string; 359 | if (this.binaryFilePath == '' || !fs.existsSync(this.binaryFilePath)) { 360 | throw new Error('invalid bin ' + this.binaryFilePath + '. Has the circuit been compiled?'); 361 | } 362 | if (!witnessFilePath.endsWith('.json') && !witnessFilePath.endsWith('.wtns')) { 363 | throw new Error('invalid witness file type'); 364 | } 365 | // gen witness 366 | if (this.backend === 'native') { 367 | cmd = `${this.binaryFilePath} ${inputFilePath} ${witnessFilePath}`; 368 | if (this.verbose) { 369 | console.log(cmd); 370 | } 371 | const genWtnsOut = shelljs.exec(cmd, { silent: !this.verbose }); 372 | 373 | if (genWtnsOut.stderr || genWtnsOut.code != 0) { 374 | if (!this.verbose) { 375 | // don't display stderr twice 376 | console.error('\n' + genWtnsOut.stderr); 377 | } 378 | throw new Error('Could not generate witness'); 379 | } 380 | } else { 381 | const snarkjsPath = path.join(require.resolve('snarkjs'), '..', 'cli.cjs'); 382 | let witnessBinFile; 383 | if (witnessFilePath.endsWith('.json')) { 384 | witnessBinFile = witnessFilePath.replace(/json$/, 'wtns'); 385 | } else { 386 | witnessBinFile = witnessFilePath; 387 | } 388 | // calculate witness bin file 389 | const sameProcess = process.platform !== 'win32'; 390 | if (sameProcess) { 391 | const input = unstringifyBigInts(JSON.parse(await fs.promises.readFile(inputFilePath, 'utf8'))); 392 | await wtns.calculate(input, this.binaryFilePath, witnessBinFile, defaultWitnessOption()); 393 | } else { 394 | cmd = `${NODE_CMD} ${snarkjsPath} wc ${this.binaryFilePath} ${inputFilePath} ${witnessBinFile}`; 395 | shelljs.exec(cmd); 396 | } 397 | // convert bin witness to json witness if needed 398 | if (witnessFilePath.endsWith('.json')) { 399 | cmd = `${NODE_CMD} ${snarkjsPath} wej ${witnessBinFile} ${witnessFilePath}`; 400 | shelljs.exec(cmd); 401 | } 402 | } 403 | } 404 | } 405 | 406 | function defaultWitnessOption() { 407 | let logFn = console.log; 408 | let calculateWitnessOptions = { 409 | sanityCheck: true, 410 | logTrigger: logFn, 411 | logOutput: logFn, 412 | logStartComponent: logFn, 413 | logFinishComponent: logFn, 414 | logSetSignal: logFn, 415 | logGetSignal: logFn, 416 | }; 417 | return calculateWitnessOptions; 418 | } 419 | 420 | export { WitnessGenerator, compileCircuitDir }; 421 | -------------------------------------------------------------------------------- /testdata/num2bits/circuit.circom: -------------------------------------------------------------------------------- 1 | template Num2Bits(n) { 2 | signal input in; 3 | signal output out[n]; 4 | var lc1=0; 5 | 6 | for (var i = 0; i> i) & 1; 8 | out[i] * (out[i] -1 ) === 0; 9 | lc1 += out[i] * 2**i; 10 | } 11 | 12 | lc1 === in; 13 | } 14 | 15 | component main = Num2Bits(8); -------------------------------------------------------------------------------- /testdata/num2bits/data/case01/input.json: -------------------------------------------------------------------------------- 1 | { "in": 3 } 2 | -------------------------------------------------------------------------------- /testdata/num2bits/data/case01/output.json: -------------------------------------------------------------------------------- 1 | { "out": ["1", "1", "0", "0", "0", "0", "0", "0"] } 2 | -------------------------------------------------------------------------------- /testdata/num2bits_err/circuit.circom: -------------------------------------------------------------------------------- 1 | template CheckNumAndBits(n) { 2 | signal input num; 3 | signal input bits[n]; 4 | var sum=0; 5 | 6 | for (var i = 0; i The default is to use the Microsoft ABI when targeting Windows. On all other systems, the default is the System V ELF ABI. 12 | 13 | It should note that some function in ffiasm is planned to be convert into asm so we may need our hacking up-to-date. -------------------------------------------------------------------------------- /win32/runtime/fr.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __FR_H 2 | #define __FR_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define Fr_N64 4 9 | #define Fr_SHORT 0x00000000 10 | #define Fr_LONG 0x80000000 11 | #define Fr_LONGMONTGOMERY 0xC0000000 12 | typedef uint64_t FrRawElement[Fr_N64]; 13 | typedef struct __attribute__((__packed__)) { 14 | int32_t shortVal; 15 | uint32_t type; 16 | FrRawElement longVal; 17 | } FrElement; 18 | typedef FrElement *PFrElement; 19 | extern FrElement Fr_q; 20 | extern FrElement Fr_R3; 21 | extern FrRawElement Fr_rawq; 22 | extern FrRawElement Fr_rawR3; 23 | 24 | extern "C" __attribute__((sysv_abi)) __attribute__((sysv_abi)) void Fr_copy(PFrElement r, PFrElement a); 25 | extern "C" __attribute__((sysv_abi)) void Fr_copyn(PFrElement r, PFrElement a, int n); 26 | extern "C" __attribute__((sysv_abi)) void Fr_add(PFrElement r, PFrElement a, PFrElement b); 27 | extern "C" __attribute__((sysv_abi)) void Fr_sub(PFrElement r, PFrElement a, PFrElement b); 28 | extern "C" __attribute__((sysv_abi)) void Fr_neg(PFrElement r, PFrElement a); 29 | extern "C" __attribute__((sysv_abi)) void Fr_mul(PFrElement r, PFrElement a, PFrElement b); 30 | extern "C" __attribute__((sysv_abi)) void Fr_square(PFrElement r, PFrElement a); 31 | extern "C" __attribute__((sysv_abi)) void Fr_band(PFrElement r, PFrElement a, PFrElement b); 32 | extern "C" __attribute__((sysv_abi)) void Fr_bor(PFrElement r, PFrElement a, PFrElement b); 33 | extern "C" __attribute__((sysv_abi)) void Fr_bxor(PFrElement r, PFrElement a, PFrElement b); 34 | extern "C" __attribute__((sysv_abi)) void Fr_bnot(PFrElement r, PFrElement a); 35 | extern "C" __attribute__((sysv_abi)) void Fr_eq(PFrElement r, PFrElement a, PFrElement b); 36 | extern "C" __attribute__((sysv_abi)) void Fr_neq(PFrElement r, PFrElement a, PFrElement b); 37 | extern "C" __attribute__((sysv_abi)) void Fr_lt(PFrElement r, PFrElement a, PFrElement b); 38 | extern "C" __attribute__((sysv_abi)) void Fr_gt(PFrElement r, PFrElement a, PFrElement b); 39 | extern "C" __attribute__((sysv_abi)) void Fr_leq(PFrElement r, PFrElement a, PFrElement b); 40 | extern "C" __attribute__((sysv_abi)) void Fr_geq(PFrElement r, PFrElement a, PFrElement b); 41 | extern "C" __attribute__((sysv_abi)) void Fr_land(PFrElement r, PFrElement a, PFrElement b); 42 | extern "C" __attribute__((sysv_abi)) void Fr_lor(PFrElement r, PFrElement a, PFrElement b); 43 | extern "C" __attribute__((sysv_abi)) void Fr_lnot(PFrElement r, PFrElement a); 44 | extern "C" __attribute__((sysv_abi)) void Fr_toNormal(PFrElement r, PFrElement a); 45 | extern "C" __attribute__((sysv_abi)) void Fr_toLongNormal(PFrElement r, PFrElement a); 46 | extern "C" __attribute__((sysv_abi)) void Fr_toMontgomery(PFrElement r, PFrElement a); 47 | 48 | extern "C" __attribute__((sysv_abi)) int Fr_isTrue(PFrElement pE); 49 | extern "C" __attribute__((sysv_abi)) int Fr_toInt(PFrElement pE); 50 | 51 | extern "C" __attribute__((sysv_abi)) void Fr_rawCopy(FrRawElement pRawResult, FrRawElement pRawA); 52 | extern "C" __attribute__((sysv_abi)) void Fr_rawSwap(FrRawElement pRawResult, FrRawElement pRawA); 53 | extern "C" __attribute__((sysv_abi)) void Fr_rawAdd(FrRawElement pRawResult, FrRawElement pRawA, FrRawElement pRawB); 54 | extern "C" __attribute__((sysv_abi)) void Fr_rawSub(FrRawElement pRawResult, FrRawElement pRawA, FrRawElement pRawB); 55 | extern "C" __attribute__((sysv_abi)) void Fr_rawNeg(FrRawElement pRawResult, FrRawElement pRawA); 56 | extern "C" __attribute__((sysv_abi)) void Fr_rawMMul(FrRawElement pRawResult, FrRawElement pRawA, FrRawElement pRawB); 57 | extern "C" __attribute__((sysv_abi)) void Fr_rawMSquare(FrRawElement pRawResult, FrRawElement pRawA); 58 | extern "C" __attribute__((sysv_abi)) void Fr_rawMMul1(FrRawElement pRawResult, FrRawElement pRawA, uint64_t pRawB); 59 | extern "C" __attribute__((sysv_abi)) void Fr_rawToMontgomery(FrRawElement pRawResult, FrRawElement pRawA); 60 | extern "C" __attribute__((sysv_abi)) void Fr_rawFromMontgomery(FrRawElement pRawResult, FrRawElement pRawA); 61 | extern "C" __attribute__((sysv_abi)) int Fr_rawIsEq(FrRawElement pRawA, FrRawElement pRawB); 62 | extern "C" __attribute__((sysv_abi)) int Fr_rawIsZero(FrRawElement pRawB); 63 | 64 | extern "C" __attribute__((sysv_abi)) void Fr_fail(); 65 | 66 | 67 | // Pending functions to convert 68 | 69 | void Fr_str2element(PFrElement pE, char const*s); 70 | char *Fr_element2str(PFrElement pE); 71 | void Fr_idiv(PFrElement r, PFrElement a, PFrElement b); 72 | void Fr_mod(PFrElement r, PFrElement a, PFrElement b); 73 | void Fr_inv(PFrElement r, PFrElement a); 74 | void Fr_div(PFrElement r, PFrElement a, PFrElement b); 75 | void Fr_shl(PFrElement r, PFrElement a, PFrElement b); 76 | void Fr_shr(PFrElement r, PFrElement a, PFrElement b); 77 | void Fr_pow(PFrElement r, PFrElement a, PFrElement b); 78 | 79 | class RawFr { 80 | 81 | public: 82 | const static int N64 = Fr_N64; 83 | const static int MaxBits = 254; 84 | 85 | 86 | struct Element { 87 | FrRawElement v; 88 | }; 89 | 90 | private: 91 | Element fZero; 92 | Element fOne; 93 | Element fNegOne; 94 | 95 | public: 96 | 97 | RawFr(); 98 | ~RawFr(); 99 | 100 | Element &zero() { return fZero; }; 101 | Element &one() { return fOne; }; 102 | Element &negOne() { return fNegOne; }; 103 | 104 | void fromString(Element &r, std::string n); 105 | std::string toString(Element &a, uint32_t radix = 10); 106 | 107 | void inline copy(Element &r, Element &a) { Fr_rawCopy(r.v, a.v); }; 108 | void inline swap(Element &a, Element &b) { Fr_rawSwap(a.v, b.v); }; 109 | void inline add(Element &r, Element &a, Element &b) { Fr_rawAdd(r.v, a.v, b.v); }; 110 | void inline sub(Element &r, Element &a, Element &b) { Fr_rawSub(r.v, a.v, b.v); }; 111 | void inline mul(Element &r, Element &a, Element &b) { Fr_rawMMul(r.v, a.v, b.v); }; 112 | void inline mul1(Element &r, Element &a, uint64_t b) { Fr_rawMMul1(r.v, a.v, b); }; 113 | void inline neg(Element &r, Element &a) { Fr_rawNeg(r.v, a.v); }; 114 | void inline square(Element &r, Element &a) { Fr_rawMSquare(r.v, a.v); }; 115 | void inv(Element &r, Element &a); 116 | void div(Element &r, Element &a, Element &b); 117 | 118 | void inline toMontgomery(Element &r, Element &a) { Fr_rawToMontgomery(r.v, a.v); }; 119 | void inline fromMontgomery(Element &r, Element &a) { Fr_rawFromMontgomery(r.v, a.v); }; 120 | int inline eq(Element &a, Element &b) { return Fr_rawIsEq(a.v, b.v); }; 121 | int inline isZero(Element &a) { return Fr_rawIsZero(a.v); }; 122 | 123 | void toMpz(mpz_t r, Element &a); 124 | 125 | }; 126 | 127 | 128 | #endif // __FR_H 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /win32/runtime/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | using json = nlohmann::json; 14 | 15 | #include "calcwit.hpp" 16 | #include "circom.hpp" 17 | #include "utils.hpp" 18 | 19 | Circom_Circuit *circuit; 20 | 21 | 22 | #define handle_error(msg) \ 23 | do { perror(msg); exit(EXIT_FAILURE); } while (0) 24 | 25 | #define SHMEM_WITNESS_KEY (123456) 26 | 27 | // assumptions 28 | // 1) There is only one key assigned for shared memory. This means 29 | // that only one witness can be computed and used at a time. If several witness 30 | // are computed before calling the prover, witness memory will be overwritten. 31 | // 2) Prover is responsible for releasing memory once is done with witness 32 | // 33 | // File format: 34 | // Type : 4B (wshm) 35 | // Version : 4B 36 | // N Section : 4B 37 | // HDR1 : 12B 38 | // N8 : 4B 39 | // Fr : N8 B 40 | // NVars : 4B 41 | // HDR2 : 12B 42 | // ShmemKey : 4B 43 | // Status : 4B (0:OK, 0xFFFF: KO) 44 | // ShmemID : 4B 45 | /*void writeOutShmem(Circom_CalcWit *ctx, std::string filename) { 46 | FILE *write_ptr; 47 | u64 *shbuf; 48 | int shmid, status = 0; 49 | 50 | write_ptr = fopen(filename.c_str(),"wb"); 51 | 52 | fwrite("wshm", 4, 1, write_ptr); 53 | 54 | u32 version = 2; 55 | fwrite(&version, 4, 1, write_ptr); 56 | 57 | u32 nSections = 2; 58 | fwrite(&nSections, 4, 1, write_ptr); 59 | 60 | // Header 61 | u32 idSection1 = 1; 62 | fwrite(&idSection1, 4, 1, write_ptr); 63 | 64 | u32 n8 = Fr_N64*8; 65 | 66 | u64 idSection1length = 8 + n8; 67 | fwrite(&idSection1length, 8, 1, write_ptr); 68 | 69 | fwrite(&n8, 4, 1, write_ptr); 70 | 71 | fwrite(Fr_q.longVal, Fr_N64*8, 1, write_ptr); 72 | 73 | u32 nVars = circuit->NVars; 74 | fwrite(&nVars, 4, 1, write_ptr); 75 | 76 | // Data 77 | u32 idSection2 = 2; 78 | fwrite(&idSection2, 4, 1, write_ptr); 79 | 80 | u64 idSection2length = n8*circuit->NVars; 81 | fwrite(&idSection2length, 8, 1, write_ptr); 82 | 83 | 84 | // generate key 85 | key_t key = SHMEM_WITNESS_KEY; 86 | fwrite(&key, sizeof(key_t), 1, write_ptr); 87 | 88 | // Setup shared memory 89 | if ((shmid = shmget(key, circuit->NVars * Fr_N64 * sizeof(u64), IPC_CREAT | 0666)) < 0) { 90 | // preallocated shared memory segment is too small => Retrieve id by accesing old segment 91 | // Delete old segment and create new with corret size 92 | shmid = shmget(key, 4, IPC_CREAT | 0666); 93 | shmctl(shmid, IPC_RMID, NULL); 94 | if ((shmid = shmget(key, circuit->NVars * Fr_N64 * sizeof(u64), IPC_CREAT | 0666)) < 0){ 95 | status = -1; 96 | fwrite(&status, sizeof(status), 1, write_ptr); 97 | fclose(write_ptr); 98 | return ; 99 | } 100 | } 101 | 102 | // Attach shared memory 103 | if ((shbuf = (u64 *)shmat(shmid, NULL, 0)) == (u64 *) -1) { 104 | status = -1; 105 | fwrite(&status, sizeof(status), 1, write_ptr); 106 | fclose(write_ptr); 107 | return; 108 | } 109 | fwrite(&status, sizeof(status), 1, write_ptr); 110 | 111 | fwrite(&shmid, sizeof(u32), 1, write_ptr); 112 | fclose(write_ptr); 113 | 114 | 115 | #pragma omp parallel for 116 | for (int i=0; iNVars;i++) { 117 | FrElement v; 118 | ctx->getWitness(i, &v); 119 | Fr_toLongNormal(&v, &v); 120 | memcpy(&shbuf[i*Fr_N64], v.longVal, Fr_N64*sizeof(u64)); 121 | } 122 | }*/ 123 | 124 | 125 | void loadBin(Circom_CalcWit *ctx, std::string filename) { 126 | int fd; 127 | struct stat sb; 128 | 129 | // map input 130 | fd = open(filename.c_str(), O_RDONLY); 131 | if (fd == -1) 132 | handle_error("open"); 133 | 134 | if (fstat(fd, &sb) == -1) /* To obtain file size */ 135 | handle_error("fstat"); 136 | 137 | 138 | u8 *in; 139 | 140 | in = (u8 *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 141 | if (in == MAP_FAILED) 142 | handle_error("mmap"); 143 | 144 | close(fd); 145 | 146 | FrElement v; 147 | u8 *p = in; 148 | for (int i=0; iNInputs; i++) { 149 | v.type = Fr_LONG; 150 | for (int j=0; jsetSignal(0, 0, circuit->wit2sig[1 + circuit->NOutputs + i], &v); 155 | } 156 | } 157 | 158 | 159 | typedef void (*ItFunc)(Circom_CalcWit *ctx, int idx, json val); 160 | 161 | void iterateArr(Circom_CalcWit *ctx, int o, Circom_Sizes sizes, json jarr, ItFunc f) { 162 | if (!jarr.is_array()) { 163 | assert((sizes[0] == 1)&&(sizes[1] == 0)); 164 | f(ctx, o, jarr); 165 | } else { 166 | int n = sizes[0] / sizes[1]; 167 | for (int i=0; i(); 181 | } else if (val.is_number()) { 182 | 183 | double vd = val.get(); 184 | std::stringstream stream; 185 | stream << std::fixed << std::setprecision(0) << vd; 186 | s = stream.str(); 187 | } else { 188 | handle_error("Invalid JSON type"); 189 | } 190 | 191 | Fr_str2element (&v, s.c_str()); 192 | 193 | ctx->setSignal(0, 0, o, &v); 194 | } 195 | 196 | void loadJson(Circom_CalcWit *ctx, std::string filename) { 197 | std::ifstream inStream(filename); 198 | json j; 199 | inStream >> j; 200 | 201 | u64 nItems = j.size(); 202 | printf("Items : %llu\n",nItems); 203 | for (json::iterator it = j.begin(); it != j.end(); ++it) { 204 | // std::cout << it.key() << " => " << it.value() << '\n'; 205 | u64 h = fnv1a(it.key()); 206 | int o; 207 | try { 208 | o = ctx->getSignalOffset(0, h); 209 | } catch (std::runtime_error e) { 210 | std::ostringstream errStrStream; 211 | errStrStream << "Error loadin variable: " << it.key() << "\n" << e.what(); 212 | throw std::runtime_error(errStrStream.str() ); 213 | } 214 | Circom_Sizes sizes = ctx->getSignalSizes(0, h); 215 | iterateArr(ctx, o, sizes, it.value(), itFunc); 216 | } 217 | } 218 | 219 | 220 | void writeOutBin(Circom_CalcWit *ctx, std::string filename) { 221 | FILE *write_ptr; 222 | 223 | write_ptr = fopen(filename.c_str(),"wb"); 224 | 225 | fwrite("wtns", 4, 1, write_ptr); 226 | 227 | u32 version = 2; 228 | fwrite(&version, 4, 1, write_ptr); 229 | 230 | u32 nSections = 2; 231 | fwrite(&nSections, 4, 1, write_ptr); 232 | 233 | // Header 234 | u32 idSection1 = 1; 235 | fwrite(&idSection1, 4, 1, write_ptr); 236 | 237 | u32 n8 = Fr_N64*8; 238 | 239 | u64 idSection1length = 8 + n8; 240 | fwrite(&idSection1length, 8, 1, write_ptr); 241 | 242 | fwrite(&n8, 4, 1, write_ptr); 243 | 244 | fwrite(Fr_q.longVal, Fr_N64*8, 1, write_ptr); 245 | 246 | u32 nVars = circuit->NVars; 247 | fwrite(&nVars, 4, 1, write_ptr); 248 | 249 | 250 | // Data 251 | u32 idSection2 = 2; 252 | fwrite(&idSection2, 4, 1, write_ptr); 253 | 254 | u64 idSection2length = (u64)n8*(u64)circuit->NVars; 255 | fwrite(&idSection2length, 8, 1, write_ptr); 256 | 257 | FrElement v; 258 | 259 | for (int i=0;iNVars;i++) { 260 | ctx->getWitness(i, &v); 261 | Fr_toLongNormal(&v, &v); 262 | fwrite(v.longVal, Fr_N64*8, 1, write_ptr); 263 | } 264 | fclose(write_ptr); 265 | 266 | } 267 | 268 | 269 | void writeOutJson(Circom_CalcWit *ctx, std::string filename) { 270 | 271 | std::ofstream outFile; 272 | outFile.open (filename); 273 | 274 | outFile << "[\n"; 275 | 276 | FrElement v; 277 | 278 | for (int i=0;iNVars;i++) { 279 | ctx->getWitness(i, &v); 280 | char *pcV = Fr_element2str(&v); 281 | std::string sV = std::string(pcV); 282 | outFile << (i ? "," : " ") << "\"" << sV << "\"\n"; 283 | free(pcV); 284 | } 285 | 286 | outFile << "]\n"; 287 | outFile.close(); 288 | } 289 | 290 | bool hasEnding (std::string const &fullString, std::string const &ending) { 291 | if (fullString.length() >= ending.length()) { 292 | return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); 293 | } else { 294 | return false; 295 | } 296 | } 297 | 298 | #define ADJ_P(a) *((void **)&a) = (void *)(((char *)circuit)+ (uint64_t)(a)) 299 | 300 | Circom_Circuit *loadCircuit(std::string const &datFileName) { 301 | Circom_Circuit *circuitF; 302 | Circom_Circuit *circuit; 303 | 304 | int fd; 305 | struct stat sb; 306 | 307 | fd = open(datFileName.c_str(), O_RDONLY); 308 | if (fd == -1) { 309 | std::cout << ".dat file not found: " << datFileName << "\n"; 310 | throw std::system_error(errno, std::generic_category(), "open"); 311 | } 312 | 313 | if (fstat(fd, &sb) == -1) { /* To obtain file size */ 314 | throw std::system_error(errno, std::generic_category(), "fstat"); 315 | } 316 | 317 | circuitF = (Circom_Circuit *)mmap(NULL, sb.st_size, PROT_READ , MAP_PRIVATE, fd, 0); 318 | close(fd); 319 | 320 | circuit = (Circom_Circuit *)malloc(sb.st_size); 321 | memcpy((void *)circuit, (void *)circuitF, sb.st_size); 322 | 323 | munmap(circuitF, sb.st_size); 324 | 325 | ADJ_P(circuit->wit2sig); 326 | ADJ_P(circuit->components); 327 | ADJ_P(circuit->mapIsInput); 328 | ADJ_P(circuit->constants); 329 | ADJ_P(circuit->P); 330 | ADJ_P(circuit->componentEntries); 331 | 332 | for (int i=0; iNComponents; i++) { 333 | ADJ_P(circuit->components[i].hashTable); 334 | ADJ_P(circuit->components[i].entries); 335 | circuit->components[i].fn = _functionTable[ (uint64_t)circuit->components[i].fn]; 336 | } 337 | 338 | for (int i=0; iNComponentEntries; i++) { 339 | ADJ_P(circuit->componentEntries[i].sizes); 340 | } 341 | 342 | return circuit; 343 | } 344 | 345 | int main(int argc, char *argv[]) { 346 | if (argc!=3) { 347 | std::string cl = argv[0]; 348 | std::string base_filename = cl.substr(cl.find_last_of("/\\") + 1); 349 | std::cout << "Usage: " << base_filename << " > >\n"; 350 | } else { 351 | 352 | struct timeval begin, end; 353 | long seconds, microseconds; 354 | double elapsed; 355 | 356 | gettimeofday(&begin,0); 357 | 358 | std::string datFileName = argv[0]; 359 | datFileName += ".dat"; 360 | 361 | circuit = loadCircuit(datFileName); 362 | 363 | // open output 364 | Circom_CalcWit *ctx = new Circom_CalcWit(circuit); 365 | 366 | std::string infilename = argv[1]; 367 | gettimeofday(&end,0); 368 | seconds = end.tv_sec - begin.tv_sec; 369 | microseconds = end.tv_usec - begin.tv_usec; 370 | elapsed = seconds + microseconds*1e-6; 371 | 372 | printf("Up to loadJson %.20f\n", elapsed); 373 | 374 | if (hasEnding(infilename, std::string(".bin"))) { 375 | loadBin(ctx, infilename); 376 | } else if (hasEnding(infilename, std::string(".json"))) { 377 | loadJson(ctx, infilename); 378 | } else { 379 | handle_error("Invalid input extension (.bin / .json)"); 380 | } 381 | 382 | ctx->join(); 383 | 384 | // printf("Finished!\n"); 385 | 386 | std::string outfilename = argv[2]; 387 | 388 | if (hasEnding(outfilename, std::string(".wtns"))) { 389 | gettimeofday(&end,0); 390 | seconds = end.tv_sec - begin.tv_sec; 391 | microseconds = end.tv_usec - begin.tv_usec; 392 | elapsed = seconds + microseconds*1e-6; 393 | 394 | printf("Up to WriteWtns %.20f\n", elapsed); 395 | writeOutBin(ctx, outfilename); 396 | } else if (hasEnding(outfilename, std::string(".json"))) { 397 | writeOutJson(ctx, outfilename); 398 | } else if (hasEnding(outfilename, std::string(".wshm"))) { 399 | gettimeofday(&end,0); 400 | seconds = end.tv_sec - begin.tv_sec; 401 | microseconds = end.tv_usec - begin.tv_usec; 402 | elapsed = seconds + microseconds*1e-6; 403 | 404 | printf("Up to WriteShmem %.20f\n", elapsed); 405 | handle_error("can not output to shmem (.wshm) under windows "); 406 | //writeOutShmem(ctx, outfilename); 407 | } else { 408 | handle_error("Invalid output extension (.bin / .json)"); 409 | } 410 | 411 | delete ctx; 412 | gettimeofday(&end,0); 413 | seconds = end.tv_sec - begin.tv_sec; 414 | microseconds = end.tv_usec - begin.tv_usec; 415 | elapsed = seconds + microseconds*1e-6; 416 | 417 | printf("Total %.20f\n", elapsed); 418 | exit(EXIT_SUCCESS); 419 | } 420 | } 421 | --------------------------------------------------------------------------------