├── book ├── .gitignore ├── src │ ├── user │ │ ├── gadgets.md │ │ ├── lookup-tables.md │ │ ├── tips-and-tricks.md │ │ ├── simple-example.md │ │ └── dev-tools.md │ ├── README.md │ ├── design │ │ ├── implementation.md │ │ ├── gadgets │ │ │ ├── sha256 │ │ │ │ ├── compression.png │ │ │ │ ├── low_sigma_0.png │ │ │ │ ├── low_sigma_1.png │ │ │ │ ├── upp_sigma_0.png │ │ │ │ ├── upp_sigma_1.png │ │ │ │ └── bit_reassignment.png │ │ │ ├── ecc │ │ │ │ └── witnessing-points.md │ │ │ ├── ecc.md │ │ │ ├── sha256.md │ │ │ └── decomposition.md │ │ ├── proving-system │ │ │ ├── permutation-diagram.png │ │ │ ├── inner-product.md │ │ │ ├── comparison.md │ │ │ ├── multipoint-opening.md │ │ │ └── vanishing.md │ │ ├── gadgets.md │ │ ├── implementation │ │ │ ├── fields.md │ │ │ └── proofs.md │ │ └── proving-system.md │ ├── user.md │ ├── concepts.md │ ├── background.md │ ├── design.md │ ├── concepts │ │ ├── gadgets.md │ │ ├── arithmetization.md │ │ └── proofs.md │ ├── IDENTIFIERS.json │ ├── SUMMARY.md │ └── background │ │ ├── recursion.md │ │ ├── plonkish.md │ │ ├── groups.md │ │ └── pc-ipa.md ├── book.toml ├── Makefile ├── edithtml.sh └── macros.txt ├── rust-toolchain ├── halo2_backend ├── src │ ├── poly │ │ ├── kzg │ │ │ ├── multiopen.rs │ │ │ ├── mod.rs │ │ │ └── multiopen │ │ │ │ ├── gwc.rs │ │ │ │ └── gwc │ │ │ │ ├── prover.rs │ │ │ │ └── verifier.rs │ │ ├── strategy.rs │ │ └── query.rs │ ├── plonk │ │ ├── lookup.rs │ │ ├── shuffle.rs │ │ ├── vanishing.rs │ │ ├── error.rs │ │ ├── permutation.rs │ │ ├── vanishing │ │ │ └── verifier.rs │ │ └── shuffle │ │ │ └── verifier.rs │ └── lib.rs └── Cargo.toml ├── halo2_frontend ├── src │ ├── lib.rs │ ├── circuit │ │ └── floor_planner.rs │ ├── plonk.rs │ ├── plonk │ │ ├── shuffle.rs │ │ ├── permutation.rs │ │ └── lookup.rs │ └── dev │ │ └── util.rs └── Cargo.toml ├── typos.toml ├── .gitignore ├── halo2 ├── src │ └── lib.rs ├── CHANGELOG.md └── Cargo.toml ├── Cargo.toml ├── halo2_middleware ├── src │ ├── lib.rs │ ├── permutation.rs │ ├── lookup.rs │ ├── shuffle.rs │ ├── poly.rs │ └── multicore.rs └── Cargo.toml ├── p3_frontend ├── src │ ├── air.rs │ └── symbolic_builder.rs ├── tests │ ├── keccak_air.rs │ ├── common │ │ └── mod.rs │ └── fib_air.rs └── Cargo.toml ├── codecov.yml ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ └── eli15.md ├── scripts │ ├── wasm-target-test-build.sh │ └── run-examples.sh ├── workflows │ ├── coverage.yml │ ├── lints-beta.yml │ ├── trigger_proverbench_dispatch.yml │ ├── docs-ghpages.yml │ └── ci.yml └── katex-header.html ├── COPYING.md ├── halo2_proofs ├── proptest-regressions │ └── plonk │ │ ├── assigned.txt │ │ └── circuit │ │ └── compress_selectors.txt ├── benches │ ├── hashtocurve.rs │ ├── commit_zk.rs │ └── dev_lookup.rs ├── src │ ├── plonk │ │ ├── error.rs │ │ └── keygen.rs │ ├── lib.rs │ └── plonk.rs ├── Cargo.toml └── examples │ ├── proof-size.rs │ └── circuit-cost.rs ├── halo2_debug ├── Cargo.toml └── src │ └── lib.rs ├── LICENSE-MIT └── README.md /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.82.0 2 | -------------------------------------------------------------------------------- /book/src/user/gadgets.md: -------------------------------------------------------------------------------- 1 | # Gadgets 2 | -------------------------------------------------------------------------------- /book/src/README.md: -------------------------------------------------------------------------------- 1 | {{#include ../../README.md}} 2 | -------------------------------------------------------------------------------- /book/src/design/implementation.md: -------------------------------------------------------------------------------- 1 | # Implementation 2 | -------------------------------------------------------------------------------- /halo2_backend/src/poly/kzg/multiopen.rs: -------------------------------------------------------------------------------- 1 | mod gwc; 2 | mod shplonk; 3 | 4 | pub use gwc::*; 5 | pub use shplonk::*; 6 | -------------------------------------------------------------------------------- /halo2_frontend/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | 3 | pub mod circuit; 4 | pub mod dev; 5 | pub mod plonk; 6 | -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | extend-ignore-re=[ 3 | "master-thm", 4 | "[aA]dvices", 5 | "projectives" 6 | ] 7 | check-filename = true 8 | -------------------------------------------------------------------------------- /book/src/design/gadgets/sha256/compression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacy-ethereum/halo2/HEAD/book/src/design/gadgets/sha256/compression.png -------------------------------------------------------------------------------- /book/src/design/gadgets/sha256/low_sigma_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacy-ethereum/halo2/HEAD/book/src/design/gadgets/sha256/low_sigma_0.png -------------------------------------------------------------------------------- /book/src/design/gadgets/sha256/low_sigma_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacy-ethereum/halo2/HEAD/book/src/design/gadgets/sha256/low_sigma_1.png -------------------------------------------------------------------------------- /book/src/design/gadgets/sha256/upp_sigma_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacy-ethereum/halo2/HEAD/book/src/design/gadgets/sha256/upp_sigma_0.png -------------------------------------------------------------------------------- /book/src/design/gadgets/sha256/upp_sigma_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacy-ethereum/halo2/HEAD/book/src/design/gadgets/sha256/upp_sigma_1.png -------------------------------------------------------------------------------- /halo2_backend/src/plonk/lookup.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod prover; 2 | pub(crate) mod verifier; 3 | 4 | use crate::plonk::circuit::LookupArgumentBack as Argument; 5 | -------------------------------------------------------------------------------- /book/src/design/gadgets/sha256/bit_reassignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacy-ethereum/halo2/HEAD/book/src/design/gadgets/sha256/bit_reassignment.png -------------------------------------------------------------------------------- /halo2_backend/src/plonk/shuffle.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod prover; 2 | pub(crate) mod verifier; 3 | 4 | use crate::plonk::circuit::ShuffleArgumentBack as Argument; 5 | -------------------------------------------------------------------------------- /book/src/design/proving-system/permutation-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacy-ethereum/halo2/HEAD/book/src/design/proving-system/permutation-diagram.png -------------------------------------------------------------------------------- /halo2_frontend/src/circuit/floor_planner.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of common circuit floor planners. 2 | pub mod single_pass; 3 | pub mod v1; 4 | 5 | pub use v1::{V1Pass, V1}; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .vscode 5 | **/*.html 6 | .DS_Store 7 | 8 | layout.png 9 | serialization-example.vk 10 | serialization-example.pk 11 | -------------------------------------------------------------------------------- /halo2_backend/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod arithmetic; 2 | pub mod helpers; 3 | pub mod plonk; 4 | pub mod poly; 5 | pub mod transcript; 6 | 7 | // Internal re-exports 8 | pub use halo2_middleware::multicore; 9 | -------------------------------------------------------------------------------- /book/src/user.md: -------------------------------------------------------------------------------- 1 | # User Documentation 2 | 3 | You're probably here because you want to write circuits? Excellent! 4 | 5 | This section will guide you through the process of creating circuits with halo2. 6 | -------------------------------------------------------------------------------- /halo2/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # halo2 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![deny(rustdoc::broken_intra_doc_links)] 5 | #![deny(missing_debug_implementations)] 6 | #![deny(missing_docs)] 7 | #![deny(unsafe_code)] 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "halo2", 4 | "halo2_proofs", 5 | "halo2_frontend", 6 | "halo2_middleware", 7 | "halo2_backend", 8 | "halo2_debug", 9 | "p3_frontend", 10 | ] 11 | resolver = "2" 12 | -------------------------------------------------------------------------------- /book/src/concepts.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | First we'll describe the concepts behind zero-knowledge proof systems; the 4 | *arithmetization* (kind of circuit description) used by Halo 2; and the 5 | abstractions we use to build circuit implementations. 6 | -------------------------------------------------------------------------------- /halo2_middleware/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod circuit; 2 | pub mod expression; 3 | pub mod lookup; 4 | pub mod multicore; 5 | pub mod permutation; 6 | pub mod poly; 7 | pub mod shuffle; 8 | pub mod zal; 9 | 10 | pub use ff; 11 | pub use halo2curves; 12 | -------------------------------------------------------------------------------- /halo2_backend/src/poly/kzg/mod.rs: -------------------------------------------------------------------------------- 1 | /// KZG commitment scheme 2 | pub mod commitment; 3 | /// Multiscalar multiplication engines 4 | pub mod msm; 5 | /// KZG multi-open scheme 6 | pub mod multiopen; 7 | /// Strategies used with KZG scheme 8 | pub mod strategy; 9 | -------------------------------------------------------------------------------- /halo2_frontend/src/plonk.rs: -------------------------------------------------------------------------------- 1 | pub mod assigned; 2 | pub mod circuit; 3 | pub mod error; 4 | pub mod keygen; 5 | pub mod lookup; 6 | pub mod permutation; 7 | pub mod shuffle; 8 | 9 | pub use assigned::*; 10 | pub use circuit::*; 11 | pub use error::*; 12 | -------------------------------------------------------------------------------- /p3_frontend/src/air.rs: -------------------------------------------------------------------------------- 1 | //! Alternative `AirBuilderWithPublicValues` trait that uses `Self::Var` instead of `Self::F`. 2 | 3 | use p3_air::AirBuilder; 4 | 5 | pub trait AirBuilderWithPublicValues: AirBuilder { 6 | fn public_values(&self) -> &[Self::Var]; 7 | } 8 | -------------------------------------------------------------------------------- /book/src/design/gadgets.md: -------------------------------------------------------------------------------- 1 | # Gadgets 2 | 3 | In this section we document the gadgets and chip designs provided in the `halo2_gadgets` 4 | crate. 5 | 6 | > Neither these gadgets, nor their implementations, have been reviewed, and they should 7 | > not be used in production. 8 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - halo2_proofs/benches 3 | - halo2_proofs/examples 4 | - halo2_proofs/tests 5 | - halo2_frontend/src/dev/graph 6 | - halo2_frontend/src/dev/graph.rs 7 | - halo2_frontend/src/dev/costs.rs 8 | - halo2_frontend/src/dev/cost_model.rs -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = [ 3 | "Jack Grigg", 4 | "Sean Bowe", 5 | "Daira Hopwood", 6 | "Ying Tong Lai", 7 | ] 8 | language = "en" 9 | multilingual = false 10 | src = "src" 11 | title = "The halo2 Book" 12 | 13 | [preprocessor.katex] 14 | macros = "macros.txt" 15 | -------------------------------------------------------------------------------- /halo2_backend/src/plonk/vanishing.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::arithmetic::CurveAffine; 4 | 5 | pub(crate) mod prover; 6 | pub(crate) mod verifier; 7 | 8 | /// A vanishing argument. 9 | pub(crate) struct Argument { 10 | _marker: PhantomData, 11 | } 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | timezone: Etc/UTC 8 | open-pull-requests-limit: 10 9 | reviewers: 10 | - str4d 11 | assignees: 12 | - str4d 13 | labels: 14 | - "A-CI" 15 | -------------------------------------------------------------------------------- /book/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: 3 | find src -type f -a -name '*.md' |sed 's/[.]md$$/.html/g' |xargs $(MAKE) 4 | 5 | clean: 6 | find src -type f -a -name '*.html' -print0 |xargs -0 rm 7 | 8 | %.html: %.md 9 | pandoc --katex --from=markdown --to=html "$<" "--output=$@" 10 | ./edithtml.sh "$@" "$<" 11 | -------------------------------------------------------------------------------- /book/src/background.md: -------------------------------------------------------------------------------- 1 | # Background Material 2 | 3 | This section covers the background material required to understand the Halo 2 proving 4 | system. It is targeted at an ELI15 (Explain It Like I'm 15) level; if you think anything 5 | could do with additional explanation, [let us know]! 6 | 7 | [let us know]: https://github.com/zcash/halo2/issues/new/choose 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/eli15.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ELI15 improvement request 3 | about: Let us know how the Halo 2 book could be improved! 4 | title: 'ELI15: ' 5 | labels: 'ELI15' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Which section of the Halo 2 book were you reading? 11 | 12 | ## What was unclear? 13 | 14 | ## What would help to make it clearer to you? 15 | 16 | -------------------------------------------------------------------------------- /halo2_middleware/src/permutation.rs: -------------------------------------------------------------------------------- 1 | use crate::circuit::{Cell, ColumnMid}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct AssemblyMid { 5 | pub copies: Vec<(Cell, Cell)>, 6 | } 7 | 8 | /// A permutation argument. 9 | #[derive(Debug, Clone)] 10 | pub struct ArgumentMid { 11 | /// A sequence of columns involved in the argument. 12 | pub columns: Vec, 13 | } 14 | -------------------------------------------------------------------------------- /halo2_middleware/src/lookup.rs: -------------------------------------------------------------------------------- 1 | use super::circuit::VarMid; 2 | use super::expression::{Expression, Variable}; 3 | use ff::Field; 4 | 5 | /// Expressions involved in a lookup argument, with a name as metadata. 6 | #[derive(Clone, Debug)] 7 | pub struct Argument { 8 | pub name: String, 9 | pub input_expressions: Vec>, 10 | pub table_expressions: Vec>, 11 | } 12 | 13 | pub type ArgumentMid = Argument; 14 | -------------------------------------------------------------------------------- /halo2_middleware/src/shuffle.rs: -------------------------------------------------------------------------------- 1 | use super::circuit::VarMid; 2 | use super::expression::{Expression, Variable}; 3 | use ff::Field; 4 | 5 | /// Expressions involved in a shuffle argument, with a name as metadata. 6 | #[derive(Clone, Debug)] 7 | pub struct Argument { 8 | pub name: String, 9 | pub input_expressions: Vec>, 10 | pub shuffle_expressions: Vec>, 11 | } 12 | 13 | pub type ArgumentMid = Argument; 14 | -------------------------------------------------------------------------------- /halo2/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to Rust's notion of 6 | [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.1.0-beta.2] - 2022-02-14 11 | ### Removed 12 | - Everything (moved to `halo2_proofs` crate). 13 | 14 | ## [0.1.0-beta.1] - 2021-09-24 15 | Initial beta release! 16 | -------------------------------------------------------------------------------- /book/src/design/proving-system/inner-product.md: -------------------------------------------------------------------------------- 1 | # Inner product argument 2 | 3 | Halo 2 uses a polynomial commitment scheme for which we can create polynomial commitment 4 | opening proofs, based around the Inner Product Argument. 5 | 6 | > TODO: Explain Halo 2's variant of the IPA. 7 | > 8 | > It is very similar to $\text{PC}_\text{DL}.\text{Open}$ from Appendix A.2 of [BCMS20]. 9 | > See [this comparison](comparison.md#bcms20-appendix-a2) for details. 10 | > 11 | > [BCMS20]: https://eprint.iacr.org/2020/499 12 | -------------------------------------------------------------------------------- /COPYING.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Licensed under either of 4 | 5 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 6 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 7 | 8 | at your option. 9 | 10 | # Contribution 11 | 12 | Unless you explicitly state otherwise, any contribution intentionally 13 | submitted for inclusion in the work by you, as defined in the Apache-2.0 14 | license, shall be dual licensed as above, without any additional terms or 15 | conditions. 16 | 17 | -------------------------------------------------------------------------------- /book/src/user/lookup-tables.md: -------------------------------------------------------------------------------- 1 | # Lookup tables 2 | 3 | In normal programs, you can trade memory for CPU to improve performance, by pre-computing 4 | and storing lookup tables for some part of the computation. We can do the same thing in 5 | halo2 circuits! 6 | 7 | A lookup table can be thought of as enforcing a *relation* between variables, where the relation is expressed as a table. 8 | Assuming we have only one lookup argument in our constraint system, the total size of tables is constrained by the size of the circuit: 9 | each table entry costs one row, and it also costs one row to do each lookup. 10 | 11 | TODO 12 | -------------------------------------------------------------------------------- /halo2_proofs/proptest-regressions/plonk/assigned.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 9ec8b547e21d3ed71ee4f99316edb8ff7d0c4d42751bb2479a2864a661860326 # shrinks to (values, operations) = ([Rational(0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000), Trivial(0x0000000000000000000000000000000000000000000000000000000000000001)], [Add]) 8 | -------------------------------------------------------------------------------- /.github/scripts/wasm-target-test-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | GIT_ROOT=$(pwd) 4 | 5 | cd /tmp 6 | 7 | # create test project 8 | cargo new foobar 9 | cd foobar 10 | 11 | # set rust-toolchain same as "halo2" 12 | cp "${GIT_ROOT}/rust-toolchain" . 13 | 14 | # add wasm32-* targets 15 | rustup target add wasm32-unknown-unknown wasm32-wasi 16 | 17 | # add dependencies 18 | cargo add --path "${GIT_ROOT}/halo2_proofs" --features batch,dev-graph,gadget-traces,lookup-any-sanity-checks 19 | cargo add getrandom --features js --target wasm32-unknown-unknown 20 | 21 | # test build for wasm32-* targets 22 | cargo build --release --target wasm32-unknown-unknown 23 | cargo build --release --target wasm32-wasi 24 | 25 | # delete test project 26 | cd ../ 27 | rm -rf foobar 28 | -------------------------------------------------------------------------------- /halo2_middleware/src/poly.rs: -------------------------------------------------------------------------------- 1 | /// Describes the relative rotation of a vector. Negative numbers represent 2 | /// reverse (leftmost) rotations and positive numbers represent forward (rightmost) 3 | /// rotations. Zero represents no rotation. 4 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 5 | pub struct Rotation(pub i32); 6 | 7 | impl Rotation { 8 | /// The current location in the evaluation domain 9 | pub fn cur() -> Rotation { 10 | Rotation(0) 11 | } 12 | 13 | /// The previous location in the evaluation domain 14 | pub fn prev() -> Rotation { 15 | Rotation(-1) 16 | } 17 | 18 | /// The next location in the evaluation domain 19 | pub fn next() -> Rotation { 20 | Rotation(1) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | coverage: 7 | runs-on: ubuntu-22.04 8 | env: 9 | CARGO_TERM_COLOR: always 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Install Rust 13 | run: rustup update stable 14 | - name: Install cargo-llvm-cov 15 | uses: taiki-e/install-action@cargo-llvm-cov 16 | - name: Generate code coverage 17 | run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info 18 | - name: Upload coverage to Codecov 19 | uses: codecov/codecov-action@v5 20 | with: 21 | token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos 22 | files: lcov.info 23 | fail_ci_if_error: true -------------------------------------------------------------------------------- /.github/workflows/lints-beta.yml: -------------------------------------------------------------------------------- 1 | name: Beta lints 2 | 3 | # These lints are only informative, so we only run them directly on branches 4 | # and not trial-merges of PRs, to reduce noise. 5 | on: push 6 | 7 | jobs: 8 | clippy-beta: 9 | name: Clippy (beta) 10 | timeout-minutes: 30 11 | runs-on: ubuntu-latest 12 | continue-on-error: true 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions-rs/toolchain@v1 17 | with: 18 | components: clippy 19 | override: false 20 | - name: Run Clippy (beta) 21 | uses: actions-rs/clippy-check@v1 22 | continue-on-error: true 23 | with: 24 | name: Clippy (beta) 25 | token: ${{ secrets.GITHUB_TOKEN }} 26 | args: --all-features --all-targets -- -W clippy::all 27 | -------------------------------------------------------------------------------- /halo2_proofs/benches/hashtocurve.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for hashing to the Pasta curves. 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | 5 | use halo2_proofs::arithmetic::CurveExt; 6 | use halo2curves::pasta::{pallas, vesta}; 7 | 8 | fn criterion_benchmark(c: &mut Criterion) { 9 | bench_hash_to_curve(c); 10 | } 11 | 12 | fn bench_hash_to_curve(c: &mut Criterion) { 13 | let mut group = c.benchmark_group("hash-to-curve"); 14 | 15 | let hash_pallas = pallas::Point::hash_to_curve("z.cash:test"); 16 | group.bench_function("Pallas", |b| b.iter(|| hash_pallas(b"benchmark"))); 17 | 18 | let hash_vesta = vesta::Point::hash_to_curve("z.cash:test"); 19 | group.bench_function("Vesta", |b| b.iter(|| hash_vesta(b"benchmark"))); 20 | } 21 | 22 | criterion_group!(benches, criterion_benchmark); 23 | criterion_main!(benches); 24 | -------------------------------------------------------------------------------- /halo2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2" 3 | version = "0.1.0-beta.2" 4 | authors = [ 5 | "Jack Grigg ", 6 | ] 7 | edition = "2021" 8 | rust-version = "1.56.1" 9 | description = "[BETA] Fast zero-knowledge proof-carrying data implementation with no trusted setup" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/privacy-scaling-explorations/halo2" 12 | documentation = "https://privacy-scaling-explorations.github.io/halo2/" 13 | readme = "../README.md" 14 | categories = ["cryptography"] 15 | keywords = ["halo", "proofs", "recursive", "zkp", "zkSNARKs"] 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 20 | 21 | [dependencies] 22 | halo2_proofs = { version = "0.4", path = "../halo2_proofs", default-features = false } 23 | 24 | [lib] 25 | bench = false 26 | -------------------------------------------------------------------------------- /halo2_proofs/src/plonk/error.rs: -------------------------------------------------------------------------------- 1 | use super::{ErrorBack, ErrorFront}; 2 | use std::fmt; 3 | 4 | /// This is an error that could occur during proving or circuit synthesis. 5 | #[derive(Debug)] 6 | pub enum Error { 7 | /// Frontend error case 8 | Frontend(ErrorFront), 9 | /// Backend error case 10 | Backend(ErrorBack), 11 | } 12 | 13 | impl fmt::Display for Error { 14 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 15 | match self { 16 | Error::Frontend(err) => write!(f, "Frontend: {err}"), 17 | Error::Backend(err) => write!(f, "Backend: {err}"), 18 | } 19 | } 20 | } 21 | 22 | impl From for Error { 23 | fn from(err: ErrorFront) -> Self { 24 | Error::Frontend(err) 25 | } 26 | } 27 | 28 | impl From for Error { 29 | fn from(err: ErrorBack) -> Self { 30 | Error::Backend(err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /book/src/design.md: -------------------------------------------------------------------------------- 1 | # Design 2 | 3 | ## Note on Language 4 | 5 | We use slightly different language than others to describe PLONK concepts. Here's the 6 | overview: 7 | 8 | 1. We like to think of PLONK-like arguments as tables, where each column corresponds to a 9 | "wire". We refer to entries in this table as "cells". 10 | 2. We like to call "selector polynomials" and so on "fixed columns" instead. We then refer 11 | specifically to a "selector constraint" when a cell in a fixed column is being used to 12 | control whether a particular constraint is enabled in that row. 13 | 3. We call the other polynomials "advice columns" usually, when they're populated by the 14 | prover. 15 | 4. We use the term "rule" to refer to a "gate" like 16 | $$A(X) \cdot q_A(X) + B(X) \cdot q_B(X) + A(X) \cdot B(X) \cdot q_M(X) + C(X) \cdot q_C(X) = 0.$$ 17 | - TODO: Check how consistent we are with this, and update the code and docs to match. 18 | -------------------------------------------------------------------------------- /.github/katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | -------------------------------------------------------------------------------- /halo2_debug/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2_debug" 3 | version = "0.4.0" 4 | authors = [ 5 | "Privacy Scaling Explorations team", 6 | ] 7 | edition = "2021" 8 | rust-version = "1.66.0" 9 | description = """ 10 | Halo2 Debug. This package contains utilities for debugging and testing within 11 | the halo2 ecosystem. 12 | """ 13 | license = "MIT OR Apache-2.0" 14 | repository = "https://github.com/privacy-scaling-explorations/halo2" 15 | documentation = "https://privacy-scaling-explorations.github.io/halo2/" 16 | categories = ["cryptography"] 17 | keywords = ["halo", "proofs", "zkp", "zkSNARKs"] 18 | 19 | [package.metadata.docs.rs] 20 | all-features = true 21 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 22 | 23 | [dependencies] 24 | ff = "0.13" 25 | halo2curves = { version = "0.7.0", default-features = false } 26 | num-bigint = "0.4.5" 27 | halo2_middleware = { path = "../halo2_middleware" } 28 | tiny-keccak = { version = "2.0.2", features=["keccak"] } 29 | hex = "0.4.3" 30 | rand_core = "0.6.4" 31 | rand_chacha = "0.3" 32 | rayon = "1.8" 33 | 34 | [features] 35 | vector-tests = [] 36 | -------------------------------------------------------------------------------- /p3_frontend/tests/keccak_air.rs: -------------------------------------------------------------------------------- 1 | use p3_keccak_air::{generate_trace_rows, KeccakAir, NUM_ROUNDS}; 2 | use p3_util::log2_ceil_usize; 3 | 4 | use halo2curves::bn256::Fr; 5 | use p3_frontend::{CompileParams, FWrap}; 6 | use rand::random; 7 | 8 | mod common; 9 | 10 | #[test] 11 | fn test_keccak() { 12 | let num_hashes = 4; 13 | // TODO: Replace `random()` with a pseudorandom generator with known seed for deterministic 14 | // results. 15 | let inputs = (0..num_hashes).map(|_| random()).collect::>(); 16 | let size = inputs.len() * NUM_ROUNDS; 17 | // TODO: 6 must be bigger than unusable rows. Add a helper function to calculate this 18 | let n = (size + 6).next_power_of_two(); 19 | let k = log2_ceil_usize(n) as u32; 20 | let air = KeccakAir {}; 21 | let num_public_values = 0; 22 | let params = CompileParams { disable_zk: false }; 23 | let trace = generate_trace_rows::>(inputs); 24 | let (compiled_circuit, witness, pis) = 25 | common::compile_witgen(air, ¶ms, k, size, num_public_values, trace); 26 | 27 | common::setup_prove_verify(&compiled_circuit, k, &pis, witness); 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2022 The Electric Coin Company 4 | Copyright (c) 2022 The Halo 2 developers 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /.github/workflows/trigger_proverbench_dispatch.yml: -------------------------------------------------------------------------------- 1 | name: Prover Bench on halo2 PR 2 | on: 3 | pull_request: 4 | types: [labeled , ready_for_review] 5 | jobs: 6 | Prover-benches-via-repo-dispatch-from-halo2-fork: 7 | if: ${{ github.event.label.name == 'benchmarks' }} 8 | runs-on: ubuntu-latest 9 | env: 10 | GH_USER: ${{ github.actor }} 11 | _TOKEN: ${{ secrets.BENCHMARKER }} 12 | REVISION: ${{ github.event.pull_request.head.sha }} 13 | REPO: ${{ github.event.repository.name }} 14 | PR_NUMBER: ${{ github.event.number }} 15 | steps: 16 | - name: Install curl 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install curl 20 | - name: Send repo api call 21 | run: | 22 | curl \ 23 | -X POST \ 24 | -H "Accept: application/vnd.github.v3+json" \ 25 | -u ZKEVMBOT:${{ env._TOKEN }} \ 26 | https://api.github.com/repos/appliedzkp/zkevm-circuits/actions/workflows/ProverBenchFromHalo2.yml/dispatches \ 27 | -d "{\"ref\":\"main\",\"inputs\":{\"halo2pr\":\"${{ env.PR_NUMBER }}\",\"revision\":\"${{ env.REVISION }}\",\"event-type\":\"halo2_wfdispatch\",\"ghuser\": \"${{ env.GH_USER }}\"}}" 28 | -------------------------------------------------------------------------------- /book/src/design/gadgets/ecc/witnessing-points.md: -------------------------------------------------------------------------------- 1 | # Witnessing points 2 | 3 | We represent elliptic curve points in the circuit in their affine representation $(x, y)$. 4 | The identity is represented as the pseudo-coordinate $(0, 0)$, which we 5 | [assume](../ecc.md#chip-assumptions) is not a valid point on the curve. 6 | 7 | ## Non-identity points 8 | 9 | To constrain a coordinate pair $(x, y)$ as representing a valid point on the curve, we 10 | directly check the curve equation. For Pallas and Vesta, this is: 11 | 12 | $$y^2 = x^3 + 5$$ 13 | 14 | $$ 15 | \begin{array}{|c|l|} 16 | \hline 17 | \text{Degree} & \text{Constraint} \\\hline 18 | 4 & q_\text{point}^\text{non-id} \cdot (y^2 - x^3 - 5) = 0 \\\hline 19 | \end{array} 20 | $$ 21 | 22 | ## Points including the identity 23 | 24 | To allow $(x, y)$ to represent either a valid point on the curve, or the pseudo-coordinate 25 | $(0, 0)$, we define a separate gate that enforces the curve equation check unless both $x$ 26 | and $y$ are zero. 27 | 28 | $$ 29 | \begin{array}{|c|l|} 30 | \hline 31 | \text{Degree} & \text{Constraint} \\\hline 32 | 5 & (q_\text{point} \cdot x) \cdot (y^2 - x^3 - 5) = 0 \\\hline 33 | 5 & (q_\text{point} \cdot y) \cdot (y^2 - x^3 - 5) = 0 \\\hline 34 | \end{array} 35 | $$ 36 | -------------------------------------------------------------------------------- /.github/scripts/run-examples.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Get the list of examples from "examples" dir & Cargo.toml 4 | EXAMPLES_WITH_FEATURES=$(awk '/^\[\[example\]\]/ { getline; name=$3; name=substr(name, 2, length(name)-2); getline; if ($1 == "required-features") { features=$NF; gsub(/["\[\]]/, "", features); print name "#" features } }' ./halo2_proofs/Cargo.toml) 5 | EXAMPLES_WITHOUT_FEATURES=$(ls ./halo2_proofs/examples/*.rs | xargs -n1 basename -s .rs) 6 | 7 | # Remove examples with features listed in Cargo.toml from examples without features 8 | EXAMPLES_WITHOUT_FEATURES=$(echo "$EXAMPLES_WITHOUT_FEATURES" | grep -vFx "$(echo "$EXAMPLES_WITH_FEATURES" | cut -d '#' -f 1)") 9 | 10 | # Combine examples with and without features 11 | EXAMPLES=$(echo "$EXAMPLES_WITH_FEATURES $EXAMPLES_WITHOUT_FEATURES" | tr ' ' '\n' | sort -u | tr '\n' ' ') 12 | 13 | # Run the examples 14 | for example in $EXAMPLES; do 15 | if [ "$(echo "$example" | grep '#')" ]; then 16 | name="$(echo "$example" | cut -d '#' -f 1)" 17 | features="$(echo "$example" | cut -d '#' -f 2)" 18 | cargo run --package halo2_proofs --example "$name" --features "$features" 19 | else 20 | cargo run --package halo2_proofs --example "$example" 21 | fi 22 | done 23 | -------------------------------------------------------------------------------- /halo2_backend/src/poly/strategy.rs: -------------------------------------------------------------------------------- 1 | use super::commitment::{CommitmentScheme, Verifier}; 2 | use crate::plonk::Error; 3 | 4 | /// Guards is unfinished verification result. Implement this to construct various 5 | /// verification strategies such as aggregation and recursion. 6 | pub trait Guard { 7 | /// Multi scalar engine which is not evaluated yet. 8 | type MSMAccumulator; 9 | } 10 | 11 | /// Trait representing a strategy for verifying Halo 2 proofs. 12 | pub trait VerificationStrategy<'params, Scheme: CommitmentScheme, V: Verifier<'params, Scheme>> { 13 | /// Creates new verification strategy instance 14 | fn new(params: &'params Scheme::ParamsVerifier) -> Self; 15 | 16 | /// Obtains an MSM from the verifier strategy and yields back the strategy 17 | fn process( 18 | self, 19 | f: impl FnOnce(V::MSMAccumulator) -> Result, 20 | ) -> Result 21 | where 22 | Self: Sized; 23 | 24 | /// Finalizes the batch and checks its validity. 25 | /// 26 | /// Returns `false` if *some* proof was invalid. If the caller needs to identify 27 | /// specific failing proofs, it must re-process the proofs separately. 28 | fn finalize(self) -> bool; 29 | } 30 | -------------------------------------------------------------------------------- /halo2_proofs/proptest-regressions/plonk/circuit/compress_selectors.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 782948e336b9fcaaf993d40cd290eff20399d34766a93793fc3a4516274c1ea7 # shrinks to (selectors, max_degree) = ([SelectorDescription { selector: 0, activations: [false], max_degree: 0 }, SelectorDescription { selector: 1, activations: [false], max_degree: 0 }], 1) 8 | cc 656e5446792c4f5fe22fd10bcd2dbadc70e84ac1ddb1a7ec8f622f64a15ff260 # shrinks to (selectors, max_degree) = ([SelectorDescription { selector: 0, activations: [false], max_degree: 1 }, SelectorDescription { selector: 1, activations: [false], max_degree: 1 }, SelectorDescription { selector: 2, activations: [false], max_degree: 1 }], 2) 9 | cc b7b81ca8745931e4dd8b4f896f7bde78f85f4d88857c5fdf9dc4bbf0f172db5e # shrinks to (selectors, max_degree) = ([SelectorDescription { selector: 0, activations: [false], max_degree: 1 }, SelectorDescription { selector: 1, activations: [false], max_degree: 1 }, SelectorDescription { selector: 2, activations: [false], max_degree: 1 }], 2) 10 | -------------------------------------------------------------------------------- /halo2_debug/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod check_witness; 2 | pub mod display; 3 | 4 | pub use check_witness::check_witness; 5 | 6 | use rand_chacha::ChaCha20Rng; 7 | use rand_core::SeedableRng; 8 | use tiny_keccak::Hasher; 9 | 10 | pub fn test_rng() -> ChaCha20Rng { 11 | ChaCha20Rng::seed_from_u64(0xdeadbeef) 12 | } 13 | 14 | /// Gets the hex representation of the keccak hash of the input data 15 | pub fn keccak_hex>(data: D) -> String { 16 | let mut hash = [0u8; 32]; 17 | let mut hasher = tiny_keccak::Keccak::v256(); 18 | hasher.update(data.as_ref()); 19 | hasher.finalize(&mut hash); 20 | hex::encode(hash) 21 | } 22 | 23 | /// When the feature `vector-tests` is enabled, executes the test in a single thread and checks the result against the expected value. 24 | /// When the feature `vector-tests` is disabled, just executes the test. 25 | pub fn test_result Vec + Send>(test: F, _expected: &str) -> Vec { 26 | #[cfg(feature = "vector-tests")] 27 | let result = rayon::ThreadPoolBuilder::new() 28 | .num_threads(1) 29 | .build() 30 | .unwrap() 31 | .install(|| { 32 | let result = test(); 33 | assert_eq!(_expected, keccak_hex(result.clone()),); 34 | result 35 | }); 36 | 37 | #[cfg(not(feature = "vector-tests"))] 38 | let result = test(); 39 | 40 | result 41 | } 42 | -------------------------------------------------------------------------------- /halo2_middleware/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2_middleware" 3 | version = "0.4.0" 4 | authors = [ 5 | "Sean Bowe ", 6 | "Ying Tong Lai ", 7 | "Daira Hopwood ", 8 | "Jack Grigg ", 9 | "Privacy Scaling Explorations team", 10 | ] 11 | edition = "2021" 12 | rust-version = "1.66.0" 13 | description = """ 14 | Halo2 middleware. This package contains the types and traits required for the frontend-backend interaction. 15 | """ 16 | license = "MIT OR Apache-2.0" 17 | repository = "https://github.com/privacy-scaling-explorations/halo2" 18 | documentation = "https://privacy-scaling-explorations.github.io/halo2/" 19 | readme = "README.md" 20 | categories = ["cryptography"] 21 | keywords = ["halo", "proofs", "zkp", "zkSNARKs"] 22 | 23 | [package.metadata.docs.rs] 24 | all-features = true 25 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 26 | 27 | [dependencies] 28 | ff = "0.13" 29 | halo2curves = { version = "0.7.0", default-features = false } 30 | serde = { version = "1", optional = true, features = ["derive"] } 31 | serde_derive = { version = "1", optional = true} 32 | rayon = "1.8" 33 | 34 | [dev-dependencies] 35 | ark-std = { version = "0.3" } 36 | proptest = "1" 37 | group = "0.13" 38 | rand_xorshift = "0.3.0" 39 | rand_core = "0.6.4" 40 | 41 | [lib] 42 | bench = false 43 | -------------------------------------------------------------------------------- /book/edithtml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cat - "$1" > "$1.prefix" < 5 | 6 | 7 | 8 | 9 | 10 | $2 11 | 17 | 18 | 19 | 21 | 22 | 23 | EOF 24 | cat "$1.prefix" - >"$1" < 26 | 27 | EOF 28 | rm -f "$1.prefix" 29 | -------------------------------------------------------------------------------- /halo2_middleware/src/multicore.rs: -------------------------------------------------------------------------------- 1 | pub use rayon::{ 2 | current_num_threads, 3 | iter::{IndexedParallelIterator, IntoParallelRefIterator}, 4 | iter::{IntoParallelIterator, IntoParallelRefMutIterator, ParallelIterator}, 5 | join, scope, 6 | slice::ParallelSliceMut, 7 | Scope, 8 | }; 9 | 10 | pub trait TryFoldAndReduce { 11 | /// Implements `iter.try_fold().try_reduce()` for `rayon::iter::ParallelIterator`, 12 | /// falling back on `Iterator::try_fold` when the `multicore` feature flag is 13 | /// disabled. 14 | /// The `try_fold_and_reduce` function can only be called by a iter with 15 | /// `Result` item type because the `fold_op` must meet the trait 16 | /// bounds of both `try_fold` and `try_reduce` from rayon. 17 | fn try_fold_and_reduce( 18 | self, 19 | identity: impl Fn() -> T + Send + Sync, 20 | fold_op: impl Fn(T, Result) -> Result + Send + Sync, 21 | ) -> Result; 22 | } 23 | 24 | impl TryFoldAndReduce for I 25 | where 26 | T: Send + Sync, 27 | E: Send + Sync, 28 | I: rayon::iter::ParallelIterator>, 29 | { 30 | fn try_fold_and_reduce( 31 | self, 32 | identity: impl Fn() -> T + Send + Sync, 33 | fold_op: impl Fn(T, Result) -> Result + Send + Sync, 34 | ) -> Result { 35 | self.try_fold(&identity, &fold_op) 36 | .try_reduce(&identity, |a, b| fold_op(a, Ok(b))) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /book/src/concepts/gadgets.md: -------------------------------------------------------------------------------- 1 | # Gadgets 2 | 3 | When implementing a circuit, we could use the features of the chips we've selected directly. 4 | Typically, though, we will use them via ***gadgets***. This indirection is useful because, 5 | for reasons of efficiency and limitations imposed by PLONKish circuits, the chip interfaces will 6 | often be dependent on low-level implementation details. The gadget interface can provide a more 7 | convenient and stable API that abstracts away from extraneous detail. 8 | 9 | For example, consider a hash function such as SHA-256. The interface of a chip supporting 10 | SHA-256 might be dependent on internals of the hash function design such as the separation 11 | between message schedule and compression function. The corresponding gadget interface can 12 | provide a more convenient and familiar `update`/`finalize` API, and can also handle parts 13 | of the hash function that do not need chip support, such as padding. This is similar to how 14 | [accelerated](https://software.intel.com/content/www/us/en/develop/articles/intel-sha-extensions.html) 15 | [instructions](https://developer.arm.com/documentation/ddi0514/g/introduction/about-the-cortex-a57-processor-cryptography-engine) 16 | for cryptographic primitives on CPUs are typically accessed via software libraries, rather 17 | than directly. 18 | 19 | Gadgets can also provide modular and reusable abstractions for circuit programming 20 | at a higher level, similar to their use in libraries such as 21 | [libsnark](https://github.com/christianlundkvist/libsnark-tutorial) and 22 | [bellman](https://electriccoin.co/blog/bellman-zksnarks-in-rust/). As well as abstracting 23 | *functions*, they can also abstract *types*, such as elliptic curve points or integers of 24 | specific sizes. 25 | 26 | -------------------------------------------------------------------------------- /p3_frontend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p3_frontend" 3 | version = "0.4.0" 4 | authors = [ 5 | "Privacy Scaling Explorations team", 6 | ] 7 | edition = "2021" 8 | description = """ 9 | Plonky3 frontend implementation. Allows using a circuit defined with the Air trait from plonky3 to be proved with a halo2 backend. 10 | """ 11 | license = "MIT OR Apache-2.0" 12 | categories = ["cryptography"] 13 | keywords = ["halo", "proofs", "zkp", "zkSNARKs", "plonky3"] 14 | 15 | [package.metadata.docs.rs] 16 | all-features = true 17 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 18 | 19 | [dependencies] 20 | p3-air = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 21 | p3-util = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 22 | p3-matrix = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 23 | p3-field = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 24 | p3-uni-stark = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 25 | halo2_middleware = { path = "../halo2_middleware" } 26 | halo2_debug = { path = "../halo2_debug" } 27 | serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } 28 | num-bigint = { version = "0.4.3", default-features = false } 29 | 30 | [dev-dependencies] 31 | halo2curves = { version = "0.7.0", default-features = false } 32 | rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } 33 | halo2_backend = { path = "../halo2_backend" } 34 | serde_test = { version = "1.0" } 35 | p3-keccak-air = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 36 | p3-keccak = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 37 | p3-util = { git = "https://github.com/Plonky3/Plonky3", rev = "7b5b8a6" } 38 | rand = "0.8.5" 39 | halo2_debug = { path = "../halo2_debug" } 40 | -------------------------------------------------------------------------------- /.github/workflows/docs-ghpages.yml: -------------------------------------------------------------------------------- 1 | name: halo2 docs ghpages 2 | # Setup docs for ghpages 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - uses: actions/checkout@v3 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: nightly-2024-10-17 20 | override: true 21 | 22 | - name: Copy the html file to workspace crates 23 | run: | 24 | for cargo_toml in $(find . -name Cargo.toml); do 25 | crate_dir=$(dirname $cargo_toml) 26 | cp .github/katex-header.html $crate_dir 27 | echo "Copied html file to $crate_dir" 28 | done 29 | 30 | - name: Build latest rustdocs 31 | uses: actions-rs/cargo@v1 32 | with: 33 | command: doc 34 | args: --no-deps --all-features --workspace 35 | env: 36 | RUSTDOCFLAGS: -Z unstable-options --enable-index-page --cfg docsrs --html-in-header ${{ github.workspace }}/halo2_proofs/katex-header.html 37 | 38 | - name: Create the required index page and move the latest rustdocs into docs 39 | run: | 40 | rm -rf ./docs 41 | cp -R ./target/doc ./docs 42 | echo "" > ./docs/index.html 43 | 44 | - name: Delete the html files copied to every crate 45 | run: | 46 | for cargo_toml in $(find . -name Cargo.toml); do 47 | crate_dir=$(dirname $cargo_toml) 48 | rm -f $crate_dir/katex-header.html 49 | echo "Deleted html file in $crate_dir" 50 | done 51 | 52 | - name: Deploy to GitHub Pages 53 | uses: peaceiris/actions-gh-pages@v4 54 | with: 55 | github_token: ${{ secrets.GITHUB_TOKEN }} 56 | publish_dir: ./docs 57 | 58 | -------------------------------------------------------------------------------- /halo2_backend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2_backend" 3 | version = "0.4.0" 4 | authors = [ 5 | "Sean Bowe ", 6 | "Ying Tong Lai ", 7 | "Daira Hopwood ", 8 | "Jack Grigg ", 9 | "Privacy Scaling Explorations team", 10 | ] 11 | edition = "2021" 12 | rust-version = "1.66.0" 13 | description = """ 14 | Halo2 backend implementation. This package implements the halo2 proof system which includes setup (key generation), proving and verifying. 15 | """ 16 | license = "MIT OR Apache-2.0" 17 | repository = "https://github.com/privacy-scaling-explorations/halo2" 18 | documentation = "https://privacy-scaling-explorations.github.io/halo2/" 19 | readme = "README.md" 20 | categories = ["cryptography"] 21 | keywords = ["halo", "proofs", "zkp", "zkSNARKs"] 22 | 23 | [package.metadata.docs.rs] 24 | all-features = true 25 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 26 | 27 | [dependencies] 28 | backtrace = { version = "0.3", optional = true } 29 | ff = "0.13" 30 | group = "0.13" 31 | halo2curves = { version = "0.7.0", default-features = false } 32 | rand_core = { version = "0.6", default-features = false } 33 | tracing = "0.1" 34 | blake2b_simd = "1" # MSRV 1.66.0 35 | sha3 = "0.9.1" 36 | rand_chacha = "0.3" 37 | serde = { version = "1", optional = true, features = ["derive"] } 38 | serde_derive = { version = "1", optional = true} 39 | rayon = "1.8" 40 | halo2_middleware = { path = "../halo2_middleware" } 41 | 42 | [dev-dependencies] 43 | assert_matches = "1.5" 44 | criterion = "0.3" 45 | gumdrop = "0.8" 46 | proptest = "1" 47 | rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } 48 | serde_json = "1" 49 | 50 | [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] 51 | getrandom = { version = "0.2", features = ["js"] } 52 | 53 | [features] 54 | default = ["batch", "bits"] 55 | bits = ["halo2curves/bits"] 56 | sanity-checks = [] 57 | batch = ["rand_core/getrandom"] 58 | derive_serde = ["halo2curves/derive_serde"] 59 | 60 | [lib] 61 | bench = false 62 | -------------------------------------------------------------------------------- /book/src/design/gadgets/ecc.md: -------------------------------------------------------------------------------- 1 | # Elliptic Curves 2 | 3 | ## `EccChip` 4 | 5 | `halo2_gadgets` provides a chip that implements `EccInstructions` using 10 advice columns. 6 | The chip is currently restricted to the Pallas curve, but will be extended to support the 7 | [Vesta curve](https://github.com/zcash/halo2/issues/578) in the near future. 8 | 9 | ### Chip assumptions 10 | 11 | A non-exhaustive list of assumptions made by `EccChip`: 12 | - $0$ is not an $x$-coordinate of a valid point on the curve. 13 | - Holds for Pallas because $5$ is not square in $\mathbb{F}_q$. 14 | - $0$ is not a $y$-coordinate of a valid point on the curve. 15 | - Holds for Pallas because $-5$ is not a cube in $\mathbb{F}_q$. 16 | 17 | ### Layout 18 | 19 | The following table shows how columns are used by the gates for various chip sub-areas: 20 | 21 | - $W$ - witnessing points. 22 | - $AI$ - incomplete point addition. 23 | - $AC$ - complete point addition. 24 | - $MF$ - Fixed-base scalar multiplication. 25 | - $MVI$ - variable-base scalar multiplication, incomplete rounds. 26 | - $MVC$ - variable-base scalar multiplication, complete rounds. 27 | - $MVO$ - variable-base scalar multiplication, overflow check. 28 | 29 | $$ 30 | \begin{array}{|c||c|c|c|c|c|c|c|c|c|c|} 31 | \hline 32 | \text{Sub-area} & a_0 & a_1 & a_2 & a_3 & a_4 & a_5 & a_6 & a_7 & a_8 & a_9 \\\hline 33 | \hline 34 | W & x & y \\\hline 35 | \hline 36 | AI & x_p & y_p & x_q & y_q \\\hline 37 | & & & x_r & y_r \\\hline 38 | \hline 39 | AC & x_p & y_p & x_q & y_q & \lambda & \alpha & \beta & \gamma & \delta & \\\hline 40 | & & & x_r & y_r \\\hline 41 | \hline 42 | MF & x_p & y_p & x_q & y_q & \text{window} & u \\\hline 43 | & & & x_r & y_r \\\hline 44 | \hline 45 | MVI & x_p & y_p & \lambda_2^{lo} & x_A^{hi} & \lambda_1^{hi} & \lambda_2^{hi} & z^{lo} & x_A^{lo} & \lambda_1^{lo} & z^{hi} \\\hline 46 | \hline 47 | MVC & x_p & y_p & x_q & y_q & \lambda & \alpha & \beta & \gamma & \delta & z^{complete} \\\hline 48 | & & & x_r & y_r \\\hline 49 | \end{array} 50 | $$ 51 | -------------------------------------------------------------------------------- /book/macros.txt: -------------------------------------------------------------------------------- 1 | # Conventions 2 | 3 | \bconcat:{\mathop{\kern 0.1em||\kern 0.1em}} 4 | \Repr:{\star} 5 | 6 | # Conversions 7 | 8 | \ItoLEBSP:{\mathsf{I2LEBSP}_{#1}} 9 | 10 | # Fields and curves 11 | 12 | \BaseLength:{\ell^\mathsf{#1\vphantom{p}}_{\mathsf{base}}} 13 | 14 | # Commitments and hashes 15 | 16 | \SinsemillaHash:{\mathsf{SinsemillaHash}} 17 | \SinsemillaCommit:{\mathsf{SinsemillaCommit}} 18 | \SinsemillaShortCommit:{\mathsf{SinsemillaShortCommit}} 19 | 20 | # Circuit constraint helper methods 21 | 22 | \BoolCheck:{\texttt{bool\_check}({#1})} 23 | \Ternary:{\texttt{ternary}({{#1}, {#2}, {#3}})} 24 | \RangeCheck:{\texttt{range\_check}({#1, #2})} 25 | \ShortLookupRangeCheck:{\texttt{short\_lookup\_range\_check}({#1})} 26 | 27 | # Halo 2 proof 28 | 29 | \field:{\mathbb{F}} 30 | \group:{\mathbb{G}} 31 | \setup:{\textnormal{Setup}} 32 | \prover:{\mathcal{P}} 33 | \verifier:{\mathcal{V}} 34 | \sec:{\lambda} 35 | \negl:{\textnormal{negl}(\lambda)} 36 | \pp:{\mathsf{pp}} 37 | \ip:{\textnormal{IP}} 38 | \relation:{\mathcal{R}} 39 | \a:{\mathcal{A}} 40 | \sim:{\mathcal{S}} 41 | \tr:{\textnormal{tr}} 42 | \srs:{\textnormal{SRS}} 43 | \srwee:{\textnormal{sr-wee}} 44 | \real:{\textnormal{real}} 45 | \ideal:{\textnormal{ideal}} 46 | \weereal:{\textnormal{WEE-real}} 47 | \weeideal:{\textnormal{WEE-ideal}} 48 | \oracle:{\mathcal{O}} 49 | \ch:{\mathsf{Ch}} 50 | \badch:{\mathsf{BadCh}} 51 | \adv:{\mathsf{Adv}} 52 | \bottom:{\perp} 53 | \alg:{#1_\textnormal{alg}} 54 | \zero:{\mathcal{O}} 55 | \dlrel:{\textsf{dl-rel}} 56 | \game:{\mathsf{G}} 57 | \innerprod:{\langle{#1},{#2}\rangle} 58 | \dlgame:{\mathsf{G}^\dlrel_{\group,n}} 59 | \distinguisher:{\mathcal{D}} 60 | \extractor:{\mathcal{E}} 61 | \state:{\mathsf{st}_{#1}} 62 | \halo:{\textsf{Halo}} 63 | \lo:{\textnormal{lo}} 64 | \hi:{\textnormal{hi}} 65 | \protocol:{\halo} 66 | \extractwitness:{\textnormal{ExtractWitness}} 67 | \pfail:{p_\textnormal{fail}} 68 | \repr:\{\kern-0.1em {#1} \kern-0.1em\}^{#2} 69 | \rep:{\repr{#1}{}} 70 | \repv:{\repr{#1}{\mathbf{#2}}_{#3}} 71 | \dlreladv:{\mathcal{H}} 72 | \mr:{\mathcal{M}^{#1}_{#2}({#3})} 73 | \mv:{\mr{\mathbf{#1}}{#2}{#3}} 74 | \m:{\mr{#1}{}{#2}} 75 | \z:{\mathcal{Z}_{#1}({#2}, {#3})} 76 | \trprefix:{{#1}|_{#2}} 77 | -------------------------------------------------------------------------------- /halo2_backend/src/poly/kzg/multiopen/gwc.rs: -------------------------------------------------------------------------------- 1 | mod prover; 2 | mod verifier; 3 | 4 | pub use prover::ProverGWC; 5 | pub use verifier::VerifierGWC; 6 | 7 | use crate::{poly::query::Query, transcript::ChallengeScalar}; 8 | use halo2_middleware::ff::Field; 9 | use std::marker::PhantomData; 10 | 11 | #[derive(Clone, Copy, Debug)] 12 | struct U {} 13 | type ChallengeU = ChallengeScalar; 14 | 15 | #[derive(Clone, Copy, Debug)] 16 | struct V {} 17 | type ChallengeV = ChallengeScalar; 18 | 19 | struct CommitmentData> { 20 | queries: Vec, 21 | point: F, 22 | _marker: PhantomData, 23 | } 24 | 25 | fn construct_intermediate_sets>( 26 | queries: I, 27 | ) -> Option>> 28 | where 29 | I: IntoIterator + Clone, 30 | { 31 | let queries = queries.into_iter().collect::>(); 32 | 33 | // Caller tried to provide two different evaluations for the same 34 | // commitment. Permitting this would be unsound. 35 | { 36 | let mut query_set: Vec<(Q::Commitment, F)> = vec![]; 37 | for query in queries.iter() { 38 | let commitment = query.get_commitment(); 39 | let rotation = query.get_point(); 40 | if query_set.contains(&(commitment, rotation)) { 41 | return None; 42 | } 43 | query_set.push((commitment, rotation)); 44 | } 45 | } 46 | 47 | let mut point_query_map: Vec<(F, Vec)> = Vec::new(); 48 | for query in queries { 49 | if let Some(pos) = point_query_map 50 | .iter() 51 | .position(|(point, _)| *point == query.get_point()) 52 | { 53 | let (_, queries) = &mut point_query_map[pos]; 54 | queries.push(query); 55 | } else { 56 | point_query_map.push((query.get_point(), vec![query])); 57 | } 58 | } 59 | 60 | Some( 61 | point_query_map 62 | .into_iter() 63 | .map(|(point, queries)| CommitmentData { 64 | queries, 65 | point, 66 | _marker: PhantomData, 67 | }) 68 | .collect(), 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /book/src/IDENTIFIERS.json: -------------------------------------------------------------------------------- 1 | { 2 | "decompose-combined-lookup": "design/gadgets/decomposition.html#combined-lookup-expression", 3 | "decompose-short-lookup": "design/gadgets/decomposition.html#short-range-check", 4 | "decompose-short-range": "design/gadgets/decomposition.html#short-range-decomposition", 5 | "ecc-complete-addition": "design/gadgets/ecc/addition.html#complete-addition-constraints", 6 | "ecc-incomplete-addition": "design/gadgets/ecc/addition.html#incomplete-addition-constraints", 7 | "ecc-fixed-mul-base-canonicity": "design/gadgets/ecc/fixed-base-scalar-mul.html#base-field-element", 8 | "ecc-fixed-mul-coordinates": "design/gadgets/ecc/fixed-base-scalar-mul.html#constrain-coordinates", 9 | "ecc-fixed-mul-full-word": "design/gadgets/ecc/fixed-base-scalar-mul.html#full-width-scalar", 10 | "ecc-fixed-mul-load-base": "design/gadgets/ecc/fixed-base-scalar-mul.html#load-fixed-base", 11 | "ecc-fixed-mul-short-msb": "design/gadgets/ecc/fixed-base-scalar-mul.html#constrain-short-signed-msb", 12 | "ecc-fixed-mul-short-conditional-neg": "design/gadgets/ecc/fixed-base-scalar-mul.html#constrain-short-signed-conditional-neg", 13 | "ecc-var-mul-complete-gate": "design/gadgets/ecc/var-base-scalar-mul.html#complete-gate", 14 | "ecc-var-mul-incomplete-first-row": "design/gadgets/ecc/var-base-scalar-mul.html#incomplete-first-row-gate", 15 | "ecc-var-mul-incomplete-last-row": "design/gadgets/ecc/var-base-scalar-mul.html#incomplete-last-row-gate", 16 | "ecc-var-mul-incomplete-main-loop": "design/gadgets/ecc/var-base-scalar-mul.html#incomplete-main-loop-gate", 17 | "ecc-var-mul-lsb-gate": "design/gadgets/ecc/var-base-scalar-mul.html#lsb-gate", 18 | "ecc-var-mul-overflow": "design/gadgets/ecc/var-base-scalar-mul.html#overflow-check-constraints", 19 | "ecc-var-mul-witness-scalar": "design/gadgets/ecc/var-base-scalar-mul.html#witness-scalar", 20 | "ecc-witness-point": "design/gadgets/ecc/witnessing-points.html#points-including-the-identity", 21 | "ecc-witness-non-identity-point": "design/gadgets/ecc/witnessing-points.html#non-identity-points", 22 | "sinsemilla-constraints": "design/gadgets/sinsemilla.html#optimized-sinsemilla-gate", 23 | "sinsemilla-merkle-crh-bit-lengths": "design/gadgets/sinsemilla/merkle-crh.html#bit-length-constraints", 24 | "sinsemilla-merkle-crh-decomposition": "design/gadgets/sinsemilla/merkle-crh.html#decomposition-constraints" 25 | } -------------------------------------------------------------------------------- /halo2_frontend/src/plonk/shuffle.rs: -------------------------------------------------------------------------------- 1 | use crate::plonk::Expression; 2 | use halo2_middleware::ff::Field; 3 | use std::fmt::{self, Debug}; 4 | 5 | /// Expressions involved in a shuffle argument, with a name as metadata. 6 | #[derive(Clone)] 7 | pub struct Argument { 8 | pub(crate) name: String, 9 | pub(crate) input_expressions: Vec>, 10 | pub(crate) shuffle_expressions: Vec>, 11 | } 12 | 13 | impl Debug for Argument { 14 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 15 | f.debug_struct("Argument") 16 | .field("input_expressions", &self.input_expressions) 17 | .field("shuffle_expressions", &self.shuffle_expressions) 18 | .finish() 19 | } 20 | } 21 | 22 | impl Argument { 23 | /// Constructs a new shuffle argument. 24 | /// 25 | /// `shuffle` is a sequence of `(input, shuffle)` tuples. 26 | pub fn new>(name: S, shuffle: Vec<(Expression, Expression)>) -> Self { 27 | let (input_expressions, shuffle_expressions) = shuffle.into_iter().unzip(); 28 | Argument { 29 | name: name.as_ref().to_string(), 30 | input_expressions, 31 | shuffle_expressions, 32 | } 33 | } 34 | 35 | pub(crate) fn required_degree(&self) -> usize { 36 | assert_eq!(self.input_expressions.len(), self.shuffle_expressions.len()); 37 | 38 | let mut input_degree = 1; 39 | for expr in self.input_expressions.iter() { 40 | input_degree = std::cmp::max(input_degree, expr.degree()); 41 | } 42 | let mut shuffle_degree = 1; 43 | for expr in self.shuffle_expressions.iter() { 44 | shuffle_degree = std::cmp::max(shuffle_degree, expr.degree()); 45 | } 46 | 47 | // (1 - (l_last + l_blind)) (z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)) 48 | std::cmp::max(2 + shuffle_degree, 2 + input_degree) 49 | } 50 | 51 | /// Returns input of this argument 52 | pub fn input_expressions(&self) -> &Vec> { 53 | &self.input_expressions 54 | } 55 | 56 | /// Returns table of this argument 57 | pub fn shuffle_expressions(&self) -> &Vec> { 58 | &self.shuffle_expressions 59 | } 60 | 61 | /// Returns name of this argument 62 | pub fn name(&self) -> &str { 63 | &self.name 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # The halo2 Book 2 | 3 | [halo2](README.md) 4 | - [Concepts](concepts.md) 5 | - [Proof systems](concepts/proofs.md) 6 | - [PLONKish Arithmetization](concepts/arithmetization.md) 7 | - [Chips](concepts/chips.md) 8 | - [Gadgets](concepts/gadgets.md) 9 | - [User Documentation](user.md) 10 | - [Developer tools](user/dev-tools.md) 11 | - [A simple example](user/simple-example.md) 12 | - [Lookup tables](user/lookup-tables.md) 13 | - [Gadgets](user/gadgets.md) 14 | - [Tips and tricks](user/tips-and-tricks.md) 15 | - [Experimental features](user/experimental-features.md) 16 | - [Design](design.md) 17 | - [Proving system](design/proving-system.md) 18 | - [Lookup argument](design/proving-system/lookup.md) 19 | - [Permutation argument](design/proving-system/permutation.md) 20 | - [Circuit commitments](design/proving-system/circuit-commitments.md) 21 | - [Vanishing argument](design/proving-system/vanishing.md) 22 | - [Multipoint opening argument](design/proving-system/multipoint-opening.md) 23 | - [Inner product argument](design/proving-system/inner-product.md) 24 | - [Comparison to other work](design/proving-system/comparison.md) 25 | - [Protocol Description](design/protocol.md) 26 | - [Implementation](design/implementation.md) 27 | - [Proofs](design/implementation/proofs.md) 28 | - [Fields](design/implementation/fields.md) 29 | - [Selector combining](design/implementation/selector-combining.md) 30 | - [Gadgets](design/gadgets.md) 31 | - [Elliptic curve cryptography](design/gadgets/ecc.md) 32 | - [Witnessing points](design/gadgets/ecc/witnessing-points.md) 33 | - [Incomplete and complete addition](design/gadgets/ecc/addition.md) 34 | - [Fixed-base scalar multiplication](design/gadgets/ecc/fixed-base-scalar-mul.md) 35 | - [Variable-base scalar multiplication](design/gadgets/ecc/var-base-scalar-mul.md) 36 | - [Sinsemilla](design/gadgets/sinsemilla.md) 37 | - [MerkleCRH](design/gadgets/sinsemilla/merkle-crh.md) 38 | - [Decomposition](design/gadgets/decomposition.md) 39 | - [SHA-256](design/gadgets/sha256.md) 40 | - [16-bit table chip](design/gadgets/sha256/table16.md) 41 | - [Background Material](background.md) 42 | - [Fields](background/fields.md) 43 | - [Polynomials](background/polynomials.md) 44 | - [Cryptographic groups](background/groups.md) 45 | - [Elliptic curves](background/curves.md) 46 | - [Polynomial commitment using inner product argument](background/pc-ipa.md) 47 | - [Recursion](background/recursion.md) 48 | -------------------------------------------------------------------------------- /halo2_frontend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2_frontend" 3 | version = "0.4.0" 4 | authors = [ 5 | "Sean Bowe ", 6 | "Ying Tong Lai ", 7 | "Daira Hopwood ", 8 | "Jack Grigg ", 9 | "Privacy Scaling Explorations team", 10 | ] 11 | edition = "2021" 12 | rust-version = "1.66.0" 13 | description = """ 14 | Halo2 frontend implementation. This package implements an API to write circuits, handles witness generation and contains the MockProver. 15 | """ 16 | license = "MIT OR Apache-2.0" 17 | repository = "https://github.com/privacy-scaling-explorations/halo2" 18 | documentation = "https://privacy-scaling-explorations.github.io/halo2/" 19 | readme = "README.md" 20 | categories = ["cryptography"] 21 | keywords = ["halo", "proofs", "zkp", "zkSNARKs"] 22 | 23 | [package.metadata.docs.rs] 24 | all-features = true 25 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 26 | 27 | [dependencies] 28 | backtrace = { version = "0.3", optional = true } 29 | ff = "0.13" 30 | group = "0.13" 31 | halo2curves = { version = "0.7.0", default-features = false } 32 | tracing = "0.1" 33 | blake2b_simd = "1" # MSRV 1.66.0 34 | serde = { version = "1", optional = true, features = ["derive"] } 35 | serde_derive = { version = "1", optional = true} 36 | halo2_middleware = { path = "../halo2_middleware" } 37 | 38 | # Developer tooling dependencies 39 | plotters = { version = "0.3.0", default-features = false, optional = true } 40 | tabbycat = { version = "0.1", features = ["attributes"], optional = true } 41 | 42 | [dev-dependencies] 43 | proptest = "1" 44 | rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } 45 | serde_json = "1" 46 | tracing-subscriber = { version = "0.3" } 47 | tracing = "0.1" 48 | tracing-capture = "0.1" 49 | 50 | [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] 51 | getrandom = { version = "0.2", features = ["js"] } 52 | 53 | [features] 54 | default = ["bits", "lookup-any-sanity-checks"] 55 | dev-graph = ["plotters", "tabbycat"] 56 | test-dev-graph = [ 57 | "dev-graph", 58 | "plotters/bitmap_backend", 59 | "plotters/bitmap_encoder", 60 | "plotters/ttf", 61 | ] 62 | bits = ["halo2curves/bits"] 63 | gadget-traces = ["backtrace"] 64 | thread-safe-region = [] 65 | circuit-params = [] 66 | cost-estimator = ["serde", "serde_derive"] 67 | derive_serde = ["halo2curves/derive_serde"] 68 | lookup-any-sanity-checks = [] 69 | 70 | [lib] 71 | bench = false 72 | -------------------------------------------------------------------------------- /book/src/background/recursion.md: -------------------------------------------------------------------------------- 1 | ## Recursion 2 | > Alternative terms: Induction; Accumulation scheme; Proof-carrying data 3 | 4 | However, the computation of $G$ requires a length-$2^k$ multiexponentiation 5 | $\langle \mathbf{G}, \mathbf{s}\rangle,$ where $\mathbf{s}$ is composed of the round 6 | challenges $u_1, \cdots, u_k$ arranged in a binary counting structure. This is the 7 | linear-time computation that we want to amortise across a batch of proof instances. 8 | Instead of computing $G,$ notice that we can express $G$ as a commitment to a polynomial 9 | 10 | $$G = \text{Commit}(\sigma, g(X, u_1, \cdots, u_k)),$$ 11 | 12 | where $g(X, u_1, \cdots, u_k) := \prod_{i=1}^k (u_i + u_i^{-1}X^{2^{i-1}})$ is a 13 | polynomial with degree $2^k - 1.$ 14 | 15 | | | | 16 | | -------- | -------- | 17 | | | Since $G$ is a commitment, it can be checked in an inner product argument. The verifier circuit witnesses $G$ and brings $G, u_1, \cdots, u_k$ out as public inputs to the proof $\pi.$ The next verifier instance checks $\pi$ using the inner product argument; this includes checking that $G = \text{Commit}(g(X, u_1, \cdots, u_k))$ evaluates at some random point to the expected value for the given challenges $u_1, \cdots, u_k.$ Recall from the [previous section](#Polynomial-commitment-using-inner-product-argument) that this check only requires $\log d$ work.

