/ src / zk / gadget / is_zero.rs
is_zero.rs
 1  /* This file is part of DarkFi (https://dark.fi)
 2   *
 3   * Copyright (C) 2020-2025 Dyne.org foundation
 4   *
 5   * This program is free software: you can redistribute it and/or modify
 6   * it under the terms of the GNU Affero General Public License as
 7   * published by the Free Software Foundation, either version 3 of the
 8   * License, or (at your option) any later version.
 9   *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Affero General Public License for more details.
14   *
15   * You should have received a copy of the GNU Affero General Public License
16   * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17   */
18  
19  use halo2_proofs::{
20      circuit::{Region, Value},
21      pasta::group::ff::WithSmallOrderMulGroup,
22      plonk::{Advice, Column, ConstraintSystem, Error, Expression, VirtualCells},
23      poly::Rotation,
24  };
25  
26  #[derive(Clone, Debug)]
27  pub struct IsZeroConfig<F> {
28      pub value_inv: Column<Advice>,
29      pub is_zero_expr: Expression<F>,
30  }
31  
32  impl<F: WithSmallOrderMulGroup<3> + Ord> IsZeroConfig<F> {
33      pub fn expr(&self) -> Expression<F> {
34          self.is_zero_expr.clone()
35      }
36  }
37  
38  pub struct IsZeroChip<F: WithSmallOrderMulGroup<3> + Ord> {
39      config: IsZeroConfig<F>,
40  }
41  
42  impl<F: WithSmallOrderMulGroup<3> + Ord> IsZeroChip<F> {
43      pub fn construct(config: IsZeroConfig<F>) -> Self {
44          Self { config }
45      }
46  
47      pub fn configure(
48          meta: &mut ConstraintSystem<F>,
49          q_enable: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F>,
50          value: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F>,
51          value_inv: Column<Advice>,
52      ) -> IsZeroConfig<F> {
53          let mut is_zero_expr = Expression::Constant(F::ZERO);
54  
55          meta.create_gate("is_zero", |meta| {
56              //
57              // valid | value |  value_inv |  1 - value * value_inv | value * (1 - value* value_inv)
58              // ------+-------+------------+------------------------+-------------------------------
59              //  yes  |   x   |    1/x     |         0              |  0
60              //  no   |   x   |    0       |         1              |  x
61              //  yes  |   0   |    0       |         1              |  0
62              //  yes  |   0   |    y       |         1              |  0
63              //
64              let value = value(meta);
65              let q_enable = q_enable(meta);
66              let value_inv = meta.query_advice(value_inv, Rotation::cur());
67  
68              is_zero_expr = Expression::Constant(F::ONE) - value.clone() * value_inv;
69              vec![q_enable * value * is_zero_expr.clone()]
70          });
71  
72          IsZeroConfig { value_inv, is_zero_expr }
73      }
74  
75      pub fn assign(
76          &self,
77          region: &mut Region<'_, F>,
78          offset: usize,
79          value: Value<F>,
80      ) -> Result<(), Error> {
81          let value_inv = value.map(|value| value.invert().unwrap_or(F::ZERO));
82          region.assign_advice(|| "value inv", self.config.value_inv, offset, || value_inv)?;
83          Ok(())
84      }
85  }