Merge pull request #7 from mileschet/master

Merging lisp v0 to master branch
This commit is contained in:
ada
2021-02-02 11:27:04 +01:00
committed by GitHub
24 changed files with 2658 additions and 717 deletions

View File

@@ -33,6 +33,11 @@ multimap = "0.8.2"
hex = "0.4.2"
num_enum = "0.5.0"
lazy_static = "1.4.0"
itertools = "0.8.0"
fnv = "1.0.6"
regex = "1"
simplelog = "0.7.4"
clap = "3.0.0-beta.1"
failure = "0.1.8"
@@ -55,6 +60,10 @@ http-types = "2.9.0"
async-h1 = "2.3.0"
async-native-tls = "0.3.3"
[[bin]]
name = "lisp"
path = "lisp/lisp.rs"
[[bin]]
name = "zkvm"
path = "src/bin/zkvm.rs"

12
lisp/README.md Normal file
View File

@@ -0,0 +1,12 @@
## zklisp
This is a DSL for ZKVMCircuit from sapvi language.
It uses the mal (lisp) version of rust with some modifications to interact with bellman backend and also sapvi vm.
## run
```
cargo run --bin lisp load new.lisp
```

7
lisp/TODO.md Normal file
View File

@@ -0,0 +1,7 @@
## TODO
- Document the language
- Integrate with zkvm command line
- Integrate with ZKVMCircuit: allocs and constraints
- Added CryptoOperation such double and square to core.rs
- Adapt ZKContract to use lisp to read contract and execute

9
lisp/bits.lisp Normal file
View File

@@ -0,0 +1,9 @@
(def! bit-dec
(fn* [x] (
(def! bits (unpack-bits x 256))
(def! enforce-step-1 (fn* [b] (enforce (add-one-lc0 (sub-lc0 b) (add-lc1 b))))
(map enforce-step-1 bits)
(map (fn* [b] ((add-lc0 b) double-coeff-lc) bits)
(enforce reset-coeff-lc sub-lc0 add-one-lc1)
)))))

489
lisp/core.rs Normal file
View File

