darkfi/zk/gadget/
small_range_check.rs1use std::marker::PhantomData;
20
21use halo2_proofs::{
22 circuit::{AssignedCell, Chip, Layouter},
23 pasta::group::ff::WithSmallOrderMulGroup,
24 plonk,
25 plonk::{Advice, Column, ConstraintSystem, Constraints, Expression, Selector},
26 poly::Rotation,
27};
28
29pub fn range_check<F: WithSmallOrderMulGroup<3> + Ord>(
32 word: Expression<F>,
33 range: u8,
34) -> Expression<F> {
35 assert!(range > 0);
36
37 (1..(range as usize))
38 .fold(word.clone(), |acc, i| acc * (Expression::Constant(F::from(i as u64)) - word.clone()))
39}
40
41#[derive(Clone, Debug)]
42pub struct SmallRangeCheckConfig {
43 pub z: Column<Advice>,
44 pub selector: Selector,
45}
46
47#[derive(Clone, Debug)]
48pub struct SmallRangeCheckChip<F> {
49 config: SmallRangeCheckConfig,
50 _marker: PhantomData<F>,
51}
52
53impl<F: WithSmallOrderMulGroup<3> + Ord> Chip<F> for SmallRangeCheckChip<F> {
54 type Config = SmallRangeCheckConfig;
55 type Loaded = ();
56
57 fn config(&self) -> &Self::Config {
58 &self.config
59 }
60
61 fn loaded(&self) -> &Self::Loaded {
62 &()
63 }
64}
65
66impl<F: WithSmallOrderMulGroup<3> + Ord> SmallRangeCheckChip<F> {
67 pub fn construct(config: SmallRangeCheckConfig) -> Self {
68 Self { config, _marker: PhantomData }
69 }
70
71 pub fn configure(
72 meta: &mut ConstraintSystem<F>,
73 z: Column<Advice>,
74 range: u8,
75 ) -> SmallRangeCheckConfig {
76 meta.enable_equality(z);
78
79 let selector = meta.selector();
80
81 meta.create_gate("bool check", |meta| {
82 let selector = meta.query_selector(selector);
83 let advice = meta.query_advice(z, Rotation::cur());
84 Constraints::with_selector(selector, Some(range_check(advice, range)))
85 });
86
87 SmallRangeCheckConfig { z, selector }
88 }
89
90 pub fn small_range_check(
91 &self,
92 mut layouter: impl Layouter<F>,
93 value: AssignedCell<F, F>,
94 ) -> Result<(), plonk::Error> {
95 layouter.assign_region(
96 || "small range constrain",
97 |mut region| {
98 self.config.selector.enable(&mut region, 0)?;
99 value.copy_advice(|| "z_0", &mut region, self.config.z, 0)?;
100 Ok(())
101 },
102 )
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::zk::assign_free_advice;
110 use halo2_proofs::{
111 circuit::{floor_planner, Value},
112 dev::MockProver,
113 pasta::pallas,
114 plonk::Circuit,
115 };
116
117 #[derive(Default)]
118 struct SmallRangeCircuit {
119 value: Value<pallas::Base>,
120 }
121
122 impl Circuit<pallas::Base> for SmallRangeCircuit {
123 type Config = (SmallRangeCheckConfig, Column<Advice>);
124 type FloorPlanner = floor_planner::V1;
125 type Params = ();
126
127 fn without_witnesses(&self) -> Self {
128 Self::default()
129 }
130
131 fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
132 let w = meta.advice_column();
133 let z = meta.advice_column();
134
135 meta.enable_equality(w);
136
137 let config = SmallRangeCheckChip::configure(meta, z, 2);
139
140 (config, w)
141 }
142
143 fn synthesize(
144 &self,
145 config: Self::Config,
146 mut layouter: impl Layouter<pallas::Base>,
147 ) -> Result<(), plonk::Error> {
148 let chip = SmallRangeCheckChip::construct(config.0.clone());
149 let value = assign_free_advice(layouter.namespace(|| "val"), config.1, self.value)?;
150 chip.small_range_check(layouter.namespace(|| "boolean check"), value)?;
151 Ok(())
152 }
153 }
154
155 #[test]
156 fn boolean_range_check() {
157 let k = 3;
158
159 for i in 0..2 {
160 let circuit = SmallRangeCircuit { value: Value::known(pallas::Base::from(i as u64)) };
161 let prover = MockProver::run(k, &circuit, vec![]).unwrap();
162 prover.assert_satisfied();
163 }
164
165 let circuit = SmallRangeCircuit { value: Value::known(pallas::Base::from(2)) };
166 let prover = MockProver::run(k, &circuit, vec![]).unwrap();
167 assert!(prover.verify().is_err());
168 }
169}