Make UInt32::addmany produce constant results when fed constant inputs, to allow for blake2s block precomputation for group hash.

This commit is contained in:
Sean Bowe 2017-12-12 14:42:13 -07:00
parent 3a6e8d448f
commit 6b43a4ed10
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
2 changed files with 80 additions and 3 deletions

View File

@ -325,6 +325,32 @@ mod test {
assert_eq!(cs.num_constraints(), 21792);
}
#[test]
fn test_blake2s_precomp_constraints() {
// Test that 512 fixed leading bits (constants)
// doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.gen()))
.chain((0..512)
.map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()))
.collect();
blake2s(&mut cs, &input_bits).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21792);
}
#[test]
fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect();
blake2s(&mut cs, &input_bits).unwrap();
assert_eq!(cs.num_constraints(), 0);
}
#[test]
fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
@ -366,7 +392,10 @@ mod test {
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
},
_ => panic!()
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
}

View File

@ -205,6 +205,8 @@ impl<Var: Copy> UInt32<Var> {
// This is a linear combination that we will enforce to be "zero"
let mut lc = LinearCombination::zero();
let mut all_constants = true;
// Iterate over the operands
for op in operands {
// Accumulate the value
@ -225,10 +227,14 @@ impl<Var: Copy> UInt32<Var> {
for bit in &op.bits {
match bit {
&Boolean::Is(ref bit) => {
all_constants = false;
// Add coeff * bit
lc = lc + (coeff, bit.get_variable());
},
&Boolean::Not(ref bit) => {
all_constants = false;
// Add coeff * (1 - bit) = coeff * ONE - coeff * bit
lc = lc + (coeff, cs.one()) - (coeff, bit.get_variable());
},
@ -246,6 +252,13 @@ impl<Var: Copy> UInt32<Var> {
// The value of the actual result is modulo 2^32
let modular_value = result_value.map(|v| v as u32);
if all_constants && modular_value.is_some() {
// We can just return a constant, rather than
// unpacking the result into allocated bits.
return Ok(UInt32::constant(modular_value.unwrap()));
}
// Storage area for the resulting bits
let mut result_bits = vec![];
@ -367,6 +380,41 @@ mod test {
}
}
#[test]
fn test_uint32_addmany_constants() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let a_bit = UInt32::constant(a);
let b_bit = UInt32::constant(b);
let c_bit = UInt32::constant(c);
let mut expected = a.wrapping_add(b).wrapping_add(c);
let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
assert!(r.value == Some(expected));
for b in r.bits.iter() {
match b {
&Boolean::Is(_) => panic!(),
&Boolean::Not(_) => panic!(),
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
}
expected >>= 1;
}
}
}
#[test]
fn test_uint32_addmany() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
@ -401,8 +449,8 @@ mod test {
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
&Boolean::Constant(_) => {
unreachable!()
}
}