@@ -0,0 +1,489 @@
use std::fs::File;
use std::io::Read;
use std::rc::Rc;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::printer::pr_seq;
use crate::reader::read_str;
use crate::types::MalErr::ErrMalVal;
use crate::types::MalVal::{
Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector, ZKScalar,
};
use crate::types::{MalArgs, MalRet, MalVal, _assoc, _dissoc, atom, error, func, hash_map};
use bls12_381;
use ff::PrimeField;
use sapvi::bls_extensions::BlsStringConversion;
use std::ops::{AddAssign, MulAssign, SubAssign};
macro_rules! fn_t_int_int {
($ret:ident, $fn:expr) => {{
|a: MalArgs| match (a[0].clone(), a[1].clone()) {
(Int(a0), Int(a1)) => Ok($ret($fn(a0, a1))),
_ => error("expecting (int,int) args"),
}
}};
}
macro_rules! fn_is_type {
($($ps:pat),*) => {{
|a:MalArgs| { Ok(Bool(match a[0] { $($ps => true,)* _ => false})) }
}};
($p:pat if $e:expr) => {{
|a:MalArgs| { Ok(Bool(match a[0] { $p if $e => true, _ => false})) }
}};
($p:pat if $e:expr,$($ps:pat),*) => {{
|a:MalArgs| { Ok(Bool(match a[0] { $p if $e => true, $($ps => true,)* _ => false})) }
}};
}
macro_rules! fn_str {
($fn:expr) => {{
|a: MalArgs| match a[0].clone() {
Str(a0) => $fn(a0),
_ => error("expecting (str) arg"),
}
}};
}
fn symbol(a: MalArgs) -> MalRet {
match a[0] {
Str(ref s) => Ok(Sym(s.to_string())),
_ => error("illegal symbol call"),
}
}
fn slurp(f: String) -> MalRet {
let mut s = String::new();
match File::open(f).and_then(|mut f| f.read_to_string(&mut s)) {
Ok(_) => Ok(Str(s)),
Err(e) => error(&e.to_string()),
}
}
fn time_ms(_a: MalArgs) -> MalRet {
let ms_e = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => d,
Err(e) => return error(&format!("{:?}", e)),
};
Ok(Int(
ms_e.as_secs() as i64 * 1000 + ms_e.subsec_nanos() as i64 / 1_000_000
))
}
fn get(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Nil, _) => Ok(Nil),
(Hash(ref hm, _), Str(ref s)) => match hm.get(s) {
Some(mv) => Ok(mv.clone()),
None => Ok(Nil),
},
_ => error("illegal get args"),
}
}
fn assoc(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm, _) => _assoc((**hm).clone(), a[1..].to_vec()),
_ => error("assoc on non-Hash Map"),
}
}
fn dissoc(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm, _) => _dissoc((**hm).clone(), a[1..].to_vec()),
_ => error("dissoc on non-Hash Map"),
}
}
fn contains_q(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Hash(ref hm, _), Str(ref s)) => Ok(Bool(hm.contains_key(s))),
_ => error("illegal get args"),
}
}
fn keys(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm, _) => Ok(list!(hm.keys().map(|k| { Str(k.to_string()) }).collect())),
_ => error("keys requires Hash Map"),
}
}
fn vals(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm, _) => Ok(list!(hm.values().map(|v| { v.clone() }).collect())),
_ => error("keys requires Hash Map"),
}
}
fn vec(a: MalArgs) -> MalRet {
match a[0] {
List(ref v, _) | Vector(ref v, _) => Ok(vector!(v.to_vec())),
_ => error("non-seq passed to vec"),
}
}
fn cons(a: MalArgs) -> MalRet {
match a[1].clone() {
List(v, _) | Vector(v, _) => {
let mut new_v = vec![a[0].clone()];
new_v.extend_from_slice(&v);
Ok(list!(new_v.to_vec()))
}
_ => error("cons expects seq as second arg"),
}
}
fn concat(a: MalArgs) -> MalRet {
let mut new_v = vec![];
for seq in a.iter() {
match seq {
List(v, _) | Vector(v, _) => new_v.extend_from_slice(v),
_ => return error("non-seq passed to concat"),
}
}
Ok(list!(new_v.to_vec()))
}
fn nth(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(List(seq, _), Int(idx)) | (Vector(seq, _), Int(idx)) => {
if seq.len() <= idx as usize {
return error("nth: index out of range");
}
Ok(seq[idx as usize].clone())
}
_ => error("invalid args to nth"),
}
}
fn unpack_bits(a: MalArgs) -> MalRet {
let mut result = vec![];
match a[0].clone() {
Str(ref s) => {
let value = bls12_381::Scalar::from_string(s);
for (_, bit) in value.to_le_bits().into_iter().cloned().enumerate() {
match bit {
true => result.push(bls12_381::Scalar::one()),
false => result.push(bls12_381::Scalar::zero()),
}
}
Ok(list!(result
.iter()
.map(|a| Str(std::string::ToString::to_string(&a)[2..].to_string()))
.collect::<Vec<MalVal>>()))
}
_ => error("invalid args to unpack-bits"),
}
}
fn last(a: MalArgs) -> MalRet {
match a[0].clone() {
List(ref seq, _) | Vector(ref seq, _) if seq.len() == 0 => Ok(Nil),
List(ref seq, _) | Vector(ref seq, _) => Ok(seq[seq.len() - 1].clone()),
Nil => Ok(Nil),
_ => error("invalid args to first"),
}
}
fn first(a: MalArgs) -> MalRet {
match a[0].clone() {
List(ref seq, _) | Vector(ref seq, _) if seq.len() == 0 => Ok(Nil),
List(ref seq, _) | Vector(ref seq, _) => Ok(seq[0].clone()),
Nil => Ok(Nil),
_ => error("invalid args to first"),
}
}
fn rest(a: MalArgs) -> MalRet {
match a[0].clone() {
List(ref seq, _) | Vector(ref seq, _) => {
if seq.len() > 1 {
Ok(list!(seq[1..].to_vec()))
} else {
Ok(list![])
}
}
Nil => Ok(list![]),
_ => error("invalid args to first"),
}
}
fn apply(a: MalArgs) -> MalRet {
match a[a.len() - 1] {
List(ref v, _) | Vector(ref v, _) => {
let f = &a[0];
let mut fargs = a[1..a.len() - 1].to_vec();
fargs.extend_from_slice(&v);
f.apply(fargs)
}
_ => error("apply called with non-seq"),
}
}
fn map(a: MalArgs) -> MalRet {
match a[1] {
List(ref v, _) | Vector(ref v, _) => {
let mut res = vec![];
for mv in v.iter() {
res.push(a[0].apply(vec![mv.clone()])?)
}
Ok(list!(res))
}
_ => error("map called with non-seq"),
}
}
fn conj(a: MalArgs) -> MalRet {
match a[0] {
List(ref v, _) => {
let sl = a[1..]
.iter()
.rev()
.map(|a| a.clone())
.collect::<Vec<MalVal>>();
Ok(list!([&sl[..], v].concat()))
}
Vector(ref v, _) => Ok(vector!([v, &a[1..]].concat())),
_ => error("conj: called with non-seq"),
}
}
fn sub_scalar(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Str(a0), Str(a1)) => {
let (mut s0, s1) = (
bls12_381::Scalar::from_string(&a0),
bls12_381::Scalar::from_string(&a1),
);
s0.sub_assign(s1);
Ok(Str(std::string::ToString::to_string(&s0)[2..].to_string()))
}
_ => error("expected (scalar, scalar)"),
}
}
fn mul_scalar(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(ZKScalar(mut a0), ZKScalar(a1)) => {
// let (mut s0, s1) = (Scalar::from_string(&a0), Scalar::from_string(&a1));
a0.mul_assign(a1);
Ok(ZKScalar(a0))
}
_ => error("expected (zkscalar, zkscalar)"),
}
}
fn div_scalar(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Str(a0), Str(a1)) => {
let (s0, s1) = (
bls12_381::Scalar::from_string(&a0),
bls12_381::Scalar::from_string(&a1),
);
let ret = s1.invert().map(|other| *&s0 * other);
Ok(Str(
std::string::ToString::to_string(&ret.unwrap())[2..].to_string()
))
}
_ => error("expected (scalar, scalar)"),
}
}
fn range(a: MalArgs) -> MalRet {
let mut result = vec![];
match (a[0].clone(), a[1].clone()) {
(Int(a0), Int(a1)) => {
for n in a0..a1 {
result.push(n);
}
Ok(list!(result.iter().map(|_a| Nil).collect::<Vec<MalVal>>()))
}
_ => error("expected int int"),
}
}
fn scalar_zero(a: MalArgs) -> MalRet {
Ok(vector![vec![
ZKScalar(bls12_381::Scalar::zero()),
a[0].clone()
]])
}
fn scalar_one(a: MalArgs) -> MalRet {
match a.len() {
0 => Ok(vector![vec![ZKScalar(bls12_381::Scalar::one())]]),
_ => Ok(vector![vec![
ZKScalar(bls12_381::Scalar::one()),
a[0].clone()
]]),
}
}
fn cs_one(_a: MalArgs) -> MalRet {
Ok(vector![vec![Sym("cs::one".to_string())]])
}
fn negate_from(a: MalArgs) -> MalRet {
match a[0].clone() {
ZKScalar(a0) => Ok(ZKScalar(a0.neg())),
_ => match a[0].apply(vec![])? {
List(v, _) | Vector(v, _) => match v[0] {
ZKScalar(val) => Ok(vector![vec![ZKScalar(val.neg())]]),
_ => error("not scalar"),
},
_ => return error("non zkscalar passed to negate"),
},
}
}
fn scalar_from(a: MalArgs) -> MalRet {
match a[0].clone() {
Str(a0) => {
let s0 = bls12_381::Scalar::from_string(&a0.to_string());
Ok(ZKScalar(s0))
}
Int(a0) => {
println!("{:?}", a0);
let s0 = bls12_381::Scalar::from(a0 as u64);
Ok(ZKScalar(s0))
}
_ => error("expected (string or int)"),
}
}
fn add_scalar(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Str(a0), Str(a1)) => {
let (mut s0, s1) = (
bls12_381::Scalar::from_string(&a0),
bls12_381::Scalar::from_string(&a1),
);
s0.add_assign(s1);
Ok(Str(std::string::ToString::to_string(&s0)[2..].to_string()))
}
_ => error("expected (scalar, scalar"),
}
}
fn seq(a: MalArgs) -> MalRet {
match a[0] {
List(ref v, _) | Vector(ref v, _) if v.len() == 0 => Ok(Nil),
List(ref v, _) | Vector(ref v, _) => Ok(list!(v.to_vec())),
Str(ref s) if s.len() == 0 => Ok(Nil),
Str(ref s) if !a[0].keyword_q() => {
Ok(list!(s.chars().map(|c| { Str(c.to_string()) }).collect()))
}
Nil => Ok(Nil),
_ => error("seq: called with non-seq"),
}
}
pub fn ns() -> Vec<(&'static str, MalVal)> {
vec![
("=", func(|a| Ok(Bool(a[0] == a[1])))),
("throw", func(|a| Err(ErrMalVal(a[0].clone())))),
("nil?", func(fn_is_type!(Nil))),
("true?", func(fn_is_type!(Bool(true)))),
("false?", func(fn_is_type!(Bool(false)))),
("symbol", func(symbol)),
("symbol?", func(fn_is_type!(Sym(_)))),
(
"string?",
func(fn_is_type!(Str(ref s) if !s.starts_with("\u{29e}"))),
),
("keyword", func(|a| a[0].keyword())),
(
"keyword?",
func(fn_is_type!(Str(ref s) if s.starts_with("\u{29e}"))),
),
("number?", func(fn_is_type!(Int(_)))),
(
"fn?",
func(fn_is_type!(MalFunc{is_macro,..} if !is_macro,Func(_,_))),
),
(
"macro?",
func(fn_is_type!(MalFunc{is_macro,..} if is_macro)),
),
("pr-str", func(|a| Ok(Str(pr_seq(&a, true, "", "", " "))))),
("str", func(|a| Ok(Str(pr_seq(&a, false, "", "", ""))))),
(
"prn",
func(|a| {
println!("{}", pr_seq(&a, true, "", "", " "));
Ok(Nil)
}),
),
(
"println",
func(|a| {
println!("{}", pr_seq(&a, false, "", "", " "));
Ok(Nil)
}),
),
("read-string", func(fn_str!(|s| { read_str(s) }))),
("slurp", func(fn_str!(|f| { slurp(f) }))),
("<", func(fn_t_int_int!(Bool, |i, j| { i < j }))),
("<=", func(fn_t_int_int!(Bool, |i, j| { i <= j }))),
(">", func(fn_t_int_int!(Bool, |i, j| { i > j }))),
(">=", func(fn_t_int_int!(Bool, |i, j| { i >= j }))),
("+", func(add_scalar)),
("-", func(sub_scalar)),
("*", func(mul_scalar)),
("/", func(div_scalar)),
("time-ms", func(time_ms)),
("i+", func(fn_t_int_int!(Int, |i, j| { i + j }))),
("i-", func(fn_t_int_int!(Int, |i, j| { i - j }))),
("i*", func(fn_t_int_int!(Int, |i, j| { i * j }))),
("i/", func(fn_t_int_int!(Int, |i, j| { i / j }))),
("i<", func(fn_t_int_int!(Bool, |i, j| { i < j }))),
("i<=", func(fn_t_int_int!(Bool, |i, j| { i <= j }))),
("i>", func(fn_t_int_int!(Bool, |i, j| { i > j }))),
("i>=", func(fn_t_int_int!(Bool, |i, j| { i >= j }))),
("time-ms", func(time_ms)),
("sequential?", func(fn_is_type!(List(_, _), Vector(_, _)))),
("list", func(|a| Ok(list!(a)))),
("list?", func(fn_is_type!(List(_, _)))),
("vector", func(|a| Ok(vector!(a)))),
("vector?", func(fn_is_type!(Vector(_, _)))),
("hash-map", func(|a| hash_map(a))),
("map?", func(fn_is_type!(Hash(_, _)))),
("assoc", func(assoc)),
("dissoc", func(dissoc)),
("get", func(get)),
("contains?", func(contains_q)),
("keys", func(keys)),
("vals", func(vals)),
("vec", func(vec)),
("cons", func(cons)),
("concat", func(concat)),
("empty?", func(|a| a[0].empty_q())),
("nth", func(nth)),
("first", func(first)),
("last", func(last)),
("rest", func(rest)),
("count", func(|a| a[0].count())),
("apply", func(apply)),
("map", func(map)),
("conj", func(conj)),
("seq", func(seq)),
("meta", func(|a| a[0].get_meta())),
("with-meta", func(|a| a[0].clone().with_meta(&a[1]))),
("atom", func(|a| Ok(atom(&a[0])))),
("atom?", func(fn_is_type!(Atom(_)))),
("deref", func(|a| a[0].deref())),
("reset!", func(|a| a[0].reset_bang(&a[1]))),
("swap!", func(|a| a[0].swap_bang(&a[1..].to_vec()))),
("unpack-bits", func(unpack_bits)),
("range", func(range)),
("scalar::one", func(scalar_one)),
("neg", func(negate_from)),
("scalar::zero", func(scalar_zero)),
("scalar", func(scalar_from)),
("cs::one", func(cs_one)),
]
}

