diff --git a/src/bit.rs b/src/bit.rs index eaebde9..f08ba67 100644 --- a/src/bit.rs +++ b/src/bit.rs @@ -1,25 +1,228 @@ use tinysnark::FieldT; +use std::rc::Rc; +use std::cell::RefCell; use super::variable::*; use self::Bit::*; +use self::Op::*; + +macro_rules! mirror { + ($a:pat, $b:pat) => (($a, $b) | ($b, $a)) +} + +macro_rules! mirror_match { + (@as_expr $e:expr) => {$e}; + + (@parse + $e:expr, ($($arms:tt)*); + $(,)* + ) => { + mirror_match!(@as_expr match $e { $($arms)* }) + }; + + (@parse + $e:expr, $arms:tt; + , $($tail:tt)* + ) => { + mirror_match!(@parse $e, $arms; $($tail)*) + }; + + (@parse + $e:expr, ($($arms:tt)*); + mirror!($a:pat, $b:pat) => $body:expr, + $($tail:tt)* + ) => { + mirror_match!( + @parse + $e, + ( + $($arms)* + ($a, $b) | ($b, $a) => $body, + ); + $($tail)* + ) + }; + + (@parse + $e:expr, ($($arms:tt)*); + $pat:pat => $body:expr, + $($tail:tt)* + ) => { + mirror_match!( + @parse + $e, + ( + $($arms)* + $pat => $body, + ); + $($tail)* + ) + }; + + (@parse + $e:expr, ($($arms:tt)*); + $pat:pat => $body:expr, + $($tail:tt)* + ) => { + mirror_match!( + @parse + $e, + ( + $($arms)* + $pat => $body, + ); + $($tail)* + ) + }; + + ($e:expr { $($arms:tt)* }) => { + mirror_match!(@parse $e, (); $($arms)*) + }; +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +enum Op { + And, + Nand, + + Xor, + Xnor, + + MaterialNonimplication, + MaterialImplication, + + Nor, + Or +} + +impl Op { + fn not(&self) -> Op { + match *self { + And => Nand, + Nand => And, + + Xor => Xnor, + Xnor => Xor, + + Nor => Or, + Or => Nor, + + MaterialNonimplication => MaterialImplication, + MaterialImplication => MaterialNonimplication + } + } + + fn val(&self, a: FieldT, b: FieldT) -> FieldT { + let a = a == FieldT::one(); + let b = b == FieldT::one(); + let res = match *self { + And => a && b, + Nand => !(a && b), + Xor => a != b, + Xnor => a == b, + Or => a || b, + Nor => !(a || b), + MaterialNonimplication => a && (!b), + MaterialImplication => !(a && (!b)) + }; + + if res { + FieldT::one() + } else { + FieldT::zero() + } + } +} + +#[derive(Clone)] +pub struct BinaryOp { + a: Var, + b: Var, + op: Op, + resolved: Rc>> +} + +impl BinaryOp { + fn new(a: Var, b: Var, op: Op) -> BinaryOp { + BinaryOp { + a: a, + b: b, + op: op, + resolved: Rc::new(RefCell::new(None)) + } + } + + fn walk(&self, counter: &mut usize, constraints: &mut Vec, witness_map: &mut WitnessMap) + { + self.a.walk(counter, constraints, witness_map); + self.b.walk(counter, constraints, witness_map); + } + + fn val(&self, map: &[FieldT], inverted: bool) -> FieldT { + let v = self.op.val(self.a.val(map), self.b.val(map)); + + if inverted { + if v == FieldT::one() { + FieldT::zero() + } else { + FieldT::one() + } + } else { + v + } + } + + fn resolve(&self, inverted: bool) -> Bit { + let res = { self.resolved.borrow_mut().clone() }; + + match res { + Some(v) => { + if inverted { + Not(v) + } else { + Is(v) + } + }, + None => { + let v = resolve(&self.a, &self.b, self.op); + + *self.resolved.borrow_mut() = Some(v.clone()); + + if inverted { + Not(v) + } else { + Is(v) + } + } + } + } +} #[derive(Clone)] pub enum Bit { Constant(bool), Is(Var), - Not(Var) + Not(Var), + Bin(BinaryOp, bool) } -fn resolve_not(v: &Var) -> Var { - gadget(&[v], 1, |vars| { - if vars.get_input(0) == FieldT::zero() { - vars.set_output(0, FieldT::one()); - } else { - vars.set_output(0, FieldT::zero()); - } +fn resolve(a: &Var, b: &Var, op: Op) -> Var { + gadget(&[a, b], 1, move |vals| { + let a = vals.get_input(0); + let b = vals.get_input(1); + + vals.set_output(0, op.val(a, b)); }, |i, o, cs| { - // (1 - a) * 1 = b - cs.push(Constraint); + cs.push(match op { + And => Constraint::And(i[0].index(), i[1].index(), o[0].index()), + Nand => Constraint::Nand(i[0].index(), i[1].index(), o[0].index()), + Xor => Constraint::Xor(i[0].index(), i[1].index(), o[0].index()), + Xnor => Constraint::Xnor(i[0].index(), i[1].index(), o[0].index()), + Nor => Constraint::Nor(i[0].index(), i[1].index(), o[0].index()), + Or => Constraint::Or(i[0].index(), i[1].index(), o[0].index()), + MaterialNonimplication => Constraint::MaterialNonimplication(i[0].index(), i[1].index(), o[0].index()), + MaterialImplication => Constraint::MaterialImplication(i[0].index(), i[1].index(), o[0].index()) + }); vec![o[0]] }).remove(0) @@ -30,7 +233,16 @@ impl Bit { match *self { Constant(c) => c, Not(ref v) => v.val(map) == FieldT::zero(), - Is(ref v) => v.val(map) == FieldT::one() + Is(ref v) => v.val(map) == FieldT::one(), + Bin(ref bin, inverted) => bin.val(map, inverted) == FieldT::one() + } + } + + // probably could remove this + pub fn resolve(&self) -> Bit { + match *self { + Bin(ref bin, inverted) => bin.resolve(inverted), + _ => self.clone() } } @@ -42,13 +254,16 @@ impl Bit { }, Is(ref v) => { v.walk(counter, constraints, witness_map); + }, + Bin(ref bin, _) => { + bin.walk(counter, constraints, witness_map); } } } pub fn new(v: &Var) -> Bit { Is(gadget(&[v], 0, |_| {}, |i, o, cs| { - cs.push(Constraint); + cs.push(Constraint::Bitness(i[0].index())); vec![i[0]] }).remove(0)) @@ -60,11 +275,11 @@ impl Bit { // self xor other pub fn xor(&self, other: &Bit) -> Bit { - match (self, other) { + mirror_match!((self, other) { (&Constant(a), &Constant(b)) => { Constant(a != b) }, - (&Is(ref v), &Constant(a)) | (&Constant(a), &Is(ref v)) => { + mirror!(&Is(ref v), &Constant(a)) => { if a { // Anything XOR 1 is the NOT of that thing. Not(v.clone()) @@ -73,104 +288,94 @@ impl Bit { Is(v.clone()) } }, - (&Is(ref a), &Is(ref b)) => { - Is(gadget(&[a, b], 1, |vars| { - if vars.get_input(0) != vars.get_input(1) { - vars.set_output(0, FieldT::one()); - } else { - vars.set_output(0, FieldT::zero()); - } - }, |i, o, cs| { - // (2*b) * c = b+c - a - cs.push(Constraint); - - vec![o[0]] - }).remove(0)) - }, - (&Not(ref v), &Constant(a)) | (&Constant(a), &Not(ref v)) => { + mirror!(&Not(ref v), &Constant(a)) => { if a { // Anything XOR 1 is the NOT of that thing. - // !A XOR 1 = A Is(v.clone()) } else { Not(v.clone()) } }, - (&Not(ref a), &Not(ref b)) => { - // !A xor !B is equivalent to A xor B - Is(a.clone()).xor(&Is(b.clone())) + mirror!(&Bin(ref bin, inverted), &Constant(c)) => { + if c { + // Anything XOR 1 is the NOT of that thing. + Bin(bin.clone(), !inverted) + } else { + Bin(bin.clone(), inverted) + } }, - (&Is(ref i), &Not(ref n)) | (&Not(ref n), &Is(ref i)) => { - Is(i.clone()).xor(&Is(resolve_not(n))) - } - } + mirror!(&Bin(ref bin, inverted), &Is(ref i)) => { + bin.resolve(inverted).xor(&Is(i.clone())) + }, + (&Bin(ref bin1, inverted1), &Bin(ref bin2, inverted2)) => { + bin1.resolve(inverted1).xor(&bin2.resolve(inverted2)) + }, + mirror!(&Bin(ref bin, inverted), &Not(ref n)) => { + bin.resolve(inverted).xor(&Not(n.clone())) + }, + (&Not(ref a), &Not(ref b)) => { + Bin(BinaryOp::new(a.clone(), b.clone(), Xor), false) + }, + mirror!(&Is(ref i), &Not(ref n)) => { + Bin(BinaryOp::new(i.clone(), n.clone(), Xnor), false) + }, + (&Is(ref a), &Is(ref b)) => { + Bin(BinaryOp::new(a.clone(), b.clone(), Xor), false) + }, + }) } - fn and(&self, other: &Bit) -> Bit { - match (self, other) { + pub fn and(&self, other: &Bit) -> Bit { + mirror_match!((self, other) { (&Constant(a), &Constant(b)) => { Constant(a && b) }, - (&Constant(a), &Is(ref v)) | (&Is(ref v), &Constant(a)) => { + mirror!(&Is(ref v), &Constant(a)) => { if a { + // Anything AND 1 is the identity of that thing Is(v.clone()) } else { + // Anything AND 0 is false Constant(false) } }, - (&Is(ref a), &Is(ref b)) => { - Is(gadget(&[a, b], 1, |vars| { - if vars.get_input(0) == FieldT::one() && vars.get_input(1) == FieldT::one() { - vars.set_output(0, FieldT::one()); - } else { - vars.set_output(0, FieldT::zero()); - } - }, |i, o, cs| { - // a * b = c - cs.push(Constraint); - - vec![o[0]] - }).remove(0)) - }, - (&Not(ref a), &Constant(c)) | (&Constant(c), &Not(ref a)) => { - if c { - // X and 1 is the identity of X - Not(a.clone()) + mirror!(&Not(ref v), &Constant(a)) => { + if a { + // Anything AND 1 is the identity of that thing + Not(v.clone()) } else { + // Anything AND 0 is false Constant(false) } }, - (&Not(ref n), &Is(ref i)) | (&Is(ref i), &Not(ref n)) => { - //Is(i.clone()).and(&Is(resolve_not(n))) - Is(gadget(&[n, i], 1, |vars| { - if vars.get_input(0) == FieldT::zero() && vars.get_input(1) == FieldT::one() { - vars.set_output(0, FieldT::one()); - } else { - vars.set_output(0, FieldT::zero()); - } - }, |i, o, cs| { - // (1-a) * b = c - cs.push(Constraint); - - vec![o[0]] - }).remove(0)) + mirror!(&Bin(ref bin, inverted), &Constant(c)) => { + if c { + // Anything AND 1 is the identity of that thing + Bin(bin.clone(), inverted) + } else { + // Anything AND 0 is false + Constant(false) + } + }, + mirror!(&Bin(ref bin, inverted), &Is(ref i)) => { + bin.resolve(inverted).and(&Is(i.clone())) + }, + (&Bin(ref bin1, inverted1), &Bin(ref bin2, inverted2)) => { + bin1.resolve(inverted1).and(&bin2.resolve(inverted2)) + }, + mirror!(&Bin(ref bin, inverted), &Not(ref n)) => { + bin.resolve(inverted).and(&Not(n.clone())) }, (&Not(ref a), &Not(ref b)) => { - //Is(resolve_not(a)).and(&Is(resolve_not(b))) - Is(gadget(&[a, b], 1, |vars| { - if vars.get_input(0) == FieldT::zero() && vars.get_input(1) == FieldT::zero() { - vars.set_output(0, FieldT::one()); - } else { - vars.set_output(0, FieldT::zero()); - } - }, |i, o, cs| { - // (1 - a) * (1 - b) = c - cs.push(Constraint); - - vec![o[0]] - }).remove(0)) - } - } + Bin(BinaryOp::new(a.clone(), b.clone(), Nor), false) + }, + mirror!(&Is(ref i), &Not(ref n)) => { + Bin(BinaryOp::new(i.clone(), n.clone(), MaterialNonimplication), false) + }, + (&Is(ref a), &Is(ref b)) => { + Bin(BinaryOp::new(a.clone(), b.clone(), And), false) + }, + }) } // (not self) and other @@ -191,6 +396,7 @@ fn test_binary_op Bit>(op: F, a_in: i64, b_in: i64, c_out: let mut constraints = vec![]; let c = op(&a, &b); + let c = c.resolve(); c.walk(&mut counter, &mut constraints, &mut witness_map); assert_eq!(counter, 4); assert_eq!(constraints.len(), 3); diff --git a/src/keccak.rs b/src/keccak.rs index 83927d9..4af38e4 100644 --- a/src/keccak.rs +++ b/src/keccak.rs @@ -282,6 +282,9 @@ fn test_sha3_256() { ((0..512).map(|_| 0x30).collect::>(), [0x1c,0x80,0x1b,0x16,0x3a,0x2a,0xbe,0xd0,0xe8,0x07,0x1e,0x7f,0xf2,0x60,0x4e,0x98,0x11,0x22,0x80,0x54,0x14,0xf3,0xc8,0xfd,0x96,0x59,0x5d,0x7e,0xe1,0xd6,0x54,0xe2] ), + ((0..64).map(|_| 0x00).collect::>(), + [0x07,0x0f,0xa1,0xab,0x6f,0xcc,0x55,0x7e,0xd1,0x4d,0x42,0x94,0x1f,0x19,0x67,0x69,0x30,0x48,0x55,0x1e,0xb9,0x04,0x2a,0x8d,0x0a,0x05,0x7a,0xfb,0xd7,0x5e,0x81,0xe0] + ), ]; for (i, &(ref message, ref expected)) in test_vector.iter().enumerate() { diff --git a/src/main.rs b/src/main.rs index d855640..502cbd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ mod bit; fn main() { tinysnark::init(); - let inbytes = 1; + let inbytes = 64; //for inbits in 0..1024 { let inbits = inbytes * 8; let input: Vec = (0..inbits).map(|i| Bit::new(&Var::new(i+1))).collect(); diff --git a/src/variable.rs b/src/variable.rs index 63275c4..4dd29ed 100644 --- a/src/variable.rs +++ b/src/variable.rs @@ -41,7 +41,33 @@ pub fn witness_field_elements(vars: &mut [FieldT], witness_map: &WitnessMap) { } #[derive(Clone)] -pub struct Constraint; +pub enum Constraint { + Bitness(Rc>), + And(Rc>, Rc>, Rc>), + Nand(Rc>, Rc>, Rc>), + Xor(Rc>, Rc>, Rc>), + Xnor(Rc>, Rc>, Rc>), + MaterialNonimplication(Rc>, Rc>, Rc>), + MaterialImplication(Rc>, Rc>, Rc>), + Nor(Rc>, Rc>, Rc>), + Or(Rc>, Rc>, Rc>) +} + +impl fmt::Debug for Constraint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", match *self { + Constraint::Bitness(ref b) => format!("bitness: {}", b.get()), + Constraint::And(ref a, ref b, ref c) => format!("{} = {} AND {}", c.get(), a.get(), b.get()), + Constraint::Nand(ref a, ref b, ref c) => format!("{} = {} NAND {}", c.get(), a.get(), b.get()), + Constraint::Xor(ref a, ref b, ref c) => format!("{} = {} XOR {}", c.get(), a.get(), b.get()), + Constraint::Xnor(ref a, ref b, ref c) => format!("{} = {} XNOR {}", c.get(), a.get(), b.get()), + Constraint::MaterialNonimplication(ref a, ref b, ref c) => format!("{} = {} ↛ {}", c.get(), a.get(), b.get()), + Constraint::MaterialImplication(ref a, ref b, ref c) => format!("{} = {} <-> {}", c.get(), a.get(), b.get()), + Constraint::Nor(ref a, ref b, ref c) => format!("{} = {} NOR {}", c.get(), a.get(), b.get()), + Constraint::Or(ref a, ref b, ref c) => format!("{} = {} OR {}", c.get(), a.get(), b.get()) + }) + } +} struct Gadget { inputs: Vec, @@ -96,6 +122,11 @@ impl Var { } } + // make this not public or unsafe too + pub fn index(&self) -> Rc> { + self.index.clone() + } + pub fn val(&self, map: &[FieldT]) -> FieldT { let index = self.index.get(); assert!(index != 0); diff --git a/tinysnark/src/lib.rs b/tinysnark/src/lib.rs index 00a5582..83084cf 100644 --- a/tinysnark/src/lib.rs +++ b/tinysnark/src/lib.rs @@ -16,6 +16,7 @@ static mut INITIALIZED: bool = false; extern "C" { fn tinysnark_init_public_params(); + pub fn tinysnark_test(); } pub fn init() { diff --git a/tinysnark/tinysnark.cpp b/tinysnark/tinysnark.cpp index 1313c68..81a62c9 100644 --- a/tinysnark/tinysnark.cpp +++ b/tinysnark/tinysnark.cpp @@ -7,6 +7,7 @@ zk-SNARK support using the ALT_BN128 curve. #include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" #include "common/default_types/r1cs_ppzksnark_pp.hpp" #include "common/utils.hpp" +#include "gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" using namespace libsnark; using namespace std; @@ -170,3 +171,31 @@ extern "C" void tinysnark_init_public_params() { assert(sizeof(p) == 32); } } + + + +extern "C" void tinysnark_test() { + typedef Fr FieldT; + + protoboard pb; + + auto input_bits = new digest_variable(pb, 512, "input_bits"); + auto output_bits = new digest_variable(pb, 256, "output_bits"); + auto input_block = new block_variable(pb, { + input_bits->bits + }, "input_block"); + auto IV = SHA256_default_IV(pb); + auto sha256 = new sha256_compression_function_gadget(pb, + IV, + input_block->bits, + *output_bits, + "sha256"); + + input_bits->generate_r1cs_constraints(); + output_bits->generate_r1cs_constraints(); + sha256->generate_r1cs_constraints(); + + const r1cs_constraint_system constraint_system = pb.get_constraint_system(); + + cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; +} \ No newline at end of file