mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
Merge pull request #7 from mileschet/master
Merging lisp v0 to master branch
This commit is contained in:
@@ -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
12
lisp/README.md
Normal 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
7
lisp/TODO.md
Normal 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
9
lisp/bits.lisp
Normal 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
489
lisp/core.rs
Normal 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
85
lisp/env.rs
Normal 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
70
lisp/jubjub.lisp
Normal 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
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
624
lisp/lisp.rs
Normal 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, ¶ms, &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
38
lisp/new-cs.lisp
Normal 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
17
lisp/new.lisp
Normal 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
63
lisp/printer.rs
Normal 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
156
lisp/reader.rs
Normal 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
2
lisp/run.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#export RUST_BACKTRACE=full
|
||||
cargo run --bin lisp load new-cs.lisp
|
||||
360
lisp/types.rs
Normal file
360
lisp/types.rs
Normal 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)
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -324,3 +324,6 @@ pub fn mimc_constants() -> Vec<&'static str> {
|
||||
"597cdd384abdad1beccc73fb39f74a18eb44d056951d602c2ef6ef6448fc5626",
|
||||
]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ¶ms, &mut OsRng).unwrap();
|
||||
println!("Prove: [{:?}]", start.elapsed());
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
let public_input = vec![bls12_381::Scalar::from(27)];
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
1300
src/serial.rs
1300
src/serial.rs
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user