mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 08:13:11 +00:00
Merge pull request #1447 from topos-protocol/plonky2_doc
Add some more explicit doc on plonky2 crate main components
This commit is contained in:
commit
3b1ed824f5
@ -63,7 +63,7 @@ where
|
||||
}
|
||||
|
||||
/// Standard addition formula for elliptic curves, restricted to the cases
|
||||
/// https://en.wikipedia.org/wiki/Elliptic_curve#Algebraic_interpretation
|
||||
/// <https://en.wikipedia.org/wiki/Elliptic_curve#Algebraic_interpretation>
|
||||
impl<T: FieldExt> Add for Curve<T> {
|
||||
type Output = Self;
|
||||
|
||||
@ -201,7 +201,7 @@ pub(crate) fn bn_tate(p: Curve<BN254>, q: Curve<Fp2<BN254>>) -> Fp12<BN254> {
|
||||
}
|
||||
|
||||
/// Standard code for miller loop, can be found on page 99 at this url:
|
||||
/// https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf#page=107
|
||||
/// <https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf#page=107>
|
||||
/// where BN_EXP is a hardcoding of the array of Booleans that the loop traverses
|
||||
pub(crate) fn bn_miller_loop(p: Curve<BN254>, q: Curve<Fp2<BN254>>) -> Fp12<BN254> {
|
||||
let mut r = p;
|
||||
|
||||
@ -43,7 +43,7 @@ impl<F: Field> Lookup<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// logUp protocol from https://ia.cr/2022/1530
|
||||
/// logUp protocol from <https://ia.cr/2022/1530>
|
||||
/// Compute the helper columns for the lookup argument.
|
||||
/// Given columns `f0,...,fk` and a column `t`, such that `∪fi ⊆ t`, and challenges `x`,
|
||||
/// this computes the helper columns `h_i = 1/(x+f_2i) + 1/(x+f_2i+1)`, `g = 1/(x+t)`,
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
//! Fast Reed-Solomon IOP (FRI) protocol.
|
||||
//!
|
||||
//! It provides both a native implementation and an in-circuit version
|
||||
//! of the FRI verifier for recursive proof composition.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
@ -191,7 +191,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.arithmetic(F::ONE, F::ONE, x, one, y)
|
||||
}
|
||||
|
||||
/// Add `n` `Target`s.
|
||||
/// Adds `n` `Target`s.
|
||||
pub fn add_many<T>(&mut self, terms: impl IntoIterator<Item = T>) -> Target
|
||||
where
|
||||
T: Borrow<Target>,
|
||||
@ -224,7 +224,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.fold(self.one(), |acc, t| self.mul(acc, *t.borrow()))
|
||||
}
|
||||
|
||||
/// Exponentiate `base` to the power of `2^power_log`.
|
||||
/// Exponentiates `base` to the power of `2^power_log`.
|
||||
pub fn exp_power_of_2(&mut self, base: Target, power_log: usize) -> Target {
|
||||
if power_log > self.num_base_arithmetic_ops_per_gate() {
|
||||
// Cheaper to just use `ExponentiateGate`.
|
||||
@ -239,7 +239,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
// TODO: Test
|
||||
/// Exponentiate `base` to the power of `exponent`, given by its little-endian bits.
|
||||
/// Exponentiates `base` to the power of `exponent`, given by its little-endian bits.
|
||||
pub fn exp_from_bits(
|
||||
&mut self,
|
||||
base: Target,
|
||||
@ -264,7 +264,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
// TODO: Test
|
||||
/// Exponentiate `base` to the power of `exponent`, where `exponent < 2^num_bits`.
|
||||
/// Exponentiates `base` to the power of `exponent`, where `exponent < 2^num_bits`.
|
||||
pub fn exp(&mut self, base: Target, exponent: Target, num_bits: usize) -> Target {
|
||||
let exponent_bits = self.split_le(exponent, num_bits);
|
||||
|
||||
@ -303,7 +303,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
product
|
||||
}
|
||||
|
||||
/// Exponentiate `base` to the power of a known `exponent`.
|
||||
/// Exponentiates `base` to the power of a known `exponent`.
|
||||
// TODO: Test
|
||||
pub fn exp_u64(&mut self, base: Target, mut exponent: u64) -> Target {
|
||||
let mut exp_bits = Vec::new();
|
||||
@ -330,28 +330,32 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.inverse_extension(x_ext).0[0]
|
||||
}
|
||||
|
||||
/// Computes the logical NOT of the provided [`BoolTarget`].
|
||||
pub fn not(&mut self, b: BoolTarget) -> BoolTarget {
|
||||
let one = self.one();
|
||||
let res = self.sub(one, b.target);
|
||||
BoolTarget::new_unsafe(res)
|
||||
}
|
||||
|
||||
/// Computes the logical AND of the provided [`BoolTarget`]s.
|
||||
pub fn and(&mut self, b1: BoolTarget, b2: BoolTarget) -> BoolTarget {
|
||||
BoolTarget::new_unsafe(self.mul(b1.target, b2.target))
|
||||
}
|
||||
|
||||
/// computes the arithmetic extension of logical "or": `b1 + b2 - b1 * b2`
|
||||
/// Computes the logical OR through the arithmetic expression: `b1 + b2 - b1 * b2`.
|
||||
pub fn or(&mut self, b1: BoolTarget, b2: BoolTarget) -> BoolTarget {
|
||||
let res_minus_b2 = self.arithmetic(-F::ONE, F::ONE, b1.target, b2.target, b1.target);
|
||||
BoolTarget::new_unsafe(self.add(res_minus_b2, b2.target))
|
||||
}
|
||||
|
||||
/// Outputs `x` if `b` is true, and else `y`, through the formula: `b*x + (1-b)*y`.
|
||||
pub fn _if(&mut self, b: BoolTarget, x: Target, y: Target) -> Target {
|
||||
let not_b = self.not(b);
|
||||
let maybe_x = self.mul(b.target, x);
|
||||
self.mul_add(not_b.target, y, maybe_x)
|
||||
}
|
||||
|
||||
/// Checks whether `x` and `y` are equal and outputs the boolean result.
|
||||
pub fn is_equal(&mut self, x: Target, y: Target) -> BoolTarget {
|
||||
let zero = self.zero();
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
//! Helper gadgets providing additional methods to
|
||||
//! [CircuitBuilder](crate::plonk::circuit_builder::CircuitBuilder),
|
||||
//! to ease circuit creation.
|
||||
|
||||
pub mod arithmetic;
|
||||
pub mod arithmetic_extension;
|
||||
pub mod hash;
|
||||
|
||||
@ -20,8 +20,8 @@ use crate::plonk::vars::{
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config
|
||||
/// supports enough routed wires, it can support several such operations in one gate.
|
||||
/// A gate which can perform a weighted multiply-add, i.e. `result = c0.x.y + c1.z`. If the config
|
||||
/// has enough routed wires, it can support several such operations in one gate.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArithmeticGate {
|
||||
/// Number of arithmetic operations performed by an arithmetic gate.
|
||||
|
||||
@ -16,8 +16,8 @@ use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData};
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config
|
||||
/// supports enough routed wires, it can support several such operations in one gate.
|
||||
/// A gate which can perform a weighted multiply-add, i.e. `result = c0.x.y + c1.z`. If the config
|
||||
/// has enough routed wires, it can support several such operations in one gate.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArithmeticExtensionGate<const D: usize> {
|
||||
/// Number of arithmetic operations performed by an arithmetic gate.
|
||||
|
||||
@ -26,15 +26,45 @@ use crate::plonk::vars::{
|
||||
use crate::util::serialization::{Buffer, IoResult};
|
||||
|
||||
/// A custom gate.
|
||||
///
|
||||
/// Vanilla Plonk arithmetization only supports basic fan-in 2 / fan-out 1 arithmetic gates,
|
||||
/// each of the form
|
||||
///
|
||||
/// $$ a.b.q_M + a.q_L + b.q_R + c.q_O + q_C = 0 $$
|
||||
///
|
||||
/// where:
|
||||
/// - q_M, q_L, q_R and q_O are boolean selectors,
|
||||
/// - a, b and c are values used as inputs and output respectively,
|
||||
/// - q_C is a constant (possibly 0).
|
||||
///
|
||||
/// This allows expressing simple operations like multiplication, addition, etc. For
|
||||
/// instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0.
|
||||
/// Hence, the gate equation simplifies to a.b - c = 0, or a.b = c.
|
||||
///
|
||||
/// However, such a gate is fairly limited for more complex computations. Hence, when a computation may
|
||||
/// require too many of these "vanilla" gates, or when a computation arises often within the same circuit,
|
||||
/// one may want to construct a tailored custom gate. These custom gates can use more selectors and are
|
||||
/// not necessarily limited to 2 inputs + 1 output = 3 wires.
|
||||
/// For instance, plonky2 supports natively a custom Poseidon hash gate that uses 135 wires.
|
||||
///
|
||||
/// Note however that extending the number of wires necessary for a custom gate comes at a price, and may
|
||||
/// impact the overall performances when generating proofs for a circuit containing them.
|
||||
pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + Sync {
|
||||
/// Defines a unique identifier for this custom gate.
|
||||
///
|
||||
/// This is used as differentiating tag in gate serializers.
|
||||
fn id(&self) -> String;
|
||||
|
||||
/// Serializes this custom gate to the targeted byte buffer, with the provided [`CommonCircuitData`].
|
||||
fn serialize(&self, dst: &mut Vec<u8>, common_data: &CommonCircuitData<F, D>) -> IoResult<()>;
|
||||
|
||||
/// Deserializes the bytes in the provided buffer into this custom gate, given some [`CommonCircuitData`].
|
||||
fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData<F, D>) -> IoResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Defines and evaluates the constraints that enforce the statement represented by this gate.
|
||||
/// Constraints must be defined in the extension of this custom gate base field.
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension>;
|
||||
|
||||
/// Like `eval_unfiltered`, but specialized for points in the base field.
|
||||
@ -88,6 +118,12 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S
|
||||
res
|
||||
}
|
||||
|
||||
/// Defines the recursive constraints that enforce the statement represented by this custom gate.
|
||||
/// This is necessary to recursively verify proofs generated from a circuit containing such gates.
|
||||
///
|
||||
/// **Note**: The order of the recursive constraints output by this method should match exactly the order
|
||||
/// of the constraints obtained by the non-recursive [`Gate::eval_unfiltered`] method, otherwise the
|
||||
/// prover won't be able to generate proofs.
|
||||
fn eval_unfiltered_circuit(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -175,10 +211,20 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S
|
||||
}
|
||||
|
||||
/// The generators used to populate the witness.
|
||||
/// Note: This should return exactly 1 generator per operation in the gate.
|
||||
///
|
||||
/// **Note**: This should return exactly 1 generator per operation in the gate.
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F, D>>;
|
||||
|
||||
/// The number of wires used by this gate.
|
||||
///
|
||||
/// While vanilla Plonk can only evaluate one addition/multiplication at a time, a wider
|
||||
/// configuration may be able to accomodate several identical gates at once. This is
|
||||
/// particularly helpful for tiny custom gates that are being used extensively in circuits.
|
||||
///
|
||||
/// For instance, the [crate::gates::multiplication_extension::MulExtensionGate] takes `3*D`
|
||||
/// wires per multiplication (where `D`` is the degree of the extension), hence for a usual
|
||||
/// configuration of 80 routed wires with D=2, one can evaluate 13 multiplications within a
|
||||
/// single gate.
|
||||
fn num_wires(&self) -> usize;
|
||||
|
||||
/// The number of constants used by this gate.
|
||||
@ -187,6 +233,7 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S
|
||||
/// The maximum degree among this gate's constraint polynomials.
|
||||
fn degree(&self) -> usize;
|
||||
|
||||
/// The number of constraints defined by this sole custom gate.
|
||||
fn num_constraints(&self) -> usize;
|
||||
|
||||
/// Number of operations performed by the gate.
|
||||
|
||||
@ -1,3 +1,25 @@
|
||||
//! plonky2 custom gates.
|
||||
//!
|
||||
//! Vanilla Plonk arithmetization only supports basic fan-in 2 / fan-out 1 arithmetic gates,
|
||||
//! each of the form
|
||||
//!
|
||||
//! $$ a.b.q_M + a.q_L + b.q_R + c.q_O + q_C = 0 $$
|
||||
//!
|
||||
//! where:
|
||||
//! - q_M, q_L, q_R and q_O are boolean selectors,
|
||||
//! - a, b and c are values used as inputs and output respectively,
|
||||
//! - q_C is a constant (possibly 0).
|
||||
//!
|
||||
//! This allows expressing simple operations like multiplication, addition, etc. For
|
||||
//! instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0.
|
||||
//! Hence, the gate equation simplifies to a.b - c = 0, or a.b = c.
|
||||
//!
|
||||
//! However, such a gate is fairly limited for more complex computations. Hence, when a computation may
|
||||
//! require too many of these "vanilla" gates, or when a computation arises often within the same circuit,
|
||||
//! one may want to construct a tailored custom gate. These custom gates can use more selectors and are
|
||||
//! not necessarily limited to 2 inputs + 1 output = 3 wires.
|
||||
//! For instance, plonky2 supports natively a custom Poseidon hash gate that uses 135 wires.
|
||||
|
||||
// Gates have `new` methods that return `GateRef`s.
|
||||
|
||||
pub mod arithmetic_base;
|
||||
|
||||
@ -16,8 +16,8 @@ use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData};
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which can perform a weighted multiplication, i.e. `result = c0 x y`. If the config
|
||||
/// supports enough routed wires, it can support several such operations in one gate.
|
||||
/// A gate which can perform a weighted multiplication, i.e. `result = c0.x.y` on [`ExtensionTarget`].
|
||||
/// If the config has enough routed wires, it can support several such operations in one gate.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MulExtensionGate<const D: usize> {
|
||||
/// Number of multiplications performed by the gate.
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
//! plonky2 hashing logic for in-circuit hashing and Merkle proof verification
|
||||
//! as well as specific hash functions implementation.
|
||||
|
||||
mod arch;
|
||||
pub mod hash_types;
|
||||
pub mod hashing;
|
||||
|
||||
@ -9,6 +9,10 @@ use crate::iop::target::Target;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
/// `Target`s representing an element of an extension field.
|
||||
///
|
||||
/// This is typically used in recursion settings, where the outer circuit must verify
|
||||
/// a proof satisfying an inner circuit's statement, which is verified using arithmetic
|
||||
/// in an extension of the base field.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct ExtensionTarget<const D: usize>(pub [Target; D]);
|
||||
|
||||
|
||||
@ -8,15 +8,25 @@ use crate::iop::wire::Wire;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
|
||||
/// A location in the witness.
|
||||
///
|
||||
/// Targets can either be placed at a specific location, or be "floating" around,
|
||||
/// serving as intermediary value holders, and copied to other locations whenever needed.
|
||||
///
|
||||
/// When generating a proof for a given circuit, the prover will "set" the values of some
|
||||
/// (or all) targets, so that they satisfy the circuit constraints. This is done through
|
||||
/// the [PartialWitness](crate::iop::witness::PartialWitness) interface.
|
||||
///
|
||||
/// There are different "variants" of the `Target` type, namely [`ExtensionTarget`],
|
||||
/// [ExtensionAlgebraTarget](crate::iop::ext_target::ExtensionAlgebraTarget).
|
||||
/// The `Target` type is the default one for most circuits verifying some simple statement.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub enum Target {
|
||||
/// A target that has a fixed location in the witness (seen as a `degree x num_wires` grid).
|
||||
Wire(Wire),
|
||||
/// A target that doesn't have any inherent location in the witness (but it can be copied to
|
||||
/// another target that does). This is useful for representing intermediate values in witness
|
||||
/// generation.
|
||||
VirtualTarget {
|
||||
index: usize,
|
||||
},
|
||||
VirtualTarget { index: usize },
|
||||
}
|
||||
|
||||
impl Default for Target {
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
pub extern crate alloc;
|
||||
|
||||
/// Re-export of `plonky2_field`.
|
||||
#[doc(inline)]
|
||||
pub use plonky2_field as field;
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//! Logic for building plonky2 circuits.
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec;
|
||||
@ -83,7 +85,60 @@ pub struct LookupWire {
|
||||
/// Index of the first lookup table row (i.e. the last `LookupTableGate`).
|
||||
pub first_lut_gate: usize,
|
||||
}
|
||||
|
||||
/// Structure used to construct a plonky2 circuit. It provides all the necessary toolkit that,
|
||||
/// from an initial circuit configuration, will enable one to design a circuit and its associated
|
||||
/// prover/verifier data.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```rust
|
||||
/// use plonky2::plonk::circuit_data::CircuitConfig;
|
||||
/// use plonky2::iop::witness::PartialWitness;
|
||||
/// use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
/// use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
/// use plonky2::field::types::Field;
|
||||
///
|
||||
/// // Define parameters for this circuit
|
||||
/// const D: usize = 2;
|
||||
/// type C = PoseidonGoldilocksConfig;
|
||||
/// type F = <C as GenericConfig<D>>::F;
|
||||
///
|
||||
/// let config = CircuitConfig::standard_recursion_config();
|
||||
/// let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
///
|
||||
/// // Build a circuit for the statement: "I know the 100th term
|
||||
/// // of the Fibonacci sequence, starting from 0 and 1".
|
||||
/// let initial_a = builder.constant(F::ZERO);
|
||||
/// let initial_b = builder.constant(F::ONE);
|
||||
/// let mut prev_target = initial_a;
|
||||
/// let mut cur_target = initial_b;
|
||||
/// for _ in 0..99 {
|
||||
/// // Encode an addition of the two previous terms
|
||||
/// let temp = builder.add(prev_target, cur_target);
|
||||
/// // Shift the two previous terms with the new value
|
||||
/// prev_target = cur_target;
|
||||
/// cur_target = temp;
|
||||
/// }
|
||||
///
|
||||
/// // The only public input is the result (which is generated).
|
||||
/// builder.register_public_input(cur_target);
|
||||
///
|
||||
/// // Build the circuit
|
||||
/// let circuit_data = builder.build::<C>();
|
||||
///
|
||||
/// // Now compute the witness and generate a proof
|
||||
/// let mut pw = PartialWitness::new();
|
||||
///
|
||||
/// // There are no public inputs to register, as the only one
|
||||
/// // will be generated while proving the statement.
|
||||
/// let proof = circuit_data.prove(pw).unwrap();
|
||||
///
|
||||
/// // Verify the proof
|
||||
/// assert!(circuit_data.verify(proof).is_ok());
|
||||
/// ```
|
||||
pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
|
||||
/// Circuit configuration to be used by this [`CircuitBuilder`].
|
||||
pub config: CircuitConfig,
|
||||
|
||||
/// A domain separator, which is included in the initial Fiat-Shamir seed. This is generally not
|
||||
@ -126,7 +181,8 @@ pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
|
||||
/// List of constant generators used to fill the constant wires.
|
||||
constant_generators: Vec<ConstantGenerator<F>>,
|
||||
|
||||
/// Rows for each LUT: LookupWire contains: first `LookupGate`, first `LookupTableGate`, last `LookupTableGate`.
|
||||
/// Rows for each LUT: [`LookupWire`] contains: first [`LookupGate`], first and last
|
||||
/// [LookupTableGate](crate::gates::lookup_table::LookupTableGate).
|
||||
lookup_rows: Vec<LookupWire>,
|
||||
|
||||
/// For each LUT index, vector of `(looking_in, looking_out)` pairs.
|
||||
@ -146,6 +202,10 @@ pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Given a [`CircuitConfig`], generate a new [`CircuitBuilder`] instance.
|
||||
/// It will also check that the configuration provided is consistent, i.e.
|
||||
/// that the different parameters provided can achieve the targeted security
|
||||
/// level.
|
||||
pub fn new(config: CircuitConfig) -> Self {
|
||||
let builder = CircuitBuilder {
|
||||
config,
|
||||
@ -173,6 +233,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
builder
|
||||
}
|
||||
|
||||
/// Assert that the configuration used to create this `CircuitBuilder` is consistent,
|
||||
/// i.e. that the different parameters meet the targeted security level.
|
||||
fn check_config(&self) {
|
||||
let &CircuitConfig {
|
||||
security_bits,
|
||||
@ -201,6 +263,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.domain_separator = Some(separator);
|
||||
}
|
||||
|
||||
/// Outputs the number of gates in this circuit.
|
||||
pub fn num_gates(&self) -> usize {
|
||||
self.gate_instances.len()
|
||||
}
|
||||
@ -215,6 +278,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
targets.iter().for_each(|&t| self.register_public_input(t));
|
||||
}
|
||||
|
||||
/// Outputs the number of public inputs in this circuit.
|
||||
pub fn num_public_inputs(&self) -> usize {
|
||||
self.public_inputs.len()
|
||||
}
|
||||
@ -244,10 +308,13 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.lut_to_lookups[lut_index].push((looking_in, looking_out));
|
||||
}
|
||||
|
||||
/// Outputs the number of lookup tables in this circuit.
|
||||
pub fn num_luts(&self) -> usize {
|
||||
self.lut_to_lookups.len()
|
||||
}
|
||||
|
||||
/// Given an index, outputs the corresponding looking table in the set of tables
|
||||
/// used in this circuit, as a sequence of target tuples `(input, output)`.
|
||||
pub fn get_lut_lookups(&self, lut_index: usize) -> &[(Target, Target)] {
|
||||
&self.lut_to_lookups[lut_index]
|
||||
}
|
||||
@ -262,22 +329,28 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
Target::VirtualTarget { index }
|
||||
}
|
||||
|
||||
/// Adds `n` new "virtual" targets.
|
||||
pub fn add_virtual_targets(&mut self, n: usize) -> Vec<Target> {
|
||||
(0..n).map(|_i| self.add_virtual_target()).collect()
|
||||
}
|
||||
|
||||
/// Adds `N` new "virtual" targets, arranged as an array.
|
||||
pub fn add_virtual_target_arr<const N: usize>(&mut self) -> [Target; N] {
|
||||
[0; N].map(|_| self.add_virtual_target())
|
||||
}
|
||||
|
||||
/// Adds a new `HashOutTarget`. `NUM_HASH_OUT_ELTS` being hardcoded to 4, it internally
|
||||
/// adds 4 virtual targets in a vector fashion.
|
||||
pub fn add_virtual_hash(&mut self) -> HashOutTarget {
|
||||
HashOutTarget::from_vec(self.add_virtual_targets(4))
|
||||
}
|
||||
|
||||
/// Adds a new `MerkleCapTarget`, consisting in `1 << cap_height` `HashOutTarget`.
|
||||
pub fn add_virtual_cap(&mut self, cap_height: usize) -> MerkleCapTarget {
|
||||
MerkleCapTarget(self.add_virtual_hashes(1 << cap_height))
|
||||
}
|
||||
|
||||
/// Adds `n` new `HashOutTarget` in a vector fashion.
|
||||
pub fn add_virtual_hashes(&mut self, n: usize) -> Vec<HashOutTarget> {
|
||||
(0..n).map(|_i| self.add_virtual_hash()).collect()
|
||||
}
|
||||
@ -337,7 +410,9 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
/// Add a virtual verifier data, register it as a public input and set it to `self.verifier_data_public_input`.
|
||||
/// WARNING: Do not register any public input after calling this! TODO: relax this
|
||||
///
|
||||
/// **WARNING**: Do not register any public input after calling this!
|
||||
// TODO: relax this
|
||||
pub fn add_verifier_data_public_inputs(&mut self) -> VerifierCircuitTarget {
|
||||
assert!(
|
||||
self.verifier_data_public_input.is_none(),
|
||||
@ -410,16 +485,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a gate type to the set of gates to be used in this circuit. This can be useful
|
||||
/// in conditional recursion to uniformize the set of gates of the different circuits.
|
||||
pub fn add_gate_to_gate_set(&mut self, gate: GateRef<F, D>) {
|
||||
self.gates.insert(gate);
|
||||
}
|
||||
|
||||
pub fn connect_extension(&mut self, src: ExtensionTarget<D>, dst: ExtensionTarget<D>) {
|
||||
for i in 0..D {
|
||||
self.connect(src.0[i], dst.0[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a generator which will copy `src` to `dst`.
|
||||
pub fn generate_copy(&mut self, src: Target, dst: Target) {
|
||||
self.add_simple_generator(CopyGenerator { src, dst });
|
||||
@ -427,6 +498,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
/// Uses Plonk's permutation argument to require that two elements be equal.
|
||||
/// Both elements must be routable, otherwise this method will panic.
|
||||
///
|
||||
/// For an example of usage, see [`CircuitBuilder::assert_one()`].
|
||||
pub fn connect(&mut self, x: Target, y: Target) {
|
||||
assert!(
|
||||
x.is_routable(&self.config),
|
||||
@ -440,17 +513,40 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.push(CopyConstraint::new((x, y), self.context_log.open_stack()));
|
||||
}
|
||||
|
||||
/// Enforces that two [`ExtensionTarget<D>`] underlying values are equal.
|
||||
pub fn connect_extension(&mut self, src: ExtensionTarget<D>, dst: ExtensionTarget<D>) {
|
||||
for i in 0..D {
|
||||
self.connect(src.0[i], dst.0[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enforces that a routable `Target` value is 0, using Plonk's permutation argument.
|
||||
pub fn assert_zero(&mut self, x: Target) {
|
||||
let zero = self.zero();
|
||||
self.connect(x, zero);
|
||||
}
|
||||
|
||||
/// Enforces that a routable `Target` value is 1, using Plonk's permutation argument.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Let say the circuit contains a target `a`, and a target `b` as public input so that the
|
||||
/// prover can non-deterministically compute the multiplicative inverse of `a` when generating
|
||||
/// a proof.
|
||||
///
|
||||
/// One can then add the following constraint in the circuit to enforce that the value provided
|
||||
/// by the prover is correct:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let c = builder.mul(a, b);
|
||||
/// builder.assert_one(c);
|
||||
/// ```
|
||||
pub fn assert_one(&mut self, x: Target) {
|
||||
let one = self.one();
|
||||
self.connect(x, one);
|
||||
}
|
||||
|
||||
pub fn add_generators(&mut self, generators: Vec<WitnessGeneratorRef<F, D>>) {
|
||||
fn add_generators(&mut self, generators: Vec<WitnessGeneratorRef<F, D>>) {
|
||||
self.generators.extend(generators);
|
||||
}
|
||||
|
||||
@ -479,10 +575,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.constant(F::NEG_ONE)
|
||||
}
|
||||
|
||||
/// Returns a routable boolean target set to false.
|
||||
pub fn _false(&mut self) -> BoolTarget {
|
||||
BoolTarget::new_unsafe(self.zero())
|
||||
}
|
||||
|
||||
/// Returns a routable boolean target set to true.
|
||||
pub fn _true(&mut self) -> BoolTarget {
|
||||
BoolTarget::new_unsafe(self.one())
|
||||
}
|
||||
@ -501,10 +599,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
target
|
||||
}
|
||||
|
||||
/// Returns a vector of routable targets with the given constant values.
|
||||
pub fn constants(&mut self, constants: &[F]) -> Vec<Target> {
|
||||
constants.iter().map(|&c| self.constant(c)).collect()
|
||||
}
|
||||
|
||||
/// Returns a routable target with the given constant boolean value.
|
||||
pub fn constant_bool(&mut self, b: bool) -> BoolTarget {
|
||||
if b {
|
||||
self._true()
|
||||
@ -513,12 +613,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a routable [`HashOutTarget`].
|
||||
pub fn constant_hash(&mut self, h: HashOut<F>) -> HashOutTarget {
|
||||
HashOutTarget {
|
||||
elements: h.elements.map(|x| self.constant(x)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a routable [`MerkleCapTarget`].
|
||||
pub fn constant_merkle_cap<H: Hasher<F, Hash = HashOut<F>>>(
|
||||
&mut self,
|
||||
cap: &MerkleCap<F, H>,
|
||||
@ -545,7 +647,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.targets_to_constants.get(&target).cloned()
|
||||
}
|
||||
|
||||
/// If the given `ExtensionTarget` is a constant (i.e. it was created by the
|
||||
/// If the given [`ExtensionTarget`] is a constant (i.e. it was created by the
|
||||
/// `constant_extension(F)` method), returns its constant value. Otherwise, returns `None`.
|
||||
pub fn target_as_constant_ext(&self, target: ExtensionTarget<D>) -> Option<F::Extension> {
|
||||
// Get a Vec of any coefficients that are constant. If we end up with exactly D of them,
|
||||
@ -906,7 +1008,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// In PLONK's permutation argument, there's a slight chance of division by zero. We can
|
||||
/// mitigate this by randomizing some unused witness elements, so if proving fails with
|
||||
/// division by zero, the next attempt will have an (almost) independent chance of success.
|
||||
/// See https://github.com/0xPolygonZero/plonky2/issues/456
|
||||
/// See <https://github.com/0xPolygonZero/plonky2/issues/456>.
|
||||
fn randomize_unused_pi_wires(&mut self, pi_gate: usize) {
|
||||
for wire in PublicInputGate::wires_public_inputs_hash().end..self.config.num_wires {
|
||||
self.add_simple_generator(RandomValueGenerator {
|
||||
@ -1178,6 +1280,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Builds a "full circuit", with both prover and verifier data.
|
||||
pub fn build<C: GenericConfig<D, F = F>>(self) -> CircuitData<F, C, D> {
|
||||
self.build_with_options(true)
|
||||
}
|
||||
|
||||
@ -1,3 +1,17 @@
|
||||
//! Circuit data specific to the prover and the verifier.
|
||||
//!
|
||||
//! This module also defines a [`CircuitConfig`] to be customized
|
||||
//! when building circuits for arbitrary statements.
|
||||
//!
|
||||
//! After building a circuit, one obtains an instance of [`CircuitData`].
|
||||
//! This contains both prover and verifier data, allowing to generate
|
||||
//! proofs for the given circuit and verify them.
|
||||
//!
|
||||
//! Most of the [`CircuitData`] is actually prover-specific, and can be
|
||||
//! extracted by calling [`CircuitData::prover_data`] method.
|
||||
//! The verifier data can similarly be extracted by calling [`CircuitData::verifier_data`].
|
||||
//! This is useful to allow even small devices to verify plonky2 proofs.
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
@ -38,10 +52,22 @@ use crate::util::serialization::{
|
||||
};
|
||||
use crate::util::timing::TimingTree;
|
||||
|
||||
/// Configuration to be used when building a circuit. This defines the shape of the circuit
|
||||
/// as well as its targeted security level and sub-protocol (e.g. FRI) parameters.
|
||||
///
|
||||
/// It supports a [`Default`] implementation tailored for recursion with Poseidon hash (of width 12)
|
||||
/// as internal hash function and FRI rate of 1/8.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct CircuitConfig {
|
||||
/// The number of wires available at each row. This corresponds to the "width" of the circuit,
|
||||
/// and consists in the sum of routed wires and advice wires.
|
||||
pub num_wires: usize,
|
||||
/// The number of routed wires, i.e. wires that will be involved in Plonk's permutation argument.
|
||||
/// This allows copy constraints, i.e. enforcing that two distant values in a circuit are equal.
|
||||
/// Non-routed wires are called advice wires.
|
||||
pub num_routed_wires: usize,
|
||||
/// The number of constants that can be used per gate. If a gate requires more constants than the config
|
||||
/// allows, the [`CircuitBuilder`] will complain when trying to add this gate to its set of gates.
|
||||
pub num_constants: usize,
|
||||
/// Whether to use a dedicated gate for base field arithmetic, rather than using a single gate
|
||||
/// for both base field and extension field arithmetic.
|
||||
@ -50,6 +76,8 @@ pub struct CircuitConfig {
|
||||
/// The number of challenge points to generate, for IOPs that have soundness errors of (roughly)
|
||||
/// `degree / |F|`.
|
||||
pub num_challenges: usize,
|
||||
/// A boolean to activate the zero-knowledge property. When this is set to `false`, proofs *may*
|
||||
/// leak additional information.
|
||||
pub zero_knowledge: bool,
|
||||
/// A cap on the quotient polynomial's degree factor. The actual degree factor is derived
|
||||
/// systematically, but will never exceed this value.
|
||||
|
||||
@ -1,3 +1,11 @@
|
||||
//! Hashing configuration to be used when building a circuit.
|
||||
//!
|
||||
//! This module defines a [`Hasher`] trait as well as its recursive
|
||||
//! counterpart [`AlgebraicHasher`] for in-circuit hashing. It also
|
||||
//! provides concrete configurations, one fully recursive leveraging
|
||||
//! the Poseidon hash function both internally and natively, and one
|
||||
//! mixing Poseidon internally and truncated Keccak externally.
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
//! plonky2 proving system.
|
||||
//!
|
||||
//! This module also defines the [CircuitBuilder](circuit_builder::CircuitBuilder)
|
||||
//! structure, used to build custom plonky2 circuits satisfying arbitrary statements.
|
||||
|
||||
pub mod circuit_builder;
|
||||
pub mod circuit_data;
|
||||
pub mod config;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//! Utility methods and constants for Plonk.
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
//! plonky2 proof definition.
|
||||
//!
|
||||
//! Proofs can be later compressed to reduce their size, into either
|
||||
//! [`CompressedProof`] or [`CompressedProofWithPublicInputs`] formats.
|
||||
//! The latter can be directly passed to a verifier to assert its correctness.
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//! plonky2 prover implementation.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
use core::cmp::min;
|
||||
@ -441,7 +443,7 @@ fn wires_permutation_partial_products_and_zs<
|
||||
}
|
||||
|
||||
/// Computes lookup polynomials for a given challenge.
|
||||
/// The polynomials hold the value of RE, Sum and Ldc of the Tip5 paper (https://eprint.iacr.org/2023/107.pdf). To reduce their
|
||||
/// The polynomials hold the value of RE, Sum and Ldc of the Tip5 paper (<https://eprint.iacr.org/2023/107.pdf>). To reduce their
|
||||
/// numbers, we batch multiple slots in a single polynomial. Since RE only involves degree one constraints, we can batch
|
||||
/// all the slots of a row. For Sum and Ldc, batching increases the constraint degree, so we bound the number of
|
||||
/// partial polynomials according to `max_quotient_degree_factor`.
|
||||
|
||||
@ -323,8 +323,8 @@ pub(crate) fn eval_vanishing_poly_base_batch<F: RichField + Extendable<D>, const
|
||||
res_batch
|
||||
}
|
||||
|
||||
/// Evaluates all lookup constraints, based on the logarithmic derivatives paper (https://eprint.iacr.org/2022/1530.pdf),
|
||||
/// following the Tip5 paper's implementation (https://eprint.iacr.org/2023/107.pdf).
|
||||
/// Evaluates all lookup constraints, based on the logarithmic derivatives paper (<https://eprint.iacr.org/2022/1530.pdf>),
|
||||
/// following the Tip5 paper's implementation (<https://eprint.iacr.org/2023/107.pdf>).
|
||||
///
|
||||
/// There are three polynomials to check:
|
||||
/// - RE ensures the well formation of lookup tables;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//! Logic for evaluating constraints.
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::field::extension::algebra::ExtensionAlgebra;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//! plonky2 verifier implementation.
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
|
||||
use crate::field::extension::Extendable;
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
//! Recursion logic for verifying recursively plonky2 circuits.
|
||||
//!
|
||||
//! This module also provides ways to perform conditional recursive verification
|
||||
//! (between two different circuits, depending on a condition), and cyclic
|
||||
//! recursion where a circuit implements its own verification logic.
|
||||
|
||||
pub mod conditional_recursive_verifier;
|
||||
pub mod cyclic_recursion;
|
||||
pub mod dummy_circuit;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//! Utility module for helper methods and plonky2 serialization logic.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use plonky2_maybe_rayon::*;
|
||||
|
||||
@ -59,7 +59,7 @@ macro_rules! get_gate_tag_impl {
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Macro implementing the `GateSerializer` trait.
|
||||
/// Macro implementing the [`GateSerializer`] trait.
|
||||
/// To serialize a list of gates used for a circuit,
|
||||
/// this macro should be called with a struct on which to implement
|
||||
/// this as first argument, followed by all the targeted gates.
|
||||
|
||||
@ -63,7 +63,7 @@ macro_rules! get_generator_tag_impl {
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Macro implementing the `WitnessGeneratorSerializer` trait.
|
||||
/// Macro implementing the [`WitnessGeneratorSerializer`] trait.
|
||||
/// To serialize a list of generators used for a circuit,
|
||||
/// this macro should be called with a struct on which to implement
|
||||
/// this as first argument, followed by all the targeted generators.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user