From 7e91faaf3e8441d6391fdf017f43c53856e22621 Mon Sep 17 00:00:00 2001 From: erhant Date: Fri, 2 Jun 2023 00:21:35 +0300 Subject: [PATCH] many many commands --- README.md | 2 + circomkit.env | 20 --- circuits/main/multiplier_3.circom | 2 +- scripts/cli.sh | 275 ++++++++++++++++++++++++++++-- scripts/functions/calldata.sh | 13 -- scripts/functions/clean.sh | 12 -- scripts/functions/compile.sh | 16 -- scripts/functions/contract.sh | 12 -- scripts/functions/debug.sh | 17 -- scripts/functions/instantiate.sh | 22 --- scripts/functions/prove.sh | 16 -- scripts/functions/setup.sh | 62 ------- scripts/functions/type.sh | 24 --- scripts/functions/verify.sh | 14 -- scripts/functions/witness.sh | 19 --- src/bin/index.ts | 29 +++- src/cli/config.ts | 68 ++++++++ src/cli/index.ts | 193 +++++++++++++++++++++ src/types/circom_tester.d.ts | 1 + src/types/circuit.ts | 4 +- src/types/snarkjs.d.ts | 1 + src/utils/instantiate.ts | 3 +- tsconfig.base.json | 2 - tsconfig.type.json | 14 -- 24 files changed, 558 insertions(+), 283 deletions(-) delete mode 100644 circomkit.env delete mode 100755 scripts/functions/calldata.sh delete mode 100755 scripts/functions/clean.sh delete mode 100755 scripts/functions/compile.sh delete mode 100755 scripts/functions/contract.sh delete mode 100644 scripts/functions/debug.sh delete mode 100755 scripts/functions/instantiate.sh delete mode 100755 scripts/functions/prove.sh delete mode 100755 scripts/functions/setup.sh delete mode 100755 scripts/functions/type.sh delete mode 100755 scripts/functions/verify.sh delete mode 100755 scripts/functions/witness.sh create mode 100644 src/cli/config.ts create mode 100644 src/cli/index.ts create mode 100644 src/types/circom_tester.d.ts create mode 100644 src/types/snarkjs.d.ts delete mode 100644 tsconfig.type.json diff --git a/README.md b/README.md index 506de0c..ca7ad60 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +> Work in progress... +

