fullcodec_plonk/constraint_system/
range.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use crate::bit_iterator::*;
8use crate::constraint_system::TurboComposer;
9use crate::constraint_system::{WireData, Witness};
10use dusk_bls12_381::BlsScalar;
11use dusk_bytes::Serializable;
12use sp_std::vec;
13use sp_std::vec::Vec;
14
15impl TurboComposer {
16    /// Adds a range-constraint gate that checks and constrains a
17    /// [`Witness`] to be inside of the range \[0,num_bits\].
18    ///
19    /// This function adds `num_bits/4` gates to the circuit description in
20    /// order to add the range constraint.
21    ///
22    ///# Panics
23    /// This function will panic if the num_bits specified is not even, ie.
24    /// `num_bits % 2 != 0`.
25    pub fn component_range(&mut self, witness: Witness, num_bits: usize) {
26        // Adds `variable` into the appropriate witness position
27        // based on the accumulator number a_i
28        let add_wire =
29            |composer: &mut TurboComposer, i: usize, witness: Witness| {
30                // Since four quads can fit into one gate, the gate index does
31                // not change for every four wires
32                let gate_index = composer.gates() as usize + (i / 4);
33
34                let wire_data = match i % 4 {
35                    0 => {
36                        composer.w_4.push(witness);
37                        WireData::Fourth(gate_index as usize)
38                    }
39                    1 => {
40                        composer.w_o.push(witness);
41                        WireData::Output(gate_index as usize)
42                    }
43                    2 => {
44                        composer.w_r.push(witness);
45                        WireData::Right(gate_index as usize)
46                    }
47                    3 => {
48                        composer.w_l.push(witness);
49                        WireData::Left(gate_index as usize)
50                    }
51                    _ => unreachable!(),
52                };
53
54                composer.perm.add_variable_to_map(witness, wire_data);
55            };
56
57        // Note: A quad is a quaternary digit
58        //
59        // Number of bits should be even, this means that user must pad the
60        // number of bits external.
61        assert_eq!(num_bits % 2, 0);
62
63        // Convert witness to bit representation and reverse
64        let bits = self.witnesses[&witness];
65        let bit_iter = BitIterator8::new(bits.to_bytes());
66        let mut bits: Vec<_> = bit_iter.collect();
67        bits.reverse();
68
69        // For a width-4 program, one gate will contain 4 accumulators
70        // Each accumulator proves that a single quad is a base-4 digit.
71        // Since there is 1-1 mapping between accumulators and quads
72        // and quads contain 2 bits, one gate accumulates 8 bits.
73        // We can therefore work out the number of gates needed;
74        let mut num_gates = num_bits >> 3;
75
76        // The number of bits may be divisible by 2 but not by 8.
77        // Example: If we wanted to prove a number was within the range [0,2^10
78        // -1 ] We would need 10 bits. When dividing by 10 by 8, we will
79        // get 1 as the number of gates, when in fact we need 2 gates In
80        // general, we will need to add an extra gate, if the number of bits is
81        // not divisible by 8
82        if num_bits % 8 != 0 {
83            num_gates += 1;
84        }
85
86        // Since each gate holds 4 quads, the number of quads that will be
87        // needed to prove that the witness is within a specific range can be
88        // computed from the number of gates
89        let num_quads = num_gates * 4;
90
91        // There are now two things to note in terms of padding:
92        // 1. (a_{i+1}, a_i) proves that {q_i+1} is a quaternary digit.
93        // In order to prove that the first digit is a quad, we need to add a
94        // zero accumulator (genesis quad) 2. We need the last gate to
95        // contain 1 quad, so the range gate equation is not used on the last
96        // gate. This is needed because the range gate equation looks at
97        // the fourth for the next gate, which is not guaranteed to pass.
98        // We therefore prepend quads until we have 1 quad in the last gate.
99        // This will at most add one extra gate.
100        //
101        // There are two cases to consider:
102        // Case 1: If the number of bits used is divisible by 8, then it is also
103        // divisible by 4. This means that we can find out how many
104        // gates are needed by dividing the number of bits by 8 However,
105        // since we will always need a genesis quad, it will mean that we will
106        // need an another gate to hold the extra quad Example: Take 32
107        // bits. We compute the number of gates to be 32/8 = 4 full gates, we
108        // then add 1 because we need the genesis accumulator
109        // In this case, we only pad by one quad, which is the genesis quad.
110        // Moreover, the genesis quad is the quad that has added the extra gate.
111        //
112        // Case 2: When the number of bits is not divisible by 8
113        // Since the number is not divisible by 4, as in case 1, when we add the
114        // genesis quad, we will have more than 1 quad on the last row
115        // In this case, the genesis quad, did not add an extra gate. What will
116        // add the extra gate, is the padding. We must apply padding in
117        // order to ensure the last row has only one quad in on the fourth wire
118        // In this case, it is the padding which will add an extra number of
119        // gates Example: 34 bits requires 17 quads. We add one for the
120        // zeroed out accumulator. To make 18 quads. We can fit all of these
121        // quads in 5 gates. 18 % 4 = 2 so on the last row, we will have
122        // two quads, which is bad. We must pad the beginning in order
123        // to get one quad on the last row We can work out how much we
124        // need to pad by the following equation (18+X) % 4 = 1
125        // X is 3 , so we pad 3 extra zeroes
126        // We now have 21 quads in the system now and 21 / 4 = 5 remainder 1, so
127        // we will need 5 full gates and extra gate with 1 quad.
128        let pad = 1 + (((num_quads << 1) - num_bits) >> 1);
129
130        // The last observation; we will always use 1 more gate than the number
131        // of gates calculated Either due to the genesis quad, or the
132        // padding used to ensure we have 1 quad on the last gate
133        let used_gates = num_gates + 1;
134
135        // We collect the set of accumulators to return back to the user
136        // and keep a running count of the current accumulator
137        let mut accumulators: Vec<Witness> = Vec::new();
138        let mut accumulator = BlsScalar::zero();
139        let four = BlsScalar::from(4);
140
141        // First we pad our gates by the necessary amount
142        for i in 0..pad {
143            add_wire(self, i, Self::constant_zero());
144        }
145
146        for i in pad..=num_quads {
147            // Convert each pair of bits to quads
148            let bit_index = (num_quads - i) << 1;
149            let q_0 = bits[bit_index] as u64;
150            let q_1 = bits[bit_index + 1] as u64;
151            let quad = q_0 + (2 * q_1);
152
153            // Compute the next accumulator term
154            accumulator = four * accumulator;
155            accumulator += BlsScalar::from(quad);
156
157            let accumulator_var = self.append_witness(accumulator);
158            accumulators.push(accumulator_var);
159
160            add_wire(self, i, accumulator_var);
161        }
162
163        // Set the selector polynomials for all of the gates we used
164        let zeros = vec![BlsScalar::zero(); used_gates];
165        let ones = vec![BlsScalar::one(); used_gates];
166
167        self.q_m.extend(zeros.iter());
168        self.q_l.extend(zeros.iter());
169        self.q_r.extend(zeros.iter());
170        self.q_o.extend(zeros.iter());
171        self.q_c.extend(zeros.iter());
172        self.q_arith.extend(zeros.iter());
173        self.q_4.extend(zeros.iter());
174        self.q_fixed_group_add.extend(zeros.iter());
175        self.q_variable_group_add.extend(zeros.iter());
176        self.q_range.extend(ones.iter());
177        self.q_logic.extend(zeros.iter());
178        self.q_lookup.extend(zeros.iter());
179        self.n += used_gates as u32;
180
181        // As mentioned above, we must switch off the range constraint for the
182        // last gate Remember; it will contain one quad in the fourth
183        // wire, which will be used in the gate before it
184        // Furthermore, we set the left, right and output wires to zero
185        *self.q_range.last_mut().unwrap() = BlsScalar::zero();
186        self.w_l.push(Self::constant_zero());
187        self.w_r.push(Self::constant_zero());
188        self.w_o.push(Self::constant_zero());
189
190        // Lastly, we must link the last accumulator value to the initial
191        // witness This last constraint will pass as long as
192        // - The witness is within the number of bits initially specified
193        let last_accumulator = accumulators.len() - 1;
194        self.assert_equal(accumulators[last_accumulator], witness);
195        accumulators[last_accumulator] = witness;
196    }
197}
198
199#[cfg(feature = "std")]
200#[cfg(test)]
201mod tests {
202    use super::super::helper::*;
203    use dusk_bls12_381::BlsScalar;
204
205    #[test]
206    fn test_range_constraint() {
207        // Should fail as the number is not 32 bits
208        let res = gadget_tester(
209            |composer| {
210                let witness = composer.append_witness(BlsScalar::from(
211                    (u32::max_value() as u64) + 1,
212                ));
213                composer.component_range(witness, 32);
214            },
215            200,
216        );
217        assert!(res.is_err());
218
219        // Should fail as number is greater than 32 bits
220        let res = gadget_tester(
221            |composer| {
222                let witness =
223                    composer.append_witness(BlsScalar::from(u64::max_value()));
224                composer.component_range(witness, 32);
225            },
226            200,
227        );
228        assert!(res.is_err());
229
230        // Should pass as the number is within 34 bits
231        let res = gadget_tester(
232            |composer| {
233                let witness =
234                    composer.append_witness(BlsScalar::from(2u64.pow(34) - 1));
235                composer.component_range(witness, 34);
236            },
237            200,
238        );
239        assert!(res.is_ok());
240    }
241
242    #[test]
243    #[should_panic]
244    fn test_odd_bit_range() {
245        // Should fail as the number we we need a even number of bits
246        let _ok = gadget_tester(
247            |composer| {
248                let witness = composer
249                    .append_witness(BlsScalar::from(u32::max_value() as u64));
250                composer.component_range(witness, 33);
251            },
252            200,
253        );
254    }
255}