Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs and readme #50

Merged
merged 3 commits into from
Dec 15, 2023
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
85 changes: 80 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,83 @@
# Spectre
Spectre is a ZK-based block header oracle protocol based on Altair fork light-client sync protocol.

Spectre is a Zero-Knowledge (ZK) coprocessor designed to offload intensive computations from the resource-limited execution layer of target chains. Iit offers a trust-minimized method for verifying block headers, adhering to the consensus rules of the originating chain.

The type of outsourced computation is specific to the arithmetic circuits. For Spectre, its primary function is to verify the Ethereum LightClient protocol introduced in the Altair hardfork.

## Requirements
- `build-essential clang pkg-config libssl-dev`
- Rust `1.73.0-nightly`
- Packages `build-essential` `clang` `pkg-config` `libssl-dev`
- [Foundry](https://book.getfoundry.sh/getting-started/installation)
- [Just](https://just.systems/man/en/)

## Technical details

Spectre prover utilizes the Halo2 proving stack ([`privacy-scaling-explorations/halo2`](https://github.com/privacy-scaling-explorations/halo2) fork).

Circuits are implemented with the [`halo2-lib`](https://github.com/axiom-crypto/halo2-lib) circuit development framework. This library contains a number of non-trivial optimization tricks, while its readable SDK prevents most of the soundness bugs and improves auditability. Our team has contributed a number of features back to the halo2-lib repository, containing some foundational cryptographic primitives powering Ethereum consensus.

Verifier contracts for consensus proofs are auto-generated via the [`privacy-scaling-explorations/snark-verifier`](https://github.com/privacy-scaling-explorations/snark-verifier). We aslo support [`privacy-scaling-explorations/halo2-solidity-verifier`](https://github.com/privacy-scaling-explorations/halo2-solidity-verifier) behind `experimental` flag. Supplemental contract logic has been introduced exclusively to manage intermediary states during proof verifications.

## Usage

### Setup circuits

#### Step circuit

```shell
cargo run -r -- circuit sync-step-compressed -k 20 -p ./build/sync_step_20.pkey -K 23 -P ./build/sync_step_verifier_23.pkey -L 19 setup
```
Flags `-k` and `-K` are circuit degrees for first and aggregation (compression) stage respectively. `-L` is the number lookup bits used in aggregation stage.

#### Committee update circuit

```shell
cargo run -r -- circuit committee-update -k 20 -p ./build/committee_update_20.pkey -K 24 -P ./build/committee_update_verifier_20.pkey setup
```

## Deploying contracts
Alternatively, you can use `just` recipes as shown below.

```shell
just setup-step-compressed testnet
just setup-committee-update testnet
```

### Generates verifier contracts

#### Step proof

```shell
cargo run -r -- circuit sync-step-compressed -p ./build/sync_step_20.pkey -P ./build/sync_step_verifier_23.pkey gen-verifier -o ./contracts/snark-verifiers/sync_step_verifier.sol
```

#### Committee update proof

```shell
cargo run -r -- circuit committee-update -p ./build/committee_update_20.pkey -P ./build/committee_update_verifier_24.pkey gen-verifier -o ./contracts/snark-verifiers/committee_update_verifier.sol
```

Or use `just` recipes as shown below.

```shell
just gen-verifier-step-compressed testnet
just gen-verifier-committee-update testnet
```

### Deploying contracts

Just scripts are provided to deploy the contracts either to a local testnet, or public networks.

For either make a copy of the `.env.example` file called `.env`. Set the `INITIAL_SYNC_PERIOD`, `INITIAL_COMMITTEE_POSEIDON` and `SLOTS_PER_PERIOD` variables according to the network you want Spectre to act as a light-client for and the starting point.

### Deploying locally
To get the `INITIAL_COMMITTEE_POSEIDON` value, run:

```shell
cargo run -r -- utils committee-poseidon --beacon-api https://lodestar-sepolia.chainsafe.io
```

`--beacon-api` is a URL of the RPC of the targeted Beacon chain.

#### Deploying locally

1. Start a local anvil instance with:

Expand All @@ -24,7 +91,7 @@ anvil
just deploy-contracts-local
```

### Deploying to a public network
#### Deploying to a public network

1. Obtain the required gas token and obtain the private key for the deployer account. Set the `DEPLOYER_PRIVATE_KEY` in the `.env` file.
2. Obtain a public RPC URL for the network and set the variable `<NETWORK>_RPC_URL` in the `.env` file (If using Infura this will require an API key)
Expand All @@ -35,3 +102,11 @@ just deploy-contracts <NETWORK>
```

where `<NETWORK>` is one of `["GOERLI", "SEPOLIA", "MAINNET"]`.

### Running the prover

Prover is accessible via JSON RPC interface. To start it, run:

```shell
cargo run -r -- rpc --port 3000
```
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {Verifier as SyncStepVerifier} from "../snark-verifiers/sync_step_verifie
contract DeploySpectre is Script {

function run() external {
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
uint256 deployerPrivateKey = vm.envUint("ANVIL_PRIVATE_KEY");
uint256 initialSyncPeriod = vm.envUint("INITIAL_SYNC_PERIOD");
uint256 initialCommitteePoseidon = vm.envUint("INITIAL_COMMITTEE_POSEIDON");
uint256 slotsPerPeriod = vm.envUint("SLOTS_PER_PERIOD");
Expand Down
6 changes: 0 additions & 6 deletions contracts/script/deploy_local.sh

This file was deleted.

6 changes: 0 additions & 6 deletions contracts/script/deploy_testnet.sh

This file was deleted.

5 changes: 4 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ build-contracts:
cd contracts && forge build

deploy-contracts-local:
cd contracts && forge script ./script/DeploySpectre.s.sol:DeploySpectre --fork-url $LOCAL_RPC_URL --broadcast
cd contracts && forge script ./script/DeploySpectreLocal.s.sol:DeploySpectre --fork-url $LOCAL_RPC_URL --broadcast

deploy-contracts-testnet:
cd contracts && forge script ./script/DeploySpectre.s.sol:DeploySpectre --private-key $DEPLOYER_PRIVATE_KEY --fork-url $SEPOLIA_RPC_URL --broadcast

deploy-contracts network: # network one of [MAINNET, GOERLI, SEPOLIA]
#! /usr/bin/env bash
Expand Down
41 changes: 36 additions & 5 deletions prover/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ use strum::EnumString;

#[derive(Clone, clap::Parser)]
#[command(name = "spectre-prover")]
#[command(about = "Spectre prover", long_about = None)]
#[command(
about = "Spectre prover",
long_about = "Spectre is a Zero-Knowledge (ZK) coprocessor designed to offload intensive verification of block headers via Altair lightclient protocol.
"
)]
pub struct Cli {
#[command(subcommand)]
pub subcommand: BaseCmd,
Expand All @@ -20,24 +24,30 @@ pub struct Cli {

#[derive(Clone, clap::Args)]
pub struct BaseArgs {
/// Path to config directory
#[clap(long, short, default_value = "./lightclient-circuits/config")]
pub config_dir: PathBuf,
}

#[derive(Clone, clap::Parser)]
#[allow(clippy::large_enum_variant)]
pub enum BaseCmd {
/// Deploy prover RPC server.
Rpc {
/// Port for RPC server to listen on
#[clap(long, short, default_value = "3000")]
port: String,
},
/// Circuit related commands.
Circuit {
#[command(subcommand)]
proof: ProofCmd,

/// Network spec
#[clap(long, short, default_value = "mainnet")]
spec: Spec,
},
/// Misc utility commands.
Utils {
#[command(subcommand)]
method: UtilsCmd,
Expand All @@ -46,63 +56,82 @@ pub enum BaseCmd {

#[derive(Clone, clap::Subcommand)]
pub enum ProofCmd {
/// Step circuit - verifies Beacon chain block header and the execution payload.
SyncStep {
#[command(subcommand)]
operation: OperationCmd,

/// Circuit degree
#[clap(long, short, default_value = "22")]
k: u32,

/// Path to prover key
#[clap(long, short)]
pk_path: PathBuf,
},
/// Step circuit (compressed) - verifies Beacon chain block header and the execution payload. Uses aggregation to reduce verifier cost.
SyncStepCompressed {
#[command(subcommand)]
operation: OperationCmd,

/// Circuit degree (first stage)
#[clap(long, short, default_value = "20")]
k: u32,

/// Path to prover key (first stage)
#[clap(long, short)]
pk_path: PathBuf,

#[clap(short='K', long, default_value = "23")]
/// Circuit degree (compression stage)
#[clap(short = 'K', long, default_value = "23")]
verifier_k: u32,

#[clap(short='P', long)]
/// Path to prover key (compression stage)
#[clap(short = 'P', long)]
verifier_pk_path: PathBuf,

/// Number of lookup bits (compression stage)
#[clap(short = 'L', long)]
verifier_lookup_bits: Option<usize>,
},
/// Committee update circuit (compressed) - maps next sync committee root to the Poseidon commitment. Uses aggregation to reduce verifier cost.
CommitteeUpdate {
#[command(subcommand)]
operation: OperationCmd,

/// Circuit degree (first stage)
#[clap(long, short, default_value = "20")]
k: u32,

/// Path to prover key (first stage)
#[clap(long, short)]
pk_path: PathBuf,

#[clap(short='K', long, default_value = "24")]
/// Circuit degree (compression stage)
#[clap(short = 'K', long, default_value = "24")]
verifier_k: u32,

#[clap(short='P', long)]
/// Path to prover key (compression stage)
#[clap(short = 'P', long)]
verifier_pk_path: PathBuf,

/// Number of lookup bits (compression stage)
#[clap(short = 'L', long)]
verifier_lookup_bits: Option<usize>,
},
}

#[derive(Clone, clap::Subcommand)]
pub enum OperationCmd {
/// Generate prover and verifier keys
Setup,
/// Generate Solidity verifier contract
GenVerifier {
/// Path to generedated Solidity contract
#[clap(long, short = 'o')]
solidity_out: PathBuf,

/// Flag whether to estimate gas
#[clap(long, short)]
estimate_gas: bool,
},
Expand All @@ -121,7 +150,9 @@ pub enum Spec {

#[derive(Clone, clap::Subcommand)]
pub enum UtilsCmd {
/// Get `INITIAL_SYNC_PERIOD`, `INITIAL_COMMITTEE_POSEIDON` for contracts deployment.
CommitteePoseidon {
/// Beacon API URL
#[clap(long, short)]
beacon_api: String,
},
Expand Down
8 changes: 4 additions & 4 deletions prover/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Code: https://github.com/ChainSafe/Spectre
// SPDX-License-Identifier: LGPL-3.0-only

use std::{ops::Deref, sync::Arc};
use std::{env, ops::Deref, sync::Arc};

use beacon_api_client::{BlockId, VersionedValue};
use ethereum_consensus_types::LightClientBootstrap;
Expand Down Expand Up @@ -39,7 +39,7 @@ pub(crate) async fn utils_cli(method: UtilsCmd) -> eyre::Result<()> {
};

let sync_period = bootstrap.header.beacon.slot / (32 * 256);
print!("{} \n", sync_period);
print!("Sync period: {} \n", sync_period);
let pubkeys_uncompressed = bootstrap
.current_sync_committee
.pubkeys
Expand All @@ -52,12 +52,12 @@ pub(crate) async fn utils_cli(method: UtilsCmd) -> eyre::Result<()> {
.pubkeys
.hash_tree_root()
.unwrap();
println!("ssz root: {:?}", hex::encode(ssz_root.deref()));
println!("SSZ root: {:?}", hex::encode(ssz_root.deref()));

let mut committee_poseidon =
poseidon_committee_commitment_from_uncompressed(&pubkeys_uncompressed).to_bytes();
committee_poseidon.reverse();
println!("poseidon commitment: {}", hex::encode(committee_poseidon));
println!("Poseidon commitment: {}", hex::encode(committee_poseidon));

Ok(())
}
Expand Down
Loading