85
lisp/env.rs Normal file
View File

@@ -0,0 +1,85 @@
use std::cell::RefCell;
use std::rc::Rc;
//use std::collections::HashMap;
use fnv::FnvHashMap;
use crate::types::MalErr::ErrString;
use crate::types::MalVal::{List, Nil, Sym, Vector};
use crate::types::{error, MalErr, MalRet, MalVal};
#[derive(Debug)]
pub struct EnvStruct {
data: RefCell<FnvHashMap<String, MalVal>>,
pub outer: Option<Env>,
}
pub type Env = Rc<EnvStruct>;
// TODO: it would be nice to use impl here but it doesn't work on
// a deftype (i.e. Env)
pub fn env_new(outer: Option<Env>) -> Env {
Rc::new(EnvStruct {
data: RefCell::new(FnvHashMap::default()),
outer: outer,
})
}
// TODO: mbinds and exprs as & types
pub fn env_bind(outer: Option<Env>, mbinds: MalVal, exprs: Vec<MalVal>) -> Result<Env, MalErr> {
let env = env_new(outer);
match mbinds {
List(binds, _) | Vector(binds, _) => {
for (i, b) in binds.iter().enumerate() {
match b {
Sym(s) if s == "&" => {
env_set(&env, binds[i + 1].clone(), list!(exprs[i..].to_vec()))?;
break;
}
_ => {
env_set(&env, b.clone(), exprs[i].clone())?;
}
}
}
Ok(env)
}
_ => Err(ErrString("env_bind binds not List/Vector".to_string())),
}
}
pub fn env_find(env: &Env, key: &str) -> Option<Env> {
match (env.data.borrow().contains_key(key), env.outer.clone()) {
(true, _) => Some(env.clone()),
(false, Some(o)) => env_find(&o, key),
_ => None,
}
}
pub fn env_get(env: &Env, key: &MalVal) -> MalRet {
match key {
Sym(ref s) => match env_find(env, s) {
Some(e) => Ok(e
.data
.borrow()
.get(s)
.ok_or(ErrString(format!("'{}' not found", s)))?
.clone()),
_ => error(&format!("'{}' not found", s)),
},
_ => error("Env.get called with non-Str"),
}
}
pub fn env_set(env: &Env, key: MalVal, val: MalVal) -> MalRet {
match key {
Sym(ref s) => {
env.data.borrow_mut().insert(s.to_string(), val.clone());
Ok(val)
}
_ => error("Env.set called with non-Str"),
}
}
pub fn env_sets(env: &Env, key: &str, val: MalVal) {
env.data.borrow_mut().insert(key.to_string(), val);
}

70
lisp/jubjub.lisp Normal file
View File

@@ -0,0 +1,70 @@
;; public params
(def! a_u "15a36d1f0f390d8852a35a8c1908dd87a361ee3fd48fdf77b9819dc82d90607e")
(def! a_v "015d8c7f5b43fe33f7891142c001d9251f3abeeb98fad3e87b0dc53c4ebf1891")
(def! b_u "15a36d1f0f390d8852a35a8c1908dd87a361ee3fd48fdf77b9819dc82d90607e")
(def! b_v "015d8c7f5b43fe33f7891142c001d9251f3abeeb98fad3e87b0dc53c4ebf1891")
(def! a "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000")
(def! d "2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1")
(def! one "0000000000000000000000000000000000000000000000000000000000000001")
(defzk! circuit ())
;; U should be evaluated just once
(def! U (fn* [x1 y1 x2 y2] (* (+ x1 y1) (+ x2 y2))))
(def! A (fn* [x1 y2] (* y2 x1)))
(def! B (fn* [y1 x2] (* x2 y1)))
(def! C (fn* [x1 y1 x2 y2] (* d (A x1 y2) (B y1 x2))))
(def! P.x (fn* [x1 y1 x2 y2] (/ (+ (A x1 y2) (B y1 x2)) (+ one (C x1 y1 x2 y2)))))
(def! P.y (fn* [x1 y1 x2 y2] (/ (- (U x1 y1 x2 y2) (A x1 y2) (B y1 x2)) (+ one (C x1 y1 x2 y2)))))
;; lc0 = bellman::LinearCombination::<Scalar>::zero();
;; (lc0-args LinearCombination<Scalar>)
;; (cs! circuit (lc0-args) (lc1-args) (lc2-args))
;; (lc-add-coeff 1 1)
(def! jubjub-add (fn* [x1 y1 x2 y2] (cs! circuit (
(add lc0 x1)
(add lc0 y1)
(add lc1 x2)
(add lc1 y2)
(add lc2 (U x1 y1 x2 y2))
enforce
;; Compute P.x = (A + B) / (1 + C)
(add-one lc0)
(add lc0 (C x1 y1 x2 y2))
(add lc1 (P.x x1 y1 x2 y2))
(add lc1 (A x1 y2))
(add lc1 (B y1 x2))
enforce
;; Compute P.y = (U - A - B) / (1 - C)
(add-one lc0)
(sub lc0 (C x1 y1 x2 y2))
(add lc1 (P.y x1 y1 x2 y2))
(add lc2 (U x1 y1 x2 y2))
(sub lc2 (A x1 y2))
(sub lc2 (B y1 x2))
enforce
))))
(def! circuit (jubjub-add a_u a_v b_u b_v))
;;(println circuit)
(def! circuit (cs! circuit (
(public (P.x a_u a_v b_u b_v))
(public (P.y a_u a_v b_u b_v))
(add lc0 (P.x a_u a_v b_u b_v))
(add-one lc1)
(add lc2 (P.x a_u a_v b_u b_v))
enforce
(add lc0 (P.y a_u a_v b_u b_v))
(add-one lc1)
(add lc2 (P.y a_u a_v b_u b_v))
enforce
)))
;;(println circuit)
;; contract exection
(def! circuit (cs! circuit (
(params [a_u a_v b_u b_v])
)))
(println circuit)

