Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request 0xPolygonZero#1447 from topos-protocol/plonky2_doc
Browse files Browse the repository at this point in the history
Add some more explicit doc on plonky2 crate main components
  • Loading branch information
Nashtare authored Jan 11, 2024
2 parents 233ddd4 + cda3084 commit 3b1ed82
Show file tree
Hide file tree
Showing 28 changed files with 301 additions and 35 deletions.
4 changes: 2 additions & 2 deletions evm/src/curve_pairings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion evm/src/lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)`,
Expand Down
5 changes: 5 additions & 0 deletions plonky2/src/fri/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
16 changes: 10 additions & 6 deletions plonky2/src/gadgets/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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`.
Expand All @@ -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,
Expand All @@ -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);

Expand Down Expand Up @@ -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();
Expand All @@ -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();

Expand Down
4 changes: 4 additions & 0 deletions plonky2/src/gadgets/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
4 changes: 2 additions & 2 deletions plonky2/src/gates/arithmetic_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions plonky2/src/gates/arithmetic_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
49 changes: 48 additions & 1 deletion plonky2/src/gates/gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
22 changes: 22 additions & 0 deletions plonky2/src/gates/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
4 changes: 2 additions & 2 deletions plonky2/src/gates/multiplication_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions plonky2/src/hash/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
4 changes: 4 additions & 0 deletions plonky2/src/iop/ext_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down
16 changes: 13 additions & 3 deletions plonky2/src/iop/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions plonky2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

pub extern crate alloc;

/// Re-export of `plonky2_field`.
#[doc(inline)]
pub use plonky2_field as field;

Expand Down
Loading

0 comments on commit 3b1ed82

Please sign in to comment.