Out with the old...
This commit is contained in:
parent
e24fcfdc5c
commit
c506c48c91
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "tinysnark/libsnark"]
|
||||
path = tinysnark/libsnark
|
||||
url = git://github.com/scipr-lab/libsnark.git
|
18
.travis.yml
18
.travis.yml
@ -1,21 +1,7 @@
|
||||
sudo: false
|
||||
language: rust
|
||||
rust:
|
||||
- nightly
|
||||
|
||||
install:
|
||||
- export CXX="g++-4.8" CC="gcc-4.8"
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- gcc-4.8
|
||||
- libgmp-dev
|
||||
- stable
|
||||
|
||||
script:
|
||||
- cd tinysnark && cargo test
|
||||
- cd .. && cargo run --release
|
||||
- cargo test
|
||||
- cargo test --release
|
||||
|
@ -9,8 +9,4 @@ repository = "https://github.com/ebfull/bellman"
|
||||
version = "0.0.1"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.3.12"
|
||||
|
||||
[dependencies.tinysnark]
|
||||
path = "tinysnark"
|
||||
version = "0.0.1"
|
||||
rand = "0.3.15"
|
||||
|
18
Dockerfile
18
Dockerfile
@ -1,18 +0,0 @@
|
||||
FROM debian
|
||||
|
||||
# Install Rust and Cargo
|
||||
# TODO: make this architecture agnostic
|
||||
RUN apt-get update && apt-get install -yy sudo wget
|
||||
RUN wget https://static.rust-lang.org/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz
|
||||
RUN tar xvf rust-nightly-x86_64-unknown-linux-gnu.tar.gz
|
||||
RUN cd rust-nightly-x86_64-unknown-linux-gnu && ./install.sh
|
||||
|
||||
|
||||
# Install libsnark dependencies
|
||||
# g++ (for building libsnark)
|
||||
# libgmp-dev (for bigint math)
|
||||
RUN apt-get update && apt-get install -yy g++ libgmp-dev
|
||||
|
||||
# Include this directory in the built image
|
||||
|
||||
ADD . /bellman
|
560
src/bit.rs
560
src/bit.rs
@ -1,560 +0,0 @@
|
||||
use tinysnark::FieldT;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use super::variable::*;
|
||||
use self::Bit::*;
|
||||
use self::Op::*;
|
||||
use super::circuit::*;
|
||||
|
||||
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<RefCell<Option<Var>>>
|
||||
}
|
||||
|
||||
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<Constraint>, 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),
|
||||
Bin(BinaryOp, bool)
|
||||
}
|
||||
|
||||
struct BitEquality {
|
||||
a: Bit,
|
||||
b: Var
|
||||
}
|
||||
|
||||
impl Constrainable for BitEquality {
|
||||
type Result = Var;
|
||||
|
||||
fn synthesize(&self, enforce: &Bit) -> Var {
|
||||
// TODO: currently only support unconditional enforcement
|
||||
match enforce {
|
||||
&Bit::Constant(true) => {},
|
||||
_ => unimplemented!()
|
||||
}
|
||||
|
||||
match self.a {
|
||||
Bin(ref binop, inverted) => {
|
||||
// TODO: figure this out later
|
||||
assert!(binop.resolved.borrow().is_none());
|
||||
|
||||
let mut op = binop.op;
|
||||
|
||||
if inverted {
|
||||
op = op.not();
|
||||
}
|
||||
|
||||
gadget(&[&binop.a, &binop.b, &self.b], 0, move |vals| {
|
||||
let a = vals.get_input(0);
|
||||
let b = vals.get_input(1);
|
||||
|
||||
unsafe { vals.set_input(2, op.val(a, b)) };
|
||||
}, |i, o, cs| {
|
||||
cs.push(binaryop_constraint(i[0], i[1], i[2], op));
|
||||
|
||||
vec![i[2]]
|
||||
}).remove(0)
|
||||
},
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Equals<Var> for Bit {
|
||||
type Result = BitEquality;
|
||||
|
||||
fn must_equal(&self, other: &Var) -> BitEquality {
|
||||
BitEquality {
|
||||
a: self.clone(),
|
||||
b: other.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn binaryop_constraint(a: &Var, b: &Var, c: &Var, op: Op) -> Constraint {
|
||||
match op {
|
||||
// a * b = c
|
||||
And => Constraint(vec![(FieldT::one(), a.clone())],
|
||||
vec![(FieldT::one(), b.clone())],
|
||||
vec![(FieldT::one(), c.clone())]
|
||||
),
|
||||
// a * b = 1 - c
|
||||
Nand => Constraint(vec![(FieldT::one(), a.clone())],
|
||||
vec![(FieldT::one(), b.clone())],
|
||||
vec![(FieldT::one(), Var::one()),
|
||||
(-FieldT::one(), c.clone())
|
||||
]
|
||||
),
|
||||
// 2a * b = a + b - c
|
||||
Xor => Constraint(vec![(FieldT::from(2), a.clone())],
|
||||
vec![(FieldT::one(), b.clone())],
|
||||
vec![(FieldT::one(), a.clone()),
|
||||
(FieldT::one(), b.clone()),
|
||||
(-FieldT::one(), c.clone())
|
||||
]
|
||||
),
|
||||
// 2a * b = a + b + c - 1
|
||||
Xnor => Constraint(vec![(FieldT::from(2), a.clone())],
|
||||
vec![(FieldT::one(), b.clone())],
|
||||
vec![
|
||||
(FieldT::one(), a.clone()),
|
||||
(FieldT::one(), b.clone()),
|
||||
(FieldT::one(), c.clone()),
|
||||
(-FieldT::one(), Var::one())
|
||||
]
|
||||
),
|
||||
// a * (1 - b) = c
|
||||
MaterialNonimplication => Constraint(vec![(FieldT::one(), a.clone())],
|
||||
vec![(FieldT::one(), Var::one()),
|
||||
(-FieldT::one(), b.clone())
|
||||
],
|
||||
vec![(FieldT::one(), c.clone())]
|
||||
),
|
||||
// a * b = a + c - 1
|
||||
MaterialImplication => Constraint(vec![(FieldT::one(), a.clone())],
|
||||
vec![(FieldT::one(), b.clone())],
|
||||
vec![(FieldT::one(), a.clone()),
|
||||
(FieldT::one(), c.clone()),
|
||||
(-FieldT::one(), Var::one())
|
||||
]
|
||||
),
|
||||
// (1 - a) * (1 - b) = c
|
||||
Nor => Constraint(vec![(FieldT::one(), Var::one()),
|
||||
(-FieldT::one(), a.clone())
|
||||
],
|
||||
vec![(FieldT::one(), Var::one()),
|
||||
(-FieldT::one(), b.clone())
|
||||
],
|
||||
vec![(FieldT::one(), c.clone())]
|
||||
),
|
||||
// a * b = a + b - c
|
||||
Or => Constraint(vec![(FieldT::one(), a.clone())],
|
||||
vec![(FieldT::one(), b.clone())],
|
||||
vec![(FieldT::one(), a.clone()),
|
||||
(FieldT::one(), b.clone()),
|
||||
(-FieldT::one(), c.clone())
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
cs.push(binaryop_constraint(i[0], i[1], o[0], op));
|
||||
|
||||
vec![o[0]]
|
||||
}).remove(0)
|
||||
}
|
||||
|
||||
impl ConstraintWalker for Bit {
|
||||
fn walk(&self, counter: &mut usize, constraints: &mut Vec<Constraint>, witness_map: &mut WitnessMap)
|
||||
{
|
||||
match *self {
|
||||
Constant(_) => {},
|
||||
Not(ref v) => {
|
||||
v.walk(counter, constraints, witness_map);
|
||||
},
|
||||
Is(ref v) => {
|
||||
v.walk(counter, constraints, witness_map);
|
||||
},
|
||||
Bin(ref bin, _) => {
|
||||
bin.walk(counter, constraints, witness_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bit {
|
||||
pub fn val(&self, map: &[FieldT]) -> bool {
|
||||
match *self {
|
||||
Constant(c) => c,
|
||||
Not(ref v) => v.val(map) == FieldT::zero(),
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(v: &Var) -> Bit {
|
||||
Is(gadget(&[v], 0, |_| {}, |i, o, cs| {
|
||||
// boolean constraint:
|
||||
// (1 - a) * a = 0
|
||||
cs.push(Constraint(vec![(FieldT::one(), Var::one()),
|
||||
(-FieldT::one(), i[0].clone())],
|
||||
vec![(FieldT::one(), i[0].clone())],
|
||||
vec![(FieldT::zero(), Var::one())]
|
||||
));
|
||||
|
||||
vec![i[0]]
|
||||
}).remove(0))
|
||||
}
|
||||
|
||||
pub fn constant(num: bool) -> Bit {
|
||||
Constant(num)
|
||||
}
|
||||
|
||||
// self xor other
|
||||
pub fn xor(&self, other: &Bit) -> Bit {
|
||||
mirror_match!(((self, other)) {
|
||||
(&Constant(a), &Constant(b)) => {
|
||||
Constant(a != b)
|
||||
},
|
||||
mirror!(&Is(ref v), &Constant(a)) => {
|
||||
if a {
|
||||
// Anything XOR 1 is the NOT of that thing.
|
||||
Not(v.clone())
|
||||
} else {
|
||||
// Anything XOR 0 equals that thing.
|
||||
Is(v.clone())
|
||||
}
|
||||
},
|
||||
mirror!(&Not(ref v), &Constant(a)) => {
|
||||
if a {
|
||||
// Anything XOR 1 is the NOT of that thing.
|
||||
Is(v.clone())
|
||||
} else {
|
||||
Not(v.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)
|
||||
}
|
||||
},
|
||||
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)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn and(&self, other: &Bit) -> Bit {
|
||||
mirror_match!(((self, other)) {
|
||||
(&Constant(a), &Constant(b)) => {
|
||||
Constant(a && b)
|
||||
},
|
||||
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)
|
||||
}
|
||||
},
|
||||
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)
|
||||
}
|
||||
},
|
||||
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)) => {
|
||||
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
|
||||
pub fn notand(&self, other: &Bit) -> Bit {
|
||||
self.xor(&Constant(true)).and(other)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_binary_op<F: Fn(&Bit, &Bit) -> Bit>(op: F, a_in: i64, b_in: i64, c_out: i64)
|
||||
{
|
||||
let a = Var::new(1);
|
||||
let b = Var::new(2);
|
||||
let a = Bit::new(&a);
|
||||
let b = Bit::new(&b);
|
||||
let mut counter = 3;
|
||||
let mut witness_map = WitnessMap::new();
|
||||
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);
|
||||
assert_eq!(witness_map.len(), 2);
|
||||
assert_eq!(witness_map[&1].len(), 2);
|
||||
assert_eq!(witness_map[&2].len(), 1);
|
||||
|
||||
let mut f: Vec<FieldT> = (0..counter).map(|_| FieldT::zero()).collect();
|
||||
f[0] = FieldT::one();
|
||||
f[1] = FieldT::from(a_in);
|
||||
f[2] = FieldT::from(b_in);
|
||||
|
||||
witness_field_elements(&mut f, &witness_map);
|
||||
|
||||
assert_eq!(f[3], FieldT::from(c_out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor() {
|
||||
use tinysnark;
|
||||
|
||||
tinysnark::init();
|
||||
|
||||
test_binary_op(Bit::xor, 0, 0, 0);
|
||||
test_binary_op(Bit::xor, 0, 1, 1);
|
||||
test_binary_op(Bit::xor, 1, 0, 1);
|
||||
test_binary_op(Bit::xor, 1, 1, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and() {
|
||||
use tinysnark;
|
||||
|
||||
tinysnark::init();
|
||||
|
||||
test_binary_op(Bit::and, 0, 0, 0);
|
||||
test_binary_op(Bit::and, 0, 1, 0);
|
||||
test_binary_op(Bit::and, 1, 0, 0);
|
||||
test_binary_op(Bit::and, 1, 1, 1);
|
||||
}
|
148
src/circuit.rs
148
src/circuit.rs
@ -1,148 +0,0 @@
|
||||
use tinysnark::{Proof, Keypair, FieldT, LinearTerm, ConstraintSystem};
|
||||
use super::variable::{Var,Constraint,WitnessMap,witness_field_elements};
|
||||
use super::bit::Bit;
|
||||
|
||||
pub trait ConstraintWalker: 'static {
|
||||
fn walk(&self,
|
||||
counter: &mut usize,
|
||||
constraints: &mut Vec<Constraint>,
|
||||
witness_map: &mut WitnessMap);
|
||||
}
|
||||
|
||||
impl<C: ConstraintWalker> ConstraintWalker for Vec<C> {
|
||||
fn walk(&self,
|
||||
counter: &mut usize,
|
||||
constraints: &mut Vec<Constraint>,
|
||||
witness_map: &mut WitnessMap)
|
||||
{
|
||||
for i in self {
|
||||
i.walk(counter, constraints, witness_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Constrainable {
|
||||
type Result: ConstraintWalker;
|
||||
|
||||
fn synthesize(&self, enforce: &Bit) -> Self::Result;
|
||||
}
|
||||
|
||||
impl<C: Constrainable> Constrainable for Vec<C> {
|
||||
type Result = Vec<C::Result>;
|
||||
|
||||
fn synthesize(&self, enforce: &Bit) -> Vec<C::Result> {
|
||||
self.iter().map(|a| a.synthesize(enforce)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Equals<Rhs: ?Sized> {
|
||||
type Result: Constrainable;
|
||||
|
||||
fn must_equal(&self, other: &Rhs) -> Self::Result;
|
||||
}
|
||||
|
||||
impl<Lhs, Rhs> Equals<[Rhs]> for [Lhs] where Lhs: Equals<Rhs> {
|
||||
type Result = Vec<Lhs::Result>;
|
||||
|
||||
fn must_equal(&self, other: &[Rhs]) -> Vec<Lhs::Result> {
|
||||
assert_eq!(self.len(), other.len());
|
||||
|
||||
self.iter().zip(other.iter()).map(|(a, b)| a.must_equal(b)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Circuit {
|
||||
public_inputs: usize,
|
||||
private_inputs: usize,
|
||||
aux_inputs: usize,
|
||||
keypair: Keypair,
|
||||
witness_map: WitnessMap
|
||||
}
|
||||
|
||||
impl Circuit {
|
||||
pub fn verify(&self, proof: &Proof, public: &[FieldT]) -> bool
|
||||
{
|
||||
proof.verify(&self.keypair, public)
|
||||
}
|
||||
|
||||
pub fn prove(&self, public: &[FieldT], private: &[FieldT]) -> Result<Proof, ()>
|
||||
{
|
||||
assert_eq!(public.len(), self.public_inputs);
|
||||
assert_eq!(private.len(), self.private_inputs);
|
||||
|
||||
let mut vars = Vec::new();
|
||||
vars.push(FieldT::one());
|
||||
vars.extend_from_slice(public);
|
||||
vars.extend_from_slice(private);
|
||||
|
||||
for i in 0..self.aux_inputs {
|
||||
vars.push(FieldT::zero());
|
||||
}
|
||||
|
||||
witness_field_elements(&mut vars, &self.witness_map);
|
||||
|
||||
let primary = &vars[1..public.len()+1];
|
||||
let aux = &vars[1+public.len()..];
|
||||
|
||||
if !self.keypair.is_satisfied(primary, aux) {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
Ok(Proof::new(&self.keypair, primary, aux))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CircuitBuilder {
|
||||
public_inputs: usize,
|
||||
private_inputs: usize,
|
||||
constraints: Vec<Box<ConstraintWalker>>
|
||||
}
|
||||
|
||||
impl CircuitBuilder {
|
||||
pub fn new(num_public: usize, num_private: usize) -> (Vec<Var>, Vec<Var>, CircuitBuilder) {
|
||||
(
|
||||
(0..num_public).map(|x| Var::new(1+x)).collect(),
|
||||
(0..num_private).map(|x| Var::new(1+num_public+x)).collect(),
|
||||
CircuitBuilder {
|
||||
public_inputs: num_public,
|
||||
private_inputs: num_private,
|
||||
constraints: Vec::new()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn constrain<C: Constrainable>(&mut self, constraint: C) {
|
||||
self.constraints.push(Box::new(constraint.synthesize(&Bit::constant(true))));
|
||||
}
|
||||
|
||||
pub fn finalize(self) -> Circuit {
|
||||
let mut counter = 1 + self.public_inputs + self.private_inputs;
|
||||
let mut constraints = vec![];
|
||||
let mut witness_map = WitnessMap::new();
|
||||
|
||||
for c in self.constraints.into_iter() {
|
||||
c.walk(&mut counter, &mut constraints, &mut witness_map);
|
||||
}
|
||||
|
||||
let mut cs = ConstraintSystem::new(self.public_inputs, (counter - 1) - self.public_inputs);
|
||||
|
||||
for Constraint(a, b, c) in constraints {
|
||||
let a: Vec<_> = a.into_iter().map(|x| LinearTerm { coeff: x.0, index: x.1.index() }).collect();
|
||||
let b: Vec<_> = b.into_iter().map(|x| LinearTerm { coeff: x.0, index: x.1.index() }).collect();
|
||||
let c: Vec<_> = c.into_iter().map(|x| LinearTerm { coeff: x.0, index: x.1.index() }).collect();
|
||||
|
||||
cs.add_constraint(&a, &b, &c);
|
||||
}
|
||||
|
||||
let kp = Keypair::new(&cs);
|
||||
|
||||
Circuit {
|
||||
public_inputs: self.public_inputs,
|
||||
private_inputs: self.private_inputs,
|
||||
aux_inputs: ((counter - 1) - self.public_inputs) - self.private_inputs,
|
||||
keypair: kp,
|
||||
witness_map: witness_map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
415
src/keccak.rs
415
src/keccak.rs
@ -1,415 +0,0 @@
|
||||
use super::bit::Bit;
|
||||
use std::slice::IterMut;
|
||||
|
||||
const KECCAKF_RNDC: [u64; 24] =
|
||||
[
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
||||
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
||||
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008
|
||||
];
|
||||
|
||||
const KECCAKF_ROTC: [usize; 24] =
|
||||
[
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
|
||||
];
|
||||
|
||||
const KECCAKF_PILN: [usize; 24] =
|
||||
[
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
|
||||
];
|
||||
|
||||
fn keccakf(st: &mut [Byte], rounds: usize)
|
||||
{
|
||||
use std::borrow::Borrow;
|
||||
|
||||
struct State<B: Borrow<Bit>> {
|
||||
bits: Vec<B>
|
||||
}
|
||||
|
||||
impl<'a> State<&'a mut Bit> {
|
||||
fn new(bytes: &'a mut [Byte]) -> State<&'a mut Bit> {
|
||||
State {
|
||||
bits: bytes.iter_mut()
|
||||
.rev() // Endianness
|
||||
.flat_map(|b| b.iter_mut())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, to: State<Bit>) {
|
||||
for (a, b) in self.bits.iter_mut()
|
||||
.zip(to.bits.into_iter()) {
|
||||
**a = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for State<Bit> {
|
||||
fn from(num: u64) -> State<Bit> {
|
||||
fn bit_at(num: u64, i: usize) -> bool {
|
||||
((num << i) >> 63) == 1
|
||||
}
|
||||
|
||||
State {
|
||||
bits: (0..64).map(|i| Bit::constant(bit_at(num, i))).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Borrow<Bit>> State<A> {
|
||||
fn duplicate(&self) -> State<Bit> {
|
||||
State {
|
||||
bits: self.bits.iter().map(|a| a.borrow())
|
||||
.map(|a| (*a).clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn binary_map<F, B>(&self, other: &State<B>, f: F) -> State<Bit>
|
||||
where F: Fn(&Bit, &Bit) -> Bit, B: Borrow<Bit>
|
||||
{
|
||||
State {
|
||||
bits: self.bits.iter().map(|a| a.borrow())
|
||||
.zip(other.bits.iter().map(|a| a.borrow()))
|
||||
.map(|(a, b)| f(a, b))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn xor<B: Borrow<Bit>>(&self, other: &State<B>) -> State<Bit> {
|
||||
self.binary_map(other, Bit::xor)
|
||||
}
|
||||
|
||||
fn notand<B: Borrow<Bit>>(&self, other: &State<B>) -> State<Bit> {
|
||||
self.binary_map(other, Bit::notand)
|
||||
}
|
||||
|
||||
fn rotl(&self, by: usize) -> State<Bit> {
|
||||
let by = by % 64;
|
||||
|
||||
State {
|
||||
bits: self.bits[by..].iter().map(|a| a.borrow())
|
||||
.chain(self.bits[0..by].iter().map(|a| a.borrow()))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut st: Vec<_> = st.chunks_mut(8).map(|c| State::new(c)).collect();
|
||||
|
||||
for round in 0..rounds {
|
||||
/*
|
||||
// Theta
|
||||
for (i = 0; i < 5; i++)
|
||||
bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
|
||||
*/
|
||||
|
||||
let mut bc: Vec<State<Bit>> = (0..5).map(|i| st[i]
|
||||
.xor(&st[i+5])
|
||||
.xor(&st[i+10])
|
||||
.xor(&st[i+15])
|
||||
.xor(&st[i+20])
|
||||
).collect();
|
||||
|
||||
/*
|
||||
for (i = 0; i < 5; i++) {
|
||||
t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
|
||||
for (j = 0; j < 25; j += 5)
|
||||
st[j + i] ^= t;
|
||||
}
|
||||
*/
|
||||
|
||||
for i in 0..5 {
|
||||
let tmp = bc[(i + 4) % 5].xor(&bc[(i + 1) % 5].rotl(1));
|
||||
|
||||
for j in (0..25).filter(|a| a % 5 == 0) {
|
||||
let new = tmp.xor(&st[j + i]);
|
||||
st[j + i].set(new);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
// Rho Pi
|
||||
t = st[1];
|
||||
for (i = 0; i < 24; i++) {
|
||||
j = keccakf_piln[i];
|
||||
bc[0] = st[j];
|
||||
st[j] = ROTL64(t, keccakf_rotc[i]);
|
||||
t = bc[0];
|
||||
}
|
||||
*/
|
||||
let mut tmp = st[1].duplicate();
|
||||
|
||||
for i in 0..24 {
|
||||
let j = KECCAKF_PILN[i];
|
||||
|
||||
bc[0] = st[j].duplicate();
|
||||
st[j].set(tmp.rotl(KECCAKF_ROTC[i]));
|
||||
tmp = bc[0].duplicate();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
// Chi
|
||||
for (j = 0; j < 25; j += 5) {
|
||||
for (i = 0; i < 5; i++)
|
||||
bc[i] = st[j + i];
|
||||
for (i = 0; i < 5; i++)
|
||||
st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
|
||||
}
|
||||
*/
|
||||
|
||||
for j in (0..25).filter(|a| a % 5 == 0) {
|
||||
for i in 0..5 {
|
||||
bc[i] = st[j + i].duplicate();
|
||||
}
|
||||
|
||||
for i in 0..5 {
|
||||
let n = st[j + i].xor(&bc[(i + 1) % 5].notand(&bc[(i + 2) % 5]));
|
||||
st[j + i].set(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Iota
|
||||
st[0] ^= keccakf_rndc[round];
|
||||
*/
|
||||
|
||||
let n = st[0].xor(&KECCAKF_RNDC[round].into());
|
||||
st[0].set(n);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sha3_256(message: &[Byte]) -> Vec<Bit> {
|
||||
// As defined by FIPS202
|
||||
keccak(1088, 512, message, 0x06, 32, 24)
|
||||
}
|
||||
|
||||
fn keccak(rate: usize, capacity: usize, mut input: &[Byte], delimited_suffix: u8, mut mdlen: usize, num_rounds: usize)
|
||||
-> Vec<Bit>
|
||||
{
|
||||
use std::cmp::min;
|
||||
|
||||
let mut st: Vec<Byte> = Some(Byte::new(0)).into_iter().cycle().take(200).collect();
|
||||
|
||||
let rate_in_bytes = rate / 8;
|
||||
let mut input_byte_len = input.len();
|
||||
let mut block_size = 0;
|
||||
|
||||
if ((rate + capacity) != 1600) || ((rate % 8) != 0) {
|
||||
panic!("invalid parameters");
|
||||
}
|
||||
|
||||
while input_byte_len > 0 {
|
||||
block_size = min(input_byte_len, rate_in_bytes);
|
||||
|
||||
for i in 0..block_size {
|
||||
st[i] = st[i].xor(&input[i]);
|
||||
}
|
||||
|
||||
input = &input[block_size..];
|
||||
input_byte_len -= block_size;
|
||||
|
||||
if block_size == rate_in_bytes {
|
||||
keccakf(&mut st, num_rounds);
|
||||
block_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
st[block_size] = st[block_size].xor(&Byte::new(delimited_suffix));
|
||||
|
||||
if ((delimited_suffix & 0x80) != 0) && (block_size == (rate_in_bytes-1)) {
|
||||
keccakf(&mut st, num_rounds);
|
||||
}
|
||||
|
||||
st[rate_in_bytes-1] = st[rate_in_bytes-1].xor(&Byte::new(0x80));
|
||||
|
||||
keccakf(&mut st, num_rounds);
|
||||
|
||||
let mut output = Vec::with_capacity(mdlen);
|
||||
|
||||
while mdlen > 0 {
|
||||
block_size = min(mdlen, rate_in_bytes);
|
||||
output.extend_from_slice(&st[0..block_size]);
|
||||
mdlen -= block_size;
|
||||
|
||||
if mdlen > 0 {
|
||||
keccakf(&mut st, num_rounds);
|
||||
}
|
||||
}
|
||||
|
||||
output.into_iter().flat_map(|byte| byte.bits.into_iter()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha3_256() {
|
||||
use super::circuit::{CircuitBuilder,Equals};
|
||||
use super::variable::Var;
|
||||
use tinysnark::{self,FieldT};
|
||||
|
||||
let test_vector: Vec<(Vec<u8>, [u8; 32])> = vec![
|
||||
(vec![0xff],
|
||||
[0x44,0x4b,0x89,0xec,0xce,0x39,0x5a,0xec,0x5d,0xc9,0x8f,0x19,0xde,0xfd,0x3a,0x23,0xbc,0xa0,0x82,0x2f,0xc7,0x22,0x26,0xf5,0x8c,0xa4,0x6a,0x17,0xee,0xec,0xa4,0x42]
|
||||
),
|
||||
(vec![0x00],
|
||||
[0x5d,0x53,0x46,0x9f,0x20,0xfe,0xf4,0xf8,0xea,0xb5,0x2b,0x88,0x04,0x4e,0xde,0x69,0xc7,0x7a,0x6a,0x68,0xa6,0x07,0x28,0x60,0x9f,0xc4,0xa6,0x5f,0xf5,0x31,0xe7,0xd0]
|
||||
),
|
||||
(vec![0x30, 0x31, 0x30, 0x31],
|
||||
[0xe5,0xbf,0x4a,0xd7,0xda,0x2b,0x4d,0x64,0x0d,0x2b,0x8d,0xd3,0xae,0x9b,0x6e,0x71,0xb3,0x6e,0x0f,0x3d,0xb7,0x6a,0x1e,0xc0,0xad,0x6b,0x87,0x2f,0x3e,0xcc,0x2e,0xbc]
|
||||
),
|
||||
(vec![0x30],
|
||||
[0xf9,0xe2,0xea,0xaa,0x42,0xd9,0xfe,0x9e,0x55,0x8a,0x9b,0x8e,0xf1,0xbf,0x36,0x6f,0x19,0x0a,0xac,0xaa,0x83,0xba,0xd2,0x64,0x1e,0xe1,0x06,0xe9,0x04,0x10,0x96,0xe4]
|
||||
),
|
||||
(vec![0x30,0x30],
|
||||
[0x2e,0x16,0xaa,0xb4,0x83,0xcb,0x95,0x57,0x7c,0x50,0xd3,0x8c,0x8d,0x0d,0x70,0x40,0xf4,0x67,0x26,0x83,0x23,0x84,0x46,0xc9,0x90,0xba,0xbb,0xca,0x5a,0xe1,0x33,0xc8]
|
||||
),
|
||||
((0..64).map(|_| 0x30).collect::<Vec<_>>(),
|
||||
[0xc6,0xfd,0xd7,0xa7,0xf7,0x08,0x62,0xb3,0x6a,0x26,0xcc,0xd1,0x47,0x52,0x26,0x80,0x61,0xe9,0x81,0x03,0x29,0x9b,0x28,0xfe,0x77,0x63,0xbd,0x96,0x29,0x92,0x6f,0x4b]
|
||||
),
|
||||
((0..128).map(|_| 0x30).collect::<Vec<_>>(),
|
||||
[0x99,0x9d,0xb4,0xd4,0x28,0x7b,0x52,0x15,0x20,0x8d,0x11,0xe4,0x0a,0x27,0xca,0x54,0xac,0xa0,0x09,0xb2,0x5c,0x4f,0x7a,0xb9,0x1a,0xd8,0xaa,0x93,0x60,0xf0,0x63,0x71]
|
||||
),
|
||||
((0..256).map(|_| 0x30).collect::<Vec<_>>(),
|
||||
[0x11,0xea,0x74,0x37,0x7b,0x74,0xf1,0x53,0x9f,0x2e,0xd9,0x0a,0xb8,0xca,0x9e,0xb1,0xe0,0x70,0x8a,0x4b,0xfb,0xad,0x4e,0x81,0xcc,0x77,0xd9,0xa1,0x61,0x9a,0x10,0xdb]
|
||||
),
|
||||
((0..512).map(|_| 0x30).collect::<Vec<_>>(),
|
||||
[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::<Vec<_>>(),
|
||||
[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() {
|
||||
let message: Vec<Byte> = message.iter().map(|a| Byte::new(*a)).collect();
|
||||
let result: Vec<u8> = sha3_256(&message)
|
||||
.chunks(8)
|
||||
.map(|a| Byte::from(a))
|
||||
.map(|a| a.unwrap_constant())
|
||||
.collect();
|
||||
|
||||
if &*result != expected {
|
||||
print!("Got: ");
|
||||
for i in result.iter() {
|
||||
print!("0x{:02x},", i);
|
||||
}
|
||||
print!("\nExpected: ");
|
||||
for i in expected.iter() {
|
||||
print!("0x{:02x},", i);
|
||||
}
|
||||
println!("");
|
||||
panic!("Hash {} failed!", i+1);
|
||||
} else {
|
||||
println!("--- HASH {} SUCCESS ---", i+1);
|
||||
}
|
||||
}
|
||||
|
||||
tinysnark::init();
|
||||
|
||||
for (i, &(ref message, ref expected)) in test_vector.iter().enumerate() {
|
||||
fn into_bytes(a: &[Var]) -> Vec<Byte> {
|
||||
let a: Vec<_> = a.into_iter().map(|a| Bit::new(a)).collect();
|
||||
|
||||
a.chunks(8).map(|a| Byte::from(a)).collect()
|
||||
}
|
||||
|
||||
fn into_fieldt(a: &[u8], vars: &mut [FieldT]) {
|
||||
let mut counter = 0;
|
||||
|
||||
for byte in a {
|
||||
for bit in (0..8).map(|i| byte & (1 << i) != 0).rev() {
|
||||
if bit { vars[counter] = FieldT::one() } else { vars[counter] = FieldT::zero() }
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (public, private, mut circuit) = CircuitBuilder::new(expected.len() * 8, message.len() * 8);
|
||||
|
||||
let private = into_bytes(&private);
|
||||
|
||||
circuit.constrain(sha3_256(&private).must_equal(&public));
|
||||
|
||||
let circuit = circuit.finalize();
|
||||
|
||||
let mut input: Vec<FieldT> = (0..message.len() * 8).map(|_| FieldT::zero()).collect();
|
||||
let mut output: Vec<FieldT> = (0..expected.len() * 8).map(|_| FieldT::zero()).collect();
|
||||
|
||||
into_fieldt(message, &mut input);
|
||||
into_fieldt(expected, &mut output);
|
||||
|
||||
let proof = circuit.prove(&output, &input).unwrap();
|
||||
assert!(circuit.verify(&proof, &output));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Byte {
|
||||
bits: Vec<Bit>
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [Bit]> for Byte {
|
||||
fn from(a: &'a [Bit]) -> Byte {
|
||||
assert_eq!(8, a.len());
|
||||
|
||||
Byte {
|
||||
bits: a.to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Byte {
|
||||
pub fn bits(&self) -> Vec<Bit> {
|
||||
self.bits.clone()
|
||||
}
|
||||
|
||||
pub fn new(byte: u8) -> Byte {
|
||||
Byte {
|
||||
bits: (0..8).map(|i| Bit::constant(byte & (1 << i) != 0))
|
||||
.rev()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<Bit> {
|
||||
self.bits.iter_mut()
|
||||
}
|
||||
|
||||
pub fn unwrap_constant(&self) -> u8 {
|
||||
let mut cur = 7;
|
||||
let mut acc = 0;
|
||||
|
||||
for bit in &self.bits {
|
||||
match bit {
|
||||
&Bit::Constant(true) => {
|
||||
acc |= 1 << cur;
|
||||
},
|
||||
&Bit::Constant(false) => {},
|
||||
_ => panic!("Tried to unwrap a constant from a non-constant")
|
||||
}
|
||||
cur -= 1;
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
pub fn xor(&self, other: &Byte) -> Byte {
|
||||
Byte {
|
||||
bits: self.bits.iter()
|
||||
.zip(other.bits.iter())
|
||||
.map(|(a, b)| a.xor(b))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
0
src/lib.rs
Normal file
0
src/lib.rs
Normal file
19
src/main.rs
19
src/main.rs
@ -1,19 +0,0 @@
|
||||
#![feature(iter_arith, btree_range, collections_bound)]
|
||||
|
||||
extern crate tinysnark;
|
||||
extern crate rand;
|
||||
|
||||
use tinysnark::{Proof, Keypair, FieldT, LinearTerm, ConstraintSystem};
|
||||
use variable::*;
|
||||
use circuit::*;
|
||||
use keccak::*;
|
||||
use bit::*;
|
||||
|
||||
mod variable;
|
||||
mod keccak;
|
||||
mod bit;
|
||||
mod circuit;
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
178
src/variable.rs
178
src/variable.rs
@ -1,178 +0,0 @@
|
||||
use tinysnark::FieldT;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::collections::BTreeMap;
|
||||
use super::circuit::ConstraintWalker;
|
||||
|
||||
pub type WitnessMap = BTreeMap<usize, Vec<(Vec<usize>, Vec<usize>, Rc<Fn(&mut VariableView) + 'static>)>>;
|
||||
|
||||
struct VariableView<'a> {
|
||||
vars: &'a mut [FieldT],
|
||||
inputs: &'a [usize],
|
||||
outputs: &'a [usize]
|
||||
}
|
||||
|
||||
impl<'a> VariableView<'a> {
|
||||
/// Sets an output variable at `index` to value `to`.
|
||||
pub fn set_output(&mut self, index: usize, to: FieldT) {
|
||||
self.vars[self.outputs[index]] = to;
|
||||
}
|
||||
|
||||
/// Gets the value of an input variable at `index`.
|
||||
pub fn get_input(&self, index: usize) -> FieldT {
|
||||
self.vars[self.inputs[index]]
|
||||
}
|
||||
|
||||
/// Sets the value of an input variable. This is unsafe
|
||||
/// because theoretically this should not be necessary,
|
||||
/// and could cause soundness problems, but I've temporarily
|
||||
/// done this to make testing easier.
|
||||
pub fn set_input(&mut self, index: usize, to: FieldT) {
|
||||
self.vars[self.inputs[index]] = to;
|
||||
}
|
||||
}
|
||||
|
||||
use std::collections::Bound::Unbounded;
|
||||
|
||||
pub fn witness_field_elements(vars: &mut [FieldT], witness_map: &WitnessMap) {
|
||||
for (n, group) in witness_map.range(Unbounded, Unbounded) {
|
||||
for &(ref i, ref o, ref f) in group.iter() {
|
||||
let mut vars = VariableView {
|
||||
vars: vars,
|
||||
inputs: &*i,
|
||||
outputs: &*o
|
||||
};
|
||||
|
||||
f(&mut vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Constraint(pub Vec<(FieldT, Var)>, pub Vec<(FieldT, Var)>, pub Vec<(FieldT, Var)>);
|
||||
|
||||
struct Gadget {
|
||||
inputs: Vec<Var>,
|
||||
aux: Vec<Var>,
|
||||
witness: Rc<Fn(&mut VariableView) + 'static>,
|
||||
constraints: Vec<Constraint>,
|
||||
group: usize,
|
||||
visited: Cell<bool>
|
||||
}
|
||||
|
||||
impl Gadget {
|
||||
pub fn walk(&self, counter: &mut usize, constraints: &mut Vec<Constraint>, witness_map: &mut WitnessMap) {
|
||||
if self.visited.get() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.visited.set(true);
|
||||
|
||||
for a in &self.aux {
|
||||
assert!(a.index.get() == 0);
|
||||
a.index.set(*counter);
|
||||
*counter += 1;
|
||||
}
|
||||
|
||||
constraints.extend_from_slice(&self.constraints);
|
||||
|
||||
for i in &self.inputs {
|
||||
i.walk(counter, constraints, witness_map);
|
||||
}
|
||||
|
||||
let input_indexes = self.inputs.iter().map(|i| i.index.get()).collect();
|
||||
let output_indexes = self.aux.iter().map(|i| i.index.get()).collect();
|
||||
|
||||
witness_map.entry(self.group)
|
||||
.or_insert_with(|| Vec::new())
|
||||
.push((input_indexes, output_indexes, self.witness.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Var {
|
||||
index: Rc<Cell<usize>>,
|
||||
gadget: Option<Rc<Gadget>>
|
||||
}
|
||||
|
||||
impl Var {
|
||||
// todo: make this not public
|
||||
pub fn new(i: usize) -> Var {
|
||||
Var {
|
||||
index: Rc::new(Cell::new(i)),
|
||||
gadget: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn one() -> Var {
|
||||
Var {
|
||||
index: Rc::new(Cell::new(0)),
|
||||
gadget: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.index.get()
|
||||
}
|
||||
|
||||
pub fn val(&self, map: &[FieldT]) -> FieldT {
|
||||
let index = self.index.get();
|
||||
assert!(index != 0);
|
||||
map[index]
|
||||
}
|
||||
|
||||
fn group(&self) -> usize {
|
||||
match self.gadget {
|
||||
None => 0,
|
||||
Some(ref g) => g.group
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstraintWalker for Var {
|
||||
fn walk(&self, counter: &mut usize, constraints: &mut Vec<Constraint>, witness_map: &mut WitnessMap) {
|
||||
match self.gadget {
|
||||
None => {},
|
||||
Some(ref g) => g.walk(counter, constraints, witness_map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gadget<W, C>(
|
||||
inputs: &[&Var],
|
||||
aux: usize,
|
||||
witness: W,
|
||||
constrain: C
|
||||
) -> Vec<Var>
|
||||
where C: for<'a> Fn(&[&'a Var], &[&'a Var], &mut Vec<Constraint>) -> Vec<&'a Var>,
|
||||
W: Fn(&mut VariableView) + 'static
|
||||
{
|
||||
let this_group = inputs.iter().map(|i| i.group()).max().map(|a| a+1).unwrap_or(0);
|
||||
|
||||
let aux: Vec<_> = (0..aux).map(|_| Var::new(0)).collect();
|
||||
let aux: Vec<_> = aux.iter().collect();
|
||||
|
||||
let mut constraints = vec![];
|
||||
|
||||
let outputs = constrain(inputs, &*aux, &mut constraints);
|
||||
|
||||
let gadget = Rc::new(Gadget {
|
||||
inputs: inputs.iter().map(|a| (*a).clone()).collect(),
|
||||
aux: aux.iter().map(|a| (*a).clone()).collect(),
|
||||
witness: Rc::new(witness),
|
||||
constraints: constraints,
|
||||
group: this_group,
|
||||
visited: Cell::new(false)
|
||||
});
|
||||
|
||||
outputs.into_iter().map(|a| {
|
||||
let mut a = (*a).clone();
|
||||
|
||||
// TODO: we should augment the gadget instead
|
||||
// of replacing it
|
||||
assert!(a.gadget.is_none());
|
||||
|
||||
a.gadget = Some(gadget.clone());
|
||||
a
|
||||
}).collect()
|
||||
}
|
2
tinysnark/.gitignore
vendored
2
tinysnark/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
target
|
||||
Cargo.lock
|
@ -1,16 +0,0 @@
|
||||
[package]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
build = "build.rs"
|
||||
description = "Tiny libsnark bindings"
|
||||
documentation = "https://github.com/ebfull/bellman"
|
||||
homepage = "https://github.com/ebfull/bellman"
|
||||
license = "MIT"
|
||||
name = "tinysnark"
|
||||
repository = "https://github.com/ebfull/bellman"
|
||||
version = "0.0.1"
|
||||
|
||||
[build-dependencies]
|
||||
gcc = "0.3"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.4"
|
@ -1 +0,0 @@
|
||||
This is a tiny wrapper around libsnark's r1cs_ppzksnark on ALT_BN128.
|
@ -1,40 +0,0 @@
|
||||
extern crate gcc;
|
||||
|
||||
fn main() {
|
||||
// we don't need ate-pairing for ALT_BN128, but
|
||||
// i'll keep this in case i need it for some reason...
|
||||
/*
|
||||
let mut cfg = gcc::Config::new();
|
||||
|
||||
cfg.cpp(true)
|
||||
.define("BN_SUPPORT_SNARK", None)
|
||||
.include("ate-pairing/include")
|
||||
.include("xbyak")
|
||||
.file("ate-pairing/src/zm.cpp")
|
||||
.file("ate-pairing/src/zm2.cpp")
|
||||
.compile("libzm.a");
|
||||
*/
|
||||
|
||||
println!("cargo:rustc-link-lib=gmp");
|
||||
println!("cargo:rustc-link-lib=gmpxx");
|
||||
|
||||
let mut cfg = gcc::Config::new();
|
||||
|
||||
cfg.cpp(true)
|
||||
.define("NO_PROCPS", None)
|
||||
.define("STATIC", None)
|
||||
.define("CURVE_ALT_BN128", None)
|
||||
.flag("-std=c++11")
|
||||
.include("libsnark/src")
|
||||
.file("tinysnark.cpp")
|
||||
.file("libsnark/src/algebra/curves/alt_bn128/alt_bn128_g1.cpp")
|
||||
.file("libsnark/src/algebra/curves/alt_bn128/alt_bn128_g2.cpp")
|
||||
.file("libsnark/src/algebra/curves/alt_bn128/alt_bn128_init.cpp")
|
||||
.file("libsnark/src/algebra/curves/alt_bn128/alt_bn128_pairing.cpp")
|
||||
.file("libsnark/src/algebra/curves/alt_bn128/alt_bn128_pp.cpp")
|
||||
.file("libsnark/src/common/utils.cpp")
|
||||
.file("libsnark/src/common/profiling.cpp")
|
||||
;
|
||||
|
||||
cfg.compile("libtinysnark.a");
|
||||
}
|
@ -1 +0,0 @@
|
||||
Subproject commit 0b928a7b36717db6f67ff7e1e34dfa3bfaee1c97
|
@ -1,98 +0,0 @@
|
||||
use libc::{c_ulong, c_long};
|
||||
use std::ops::{Neg, Add, Mul};
|
||||
|
||||
extern "C" {
|
||||
fn tinysnark_fieldt_zero() -> FieldT;
|
||||
fn tinysnark_fieldt_one() -> FieldT;
|
||||
fn tinysnark_fieldt_neg(val: FieldT) -> FieldT;
|
||||
fn tinysnark_fieldt_inverse(val: FieldT) -> FieldT;
|
||||
fn tinysnark_fieldt_from_long(val: c_long) -> FieldT;
|
||||
fn tinysnark_long_from_fieldt(val: FieldT) -> c_ulong;
|
||||
fn tinysnark_fieldt_mul(a: FieldT, b: FieldT) -> FieldT;
|
||||
fn tinysnark_fieldt_add(a: FieldT, b: FieldT) -> FieldT;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(simd)]
|
||||
struct EightBytes(u64);
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct FieldT([u8; 32], [EightBytes; 0]);
|
||||
|
||||
impl FieldT {
|
||||
#[inline(always)]
|
||||
pub fn one() -> FieldT {
|
||||
unsafe { tinysnark_fieldt_one() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn zero() -> FieldT {
|
||||
unsafe { tinysnark_fieldt_zero() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn inverse(self) -> FieldT {
|
||||
unsafe { tinysnark_fieldt_inverse(self) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn debug_equal(&self, is: [u8; 32]) -> bool {
|
||||
&FieldT(is, []) == self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for FieldT {
|
||||
#[inline(always)]
|
||||
fn from(num: i64) -> FieldT {
|
||||
unsafe { tinysnark_fieldt_from_long(num) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FieldT> for u64 {
|
||||
#[inline(always)]
|
||||
fn from(num: FieldT) -> u64 {
|
||||
unsafe { tinysnark_long_from_fieldt(num) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for FieldT {
|
||||
type Output = FieldT;
|
||||
|
||||
#[inline(always)]
|
||||
fn neg(self) -> FieldT {
|
||||
unsafe { tinysnark_fieldt_neg(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for FieldT {
|
||||
type Output = FieldT;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(self, other: FieldT) -> FieldT {
|
||||
unsafe { tinysnark_fieldt_add(self, other) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for FieldT {
|
||||
type Output = FieldT;
|
||||
|
||||
#[inline(always)]
|
||||
fn mul(self, other: FieldT) -> FieldT {
|
||||
unsafe { tinysnark_fieldt_mul(self, other) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FieldT {
|
||||
fn eq(&self, other: &FieldT) -> bool {
|
||||
for i in 0..32 {
|
||||
if (self.0)[i] != (other.0)[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FieldT { }
|
@ -1,156 +0,0 @@
|
||||
#![feature(box_syntax, repr_simd)]
|
||||
#![allow(improper_ctypes)]
|
||||
//#![cfg_attr(test, feature(test))]
|
||||
extern crate libc;
|
||||
|
||||
mod arith;
|
||||
mod r1cs;
|
||||
|
||||
pub use self::arith::*;
|
||||
pub use self::r1cs::*;
|
||||
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
static START: Once = ONCE_INIT;
|
||||
static mut INITIALIZED: bool = false;
|
||||
|
||||
extern "C" {
|
||||
fn tinysnark_init_public_params();
|
||||
pub fn tinysnark_test();
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
START.call_once(|| {
|
||||
unsafe { tinysnark_init_public_params(); }
|
||||
unsafe { INITIALIZED = true; }
|
||||
});
|
||||
}
|
||||
|
||||
pub fn is_initialized() -> bool {
|
||||
unsafe { INITIALIZED }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//extern crate test;
|
||||
use super::{init, FieldT, Proof, Keypair, LinearTerm, ConstraintSystem};
|
||||
//use self::test::Bencher;
|
||||
|
||||
#[test]
|
||||
fn test_zk() {
|
||||
fn test_cs_and_prove<N: Into<FieldT> + Copy>(cs: &ConstraintSystem, primary: &[N], aux: &[N]) -> bool
|
||||
{
|
||||
let primary: Vec<FieldT> = primary.iter().map(|n| (*n).into()).collect();
|
||||
let aux: Vec<FieldT> = aux.iter().map(|n| (*n).into()).collect();
|
||||
|
||||
if !cs.test(&primary, &aux) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let kp = Keypair::new(cs);
|
||||
let proof = Proof::new(&kp, &primary, &aux);
|
||||
// If we construct a proof, it should be impossible
|
||||
// that it doesn't verify.
|
||||
assert!(proof.verify(&kp, &primary));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
init();
|
||||
{
|
||||
let mut cs = ConstraintSystem::new(1, 2);
|
||||
// zkpok { (a, b) c = a * b }
|
||||
cs.add_constraint(
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 2}],
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 3}],
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 1}]
|
||||
);
|
||||
|
||||
assert!(test_cs_and_prove(&cs, &[1], &[1, 1]));
|
||||
assert!(test_cs_and_prove(&cs, &[0], &[0, 1]));
|
||||
assert!(test_cs_and_prove(&cs, &[10], &[5, 2]));
|
||||
assert!(!test_cs_and_prove(&cs, &[10], &[6, 2]));
|
||||
}
|
||||
{
|
||||
let mut cs = ConstraintSystem::new(0, 1);
|
||||
// simple boolean constraint
|
||||
// (1-x) * x = 0
|
||||
cs.add_constraint(
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 0},
|
||||
LinearTerm{coeff: -FieldT::one(), index: 1}],
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 1}],
|
||||
&[LinearTerm{coeff: FieldT::zero(), index: 0}]
|
||||
);
|
||||
|
||||
assert!(test_cs_and_prove(&cs, &[], &[0]));
|
||||
assert!(test_cs_and_prove(&cs, &[], &[1]));
|
||||
assert!(!test_cs_and_prove(&cs, &[], &[2]));
|
||||
}
|
||||
{
|
||||
let mut cs = ConstraintSystem::new(2, 1);
|
||||
// boolean + xor
|
||||
cs.add_constraint(
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 0},
|
||||
LinearTerm{coeff: -FieldT::one(), index: 3}],
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 3}],
|
||||
&[LinearTerm{coeff: FieldT::zero(), index: 0}]
|
||||
);
|
||||
cs.add_constraint(
|
||||
&[LinearTerm{coeff: FieldT::from(2), index: 2}],
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 3}],
|
||||
&[LinearTerm{coeff: FieldT::one(), index: 2},
|
||||
LinearTerm{coeff: FieldT::one(), index: 3},
|
||||
LinearTerm{coeff: -FieldT::one(), index: 1}]
|
||||
);
|
||||
|
||||
assert!(test_cs_and_prove(&cs, &[0, 0], &[0]));
|
||||
assert!(test_cs_and_prove(&cs, &[1, 1], &[0]));
|
||||
assert!(test_cs_and_prove(&cs, &[1, 0], &[1]));
|
||||
assert!(test_cs_and_prove(&cs, &[0, 1], &[1]));
|
||||
assert!(!test_cs_and_prove(&cs, &[0, 1], &[100]));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one() {
|
||||
init();
|
||||
let one = FieldT::one();
|
||||
let negone = -one;
|
||||
let newone = -negone;
|
||||
|
||||
assert!(one == newone);
|
||||
assert!(one != negone);
|
||||
assert!(newone != negone);
|
||||
|
||||
assert_eq!(one, 1.into());
|
||||
assert_eq!(negone, (-1).into());
|
||||
|
||||
assert!(one.debug_equal([251, 255, 255, 79, 28, 52, 150, 172, 41, 205, 96, 159, 149, 118, 252, 54, 46, 70, 121, 120, 111, 163, 110, 102, 47, 223, 7, 154, 193, 119, 10, 14]));
|
||||
assert!(negone.debug_equal([6, 0, 0, 160, 119, 193, 75, 151, 103, 163, 88, 218, 178, 113, 55, 241, 46, 18, 8, 9, 71, 162, 225, 81, 250, 192, 41, 71, 177, 214, 89, 34]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_math() {
|
||||
init();
|
||||
|
||||
assert_eq!(FieldT::one() + 10.into(), 11.into());
|
||||
assert_eq!(FieldT::from(2) + 2.into(), FieldT::from(2) * 2.into());
|
||||
assert_eq!(FieldT::from(2), FieldT::from(-1) + FieldT::one() * 3.into());
|
||||
|
||||
assert_eq!(FieldT::one(), FieldT::from(100) * FieldT::from(100).inverse());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conversions() {
|
||||
init();
|
||||
|
||||
for i in 0..10000 {
|
||||
let num: FieldT = i.into();
|
||||
let back: u64 = num.into();
|
||||
|
||||
assert_eq!(i, back as i64);
|
||||
}
|
||||
|
||||
assert_eq!(u64::from(FieldT::from(-1)), 4891460686036598784);
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
use libc::{size_t};
|
||||
use super::arith::FieldT;
|
||||
|
||||
#[repr(C)]
|
||||
struct R1ConstraintSystem;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LinearTerm {
|
||||
pub coeff: FieldT,
|
||||
pub index: size_t
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn tinysnark_new_r1cs(primary: size_t, aux: size_t) -> *mut R1ConstraintSystem;
|
||||
fn tinysnark_drop_r1cs(cs: *mut R1ConstraintSystem);
|
||||
fn tinysnark_satisfy_test(cs: *mut R1ConstraintSystem, primary: *const FieldT, aux: *const FieldT) -> bool;
|
||||
fn tinysnark_add_constraint(cs: *mut R1ConstraintSystem,
|
||||
a: *const LinearTerm,
|
||||
a_len: size_t,
|
||||
b: *const LinearTerm,
|
||||
b_len: size_t,
|
||||
c: *const LinearTerm,
|
||||
c_len: size_t
|
||||
);
|
||||
}
|
||||
|
||||
pub struct ConstraintSystem {
|
||||
cs: *mut R1ConstraintSystem,
|
||||
primary_size: usize,
|
||||
aux_size: usize
|
||||
}
|
||||
|
||||
impl Drop for ConstraintSystem {
|
||||
fn drop(&mut self) {
|
||||
unsafe { tinysnark_drop_r1cs(self.cs) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstraintSystem {
|
||||
pub fn new(primary_size: usize, aux_size: usize) -> ConstraintSystem {
|
||||
ConstraintSystem {
|
||||
cs: unsafe { tinysnark_new_r1cs(primary_size, aux_size) },
|
||||
primary_size: primary_size,
|
||||
aux_size: aux_size
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_constraint(&mut self, a: &[LinearTerm], b: &[LinearTerm], c: &[LinearTerm])
|
||||
{
|
||||
unsafe {
|
||||
tinysnark_add_constraint(
|
||||
self.cs,
|
||||
a.get_unchecked(0),
|
||||
a.len(),
|
||||
b.get_unchecked(0),
|
||||
b.len(),
|
||||
c.get_unchecked(0),
|
||||
c.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test(&self, primary: &[FieldT], aux: &[FieldT]) -> bool
|
||||
{
|
||||
assert_eq!(primary.len(), self.primary_size);
|
||||
assert_eq!(aux.len(), self.aux_size);
|
||||
|
||||
unsafe {
|
||||
tinysnark_satisfy_test(self.cs, primary.get_unchecked(0), aux.get_unchecked(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct R1CSKeypair;
|
||||
|
||||
pub struct Keypair {
|
||||
kp: *mut R1CSKeypair,
|
||||
primary_size: usize,
|
||||
aux_size: usize
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
pub fn new(constraint_system: &ConstraintSystem) -> Keypair {
|
||||
Keypair {
|
||||
kp: unsafe { tinysnark_gen_keypair(constraint_system.cs) },
|
||||
primary_size: constraint_system.primary_size,
|
||||
aux_size: constraint_system.aux_size
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_satisfied(&self, primary: &[FieldT], aux: &[FieldT]) -> bool {
|
||||
assert_eq!(primary.len(), self.primary_size);
|
||||
assert_eq!(aux.len(), self.aux_size);
|
||||
|
||||
unsafe {
|
||||
tinysnark_keypair_satisfies_test(self.kp, primary.get_unchecked(0), aux.get_unchecked(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Keypair {
|
||||
fn drop(&mut self) {
|
||||
unsafe { tinysnark_drop_keypair(self.kp) }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn tinysnark_gen_keypair(cs: *mut R1ConstraintSystem) -> *mut R1CSKeypair;
|
||||
fn tinysnark_drop_keypair(cs: *mut R1CSKeypair);
|
||||
fn tinysnark_keypair_satisfies_test(kp: *mut R1CSKeypair, primary: *const FieldT, aux: *const FieldT) -> bool;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct R1CSProof;
|
||||
|
||||
pub struct Proof {
|
||||
proof: *mut R1CSProof
|
||||
}
|
||||
|
||||
impl Proof {
|
||||
pub fn new(keypair: &Keypair, primary: &[FieldT], aux: &[FieldT])
|
||||
-> Proof
|
||||
{
|
||||
assert_eq!(primary.len(), keypair.primary_size);
|
||||
assert_eq!(aux.len(), keypair.aux_size);
|
||||
|
||||
unsafe {
|
||||
Proof {
|
||||
proof: tinysnark_gen_proof(keypair.kp, primary.get_unchecked(0), aux.get_unchecked(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify(&self, keypair: &Keypair, primary: &[FieldT]) -> bool {
|
||||
assert_eq!(primary.len(), keypair.primary_size);
|
||||
|
||||
unsafe {
|
||||
tinysnark_verify_proof(self.proof, keypair.kp, primary.get_unchecked(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Proof {
|
||||
fn drop(&mut self) {
|
||||
unsafe { tinysnark_drop_proof(self.proof) }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn tinysnark_gen_proof(keypair: *mut R1CSKeypair,
|
||||
primary: *const FieldT,
|
||||
aux: *const FieldT) -> *mut R1CSProof;
|
||||
fn tinysnark_verify_proof(proof: *mut R1CSProof,
|
||||
keypair: *mut R1CSKeypair,
|
||||
primary: *const FieldT) -> bool;
|
||||
fn tinysnark_drop_proof(proof: *mut R1CSProof);
|
||||
}
|
@ -1,213 +0,0 @@
|
||||
/*
|
||||
This is a wrapper around libsnark which provides basic R1CS
|
||||
zk-SNARK support using the ALT_BN128 curve.
|
||||
*/
|
||||
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.hpp"
|
||||
#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;
|
||||
|
||||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
|
||||
|
||||
struct tinysnark_linear_term {
|
||||
FieldT coeff;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
extern "C" void * tinysnark_gen_proof(void * kp, FieldT* primary, FieldT* aux) {
|
||||
r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>* keypair = static_cast<r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>*>(kp);
|
||||
|
||||
r1cs_primary_input<FieldT> primary_input(primary, primary+(keypair->pk.constraint_system.primary_input_size));
|
||||
r1cs_auxiliary_input<FieldT> aux_input(aux, aux+(keypair->pk.constraint_system.auxiliary_input_size));
|
||||
|
||||
auto proof = new r1cs_ppzksnark_proof<default_r1cs_ppzksnark_pp>(
|
||||
r1cs_ppzksnark_prover<default_r1cs_ppzksnark_pp>(keypair->pk, primary_input, aux_input)
|
||||
);
|
||||
|
||||
return static_cast<void*>(std::move(proof));
|
||||
}
|
||||
|
||||
extern "C" bool tinysnark_verify_proof(void * iproof, void * kp, FieldT* primary) {
|
||||
r1cs_ppzksnark_proof<default_r1cs_ppzksnark_pp>* proof = static_cast<r1cs_ppzksnark_proof<default_r1cs_ppzksnark_pp>*>(iproof);
|
||||
r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>* keypair = static_cast<r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>*>(kp);
|
||||
|
||||
r1cs_primary_input<FieldT> primary_input(primary, primary+(keypair->pk.constraint_system.primary_input_size));
|
||||
|
||||
return r1cs_ppzksnark_verifier_strong_IC<default_r1cs_ppzksnark_pp>(keypair->vk, primary_input, *proof);
|
||||
}
|
||||
|
||||
extern "C" void * tinysnark_drop_proof(void * proof) {
|
||||
r1cs_ppzksnark_proof<default_r1cs_ppzksnark_pp>* p = static_cast<r1cs_ppzksnark_proof<default_r1cs_ppzksnark_pp>*>(proof);
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
extern "C" void * tinysnark_gen_keypair(void * ics) {
|
||||
r1cs_constraint_system<FieldT>* cs = static_cast<r1cs_constraint_system<FieldT>*>(ics);
|
||||
|
||||
auto keypair = new r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>(
|
||||
r1cs_ppzksnark_generator<default_r1cs_ppzksnark_pp>(*cs)
|
||||
);
|
||||
|
||||
return static_cast<void*>(std::move(keypair));
|
||||
}
|
||||
|
||||
extern "C" void * tinysnark_drop_keypair(void * kp) {
|
||||
r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>* k = static_cast<r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>*>(kp);
|
||||
|
||||
delete k;
|
||||
}
|
||||
|
||||
extern "C" void * tinysnark_new_r1cs(size_t primary_size, size_t aux_size) {
|
||||
auto cs = new r1cs_constraint_system<FieldT>();
|
||||
|
||||
cs->primary_input_size = primary_size;
|
||||
cs->auxiliary_input_size = aux_size;
|
||||
|
||||
return static_cast<void*>(std::move(cs));
|
||||
}
|
||||
|
||||
extern "C" void tinysnark_drop_r1cs(void * ics) {
|
||||
r1cs_constraint_system<FieldT>* cs = static_cast<r1cs_constraint_system<FieldT>*>(ics);
|
||||
|
||||
delete cs;
|
||||
}
|
||||
|
||||
extern "C" bool tinysnark_keypair_satisfies_test(void * kp, FieldT* primary, FieldT* aux)
|
||||
{
|
||||
r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>* keypair = static_cast<r1cs_ppzksnark_keypair<default_r1cs_ppzksnark_pp>*>(kp);
|
||||
|
||||
r1cs_constraint_system<FieldT>* cs = &keypair->pk.constraint_system;
|
||||
|
||||
r1cs_primary_input<FieldT> primary_input(primary, primary+(cs->primary_input_size));
|
||||
r1cs_auxiliary_input<FieldT> aux_input(aux, aux+(cs->auxiliary_input_size));
|
||||
|
||||
return cs->is_valid() && cs->is_satisfied(primary_input, aux_input);
|
||||
}
|
||||
|
||||
extern "C" bool tinysnark_satisfy_test(void * ics, FieldT* primary, FieldT* aux) {
|
||||
r1cs_constraint_system<FieldT>* cs = static_cast<r1cs_constraint_system<FieldT>*>(ics);
|
||||
|
||||
r1cs_primary_input<FieldT> primary_input(primary, primary+(cs->primary_input_size));
|
||||
r1cs_auxiliary_input<FieldT> aux_input(aux, aux+(cs->auxiliary_input_size));
|
||||
|
||||
return cs->is_valid() && cs->is_satisfied(primary_input, aux_input);
|
||||
}
|
||||
|
||||
extern "C" void * tinysnark_add_constraint(
|
||||
void * ics,
|
||||
tinysnark_linear_term * a_terms,
|
||||
size_t a_terms_len,
|
||||
tinysnark_linear_term * b_terms,
|
||||
size_t b_terms_len,
|
||||
tinysnark_linear_term * c_terms,
|
||||
size_t c_terms_len
|
||||
) {
|
||||
r1cs_constraint_system<FieldT>* cs = static_cast<r1cs_constraint_system<FieldT>*>(ics);
|
||||
|
||||
std::vector<linear_term<FieldT>> a;
|
||||
std::vector<linear_term<FieldT>> b;
|
||||
std::vector<linear_term<FieldT>> c;
|
||||
|
||||
for (size_t i = 0; i < a_terms_len; i++) {
|
||||
FieldT coeff = a_terms[i].coeff;
|
||||
size_t index = a_terms[i].index;
|
||||
|
||||
a.push_back(linear_term<FieldT>(variable<FieldT>(index), coeff));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < b_terms_len; i++) {
|
||||
FieldT coeff = b_terms[i].coeff;
|
||||
size_t index = b_terms[i].index;
|
||||
|
||||
b.push_back(linear_term<FieldT>(variable<FieldT>(index), coeff));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < c_terms_len; i++) {
|
||||
FieldT coeff = c_terms[i].coeff;
|
||||
size_t index = c_terms[i].index;
|
||||
|
||||
c.push_back(linear_term<FieldT>(variable<FieldT>(index), coeff));
|
||||
}
|
||||
|
||||
linear_combination<FieldT> a_lc(a);
|
||||
linear_combination<FieldT> b_lc(b);
|
||||
linear_combination<FieldT> c_lc(c);
|
||||
|
||||
r1cs_constraint<FieldT> constraint(a_lc, b_lc, c_lc);
|
||||
|
||||
cs->add_constraint(constraint);
|
||||
}
|
||||
|
||||
extern "C" FieldT tinysnark_fieldt_mul(FieldT a, FieldT b) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
extern "C" FieldT tinysnark_fieldt_add(FieldT a, FieldT b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
extern "C" unsigned long tinysnark_long_from_fieldt(FieldT num) {
|
||||
return num.as_bigint().as_ulong();
|
||||
}
|
||||
|
||||
extern "C" FieldT tinysnark_fieldt_from_long(long num) {
|
||||
return FieldT(num);
|
||||
}
|
||||
|
||||
extern "C" FieldT tinysnark_fieldt_one() {
|
||||
return FieldT::one();
|
||||
}
|
||||
|
||||
extern "C" FieldT tinysnark_fieldt_zero() {
|
||||
return FieldT::zero();
|
||||
}
|
||||
|
||||
extern "C" FieldT tinysnark_fieldt_neg(FieldT val) {
|
||||
return -val;
|
||||
}
|
||||
|
||||
extern "C" FieldT tinysnark_fieldt_inverse(FieldT val) {
|
||||
return val.inverse();
|
||||
}
|
||||
|
||||
extern "C" void tinysnark_init_public_params() {
|
||||
default_r1cs_ppzksnark_pp::init_public_params();
|
||||
{
|
||||
auto p = FieldT::one();
|
||||
assert(sizeof(p) == 32);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" void tinysnark_test() {
|
||||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
|
||||
|
||||
protoboard<FieldT> pb;
|
||||
|
||||
auto input_bits = new digest_variable<FieldT>(pb, 512, "input_bits");
|
||||
auto output_bits = new digest_variable<FieldT>(pb, 256, "output_bits");
|
||||
auto input_block = new block_variable<FieldT>(pb, {
|
||||
input_bits->bits
|
||||
}, "input_block");
|
||||
auto IV = SHA256_default_IV(pb);
|
||||
auto sha256 = new sha256_compression_function_gadget<FieldT>(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<FieldT> constraint_system = pb.get_constraint_system();
|
||||
|
||||
cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl;
|
||||
}
|
Loading…
Reference in New Issue
Block a user