darkfi/zk/gadget/
is_zero.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 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
19use 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)]
27pub struct IsZeroConfig<F> {
28    pub value_inv: Column<Advice>,
29    pub is_zero_expr: Expression<F>,
30}
31
32impl<F: WithSmallOrderMulGroup<3> + Ord> IsZeroConfig<F> {
33    pub fn expr(&self) -> Expression<F> {
34        self.is_zero_expr.clone()
35    }
36}
37
38pub struct IsZeroChip<F: WithSmallOrderMulGroup<3> + Ord> {
39    config: IsZeroConfig<F>,
40}
41
42impl<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}