Skip to content

Commit

Permalink
client CLI binary (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
weijiekoh committed Mar 28, 2023
1 parent 25a749e commit 273ae84
Show file tree
Hide file tree
Showing 60 changed files with 41,168 additions and 6,564 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
#args: --all -- --check

clippy:
if: github.event.pull_request.draft == false
Expand Down
32 changes: 32 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: publish

on:
push:
branches: ["docs"]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install mdbook
uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: '0.4.25'

- name: Install mdbook-katex
uses: actions-rs/cargo@v1
with:
command: install
#args: [email protected] [email protected]
args: [email protected]

- name: Build html
run: mdbook build ./docs

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/book/html
4,097 changes: 0 additions & 4,097 deletions 11.hex

This file was deleted.

Binary file added 11.ptau
Binary file not shown.
11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ ark-poly-commit = { version = "^0.3.0", default-features = false }
digest = { version = "0.9" }
ethers = "1.0.2"
hex = "0.4.3"
semaphore = { git = "https://github.com/worldcoin/semaphore-rs" }
semaphore = { git = "https://github.com/worldcoin/semaphore-rs", rev = "ee658c22684696232f68ef08beb8494280fb7da4" }
serde = "1.0"
ark-bn254 = "0.3.0"
sha3 = "0.10.6"
tokio = { version = "1.22.0", features = ["macros"] }
stopwatch = "0.0.7"
ppot-rs = "0.1.1"
clap = { version = "4.1.8", features = ["derive", "cargo"] }
clap-num = { version = "1.0.2" }
reqwest = "0.11.15"

[dev-dependencies]
rand_chacha = { version = "0.3.0", default-features = false }
Expand All @@ -36,3 +40,8 @@ path = "src/setup/main.rs"
edition = "2021"
name = "demo"
path = "src/demo/main.rs"

[[bin]]
edition = "2021"
name = "client"
path = "src/client/main.rs"
77 changes: 68 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,24 @@ cd semacaulk
cargo test
```

## Documentation

We use `mdbook` v0.4.25 for documentation.

Install `mdbook` using [these
instructions](https://rust-lang.github.io/mdBook/guide/installation.html).

To build the Semacaulk documentation, run this in the Semacaulk project root:

```bash
mdbook serve ./docs
```

## Demo

The setup files `11.hex` and `lagrangeComms_11` are already in the repository.
To run a demo of Semacaulk with a maximum capacity of `2 ^ 11 = 2048`, first
build the demo executable:
The Powers of Tau output from Hermez Network (`11.ptau`) is already in the
repository. To run a demo of Semacaulk with a maximum capacity of `2 ^ 11 =
2048`, first build the demo executable:

```bash
cargo build --release
Expand All @@ -48,14 +61,10 @@ cargo build --release
Run the demo:

```bash
./target/release/demo 11 11.hex lagrangeComms_11
./target/release/demo 11 11.ptau
```

To run a demo with a different maximum capacity, first generate the `.hex` file
with
[export-ptau-points](https://github.com/geometryresearch/export-ptau-points),
then use the `setup` executable to generate the `lagrangeComms_n` file. For
example for a maximum capacity of `2 ^ 12`:
To run a demo with a different maximum capacity, download a larger `.ptau` file and specify the log 2 of the desired maximum capacity:

```bash
./target/release/setup 12 12.hex lagrangeComms_12
Expand All @@ -70,3 +79,53 @@ Now, run:
A future release will integrate implement the functionality of
`export-ptau-points` into the `setup` executable, so a separate step will not
be needed.

### CLI client

For testing and demonstration purposes, we also include a CLI client binary.

To deploy a Semacaulk contract (supporting a capacity of 2 ^ 11), first run
`anvil` or any Ethereum node at `127.0.0.1:8545` or use the `--rpc` flag to
specify a node.

Make sure you have built the `client` binary:

```bash
./build_contracts.sh && \
cargo build --release --bin client
```

Run the `client deploy` subcommand. Make sure that the `-l` flag is set to the
correct value!

```bash
./target/release/client deploy --ptau ./11.ptau --rpc http://127.0.0.1:8545 -l 11
```

The contract address will be printed to the console. With the default private
key on a fresh RPC node, the address should be `0x5fbdb2315678afecb367f032d93f642f64180aa3`.

To insert an identity commitment (where the identity nullifier is `1` and the
identity trapdoor is `2`, run `client insert`:

```bash
./target/release/client insert --ptau 11.ptau -c 0x5fbdb2315678afecb367f032d93f642f64180aa3 --rpc http://127.0.0.1:8545 -n 0x1 -t 0x2 -l 11
```

The client will print the transaction hash and the index of the insertion.

```
Transaction hash:
0x634bfbfd1984fd27205c2995860572703d8f2b92face4cf7b827e70f33009617
Identity index:
0x0000000000000000000000000000000000000000000000000000000000000000
```

## Documentation

We use `mdbook` v0.4.25 for documentation To build the documentation, navigate
to the `docs` directory and run:

```bash
mdbook serve
```
2 changes: 2 additions & 0 deletions build_contracts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ rm -rf src/contracts/out
cargo clean -p semacaulk
forge clean --root src/contracts/
forge build --root src/contracts/
cp src/contracts/out/Verifier.sol/Verifier.json src/contracts/
cp src/contracts/out/Semacaulk.sol/Semacaulk.json src/contracts/
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
book
15 changes: 15 additions & 0 deletions docs/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[book]
authors = ["Andrija Novakovic", "Koh Wei Jie", "Kobi Gurkan"]
language = "en"
multilingual = false
src = "src"
title = "Semacaulk"

[output.html]
mathjax-support = true

[output.katex]

[preprocessor.katex]
renderers = ["html"]
#inline-delimiter = {left = "\\(", right = "\\)"}
19 changes: 19 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Summary

- [Overview](./overview.md)
- [Quick start](./quick_start.md)
- [Trusted Setup](./trusted_setup.md)
- [Cryptographic Specification](./cryptographic_specification.md)
- [System invariants](./system_invariants.md)
- [Insertion](./insertion.md)
- [The Circuit and Gates](./circuit_and_gates.md)
- [Precomputation and updates](./precomputation_and_updates.md)
- [The Fiat-Shamir Transcript](./fiat_shamir_transcript.md)
- [Proof generation](./proof_generation.md)
- [Proof verification](./verification.md)
- [The Lagrange Basis Polynomial Commitment Tree](./lagrange_basis_polynomial_commitment_tree.md)
- [Mechanism of Operation](./mechanism_of_operation.md)
- [Ethereum contracts](./ethereum_contracts.md)
- [Gas costs](./gas_costs.md)
- [Performance and Benchmarks](./performance_benchmarks.md)
- [Credits](./credits.md)
184 changes: 184 additions & 0 deletions docs/src/circuit_and_gates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# The Circuit and Gates

Semacaulk uses a custom Plonk-style proof system where a prover must convince a
verifier that it knows of some private *witness* values which are the result of
the correct execution of predefined logical operations upon public inputs and
fixed data. In other terms, there is a *circuit* which represents some program.
In proof systems like Groth16, circuits are represented in the form of a Rank-1
Constraint System (R1CS), and compilers like [circom](https://iden3.io/circom)
can be used to easily compile circuits to this format. Semacaulk, by contrast,
uses a set of custom gates on a set of data columns to represent its
logic.

## Private inputs (witness)

- \\(\mathsf{id\\_nul}\\): the identity nullifier.
- \\(\mathsf{id\\_trap}\\): the identity trapdoor.
- \\(i\\): the index of the prover's identity commitment in the accumulator.

## Public inputs

- \\(\mathsf{ext\\_nul}\\): the extenal nullifier.
- \\(\mathsf{id\\_comm}\\): the identity commitment, which is the MiMC7
`multi_hash` of \\([\mathsf{id\\_nul}, \mathsf{id\\_trap}]\\).
- \\(\mathsf{nul\\_hash}\\): the nullifier hash, which is the MiMC7
`multi_hash` of \\([\mathsf{id\\_nul}, \mathsf{ext\\_nul}]\\).

## Columns

| Row | \\(\mathsf{w}_0\\) | \\(\mathsf{w}_1\\) | \\(\mathsf{w}_2\\) | \\(\mathsf{key}\\) | \\(\mathsf{c}\\) | \\(\mathsf{q\\_mimc}\\) |
|-|-|-|-|-|-|-|
|0| \\(\mathsf{id\\_nul}\\) | \\(\mathsf{id\\_trap}\\) | \\(\mathsf{ext\\_nul}\\) | \\(\mathsf{w}_0[n] + \mathsf{w}_0[0] \\) | \\(\mathsf{cts}[0]\\) | 1 |
|1| \\((\mathsf{w}_0[0] + \mathsf{c}[0]) ^ 7\\) | \\((\mathsf{w}_1[0] + \mathsf{key}[0] + \mathsf{c}[0]) ^ 7\\) | \\((\mathsf{w}_2[0] + \mathsf{key}[0] + \mathsf{c}[0]) ^ 7\\)| \\(\mathsf{w}_0[n] + \mathsf{w}_0[0] \\) | \\(\mathsf{cts}[1]\\) | 1 |
|\ldots|\ldots|\ldots|\ldots|\ldots|\ldots|\ldots|
| \\(n\\) | \\((\mathsf{w}_0[n - 1] + \mathsf{c}[n - 1]) ^ 7\\) | \\((\mathsf{w}_1[n - 1] + \mathsf{key}[n - 1] + \mathsf{c}[n - 1]) ^ 7\\) | \\((\mathsf{w}_2[n - 1] + \mathsf{key}[n - 1] + \mathsf{c}[n - 1]) ^ 7\\)| \\(\mathsf{w}_0[n] + \mathsf{w}_0[0] \\) | \\(\mathsf{dummy}\\) | 0 |
| 128 | \\(b\\) | \\(b\\) | \\(b\\) | \\(b\\) | \\(b\\) | \\(b\\)

Notes:

- The 0th row contains the \\(\mathsf{id\\_nul}\\), \\(\mathsf{id\\_trap}\\), etc
values. They are not table headers.
- \\(n\\) is the constant (91) defined in
[4](./cryptographic_specification.html#4-the-mimc7-hash-function).
- \\(\mathsf{dummy}\\) can be any value as it will not be used by any of the gates.
- \\(\mathsf{q\\_mimc}\\) is a selector column. It is a vector starting with
\\(n\\) 1 values followed by zeros.
- \\(\mathsf{c}\\) is a fixed column starting with \\(n\\) MiMC7 round constants.
- \\(b\\) are random values used to blind the columns, in order to
make it computationally infeasible to brute-force their polynomial commitments.

## Gates

To understand how the logic of the circuit is encoded, consider each row of the
table as inputs to the linear combination of the following gates, which must
evaluate to 0 for a valid proof to be generated. In effect:

\\(\mathsf{gate}_0(r) + \ldots + \mathsf{gate}_n(r) = 0\\) must be true.

Each and every gate must evaluate to 0. It is not possible for the prover to
cheat by having some gates evaluate to some value such that the total evaluates
to 0, since the prover will be forced to separate each gate with a challenge
that they cannot control. Internally, the equation is actually:

\\(\mathsf{gate}_0(r) \cdot v_0 + \ldots + \mathsf{gate}_n(r) \cdot v_n = 0\\) must be true.

where the \\(v\\) values are successive powers of the hash of the public
inputs. The prover would have to break a strong hash function to choose the
public inputs and \\(v\\) values in order to cheat.

### 0. `Mimc7RoundGate`

The equation is:

\\(\mathsf{q\\_mimc}[i] \cdot (\mathsf{w}_0[i] + 0 + \mathsf{c}[i]) ^ 7\\)

This makes each row from 1 to \\(n\\) contain the successive outputs of the
MiMC7 round function over \\(\mathsf{id\\_nul}\\).

The key is set to 0 for all rows.

### 1. `Mimc7RoundGate` for the identity commitment

The equation is:

\\(\mathsf{q\\_mimc}[i] \cdot (\mathsf{w}_1[i] + \mathsf{key}[i] + \mathsf{c}[i]) ^ 7\\)

To understand this, first note that gate 4 (`KeyCopyGate`) and gate 3
(`KeyEqualityGate`) ensure that the \\(\mathsf{key}\\) values are all the MiMC7
`hash` of \\(\mathsf{id\\_nul}\\) plus \\(\mathsf{id\\_nul}\\).

As described in
[4.3.1](./cryptographic_specification.html#431-multi_hash-with-two-field-elements),
this means that the key for step 4 of the `multi_hash` function on two inputs
is the value in any row of \\(key\\) from 0 to \\(n\\). As such, this gate
represents the circuit logic for step 4 of `multi_hash`, which brings it us
closer to computing the identity commitment.

Recall from [5.1](./mechanism_of_operation.html#51-user-identities):

\\(\mathsf{id\\_comm} = \mathsf{multi\\_hash}([\mathsf{id\\_nul}, \mathsf{id\\_trap}])\\)

### 2. `Mimc7RoundGate` for the nullifier hash

The equation is:

\\(\mathsf{q\\_mimc}[i] \cdot (\mathsf{w}_2[i] + \mathsf{key}[i] + \mathsf{c}[i]) ^ 7\\)

Recall that:

\\(\mathsf{nul\\_hash} = \mathsf{multi\\_hash}([\mathsf{id\\_nul}, \mathsf{ext\\_nul}])\\)

By the same logic behind the `Mimc7RoundGate` for the identity commitment, this
gate brings us closer to compuing the nullifier hash.

### 3. `KeyEqualityGate`

The equation is:

\\(\mathsf{q\\_mimc}[i] \cdot (\mathsf{key}[i] + \mathsf{key}[n])\\)

This gate ensures that every row of \\(\mathsf{key}\\) from 0 to \\(n\\) contains the
same value.

### 4. `KeyCopyGate`

The equation is:

\\(L_0(\omega_i) \cdot (\mathsf{key}[i] - \mathsf{w}_0[i] - \mathsf{w}_0[n])\\)

This gate ensures that the first row in the \\(\mathsf{key}\\) column is equal
to \\(\mathsf{id\\_nul}\\) plus the \\(n\\)th iteration of the MiMC7 round
function on \\(\mathsf{id\\_nul}\\).

\\(L_0\\) is a precomputed polynomial in the multiplicative subgroup which
evaluates to 1 at \\(\omega_i\\), and 0 at all other roots of unity.
Effectively, it acts as a selector without the overhead of a selector column.

### 5. `NullifierHashGate`

The equation is:

\\(L_0(\omega_i) \cdot (\mathsf{nul\\_hash} - \mathsf{w}_2[n] - (2 \cdot \mathsf{key}[i]) - \mathsf{w}_2[i])\\)

This gate ensures that the \\(\mathsf{nul\\_hash}\\) public input is equal to:

\\(\mathsf{w}_2[n] + (2 \cdot \mathsf{key}[0]) + \mathsf{w}_2[0])\\)

To understand why, let us trace the computation of \\(\mathsf{nul\\_hash}\\):

Given inputs \\(\mathsf{id\\_nul}\\) and \\(\mathsf{ext\\_nul}\\):

1. Set \\(r\\) as 0.
2. Set \\(h_0 = \mathsf{hash}(\mathsf{id\\_nul}, r)\\).
3. Set \\(r = r + \mathsf{id\\_nul} + h_0\\).
4. Set \\(h_1 = \mathsf{hash}(\mathsf{ext\\_nul}, r)\\).
5. Return \\(r + \mathsf{ext\\_nul} + h_1\\).

Hence, \\(\mathsf{nul\\_hash}\\) equals:

\\(r + \mathsf{ext\\_nul} + h_1 =\\)

\\(\mathsf{id\\_nul} + h_0 + \mathsf{ext\\_nul} + h_1 =\\)

\\(\mathsf{id\\_nul} + \mathsf{hash}(\mathsf{id\\_nul}, 0) + \mathsf{ext\\_nul} + \mathsf{hash}(\mathsf{ext\\_nul}, \mathsf{id\\_nul} + \mathsf{hash}(\mathsf{id\\_nul}, 0))\\)

Since the value \\(r\\) from step 3 is used as the key in step 4, the above is
equal to:

\\(\mathsf{key}[0] + \mathsf{ext\\_nul} + \mathsf{hash}(\mathsf{ext\\_nul}, \mathsf{key}[0])\\)

Since \\(\mathsf{hash}(x, k)\\) equals \\(n\\) round digests of \\(x\\) plus
\\(k\\), the above equals:

\\(\mathsf{key}[0] + \mathsf{w}_2[0] + \mathsf{w}_2[n] + \mathsf{key}[0] =\\)

\\(\mathsf{w}_2[n] + (2 \cdot \mathsf{key}[0]) + \mathsf{w}_2[0])\\)

### 6. `ExternalNullifierGate`

The equation is:

\\(L_0(\omega_i) \cdot (\mathsf{w}_2[i] - \mathsf{ext\\_nul})\\)

This gate ensures that the \\(\mathsf{ext\\_nul}\\) public input is equal to
\\(\mathsf{w}_2[0]\\).
Loading

0 comments on commit 273ae84

Please sign in to comment.