BIN
lisp/lisp-cheat-sheet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

624
lisp/lisp.rs Normal file
View File

@@ -0,0 +1,624 @@
#![allow(non_snake_case)]
use crate::types::LispCircuit;
use bellman::groth16::PreparedVerifyingKey;
use simplelog::*;
use bellman::{groth16};
use bls12_381::Bls12;
use fnv::FnvHashMap;
use itertools::Itertools;
use rand::rngs::OsRng;
use std::time::Instant;
use std::{rc::Rc};
use types::EnforceAllocation;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate fnv;
extern crate itertools;
extern crate regex;
#[macro_use]
mod types;
use crate::types::MalErr::{ErrMalVal, ErrString};
use crate::types::MalVal::{Bool, Enforce, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
mod printer;
mod reader;
use crate::env::{env_bind, env_find, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
pub const ZK_CIRCUIT_ENV_KEY: &str = "ZKC";
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
}
// eval
fn qq_iter(elts: &MalArgs) -> MalVal {
let mut acc = list![];
for elt in elts.iter().rev() {
if let List(v, _) = elt {
if v.len() == 2 {
if let Sym(ref s) = v[0] {
if s == "splice-unquote" {
acc = list![Sym("concat".to_string()), v[1].clone(), acc];
continue;
}
}
}
}
acc = list![Sym("cons".to_string()), quasiquote(&elt), acc];
}
return acc;
}
fn quasiquote(ast: &MalVal) -> MalVal {
match ast {
List(v, _) => {
if v.len() == 2 {
if let Sym(ref s) = v[0] {
if s == "unquote" {
return v[1].clone();
}
}
}
return qq_iter(&v);
}
Vector(v, _) => return list![Sym("vec".to_string()), qq_iter(&v)],
Hash(_, _) | Sym(_) => return list![Sym("quote".to_string()), ast.clone()],
_ => ast.clone(),
}
}
fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal, MalArgs)> {
match ast {
List(v, _) => match v[0] {
Sym(ref s) => match env_find(env, s) {
Some(e) => match env_get(&e, &v[0]) {
Ok(f @ MalFunc { is_macro: true, .. }) => Some((f, v[1..].to_vec())),
_ => None,
},
_ => None,
},
_ => None,
},
_ => None,
}
}
fn macroexpand(mut ast: MalVal, env: &Env) -> (bool, MalRet) {
let mut was_expanded = false;
while let Some((mf, args)) = is_macro_call(&ast, env) {
//println!("macroexpand 1: {:?}", ast);
ast = match mf.apply(args) {
Err(e) => return (false, Err(e)),
Ok(a) => a,
};
//println!("macroexpand 2: {:?}", ast);
was_expanded = true;
}
(was_expanded, Ok(ast))
}
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
List(v, _) => {
let mut lst: MalArgs = vec![];
for a in v.iter() {
lst.push(eval(a.clone(), env.clone())?)
}
Ok(list!(lst))
}
Vector(v, _) => {
let mut lst: MalArgs = vec![];
for a in v.iter() {
lst.push(eval(a.clone(), env.clone())?)
}
Ok(vector!(lst))
}
Hash(hm, _) => {
let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();
for (k, v) in hm.iter() {
new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
}
Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))
}
_ => Ok(ast.clone()),
}
}
fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
let ret: MalRet;
'tco: loop {
ret = match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
match macroexpand(ast.clone(), &env) {
(true, Ok(new_ast)) => {
ast = new_ast;
continue 'tco;
}
(_, Err(e)) => return Err(e),
_ => (),
}
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&env,
b.clone(),
eval(e.clone(), env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()),
Sym(ref a0sym) if a0sym == "quasiquoteexpand" => Ok(quasiquote(&l[1])),
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
}
Sym(ref a0sym) if a0sym == "defmacro!" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
let r = eval(a2, env.clone())?;
match r {
MalFunc {
eval,
ast,
env,
params,
..
} => Ok(env_set(
&env,
a1.clone(),
MalFunc {
eval: eval,
ast: ast.clone(),
env: env.clone(),
params: params.clone(),
is_macro: true,
meta: Rc::new(Nil),
},
)?),
_ => error("set_macro on non-function"),
}
}
Sym(ref a0sym) if a0sym == "macroexpand" => {
match macroexpand(l[1].clone(), &env) {
(_, Ok(new_ast)) => Ok(new_ast),
(_, e) => return e,
}
}
Sym(ref a0sym) if a0sym == "try*" => match eval(l[1].clone(), env.clone()) {
Err(ref e) if l.len() >= 3 => {
let exc = match e {
ErrMalVal(mv) => mv.clone(),
ErrString(s) => Str(s.to_string()),
};
match l[2].clone() {
List(c, _) => {
let catch_env = env_bind(
Some(env.clone()),
list!(vec![c[1].clone()]),
vec![exc],
)?;
eval(c[2].clone(), catch_env)
}
_ => error("invalid catch block"),
}
}
res => res,
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
List(_, _) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
}
_ => error("invalid do form"),
}
}
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
}
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
}
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
}
Sym(ref a0sym) if a0sym == "setup" => {
let a1 = l[1].clone();
// todo
ast = eval(a1.clone(), env.clone())?;
let _pvk = setup(a1.clone(), env.clone())?;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "prove" => {
let a1 = l[1].clone();
ast = eval(a1.clone(), env.clone())?;
prove(a1.clone(), env.clone())
}
Sym(ref a0sym) if a0sym == "alloc-input" => {
let a1 = l[1].clone();
let value = eval(l[2].clone(), env.clone())?;
let result = eval(value.clone(), env.clone())?;
let allocs = get_allocations(&env, "AllocationsInput");
let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();
for (k, v) in allocs.iter() {
new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
}
new_hm.insert(a1.pr_str(false), result);
env_set(
&env,
Sym("AllocationsInput".to_string()),
Hash(Rc::new(new_hm), Rc::new(Nil)),
)?;
Ok(Nil)
}
Sym(ref a0sym) if a0sym == "alloc" => {
let a1 = l[1].clone();
let value = eval(l[2].clone(), env.clone())?;
let result = eval(value.clone(), env.clone())?;
let allocs = get_allocations(&env, "Allocations");
let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();
for (k, v) in allocs.iter() {
new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
}
new_hm.insert(a1.pr_str(false), result);
env_set(
&env,
Sym("Allocations".to_string()),
Hash(Rc::new(new_hm), Rc::new(Nil)),
)?;
Ok(Nil)
}
//Sym(ref a0sym) if a0sym == "verify" => {
Sym(ref a0sym) if a0sym == "enforce" => {
// here i'm considering that we always have tuple with only two elements
// also it's important to keep in mind for the sake of brevity of this v0
// we will not allow calculation or any lisp evaluations inside the enforce
// it means that every symbol will be on allocations and we will do the
// find/replace on the bellman circuit, it's nasty v0
let mut left_vec = vec![];
let mut right_vec = vec![];
let mut out_vec = vec![];
// todo extract a macro for this
match l[1].clone() {
List(v, _) | Vector(v, _) => {
if let List(_, _) = &v.to_vec()[0] {
for ele in v.to_vec().iter() {
if let List(ele_vec, _) = ele {
left_vec.push((
ele_vec[0].pr_str(false),
ele_vec[1].pr_str(false),
));
}
}
} else {
left_vec.push((v[0].pr_str(false), v[1].pr_str(false)));
}
}
_ => {}
};
match l[2].clone() {
List(v, _) | Vector(v, _) => {
if let List(_, _) = &v.to_vec()[0] {
for ele in v.to_vec().iter() {
if let List(ele_vec, _) = ele {
right_vec.push((
ele_vec[0].pr_str(false),
ele_vec[1].pr_str(false),
));
}
}
} else {
right_vec.push((v[0].pr_str(false), v[1].pr_str(false)));
}
}
_ => {}
};
match l[3].clone() {
List(v, _) | Vector(v, _) => {
if let List(_, _) = &v.to_vec()[0] {
for ele in v.to_vec().iter() {
if let List(ele_vec, _) = ele {
out_vec.push((
ele_vec[0].pr_str(false),
ele_vec[1].pr_str(false),
));
}
}
} else {
out_vec.push((v[0].pr_str(false), v[1].pr_str(false)));
}
}
_ => {}
};
let enforce = EnforceAllocation {
left: left_vec,
right: right_vec,
output: out_vec,
};
let mut new_vec: Vec<EnforceAllocation> = vec![enforce];
for value in get_enforce_allocs(&env).iter() {
new_vec.push(value.clone());
}
env_set(
&env,
Sym("AllocationsEnforce".to_string()),
vector![vec![Enforce(Rc::new(new_vec))]],
);
/*
println!("\n\nallocations {:?}", get_allocations(&env, "Allocations"));
println!(
"\n\nallocations input {:?}",
get_allocations(&env, "AllocationsInput")
);
println!("\n\nallocations enforce {:?}", get_enforce_allocs(&env));
*/
Ok(vector![vec![]])
}
_ => match eval_ast(&ast, &env)? {
List(ref el, _) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_, _) => f.apply(args),
MalFunc {
ast: mast,
env: menv,
params,
..
} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
}
_ => {
Ok(vector![el.to_vec()])
//error("call non-function")
}
}
}
_ => error("expected a list"),
},
}
}
_ => eval_ast(&ast, &env),
};
break;
} // end 'tco loop
ret
}
pub fn get_enforce_allocs(env: &Env) -> Vec<EnforceAllocation> {
// todo need some cleanup
match env_find(env, "AllocationsEnforce") {
Some(e) => match env_get(&e, &Sym("AllocationsEnforce".to_string())) {
Ok(f) => {
if let Vector(val, _) = f {
if let Enforce(ret) = &val[0] {
ret.to_vec()
} else {
vec![]
}
} else {
vec![]
}
}
_ => vec![],
},
_ => vec![],
}
}
pub fn get_allocations(env: &Env, key: &str) -> Rc<FnvHashMap<String, MalVal>> {
let alloc_hm: Rc<FnvHashMap<String, MalVal>> = Rc::new(FnvHashMap::default());
match env_find(env, key) {
Some(e) => match env_get(&e, &Sym(key.to_string())) {
Ok(f) => {
if let Hash(allocs, _) = f {
allocs
} else {
alloc_hm
}
}
_ => alloc_hm,
},
_ => alloc_hm,
}
}
pub fn setup(_ast: MalVal, env: Env) -> Result<PreparedVerifyingKey<Bls12>, MalErr> {
let start = Instant::now();
// Create parameters for our circuit. In a production deployment these would
// be generated securely using a multiparty computation.
let allocs_input = get_allocations(&env, "AllocationsInput");
let allocs = get_allocations(&env, "Allocations");
let enforce_allocs = get_enforce_allocs(&env);
let c = LispCircuit {
params: vec![],
allocs: allocs.as_ref().clone(),
alloc_inputs: allocs_input.as_ref().clone(),
constraints: enforce_allocs,
env: env.clone(),
};
// TODO move to another fn
let random_parameters =
groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap();
let pvk = groth16::prepare_verifying_key(&random_parameters.vk);
println!("Setup: [{:?}]", start.elapsed());
Ok(pvk)
}
pub fn prove(_ast: MalVal, env: Env) -> MalRet {
// TODO remove it
let _quantity = bls12_381::Scalar::from(3);
let allocs_input = get_allocations(&env, "AllocationsInput");
let allocs = get_allocations(&env, "Allocations");
let enforce_allocs = get_enforce_allocs(&env);
let circuit = LispCircuit {
params: vec![],
allocs: allocs.as_ref().clone(),
alloc_inputs: allocs_input.as_ref().clone(),
constraints: enforce_allocs,
env: env.clone(),
};
// Create an instance of our circuit (with the preimage as a witness).
// todo check if circuit.clone is valid
let params = {
let c = circuit.clone();
groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
};
let start = Instant::now();
// Create a Groth16 proof with our parameters.
let _proof = groth16::create_random_proof(circuit, &params, &mut OsRng).unwrap();
println!("Prove: [{:?}]", start.elapsed());
Ok(MalVal::Nil)
}
pub fn verify(_ast: &MalVal) -> MalRet {
let _public_input = vec![bls12_381::Scalar::from(27)];
let start = Instant::now();
// Check the proof!
//assert!(groth16::verify_proof(&pvk, &proof, &public_input).is_ok());
println!("Verify: [{:?}]", start.elapsed());
Ok(MalVal::Nil)
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
}
fn rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
fn main() -> Result<(), ()> {
let matches = clap_app!(zklisp =>
(version: "0.1.0")
(author: "mileschet <miles.chet@gmail.com>")
(about: "A Lisp Interpreter for Zero Knowledge Virtual Machine")
(@subcommand load =>
(about: "Load the file into the interpreter")
(@arg FILE: +required "Lisp Contract filename")
)
)
.get_matches();
CombinedLogger::init(vec![TermLogger::new(
LevelFilter::Debug,
Config::default(),
TerminalMode::Mixed,
)
.unwrap()])
.unwrap();
match matches.subcommand() {
Some(("load", matches)) => {
let file: String = matches.value_of("FILE").unwrap().parse().unwrap();
repl_load(file)?;
}
_ => {
eprintln!("error: Invalid subcommand invoked");
std::process::exit(-1);
}
}
Ok(())
}
fn repl_load(file: String) -> Result<(), ()> {
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep(
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
&repl_env,
);
//let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
match rep(&format!("(load-file \"{}\")", file), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
}
}

