ark-r1cs-std/src/bits/uint8.rs
src/bits/uint8.rsRaw
1use ark_ff::{Field, FpParameters, PrimeField, ToConstraintField};
2
3use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
4
5use crate::fields::fp::{AllocatedFp, FpVar};
6use crate::{prelude::*, Assignment, ToConstraintFieldGadget, Vec};
7use core::{borrow::Borrow, convert::TryFrom};
8
9/// Represents an interpretation of 8 `Boolean` objects as an
10/// unsigned integer.
11#[derive(Clone, Debug)]
12pub struct UInt8<F: Field> {
13 /// Little-endian representation: least significant bit first
14 pub(crate) bits: [Boolean<F>; 8],
15 pub(crate) value: Option<u8>,
16}
17
18impl<F: Field> R1CSVar<F> for UInt8<F> {
19 type Value = u8;
20
21 fn cs(&self) -> ConstraintSystemRef<F> {
22 self.bits.as_ref().cs()
23 }
24
25 fn value(&self) -> Result<Self::Value, SynthesisError> {
26 let mut value = None;
27 for (i, bit) in self.bits.iter().enumerate() {
28 let b = u8::from(bit.value()?);
29 value = match value {
30 Some(value) => Some(value + (b << i)),
31 None => Some(b << i),
32 };
33 }
34 debug_assert_eq!(self.value, value);
35 value.get()
36 }
37}
38
39impl<F: Field> UInt8<F> {
40 /// Construct a constant vector of `UInt8` from a vector of `u8`
41 ///
42 /// This *does not* create any new variables or constraints.
43 /// ```
44 /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
45 /// // We'll use the BLS12-381 scalar field for our constraints.
46 /// use ark_test_curves::bls12_381::Fr;
47 /// use ark_relations::r1cs::*;
48 /// use ark_r1cs_std::prelude::*;
49 ///
50 /// let cs = ConstraintSystem::<Fr>::new_ref();
51 /// let var = vec![UInt8::new_witness(cs.clone(), || Ok(2))?];
52 ///
53 /// let constant = UInt8::constant_vec(&[2]);
54 /// var.enforce_equal(&constant)?;
55 /// assert!(cs.is_satisfied().unwrap());
56 /// # Ok(())
57 /// # }
58 /// ```
59 pub fn constant_vec(values: &[u8]) -> Vec<Self> {
60 let mut result = Vec::new();
61 for value in values {
62 result.push(UInt8::constant(*value));
63 }
64 result
65 }
66
67 /// Construct a constant `UInt8` from a `u8`
68 ///
69 /// This *does not* create new variables or constraints.
70 ///
71 /// ```
72 /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
73 /// // We'll use the BLS12-381 scalar field for our constraints.
74 /// use ark_test_curves::bls12_381::Fr;
75 /// use ark_relations::r1cs::*;
76 /// use ark_r1cs_std::prelude::*;
77 ///
78 /// let cs = ConstraintSystem::<Fr>::new_ref();
79 /// let var = UInt8::new_witness(cs.clone(), || Ok(2))?;
80 ///
81 /// let constant = UInt8::constant(2);
82 /// var.enforce_equal(&constant)?;
83 /// assert!(cs.is_satisfied().unwrap());
84 /// # Ok(())
85 /// # }
86 /// ```
87 pub fn constant(value: u8) -> Self {
88 let mut bits = [Boolean::FALSE; 8];
89
90 let mut tmp = value;
91 for i in 0..8 {
92 // If last bit is one, push one.
93 bits[i] = Boolean::constant((tmp & 1) == 1);
94 tmp >>= 1;
95 }
96
97 Self {
98 bits,
99 value: Some(value),
100 }
101 }
102
103 /// Allocates a slice of `u8`'s as private witnesses.
104 pub fn new_witness_vec(
105 cs: impl Into<Namespace<F>>,
106 values: &[impl Into<Option<u8>> + Copy],
107 ) -> Result<Vec<Self>, SynthesisError> {
108 let ns = cs.into();
109 let cs = ns.cs();
110 let mut output_vec = Vec::with_capacity(values.len());
111 for value in values {
112 let byte: Option<u8> = Into::into(*value);
113 output_vec.push(Self::new_witness(cs.clone(), || byte.get())?);
114 }
115 Ok(output_vec)
116 }
117
118 /// Allocates a slice of `u8`'s as public inputs by first packing them into
119 /// elements of `F`, (thus reducing the number of input allocations),
120 /// allocating these elements as public inputs, and then converting
121 /// these field variables `FpVar<F>` variables back into bytes.
122 ///
123 /// From a user perspective, this trade-off adds constraints, but improves
124 /// verifier time and verification key size.
125 ///
126 /// ```
127 /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
128 /// // We'll use the BLS12-381 scalar field for our constraints.
129 /// use ark_test_curves::bls12_381::Fr;
130 /// use ark_relations::r1cs::*;
131 /// use ark_r1cs_std::prelude::*;
132 ///
133 /// let cs = ConstraintSystem::<Fr>::new_ref();
134 /// let two = UInt8::new_witness(cs.clone(), || Ok(2))?;
135 /// let var = vec![two.clone(); 32];
136 ///
137 /// let c = UInt8::new_input_vec(cs.clone(), &[2; 32])?;
138 /// var.enforce_equal(&c)?;
139 /// assert!(cs.is_satisfied().unwrap());
140 /// # Ok(())
141 /// # }
142 /// ```
143 pub fn new_input_vec(
144 cs: impl Into<Namespace<F>>,
145 values: &[u8],
146 ) -> Result<Vec<Self>, SynthesisError>
147 where
148 F: PrimeField,
149 {
150 let ns = cs.into();
151 let cs = ns.cs();
152 let values_len = values.len();
153 let field_elements: Vec<F> = ToConstraintField::<F>::to_field_elements(values).unwrap();
154
155 let max_size = 8 * (F::Params::CAPACITY / 8) as usize;
156 let mut allocated_bits = Vec::new();
157 for field_element in field_elements.into_iter() {
158 let fe = AllocatedFp::new_input(cs.clone(), || Ok(field_element))?;
159 let fe_bits = fe.to_bits_le()?;
160
161 // Remove the most significant bit, because we know it should be zero
162 // because `values.to_field_elements()` only
163 // packs field elements up to the penultimate bit.
164 // That is, the most significant bit (`ConstraintF::NUM_BITS`-th bit) is
165 // unset, so we can just pop it off.
166 allocated_bits.extend_from_slice(&fe_bits[0..max_size]);
167 }
168
169 // Chunk up slices of 8 bit into bytes.
170 Ok(allocated_bits[0..(8 * values_len)]
171 .chunks(8)
172 .map(Self::from_bits_le)
173 .collect())
174 }
175
176 /// Converts a little-endian byte order representation of bits into a
177 /// `UInt8`.
178 ///
179 /// ```
180 /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
181 /// // We'll use the BLS12-381 scalar field for our constraints.
182 /// use ark_test_curves::bls12_381::Fr;
183 /// use ark_relations::r1cs::*;
184 /// use ark_r1cs_std::prelude::*;
185 ///
186 /// let cs = ConstraintSystem::<Fr>::new_ref();
187 /// let var = UInt8::new_witness(cs.clone(), || Ok(128))?;
188 ///
189 /// let f = Boolean::FALSE;
190 /// let t = Boolean::TRUE;
191 ///
192 /// // Construct [0, 0, 0, 0, 0, 0, 0, 1]
193 /// let mut bits = vec![f.clone(); 7];
194 /// bits.push(t);
195 ///
196 /// let mut c = UInt8::from_bits_le(&bits);
197 /// var.enforce_equal(&c)?;
198 /// assert!(cs.is_satisfied().unwrap());
199 /// # Ok(())
200 /// # }
201 /// ```
202 #[tracing::instrument(target = "r1cs")]
203 pub fn from_bits_le(bits: &[Boolean<F>]) -> Self {
204 assert_eq!(bits.len(), 8);
205 let bits = <&[Boolean<F>; 8]>::try_from(bits).unwrap().clone();
206
207 let mut value = Some(0u8);
208 for (i, b) in bits.iter().enumerate() {
209 value = match b.value().ok() {
210 Some(b) => value.map(|v| v + (u8::from(b) << i)),
211 None => None,
212 }
213 }
214
215 Self { value, bits }
216 }
217
218 /// Outputs `self ^ other`.
219 ///
220 /// If at least one of `self` and `other` are constants, then this method
221 /// *does not* create any constraints or variables.
222 ///
223 /// ```
224 /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
225 /// // We'll use the BLS12-381 scalar field for our constraints.
226 /// use ark_test_curves::bls12_381::Fr;
227 /// use ark_relations::r1cs::*;
228 /// use ark_r1cs_std::prelude::*;
229 ///
230 /// let cs = ConstraintSystem::<Fr>::new_ref();
231 /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?;
232 /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?;
233 /// let c = UInt8::new_witness(cs.clone(), || Ok(1))?;
234 ///
235 /// a.xor(&b)?.enforce_equal(&c)?;
236 /// assert!(cs.is_satisfied().unwrap());
237 /// # Ok(())
238 /// # }
239 /// ```
240 #[tracing::instrument(target = "r1cs")]
241 pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> {
242 let mut result = self.clone();
243 result.value = match (self.value, other.value) {
244 (Some(a), Some(b)) => Some(a ^ b),
245 _ => None,
246 };
247
248 let new_bits = self.bits.iter().zip(&other.bits).map(|(a, b)| a.xor(b));
249
250 for (res, new) in result.bits.iter_mut().zip(new_bits) {
251 *res = new?;
252 }
253
254 Ok(result)
255 }
256}
257
258impl<ConstraintF: Field> EqGadget<ConstraintF> for UInt8<ConstraintF> {
259 #[tracing::instrument(target = "r1cs")]
260 fn is_eq(&self, other: &Self) -> Result<Boolean<ConstraintF>, SynthesisError> {
261 self.bits.as_ref().is_eq(&other.bits)
262 }
263
264 #[tracing::instrument(target = "r1cs")]
265 fn conditional_enforce_equal(
266 &self,
267 other: &Self,
268 condition: &Boolean<ConstraintF>,
269 ) -> Result<(), SynthesisError> {
270 self.bits.conditional_enforce_equal(&other.bits, condition)
271 }
272
273 #[tracing::instrument(target = "r1cs")]
274 fn conditional_enforce_not_equal(
275 &self,
276 other: &Self,
277 condition: &Boolean<ConstraintF>,
278 ) -> Result<(), SynthesisError> {
279 self.bits
280 .conditional_enforce_not_equal(&other.bits, condition)
281 }
282}
283
284impl<ConstraintF: Field> CondSelectGadget<ConstraintF> for UInt8<ConstraintF> {
285 #[tracing::instrument(target = "r1cs", skip(cond, true_value, false_value))]
286 fn conditionally_select(
287 cond: &Boolean<ConstraintF>,
288 true_value: &Self,
289 false_value: &Self,
290 ) -> Result<Self, SynthesisError> {
291 let selected_bits = true_value
292 .bits
293 .iter()
294 .zip(&false_value.bits)
295 .map(|(t, f)| cond.select(t, f));
296 let mut bits = [Boolean::FALSE; 8];
297 for (result, new) in bits.iter_mut().zip(selected_bits) {
298 *result = new?;
299 }
300
301 let value = cond.value().ok().and_then(|cond| {
302 if cond {
303 true_value.value().ok()
304 } else {
305 false_value.value().ok()
306 }
307 });
308 Ok(Self { bits, value })
309 }
310}
311
312impl<ConstraintF: Field> AllocVar<u8, ConstraintF> for UInt8<ConstraintF> {
313 fn new_variable<T: Borrow<u8>>(
314 cs: impl Into<Namespace<ConstraintF>>,
315 f: impl FnOnce() -> Result<T, SynthesisError>,
316 mode: AllocationMode,
317 ) -> Result<Self, SynthesisError> {
318 let ns = cs.into();
319 let cs = ns.cs();
320 let value = f().map(|f| *f.borrow()).ok();
321
322 let mut values = [None; 8];
323 if let Some(val) = value {
324 values
325 .iter_mut()
326 .enumerate()
327 .for_each(|(i, v)| *v = Some((val >> i) & 1 == 1));
328 }
329
330 let mut bits = [Boolean::FALSE; 8];
331 for (b, v) in bits.iter_mut().zip(&values) {
332 *b = Boolean::new_variable(cs.clone(), || v.get(), mode)?;
333 }
334 Ok(Self { bits, value })
335 }
336}
337
338/// Parses the `Vec<UInt8<ConstraintF>>` in fixed-sized `ConstraintF::Params::CAPACITY` chunks and
339/// converts each chunk, which is assumed to be little-endian, to its `FpVar<ConstraintF>`
340/// representation.
341/// This is the gadget counterpart to the `[u8]` implementation of
342/// [ToConstraintField](ark_ff::ToConstraintField).
343impl<ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for [UInt8<ConstraintF>] {
344 #[tracing::instrument(target = "r1cs")]
345 fn to_constraint_field(&self) -> Result<Vec<FpVar<ConstraintF>>, SynthesisError> {
346 let max_size = (ConstraintF::Params::CAPACITY / 8) as usize;
347 self.chunks(max_size)
348 .map(|chunk| Boolean::le_bits_to_fp_var(chunk.to_bits_le()?.as_slice()))
349 .collect::<Result<Vec<_>, SynthesisError>>()
350 }
351}
352
353impl<ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for Vec<UInt8<ConstraintF>> {
354 #[tracing::instrument(target = "r1cs")]
355 fn to_constraint_field(&self) -> Result<Vec<FpVar<ConstraintF>>, SynthesisError> {
356 self.as_slice().to_constraint_field()
357 }
358}
359
360#[cfg(test)]
361mod test {
362 use super::UInt8;
363 use crate::fields::fp::FpVar;
364 use crate::prelude::AllocationMode::{Constant, Input, Witness};
365 use crate::{prelude::*, ToConstraintFieldGadget, Vec};
366 use ark_ff::{FpParameters, PrimeField, ToConstraintField};
367 use ark_relations::r1cs::{ConstraintSystem, SynthesisError};
368 use ark_test_curves::bls12_381::Fr;
369 use rand::distributions::Uniform;
370 use rand::{Rng, SeedableRng};
371 use rand_xorshift::XorShiftRng;
372
373 #[test]
374 fn test_uint8_from_bits_to_bits() -> Result<(), SynthesisError> {
375 let cs = ConstraintSystem::<Fr>::new_ref();
376 let byte_val = 0b01110001;
377 let byte =
378 UInt8::new_witness(ark_relations::ns!(cs, "alloc value"), || Ok(byte_val)).unwrap();
379 let bits = byte.to_bits_le()?;
380 for (i, bit) in bits.iter().enumerate() {
381 assert_eq!(bit.value()?, (byte_val >> i) & 1 == 1)
382 }
383 Ok(())
384 }
385
386 #[test]
387 fn test_uint8_new_input_vec() -> Result<(), SynthesisError> {
388 let cs = ConstraintSystem::<Fr>::new_ref();
389 let byte_vals = (64u8..128u8).collect::<Vec<_>>();
390 let bytes =
391 UInt8::new_input_vec(ark_relations::ns!(cs, "alloc value"), &byte_vals).unwrap();
392 dbg!(bytes.value())?;
393 for (native, variable) in byte_vals.into_iter().zip(bytes) {
394 let bits = variable.to_bits_le()?;
395 for (i, bit) in bits.iter().enumerate() {
396 assert_eq!(
397 bit.value()?,
398 (native >> i) & 1 == 1,
399 "native value {}: bit {:?}",
400 native,
401 i
402 )
403 }
404 }
405 Ok(())
406 }
407
408 #[test]
409 fn test_uint8_from_bits() -> Result<(), SynthesisError> {
410 let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
411
412 for _ in 0..1000 {
413 let v = (0..8)
414 .map(|_| Boolean::<Fr>::Constant(rng.gen()))
415 .collect::<Vec<_>>();
416
417 let val = UInt8::from_bits_le(&v);
418
419 for (i, bit) in val.bits.iter().enumerate() {
420 match bit {
421 Boolean::Constant(b) => assert!(*b == ((val.value()? >> i) & 1 == 1)),
422 _ => unreachable!(),
423 }
424 }
425
426 let expected_to_be_same = val.to_bits_le()?;
427
428 for x in v.iter().zip(expected_to_be_same.iter()) {
429 match x {
430 (&Boolean::Constant(true), &Boolean::Constant(true)) => {}
431 (&Boolean::Constant(false), &Boolean::Constant(false)) => {}
432 _ => unreachable!(),
433 }
434 }
435 }
436 Ok(())
437 }
438
439 #[test]
440 fn test_uint8_xor() -> Result<(), SynthesisError> {
441 let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
442
443 for _ in 0..1000 {
444 let cs = ConstraintSystem::<Fr>::new_ref();
445
446 let a: u8 = rng.gen();
447 let b: u8 = rng.gen();
448 let c: u8 = rng.gen();
449
450 let mut expected = a ^ b ^ c;
451
452 let a_bit = UInt8::new_witness(ark_relations::ns!(cs, "a_bit"), || Ok(a)).unwrap();
453 let b_bit = UInt8::constant(b);
454 let c_bit = UInt8::new_witness(ark_relations::ns!(cs, "c_bit"), || Ok(c)).unwrap();
455
456 let r = a_bit.xor(&b_bit).unwrap();
457 let r = r.xor(&c_bit).unwrap();
458
459 assert!(cs.is_satisfied().unwrap());
460
461 assert!(r.value == Some(expected));
462
463 for b in r.bits.iter() {
464 match b {
465 Boolean::Is(b) => assert!(b.value()? == (expected & 1 == 1)),
466 Boolean::Not(b) => assert!(!b.value()? == (expected & 1 == 1)),
467 Boolean::Constant(b) => assert!(*b == (expected & 1 == 1)),
468 }
469
470 expected >>= 1;
471 }
472 }
473 Ok(())
474 }
475
476 #[test]
477 fn test_uint8_to_constraint_field() -> Result<(), SynthesisError> {
478 let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
479 let max_size = (<Fr as PrimeField>::Params::CAPACITY / 8) as usize;
480
481 let modes = [Input, Witness, Constant];
482 for mode in &modes {
483 for _ in 0..1000 {
484 let cs = ConstraintSystem::<Fr>::new_ref();
485
486 let bytes: Vec<u8> = (&mut rng)
487 .sample_iter(&Uniform::new_inclusive(0, u8::max_value()))
488 .take(max_size * 3 + 5)
489 .collect();
490
491 let bytes_var = bytes
492 .iter()
493 .map(|byte| UInt8::new_variable(cs.clone(), || Ok(*byte), *mode))
494 .collect::<Result<Vec<_>, SynthesisError>>()?;
495
496 let f_vec: Vec<Fr> = bytes.to_field_elements().unwrap();
497 let f_var_vec: Vec<FpVar<Fr>> = bytes_var.to_constraint_field()?;
498
499 assert!(cs.is_satisfied().unwrap());
500 assert_eq!(f_vec, f_var_vec.value()?);
501 }
502 }
503
504 Ok(())
505 }
506}