diff --git a/snark-verifier/src/cost.rs b/snark-verifier/src/cost.rs index a85d6af4..46bc6145 100644 --- a/snark-verifier/src/cost.rs +++ b/snark-verifier/src/cost.rs @@ -1,11 +1,19 @@ +//! Cost estimation. + use std::ops::Add; +/// Cost of verification. #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Cost { + /// Number of instances. pub num_instance: usize, + /// Number of commitments in proof. pub num_commitment: usize, + /// Number of evaluations in proof. pub num_evaluation: usize, + /// Number of scalar multiplications to perform. pub num_msm: usize, + /// Number of pairings to perform. pub num_pairing: usize, } @@ -22,8 +30,11 @@ impl Add for Cost { } } +/// For estimating cost of a verifier. pub trait CostEstimation { + /// Input for [`CostEstimation::estimate_cost`]. type Input; + /// Estimate cost of verifier given the input. fn estimate_cost(input: &Self::Input) -> Cost; } diff --git a/snark-verifier/src/lib.rs b/snark-verifier/src/lib.rs index c2b9350a..11187659 100644 --- a/snark-verifier/src/lib.rs +++ b/snark-verifier/src/lib.rs @@ -1,6 +1,11 @@ -#![allow(clippy::type_complexity)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::upper_case_acronyms)] +//! Generic (S)NARK verifier. + +#![allow( + clippy::type_complexity, + clippy::too_many_arguments, + clippy::upper_case_acronyms +)] +#![deny(missing_debug_implementations, missing_docs, unsafe_code, rustdoc::all)] pub mod cost; pub mod loader; @@ -9,10 +14,15 @@ pub mod system; pub mod util; pub mod verifier; +/// Error that could happen while verification. #[derive(Clone, Debug)] pub enum Error { + /// Instances that don't match the amount specified in protocol. InvalidInstances, + /// Protocol that is unreasonable for a verifier. InvalidProtocol(String), + /// Assertion failure while verification. AssertionFailure(String), + /// Transcript error. Transcript(std::io::ErrorKind, String), } diff --git a/snark-verifier/src/loader.rs b/snark-verifier/src/loader.rs index 297390d0..897890b1 100644 --- a/snark-verifier/src/loader.rs +++ b/snark-verifier/src/loader.rs @@ -1,3 +1,6 @@ +//! Abstraction of field element and elliptic curve point for generic verifier +//! implementation. + use crate::{ util::{ arithmetic::{CurveAffine, FieldOps, PrimeField}, @@ -15,25 +18,34 @@ pub mod evm; #[cfg(feature = "loader_halo2")] pub mod halo2; +/// Loaded elliptic curve point. pub trait LoadedEcPoint: Clone + Debug + PartialEq { + /// [`Loader`]. type Loader: Loader; + /// Returns [`Loader`]. fn loader(&self) -> &Self::Loader; } +/// Loaded field element. pub trait LoadedScalar: Clone + Debug + PartialEq + FieldOps { + /// [`Loader`]. type Loader: ScalarLoader; + /// Returns [`Loader`]. fn loader(&self) -> &Self::Loader; + /// Returns square. fn square(&self) -> Self { self.clone() * self } + /// Returns inverse if any. fn invert(&self) -> Option { FieldOps::invert(self) } + /// Returns power to exponent. fn pow_const(&self, mut exp: u64) -> Self { assert!(exp > 0); @@ -55,6 +67,7 @@ pub trait LoadedScalar: Clone + Debug + PartialEq + FieldOps { acc } + /// Returns powers up to exponent `n-1`. fn powers(&self, n: usize) -> Vec { iter::once(self.loader().load_one()) .chain( @@ -65,19 +78,25 @@ pub trait LoadedScalar: Clone + Debug + PartialEq + FieldOps { } } +/// Elliptic curve point loader. pub trait EcPointLoader { + /// [`LoadedEcPoint`]. type LoadedEcPoint: LoadedEcPoint; + /// Load a constant elliptic curve point. fn ec_point_load_const(&self, value: &C) -> Self::LoadedEcPoint; + /// Load `identity` as constant. fn ec_point_load_zero(&self) -> Self::LoadedEcPoint { self.ec_point_load_const(&C::identity()) } + /// Load `generator` as constant. fn ec_point_load_one(&self) -> Self::LoadedEcPoint { self.ec_point_load_const(&C::generator()) } + /// Assert lhs and rhs elliptic curve points are equal. fn ec_point_assert_eq( &self, annotation: &str, @@ -85,6 +104,7 @@ pub trait EcPointLoader { rhs: &Self::LoadedEcPoint, ) -> Result<(), Error>; + /// Perform multi-scalar multiplication. fn multi_scalar_multiplication( pairs: &[(&Self::LoadedScalar, &Self::LoadedEcPoint)], ) -> Self::LoadedEcPoint @@ -92,19 +112,25 @@ pub trait EcPointLoader { Self: ScalarLoader; } +/// Field element loader. pub trait ScalarLoader { + /// [`LoadedScalar`]. type LoadedScalar: LoadedScalar; + /// Load a constant field element. fn load_const(&self, value: &F) -> Self::LoadedScalar; + /// Load `zero` as constant. fn load_zero(&self) -> Self::LoadedScalar { self.load_const(&F::zero()) } + /// Load `one` as constant. fn load_one(&self) -> Self::LoadedScalar { self.load_const(&F::one()) } + /// Assert lhs and rhs field elements are equal. fn assert_eq( &self, annotation: &str, @@ -112,6 +138,7 @@ pub trait ScalarLoader { rhs: &Self::LoadedScalar, ) -> Result<(), Error>; + /// Sum field elements with coefficients and constant. fn sum_with_coeff_and_const( &self, values: &[(F, &Self::LoadedScalar)], @@ -140,6 +167,7 @@ pub trait ScalarLoader { .into_owned() } + /// Sum product of field elements with coefficients and constant. fn sum_products_with_coeff_and_const( &self, values: &[(F, &Self::LoadedScalar, &Self::LoadedScalar)], @@ -167,10 +195,12 @@ pub trait ScalarLoader { .unwrap() } + /// Sum field elements with coefficients. fn sum_with_coeff(&self, values: &[(F, &Self::LoadedScalar)]) -> Self::LoadedScalar { self.sum_with_coeff_and_const(values, F::zero()) } + /// Sum field elements and constant. fn sum_with_const(&self, values: &[&Self::LoadedScalar], constant: F) -> Self::LoadedScalar { self.sum_with_coeff_and_const( &values.iter().map(|&value| (F::one(), value)).collect_vec(), @@ -178,10 +208,12 @@ pub trait ScalarLoader { ) } + /// Sum field elements. fn sum(&self, values: &[&Self::LoadedScalar]) -> Self::LoadedScalar { self.sum_with_const(values, F::zero()) } + /// Sum product of field elements with coefficients. fn sum_products_with_coeff( &self, values: &[(F, &Self::LoadedScalar, &Self::LoadedScalar)], @@ -189,6 +221,7 @@ pub trait ScalarLoader { self.sum_products_with_coeff_and_const(values, F::zero()) } + /// Sum product of field elements and constant. fn sum_products_with_const( &self, values: &[(&Self::LoadedScalar, &Self::LoadedScalar)], @@ -203,6 +236,7 @@ pub trait ScalarLoader { ) } + /// Sum product of field elements. fn sum_products( &self, values: &[(&Self::LoadedScalar, &Self::LoadedScalar)], @@ -210,12 +244,14 @@ pub trait ScalarLoader { self.sum_products_with_const(values, F::zero()) } + /// Product of field elements. fn product(&self, values: &[&Self::LoadedScalar]) -> Self::LoadedScalar { values .iter() .fold(self.load_one(), |acc, value| acc * *value) } + /// Batch invert field elements. fn batch_invert<'a>(values: impl IntoIterator) where Self::LoadedScalar: 'a, @@ -226,10 +262,13 @@ pub trait ScalarLoader { } } +/// [`EcPointLoader`] and [`ScalarLoader`] with some helper methods. pub trait Loader: EcPointLoader + ScalarLoader + Clone + Debug { - fn start_cost_metering(&self, _: &str) {} + /// Start cost metering with an `identifier`. + fn start_cost_metering(&self, _identifier: &str) {} + /// End latest started cost metering. fn end_cost_metering(&self) {} } diff --git a/snark-verifier/src/loader/evm.rs b/snark-verifier/src/loader/evm.rs index c11670d8..e942b4a3 100644 --- a/snark-verifier/src/loader/evm.rs +++ b/snark-verifier/src/loader/evm.rs @@ -1,6 +1,8 @@ +//! `Loader` implementation for generating yul code as EVM verifier. + mod code; pub(crate) mod loader; -mod util; +pub(crate) mod util; #[cfg(test)] mod test; @@ -8,7 +10,7 @@ mod test; pub use loader::{EcPoint, EvmLoader, Scalar}; pub use util::{ compile_yul, encode_calldata, estimate_gas, fe_to_u256, modulus, u256_to_fe, Address, - ExecutorBuilder, MemoryChunk, H256, U256, U512, + ExecutorBuilder, H256, U256, U512, }; #[cfg(test)] diff --git a/snark-verifier/src/loader/evm/code.rs b/snark-verifier/src/loader/evm/code.rs index 840d1e67..2fec71d2 100644 --- a/snark-verifier/src/loader/evm/code.rs +++ b/snark-verifier/src/loader/evm/code.rs @@ -44,20 +44,13 @@ impl YulCode { let y_lt_p:bool := lt(y, {base_modulus}) valid := and(x_lt_p, y_lt_p) }} - {{ - let x_is_zero:bool := eq(x, 0) - let y_is_zero:bool := eq(y, 0) - let x_or_y_is_zero:bool := or(x_is_zero, y_is_zero) - let x_and_y_is_not_zero:bool := not(x_or_y_is_zero) - valid := and(x_and_y_is_not_zero, valid) - }} {{ let y_square := mulmod(y, y, {base_modulus}) let x_square := mulmod(x, x, {base_modulus}) let x_cube := mulmod(x_square, x, {base_modulus}) let x_cube_plus_3 := addmod(x_cube, 3, {base_modulus}) - let y_square_eq_x_cube_plus_3:bool := eq(x_cube_plus_3, y_square) - valid := and(y_square_eq_x_cube_plus_3, valid) + let is_affine:bool := eq(x_cube_plus_3, y_square) + valid := and(valid, is_affine) }} }} {} diff --git a/snark-verifier/src/loader/evm/loader.rs b/snark-verifier/src/loader/evm/loader.rs index fdd0ee9c..911625b6 100644 --- a/snark-verifier/src/loader/evm/loader.rs +++ b/snark-verifier/src/loader/evm/loader.rs @@ -48,6 +48,7 @@ impl Value { } } +/// `Loader` implementation for generating yul code as EVM verifier. #[derive(Clone, Debug)] pub struct EvmLoader { base_modulus: U256, @@ -66,6 +67,7 @@ fn hex_encode_u256(value: &U256) -> String { } impl EvmLoader { + /// Initialize a [`EvmLoader`] with base and scalar field. pub fn new() -> Rc where Base: PrimeField, @@ -86,6 +88,7 @@ impl EvmLoader { }) } + /// Returns generated yul code. pub fn yul_code(self: &Rc) -> String { let code = " if not(success) { revert(0, 0) } @@ -98,6 +101,7 @@ impl EvmLoader { ) } + /// Allocates memory chunk with given `size` and returns pointer. pub fn allocate(self: &Rc, size: usize) -> usize { let ptr = *self.ptr.borrow(); *self.ptr.borrow_mut() += size; @@ -137,6 +141,7 @@ impl EvmLoader { } } + /// Calldata load a field element. pub fn calldataload_scalar(self: &Rc, offset: usize) -> Scalar { let ptr = self.allocate(0x20); let code = format!("mstore({ptr:#x}, mod(calldataload({offset:#x}), f_q))"); @@ -144,6 +149,8 @@ impl EvmLoader { self.scalar(Value::Memory(ptr)) } + /// Calldata load an elliptic curve point and validate it's on affine plane. + /// Note that identity will cause the verification to fail. pub fn calldataload_ec_point(self: &Rc, offset: usize) -> EcPoint { let x_ptr = self.allocate(0x40); let y_ptr = x_ptr + 0x20; @@ -164,6 +171,7 @@ impl EvmLoader { self.ec_point(Value::Memory(x_ptr)) } + /// Decode an elliptic curve point from limbs. pub fn ec_point_from_limbs( self: &Rc, x_limbs: [&Scalar; LIMBS], @@ -246,6 +254,8 @@ impl EvmLoader { } } + /// Performs `KECCAK256` on `memory[ptr..ptr+len]` and returns pointer of + /// hash. pub fn keccak256(self: &Rc, ptr: usize, len: usize) -> usize { let hash_ptr = self.allocate(0x20); let code = format!("mstore({hash_ptr:#x}, keccak256({ptr:#x}, {len}))"); @@ -253,6 +263,7 @@ impl EvmLoader { hash_ptr } + /// Copies a field element into given `ptr`. pub fn copy_scalar(self: &Rc, scalar: &Scalar, ptr: usize) { let scalar = self.push(scalar); self.code @@ -260,12 +271,14 @@ impl EvmLoader { .runtime_append(format!("mstore({ptr:#x}, {scalar})")); } + /// Allocates a new field element and copies the given value into it. pub fn dup_scalar(self: &Rc, scalar: &Scalar) -> Scalar { let ptr = self.allocate(0x20); self.copy_scalar(scalar, ptr); self.scalar(Value::Memory(ptr)) } + /// Allocates a new elliptic curve point and copies the given value into it. pub fn dup_ec_point(self: &Rc, value: &EcPoint) -> EcPoint { let ptr = self.allocate(0x40); match value.value { @@ -339,6 +352,7 @@ impl EvmLoader { self.ec_point(Value::Memory(rd_ptr)) } + /// Performs pairing. pub fn pairing( self: &Rc, lhs: &EcPoint, @@ -454,6 +468,7 @@ impl EvmLoader { } } +/// Elliptic curve point. #[derive(Clone)] pub struct EcPoint { loader: Rc, @@ -503,6 +518,7 @@ where } } +/// Field element. #[derive(Clone)] pub struct Scalar { loader: Rc, diff --git a/snark-verifier/src/loader/evm/util.rs b/snark-verifier/src/loader/evm/util.rs index 2c74731a..62792ee8 100644 --- a/snark-verifier/src/loader/evm/util.rs +++ b/snark-verifier/src/loader/evm/util.rs @@ -14,43 +14,43 @@ pub(crate) mod executor; pub use executor::ExecutorBuilder; +/// Memory chunk in EVM. +#[derive(Debug)] pub struct MemoryChunk { ptr: usize, len: usize, } impl MemoryChunk { - pub fn new(ptr: usize) -> Self { + pub(crate) fn new(ptr: usize) -> Self { Self { ptr, len: 0 } } - pub fn ptr(&self) -> usize { + pub(crate) fn ptr(&self) -> usize { self.ptr } - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.len } - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - pub fn end(&self) -> usize { + pub(crate) fn end(&self) -> usize { self.ptr + self.len } - pub fn reset(&mut self, ptr: usize) { + pub(crate) fn reset(&mut self, ptr: usize) { self.ptr = ptr; self.len = 0; } - pub fn extend(&mut self, size: usize) { + pub(crate) fn extend(&mut self, size: usize) { self.len += size; } } -// Assume fields implements traits in crate `ff` always have little-endian representation. +/// Convert a [`PrimeField`] into a [`U256`]. +/// Assuming fields that implement traits in crate `ff` always have +/// little-endian representation. pub fn fe_to_u256(f: F) -> U256 where F: PrimeField, @@ -58,6 +58,7 @@ where U256::from_little_endian(f.to_repr().as_ref()) } +/// Convert a [`U256`] into a [`PrimeField`]. pub fn u256_to_fe(value: U256) -> F where F: PrimeField, @@ -68,6 +69,7 @@ where F::from_repr(repr).unwrap() } +/// Returns modulus of [`PrimeField`] as [`U256`]. pub fn modulus() -> U256 where F: PrimeField, @@ -75,6 +77,7 @@ where U256::from_little_endian((-F::one()).to_repr().as_ref()) + 1 } +/// Encode instances and proof into calldata. pub fn encode_calldata(instances: &[Vec], proof: &[u8]) -> Vec where F: PrimeField, @@ -90,6 +93,7 @@ where .collect() } +/// Estimate gas cost with given [`Cost`]. pub fn estimate_gas(cost: Cost) -> usize { let proof_size = cost.num_commitment * 64 + (cost.num_evaluation + cost.num_instance) * 32; @@ -100,6 +104,7 @@ pub fn estimate_gas(cost: Cost) -> usize { intrinsic_cost + calldata_cost + ec_operation_cost } +/// Compile given yul `code` into deployment bytecode. pub fn compile_yul(code: &str) -> Vec { let mut cmd = Command::new("solc") .stdin(Stdio::piped()) diff --git a/snark-verifier/src/loader/evm/util/executor.rs b/snark-verifier/src/loader/evm/util/executor.rs index 47a80658..de466c08 100644 --- a/snark-verifier/src/loader/evm/util/executor.rs +++ b/snark-verifier/src/loader/evm/util/executor.rs @@ -1,4 +1,5 @@ -//! Copied and modified from https://github.com/foundry-rs/foundry/blob/master/evm/src/executor/mod.rs +//! Copied and modified from +//! use crate::loader::evm::{Address, H256, U256}; use bytes::Bytes; @@ -416,7 +417,6 @@ impl Inspector for Debugger { } } -#[macro_export] macro_rules! call_inspectors { ($id:ident, [ $($inspector:expr),+ ], $call:block) => { $({ @@ -669,16 +669,28 @@ impl Inspector for InspectorStack { } } +/// Call result. +#[derive(Debug)] pub struct RawCallResult { + /// Exit reason pub exit_reason: Return, + /// If the call is reverted or not. pub reverted: bool, + /// Returndata pub result: Bytes, + /// Gas used pub gas_used: u64, + /// Gas refunded pub gas_refunded: u64, + /// Logs emitted during the call pub logs: Vec, + /// Debug information if any pub debug: Option, + /// State changes if any pub state_changeset: Option>, + /// Environment pub env: Env, + /// Output pub out: TransactOut, } @@ -694,6 +706,7 @@ pub struct DeployResult { pub env: Env, } +/// Executor builder. #[derive(Debug, Default)] pub struct ExecutorBuilder { debugger: bool, @@ -701,16 +714,19 @@ pub struct ExecutorBuilder { } impl ExecutorBuilder { + /// Set `debugger`. pub fn set_debugger(mut self, enable: bool) -> Self { self.debugger = enable; self } + /// Set `gas_limit`. pub fn with_gas_limit(mut self, gas_limit: U256) -> Self { self.gas_limit = Some(gas_limit); self } + /// Initialize an `Executor`. pub fn build(self) -> Executor { Executor::new(self.debugger, self.gas_limit.unwrap_or(U256::MAX)) } diff --git a/snark-verifier/src/loader/halo2.rs b/snark-verifier/src/loader/halo2.rs index ce75b00c..93a31a26 100644 --- a/snark-verifier/src/loader/halo2.rs +++ b/snark-verifier/src/loader/halo2.rs @@ -1,3 +1,5 @@ +//! `Loader` implementation for generating verifier in [`halo2_proofs`] circuit. + pub(crate) mod loader; mod shim; @@ -13,7 +15,10 @@ pub use halo2_wrong_ecc; mod util { use halo2_proofs::circuit::Value; + /// Helper methods when dealing with iterator of [`Value`]. pub trait Valuetools: Iterator> { + /// Fold zipped values into accumulator, returns `Value::unknown()` if + /// any is `Value::unknown()`. fn fold_zipped(self, init: B, mut f: F) -> Value where Self: Sized, diff --git a/snark-verifier/src/loader/halo2/loader.rs b/snark-verifier/src/loader/halo2/loader.rs index 67c2b12d..17a51183 100644 --- a/snark-verifier/src/loader/halo2/loader.rs +++ b/snark-verifier/src/loader/halo2/loader.rs @@ -17,6 +17,7 @@ use std::{ rc::Rc, }; +/// `Loader` implementation for generating verifier in [`halo2_proofs`] circuit. #[derive(Debug)] pub struct Halo2Loader<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> { ecc_chip: RefCell, @@ -29,6 +30,8 @@ pub struct Halo2Loader<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> { } impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> Halo2Loader<'a, C, EccChip> { + /// Initialize a [`Halo2Loader`] with given [`EccInstructions`] and + /// [`EccInstructions::Context`]. pub fn new(ecc_chip: EccChip, ctx: EccChip::Context) -> Rc { Rc::new(Self { ecc_chip: RefCell::new(ecc_chip), @@ -41,22 +44,27 @@ impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> Halo2Loader<'a, C, Ecc }) } + /// Into [`EccInstructions::Context`]. pub fn into_ctx(self) -> EccChip::Context { self.ctx.into_inner() } + /// Returns reference of [`EccInstructions`]. pub fn ecc_chip(&self) -> Ref { self.ecc_chip.borrow() } + /// Returns reference of [`EccInstructions::ScalarChip`]. pub fn scalar_chip(&self) -> Ref { Ref::map(self.ecc_chip(), |ecc_chip| ecc_chip.scalar_chip()) } + /// Returns reference of [`EccInstructions::Context`]. pub fn ctx(&self) -> Ref { self.ctx.borrow() } + /// Returns mutable reference of [`EccInstructions::Context`]. pub fn ctx_mut(&self) -> RefMut<'_, EccChip::Context> { self.ctx.borrow_mut() } @@ -67,6 +75,7 @@ impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> Halo2Loader<'a, C, Ecc .unwrap() } + /// Assign a field element witness. pub fn assign_scalar( self: &Rc, scalar: circuit::Value, @@ -78,6 +87,7 @@ impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> Halo2Loader<'a, C, Ecc self.scalar_from_assigned(assigned) } + /// Returns [`Scalar`] with assigned field element. pub fn scalar_from_assigned( self: &Rc, assigned: EccChip::AssignedScalar, @@ -104,6 +114,7 @@ impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> Halo2Loader<'a, C, Ecc .unwrap() } + /// Assign an elliptic curve point witness. pub fn assign_ec_point( self: &Rc, ec_point: circuit::Value, @@ -115,6 +126,7 @@ impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> Halo2Loader<'a, C, Ecc self.ec_point_from_assigned(assigned) } + /// Returns [`EcPoint`] with assigned elliptic curve point. pub fn ec_point_from_assigned( self: &Rc, assigned: EccChip::AssignedEcPoint, @@ -307,6 +319,7 @@ impl Value { } } +/// Field element #[derive(Clone)] pub struct Scalar<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> { loader: Rc>, @@ -315,10 +328,12 @@ pub struct Scalar<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> { } impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> Scalar<'a, C, EccChip> { + /// Returns reference of [`Rc`] pub fn loader(&self) -> &Rc> { &self.loader } + /// Returns reference of [`EccInstructions::AssignedScalar`]. pub fn assigned(&self) -> Ref { if let Some(constant) = self.maybe_const() { *self.value.borrow_mut() = Value::Assigned(self.loader.assign_const_scalar(constant)) @@ -469,6 +484,7 @@ impl<'a, 'b, C: CurveAffine, EccChip: EccInstructions<'a, C>> MulAssign<&'b Self } } +/// Elliptic curve point #[derive(Clone)] pub struct EcPoint<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> { loader: Rc>, @@ -477,6 +493,7 @@ pub struct EcPoint<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> { } impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> EcPoint<'a, C, EccChip> { + /// Into [`EccInstructions::AssignedEcPoint`]. pub fn into_assigned(self) -> EccChip::AssignedEcPoint { match self.value.into_inner() { Value::Constant(constant) => self.loader.assign_const_ec_point(constant), @@ -484,6 +501,7 @@ impl<'a, C: CurveAffine, EccChip: EccInstructions<'a, C>> EcPoint<'a, C, EccChip } } + /// Returns reference of [`EccInstructions::AssignedEcPoint`]. pub fn assigned(&self) -> Ref { if let Some(constant) = self.maybe_const() { *self.value.borrow_mut() = Value::Assigned(self.loader.assign_const_ec_point(constant)) diff --git a/snark-verifier/src/loader/halo2/shim.rs b/snark-verifier/src/loader/halo2/shim.rs index 1d7b6258..7ae65496 100644 --- a/snark-verifier/src/loader/halo2/shim.rs +++ b/snark-verifier/src/loader/halo2/shim.rs @@ -5,29 +5,39 @@ use halo2_proofs::{ }; use std::{fmt::Debug, ops::Deref}; +/// Context for instructions. pub trait Context: Debug { + /// Enforce equality constraint on lhs and rhs. fn constrain_equal(&mut self, lhs: Cell, rhs: Cell) -> Result<(), Error>; + /// Returns current region offset. fn offset(&self) -> usize; } +/// Instructions to handle field element operations. pub trait IntegerInstructions<'a, F: FieldExt>: Clone + Debug { + /// Context. type Context: Context; + /// Assigned cell. type AssignedCell: Clone + Debug; + /// Assigned integer. type AssignedInteger: Clone + Debug; + /// Assign an integer witness. fn assign_integer( &self, ctx: &mut Self::Context, integer: Value, ) -> Result; + /// Assign an integer constant. fn assign_constant( &self, ctx: &mut Self::Context, integer: F, ) -> Result; + /// Sum integers with coefficients and constant. fn sum_with_coeff_and_const( &self, ctx: &mut Self::Context, @@ -35,6 +45,7 @@ pub trait IntegerInstructions<'a, F: FieldExt>: Clone + Debug { constant: F::Scalar, ) -> Result; + /// Sum product of integers with coefficients and constant. fn sum_products_with_coeff_and_const( &self, ctx: &mut Self::Context, @@ -46,6 +57,7 @@ pub trait IntegerInstructions<'a, F: FieldExt>: Clone + Debug { constant: F::Scalar, ) -> Result; + /// Returns `lhs - rhs`. fn sub( &self, ctx: &mut Self::Context, @@ -53,18 +65,21 @@ pub trait IntegerInstructions<'a, F: FieldExt>: Clone + Debug { rhs: &Self::AssignedInteger, ) -> Result; + /// Returns `-value`. fn neg( &self, ctx: &mut Self::Context, value: &Self::AssignedInteger, ) -> Result; + /// Returns `1/value`. fn invert( &self, ctx: &mut Self::Context, value: &Self::AssignedInteger, ) -> Result; + /// Enforce `lhs` and `rhs` are equal. fn assert_equal( &self, ctx: &mut Self::Context, @@ -73,8 +88,11 @@ pub trait IntegerInstructions<'a, F: FieldExt>: Clone + Debug { ) -> Result<(), Error>; } +/// Instructions to handle elliptic curve point operations. pub trait EccInstructions<'a, C: CurveAffine>: Clone + Debug { + /// Context type Context: Context; + /// [`IntegerInstructions`] to handle scalar field operation. type ScalarChip: IntegerInstructions< 'a, C::Scalar, @@ -82,24 +100,31 @@ pub trait EccInstructions<'a, C: CurveAffine>: Clone + Debug { AssignedCell = Self::AssignedCell, AssignedInteger = Self::AssignedScalar, >; + /// Assigned cell. type AssignedCell: Clone + Debug; + /// Assigned scalar field element. type AssignedScalar: Clone + Debug; + /// Assigned elliptic curve point. type AssignedEcPoint: Clone + Debug; + /// Returns reference of [`EccInstructions::ScalarChip`]. fn scalar_chip(&self) -> &Self::ScalarChip; + /// Assign a elliptic curve point constant. fn assign_constant( &self, ctx: &mut Self::Context, ec_point: C, ) -> Result; + /// Assign a elliptic curve point witness. fn assign_point( &self, ctx: &mut Self::Context, ec_point: Value, ) -> Result; + /// Sum elliptic curve points and constant. fn sum_with_const( &self, ctx: &mut Self::Context, @@ -107,12 +132,14 @@ pub trait EccInstructions<'a, C: CurveAffine>: Clone + Debug { constant: C, ) -> Result; + /// Perform fixed base multi-scalar multiplication. fn fixed_base_msm( &mut self, ctx: &mut Self::Context, pairs: &[(impl Deref, C)], ) -> Result; + /// Perform variable base multi-scalar multiplication. fn variable_base_msm( &mut self, ctx: &mut Self::Context, @@ -122,6 +149,7 @@ pub trait EccInstructions<'a, C: CurveAffine>: Clone + Debug { )], ) -> Result; + /// Enforce `lhs` and `rhs` are equal. fn assert_equal( &self, ctx: &mut Self::Context, diff --git a/snark-verifier/src/loader/native.rs b/snark-verifier/src/loader/native.rs index 6fce383a..18d27c50 100644 --- a/snark-verifier/src/loader/native.rs +++ b/snark-verifier/src/loader/native.rs @@ -1,3 +1,5 @@ +//! `Loader` implementation in native rust. + use crate::{ loader::{EcPointLoader, LoadedEcPoint, LoadedScalar, Loader, ScalarLoader}, util::arithmetic::{Curve, CurveAffine, FieldOps, PrimeField}, @@ -7,9 +9,12 @@ use lazy_static::lazy_static; use std::fmt::Debug; lazy_static! { + /// NativeLoader instance for [`LoadedEcPoint::loader`] and + /// [`LoadedScalar::loader`] referencing. pub static ref LOADER: NativeLoader = NativeLoader; } +/// `Loader` implementation in native rust. #[derive(Clone, Debug)] pub struct NativeLoader; diff --git a/snark-verifier/src/pcs.rs b/snark-verifier/src/pcs.rs index 45444a07..535ebedf 100644 --- a/snark-verifier/src/pcs.rs +++ b/snark-verifier/src/pcs.rs @@ -1,3 +1,5 @@ +//! Verfieirs for polynomial commitment schemes. + use crate::{ loader::{native::NativeLoader, Loader}, util::{ @@ -13,14 +15,29 @@ use std::{fmt::Debug, marker::PhantomData}; pub mod ipa; pub mod kzg; +/// Query to an oracle. +/// It assumes all queries are based on the same point, but with some `shift`. #[derive(Clone, Debug)] pub struct Query { + /// Index of polynomial to query pub poly: usize, + /// Shift of the query point. pub shift: F, + /// Evaluation read from transcript. pub eval: T, } impl Query { + /// Initialize [`Query`] without evaluation. + pub fn new(poly: usize, shift: F) -> Self { + Self { + poly, + shift, + eval: (), + } + } + + /// Returns [`Query`] with evaluation. pub fn with_evaluation(self, eval: T) -> Query { Query { poly: self.poly, @@ -30,15 +47,20 @@ impl Query { } } +/// Polynomial commitment scheme verifier. pub trait PolynomialCommitmentScheme: Clone + Debug where C: CurveAffine, L: Loader, { + /// Verifying key. type VerifyingKey: Clone + Debug; + /// Structured proof read from transcript. type Proof: Clone + Debug; + /// Output of verification. type Output: Clone + Debug; + /// Read [`PolynomialCommitmentScheme::Proof`] from transcript. fn read_proof( vk: &Self::VerifyingKey, queries: &[Query], @@ -47,6 +69,7 @@ where where T: TranscriptRead; + /// Verify [`PolynomialCommitmentScheme::Proof`] and output [`PolynomialCommitmentScheme::Output`]. fn verify( vk: &Self::VerifyingKey, commitments: &[Msm], @@ -56,15 +79,20 @@ where ) -> Result; } +/// Accumulation scheme verifier. pub trait AccumulationScheme where C: CurveAffine, L: Loader, { + /// Accumulator to be accumulated. type Accumulator: Clone + Debug; + /// Verifying key. type VerifyingKey: Clone + Debug; + /// Structured proof read from transcript. type Proof: Clone + Debug; + /// Read a [`AccumulationScheme::Proof`] from transcript. fn read_proof( vk: &Self::VerifyingKey, instances: &[Self::Accumulator], @@ -73,6 +101,9 @@ where where T: TranscriptRead; + /// Verify old [`AccumulationScheme::Accumulator`]s are accumulated properly + /// into a new one with the [`AccumulationScheme::Proof`], and returns the + /// new one as output. fn verify( vk: &Self::VerifyingKey, instances: &[Self::Accumulator], @@ -80,27 +111,40 @@ where ) -> Result; } +/// Accumulation scheme decider. +/// When accumulation is going to end, the decider will perform the check if the +/// final accumulator is valid or not, where the check is usually much more +/// expensive than accumulation verification. pub trait AccumulationDecider: AccumulationScheme where C: CurveAffine, L: Loader, { + /// Deciding key. The key for decider for perform the final accumulator + /// check. type DecidingKey: Clone + Debug; + /// Decide if a [`AccumulationScheme::Accumulator`] is valid. fn decide(dk: &Self::DecidingKey, accumulator: Self::Accumulator) -> Result<(), Error>; + /// Decide if all [`AccumulationScheme::Accumulator`]s are valid. fn decide_all( dk: &Self::DecidingKey, accumulators: Vec, ) -> Result<(), Error>; } +/// Accumulation scheme prover. pub trait AccumulationSchemeProver: AccumulationScheme where C: CurveAffine, { + /// Proving key. type ProvingKey: Clone + Debug; + /// Create a proof that argues if old [`AccumulationScheme::Accumulator`]s + /// are properly accumulated into the new one, and returns the new one as + /// output. fn create_proof( pk: &Self::ProvingKey, instances: &[Self::Accumulator], @@ -112,13 +156,17 @@ where R: Rng; } +/// Accumulator encoding. pub trait AccumulatorEncoding: Clone + Debug where C: CurveAffine, L: Loader, { + /// Accumulator to be encoded. type Accumulator: Clone + Debug; + /// Decode an [`AccumulatorEncoding::Accumulator`] from serveral + /// [`crate::loader::ScalarLoader::LoadedScalar`]s. fn from_repr(repr: &[&L::LoadedScalar]) -> Result; } diff --git a/snark-verifier/src/pcs/ipa.rs b/snark-verifier/src/pcs/ipa.rs index 249e0f0d..c782c65f 100644 --- a/snark-verifier/src/pcs/ipa.rs +++ b/snark-verifier/src/pcs/ipa.rs @@ -1,3 +1,6 @@ +//! Inner product argument polynomial commitment scheme and accumulation scheme. +//! The notations are following . + use crate::{ loader::{native::NativeLoader, LoadedScalar, Loader, ScalarLoader}, util::{ @@ -25,6 +28,7 @@ pub use accumulator::IpaAccumulator; pub use decider::IpaDecidingKey; pub use multiopen::{Bgh19, Bgh19Proof}; +/// Inner product argument polynomial commitment scheme. #[derive(Clone, Debug)] pub struct Ipa(PhantomData); @@ -32,6 +36,7 @@ impl Ipa where C: CurveAffine, { + /// Create an inner product argument. pub fn create_proof( pk: &IpaProvingKey, p: &[C::Scalar], @@ -117,6 +122,7 @@ where Ok(IpaAccumulator::new(xi, bases[0])) } + /// Read [`IpaProof`] from transcript. pub fn read_proof>( svk: &IpaSuccinctVerifyingKey, transcript: &mut T, @@ -127,6 +133,7 @@ where IpaProof::read(svk, transcript) } + /// Perform the succinct check of the proof and returns [`IpaAccumulator`]. pub fn succinct_verify>( svk: &IpaSuccinctVerifyingKey, commitment: &Msm, @@ -176,31 +183,41 @@ where } } +/// Inner product argument proving key. #[derive(Clone, Debug)] pub struct IpaProvingKey { + /// Working domain. pub domain: Domain, + /// $\mathbb{G}$ pub g: Vec, + /// $H$ pub h: C, + /// $S$ pub s: Option, } impl IpaProvingKey { + /// Initialize an [`IpaProvingKey`]. pub fn new(domain: Domain, g: Vec, h: C, s: Option) -> Self { Self { domain, g, h, s } } + /// Returns if it supports zero-knowledge. pub fn zk(&self) -> bool { self.s.is_some() } + /// Returns [`IpaSuccinctVerifyingKey`]. pub fn svk(&self) -> IpaSuccinctVerifyingKey { IpaSuccinctVerifyingKey::new(self.domain.clone(), self.g[0], self.h, self.s) } + /// Returns [`IpaDecidingKey`]. pub fn dk(&self) -> IpaDecidingKey { IpaDecidingKey::new(self.svk(), self.g.clone()) } + /// Commit a polynomial into with a randomizer if any. pub fn commit(&self, poly: &Polynomial, omega: Option) -> C { let mut c = multi_scalar_multiplication(&poly[..], &self.g); match (self.s, omega) { @@ -214,7 +231,7 @@ impl IpaProvingKey { impl IpaProvingKey { #[cfg(test)] - pub fn rand(k: usize, zk: bool, mut rng: R) -> Self { + pub(crate) fn rand(k: usize, zk: bool, mut rng: R) -> Self { use crate::util::arithmetic::{root_of_unity, Group}; let domain = Domain::new(k, root_of_unity(k)); @@ -231,24 +248,32 @@ impl IpaProvingKey { } } +/// Inner product argument succinct verifying key. #[derive(Clone, Debug)] pub struct IpaSuccinctVerifyingKey { + /// Working domain. pub domain: Domain, + /// $G_0$ pub g: C, + /// $H$ pub h: C, + /// $S$ pub s: Option, } impl IpaSuccinctVerifyingKey { + /// Initialize an [`IpaSuccinctVerifyingKey`]. pub fn new(domain: Domain, g: C, h: C, s: Option) -> Self { Self { domain, g, h, s } } + /// Returns if it supports zero-knowledge. pub fn zk(&self) -> bool { self.s.is_some() } } +/// Inner product argument #[derive(Clone, Debug)] pub struct IpaProof where @@ -268,7 +293,7 @@ where C: CurveAffine, L: Loader, { - pub fn new( + fn new( c_bar_alpha: Option<(L::LoadedEcPoint, L::LoadedScalar)>, omega_prime: Option, xi_0: L::LoadedScalar, @@ -286,6 +311,7 @@ where } } + /// Read [`crate::pcs::AccumulationScheme::Proof`] from transcript. pub fn read(svk: &IpaSuccinctVerifyingKey, transcript: &mut T) -> Result where T: TranscriptRead, @@ -321,10 +347,12 @@ where }) } + /// Returns $\{\xi_0, \xi_1, ...\}$. pub fn xi(&self) -> Vec { self.rounds.iter().map(|round| round.xi.clone()).collect() } + /// Returns $\{\xi_0^{-1}, \xi_1^{-1}, ...\}$. pub fn xi_inv(&self) -> Vec { let mut xi_inv = self.xi().into_iter().map(Fraction::one_over).collect_vec(); L::batch_invert(xi_inv.iter_mut().filter_map(Fraction::denom_mut)); @@ -337,7 +365,7 @@ where } #[derive(Clone, Debug)] -pub struct Round +struct Round where C: CurveAffine, L: Loader, @@ -352,12 +380,12 @@ where C: CurveAffine, L: Loader, { - pub fn new(l: L::LoadedEcPoint, r: L::LoadedEcPoint, xi: L::LoadedScalar) -> Self { + fn new(l: L::LoadedEcPoint, r: L::LoadedEcPoint, xi: L::LoadedScalar) -> Self { Self { l, r, xi } } } -pub fn h_eval>(xi: &[T], z: &T) -> T { +fn h_eval>(xi: &[T], z: &T) -> T { let loader = z.loader(); let one = loader.load_one(); loader.product( @@ -370,7 +398,7 @@ pub fn h_eval>(xi: &[T], z: &T) -> T { ) } -pub fn h_coeffs(xi: &[F], scalar: F) -> Vec { +fn h_coeffs(xi: &[F], scalar: F) -> Vec { assert!(!xi.is_empty()); let mut coeffs = vec![F::zero(); 1 << xi.len()]; @@ -406,7 +434,7 @@ mod test { #[test] fn test_ipa() { type Ipa = ipa::Ipa; - type IpaAs = ipa::IpaAs; + type IpaAs = ipa::IpaAs; let k = 10; let mut rng = OsRng; diff --git a/snark-verifier/src/pcs/ipa/accumulation.rs b/snark-verifier/src/pcs/ipa/accumulation.rs index 888e11aa..7da4197a 100644 --- a/snark-verifier/src/pcs/ipa/accumulation.rs +++ b/snark-verifier/src/pcs/ipa/accumulation.rs @@ -18,8 +18,10 @@ use crate::{ use rand::Rng; use std::{array, fmt::Debug, iter, marker::PhantomData}; +/// Inner product argument accumulation scheme. The second generic `MOS` stands +/// for different kind of multi-open scheme. #[derive(Clone, Debug)] -pub struct IpaAs(PhantomData<(C, MOS)>); +pub struct IpaAs(PhantomData<(C, MOS)>); impl AccumulationScheme for IpaAs where @@ -76,6 +78,7 @@ where } } +/// Inner product argument accumulation scheme proof. #[derive(Clone, Debug)] pub struct IpaAsProof where @@ -236,7 +239,7 @@ mod test { #[test] fn test_ipa_as() { type Ipa = ipa::Ipa; - type IpaAs = ipa::IpaAs; + type IpaAs = ipa::IpaAs; let k = 10; let zk = true; diff --git a/snark-verifier/src/pcs/ipa/accumulator.rs b/snark-verifier/src/pcs/ipa/accumulator.rs index 27d9d5c7..fc8d9fd7 100644 --- a/snark-verifier/src/pcs/ipa/accumulator.rs +++ b/snark-verifier/src/pcs/ipa/accumulator.rs @@ -1,12 +1,15 @@ use crate::{loader::Loader, util::arithmetic::CurveAffine}; +/// Inner product argument accumulator. #[derive(Clone, Debug)] pub struct IpaAccumulator where C: CurveAffine, L: Loader, { + /// $\xi$. pub xi: Vec, + /// $U$. pub u: L::LoadedEcPoint, } @@ -15,6 +18,7 @@ where C: CurveAffine, L: Loader, { + /// Initialize a [`IpaAccumulator`]. pub fn new(xi: Vec, u: L::LoadedEcPoint) -> Self { Self { xi, u } } diff --git a/snark-verifier/src/pcs/ipa/decider.rs b/snark-verifier/src/pcs/ipa/decider.rs index db7b42fb..933480f3 100644 --- a/snark-verifier/src/pcs/ipa/decider.rs +++ b/snark-verifier/src/pcs/ipa/decider.rs @@ -1,12 +1,15 @@ use crate::{pcs::ipa::IpaSuccinctVerifyingKey, util::arithmetic::CurveAffine}; +/// Inner product argument deciding key. #[derive(Clone, Debug)] pub struct IpaDecidingKey { svk: IpaSuccinctVerifyingKey, + /// Committing key. g: Vec, } impl IpaDecidingKey { + /// Initialize an [`IpaDecidingKey`]. pub fn new(svk: IpaSuccinctVerifyingKey, g: Vec) -> Self { Self { svk, g } } diff --git a/snark-verifier/src/pcs/ipa/multiopen/bgh19.rs b/snark-verifier/src/pcs/ipa/multiopen/bgh19.rs index 6f19a44a..a852267a 100644 --- a/snark-verifier/src/pcs/ipa/multiopen/bgh19.rs +++ b/snark-verifier/src/pcs/ipa/multiopen/bgh19.rs @@ -5,7 +5,7 @@ use crate::{ PolynomialCommitmentScheme, Query, }, util::{ - arithmetic::{ilog2, CurveAffine, FieldExt, Fraction}, + arithmetic::{CurveAffine, FieldExt, Fraction}, msm::Msm, transcript::TranscriptRead, Itertools, @@ -18,6 +18,9 @@ use std::{ marker::PhantomData, }; +/// Verifier of multi-open inner product argument. It is for the implementation +/// in [`halo2_proofs`], which is previously +/// . #[derive(Clone, Debug)] pub struct Bgh19; @@ -92,6 +95,7 @@ where } } +/// Structured proof of [`Bgh19`]. #[derive(Clone, Debug)] pub struct Bgh19Proof where @@ -222,9 +226,12 @@ where .sorted() .dedup(); - let size = 2.max( - ilog2((sets.iter().map(|set| set.shifts.len()).max().unwrap() - 1).next_power_of_two()) + 1, - ); + let size = sets + .iter() + .map(|set| set.shifts.len()) + .chain(Some(2)) + .max() + .unwrap(); let powers_of_x = x.powers(size); let x_3_minus_x_shift_i = BTreeMap::from_iter( superset.map(|shift| (shift, x_3.clone() - x.clone() * loader.load_const(&shift))), @@ -323,26 +330,15 @@ where .collect_vec(); let x = &powers_of_x[1].clone(); - let x_pow_k_minus_one = { - let k_minus_one = shifts.len() - 1; - powers_of_x - .iter() - .enumerate() - .skip(1) - .filter_map(|(i, power_of_x)| { - (k_minus_one & (1 << i) == 1).then(|| power_of_x.clone()) - }) - .reduce(|acc, value| acc * value) - .unwrap_or_else(|| loader.load_one()) - }; + let x_pow_k_minus_one = &powers_of_x[shifts.len() - 1]; let barycentric_weights = shifts .iter() .zip(normalized_ell_primes.iter()) .map(|(shift, normalized_ell_prime)| { loader.sum_products_with_coeff(&[ - (*normalized_ell_prime, &x_pow_k_minus_one, x_3), - (-(*normalized_ell_prime * shift), &x_pow_k_minus_one, x), + (*normalized_ell_prime, x_pow_k_minus_one, x_3), + (-(*normalized_ell_prime * shift), x_pow_k_minus_one, x), ]) }) .map(Fraction::one_over) diff --git a/snark-verifier/src/pcs/kzg.rs b/snark-verifier/src/pcs/kzg.rs index 80d7ab63..8f416ee3 100644 --- a/snark-verifier/src/pcs/kzg.rs +++ b/snark-verifier/src/pcs/kzg.rs @@ -1,3 +1,6 @@ +//! [KZG]() +//! polynomial commitment scheme and accumulation scheme. + use crate::util::arithmetic::CurveAffine; mod accumulation; @@ -13,12 +16,15 @@ pub use multiopen::{Bdfg21, Bdfg21Proof, Gwc19, Gwc19Proof}; #[cfg(feature = "loader_halo2")] pub use accumulator::LimbsEncodingInstructions; +/// KZG succinct verifying key. #[derive(Clone, Copy, Debug)] pub struct KzgSuccinctVerifyingKey { + /// Generator. pub g: C, } impl KzgSuccinctVerifyingKey { + /// Initialize a [`KzgSuccinctVerifyingKey`]. pub fn new(g: C) -> Self { Self { g } } diff --git a/snark-verifier/src/pcs/kzg/accumulation.rs b/snark-verifier/src/pcs/kzg/accumulation.rs index 202a8ce1..1f901568 100644 --- a/snark-verifier/src/pcs/kzg/accumulation.rs +++ b/snark-verifier/src/pcs/kzg/accumulation.rs @@ -11,8 +11,10 @@ use crate::{ use rand::Rng; use std::{fmt::Debug, marker::PhantomData}; +/// KZG accumulation scheme. The second generic `MOS` stands for different kind +/// of multi-open scheme. #[derive(Clone, Debug)] -pub struct KzgAs(PhantomData<(M, MOS)>); +pub struct KzgAs(PhantomData<(M, MOS)>); impl AccumulationScheme for KzgAs where @@ -60,32 +62,39 @@ where } } +/// KZG accumulation scheme proving key. #[derive(Clone, Copy, Debug, Default)] pub struct KzgAsProvingKey(pub Option<(C, C)>); impl KzgAsProvingKey { + /// Initialize a [`KzgAsProvingKey`]. pub fn new(g: Option<(C, C)>) -> Self { Self(g) } + /// Returns if it supports zero-knowledge or not. pub fn zk(&self) -> bool { self.0.is_some() } + /// Returns [`KzgAsVerifyingKey`]. pub fn vk(&self) -> KzgAsVerifyingKey { KzgAsVerifyingKey(self.zk()) } } +/// KZG accumulation scheme verifying key. #[derive(Clone, Copy, Debug, Default)] pub struct KzgAsVerifyingKey(bool); impl KzgAsVerifyingKey { + /// Returns if it supports zero-knowledge or not. pub fn zk(&self) -> bool { self.0 } } +/// KZG accumulation scheme proof. #[derive(Clone, Debug)] pub struct KzgAsProof where diff --git a/snark-verifier/src/pcs/kzg/accumulator.rs b/snark-verifier/src/pcs/kzg/accumulator.rs index b76ab80a..a3fd8e42 100644 --- a/snark-verifier/src/pcs/kzg/accumulator.rs +++ b/snark-verifier/src/pcs/kzg/accumulator.rs @@ -1,13 +1,16 @@ use crate::{loader::Loader, util::arithmetic::CurveAffine}; use std::fmt::Debug; +/// KZG accumulator, containing lhs G1 and rhs G1 of pairing. #[derive(Clone, Debug)] pub struct KzgAccumulator where C: CurveAffine, L: Loader, { + /// Left-hand side G1 of pairing. pub lhs: L::LoadedEcPoint, + /// Right-hand side G1 of pairing. pub rhs: L::LoadedEcPoint, } @@ -16,6 +19,7 @@ where C: CurveAffine, L: Loader, { + /// Initialize a [`KzgAccumulator`]. pub fn new(lhs: L::LoadedEcPoint, rhs: L::LoadedEcPoint) -> Self { Self { lhs, rhs } } @@ -164,15 +168,18 @@ mod halo2 { x.zip(y).map(|(x, y)| C::from_xy(x, y).unwrap()) } + /// Instructions to encode/decode a elliptic curve point into/from limbs. pub trait LimbsEncodingInstructions<'a, C: CurveAffine, const LIMBS: usize, const BITS: usize>: EccInstructions<'a, C> { + /// Decode and assign an elliptic curve point from limbs. fn assign_ec_point_from_limbs( &self, ctx: &mut Self::Context, limbs: &[impl Deref], ) -> Result; + /// Encode an elliptic curve point into limbs. fn assign_ec_point_to_limbs( &self, ctx: &mut Self::Context, diff --git a/snark-verifier/src/pcs/kzg/decider.rs b/snark-verifier/src/pcs/kzg/decider.rs index 53cbd601..417a39dc 100644 --- a/snark-verifier/src/pcs/kzg/decider.rs +++ b/snark-verifier/src/pcs/kzg/decider.rs @@ -1,15 +1,19 @@ use crate::{pcs::kzg::KzgSuccinctVerifyingKey, util::arithmetic::MultiMillerLoop}; use std::marker::PhantomData; +/// KZG deciding key. #[derive(Debug, Clone, Copy)] pub struct KzgDecidingKey { svk: KzgSuccinctVerifyingKey, + /// Generator on G2. g2: M::G2Affine, + /// Generator to the trusted-setup secret on G2. s_g2: M::G2Affine, _marker: PhantomData, } impl KzgDecidingKey { + /// Initialize a [`KzgDecidingKey`] pub fn new( svk: impl Into>, g2: M::G2Affine, diff --git a/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs b/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs index b532f684..3a448056 100644 --- a/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs +++ b/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs @@ -6,7 +6,7 @@ use crate::{ PolynomialCommitmentScheme, Query, }, util::{ - arithmetic::{ilog2, CurveAffine, FieldExt, Fraction, MultiMillerLoop}, + arithmetic::{CurveAffine, FieldExt, Fraction, MultiMillerLoop}, msm::Msm, transcript::TranscriptRead, Itertools, @@ -18,6 +18,9 @@ use std::{ marker::PhantomData, }; +/// Verifier of multi-open KZG. It is for the SHPLONK implementation +/// in [`halo2_proofs`]. +/// Notations are following . #[derive(Clone, Debug)] pub struct Bdfg21; @@ -76,6 +79,7 @@ where } } +/// Structured proof of [`Bdfg21`]. #[derive(Clone, Debug)] pub struct Bdfg21Proof where @@ -175,9 +179,12 @@ fn query_set_coeffs<'a, F: FieldExt, T: LoadedScalar>( .sorted() .dedup(); - let size = 2.max( - ilog2((sets.iter().map(|set| set.shifts.len()).max().unwrap() - 1).next_power_of_two()) + 1, - ); + let size = sets + .iter() + .map(|set| set.shifts.len()) + .chain(Some(2)) + .max() + .unwrap(); let powers_of_z = z.powers(size); let z_prime_minus_z_shift_i = BTreeMap::from_iter(superset.map(|shift| { ( @@ -290,23 +297,15 @@ where .collect_vec(); let z = &powers_of_z[1]; - let z_pow_k_minus_one = { - let k_minus_one = shifts.len() - 1; - powers_of_z - .iter() - .enumerate() - .skip(1) - .filter_map(|(i, power_of_z)| (k_minus_one & (1 << i) == 1).then(|| power_of_z)) - .fold(loader.load_one(), |acc, value| acc * value) - }; + let z_pow_k_minus_one = &powers_of_z[shifts.len() - 1]; let barycentric_weights = shifts .iter() .zip(normalized_ell_primes.iter()) .map(|(shift, normalized_ell_prime)| { loader.sum_products_with_coeff(&[ - (*normalized_ell_prime, &z_pow_k_minus_one, z_prime), - (-(*normalized_ell_prime * shift), &z_pow_k_minus_one, z), + (*normalized_ell_prime, z_pow_k_minus_one, z_prime), + (-(*normalized_ell_prime * shift), z_pow_k_minus_one, z), ]) }) .map(Fraction::one_over) diff --git a/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs b/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs index 31786e7d..e5741163 100644 --- a/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs +++ b/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs @@ -14,6 +14,9 @@ use crate::{ Error, }; +/// Verifier of multi-open KZG. It is for the GWC implementation +/// in [`halo2_proofs`]. +/// Notations are following . #[derive(Clone, Debug)] pub struct Gwc19; @@ -77,6 +80,7 @@ where } } +/// Structured proof of [`Gwc19`]. #[derive(Clone, Debug)] pub struct Gwc19Proof where diff --git a/snark-verifier/src/system.rs b/snark-verifier/src/system.rs index 5d5aa99c..0ebe4311 100644 --- a/snark-verifier/src/system.rs +++ b/snark-verifier/src/system.rs @@ -1,2 +1,4 @@ +//! Proof systems `snark-verifier` supports + #[cfg(feature = "system_halo2")] pub mod halo2; diff --git a/snark-verifier/src/system/halo2.rs b/snark-verifier/src/system/halo2.rs index bb7b1bb9..a271a17c 100644 --- a/snark-verifier/src/system/halo2.rs +++ b/snark-verifier/src/system/halo2.rs @@ -1,3 +1,5 @@ +//! [`halo2_proofs`] proof system + use crate::{ util::{ arithmetic::{root_of_unity, CurveAffine, Domain, FieldExt, Rotation}, @@ -22,16 +24,19 @@ pub mod transcript; #[cfg(test)] pub(crate) mod test; +/// Configuration for converting a [`VerifyingKey`] of [`halo2_proofs`] into +/// [`PlonkProtocol`]. #[derive(Clone, Debug, Default)] pub struct Config { - pub zk: bool, - pub query_instance: bool, - pub num_proof: usize, - pub num_instance: Vec, - pub accumulator_indices: Option>, + zk: bool, + query_instance: bool, + num_proof: usize, + num_instance: Vec, + accumulator_indices: Option>, } impl Config { + /// Returns [`Config`] with `query_instance` set to `false`. pub fn kzg() -> Self { Self { zk: true, @@ -41,6 +46,7 @@ impl Config { } } + /// Returns [`Config`] with `query_instance` set to `true`. pub fn ipa() -> Self { Self { zk: true, @@ -50,27 +56,32 @@ impl Config { } } + /// Set `zk` pub fn set_zk(mut self, zk: bool) -> Self { self.zk = zk; self } + /// Set `query_instance` pub fn set_query_instance(mut self, query_instance: bool) -> Self { self.query_instance = query_instance; self } + /// Set `num_proof` pub fn with_num_proof(mut self, num_proof: usize) -> Self { assert!(num_proof > 0); self.num_proof = num_proof; self } + /// Set `num_instance` pub fn with_num_instance(mut self, num_instance: Vec) -> Self { self.num_instance = num_instance; self } + /// Set `accumulator_indices` pub fn with_accumulator_indices( mut self, accumulator_indices: Option>, @@ -80,6 +91,7 @@ impl Config { } } +/// Convert a [`VerifyingKey`] of [`halo2_proofs`] into [`PlonkProtocol`]. pub fn compile<'a, C: CurveAffine, P: Params<'a, C>>( params: &P, vk: &VerifyingKey, diff --git a/snark-verifier/src/system/halo2/strategy.rs b/snark-verifier/src/system/halo2/strategy.rs index de66f8e3..0be5c0b0 100644 --- a/snark-verifier/src/system/halo2/strategy.rs +++ b/snark-verifier/src/system/halo2/strategy.rs @@ -1,4 +1,8 @@ +//! Verifier strategy + pub mod ipa { + //! IPA verifier strategy + use crate::util::arithmetic::CurveAffine; use halo2_proofs::{ plonk::Error, @@ -14,6 +18,8 @@ pub mod ipa { }, }; + /// Strategy that handles single proof and decide immediately, but also + /// returns `g` if the proof is valid. #[derive(Clone, Debug)] pub struct SingleStrategy<'a, C: CurveAffine> { msm: MSMIPA<'a, C>, diff --git a/snark-verifier/src/system/halo2/transcript.rs b/snark-verifier/src/system/halo2/transcript.rs index 2200bbf4..6de1b739 100644 --- a/snark-verifier/src/system/halo2/transcript.rs +++ b/snark-verifier/src/system/halo2/transcript.rs @@ -1,3 +1,5 @@ +//! Transcripts implemented with both `halo2_proofs::transcript` and +//! `crate::util::transcript`. use crate::{ loader::native::{self, NativeLoader}, util::{ diff --git a/snark-verifier/src/system/halo2/transcript/evm.rs b/snark-verifier/src/system/halo2/transcript/evm.rs index 68c3ae4a..b7e81c81 100644 --- a/snark-verifier/src/system/halo2/transcript/evm.rs +++ b/snark-verifier/src/system/halo2/transcript/evm.rs @@ -1,6 +1,8 @@ +//! Transcript for verifier on EVM. + use crate::{ loader::{ - evm::{loader::Value, u256_to_fe, EcPoint, EvmLoader, MemoryChunk, Scalar, U256}, + evm::{loader::Value, u256_to_fe, util::MemoryChunk, EcPoint, EvmLoader, Scalar, U256}, native::{self, NativeLoader}, Loader, }, @@ -19,6 +21,9 @@ use std::{ marker::PhantomData, rc::Rc, }; + +/// Transcript for verifier on EVM using keccak256 as hasher. +#[derive(Debug)] pub struct EvmTranscript, S, B> { loader: L, stream: S, @@ -31,6 +36,8 @@ where C: CurveAffine, C::Scalar: PrimeField, { + /// Initialize [`EvmTranscript`] given [`Rc`] and pre-allocate an + /// u256 for `transcript_initial_state`. pub fn new(loader: &Rc) -> Self { let ptr = loader.allocate(0x20); assert_eq!(ptr, 0); @@ -44,6 +51,7 @@ where } } + /// Load `num_instance` instances from calldata to memory. pub fn load_instances(&mut self, num_instance: Vec) -> Vec> { num_instance .into_iter() @@ -147,6 +155,8 @@ impl EvmTranscript> where C: CurveAffine, { + /// Initialize [`EvmTranscript`] given readable or writeable stream for + /// verifying or proving with [`NativeLoader`]. pub fn new(stream: S) -> Self { Self { loader: NativeLoader, @@ -187,7 +197,7 @@ where Option::>::from(ec_point.coordinates()).ok_or_else(|| { Error::Transcript( io::ErrorKind::Other, - "Cannot write points at infinity to the transcript".to_string(), + "Invalid elliptic curve point".to_string(), ) })?; @@ -257,15 +267,20 @@ where C: CurveAffine, S: Write, { + /// Returns mutable `stream`. pub fn stream_mut(&mut self) -> &mut S { &mut self.stream } + /// Finalize transcript and returns `stream`. pub fn finalize(self) -> S { self.stream } } +/// [`EncodedChallenge`] implemented for verifier on EVM, which use input in +/// big-endian as the challenge. +#[derive(Debug)] pub struct ChallengeEvm(C::Scalar) where C: CurveAffine, diff --git a/snark-verifier/src/system/halo2/transcript/halo2.rs b/snark-verifier/src/system/halo2/transcript/halo2.rs index ab7d548c..8519a8ca 100644 --- a/snark-verifier/src/system/halo2/transcript/halo2.rs +++ b/snark-verifier/src/system/halo2/transcript/halo2.rs @@ -1,3 +1,5 @@ +//! Transcript for verifier in [`halo2_proofs`] circuit. + use crate::{ loader::{ halo2::{EcPoint, EccInstructions, Halo2Loader, Scalar}, @@ -23,6 +25,7 @@ pub trait NativeEncoding<'a, C>: EccInstructions<'a, C> where C: CurveAffine, { + /// Encode an elliptic curve point into field elements. fn encode( &self, ctx: &mut Self::Context, @@ -30,6 +33,10 @@ where ) -> Result, Error>; } +/// Transcript for verifier in [`halo2_proofs`] circuit using poseidon hasher. +/// Currently It assumes the elliptic curve scalar field is same as native +/// field. +#[derive(Debug)] pub struct PoseidonTranscript< C, L, @@ -54,6 +61,7 @@ where R: Read, EccChip: NativeEncoding<'a, C>, { + /// Initialize [`PoseidonTranscript`] given [`Rc`]. pub fn new(loader: &Rc>, stream: Value) -> Self { let buf = Poseidon::new(loader, R_F, R_P); Self { @@ -99,7 +107,7 @@ where .map_err(|_| { Error::Transcript( io::ErrorKind::Other, - "Failed to encode elliptic curve point into native field elements".to_string(), + "Invalid elliptic curve point".to_string(), ) })?; self.buf.update(&encoded); @@ -149,6 +157,8 @@ where impl PoseidonTranscript { + /// Initialize [`PoseidonTranscript`] given readable or writeable stream for + /// verifying or proving with [`NativeLoader`]. pub fn new(stream: S) -> Self { Self { loader: NativeLoader, @@ -236,10 +246,12 @@ where C: CurveAffine, W: Write, { + /// Returns mutable `stream`. pub fn stream_mut(&mut self) -> &mut W { &mut self.stream } + /// Finalize transcript and returns `stream`. pub fn finalize(self) -> W { self.stream } @@ -274,6 +286,10 @@ where } } +/// [`EncodedChallenge`] implemented for verifier in [`halo2_proofs`] circuit. +/// Currently It assumes the elliptic curve scalar field is same as native +/// field. +#[derive(Debug)] pub struct ChallengeScalar(C::Scalar); impl EncodedChallenge for ChallengeScalar { diff --git a/snark-verifier/src/util.rs b/snark-verifier/src/util.rs index bbfa8c89..508c0eeb 100644 --- a/snark-verifier/src/util.rs +++ b/snark-verifier/src/util.rs @@ -1,3 +1,5 @@ +//! Utilities. + pub mod arithmetic; pub mod hash; pub mod msm; @@ -9,6 +11,7 @@ pub(crate) use itertools::Itertools; #[cfg(feature = "parallel")] pub(crate) use rayon::current_num_threads; +/// Parallelly executing the function on the items of the given iterator. pub fn parallelize_iter(iter: I, f: F) where I: Send + Iterator, @@ -26,6 +29,7 @@ where iter.for_each(f); } +/// Parallelly executing the function on the given mutable slice. pub fn parallelize(v: &mut [T], f: F) where T: Send, diff --git a/snark-verifier/src/util/arithmetic.rs b/snark-verifier/src/util/arithmetic.rs index dacd2443..af32ded0 100644 --- a/snark-verifier/src/util/arithmetic.rs +++ b/snark-verifier/src/util/arithmetic.rs @@ -1,3 +1,5 @@ +//! Arithmetic related re-exported traits and utilities. + use crate::util::Itertools; use num_bigint::BigUint; use num_traits::One; @@ -18,10 +20,12 @@ pub use halo2_curves::{ Coordinates, CurveAffine, CurveExt, FieldExt, }; +/// [`halo2_curves::pairing::MultiMillerLoop`] with [`std::fmt::Debug`]. pub trait MultiMillerLoop: halo2_curves::pairing::MultiMillerLoop + Debug {} impl MultiMillerLoop for M {} +/// Operations that could be done with field elements. pub trait FieldOps: Sized + Neg @@ -38,9 +42,11 @@ pub trait FieldOps: + for<'a> SubAssign<&'a Self> + for<'a> MulAssign<&'a Self> { + /// Returns multiplicative inversion if any. fn invert(&self) -> Option; } +/// Batch invert [`PrimeField`] elements and multiply all with given coefficient. pub fn batch_invert_and_mul(values: &mut [F], coeff: &F) { let products = values .iter() @@ -65,10 +71,18 @@ pub fn batch_invert_and_mul(values: &mut [F], coeff: &F) { } } +/// Batch invert [`PrimeField`] elements. pub fn batch_invert(values: &mut [F]) { batch_invert_and_mul(values, &F::one()) } +/// Root of unity of 2^k-sized multiplicative subgroup of [`PrimeField`] by +/// repeatedly squaring the root of unity of the largest multiplicative +/// subgroup. +/// +/// # Panic +/// +/// If given `k` is greater than [`PrimeField::S`]. pub fn root_of_unity(k: usize) -> F { assert!(k <= F::S as usize); @@ -78,18 +92,22 @@ pub fn root_of_unity(k: usize) -> F { .unwrap() } +/// Rotation on a group. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Rotation(pub i32); impl Rotation { + /// No rotation pub fn cur() -> Self { Rotation(0) } + /// To previous element pub fn prev() -> Self { Rotation(-1) } + /// To next element pub fn next() -> Self { Rotation(1) } @@ -101,16 +119,23 @@ impl From for Rotation { } } +/// 2-adicity multiplicative domain #[derive(Clone, Debug)] pub struct Domain { + /// Log size of the domain. pub k: usize, + /// Size of the domain. pub n: usize, + /// Inverse of `n`. pub n_inv: F, + /// Generator of the domain. pub gen: F, + /// Inverse of `gen`. pub gen_inv: F, } impl Domain { + /// Initialize a domain with specified generator. pub fn new(k: usize, gen: F) -> Self { let n = 1 << k; let n_inv = F::from(n as u64).invert().unwrap(); @@ -125,6 +150,7 @@ impl Domain { } } + /// Rotate an element to given `rotation`. pub fn rotate_scalar(&self, scalar: F, rotation: Rotation) -> F { match rotation.0.cmp(&0) { Ordering::Equal => scalar, @@ -134,6 +160,7 @@ impl Domain { } } +/// Contains numerator and denominator for deferred evaluation. #[derive(Clone, Debug)] pub struct Fraction { numer: Option, @@ -143,6 +170,7 @@ pub struct Fraction { } impl Fraction { + /// Initialize an unevaluated fraction. pub fn new(numer: T, denom: T) -> Self { Self { numer: Some(numer), @@ -152,6 +180,7 @@ impl Fraction { } } + /// Initialize an unevaluated fraction without numerator. pub fn one_over(denom: T) -> Self { Self { numer: None, @@ -161,6 +190,7 @@ impl Fraction { } } + /// Returns denominator. pub fn denom(&self) -> Option<&T> { if !self.inv { Some(&self.denom) @@ -169,6 +199,8 @@ impl Fraction { } } + #[must_use = "To be inverted"] + /// Returns mutable denominator for doing inversion. pub fn denom_mut(&mut self) -> Option<&mut T> { if !self.inv { self.inv = true; @@ -180,18 +212,29 @@ impl Fraction { } impl Fraction { + /// Evaluate the fraction and cache the result. + /// + /// # Panic + /// + /// If `denom_mut` is not called before. pub fn evaluate(&mut self) { assert!(self.inv); - assert!(self.eval.is_none()); - - self.eval = Some( - self.numer - .take() - .map(|numer| numer * &self.denom) - .unwrap_or_else(|| self.denom.clone()), - ); + + if self.eval.is_none() { + self.eval = Some( + self.numer + .take() + .map(|numer| numer * &self.denom) + .unwrap_or_else(|| self.denom.clone()), + ); + } } + /// Returns cached fraction evaluation. + /// + /// # Panic + /// + /// If `evaluate` is not called before. pub fn evaluated(&self) -> &T { assert!(self.eval.is_some()); @@ -199,14 +242,12 @@ impl Fraction { } } -pub fn ilog2(value: usize) -> usize { - (usize::BITS - value.leading_zeros() - 1) as usize -} - +/// Modulus of a [`PrimeField`] pub fn modulus() -> BigUint { fe_to_big(-F::one()) + 1usize } +/// Convert a [`BigUint`] into a [`PrimeField`] . pub fn fe_from_big(big: BigUint) -> F { let bytes = big.to_bytes_le(); let mut repr = F::Repr::default(); @@ -215,14 +256,18 @@ pub fn fe_from_big(big: BigUint) -> F { F::from_repr(repr).unwrap() } +/// Convert a [`PrimeField`] into a [`BigUint`]. pub fn fe_to_big(fe: F) -> BigUint { BigUint::from_bytes_le(fe.to_repr().as_ref()) } +/// Convert a [`PrimeField`] into another [`PrimeField`]. pub fn fe_to_fe(fe: F1) -> F2 { fe_from_big(fe_to_big(fe) % modulus::()) } +/// Convert `LIMBS` limbs into a [`PrimeField`], assuming each limb contains at +/// most `BITS`. pub fn fe_from_limbs( limbs: [F1; LIMBS], ) -> F2 { @@ -237,6 +282,8 @@ pub fn fe_from_limbs( fe: F1, ) -> [F2; LIMBS] { @@ -251,10 +298,12 @@ pub fn fe_to_limbs(scalar: F) -> impl Iterator { iter::successors(Some(F::one()), move |power| Some(scalar * power)) } +/// Compute inner product of 2 slice of [`Field`]. pub fn inner_product(lhs: &[F], rhs: &[F]) -> F { lhs.iter() .zip_eq(rhs.iter()) diff --git a/snark-verifier/src/util/hash.rs b/snark-verifier/src/util/hash.rs index 17ede0b3..758e26d5 100644 --- a/snark-verifier/src/util/hash.rs +++ b/snark-verifier/src/util/hash.rs @@ -1,3 +1,5 @@ +//! Hash algorithms. + mod poseidon; pub use crate::util::hash::poseidon::Poseidon; diff --git a/snark-verifier/src/util/hash/poseidon.rs b/snark-verifier/src/util/hash/poseidon.rs index c0fc03a6..60d55d9f 100644 --- a/snark-verifier/src/util/hash/poseidon.rs +++ b/snark-verifier/src/util/hash/poseidon.rs @@ -5,6 +5,7 @@ use crate::{ use poseidon::{self, SparseMDSMatrix, Spec}; use std::{iter, marker::PhantomData, mem}; +#[derive(Debug)] struct State { inner: [L; T], _marker: PhantomData, @@ -106,6 +107,8 @@ impl, const T: usize, const RATE: usize> State { spec: Spec, state: State, @@ -113,6 +116,7 @@ pub struct Poseidon { } impl, const T: usize, const RATE: usize> Poseidon { + /// Initialize a poseidon hasher. pub fn new(loader: &L::Loader, r_f: usize, r_p: usize) -> Self { Self { spec: Spec::new(r_f, r_p), @@ -125,10 +129,13 @@ impl, const T: usize, const RATE: usize> Poseido } } + /// Store given `elements` into buffer. pub fn update(&mut self, elements: &[L]) { self.buf.extend_from_slice(elements); } + /// Consume buffer and perform permutation, then output second element of + /// state. pub fn squeeze(&mut self) -> L { let buf = mem::take(&mut self.buf); let exact = buf.len() % RATE == 0; diff --git a/snark-verifier/src/util/msm.rs b/snark-verifier/src/util/msm.rs index 014a29e8..f68ce6f4 100644 --- a/snark-verifier/src/util/msm.rs +++ b/snark-verifier/src/util/msm.rs @@ -1,3 +1,5 @@ +//! Multi-scalar multiplication algorithm. + use crate::{ loader::{LoadedEcPoint, Loader}, util::{ @@ -14,6 +16,7 @@ use std::{ }; #[derive(Clone, Debug)] +/// Contains unevaluated multi-scalar multiplication. pub struct Msm<'a, C: CurveAffine, L: Loader> { constant: Option, scalars: Vec, @@ -39,6 +42,7 @@ where C: CurveAffine, L: Loader, { + /// Initialize with a constant. pub fn constant(constant: L::LoadedScalar) -> Self { Msm { constant: Some(constant), @@ -46,6 +50,7 @@ where } } + /// Initialize with a base. pub fn base<'b: 'a>(base: &'b L::LoadedEcPoint) -> Self { let one = base.loader().load_one(); Msm { @@ -68,6 +73,11 @@ where self.bases.is_empty().then(|| self.constant.unwrap()) } + /// Evaluate multi-scalar multiplication. + /// + /// # Panic + /// + /// If given `gen` is `None` but there `constant` has some value. pub fn evaluate(self, gen: Option) -> L::LoadedEcPoint { let gen = gen.map(|gen| { self.bases @@ -87,7 +97,7 @@ where L::multi_scalar_multiplication(&pairs) } - pub fn scale(&mut self, factor: &L::LoadedScalar) { + fn scale(&mut self, factor: &L::LoadedScalar) { if let Some(constant) = self.constant.as_mut() { *constant *= factor; } @@ -96,7 +106,7 @@ where } } - pub fn push<'b: 'a>(&mut self, scalar: L::LoadedScalar, base: &'b L::LoadedEcPoint) { + fn push<'b: 'a>(&mut self, scalar: L::LoadedScalar, base: &'b L::LoadedEcPoint) { if let Some(pos) = self.bases.iter().position(|exist| exist.eq(&base)) { self.scalars[pos] += &scalar; } else { @@ -105,7 +115,7 @@ where } } - pub fn extend<'b: 'a>(&mut self, mut other: Msm<'b, C, L>) { + fn extend<'b: 'a>(&mut self, mut other: Msm<'b, C, L>) { match (self.constant.as_mut(), other.constant.as_ref()) { (Some(lhs), Some(rhs)) => *lhs += rhs, (None, Some(_)) => self.constant = other.constant.take(), @@ -293,7 +303,8 @@ fn multi_scalar_multiplication_serial( } } -// Copy from https://github.com/zcash/halo2/blob/main/halo2_proofs/src/arithmetic.rs +/// Multi-scalar multiplication algorithm copied from +/// . pub fn multi_scalar_multiplication(scalars: &[C::Scalar], bases: &[C]) -> C::Curve { assert_eq!(scalars.len(), bases.len()); diff --git a/snark-verifier/src/util/poly.rs b/snark-verifier/src/util/poly.rs index ea120b33..eef2f593 100644 --- a/snark-verifier/src/util/poly.rs +++ b/snark-verifier/src/util/poly.rs @@ -1,3 +1,5 @@ +//! Polynomial. + use crate::util::{arithmetic::Field, parallelize}; use rand::Rng; use std::{ @@ -9,39 +11,47 @@ use std::{ }; #[derive(Clone, Debug)] +/// Univariate polynomial. pub struct Polynomial(Vec); impl Polynomial { + /// Initialize an univariate polynomial. pub fn new(inner: Vec) -> Self { Self(inner) } + /// Returns `true` if the `Polynomial` contains no elements. pub fn is_empty(&self) -> bool { self.0.is_empty() } + /// Returns the length of the `Polynomial`. pub fn len(&self) -> usize { self.0.len() } + /// Returns an iterator of the `Polynomial`. pub fn iter(&self) -> impl Iterator { self.0.iter() } + /// Returns a mutable iterator of the `Polynomial`. pub fn iter_mut(&mut self) -> impl Iterator { self.0.iter_mut() } + /// Into vector of coefficients. pub fn to_vec(self) -> Vec { self.0 } } impl Polynomial { - pub fn rand(n: usize, mut rng: R) -> Self { + pub(crate) fn rand(n: usize, mut rng: R) -> Self { Self::new(iter::repeat_with(|| F::random(&mut rng)).take(n).collect()) } + /// Returns evaluation at given `x`. pub fn evaluate(&self, x: F) -> F { let evaluate_serial = |coeffs: &[F]| { coeffs diff --git a/snark-verifier/src/util/protocol.rs b/snark-verifier/src/util/protocol.rs deleted file mode 100644 index 6ae361db..00000000 --- a/snark-verifier/src/util/protocol.rs +++ /dev/null @@ -1,368 +0,0 @@ -use crate::{ - loader::{LoadedScalar, Loader}, - util::{ - arithmetic::{CurveAffine, Domain, Field, Fraction, Rotation}, - Itertools, - }, -}; -use num_integer::Integer; -use num_traits::One; -use std::{ - cmp::max, - collections::{BTreeMap, BTreeSet}, - fmt::Debug, - iter::{self, Sum}, - ops::{Add, Mul, Neg, Sub}, -}; - -#[derive(Clone, Copy, Debug)] -pub enum CommonPolynomial { - Identity, - Lagrange(i32), -} - -#[derive(Clone, Debug)] -pub struct CommonPolynomialEvaluation -where - C: CurveAffine, - L: Loader, -{ - zn: L::LoadedScalar, - zn_minus_one: L::LoadedScalar, - zn_minus_one_inv: Fraction, - identity: L::LoadedScalar, - lagrange: BTreeMap>, -} - -impl CommonPolynomialEvaluation -where - C: CurveAffine, - L: Loader, -{ - pub fn new( - domain: &Domain, - langranges: impl IntoIterator, - z: &L::LoadedScalar, - ) -> Self { - let loader = z.loader(); - - let zn = z.pow_const(domain.n as u64); - let langranges = langranges.into_iter().sorted().dedup().collect_vec(); - - let one = loader.load_one(); - let zn_minus_one = zn.clone() - &one; - let zn_minus_one_inv = Fraction::one_over(zn_minus_one.clone()); - - let n_inv = loader.load_const(&domain.n_inv); - let numer = zn_minus_one.clone() * &n_inv; - let omegas = langranges - .iter() - .map(|&i| loader.load_const(&domain.rotate_scalar(C::Scalar::one(), Rotation(i)))) - .collect_vec(); - let lagrange_evals = omegas - .iter() - .map(|omega| Fraction::new(numer.clone() * omega, z.clone() - omega)) - .collect_vec(); - - Self { - zn, - zn_minus_one, - zn_minus_one_inv, - identity: z.clone(), - lagrange: langranges.into_iter().zip(lagrange_evals).collect(), - } - } - - pub fn zn(&self) -> &L::LoadedScalar { - &self.zn - } - - pub fn zn_minus_one(&self) -> &L::LoadedScalar { - &self.zn_minus_one - } - - pub fn zn_minus_one_inv(&self) -> &L::LoadedScalar { - self.zn_minus_one_inv.evaluated() - } - - pub fn get(&self, poly: CommonPolynomial) -> &L::LoadedScalar { - match poly { - CommonPolynomial::Identity => &self.identity, - CommonPolynomial::Lagrange(i) => self.lagrange.get(&i).unwrap().evaluated(), - } - } - - pub fn denoms(&mut self) -> impl IntoIterator { - self.lagrange - .iter_mut() - .map(|(_, value)| value.denom_mut()) - .chain(iter::once(self.zn_minus_one_inv.denom_mut())) - .flatten() - } - - pub fn evaluate(&mut self) { - self.lagrange - .iter_mut() - .map(|(_, value)| value) - .chain(iter::once(&mut self.zn_minus_one_inv)) - .for_each(Fraction::evaluate) - } -} - -#[derive(Clone, Debug)] -pub struct QuotientPolynomial { - pub chunk_degree: usize, - pub numerator: Expression, -} - -impl QuotientPolynomial { - pub fn num_chunk(&self) -> usize { - Integer::div_ceil( - &(self.numerator.degree().checked_sub(1).unwrap_or_default()), - &self.chunk_degree, - ) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Query { - pub poly: usize, - pub rotation: Rotation, -} - -impl Query { - pub fn new>(poly: usize, rotation: R) -> Self { - Self { - poly, - rotation: rotation.into(), - } - } -} - -#[derive(Clone, Debug)] -pub enum Expression { - Constant(F), - CommonPolynomial(CommonPolynomial), - Polynomial(Query), - Challenge(usize), - Negated(Box>), - Sum(Box>, Box>), - Product(Box>, Box>), - Scaled(Box>, F), - DistributePowers(Vec>, Box>), -} - -impl Expression { - pub fn evaluate( - &self, - constant: &impl Fn(F) -> T, - common_poly: &impl Fn(CommonPolynomial) -> T, - poly: &impl Fn(Query) -> T, - challenge: &impl Fn(usize) -> T, - negated: &impl Fn(T) -> T, - sum: &impl Fn(T, T) -> T, - product: &impl Fn(T, T) -> T, - scaled: &impl Fn(T, F) -> T, - ) -> T { - let evaluate = |expr: &Expression| { - expr.evaluate( - constant, - common_poly, - poly, - challenge, - negated, - sum, - product, - scaled, - ) - }; - match self { - Expression::Constant(scalar) => constant(scalar.clone()), - Expression::CommonPolynomial(poly) => common_poly(*poly), - Expression::Polynomial(query) => poly(*query), - Expression::Challenge(index) => challenge(*index), - Expression::Negated(a) => { - let a = evaluate(a); - negated(a) - } - Expression::Sum(a, b) => { - let a = evaluate(a); - let b = evaluate(b); - sum(a, b) - } - Expression::Product(a, b) => { - let a = evaluate(a); - let b = evaluate(b); - product(a, b) - } - Expression::Scaled(a, scalar) => { - let a = evaluate(a); - scaled(a, scalar.clone()) - } - Expression::DistributePowers(exprs, scalar) => { - assert!(!exprs.is_empty()); - if exprs.len() == 1 { - return evaluate(exprs.first().unwrap()); - } - let mut exprs = exprs.iter(); - let first = evaluate(exprs.next().unwrap()); - let scalar = evaluate(scalar); - exprs.fold(first, |acc, expr| { - sum(product(acc, scalar.clone()), evaluate(expr)) - }) - } - } - } - - pub fn degree(&self) -> usize { - match self { - Expression::Constant(_) => 0, - Expression::CommonPolynomial(_) => 1, - Expression::Polynomial { .. } => 1, - Expression::Challenge { .. } => 0, - Expression::Negated(a) => a.degree(), - Expression::Sum(a, b) => max(a.degree(), b.degree()), - Expression::Product(a, b) => a.degree() + b.degree(), - Expression::Scaled(a, _) => a.degree(), - Expression::DistributePowers(a, b) => a - .iter() - .chain(Some(b.as_ref())) - .map(Self::degree) - .max() - .unwrap_or_default(), - } - } - - pub fn used_langrange(&self) -> BTreeSet { - self.evaluate( - &|_| None, - &|poly| match poly { - CommonPolynomial::Lagrange(i) => Some(BTreeSet::from_iter([i])), - _ => None, - }, - &|_| None, - &|_| None, - &|a| a, - &merge_left_right, - &merge_left_right, - &|a, _| a, - ) - .unwrap_or_default() - } - - pub fn used_query(&self) -> BTreeSet { - self.evaluate( - &|_| None, - &|_| None, - &|query| Some(BTreeSet::from_iter([query])), - &|_| None, - &|a| a, - &merge_left_right, - &merge_left_right, - &|a, _| a, - ) - .unwrap_or_default() - } -} - -impl From for Expression { - fn from(query: Query) -> Self { - Self::Polynomial(query) - } -} - -impl From for Expression { - fn from(common_poly: CommonPolynomial) -> Self { - Self::CommonPolynomial(common_poly) - } -} - -macro_rules! impl_expression_ops { - ($trait:ident, $op:ident, $variant:ident, $rhs:ty, $rhs_expr:expr) => { - impl $trait<$rhs> for Expression { - type Output = Expression; - fn $op(self, rhs: $rhs) -> Self::Output { - Expression::$variant((self).into(), $rhs_expr(rhs).into()) - } - } - impl $trait<$rhs> for &Expression { - type Output = Expression; - fn $op(self, rhs: $rhs) -> Self::Output { - Expression::$variant((self.clone()).into(), $rhs_expr(rhs).into()) - } - } - impl $trait<&$rhs> for Expression { - type Output = Expression; - fn $op(self, rhs: &$rhs) -> Self::Output { - Expression::$variant((self).into(), $rhs_expr(rhs.clone()).into()) - } - } - impl $trait<&$rhs> for &Expression { - type Output = Expression; - fn $op(self, rhs: &$rhs) -> Self::Output { - Expression::$variant((self.clone()).into(), $rhs_expr(rhs.clone()).into()) - } - } - }; -} - -impl_expression_ops!(Mul, mul, Product, Expression, std::convert::identity); -impl_expression_ops!(Mul, mul, Scaled, F, std::convert::identity); -impl_expression_ops!(Add, add, Sum, Expression, std::convert::identity); -impl_expression_ops!(Sub, sub, Sum, Expression, Neg::neg); - -impl Neg for Expression { - type Output = Expression; - fn neg(self) -> Self::Output { - Expression::Negated(Box::new(self)) - } -} - -impl Neg for &Expression { - type Output = Expression; - fn neg(self) -> Self::Output { - Expression::Negated(Box::new(self.clone())) - } -} - -impl Sum for Expression { - fn sum>(iter: I) -> Self { - iter.reduce(|acc, item| acc + item) - .unwrap_or_else(|| Expression::Constant(F::default())) - } -} - -impl One for Expression { - fn one() -> Self { - Expression::Constant(F::one()) - } -} - -fn merge_left_right(a: Option>, b: Option>) -> Option> { - match (a, b) { - (Some(a), None) | (None, Some(a)) => Some(a), - (Some(mut a), Some(b)) => { - a.extend(b); - Some(a) - } - _ => None, - } -} - -#[derive(Clone, Copy, Debug)] -pub enum LinearizationStrategy { - /// Older linearization strategy of GWC19, which has linearization - /// polynomial that doesn't evaluate to 0, and requires prover to send extra - /// evaluation of it to verifier. - WithoutConstant, - /// Current linearization strategy of GWC19, which has linearization - /// polynomial that evaluate to 0 by subtracting product of vanishing and - /// quotient polynomials. - MinusVanishingTimesQuotient, -} - -#[derive(Clone, Debug, Default)] -pub struct InstanceCommittingKey { - pub bases: Vec, - pub constant: Option, -} diff --git a/snark-verifier/src/util/transcript.rs b/snark-verifier/src/util/transcript.rs index 3337324d..b871b083 100644 --- a/snark-verifier/src/util/transcript.rs +++ b/snark-verifier/src/util/transcript.rs @@ -1,46 +1,62 @@ +//! Transcript traits. + use crate::{ loader::{native::NativeLoader, Loader}, {util::arithmetic::CurveAffine, Error}, }; +/// Common methods for prover and verifier. pub trait Transcript where C: CurveAffine, L: Loader, { + /// Returns [`Loader`]. fn loader(&self) -> &L; + /// Squeeze a challenge. fn squeeze_challenge(&mut self) -> L::LoadedScalar; + /// Squeeze `n` challenges. fn squeeze_n_challenges(&mut self, n: usize) -> Vec { (0..n).map(|_| self.squeeze_challenge()).collect() } + /// Update with an elliptic curve point. fn common_ec_point(&mut self, ec_point: &L::LoadedEcPoint) -> Result<(), Error>; + /// Update with a scalar. fn common_scalar(&mut self, scalar: &L::LoadedScalar) -> Result<(), Error>; } +/// Transcript for verifier. pub trait TranscriptRead: Transcript where C: CurveAffine, L: Loader, { + /// Read a scalar. fn read_scalar(&mut self) -> Result; + /// Read `n` scalar. fn read_n_scalars(&mut self, n: usize) -> Result, Error> { (0..n).map(|_| self.read_scalar()).collect() } + /// Read a elliptic curve point. fn read_ec_point(&mut self) -> Result; + /// Read `n` elliptic curve point. fn read_n_ec_points(&mut self, n: usize) -> Result, Error> { (0..n).map(|_| self.read_ec_point()).collect() } } +/// Transcript for prover. pub trait TranscriptWrite: Transcript { + /// Write a scalar. fn write_scalar(&mut self, scalar: C::Scalar) -> Result<(), Error>; + /// Write a elliptic curve point. fn write_ec_point(&mut self, ec_point: C) -> Result<(), Error>; } diff --git a/snark-verifier/src/verifier.rs b/snark-verifier/src/verifier.rs index c492fe55..813065db 100644 --- a/snark-verifier/src/verifier.rs +++ b/snark-verifier/src/verifier.rs @@ -1,3 +1,5 @@ +//! Verifiers for (S)NARK. + use crate::{ loader::Loader, util::{arithmetic::CurveAffine, transcript::TranscriptRead}, @@ -7,16 +9,22 @@ use std::fmt::Debug; pub mod plonk; +/// (S)NARK verifier for verifying a (S)NARK. pub trait SnarkVerifier where C: CurveAffine, L: Loader, { + /// Verifying key for subroutines if any. type VerifyingKey: Clone + Debug; + /// Protocol specifying configuration of a (S)NARK. type Protocol: Clone + Debug; + /// Structured proof read from transcript. type Proof: Clone + Debug; + /// Output of verification. type Output: Clone + Debug; + /// Read [`SnarkVerifier::Proof`] from transcript. fn read_proof( vk: &Self::VerifyingKey, protocol: &Self::Protocol, @@ -26,6 +34,7 @@ where where T: TranscriptRead; + /// Verify [`SnarkVerifier::Proof`] and output [`SnarkVerifier::Output`]. fn verify( vk: &Self::VerifyingKey, protocol: &Self::Protocol, diff --git a/snark-verifier/src/verifier/plonk.rs b/snark-verifier/src/verifier/plonk.rs index 08bf5dc9..e2f6f940 100644 --- a/snark-verifier/src/verifier/plonk.rs +++ b/snark-verifier/src/verifier/plonk.rs @@ -1,3 +1,13 @@ +//! Verifiers for [PLONK], currently there are [`PlonkSuccinctVerifier`] and +//! [`PlonkVerifier`] implemented and both are implemented assuming the used +//! [`PolynomialCommitmentScheme`] has [atomic] or [split] accumulation scheme +//! ([`PlonkVerifier`] is just [`PlonkSuccinctVerifier`] plus doing accumulator +//! deciding then returns accept/reject as ouput). +//! +//! [PLONK]: https://eprint.iacr.org/2019/953 +//! [atomic]: https://eprint.iacr.org/2020/499 +//! [split]: https://eprint.iacr.org/2020/1618 + use crate::{ cost::{Cost, CostEstimation}, loader::Loader, @@ -17,6 +27,8 @@ pub(crate) mod protocol; pub use proof::PlonkProof; pub use protocol::PlonkProtocol; +/// Verifier that verifies the cheap part of PLONK and ouput the accumulator. +#[derive(Debug)] pub struct PlonkSuccinctVerifier>(PhantomData<(AS, AE)>); impl SnarkVerifier for PlonkSuccinctVerifier @@ -80,6 +92,9 @@ where } } +/// Verifier that first verifies the cheap part of PLONK, then decides +/// accumulator and returns accept/reject as ouput. +#[derive(Debug)] pub struct PlonkVerifier>(PhantomData<(AS, AE)>); impl SnarkVerifier for PlonkVerifier diff --git a/snark-verifier/src/verifier/plonk/proof.rs b/snark-verifier/src/verifier/plonk/proof.rs index fb12aed6..962417ec 100644 --- a/snark-verifier/src/verifier/plonk/proof.rs +++ b/snark-verifier/src/verifier/plonk/proof.rs @@ -15,6 +15,8 @@ use crate::{ }; use std::{collections::HashMap, iter}; +/// Proof of PLONK with [`PolynomialCommitmentScheme`] that has +/// [`AccumulationScheme`]. #[derive(Clone, Debug)] pub struct PlonkProof where @@ -22,13 +24,21 @@ where L: Loader, AS: AccumulationScheme + PolynomialCommitmentScheme, { + /// Computed commitments of instance polynomials. pub committed_instances: Option>, + /// Commitments of witness polynomials read from transcript. pub witnesses: Vec, + /// Challenges squeezed from transcript. pub challenges: Vec, + /// Quotient commitments read from transcript. pub quotients: Vec, + /// Query point squeezed from transcript. pub z: L::LoadedScalar, + /// Evaluations read from transcript. pub evaluations: Vec, + /// Proof of [`PolynomialCommitmentScheme`]. pub pcs: >::Proof, + /// Old [`AccumulationScheme::Accumulator`]s read from instnaces. pub old_accumulators: Vec, } @@ -38,6 +48,7 @@ where L: Loader, AS: AccumulationScheme + PolynomialCommitmentScheme, { + /// Reads each part from transcript as [`PlonkProof`]. pub fn read( svk: &>::VerifyingKey, protocol: &PlonkProtocol, @@ -157,16 +168,15 @@ where }) } - pub fn empty_queries(protocol: &PlonkProtocol) -> Vec> { + pub(super) fn empty_queries(protocol: &PlonkProtocol) -> Vec> { protocol .queries .iter() - .map(|query| pcs::Query { - poly: query.poly, - shift: protocol + .map(|query| { + let shift = protocol .domain - .rotate_scalar(C::Scalar::one(), query.rotation), - eval: (), + .rotate_scalar(C::Scalar::one(), query.rotation); + pcs::Query::new(query.poly, shift) }) .collect() } diff --git a/snark-verifier/src/verifier/plonk/protocol.rs b/snark-verifier/src/verifier/plonk/protocol.rs index daaa58a9..94f97201 100644 --- a/snark-verifier/src/verifier/plonk/protocol.rs +++ b/snark-verifier/src/verifier/plonk/protocol.rs @@ -15,25 +15,37 @@ use std::{ ops::{Add, Mul, Neg, Sub}, }; +/// Protocol specifying configuration of a PLONK. #[derive(Clone, Debug)] pub struct PlonkProtocol where C: CurveAffine, L: Loader, { - // Common description + /// Working domain. pub domain: Domain, + /// Commitments of preprocessed polynomials. pub preprocessed: Vec, + /// Number of instances in each instance polynomial. pub num_instance: Vec, + /// Number of witness polynomials in each phase. pub num_witness: Vec, + /// Number of challenges to squeeze from transcript after each phase. pub num_challenge: Vec, + /// Evaluations to read from transcript. pub evaluations: Vec, + /// [`crate::pcs::PolynomialCommitmentScheme`] queries to verify. pub queries: Vec, + /// Structure of quotient polynomial. pub quotient: QuotientPolynomial, - // Minor customization + /// Prover and verifier common initial state to write to transcript if any. pub transcript_initial_state: Option, + /// Instance polynomials commiting key if any. pub instance_committing_key: Option>, + /// Linearization strategy. pub linearization: Option, + /// Indices (instance polynomial index, row) of encoded + /// [`crate::pcs::AccumulationScheme::Accumulator`]s. pub accumulator_indices: Vec>, } @@ -76,6 +88,8 @@ impl PlonkProtocol where C: CurveAffine, { + /// Loaded `PlonkProtocol` with `preprocessed` and + /// `transcript_initial_state` loaded as constant. pub fn loaded>(&self, loader: &L) -> PlonkProtocol { let preprocessed = self .preprocessed @@ -117,6 +131,9 @@ mod halo2 { where C: CurveAffine, { + /// Loaded `PlonkProtocol` with `preprocessed` and + /// `transcript_initial_state` loaded as witness, which is useful when + /// doing recursion. pub fn loaded_preprocessed_as_witness<'a, EccChip: EccInstructions<'a, C>>( &self, loader: &Rc>,