1use ark_ff::{Field, PrimeField, ToConstraintField};
2
3use ark_relations::r1cs::{Namespace, SynthesisError};
4
5use crate::{
6 convert::{ToBitsGadget, ToConstraintFieldGadget},
7 fields::fp::{AllocatedFp, FpVar},
8 prelude::*,
9 Vec,
10};
11
12pub type UInt8<F> = super::uint::UInt<8, u8, F>;
13
14impl<F: Field> UInt8<F> {
15 pub fn new_input_vec(
41 cs: impl Into<Namespace<F>>,
42 values: &[u8],
43 ) -> Result<Vec<Self>, SynthesisError>
44 where
45 F: PrimeField,
46 {
47 let ns = cs.into();
48 let cs = ns.cs();
49 let values_len = values.len();
50 let field_elements: Vec<F> = ToConstraintField::<F>::to_field_elements(values).unwrap();
51
52 let max_size = 8 * ((F::MODULUS_BIT_SIZE - 1) / 8) as usize;
53 let mut allocated_bits = Vec::new();
54 for field_element in field_elements.into_iter() {
55 let fe = AllocatedFp::new_input(cs.clone(), || Ok(field_element))?;
56 let fe_bits = fe.to_bits_le()?;
57
58 allocated_bits.extend_from_slice(&fe_bits[0..max_size]);
64 }
65
66 Ok(allocated_bits[0..(8 * values_len)]
68 .chunks(8)
69 .map(Self::from_bits_le)
70 .collect())
71 }
72}
73
74impl<ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for [UInt8<ConstraintF>] {
80 #[tracing::instrument(target = "r1cs")]
81 fn to_constraint_field(&self) -> Result<Vec<FpVar<ConstraintF>>, SynthesisError> {
82 let max_size = ((ConstraintF::MODULUS_BIT_SIZE - 1) / 8) as usize;
83 self.chunks(max_size)
84 .map(|chunk| Boolean::le_bits_to_fp(chunk.to_bits_le()?.as_slice()))
85 .collect::<Result<Vec<_>, SynthesisError>>()
86 }
87}
88
89impl<ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for Vec<UInt8<ConstraintF>> {
90 #[tracing::instrument(target = "r1cs")]
91 fn to_constraint_field(&self) -> Result<Vec<FpVar<ConstraintF>>, SynthesisError> {
92 self.as_slice().to_constraint_field()
93 }
94}
95
96#[cfg(test)]
97mod test {
98 use super::UInt8;
99 use crate::{
100 convert::{ToBitsGadget, ToConstraintFieldGadget},
101 fields::fp::FpVar,
102 prelude::{
103 AllocationMode::{Constant, Input, Witness},
104 *,
105 },
106 Vec,
107 };
108 use ark_ff::{PrimeField, ToConstraintField};
109 use ark_relations::r1cs::{ConstraintSystem, SynthesisError};
110 use ark_std::rand::{distributions::Uniform, Rng};
111 use ark_test_curves::bls12_381::Fr;
112
113 #[test]
114 fn test_uint8_from_bits_to_bits() -> Result<(), SynthesisError> {
115 let cs = ConstraintSystem::<Fr>::new_ref();
116 let byte_val = 0b01110001;
117 let byte =
118 UInt8::new_witness(ark_relations::ns!(cs, "alloc value"), || Ok(byte_val)).unwrap();
119 let bits = byte.to_bits_le()?;
120 for (i, bit) in bits.iter().enumerate() {
121 assert_eq!(bit.value()?, (byte_val >> i) & 1 == 1)
122 }
123 Ok(())
124 }
125
126 #[test]
127 fn test_uint8_new_input_vec() -> Result<(), SynthesisError> {
128 let cs = ConstraintSystem::<Fr>::new_ref();
129 let byte_vals = (64u8..128u8).collect::<Vec<_>>();
130 let bytes =
131 UInt8::new_input_vec(ark_relations::ns!(cs, "alloc value"), &byte_vals).unwrap();
132 for (native, variable) in byte_vals.into_iter().zip(bytes) {
133 let bits = variable.to_bits_le()?;
134 for (i, bit) in bits.iter().enumerate() {
135 assert_eq!(
136 bit.value()?,
137 (native >> i) & 1 == 1,
138 "native value {}: bit {:?}",
139 native,
140 i
141 )
142 }
143 }
144 Ok(())
145 }
146
147 #[test]
148 fn test_uint8_from_bits() -> Result<(), SynthesisError> {
149 let mut rng = ark_std::test_rng();
150
151 for _ in 0..1000 {
152 let v = (0..8)
153 .map(|_| Boolean::<Fr>::Constant(rng.gen()))
154 .collect::<Vec<_>>();
155
156 let val = UInt8::from_bits_le(&v);
157
158 let value = val.value()?;
159 for (i, bit) in val.bits.iter().enumerate() {
160 match bit {
161 Boolean::Constant(b) => assert_eq!(*b, ((value >> i) & 1 == 1)),
162 _ => unreachable!(),
163 }
164 }
165
166 let expected_to_be_same = val.to_bits_le()?;
167
168 for x in v.iter().zip(expected_to_be_same.iter()) {
169 match x {
170 (&Boolean::Constant(true), &Boolean::Constant(true)) => {},
171 (&Boolean::Constant(false), &Boolean::Constant(false)) => {},
172 _ => unreachable!(),
173 }
174 }
175 }
176 Ok(())
177 }
178
179 #[test]
180 fn test_uint8_xor() -> Result<(), SynthesisError> {
181 let mut rng = ark_std::test_rng();
182
183 for _ in 0..1000 {
184 let cs = ConstraintSystem::<Fr>::new_ref();
185
186 let a: u8 = rng.gen();
187 let b: u8 = rng.gen();
188 let c: u8 = rng.gen();
189
190 let mut expected = a ^ b ^ c;
191
192 let a_bit = UInt8::new_witness(ark_relations::ns!(cs, "a_bit"), || Ok(a)).unwrap();
193 let b_bit = UInt8::constant(b);
194 let c_bit = UInt8::new_witness(ark_relations::ns!(cs, "c_bit"), || Ok(c)).unwrap();
195
196 let mut r = a_bit ^ b_bit;
197 r ^= &c_bit;
198
199 assert!(cs.is_satisfied().unwrap());
200
201 assert_eq!(r.value, Some(expected));
202
203 for b in r.bits.iter() {
204 match b {
205 Boolean::Var(b) => assert!(b.value()? == (expected & 1 == 1)),
206 Boolean::Constant(b) => assert!(*b == (expected & 1 == 1)),
207 }
208
209 expected >>= 1;
210 }
211 }
212 Ok(())
213 }
214
215 #[test]
216 fn test_uint8_to_constraint_field() -> Result<(), SynthesisError> {
217 let mut rng = ark_std::test_rng();
218 let max_size = ((<Fr as PrimeField>::MODULUS_BIT_SIZE - 1) / 8) as usize;
219
220 let modes = [Input, Witness, Constant];
221 for mode in &modes {
222 for _ in 0..1000 {
223 let cs = ConstraintSystem::<Fr>::new_ref();
224
225 let bytes: Vec<u8> = (&mut rng)
226 .sample_iter(&Uniform::new_inclusive(0, u8::max_value()))
227 .take(max_size * 3 + 5)
228 .collect();
229
230 let bytes_var = bytes
231 .iter()
232 .map(|byte| UInt8::new_variable(cs.clone(), || Ok(*byte), *mode))
233 .collect::<Result<Vec<_>, SynthesisError>>()?;
234
235 let f_vec: Vec<Fr> = bytes.to_field_elements().unwrap();
236 let f_var_vec: Vec<FpVar<Fr>> = bytes_var.to_constraint_field()?;
237
238 assert!(cs.is_satisfied().unwrap());
239 assert_eq!(f_vec, f_var_vec.value()?);
240 }
241 }
242
243 Ok(())
244 }
245
246 #[test]
247 fn test_uint8_random_access() {
248 let mut rng = ark_std::test_rng();
249
250 for _ in 0..100 {
251 let cs = ConstraintSystem::<Fr>::new_ref();
252
253 let values: Vec<u8> = (0..128).map(|_| rng.gen()).collect();
255 let values_const: Vec<UInt8<Fr>> = values.iter().map(|x| UInt8::constant(*x)).collect();
256
257 let position: Vec<bool> = (0..7).map(|_| rng.gen()).collect();
259 let position_var: Vec<Boolean<Fr>> = position
260 .iter()
261 .map(|b| {
262 Boolean::new_witness(ark_relations::ns!(cs, "index_arr_element"), || Ok(*b))
263 .unwrap()
264 })
265 .collect();
266
267 let mut index = 0;
269 for x in position {
270 index *= 2;
271 index += if x { 1 } else { 0 };
272 }
273
274 assert_eq!(
275 UInt8::conditionally_select_power_of_two_vector(&position_var, &values_const)
276 .unwrap()
277 .value()
278 .unwrap(),
279 values[index]
280 )
281 }
282 }
283}