From 9fd8b7deb49b1b4a6a9c0f1dd9eaf1c6aa6792d8 Mon Sep 17 00:00:00 2001 From: timofey Date: Fri, 15 Dec 2023 16:42:08 +0100 Subject: [PATCH] Update docs and readme (#50) * document CLI * update justfile * update README --- README.md | 85 +++++++++++++++++-- ...Spectre.s.sol => DeploySpectreLocal.s.sol} | 2 +- contracts/script/deploy_local.sh | 6 -- contracts/script/deploy_testnet.sh | 6 -- justfile | 5 +- prover/src/args.rs | 41 +++++++-- prover/src/utils.rs | 8 +- 7 files changed, 125 insertions(+), 28 deletions(-) rename contracts/script/{DeploySpectre.s.sol => DeploySpectreLocal.s.sol} (93%) delete mode 100644 contracts/script/deploy_local.sh delete mode 100644 contracts/script/deploy_testnet.sh diff --git a/README.md b/README.md index 489fbabe..df7bca44 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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 `_RPC_URL` in the `.env` file (If using Infura this will require an API key) @@ -35,3 +102,11 @@ just deploy-contracts ``` where `` 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 +``` diff --git a/contracts/script/DeploySpectre.s.sol b/contracts/script/DeploySpectreLocal.s.sol similarity index 93% rename from contracts/script/DeploySpectre.s.sol rename to contracts/script/DeploySpectreLocal.s.sol index bcb4a902..4a3c6aa7 100644 --- a/contracts/script/DeploySpectre.s.sol +++ b/contracts/script/DeploySpectreLocal.s.sol @@ -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"); diff --git a/contracts/script/deploy_local.sh b/contracts/script/deploy_local.sh deleted file mode 100644 index 6132d7ac..00000000 --- a/contracts/script/deploy_local.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -cd $(git rev-parse --show-toplevel) -source .env -LOCAL_RPC_URL="http://localhost:8545" - -forge script script/DeploySpectre.s.sol:DeploySpectre --private-key $ANVIL_PRIVATE_KEY --rpc-url $LOCAL_RPC_URL --broadcast -vvvv diff --git a/contracts/script/deploy_testnet.sh b/contracts/script/deploy_testnet.sh deleted file mode 100644 index a435ad4c..00000000 --- a/contracts/script/deploy_testnet.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -cd $(git rev-parse --show-toplevel) -source .env -SEPOLIA_RPC_URL="https://rpc.sepolia.org/" - -forge script script/DeploySpectreTestnet.s.sol:DeploySpectre --private-key $DEPLOYER_PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL --broadcast -vvvv diff --git a/justfile b/justfile index 469de5ee..737a84d7 100644 --- a/justfile +++ b/justfile @@ -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 diff --git a/prover/src/args.rs b/prover/src/args.rs index 5f77a36b..668c1cf8 100644 --- a/prover/src/args.rs +++ b/prover/src/args.rs @@ -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, @@ -20,6 +24,7 @@ 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, } @@ -27,17 +32,22 @@ pub struct BaseArgs { #[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, @@ -46,51 +56,66 @@ 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, }, + /// 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, }, @@ -98,11 +123,15 @@ pub enum ProofCmd { #[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, }, @@ -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, }, diff --git a/prover/src/utils.rs b/prover/src/utils.rs index b2e747a8..ba56ff59 100644 --- a/prover/src/utils.rs +++ b/prover/src/utils.rs @@ -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; @@ -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 @@ -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(()) }