Circomkit diff --git a/circomkit.env b/circomkit.env deleted file mode 100644 index 85fe643..0000000 --- a/circomkit.env +++ /dev/null @@ -1,20 +0,0 @@ -# proof system to be used, can be: groth16 | plonk | fflonk -CIRCOMKIT_PROOF_SYSTEM="groth16" - -# name of the curve to be used: bn128 | bls12381 | goldilocks -CIRCOMKIT_ELLIPTIC_CURVE="bn128" - -# solidity contract export path -CIRCOMKIT_SOLIDITY_PATH="./contracts" - -# compiler args, can add --inspect and -c for example -CIRCOMKIT_COMPILER_ARGS="--r1cs --wasm --sym -l ./node_modules -p $CIRCOMKIT_ELLIPTIC_CURVE --inspect" - -# solidity contract export path -CIRCOMKIT_VERSION="2.1.0" - -# colors for swag terminal outputs -CIRCOMKIT_COLOR_TITLE='\033[0;34m' # blue -CIRCOMKIT_COLOR_LOG='\033[2;37m' # gray -CIRCOMKIT_COLOR_ERR='\033[0;31m' # red -CIRCOMKIT_COLOR_RESET='\033[0m' # reset color diff --git a/circuits/main/multiplier_3.circom b/circuits/main/multiplier_3.circom index 8b5618b..87c6dc4 100644 --- a/circuits/main/multiplier_3.circom +++ b/circuits/main/multiplier_3.circom @@ -1,4 +1,4 @@ -// auto-generated by instantiate.js +// auto-generated by circomkit pragma circom 2.1.0; include "../multiplier.circom"; diff --git a/scripts/cli.sh b/scripts/cli.sh index 4b000fd..77e1794 100755 --- a/scripts/cli.sh +++ b/scripts/cli.sh @@ -1,12 +1,28 @@ #!/bin/bash +# I couldn't bring myself to delete this file :( + cd "${0%/*}"/.. # get to project root set -e # abort on error -# load CLI environment variables -source ./circomkit.env +### load CLI environment variables +# proof system to be used, can be: groth16 | plonk | fflonk +CIRCOMKIT_PROOF_SYSTEM="groth16" +# name of the curve to be used: bn128 | bls12381 | goldilocks +CIRCOMKIT_ELLIPTIC_CURVE="bn128" +# solidity contract export path +CIRCOMKIT_SOLIDITY_PATH="./contracts" +# compiler args, can add --inspect and -c for example +CIRCOMKIT_COMPILER_ARGS="--r1cs --wasm --sym -l ./node_modules -p $CIRCOMKIT_ELLIPTIC_CURVE --inspect" +# circom version +CIRCOMKIT_VERSION="2.1.0" +# colors for swag terminal outputs +CIRCOMKIT_COLOR_TITLE='\033[0;34m' # blue +CIRCOMKIT_COLOR_LOG='\033[2;37m' # gray +CIRCOMKIT_COLOR_ERR='\033[0;31m' # red +CIRCOMKIT_COLOR_RESET='\033[0m' # reset color -# check validness of variables +### check validness of variables valid_proof_systems=("groth16" "plonk" "fflonk") if [[ ! " ${valid_proof_systems[@]} " =~ " ${CIRCOMKIT_PROOF_SYSTEM} " ]]; then echo -e "${CIRCOMKIT_COLOR_ERR}Invalid proof system: $CIRCOMKIT_PROOF_SYSTEM${CIRCOMKIT_COLOR_RESET}" @@ -18,19 +34,248 @@ if [[ ! " ${valid_elliptic_curves[@]} " =~ " ${CIRCOMKIT_ELLIPTIC_CURVE} " ]]; t exit 1 fi -# import functions -source ./scripts/functions/type.sh -source ./scripts/functions/setup.sh -source ./scripts/functions/compile.sh -source ./scripts/functions/clean.sh -source ./scripts/functions/contract.sh -source ./scripts/functions/calldata.sh -source ./scripts/functions/debug.sh -source ./scripts/functions/instantiate.sh -source ./scripts/functions/prove.sh -source ./scripts/functions/verify.sh -source ./scripts/functions/witness.sh +## Function definitions +# Generate types from a circuit template (incomplete) +type() { + set -e + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Generating types ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local SYM=./build/$CIRCUIT/$CIRCUIT.sym + + # choose lines with 1 dot only (these are the signals of the main component), extract their names + local MAIN_SIGNALS=$(cat $SYM | awk -F '.' 'NF==2 {print $2}') + + # get the unique signal names + local MAIN_SIGNAL_NAMES=$(echo "$MAIN_SIGNALS" | awk -F '[' '{print $1}' | uniq) + + # get the last signal for each signal name + local SIGNALS="" + for SIGNAL in $MAIN_SIGNAL_NAMES; do + SIGNALS+="$(echo "$MAIN_SIGNALS" | grep $SIGNAL | tail -n 1) " + done + echo "$SIGNALS" + + echo -e "\n${CIRCOMKIT_COLOR_LOG}Types generated!${CIRCOMKIT_COLOR_RESET}" +} + + +# Commence a circuit-specific setup +setup() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Circuit Setup ($CIRCOMKIT_PROOF_SYSTEM) ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 # circuit name + local P1_PTAU=$2 # path to phase-1 ptau + local NUM_CONTRIBS=$3 # number of contributions (for groth16) + local CIRCUIT_DIR=./build/$CIRCUIT # circuit directory + local PROVER_KEY=$CIRCUIT_DIR/prover_key.zkey + local VERIFICATION_KEY=$CIRCUIT_DIR/verifier_key.json + + # check if P1_PTAU exists + if [ ! -f "$P1_PTAU" ]; then + echo -e "PTAU file ${CIRCOMKIT_COLOR_ERR}${P1_PTAU} does not exist.${CIRCOMKIT_COLOR_RESET}" + exit 1 + fi + + if [[ "$CIRCOMKIT_PROOF_SYSTEM" == "groth16" ]]; then + local P2_PTAU=$CIRCUIT_DIR/phase2_final.ptau # phase-2 ptau + local CUR=000 # zkey id, initially 0 + + # if Groth16, circuit specific ceremony is needed + # start phase-2 ceremony (circuit specific) + echo -e "${CIRCOMKIT_COLOR_LOG}this may take a while...${CIRCOMKIT_COLOR_RESET}" + snarkjs powersoftau prepare phase2 $P1_PTAU $P2_PTAU -v + + # generate a zkey that contains proving and verification keys, along with phase-2 contributions + snarkjs groth16 setup \ + $CIRCUIT_DIR/$CIRCUIT.r1cs \ + $P2_PTAU \ + $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey + + # get rid of phase-2 ptau + rm $P2_PTAU + + # make contributions (001, 002, ...) + for NEXT in $(seq -f "%03g" 1 ${NUM_CONTRIBS}) + do + echo "Making Phase-2 Contribution: ${NEXT}" + snarkjs zkey contribute \ + $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey \ + $CIRCUIT_DIR/${CIRCUIT}_${NEXT}.zkey -v + + rm $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey + + CUR=$NEXT + done + echo "Phase 2 Complete." + + # rename key to the prover key + mv $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey $PROVER_KEY + else + # otherwise, we can use that phase-1 ptau alone + snarkjs $CIRCOMKIT_PROOF_SYSTEM setup $CIRCUIT_DIR/$CIRCUIT.r1cs \ + $P1_PTAU \ + $PROVER_KEY + fi + + # export + snarkjs zkey export verificationkey $PROVER_KEY $VERIFICATION_KEY + + echo -e "${CIRCOMKIT_COLOR_LOG}Generated keys\n\tProver key: $PROVER_KEY\n\tVerification key: $VERIFICATION_KEY${CIRCOMKIT_COLOR_RESET}" +} + +# Compile the circuit, outputting R1CS and JS files +compile() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Compiling the circuit ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local CIRCOM_IN=./circuits/main/$CIRCUIT.circom + local CIRCOM_OUT=./build/$CIRCUIT + + # create build dir if not exists already + mkdir -p $CIRCOM_OUT + + # compile with circom + echo "circom $CIRCOM_IN -o $CIRCOM_OUT $CIRCOMKIT_COMPILER_ARGS" + circom $CIRCOM_IN -o $CIRCOM_OUT $CIRCOMKIT_COMPILER_ARGS + + echo -e "${CIRCOMKIT_COLOR_LOG}Built artifacts under $CIRCOM_OUT${CIRCOMKIT_COLOR_RESET}" +} + + +# Clean build files +clean() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Cleaning artifacts ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local CIRCUIT_DIR=./build/$CIRCUIT + local TARGET=./circuits/main/$CIRCUIT.circom + + rm -rf $CIRCUIT_DIR + rm -f $TARGET + + echo -e "${CIRCOMKIT_COLOR_LOG}Deleted $CIRCUIT_DIR and $TARGET${CIRCOMKIT_COLOR_RESET}" +} + +# Exports a solidity contract for the verifier +contract() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Generating Solidity verifier ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local CIRCUIT_DIR=./build/$CIRCUIT + + snarkjs zkey export solidityverifier \ + $CIRCUIT_DIR/prover_key.zkey \ + $CIRCUIT_DIR/verifier.sol + + echo -e "${CIRCOMKIT_COLOR_LOG}Contract created at $CIRCUIT_DIR/verifier.sol!${CIRCOMKIT_COLOR_RESET}" +} + + +# Exports a solidity contract for the verifier +calldata() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Exporting calldata ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local INPUT=$2 + local CIRCUIT_DIR=./build/$CIRCUIT + + snarkjs zkey export soliditycalldata \ + $CIRCUIT_DIR/$INPUT/public.json \ + $CIRCUIT_DIR/$INPUT/proof.json + + echo -e "${CIRCOMKIT_COLOR_LOG}Done!${CIRCOMKIT_COLOR_RESET}" +} + +# Debug a witness +debug() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Debugging witness ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local INPUT=$2 + local CIRCUIT_DIR=./build/$CIRCUIT + local OUTPUT_DIR=./build/$CIRCUIT/$INPUT # directory for proof & public signals + local INPUT_DIR=./inputs/$CIRCUIT # directory for inputs + + snarkjs wtns debug \ + $CIRCUIT_DIR/${CIRCUIT}_js/$CIRCUIT.wasm \ + $INPUT_DIR/$INPUT.json \ + $OUTPUT_DIR/witness.wtns \ + $CIRCUIT_DIR/$CIRCUIT.sym + + echo -e "${CIRCOMKIT_COLOR_LOG}Done!${CIRCOMKIT_COLOR_RESET}" +} + +# Instantiate the main component +instantiate() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Creating main component ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + + # parse json for the circuit, trim first and last lines, and then remove all whitespace + local MATCH=$(sed -n "/ *\"${CIRCUIT}\": *{/, /^ *}[, ]$/p" ./circuits.json | sed '1d;$d' | tr -d "[:space:]") + if [ -z "$MATCH" ] + then + echo -e "${CIRCOMKIT_COLOR_ERR}No such circuit found!${CIRCOMKIT_COLOR_RESET}" + exit + fi + + # create JSON object + local JSON_IN="{\"version\":\"${CIRCOMKIT_VERSION}\",$MATCH}" + + # generate the circuit main component + local OUTDIR="./circuits/main/$CIRCUIT.circom" + npx ejs ./ejs/template.circom -i $JSON_IN -o $OUTDIR + + echo -e "${CIRCOMKIT_COLOR_LOG}Done!${CIRCOMKIT_COLOR_RESET}" +} + +# Generate a proof +prove() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Generating proof ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local INPUT=$2 + local CIRCUIT_DIR=./build/$CIRCUIT + local OUTPUT_DIR=$CIRCUIT_DIR/$INPUT + + snarkjs $CIRCOMKIT_PROOF_SYSTEM prove \ + $CIRCUIT_DIR/prover_key.zkey \ + $OUTPUT_DIR/witness.wtns \ + $OUTPUT_DIR/proof.json \ + $OUTPUT_DIR/public.json + + echo -e "${CIRCOMKIT_COLOR_LOG}Generated under $OUTPUT_DIR${CIRCOMKIT_COLOR_RESET}" +} + +# Verify a witness & proof +verify() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Verifying proof ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local INPUT=$2 + local CIRCUIT_DIR=./build/$CIRCUIT + + snarkjs $CIRCOMKIT_PROOF_SYSTEM verify \ + $CIRCUIT_DIR/verifier_key.json \ + $CIRCUIT_DIR/$INPUT/public.json \ + $CIRCUIT_DIR/$INPUT/proof.json + + echo -e "${CIRCOMKIT_COLOR_LOG}Verification complete.${CIRCOMKIT_COLOR_RESET}" +} + +# Calculates the witness for the given circuit and input +witness() { + echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Computing witness ===${CIRCOMKIT_COLOR_RESET}" + local CIRCUIT=$1 + local INPUT=$2 + local JS_DIR=./build/$CIRCUIT/${CIRCUIT}_js # JS files for the circuit + local OUTPUT_DIR=./build/$CIRCUIT/$INPUT # directory for proof & public signals + local INPUT_DIR=./inputs/$CIRCUIT # directory for inputs + local WITNESS=$OUTPUT_DIR/witness.wtns # witness output + + mkdir -p $OUTPUT_DIR + + node $JS_DIR/generate_witness.js \ + $JS_DIR/$CIRCUIT.wasm \ + $INPUT_DIR/$INPUT.json \ + $WITNESS + + echo -e "${CIRCOMKIT_COLOR_LOG}Generated\n\tWitness: $WITNESS${CIRCOMKIT_COLOR_RESET}" +} + +## CLI # default values NUM_CONTRIBS=1 INPUT="default" diff --git a/scripts/functions/calldata.sh b/scripts/functions/calldata.sh deleted file mode 100755 index 530cc4f..0000000 --- a/scripts/functions/calldata.sh +++ /dev/null @@ -1,13 +0,0 @@ -## Exports a solidity contract for the verifier -calldata() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Exporting calldata ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local INPUT=$2 - local CIRCUIT_DIR=./build/$CIRCUIT - - snarkjs zkey export soliditycalldata \ - $CIRCUIT_DIR/$INPUT/public.json \ - $CIRCUIT_DIR/$INPUT/proof.json - - echo -e "${CIRCOMKIT_COLOR_LOG}Done!${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/clean.sh b/scripts/functions/clean.sh deleted file mode 100755 index bf4afcf..0000000 --- a/scripts/functions/clean.sh +++ /dev/null @@ -1,12 +0,0 @@ -## Clean build files -clean() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Cleaning artifacts ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local CIRCUIT_DIR=./build/$CIRCUIT - local TARGET=./circuits/main/$CIRCUIT.circom - - rm -rf $CIRCUIT_DIR - rm -f $TARGET - - echo -e "${CIRCOMKIT_COLOR_LOG}Deleted $CIRCUIT_DIR and $TARGET${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/compile.sh b/scripts/functions/compile.sh deleted file mode 100755 index 6d6163e..0000000 --- a/scripts/functions/compile.sh +++ /dev/null @@ -1,16 +0,0 @@ -## Compile the circuit, outputting R1CS and JS files -compile() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Compiling the circuit ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local CIRCOM_IN=./circuits/main/$CIRCUIT.circom - local CIRCOM_OUT=./build/$CIRCUIT - - # create build dir if not exists already - mkdir -p $CIRCOM_OUT - - # compile with circom - echo "circom $CIRCOM_IN -o $CIRCOM_OUT $CIRCOMKIT_COMPILER_ARGS" - circom $CIRCOM_IN -o $CIRCOM_OUT $CIRCOMKIT_COMPILER_ARGS - - echo -e "${CIRCOMKIT_COLOR_LOG}Built artifacts under $CIRCOM_OUT${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/contract.sh b/scripts/functions/contract.sh deleted file mode 100755 index 8d94161..0000000 --- a/scripts/functions/contract.sh +++ /dev/null @@ -1,12 +0,0 @@ -## Exports a solidity contract for the verifier -contract() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Generating Solidity verifier ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local CIRCUIT_DIR=./build/$CIRCUIT - - snarkjs zkey export solidityverifier \ - $CIRCUIT_DIR/prover_key.zkey \ - $CIRCUIT_DIR/verifier.sol - - echo -e "${CIRCOMKIT_COLOR_LOG}Contract created at $CIRCUIT_DIR/verifier.sol!${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/debug.sh b/scripts/functions/debug.sh deleted file mode 100644 index a525411..0000000 --- a/scripts/functions/debug.sh +++ /dev/null @@ -1,17 +0,0 @@ -## Debug a witness -debug() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Debugging witness ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local INPUT=$2 - local CIRCUIT_DIR=./build/$CIRCUIT - local OUTPUT_DIR=./build/$CIRCUIT/$INPUT # directory for proof & public signals - local INPUT_DIR=./inputs/$CIRCUIT # directory for inputs - - snarkjs wtns debug \ - $CIRCUIT_DIR/${CIRCUIT}_js/$CIRCUIT.wasm \ - $INPUT_DIR/$INPUT.json \ - $OUTPUT_DIR/witness.wtns \ - $CIRCUIT_DIR/$CIRCUIT.sym - - echo -e "${CIRCOMKIT_COLOR_LOG}Done!${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/instantiate.sh b/scripts/functions/instantiate.sh deleted file mode 100755 index d848a8f..0000000 --- a/scripts/functions/instantiate.sh +++ /dev/null @@ -1,22 +0,0 @@ -## Instantiate the main component -instantiate() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Creating main component ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - - # parse json for the circuit, trim first and last lines, and then remove all whitespace - local MATCH=$(sed -n "/ *\"${CIRCUIT}\": *{/, /^ *}[, ]$/p" ./circuits.json | sed '1d;$d' | tr -d "[:space:]") - if [ -z "$MATCH" ] - then - echo -e "${CIRCOMKIT_COLOR_ERR}No such circuit found!${CIRCOMKIT_COLOR_RESET}" - exit - fi - - # create JSON object - local JSON_IN="{\"version\":\"${CIRCOMKIT_VERSION}\",$MATCH}" - - # generate the circuit main component - local OUTDIR="./circuits/main/$CIRCUIT.circom" - npx ejs ./ejs/template.circom -i $JSON_IN -o $OUTDIR - - echo -e "${CIRCOMKIT_COLOR_LOG}Done!${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/prove.sh b/scripts/functions/prove.sh deleted file mode 100755 index 386651b..0000000 --- a/scripts/functions/prove.sh +++ /dev/null @@ -1,16 +0,0 @@ -## Generate a proof -prove() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Generating proof ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local INPUT=$2 - local CIRCUIT_DIR=./build/$CIRCUIT - local OUTPUT_DIR=$CIRCUIT_DIR/$INPUT - - snarkjs $CIRCOMKIT_PROOF_SYSTEM prove \ - $CIRCUIT_DIR/prover_key.zkey \ - $OUTPUT_DIR/witness.wtns \ - $OUTPUT_DIR/proof.json \ - $OUTPUT_DIR/public.json - - echo -e "${CIRCOMKIT_COLOR_LOG}Generated under $OUTPUT_DIR${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/setup.sh b/scripts/functions/setup.sh deleted file mode 100755 index ebfde16..0000000 --- a/scripts/functions/setup.sh +++ /dev/null @@ -1,62 +0,0 @@ -## Commence a circuit-specific setup -setup() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Circuit Setup ($CIRCOMKIT_PROOF_SYSTEM) ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 # circuit name - local P1_PTAU=$2 # path to phase-1 ptau - local NUM_CONTRIBS=$3 # number of contributions (for groth16) - local CIRCUIT_DIR=./build/$CIRCUIT # circuit directory - local PROVER_KEY=$CIRCUIT_DIR/prover_key.zkey - local VERIFICATION_KEY=$CIRCUIT_DIR/verification_key.json - - # check if P1_PTAU exists - if [ ! -f "$P1_PTAU" ]; then - echo -e "PTAU file ${CIRCOMKIT_COLOR_ERR}${P1_PTAU} does not exist.${CIRCOMKIT_COLOR_RESET}" - exit 1 - fi - - if [[ "$CIRCOMKIT_PROOF_SYSTEM" == "groth16" ]]; then - local P2_PTAU=$CIRCUIT_DIR/phase2_final.ptau # phase-2 ptau - local CUR=000 # zkey id, initially 0 - - # if Groth16, circuit specific ceremony is needed - # start phase-2 ceremony (circuit specific) - echo -e "${CIRCOMKIT_COLOR_LOG}this may take a while...${CIRCOMKIT_COLOR_RESET}" - snarkjs powersoftau prepare phase2 $P1_PTAU $P2_PTAU -v - - # generate a zkey that contains proving and verification keys, along with phase-2 contributions - snarkjs groth16 setup \ - $CIRCUIT_DIR/$CIRCUIT.r1cs \ - $P2_PTAU \ - $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey - - # get rid of phase-2 ptau - rm $P2_PTAU - - # make contributions (001, 002, ...) - for NEXT in $(seq -f "%03g" 1 ${NUM_CONTRIBS}) - do - echo "Making Phase-2 Contribution: ${NEXT}" - snarkjs zkey contribute \ - $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey \ - $CIRCUIT_DIR/${CIRCUIT}_${NEXT}.zkey -v - - rm $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey - - CUR=$NEXT - done - echo "Phase 2 Complete." - - # rename key to the prover key - mv $CIRCUIT_DIR/${CIRCUIT}_${CUR}.zkey $PROVER_KEY - else - # otherwise, we can use that phase-1 ptau alone - snarkjs $CIRCOMKIT_PROOF_SYSTEM setup $CIRCUIT_DIR/$CIRCUIT.r1cs \ - $P1_PTAU \ - $PROVER_KEY - fi - - # export - snarkjs zkey export verificationkey $PROVER_KEY $VERIFICATION_KEY - - echo -e "${CIRCOMKIT_COLOR_LOG}Generated keys\n\tProver key: $PROVER_KEY\n\tVerification key: $VERIFICATION_KEY${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/type.sh b/scripts/functions/type.sh deleted file mode 100755 index c1d6af1..0000000 --- a/scripts/functions/type.sh +++ /dev/null @@ -1,24 +0,0 @@ -## Parse the template circuit that you are using for your main component -## and generate TypeScript interfaces for it -type() { - set -e - - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Generating types ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local SYM=./build/$CIRCUIT/$CIRCUIT.sym - - # choose lines with 1 dot only (these are the signals of the main component), extract their names - local MAIN_SIGNALS=$(cat $SYM | awk -F '.' 'NF==2 {print $2}') - - # get the unique signal names - local MAIN_SIGNAL_NAMES=$(echo "$MAIN_SIGNALS" | awk -F '[' '{print $1}' | uniq) - - # get the last signal for each signal name - local SIGNALS="" - for SIGNAL in $MAIN_SIGNAL_NAMES; do - SIGNALS+="$(echo "$MAIN_SIGNALS" | grep $SIGNAL | tail -n 1) " - done - echo "$SIGNALS" - - echo -e "\n${CIRCOMKIT_COLOR_LOG}Types generated!${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/verify.sh b/scripts/functions/verify.sh deleted file mode 100755 index 78001a6..0000000 --- a/scripts/functions/verify.sh +++ /dev/null @@ -1,14 +0,0 @@ -## Verify a witness & proof -verify() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Verifying proof ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local INPUT=$2 - local CIRCUIT_DIR=./build/$CIRCUIT - - snarkjs $CIRCOMKIT_PROOF_SYSTEM verify \ - $CIRCUIT_DIR/verification_key.json \ - $CIRCUIT_DIR/$INPUT/public.json \ - $CIRCUIT_DIR/$INPUT/proof.json - - echo -e "${CIRCOMKIT_COLOR_LOG}Verification complete.${CIRCOMKIT_COLOR_RESET}" -} diff --git a/scripts/functions/witness.sh b/scripts/functions/witness.sh deleted file mode 100755 index 10af6a1..0000000 --- a/scripts/functions/witness.sh +++ /dev/null @@ -1,19 +0,0 @@ -## Calculates the witness for the given circuit and input -witness() { - echo -e "\n${CIRCOMKIT_COLOR_TITLE}=== Computing witness ===${CIRCOMKIT_COLOR_RESET}" - local CIRCUIT=$1 - local INPUT=$2 - local JS_DIR=./build/$CIRCUIT/${CIRCUIT}_js # JS files for the circuit - local OUTPUT_DIR=./build/$CIRCUIT/$INPUT # directory for proof & public signals - local INPUT_DIR=./inputs/$CIRCUIT # directory for inputs - local WITNESS=$OUTPUT_DIR/witness.wtns # witness output - - mkdir -p $OUTPUT_DIR - - node $JS_DIR/generate_witness.js \ - $JS_DIR/$CIRCUIT.wasm \ - $INPUT_DIR/$INPUT.json \ - $WITNESS - - echo -e "${CIRCOMKIT_COLOR_LOG}Generated\n\tWitness: $WITNESS${CIRCOMKIT_COLOR_RESET}" -} diff --git a/src/bin/index.ts b/src/bin/index.ts index 401205e..e36754b 100644 --- a/src/bin/index.ts +++ b/src/bin/index.ts @@ -1,3 +1,30 @@ #!/usr/bin/env node -console.log('todo todoo'); +import {Circomkit} from '../cli/'; + +async function main() { + const cli = new Circomkit(); + + // parse commands & arguments + + // await cli.prove('multiplier_3', 'default'); + // await cli.verify('multiplier_3', 'default'); + await cli.witness('multiplier_3', 'default'); + + // cli.clean('multiplier_3'); + // cli.instantiate('multiplier_3'); + // await cli.compile('multiplier_3'); + /** + * We have to exit forcefully, as SnarkJS CLI does + * too. In their code, each function returns a code, + * with the succesfull ones returning 0. If an error is + * thrown, that error is logged and process is exited + * with error code 1. + * + * See line 312 in snarkjs/cli.js + */ + // eslint-disable-next-line no-process-exit + process.exit(0); +} + +main(); diff --git a/src/cli/config.ts b/src/cli/config.ts new file mode 100644 index 0000000..62224da --- /dev/null +++ b/src/cli/config.ts @@ -0,0 +1,68 @@ +type ColorType = `\x1b[${number}m` | `\x1b[${number};${number}m`; +type VersionType = `${number}.${number}.${number}`; + +export type CircomkitConfig = { + /** Proof system to be used. */ + proofSystem: 'groth16' | 'plonk'; + /** Curve to be used, which defines the underlying prime field. */ + curve: 'bn128' | 'bls12381' | 'goldilocks'; + /** Version number for main components. */ + version: VersionType; + /** Hide Circomkit logs */ + silent: boolean; + /** Which files to output. */ + outputs: { + /** Output R1CS file. */ + r1cs: boolean; + /** Output symbol file. */ + sym: boolean; + /** Output WASM circuit. */ + wasm: boolean; + /** Output witness generator in C. */ + c: boolean; + wat: boolean; + json: boolean; + }; + colors: { + title: ColorType; // blue + log: ColorType; // gray + error: ColorType; // red + }; + /** Do an additional check over the produced constraints. */ + inspect: boolean; + /** Show Circom logs during compilation. */ + verbose: boolean; + /** + * Optimization level. + * - `0`: No simplification is applied. + * - `1`: Only applies `var` to `var` and `var` to `constant` simplification. + * - `2`: Full constraint simplification. + */ + optimization: 0 | 1 | 2; + /** Libraries to be linked to search path. */ + libraries: string[]; +}; + +export const defaultConfig: Readonly = { + proofSystem: 'groth16', + curve: 'bn128', + version: '2.1.0', + silent: false, + outputs: { + r1cs: true, + sym: true, + wasm: true, + c: false, + wat: false, + json: false, + }, + colors: { + title: '\x1b[0;34m', // blue + log: '\x1b[2;37m', // gray + error: '\x1b[0;31m', // red + }, + inspect: true, + optimization: 0, + verbose: false, + libraries: ['./node_modules'], +}; diff --git a/src/cli/index.ts b/src/cli/index.ts new file mode 100644 index 0000000..738f89d --- /dev/null +++ b/src/cli/index.ts @@ -0,0 +1,193 @@ +import * as snarkjs from 'snarkjs'; +import {wasm as wasm_tester} from 'circom_tester'; +import {CircomkitConfig, defaultConfig} from './config'; +import {writeFileSync, readFileSync} from 'fs'; +import {mkdirSync, rmSync} from 'fs'; +import instantiate from '../utils/instantiate'; +import {CircuitConfig} from '../types/circuit'; + +/** + * Circomkit is an opinionated wrapper around a few + * [Snarkjs](../../node_modules/snarkjs/main.js) functions. + */ +export class Circomkit { + readonly config: CircomkitConfig; + private readonly resetColor = '\x1b[0m'; + + constructor() { + this.config = defaultConfig; + + // TODO read config from user workspace + } + + /** Colorful logging based on config. */ + private log(message: string, type: keyof CircomkitConfig['colors'] = 'log') { + console.log(`${this.config.colors[type]}${message}${this.resetColor}`); + } + + /** + * Computes a path that requires a circuit name. + * @param circuit circuit name + * @param type path type + * @returns path + */ + private path(circuit: string, type: 'target' | 'sym' | 'pkey' | 'vkey' | 'wasm' | 'sol' | 'dir'): string { + const dir = `./build/${circuit}`; + switch (type) { + case 'dir': + return dir; + case 'target': + return `./circuits/main/${circuit}.circom`; + case 'pkey': + return `${dir}/prover_key.zkey`; + case 'vkey': + return `${dir}/verifier_key.json`; + case 'sym': + return `${dir}/symbols.sym`; + case 'sol': + return `${dir}/Verifier.sol`; + case 'wasm': + return `${dir}/${circuit}_js/${circuit}.wasm`; + default: + throw new Error('Invalid type: ' + type); + } + } + + /** + * Computes a path that requires a circuit and an input name. + * @param circuit circuit name + * @param input input name + * @param type path type + * @returns path + */ + private path2(circuit: string, input: string, type: 'pubs' | 'proof' | 'wtns' | 'in' | 'dir'): string { + const dir = `./build/${circuit}/${input}`; + switch (type) { + case 'dir': + return dir; + case 'pubs': + return `${dir}/public.json`; + case 'proof': + return `${dir}/proof.json`; + case 'wtns': + return `${dir}/witness.wtns`; + case 'in': + return `./inputs/${circuit}/${input}.json`; + default: + throw new Error('Invalid type: ' + type); + } + } + + /** Clean build files */ + clean(circuit: string) { + this.log('=== Cleaning artifacts ===', 'title'); + rmSync(this.path(circuit, 'dir'), {recursive: true, force: true}); + rmSync(this.path(circuit, 'target')); + this.log('All done!'); + } + + /** Compile the circuit */ + async compile(circuit: string) { + this.instantiate(circuit); + + this.log('=== Compiling the circuit ===', 'title'); + const outDir = this.path(circuit, 'dir'); + await wasm_tester(this.path(circuit, 'target'), { + output: outDir, + }); + this.log('Built at: ' + outDir); + } + + /** Exports a solidity contract for the verifier */ + async contract(circuit: string) { + this.log('=== Generating verifier contract ===', 'title'); + + const pkey = this.path(circuit, 'pkey'); + const verifierCode = await snarkjs.zKey.exportSolidityVerifier(pkey, { + groth16: './node_modules/snarkjs/templates/verifier_groth16.sol.ejs', + plonk: './node_modules/snarkjs/templates/verifier_plonk.sol.ejs', + }); + // TODO: hangs here??? + this.log(verifierCode); + + // output to file + const sol = this.path(circuit, 'sol'); + + this.log('Contract created at: ' + sol); + } + + /** Instantiate the main component */ + instantiate(circuit: string) { + this.log('=== Creating main component ===', 'title'); + const circuits = JSON.parse(readFileSync('./circuits.json', 'utf-8')); + if (!(circuit in circuits)) { + throw new Error('No such circuit in circuits.json'); + } + const circuitConfig = circuits[circuit] as CircuitConfig; + instantiate(circuit, { + ...circuitConfig, + dir: 'main', + version: this.config.version, + }); + this.log('Done!'); + } + + /** Generate a proof */ + async prove(circuit: string, input: string) { + this.log('=== Generating proof ===', 'title'); + const jsonInput = JSON.parse(readFileSync(this.path2(circuit, input, 'in'), 'utf-8')); + const fullProof = await snarkjs[this.config.proofSystem].fullProve( + jsonInput, + this.path(circuit, 'wasm'), + this.path(circuit, 'pkey') + ); + + const dir = this.path2(circuit, input, 'dir'); + mkdirSync(dir, {recursive: true}); + writeFileSync(this.path2(circuit, input, 'pubs'), JSON.stringify(fullProof.publicSignals, undefined, 2)); + writeFileSync(this.path2(circuit, input, 'proof'), JSON.stringify(fullProof.proof, undefined, 2)); + this.log('Generated under: ' + dir); + } + + /** Commence a circuit-specific setup */ + setup() { + throw new Error('Not implemented.'); + } + + /** + * Parse the template circuit that you are using for your main component + * and generate TypeScript interfaces for it + * + * @deprecated work in progress, do not use + */ + type() { + throw new Error('Not implemented.'); + } + + /** Verify a proof for some public signals. */ + async verify(circuit: string, input: string) { + this.log('=== Verifying proof ===', 'title'); + + const vkey = JSON.parse(readFileSync(this.path(circuit, 'vkey'), 'utf-8')); + const pubs = JSON.parse(readFileSync(this.path2(circuit, input, 'pubs'), 'utf-8')); + const proof = JSON.parse(readFileSync(this.path2(circuit, input, 'proof'), 'utf-8')); + + const result = await snarkjs[this.config.proofSystem].verify(vkey, pubs, proof); + if (result) { + this.log('Verification successful.', 'log'); + } else { + this.log('Verification failed!', 'error'); + } + } + + /** Calculates the witness for the given circuit and input. */ + async witness(circuit: string, input: string) { + this.log('=== Calculating witness ===', 'title'); + const jsonInput = JSON.parse(readFileSync(this.path2(circuit, input, 'in'), 'utf-8')); + + const dir = this.path2(circuit, input, 'dir'); + mkdirSync(dir, {recursive: true}); + await snarkjs.wtns.calculate(jsonInput, this.path(circuit, 'wasm'), this.path2(circuit, input, 'wtns')); + this.log('Created under: ' + dir); + } +} diff --git a/src/types/circom_tester.d.ts b/src/types/circom_tester.d.ts new file mode 100644 index 0000000..834fca3 --- /dev/null +++ b/src/types/circom_tester.d.ts @@ -0,0 +1 @@ +declare module 'circom_tester'; diff --git a/src/types/circuit.ts b/src/types/circuit.ts index 46afff0..a88877c 100644 --- a/src/types/circuit.ts +++ b/src/types/circuit.ts @@ -58,8 +58,10 @@ export type CircuitConfig = { file: string; /** The template name to instantiate */ template: string; - /** Directory to read the file, defaults to `test` */ + /** Directory to instantiate at */ dir?: string; + /** Target version */ + version?: `${number}.${number}.${number}`; /** An array of public input signal names, defaults to `[]` */ pubs?: string[]; /** An array of template parameters, defaults to `[]` */ diff --git a/src/types/snarkjs.d.ts b/src/types/snarkjs.d.ts new file mode 100644 index 0000000..48d4b0c --- /dev/null +++ b/src/types/snarkjs.d.ts @@ -0,0 +1 @@ +declare module 'snarkjs'; diff --git a/src/utils/instantiate.ts b/src/utils/instantiate.ts index 1d35a6d..54bed78 100644 --- a/src/utils/instantiate.ts +++ b/src/utils/instantiate.ts @@ -2,8 +2,7 @@ import type {CircuitConfig} from '../types/circuit'; import ejs from 'ejs'; import {writeFileSync, existsSync, mkdirSync} from 'fs'; -const EJS_TEMPLATE = ` -// auto-generated by circomkit +const EJS_TEMPLATE = `// auto-generated by circomkit pragma circom <%= typeof version == "undefined" ? "2.0.0" : version %>; include "../<%= file %>.circom"; diff --git a/tsconfig.base.json b/tsconfig.base.json index 65920cf..1ea7f64 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -6,8 +6,6 @@ "strict": true, "resolveJsonModule": true, "esModuleInterop": true, - "allowJs": true, - "checkJs": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "moduleResolution": "node", diff --git a/tsconfig.type.json b/tsconfig.type.json deleted file mode 100644 index 8ba7e22..0000000 --- a/tsconfig.type.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "include": ["src/**/*.ts"], - "compilerOptions": { - "module": "esnext", - "target": "es2019", - "removeComments": false, - "declaration": true, - "declarationMap": true, - "declarationDir": "./dist/types", - "outDir": "./dist/types", - "emitDeclarationOnly": true - } -}