Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 103 additions & 82 deletions docs/pages/CRISP/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ vulnerabilities.

This project serves as a comprehensive example of an E3 Program, demonstrating secure and impartial
decision-making processes with a modern Hardhat-based architecture. It showcases a complete
full-stack implementation including frontend, backend, smart contracts, and zkVM components.
full-stack implementation including frontend, backend, smart contracts, and zero-knowledge
components.

## Why CRISP?

Expand All @@ -27,97 +28,117 @@ coercion. CRISP mitigates these risks through:

## Project Structure

CRISP follows a modern Hardhat-based structure with clear separation of concerns:
CRISP, whose implementation is available in the
[Enclave repository](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP), follows a
modern structure with clear separation of concerns:

```
CRISP/
├── client/ # React frontend application (Vite + @crisp-e3/sdk)
├── server/ # Rust coordination server & CLI
├── program/ # RISC Zero guest + prover control plane
├── program/ # FHE program for encrypted computation + RISC Zero verification
├── packages/
│ ├── crisp-contracts/ # Hardhat deployment + helpers
│ └── crisp-sdk/ # CRISP-specific TypeScript helpers to generate a ZK proof
├── crates/ # Rust libraries for the CLI + services
├── circuits/ # Noir circuits + verifiers (see [Noir Circuits](/noir-circuits))
├── scripts/ # dev.sh, setup.sh, compile_circuits.sh, etc.
│ ├── crisp-contracts/ # CRISP program contract + Hardhat deployment scripts
│ └── crisp-sdk/ # TypeScript helpers to generate a ZK proof
├── crates/ # Rust libraries used by the server
├── circuits/ # Noir zero-knowledge circuits
├── scripts/ # Development scripts for running, testing, and deployment
├── enclave.config.yaml # Ciphernodes + aggregator config
└── docker-compose.yaml # Optional multi-node deployment
```

---

### **Client Application** (`/client`)

The client is a React application built with TypeScript that provides a voting interface and reuses
the shared [CRISP SDK](/sdk):

- Wallet connection with MetaMask and other wallets
- Vote encryption using WebAssembly-based FHE encryption before submission
- Noir zero-knowledge proof generation for vote validation
- Real-time updates on voting status and results

---

### **Coordination Server** (`/server`)

The server is a Rust-based coordination service that manages the E3 lifecycle and drives the same
SDK from a privileged wallet:

- Listens to blockchain events and coordinates protocol progression
- Collects encrypted votes from the Smart Contract
- Triggers FHE computations after the voting round is closed.
- Publishes results back to the blockchain
- RESTful API for client interactions

---

### **ZK Program + Noir Circuits** (`/program`, `/circuits`)

- `program/`: Rust guest code compiled to the RISC Zero zkVM image that runs the CRISP tally logic
- `circuits/`: Noir circuits + SAFE/GRECO libraries used for membership proofs and encryption checks
- `scripts/compile_circuits.sh`: compiles Noir circuits, writes verification keys, and emits the
`CRISPVerifier.sol` contract used by `packages/crisp-contracts`

---

### **Smart Contracts** (`/packages/crisp-contracts`)

Solidity contracts implementing the E3 program interface:

#### **CRISPProgram.sol**

Main E3 program contract implementing the `IE3Program` interface:

- `validate()`: Validates voting parameters and setup
- `verify()`: Verifies zkVM proofs of computation
- `validateInput()`: Handles vote validation and zero-knowledge proof verification:
- Validates encrypted vote format
- Verifies voter eligibility proofs
- Ensures vote uniqueness

#### **CRISPVerifier.sol**

Noir verifier for proof of correct encryption:

- Verifies if the vote was encrypted correctly
- Verifies if the vote is valid

#### **CRISPPolicy.sol**

Manages voting policies and governance:

- Defines voting rules and constraints
- Manages voter registration

#### **CRISPChecker.sol**

Additional verification and checking logic:

- Vote format validation
- Eligibility checking
- Result verification

---
### Client Application

