Implementation of SHA256 choice operation for Boolean.

This commit is contained in:
Sean Bowe 2018-03-15 13:01:15 -06:00
parent 4f0a553fbb
commit abca61401e
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
1 changed files with 234 additions and 0 deletions

View File

@ -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 {
@ -797,6 +923,31 @@ mod test {
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]
fn test_boolean_xor() {
let variants = [
@ -1115,4 +1266,87 @@ mod test {
assert_eq!(bits[254 - 20].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");
}
}
}
}
}
}