Giter Club home page Giter Club logo

circomkit's Introduction

Circomkit

A simple-to-use & opinionated circuit development & testing toolkit.

NPM Workflow: Tests

  • Simple CLI, abstracting away all paths with a simple config.
  • Provides type-safe testing utilities to check for circuit computations & soundness errors, with minimal boilerplate code!
  • Supports all protocols: groth16, plonk, and fflonk.
  • Automatically downloads phase-1 PTAU when using bn128.
  • Supports multiple exports such as a Solidity verifier contract and its calldata for some input, or JSON exports for R1CS and the witness file.

Installation

Circomkit can be installed via:

npm  install circomkit
pnpm install circomkit
yarn add     circomkit
bun  add     circomkit

You will also need Circom, which can be installed following the instructions here.

Usage

You can see available commands with:

npx circomkit help

You can check out examples at the circomkit-examples repository.

Command Line Interface

Actions that require a circuit name can be called as follows:

# Compile the circuit
npx circomkit compile <circuit>

# Create the main component
npx circomkit instantiate <circuit>

# Create a Solidity verifier contract
npx circomkit contract <circuit>

# Clean circuit artifacts
npx circomkit clean <circuit>

# Circuit-specific setup
npx circomkit setup <circuit> [ptau-path]

# Create verification key
npx circomkit vkey <circuit> [pkey-path]

# Automatically download PTAU (for BN128)
npx circomkit ptau <circuit>

Note

Circuit-specific setup optionally takes the path to a PTAU file as argument. If not provided, it will automatically decide the PTAU to use with respect to constraint count, and download that for you! This feature only works for bn128 prime, and has an upper-limit of at most $2^{28}$ constraints.

Some actions such as generating a witness, generating a proof and verifying a proof require JSON inputs to provide the signal values. For that, we specifically create our input files under the inputs folder, and under the target circuit name there. For example, an input named foo for some circuit named bar would be at inputs/bar/foo.json.

# Generate a witness
npx circomkit witness <circuit> <input>

# Generate a proof
npx circomkit prove <circuit> <input>

# Verify a proof with public signals
npx circomkit verify <circuit> <input>

# Export Solidity calldata to console
npx circomkit calldata <circuit> <input>

Circomkit Configurations

Everything used by Circomkit can be optionally overridden by providing the selected fields in its constructor. Circomkit CLI does this automatically by checking out circomkit.json and overriding the defaults with that. You can print the active configuration via the following command:

npx circomkit config

You can edit any of the fields there to fit your needs. Most importantly, you can change the protocol to be groth16, plonk or fflonk; and you can change the underlying prime field to bn128, bls12381 and goldilocks.

Note

Using a prime other than bn128 makes things a bit harder in circuit-specific setup, as you will have to find the PTAU files yourself, whereas in bn128 we can use Perpetual Powers of Tau.

Circuit Configurations

A circuit config within circuits.json looks like below, where the key is the circuit name to be used in commands, and the value is an object that describes the filename, template name, public signals and template parameters:

sudoku_9x9: {
  file:     'sudoku',
  template: 'Sudoku',
  pubs:     ['puzzle'],
  params:   [3], // sqrt(9)
}

Tip

The pubs and params options can be omitted, in which case they will default to [].

Using Circomkit in Code

All CLI commands other than init can be used with the same name and arguments within Circomkit. Furthermore, you can provide configuration & inputs directly, instead of letting Circomkit read from circuits.json or from within the inputs folder.

import {Circomkit} from 'circomkit';

const circomkit = new Circomkit({
  // custom configurations
  protocol: 'plonk',
});

// artifacts output at `build/multiplier_3` directory
await circomkit.compile('multiplier_3', {
  file: 'multiplier',
  template: 'Multiplier',
  params: [3],
});

// proof & public signals at `build/multiplier_3/my_input` directory
await circomkit.prove('multiplier_3', 'my_input', {in: [3, 5, 7]});

