diff --git a/.gitignore b/.gitignore index 7585238..96ef6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -book +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..66e13b0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "pod2" +version = "0.1.0" +edition = "2021" + +[lib] +name = "pod2" +path = "src/lib.rs" + +[dependencies] +plonky2 = { git = "https://github.com/mir-protocol/plonky2" } +hex = "0.4.3" +itertools = "0.14.0" +strum = "0.26" +strum_macros = "0.26" diff --git a/README.md b/README.md index f9ff17a..b7fb583 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,3 @@ -# pod2-docs -> Hackmds for sharing ideas, once there is consensus around it, move it into this repo to consolidate it. Also we can use PRs to discuss asynchronously specific ideas of the spec. +# POD2 -## Usage -A rendered version of the site can be found at: https://0xparc.github.io/pod2-docs/ - -To run it locally: -- Needs [mdbook](https://github.com/rust-lang/mdBook), to install: `cargo install mdbook` -- Needs [mdbook-katex](https://github.com/lzanini/mdbook-katex), to install: `cargo install mdbook-katex` -- Run locally: `mdbook serve` +TODO diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/book/.gitignore @@ -0,0 +1 @@ +book diff --git a/book/README.md b/book/README.md new file mode 100644 index 0000000..f9ff17a --- /dev/null +++ b/book/README.md @@ -0,0 +1,10 @@ +# pod2-docs +> Hackmds for sharing ideas, once there is consensus around it, move it into this repo to consolidate it. Also we can use PRs to discuss asynchronously specific ideas of the spec. + +## Usage +A rendered version of the site can be found at: https://0xparc.github.io/pod2-docs/ + +To run it locally: +- Needs [mdbook](https://github.com/rust-lang/mdBook), to install: `cargo install mdbook` +- Needs [mdbook-katex](https://github.com/lzanini/mdbook-katex), to install: `cargo install mdbook-katex` +- Run locally: `mdbook serve` diff --git a/book.toml b/book/book.toml similarity index 100% rename from book.toml rename to book/book.toml diff --git a/src/SUMMARY.md b/book/src/SUMMARY.md similarity index 100% rename from src/SUMMARY.md rename to book/src/SUMMARY.md diff --git a/src/datatypes.md b/book/src/datatypes.md similarity index 100% rename from src/datatypes.md rename to book/src/datatypes.md diff --git a/src/deductions.md b/book/src/deductions.md similarity index 100% rename from src/deductions.md rename to book/src/deductions.md diff --git a/src/examples.md b/book/src/examples.md similarity index 100% rename from src/examples.md rename to book/src/examples.md diff --git a/src/introduction.md b/book/src/introduction.md similarity index 100% rename from src/introduction.md rename to book/src/introduction.md diff --git a/src/mainpod.md b/book/src/mainpod.md similarity index 100% rename from src/mainpod.md rename to book/src/mainpod.md diff --git a/src/merkletree.md b/book/src/merkletree.md similarity index 100% rename from src/merkletree.md rename to book/src/merkletree.md diff --git a/src/mockpod.md b/book/src/mockpod.md similarity index 100% rename from src/mockpod.md rename to book/src/mockpod.md diff --git a/src/operations.md b/book/src/operations.md similarity index 100% rename from src/operations.md rename to book/src/operations.md diff --git a/src/podtypes.md b/book/src/podtypes.md similarity index 100% rename from src/podtypes.md rename to book/src/podtypes.md diff --git a/src/signedpod.md b/book/src/signedpod.md similarity index 100% rename from src/signedpod.md rename to book/src/signedpod.md diff --git a/src/statements.md b/book/src/statements.md similarity index 100% rename from src/statements.md rename to book/src/statements.md diff --git a/src/backend.rs b/src/backend.rs new file mode 100644 index 0000000..9d8f6d6 --- /dev/null +++ b/src/backend.rs @@ -0,0 +1,296 @@ +use plonky2::field::types::{Field, PrimeField64}; +use std::collections::HashMap; +use std::fmt; +use std::io::{self, Write}; +use std::iter; +use strum_macros::FromRepr; + +use crate::{Hash, Params, PodId, F, NULL}; + +#[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq)] +pub enum NativeStatement { + None = 0, + ValueOf = 1, + Equal = 2, + NotEqual, + Gt, + Lt, + Contains, + NotContains, + SumOf, + ProductOf, + MaxOf, +} + +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +pub struct Value(pub [F; 4]); + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0[2].is_zero() && self.0[3].is_zero() { + // Assume this is an integer + let (l0, l1) = (self.0[0].to_canonical_u64(), self.0[1].to_canonical_u64()); + assert!(l0 < (1 << 32)); + assert!(l1 < (1 << 32)); + write!(f, "{}", l0 + l1 * (1 << 32)) + } else { + // Assume this is a hash + Hash(self.0).fmt(f) + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct AnchoredKey(pub PodId, pub Hash); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum StatementArg { + None, + Literal(Value), + Ref(AnchoredKey), +} + +impl StatementArg { + pub fn is_none(&self) -> bool { + matches!(self, Self::None) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Statement(pub NativeStatement, pub Vec); + +impl Statement { + pub fn is_none(&self) -> bool { + matches!(self.0, NativeStatement::None) + } +} + +#[derive(Clone, Debug)] +pub struct SignedPod { + pub params: Params, + pub id: PodId, + pub kvs: HashMap, +} + +impl SignedPod { + pub fn is_null(&self) -> bool { + self.id.0 == NULL + } +} + +#[derive(Clone, Debug)] +pub struct MainPod { + pub params: Params, + pub id: PodId, + pub input_signed_pods: Vec, + pub statements: Vec, +} + +fn fill_pad(v: &mut Vec, pad_value: T, len: usize) { + if v.len() > len { + panic!("length exceeded"); + } + while v.len() < len { + v.push(pad_value.clone()); + } +} + +impl MainPod { + pub fn new( + params: Params, + mut input_signed_pods: Vec, + input_main_pods: Vec, + mut statements: Vec, + ) -> Self { + Self::pad_statements(¶ms, &mut statements, params.max_statements); + Self::pad_input_signed_pods(¶ms, &mut input_signed_pods); + Self { + params, + id: PodId::default(), // TODO + input_signed_pods, + statements, + } + } + + fn statement_none(params: &Params) -> Statement { + let mut args = Vec::with_capacity(params.max_statement_args); + Self::pad_statement_args(¶ms, &mut args); + Statement(NativeStatement::None, args) + } + + fn pad_statements(params: &Params, statements: &mut Vec, len: usize) { + for st in statements.iter_mut() { + fill_pad(&mut st.1, StatementArg::None, params.max_statement_args) + } + fill_pad(statements, Self::statement_none(params), len) + } + + fn pad_statement_args(params: &Params, args: &mut Vec) { + fill_pad(args, StatementArg::None, params.max_statement_args) + } + + fn pad_input_signed_pods(params: &Params, pods: &mut Vec) { + let pod_none = SignedPod { + params: params.clone(), + id: PodId::default(), + kvs: HashMap::new(), + }; + fill_pad(pods, pod_none, params.max_input_signed_pods) + } + + pub fn input_signed_pods_statements(&self) -> Vec> { + let mut pods_statements = Vec::new(); + let st_none = Self::statement_none(&self.params); + for pod in &self.input_signed_pods { + let mut pod_statements: Vec = Vec::new(); + for kv in &pod.kvs { + let args = vec![ + StatementArg::Ref(AnchoredKey(pod.id, *kv.0)), + StatementArg::Literal(*kv.1), + ]; + pod_statements.push(Statement(NativeStatement::ValueOf, args)); + } + Self::pad_statements( + &self.params, + &mut pod_statements, + self.params.max_signed_pod_values, + ); + pods_statements.push(pod_statements); + } + let statements_none: Vec = iter::repeat(st_none.clone()) + .take(self.params.max_signed_pod_values) + .collect(); + fill_pad( + &mut pods_statements, + statements_none, + self.params.max_input_signed_pods, + ); + pods_statements + } + + pub fn prv_statements(&self) -> Vec { + self.statements + .iter() + .take(self.params.max_priv_statements()) + .cloned() + .collect() + } + + pub fn pub_statements(&self) -> Vec { + self.statements + .iter() + .skip(self.params.max_priv_statements()) + .cloned() + .collect() + } +} + +pub struct Printer { + pub skip_none: bool, +} + +impl Printer { + pub fn fmt_arg(&self, w: &mut dyn Write, arg: &StatementArg) -> io::Result<()> { + match arg { + StatementArg::None => write!(w, "none"), + StatementArg::Literal(v) => write!(w, "{}", v), + StatementArg::Ref(r) => write!(w, "{}.{}", r.0, r.1), + } + } + + pub fn fmt_signed_pod(&self, w: &mut dyn Write, pod: &SignedPod) -> io::Result<()> { + writeln!(w, "SignedPod ({}):", pod.id)?; + // for (k, v) in pod.kvs.iter().sorted_by_key(|kv| kv.0) { + // TODO: print in a stable order + for (k, v) in pod.kvs.iter() { + println!(" - {}: {}", k, v); + } + Ok(()) + } + + pub fn fmt_statement(&self, w: &mut dyn Write, st: &Statement) -> io::Result<()> { + write!(w, "{:?} ", st.0)?; + for (i, arg) in st.1.iter().enumerate() { + if !(self.skip_none && arg.is_none()) { + if i != 0 { + write!(w, " ")?; + } + self.fmt_arg(w, arg)?; + } + } + Ok(()) + } + + pub fn fmt_statement_index( + &self, + w: &mut dyn Write, + st: &Statement, + index: usize, + ) -> io::Result<()> { + if !(self.skip_none && st.is_none()) { + write!(w, " {:03}. ", index)?; + self.fmt_statement(w, &st)?; + write!(w, "\n")?; + } + Ok(()) + } + + pub fn fmt_main_pod(&self, w: &mut dyn Write, pod: &MainPod) -> io::Result<()> { + writeln!(w, "MainPod ({}):", pod.id)?; + // TODO + // writeln!(w, " input_main_pods:")?; + // for in_pod in &pod.input_main_pods { + // writeln!(w, " - {}", in_pod.id)?; + // } + let mut st_index = 0; + for (i, (pod, statements)) in pod + .input_signed_pods + .iter() + .zip(pod.input_signed_pods_statements()) + .enumerate() + { + if !(self.skip_none && pod.is_null()) { + writeln!(w, " in sig_pod {:02} (id:{}) statements:", i, pod.id)?; + for st in statements { + self.fmt_statement_index(w, &st, st_index)?; + st_index += 1; + } + } else { + st_index += pod.params.max_signed_pod_values; + } + } + writeln!(w, " prv statements:")?; + for st in pod.prv_statements() { + self.fmt_statement_index(w, &st, st_index)?; + st_index += 1; + } + writeln!(w, " pub statements:")?; + for st in pod.pub_statements() { + self.fmt_statement_index(w, &st, st_index)?; + st_index += 1; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::frontend; + + #[test] + fn test_back_0() { + let params = Params::default(); + let (front_gov_id, front_pay_stub, front_kyc) = frontend::tests::data_zu_kyc(params); + let gov_id = front_gov_id.compile(); + let pay_stub = front_pay_stub.compile(); + let kyc = front_kyc.compile(); + // println!("{:#?}", kyc); + + let printer = Printer { skip_none: true }; + let mut w = io::stdout(); + printer.fmt_signed_pod(&mut w, &gov_id).unwrap(); + printer.fmt_signed_pod(&mut w, &pay_stub).unwrap(); + printer.fmt_main_pod(&mut w, &kyc).unwrap(); + } +} diff --git a/src/frontend.rs b/src/frontend.rs new file mode 100644 index 0000000..dfd066f --- /dev/null +++ b/src/frontend.rs @@ -0,0 +1,456 @@ +use itertools::Itertools; +use plonky2::field::types::Field; +use std::collections::HashMap; +use std::convert::From; +use std::fmt; +use std::io::{self, Write}; + +use crate::backend; +use crate::{hash_str, Params, PodId, F, SELF}; + +#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)] +pub enum PodType { + #[default] + Signed = 1, + Main, +} + +// An Origin, which represents a reference to an ancestor POD. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] +pub struct Origin(pub PodType, pub PodId); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MerkleTree { + pub root: u8, // TODO +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Value { + String(String), + Int(i64), + MerkleTree(MerkleTree), +} + +impl From<&str> for Value { + fn from(s: &str) -> Self { + Value::String(s.to_string()) + } +} + +impl From for Value { + fn from(v: i64) -> Self { + Value::Int(v) + } +} + +impl From<&Value> for backend::Value { + fn from(v: &Value) -> Self { + match v { + Value::String(s) => backend::Value(hash_str(s).0), + Value::Int(v) => { + backend::Value([F::from_canonical_u64(*v as u64), F::ZERO, F::ZERO, F::ZERO]) + } + // TODO + Value::MerkleTree(mt) => backend::Value([ + F::from_canonical_u64(mt.root as u64), + F::ZERO, + F::ZERO, + F::ZERO, + ]), + } + } +} + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Value::String(s) => write!(f, "\"{}\"", s), + Value::Int(v) => write!(f, "{}", v), + Value::MerkleTree(mt) => write!(f, "mt:{}", mt.root), + } + } +} + +#[derive(Clone, Debug)] +pub struct SignedPod { + pub params: Params, + pub id: PodId, + pub kvs: HashMap, +} + +impl SignedPod { + pub fn origin(&self) -> Origin { + Origin(PodType::Signed, self.id) + } + pub fn compile(&self) -> backend::SignedPod { + backend::SignedPod { + params: self.params, + id: self.id, + kvs: self + .kvs + .iter() + .map(|(k, v)| (hash_str(k), backend::Value::from(v))) + .collect(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum NativeStatement { + Equal = 2, + NotEqual, + Gt, + Lt, + Contains, + NotContains, + SumOf, + ProductOf, + MaxOf, +} + +impl From for backend::NativeStatement { + fn from(v: NativeStatement) -> Self { + Self::from_repr(v as usize).unwrap() + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct AnchoredKey(pub Origin, pub String); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum StatementArg { + Literal(Value), + Ref(AnchoredKey), +} + +impl fmt::Display for StatementArg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Literal(v) => write!(f, "{}", v), + Self::Ref(r) => write!(f, "{}.{}", r.0 .1, r.1), + } + } +} + +impl From for StatementArg { + fn from(v: Value) -> Self { + StatementArg::Literal(v) + } +} + +impl From<&str> for StatementArg { + fn from(s: &str) -> Self { + StatementArg::Literal(Value::from(s)) + } +} + +impl From for StatementArg { + fn from(v: i64) -> Self { + StatementArg::Literal(Value::from(v)) + } +} + +impl From<(Origin, &str)> for StatementArg { + fn from((origin, key): (Origin, &str)) -> Self { + StatementArg::Ref(AnchoredKey(origin, key.to_string())) + } +} + +impl From<(&SignedPod, &str)> for StatementArg { + fn from((pod, key): (&SignedPod, &str)) -> Self { + StatementArg::Ref(AnchoredKey(pod.origin(), key.to_string())) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Statement(pub NativeStatement, pub Vec); + +impl fmt::Display for Statement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?} ", self.0)?; + for (i, arg) in self.1.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + write!(f, "{}", arg)?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct MainPodBuilder { + pub params: Params, + pub statements: Vec, + pub operations: Vec, +} + +impl MainPodBuilder { + pub fn push_statement(&mut self, st: Statement, op: Operation) { + self.statements.push(st); + self.operations.push(op); + } + pub fn build(self) -> MainPod { + MainPod { + params: self.params, + id: PodId::default(), // TODO + input_signed_pods: vec![], // TODO + input_main_pods: vec![], // TODO + statements: self + .statements + .into_iter() + .zip(self.operations.into_iter()) + .collect(), + } + } +} + +#[derive(Clone, Debug)] +pub struct MainPod { + pub params: Params, + pub id: PodId, + pub input_signed_pods: Vec, + pub input_main_pods: Vec, + pub statements: Vec<(Statement, Operation)>, +} + +impl MainPod { + pub fn origin(&self) -> Origin { + Origin(PodType::Main, self.id) + } + + pub fn compile(&self) -> backend::MainPod { + MainPodCompiler::new(self).compile() + } + + pub fn max_priv_statements(&self) -> usize { + self.params.max_statements - self.params.max_public_statements + } +} + +struct MainPodCompiler<'a> { + // Input + pod: &'a MainPod, + // Output + statements: Vec, + // Internal state + const_cnt: usize, +} + +impl<'a> MainPodCompiler<'a> { + fn new(pod: &'a MainPod) -> Self { + Self { + pod, + statements: Vec::new(), + const_cnt: 0, + } + } + + fn compile_st(&mut self, st: &Statement) { + let mut args = Vec::new(); + let Statement(front_typ, front_args) = st; + for front_arg in front_args { + let key = match front_arg { + StatementArg::Literal(v) => { + let key = format!("_c{}", self.const_cnt); + let key_hash = hash_str(&key); + self.const_cnt += 1; + let value_of_args = vec![ + backend::StatementArg::Ref(backend::AnchoredKey(SELF, key_hash)), + backend::StatementArg::Literal(backend::Value::from(v)), + ]; + self.statements.push(backend::Statement( + backend::NativeStatement::ValueOf, + value_of_args, + )); + backend::AnchoredKey(SELF, key_hash) + } + StatementArg::Ref(k) => backend::AnchoredKey(k.0 .1, hash_str(&k.1)), + }; + args.push(backend::StatementArg::Ref(key)); + if args.len() > self.pod.params.max_statement_args { + panic!("too many statement args"); + } + } + self.statements.push(backend::Statement( + backend::NativeStatement::from(*front_typ), + args, + )); + } + + pub fn compile(mut self) -> backend::MainPod { + let MainPod { + statements, + params, + input_signed_pods, + .. + } = self.pod; + for st in statements { + self.compile_st(&st.0); + if self.statements.len() > params.max_statements { + panic!("too many statements"); + } + } + let input_signed_pods = input_signed_pods.iter().map(|p| p.compile()).collect(); + backend::MainPod::new(params.clone(), input_signed_pods, vec![], self.statements) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NativeOperation { + TransitiveEqualityFromStatements = 1, + GtToNonequality = 2, + LtToNonequality = 3, + Auto = 1024, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum OperationArg { + Statement(Statement), + Key(AnchoredKey), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Operation(pub NativeOperation, pub Vec); + +pub struct Printer {} + +impl Printer { + pub fn fmt_op_arg(&self, w: &mut dyn Write, arg: &OperationArg) -> io::Result<()> { + match arg { + OperationArg::Statement(s) => write!(w, "{}", s), + OperationArg::Key(r) => write!(w, "{}.{}", r.0 .1, r.1), + } + } + + pub fn fmt_op(&self, w: &mut dyn Write, op: &Operation) -> io::Result<()> { + write!(w, "{:?} ", op.0)?; + for (i, arg) in op.1.iter().enumerate() { + if i != 0 { + write!(w, " ")?; + } + self.fmt_op_arg(w, arg)?; + } + Ok(()) + } + + pub fn fmt_signed_pod(&self, w: &mut dyn Write, pod: &SignedPod) -> io::Result<()> { + writeln!(w, "SignedPod (id:{}):", pod.id)?; + for (k, v) in pod.kvs.iter().sorted_by_key(|kv| kv.0) { + println!(" - {}: {}", k, v); + } + Ok(()) + } + + pub fn fmt_main_pod(&self, w: &mut dyn Write, pod: &MainPod) -> io::Result<()> { + writeln!(w, "MainPod (id:{}):", pod.id)?; + writeln!(w, " input_signed_pods:")?; + for in_pod in &pod.input_signed_pods { + writeln!(w, " - {}", in_pod.id)?; + } + writeln!(w, " input_main_pods:")?; + for in_pod in &pod.input_main_pods { + writeln!(w, " - {}", in_pod.id)?; + } + writeln!(w, " statements:")?; + for st in &pod.statements { + let (st, op) = st; + write!(w, " - {} <- ", st)?; + self.fmt_op(w, op)?; + write!(w, "\n")?; + } + Ok(()) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::Hash; + use hex::FromHex; + use std::io; + + fn pod_id(hex: &str) -> PodId { + PodId(Hash::from_hex(hex).unwrap()) + } + + fn auto() -> Operation { + Operation(NativeOperation::Auto, vec![]) + } + + macro_rules! args { + ($($arg:expr),+) => {vec![$(StatementArg::from($arg)),*]} + } + + macro_rules! st { + (eq, $($arg:expr),+) => { Statement(NativeStatement::Equal, args!($($arg),*)) }; + (ne, $($arg:expr),+) => { Statement(NativeStatement::NotEqual, args!($($arg),*)) }; + (gt, $($arg:expr),+) => { Statement(NativeStatement::Gt, args!($($arg),*)) }; + (lt, $($arg:expr),+) => { Statement(NativeStatement::Lt, args!($($arg),*)) }; + (contains, $($arg:expr),+) => { Statement(NativeStatement::Contains, args!($($arg),*)) }; + (not_contains, $($arg:expr),+) => { Statement(NativeStatement::NotContains, args!($($arg),*)) }; + (sum_of, $($arg:expr),+) => { Statement(NativeStatement::SumOf, args!($($arg),*)) }; + (product_of, $($arg:expr),+) => { Statement(NativeStatement::product_of, args!($($arg),*)) }; + (max_of, $($arg:expr),+) => { Statement(NativeStatement::max_of, args!($($arg),*)) }; + } + + pub fn data_zu_kyc(params: Params) -> (SignedPod, SignedPod, MainPod) { + let mut kvs = HashMap::new(); + kvs.insert("idNumber".into(), "4242424242".into()); + kvs.insert("dateOfBirth".into(), 1169909384.into()); + kvs.insert("socialSecurityNumber".into(), "G2121210".into()); + let gov_id = SignedPod { + params: params.clone(), + id: pod_id("1100000000000000000000000000000000000000000000000000000000000000"), + kvs, + }; + + let mut kvs = HashMap::new(); + kvs.insert("socialSecurityNumber".into(), "G2121210".into()); + kvs.insert("startDate".into(), 1706367566.into()); + let pay_stub = SignedPod { + params: params.clone(), + id: pod_id("2200000000000000000000000000000000000000000000000000000000000000"), + kvs, + }; + + let sanction_list = Value::MerkleTree(MerkleTree { root: 1 }); + let now_minus_18y: i64 = 1169909388; + let now_minus_1y: i64 = 1706367566; + let mut statements: Vec<(Statement, Operation)> = Vec::new(); + statements.push(( + st!(not_contains, sanction_list, (&gov_id, "idNumber")), + auto(), + )); + statements.push((st!(lt, (&gov_id, "dateOfBirth"), now_minus_18y), auto())); + statements.push(( + st!( + eq, + (&gov_id, "socialSecurityNumber"), + (&pay_stub, "socialSecurityNumber") + ), + auto(), + )); + statements.push((st!(eq, (&pay_stub, "startDate"), now_minus_1y), auto())); + let kyc = MainPod { + params: params.clone(), + id: pod_id("3300000000000000000000000000000000000000000000000000000000000000"), + input_signed_pods: vec![gov_id.clone(), pay_stub.clone()], + input_main_pods: vec![], + statements, + }; + + (gov_id, pay_stub, kyc) + } + + #[test] + fn test_front_0() { + let (gov_id, pay_stub, kyc) = data_zu_kyc(Params::default()); + + let printer = Printer {}; + let mut w = io::stdout(); + printer.fmt_signed_pod(&mut w, &gov_id).unwrap(); + printer.fmt_signed_pod(&mut w, &pay_stub).unwrap(); + printer.fmt_main_pod(&mut w, &kyc).unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1ead253 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,104 @@ +use hex::{FromHex, FromHexError}; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::{Field, PrimeField64}; +use plonky2::hash::poseidon::PoseidonHash; +use plonky2::plonk::config::Hasher; +use std::fmt; + +pub mod backend; +pub mod frontend; + +pub type F = GoldilocksField; + +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +pub struct Hash(pub [F; 4]); +pub const NULL: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]); + +impl fmt::Display for Hash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let v0 = self.0[0].to_canonical_u64(); + for i in 0..4 { + write!(f, "{:02x}", (v0 >> (i * 8)) & 0xff)?; + } + write!(f, "…") + } +} + +impl FromHex for Hash { + type Error = FromHexError; + + fn from_hex>(hex: T) -> Result { + // In little endian + let bytes = <[u8; 32]>::from_hex(hex)?; + let mut buf: [u8; 8] = [0; 8]; + let mut inner = [F::ZERO; 4]; + for i in 0..4 { + buf.copy_from_slice(&bytes[8 * i..8 * (i + 1)]); + inner[i] = F::from_canonical_u64(u64::from_le_bytes(buf)); + } + Ok(Self(inner)) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] +pub struct PodId(pub Hash); +pub const SELF: PodId = PodId(Hash([F::ONE, F::ZERO, F::ZERO, F::ZERO])); + +impl fmt::Display for PodId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if *self == SELF { + write!(f, "self") + } else if self.0 == NULL { + write!(f, "null") + } else { + write!(f, "{}", self.0) + } + } +} + +pub fn hash_str(s: &str) -> Hash { + let mut input = s.as_bytes().to_vec(); + input.push(1); // padding + // Merge 7 bytes into 1 field, because the field is slightly below 64 bits + let input: Vec = input + .chunks(7) + .map(|bytes| { + let mut v: u64 = 0; + for b in bytes.iter().rev() { + v <<= 8; + v += *b as u64; + } + F::from_canonical_u64(v) + }) + .collect(); + Hash(PoseidonHash::hash_no_pad(&input).elements) +} + +#[derive(Clone, Debug, Copy)] +pub struct Params { + pub max_input_signed_pods: usize, + pub max_input_main_pods: usize, + pub max_statements: usize, + pub max_signed_pod_values: usize, + pub max_public_statements: usize, + pub max_statement_args: usize, +} + +impl Params { + pub fn max_priv_statements(&self) -> usize { + self.max_statements - self.max_public_statements + } +} + +impl Default for Params { + fn default() -> Self { + Self { + max_input_signed_pods: 3, + max_input_main_pods: 3, + max_statements: 20, + max_signed_pod_values: 8, + max_public_statements: 10, + max_statement_args: 5, + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +}