From 93330f065b5cd4d8548cc37af4c4078a7f8d92a6 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Thu, 28 Nov 2019 15:10:59 +0100 Subject: [PATCH] for loops --- .gitignore | 3 + Project.sublime-project | 24 + Project.sublime-workspace | 878 +++++++++++++++++++++++++++++++ c/calcwit.cpp | 8 +- c/calcwit.h | 4 +- c/circom.h | 1 + c/zqfield.cpp | 30 ++ c/zqfield.h | 20 + src/c_build.js | 14 +- src/c_gen.js | 275 +++++++--- src/c_tester.js | 9 +- src/compiler.js | 3 + src/exec.js | 2 +- src/utils.js | 56 +- src/zqfield.js | 17 + test/basiccases.js | 62 +++ test/circuits/add.circom | 9 + test/circuits/addconst1.circom | 16 + test/circuits/addin.json | 1 + test/circuits/forrolled.circom | 14 + test/circuits/forunrolled.circom | 10 + test/circuits/in.json | 2 +- test/circuits/out.json | 5 + test/inout.js | 3 +- 24 files changed, 1385 insertions(+), 81 deletions(-) create mode 100644 Project.sublime-project create mode 100644 Project.sublime-workspace create mode 100644 c/zqfield.cpp create mode 100644 c/zqfield.h create mode 100644 src/zqfield.js create mode 100644 test/basiccases.js create mode 100644 test/circuits/add.circom create mode 100644 test/circuits/addconst1.circom create mode 100644 test/circuits/addin.json create mode 100644 test/circuits/forrolled.circom create mode 100644 test/circuits/forunrolled.circom create mode 100644 test/circuits/out.json diff --git a/.gitignore b/.gitignore index 8b18f83..0ad0eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,6 @@ typings/ .next tmp + +.DS_Store + diff --git a/Project.sublime-project b/Project.sublime-project new file mode 100644 index 0000000..1d35805 --- /dev/null +++ b/Project.sublime-project @@ -0,0 +1,24 @@ + { + "folders": [ + { + "path": ".", + } + ], + "settings": { + "SublimeAnarchyDebug": { + "debug": { + "executable": "${project_path}/test/circuits/add", + "params": [ + "addin.json", + "out.bin", + ], + "path": [ + ], + "environment": [ + ], + "working_dir": "${project_path}" + } + } + } + } + diff --git a/Project.sublime-workspace b/Project.sublime-workspace new file mode 100644 index 0000000..b943a7c --- /dev/null +++ b/Project.sublime-workspace @@ -0,0 +1,878 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "ins", + "instantiateConstant" + ], + [ + "c", + "circuit" + ], + [ + "type", + "typeof" + ], + [ + "b", + "bName" + ], + [ + "e", + "eOut" + ], + [ + "sym", + "symbols" + ], + [ + "res", + "resStr" + ], + [ + "base", + "baseName" + ], + [ + "a", + "async" + ], + [ + "out", + "outFile" + ], + [ + "addS", + "addSymbolArray" + ], + [ + "of", + "offset" + ], + [ + "si", + "signal" + ], + [ + "s", + "signals" + ], + [ + "dir", + "dirName" + ], + [ + "main", + "mainComponent" + ], + [ + "ci", + "circomFile" + ], + [ + "pc", + "pcV1" + ], + [ + "re", + "require" + ], + [ + "buildC", + "buildCircuit" + ], + [ + "Ti", + "Ticker" + ], + [ + "Ma", + "MaxBandwith" + ], + [ + "Bl", + "BlackList" + ], + [ + "Max", + "MaxConnections" + ], + [ + "uint", + "uint64" + ], + [ + "he", + "helloLen" + ], + [ + "Pr", + "Printf" + ], + [ + "n", + "nil" + ], + [ + "ne", + "newBuff" + ], + [ + "new", + "newBuff" + ], + [ + "H", + "HiddenDomain" + ], + [ + "Hidden", + "hiddenDomain" + ], + [ + "ex", + "extensionLen" + ], + [ + "sess", + "sessionLen" + ], + [ + "ses", + "sessionId" + ], + [ + "exte", + "extensionLen" + ], + [ + "se", + "sessionLen" + ], + [ + "by", + "byte" + ], + [ + "clie", + "clientKS" + ], + [ + "de", + "decrypter" + ], + [ + "buf", + "buftype" + ], + [ + "set", + "setSignal" + ], + [ + "ha", + "handle_error" + ], + [ + "in", + "infilename" + ], + [ + "wri", + "writeOut" + ], + [ + "str", + "string" + ], + [ + "it", + "itFunc" + ], + [ + "iter", + "iterateArr" + ], + [ + "idx", + "idxInput" + ], + [ + "NS", + "NSignals" + ], + [ + "en", + "entryPos" + ], + [ + "h", + "hIdx" + ], + [ + "nS", + "NSignals" + ], + [ + "inc", + "include" + ], + [ + "bui", + "buildOutArray" + ], + [ + "N", + "NVars" + ], + [ + "pC", + "pCurrent" + ], + [ + "acc", + "account2" + ], + [ + "tim", + "timestamp" + ], + [ + "not", + "notAvTxs" + ], + [ + "con", + "continue" + ], + [ + "on", + "onChainTxs" + ], + [ + "ge", + "getOperatorFee" + ], + [ + "use", + "userFee" + ], + [ + "no", + "normalizedFee" + ], + [ + "fi", + "firstNonce" + ], + [ + "am", + "amountF" + ], + [ + "cal", + "calcSlots" + ], + [ + "tx", + "txPool" + ], + [ + "non", + "nonExecutableSlots" + ], + [ + "max", + "maxSlots" + ], + [ + "slo", + "slotKeys" + ], + [ + "Tx", + "TXPool" + ], + [ + "cla", + "_classifyTxs" + ], + [ + "remo", + "removed" + ], + [ + "u", + "updateSlotsPending" + ], + [ + "txs", + "txsByCoin" + ], + [ + "forged", + "forgedTxs" + ], + [ + "removed", + "removedCoins" + ], + [ + "bestN", + "bestNTx" + ], + [ + "av", + "avTxs" + ], + [ + "ad", + "adjustedFee" + ], + [ + "M", + "MaxCoins" + ], + [ + "norm", + "normalizedFee" + ], + [ + "us", + "userFeeF" + ], + [ + "from", + "idxFrom" + ], + [ + "eq", + "equal" + ], + [ + "fee", + "feePlanCoins" + ], + [ + "as", + "async" + ], + [ + "ke", + "key" + ], + [ + "pro", + "promises" + ], + [ + "DB", + "DB_Master" + ], + [ + "va", + "value" + ], + [ + "add", + "addSizes" + ], + [ + "labe", + "labelSize" + ], + [ + "las", + "last_column" + ], + [ + "stat", + "statements" + ], + [ + "firs", + "first_column" + ], + [ + "f", + "first_line" + ], + [ + "tmp", + "tmpNames" + ], + [ + "su", + "suggestedName" + ], + [ + "fn", + "fnvHash" + ], + [ + "def", + "definedHashTables" + ], + [ + "le", + "length" + ], + [ + "la", + "labelName" + ], + [ + "size", + "sizes" + ], + [ + "code", + "codeFooter" + ], + [ + "sc", + "scopes" + ], + [ + "rN", + "rName" + ], + [ + "genGet", + "genGetSignal" + ], + [ + "label", + "labelName" + ], + [ + "instan", + "instantiateRef" + ], + [ + "v", + "vOffset" + ], + [ + "BIGIN", + "BIGINT" + ], + [ + "st", + "stack" + ], + [ + "SI", + "SIZES" + ], + [ + "newS", + "newStackVar" + ], + [ + "sco", + "scopes" + ], + [ + "cons", + "constant" + ], + [ + "val", + "value" + ], + [ + "com", + "components" + ], + [ + "calc", + "calcAcc" + ], + [ + "ca", + "calcStr" + ], + [ + "to", + "toJSNumber" + ], + [ + "get", + "getTmpName" + ], + [ + "cS", + "cSourceDone" + ], + [ + "r1", + "r1csDone" + ], + [ + "B", + "Buffer" + ] + ] + }, + "buffers": + [ + ], + "build_system": "", + "build_system_choices": + [ + ], + "build_varint": "", + "command_palette": + { + "height": 0.0, + "last_filter": "", + "selected_items": + [ + [ + "in", + "Package Control: Install Package" + ], + [ + "ins", + "Package Control: Install Package" + ], + [ + "", + "Arithmetic" + ], + [ + "Package Control: ", + "Package Control: Disable Package" + ], + [ + "install", + "Package Control: Install Package" + ] + ], + "width": 0.0 + }, + "console": + { + "height": 210.0, + "history": + [ + ] + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": false, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "file_history": + [ + "/Users/jbaylina/git/iden3/circom/cli.js", + "/Users/jbaylina/git/iden3/circom/src/c_build.js", + "/Users/jbaylina/git/iden3/circom/src/c_gen.js", + "/Users/jbaylina/git/iden3/circom/test/circuits/add.circom", + "/Users/jbaylina/git/iden3/circom/src/c_tester.js", + "/Users/jbaylina/git/iden3/circom/test/basiccases.js", + "/Users/jbaylina/git/iden3/circom/test/inout.js", + "/Users/jbaylina/git/iden3/circom/c/main.cpp", + "/Users/jbaylina/git/iden3/circom/src/utils.js", + "/Users/jbaylina/git/iden3/circom/src/zqfield.js", + "/Users/jbaylina/git/iden3/circom/src/compiler.js", + "/Users/jbaylina/git/iden3/circom/c/calcwit.h", + "/Users/jbaylina/git/iden3/circom/c/zqfield.cpp", + "/Users/jbaylina/git/iden3/circom/c/circom.h", + "/Users/jbaylina/git/iden3/circom/test/circuits/add.cpp", + "/Users/jbaylina/git/iden3/circom/c/zqfield.h", + "/Users/jbaylina/git/iden3/circom/c/calcwit.cpp", + "/Users/jbaylina/git/iden3/circom/src/ctx.js", + "/Users/jbaylina/git/iden3/circom/index.js", + "/Users/jbaylina/git/iden3/circom/test/circuits/inout.cpp", + "/Users/jbaylina/git/iden3/circom/src/tester.js", + "/Users/jbaylina/git/iden3/rollup/doc/rollup_tx.txt", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/tls/conn.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/tls/handshake_client.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/tls/alert.go", + "/Users/jbaylina/git/personal/tls-tris/13.go", + "/Users/jbaylina/git/personal/tls-tris/tls_test.go", + "/Users/jbaylina/git/personal/tls-tris/conn.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/x509/verify.go", + "/Users/jbaylina/git/personal/tls-tris/handshake_client.go", + "/Users/jbaylina/git/personal/testimg/dosfilter.go", + "/Users/jbaylina/git/personal/testimg/main.go", + "/Users/jbaylina/git/personal/hiddenproxy/hiddenproxy/main.go", + "/Users/jbaylina/git/personal/tls-tris/cipher_suites.go", + "/Users/jbaylina/git/personal/hiddenproxy.old/proxy.go", + "/Users/jbaylina/git/personal/hiddenproxy/hiddenproxy/proxy.go", + "/Users/jbaylina/git/personal/hiddenproxy/client-test/client-test", + "/Users/jbaylina/git/personal/hiddenproxy/client-test/main.go", + "/Users/jbaylina/git/personal/tls-tris/key_agreement.go", + "/Users/jbaylina/git/personal/tls-tris/common.go", + "/Users/jbaylina/git/personal/testesni/testesni.go", + "/Users/jbaylina/git/personal/hiddenproxy/main.go", + "/Users/jbaylina/git/personal/testesni2/testesni2.go", + "/Users/jbaylina/git/personal/hiddenproxy/proxy.go", + "/Users/jbaylina/git/personal/hiddenproxy/testenc/main.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/blake2b/blake2b_amd64.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/blake2b/blake2b.go", + "/Users/jbaylina/git/personal/tls-tris/esni.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/tris-testclient/esni_query.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/tris-testclient/client.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/tris-localserver/server.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/tls/esni.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/crypto/tls/common.go", + "/Users/jbaylina/git/iden3/circom/src/exec.js", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/net/tcpsock_plan9.go", + "/Users/jbaylina/git/personal/tls-tris/tls.go", + "/Users/jbaylina/git/personal/tls-tris/hiddenproxy.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/net/tcpsock.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/net/tcpsock_posix.go", + "/Users/jbaylina/git/personal/tls-tris/_dev/GOROOT/darwin_amd64/src/net/ipsock_plan9.go", + "/Users/jbaylina/git/iden3/circom/src/gen_c.js", + "/Users/jbaylina/git/iden3/circom/test/circuits/inout.circom", + "/Users/jbaylina/git/iden3/circom/c/utils.cpp", + "/Users/jbaylina/git/iden3/circom/c/utils.h", + "/Users/jbaylina/git/iden3/circom/c/mainjson.cpp", + "/Users/jbaylina/git/iden3/circom/c/mainjson", + "/Users/jbaylina/git/iden3/circom/doc/r1cs_bin_format.md", + "/Users/jbaylina/git/iden3/circom/c/circom.cpp", + "/Users/jbaylina/git/iden3/circomlib/src/eddsa.js", + "/Users/jbaylina/git/iden3/rollup/js/utils.js", + "/Users/jbaylina/git/iden3/rollup/js/rollupaccount.js", + "/Users/jbaylina/git/iden3/rollup/js/txpool.js", + "/Users/jbaylina/git/iden3/rollup/test/txpool.js", + "/Users/jbaylina/git/iden3/rollup/js/tmpstate.js", + "/Users/jbaylina/git/iden3/rollup/js/rollupdb.js", + "/Users/jbaylina/git/iden3/rollup/circuits/balancesupdater.circom", + "/Users/jbaylina/git/iden3/rollup/test/feeselector.js", + "/Users/jbaylina/git/iden3/rollup/test/circuits/rollup_test.circom", + "/Users/jbaylina/git/iden3/rollup/js/batchbuilder.js", + "/Users/jbaylina/git/iden3/rollup/test/rollup_circuit.js", + "/Users/jbaylina/git/iden3/rollup/circuits/rollup.circom", + "/Users/jbaylina/git/iden3/rollup/circuits/rolluptx.circom", + "/Users/jbaylina/git/iden3/rollup/circuits/feeselector.circom", + "/Users/jbaylina/git/iden3/rollup/circuits/feeplandecoder.circom", + "/Users/jbaylina/git/iden3/rollup/test.txt", + "/Users/jbaylina/git/iden3/rollup/package.json", + "/Users/jbaylina/git/iden3/rollup/circuits/rolluptxstates.circom", + "/Users/jbaylina/git/iden3/rollup/js/constants.js", + "/Users/jbaylina/git/iden3/rollup/.gitignore", + "/Users/jbaylina/git/iden3/rollup/test/helpers/checkbatch.js", + "/Users/jbaylina/git/iden3/circomlib/package.json", + "/Users/jbaylina/git/iden3/snarkjs/src/bn128.js", + "/Users/jbaylina/git/iden3/circomlib/test/smtverifier.js", + "/Users/jbaylina/git/iden3/circomlib/test/smtverifier_adria.js", + "/Users/jbaylina/git/iden3/circomlib/src/smt_memdb.js", + "/Users/jbaylina/git/iden3/rollup/js/smttmpdb.js", + "/Users/jbaylina/git/iden3/rollup/js/blockbuilder.js", + "/Users/jbaylina/git/iden3/circom/src/buildc.js", + "/Users/jbaylina/git/iden3/circom/package.json", + "/Users/jbaylina/git/iden3/circom/test/cases.js", + "/Users/jbaylina/git/iden3/circom/src/lcalgebra.js", + "/Users/jbaylina/git/iden3/rollup/circuits/decodetx.circom", + "/Users/jbaylina/git/personal/semaphore/semaphorejs/snark/test.circom", + "/Users/jbaylina/git/iden3/circom/src/gencode.js", + "/Users/jbaylina/git/iden3/circom/src/buildwasm.js", + "/Users/jbaylina/git/iden3/rollup/circuits/statepacker.circom", + "/Users/jbaylina/git/personal/semaphore/semaphorejs/snark/circuit.json", + "/Users/jbaylina/git/iden3/circomlib/test/eddsaposeidon.js", + "/Users/jbaylina/git/iden3/rollup/.eslintrc.js", + "/Users/jbaylina/git/iden3/rollup/.eslintrc", + "/Users/jbaylina/git/iden3/circomlib/index.js", + "/Users/jbaylina/git/iden3/snarkjs/src/calculateWitness.js", + "/Users/jbaylina/git/iden3/circomlib/src/smt.js", + "/Users/jbaylina/git/iden3/snarkjs/src/bigint.js", + "/Users/jbaylina/git/iden3/rollup/circuits/requiredtxverifier.circom", + "/Users/jbaylina/git/codereview/rattack/deploy_withdraw.js", + "/Users/jbaylina/git/iden3/circomlib/src/poseidon.js", + "/Users/jbaylina/git/iden3/rollup/test/rollupaccount.js", + "/Users/jbaylina/git/iden3/rollup/test/circuits/input.json", + "/Users/jbaylina/git/iden3/rollup/js/tmpdb.js", + "/Users/jbaylina/git/iden3/circomlib/circuits/eddsaposeidon.circom", + "/Users/jbaylina/git/iden3/rollup/circuits/decodefloat.circom", + "/Users/jbaylina/git/iden3/circomlib/circuits/escalarmulany.circom", + "/Users/jbaylina/git/iden3/circomlib/circuits/escalarmulfix.circom", + "/Users/jbaylina/git/iden3/rollup/test/decodefloat.js", + "/Users/jbaylina/git/iden3/circomlib/test/sha256.js", + "/Users/jbaylina/git/iden3/circomlib/test/circuits/sha256_test448.circom", + "/Users/jbaylina/git/iden3/circomlib/test/circuits/sha256_test512.circom" + ], + "find": + { + "height": 40.0 + }, + "find_in_files": + { + "height": 313.0, + "where_history": + [ + ] + }, + "find_state": + { + "case_sensitive": true, + "find_history": + [ + "genSignalAssignConstrain", + "mpz_impo", + "instantiateConstant", + "genVariable", + "instantiateConstant", + "error", + "used", + "instantiateRef", + "newRef", + "used", + "main", + "components", + "execDeclareSignal", + ".e", + "execSignalAssign", + "execSignalAssignConstrain", + "buildWit2Sig", + "__P__", + "genConstraint", + "checkConstraint", + "genConstraint", + "genConstrain", + "setS", + "asse", + "genConstrain", + "codeHeader", + "ht_InOut", + "addSizes", + "codes_sizes", + "definedSizes", + "getTmpName", + "getTmpNames", + "getTmp", + "getTmpName", + "utils", + "buildWit2Sig", + "buildMapIsInput", + "globalNames", + "buildInit", + "ctx", + "buildInit", + "genHe" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": false, + "replace_history": + [ + ], + "reverse": false, + "show_context": true, + "use_buffer2": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "sheets": + [ + ] + } + ], + "incremental_find": + { + "height": 28.0 + }, + "input": + { + "height": 107.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ] + ], + "cols": + [ + 0.0, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": true, + "output.SublimeLinter": + { + "height": 0.0 + }, + "output.SublimeLinter Messages": + { + "height": 246.0 + }, + "output.find_results": + { + "height": 0.0 + }, + "pinned_build_system": "", + "project": "Project.sublime-project", + "replace": + { + "height": 52.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "last_filter": "", + "selected_items": + [ + [ + "", + "iden3/circom/src/exec.js" + ] + ], + "width": 0.0 + }, + "select_project": + { + "height": 0.0, + "last_filter": "", + "selected_items": + [ + ], + "width": 0.0 + }, + "select_symbol": + { + "height": 0.0, + "last_filter": "", + "selected_items": + [ + ], + "width": 0.0 + }, + "selected_group": 0, + "settings": + { + }, + "show_minimap": true, + "show_open_files": false, + "show_tabs": true, + "side_bar_visible": true, + "side_bar_width": 300.0, + "status_bar_visible": true, + "template_settings": + { + } +} diff --git a/c/calcwit.cpp b/c/calcwit.cpp index d1b454f..64dfd82 100644 --- a/c/calcwit.cpp +++ b/c/calcwit.cpp @@ -25,8 +25,12 @@ Circom_CalcWit::Circom_CalcWit(Circom_Circuit *aCircuit) { // Initialize remaining signals for (int i=1; iNSignals; i++) mpz_init2(signalValues[i], 256); - reset(); + BigInt p; + mpz_init_set_str(p, circuit->P, 10); + field = new ZqField(&p); + mpz_clear(p); + reset(); } void Circom_CalcWit::reset() { @@ -43,6 +47,8 @@ void Circom_CalcWit::reset() { Circom_CalcWit::~Circom_CalcWit() { + delete field; + #ifdef SANITY_CHECK delete signalAssigned; #endif diff --git a/c/calcwit.h b/c/calcwit.h index 259fe4d..ce8e7cb 100644 --- a/c/calcwit.h +++ b/c/calcwit.h @@ -2,6 +2,7 @@ #define CIRCOM_CALCWIT_H #include "circom.h" +#include "zqfield.h" class Circom_CalcWit { @@ -18,13 +19,12 @@ class Circom_CalcWit { Circom_Circuit *circuit; - - void triggerComponent(int newCIdx); void calculateWitness(void *input, void *output); public: + ZqField *field; int cIdx; // Functions called by the circuit Circom_CalcWit(Circom_Circuit *aCircuit); diff --git a/c/circom.h b/c/circom.h index d81400d..db83e8b 100644 --- a/c/circom.h +++ b/c/circom.h @@ -48,6 +48,7 @@ public: int *wit2sig; Circom_Component *components; u32 *mapIsInput; + const char *P; }; #define BITMAP_ISSET(m, b) (m[b>>5] & (1 << (b&0x1F))) diff --git a/c/zqfield.cpp b/c/zqfield.cpp new file mode 100644 index 0000000..bb2715c --- /dev/null +++ b/c/zqfield.cpp @@ -0,0 +1,30 @@ +#include "zqfield.h" + +ZqField::ZqField(PBigInt ap) { + mpz_init_set(p, *ap); + mpz_init_set_ui(zero, 0); + mpz_init_set_ui(one, 1); +} + + +void ZqField::add(PBigInt r, PBigInt a, PBigInt b) { + mpz_add(*r,*a,*b); + mpz_fdiv_r(*r, *r, p); +} + +void ZqField::lt(PBigInt r, PBigInt a, PBigInt b) { + int c = mpz_cmp(*a, *b); + if (c<0) { + mpz_set(*r, one); + } else { + mpz_set(*r, zero); + } +} + +int ZqField::isTrue(PBigInt a) { + return mpz_sgn(*a); +} + +void ZqField::copy(PBigInt a, PBigInt b) { + return mpz_set(*a, *b); +} diff --git a/c/zqfield.h b/c/zqfield.h new file mode 100644 index 0000000..7bc9244 --- /dev/null +++ b/c/zqfield.h @@ -0,0 +1,20 @@ +#ifndef ZQFIELD_H +#define ZQFIELD_H + +#include "circom.h" + +class ZqField { + +public: + BigInt p; + BigInt one; + BigInt zero; + ZqField(PBigInt ap); + + void copy(PBigInt a, PBigInt b); + void add(PBigInt r,PBigInt a, PBigInt b); + void lt(PBigInt r, PBigInt a, PBigInt b); + int isTrue(PBigInt a); +}; + +#endif // ZQFIELD_H diff --git a/src/c_build.js b/src/c_build.js index 51c7efc..a3b6bec 100644 --- a/src/c_build.js +++ b/src/c_build.js @@ -17,15 +17,18 @@ along with circom. If not, see . */ -const utils = require("./utils"); const assert = require("assert"); -const gen = require("./c_gen"); +const bigInt = require("big-integer"); +const utils = require("./utils"); +const gen = require("./c_gen").gen; +const newRef = require("./c_gen").newRef; module.exports = buildC; function buildC(ctx) { ctx.code = ""; + ctx.conditionalCodeHeader = ""; ctx.tmpNames = {}; ctx.getTmpName = getTmpName; ctx.codes_sizes = []; @@ -158,6 +161,7 @@ function buildCode(ctx) { const scope = {_prefix : ""}; ctx.scopes = [scope]; + ctx.conditionalCode = false; ctx.nScopes = 0; ctx.code = ""; ctx.codeHeader = "// Header\n"; @@ -165,7 +169,7 @@ function buildCode(ctx) { ctx.tmpNames = Object.assign({},globalNames); for (let p in ctx.components[i].params) { - newRef(ctx, "BIGINT", p, ctx.components[i].params[p]); + newRef(ctx, "BIGINT", p, bigInt(ctx.components[i].params[p])); } gen(ctx, ctx.templates[ctx.components[i].template].block); @@ -212,6 +216,7 @@ function buildHeader(ctx) { `#define NInputs ${ctx.components[ ctx.getComponentIdx("main") ].nInSignals}\n`+ `#define NOutputs ${ctx.totals[ ctx.stOUTPUT ]}\n`+ `#define NVars ${ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL]}\n` + + `#define __P__ "${ctx.field.p.toString()}"\n` + "\n"; } @@ -310,7 +315,8 @@ function buildCircuitVar() { " NVars,\n"+ " _wit2sig,\n"+ " _components,\n"+ - " _mapIsInput\n"+ + " _mapIsInput,\n"+ + " __P__\n" + "};\n"; } diff --git a/src/c_gen.js b/src/c_gen.js index d7945b7..f6053db 100644 --- a/src/c_gen.js +++ b/src/c_gen.js @@ -1,8 +1,9 @@ const bigInt = require("big-integer"); const utils = require("./utils"); +const assert = require("assert"); -module.exports = gen; - +module.exports.gen = gen; +module.exports.newRef = newRef; function newRef(ctx, type, _name, value, _sizes) { const isValue = ((typeof(value) != "undefined")&&(value != null)); @@ -28,7 +29,7 @@ function newRef(ctx, type, _name, value, _sizes) { } const scope = ctx.scopes[ctx.scopes.length-1]; - if (scope[name]) return error("Variable already exists: " + name); + if (scope[name]) return error(ctx, null, "Variable already exists: " + name); const label = scope._prefix + name; scope[name] = { @@ -64,9 +65,9 @@ function newSizes(ctx, sizes) { } -function instantiateRef(ctx, name) { +function instantiateRef(ctx, name, initValue) { const v = getScope(ctx, name); - if (!v.stack) return error("Using a non existing var: " + name); + if (!v.stack) return error(ctx, null, "Using a non existing var: " + name); if (v.used) return; if (v.type=="BIGINT") { @@ -76,7 +77,12 @@ function instantiateRef(ctx, name) { if (iSize.used) { const labelSize = iSize.label; ctx.codeHeader += `PBigInt ${v.label};\n`; - ctx.code += `${v.label} = ctx->allocBigInts(${labelSize});\n`; + const c = `${v.label} = ctx->allocBigInts(${labelSize});\n`; + if (ctx.conditionalCode) { + ctx.conditionalCodeHeader += c; + } else { + ctx.code += c; + } ctx.codeFooter += `ctx->freeBigInts(${v.label}, ${labelSize});\n`; } else if (iSize.value) { const labelSize = ctx.addSizes(iSize.value); @@ -92,6 +98,30 @@ function instantiateRef(ctx, name) { ctx.codeHeader += `Circom_Sizes ${v.label};\n`; } v.used = true; + if ((typeof initValue!= "undefined")&&(initValue != null)) { + const flatedValue = utils.flatArray(initValue); + for (let i=0; iallocBigInts(${labelSize});\n`; + for (let i=0; ifreeBigInts(${res}, ${labelSize});\n`; + return res; } @@ -140,7 +170,7 @@ function gen(ctx, ast) { } else if (ast.op == "*=") { return genVarMulAssignement(ctx, ast); } else if (ast.op == "+") { - return genAdd(ctx, ast); + return genBinaryOp(ctx, ast, "add"); } else if (ast.op == "-") { return genSub(ctx, ast); } else if (ast.op == "UMINUS") { @@ -174,7 +204,7 @@ function gen(ctx, ast) { } else if (ast.op == ">>") { return genShr(ctx, ast); } else if (ast.op == "<") { - return genLt(ctx, ast); + return genBinaryOp(ctx, ast, "lt"); } else if (ast.op == ">") { return genGt(ctx, ast); } else if (ast.op == "<=") { @@ -226,6 +256,7 @@ function gen(ctx, ast) { } function genBlock(ctx, ast) { + ctx.scopes.push({_prefix : ""}); const oldCode = ctx.code; let res = null; ctx.code = ""; @@ -236,26 +267,35 @@ function genBlock(ctx, ast) { res = gen(ctx, ast.statements[i]); if (ctx.error) return; } - if (ctx.scopes.length>1) { - ctx.code = oldCode + `{\n${utils.ident(ctx.code)}}\n`; - } else { - ctx.code = oldCode + ctx.code; - } + ctx.code = oldCode + ctx.code; + ctx.scopes.pop(); return res; } -function genSrcComment(ctx, ast) { +function getSource(ctx, ast) { let codes = []; const fl= ast.first_line-1; const ll = ast.last_line-1; - const fc = ast.first_column-1; + const fc = ast.first_column; const lc = ast.last_column; for (let i=fl; i<=ll; i++) { const p1 = i==fl ? fc : 0; const p2 = i==ll ? lc : -1; codes.push(ctx.includedFiles[ctx.fileName][i].slice(p1, p2>=0 ? p2 : undefined)); } - ctx.code += "\n/* "+codes.join("\n")+"*/\n"; + return codes.join("\n"); +} + +function genSrcComment(ctx, ast) { + const code = getSource(ctx, ast); + ctx.code += "\n/* "+code+" */\n"; +} + +function genForSrcComment(ctx, ast) { + const init = getSource(ctx, ast.init); + const condition = getSource(ctx, ast.condition); + const step = getSource(ctx, ast.step); + ctx.code += `\n/* for (${init},${condition},${step}) */\n`; } @@ -297,7 +337,7 @@ function genDeclareVariable(ctx, ast) { const labelName = scope._prefix + ast.name.name; if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); - if (ctx.scope[varName]) return error(ctx, ast, "Name already exists: "+varName); + if (typeof scope[varName] != "undefined") return error(ctx, ast, "Name already exists: "+varName); const sizes=[]; let instantiate = false; @@ -329,16 +369,17 @@ function genDeclareVariable(ctx, ast) { iSizes.value = sizes; } - const res = ctx.scope[varName] = { + const res = scope[varName] = { stack: true, type: "BIGINT", sizes: vSizes, - label: labelName + label: labelName, + used: false, }; scope[varName] = res; - return res; + return varName; } function genNumber(ctx, ast) { @@ -432,17 +473,23 @@ function genVariable(ctx, ast) { } else if (v.type == "BIGINT") { const vOffset = genGetOffset(ctx, 0, v.sizes, ast.sels ); + const offset = getScope(ctx, vOffset); if (v.used) { - if (vOffset == 0) { - return ast.name; - } else { + if (offset.used) { const res = newRef(ctx, "BIGINT", "_v"); instantiateRef(ctx, res); ctx.code += `${res} = ${ast.name} + ${vOffset};\n`; return res; + } else if (offset.value) { + const res = newRef(ctx, "BIGINT", "_v"); + instantiateRef(ctx, res); + ctx.code += `${res} = ${ast.name} + ${vOffset.value};\n`; + return res; + } else { + return ast.name; } } else { - if (typeof(vOffset) == "string") { + if (offset.used) { instantiateRef(ctx, ast.name); const vConstant = instantiateConstant(ctx, v.value); const res = newRef(ctx, "BIGINT", "_v"); @@ -475,17 +522,6 @@ function genVariable(ctx, ast) { } } -function instantiateConstant(ctx, value) { - const flatedValue = utils.flatArray(value); - const res = ctx.getTmpName("_const"); - ctx.codeHeader += `PBigInt ${res};\n`; - ctx.code += `${res.label} = ctx->allocBigInts(${flatedValue.length});\n`; - for (let i=0; ifreeBigInts(${res.label});\n`; - return res; -} function genPin(ctx, ast) { let vcIdx; @@ -546,7 +582,12 @@ function genGetSignalSizes(ctx, sIdx, label) { } function genSetSignal(ctx, cIdx, sIdx, value) { + const v = getScope(ctx, value); + if (!v.used) { + value = instantiateConstant(ctx, v.value); + } ctx.code += `ctx->setSignal(${cIdx}, ${sIdx}, ${value});\n`; + return value; } @@ -604,6 +645,10 @@ function genVarAssignement(ctx, ast) { const left = getScope(ctx, lName); if (!left) return error(ctx, ast, "Variable does not exists: "+ast.values[0].name); + if (ctx.conditionalCode && !left.used) { + instantiateRef(ctx, lName, left.value); + } + // Component instantiation is already done. if (left.type == "COMPONENT") { ctx.last = lName; @@ -626,36 +671,29 @@ function genVarAssignement(ctx, ast) { } return genSetSignal(ctx, "ctx->cIdx", vsIdx, rName); - } else if (left.type == "VAR") { + } else if (left.type == "BIGINT") { if (left.used) { if (!right.used) { instantiateRef(ctx, rName); } - ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; - return lName; + ctx.code += `ctx->field->copy(${left.label}, ${right.label});\n`; } else { if (right.used) { instantiateRef(ctx, lName); - ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; - return lName; + ctx.code += `ctx->field->copy(${left.label}, ${right.label});\n`; } else { - if (!left.value) { - left.value = right.value; + if (ctx.conditionalCode) { + instantiateRef(ctx, lName); + ctx.code += `ctx->field->copy(${left.label}, ${right.label});\n`; } else { - if (!left.value.equals(right.value)) { - if (ctx.scopes.length > 1) { - instantiateRef(ctx, lName); - ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; - } else { - left.value = right.value; - } - } + left.value = right.value; } } } } else { return error(ctx, ast, "Assigning to invalid"); } + return lName; } function genConstraint(ctx, ast) { @@ -690,18 +728,103 @@ function genFunctionCall(ctx, ast) { return `ctx.callFunction("${ast.name}", ${S})`; } +function enterConditionalCode(ctx) { + if (!ctx.conditionalCode) { + ctx.conditionalCode = 1; + ctx.conditionalCodeHeader = ""; + ctx.code += "__CONDITIONAL_CODE_HEADER__"; + } else { + ctx.conditionalCode ++; + } +} + +function leaveConditionalCode(ctx) { + assert(ctx.conditionalCode, "Leaving conditional code too many times"); + ctx.conditionalCode --; + if (!ctx.conditionalCode) { + ctx.code = ctx.code.replace("__CONDITIONAL_CODE_HEADER__", + "// Conditional Code Header\n" + + ctx.conditionalCodeHeader+ + "\n"); + delete ctx.conditionalCodeHeader; + delete ctx.conditionalCode; + } +} + function genFor(ctx, ast) { - ctx.scopes.push({}); - const init = gen(ctx, ast.init); - if (ctx.error) return; - const condition = gen(ctx, ast.condition); - if (ctx.error) return; - const step = gen(ctx, ast.step); - if (ctx.error) return; - const body = gen(ctx, ast.body); + genForSrcComment(ctx, ast); + let inLoop = false; + ctx.scopes.push({_prefix : ""}); + gen(ctx, ast.init); if (ctx.error) return; + + let end=false; + let condVar; + + + const condName = gen(ctx, ast.condition); + const cond = getScope(ctx, condName); + if (cond.used) { + inLoop = true; + enterConditionalCode(ctx); + condVar = newRef(ctx, "INT", "_cond"); + instantiateRef(ctx, condVar); + + ctx.code += + `${condVar} = ctx->field->isTrue(${condName});\n` + + `while (${condVar}) {\n`; + } else { + if ((typeof cond.value == "undefined")||(cond.value == null)) return error(ctx, ast, "condition value not assigned"); + if (cond.value.isZero()) end=true; + } + + + while (!end) { + + const oldCode = ctx.code; + ctx.code = ""; + + if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body); + gen(ctx, ast.body); + if (ctx.error) return; + + gen(ctx, ast.step); + if (ctx.error) return; + + const condName2 = gen(ctx, ast.condition); + const cond2 = getScope(ctx, condName2); + + if (!inLoop) { + if (cond2.used) { + ctx.code = oldCode + ctx.code; + inLoop = true; + enterConditionalCode(ctx); + condVar = newRef(ctx, "INT", "_cond"); + instantiateRef(ctx, condVar); + + ctx.code = + oldCode + + ctx.code + + `${condVar} = ctx->field->isTrue(${condName2});\n` + + `while (${condVar}) {\n`; + } else { + ctx.code = oldCode + ctx.code; + if (cond2.value.isZero()) end=true; + } + } else { + ctx.code = + oldCode + + utils.ident( + ctx.code + + `${condVar} = ctx->field->isTrue(${condName2});\n`); + end=true; + } + } + if (inLoop) { + ctx.code += "}\n"; + leaveConditionalCode(ctx); + } ctx.scopes.pop(); - return `for (${init};bigInt(${condition}).neq(bigInt(0));${step}) { \n${body}\n }\n`; } function genWhile(ctx, ast) { @@ -743,9 +866,9 @@ function genReturn(ctx, ast) { function genSignalAssignConstrain(ctx, ast) { const res = genVarAssignement(ctx, ast); - genConstraint(ctx, ast); + // genConstraint(ctx, ast); return res; -// return genVarAssignement(ctx, ast); + // return genVarAssignement(ctx, ast); } function genVarAddAssignement(ctx, ast) { @@ -772,12 +895,34 @@ function genMinusMinusLeft(ctx, ast) { return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); } -function genAdd(ctx, ast) { - const a = gen(ctx, ast.values[0]); +function genBinaryOp(ctx, ast, op) { + let aName = gen(ctx, ast.values[0]); if (ctx.error) return; - const b = gen(ctx, ast.values[1]); + const a = getScope(ctx, aName); + + let bName = gen(ctx, ast.values[1]); if (ctx.error) return; - return `bigInt(${a}).add(bigInt(${b})).mod(__P__)`; + const b = getScope(ctx, bName); + + if ((!a.used)&&(typeof a.value == "undefined")) return error(ctx, ast, "Using a not assigned varialble: "+aName); + if ((!b.used)&&(typeof b.value == "undefined")) return error(ctx, ast, "Using a not assigned varialble: "+bName); + + let rName; + if (a.used || b.used) { + if (!a.used) { + aName = instantiateConstant(ctx, a.value); + } + if (!b.used) { + bName = instantiateConstant(ctx, b.value); + } + + rName = newRef(ctx, "BIGINT", "_tmp"); + instantiateRef(ctx, rName); + ctx.code += `ctx->field->${op}(${rName},${aName}, ${bName});\n`; + } else { + rName = newRef(ctx, "BIGINT", "_tmp", ctx.field[op](a.value, b.value)); + } + return rName; } function genMul(ctx, ast) { diff --git a/src/c_tester.js b/src/c_tester.js index 48ee424..e8dc1d8 100644 --- a/src/c_tester.js +++ b/src/c_tester.js @@ -8,8 +8,8 @@ const compiler = require("./compiler"); const util = require("util"); const exec = util.promisify(require("child_process").exec); -const stringifyBigInts = require("snarkjs").stringifyBigInts; -const unstringifyBigInts = require("snarkjs").unstringifyBigInts; +const stringifyBigInts = require("./utils").stringifyBigInts; +const unstringifyBigInts = require("./utils").unstringifyBigInts; const bigInt = require("snarkjs").bigInt; module.exports = c_tester; @@ -35,12 +35,13 @@ async function c_tester(circomFile, mainComponent, _options) { ` ${path.join(cdir, "main.cpp")}` + ` ${path.join(cdir, "calcwit.cpp")}` + ` ${path.join(cdir, "utils.cpp")}` + + ` ${path.join(cdir, "zqfield.cpp")}` + ` -o ${path.join(dir.path, baseName)}` + ` -I ${cdir}` + " -lgmp -std=c++11 -DSANITY_CHECK" ); - console.log(dir.path); + // console.log(dir.path); return new CTester(dir, baseName, mainComponent); } @@ -102,7 +103,7 @@ class CTester { for (let i=0; i new Promise(res => setTimeout(res, ms)); async function compile(srcFile, options) { + options.p = options.p || __P__; if (!options) { options = {}; } @@ -51,6 +53,7 @@ async function compile(srcFile, options) { assert(ast.type == "BLOCK"); const ctx = new Ctx(); + ctx.field = new ZqField(options.p); ctx.mainComponent = options.mainComponent || "main"; ctx.filePath= fullFilePath; ctx.fileName= fullFileName; diff --git a/src/exec.js b/src/exec.js index 70a9439..76f9d37 100644 --- a/src/exec.js +++ b/src/exec.js @@ -1143,7 +1143,7 @@ function execConstrain(ctx, ast) { ctx.constraints.push(lc.toQEQ(res)); } - if (ctx.constraints.length % 10000 == 0) console.log("Constraints: " + ctx.constraints.length); + if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length); return res; } diff --git a/src/utils.js b/src/utils.js index 8ac8473..6af0201 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,12 +1,16 @@ const fnv = require("fnv-plus"); +const bigInt = require("big-integer"); module.exports.ident =ident; module.exports.extractSizes =extractSizes; +module.exports.flatArray = flatArray; module.exports.csArr = csArr; module.exports.subArray = subArray; module.exports.accSizes = accSizes; module.exports.fnvHash = fnvHash; +module.exports.stringifyBigInts = stringifyBigInts; +module.exports.unstringifyBigInts = unstringifyBigInts; function ident(text) { let lines = text.split("\n"); @@ -17,10 +21,26 @@ function ident(text) { } function extractSizes (o) { - if (! Array.isArray(o)) return [1, 0]; + if (! Array.isArray(o)) return []; return [o.length, ...extractSizes(o[0])]; } +function flatArray(a) { + var res = []; + fillArray(res, a); + return res; + + function fillArray(res, a) { + if (Array.isArray(a)) { + for (let i=0; i { + await doTest( + "add.circom", + [ + [{in: [0,0]}, {out: 0}], + [{in: [0,1]}, {out: 1}], + [{in: [1,2]}, {out: 3}], + [{in: [__P__.minus(1),1]}, {out: 0}], + ] + ); + }); + it("add constant", async () => { + await doTest( + "addconst1.circom", + [ + [{in: 0}, {out: 15}], + [{in: 10}, {out: 25}], + [{in: __P__.minus(2)}, {out: 13}], + ] + ); + }); + it("for unrolled", async () => { + await doTest( + "forunrolled.circom", + [ + [{in: 0}, {out: [0,1,2]}], + [{in: 10}, {out: [10, 11, 12]}], + [{in: __P__.minus(2)}, {out: [__P__.minus(2), __P__.minus(1), 0]}], + ] + ); + }); + it("for rolled", async () => { + await doTest( + "forrolled.circom", + [ + [{in: 0}, {out: 0}], + [{in: 10}, {out: 10}], + ] + ); + }); +}); diff --git a/test/circuits/add.circom b/test/circuits/add.circom new file mode 100644 index 0000000..3201456 --- /dev/null +++ b/test/circuits/add.circom @@ -0,0 +1,9 @@ + +template Add() { + signal input in[2]; + signal output out; + + out <== in[0] + in[1]; +} + +component main = Add(); diff --git a/test/circuits/addconst1.circom b/test/circuits/addconst1.circom new file mode 100644 index 0000000..773f76a --- /dev/null +++ b/test/circuits/addconst1.circom @@ -0,0 +1,16 @@ + + +template AddConst(c) { + signal input in; + signal output out; + var a = 2; + var b = 3; + a=a+b; + a=a+4; + a=a+c; + + out <== 5 + a + in; +} + +// It should out <== in + 1+2+3+4+5 = in + 15 +component main = AddConst(1); diff --git a/test/circuits/addin.json b/test/circuits/addin.json new file mode 100644 index 0000000..370eb40 --- /dev/null +++ b/test/circuits/addin.json @@ -0,0 +1 @@ +{"in":[1,2]} diff --git a/test/circuits/forrolled.circom b/test/circuits/forrolled.circom new file mode 100644 index 0000000..37b528e --- /dev/null +++ b/test/circuits/forrolled.circom @@ -0,0 +1,14 @@ +template ForRolled() { + signal input in; + signal output out; + + var acc = 0; + + for (var i=0; i