// verify with proof & public signals at `build/multiplier_3/my_input`
await circomkit.verify('multiplier_3', 'my_input');

Writing Tests

Circomkit provides two tester utilities that use Chai assertions within, which may be used in a test suite such as Mocha. The key point of these utilities is to help reduce boilerplate code and let you simply worry about the inputs and outputs of a circuit.

Witness Tester

The Witness tester extends require('circom_tester').wasm tool with type-safety and few assertion functions. It provides a very simple interface:

  • expectPass(input) checks if constraints & assertions are passing for an input
  • expectPass(input, output) additionally checks if the outputs are matching
  • expectFail(input) checks if any constraints / assertions are failing

See an example below:

describe('witness tester', () => {
  // input signals and output signals can be given as type parameters
  // this makes all functions type-safe!
  let circuit: WitnessTester<['in'], ['out']>;

  beforeAll(async () => {
    const circomkit = new Circomkit();
    circuit = await circomkit.WitnessTester(CIRCUIT_NAME, CIRCUIT_CONFIG);
  });

  it('should pass on correct input & output', async () => {
    await circuit.expectPass(INPUT, OUTPUT);
  });

  it('should fail on wrong output', async () => {
    await circuit.expectFail(INPUT, WRONG_OUTPUT);
  });

  it('should fail on bad input', async () => {
    await circuit.expectFail(BAD_INPUT);
  });
});

You can check if the number of constraints are correct using expectConstraintCount, as shown below:

it('should have correct number of constraints', async () => {
  // expects at least N constraints
  await circuit.expectConstraintCount(N);
  // expects exactly N constraints
  await circuit.expectConstraintCount(N, true);
});

If you want more control over the output signals, you can use the compute function. It takes in an input, and an array of output signal names used in the main component so that they can be extracted from the witness.

it('should compute correctly', async () => {
  const output = await circuit.compute(INPUT, ['out']);
  expect(output).to.haveOwnProperty('out');
  expect(output.out).to.eq(BigInt(OUTPUT.out));
});

Finally, you can run tests on the witnesses too. This is most useful when you would like to check for soundness errors.

  • expectConstraintPass(witness) checks if constraints are passing for a witness
  • expectConstraintFail(witness) checks if constraints are failing

You can compute the witness via the calculateWitness(input) function. To test for soundness errors, you may edit the witness and see if constraints are failing.

Warning

Circomkit provides a nice utility for this purpose, called editWitness(witness, symbols). You simply provide a dictionary of symbols to their new values, and it will edit the witness accordingly. See the example below:

it('should pass on correct witness', async () => {
  const witness = await circuit.calculateWitness(INPUT);
  await circuit.expectConstraintPass(witness);
});

it('should fail on fake witness', async () => {
  const witness = await circuit.calculateWitness(INPUT);
  const badWitness = await circuit.editWitness(witness, {
    'main.signal': BigInt(1234),
    'main.component.signal': BigInt('0xCAFE'),
    'main.foo.bar[0]': BigInt('0b0101'),
  });
  await circuit.expectConstraintFail(badWitness);
});

Proof Tester

As an alternative to simulate generating a proof and verifying it, you can use Proof Tester. The proof tester makes use of WASM file, prover key and verifier key in the background. It will use the underlying Circomkit configuration to look for those files, and it can generate them automatically if they do not exist. An example using Plonk protocol is given below. Notice how we create the necessary files before creating the tester, as they are required for proof generation and verification.

