mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-11-01 20:07:02 +00:00
Implementation of SHA256 choice operation for Boolean.
This commit is contained in:
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user