From 6b43a4ed108c5124ee14c8df081679d2af432f67 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 12 Dec 2017 14:42:13 -0700 Subject: [PATCH] Make UInt32::addmany produce constant results when fed constant inputs, to allow for blake2s block precomputation for group hash. --- src/circuit/blake2s.rs | 31 ++++++++++++++++++++++++- src/circuit/uint32.rs | 52 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/circuit/blake2s.rs b/src/circuit/blake2s.rs index d49a233f4..9a0d283ad 100644 --- a/src/circuit/blake2s.rs +++ b/src/circuit/blake2s.rs @@ -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::::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::::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); + } } } } diff --git a/src/circuit/uint32.rs b/src/circuit/uint32.rs index c8bb62997..7954538f5 100644 --- a/src/circuit/uint32.rs +++ b/src/circuit/uint32.rs @@ -205,6 +205,8 @@ impl UInt32 { // 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 UInt32 { 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 UInt32 { // 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::::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!() } }