describe('proof tester', () => {
  // input signals and output signals can be given as type parameters
  // this makes all functions type-safe!
  let circuit: ProofTester<['in']>;
  const protocol = 'plonk';

  beforeAll(async () => {
    const circomkit = new Circomkit({protocol});
    circomkit.instantiate(CIRCUIT_NAME, CIRCUIT_CONFIG);
    await circomkit.setup(CIRCUIT_NAME, PTAU_PATH);
    circuit = await circomkit.ProofTester(CIRCUIT_NAME, protocol);
  });

  it('should verify a proof correctly', async () => {
    const {proof, publicSignals} = await circuit.prove(INPUT);
    await circuit.expectPass(proof, publicSignals);
  });

  it('should NOT verify a proof with invalid public signals', async () => {
    const {proof} = await circuit.prove(INPUT);
    await circuit.expectFail(proof, BAD_PUBLIC_SIGNALS);
  });
});

Type-Safety

You may notice that there are optional template parameters in both testers: WitnessTester<InputSignals, OutputSignals> and ProofTester<InputSignals>. These template parameters take in an array of strings corresponding to signal names. For example, if your circuit has two input signals in1, in2 and an output out, you may instantiate the tester as WitnessTester<['in1', 'in2'], ['out']>. In doing so, you will get type-checking on all inputs and outputs required by the tester.

File Structure

Circomkit with its default configuration follows an opinionated file structure, abstracting away the pathing and orientation behind the scenes. All of these can be customized by overriding the respective settings in circomkit.json.

An example structure is shown below. Suppose there is a generic circuit for a Sudoku solution knowledge proof written under circuits folder. When instantiated, a main component for a 9x9 board is created under circuits/main. The solution along with it's puzzle is stored as a JSON object under inputs/sudoku_9x9. You can see the respective artifacts under build directory. In particular, we see groth16 prefix on some files, indicating that Groth16 protocol was used to create them.

circomkit
├── circuits.json
├── circomkit.json
│
├── circuits
│   ├── main
│   │   └── sudoku_9x9.circom
│   └── sudoku.circom
│
├── inputs
│   └── sudoku_9x9
│       └── my_solution.json
│
├── ptau
│   └── powersOfTau28_hez_final_08.ptau
│
└── build
    └── sudoku_9x9
        ├── sudoku_9x9_js
        │   │── generate_witness.js
        │   │── witness_calculator.js
        │   └── sudoku_9x9.wasm
        │
        ├── my_solution
        │   │── proof.json
        │   │── public.json
        │   └── witness.wtns
        │
        ├── sudoku_9x9.r1cs
        ├── sudoku_9x9.sym
        │
        ├── groth16_pkey.zkey
        ├── groth16_vkey.json
        └── groth16_verifier.sol

Testing

Run all tests via:

pnpm test

Tip

You can also use the CLI while developing Circomkit locally via pnpm cli as if you are using npx circomkit. This is useful for hands-on testing stuff.

Styling

Circomkit uses Google TypeScript Style Guide.

# check the formatting
pnpm format

# lint everything
pnpm lint

Acknowledgements

We wholeheartedly thank BuidlGuild & Austin Griffith for providing Circomkit with an Ecosystem Impact Grant!

circomkit's People

Contributors

0xjei avatar ctrlc03 avatar erhant avatar numtel avatar wp-lai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

circomkit's Issues

Bun Integration

Tracker issue for Bun compatibility, mostly SnarkJS related. Will update the list below:

  • Hangs at snarkjs.plonk.setup
  • Hangs at snarkjs.info | resolved by #76

circomkit:setup

I was wondering if it's completely necessary to perform this step? https://github.com/erhant/circomkit/blob/main/src/circomkit.ts#L417

The ptau(s) that circomkit downloads are from the Hermez phase1 ceremony, which are ready to be used in a phase2 ceremony.

ps. the base url of the ptau files should be updated just to be safe - seems like they have moved to google storage

EDIT: issue breakdown

  • Remove these lines and use existing PTAU instead of thats being created ptau2init there
  • Update base URL for Perpetual Powers of Tau urls
  • Add an extract function that looks for the zKey (w.r.t circuit name and configured protocol) and generates the vKey
  • Add CLI wrapper that calls extract
  • No need to check if PTAUs for larger constrain counts exist as you mentioned above, but maybe we can add a comment TODO: check for performance gains when larger PTAUs are found instead of the target PTAU; I want to check that out sometime :)