38
lisp/new-cs.lisp Normal file
View File

@@ -0,0 +1,38 @@
(println "new-cs.lisp")
( (let* [aux (scalar 3)
x (alloc "x" aux)
x2 (alloc "x2" (* aux aux))
x3 (alloc "x3" (* aux (* aux aux)))
input (alloc-input "input" aux)
]
(prove
(setup
(
(enforce
(
(scalar::one x)
(scalar::one x2)
)
;;(scalar::one::neg x)
(scalar::one x)
(scalar::one x2)
)
(enforce
(scalar::one x2)
(scalar::one x)
(scalar::one x3)
)
(enforce
(scalar::one input)
(scalar::one cs::one)
(scalar::one x3)
)
)
)
)
)
)
;; (println 'verify (MyCircuit (scalar 27)))

17
lisp/new.lisp Normal file
View File

@@ -0,0 +1,17 @@
(def! x "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000")
(def! one "0000000000000000000000000000000000000000000000000000000000000001")
(def! bits (unpack-bits x))
(defzk! circuit ())
(def! cvalues (map (fn* [b] (eval
(add lc0 one)
(sub lc0 b)
(add lc1 x)
enforce)
) bits))
(def! cs (concat cvalues (list
'reset-coeff-lc
(sub lc0 x)
(add lc1 one)
'enforce)))
(println "bit-dec")
(cs! circuit cs)

63
lisp/printer.rs Normal file
View File

@@ -0,0 +1,63 @@
use crate::types::MalVal;
use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector};
fn escape_str(s: &str) -> String {
s.chars()
.map(|c| match c {
'"' => "\\\"".to_string(),
'\n' => "\\n".to_string(),
'\\' => "\\\\".to_string(),
_ => c.to_string(),
})
.collect::<Vec<String>>()
.join("")
}
impl MalVal {
pub fn pr_str(&self, print_readably: bool) -> String {
match self {
Nil => String::from("nil"),
Bool(true) => String::from("true"),
Bool(false) => String::from("false"),
Int(i) => format!("{}", i),
//Float(f) => format!("{}", f),
Str(s) => {
if s.starts_with("\u{29e}") {
format!(":{}", &s[2..])
} else if print_readably {
format!("\"{}\"", escape_str(s))
} else {
s.clone()
}
}
Sym(s) => s.clone(),
List(l, _) => pr_seq(&**l, print_readably, "(", ")", " "),
Vector(l, _) => pr_seq(&**l, print_readably, "[", "]", " "),
Hash(hm, _) => {
let l: Vec<MalVal> = hm
.iter()
.flat_map(|(k, v)| vec![Str(k.to_string()), v.clone()])
.collect();
pr_seq(&l, print_readably, "{", "}", " ")
}
Func(f, _) => format!("#<fn {:?}>", f),
MalFunc {
ast: a, params: p, ..
} => format!("(fn* {} {})", p.pr_str(true), a.pr_str(true)),
Atom(a) => format!("(atom {})", a.borrow().pr_str(true)),
MalVal::ZKScalar(a) => format!("{:?}", a),
i => format!("{:?}", i.pr_str(true)),
}
}
}
pub fn pr_seq(
seq: &Vec<MalVal>,
print_readably: bool,
start: &str,
end: &str,
join: &str,
) -> String {
let strs: Vec<String> = seq.iter().map(|x| x.pr_str(print_readably)).collect();
format!("{}{}{}", start, strs.join(join), end)
}

156
lisp/reader.rs Normal file
View File

@@ -0,0 +1,156 @@
use regex::{Captures, Regex};
use std::rc::Rc;
use crate::types::MalErr::ErrString;
use crate::types::MalVal::{Bool, Int, List, Nil, Str, Sym, Vector};
use crate::types::{error, hash_map, MalErr, MalRet, MalVal};
#[derive(Debug, Clone)]
struct Reader {
tokens: Vec<String>,
pos: usize,
}
impl Reader {
fn next(&mut self) -> Result<String, MalErr> {
self.pos = self.pos + 1;
Ok(self
.tokens
.get(self.pos - 1)
.ok_or(ErrString("underflow".to_string()))?
.to_string())
}
fn peek(&self) -> Result<String, MalErr> {
Ok(self
.tokens
.get(self.pos)
.ok_or(ErrString("underflow".to_string()))?
.to_string())
}
}
fn tokenize(str: &str) -> Vec<String> {
lazy_static! {
static ref RE: Regex = Regex::new(
r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]+)"###
)
.unwrap();
}
let mut res = vec![];
for cap in RE.captures_iter(str) {
if cap[1].starts_with(";") {
continue;
}
res.push(String::from(&cap[1]));
}
res
}
fn unescape_str(s: &str) -> String {
lazy_static! {
static ref RE: Regex = Regex::new(r#"\\(.)"#).unwrap();
}
RE.replace_all(&s, |caps: &Captures| {
format!("{}", if &caps[1] == "n" { "\n" } else { &caps[1] })
})
.to_string()
}
fn read_atom(rdr: &mut Reader) -> MalRet {
lazy_static! {
static ref INT_RE: Regex = Regex::new(r"^-?[0-9]+$").unwrap();
static ref STR_RE: Regex = Regex::new(r#""(?:\\.|[^\\"])*""#).unwrap();
}
let token = rdr.next()?;
match &token[..] {
"nil" => Ok(Nil),
"false" => Ok(Bool(false)),
"true" => Ok(Bool(true)),
_ => {
if INT_RE.is_match(&token) {
Ok(Int(token.parse().unwrap()))
} else if STR_RE.is_match(&token) {
Ok(Str(unescape_str(&token[1..token.len() - 1])))
} else if token.starts_with("\"") {
error("expected '\"', got EOF")
} else if token.starts_with(":") {
Ok(Str(format!("\u{29e}{}", &token[1..])))
} else {
Ok(Sym(token.to_string()))
}
}
}
}
fn read_seq(rdr: &mut Reader, end: &str) -> MalRet {
let mut seq: Vec<MalVal> = vec![];
rdr.next()?;
loop {
let token = match rdr.peek() {
Ok(t) => t,
Err(_) => return error(&format!("expected '{}', got EOF", end)),
};
if token == end {
break;
}
seq.push(read_form(rdr)?)
}
let _ = rdr.next();
match end {
")" => Ok(list!(seq)),
"]" => Ok(vector!(seq)),
"}" => hash_map(seq),
_ => error("read_seq unknown end value"),
}
}
fn read_form(rdr: &mut Reader) -> MalRet {
let token = rdr.peek()?;
match &token[..] {
"'" => {
let _ = rdr.next();
Ok(list![Sym("quote".to_string()), read_form(rdr)?])
}
"`" => {
let _ = rdr.next();
Ok(list![Sym("quasiquote".to_string()), read_form(rdr)?])
}
"~" => {
let _ = rdr.next();
Ok(list![Sym("unquote".to_string()), read_form(rdr)?])
}
"~@" => {
let _ = rdr.next();
Ok(list![Sym("splice-unquote".to_string()), read_form(rdr)?])
}
"^" => {
let _ = rdr.next();
let meta = read_form(rdr)?;
Ok(list![Sym("with-meta".to_string()), read_form(rdr)?, meta])
}
"@" => {
let _ = rdr.next();
Ok(list![Sym("deref".to_string()), read_form(rdr)?])
}
")" => error("unexpected ')'"),
"(" => read_seq(rdr, ")"),
"]" => error("unexpected ']'"),
"[" => read_seq(rdr, "]"),
"}" => error("unexpected '}'"),
"{" => read_seq(rdr, "}"),
_ => read_atom(rdr),
}
}
pub fn read_str(str: String) -> MalRet {
let tokens = tokenize(&str);
//println!("tokens: {:?}", tokens);
if tokens.len() == 0 {
return error("no input");
}
read_form(&mut Reader {
pos: 0,
tokens: tokens,
})
}

2
lisp/run.sh Executable file
View File

@@ -0,0 +1,2 @@
#export RUST_BACKTRACE=full
cargo run --bin lisp load new-cs.lisp

360
lisp/types.rs Normal file
View File

@@ -0,0 +1,360 @@
use bellman::{
gadgets::{
Assignment,
},
groth16, Circuit, ConstraintSystem, SynthesisError,
};
use std::ops::{Add, AddAssign, MulAssign, SubAssign};
use std::cell::RefCell;
use std::rc::Rc;
//use std::collections::HashMap;
use fnv::FnvHashMap;
use itertools::Itertools;
use crate::env::{env_bind, Env};
use crate::types::MalErr::{ErrMalVal, ErrString};
use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector};
use bellman::Variable;
use bls12_381::Scalar;
#[derive(Debug, Clone)]
pub struct Allocation {
pub symbol: String,
pub value: Scalar,
}
#[derive(Debug, Clone)]
pub struct EnforceAllocation {
pub left: Vec<(String, String)>,
pub right: Vec<(String, String)>,
pub output: Vec<(String, String)>,
}
#[derive(Debug, Clone)]
pub struct LispCircuit {
pub params: Vec<Option<Scalar>>,
pub allocs: FnvHashMap<String, MalVal>,
pub alloc_inputs: FnvHashMap<String, MalVal>,
pub constraints: Vec<EnforceAllocation>,
pub env: Env,
}
impl Circuit<bls12_381::Scalar> for LispCircuit {
fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
let mut variables: FnvHashMap<String, Variable> = FnvHashMap::default();
println!("Allocations\n");
for (k, v) in &self.allocs {
if let MalVal::ZKScalar(val) = v {
println!("val {:?}", val);
let var = cs.alloc(|| "alloc", || Ok(*val))?;
variables.insert(k.to_string(), var);
} else {
println!("k {:?} v {:?}", k, v);
}
}
println!("Allocations Input\n");
for (k, v) in &self.alloc_inputs {
if let MalVal::ZKScalar(val) = v {
println!("val {:?}", val);
let var = cs.alloc_input(|| "alloc", || Ok(*val))?;
variables.insert(k.to_string(), var);
} else {
println!("k {:?} v {:?}", k, v);
}
}
println!("Enforce Allocations\n");
for alloc_value in &self.constraints {
println!("{:?}", alloc_value);
let coeff = bls12_381::Scalar::one();
let mut left = bellman::LinearCombination::<Scalar>::zero();
let mut right = bellman::LinearCombination::<Scalar>::zero();
let mut output = bellman::LinearCombination::<Scalar>::zero();
for values in alloc_value.left.iter() {
let (a, b) = values;
let mut val_b = CS::one();
if b != "cs::one" {
val_b = *variables.get(b).unwrap();
}
if a == "scalar::one" {
left = left + (coeff, val_b);
} else if a == "scalar::one::neg" {
left = left + (coeff.neg(), val_b);
}
}
for values in alloc_value.right.iter() {
let (a, b) = values;
let mut val_b = CS::one();
if b != "cs::one" {
val_b = *variables.get(b).unwrap();
}
if a == "scalar::one" {
right = right + (coeff, val_b);
} else if a == "scalar::one::neg" {
right = right + (coeff.neg(), val_b);
}
}
for values in alloc_value.output.iter() {
let (a, b) = values;
let mut val_b = CS::one();
if b != "cs::one" {
val_b = *variables.get(b).unwrap();
}
if a == "scalar::one" {
output = output + (coeff, val_b);
} else if a == "scalar::one::neg" {
output = output + (coeff.neg(), val_b);
}
}
cs.enforce(
|| "constraint",
|_| left.clone(),
|_| right.clone(),
|_| output.clone(),
);
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum MalVal {
Nil,
Bool(bool),
Int(i64),
Str(String),
Sym(String),
List(Rc<Vec<MalVal>>, Rc<MalVal>),
Vector(Rc<Vec<MalVal>>, Rc<MalVal>),
Hash(Rc<FnvHashMap<String, MalVal>>, Rc<MalVal>),
Func(fn(MalArgs) -> MalRet, Rc<MalVal>),
MalFunc {
eval: fn(ast: MalVal, env: Env) -> MalRet,
ast: Rc<MalVal>,
env: Env,
params: Rc<MalVal>,
is_macro: bool,
meta: Rc<MalVal>,
},
Atom(Rc<RefCell<MalVal>>),
Zk(Rc<LispCircuit>), // TODO remote it
Enforce(Rc<Vec<EnforceAllocation>>),
ZKScalar(bls12_381::Scalar),
}
#[derive(Debug)]
pub enum MalErr {
ErrString(String),
ErrMalVal(MalVal),
}
pub type MalArgs = Vec<MalVal>;
pub type MalRet = Result<MalVal, MalErr>;
// type utility macros
macro_rules! list {
($seq:expr) => {{
List(Rc::new($seq),Rc::new(Nil))
}};
[$($args:expr),*] => {{
let v: Vec<MalVal> = vec![$($args),*];
List(Rc::new(v),Rc::new(Nil))
}}
}
macro_rules! vector {
($seq:expr) => {{
Vector(Rc::new($seq),Rc::new(Nil))
}};
[$($args:expr),*] => {{
let v: Vec<MalVal> = vec![$($args),*];
Vector(Rc::new(v),Rc::new(Nil))
}}
}
// type utility functions
pub fn error(s: &str) -> MalRet {
Err(ErrString(s.to_string()))
}
pub fn format_error(e: MalErr) -> String {
match e {
ErrString(s) => s.clone(),
ErrMalVal(mv) => mv.pr_str(true),
}
}
pub fn atom(mv: &MalVal) -> MalVal {
Atom(Rc::new(RefCell::new(mv.clone())))
}
impl MalVal {
pub fn keyword(&self) -> MalRet {
match self {
Str(s) if s.starts_with("\u{29e}") => Ok(Str(s.to_string())),
Str(s) => Ok(Str(format!("\u{29e}{}", s))),
_ => error("invalid type for keyword"),
}
}
pub fn empty_q(&self) -> MalRet {
match self {
List(l, _) | Vector(l, _) => Ok(Bool(l.len() == 0)),
Nil => Ok(Bool(true)),
_ => error("invalid type for empty?"),
}
}
pub fn count(&self) -> MalRet {
match self {
List(l, _) | Vector(l, _) => Ok(Int(l.len() as i64)),
Nil => Ok(Int(0)),
_ => error("invalid type for count"),
}
}
pub fn apply(&self, args: MalArgs) -> MalRet {
match *self {
Func(f, _) => f(args),
MalFunc {
eval,
ref ast,
ref env,
ref params,
..
} => {
let a = &**ast;
let p = &**params;
let fn_env = env_bind(Some(env.clone()), p.clone(), args)?;
Ok(eval(a.clone(), fn_env)?)
}
_ => error("attempt to call non-function"),
}
}
pub fn keyword_q(&self) -> bool {
match self {
Str(s) if s.starts_with("\u{29e}") => true,
_ => false,
}
}
pub fn deref(&self) -> MalRet {
match self {
Atom(a) => Ok(a.borrow().clone()),
_ => error("attempt to deref a non-Atom"),
}
}
pub fn reset_bang(&self, new: &MalVal) -> MalRet {
match self {
Atom(a) => {
*a.borrow_mut() = new.clone();
Ok(new.clone())
}
_ => error("attempt to reset! a non-Atom"),
}
}
pub fn swap_bang(&self, args: &MalArgs) -> MalRet {
match self {
Atom(a) => {
let f = &args[0];
let mut fargs = args[1..].to_vec();
fargs.insert(0, a.borrow().clone());
*a.borrow_mut() = f.apply(fargs)?;
Ok(a.borrow().clone())
}
_ => error("attempt to swap! a non-Atom"),
}
}
pub fn get_meta(&self) -> MalRet {
match self {
List(_, meta) | Vector(_, meta) | Hash(_, meta) => Ok((&**meta).clone()),
Func(_, meta) => Ok((&**meta).clone()),
MalFunc { meta, .. } => Ok((&**meta).clone()),
_ => error("meta not supported by type"),
}
}
pub fn with_meta(&mut self, new_meta: &MalVal) -> MalRet {
match self {
List(_, ref mut meta)
| Vector(_, ref mut meta)
| Hash(_, ref mut meta)
| Func(_, ref mut meta)
| MalFunc { ref mut meta, .. } => {
*meta = Rc::new((&*new_meta).clone());
}
_ => return error("with-meta not supported by type"),
};
Ok(self.clone())
}
}
impl PartialEq for MalVal {
fn eq(&self, other: &MalVal) -> bool {
match (self, other) {
(Nil, Nil) => true,
(Bool(ref a), Bool(ref b)) => a == b,
(Int(ref a), Int(ref b)) => a == b,
(Str(ref a), Str(ref b)) => a == b,
(Sym(ref a), Sym(ref b)) => a == b,
(List(ref a, _), List(ref b, _))
| (Vector(ref a, _), Vector(ref b, _))
| (List(ref a, _), Vector(ref b, _))
| (Vector(ref a, _), List(ref b, _)) => a == b,
(Hash(ref a, _), Hash(ref b, _)) => a == b,
(MalFunc { .. }, MalFunc { .. }) => false,
_ => false,
}
}
}
pub fn func(f: fn(MalArgs) -> MalRet) -> MalVal {
Func(f, Rc::new(Nil))
}
pub fn _assoc(mut hm: FnvHashMap<String, MalVal>, kvs: MalArgs) -> MalRet {
if kvs.len() % 2 != 0 {
return error("odd number of elements");
}
for (k, v) in kvs.iter().tuples() {
match k {
Str(s) => {
hm.insert(s.to_string(), v.clone());
}
_ => return error("key is not string"),
}
}
Ok(Hash(Rc::new(hm), Rc::new(Nil)))
}
pub fn _dissoc(mut hm: FnvHashMap<String, MalVal>, ks: MalArgs) -> MalRet {
for k in ks.iter() {
match k {
Str(ref s) => {
hm.remove(s);
}
_ => return error("key is not string"),
}
}
Ok(Hash(Rc::new(hm), Rc::new(Nil)))
}
pub fn hash_map(kvs: MalArgs) -> MalRet {
let hm: FnvHashMap<String, MalVal> = FnvHashMap::default();
_assoc(hm, kvs)
}

View File

@@ -1,6 +1,6 @@
use bls12_381::Scalar;
use ff::{Field, PrimeField};
use sapvi::{BlsStringConversion, Decodable, ZKContract};
use ff::{Field};
use sapvi::{Decodable, ZKContract};
use std::fs::File;
use std::ops::{Add, AddAssign, MulAssign, Neg, SubAssign};
use std::time::Instant;

View File

@@ -324,3 +324,6 @@ pub fn mimc_constants() -> Vec<&'static str> {
"597cdd384abdad1beccc73fb39f74a18eb44d056951d602c2ef6ef6448fc5626",
]
}
fn main() {
}

View File

@@ -1,10 +1,10 @@
use sapvi::{BlsStringConversion, Decodable, ZKContract};
use sapvi::{Decodable, ZKContract};
use std::fs::File;
use std::time::Instant;
use bls12_381::Scalar;
use ff::{Field, PrimeField};
use group::{Curve, Group, GroupEncoding};
use group::{Curve, Group};
use rand::rngs::OsRng;
type Result<T> = std::result::Result<T, failure::Error>;
@@ -13,7 +13,7 @@ type Result<T> = std::result::Result<T, failure::Error>;
fn unpack<F: PrimeField>(value: F) -> Vec<Scalar> {
let mut bits = Vec::new();
print!("Unpack: ");
for (i, bit) in value.to_le_bits().into_iter().cloned().enumerate() {
for (_i, bit) in value.to_le_bits().into_iter().cloned().enumerate() {
match bit {
true => bits.push(Scalar::one()),
false => bits.push(Scalar::zero()),

View File

@@ -5,77 +5,77 @@ use crate::error::{Error, Result};
use crate::serial::{Decodable, Encodable, ReadExt, WriteExt};
macro_rules! from_slice {
($data:expr, $len:literal) => {{
let mut array = [0; $len];
// panics if not enough data
let bytes = &$data[..array.len()];
array.copy_from_slice(bytes);
array
}};
($data:expr, $len:literal) => {{
let mut array = [0; $len];
// panics if not enough data
let bytes = &$data[..array.len()];
array.copy_from_slice(bytes);
array
}};
}
pub trait BlsStringConversion {
fn to_string(&self) -> String;
fn from_string(object: &str) -> Self;
fn to_string(&self) -> String;
fn from_string(object: &str) -> Self;
}
impl BlsStringConversion for bls::Scalar {
fn to_string(&self) -> String {
let mut bytes = self.to_bytes();
bytes.reverse();
hex::encode(bytes)
}
fn from_string(object: &str) -> Self {
let mut bytes = from_slice!(&hex::decode(object).unwrap(), 32);
bytes.reverse();
bls::Scalar::from_bytes(&bytes).unwrap()
}
fn to_string(&self) -> String {
let mut bytes = self.to_bytes();
bytes.reverse();
hex::encode(bytes)
}
fn from_string(object: &str) -> Self {
let mut bytes = from_slice!(&hex::decode(object).unwrap(), 32);
bytes.reverse();
bls::Scalar::from_bytes(&bytes).unwrap()
}
}
macro_rules! serialization_bls {
($type:ty, $to_x:ident, $from_x:ident, $size:literal) => {
impl Encodable for $type {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
let data = self.$to_x();
assert_eq!(data.len(), $size);
s.write_slice(&data)?;
Ok(data.len())
}
}
($type:ty, $to_x:ident, $from_x:ident, $size:literal) => {
impl Encodable for $type {
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
let data = self.$to_x();
assert_eq!(data.len(), $size);
s.write_slice(&data)?;
Ok(data.len())
}
}
impl Decodable for $type {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let mut slice = [0u8; $size];
d.read_slice(&mut slice)?;
let result = Self::$from_x(&slice);
if bool::from(result.is_none()) {
return Err(Error::ParseFailed("$t conversion from slice failed"));
}
Ok(result.unwrap())
}
}
};
impl Decodable for $type {
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
let mut slice = [0u8; $size];
d.read_slice(&mut slice)?;
let result = Self::$from_x(&slice);
if bool::from(result.is_none()) {
return Err(Error::ParseFailed("$t conversion from slice failed"));
}
Ok(result.unwrap())
}
}
};
}
serialization_bls!(bls::Scalar, to_bytes, from_bytes, 32);
macro_rules! make_serialize_deserialize_test {
($name:ident, $type:ty, $default_func:ident) => {
#[test]
fn $name() {
let point = <$type>::$default_func();
($name:ident, $type:ty, $default_func:ident) => {
#[test]
fn $name() {
let point = <$type>::$default_func();
let mut data: Vec<u8> = vec![];
let result = point.encode(&mut data);
assert!(result.is_ok());
let mut data: Vec<u8> = vec![];
let result = point.encode(&mut data);
assert!(result.is_ok());
let point2 = <$type>::decode(&data[..]);
assert!(point2.is_ok());
let point2 = point2.unwrap();
let point2 = <$type>::decode(&data[..]);
assert!(point2.is_ok());
let point2 = point2.unwrap();
assert_eq!(point, point2);
}
};
assert_eq!(point, point2);
}
};
}
make_serialize_deserialize_test!(serial_test_scalar, bls::Scalar, zero);

View File

@@ -1,16 +1,15 @@
use bellman::{
gadgets::{
boolean::{AllocatedBit, Boolean},
multipack, num, Assignment,
Assignment,
},
groth16, Circuit, ConstraintSystem, SynthesisError,
};
use bls12_381::Bls12;
use bls12_381::Scalar;
use ff::{Field, PrimeField};
use group::Curve;
use ff::{Field};
use rand::rngs::OsRng;
use std::ops::{MulAssign, Neg, SubAssign};
pub const CRH_IVK_PERSONALIZATION: &[u8; 8] = b"Zcashivk";
@@ -105,8 +104,6 @@ fn main() {
let proof = groth16::create_random_proof(c, &params, &mut OsRng).unwrap();
println!("Prove: [{:?}]", start.elapsed());
let start = Instant::now();
let public_input = vec![bls12_381::Scalar::from(27)];
let start = Instant::now();

File diff suppressed because it is too large Load Diff

View File

@@ -50,7 +50,7 @@ pub enum AllocType {
Public,
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum ConstraintInstruction {
Lc0Add(VariableIndex),
Lc1Add(VariableIndex),