Skip to content

Commit

Permalink
add test circuits, some minor fixes, array tests
Browse files Browse the repository at this point in the history
  • Loading branch information
erhant committed Feb 18, 2024
1 parent f255594 commit 60ada1d
Show file tree
Hide file tree
Showing 35 changed files with 377 additions and 100 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
node_modules
circuits/test
circuits/main
# circuits/test # keep the test files
# circuits/main # keep the main files
build
.yarn
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ The tests make use of [Circomkit](https://github.com/erhant/circomkit).

## Style

Check the formatting of test codes using:
Check the formatting with the following command:

```sh
yarn format
```

This command checks the test code with [Prettier](https://www.npmjs.com/package/prettier), and lints the book with [Markdownlint](https://www.npmjs.com/package/markdownlint).
4 changes: 2 additions & 2 deletions book/src/arrays/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Circom arrays are a different kind of beast. The main reason is that in Circom,
- Array sizes are fixed, e.g. you can't define an array based on the user input after compiling the circuit.
- Array indexing should be known at compile time, e.g. you can't ask a user for index `i` and return `arr[i]` like you _normally_ do.

Before we get to the problematic unknown-at-compile-time stuff, let's quickly recap the known-time array operations:
Before we get to the problematic unknown-at-compile-time stuff, let's quickly recap the known-at-compile-time array operations:

```cs
// an array with N elements
Expand All @@ -17,7 +17,7 @@ signal arr[N][M];
// read element at index i for known i
foo <== arr[i];

// write to element at index j for known i
// write to element at index j for known j
arr[j] <== bar;
```

Expand Down
2 changes: 1 addition & 1 deletion book/src/control-flow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ It is often useful to switch the places of two signals based on a condition, whi
> signal input in[2];
> signal output out[2];
>
> aux <== (in[1] - in[0]) * cond;
> signal aux <== (in[1] - in[0]) * cond;
>
> out[0] <== aux + in[0];
> out[1] <== -aux + in[1];
Expand Down
11 changes: 2 additions & 9 deletions book/src/merkle-trees/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Merkle Trees

If you have been in the world of crypto for a while, it is highly likely that you have heard the term [Merkle Tree](https://brilliant.org/wiki/merkle-tree/), also known as Merkle Hash Tree. A Merkle Tree is a hash-based data structure, an can serve as a cryptographic commitment scheme.
If you have been in the world of crypto for a while, it is highly likely that you have heard the term [Merkle Tree](https://brilliant.org/wiki/merkle-tree/), also known as Merkle Hash Tree. A Merkle Tree is a hash-based data structure, and can serve as a cryptographic commitment scheme.

You can commit to a set of values using a merkle tree, such as:

Expand Down Expand Up @@ -40,7 +40,7 @@ In a Merkle Tree, every node is made up of the hash of its children. In this exa
- $h_3 = H(h_6, h_7)$
- and so on.

The leaf nodes are the hashes of elements of the committed set of data. The final hash $h1$ at the root of the tree is called the **Merkle Root**.
The leaf nodes are the hashes of elements of the committed set of data. The final hash $h_1$ at the root of the tree is called the **Merkle Root**.

> Merkle Trees are often implemented as binary trees, but the concept works for $n$-ary trees as well, where each node has $n$ children.
Expand Down Expand Up @@ -90,13 +90,6 @@ graph BT

You see, we only needed to provide 3 hashes here, although our data had 8 elements! In fact, if you have $n$ elements you only need to provide $\log_2{n}$ elements to the verifier, this is so much more efficient than the naive method of sending all the data to the verifier.

- The root is the **commitment** to the vector.
- The **reveal** a value in the commitment (which is a leaf in the tree) prover does the following:
- Send sibling hashes of all nodes on root-to-leaf path.
- Verifier checks if the hashes are consistent with the root hash.
- The size of this proof to reveal a value is $\mathcal{O}(\log n)$ hash values.
- This is a **binding** scheme: once the root hash is sent, the committer is bound to the committed vector. Opening any leaf to two different values requires finding a hash collision, assumed to be intractable.

## As a Commitment Scheme

A Merkle Root can serve as a cryptographic **commitment** to a set of data.
Expand Down
12 changes: 4 additions & 8 deletions circuits/basics/magic.circom
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,19 @@ pragma circom 2.1.0;
//
// Inputs:
// - in: an N-by-N square
//
// Outputs:
// - sum: the magic sum
// - sum (public): the magic sum
template MagicSquare(n) {
signal input in[n][n];
signal output sum;
signal input sum;

// sum diagonals
var diags[2];
for (var d = 0; d < n; d++) {
diags[0] += in[d][d]; // top-left -> bottom-right
diags[1] += in[n-1-d][d]; // bottom-left -> top-right
}
sum <== diags[0]; // assign this one
sum === diags[1]; // check others with equality
sum === diags[0];
sum === diags[1];

// sum rows
var rowsums[n];
Expand All @@ -40,6 +38,4 @@ template MagicSquare(n) {
}
sum === colsums[j];
}

// TODO: complete circuit
}
6 changes: 6 additions & 0 deletions circuits/test/arrays/arr_read_10.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../arrays/index.circom";

component main = ArrayRead(10);
6 changes: 6 additions & 0 deletions circuits/test/arrays/arr_sum_10.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../arrays/index.circom";

component main = Sum(10);
6 changes: 6 additions & 0 deletions circuits/test/arrays/distinct_3.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../arrays/distinct.circom";

component main = AssertDistinct(3);
6 changes: 6 additions & 0 deletions circuits/test/arrays/is_distinct_3.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../arrays/distinct.circom";

component main = IsDistinct(3);
6 changes: 6 additions & 0 deletions circuits/test/basics/fibonacci_14.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../basics/fibonacci.circom";

component main = Fibonacci(14);
6 changes: 6 additions & 0 deletions circuits/test/basics/magic_3x3.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../basics/magic.circom";

component main {public[sum]} = MagicSquare(3);
6 changes: 6 additions & 0 deletions circuits/test/basics/magic_4x4.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../basics/magic.circom";

component main {public[sum]} = MagicSquare(4);
6 changes: 6 additions & 0 deletions circuits/test/basics/multiplier_3.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../basics/multiplier.circom";

component main = Multiplier(3);
6 changes: 6 additions & 0 deletions circuits/test/basics/sudoku_4x4.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../basics/sudoku.circom";

component main {public[puzzle]} = Sudoku(2);
6 changes: 6 additions & 0 deletions circuits/test/basics/sudoku_9x9.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../basics/sudoku.circom";

component main {public[puzzle]} = Sudoku(3);
6 changes: 6 additions & 0 deletions circuits/test/bits/and_gate.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../bits/gates.circom";

component main = AND();
6 changes: 6 additions & 0 deletions circuits/test/bits/bits2num_5.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../bits/index.circom";

component main = Bits2Num(5);
6 changes: 6 additions & 0 deletions circuits/test/bits/not_gate.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../bits/gates.circom";

component main = NOT();
6 changes: 6 additions & 0 deletions circuits/test/bits/num2bits_5.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../bits/index.circom";

component main = Num2Bits(5);
6 changes: 6 additions & 0 deletions circuits/test/bits/or_gate.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../bits/gates.circom";

component main = OR();
6 changes: 6 additions & 0 deletions circuits/test/bits/xor_gate.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../bits/gates.circom";

component main = XOR();
6 changes: 6 additions & 0 deletions circuits/test/comparators/compconstant.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../comparators/compconstant.circom";

component main = CompConstant(6896);
6 changes: 6 additions & 0 deletions circuits/test/comparators/ifelse.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../comparators/index.circom";

component main = IfElse();
6 changes: 6 additions & 0 deletions circuits/test/comparators/inRange_1_9.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../comparators/range.circom";

component main = AssertInRange(1, 9);
6 changes: 6 additions & 0 deletions circuits/test/comparators/switch.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../comparators/index.circom";

component main = Switch();
6 changes: 6 additions & 0 deletions circuits/test/control-flow/ifelse.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../control-flow/index.circom";

component main = IfElse();
6 changes: 6 additions & 0 deletions circuits/test/control-flow/switch.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// auto-generated by circomkit
pragma circom 2.0.0;

include "../../control-flow/index.circom";

component main = Switch();
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"license": "MIT",
"scripts": {
"test": "npx jest",
"format": "npx prettier --check ./tests/**/*.ts",
"format": "yarn format:ts",
"format:ts": "npx prettier --check ./tests/**/*.ts",
"format:md": "echo todo",
"book": "cd book && mdbook serve --open",
"book:build": "cd book && mdbook build"
},
Expand All @@ -16,6 +18,7 @@
"@types/jest": "^29.5.12",
"@types/node": "^20.11.16",
"jest": "^29.7.0",
"markdownlint": "^0.33.0",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.1",
Expand Down
77 changes: 74 additions & 3 deletions tests/arrays/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,77 @@
import type { WitnessTester } from "circomkit";
import { circomkit } from "../common";
import { circomkit, primes } from "../common";

describe.skip("arrays", () => {
// TODO
describe("arrays", () => {
const N = 10;
const arr = Array.from({ length: N }, (_, i) => i);

describe("reads", () => {
let circuit: WitnessTester<["in", "index"], ["out"]>;

beforeAll(async () => {
circuit = await circomkit.WitnessTester(`arr_read_${N}`, {
file: "arrays/index",
template: "ArrayRead",
dir: "test/arrays",
params: [N],
});
});

it("should read correct index", async () => {
for (let i = 0; i < N; i += N >> 2) {
await circuit.expectPass({ in: arr, index: i }, { out: arr[i] });
}
});

it("should read 0 for out-of-bounds index", async () => {
await circuit.expectPass({ in: arr, index: N }, { out: 0 });
await circuit.expectPass({ in: arr, index: -1 }, { out: 0 });
});
});

describe("writes", () => {
let circuit: WitnessTester<["in", "index", "value"], ["out"]>;

beforeAll(async () => {
circuit = await circomkit.WitnessTester(`arr_sum_${N}`, {
file: "arrays/index",
template: "ArrayWrite",
dir: "test/arrays",
params: [N],
});
});

it("should write correctly", async () => {
for (let i = 0; i < N; i += N >> 2) {
const newArr = [...arr];
const val = i * 10;
newArr[i] = val;
await circuit.expectPass({ in: arr, index: i, value: val }, { out: newArr });
}
});

it("should return the same array for out-of-bounds index", async () => {
await circuit.expectPass({ in: arr, index: N, value: -1 }, { out: arr });
await circuit.expectPass({ in: arr, index: -1, value: -1 }, { out: arr });
});
});

describe("sum", () => {
let circuit: WitnessTester<["in"], ["out"]>;

beforeAll(async () => {
circuit = await circomkit.WitnessTester(`arr_sum_${N}`, {
file: "arrays/index",
template: "Sum",
dir: "test/arrays",
params: [N],
});
});

it("should sum correctly", async () => {
// NOTE: may give wrong results for large N, use modulo if you have to
const sum = arr.reduce((acc, cur) => acc + cur);
await circuit.expectPass({ in: arr }, { out: sum });
});
});
});
Loading

0 comments on commit 60ada1d

Please sign in to comment.