TCKN digit-knowledge circuit

TCKN Wiki

  • Must have 11 digits
  • Sum of first 10 digits mod 10 should give 11th digit
  • Let d[i] denote the i'th digit, 1-indexed. Then,
    • ((d[1] + d[3] + d[5] + d[7] + d[9]) * 7 + (d[2] + d[4] + d[6] + d[8]) * 9) mod 10 should give 10th digit
    • (d[1] + d[3] + d[5] + d[7] + d[9]) * 8 mod 10 should give 11th digit.
  • Also note that 11th digit is an even number

Use Parcel

Multiple target is buggy with generating types, but can be done within a script with using Parcel API

Old Circomkit for legacy

Just found the first version of Circomkit, written in bash entirely:

#!/bin/bash

cd "${0%/*}"/.. # get to project root
set -e # abort on error

### 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
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}"
  exit 1
fi
valid_elliptic_curves=("bn128" "bls12381" "goldilocks")
if [[ ! " ${valid_elliptic_curves[@]} " =~ " ${CIRCOMKIT_ELLIPTIC_CURVE} " ]]; then
  echo -e "${CIRCOMKIT_COLOR_ERR}Invalid elliptic curve: $CIRCOMKIT_ELLIPTIC_CURVE${CIRCOMKIT_COLOR_RESET}"
  exit 1
fi 

## 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"
P1_PTAU="./ptau/powersOfTau28_hez_final_12.ptau"

# get arguments
while getopts "f:c:n:i:p:d:" opt; do
  case $opt in
    # function to call
    f) 
      FUNC="$OPTARG"
      ;;
    # circuit name
    c) 
      CIRCUIT="$OPTARG"
      ;;
    # number of contributions
    n) 
      NUM_CONTRIBS="$OPTARG"
      ;;
    # input name
    i) 
      INPUT="$OPTARG"
      ;;
    # phase-1 ptau path
    p) 
      P1_PTAU="$OPTARG"
      ;;
    # invalid option
    \?) 
      echo "Invalid option -$OPTARG" >&2
      exit 1
      ;; 
  esac
      
  case $OPTARG in
    -*) echo "Option $opt needs a valid argument"
    exit 1
    ;;
  esac
done

# parse circuit & input paths via basename
# TODO, maybe not needed
CIRCUIT=$(basename $CIRCUIT .circom)
INPUT=$(basename $INPUT .json)

case $FUNC in
  clean)
    clean $CIRCUIT
    ;;
  contract) 
    contract $CIRCUIT
    ;;
  calldata) 
    calldata $CIRCUIT $INPUT
    ;;
  compile) 
    instantiate $CIRCUIT && compile $CIRCUIT
    ;;
  debug) 
    debug $CIRCUIT $INPUT
    ;;
  instantiate) 
    instantiate $CIRCUIT
    ;;
  type) 
    type $CIRCUIT
    ;;
  setup) 
    setup $CIRCUIT $P1_PTAU $NUM_CONTRIBS
    ;;
  keygen) 
    compile $CIRCUIT && setup $CIRCUIT $P1_PTAU $NUM_CONTRIBS
    ;;
  prove) 
    witness $CIRCUIT $INPUT && prove $CIRCUIT $INPUT
    ;;
  witness) 
    witness $CIRCUIT $INPUT
    ;;
  verify) 
    verify $CIRCUIT $INPUT
    ;;
  *) 
    echo "Usage:"
    echo "  -f <function>"
    echo "    clean        Cleans the build artifacts"
    echo "    contract     Export Solidity verifier"
    echo "    calldata     Export Solidity calldata for verification"
    echo "    compile      Compile the circuit"
    echo "    instantiate  Instantiate the main component"
    echo "    type         Generate types for TypeScript"
    echo "    setup        Phase-2 setup for the circuit"
    echo "    witness      Generate witness from an input"
    echo "    prove        Prove an input"
    echo "    verify       Verify a proof & public signals"
    echo "    keygen       Shorthand for compile & setup"
    echo "  -c <circuit-name>"
    echo "  -n <num-contributions> (default: $NUM_CONTRIBS)"
    echo "  -i <input-name>"
    echo "  -p <phase1-ptau-path>"
    ;;
