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}