Skip to content

Commit 4616510

Browse files
committed
initial commit
0 parents  commit 4616510

21 files changed

+2762
-0
lines changed

README.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Assignment: Writing and Proving Arithmetic Circuits
2+
3+
In this assignment you’ll learn about:
4+
* `circom`: a domain-specific language for describing arithmetic circuits, and
5+
* `snarkjs`: a tool for generating and verifying zk-SNARKs for circuit satisfiability.
6+
7+
# Setup
8+
9+
1. Install [nodejs](https://nodejs.org/en/download/) and [npm](https://www.npmjs.com/get-npm).
10+
2. Install `circom` following this [installation guide](https://docs.circom.io/getting-started/installation/). Once installed, ensure that you're using the correct version of `circom` by running `circom --version`. You should see `circom compiler 2.1.4` or later.
11+
3. Install `snarkjs`: run `npm install -g snarkjs@latest`.
12+
4. Install the `mocha test runner`: run `npm install -g mocha`.
13+
5. Run `npm install` in the same directory as this readme to install the dependencies for this assignment.
14+
6. Run `mocha test` and verify that most of the tests fail, but not because of missing dependencies.
15+
16+
# Assignment Details
17+
18+
## Task 1: Implement a (simplified) floating-point addition circuit in `circom`
19+
20+
In this task, you'll be implementing a (simplified) floating-point addition circuit in `circom`. **This task does not assume any familiarity with floating-point arithmetic and you are not required to understand floating-point addition to complete it**.
21+
Before you begin, please go through the [circom documentation](https://docs.circom.io/circom-language/signals/) and `circuits/example.circom`.
22+
23+
**The `src/` directory has a python program `float_add.py` that implements the floating-point addition logic. Use this file as a reference point for the set of instructions you have to translate into a circuit**. We have added minimal comments in `float_add.py` as they can be distracting. For curious students, we've added another file `src/float_add_with_comments.py` that implements the same logic and has extensive comments explaining the floating-point representation and the addition algorithm.
24+
25+
**The `circuits/` directory has a file `float_add.circom` that contains the skeleton of the circuit that you have to complete**.
26+
We've broken down the circuit into several templates such that each function in `float_add.py` has a corresponding template in `float_add.circom`.
27+
Each template has comments explaining its inputs and outputs, as well as any conditions that have to be enforced by that template.
28+
**You have to implement the empty templates one-by-one in the order they appear in the file**. You can independently test each template by running `mocha test/[template_name_in_snake_case].js`. **There's partial credit for each template**.
29+
Some useful templates are already implemented in `float_add.circom` for you to use in your circuit and to serve as examples.
30+
31+
`circom` will compile your circuits to a Rank-1 Constraint System (R1CS) instance (see Lecture 3 for definition), the primary efficiency metric for which is the number of constraints (fewer is better). You can find the number of constraints in your implementation using the testing suite. The suite also tells you the number of constraints expected from an optimized circuit implementation. **There's bonus points if the number of constraints in your implementation are close to the optimized constraints**.
32+
33+
> **Deliverable**: completed `float_add.circom`
34+
35+
## Task 2: Generate a zk-SNARK proof using `snarkjs`
36+
37+
In this task, you will use `snarkjs` to generate a `Groth16` proof that proves $7 \times 17 \times 19 = 2261$ using the `SmallOddFactorization` circuit implemented in `circuits/example.circom`.
38+
Follow the steps in `snarkjs` [README](https://github.com/iden3/snarkjs) until Step 24, and you will learn how to create a `Groth16` proof and verify it. You can use the `powersOfTau28_hez_final_08.ptau` file in the root directory of this assignment to skip the first 9 steps.
39+
40+
> **Deliverable**: `proof.json` and `verification_key.json` generated while following the proof generation steps.
41+
42+
# Testing
43+
44+
We’ve provided a few unit tests for the various components you have to implement to test their correctness. You can run all the tests using `mocha test`.
45+
46+
The unit tests **only check correctness of your constraint system**, i.e., the constraints are satisfied given a valid witness. They **do not check the soundness of your system**, i.e., for all invalid witnesses, the constraints are not satisfied. **To get full credit, your circuit has to be correct as well as sound**.
47+
48+
As a sanity check, the test suite also checks the number of constraints in your circuits, and throws a warning if that number is smaller than expected. If there's a warning, it is likely that you're not appropriately constraining all the signals, and thus, your system is not sound.
49+
50+
# Submission
51+
52+
Use the submission link on the course webpage to submit the deliverables (i.e., `float_add.circom`, `proof.json`, and `verification_key.json`) in a zip file.

circuits/example.circom

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
pragma circom 2.0.0;
2+
3+
/*
4+
* Decomposes `in` into `b` bits, given by `bits`.
5+
* Least significant bit in `bits[0]`.
6+
* Enforces that `in` is at most `b` bits long.
7+
*/
8+
template Num2Bits(b) {
9+
signal input in;
10+
signal output bits[b];
11+
12+
// First, compute the bit values
13+
for (var i = 0; i < b; i++) {
14+
// Use `<--` to assign to a signal without constraining it.
15+
// While our constraints can only use multiplication/addition, our
16+
// assignments can use any operation.
17+
bits[i] <-- (in >> i) & 1;
18+
}
19+
20+
// Now, contrain each bit to be 0 or 1.
21+
for (var i = 0; i < b; i++) {
22+
// Use `===` to enforce a rank-1 constraint (R1C) on signals.
23+
bits[i] * (1 - bits[i]) === 0;
24+
// ^--A--^ ^-----B-----^ C
25+
//
26+
// The linear combinations A, B, and C in this R1C.
27+
}
28+
29+
// Now, construct a sum of all the bits.
30+
// Note that var is a linear combination of signals since the `(2 ** i)` terms are constants.
31+
var sum_of_bits = 0;
32+
for (var i = 0; i < b; i++) {
33+
sum_of_bits += (2 ** i) * bits[i];
34+
}
35+
// Constrain that `sum` is equal to the input `in`.
36+
sum_of_bits === in;
37+
}
38+
39+
// Now we look at `SmallOdd`, a circuit which features:
40+
//
41+
// * the use of components, or sub-circuits
42+
// * the `<==` operator, which combines `<--` and `===`.
43+
44+
/*
45+
* Enforces that `in` is an odd number less than 2 ** `b`.
46+
*/
47+
template SmallOdd(b) {
48+
signal input in;
49+
50+
// Declare and intialize a sub-circuit;
51+
component binaryDecomposition = Num2Bits(b);
52+
53+
// Use `<==` to **assign** and **constrain** simultaneously.
54+
binaryDecomposition.in <== in;
55+
56+
// Constrain the least significant bit to be 1.
57+
binaryDecomposition.bits[0] === 1;
58+
}
59+
60+
// Next we look at `SmallOddFactorization`, a circuit which features:
61+
//
62+
// * arrays of components
63+
// * using helper (witness) signals to express multiple multiplications
64+
// * (or any iterator general computation)
65+
66+
/*
67+
* Enforces the factorization of `product` into `n` odd factors that are each
68+
* less than 2 ** `b`.
69+
*/
70+
template SmallOddFactorization(n, b) {
71+
signal input product;
72+
signal input factors[n];
73+
74+
// Constrain each factor to be small and odd.
75+
// We're going to need `n` subcircuits for small-odd-ness.
76+
component smallOdd[n];
77+
for (var i = 0; i < n; i++) {
78+
smallOdd[i] = SmallOdd(b);
79+
smallOdd[i].in <== factors[i];
80+
}
81+
82+
// Now constrain the factors to multiply to the product. Since there are
83+
// many multiplications, we introduce helper signals to split the
84+
// multiplications up into R1Cs.
85+
signal partialProducts[n + 1];
86+
partialProducts[0] <== 1;
87+
for (var i = 0; i < n; i++) {
88+
partialProducts[i + 1] <== partialProducts[i] * factors[i];
89+
}
90+
product === partialProducts[n];
91+
}
92+
93+
// Finally, we set the `main` circuit for this file, which is the circuit that
94+
// `circom` will synthesize.
95+
component main {public [product]} = SmallOddFactorization(3, 8);

0 commit comments

Comments
 (0)