esac

Specifying a custom dir with "dirCircuits"

If I specify a custom directory for my circuit templates (e.g. src) and run circomkit compile it still creates a circuits/main/circuit.circom folder for its component. It fails because the relative path generated within that circuit is wrong. "../circuit.circom" doesn't exist in that folder.

I think the right folder for components should be src/main/circuit.circom.

The following line could be the problem.

const targetDir = `./circuits/${directory}`;

Does it make sense? Or am I missing something?

feat: Allow to generate the C witness calculator on compilation

Currently, when compiling a circuit, it is not possible to generate the C witness calculator files. By adding a optional config entry, we can decide whether to generate those files or not.

Would you be happy to have this feature in?

ps. great work with circomkit, we are just now integrating it in maci, and having this option would make it much easier for us to integrate it in our workflows.

`proof.json` and `public.json` should be protocol-specific

For example, groth16_proof.json & groth16_public.json and such. The reason is, when you change protocols the previous results will be overwritten due to them using the same path.

Warning

This would be a breaking change due to pathing.

Hardhat hangs on test

Can be shown with --detectOpenHandles

@numtel I was able to reduce some number packages for hardhat, but I dont think it has to do with this one. Would you have any idea on why Hardhat might leave an open handle?

Add a symbol viewer function

For example, it should output a list of pairs which the CLI prints out as something like:

Symbol Value
main.in 1
main.comp.in1 2
main.comp.in1 3
main.out 4

and such.

CLI command `circomkit list`

This command will list the built circuits under the configured build folder. It's just a quality-of-life command to see which circuits are built. It could even go beyond and see which inputs are exported, and which protocols are outputted for each.

Parallel testing

Consider the following test (of a logical OR gate):

await circuit.expectPass({ in: [0, 0] }, { out: 0 });
await circuit.expectPass({ in: [0, 1] }, { out: 0 });
await circuit.expectPass({ in: [1, 0] }, { out: 0 });
await circuit.expectPass({ in: [1, 1] }, { out: 1 });

If we were to use Promise.all for these, we get errors. Need to dive deeper into why that happens.

Detect optimized symbols

When reading a witness value, if that witness got optimized due to Circom optimization parameters, their variable index will be -1. It is possible to keep track of this somewhere (--simplification_substitution option) but we may just want to give a more explicit error for these cases.

Currently, it returns undefined if you read an optimized symbol.

Tests to-do

Need unit-tests for Circomkit functions.

Also check: how to install Circom in a Github workflow?

`fflonk` calldata export bug

The issue is found: for fflonk snarkjs takes the parameters (pubs, proof) instead of (proof, pubs) like it does for the others.

Anyways, Im rewriting that whole part so that we can take Solidity-compatible exports too.

Add pattern matching to `compile` command

  • For instance, npx circomkit compile * should compile everything in circuits.json
  • npx circomkit compile by itself can default to the command above
  • npx circomkit compile mul_* could complie circuits like mul_1, mul_2, mul_xx.

Allow circuit-specific Circomkit config overrides

For example, we may set plonk as the Circomkit protocol in circomkit.json, but we would also like something such as:

{
  "multiplier_3": {
    "file": "multiplier",
    "template": "Multiplier",
    "params": [3],
    "overrides:" {
        // circomkit overrides here!
        "version": "2.1.6",
        "protocol": "groth16"
    }
  },
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.