zero_cond.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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. use halo2_proofs::{
  19. circuit::{AssignedCell, Layouter},
  20. pasta::group::ff::WithSmallOrderMulGroup,
  21. plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
  22. poly::Rotation,
  23. };
  24. use super::is_zero::{IsZeroChip, IsZeroConfig};
  25. #[derive(Clone, Debug)]
  26. pub struct ZeroCondConfig<F> {
  27. selector: Selector,
  28. a: Column<Advice>,
  29. b: Column<Advice>,
  30. is_zero: IsZeroConfig<F>,
  31. output: Column<Advice>,
  32. }
  33. #[derive(Clone, Debug)]
  34. pub struct ZeroCondChip<F: WithSmallOrderMulGroup<3> + Ord> {
  35. config: ZeroCondConfig<F>,
  36. }
  37. impl<F: WithSmallOrderMulGroup<3> + Ord> ZeroCondChip<F> {
  38. pub fn construct(config: ZeroCondConfig<F>) -> Self {
  39. Self { config }
  40. }
  41. /// Configure the chip.
  42. ///
  43. /// Advice columns:
  44. /// * `[0]` - a
  45. /// * `[1]` - b
  46. /// * `[2]` - is_zero output
  47. /// * `[3]` - zero_cond output
  48. pub fn configure(
  49. meta: &mut ConstraintSystem<F>,
  50. advices: [Column<Advice>; 4],
  51. ) -> ZeroCondConfig<F> {
  52. for i in advices {
  53. meta.enable_equality(i);
  54. }
  55. let selector = meta.selector();
  56. let is_zero = IsZeroChip::configure(
  57. meta,
  58. |meta| meta.query_selector(selector),
  59. |meta| meta.query_advice(advices[0], Rotation::cur()),
  60. advices[2],
  61. );
  62. // NOTE: a is not used here because it already went into IsZero
  63. meta.create_gate("f(a, b) = if a == 0 {a} else {b}", |meta| {
  64. let s = meta.query_selector(selector);
  65. let b = meta.query_advice(advices[1], Rotation::cur());
  66. let output = meta.query_advice(advices[3], Rotation::cur());
  67. let one = Expression::Constant(F::ONE);
  68. vec![s * (is_zero.expr() * output.clone() + (one - is_zero.expr()) * (output - b))]
  69. });
  70. ZeroCondConfig { selector, a: advices[0], b: advices[1], is_zero, output: advices[3] }
  71. }
  72. pub fn assign(
  73. &self,
  74. mut layouter: impl Layouter<F>,
  75. a: AssignedCell<F, F>,
  76. b: AssignedCell<F, F>,
  77. ) -> Result<AssignedCell<F, F>, Error> {
  78. let is_zero_chip = IsZeroChip::construct(self.config.is_zero.clone());
  79. let out = layouter.assign_region(
  80. || "f(a, b) = if a == 0 {a} else {b}",
  81. |mut region| {
  82. self.config.selector.enable(&mut region, 0)?;
  83. let a = a.copy_advice(|| "copy a", &mut region, self.config.a, 0)?;
  84. let b = b.copy_advice(|| "copy b", &mut region, self.config.b, 0)?;
  85. is_zero_chip.assign(&mut region, 0, a.value().copied())?;
  86. let output = a.value().copied().to_field().zip(b.value().copied()).map(|(a, b)| {
  87. if a == F::ZERO.into() {
  88. F::ZERO
  89. } else {
  90. b
  91. }
  92. });
  93. let cell = region.assign_advice(|| "output", self.config.output, 0, || output)?;
  94. Ok(cell)
  95. },
  96. )?;
  97. Ok(out)
  98. }
  99. }
  100. #[cfg(test)]
  101. mod tests {
  102. use super::*;
  103. use crate::zk::assign_free_advice;
  104. use halo2_proofs::{
  105. circuit::{SimpleFloorPlanner, Value},
  106. dev::MockProver,
  107. pasta::Fp,
  108. plonk::{Circuit, Instance},
  109. };
  110. #[derive(Default)]
  111. struct MyCircuit {
  112. a: Value<Fp>,
  113. b: Value<Fp>,
  114. }
  115. impl Circuit<Fp> for MyCircuit {
  116. type Config = (ZeroCondConfig<Fp>, [Column<Advice>; 5], Column<Instance>);
  117. type FloorPlanner = SimpleFloorPlanner;
  118. type Params = ();
  119. fn without_witnesses(&self) -> Self {
  120. Self::default()
  121. }
  122. fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
  123. let advices = [
  124. meta.advice_column(),
  125. meta.advice_column(),
  126. meta.advice_column(),
  127. meta.advice_column(),
  128. meta.advice_column(),
  129. ];
  130. for i in advices {
  131. meta.enable_equality(i);
  132. }
  133. let instance = meta.instance_column();
  134. meta.enable_equality(instance);
  135. let zcc = ZeroCondChip::configure(meta, advices[1..5].try_into().unwrap());
  136. (zcc, advices, instance)
  137. }
  138. fn synthesize(
  139. &self,
  140. config: Self::Config,
  141. mut layouter: impl Layouter<Fp>,
  142. ) -> Result<(), Error> {
  143. let a = assign_free_advice(layouter.namespace(|| "load a"), config.1[0], self.a)?;
  144. let b = assign_free_advice(layouter.namespace(|| "load b"), config.1[0], self.b)?;
  145. let zcc = ZeroCondChip::construct(config.0);
  146. let output = zcc.assign(layouter.namespace(|| "zero_cond"), a, b)?;
  147. layouter.constrain_instance(output.cell(), config.2, 0)?;
  148. Ok(())
  149. }
  150. }
  151. #[test]
  152. fn zero_cond() {
  153. let a = Fp::from(0);
  154. let b = Fp::from(69);
  155. let p_circuit = MyCircuit { a: Value::known(a), b: Value::known(b) };
  156. let public_inputs = vec![a];
  157. let prover = MockProver::run(3, &p_circuit, vec![public_inputs]).unwrap();
  158. prover.assert_satisfied();
  159. let a = Fp::from(12);
  160. let b = Fp::from(42);
  161. let p_circuit = MyCircuit { a: Value::known(a), b: Value::known(b) };
  162. let public_inputs = vec![b];
  163. let prover = MockProver::run(3, &p_circuit, vec![public_inputs]).unwrap();
  164. prover.assert_satisfied();
  165. }
  166. }