Out with the old...

This commit is contained in:
Sean Bowe 2017-01-07 20:30:09 -07:00
parent e24fcfdc5c
commit c506c48c91
19 changed files with 3 additions and 2047 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "tinysnark/libsnark"]
path = tinysnark/libsnark
url = git://github.com/scipr-lab/libsnark.git

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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);
}

View File

@ -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
}
}
}

View File

@ -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
View File

View File

@ -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() {
}

View File

@ -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()
}

View File

@ -1,2 +0,0 @@
target
Cargo.lock

View File

@ -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"

View File

@ -1 +0,0 @@
This is a tiny wrapper around libsnark's r1cs_ppzksnark on ALT_BN128.

View File

@ -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

View File

@ -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 { }

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}