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}