|
|
@ -523,6 +523,132 @@ impl Boolean { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Computes (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
pub fn sha256_ch<'a, E, CS>( |
|
|
|
|
|
|
|
mut cs: CS, |
|
|
|
|
|
|
|
a: &'a Self, |
|
|
|
|
|
|
|
b: &'a Self, |
|
|
|
|
|
|
|
c: &'a Self |
|
|
|
|
|
|
|
) -> Result<Self, SynthesisError> |
|
|
|
|
|
|
|
where E: Engine, |
|
|
|
|
|
|
|
CS: ConstraintSystem<E> |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { |
|
|
|
|
|
|
|
(Some(a), Some(b), Some(c)) => { |
|
|
|
|
|
|
|
// (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
Some((a & b) ^ ((!a) & c)) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
_ => None |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match (a, b, c) { |
|
|
|
|
|
|
|
(&Boolean::Constant(_), |
|
|
|
|
|
|
|
&Boolean::Constant(_), |
|
|
|
|
|
|
|
&Boolean::Constant(_)) => { |
|
|
|
|
|
|
|
// They're all constants, so we can just compute the value.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Ok(Boolean::Constant(ch_value.expect("they're all constants"))); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
(&Boolean::Constant(false), _, c) => { |
|
|
|
|
|
|
|
// If a is false
|
|
|
|
|
|
|
|
// (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// (false) xor (c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// c
|
|
|
|
|
|
|
|
return Ok(c.clone()); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
(a, &Boolean::Constant(false), c) => { |
|
|
|
|
|
|
|
// If b is false
|
|
|
|
|
|
|
|
// (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// ((not a) and c)
|
|
|
|
|
|
|
|
return Boolean::and( |
|
|
|
|
|
|
|
cs, |
|
|
|
|
|
|
|
&a.not(), |
|
|
|
|
|
|
|
&c |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
(a, b, &Boolean::Constant(false)) => { |
|
|
|
|
|
|
|
// If c is false
|
|
|
|
|
|
|
|
// (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// (a and b)
|
|
|
|
|
|
|
|
return Boolean::and( |
|
|
|
|
|
|
|
cs, |
|
|
|
|
|
|
|
&a, |
|
|
|
|
|
|
|
&b |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
(a, b, &Boolean::Constant(true)) => { |
|
|
|
|
|
|
|
// If c is true
|
|
|
|
|
|
|
|
// (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// (a and b) xor (not a)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// not (a and (not b))
|
|
|
|
|
|
|
|
return Ok(Boolean::and( |
|
|
|
|
|
|
|
cs, |
|
|
|
|
|
|
|
&a, |
|
|
|
|
|
|
|
&b.not() |
|
|
|
|
|
|
|
)?.not()); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
(a, &Boolean::Constant(true), c) => { |
|
|
|
|
|
|
|
// If b is true
|
|
|
|
|
|
|
|
// (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// a xor ((not a) and c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// not ((not a) and (not c))
|
|
|
|
|
|
|
|
return Ok(Boolean::and( |
|
|
|
|
|
|
|
cs, |
|
|
|
|
|
|
|
&a.not(), |
|
|
|
|
|
|
|
&c.not() |
|
|
|
|
|
|
|
)?.not()); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
(&Boolean::Constant(true), _, _) => { |
|
|
|
|
|
|
|
// If a is true
|
|
|
|
|
|
|
|
// (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
// equals
|
|
|
|
|
|
|
|
// b xor ((not a) and c)
|
|
|
|
|
|
|
|
// So we just continue!
|
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
(&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) | |
|
|
|
|
|
|
|
(&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) | |
|
|
|
|
|
|
|
(&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) | |
|
|
|
|
|
|
|
(&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) | |
|
|
|
|
|
|
|
(&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) | |
|
|
|
|
|
|
|
(&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) | |
|
|
|
|
|
|
|
(&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) | |
|
|
|
|
|
|
|
(&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) |
|
|
|
|
|
|
|
=> {} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let ch = cs.alloc(|| "ch", || { |
|
|
|
|
|
|
|
ch_value.get().map(|v| { |
|
|
|
|
|
|
|
if *v { |
|
|
|
|
|
|
|
E::Fr::one() |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
E::Fr::zero() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// a(b - c) = ch - c
|
|
|
|
|
|
|
|
cs.enforce( |
|
|
|
|
|
|
|
|| "ch computation", |
|
|
|
|
|
|
|
|_| b.lc(CS::one(), E::Fr::one()) |
|
|
|
|
|
|
|
- &c.lc(CS::one(), E::Fr::one()), |
|
|
|
|
|
|
|
|_| a.lc(CS::one(), E::Fr::one()), |
|
|
|
|
|
|
|
|lc| lc + ch - &c.lc(CS::one(), E::Fr::one()) |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(AllocatedBit { |
|
|
|
|
|
|
|
value: ch_value, |
|
|
|
|
|
|
|
variable: ch |
|
|
|
|
|
|
|
}.into()) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl From<AllocatedBit> for Boolean { |
|
|
|
impl From<AllocatedBit> for Boolean { |
|
|
@ -797,6 +923,31 @@ mod test { |
|
|
|
NegatedAllocatedFalse |
|
|
|
NegatedAllocatedFalse |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl OperandType { |
|
|
|
|
|
|
|
fn is_constant(&self) -> bool { |
|
|
|
|
|
|
|
match *self { |
|
|
|
|
|
|
|
OperandType::True => true, |
|
|
|
|
|
|
|
OperandType::False => true, |
|
|
|
|
|
|
|
OperandType::AllocatedTrue => false, |
|
|
|
|
|
|
|
OperandType::AllocatedFalse => false, |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedTrue => false, |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedFalse => false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn val(&self) -> bool { |
|
|
|
|
|
|
|
match *self { |
|
|
|
|
|
|
|
OperandType::True => true, |
|
|
|
|
|
|
|
OperandType::False => false, |
|
|
|
|
|
|
|
OperandType::AllocatedTrue => true, |
|
|
|
|
|
|
|
OperandType::AllocatedFalse => false, |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedTrue => false, |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedFalse => true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
fn test_boolean_xor() { |
|
|
|
fn test_boolean_xor() { |
|
|
|
let variants = [ |
|
|
|
let variants = [ |
|
|
@ -1115,4 +1266,87 @@ mod test { |
|
|
|
assert_eq!(bits[254 - 20].value.unwrap(), true); |
|
|
|
assert_eq!(bits[254 - 20].value.unwrap(), true); |
|
|
|
assert_eq!(bits[254 - 23].value.unwrap(), true); |
|
|
|
assert_eq!(bits[254 - 23].value.unwrap(), true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_boolean_sha256_ch() { |
|
|
|
|
|
|
|
let variants = [ |
|
|
|
|
|
|
|
OperandType::True, |
|
|
|
|
|
|
|
OperandType::False, |
|
|
|
|
|
|
|
OperandType::AllocatedTrue, |
|
|
|
|
|
|
|
OperandType::AllocatedFalse, |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedTrue, |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedFalse |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for first_operand in variants.iter().cloned() { |
|
|
|
|
|
|
|
for second_operand in variants.iter().cloned() { |
|
|
|
|
|
|
|
for third_operand in variants.iter().cloned() { |
|
|
|
|
|
|
|
let mut cs = TestConstraintSystem::<Bls12>::new(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let a; |
|
|
|
|
|
|
|
let b; |
|
|
|
|
|
|
|
let c; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ch = (a and b) xor ((not a) and c)
|
|
|
|
|
|
|
|
let expected = (first_operand.val() & second_operand.val()) ^ |
|
|
|
|
|
|
|
((!first_operand.val()) & third_operand.val()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let mut dyn_construct = |operand, name| { |
|
|
|
|
|
|
|
let cs = cs.namespace(|| name); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match operand { |
|
|
|
|
|
|
|
OperandType::True => Boolean::constant(true), |
|
|
|
|
|
|
|
OperandType::False => Boolean::constant(false), |
|
|
|
|
|
|
|
OperandType::AllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()), |
|
|
|
|
|
|
|
OperandType::AllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()), |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not(), |
|
|
|
|
|
|
|
OperandType::NegatedAllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not(), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a = dyn_construct(first_operand, "a"); |
|
|
|
|
|
|
|
b = dyn_construct(second_operand, "b"); |
|
|
|
|
|
|
|
c = dyn_construct(third_operand, "c"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let maj = Boolean::sha256_ch(&mut cs, &a, &b, &c).unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(maj.get_value().unwrap(), expected); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if first_operand.is_constant() || |
|
|
|
|
|
|
|
second_operand.is_constant() || |
|
|
|
|
|
|
|
third_operand.is_constant() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if first_operand.is_constant() && |
|
|
|
|
|
|
|
second_operand.is_constant() && |
|
|
|
|
|
|
|
third_operand.is_constant() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
assert_eq!(cs.num_constraints(), 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
assert_eq!(cs.get("ch"), { |
|
|
|
|
|
|
|
if expected { |
|
|
|
|
|
|
|
Fr::one() |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
Fr::zero() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
cs.set("ch", { |
|
|
|
|
|
|
|
if expected { |
|
|
|
|
|
|
|
Fr::zero() |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
Fr::one() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
assert_eq!(cs.which_is_unsatisfied().unwrap(), "ch computation"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|