Change default `to_bits` and `to_bytes` functions to the safe versions
jump to
@@ -388,13 +388,6 @@ bytes.extend_from_slice(&q.to_bytes(&mut cs.ns(|| "q"))?);
} Ok(bytes) } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) - } } #[cfg(test)]
@@ -335,13 +335,6 @@ bytes.extend_from_slice(&g.to_bytes(&mut cs.ns(|| "g"))?);
} Ok(bytes) } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) - } } #[cfg(test)]
@@ -447,14 +447,6 @@ _cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> { Ok(self.0.clone()) } - - #[inline] - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) - } } impl<ConstraintF: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sOutputGadget {
@@ -208,12 +208,4 @@ mut cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> { self.pub_key.to_bytes(&mut cs.ns(|| "PubKey To Bytes")) } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - mut cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.pub_key - .to_bytes_strict(&mut cs.ns(|| "PubKey To Bytes")) - } }
@@ -802,6 +802,7 @@ }
} impl<ConstraintF: Field> ToBytesGadget<ConstraintF> for Boolean { + /// Outputs `1u8` if `self` is true, and `0u8` otherwise. fn to_bytes<CS: ConstraintSystem<ConstraintF>>( &self, _cs: CS,@@ -812,14 +813,6 @@ bits.reverse();
let value = self.get_value().map(|val| val as u8); let byte = UInt8 { bits, value }; Ok(vec![byte]) - } - - /// Additionally checks if the produced list of booleans is 'valid'. - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) } }
@@ -11,16 +11,24 @@ pub mod uint64;
pub mod uint8; pub trait ToBitsGadget<ConstraintF: Field> { + /// Outputs the canonical bit-wise representation of `self`. + /// + /// This is the correct default for 99% of use cases. fn to_bits<CS: ConstraintSystem<ConstraintF>>( &self, cs: CS, ) -> Result<Vec<Boolean>, SynthesisError>; - /// Additionally checks if the produced list of booleans is 'valid'. - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( + /// Outputs a possibly non-unique bit-wise representation of `self`. + /// + /// If you're not absolutely certain that your usecase can get away with a + /// non-canonical representation, please use `self.to_bits(cs)` instead. + fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>( &self, cs: CS, - ) -> Result<Vec<Boolean>, SynthesisError>; + ) -> Result<Vec<Boolean>, SynthesisError> { + self.to_bits(cs) + } } impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for Boolean {@@ -30,13 +38,6 @@ _: CS,
) -> Result<Vec<Boolean>, SynthesisError> { Ok(vec![self.clone()]) } - - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - _: CS, - ) -> Result<Vec<Boolean>, SynthesisError> { - Ok(vec![self.clone()]) - } } impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for [Boolean] {@@ -46,14 +47,8 @@ _cs: CS,
) -> Result<Vec<Boolean>, SynthesisError> { Ok(self.to_vec()) } - - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - _cs: CS, - ) -> Result<Vec<Boolean>, SynthesisError> { - Ok(self.to_vec()) - } } + impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for Vec<Boolean> { fn to_bits<CS: ConstraintSystem<ConstraintF>>( &self,@@ -61,13 +56,6 @@ _cs: CS,
) -> Result<Vec<Boolean>, SynthesisError> { Ok(self.clone()) } - - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - _cs: CS, - ) -> Result<Vec<Boolean>, SynthesisError> { - Ok(self.clone()) - } } impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for [UInt8] {@@ -81,26 +69,27 @@ result.extend_from_slice(&byte.into_bits_le());
} Ok(result) } - - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<Boolean>, SynthesisError> { - self.to_bits(cs) - } } pub trait ToBytesGadget<ConstraintF: Field> { + /// Outputs a canonical byte-wise representation of `self`. + /// + /// This is the correct default for 99% of use cases. fn to_bytes<CS: ConstraintSystem<ConstraintF>>( &self, cs: CS, ) -> Result<Vec<UInt8>, SynthesisError>; - /// Additionally checks if the produced list of booleans is 'valid'. - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( + /// Outputs a possibly non-unique byte decomposition of `self`. + /// + /// If you're not absolutely certain that your usecase can get away with a + /// non-canonical representation, please use `self.to_bytes(cs)` instead. + fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>( &self, cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError>; + ) -> Result<Vec<UInt8>, SynthesisError> { + self.to_bytes(cs) + } } impl<ConstraintF: Field> ToBytesGadget<ConstraintF> for [UInt8] {@@ -110,13 +99,6 @@ _cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> { Ok(self.to_vec()) } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) - } } impl<'a, ConstraintF: Field, T: 'a + ToBytesGadget<ConstraintF>> ToBytesGadget<ConstraintF>@@ -128,13 +110,6 @@ cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> { (*self).to_bytes(cs) } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) - } } impl<'a, ConstraintF: Field> ToBytesGadget<ConstraintF> for &'a [UInt8] {@@ -143,12 +118,5 @@ &self,
_cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { Ok(self.to_vec()) - } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) } }
@@ -306,13 +306,6 @@ }
Ok(bytes) } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) - } } impl PartialEq for UInt32 {
@@ -310,13 +310,6 @@ }
Ok(bytes) } - - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( - &self, - cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) - } } impl PartialEq for UInt64 {
@@ -326,9 +326,18 @@ }
} impl<F: PrimeField> ToBitsGadget<F> for FpGadget<F> { - /// Outputs the binary representation of the value in `self` in *big-endian* + /// Outputs the unique bit-wise decomposition of `self` in *big-endian* /// form. fn to_bits<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> { + let bits = self.to_non_unique_bits(&mut cs)?; + Boolean::enforce_in_field::<_, _, F>(&mut cs, &bits)?; + Ok(bits) + } + + fn to_non_unique_bits<CS: ConstraintSystem<F>>( + &self, + mut cs: CS, + ) -> Result<Vec<Boolean>, SynthesisError> { let num_bits = F::Params::MODULUS_BITS; use algebra::BitIterator; let bit_values = match self.value {@@ -375,20 +384,29 @@ cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc);
Ok(bits.into_iter().map(Boolean::from).collect()) } +} - fn to_bits_strict<CS: ConstraintSystem<F>>( - &self, - mut cs: CS, - ) -> Result<Vec<Boolean>, SynthesisError> { - let bits = self.to_bits(&mut cs)?; - Boolean::enforce_in_field::<_, _, F>(&mut cs, &bits)?; +impl<F: PrimeField> ToBytesGadget<F> for FpGadget<F> { + /// Outputs the unique byte decomposition of `self` in *little-endian* + /// form. + fn to_bytes<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> { + let bytes = self.to_non_unique_bytes(&mut cs)?; + Boolean::enforce_in_field::<_, _, F>( + &mut cs, + &bytes.iter() + .flat_map(|byte_gadget| byte_gadget.into_bits_le()) + // This reverse maps the bits into big-endian form, as required by `enforce_in_field`. + .rev() + .collect::<Vec<_>>(), + )?; - Ok(bits) + Ok(bytes) } -} -impl<F: PrimeField> ToBytesGadget<F> for FpGadget<F> { - fn to_bytes<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> { + fn to_non_unique_bytes<CS: ConstraintSystem<F>>( + &self, + mut cs: CS, + ) -> Result<Vec<UInt8>, SynthesisError> { let byte_values = match self.value { Some(value) => to_bytes![&value.into_repr()]? .into_iter()@@ -422,23 +440,6 @@
lc = &self.variable - lc; cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc); - - Ok(bytes) - } - - fn to_bytes_strict<CS: ConstraintSystem<F>>( - &self, - mut cs: CS, - ) -> Result<Vec<UInt8>, SynthesisError> { - let bytes = self.to_bytes(&mut cs)?; - Boolean::enforce_in_field::<_, _, F>( - &mut cs, - &bytes.iter() - .flat_map(|byte_gadget| byte_gadget.into_bits_le()) - // This reverse maps the bits into big-endian form, as required by `enforce_in_field`. - .rev() - .collect::<Vec<_>>(), - )?; Ok(bytes) }
@@ -731,12 +731,12 @@ c0.append(&mut c1);
Ok(c0) } - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<Boolean>, SynthesisError> { - let mut c0 = self.c0.to_bits_strict(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits_strict(cs.ns(|| "c1"))?; + let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; + let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; c0.append(&mut c1); Ok(c0) }@@ -757,12 +757,12 @@ c0.append(&mut c1);
Ok(c0) } - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { - let mut c0 = self.c0.to_bytes_strict(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes_strict(cs.ns(|| "c1"))?; + let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; + let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; c0.append(&mut c1); Ok(c0) }
@@ -527,12 +527,12 @@ c0.append(&mut c1);
Ok(c0) } - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<Boolean>, SynthesisError> { - let mut c0 = self.c0.to_bits_strict(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits_strict(cs.ns(|| "c1"))?; + let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; + let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; c0.append(&mut c1); Ok(c0) }@@ -551,12 +551,12 @@ c0.append(&mut c1);
Ok(c0) } - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { - let mut c0 = self.c0.to_bytes_strict(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes_strict(cs.ns(|| "c1"))?; + let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; + let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; c0.append(&mut c1); Ok(c0) }
@@ -800,13 +800,13 @@
Ok(c0) } - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<Boolean>, SynthesisError> { - let mut c0 = self.c0.to_bits_strict(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits_strict(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_bits_strict(cs.ns(|| "c2"))?; + let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; + let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; + let mut c2 = self.c2.to_non_unique_bits(cs.ns(|| "c2"))?; c0.append(&mut c1); c0.append(&mut c2);@@ -834,11 +834,18 @@
Ok(c0) } - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>( &self, - cs: CS, + mut cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) + let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; + let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; + let mut c2 = self.c2.to_non_unique_bytes(cs.ns(|| "c2"))?; + + c0.append(&mut c1); + c0.append(&mut c2); + + Ok(c0) } }
@@ -452,7 +452,9 @@ let negone: FE = UniformRand::rand(&mut test_rng());
let n = F::alloc(&mut cs.ns(|| "alloc new var"), || Ok(negone)).unwrap(); let _ = n.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap(); - let _ = n.to_bytes_strict(&mut cs.ns(|| "ToBytes Strict")).unwrap(); + let _ = n + .to_non_unique_bytes(&mut cs.ns(|| "ToBytes Strict")) + .unwrap(); let ab_false = a .conditionally_add_constant(
@@ -52,11 +52,12 @@ ) -> Result<Vec<UInt8>, SynthesisError> {
self.0.to_bytes(&mut cs.ns(|| "g_alpha to bytes")) } - fn to_bytes_strict<CS: ConstraintSystem<P::Fp>>( + fn to_non_unique_bytes<CS: ConstraintSystem<P::Fp>>( &self, - cs: CS, + mut cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) + self.0 + .to_non_unique_bytes(&mut cs.ns(|| "g_alpha to bytes")) } }@@ -86,11 +87,17 @@ }
Ok(bytes) } - fn to_bytes_strict<CS: ConstraintSystem<P::Fp>>( + fn to_non_unique_bytes<CS: ConstraintSystem<P::Fp>>( &self, - cs: CS, + mut cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { - self.to_bytes(cs) + let mut bytes = Vec::new(); + for (i, coeffs) in self.ell_coeffs.iter().enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + bytes.extend_from_slice(&coeffs.0.to_non_unique_bytes(&mut cs.ns(|| "c0"))?); + bytes.extend_from_slice(&coeffs.1.to_non_unique_bytes(&mut cs.ns(|| "c1"))?); + } + Ok(bytes) } }
@@ -605,16 +605,16 @@ x_bits.push(self.infinity);
Ok(x_bits) } - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<Boolean>, SynthesisError> { let mut x_bits = self .x - .to_bits_strict(&mut cs.ns(|| "X Coordinate To Bits"))?; + .to_non_unique_bits(&mut cs.ns(|| "X Coordinate To Bits"))?; let y_bits = self .y - .to_bits_strict(&mut cs.ns(|| "Y Coordinate To Bits"))?; + .to_non_unique_bits(&mut cs.ns(|| "Y Coordinate To Bits"))?; x_bits.extend_from_slice(&y_bits); x_bits.push(self.infinity);@@ -640,17 +640,19 @@ x_bytes.extend_from_slice(&inf_bytes);
Ok(x_bytes) } - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { let mut x_bytes = self .x - .to_bytes_strict(&mut cs.ns(|| "X Coordinate To Bytes"))?; + .to_non_unique_bytes(&mut cs.ns(|| "X Coordinate To Bytes"))?; let y_bytes = self .y - .to_bytes_strict(&mut cs.ns(|| "Y Coordinate To Bytes"))?; - let inf_bytes = self.infinity.to_bytes(&mut cs.ns(|| "Infinity to Bytes"))?; + .to_non_unique_bytes(&mut cs.ns(|| "Y Coordinate To Bytes"))?; + let inf_bytes = self + .infinity + .to_non_unique_bytes(&mut cs.ns(|| "Infinity to Bytes"))?; x_bytes.extend_from_slice(&y_bytes); x_bytes.extend_from_slice(&inf_bytes);
@@ -1359,12 +1359,16 @@ x_bits.extend_from_slice(&y_bits);
Ok(x_bits) } - fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<Boolean>, SynthesisError> { - let mut x_bits = self.x.to_bits_strict(cs.ns(|| "X Coordinate To Bits"))?; - let y_bits = self.y.to_bits_strict(cs.ns(|| "Y Coordinate To Bits"))?; + let mut x_bits = self + .x + .to_non_unique_bits(cs.ns(|| "X Coordinate To Bits"))?; + let y_bits = self + .y + .to_non_unique_bits(cs.ns(|| "Y Coordinate To Bits"))?; x_bits.extend_from_slice(&y_bits); Ok(x_bits)@@ -1387,12 +1391,12 @@ x_bytes.extend_from_slice(&y_bytes);
Ok(x_bytes) } - fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>( + fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>( &self, mut cs: CS, ) -> Result<Vec<UInt8>, SynthesisError> { - let mut x_bytes = self.x.to_bytes_strict(cs.ns(|| "x"))?; - let y_bytes = self.y.to_bytes_strict(cs.ns(|| "y"))?; + let mut x_bytes = self.x.to_non_unique_bytes(cs.ns(|| "x"))?; + let y_bytes = self.y.to_non_unique_bytes(cs.ns(|| "y"))?; x_bytes.extend_from_slice(&y_bytes); Ok(x_bytes)
@@ -206,11 +206,13 @@ let b_b = b.add(cs.ns(|| "b + b"), &b).unwrap();
assert_eq!(b2, b_b); let _ = a.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap(); - let _ = a.to_bytes_strict(&mut cs.ns(|| "ToBytes Strict")).unwrap(); + let _ = a + .to_non_unique_bytes(&mut cs.ns(|| "ToBytes Strict")) + .unwrap(); let _ = b.to_bytes(&mut cs.ns(|| "b ToBytes")).unwrap(); let _ = b - .to_bytes_strict(&mut cs.ns(|| "b ToBytes Strict")) + .to_non_unique_bytes(&mut cs.ns(|| "b ToBytes Strict")) .unwrap(); if !cs.is_satisfied() { println!("{:?}", cs.which_is_unsatisfied().unwrap());