halo2_base/
lib.rs

1//! Base library to build Halo2 circuits.
2#![allow(incomplete_features)]
3#![deny(clippy::perf)]
4#![allow(clippy::too_many_arguments)]
5#![warn(clippy::default_numeric_fallback)]
6#![warn(missing_docs)]
7
8use getset::CopyGetters;
9use itertools::Itertools;
10// Different memory allocator options:
11#[cfg(feature = "jemallocator")]
12use jemallocator::Jemalloc;
13#[cfg(feature = "jemallocator")]
14#[global_allocator]
15static GLOBAL: Jemalloc = Jemalloc;
16
17// mimalloc is fastest on Mac M2
18#[cfg(feature = "mimalloc")]
19use mimalloc::MiMalloc;
20#[cfg(feature = "mimalloc")]
21#[global_allocator]
22static GLOBAL: MiMalloc = MiMalloc;
23
24#[cfg(all(feature = "halo2-pse", feature = "halo2-axiom"))]
25compile_error!(
26    "Cannot have both \"halo2-pse\" and \"halo2-axiom\" features enabled at the same time!"
27);
28#[cfg(not(any(feature = "halo2-pse", feature = "halo2-axiom")))]
29compile_error!("Must enable exactly one of \"halo2-pse\" or \"halo2-axiom\" features to choose which halo2_proofs crate to use.");
30
31// use gates::flex_gate::MAX_PHASE;
32#[cfg(feature = "halo2-pse")]
33pub use halo2_proofs;
34#[cfg(feature = "halo2-axiom")]
35pub use halo2_proofs_axiom as halo2_proofs;
36
37use halo2_proofs::halo2curves::ff;
38use halo2_proofs::plonk::Assigned;
39use utils::ScalarField;
40use virtual_region::copy_constraints::SharedCopyConstraintManager;
41
42/// Module that contains the main API for creating and working with circuits.
43/// `gates` is misleading because we currently only use one custom gate throughout.
44pub mod gates;
45/// Module for the Poseidon hash function.
46pub mod poseidon;
47/// Module for SafeType which enforce value range and realted functions.
48pub mod safe_types;
49/// Utility functions for converting between different types of field elements.
50pub mod utils;
51pub mod virtual_region;
52
53/// Constant representing whether the Layouter calls `synthesize` once just to get region shape.
54#[cfg(feature = "halo2-axiom")]
55pub const SKIP_FIRST_PASS: bool = false;
56/// Constant representing whether the Layouter calls `synthesize` once just to get region shape.
57#[cfg(feature = "halo2-pse")]
58pub const SKIP_FIRST_PASS: bool = true;
59
60/// Convenience Enum which abstracts the scenarios under a value is added to an advice column.
61#[derive(Clone, Copy, Debug)]
62pub enum QuantumCell<F: ScalarField> {
63    /// An [AssignedValue] already existing in the advice column (e.g., a witness value that was already assigned in a previous cell in the column).
64    /// * Assigns a new cell into the advice column with value equal to the value of a.
65    /// * Imposes an equality constraint between the new cell and the cell of a so the Verifier guarantees that these two cells are always equal.
66    Existing(AssignedValue<F>),
67    // This is a guard for witness values assigned after pkey generation. We do not use `Value` api anymore.
68    /// A non-existing witness [ScalarField] value (e.g. private input) to add to an advice column.
69    Witness(F),
70    /// A non-existing witness [ScalarField] marked as a fraction for optimization in batch inversion later.
71    WitnessFraction(Assigned<F>),
72    /// A known constant value added as a witness value to the advice column and added to the "Fixed" column during circuit creation time.
73    /// * Visible to both the Prover and the Verifier.
74    /// * Imposes an equality constraint between the two corresponding cells in the advice and fixed columns.
75    Constant(F),
76}
77
78impl<F: ScalarField> From<AssignedValue<F>> for QuantumCell<F> {
79    /// Converts an [`AssignedValue<F>`] into a [`QuantumCell<F>`] of enum variant `Existing`.
80    fn from(a: AssignedValue<F>) -> Self {
81        Self::Existing(a)
82    }
83}
84
85impl<F: ScalarField> QuantumCell<F> {
86    /// Returns an immutable reference to the underlying [ScalarField] value of a [`QuantumCell<F>`].
87    ///
88    /// Panics if the [`QuantumCell<F>`] is of type `WitnessFraction`.
89    pub fn value(&self) -> &F {
90        match self {
91            Self::Existing(a) => a.value(),
92            Self::Witness(a) => a,
93            Self::WitnessFraction(_) => {
94                panic!("Trying to get value of a fraction before batch inversion")
95            }
96            Self::Constant(a) => a,
97        }
98    }
99}
100
101/// Unique tag for a context across all virtual regions.
102/// In the form `(type_id, context_id)` where `type_id` should be a unique identifier
103/// for the virtual region this context belongs to, and `context_id` is a counter local to that virtual region.
104pub type ContextTag = (&'static str, usize);
105
106/// Pointer to the position of a cell at `offset` in an advice column within a [Context] of `context_id`.
107#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
108pub struct ContextCell {
109    /// The unique string identifier of the virtual region that this cell belongs to.
110    pub type_id: &'static str,
111    /// Identifier of the [Context] that this cell belongs to.
112    pub context_id: usize,
113    /// Relative offset of the cell within this [Context] advice column.
114    pub offset: usize,
115}
116
117impl ContextCell {
118    /// Creates a new [ContextCell] with the given `type_id`, `context_id`, and `offset`.
119    ///
120    /// **Warning:** If you create your own `Context` in a new virtual region not provided by our libraries, you must ensure that the `type_id: &str` of the context is a globally unique identifier for the virtual region, distinct from the other `type_id` strings used to identify other virtual regions. We suggest that you either include your crate name as a prefix in the `type_id` or use [`module_path!`](https://doc.rust-lang.org/std/macro.module_path.html) to generate a prefix.
121    /// In the future we will introduce a macro to check this uniqueness at compile time.
122    pub fn new(type_id: &'static str, context_id: usize, offset: usize) -> Self {
123        Self { type_id, context_id, offset }
124    }
125}
126
127/// Pointer containing cell value and location within [Context].
128///
129/// Note: Performs a copy of the value, should only be used when you are about to assign the value again elsewhere.
130#[derive(Clone, Copy, Debug)]
131pub struct AssignedValue<F: crate::ff::Field> {
132    /// Value of the cell.
133    pub value: Assigned<F>, // we don't use reference to avoid issues with lifetimes (you can't safely borrow from vector and push to it at the same time).
134    // only needed during vkey, pkey gen to fetch the actual cell from the relevant context
135    /// [ContextCell] pointer to the cell the value is assigned to within an advice column of a [Context].
136    pub cell: Option<ContextCell>,
137}
138
139impl<F: ScalarField> AssignedValue<F> {
140    /// Returns an immutable reference to the underlying value of an [`AssignedValue<F>`].
141    ///
142    /// Panics if the witness value is of type [Assigned::Rational] or [Assigned::Zero].
143    pub fn value(&self) -> &F {
144        match &self.value {
145            Assigned::Trivial(a) => a,
146            _ => unreachable!(), // if trying to fetch an un-evaluated fraction, you will have to do something manual
147        }
148    }
149
150    /// Debug helper function for writing negative tests. This will change the **witness** value in `ctx` corresponding to `self.offset`.
151    /// This assumes that `ctx` is the context that `self` lies in.
152    pub fn debug_prank(&self, ctx: &mut Context<F>, prank_value: F) {
153        ctx.advice[self.cell.unwrap().offset] = Assigned::Trivial(prank_value);
154    }
155}
156
157impl<F: ScalarField> AsRef<AssignedValue<F>> for AssignedValue<F> {
158    fn as_ref(&self) -> &AssignedValue<F> {
159        self
160    }
161}
162
163/// Represents a single thread of an execution trace.
164/// * We keep the naming [Context] for historical reasons.
165///
166/// [Context] is CPU thread-local.
167#[derive(Clone, Debug, CopyGetters)]
168pub struct Context<F: ScalarField> {
169    /// Flag to determine whether only witness generation or proving and verification key generation is being performed.
170    /// * If witness gen is performed many operations can be skipped for optimization.
171    #[getset(get_copy = "pub")]
172    witness_gen_only: bool,
173    /// The challenge phase that this [Context] will map to.
174    #[getset(get_copy = "pub")]
175    phase: usize,
176    /// Identifier for what virtual region this context is in.
177    /// Warning: the circuit writer must ensure that distinct virtual regions have distinct names as strings to prevent possible errors.
178    /// We do not use [std::any::TypeId] because it is not stable across rust builds or dependencies.
179    #[getset(get_copy = "pub")]
180    type_id: &'static str,
181    /// Identifier to reference cells from this [Context].
182    context_id: usize,
183
184    /// Single column of advice cells.
185    pub advice: Vec<Assigned<F>>,
186
187    /// Slight optimization: since zero is so commonly used, keep a reference to the zero cell.
188    zero_cell: Option<AssignedValue<F>>,
189
190    // ========================================
191    // General principle: we don't need to optimize anything specific to `witness_gen_only == false` because it is only done during keygen
192    // If `witness_gen_only == false`:
193    /// [Vec] representing the selector column of this [Context] accompanying each `advice` column
194    /// * Assumed to have the same length as `advice`
195    pub selector: Vec<bool>,
196
197    /// Global shared thread-safe manager for all copy (equality) constraints between virtual advice, constants, and raw external Halo2 cells.
198    pub copy_manager: SharedCopyConstraintManager<F>,
199}
200
201impl<F: ScalarField> Context<F> {
202    /// Creates a new [Context] with the given `context_id` and witness generation enabled/disabled by the `witness_gen_only` flag.
203    /// * `witness_gen_only`: flag to determine whether public key generation or only witness generation is being performed.
204    /// * `context_id`: identifier to reference advice cells from this [Context] later.
205    ///
206    /// **Warning:** If you create your own `Context` in a new virtual region not provided by our libraries, you must ensure that the `type_id: &str` of the context is a globally unique identifier for the virtual region, distinct from the other `type_id` strings used to identify other virtual regions. We suggest that you either include your crate name as a prefix in the `type_id` or use [`module_path!`](https://doc.rust-lang.org/std/macro.module_path.html) to generate a prefix.
207    /// In the future we will introduce a macro to check this uniqueness at compile time.
208    pub fn new(
209        witness_gen_only: bool,
210        phase: usize,
211        type_id: &'static str,
212        context_id: usize,
213        copy_manager: SharedCopyConstraintManager<F>,
214    ) -> Self {
215        Self {
216            witness_gen_only,
217            phase,
218            type_id,
219            context_id,
220            advice: Vec::new(),
221            selector: Vec::new(),
222            zero_cell: None,
223            copy_manager,
224        }
225    }
226
227    /// The context id, this can be used as a tag when CPU multi-threading
228    pub fn id(&self) -> usize {
229        self.context_id
230    }
231
232    /// A unique tag that should identify this context across all virtual regions and phases.
233    pub fn tag(&self) -> ContextTag {
234        (self.type_id, self.context_id)
235    }
236
237    fn latest_cell(&self) -> ContextCell {
238        ContextCell::new(self.type_id, self.context_id, self.advice.len() - 1)
239    }
240
241    /// Virtually assigns the `input` within the current [Context], with different handling depending on the [QuantumCell] variant.
242    pub fn assign_cell(&mut self, input: impl Into<QuantumCell<F>>) {
243        // Determine the type of the cell and push it to the relevant vector
244        match input.into() {
245            QuantumCell::Existing(acell) => {
246                self.advice.push(acell.value);
247                // If witness generation is not performed, enforce equality constraints between the existing cell and the new cell
248                if !self.witness_gen_only {
249                    let new_cell = self.latest_cell();
250                    self.copy_manager
251                        .lock()
252                        .unwrap()
253                        .advice_equalities
254                        .push((new_cell, acell.cell.unwrap()));
255                }
256            }
257            QuantumCell::Witness(val) => {
258                self.advice.push(Assigned::Trivial(val));
259            }
260            QuantumCell::WitnessFraction(val) => {
261                self.advice.push(val);
262            }
263            QuantumCell::Constant(c) => {
264                self.advice.push(Assigned::Trivial(c));
265                // If witness generation is not performed, enforce equality constraints between the existing cell and the new cell
266                if !self.witness_gen_only {
267                    let new_cell = self.latest_cell();
268                    self.copy_manager.lock().unwrap().constant_equalities.push((c, new_cell));
269                }
270            }
271        }
272    }
273
274    /// Returns the [AssignedValue] of the last cell in the `advice` column of [Context] or [None] if `advice` is empty
275    pub fn last(&self) -> Option<AssignedValue<F>> {
276        self.advice.last().map(|v| {
277            let cell = (!self.witness_gen_only).then_some(self.latest_cell());
278            AssignedValue { value: *v, cell }
279        })
280    }
281
282    /// Returns the [AssignedValue] of the cell at the given `offset` in the `advice` column of [Context]
283    /// * `offset`: the offset of the cell to be fetched
284    ///     * `offset` may be negative indexing from the end of the column (e.g., `-1` is the last cell)
285    /// * Assumes `offset` is a valid index in `advice`;
286    ///     * `0` <= `offset` < `advice.len()` (or `advice.len() + offset >= 0` if `offset` is negative)
287    pub fn get(&self, offset: isize) -> AssignedValue<F> {
288        let offset = if offset < 0 {
289            self.advice.len().wrapping_add_signed(offset)
290        } else {
291            offset as usize
292        };
293        assert!(offset < self.advice.len());
294        let cell = (!self.witness_gen_only).then_some(ContextCell::new(
295            self.type_id,
296            self.context_id,
297            offset,
298        ));
299        AssignedValue { value: self.advice[offset], cell }
300    }
301
302    /// Creates an equality constraint between two `advice` cells.
303    /// * `a`: the first `advice` cell to be constrained equal
304    /// * `b`: the second `advice` cell to be constrained equal
305    /// * Assumes both cells are `advice` cells
306    pub fn constrain_equal(&mut self, a: &AssignedValue<F>, b: &AssignedValue<F>) {
307        if !self.witness_gen_only {
308            self.copy_manager
309                .lock()
310                .unwrap()
311                .advice_equalities
312                .push((a.cell.unwrap(), b.cell.unwrap()));
313        }
314    }
315
316    /// Pushes multiple advice cells to the `advice` column of [Context] and enables them by enabling the corresponding selector specified in `gate_offset`.
317    ///
318    /// * `inputs`: Iterator that specifies the cells to be assigned
319    /// * `gate_offsets`: specifies relative offset from current position to enable selector for the gate (e.g., `0` is `inputs[0]`).
320    ///     * `offset` may be negative indexing from the end of the column (e.g., `-1` is the last previously assigned cell)
321    pub fn assign_region<Q>(
322        &mut self,
323        inputs: impl IntoIterator<Item = Q>,
324        gate_offsets: impl IntoIterator<Item = isize>,
325    ) where
326        Q: Into<QuantumCell<F>>,
327    {
328        if self.witness_gen_only {
329            for input in inputs {
330                self.assign_cell(input);
331            }
332        } else {
333            let row_offset = self.advice.len();
334            // note: row_offset may not equal self.selector.len() at this point if we previously used `load_constant` or `load_witness`
335            for input in inputs {
336                self.assign_cell(input);
337            }
338            self.selector.resize(self.advice.len(), false);
339            for offset in gate_offsets {
340                *self
341                    .selector
342                    .get_mut(row_offset.checked_add_signed(offset).expect("Invalid gate offset"))
343                    .expect("Invalid selector offset") = true;
344            }
345        }
346    }
347
348    /// Pushes multiple advice cells to the `advice` column of [Context] and enables them by enabling the corresponding selector specified in `gate_offset` and returns the last assigned cell.
349    ///
350    /// Assumes `gate_offsets` is the same length as `inputs`
351    ///
352    /// Returns the last assigned cell
353    /// * `inputs`: Iterator that specifies the cells to be assigned
354    /// * `gate_offsets`: specifies indices to enable selector for the gate; assume `gate_offsets` is sorted in increasing order
355    ///     * `offset` may be negative indexing from the end of the column (e.g., `-1` is the last cell)
356    pub fn assign_region_last<Q>(
357        &mut self,
358        inputs: impl IntoIterator<Item = Q>,
359        gate_offsets: impl IntoIterator<Item = isize>,
360    ) -> AssignedValue<F>
361    where
362        Q: Into<QuantumCell<F>>,
363    {
364        self.assign_region(inputs, gate_offsets);
365        self.last().unwrap()
366    }
367
368    /// Pushes multiple advice cells to the `advice` column of [Context] and enables them by enabling the corresponding selector specified in `gate_offset`.
369    ///
370    /// Allows for the specification of equality constraints between cells at `equality_offsets` within the `advice` column and external advice cells specified in `external_equality` (e.g, Fixed column).
371    /// * `gate_offsets`: specifies indices to enable selector for the gate;
372    ///     * `offset` may be negative indexing from the end of the column (e.g., `-1` is the last cell)
373    /// * `equality_offsets`: specifies pairs of indices to constrain equality
374    /// * `external_equality`: specifies an existing cell to constrain equality with the cell at a certain index
375    pub fn assign_region_smart<Q>(
376        &mut self,
377        inputs: impl IntoIterator<Item = Q>,
378        gate_offsets: impl IntoIterator<Item = isize>,
379        equality_offsets: impl IntoIterator<Item = (isize, isize)>,
380        external_equality: impl IntoIterator<Item = (Option<ContextCell>, isize)>,
381    ) where
382        Q: Into<QuantumCell<F>>,
383    {
384        let row_offset = self.advice.len();
385        self.assign_region(inputs, gate_offsets);
386
387        // note: row_offset may not equal self.selector.len() at this point if we previously used `load_constant` or `load_witness`
388        // If not in witness generation mode, add equality constraints.
389        if !self.witness_gen_only {
390            // Add equality constraints between cells in the advice column.
391            for (offset1, offset2) in equality_offsets {
392                self.copy_manager.lock().unwrap().advice_equalities.push((
393                    ContextCell::new(
394                        self.type_id,
395                        self.context_id,
396                        row_offset.wrapping_add_signed(offset1),
397                    ),
398                    ContextCell::new(
399                        self.type_id,
400                        self.context_id,
401                        row_offset.wrapping_add_signed(offset2),
402                    ),
403                ));
404            }
405            // Add equality constraints between cells in the advice column and external cells (Fixed column).
406            for (cell, offset) in external_equality {
407                self.copy_manager.lock().unwrap().advice_equalities.push((
408                    cell.unwrap(),
409                    ContextCell::new(
410                        self.type_id,
411                        self.context_id,
412                        row_offset.wrapping_add_signed(offset),
413                    ),
414                ));
415            }
416        }
417    }
418
419    /// Assigns a region of witness cells in an iterator and returns a [Vec] of assigned cells.
420    /// * `witnesses`: Iterator that specifies the cells to be assigned
421    pub fn assign_witnesses(
422        &mut self,
423        witnesses: impl IntoIterator<Item = F>,
424    ) -> Vec<AssignedValue<F>> {
425        let row_offset = self.advice.len();
426        self.assign_region(witnesses.into_iter().map(QuantumCell::Witness), []);
427        self.advice[row_offset..]
428            .iter()
429            .enumerate()
430            .map(|(i, v)| {
431                let cell = (!self.witness_gen_only).then_some(ContextCell::new(
432                    self.type_id,
433                    self.context_id,
434                    row_offset + i,
435                ));
436                AssignedValue { value: *v, cell }
437            })
438            .collect()
439    }
440
441    /// Assigns a witness value and returns the corresponding assigned cell.
442    /// * `witness`: the witness value to be assigned
443    pub fn load_witness(&mut self, witness: F) -> AssignedValue<F> {
444        self.assign_cell(QuantumCell::Witness(witness));
445        if !self.witness_gen_only {
446            self.selector.resize(self.advice.len(), false);
447        }
448        self.last().unwrap()
449    }
450
451    /// Assigns a constant value and returns the corresponding assigned cell.
452    /// * `c`: the constant value to be assigned
453    pub fn load_constant(&mut self, c: F) -> AssignedValue<F> {
454        self.assign_cell(QuantumCell::Constant(c));
455        if !self.witness_gen_only {
456            self.selector.resize(self.advice.len(), false);
457        }
458        self.last().unwrap()
459    }
460
461    /// Assigns a list of constant values and returns the corresponding assigned cells.
462    /// * `c`: the list of constant values to be assigned
463    pub fn load_constants(&mut self, c: &[F]) -> Vec<AssignedValue<F>> {
464        c.iter().map(|v| self.load_constant(*v)).collect_vec()
465    }
466
467    /// Assigns the 0 value to a new cell or returns a previously assigned zero cell from `zero_cell`.
468    pub fn load_zero(&mut self) -> AssignedValue<F> {
469        if let Some(zcell) = &self.zero_cell {
470            return *zcell;
471        }
472        let zero_cell = self.load_constant(F::ZERO);
473        self.zero_cell = Some(zero_cell);
474        zero_cell
475    }
476
477    /// Helper function for debugging using `MockProver`. This adds a constraint that always fails.
478    /// The `MockProver` will print out the row, column where it fails, so it serves as a debugging "break point"
479    /// so you can add to your code to search for where the actual constraint failure occurs.
480    pub fn debug_assert_false(&mut self) {
481        use rand_chacha::rand_core::OsRng;
482        let rand1 = self.load_witness(F::random(OsRng));
483        let rand2 = self.load_witness(F::random(OsRng));
484        self.constrain_equal(&rand1, &rand2);
485    }
486}