The [client](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP/client) is a React
application built with TypeScript that provides a voting interface. It uses the CRISP
[SDK](/CRISP/introduction#javascript-sdk) and [server](/CRISP/introduction#coordination-server)
endpoints to deliver the following capabilities:

- Connecting wallets via MetaMask
- Encrypting votes using FHE encryption before submission
- Generating Noir zero-knowledge proofs for vote validation
- Displaying real-time updates on voting status and results

### Coordination Server

The [server](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP/server) is a Rust-based
coordination service that manages the E3 lifecycle and drives the same SDK from a privileged wallet.
It acts as a relayer and facilitator, handling blockchain interactions on behalf of clients.
However, clients can bypass the server entirely and interact directly with the blockchain if they
prefer.

The server's key responsibilities include:

- Monitoring blockchain events and coordinating protocol progression
- Collecting encrypted votes from smart contracts
- Triggering FHE computations after voting rounds are closed
- Publishing computation results back to the blockchain
- Providing a RESTful API for client interactions

### FHE Program

The [FHE program](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP/program) is a Rust
guest program that runs inside the RISC Zero zkVM to perform homomorphic addition on encrypted votes
(BFV ciphertexts). It computes the encrypted tally without decrypting individual votes, ensuring
voter privacy throughout the process.

The program decodes BFV parameters and ciphertexts, then sums all encrypted votes homomorphically to
produce a single encrypted ciphertext containing the total. RISC Zero generates zero-knowledge
proofs that verify the computation was executed correctly, enabling trustless verification of the
tally results.

### Zero-knowledge Circuits

[Noir circuits](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP/circuits) provide
zero-knowledge proofs for vote validation before votes are accepted into the system. These circuits
verify that votes are correctly encrypted using BFV fully homomorphic encryption under a valid
public key, check that the voter's address is included in the eligibility Merkle tree, and ensure
votes conform to the expected structure and values. The circuits also support mask votes, allowing
anyone to submit zero votes to mask slot activity and reduce the risk of collusion and coercion.

For detailed documentation on how the circuit works, see the
[main circuit implementation](https://github.com/gnosisguild/enclave/blob/main/examples/CRISP/circuits/src/main.nr).

### Smart Contracts

The CRISP smart contracts implement the E3 program interface and handle the on-chain logic for
voting rounds. The main contract is
[CRISPProgram.sol](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol),
which orchestrates the entire voting process.

When a new voting round is initialized, the `validate()` function sets up round parameters and
initializes a Merkle tree to store votes efficiently. As votes are submitted, `validateInput()`
processes each encrypted vote by verifying its Noir zero-knowledge proof through the CRISPVerifier
contract, ensuring votes are correctly encrypted and voters are eligible. Votes are stored in the
Merkle tree with slot addresses mapping to vote indices, allowing updates while maintaining
uniqueness.

After the voting period ends and FHE computation completes, the `verify()` function validates the
RISC Zero proof attesting to the correctness of the homomorphic tally computation, verifying it
corresponds to the correct input Merkle root, parameters hash, and ciphertext output. Once verified,
`decodeTally()` decodes the encrypted results into readable yes and no vote counts.

The
[CRISPVerifier.sol](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol)
contract is a Honk verifier generated from compiled Noir circuits that verifies zero-knowledge
proofs demonstrating votes are correctly encrypted using BFV fully homomorphic encryption under the
valid public key.

### JavaScript SDK

The [CRISP SDK](https://github.com/gnosisguild/enclave/tree/main/examples/CRISP/packages/crisp-sdk)
is a TypeScript library that abstracts away the complex cryptographic operations required for vote
submission and proof generation. It fetches round details from the coordination server, queries
token balances at snapshot blocks to verify eligibility, and generates Merkle proofs demonstrating
voter inclusion in the eligibility tree.

When casting a vote, the SDK encrypts votes using BFV fully homomorphic encryption under the
committee's public key and generates zero-knowledge proofs using compiled Noir circuits. These
proofs demonstrate that votes are correctly encrypted and that voters are eligible to participate.
The SDK also supports mask votes, generating proofs for zero-value votes that help reduce the risk
of collusion and coercion. Before submission, proofs are verified locally to prevent failed
transactions, and after voting concludes, the SDK decodes encrypted tally results into readable vote
counts.

## Next Steps

Expand Down
77 changes: 6 additions & 71 deletions docs/pages/CRISP/running-e3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,19 @@ import { Steps } from 'nextra/components'
In this section, we will go through all the steps to run an E3 Program using CRISP. We will run a
complete voting round of CRISP and do the following:

- Start the infrastructure (nodes and contracts)
- Start the CRISP applications (client, server, program)
- Request an E3 Voting Round
- Submit votes through the web interface
- Compute and verify results

Please make sure you have followed the [CRISP Setup](/CRISP/setup) guide before proceeding.
Please make sure you have followed the [CRISP Setup](/CRISP/setup) guide and
[started the application](/CRISP/setup#using-the-application) before proceeding.

<Steps>

### Prep Once per Checkout

From the repo root run the bundled setup script (it installs dependencies, builds the CLI, prepares
env files, and compiles contracts):

```sh
cd examples/CRISP
pnpm dev:setup
```

You only need to re-run this when dependencies change.

### Start Everything with One Command

The CRISP workspace ships with a supervisor that launches Hardhat, deploys contracts, boots
Ciphernodes, runs the RISC Zero program server, the Rust backend, and the React client. Start it in
the example root:

```sh
pnpm dev:up
```

Behind the scenes `scripts/dev.sh` calls `scripts/dev_services.sh`, which:

- Spawns a Hardhat chain on `http://localhost:8545`
- Deploys contracts and registers five Ciphernodes against the local registry
- Runs `enclave program start --dev true` so proving happens instantly while developing
- Launches the Rust server with `cargo run --bin server`
- Starts the React client via `pnpm dev-static`

Keep this terminal open; logs from every service are multiplexed with `pnpm concurrently`.

### Initialize a New Voting Round

Open a new terminal and launch the CLI from the example root:
As explained in the previous section, you can launch the CLI from the example root with the
following command:

```sh
pnpm cli
Expand Down Expand Up @@ -90,15 +58,14 @@ You should see output similar to:

### Set Up MetaMask

Whether you used `pnpm dev:up` or the manual flow below, you will interact with the Hardhat chain
running at `http://localhost:8545` (chain ID `31337`). Configure MetaMask once:
Whether you used `pnpm dev:up` or the manual flow, you will interact with the Hardhat chain running
at `http://localhost:8545` (chain ID `31337`). Configure MetaMask once:

1. Import the Hardhat deployer key so you have funds available:
```
0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
```
2. Add a custom network pointing to `http://localhost:8545` with symbol `ETH` and chain ID `31337`.
3. Connect your wallet to `http://localhost:3000` when the client asks.

### Submit Votes via Web Interface

Expand All @@ -107,38 +74,6 @@ running at `http://localhost:8545` (chain ID `31337`). Configure MetaMask once:
3. You should see the active voting round seeded by the CLI request
4. Cast a vote, approve the transaction, and wait for the confirmation toast

### Manual Control (Optional)

If you prefer to run each process in its own terminal (or need to customize flags), replicate what
`pnpm dev:up` does under the hood:

1. **Hardhat chain + deploy contracts**
```sh
pnpm -C packages/crisp-contracts hardhat node
# in another terminal
./scripts/crisp_deploy.sh
```
2. **Ciphernodes & wallets**
```sh
./scripts/dev_cipher.sh ./.enclave/ready
```
This script wipes previous `.enclave` state, installs dev wallets, starts `enclave nodes up -v`,
and waits until all nodes are registered.
3. **Program server**
```sh
./scripts/dev_program.sh # add --dev true inside to skip proofs in dev
```
4. **Rust server**
```sh
wait-on tcp:13151 && ./scripts/dev_server.sh
```
5. **React client**
```sh
wait-on tcp:4000 && wait-on file:./.enclave/ready && ./scripts/dev_client.sh
```

You can also let `./scripts/dev_services.sh` orchestrate steps 2–5 once Hardhat is up.

### Monitor the Process

You can monitor the entire process through the various terminal outputs:
Expand Down
Loading
Loading