At the end of checking $\pi$ and $G,$ the circuit is left with a new $G',$ along with the $u_1', \cdots, u_k'$ challenges sampled for the check. To fully accept $\pi$ as valid, we should perform a linear-time computation of $G' = \langle\mathbf{G}, \mathbf{s}'\rangle$. Once again, we delay this computation by witnessing $G'$ and bringing $G', u_1', \cdots, u_k'$ out as public inputs to the proof $\pi'.$

This goes on from one proof instance to the next, until we are satisfied with the size of our batch of proofs. We finally perform a single linear-time computation, thus deciding the validity of the whole batch. | 18 | 19 | We recall from the section [Cycles of curves](curves.md#cycles-of-curves) that we can 20 | instantiate this protocol over a two-cycle, where a proof produced by one curve is 21 | efficiently verified in the circuit of the other curve. However, some of these verifier 22 | checks can actually be efficiently performed in the native circuit; these are "deferred" 23 | to the next native circuit (see diagram below) instead of being immediately passed over to 24 | the other curve. 25 | 26 | ![](https://i.imgur.com/l4HrYgE.png) 27 | -------------------------------------------------------------------------------- /halo2_proofs/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Legacy halo2 API that wraps the frontend-backend split API. This crate doesn't implement any 2 | //! core functionality, it just imports from the other crates and offers the legacy API in the same 3 | //! module structure so that projects depending on halo2 can update their dependency towards it 4 | //! without breaking. 5 | 6 | #![cfg_attr(docsrs, feature(doc_cfg))] 7 | // The actual lints we want to disable. 8 | #![allow(clippy::op_ref, clippy::many_single_char_names)] 9 | #![deny(rustdoc::broken_intra_doc_links)] 10 | #![deny(missing_debug_implementations)] 11 | #![deny(missing_docs)] 12 | #![deny(unsafe_code)] 13 | 14 | pub mod plonk; 15 | 16 | /// Traits and structs for implementing circuit components. 17 | pub mod circuit { 18 | pub use halo2_frontend::circuit::floor_planner; 19 | pub use halo2_frontend::circuit::{ 20 | AssignedCell, Cell, Chip, Layouter, NamespacedLayouter, Region, RegionIndex, 21 | SimpleFloorPlanner, Table, Value, 22 | }; 23 | } 24 | /// This module provides common utilities, traits and structures for group, 25 | /// field and polynomial arithmetic. 26 | pub mod arithmetic { 27 | pub use halo2_backend::arithmetic::{parallelize, CurveAffine, CurveExt, Field}; 28 | } 29 | /// Tools for developing circuits. 30 | pub mod dev { 31 | pub use halo2_frontend::dev::{ 32 | metadata, CellValue, FailureLocation, InstanceValue, MockProver, VerifyFailure, 33 | }; 34 | 35 | #[cfg(feature = "cost-estimator")] 36 | pub use halo2_frontend::dev::cost_model; 37 | 38 | #[cfg(feature = "dev-graph")] 39 | pub use halo2_frontend::dev::{circuit_dot_graph, CircuitLayout}; 40 | } 41 | /// Contains utilities for performing arithmetic over univariate polynomials in 42 | /// various forms, including computing commitments to them and provably opening 43 | /// the committed polynomials at arbitrary points. 44 | pub mod poly { 45 | pub use halo2_backend::poly::VerificationStrategy; 46 | pub use halo2_backend::poly::{commitment, kzg, EvaluationDomain}; 47 | pub use halo2_middleware::poly::Rotation; 48 | } 49 | /// This module contains utilities and traits for dealing with Fiat-Shamir 50 | /// transcripts. 51 | pub mod transcript { 52 | pub use halo2_backend::transcript::{ 53 | Blake2bRead, Blake2bWrite, Challenge255, EncodedChallenge, Transcript, TranscriptRead, 54 | TranscriptReadBuffer, TranscriptWrite, TranscriptWriterBuffer, 55 | }; 56 | } 57 | mod helpers { 58 | pub use halo2_backend::helpers::SerdeFormat; 59 | } 60 | 61 | pub use crate::helpers::SerdeFormat; 62 | 63 | pub use halo2curves; 64 | -------------------------------------------------------------------------------- /halo2_proofs/benches/commit_zk.rs: -------------------------------------------------------------------------------- 1 | extern crate criterion; 2 | 3 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 4 | use group::ff::Field; 5 | use halo2_proofs::arithmetic::parallelize; 6 | use halo2curves::pasta::pallas::Scalar; 7 | use rand_chacha::rand_core::RngCore; 8 | use rand_chacha::ChaCha20Rng; 9 | use rand_core::SeedableRng; 10 | use rayon::current_num_threads; 11 | use std::{collections::HashMap, iter}; 12 | 13 | fn rand_poly_serial(mut rng: ChaCha20Rng, domain: usize) -> Vec { 14 | // Sample a random polynomial of degree n - 1 15 | let mut random_poly = vec![Scalar::zero(); 1 << domain]; 16 | for coeff in random_poly.iter_mut() { 17 | *coeff = Scalar::random(&mut rng); 18 | } 19 | 20 | random_poly 21 | } 22 | 23 | fn rand_poly_par(mut rng: ChaCha20Rng, domain: usize) -> Vec { 24 | // Sample a random polynomial of degree n - 1 25 | let n = 1usize << domain; 26 | let mut random_poly = vec![Scalar::ZERO; n]; 27 | 28 | let num_threads = current_num_threads(); 29 | let chunk_size = n / num_threads; 30 | let thread_seeds = (0..) 31 | .step_by(chunk_size + 1) 32 | .take(n % num_threads) 33 | .chain( 34 | (chunk_size != 0) 35 | .then(|| ((n % num_threads) * (chunk_size + 1)..).step_by(chunk_size)) 36 | .into_iter() 37 | .flatten(), 38 | ) 39 | .take(num_threads) 40 | .zip(iter::repeat_with(|| { 41 | let mut seed = [0u8; 32]; 42 | rng.fill_bytes(&mut seed); 43 | ChaCha20Rng::from_seed(seed) 44 | })) 45 | .collect::>(); 46 | 47 | parallelize(&mut random_poly, |chunk, offset| { 48 | let mut rng = thread_seeds[&offset].clone(); 49 | chunk.iter_mut().for_each(|v| *v = Scalar::random(&mut rng)); 50 | }); 51 | random_poly 52 | } 53 | 54 | fn bench_commit(c: &mut Criterion) { 55 | let mut group = c.benchmark_group("Blinder_poly"); 56 | let rand = ChaCha20Rng::from_seed([1u8; 32]); 57 | for i in [ 58 | 18usize, 19usize, 20usize, 21usize, 22usize, 23usize, 24usize, 25usize, 59 | ] 60 | .iter() 61 | { 62 | group.bench_with_input(BenchmarkId::new("serial", i), i, |b, i| { 63 | b.iter(|| rand_poly_serial(rand.clone(), *i)) 64 | }); 65 | group.bench_with_input(BenchmarkId::new("parallel", i), i, |b, i| { 66 | b.iter(|| rand_poly_par(rand.clone(), *i)) 67 | }); 68 | } 69 | group.finish(); 70 | } 71 | 72 | criterion_group!(benches, bench_commit); 73 | criterion_main!(benches); 74 | -------------------------------------------------------------------------------- /book/src/design/gadgets/sha256.md: -------------------------------------------------------------------------------- 1 | # SHA-256 2 | 3 | ## Specification 4 | 5 | SHA-256 is specified in [NIST FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). 6 | 7 | Unlike the specification, we use $\boxplus$ for addition modulo $2^{32}$, and $+$ for 8 | field addition. $\oplus$ is used for XOR. 9 | 10 | ## Gadget interface 11 | 12 | SHA-256 maintains state in eight 32-bit variables. It processes input as 512-bit blocks, 13 | but internally splits these blocks into 32-bit chunks. We therefore designed the SHA-256 14 | gadget to consume input in 32-bit chunks. 15 | 16 | ## Chip instructions 17 | 18 | The SHA-256 gadget requires a chip with the following instructions: 19 | 20 | ```rust 21 | # extern crate halo2_proofs; 22 | # use halo2_proofs::plonk::Error; 23 | # use std::fmt; 24 | # 25 | # trait Chip: Sized {} 26 | # trait Layouter {} 27 | const BLOCK_SIZE: usize = 16; 28 | const DIGEST_SIZE: usize = 8; 29 | 30 | pub trait Sha256Instructions: Chip { 31 | /// Variable representing the SHA-256 internal state. 32 | type State: Clone + fmt::Debug; 33 | /// Variable representing a 32-bit word of the input block to the SHA-256 compression 34 | /// function. 35 | type BlockWord: Copy + fmt::Debug; 36 | 37 | /// Places the SHA-256 IV in the circuit, returning the initial state variable. 38 | fn initialization_vector(layouter: &mut impl Layouter) -> Result; 39 | 40 | /// Starting from the given initial state, processes a block of input and returns the 41 | /// final state. 42 | fn compress( 43 | layouter: &mut impl Layouter, 44 | initial_state: &Self::State, 45 | input: [Self::BlockWord; BLOCK_SIZE], 46 | ) -> Result; 47 | 48 | /// Converts the given state into a message digest. 49 | fn digest( 50 | layouter: &mut impl Layouter, 51 | state: &Self::State, 52 | ) -> Result<[Self::BlockWord; DIGEST_SIZE], Error>; 53 | } 54 | ``` 55 | 56 | TODO: Add instruction for computing padding. 57 | 58 | This set of instructions was chosen to strike a balance between the reusability of the 59 | instructions, and the scope for chips to internally optimise them. In particular, we 60 | considered splitting the compression function into its constituent parts (Ch, Maj etc), 61 | and providing a compression function gadget that implemented the round logic. However, 62 | this would prevent chips from using relative references between the various parts of a 63 | compression round. Having an instruction that implements all compression rounds is also 64 | similar to the Intel SHA extensions, which provide an instruction that performs multiple 65 | compression rounds. 66 | -------------------------------------------------------------------------------- /halo2_backend/src/plonk/error.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | use std::fmt; 3 | use std::io; 4 | 5 | use halo2_middleware::circuit::ColumnMid; 6 | 7 | /// This is an error that could occur during proving. 8 | #[derive(Debug)] 9 | pub enum Error { 10 | /// The provided instances do not match the circuit parameters. 11 | InvalidInstances, 12 | /// The constraint system is not satisfied. 13 | ConstraintSystemFailure, 14 | /// Out of bounds index passed to a backend 15 | BoundsFailure, 16 | /// Opening error 17 | Opening, 18 | /// Transcript error 19 | Transcript(io::Error), 20 | /// `k` is too small for the given circuit. 21 | NotEnoughRowsAvailable { 22 | /// The current value of `k` being used. 23 | current_k: u32, 24 | }, 25 | /// Instance provided exceeds number of available rows 26 | InstanceTooLarge, 27 | /// The instance sets up a copy constraint involving a column that has not been 28 | /// included in the permutation. 29 | ColumnNotInPermutation(ColumnMid), 30 | /// Generic error not covered by previous cases 31 | Other(String), 32 | } 33 | 34 | impl From for Error { 35 | fn from(error: io::Error) -> Self { 36 | // The only place we can get io::Error from is the transcript. 37 | Error::Transcript(error) 38 | } 39 | } 40 | 41 | impl Error { 42 | /// Constructs an `Error::NotEnoughRowsAvailable`. 43 | pub fn not_enough_rows_available(current_k: u32) -> Self { 44 | Error::NotEnoughRowsAvailable { current_k } 45 | } 46 | } 47 | 48 | impl fmt::Display for Error { 49 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 50 | match self { 51 | Error::InvalidInstances => write!(f, "Provided instances do not match the circuit"), 52 | Error::ConstraintSystemFailure => write!(f, "The constraint system is not satisfied"), 53 | Error::BoundsFailure => write!(f, "An out-of-bounds index was passed to the backend"), 54 | Error::Opening => write!(f, "Multi-opening proof was invalid"), 55 | Error::Transcript(e) => write!(f, "Transcript error: {e}"), 56 | Error::NotEnoughRowsAvailable { current_k } => write!( 57 | f, 58 | "k = {current_k} is too small for the given circuit. Try using a larger value of k", 59 | ), 60 | Error::InstanceTooLarge => write!(f, "Instance vectors are larger than the circuit"), 61 | Error::ColumnNotInPermutation(column) => { 62 | write!(f, "Column {column:?} must be included in the permutation",) 63 | } 64 | Error::Other(error) => write!(f, "Other: {error}"), 65 | } 66 | } 67 | } 68 | 69 | impl error::Error for Error { 70 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { 71 | match self { 72 | Error::Transcript(e) => Some(e), 73 | _ => None, 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /book/src/design/proving-system/comparison.md: -------------------------------------------------------------------------------- 1 | # Comparison to other work 2 | 3 | ## BCMS20 Appendix A.2 4 | 5 | Appendix A.2 of [BCMS20] describes a polynomial commitment scheme that is similar to the 6 | one described in [BGH19] (BCMS20 being a generalization of the original Halo paper). Halo 7 | 2 builds on both of these works, and thus itself uses a polynomial commitment scheme that 8 | is very similar to the one in BCMS20. 9 | 10 | [BGH19]: https://eprint.iacr.org/2019/1021 11 | [BCMS20]: https://eprint.iacr.org/2020/499 12 | 13 | The following table provides a mapping between the variable names in BCMS20, and the 14 | equivalent objects in Halo 2 (which builds on the nomenclature from the Halo paper): 15 | 16 | | BCMS20 | Halo 2 | 17 | | :------------: | :-----------------: | 18 | | $S$ | $H$ | 19 | | $H$ | $U$ | 20 | | $C$ | `msm` or $P$ | 21 | | $\alpha$ | $\iota$ | 22 | | $\xi_0$ | $z$ | 23 | | $\xi_i$ | `challenge_i` | 24 | | $H'$ | $[z] U$ | 25 | | $\bar{p}$ | `s_poly` | 26 | | $\bar{\omega}$ | `s_poly_blind` | 27 | | $\bar{C}$ | `s_poly_commitment` | 28 | | $h(X)$ | $g(X)$ | 29 | | $U$ | $G$ | 30 | | $\omega'$ | `blind` / $\xi$ | 31 | | $\mathbf{c}$ | $\mathbf{a}$ | 32 | | $c$ | $a = \mathbf{a}_0$ | 33 | | $v'$ | $ab$ | 34 | 35 | Halo 2's polynomial commitment scheme differs from Appendix A.2 of BCMS20 in two ways: 36 | 37 | 1. Step 8 of the $\text{Open}$ algorithm computes a "non-hiding" commitment $C'$ prior to 38 | the inner product argument, which opens to the same value as $C$ but is a commitment to 39 | a randomly-drawn polynomial. The remainder of the protocol involves no blinding. By 40 | contrast, in Halo 2 we blind every single commitment that we make (even for instance 41 | and fixed polynomials, though using a blinding factor of 1 for the fixed polynomials); 42 | this makes the protocol simpler to reason about. As a consequence of this, the verifier 43 | needs to handle the cumulative blinding factor at the end of the protocol, and so there 44 | is no need to derive an equivalent to $C'$ at the start of the protocol. 45 | 46 | - $C'$ is also an input to the random oracle for $\xi_0$; in Halo 2 we utilize a 47 | transcript that has already committed to the equivalent components of $C'$ prior to 48 | sampling $z$. 49 | 50 | 2. The $\text{PC}_\text{DL}.\text{SuccinctCheck}$ subroutine (Figure 2 of BCMS20) computes 51 | the initial group element $C_0$ by adding $[v] H' = [v \xi_0] H$, which requires two 52 | scalar multiplications. Instead, we subtract $[v] G_0$ from the original commitment $P$, 53 | so that we're effectively opening the polynomial at the point to the value zero. The 54 | computation $[v] G_0$ is more efficient in the context of recursion because $G_0$ is a 55 | fixed base (so we can use lookup tables). 56 | -------------------------------------------------------------------------------- /book/src/user/tips-and-tricks.md: -------------------------------------------------------------------------------- 1 | # Tips and tricks 2 | 3 | This section contains various ideas and snippets that you might find useful while writing 4 | halo2 circuits. 5 | 6 | ## Small range constraints 7 | 8 | A common constraint used in R1CS circuits is the boolean constraint: $b * (1 - b) = 0$. 9 | This constraint can only be satisfied by $b = 0$ or $b = 1$. 10 | 11 | In halo2 circuits, you can similarly constrain a cell to have one of a small set of 12 | values. For example, to constrain $a$ to the range $[0..5]$, you would create a gate of 13 | the form: 14 | 15 | $$a \cdot (1 - a) \cdot (2 - a) \cdot (3 - a) \cdot (4 - a) = 0$$ 16 | 17 | while to constrain $c$ to be either 7 or 13, you would use: 18 | 19 | $$(7 - c) \cdot (13 - c) = 0$$ 20 | 21 | > The underlying principle here is that we create a polynomial constraint with roots at 22 | > each value in the set of possible values we want to allow. In R1CS circuits, the maximum 23 | > supported polynomial degree is 2 (due to all constraints being of the form $a * b = c$). 24 | > In halo2 circuits, you can use arbitrary-degree polynomials - with the proviso that 25 | > higher-degree constraints are more expensive to use. 26 | 27 | Note that the roots don't have to be constants; for example $(a - x) \cdot (a - y) \cdot (a - z) = 0$ will constrain $a$ to be equal to one of $\{ x, y, z \}$ where the latter can be arbitrary polynomials, as long as the whole expression stays within the maximum degree bound. 28 | 29 | ## Small set interpolation 30 | We can use Lagrange interpolation to create a polynomial constraint that maps 31 | $f(X) = Y$ for small sets of $X \in \{x_i\}, Y \in \{y_i\}$. 32 | 33 | For instance, say we want to map a 2-bit value to a "spread" version interleaved 34 | with zeros. We first precompute the evaluations at each point: 35 | 36 | $$ 37 | \begin{array}{rcl} 38 | 00 \rightarrow 0000 &\implies& 0 \rightarrow 0 \\ 39 | 01 \rightarrow 0001 &\implies& 1 \rightarrow 1 \\ 40 | 10 \rightarrow 0100 &\implies& 2 \rightarrow 4 \\ 41 | 11 \rightarrow 0101 &\implies& 3 \rightarrow 5 42 | \end{array} 43 | $$ 44 | 45 | Then, we construct the Lagrange basis polynomial for each point using the 46 | identity: 47 | $$\mathcal{l}_j(X) = \prod_{0 \leq m < k,\; m \neq j} \frac{x - x_m}{x_j - x_m},$$ 48 | where $k$ is the number of data points. ($k = 4$ in our example above.) 49 | 50 | Recall that the Lagrange basis polynomial $\mathcal{l}_j(X)$ evaluates to $1$ at 51 | $X = x_j$ and $0$ at all other $x_i, j \neq i.$ 52 | 53 | Continuing our example, we get four Lagrange basis polynomials: 54 | 55 | $$ 56 | \begin{array}{ccc} 57 | l_0(X) &=& \frac{(X - 3)(X - 2)(X - 1)}{(-3)(-2)(-1)} \\[1ex] 58 | l_1(X) &=& \frac{(X - 3)(X - 2)(X)}{(-2)(-1)(1)} \\[1ex] 59 | l_2(X) &=& \frac{(X - 3)(X - 1)(X)}{(-1)(1)(2)} \\[1ex] 60 | l_3(X) &=& \frac{(X - 2)(X - 1)(X)}{(1)(2)(3)} 61 | \end{array} 62 | $$ 63 | 64 | Our polynomial constraint is then 65 | 66 | $$ 67 | \begin{array}{cccccccccccl} 68 | &f(0) \cdot l_0(X) &+& f(1) \cdot l_1(X) &+& f(2) \cdot l_2(X) &+& f(3) \cdot l_3(X) &-& f(X) &=& 0 \\ 69 | \implies& 0 \cdot l_0(X) &+& 1 \cdot l_1(X) &+& 4 \cdot l_2(X) &+& 5 \cdot l_3(X) &-& f(X) &=& 0. \\ 70 | \end{array} 71 | $$ 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # halo2 [![codecov](https://codecov.io/github/privacy-scaling-explorations/halo2/graph/badge.svg?token=6WX7KBHFIP)](https://codecov.io/github/privacy-scaling-explorations/halo2) 2 | 3 | ## [Documentation](https://privacy-scaling-explorations.github.io/halo2/halo2_proofs) 4 | 5 | PSE-Halo2 is [halo2](https://github.com/zcash/halo2) fork by 6 | [PSE](https://pse.dev) and includes contributions from the community. 7 | 8 | PSE-Halo2 modified, extended, and eventually [re-architected](https://github.com/privacy-scaling-explorations/halo2/pull/254) the [original Halo2 implementation by Zcash](https://github.com/zcash/halo2) - an instantiation of the PLONK proof system. 9 | 10 | The original IPA backend was swapped with KZG for cost-effective Ethereum L1 verifiability, and comes with a Solidity verifier. Support for many [additional curves](https://github.com/privacy-scaling-explorations/halo2curves) and other [experimental features](https://github.com/kilic/tetris) were added, and the system was eventually re-architected by [splitting the front- and backends](https://github.com/privacy-scaling-explorations/halo2/pull/254). 11 | 12 | PSE-Halo2 is in maintenance mode starting January 2025, which means: 13 | - Bugs reported will be fixed 14 | - PRs with non-trivial but **narrow-scope** additions or fixes will be reviewed 15 | - Feature-add PRs or wide-scope changes to the architecture **will not be reviewed**. To extend PSE-Halo2 with significant features we recommend [Axiom's fork](https://github.com/axiom-crypto/halo2) instead. 16 | 17 | We use the `main` branch for development, which means it may contain 18 | unstable/unfinished features. For end-users we recommend using the tag releases 19 | which can be seen as curated checkpoints with some level of guarantee of 20 | stability. 21 | 22 | For experimental features `privacy-scaling-explorations/halo2` fork adds, please refer to [`experimental-features.md`](./book/src/user/experimental-features.md). 23 | 24 | ## Minimum Supported Rust Version 25 | 26 | Requires Rust **1.65.0** or higher. 27 | 28 | Minimum supported Rust version can be changed in the future, but it will be done with a 29 | minor version bump. 30 | 31 | ## Controlling parallelism 32 | 33 | `halo2` currently uses [rayon](https://github.com/rayon-rs/rayon) for parallel computation. The `RAYON_NUM_THREADS` environment variable can be used to set the number of threads. 34 | 35 | When compiling to WASM-targets, notice that since version `1.7`, `rayon` will fallback automatically (with no need to handle features) to require `getrandom` in order to be able to work. For more info related to WASM-compilation. 36 | 37 | See: [Rayon: Usage with WebAssembly](https://github.com/rayon-rs/rayon#usage-with-webassembly) for more 38 | 39 | ## License 40 | 41 | Licensed under either of 42 | 43 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 44 | http://www.apache.org/licenses/LICENSE-2.0) 45 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 46 | 47 | at your option. 48 | 49 | ### Contribution 50 | 51 | Unless you explicitly state otherwise, any contribution intentionally 52 | submitted for inclusion in the work by you, as defined in the Apache-2.0 53 | license, shall be dual licensed as above, without any additional terms or 54 | conditions. 55 | -------------------------------------------------------------------------------- /book/src/concepts/arithmetization.md: -------------------------------------------------------------------------------- 1 | # PLONKish Arithmetization 2 | 3 | The arithmetization used by Halo 2 comes from [PLONK](https://eprint.iacr.org/2019/953), or 4 | more precisely its extension UltraPLONK that supports custom gates and lookup arguments. We'll 5 | call it [***PLONKish***](https://twitter.com/feministPLT/status/1413815927704014850). 6 | 7 | ***PLONKish circuits*** are defined in terms of a rectangular matrix of values. We refer to 8 | ***rows***, ***columns***, and ***cells*** of this matrix with the conventional meanings. 9 | 10 | A PLONKish circuit depends on a ***configuration***: 11 | 12 | * A finite field $\mathbb{F}$, where cell values (for a given statement and witness) will be 13 | elements of $\mathbb{F}$. 14 | * The number of columns in the matrix, and a specification of each column as being 15 | ***fixed***, ***advice***, or ***instance***. Fixed columns are fixed by the circuit; 16 | advice columns correspond to witness values; and instance columns are normally used for 17 | public inputs (technically, they can be used for any elements shared between the prover 18 | and verifier). 19 | 20 | * A subset of the columns that can participate in equality constraints. 21 | 22 | * A ***maximum constraint degree***. 23 | 24 | * A sequence of ***polynomial constraints***. These are multivariate polynomials over 25 | $\mathbb{F}$ that must evaluate to zero *for each row*. The variables in a polynomial 26 | constraint may refer to a cell in a given column of the current row, or a given column of 27 | another row relative to this one (with wrap-around, i.e. taken modulo $n$). The maximum 28 | degree of each polynomial is given by the maximum constraint degree. 29 | 30 | * A sequence of ***lookup arguments*** defined over tuples of ***input expressions*** 31 | (which are multivariate polynomials as above) and ***table columns***. 32 | 33 | A PLONKish circuit also defines: 34 | 35 | * The number of rows $n$ in the matrix. $n$ must correspond to the size of a multiplicative 36 | subgroup of $\mathbb{F}^\times$; typically a power of two. 37 | 38 | * A sequence of ***equality constraints***, which specify that two given cells must have equal 39 | values. 40 | 41 | * The values of the fixed columns at each row. 42 | 43 | From a circuit description we can generate a ***proving key*** and a ***verification key***, 44 | which are needed for the operations of proving and verification for that circuit. 45 | 46 | > Note that we specify the ordering of columns, polynomial constraints, lookup arguments, and 47 | > equality constraints, even though these do not affect the meaning of the circuit. This makes 48 | > it easier to define the generation of proving and verification keys as a deterministic 49 | > process. 50 | 51 | Typically, a configuration will define polynomial constraints that are switched off and on by 52 | ***selectors*** defined in fixed columns. For example, a constraint $q_i \cdot p(...) = 0$ can 53 | be switched off for a particular row $i$ by setting $q_i = 0$. In this case we sometimes refer 54 | to a set of constraints controlled by a set of selector columns that are designed to be used 55 | together, as a ***gate***. Typically there will be a ***standard gate*** that supports generic 56 | operations like field multiplication and division, and possibly also ***custom gates*** that 57 | support more specialized operations. 58 | -------------------------------------------------------------------------------- /halo2_proofs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2_proofs" 3 | version = "0.4.0" 4 | authors = [ 5 | "Sean Bowe ", 6 | "Ying Tong Lai ", 7 | "Daira Hopwood ", 8 | "Jack Grigg ", 9 | "Privacy Scaling Explorations team", 10 | ] 11 | edition = "2021" 12 | rust-version = "1.66.0" 13 | description = """ 14 | Fast PLONK-based zero-knowledge proving system with no trusted setup 15 | """ 16 | license = "MIT OR Apache-2.0" 17 | repository = "https://github.com/privacy-scaling-explorations/halo2" 18 | documentation = "https://privacy-scaling-explorations.github.io/halo2/" 19 | readme = "README.md" 20 | categories = ["cryptography"] 21 | keywords = ["halo", "proofs", "zkp", "zkSNARKs"] 22 | 23 | [package.metadata.docs.rs] 24 | all-features = true 25 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 26 | 27 | [[bench]] 28 | name = "commit_zk" 29 | harness = false 30 | 31 | [[bench]] 32 | name = "hashtocurve" 33 | harness = false 34 | 35 | [[bench]] 36 | name = "plonk" 37 | harness = false 38 | 39 | [[bench]] 40 | name = "dev_lookup" 41 | harness = false 42 | 43 | [dependencies] 44 | halo2_middleware = { path = "../halo2_middleware" } 45 | halo2_backend = { path = "../halo2_backend", default-features = false } 46 | halo2_frontend = { path = "../halo2_frontend", default-features = false } 47 | halo2curves = { version = "0.7.0", default-features = false } 48 | rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } 49 | plotters = { version = "0.3.0", default-features = false, optional = true } 50 | group = "0.13" 51 | 52 | [dev-dependencies] 53 | ff = "0.13" 54 | group = "0.13" 55 | tracing = "0.1" 56 | rand_chacha = "0.3" 57 | rayon = "1.8" 58 | assert_matches = "1.5" 59 | criterion = "0.3" 60 | gumdrop = "0.8" 61 | proptest = "1" 62 | dhat = "0.3.2" 63 | serde_json = "1" 64 | halo2_debug = { path = "../halo2_debug" } 65 | 66 | [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] 67 | getrandom = { version = "0.2", features = ["js"] } 68 | 69 | [features] 70 | default = ["batch", "bits", "halo2_frontend/default", "halo2_backend/default", "lookup-any-sanity-checks"] 71 | dev-graph = ["halo2_frontend/dev-graph", "plotters"] 72 | test-dev-graph = [ 73 | "halo2_frontend/test-dev-graph", 74 | "dev-graph", 75 | "plotters/bitmap_backend", 76 | "plotters/bitmap_encoder", 77 | "plotters/ttf" 78 | ] 79 | bits = ["halo2curves/bits", "halo2_frontend/bits", "halo2_backend/bits"] 80 | gadget-traces = ["halo2_frontend/gadget-traces"] 81 | thread-safe-region = ["halo2_frontend/thread-safe-region"] 82 | sanity-checks = ["halo2_backend/sanity-checks"] 83 | batch = ["rand_core/getrandom", "halo2_backend/batch"] 84 | circuit-params = ["halo2_frontend/circuit-params"] 85 | cost-estimator = ["halo2_frontend/cost-estimator"] 86 | derive_serde = ["halo2curves/derive_serde", "halo2_frontend/derive_serde", "halo2_backend/derive_serde"] 87 | vector-tests = [] 88 | lookup-any-sanity-checks = ["halo2_frontend/lookup-any-sanity-checks"] 89 | 90 | [lib] 91 | bench = false 92 | 93 | [[example]] 94 | name = "circuit-layout" 95 | required-features = ["test-dev-graph"] 96 | 97 | [[example]] 98 | name = "proof-size" 99 | required-features = ["cost-estimator"] 100 | -------------------------------------------------------------------------------- /book/src/user/simple-example.md: -------------------------------------------------------------------------------- 1 | # A simple example 2 | 3 | Let's start with a simple circuit, to introduce you to the common APIs and how they are 4 | used. The circuit will take a public input $c$, and will prove knowledge of two private 5 | inputs $a$ and $b$ such that 6 | 7 | $$a^2 \cdot b^2 = c.$$ 8 | 9 | ## Define instructions 10 | 11 | Firstly, we need to define the instructions that our circuit will rely on. Instructions 12 | are the boundary between high-level [gadgets](../concepts/gadgets.md) and the low-level 13 | circuit operations. Instructions may be as coarse or as granular as desired, but in 14 | practice you want to strike a balance between an instruction being large enough to 15 | effectively optimize its implementation, and small enough that it is meaningfully 16 | reusable. 17 | 18 | For our circuit, we will use three instructions: 19 | - Load a private number into the circuit. 20 | - Multiply two numbers. 21 | - Expose a number as a public input to the circuit. 22 | 23 | We also need a type for a variable representing a number. Instruction interfaces provide 24 | associated types for their inputs and outputs, to allow the implementations to represent 25 | these in a way that makes the most sense for their optimization goals. 26 | 27 | ```rust,ignore,no_run 28 | {{#include ../../../halo2_proofs/examples/simple-example.rs:instructions}} 29 | ``` 30 | 31 | ## Define a chip implementation 32 | 33 | For our circuit, we will build a [chip](../concepts/chips.md) that provides the above 34 | numeric instructions for a finite field. 35 | 36 | ```rust,ignore,no_run 37 | {{#include ../../../halo2_proofs/examples/simple-example.rs:chip}} 38 | ``` 39 | 40 | Every chip needs to implement the `Chip` trait. This defines the properties of the chip 41 | that a `Layouter` may rely on when synthesizing a circuit, as well as enabling any initial 42 | state that the chip requires to be loaded into the circuit. 43 | 44 | ```rust,ignore,no_run 45 | {{#include ../../../halo2_proofs/examples/simple-example.rs:chip-impl}} 46 | ``` 47 | 48 | ## Configure the chip 49 | 50 | The chip needs to be configured with the columns, permutations, and gates that will be 51 | required to implement all of the desired instructions. 52 | 53 | ```rust,ignore,no_run 54 | {{#include ../../../halo2_proofs/examples/simple-example.rs:chip-config}} 55 | ``` 56 | 57 | ## Implement chip traits 58 | 59 | ```rust,ignore,no_run 60 | {{#include ../../../halo2_proofs/examples/simple-example.rs:instructions-impl}} 61 | ``` 62 | 63 | ## Build the circuit 64 | 65 | Now that we have the instructions we need, and a chip that implements them, we can finally 66 | build our circuit! 67 | 68 | ```rust,ignore,no_run 69 | {{#include ../../../halo2_proofs/examples/simple-example.rs:circuit}} 70 | ``` 71 | 72 | ## Testing the circuit 73 | 74 | `halo2_proofs::dev::MockProver` can be used to test that the circuit is working correctly. The 75 | private and public inputs to the circuit are constructed as we will do to create a proof, 76 | but by passing them to `MockProver::run` we get an object that can test every constraint 77 | in the circuit, and tell us exactly what is failing (if anything). 78 | 79 | ```rust,ignore,no_run 80 | {{#include ../../../halo2_proofs/examples/simple-example.rs:test-circuit}} 81 | ``` 82 | 83 | ## Full example 84 | 85 | You can find the source code for this example 86 | [here](https://github.com/zcash/halo2/tree/main/halo2_proofs/examples/simple-example.rs). 87 | -------------------------------------------------------------------------------- /halo2_proofs/examples/proof-size.rs: -------------------------------------------------------------------------------- 1 | use ff::Field; 2 | use halo2_proofs::{ 3 | circuit::{Layouter, SimpleFloorPlanner, Value}, 4 | plonk::{Advice, Circuit, Column, ConstraintSystem, ErrorFront}, 5 | }; 6 | use halo2curves::pasta::Fp; 7 | 8 | use halo2_proofs::dev::cost_model::{from_circuit_to_model_circuit, CommitmentScheme}; 9 | use halo2_proofs::plonk::{Expression, Selector, TableColumn}; 10 | use halo2_proofs::poly::Rotation; 11 | 12 | // We use a lookup example 13 | #[derive(Clone, Copy)] 14 | struct TestCircuit {} 15 | 16 | #[derive(Debug, Clone)] 17 | struct MyConfig { 18 | selector: Selector, 19 | table: TableColumn, 20 | advice: Column, 21 | } 22 | 23 | impl Circuit for TestCircuit { 24 | type Config = MyConfig; 25 | type FloorPlanner = SimpleFloorPlanner; 26 | #[cfg(feature = "circuit-params")] 27 | type Params = (); 28 | 29 | fn without_witnesses(&self) -> Self { 30 | Self {} 31 | } 32 | 33 | fn configure(meta: &mut ConstraintSystem) -> MyConfig { 34 | let config = MyConfig { 35 | selector: meta.complex_selector(), 36 | table: meta.lookup_table_column(), 37 | advice: meta.advice_column(), 38 | }; 39 | 40 | meta.lookup("lookup", |meta| { 41 | let selector = meta.query_selector(config.selector); 42 | let not_selector = Expression::Constant(Fp::ONE) - selector.clone(); 43 | let advice = meta.query_advice(config.advice, Rotation::cur()); 44 | vec![(selector * advice + not_selector, config.table)] 45 | }); 46 | 47 | config 48 | } 49 | 50 | fn synthesize( 51 | &self, 52 | config: MyConfig, 53 | mut layouter: impl Layouter, 54 | ) -> Result<(), ErrorFront> { 55 | layouter.assign_table( 56 | || "8-bit table", 57 | |mut table| { 58 | for row in 0u64..(1 << 8) { 59 | table.assign_cell( 60 | || format!("row {row}"), 61 | config.table, 62 | row as usize, 63 | || Value::known(Fp::from(row + 1)), 64 | )?; 65 | } 66 | 67 | Ok(()) 68 | }, 69 | )?; 70 | 71 | layouter.assign_region( 72 | || "assign values", 73 | |mut region| { 74 | for offset in 0u64..(1 << 10) { 75 | config.selector.enable(&mut region, offset as usize)?; 76 | region.assign_advice( 77 | || format!("offset {offset}"), 78 | config.advice, 79 | offset as usize, 80 | || Value::known(Fp::from((offset % 256) + 1)), 81 | )?; 82 | } 83 | 84 | Ok(()) 85 | }, 86 | ) 87 | } 88 | } 89 | 90 | const K: u32 = 11; 91 | 92 | fn main() { 93 | let circuit = TestCircuit {}; 94 | 95 | let model = from_circuit_to_model_circuit::<_, _, 56, 56>( 96 | K, 97 | &circuit, 98 | vec![], 99 | CommitmentScheme::KZGGWC, 100 | ); 101 | println!( 102 | "Cost of circuit with 8 bit lookup table: \n{}", 103 | serde_json::to_string_pretty(&model).unwrap() 104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /halo2_backend/src/poly/kzg/multiopen/gwc/prover.rs: -------------------------------------------------------------------------------- 1 | use super::{construct_intermediate_sets, ChallengeV, Query}; 2 | use crate::arithmetic::{kate_division, powers}; 3 | use crate::helpers::SerdeCurveAffine; 4 | use crate::poly::commitment::ParamsProver; 5 | use crate::poly::commitment::Prover; 6 | use crate::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; 7 | use crate::poly::query::ProverQuery; 8 | use crate::poly::{commitment::Blind, Polynomial}; 9 | use crate::transcript::{EncodedChallenge, TranscriptWrite}; 10 | 11 | use group::Curve; 12 | use halo2_middleware::zal::traits::MsmAccel; 13 | use halo2curves::pairing::Engine; 14 | use halo2curves::CurveExt; 15 | use rand_core::RngCore; 16 | use std::fmt::Debug; 17 | use std::io; 18 | use std::marker::PhantomData; 19 | 20 | /// Concrete KZG prover with GWC variant 21 | #[derive(Debug)] 22 | pub struct ProverGWC<'params, E: Engine> { 23 | params: &'params ParamsKZG, 24 | } 25 | 26 | /// Create a multi-opening proof 27 | impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> for ProverGWC<'params, E> 28 | where 29 | E::G1Affine: SerdeCurveAffine::Fr, CurveExt = ::G1>, 30 | E::G1: CurveExt, 31 | E::G2Affine: SerdeCurveAffine, 32 | { 33 | fn new(params: &'params ParamsKZG) -> Self { 34 | Self { params } 35 | } 36 | 37 | /// Create a multi-opening proof 38 | fn create_proof_with_engine< 39 | 'com, 40 | Ch: EncodedChallenge, 41 | T: TranscriptWrite, 42 | R, 43 | I, 44 | >( 45 | &self, 46 | engine: &impl MsmAccel, 47 | _: R, 48 | transcript: &mut T, 49 | queries: I, 50 | ) -> io::Result<()> 51 | where 52 | I: IntoIterator> + Clone, 53 | R: RngCore, 54 | { 55 | let v: ChallengeV<_> = transcript.squeeze_challenge_scalar(); 56 | let commitment_data = construct_intermediate_sets(queries).ok_or_else(|| { 57 | io::Error::new( 58 | io::ErrorKind::InvalidInput, 59 | "queries iterator contains mismatching evaluations", 60 | ) 61 | })?; 62 | 63 | for commitment_at_a_point in commitment_data.iter() { 64 | let z = commitment_at_a_point.point; 65 | let (poly_batch, eval_batch) = commitment_at_a_point 66 | .queries 67 | .iter() 68 | .zip(powers(*v)) 69 | .map(|(query, power_of_v)| { 70 | assert_eq!(query.get_point(), z); 71 | 72 | let poly = query.get_commitment().poly; 73 | let eval = query.get_eval(); 74 | 75 | (poly.clone() * power_of_v, eval * power_of_v) 76 | }) 77 | .reduce(|(poly_acc, eval_acc), (poly, eval)| (poly_acc + &poly, eval_acc + eval)) 78 | .unwrap(); 79 | 80 | let poly_batch = &poly_batch - eval_batch; 81 | let witness_poly = Polynomial { 82 | values: kate_division(&poly_batch.values, z), 83 | _marker: PhantomData, 84 | }; 85 | let w = self 86 | .params 87 | .commit(engine, &witness_poly, Blind::default()) 88 | .to_affine(); 89 | 90 | transcript.write_point(w)?; 91 | } 92 | Ok(()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /book/src/design/implementation/fields.md: -------------------------------------------------------------------------------- 1 | # Fields 2 | 3 | The [Pasta curves](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/) 4 | that we use in `halo2` are designed to be highly 2-adic, meaning that a large $2^S$ 5 | [multiplicative subgroup](../../background/fields.md#multiplicative-subgroups) exists in 6 | each field. That is, we can write $p - 1 \equiv 2^S \cdot T$ with $T$ odd. For both Pallas 7 | and Vesta, $S = 32$; this helps to simplify the field implementations. 8 | 9 | ## Sarkar square-root algorithm (table-based variant) 10 | 11 | We use a technique from [Sarkar2020](https://eprint.iacr.org/2020/1407.pdf) to compute 12 | [square roots](../../background/fields.md#square-roots) in `halo2`. The intuition behind 13 | the algorithm is that we can split the task into computing square roots in each 14 | multiplicative subgroup. 15 | 16 | Suppose we want to find the square root of $u$ modulo one of the Pasta primes $p$, where 17 | $u$ is a non-zero square in $\mathbb{Z}_p^\times$. We define a $2^S$ 18 | [root of unity](../../background/fields.md#roots-of-unity) $g = z^T$ where $z$ is a 19 | non-square in $\mathbb{Z}_p^\times$, and precompute the following tables: 20 | 21 | $$ 22 | gtab = \begin{bmatrix} 23 | g^0 & g^1 & ... & g^{2^8 - 1} \\ 24 | (g^{2^8})^0 & (g^{2^8})^1 & ... & (g^{2^8})^{2^8 - 1} \\ 25 | (g^{2^{16}})^0 & (g^{2^{16}})^1 & ... & (g^{2^{16}})^{2^8 - 1} \\ 26 | (g^{2^{24}})^0 & (g^{2^{24}})^1 & ... & (g^{2^{24}})^{2^8 - 1} 27 | \end{bmatrix} 28 | $$ 29 | 30 | $$ 31 | invtab = \begin{bmatrix} 32 | (g^{-2^{24}})^0 & (g^{-2^{24}})^1 & ... & (g^{-2^{24}})^{2^8 - 1} 33 | \end{bmatrix} 34 | $$ 35 | 36 | Let $v = u^{(T-1)/2}$. We can then define $x = uv \cdot v = u^T$ as an element of the 37 | $2^S$ multiplicative subgroup. 38 | 39 | Let $x_3 = x, x_2 = x_3^{2^8}, x_1 = x_2^{2^8}, x_0 = x_1^{2^8}.$ 40 | 41 | ### i = 0, 1 42 | Using $invtab$, we lookup $t_0$ such that 43 | $$ 44 | x_0 = (g^{-2^{24}})^{t_0} \implies x_0 \cdot g^{t_0 \cdot 2^{24}} = 1. 45 | $$ 46 | 47 | Define $\alpha_1 = x_1 \cdot (g^{2^{16}})^{t_0}.$ 48 | 49 | ### i = 2 50 | Lookup $t_1$ s.t. 51 | $$ 52 | \begin{array}{ll} 53 | \alpha_1 = (g^{-2^{24}})^{t_1} &\implies x_1 \cdot (g^{2^{16}})^{t_0} = (g^{-2^{24}})^{t_1} \\ 54 | &\implies 55 | x_1 \cdot g^{(t_0 + 2^8 \cdot t_1) \cdot 2^{16}} = 1. 56 | \end{array} 57 | $$ 58 | 59 | Define $\alpha_2 = x_2 \cdot (g^{2^8})^{t_0 + 2^8 \cdot t_1}.$ 60 | 61 | ### i = 3 62 | Lookup $t_2$ s.t. 63 | 64 | $$ 65 | \begin{array}{ll} 66 | \alpha_2 = (g^{-2^{24}})^{t_2} &\implies x_2 \cdot (g^{2^8})^{t_0 + 2^8\cdot {t_1}} = (g^{-2^{24}})^{t_2} \\ 67 | &\implies x_2 \cdot g^{(t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2) \cdot 2^8} = 1. 68 | \end{array} 69 | $$ 70 | 71 | Define $\alpha_3 = x_3 \cdot g^{t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2}.$ 72 | 73 | ### Final result 74 | Lookup $t_3$ such that 75 | 76 | $$ 77 | \begin{array}{ll} 78 | \alpha_3 = (g^{-2^{24}})^{t_3} &\implies x_3 \cdot g^{t_0 + 2^8\cdot {t_1} + 2^{16} \cdot t_2} = (g^{-2^{24}})^{t_3} \\ 79 | &\implies x_3 \cdot g^{t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2 + 2^{24} \cdot t_3} = 1. 80 | \end{array} 81 | $$ 82 | 83 | Let $t = t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2 + 2^{24} \cdot t_3$. 84 | 85 | We can now write 86 | $$ 87 | \begin{array}{lclcl} 88 | x_3 \cdot g^{t} = 1 &\implies& x_3 &=& g^{-t} \\ 89 | &\implies& uv^2 &=& g^{-t} \\ 90 | &\implies& uv &=& v^{-1} \cdot g^{-t} \\ 91 | &\implies& uv \cdot g^{t / 2} &=& v^{-1} \cdot g^{-t / 2}. 92 | \end{array} 93 | $$ 94 | 95 | Squaring the RHS, we observe that $(v^{-1} g^{-t / 2})^2 = v^{-2}g^{-t} = u.$ Therefore, 96 | the square root of $u$ is $uv \cdot g^{t / 2}$; the first part we computed earlier, and 97 | the second part can be computed with three multiplications using lookups in $gtab$. 98 | -------------------------------------------------------------------------------- /halo2_proofs/src/plonk/keygen.rs: -------------------------------------------------------------------------------- 1 | use crate::plonk::Error; 2 | use halo2_backend::plonk::{ 3 | keygen::{keygen_pk as backend_keygen_pk, keygen_vk as backend_keygen_vk}, 4 | ProvingKey, VerifyingKey, 5 | }; 6 | use halo2_backend::{arithmetic::CurveAffine, poly::commitment::Params}; 7 | use halo2_frontend::circuit::compile_circuit; 8 | use halo2_frontend::plonk::Circuit; 9 | use halo2_middleware::ff::FromUniformBytes; 10 | 11 | /// Generate a `VerifyingKey` from an instance of `Circuit`. 12 | /// By default, selector compression is turned **ON**. 13 | /// 14 | /// **NOTE**: This `keygen_vk` is legacy one, assuming that `compress_selector: true`. 15 | /// Hence, it is HIGHLY recommended to pair this util with `keygen_pk`. 16 | /// In addition, when using this for key generation, user MUST use `compress_selectors: true`. 17 | pub fn keygen_vk( 18 | params: &P, 19 | circuit: &ConcreteCircuit, 20 | ) -> Result, Error> 21 | where 22 | C: CurveAffine, 23 | P: Params, 24 | ConcreteCircuit: Circuit, 25 | C::Scalar: FromUniformBytes<64>, 26 | { 27 | keygen_vk_custom(params, circuit, true) 28 | } 29 | 30 | /// Generate a `VerifyingKey` from an instance of `Circuit`. 31 | /// 32 | /// The selector compression optimization is turned on only if `compress_selectors` is `true`. 33 | /// 34 | /// **NOTE**: This `keygen_vk_custom` MUST share the same `compress_selectors` with 35 | /// `ProvingKey` generation process. 36 | /// Otherwise, the user could get unmatching pk/vk pair. 37 | /// Hence, it is HIGHLY recommended to pair this util with `keygen_pk_custom`. 38 | pub fn keygen_vk_custom( 39 | params: &P, 40 | circuit: &ConcreteCircuit, 41 | compress_selectors: bool, 42 | ) -> Result, Error> 43 | where 44 | C: CurveAffine, 45 | P: Params, 46 | ConcreteCircuit: Circuit, 47 | C::Scalar: FromUniformBytes<64>, 48 | { 49 | let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit, compress_selectors)?; 50 | Ok(backend_keygen_vk(params, &compiled_circuit)?) 51 | } 52 | 53 | /// Generate a `ProvingKey` from a `VerifyingKey` and an instance of `Circuit`. 54 | /// By default, selector compression is turned **ON**. 55 | /// 56 | /// **NOTE**: This `keygen_pk` is legacy one, assuming that `compress_selector: true`. 57 | /// Hence, it is HIGHLY recommended to pair this util with `keygen_vk`. 58 | /// In addition, when using this for key generation, user MUST use `compress_selectors: true`. 59 | pub fn keygen_pk( 60 | params: &P, 61 | vk: VerifyingKey, 62 | circuit: &ConcreteCircuit, 63 | ) -> Result, Error> 64 | where 65 | C: CurveAffine, 66 | P: Params, 67 | ConcreteCircuit: Circuit, 68 | { 69 | keygen_pk_custom(params, vk, circuit, true) 70 | } 71 | 72 | /// Generate a `ProvingKey` from an instance of `Circuit`. 73 | /// 74 | /// The selector compression optimization is turned on only if `compress_selectors` is `true`. 75 | /// 76 | /// **NOTE**: This `keygen_pk_custom` MUST share the same `compress_selectors` with 77 | /// `VerifyingKey` generation process. 78 | /// Otherwise, the user could get unmatching pk/vk pair. 79 | /// Hence, it is HIGHLY recommended to pair this util with `keygen_vk_custom`. 80 | pub fn keygen_pk_custom( 81 | params: &P, 82 | vk: VerifyingKey, 83 | circuit: &ConcreteCircuit, 84 | compress_selectors: bool, 85 | ) -> Result, Error> 86 | where 87 | C: CurveAffine, 88 | P: Params, 89 | ConcreteCircuit: Circuit, 90 | { 91 | let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit, compress_selectors)?; 92 | Ok(backend_keygen_pk(params, vk, &compiled_circuit)?) 93 | } 94 | -------------------------------------------------------------------------------- /halo2_frontend/src/plonk/permutation.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of permutation argument. 2 | 3 | use crate::plonk::{Column, Error}; 4 | use halo2_middleware::circuit::{Any, Cell}; 5 | 6 | /// A permutation argument. 7 | #[derive(Default, Debug, Clone)] 8 | pub struct Argument { 9 | /// A sequence of columns involved in the argument. 10 | pub(crate) columns: Vec>, 11 | } 12 | 13 | impl Argument { 14 | /// Returns the minimum circuit degree required by the permutation argument. 15 | /// The argument may use larger degree gates depending on the actual 16 | /// circuit's degree and how many columns are involved in the permutation. 17 | pub(crate) fn required_degree(&self) -> usize { 18 | // degree 2: 19 | // l_0(X) * (1 - z(X)) = 0 20 | // 21 | // We will fit as many polynomials p_i(X) as possible 22 | // into the required degree of the circuit, so the 23 | // following will not affect the required degree of 24 | // this middleware. 25 | // 26 | // (1 - (l_last(X) + l_blind(X))) * ( 27 | // z(\omega X) \prod (p(X) + \beta s_i(X) + \gamma) 28 | // - z(X) \prod (p(X) + \delta^i \beta X + \gamma) 29 | // ) 30 | // 31 | // On the first sets of columns, except the first 32 | // set, we will do 33 | // 34 | // l_0(X) * (z(X) - z'(\omega^(last) X)) = 0 35 | // 36 | // where z'(X) is the permutation for the previous set 37 | // of columns. 38 | // 39 | // On the final set of columns, we will do 40 | // 41 | // degree 3: 42 | // l_last(X) * (z'(X)^2 - z'(X)) = 0 43 | // 44 | // which will allow the last value to be zero to 45 | // ensure the argument is perfectly complete. 46 | 47 | // There are constraints of degree 3 regardless of the 48 | // number of columns involved. 49 | 3 50 | } 51 | 52 | pub(crate) fn add_column(&mut self, column: Column) { 53 | if !self.columns.contains(&column) { 54 | self.columns.push(column); 55 | } 56 | } 57 | 58 | /// Returns columns that participate on the permutation argument. 59 | pub fn get_columns(&self) -> Vec> { 60 | self.columns.clone() 61 | } 62 | } 63 | 64 | #[derive(Clone, Debug)] 65 | pub(crate) struct Assembly { 66 | pub(crate) n: usize, 67 | pub(crate) columns: Vec>, 68 | pub(crate) copies: Vec<(Cell, Cell)>, 69 | } 70 | 71 | impl Assembly { 72 | pub(crate) fn new(n: usize, p: &Argument) -> Self { 73 | Self { 74 | n, 75 | columns: p.columns.clone(), 76 | copies: Vec::new(), 77 | } 78 | } 79 | 80 | pub(crate) fn copy( 81 | &mut self, 82 | left_column: Column, 83 | left_row: usize, 84 | right_column: Column, 85 | right_row: usize, 86 | ) -> Result<(), Error> { 87 | if !self.columns.contains(&left_column) { 88 | return Err(Error::ColumnNotInPermutation(left_column)); 89 | } 90 | if !self.columns.contains(&right_column) { 91 | return Err(Error::ColumnNotInPermutation(right_column)); 92 | } 93 | // Check bounds 94 | if left_row >= self.n || right_row >= self.n { 95 | return Err(Error::BoundsFailure); 96 | } 97 | self.copies.push(( 98 | Cell { 99 | column: left_column.into(), 100 | row: left_row, 101 | }, 102 | Cell { 103 | column: right_column.into(), 104 | row: right_row, 105 | }, 106 | )); 107 | Ok(()) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /halo2_backend/src/plonk/permutation.rs: -------------------------------------------------------------------------------- 1 | //! Verifying/Proving key of a permutation argument, with its serialization. 2 | 3 | use crate::helpers::{SerdeCurveAffine, SerdeFormat, SerdePrimeField}; 4 | use crate::{ 5 | arithmetic::CurveAffine, 6 | helpers::{polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice}, 7 | poly::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}, 8 | }; 9 | pub use halo2_middleware::permutation::ArgumentMid; 10 | 11 | use std::io; 12 | 13 | pub(crate) mod keygen; 14 | pub(crate) mod prover; 15 | pub(crate) mod verifier; 16 | 17 | /// The verifying key for a single permutation argument. 18 | #[derive(Clone, Debug)] 19 | pub(crate) struct VerifyingKey { 20 | commitments: Vec, 21 | } 22 | 23 | impl VerifyingKey { 24 | /// Returns commitments of sigma polynomials 25 | pub(crate) fn commitments(&self) -> &[C] { 26 | &self.commitments 27 | } 28 | 29 | pub(crate) fn write(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> 30 | where 31 | C: SerdeCurveAffine, 32 | { 33 | for commitment in &self.commitments { 34 | commitment.write(writer, format)?; 35 | } 36 | Ok(()) 37 | } 38 | 39 | pub(crate) fn read( 40 | reader: &mut R, 41 | argument: &ArgumentMid, 42 | format: SerdeFormat, 43 | ) -> io::Result 44 | where 45 | C: SerdeCurveAffine, 46 | { 47 | let commitments = (0..argument.columns.len()) 48 | .map(|_| C::read(reader, format)) 49 | .collect::, _>>()?; 50 | Ok(VerifyingKey { commitments }) 51 | } 52 | 53 | pub(crate) fn bytes_length(&self, format: SerdeFormat) -> usize 54 | where 55 | C: SerdeCurveAffine, 56 | { 57 | self.commitments.len() * C::byte_length(format) 58 | } 59 | } 60 | 61 | /// The proving key for a single permutation argument. 62 | #[derive(Clone, Debug)] 63 | pub(crate) struct ProvingKey { 64 | permutations: Vec>, 65 | polys: Vec>, 66 | pub(super) cosets: Vec>, 67 | } 68 | 69 | impl ProvingKey 70 | where 71 | C::Scalar: SerdePrimeField, 72 | { 73 | /// Reads proving key for a single permutation argument from buffer using `Polynomial::read`. 74 | pub(super) fn read(reader: &mut R, format: SerdeFormat) -> io::Result { 75 | let permutations = read_polynomial_vec(reader, format)?; 76 | let polys = read_polynomial_vec(reader, format)?; 77 | let cosets = read_polynomial_vec(reader, format)?; 78 | Ok(ProvingKey { 79 | permutations, 80 | polys, 81 | cosets, 82 | }) 83 | } 84 | 85 | /// Writes proving key for a single permutation argument to buffer using `Polynomial::write`. 86 | pub(super) fn write( 87 | &self, 88 | writer: &mut W, 89 | format: SerdeFormat, 90 | ) -> io::Result<()> { 91 | write_polynomial_slice(&self.permutations, writer, format)?; 92 | write_polynomial_slice(&self.polys, writer, format)?; 93 | write_polynomial_slice(&self.cosets, writer, format)?; 94 | Ok(()) 95 | } 96 | } 97 | 98 | impl ProvingKey { 99 | /// Gets the total number of bytes in the serialization of `self` 100 | pub(super) fn bytes_length(&self) -> usize { 101 | polynomial_slice_byte_length(&self.permutations) 102 | + polynomial_slice_byte_length(&self.polys) 103 | + polynomial_slice_byte_length(&self.cosets) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /p3_frontend/src/symbolic_builder.rs: -------------------------------------------------------------------------------- 1 | //! `SymbolicAirBuilder` copied from plonky3 and adapted for the Air to Plonkish usecase, at commit 2 | //! `7b5b8a69f633bc61c530f3722701e5f701b11963`. 3 | 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2022 The Plonky3 Authors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | use alloc::vec; 27 | use alloc::vec::Vec; 28 | 29 | use p3_air::AirBuilder; 30 | use p3_field::Field; 31 | use p3_matrix::dense::RowMajorMatrix; 32 | 33 | use crate::air::AirBuilderWithPublicValues; 34 | use crate::symbolic_expression::{Location, SymbolicExpression}; 35 | use crate::symbolic_variable::SymbolicVariable; 36 | 37 | /// An `AirBuilder` for evaluating constraints symbolically, and recording them for later use. 38 | pub struct SymbolicAirBuilder { 39 | pub(crate) main: RowMajorMatrix>, 40 | pub(crate) public_values: Vec>, 41 | pub(crate) constraints: Vec>, 42 | } 43 | 44 | impl SymbolicAirBuilder { 45 | pub(crate) fn new(width: usize, num_public_values: usize) -> Self { 46 | let values = [false, true] 47 | .into_iter() 48 | .flat_map(|is_next| { 49 | (0..width).map(move |column| SymbolicVariable::new_query(is_next, column)) 50 | }) 51 | .collect(); 52 | Self { 53 | main: RowMajorMatrix::new(values, width), 54 | public_values: (0..num_public_values) 55 | .map(|i| SymbolicVariable::new_public(i)) 56 | .collect(), 57 | constraints: vec![], 58 | } 59 | } 60 | } 61 | 62 | impl AirBuilder for SymbolicAirBuilder { 63 | type F = F; 64 | type Expr = SymbolicExpression; 65 | type Var = SymbolicVariable; 66 | type M = RowMajorMatrix; 67 | 68 | fn main(&self) -> Self::M { 69 | self.main.clone() 70 | } 71 | 72 | fn is_first_row(&self) -> Self::Expr { 73 | SymbolicExpression::Location(Location::FirstRow) 74 | } 75 | 76 | fn is_last_row(&self) -> Self::Expr { 77 | SymbolicExpression::Location(Location::LastRow) 78 | } 79 | 80 | // TODO: Figure out what's a window size > 2. 81 | fn is_transition_window(&self, size: usize) -> Self::Expr { 82 | if size == 2 { 83 | SymbolicExpression::Location(Location::Transition) 84 | } else { 85 | panic!("uni-stark only supports a window size of 2") 86 | } 87 | } 88 | 89 | fn assert_zero>(&mut self, x: I) { 90 | self.constraints.push(x.into()); 91 | } 92 | } 93 | 94 | impl AirBuilderWithPublicValues for SymbolicAirBuilder { 95 | fn public_values(&self) -> &[Self::Var] { 96 | self.public_values.as_slice() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /book/src/design/proving-system/multipoint-opening.md: -------------------------------------------------------------------------------- 1 | # Multipoint opening argument 2 | 3 | Consider the commitments $A, B, C, D$ to polynomials $a(X), b(X), c(X), d(X)$. 4 | Let's say that $a$ and $b$ were queried at the point $x$, while $c$ and $d$ 5 | were queried at both points $x$ and $\omega x$. (Here, $\omega$ is the primitive 6 | root of unity in the multiplicative subgroup over which we constructed the 7 | polynomials). 8 | 9 | To open these commitments, we could create a polynomial $Q$ for each point that we queried 10 | at (corresponding to each relative rotation used in the circuit). But this would not be 11 | efficient in the circuit; for example, $c(X)$ would appear in multiple polynomials. 12 | 13 | Instead, we can group the commitments by the sets of points at which they were queried: 14 | $$ 15 | \begin{array}{cccc} 16 | &\{x\}& &\{x, \omega x\}& \\ 17 | &A& &C& \\ 18 | &B& &D& 19 | \end{array} 20 | $$ 21 | 22 | For each of these groups, we combine them into a polynomial set, and create a single $Q$ 23 | for that set, which we open at each rotation. 24 | 25 | ## Optimization steps 26 | 27 | The multipoint opening optimization takes as input: 28 | 29 | - A random $x$ sampled by the verifier, at which we evaluate $a(X), b(X), c(X), d(X)$. 30 | - Evaluations of each polynomial at each point of interest, provided by the prover: 31 | $a(x), b(x), c(x), d(x), c(\omega x), d(\omega x)$ 32 | 33 | These are the outputs of the [vanishing argument](vanishing.md#evaluating-the-polynomials). 34 | 35 | The multipoint opening optimization proceeds as such: 36 | 37 | 1. Sample random $x_1$, to keep $a, b, c, d$ linearly independent. 38 | 2. Accumulate polynomials and their corresponding evaluations according 39 | to the point set at which they were queried: 40 | `q_polys`: 41 | $$ 42 | \begin{array}{rccl} 43 | q_1(X) &=& a(X) &+& x_1 b(X) \\ 44 | q_2(X) &=& c(X) &+& x_1 d(X) 45 | \end{array} 46 | $$ 47 | `q_eval_sets`: 48 | ```math 49 | [ 50 | [a(x) + x_1 b(x)], 51 | [ 52 | c(x) + x_1 d(x), 53 | c(\omega x) + x_1 d(\omega x) 54 | ] 55 | ] 56 | ``` 57 | NB: `q_eval_sets` is a vector of sets of evaluations, where the outer vector 58 | corresponds to the point sets, which in this example are $\{x\}$ and $\{x, \omega x\}$, 59 | and the inner vector corresponds to the points in each set. 60 | 3. Interpolate each set of values in `q_eval_sets`: 61 | `r_polys`: 62 | $$ 63 | \begin{array}{cccc} 64 | r_1(X) s.t.&&& \\ 65 | &r_1(x) &=& a(x) + x_1 b(x) \\ 66 | r_2(X) s.t.&&& \\ 67 | &r_2(x) &=& c(x) + x_1 d(x) \\ 68 | &r_2(\omega x) &=& c(\omega x) + x_1 d(\omega x) \\ 69 | \end{array} 70 | $$ 71 | 4. Construct `f_polys` which check the correctness of `q_polys`: 72 | `f_polys` 73 | $$ 74 | \begin{array}{rcl} 75 | f_1(X) &=& \frac{ q_1(X) - r_1(X)}{X - x} \\ 76 | f_2(X) &=& \frac{ q_2(X) - r_2(X)}{(X - x)(X - \omega x)} \\ 77 | \end{array} 78 | $$ 79 | 80 | If $q_1(x) = r_1(x)$, then $f_1(X)$ should be a polynomial. 81 | If $q_2(x) = r_2(x)$ and $q_2(\omega x) = r_2(\omega x)$ 82 | then $f_2(X)$ should be a polynomial. 83 | 5. Sample random $x_2$ to keep the `f_polys` linearly independent. 84 | 6. Construct $f(X) = f_1(X) + x_2 f_2(X)$. 85 | 7. Sample random $x_3$, at which we evaluate $f(X)$: 86 | $$ 87 | \begin{array}{rcccl} 88 | f(x_3) &=& f_1(x_3) &+& x_2 f_2(x_3) \\ 89 | &=& \frac{q_1(x_3) - r_1(x_3)}{x_3 - x} &+& x_2\frac{q_2(x_3) - r_2(x_3)}{(x_3 - x)(x_3 - \omega x)} 90 | \end{array} 91 | $$ 92 | 8. Sample random $x_4$ to keep $f(X)$ and `q_polys` linearly independent. 93 | 9. Construct `final_poly`, $$final\_poly(X) = f(X) + x_4 q_1(X) + x_4^2 q_2(X),$$ 94 | which is the polynomial we commit to in the inner product argument. 95 | -------------------------------------------------------------------------------- /halo2_proofs/benches/dev_lookup.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use ff::{Field, PrimeField}; 5 | use halo2_proofs::circuit::{Layouter, SimpleFloorPlanner, Value}; 6 | use halo2_proofs::dev::MockProver; 7 | use halo2_proofs::plonk::*; 8 | use halo2_proofs::poly::Rotation; 9 | use halo2curves::pasta::pallas; 10 | 11 | use std::marker::PhantomData; 12 | 13 | use criterion::{BenchmarkId, Criterion}; 14 | 15 | fn criterion_benchmark(c: &mut Criterion) { 16 | #[derive(Clone, Default)] 17 | struct MyCircuit { 18 | _marker: PhantomData, 19 | } 20 | 21 | #[derive(Clone)] 22 | struct MyConfig { 23 | selector: Selector, 24 | table: TableColumn, 25 | advice: Column, 26 | } 27 | 28 | impl Circuit for MyCircuit { 29 | type Config = MyConfig; 30 | type FloorPlanner = SimpleFloorPlanner; 31 | #[cfg(feature = "circuit-params")] 32 | type Params = (); 33 | 34 | fn without_witnesses(&self) -> Self { 35 | Self::default() 36 | } 37 | 38 | fn configure(meta: &mut ConstraintSystem) -> MyConfig { 39 | let config = MyConfig { 40 | selector: meta.complex_selector(), 41 | table: meta.lookup_table_column(), 42 | advice: meta.advice_column(), 43 | }; 44 | 45 | meta.lookup("lookup", |meta| { 46 | let selector = meta.query_selector(config.selector); 47 | let not_selector = Expression::Constant(F::ONE) - selector.clone(); 48 | let advice = meta.query_advice(config.advice, Rotation::cur()); 49 | vec![(selector * advice + not_selector, config.table)] 50 | }); 51 | 52 | config 53 | } 54 | 55 | fn synthesize( 56 | &self, 57 | config: MyConfig, 58 | mut layouter: impl Layouter, 59 | ) -> Result<(), ErrorFront> { 60 | layouter.assign_table( 61 | || "8-bit table", 62 | |mut table| { 63 | for row in 0u64..(1 << 8) { 64 | table.assign_cell( 65 | || format!("row {row}"), 66 | config.table, 67 | row as usize, 68 | || Value::known(F::from(row + 1)), 69 | )?; 70 | } 71 | 72 | Ok(()) 73 | }, 74 | )?; 75 | 76 | layouter.assign_region( 77 | || "assign values", 78 | |mut region| { 79 | for offset in 0u64..(1 << 10) { 80 | config.selector.enable(&mut region, offset as usize)?; 81 | region.assign_advice( 82 | || format!("offset {offset}"), 83 | config.advice, 84 | offset as usize, 85 | || Value::known(F::from((offset % 256) + 1)), 86 | )?; 87 | } 88 | 89 | Ok(()) 90 | }, 91 | ) 92 | } 93 | } 94 | 95 | fn prover(k: u32) { 96 | let circuit = MyCircuit:: { 97 | _marker: PhantomData, 98 | }; 99 | let prover = MockProver::run(k, &circuit, vec![]).unwrap(); 100 | assert_eq!(prover.verify(), Ok(())) 101 | } 102 | 103 | let k_range = 14..=18; 104 | 105 | let mut prover_group = c.benchmark_group("dev-lookup"); 106 | prover_group.sample_size(10); 107 | for k in k_range { 108 | prover_group.bench_with_input(BenchmarkId::from_parameter(k), &k, |b, &k| { 109 | b.iter(|| prover(k)); 110 | }); 111 | } 112 | prover_group.finish(); 113 | } 114 | 115 | criterion_group!(benches, criterion_benchmark); 116 | criterion_main!(benches); 117 | -------------------------------------------------------------------------------- /halo2_frontend/src/plonk/lookup.rs: -------------------------------------------------------------------------------- 1 | use crate::plonk::Expression; 2 | use halo2_middleware::ff::Field; 3 | use std::fmt::{self, Debug}; 4 | 5 | /// Expressions involved in a lookup argument, with a name as metadata. 6 | #[derive(Clone)] 7 | pub struct Argument { 8 | pub(crate) name: String, 9 | pub(crate) input_expressions: Vec>, 10 | pub(crate) table_expressions: Vec>, 11 | } 12 | 13 | impl Debug for Argument { 14 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 15 | f.debug_struct("Argument") 16 | .field("input_expressions", &self.input_expressions) 17 | .field("table_expressions", &self.table_expressions) 18 | .finish() 19 | } 20 | } 21 | 22 | impl Argument { 23 | /// Constructs a new lookup argument. 24 | /// 25 | /// `table_map` is a sequence of `(input, table)` tuples. 26 | pub fn new>(name: S, table_map: Vec<(Expression, Expression)>) -> Self { 27 | let (input_expressions, table_expressions) = table_map.into_iter().unzip(); 28 | Argument { 29 | name: name.as_ref().to_string(), 30 | input_expressions, 31 | table_expressions, 32 | } 33 | } 34 | 35 | pub(crate) fn required_degree(&self) -> usize { 36 | assert_eq!(self.input_expressions.len(), self.table_expressions.len()); 37 | 38 | // The first value in the permutation poly should be one. 39 | // degree 2: 40 | // l_0(X) * (1 - z(X)) = 0 41 | // 42 | // The "last" value in the permutation poly should be a boolean, for 43 | // completeness and soundness. 44 | // degree 3: 45 | // l_last(X) * (z(X)^2 - z(X)) = 0 46 | // 47 | // Enable the permutation argument for only the rows involved. 48 | // degree (2 + input_degree + table_degree) or 4, whichever is larger: 49 | // (1 - (l_last(X) + l_blind(X))) * ( 50 | // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma) 51 | // - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) 52 | // ) = 0 53 | // 54 | // The first two values of a' and s' should be the same. 55 | // degree 2: 56 | // l_0(X) * (a'(X) - s'(X)) = 0 57 | // 58 | // Either the two values are the same, or the previous 59 | // value of a' is the same as the current value. 60 | // degree 3: 61 | // (1 - (l_last(X) + l_blind(X))) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0 62 | let mut input_degree = 1; 63 | for expr in self.input_expressions.iter() { 64 | input_degree = std::cmp::max(input_degree, expr.degree()); 65 | } 66 | let mut table_degree = 1; 67 | for expr in self.table_expressions.iter() { 68 | table_degree = std::cmp::max(table_degree, expr.degree()); 69 | } 70 | 71 | // In practice because input_degree and table_degree are initialized to 72 | // one, the latter half of this max() invocation is at least 4 always, 73 | // rendering this call pointless except to be explicit in case we change 74 | // the initialization of input_degree/table_degree in the future. 75 | std::cmp::max( 76 | // (1 - (l_last + l_blind)) z(\omega X) (a'(X) + \beta) (s'(X) + \gamma) 77 | 4, 78 | // (1 - (l_last + l_blind)) z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) 79 | 2 + input_degree + table_degree, 80 | ) 81 | } 82 | 83 | /// Returns input of this argument 84 | pub fn input_expressions(&self) -> &Vec> { 85 | &self.input_expressions 86 | } 87 | 88 | /// Returns table of this argument 89 | pub fn table_expressions(&self) -> &Vec> { 90 | &self.table_expressions 91 | } 92 | 93 | /// Returns name of this argument 94 | pub fn name(&self) -> &str { 95 | &self.name 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /book/src/design/gadgets/decomposition.md: -------------------------------------------------------------------------------- 1 | # Decomposition 2 | Given a field element $\alpha$, these gadgets decompose it into $W$ $K$-bit windows $$\alpha = k_0 + 2^{K} \cdot k_1 + 2^{2K} \cdot k_2 + \cdots + 2^{(W-1)K} \cdot k_{W-1}$$ where each $k_i$ a $K$-bit value. 3 | 4 | This is done using a running sum $z_i, i \in [0..W).$ We initialize the running sum $z_0 = \alpha,$ and compute subsequent terms $z_{i+1} = \frac{z_i - k_i}{2^{K}}.$ This gives us: 5 | 6 | $$ 7 | \begin{aligned} 8 | z_0 &= \alpha \\ 9 | &= k_0 + 2^{K} \cdot k_1 + 2^{2K} \cdot k_2 + 2^{3K} \cdot k_3 + \cdots, \\ 10 | z_1 &= (z_0 - k_0) / 2^K \\ 11 | &= k_1 + 2^{K} \cdot k_2 + 2^{2K} \cdot k_3 + \cdots, \\ 12 | z_2 &= (z_1 - k_1) / 2^K \\ 13 | &= k_2 + 2^{K} \cdot k_3 + \cdots, \\ 14 | &\vdots \\ 15 | \downarrow &\text{ (in strict mode)} \\ 16 | z_W &= (z_{W-1} - k_{W-1}) / 2^K \\ 17 | &= 0 \text{ (because } z_{W-1} = k_{W-1} \text{)} 18 | \end{aligned} 19 | $$ 20 | 21 | ### Strict mode 22 | Strict mode constrains the running sum output $z_{W}$ to be zero, thus range-constraining the field element to be within $W \cdot K$ bits. 23 | 24 | In strict mode, we are also assured that $z_{W-1} = k_{W-1}$ gives us the last window in the decomposition. 25 | ## Lookup decomposition 26 | This gadget makes use of a $K$-bit lookup table to decompose a field element $\alpha$ into $K$-bit words. Each $K$-bit word $k_i = z_i - 2^K \cdot z_{i+1}$ is range-constrained by a lookup in the $K$-bit table. 27 | 28 | The region layout for the lookup decomposition uses a single advice column $z$, and two selectors $q_{lookup}$ and $q_{running}.$ 29 | $$ 30 | \begin{array}{|c|c|c|} 31 | \hline 32 | z & q_\mathit{lookup} & q_\mathit{running} \\\hline 33 | \hline 34 | z_0 & 1 & 1 \\\hline 35 | z_1 & 1 & 1 \\\hline 36 | \vdots & \vdots & \vdots \\\hline 37 | z_{n-1} & 1 & 1 \\\hline 38 | z_n & 0 & 0 \\\hline 39 | \end{array} 40 | $$ 41 | ### Short range check 42 | Using two $K$-bit lookups, we can range-constrain a field element $\alpha$ to be $n$ bits, where $n \leq K.$ To do this: 43 | 44 | 1. Constrain $0 \leq \alpha < 2^K$ to be within $K$ bits using a $K$-bit lookup. 45 | 2. Constrain $0 \leq \alpha \cdot 2^{K - n} < 2^K$ to be within $K$ bits using a $K$-bit lookup. 46 | 47 | The short variant of the lookup decomposition introduces a $q_{bitshift}$ selector. The same advice column $z$ has here been renamed to $\textsf{word}$ for clarity: 48 | $$ 49 | \begin{array}{|c|c|c|c|} 50 | \hline 51 | \textsf{word} & q_\mathit{lookup} & q_\mathit{running} & q_\mathit{bitshift} \\\hline 52 | \hline 53 | \alpha & 1 & 0 & 0 \\\hline 54 | \alpha' & 1 & 0 & 1 \\\hline 55 | 2^{K-n} & 0 & 0 & 0 \\\hline 56 | \end{array} 57 | $$ 58 | 59 | where $\alpha' = \alpha \cdot 2^{K - n}.$ Note that $2^{K-n}$ is assigned to a fixed column at keygen, and copied in at proving time. This is used in the gate enabled by the $q_\mathit{bitshift}$ selector to check that $\alpha$ was shifted correctly: 60 | $$ 61 | \begin{array}{|c|l|} 62 | \hline 63 | \text{Degree} & \text{Constraint} \\\hline 64 | 2 & q_\mathit{bitshift} \cdot ((\alpha \cdot 2^{K - n}) - \alpha') \\\hline 65 | \end{array} 66 | $$ 67 | 68 | ### Combined lookup expression 69 | Since the lookup decomposition and its short variant both make use of the same lookup table, we combine their lookup input expressions into a single one: 70 | 71 | $$q_\mathit{lookup} \cdot \left(q_\mathit{running} \cdot (z_i - 2^K \cdot z_{i+1}) + (1 - q_\mathit{running}) \cdot \textsf{word} \right)$$ 72 | 73 | where $z_i$ and $\textsf{word}$ are the same cell (but distinguished here for clarity of usage). 74 | 75 | ## Short range decomposition 76 | For a short range (for instance, $[0, \texttt{range})$ where $\texttt{range} \leq 8$), we can range-constrain each word using a degree-$\texttt{range}$ polynomial constraint instead of a lookup: $$\RangeCheck{word}{range} = \texttt{word} \cdot (1 - \texttt{word}) \cdots (\texttt{range} - 1 - \texttt{word}).$$ 77 | -------------------------------------------------------------------------------- /book/src/design/implementation/proofs.md: -------------------------------------------------------------------------------- 1 | # Halo 2 proofs 2 | 3 | ## Proofs as opaque byte streams 4 | 5 | In proving system implementations like `bellman`, there is a concrete `Proof` struct that 6 | encapsulates the proof data, is returned by a prover, and can be passed to a verifier. 7 | 8 | `halo2` does not contain any proof-like structures, for several reasons: 9 | 10 | - The Proof structures would contain vectors of (vectors of) curve points and scalars. 11 | This complicates serialization/deserialization of proofs because the lengths of these 12 | vectors depend on the configuration of the circuit. However, we didn't want to encode 13 | the lengths of vectors inside of proofs, because at runtime the circuit is fixed, and 14 | thus so are the proof sizes. 15 | - It's easy to accidentally put stuff into a Proof structure that isn't also placed in the 16 | transcript, which is a hazard when developing and implementing a proving system. 17 | - We needed to be able to create multiple PLONK proofs at the same time; these proofs 18 | share many different substructures when they are for the same circuit. 19 | 20 | Instead, `halo2` treats proof objects as opaque byte streams. Creation and consumption of 21 | these byte streams happens via the transcript: 22 | 23 | - The `TranscriptWrite` trait represents something that we can write proof components to 24 | (at proving time). 25 | - The `TranscriptRead` trait represents something that we can read proof components from 26 | (at verifying time). 27 | 28 | Crucially, implementations of `TranscriptWrite` are responsible for simultaneously writing 29 | to some `std::io::Write` buffer at the same time that they hash things into the transcript, 30 | and similarly for `TranscriptRead`/`std::io::Read`. 31 | 32 | As a bonus, treating proofs as opaque byte streams ensures that verification accounts for 33 | the cost of deserialization, which isn't negligible due to point compression. 34 | 35 | ## Proof encoding 36 | 37 | A Halo 2 proof, constructed over a curve $E(\mathbb{F}_p)$, is encoded as a stream of: 38 | 39 | - Points $P \in E(\mathbb{F}_p)$ (for commitments to polynomials), and 40 | - Scalars $s \in \mathbb{F}_q$ (for evaluations of polynomials, and blinding values). 41 | 42 | For the Pallas and Vesta curves, both points and scalars have 32-byte encodings, meaning 43 | that proofs are always a multiple of 32 bytes. 44 | 45 | The `halo2` crate supports proving multiple instances of a circuit simultaneously, in 46 | order to share common proof components and protocol logic. 47 | 48 | In the encoding description below, we will use the following circuit-specific constants: 49 | 50 | - $k$ - the size parameter of the circuit (which has $2^k$ rows). 51 | - $A$ - the number of advice columns. 52 | - $F$ - the number of fixed columns. 53 | - $I$ - the number of instance columns. 54 | - $L$ - the number of lookup arguments. 55 | - $P$ - the number of permutation arguments. 56 | - $\textsf{Col}_P$ - the number of columns involved in permutation argument $P$. 57 | - $D$ - the maximum degree for the quotient polynomial. 58 | - $Q_A$ - the number of advice column queries. 59 | - $Q_F$ - the number of fixed column queries. 60 | - $Q_I$ - the number of instance column queries. 61 | - $M$ - the number of instances of the circuit that are being proven simultaneously. 62 | 63 | As the proof encoding directly follows the transcript, we can break the encoding into 64 | sections matching the Halo 2 protocol: 65 | 66 | - PLONK commitments: 67 | - $A$ points (repeated $M$ times). 68 | - $2L$ points (repeated $M$ times). 69 | - $P$ points (repeated $M$ times). 70 | - $L$ points (repeated $M$ times). 71 | 72 | - Vanishing argument: 73 | - $D - 1$ points. 74 | - $Q_I$ scalars (repeated $M$ times). 75 | - $Q_A$ scalars (repeated $M$ times). 76 | - $Q_F$ scalars. 77 | - $D - 1$ scalars. 78 | 79 | - PLONK evaluations: 80 | - $(2 + \textsf{Col}_P) \times P$ scalars (repeated $M$ times). 81 | - $5L$ scalars (repeated $M$ times). 82 | 83 | - Multiopening argument: 84 | - 1 point. 85 | - 1 scalar per set of points in the multiopening argument. 86 | 87 | - Polynomial commitment scheme: 88 | - $1 + 2k$ points. 89 | - $2$ scalars. 90 | -------------------------------------------------------------------------------- /halo2_proofs/src/plonk.rs: -------------------------------------------------------------------------------- 1 | //! This module provides an implementation of a variant of (Turbo)[PLONK][plonk] 2 | //! that is designed specifically for the polynomial commitment scheme described 3 | //! in the [Halo][halo] paper. 4 | //! 5 | //! [halo]: https://eprint.iacr.org/2019/1021 6 | //! [plonk]: https://eprint.iacr.org/2019/953 7 | 8 | mod error; 9 | mod keygen; 10 | mod prover; 11 | mod verifier { 12 | pub use halo2_backend::plonk::verifier::verify_proof_multi; 13 | } 14 | 15 | use halo2_frontend::circuit::compile_circuit; 16 | pub use keygen::{keygen_pk, keygen_pk_custom, keygen_vk, keygen_vk_custom}; 17 | 18 | pub use prover::{create_proof, create_proof_with_engine}; 19 | pub use verifier::verify_proof_multi; 20 | 21 | pub use error::Error; 22 | pub use halo2_backend::plonk::{Error as ErrorBack, ProvingKey, VerifyingKey}; 23 | pub use halo2_frontend::plonk::{ 24 | Advice, Assigned, Assignment, Challenge, Circuit, Column, ColumnType, ConstraintSystem, 25 | Constraints, Error as ErrorFront, Expression, FirstPhase, Fixed, FixedQuery, FloorPlanner, 26 | Instance, Phase, SecondPhase, Selector, TableColumn, ThirdPhase, VirtualCells, 27 | }; 28 | pub use halo2_middleware::circuit::{Any, ConstraintSystemMid}; 29 | 30 | use group::ff::FromUniformBytes; 31 | use halo2_backend::helpers::{SerdeCurveAffine, SerdeFormat, SerdePrimeField}; 32 | use std::io; 33 | 34 | /// Reads a verification key from a buffer. 35 | /// 36 | /// Reads a curve element from the buffer and parses it according to the `format`: 37 | /// - `Processed`: Reads a compressed curve element and decompresses it. 38 | /// Reads a field element in standard form, with endianness specified by the 39 | /// `PrimeField` implementation, and checks that the element is less than the modulus. 40 | /// - `RawBytes`: Reads an uncompressed curve element with coordinates in Montgomery form. 41 | /// Checks that field elements are less than modulus, and then checks that the point is on the curve. 42 | /// - `RawBytesUnchecked`: Reads an uncompressed curve element with coordinates in Montgomery form; 43 | /// does not perform any checks 44 | pub fn vk_read>( 45 | reader: &mut R, 46 | format: SerdeFormat, 47 | k: u32, 48 | circuit: &ConcreteCircuit, 49 | compress_selectors: bool, 50 | ) -> io::Result> 51 | where 52 | C::Scalar: SerdePrimeField + FromUniformBytes<64>, 53 | { 54 | let (_, _, cs) = compile_circuit(k, circuit, compress_selectors) 55 | .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; 56 | let cs_mid: ConstraintSystemMid<_> = cs.into(); 57 | VerifyingKey::read(reader, format, cs_mid.into()) 58 | } 59 | 60 | /// Reads a proving key from a buffer. 61 | /// Does so by reading verification key first, and then deserializing the rest of the file into the 62 | /// remaining proving key data. 63 | /// 64 | /// Reads a curve element from the buffer and parses it according to the `format`: 65 | /// - `Processed`: Reads a compressed curve element and decompresses it. 66 | /// Reads a field element in standard form, with endianness specified by the 67 | /// `PrimeField` implementation, and checks that the element is less than the modulus. 68 | /// - `RawBytes`: Reads an uncompressed curve element with coordinates in Montgomery form. 69 | /// Checks that field elements are less than modulus, and then checks that the point is on the curve. 70 | /// - `RawBytesUnchecked`: Reads an uncompressed curve element with coordinates in Montgomery form; 71 | /// does not perform any checks 72 | pub fn pk_read>( 73 | reader: &mut R, 74 | format: SerdeFormat, 75 | k: u32, 76 | circuit: &ConcreteCircuit, 77 | compress_selectors: bool, 78 | ) -> io::Result> 79 | where 80 | C::Scalar: SerdePrimeField + FromUniformBytes<64>, 81 | { 82 | let (_, _, cs) = compile_circuit(k, circuit, compress_selectors) 83 | .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; 84 | let cs_mid: ConstraintSystemMid<_> = cs.into(); 85 | ProvingKey::read(reader, format, cs_mid.into()) 86 | } 87 | -------------------------------------------------------------------------------- /book/src/design/proving-system/vanishing.md: -------------------------------------------------------------------------------- 1 | # Vanishing argument 2 | 3 | Having committed to the circuit assignments, the prover now needs to demonstrate that the 4 | various circuit relations are satisfied: 5 | 6 | - The custom gates, represented by polynomials $\text{gate}_i(X)$. 7 | - The rules of the lookup arguments. 8 | - The rules of the equality constraint permutations. 9 | 10 | Each of these relations is represented as a polynomial of degree $d$ (the maximum degree 11 | of any of the relations) with respect to the circuit columns. Given that the degree of the 12 | assignment polynomials for each column is $n - 1$, the relation polynomials have degree 13 | $d(n - 1)$ with respect to $X$. 14 | 15 | > In our [example](../proving-system.md#example), these would be the gate polynomials, of 16 | > degree $3n - 3$: 17 | > 18 | > - $\text{gate}_0(X) = a_0(X) \cdot a_1(X) \cdot a_2(X \omega^{-1}) - a_3(X)$ 19 | > - $\text{gate}_1(X) = f_0(X \omega^{-1}) \cdot a_2(X)$ 20 | > - $\text{gate}_2(X) = f_0(X) \cdot a_3(X) \cdot a_0(X)$ 21 | 22 | A relation is satisfied if its polynomial is equal to zero. One way to demonstrate this is 23 | to divide each polynomial relation by the vanishing polynomial $t(X) = (X^n - 1)$, which 24 | is the lowest-degree monomial that has roots at every $\omega^i$. If relation's polynomial 25 | is perfectly divisible by $t(X)$, it is equal to zero over the domain (as desired). 26 | 27 | This simple construction would require a polynomial commitment per relation. Instead, we 28 | commit to all of the circuit relations simultaneously: the verifier samples $y$, and then 29 | the prover constructs the quotient polynomial 30 | 31 | $$h(X) = \frac{\text{gate}_0(X) + y \cdot \text{gate}_1(X) + \dots + y^i \cdot \text{gate}_i(X) + \dots}{t(X)},$$ 32 | 33 | where the numerator is a random (the prover commits to the cell assignments before the 34 | verifier samples $y$) linear combination of the circuit relations. 35 | 36 | - If the numerator polynomial (in formal indeterminate $X$) is perfectly divisible by 37 | $t(X)$, then with high probability all relations are satisfied. 38 | - Conversely, if at least one relation is not satisfied, then with high probability 39 | $h(x) \cdot t(x)$ will not equal the evaluation of the numerator at $x$. In this case, 40 | the numerator polynomial would not be perfectly divisible by $t(X)$. 41 | 42 | ## Committing to $h(X)$ 43 | 44 | $h(X)$ has degree $d(n - 1) - n$ (because the divisor $t(X)$ has degree $n$). However, the 45 | polynomial commitment scheme we use for Halo 2 only supports committing to polynomials of 46 | degree $n - 1$ (which is the maximum degree that the rest of the protocol needs to commit 47 | to). Instead of increasing the cost of the polynomial commitment scheme, the prover split 48 | $h(X)$ into pieces of degree $n - 1$ 49 | 50 | $$h_0(X) + X^n h_1(X) + \dots + X^{n(d-1)} h_{d-1}(X),$$ 51 | 52 | and produces blinding commitments to each piece 53 | 54 | $$\mathbf{H} = [\text{Commit}(h_0(X)), \text{Commit}(h_1(X)), \dots, \text{Commit}(h_{d-1}(X))].$$ 55 | 56 | ## Evaluating the polynomials 57 | 58 | At this point, all properties of the circuit have been committed to. The verifier now 59 | wants to see if the prover committed to the correct $h(X)$ polynomial. The verifier 60 | samples $x$, and the prover produces the purported evaluations of the various polynomials 61 | at $x$, for all the relative offsets used in the circuit, as well as $h(X)$. 62 | 63 | > In our [example](../proving-system.md#example), this would be: 64 | > 65 | > - $a_0(x)$ 66 | > - $a_1(x)$ 67 | > - $a_2(x)$, $a_2(x \omega^{-1})$ 68 | > - $a_3(x)$ 69 | > - $f_0(x)$, $f_0(x \omega^{-1})$ 70 | > - $h_0(x)$, ..., $h_{d-1}(x)$ 71 | 72 | The verifier checks that these evaluations satisfy the form of $h(X)$: 73 | 74 | $$\frac{\text{gate}_0(x) + \dots + y^i \cdot \text{gate}_i(x) + \dots}{t(x)} = h_0(x) + \dots + x^{n(d-1)} h_{d-1}(x)$$ 75 | 76 | Now content that the evaluations collectively satisfy the gate constraints, the verifier 77 | needs to check that the evaluations themselves are consistent with the original 78 | [circuit commitments](circuit-commitments.md), as well as $\mathbf{H}$. To implement this 79 | efficiently, we use a [multipoint opening argument](multipoint-opening.md). 80 | -------------------------------------------------------------------------------- /p3_frontend/tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2_backend::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; 2 | use halo2_backend::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; 3 | use halo2_backend::poly::kzg::strategy::SingleStrategy; 4 | use halo2_backend::{ 5 | plonk::{ 6 | keygen::{keygen_pk, keygen_vk}, 7 | prover::Prover, 8 | verifier::verify_proof, 9 | }, 10 | transcript::{ 11 | Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, 12 | }, 13 | }; 14 | use halo2_debug::check_witness; 15 | use halo2_debug::test_rng; 16 | use halo2_middleware::circuit::CompiledCircuit; 17 | use halo2_middleware::zal::impls::H2cEngine; 18 | use halo2curves::bn256::{Bn256, Fr, G1Affine}; 19 | use p3_air::Air; 20 | use p3_frontend::{ 21 | compile_circuit_cs, compile_preprocessing, get_public_inputs, trace_to_wit, CompileParams, 22 | FWrap, SymbolicAirBuilder, 23 | }; 24 | use p3_matrix::dense::RowMajorMatrix; 25 | use std::time::Instant; 26 | 27 | #[allow(clippy::type_complexity)] 28 | pub(crate) fn compile_witgen( 29 | air: A, 30 | params: &CompileParams, 31 | k: u32, 32 | size: usize, 33 | num_public_values: usize, 34 | trace: RowMajorMatrix>, 35 | ) -> (CompiledCircuit, Vec>>, Vec>) 36 | where 37 | A: Air>>, 38 | { 39 | let n = 2usize.pow(k); 40 | println!("k = {k}"); 41 | println!("n = {n}"); 42 | println!("size = {size}"); 43 | println!("columns = {}", A::width(&air)); 44 | let (cs, preprocessing_info) = compile_circuit_cs::(&air, params, num_public_values); 45 | println!( 46 | "degree = {}", 47 | cs.gates.iter().map(|g| g.poly.degree()).max().unwrap() 48 | ); 49 | let preprocessing = compile_preprocessing::(k, size, &preprocessing_info, &air); 50 | let compiled_circuit = CompiledCircuit { cs, preprocessing }; 51 | let witness = trace_to_wit(k, trace); 52 | let pis = get_public_inputs(&preprocessing_info, size, &witness); 53 | 54 | check_witness(&compiled_circuit, k, 5, &witness, &pis); 55 | ( 56 | compiled_circuit, 57 | witness.into_iter().map(Some).collect(), 58 | pis, 59 | ) 60 | } 61 | 62 | pub(crate) fn setup_prove_verify( 63 | compiled_circuit: &CompiledCircuit, 64 | k: u32, 65 | pis: &[Vec], 66 | witness: Vec>>, 67 | ) { 68 | // Setup 69 | let mut rng = test_rng(); 70 | let params = ParamsKZG::::setup(k, &mut rng); 71 | let verifier_params = params.verifier_params(); 72 | let start = Instant::now(); 73 | let vk = keygen_vk(¶ms, compiled_circuit).expect("keygen_vk should not fail"); 74 | let pk = keygen_pk(¶ms, vk.clone(), compiled_circuit).expect("keygen_pk should not fail"); 75 | println!("Keygen: {:?}", start.elapsed()); 76 | 77 | // Proving 78 | println!("Proving..."); 79 | let start = Instant::now(); 80 | let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]); 81 | let mut prover = Prover::< 82 | KZGCommitmentScheme, 83 | ProverSHPLONK<'_, Bn256>, 84 | _, 85 | _, 86 | _, 87 | H2cEngine, 88 | >::new(¶ms, &pk, pis.to_vec(), &mut rng, &mut transcript) 89 | .unwrap(); 90 | println!("phase 0"); 91 | prover.commit_phase(0, witness).unwrap(); 92 | prover.create_proof().unwrap(); 93 | let proof = transcript.finalize(); 94 | println!("Prove: {:?}", start.elapsed()); 95 | 96 | // Verify 97 | let start = Instant::now(); 98 | println!("Verifying..."); 99 | let mut verifier_transcript = 100 | Blake2bRead::<_, G1Affine, Challenge255<_>>::init(proof.as_slice()); 101 | 102 | assert!( 103 | verify_proof::, VerifierSHPLONK, _, _, SingleStrategy<_>>( 104 | &verifier_params, 105 | &vk, 106 | pis.to_vec(), 107 | &mut verifier_transcript, 108 | ), 109 | "failed to verify proof" 110 | ); 111 | println!("Verify: {:?}", start.elapsed()); 112 | } 113 | -------------------------------------------------------------------------------- /book/src/background/plonkish.md: -------------------------------------------------------------------------------- 1 | # [WIP] PLONKish arithmetization 2 | 3 | We call the field over which the circuit is defined $\mathbb{F} = \mathbb{F}_p$. 4 | 5 | Let $n = 2^k$, and assume that $\omega$ is a primitive root of unity of order $n$ in 6 | $\mathbb{F}^\times$, so that $\mathbb{F}^\times$ has a multiplicative subgroup 7 | $\mathcal{H} = \{1, \omega, \omega^2, \cdots, \omega^{n-1}\}$. This forms a Lagrange 8 | basis corresponding to the elements in the subgroup. 9 | 10 | ## Polynomial rules 11 | A polynomial rule defines a constraint that must hold between its specified columns at 12 | every row (i.e. at every element in the multiplicative subgroup). 13 | 14 | e.g. 15 | 16 | ```text 17 | a * sa + b * sb + a * b * sm + c * sc + PI = 0 18 | ``` 19 | 20 | ## Columns 21 | - **fixed columns**: fixed for all instances of a particular circuit. These include 22 | selector columns, which toggle parts of a polynomial rule "on" or "off" to form a 23 | "custom gate". They can also include any other fixed data. 24 | - **advice columns**: variable values assigned in each instance of the circuit. 25 | Corresponds to the prover's secret witness. 26 | - **public input**: like advice columns, but publicly known values. 27 | 28 | Each column is a vector of $n$ values, e.g. $\mathbf{a} = [a_0, a_1, \cdots, a_{n-1}]$. We 29 | can think of the vector as the evaluation form of the column polynomial 30 | $a(X), X \in \mathcal{H}.$ To recover the coefficient form, we can use 31 | [Lagrange interpolation](polynomials.md#lagrange-interpolation), such that 32 | $a(\omega^i) = a_i.$ 33 | 34 | ## Equality constraints 35 | - Define permutation between a set of columns, e.g. $\sigma(a, b, c)$ 36 | - Assert equalities between specific cells in these columns, e.g. $b_1 = c_0$ 37 | - Construct permuted columns which should evaluate to same value as original columns 38 | 39 | ## Permutation grand product 40 | $$Z(\omega^i) := \prod_{0 \leq j \leq i} \frac{C_k(\omega^j) + \beta\delta^k \omega^j + \gamma}{C_k(\omega^j) + \beta S_k(\omega^j) + \gamma},$$ 41 | where $i = 0, \cdots, n-1$ indexes over the size of the multiplicative subgroup, and 42 | $k = 0, \cdots, m-1$ indexes over the advice columns involved in the permutation. This is 43 | a running product, where each term includes the cumulative product of the terms before it. 44 | 45 | > TODO: what is $\delta$? keep columns linearly independent 46 | 47 | Check the constraints: 48 | 49 | 1. First term is equal to one 50 | $$\mathcal{L}_0(X) \cdot (1 - Z(X)) = 0$$ 51 | 52 | 2. Running product is well-constructed. For each row, we check that this holds: 53 | $$Z(\omega^i) \cdot{(C(\omega^i) + \beta S_k(\omega^i) + \gamma)} - Z(\omega^{i-1}) \cdot{(C(\omega^i) + \delta^k \beta \omega^i + \gamma)} = 0$$ 54 | Rearranging gives 55 | $$Z(\omega^i) = Z(\omega^{i-1}) \frac{C(\omega^i) + \beta\delta^k \omega^i + \gamma}{C(\omega^i) + \beta S_k(\omega^i) + \gamma},$$ 56 | which is how we defined the grand product polynomial in the first place. 57 | 58 | ### Lookup 59 | Reference: [Generic Lookups with PLONK (DRAFT)](/LTPc5f-3S0qNF6MtwD-Tdg?view) 60 | 61 | ### Vanishing argument 62 | We want to check that the expressions defined by the gate constraints, permutation 63 | constraints and lookup constraints evaluate to zero at all elements in the multiplicative 64 | subgroup. To do this, the prover collapses all the expressions into one polynomial 65 | $$H(X) = \sum_{i=0}^e y^i E_i(X),$$ 66 | where $e$ is the number of expressions and $y$ is a random challenge used to keep the 67 | constraints linearly independent. The prover then divides this by the vanishing polynomial 68 | (see section: [Vanishing polynomial](polynomials.md#vanishing-polynomial)) and commits to 69 | the resulting quotient 70 | 71 | $$\text{Commit}(Q(X)), \text{where } Q(X) = \frac{H(X)}{Z_H(X)}.$$ 72 | 73 | The verifier responds with a random evaluation point $x,$ to which the prover replies with 74 | the claimed evaluations $q = Q(x), \{e_i\}_{i=0}^e = \{E_i(x)\}_{i=0}^e.$ Now, all that 75 | remains for the verifier to check is that the evaluations satisfy 76 | 77 | $$q \stackrel{?}{=} \frac{\sum_{i=0}^e y^i e_i}{Z_H(x)}.$$ 78 | 79 | Notice that we have yet to check that the committed polynomials indeed evaluate to the 80 | claimed values at 81 | $x, q \stackrel{?}{=} Q(x), \{e_i\}_{i=0}^e \stackrel{?}{=} \{E_i(x)\}_{i=0}^e.$ 82 | This check is handled by the polynomial commitment scheme (described in the next section). 83 | -------------------------------------------------------------------------------- /halo2_proofs/examples/circuit-cost.rs: -------------------------------------------------------------------------------- 1 | use halo2_frontend::dev::CircuitCost; 2 | use halo2_proofs::{ 3 | circuit::{Layouter, SimpleFloorPlanner, Value}, 4 | plonk::{Advice, Circuit, Column, ConstraintSystem, ErrorFront, Fixed, Instance}, 5 | poly::Rotation, 6 | }; 7 | use halo2curves::pasta::{Eq, Fp}; 8 | 9 | #[derive(Clone, Copy)] 10 | struct StandardPlonkConfig { 11 | a: Column, 12 | b: Column, 13 | c: Column, 14 | q_a: Column, 15 | q_b: Column, 16 | q_c: Column, 17 | q_ab: Column, 18 | constant: Column, 19 | #[allow(dead_code)] 20 | instance: Column, 21 | } 22 | 23 | impl StandardPlonkConfig { 24 | fn configure(meta: &mut ConstraintSystem) -> Self { 25 | let [a, b, c] = [(); 3].map(|_| meta.advice_column()); 26 | let [q_a, q_b, q_c, q_ab, constant] = [(); 5].map(|_| meta.fixed_column()); 27 | let instance = meta.instance_column(); 28 | 29 | [a, b, c].map(|column| meta.enable_equality(column)); 30 | 31 | meta.create_gate( 32 | "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", 33 | |meta| { 34 | let [a, b, c] = [a, b, c].map(|column| meta.query_advice(column, Rotation::cur())); 35 | let [q_a, q_b, q_c, q_ab, constant] = [q_a, q_b, q_c, q_ab, constant] 36 | .map(|column| meta.query_fixed(column, Rotation::cur())); 37 | let instance = meta.query_instance(instance, Rotation::cur()); 38 | Some( 39 | q_a * a.clone() 40 | + q_b * b.clone() 41 | + q_c * c 42 | + q_ab * a * b 43 | + constant 44 | + instance, 45 | ) 46 | }, 47 | ); 48 | 49 | StandardPlonkConfig { 50 | a, 51 | b, 52 | c, 53 | q_a, 54 | q_b, 55 | q_c, 56 | q_ab, 57 | constant, 58 | instance, 59 | } 60 | } 61 | } 62 | 63 | #[derive(Clone, Default)] 64 | struct StandardPlonk(Fp); 65 | 66 | impl Circuit for StandardPlonk { 67 | type Config = StandardPlonkConfig; 68 | type FloorPlanner = SimpleFloorPlanner; 69 | #[cfg(feature = "circuit-params")] 70 | type Params = (); 71 | 72 | fn without_witnesses(&self) -> Self { 73 | Self::default() 74 | } 75 | 76 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 77 | StandardPlonkConfig::configure(meta) 78 | } 79 | 80 | fn synthesize( 81 | &self, 82 | config: Self::Config, 83 | mut layouter: impl Layouter, 84 | ) -> Result<(), ErrorFront> { 85 | layouter.assign_region( 86 | || "", 87 | |mut region| { 88 | region.assign_advice(|| "", config.a, 0, || Value::known(self.0))?; 89 | region.assign_fixed(|| "", config.q_a, 0, || Value::known(-Fp::one()))?; 90 | 91 | region.assign_advice(|| "", config.a, 1, || Value::known(-Fp::from(5u64)))?; 92 | for (idx, column) in (1..).zip([ 93 | config.q_a, 94 | config.q_b, 95 | config.q_c, 96 | config.q_ab, 97 | config.constant, 98 | ]) { 99 | region.assign_fixed(|| "", column, 1, || Value::known(Fp::from(idx as u64)))?; 100 | } 101 | 102 | let a = region.assign_advice(|| "", config.a, 2, || Value::known(Fp::one()))?; 103 | a.copy_advice(|| "", &mut region, config.b, 3)?; 104 | a.copy_advice(|| "", &mut region, config.c, 4)?; 105 | Ok(()) 106 | }, 107 | ) 108 | } 109 | } 110 | 111 | fn main() { 112 | const K: u32 = 6; 113 | let circuit = StandardPlonk(Fp::one()); 114 | 115 | let cost = CircuitCost::::measure(K, &circuit); 116 | 117 | let marginal_proof_size = cost.marginal_proof_size(); 118 | println!("Marginal proof size: {}", usize::from(marginal_proof_size)); 119 | 120 | let proof_size = cost.proof_size(1); 121 | println!("Proof size: {}", usize::from(proof_size)); 122 | } 123 | -------------------------------------------------------------------------------- /halo2_backend/src/plonk/vanishing/verifier.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use halo2_middleware::ff::Field; 4 | 5 | use crate::{ 6 | arithmetic::CurveAffine, 7 | plonk::{ChallengeX, ChallengeY, Error, VerifyingKey}, 8 | poly::{ 9 | commitment::{ParamsVerifier, MSM}, 10 | VerifierQuery, 11 | }, 12 | transcript::{read_n_points, EncodedChallenge, TranscriptRead}, 13 | }; 14 | 15 | use super::Argument; 16 | 17 | pub(in crate::plonk) struct Committed { 18 | random_poly_commitment: C, 19 | } 20 | 21 | pub(in crate::plonk) struct Constructed { 22 | h_commitments: Vec, 23 | random_poly_commitment: C, 24 | } 25 | 26 | pub(in crate::plonk) struct PartiallyEvaluated { 27 | h_commitments: Vec, 28 | random_poly_commitment: C, 29 | random_eval: C::Scalar, 30 | } 31 | 32 | pub(in crate::plonk) struct Evaluated> { 33 | h_commitment: M, 34 | random_poly_commitment: C, 35 | expected_h_eval: C::Scalar, 36 | random_eval: C::Scalar, 37 | } 38 | 39 | impl Argument { 40 | pub(in crate::plonk) fn read_commitments_before_y< 41 | E: EncodedChallenge, 42 | T: TranscriptRead, 43 | >( 44 | transcript: &mut T, 45 | ) -> Result, Error> { 46 | let random_poly_commitment = transcript.read_point()?; 47 | 48 | Ok(Committed { 49 | random_poly_commitment, 50 | }) 51 | } 52 | } 53 | 54 | impl Committed { 55 | pub(in crate::plonk) fn read_commitments_after_y< 56 | E: EncodedChallenge, 57 | T: TranscriptRead, 58 | >( 59 | self, 60 | vk: &VerifyingKey, 61 | transcript: &mut T, 62 | ) -> Result, Error> { 63 | // Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1 64 | let h_commitments = read_n_points(transcript, vk.domain.get_quotient_poly_degree())?; 65 | 66 | Ok(Constructed { 67 | h_commitments, 68 | random_poly_commitment: self.random_poly_commitment, 69 | }) 70 | } 71 | } 72 | 73 | impl Constructed { 74 | pub(in crate::plonk) fn evaluate_after_x, T: TranscriptRead>( 75 | self, 76 | transcript: &mut T, 77 | ) -> Result, Error> { 78 | let random_eval = transcript.read_scalar()?; 79 | 80 | Ok(PartiallyEvaluated { 81 | h_commitments: self.h_commitments, 82 | random_poly_commitment: self.random_poly_commitment, 83 | random_eval, 84 | }) 85 | } 86 | } 87 | 88 | impl PartiallyEvaluated { 89 | pub(in crate::plonk) fn verify<'params, P: ParamsVerifier<'params, C>>( 90 | self, 91 | params: &'params P, 92 | expressions: impl Iterator, 93 | y: ChallengeY, 94 | xn: C::Scalar, 95 | ) -> Evaluated { 96 | let expected_h_eval = expressions.fold(C::Scalar::ZERO, |h_eval, v| h_eval * *y + v); 97 | let expected_h_eval = expected_h_eval * ((xn - C::Scalar::ONE).invert().unwrap()); 98 | 99 | let h_commitment = 100 | self.h_commitments 101 | .iter() 102 | .rev() 103 | .fold(params.empty_msm(), |mut acc, commitment| { 104 | acc.scale(xn); 105 | let commitment: C::CurveExt = (*commitment).into(); 106 | acc.append_term(C::Scalar::ONE, commitment); 107 | 108 | acc 109 | }); 110 | 111 | Evaluated { 112 | expected_h_eval, 113 | h_commitment, 114 | random_poly_commitment: self.random_poly_commitment, 115 | random_eval: self.random_eval, 116 | } 117 | } 118 | } 119 | 120 | impl> Evaluated { 121 | pub(in crate::plonk) fn queries( 122 | &self, 123 | x: ChallengeX, 124 | ) -> impl Iterator> + Clone { 125 | iter::empty() 126 | .chain(Some(VerifierQuery::new_msm( 127 | &self.h_commitment, 128 | *x, 129 | self.expected_h_eval, 130 | ))) 131 | .chain(Some(VerifierQuery::new_commitment( 132 | &self.random_poly_commitment, 133 | *x, 134 | self.random_eval, 135 | ))) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /book/src/background/groups.md: -------------------------------------------------------------------------------- 1 | # Cryptographic groups 2 | 3 | In the section [Inverses and groups](fields.md#inverses-and-groups) we introduced the 4 | concept of *groups*. A group has an identity and a group operation. In this section we 5 | will write groups additively, i.e. the identity is $\mathcal{O}$ and the group operation 6 | is $+$. 7 | 8 | Some groups can be used as *cryptographic groups*. At the risk of oversimplifying, this 9 | means that the problem of finding a discrete logarithm of a group element $P$ to a given 10 | base $G$, i.e. finding $x$ such that $P = [x] G$, is hard in general. 11 | 12 | ## Pedersen commitment 13 | The Pedersen commitment [[P99]] is a way to commit to a secret message in a verifiable 14 | way. It uses two random public generators $G, H \in \mathbb{G},$ where $\mathbb{G}$ is a 15 | cryptographic group of order $p$. A random secret $r$ is chosen in $\mathbb{Z}_q$, and the 16 | message to commit to $m$ is from any subset of $\mathbb{Z}_q$. The commitment is 17 | 18 | $$c = \text{Commit}(m,r)=[m]G + [r]H.$$ 19 | 20 | To open the commitment, the committer reveals $m$ and $r,$ thus allowing anyone to verify 21 | that $c$ is indeed a commitment to $m.$ 22 | 23 | [P99]: https://link.springer.com/content/pdf/10.1007%2F3-540-46766-1_9.pdf#page=3 24 | 25 | Notice that the Pedersen commitment scheme is homomorphic: 26 | 27 | $$ 28 | \begin{aligned} 29 | \text{Commit}(m,r) + \text{Commit}(m',r') &= [m]G + [r]H + [m']G + [r']H \\ 30 | &= [m + m']G + [r + r']H \\ 31 | &= \text{Commit}(m + m',r + r'). 32 | \end{aligned} 33 | $$ 34 | 35 | Assuming the discrete log assumption holds, Pedersen commitments are also perfectly hiding 36 | and computationally binding: 37 | 38 | * **hiding**: the adversary chooses messages $m_0, m_1.$ The committer commits to one of 39 | these messages $c = \text{Commit}(m_b,r), b \in \{0,1\}.$ Given $c,$ the probability of 40 | the adversary guessing the correct $b$ is no more than $\frac{1}{2}$. 41 | * **binding**: the adversary cannot pick two different messages $m_0 \neq m_1,$ and 42 | randomness $r_0, r_1,$ such that $\text{Commit}(m_0,r_0) = \text{Commit}(m_1,r_1).$ 43 | 44 | ### Vector Pedersen commitment 45 | We can use a variant of the Pedersen commitment scheme to commit to multiple messages at 46 | once, $\mathbf{m} = (m_0, \cdots, m_{n-1})$. This time, we'll have to sample a corresponding 47 | number of random public generators $\mathbf{G} = (G_0, \cdots, G_{n-1}),$ along with a 48 | single random generator $H$ as before (for use in hiding). Then, our commitment scheme is: 49 | 50 | $$ 51 | \begin{aligned} 52 | \text{Commit}(\mathbf{m}; r) &= \text{Commit}((m_0, \cdots, m_{n-1}); r) \\ 53 | &= [r]H + [m_0]G_0 + \cdots + [m_{n-1}]G_{n-1} \\ 54 | &= [r]H + \sum_{i= 0}^{n-1} [m_i]G_i. 55 | \end{aligned} 56 | $$ 57 | 58 | > TODO: is this positionally binding? 59 | 60 | ## Diffie–Hellman 61 | 62 | An example of a protocol that uses cryptographic groups is Diffie–Hellman key agreement 63 | [[DH1976]]. The Diffie–Hellman protocol is a method for two users, Alice and Bob, to 64 | generate a shared private key. It proceeds as follows: 65 | 66 | 1. Alice and Bob publicly agree on two prime numbers, $p$ and $G,$ where $p$ is large and 67 | $G$ is a primitive root $\pmod p.$ (Note that $g$ is a generator of the group 68 | $\mathbb{F}_p^\times.$) 69 | 2. Alice chooses a large random number $a$ as her private key. She computes her public key 70 | $A = [a]G \pmod p,$ and sends $A$ to Bob. 71 | 3. Similarly, Bob chooses a large random number $b$ as his private key. He computes his 72 | public key $B = [b]G \pmod p,$ and sends $B$ to Alice. 73 | 4. Now both Alice and Bob compute their shared key $K = [ab]G \pmod p,$ which Alice 74 | computes as 75 | $$K = [a]B \pmod p = [a]([b]G) \pmod p,$$ 76 | and Bob computes as 77 | $$K = [b]A \pmod p = [b]([a]G) \pmod p.$$ 78 | 79 | [DH1976]: https://ee.stanford.edu/~hellman/publications/24.pdf 80 | 81 | A potential eavesdropper would need to derive $K = [ab]g \pmod p$ knowing only 82 | $g, p, A = [a]G,$ and $B = [b]G$: in other words, they would need to either get the 83 | discrete logarithm $a$ from $A = [a]G$ or $b$ from $B = [b]G,$ which we assume to be 84 | computationally infeasible in $\mathbb{F}_p^\times.$ 85 | 86 | More generally, protocols that use similar ideas to Diffie–Hellman are used throughout 87 | cryptography. One way of instantiating a cryptographic group is as an 88 | [elliptic curve](curves.md). Before we go into detail on elliptic curves, we'll describe 89 | some algorithms that can be used for any group. 90 | 91 | ## Multiscalar multiplication 92 | 93 | ### TODO: Pippenger's algorithm 94 | Reference: https://jbootle.github.io/Misc/pippenger.pdf 95 | -------------------------------------------------------------------------------- /book/src/user/dev-tools.md: -------------------------------------------------------------------------------- 1 | # Developer tools 2 | 3 | The `halo2` crate includes several utilities to help you design and implement your 4 | circuits. 5 | 6 | ## Mock prover 7 | 8 | `halo2_proofs::dev::MockProver` is a tool for debugging circuits, as well as cheaply verifying 9 | their correctness in unit tests. The private and public inputs to the circuit are 10 | constructed as would normally be done to create a proof, but `MockProver::run` instead 11 | creates an object that will test every constraint in the circuit directly. It returns 12 | granular error messages that indicate which specific constraint (if any) is not satisfied. 13 | 14 | ## Circuit visualizations 15 | 16 | The `dev-graph` feature flag exposes several helper methods for creating graphical 17 | representations of circuits. 18 | 19 | On Debian systems, you will need the following additional packages: 20 | ```plaintext 21 | sudo apt install cmake libexpat1-dev libfreetype6-dev 22 | ``` 23 | 24 | ### Circuit layout 25 | 26 | `halo2_proofs::dev::CircuitLayout` renders the circuit layout as a grid: 27 | 28 | ```rust,ignore,no_run 29 | {{#include ../../../halo2_proofs/examples/circuit-layout.rs:dev-graph}} 30 | ``` 31 | 32 | - Columns are laid out from left to right as instance, advice and fixed. The order of 33 | columns is otherwise without meaning. 34 | - Instance columns have a white background. 35 | - Advice columns have a red background. 36 | - Fixed columns have a blue background. 37 | - Regions are shown as labelled green boxes (overlaying the background colour). A region 38 | may appear as multiple boxes if some of its columns happen to not be adjacent. 39 | - Cells that have been assigned to by the circuit will be shaded in grey. If any cells are 40 | assigned to more than once (which is usually a mistake), they will be shaded darker than 41 | the surrounding cells. 42 | 43 | ### Circuit structure 44 | 45 | `halo2_proofs::dev::circuit_dot_graph` builds a [DOT graph string] representing the given 46 | circuit, which can then be rendered with a variety of [layout programs]. The graph is built 47 | from calls to `Layouter::namespace` both within the circuit, and inside the gadgets and 48 | chips that it uses. 49 | 50 | [DOT graph string]: https://graphviz.org/doc/info/lang.html 51 | [layout programs]: https://en.wikipedia.org/wiki/DOT_(graph_description_language)#Layout_programs 52 | 53 | ```rust,ignore,no_run 54 | fn main() { 55 | // Prepare the circuit you want to render. 56 | // You don't need to include any witness variables. 57 | let a = Fp::rand(); 58 | let instance = Fp::one() + Fp::one(); 59 | let lookup_table = vec![instance, a, a, Fp::zero()]; 60 | let circuit: MyCircuit = MyCircuit { 61 | a: None, 62 | lookup_table, 63 | }; 64 | 65 | // Generate the DOT graph string. 66 | let dot_string = halo2_proofs::dev::circuit_dot_graph(&circuit); 67 | 68 | // Now you can either handle it in Rust, or just 69 | // print it out to use with command-line tools. 70 | print!("{}", dot_string); 71 | } 72 | ``` 73 | 74 | ## Cost estimator 75 | 76 | The `cost-model` binary takes high-level parameters for a circuit design, and estimates 77 | the verification cost, as well as resulting proof size. 78 | 79 | ```plaintext 80 | Usage: cargo run --example cost-model -- [OPTIONS] k 81 | 82 | Positional arguments: 83 | k 2^K bound on the number of rows. 84 | 85 | Optional arguments: 86 | -h, --help Print this message. 87 | -a, --advice R[,R..] An advice column with the given rotations. May be repeated. 88 | -i, --instance R[,R..] An instance column with the given rotations. May be repeated. 89 | -f, --fixed R[,R..] A fixed column with the given rotations. May be repeated. 90 | -g, --gate-degree D Maximum degree of the custom gates. 91 | -l, --lookup N,I,T A lookup over N columns with max input degree I and max table degree T. May be repeated. 92 | -p, --permutation N A permutation over N columns. May be repeated. 93 | ``` 94 | 95 | For example, to estimate the cost of a circuit with three advice columns and one fixed 96 | column (with various rotations), and a maximum gate degree of 4: 97 | 98 | ```plaintext 99 | > cargo run --example cost-model -- -a 0,1 -a 0 -a-0,-1,1 -f 0 -g 4 11 100 | Finished dev [unoptimized + debuginfo] target(s) in 0.03s 101 | Running `target/debug/examples/cost-model -a 0,1 -a 0 -a 0,-1,1 -f 0 -g 4 11` 102 | Circuit { 103 | k: 11, 104 | max_deg: 4, 105 | advice_columns: 3, 106 | lookups: 0, 107 | permutations: [], 108 | column_queries: 7, 109 | point_sets: 3, 110 | estimator: Estimator, 111 | } 112 | Proof size: 1440 bytes 113 | Verification: at least 81.689ms 114 | ``` 115 | -------------------------------------------------------------------------------- /book/src/background/pc-ipa.md: -------------------------------------------------------------------------------- 1 | # Polynomial commitment using inner product argument 2 | We want to commit to some polynomial $p(X) \in \mathbb{F}_p[X]$, and be able to provably 3 | evaluate the committed polynomial at arbitrary points. The naive solution would be for the 4 | prover to simply send the polynomial's coefficients to the verifier: however, this 5 | requires $O(n)$ communication. Our polynomial commitment scheme gets the job done using 6 | $O(\log n)$ communication. 7 | 8 | ### `Setup` 9 | Given a parameter $d = 2^k,$ we generate the common reference string 10 | $\sigma = (\mathbb{G}, \mathbf{G}, H, \mathbb{F}_p)$ defining certain constants for this 11 | scheme: 12 | * $\mathbb{G}$ is a group of prime order $p;$ 13 | * $\mathbf{G} \in \mathbb{G}^d$ is a vector of $d$ random group elements; 14 | * $H \in \mathbb{G}$ is a random group element; and 15 | * $\mathbb{F}_p$ is the finite field of order $p.$ 16 | 17 | ### `Commit` 18 | The Pedersen vector commitment $\text{Commit}$ is defined as 19 | 20 | $$\text{Commit}(\sigma, p(X); r) = \langle\mathbf{a}, \mathbf{G}\rangle + [r]H,$$ 21 | 22 | for some polynomial $p(X) \in \mathbb{F}_p[X]$ and some blinding factor 23 | $r \in \mathbb{F}_p.$ Here, each element of the vector $\mathbf{a}_i \in \mathbb{F}_p$ is 24 | the coefficient for the $i$th degree term of $p(X),$ and $p(X)$ is of maximal degree 25 | $d - 1.$ 26 | 27 | ### `Open` (prover) and `OpenVerify` (verifier) 28 | The modified inner product argument is an argument of knowledge for the relation 29 | 30 | $$\boxed{\{((P, x, v); (\mathbf{a}, r)): P = \langle\mathbf{a}, \mathbf{G}\rangle + [r]H, v = \langle\mathbf{a}, \mathbf{b}\rangle\}},$$ 31 | 32 | where $\mathbf{b} = (1, x, x^2, \cdots, x^{d-1})$ is composed of increasing powers of the 33 | evaluation point $x.$ This allows a prover to demonstrate to a verifier that the 34 | polynomial contained “inside” the commitment $P$ evaluates to $v$ at $x,$ and moreover, 35 | that the committed polynomial has maximum degree $d − 1.$ 36 | 37 | The inner product argument proceeds in $k = \log_2 d$ rounds. For our purposes, it is 38 | sufficient to know about its final outputs, while merely providing intuition about the 39 | intermediate rounds. (Refer to Section 3 in the [Halo] paper for a full explanation.) 40 | 41 | [Halo]: https://eprint.iacr.org/2019/1021.pdf 42 | 43 | Before beginning the argument, the verifier selects a random group element $U$ and sends it 44 | to the prover. We initialize the argument at round $k,$ with the vectors 45 | $\mathbf{a}^{(k)} := \mathbf{a},$ $\mathbf{G}^{(k)} := \mathbf{G}$ and 46 | $\mathbf{b}^{(k)} := \mathbf{b}.$ In each round $j = k, k-1, \cdots, 1$: 47 | 48 | * the prover computes two values $L_j$ and $R_j$ by taking some inner product of 49 | $\mathbf{a}^{(j)}$ with $\mathbf{G}^{(j)}$ and $\mathbf{b}^{(j)}$. Note that are in some 50 | sense "cross-terms": the lower half of $\mathbf{a}$ is used with the higher half of 51 | $\mathbf{G}$ and $\mathbf{b}$, and vice versa: 52 | 53 | $$ 54 | \begin{aligned} 55 | L_j &= \langle\mathbf{a_{lo}^{(j)}}, \mathbf{G_{hi}^{(j)}}\rangle + [l_j]H + [\langle\mathbf{a_{lo}^{(j)}}, \mathbf{b_{hi}^{(j)}}\rangle] U\\ 56 | R_j &= \langle\mathbf{a_{hi}^{(j)}}, \mathbf{G_{lo}^{(j)}}\rangle + [r_j]H + [\langle\mathbf{a_{hi}^{(j)}}, \mathbf{b_{lo}^{(j)}}\rangle] U\\ 57 | \end{aligned} 58 | $$ 59 | 60 | * the verifier issues a random challenge $u_j$; 61 | * the prover uses $u_j$ to compress the lower and higher halves of $\mathbf{a}^{(j)}$, 62 | thus producing a new vector of half the original length 63 | $$\mathbf{a}^{(j-1)} = \mathbf{a_{hi}^{(j)}}\cdot u_j^{-1} + \mathbf{a_{lo}^{(j)}}\cdot u_j.$$ 64 | The vectors $\mathbf{G}^{(j)}$ and $\mathbf{b}^{(j)}$ are similarly compressed to give 65 | $\mathbf{G}^{(j-1)}$ and $\mathbf{b}^{(j-1)}$. 66 | * $\mathbf{a}^{(j-1)}$, $\mathbf{G}^{(j-1)}$ and $\mathbf{b}^{(j-1)}$ are input to the 67 | next round $j - 1.$ 68 | 69 | Note that at the end of the last round $j = 1,$ we are left with $a := \mathbf{a}^{(0)}$, 70 | $G := \mathbf{G}^{(0)}$, $b := \mathbf{b}^{(0)},$ each of length 1. The intuition is that 71 | these final scalars, together with the challenges $\{u_j\}$ and "cross-terms" 72 | $\{L_j, R_j\}$ from each round, encode the compression in each round. Since the prover did 73 | not know the challenges $U, \{u_j\}$ in advance, they would have been unable to manipulate 74 | the round compressions. Thus, checking a constraint on these final terms should enforce 75 | that the compression had been performed correctly, and that the original $\mathbf{a}$ 76 | satisfied the relation before undergoing compression. 77 | 78 | Note that $G, b$ are simply rearrangements of the publicly known $\mathbf{G}, \mathbf{b},$ 79 | with the round challenges $\{u_j\}$ mixed in: this means the verifier can compute $G, b$ 80 | independently and verify that the prover had provided those same values. 81 | -------------------------------------------------------------------------------- /book/src/concepts/proofs.md: -------------------------------------------------------------------------------- 1 | # Proof systems 2 | 3 | The aim of any ***proof system*** is to be able to prove interesting mathematical or 4 | cryptographic ***statements***. 5 | 6 | Typically, in a given protocol we will want to prove families of statements that differ 7 | in their ***public inputs***. The prover will also need to show that they know some 8 | ***private inputs*** that make the statement hold. 9 | 10 | To do this we write down a ***relation***, $\mathcal{R}$, that specifies which 11 | combinations of public and private inputs are valid. 12 | 13 | > The terminology above is intended to be aligned with the 14 | > [ZKProof Community Reference](https://docs.zkproof.org/reference#latest-version). 15 | 16 | To be precise, we should distinguish between the relation $\mathcal{R}$, and its 17 | implementation to be used in a proof system. We call the latter a ***circuit***. 18 | 19 | The language that we use to express circuits for a particular proof system is called an 20 | ***arithmetization***. Usually, an arithmetization will define circuits in terms of 21 | polynomial constraints on variables over a field. 22 | 23 | > The _process_ of expressing a particular relation as a circuit is also sometimes called 24 | > "arithmetization", but we'll avoid that usage. 25 | 26 | To create a proof of a statement, the prover will need to know the private inputs, 27 | and also intermediate values, called ***advice*** values, that are used by the circuit. 28 | 29 | We assume that we can compute advice values efficiently from the private and public inputs. 30 | The particular advice values will depend on how we write the circuit, not only on the 31 | high-level statement. 32 | 33 | The private inputs and advice values are collectively called a ***witness***. 34 | 35 | > Some authors use "witness" as just a synonym for private inputs. But in our usage, 36 | > a witness includes advice, i.e. it includes all values that the prover supplies to 37 | > the circuit. 38 | 39 | For example, suppose that we want to prove knowledge of a preimage $x$ of a 40 | hash function $H$ for a digest $y$: 41 | 42 | * The private input would be the preimage $x$. 43 | 44 | * The public input would be the digest $y$. 45 | 46 | * The relation would be $\{(x, y) : H(x) = y\}$. 47 | 48 | * For a particular public input $Y$, the statement would be: $\{(x) : H(x) = Y\}$. 49 | 50 | * The advice would be all of the intermediate values in the circuit implementing the 51 | hash function. The witness would be $x$ and the advice. 52 | 53 | A ***Non-interactive Argument*** allows a ***prover*** to create a ***proof*** for a 54 | given statement and witness. The proof is data that can be used to convince a ***verifier*** 55 | that _there exists_ a witness for which the statement holds. The security property that 56 | such proofs cannot falsely convince a verifier is called ***soundness***. 57 | 58 | A ***Non-interactive Argument of Knowledge*** (***NARK***) further convinces the verifier 59 | that the prover _knew_ a witness for which the statement holds. This security property is 60 | called ***knowledge soundness***, and it implies soundness. 61 | 62 | In practice knowledge soundness is more useful for cryptographic protocols than soundness: 63 | if we are interested in whether Alice holds a secret key in some protocol, say, we need 64 | Alice to prove that _she knows_ the key, not just that it exists. 65 | 66 | Knowledge soundness is formalized by saying that an ***extractor***, which can observe 67 | precisely how the proof is generated, must be able to compute the witness. 68 | 69 | > This property is subtle given that proofs can be ***malleable***. That is, depending on the 70 | > proof system it may be possible to take an existing proof (or set of proofs) and, without 71 | > knowing the witness(es), modify it/them to produce a distinct proof of the same or a related 72 | > statement. Higher-level protocols that use malleable proof systems need to take this into 73 | > account. 74 | > 75 | > Even without malleability, proofs can also potentially be ***replayed***. For instance, 76 | > we would not want Alice in our example to be able to present a proof generated by someone 77 | > else, and have that be taken as a demonstration that she knew the key. 78 | 79 | If a proof yields no information about the witness (other than that a witness exists and was 80 | known to the prover), then we say that the proof system is ***zero knowledge***. 81 | 82 | If a proof system produces short proofs —i.e. of length polylogarithmic in the circuit 83 | size— then we say that it is ***succinct***. A succinct NARK is called a ***SNARK*** 84 | (***Succinct Non-Interactive Argument of Knowledge***). 85 | 86 | > By this definition, a SNARK need not have verification time polylogarithmic in the circuit 87 | > size. Some papers use the term ***efficient*** to describe a SNARK with that property, but 88 | > we'll avoid that term since it's ambiguous for SNARKs that support amortized or recursive 89 | > verification, which we'll get to later. 90 | 91 | A ***zk-SNARK*** is a zero-knowledge SNARK. 92 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI checks 2 | 3 | on: 4 | merge_group: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | 12 | fmt: 13 | name: Rustfmt 14 | timeout-minutes: 30 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions-rs/toolchain@v1 19 | with: 20 | override: false 21 | - run: rustup component add rustfmt 22 | - uses: actions-rs/cargo@v1 23 | with: 24 | command: fmt 25 | args: --all -- --check 26 | 27 | typos-check: 28 | name: Spell Check with Typos 29 | timeout-minutes: 5 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: crate-ci/typos@v1.33.1 34 | with: 35 | config: ./typos.toml 36 | isolated: true 37 | 38 | test: 39 | name: Test on ${{ matrix.os }} with ${{ matrix.feature_set }} features 40 | runs-on: ${{ matrix.os }} 41 | strategy: 42 | matrix: 43 | feature_set: [basic, all] 44 | os: [ubuntu-latest, windows-latest, macOS-latest] 45 | include: 46 | - feature_set: basic 47 | features: --features batch,dev-graph,gadget-traces 48 | - feature_set: all 49 | features: --all-features 50 | steps: 51 | - uses: actions/checkout@v3 52 | - uses: actions-rs/toolchain@v1 53 | with: 54 | override: false 55 | - name: Install fontconfig system dependency 56 | run: | 57 | if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then 58 | sudo apt-get update && sudo apt-get install -y libfontconfig1-dev pkg-config 59 | elif [[ "${{ matrix.os }}" == "macOS-latest" ]]; then 60 | brew update && brew install fontconfig pkg-config 61 | elif [[ "${{ matrix.os }}" == "windows-latest" ]]; then 62 | choco install -y pkgconfiglite 63 | echo "PKG_CONFIG_PATH=\"C:/ProgramData/chocolatey/lib/pkgconfiglite/tools/$env:VCPKG_INSTANCE_NAME/lib/pkgconfig\"" >> "$GITHUB_ENV" 64 | fi 65 | shell: bash 66 | - name: Configure environment variables 67 | run: | 68 | echo "PKG_CONFIG_ALLOW_CROSS=1" >> "$GITHUB_ENV" 69 | echo "RUST_FONTCONFIG_DLOPEN=on" >> "$GITHUB_ENV" 70 | - name: Run tests 71 | uses: actions-rs/cargo@v1 72 | with: 73 | command: test 74 | args: --verbose --release --workspace --no-default-features ${{ matrix.features }} 75 | 76 | examples: 77 | name: Run the examples 78 | runs-on: ubuntu-latest 79 | 80 | steps: 81 | - uses: actions/checkout@v3 82 | - uses: actions-rs/toolchain@v1 83 | - name: Run examples 84 | run: | 85 | .github/scripts/run-examples.sh 86 | 87 | build: 88 | name: Build target ${{ matrix.target }} 89 | runs-on: ubuntu-latest 90 | strategy: 91 | matrix: 92 | target: 93 | - wasm32-unknown-unknown 94 | - wasm32-wasi 95 | 96 | steps: 97 | - uses: actions/checkout@v3 98 | - uses: actions-rs/toolchain@v1 99 | with: 100 | override: false 101 | default: true 102 | - name: Add target 103 | run: rustup target add ${{ matrix.target }} 104 | - name: Run script file 105 | run: | 106 | .github/scripts/wasm-target-test-build.sh 107 | 108 | bitrot: 109 | name: Bitrot check 110 | runs-on: ubuntu-latest 111 | 112 | steps: 113 | - uses: actions/checkout@v3 114 | - name: Install dependencies 115 | run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev pkg-config 116 | - name: Set up env 117 | run: | 118 | echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV 119 | echo "RUST_FONTCONFIG_DLOPEN=on" >> $GITHUB_ENV 120 | - uses: actions-rs/toolchain@v1 121 | with: 122 | override: false 123 | # Build benchmarks to prevent bitrot 124 | - name: Build benchmarks 125 | uses: actions-rs/cargo@v1 126 | with: 127 | command: build 128 | args: --benches --examples --all-features 129 | 130 | doc-links: 131 | name: Intra-doc links 132 | runs-on: ubuntu-latest 133 | 134 | steps: 135 | - uses: actions/checkout@v3 136 | - uses: actions-rs/toolchain@v1 137 | with: 138 | override: false 139 | - name: cargo fetch 140 | uses: actions-rs/cargo@v1 141 | with: 142 | command: fetch 143 | 144 | # Ensure intra-documentation links all resolve correctly 145 | # Requires #![deny(intra_doc_link_resolution_failure)] in crates. 146 | - name: Check intra-doc links 147 | uses: actions-rs/cargo@v1 148 | with: 149 | command: doc 150 | args: --all --document-private-items 151 | -------------------------------------------------------------------------------- /p3_frontend/tests/fib_air.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2022 The Plonky3 Authors 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | use std::borrow::Borrow; 24 | 25 | use p3_air::{Air, AirBuilder, BaseAir}; 26 | use p3_field::PrimeField; 27 | use p3_frontend::AirBuilderWithPublicValues; 28 | use p3_matrix::dense::RowMajorMatrix; 29 | use p3_matrix::MatrixRowSlices; 30 | 31 | /// For testing the public values feature 32 | 33 | pub struct FibonacciAir {} 34 | 35 | impl BaseAir for FibonacciAir { 36 | fn width(&self) -> usize { 37 | NUM_FIBONACCI_COLS 38 | } 39 | } 40 | 41 | impl Air for FibonacciAir { 42 | fn eval(&self, builder: &mut AB) { 43 | let main = builder.main(); 44 | let pis = builder.public_values(); 45 | 46 | let a = pis[0]; 47 | let b = pis[1]; 48 | let x = pis[2]; 49 | 50 | let local: &FibonacciRow = main.row_slice(0).borrow(); 51 | let next: &FibonacciRow = main.row_slice(1).borrow(); 52 | 53 | let mut when_first_row = builder.when_first_row(); 54 | 55 | when_first_row.assert_eq(local.left, a); 56 | when_first_row.assert_eq(local.right, b); 57 | 58 | let mut when_transition = builder.when_transition(); 59 | 60 | // a' <- b 61 | when_transition.assert_eq(local.right, next.left); 62 | 63 | // b' <- a + b 64 | when_transition.assert_eq(local.left + local.right, next.right); 65 | 66 | builder.when_last_row().assert_eq(local.right, x); 67 | } 68 | } 69 | 70 | pub fn generate_trace_rows(a: u64, b: u64, n: usize) -> RowMajorMatrix { 71 | assert!(n.is_power_of_two()); 72 | 73 | let mut trace = 74 | RowMajorMatrix::new(vec![F::zero(); n * NUM_FIBONACCI_COLS], NUM_FIBONACCI_COLS); 75 | 76 | let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::>() }; 77 | assert!(prefix.is_empty(), "Alignment should match"); 78 | assert!(suffix.is_empty(), "Alignment should match"); 79 | assert_eq!(rows.len(), n); 80 | 81 | rows[0] = FibonacciRow::new(F::from_canonical_u64(a), F::from_canonical_u64(b)); 82 | 83 | for i in 1..n { 84 | rows[i].left = rows[i - 1].right; 85 | rows[i].right = rows[i - 1].left + rows[i - 1].right; 86 | } 87 | 88 | trace 89 | } 90 | 91 | const NUM_FIBONACCI_COLS: usize = 2; 92 | 93 | pub struct FibonacciRow { 94 | pub left: F, 95 | pub right: F, 96 | } 97 | 98 | impl FibonacciRow { 99 | fn new(left: F, right: F) -> FibonacciRow { 100 | FibonacciRow { left, right } 101 | } 102 | } 103 | 104 | impl Borrow> for [F] { 105 | fn borrow(&self) -> &FibonacciRow { 106 | debug_assert_eq!(self.len(), NUM_FIBONACCI_COLS); 107 | let (prefix, shorts, suffix) = unsafe { self.align_to::>() }; 108 | debug_assert!(prefix.is_empty(), "Alignment should match"); 109 | debug_assert!(suffix.is_empty(), "Alignment should match"); 110 | debug_assert_eq!(shorts.len(), 1); 111 | &shorts[0] 112 | } 113 | } 114 | 115 | use halo2curves::bn256::Fr; 116 | use p3_frontend::{CompileParams, FWrap}; 117 | 118 | mod common; 119 | 120 | #[test] 121 | fn test_fib() { 122 | let k = 5; 123 | let n = 2usize.pow(k); 124 | // TODO: 6 must be bigger than unusable rows. Add a helper function to calculate this 125 | let size = n - 6; 126 | let air = FibonacciAir {}; 127 | let num_public_values = 3; 128 | let params = CompileParams::default(); 129 | let trace = generate_trace_rows::>(0, 1, n); 130 | let (compiled_circuit, witness, pis) = 131 | common::compile_witgen(air, ¶ms, k, size, num_public_values, trace); 132 | 133 | common::setup_prove_verify(&compiled_circuit, k, &pis, witness); 134 | } 135 | -------------------------------------------------------------------------------- /halo2_backend/src/poly/kzg/multiopen/gwc/verifier.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::marker::PhantomData; 3 | 4 | use super::{construct_intermediate_sets, ChallengeU, ChallengeV}; 5 | use crate::arithmetic::powers; 6 | use crate::helpers::SerdeCurveAffine; 7 | use crate::poly::commitment::Verifier; 8 | use crate::poly::commitment::MSM; 9 | use crate::poly::kzg::commitment::KZGCommitmentScheme; 10 | use crate::poly::kzg::msm::{DualMSM, MSMKZG}; 11 | use crate::poly::kzg::strategy::GuardKZG; 12 | use crate::poly::query::Query; 13 | use crate::poly::query::{CommitmentReference, VerifierQuery}; 14 | use crate::poly::Error; 15 | use crate::transcript::{EncodedChallenge, TranscriptRead}; 16 | 17 | use group::prime::PrimeCurveAffine; 18 | use halo2_middleware::ff::Field; 19 | use halo2curves::pairing::{Engine, MultiMillerLoop}; 20 | use halo2curves::CurveExt; 21 | 22 | #[derive(Debug)] 23 | /// Concrete KZG verifier with GWC variant 24 | pub struct VerifierGWC { 25 | _marker: PhantomData, 26 | } 27 | 28 | impl<'params, E> Verifier<'params, KZGCommitmentScheme> for VerifierGWC 29 | where 30 | E: MultiMillerLoop + Debug, 31 | E::G1Affine: SerdeCurveAffine::Fr, CurveExt = ::G1>, 32 | E::G1: CurveExt, 33 | E::G2Affine: SerdeCurveAffine, 34 | { 35 | type Guard = GuardKZG; 36 | type MSMAccumulator = DualMSM; 37 | 38 | fn new() -> Self { 39 | Self { 40 | _marker: PhantomData, 41 | } 42 | } 43 | 44 | fn verify_proof< 45 | 'com, 46 | Ch: EncodedChallenge, 47 | T: TranscriptRead, 48 | I, 49 | >( 50 | &self, 51 | transcript: &mut T, 52 | queries: I, 53 | mut msm_accumulator: DualMSM, 54 | ) -> Result 55 | where 56 | I: IntoIterator>> + Clone, 57 | { 58 | let v: ChallengeV<_> = transcript.squeeze_challenge_scalar(); 59 | 60 | let commitment_data = construct_intermediate_sets(queries).ok_or(Error::OpeningError)?; 61 | 62 | let w: Vec = (0..commitment_data.len()) 63 | .map(|_| transcript.read_point().map_err(|_| Error::SamplingError)) 64 | .collect::, Error>>()?; 65 | 66 | let u: ChallengeU<_> = transcript.squeeze_challenge_scalar(); 67 | 68 | let mut commitment_multi = MSMKZG::::new(); 69 | let mut eval_multi = E::Fr::ZERO; 70 | 71 | let mut witness = MSMKZG::::new(); 72 | let mut witness_with_aux = MSMKZG::::new(); 73 | 74 | for ((commitment_at_a_point, wi), power_of_u) in 75 | commitment_data.iter().zip(w.into_iter()).zip(powers(*u)) 76 | { 77 | assert!(!commitment_at_a_point.queries.is_empty()); 78 | let z = commitment_at_a_point.point; 79 | 80 | let (mut commitment_batch, eval_batch) = commitment_at_a_point 81 | .queries 82 | .iter() 83 | .zip(powers(*v)) 84 | .map(|(query, power_of_v)| { 85 | assert_eq!(query.get_point(), z); 86 | 87 | let commitment = match query.get_commitment() { 88 | CommitmentReference::Commitment(c) => { 89 | let mut msm = MSMKZG::::new(); 90 | msm.append_term(power_of_v, (*c).into()); 91 | msm 92 | } 93 | CommitmentReference::MSM(msm) => { 94 | let mut msm = msm.clone(); 95 | msm.scale(power_of_v); 96 | msm 97 | } 98 | }; 99 | let eval = power_of_v * query.get_eval(); 100 | 101 | (commitment, eval) 102 | }) 103 | .reduce(|(mut commitment_acc, eval_acc), (commitment, eval)| { 104 | commitment_acc.add_msm(&commitment); 105 | (commitment_acc, eval_acc + eval) 106 | }) 107 | .unwrap(); 108 | 109 | commitment_batch.scale(power_of_u); 110 | commitment_multi.add_msm(&commitment_batch); 111 | eval_multi += power_of_u * eval_batch; 112 | 113 | witness_with_aux.append_term(power_of_u * z, wi.into()); 114 | witness.append_term(power_of_u, wi.into()); 115 | } 116 | 117 | msm_accumulator.left.add_msm(&witness); 118 | 119 | msm_accumulator.right.add_msm(&witness_with_aux); 120 | msm_accumulator.right.add_msm(&commitment_multi); 121 | let g0: E::G1 = ::generator().into(); 122 | msm_accumulator.right.append_term(eval_multi, -g0); 123 | 124 | Ok(Self::Guard::new(msm_accumulator)) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /halo2_backend/src/poly/query.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use super::commitment::{Blind, MSM}; 4 | use crate::{ 5 | arithmetic::eval_polynomial, 6 | poly::{Coeff, Polynomial}, 7 | }; 8 | use halo2curves::CurveAffine; 9 | 10 | pub trait Query: Sized + Clone + Send + Sync { 11 | type Commitment: PartialEq + Copy + Send + Sync; 12 | type Eval: Clone + Default + Debug; 13 | 14 | fn get_point(&self) -> F; 15 | fn get_eval(&self) -> Self::Eval; 16 | fn get_commitment(&self) -> Self::Commitment; 17 | } 18 | 19 | /// A polynomial query at a point 20 | #[derive(Debug, Clone, Copy)] 21 | pub struct ProverQuery<'com, C: CurveAffine> { 22 | /// Point at which polynomial is queried 23 | pub(crate) point: C::Scalar, 24 | /// Coefficients of polynomial 25 | pub(crate) poly: &'com Polynomial, 26 | } 27 | 28 | impl<'com, C> ProverQuery<'com, C> 29 | where 30 | C: CurveAffine, 31 | { 32 | /// Create a new prover query based on a polynomial 33 | pub fn new( 34 | point: C::Scalar, 35 | poly: &'com Polynomial, 36 | _blind: Blind, 37 | ) -> Self { 38 | ProverQuery { point, poly } 39 | } 40 | } 41 | 42 | #[doc(hidden)] 43 | #[derive(Copy, Clone)] 44 | pub struct PolynomialPointer<'com, C: CurveAffine> { 45 | pub(crate) poly: &'com Polynomial, 46 | } 47 | 48 | impl<'com, C: CurveAffine> PartialEq for PolynomialPointer<'com, C> { 49 | fn eq(&self, other: &Self) -> bool { 50 | std::ptr::eq(self.poly, other.poly) 51 | } 52 | } 53 | 54 | impl<'com, C: CurveAffine> Query for ProverQuery<'com, C> { 55 | type Commitment = PolynomialPointer<'com, C>; 56 | type Eval = C::Scalar; 57 | 58 | fn get_point(&self) -> C::Scalar { 59 | self.point 60 | } 61 | fn get_eval(&self) -> Self::Eval { 62 | eval_polynomial(&self.poly[..], self.get_point()) 63 | } 64 | fn get_commitment(&self) -> Self::Commitment { 65 | PolynomialPointer { poly: self.poly } 66 | } 67 | } 68 | 69 | impl<'com, C: CurveAffine, M: MSM> VerifierQuery<'com, C, M> { 70 | /// Create a new verifier query based on a commitment 71 | pub fn new_commitment(commitment: &'com C, point: C::Scalar, eval: C::Scalar) -> Self { 72 | VerifierQuery { 73 | point, 74 | eval, 75 | commitment: CommitmentReference::Commitment(commitment), 76 | } 77 | } 78 | 79 | /// Create a new verifier query based on a linear combination of commitments 80 | pub fn new_msm(msm: &'com M, point: C::Scalar, eval: C::Scalar) -> VerifierQuery<'com, C, M> { 81 | VerifierQuery { 82 | point, 83 | eval, 84 | commitment: CommitmentReference::MSM(msm), 85 | } 86 | } 87 | } 88 | 89 | /// A polynomial query at a point 90 | #[derive(Debug, Clone, Copy)] 91 | pub struct VerifierQuery<'com, C: CurveAffine, M: MSM> { 92 | /// Point at which polynomial is queried 93 | pub(crate) point: C::Scalar, 94 | /// Commitment to polynomial 95 | pub(crate) commitment: CommitmentReference<'com, C, M>, 96 | /// Evaluation of polynomial at query point 97 | pub(crate) eval: C::Scalar, 98 | } 99 | 100 | impl<'com, C, M> VerifierQuery<'com, C, M> 101 | where 102 | C: CurveAffine, 103 | M: MSM, 104 | { 105 | /// Create a new verifier query based on a commitment 106 | pub fn new( 107 | point: C::Scalar, 108 | commitment: CommitmentReference<'com, C, M>, 109 | eval: C::Scalar, 110 | ) -> Self { 111 | VerifierQuery { 112 | point, 113 | commitment, 114 | eval, 115 | } 116 | } 117 | } 118 | 119 | #[allow(clippy::upper_case_acronyms)] 120 | #[derive(Clone, Debug)] 121 | pub enum CommitmentReference<'r, C: CurveAffine, M: MSM> { 122 | Commitment(&'r C), 123 | MSM(&'r M), 124 | } 125 | 126 | impl<'r, C: CurveAffine, M: MSM> Copy for CommitmentReference<'r, C, M> {} 127 | 128 | impl<'r, C: CurveAffine, M: MSM> PartialEq for CommitmentReference<'r, C, M> { 129 | #![allow(ambiguous_wide_pointer_comparisons)] 130 | fn eq(&self, other: &Self) -> bool { 131 | match (self, other) { 132 | (&CommitmentReference::Commitment(a), &CommitmentReference::Commitment(b)) => { 133 | std::ptr::eq(a, b) 134 | } 135 | (&CommitmentReference::MSM(a), &CommitmentReference::MSM(b)) => std::ptr::eq(a, b), 136 | _ => false, 137 | } 138 | } 139 | } 140 | 141 | impl<'com, C: CurveAffine, M: MSM> Query for VerifierQuery<'com, C, M> { 142 | type Eval = C::Scalar; 143 | type Commitment = CommitmentReference<'com, C, M>; 144 | 145 | fn get_point(&self) -> C::Scalar { 146 | self.point 147 | } 148 | fn get_eval(&self) -> C::Scalar { 149 | self.eval 150 | } 151 | fn get_commitment(&self) -> Self::Commitment { 152 | self.commitment 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /book/src/design/proving-system.md: -------------------------------------------------------------------------------- 1 | # Proving system 2 | 3 | The Halo 2 proving system can be broken down into five stages: 4 | 5 | 1. Commit to polynomials encoding the main components of the circuit: 6 | - Cell assignments. 7 | - Permuted values and products for each lookup argument. 8 | - Equality constraint permutations. 9 | 2. Construct the vanishing argument to constrain all circuit relations to zero: 10 | - Standard and custom gates. 11 | - Lookup argument rules. 12 | - Equality constraint permutation rules. 13 | 3. Evaluate the above polynomials at all necessary points: 14 | - All relative rotations used by custom gates across all columns. 15 | - Vanishing argument pieces. 16 | 4. Construct the multipoint opening argument to check that all evaluations are consistent 17 | with their respective commitments. 18 | 5. Run the inner product argument to create a polynomial commitment opening proof for the 19 | multipoint opening argument polynomial. 20 | 21 | These stages are presented in turn across this section of the book. 22 | 23 | ## Example 24 | 25 | To aid our explanations, we will at times refer to the following example constraint 26 | system: 27 | 28 | - Four advice columns $a, b, c, d$. 29 | - One fixed column $f$. 30 | - Three custom gates: 31 | - $a \cdot b \cdot c_{-1} - d = 0$ 32 | - $f_{-1} \cdot c = 0$ 33 | - $f \cdot d \cdot a = 0$ 34 | 35 | ## tl;dr 36 | 37 | The table below provides a (probably too) succinct description of the Halo 2 protocol. 38 | This description will likely be replaced by the Halo 2 paper and security proof, but for 39 | now serves as a summary of the following sub-sections. 40 | 41 | | Prover | | Verifier | 42 | | --------------------------------------------------------------------------- | ------- | ---------------------------------- | 43 | | | $\larr$ | $t(X) = (X^n - 1)$ | 44 | | | $\larr$ | $F = [F_0, F_1, \dots, F_{m - 1}]$ | 45 | | $\mathbf{A} = [A_0, A_1, \dots, A_{m - 1}]$ | $\rarr$ | | 46 | | | $\larr$ | $\theta$ | 47 | | $\mathbf{L} = [(A'_0, S'_0), \dots, (A'_{m - 1}, S'_{m - 1})]$ | $\rarr$ | | 48 | | | $\larr$ | $\beta, \gamma$ | 49 | | $\mathbf{Z_P} = [Z_{P,0}, Z_{P,1}, \ldots]$ | $\rarr$ | | 50 | | $\mathbf{Z_L} = [Z_{L,0}, Z_{L,1}, \ldots]$ | $\rarr$ | | 51 | | | $\larr$ | $y$ | 52 | | $h(X) = \frac{\text{gate}_0(X) + \dots + y^i \cdot \text{gate}_i(X)}{t(X)}$ | | | 53 | | $h(X) = h_0(X) + \dots + X^{n(d-1)} h_{d-1}(X)$ | | | 54 | | $\mathbf{H} = [H_0, H_1, \dots, H_{d-1}]$ | $\rarr$ | | 55 | | | $\larr$ | $x$ | 56 | | $evals = [A_0(x), \dots, H_{d - 1}(x)]$ | $\rarr$ | | 57 | | | | Checks $h(x)$ | 58 | | | $\larr$ | $x_1, x_2$ | 59 | | Constructs $h'(X)$ multipoint opening poly | | | 60 | | $U = \text{Commit}(h'(X))$ | $\rarr$ | | 61 | | | $\larr$ | $x_3$ | 62 | | $\mathbf{q}_\text{evals} = [Q_0(x_3), Q_1(x_3), \dots]$ | $\rarr$ | | 63 | | $u_\text{eval} = U(x_3)$ | $\rarr$ | | 64 | | | $\larr$ | $x_4$ | 65 | 66 | Then the prover and verifier: 67 | 68 | - Construct $\text{finalPoly}(X)$ as a linear combination of $\mathbf{Q}$ and $U$ using 69 | powers of $x_4$; 70 | - Construct $\text{finalPolyEval}$ as the equivalent linear combination of 71 | $\mathbf{q}_\text{evals}$ and $u_\text{eval}$; and 72 | - Perform $\text{InnerProduct}(\text{finalPoly}(X), x_3, \text{finalPolyEval}).$ 73 | 74 | > TODO: Write up protocol components that provide zero-knowledge. 75 | -------------------------------------------------------------------------------- /halo2_frontend/src/dev/util.rs: -------------------------------------------------------------------------------- 1 | use group::ff::Field; 2 | use std::collections::BTreeMap; 3 | 4 | use super::{metadata, CellValue, InstanceValue, Value}; 5 | use crate::plonk::{ 6 | AdviceQuery, Column, ColumnType, Expression, FixedQuery, Gate, InstanceQuery, VirtualCell, 7 | }; 8 | use halo2_middleware::circuit::Any; 9 | use halo2_middleware::poly::Rotation; 10 | 11 | pub(crate) struct AnyQuery { 12 | /// Query index 13 | pub index: Option, 14 | /// Column type 15 | pub column_type: Any, 16 | /// Column index 17 | pub column_index: usize, 18 | /// Rotation of this query 19 | pub rotation: Rotation, 20 | } 21 | 22 | impl From for AnyQuery { 23 | fn from(query: FixedQuery) -> Self { 24 | Self { 25 | index: query.index, 26 | column_type: Any::Fixed, 27 | column_index: query.column_index, 28 | rotation: query.rotation, 29 | } 30 | } 31 | } 32 | 33 | impl From for AnyQuery { 34 | fn from(query: AdviceQuery) -> Self { 35 | Self { 36 | index: query.index, 37 | column_type: Any::Advice, 38 | column_index: query.column_index, 39 | rotation: query.rotation, 40 | } 41 | } 42 | } 43 | 44 | impl From for AnyQuery { 45 | fn from(query: InstanceQuery) -> Self { 46 | Self { 47 | index: query.index, 48 | column_type: Any::Instance, 49 | column_index: query.column_index, 50 | rotation: query.rotation, 51 | } 52 | } 53 | } 54 | 55 | pub(super) fn format_value(v: F) -> String { 56 | if v.is_zero_vartime() { 57 | "0".into() 58 | } else if v == F::ONE { 59 | "1".into() 60 | } else if v == -F::ONE { 61 | "-1".into() 62 | } else { 63 | // Format value as hex. 64 | let s = format!("{v:?}"); 65 | // Remove leading zeroes. 66 | let s = s.strip_prefix("0x").unwrap(); 67 | let s = s.trim_start_matches('0'); 68 | format!("0x{s}") 69 | } 70 | } 71 | 72 | pub(super) fn load<'a, F: Field, T: ColumnType, Q: Into + Copy>( 73 | n: i32, 74 | row: i32, 75 | queries: &'a [(Column, Rotation)], 76 | cells: &'a [Vec>], 77 | ) -> impl Fn(Q) -> Value + 'a { 78 | move |query| { 79 | let (column, at) = &queries[query.into().index.unwrap()]; 80 | let resolved_row = (row + at.0) % n; 81 | cells[column.index()][resolved_row as usize].into() 82 | } 83 | } 84 | 85 | pub(super) fn load_instance<'a, F: Field, T: ColumnType, Q: Into + Copy>( 86 | n: i32, 87 | row: i32, 88 | queries: &'a [(Column, Rotation)], 89 | cells: &'a [Vec>], 90 | ) -> impl Fn(Q) -> Value + 'a { 91 | move |query| { 92 | let (column, at) = &queries[query.into().index.unwrap()]; 93 | let resolved_row = (row + at.0) % n; 94 | let cell = &cells[column.index()][resolved_row as usize]; 95 | Value::Real(cell.value()) 96 | } 97 | } 98 | 99 | fn cell_value<'a, F: Field, Q: Into + Copy>( 100 | virtual_cells: &'a [VirtualCell], 101 | load: impl Fn(Q) -> Value + 'a, 102 | ) -> impl Fn(Q) -> BTreeMap + 'a { 103 | move |query| { 104 | let AnyQuery { 105 | column_type, 106 | column_index, 107 | rotation, 108 | .. 109 | } = query.into(); 110 | virtual_cells 111 | .iter() 112 | .find(|c| { 113 | c.column.column_type() == &column_type 114 | && c.column.index() == column_index 115 | && c.rotation == rotation 116 | }) 117 | // None indicates a selector, which we don't bother showing. 118 | .map(|cell| { 119 | ( 120 | cell.clone().into(), 121 | match load(query) { 122 | Value::Real(v) => format_value(v), 123 | Value::Poison => unreachable!(), 124 | }, 125 | ) 126 | }) 127 | .into_iter() 128 | .collect() 129 | } 130 | } 131 | 132 | pub(super) fn cell_values<'a, F: Field>( 133 | gate: &Gate, 134 | poly: &Expression, 135 | load_fixed: impl Fn(FixedQuery) -> Value + 'a, 136 | load_advice: impl Fn(AdviceQuery) -> Value + 'a, 137 | load_instance: impl Fn(InstanceQuery) -> Value + 'a, 138 | ) -> Vec<(metadata::VirtualCell, String)> { 139 | let virtual_cells = gate.queried_cells(); 140 | let cell_values = poly.evaluate( 141 | &|_| BTreeMap::default(), 142 | &|_| panic!("virtual selectors are removed during optimization"), 143 | &cell_value(virtual_cells, load_fixed), 144 | &cell_value(virtual_cells, load_advice), 145 | &cell_value(virtual_cells, load_instance), 146 | &|_| BTreeMap::default(), 147 | &|a| a, 148 | &|mut a, mut b| { 149 | a.append(&mut b); 150 | a 151 | }, 152 | &|mut a, mut b| { 153 | a.append(&mut b); 154 | a 155 | }, 156 | &|a, _| a, 157 | ); 158 | cell_values.into_iter().collect() 159 | } 160 | -------------------------------------------------------------------------------- /halo2_backend/src/plonk/shuffle/verifier.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use super::Argument; 4 | use crate::{ 5 | arithmetic::CurveAffine, 6 | plonk::circuit::{ExpressionBack, QueryBack, VarBack}, 7 | plonk::{ChallengeGamma, ChallengeTheta, ChallengeX, Error, VerifyingKey}, 8 | poly::{commitment::MSM, VerifierQuery}, 9 | transcript::{EncodedChallenge, TranscriptRead}, 10 | }; 11 | use halo2_middleware::circuit::Any; 12 | use halo2_middleware::ff::Field; 13 | use halo2_middleware::poly::Rotation; 14 | 15 | pub(crate) struct Committed { 16 | product_commitment: C, 17 | } 18 | 19 | pub(crate) struct Evaluated { 20 | committed: Committed, 21 | product_eval: C::Scalar, 22 | product_next_eval: C::Scalar, 23 | } 24 | 25 | pub(in crate::plonk) fn shuffle_read_product_commitment< 26 | F: Field, 27 | C: CurveAffine, 28 | E: EncodedChallenge, 29 | T: TranscriptRead, 30 | >( 31 | transcript: &mut T, 32 | ) -> Result, Error> { 33 | let product_commitment = transcript.read_point()?; 34 | 35 | Ok(Committed { product_commitment }) 36 | } 37 | 38 | impl Committed { 39 | pub(crate) fn evaluate, T: TranscriptRead>( 40 | self, 41 | transcript: &mut T, 42 | ) -> Result, Error> { 43 | let product_eval = transcript.read_scalar()?; 44 | let product_next_eval = transcript.read_scalar()?; 45 | 46 | Ok(Evaluated { 47 | committed: self, 48 | product_eval, 49 | product_next_eval, 50 | }) 51 | } 52 | } 53 | 54 | impl Evaluated { 55 | #[allow(clippy::too_many_arguments)] 56 | pub(in crate::plonk) fn expressions<'a>( 57 | &'a self, 58 | l_0: C::Scalar, 59 | l_last: C::Scalar, 60 | l_blind: C::Scalar, 61 | argument: &'a Argument, 62 | theta: ChallengeTheta, 63 | gamma: ChallengeGamma, 64 | advice_evals: &[C::Scalar], 65 | fixed_evals: &[C::Scalar], 66 | instance_evals: &[C::Scalar], 67 | challenges: &[C::Scalar], 68 | ) -> impl Iterator + 'a { 69 | let active_rows = C::Scalar::ONE - (l_last + l_blind); 70 | 71 | let product_expression = || { 72 | // z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma) 73 | let compress_expressions = |expressions: &[ExpressionBack]| { 74 | expressions 75 | .iter() 76 | .map(|expression| { 77 | expression.evaluate( 78 | &|scalar| scalar, 79 | &|var| match var { 80 | VarBack::Challenge(challenge) => challenges[challenge.index], 81 | VarBack::Query(QueryBack { index, column, .. }) => { 82 | match column.column_type { 83 | Any::Fixed => fixed_evals[index], 84 | Any::Advice => advice_evals[index], 85 | Any::Instance => instance_evals[index], 86 | } 87 | } 88 | }, 89 | &|a| -a, 90 | &|a, b| a + b, 91 | &|a, b| a * b, 92 | ) 93 | }) 94 | .fold(C::Scalar::ZERO, |acc, eval| acc * *theta + eval) 95 | }; 96 | // z(\omega X) (s(X) + \gamma) 97 | let left = self.product_next_eval 98 | * (compress_expressions(&argument.shuffle_expressions) + *gamma); 99 | // z(X) (a(X) + \gamma) 100 | let right = 101 | self.product_eval * (compress_expressions(&argument.input_expressions) + *gamma); 102 | 103 | (left - right) * active_rows 104 | }; 105 | 106 | std::iter::empty() 107 | .chain( 108 | // l_0(X) * (1 - z'(X)) = 0 109 | Some(l_0 * (C::Scalar::ONE - self.product_eval)), 110 | ) 111 | .chain( 112 | // l_last(X) * (z(X)^2 - z(X)) = 0 113 | Some(l_last * (self.product_eval.square() - self.product_eval)), 114 | ) 115 | .chain( 116 | // (1 - (l_last(X) + l_blind(X))) * ( z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)) 117 | Some(product_expression()), 118 | ) 119 | } 120 | 121 | pub(in crate::plonk) fn queries<'r, M: MSM + 'r>( 122 | &'r self, 123 | vk: &'r VerifyingKey, 124 | x: ChallengeX, 125 | ) -> impl Iterator> + Clone { 126 | let x_next = vk.domain.rotate_omega(*x, Rotation::next()); 127 | 128 | iter::empty() 129 | // Open shuffle product commitment at x 130 | .chain(Some(VerifierQuery::new_commitment( 131 | &self.committed.product_commitment, 132 | *x, 133 | self.product_eval, 134 | ))) 135 | // Open shuffle product commitment at \omega x 136 | .chain(Some(VerifierQuery::new_commitment( 137 | &self.committed.product_commitment, 138 | x_next, 139 | self.product_next_eval, 140 | ))) 141 | } 142 | } 143 | --------------------------------------------------------------------------------