Partial implementation of the MockMainPod (#33)

* wip

* wip

* feat: handle public statements

* fix: remove old backend module
This commit is contained in:
Eduard S. 2025-02-05 16:50:00 +01:00 committed by GitHub
parent e074d34078
commit 8945d7f8a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 555 additions and 268 deletions

View file

@ -1,229 +0,0 @@
// TODO: Move the MainPod logic to mock_main and implement the MainPod trait
/*
use anyhow::Result;
use itertools::Itertools;
use plonky2::field::types::{Field, PrimeField64};
use std::collections::HashMap;
use std::io::{self, Write};
use std::iter;
use crate::merkletree::MerkleTree;
use crate::middleware::{Hash, Params, PodId, Value, NULL};
#[derive(Clone, Debug)]
pub struct MainPod {
pub params: Params,
pub id: PodId,
pub input_signed_pods: Vec<SignedPod>,
pub statements: Vec<Statement>,
}
fn fill_pad<T: Clone>(v: &mut Vec<T>, 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<SignedPod>,
input_main_pods: Vec<MainPod>,
mut statements: Vec<Statement>,
) -> Result<Self> {
Self::pad_statements(&params, &mut statements, params.max_statements);
Self::pad_input_signed_pods(&params, &mut input_signed_pods)?;
Ok(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(&params, &mut args);
Statement(NativeStatement::None, args)
}
fn pad_statements(params: &Params, statements: &mut Vec<Statement>, 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<StatementArg>) {
fill_pad(args, StatementArg::None, params.max_statement_args)
}
fn pad_input_signed_pods(params: &Params, pods: &mut Vec<SignedPod>) -> Result<()> {
let pod_none = SignedPod::new(params, HashMap::new())?;
Ok(fill_pad(pods, pod_none, params.max_input_signed_pods))
}
pub fn input_signed_pods_statements(&self) -> Vec<Vec<Statement>> {
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<Statement> = 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<Statement> = 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<Statement> {
self.statements
.iter()
.take(self.params.max_priv_statements())
.cloned()
.collect()
}
pub fn pub_statements(&self) -> Vec<Statement> {
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)?;
// Note: current version iterates sorting by keys of the kvs, but the merkletree defined at
// https://0xparc.github.io/pod2/merkletree.html will not need it since it will be
// deterministic based on the keys values not on the order of the keys when added into the
// tree.
for (k, v) in pod.kvs.iter().sorted_by_key(|kv| kv.0) {
writeln!(w, " - {}: {}", 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() -> Result<()> {
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.pod; // get backend's pod
let pay_stub = front_pay_stub.pod; // get backend's pod
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)?;
printer.fmt_signed_pod(&mut w, &pay_stub)?;
printer.fmt_main_pod(&mut w, &kyc)?;
Ok(())
}
}
*/

View file

@ -0,0 +1,436 @@
use crate::middleware::{
self, MainPod, MainPodInputs, NativeOperation, NativeStatement, NoneMainPod, NoneSignedPod,
Params, PodId, PodProver, SignedPod, Statement, StatementArg,
};
use anyhow::Result;
use itertools::Itertools;
use std::any::Any;
use std::io::{self, Write};
pub struct MockProver {}
impl PodProver for MockProver {
fn prove(&mut self, params: &Params, inputs: MainPodInputs) -> Result<Box<dyn MainPod>> {
Ok(Box::new(MockMainPod::new(params, inputs)?))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum OperationArg {
None,
Index(usize),
}
impl OperationArg {
fn is_none(&self) -> bool {
matches!(self, OperationArg::None)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct Operation(pub NativeOperation, pub Vec<OperationArg>);
#[derive(Clone, Debug)]
pub struct MockMainPod {
params: Params,
id: PodId,
input_signed_pods: Vec<Box<dyn SignedPod>>,
input_main_pods: Vec<Box<dyn MainPod>>,
// New statements introduced by this pod
input_statements: Vec<Statement>,
public_statements: Vec<Statement>,
operations: Vec<Operation>,
// All statements (inherited + new)
statements: Vec<Statement>,
}
fn fill_pad<T: Clone>(v: &mut Vec<T>, pad_value: T, len: usize) {
if v.len() > len {
panic!("length exceeded");
}
while v.len() < len {
v.push(pad_value.clone());
}
}
impl MockMainPod {
fn offset_input_signed_pods(&self) -> usize {
0
}
fn offset_input_main_pods(&self) -> usize {
self.params.max_input_signed_pods * self.params.max_signed_pod_values
}
fn offset_input_statements(&self) -> usize {
self.offset_input_main_pods()
+ self.params.max_input_main_pods * self.params.max_public_statements
}
fn offset_public_statements(&self) -> usize {
self.offset_input_statements() + self.params.max_priv_statements()
}
fn layout_statements(params: &Params, inputs: &MainPodInputs) -> Vec<Statement> {
let mut statements = Vec::new();
let st_none = Self::statement_none(params);
// Input signed pods region
let none_sig_pod: Box<dyn SignedPod> = Box::new(NoneSignedPod {});
assert!(inputs.signed_pods.len() <= params.max_input_signed_pods);
for i in 0..params.max_input_signed_pods {
let pod = inputs
.signed_pods
.get(i)
.map(|p| *p)
.unwrap_or(&none_sig_pod);
let sts = pod.pub_statements();
assert!(sts.len() <= params.max_signed_pod_values);
for j in 0..params.max_signed_pod_values {
let mut st = sts.get(j).unwrap_or(&st_none).clone();
Self::pad_statement_args(params, &mut st.1);
statements.push(st);
}
}
// Input main pods region
let none_main_pod: Box<dyn MainPod> = Box::new(NoneMainPod {});
assert!(inputs.main_pods.len() <= params.max_input_main_pods);
for i in 0..params.max_input_main_pods {
let pod = inputs
.main_pods
.get(i)
.map(|p| *p)
.unwrap_or(&none_main_pod);
let sts = pod.pub_statements();
assert!(sts.len() <= params.max_public_statements);
for j in 0..params.max_public_statements {
let mut st = sts.get(j).unwrap_or(&st_none).clone();
Self::pad_statement_args(params, &mut st.1);
statements.push(st);
}
}
// Input statements
assert!(inputs.statements.len() <= params.max_priv_statements());
for i in 0..params.max_priv_statements() {
let mut st = inputs.statements.get(i).unwrap_or(&st_none).clone();
Self::pad_statement_args(params, &mut st.1);
statements.push(st);
}
// Public statements
assert!(inputs.public_statements.len() <= params.max_public_statements);
for i in 0..params.max_public_statements {
let mut st = inputs.public_statements.get(i).unwrap_or(&st_none).clone();
Self::pad_statement_args(params, &mut st.1);
statements.push(st);
}
statements
}
fn find_op_arg(statements: &[Statement], op_arg: &middleware::OperationArg) -> OperationArg {
match op_arg {
middleware::OperationArg::None => OperationArg::None,
middleware::OperationArg::Key(k) => OperationArg::Index(
// TODO: Error handling when the key is not found in any ValueOf statement
statements
.iter()
.enumerate()
.find_map(|(i, s)| match s.0 {
NativeStatement::ValueOf => match &s.1[0] {
StatementArg::Key(sk) => (sk == k).then_some(i),
_ => None,
},
_ => None,
})
.unwrap(),
),
middleware::OperationArg::Statement(st) => OperationArg::Index(
// TODO: Error handling when the statement is not found
statements
.iter()
.enumerate()
.find_map(|(i, s)| (s == st).then_some(i))
.unwrap(),
),
}
}
fn process_priavte_statements_operations(
params: &Params,
statements: &[Statement],
input_operations: &[middleware::Operation],
) -> Vec<Operation> {
let op_none = Self::operation_none(params);
let mut operations = Vec::new();
for i in 0..params.max_priv_statements() {
let op = input_operations.get(i).unwrap_or(&op_none).clone();
let mut mid_args = op.1;
Self::pad_operation_args(params, &mut mid_args);
let mut args = Vec::with_capacity(mid_args.len());
for mid_arg in &mid_args {
args.push(Self::find_op_arg(statements, mid_arg));
}
operations.push(Operation(op.0, args));
}
operations
}
// NOTE: In this implementation public statements are always copies from previous statements,
// so we fill in the operations accordingly.
fn process_public_statements_operations(
params: &Params,
statements: &[Statement],
mut operations: Vec<Operation>,
) -> Vec<Operation> {
let op_none = Self::operation_none(params);
let offset_public_statements = statements.len() - params.max_public_statements;
for i in 0..params.max_public_statements {
let st = &statements[offset_public_statements + i];
let mut op = if st.is_none() {
Operation(NativeOperation::None, vec![])
} else {
let mid_arg = middleware::OperationArg::Statement(st.clone());
Operation(
NativeOperation::CopyStatement,
vec![Self::find_op_arg(statements, &mid_arg)],
)
};
fill_pad(&mut op.1, OperationArg::None, params.max_operation_args);
operations.push(op);
}
operations
}
pub fn new(params: &Params, inputs: MainPodInputs) -> Result<Self> {
// TODO: Figure out a way to handle public statements. For example, in the public slots
// use copy operations taking the private statements that need to be public. We may change
// the MainPodInputs type to accomodate for that.
// TODO: Insert a new public statement of ValueOf with `key=KEY_TYPE,
// value=PodType::MockMainPod`
let statements = Self::layout_statements(params, &inputs);
let operations =
Self::process_priavte_statements_operations(params, &statements, inputs.operations);
let operations =
Self::process_public_statements_operations(params, &statements, operations);
let input_signed_pods = inputs
.signed_pods
.iter()
.map(|p| (*p).clone())
.collect_vec();
let input_main_pods = inputs.main_pods.iter().map(|p| (*p).clone()).collect_vec();
let input_statements = inputs.statements.iter().cloned().collect_vec();
let public_statements = inputs.public_statements.iter().cloned().collect_vec();
// TODO: Calculate the PodId from a subset of the `statements` vector. For example it
// could be the public subset (which is the last `params.max_public_statements` of the
// vector`).
Ok(Self {
params: params.clone(),
id: PodId::default(), // TODO
input_signed_pods,
input_main_pods,
input_statements,
public_statements,
statements,
operations,
})
}
fn statement_none(params: &Params) -> Statement {
let mut args = Vec::with_capacity(params.max_statement_args);
Self::pad_statement_args(&params, &mut args);
Statement(NativeStatement::None, args)
}
fn operation_none(params: &Params) -> middleware::Operation {
let mut args = Vec::with_capacity(params.max_operation_args);
Self::pad_operation_args(&params, &mut args);
middleware::Operation(NativeOperation::None, args)
}
fn pad_statement_args(params: &Params, args: &mut Vec<StatementArg>) {
fill_pad(args, StatementArg::None, params.max_statement_args)
}
fn pad_operation_args(params: &Params, args: &mut Vec<middleware::OperationArg>) {
fill_pad(
args,
middleware::OperationArg::None,
params.max_operation_args,
)
}
}
impl MainPod for MockMainPod {
fn verify(&self) -> bool {
// TODO
// - define input_statements as `statements.[self.offset_input_statements()..]`
// - Calculate the id from a subset of the statements. Check it's equal to self.id
// - Find a ValueOf statement from the public statements with key=KEY_TYPE and check that
// the value is PodType::MockMainPod
// - Check that all `input_statements` of type `ValueOf` with origin=SELF have unique keys
// (no duplicates)
// - Verify that all `input_statements` are correctly generated
// by `self.operations` (where each operation can only access previous statements)
todo!()
}
fn id(&self) -> PodId {
self.id
}
fn pub_statements(&self) -> Vec<Statement> {
// TODO: All arguments that use origin=SELF need to be replaced by origin=self.id()
self.statements
.iter()
.skip(self.offset_public_statements())
.cloned()
.collect()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
/// Useful for debugging
pub struct Printer {
pub skip_none: bool,
}
impl Printer {
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::Key(r) => write!(w, "{}.{}", r.0, r.1),
}
}
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(())
}
fn fmt_operation(&self, w: &mut dyn Write, op: &Operation) -> io::Result<()> {
write!(w, "{:?} ", op.0)?;
for (i, arg) in op.1.iter().enumerate() {
if !(self.skip_none && arg.is_none()) {
if i != 0 {
write!(w, " ")?;
}
match arg {
OperationArg::None => write!(w, "none")?,
OperationArg::Index(i) => write!(w, "{:02}", i)?,
}
}
}
Ok(())
}
fn fmt_statement_index(
&self,
w: &mut dyn Write,
st: &Statement,
op: Option<&Operation>,
index: usize,
) -> io::Result<()> {
if !(self.skip_none && st.is_none()) {
write!(w, " {:03}. ", index)?;
self.fmt_statement(w, &st)?;
if let Some(op) = op {
write!(w, " <- ")?;
self.fmt_operation(w, op)?;
}
write!(w, "\n")?;
}
Ok(())
}
pub fn fmt_mock_main_pod(&self, w: &mut dyn Write, pod: &MockMainPod) -> io::Result<()> {
writeln!(w, "MockMainPod ({}):", pod.id)?;
// TODO print input signed pods id and type
// TODO print input main pods id and type
let offset_input_main_pods = pod.offset_input_main_pods();
let offset_input_statements = pod.offset_input_statements();
let offset_public_statements = pod.offset_public_statements();
for (i, st) in pod.statements.iter().enumerate() {
if (i < pod.offset_input_main_pods()) && (i % pod.params.max_signed_pod_values == 0) {
writeln!(
w,
" from input SignedPod {}:",
i / pod.params.max_signed_pod_values
)?;
}
if (i >= offset_input_main_pods)
&& (i < offset_input_statements)
&& (i % pod.params.max_public_statements == 0)
{
writeln!(
w,
" from input MainPod {}:",
(i - offset_input_main_pods) / pod.params.max_signed_pod_values
)?;
}
if i == offset_input_statements {
writeln!(w, " private statements:")?;
}
if i == offset_public_statements {
writeln!(w, " public statements:")?;
}
let op = (i >= offset_input_statements)
.then(|| &pod.operations[i - offset_input_statements]);
if !(self.skip_none && st.is_none()) {
self.fmt_statement_index(w, &st, op, i)?;
}
}
Ok(())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::backends::mock_signed::MockSigner;
use crate::frontend;
use crate::middleware;
#[test]
fn test_mock_main_0() {
let params = middleware::Params::default();
let (gov_id, pay_stub) = frontend::tests::zu_kyc_sign_pod_builders(&params);
let mut signer = MockSigner {
pk: "ZooGov".into(),
};
let gov_id = gov_id.sign(&mut signer).unwrap();
let mut signer = MockSigner {
pk: "ZooDeel".into(),
};
let pay_stub = pay_stub.sign(&mut signer).unwrap();
let kyc = frontend::tests::zu_kyc_pod_builder(&params, &gov_id, &pay_stub);
let mut prover = MockProver {};
let kyc = kyc.prove(&mut prover).unwrap();
let pod = kyc.pod.into_any().downcast::<MockMainPod>().unwrap();
let printer = Printer { skip_none: false };
let mut w = io::stdout();
printer.fmt_mock_main_pod(&mut w, &pod).unwrap();
// assert_eq!(pod.verify(), true); // TODO
// println!("id: {}", pod.id());
// println!("kvs: {:?}", pod.pub_statements());
}
}

View file

@ -26,9 +26,9 @@ impl PodSigner for MockSigner {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MockSignedPod { pub struct MockSignedPod {
pub id: PodId, id: PodId,
pub signature: String, signature: String,
pub mt: MerkleTree, mt: MerkleTree,
} }
impl SignedPod for MockSignedPod { impl SignedPod for MockSignedPod {

View file

@ -222,6 +222,7 @@ pub struct MainPodBuilder {
pub input_main_pods: Vec<MainPod>, pub input_main_pods: Vec<MainPod>,
pub statements: Vec<Statement>, pub statements: Vec<Statement>,
pub operations: Vec<Operation>, pub operations: Vec<Operation>,
pub public_statements: Vec<Statement>,
// Internal state // Internal state
const_cnt: usize, const_cnt: usize,
} }
@ -234,6 +235,7 @@ impl MainPodBuilder {
input_main_pods: Vec::new(), input_main_pods: Vec::new(),
statements: Vec::new(), statements: Vec::new(),
operations: Vec::new(), operations: Vec::new(),
public_statements: Vec::new(),
const_cnt: 0, const_cnt: 0,
} }
} }
@ -250,7 +252,7 @@ impl MainPodBuilder {
} }
/// Convert [OperationArg]s to [StatementArg]s for the operations that work with entries /// Convert [OperationArg]s to [StatementArg]s for the operations that work with entries
fn op_args_entries(&mut self, args: &mut [OperationArg]) -> Vec<StatementArg> { fn op_args_entries(&mut self, public: bool, args: &mut [OperationArg]) -> Vec<StatementArg> {
let mut st_args = Vec::new(); let mut st_args = Vec::new();
for arg in args.iter_mut() { for arg in args.iter_mut() {
match arg { match arg {
@ -259,10 +261,13 @@ impl MainPodBuilder {
OperationArg::Literal(v) => { OperationArg::Literal(v) => {
let k = format!("c{}", self.const_cnt); let k = format!("c{}", self.const_cnt);
self.const_cnt += 1; self.const_cnt += 1;
let value_of_st = self.op(Operation( let value_of_st = self.op(
NativeOperation::NewEntry, public,
vec![OperationArg::Entry(k.clone(), v.clone())], Operation(
)); NativeOperation::NewEntry,
vec![OperationArg::Entry(k.clone(), v.clone())],
),
);
*arg = OperationArg::Key(AnchoredKey(Origin(PodClass::Main, SELF), k.clone())); *arg = OperationArg::Key(AnchoredKey(Origin(PodClass::Main, SELF), k.clone()));
st_args.push(value_of_st.1[0].clone()) st_args.push(value_of_st.1[0].clone())
} }
@ -278,33 +283,53 @@ impl MainPodBuilder {
st_args st_args
} }
pub fn op(&mut self, mut op: Operation) -> &Statement { pub fn pub_op(&mut self, op: Operation) -> Statement {
self.op(true, op)
}
pub fn op(&mut self, public: bool, mut op: Operation) -> Statement {
use NativeOperation::*; use NativeOperation::*;
let Operation(op_type, ref mut args) = op; let Operation(op_type, ref mut args) = op;
// TODO: argument type checking // TODO: argument type checking
let st = match op_type { let st = match op_type {
None => Statement(NativeStatement::None, vec![]), None => Statement(NativeStatement::None, vec![]),
NewEntry => Statement(NativeStatement::ValueOf, self.op_args_entries(args)), NewEntry => Statement(NativeStatement::ValueOf, self.op_args_entries(public, args)),
CopyStatement => todo!(), CopyStatement => todo!(),
EqualFromEntries => Statement(NativeStatement::Equal, self.op_args_entries(args)), EqualFromEntries => {
NotEqualFromEntries => Statement(NativeStatement::NotEqual, self.op_args_entries(args)), Statement(NativeStatement::Equal, self.op_args_entries(public, args))
GtFromEntries => Statement(NativeStatement::Gt, self.op_args_entries(args)), }
LtFromEntries => Statement(NativeStatement::Lt, self.op_args_entries(args)), NotEqualFromEntries => Statement(
NativeStatement::NotEqual,
self.op_args_entries(public, args),
),
GtFromEntries => Statement(NativeStatement::Gt, self.op_args_entries(public, args)),
LtFromEntries => Statement(NativeStatement::Lt, self.op_args_entries(public, args)),
TransitiveEqualFromStatements => todo!(), TransitiveEqualFromStatements => todo!(),
GtToNotEqual => todo!(), GtToNotEqual => todo!(),
LtToNotEqual => todo!(), LtToNotEqual => todo!(),
ContainsFromEntries => Statement(NativeStatement::Contains, self.op_args_entries(args)), ContainsFromEntries => Statement(
NotContainsFromEntries => { NativeStatement::Contains,
Statement(NativeStatement::NotContains, self.op_args_entries(args)) self.op_args_entries(public, args),
} ),
NotContainsFromEntries => Statement(
NativeStatement::NotContains,
self.op_args_entries(public, args),
),
RenameContainedBy => todo!(), RenameContainedBy => todo!(),
SumOf => todo!(), SumOf => todo!(),
ProductOf => todo!(), ProductOf => todo!(),
MaxOf => todo!(), MaxOf => todo!(),
}; };
self.operations.push(op); self.operations.push(op);
if public {
self.public_statements.push(st.clone());
}
self.statements.push(st); self.statements.push(st);
&self.statements[self.statements.len() - 1] self.statements[self.statements.len() - 1].clone()
}
pub fn reveal(&mut self, st: &Statement) {
self.public_statements.push(st.clone());
} }
pub fn prove<P: PodProver>(&self, prover: &mut P) -> Result<MainPod> { pub fn prove<P: PodProver>(&self, prover: &mut P) -> Result<MainPod> {
@ -314,22 +339,17 @@ impl MainPodBuilder {
// main_pods: &self.input_main_pods, // main_pods: &self.input_main_pods,
statements: &self.statements, statements: &self.statements,
operations: &self.operations, operations: &self.operations,
public_statements: &self.public_statements,
}; };
let (statements, operations) = compiler.compile(inputs)?; let (statements, operations, public_statements) = compiler.compile(inputs)?;
// TODO: Add API to specify public/private statement
let inputs = MainPodInputs { let inputs = MainPodInputs {
signed_pods: &self signed_pods: &self.input_signed_pods.iter().map(|p| &p.pod).collect_vec(),
.input_signed_pods main_pods: &self.input_main_pods.iter().map(|p| &p.pod).collect_vec(),
.iter()
.map(|p| p.pod.as_ref())
.collect_vec(),
main_pods: &self
.input_main_pods
.iter()
.map(|p| p.pod.as_ref())
.collect_vec(),
statements: &statements, statements: &statements,
operations: &operations, operations: &operations,
public_statements: &public_statements,
}; };
let pod = prover.prove(&self.params, inputs)?; let pod = prover.prove(&self.params, inputs)?;
Ok(MainPod { pod }) Ok(MainPod { pod })
@ -356,6 +376,7 @@ struct MainPodCompilerInputs<'a> {
// pub main_pods: &'a [Box<dyn middleware::MainPod>], // pub main_pods: &'a [Box<dyn middleware::MainPod>],
pub statements: &'a [Statement], pub statements: &'a [Statement],
pub operations: &'a [Operation], pub operations: &'a [Operation],
pub public_statements: &'a [Statement],
} }
struct MainPodCompiler { struct MainPodCompiler {
@ -438,12 +459,17 @@ impl MainPodCompiler {
pub fn compile<'a>( pub fn compile<'a>(
mut self, mut self,
inputs: MainPodCompilerInputs<'a>, inputs: MainPodCompilerInputs<'a>,
) -> Result<(Vec<middleware::Statement>, Vec<middleware::Operation>)> { ) -> Result<(
Vec<middleware::Statement>, // input statements
Vec<middleware::Operation>,
Vec<middleware::Statement>, // public statements
)> {
let MainPodCompilerInputs { let MainPodCompilerInputs {
// signed_pods: _, // signed_pods: _,
// main_pods: _, // main_pods: _,
statements, statements,
operations, operations,
public_statements,
} = inputs; } = inputs;
for (st, op) in statements.iter().zip_eq(operations.iter()) { for (st, op) in statements.iter().zip_eq(operations.iter()) {
self.compile_st_op(st, op); self.compile_st_op(st, op);
@ -451,7 +477,11 @@ impl MainPodCompiler {
panic!("too many statements"); panic!("too many statements");
} }
} }
Ok((self.statements, self.operations)) let public_statements = public_statements
.iter()
.map(|st| self.compile_st(st))
.collect_vec();
Ok((self.statements, self.operations, public_statements))
} }
} }
@ -558,14 +588,14 @@ pub mod tests {
let mut kyc = MainPodBuilder::new(&params); let mut kyc = MainPodBuilder::new(&params);
kyc.add_signed_pod(&gov_id); kyc.add_signed_pod(&gov_id);
kyc.add_signed_pod(&pay_stub); kyc.add_signed_pod(&pay_stub);
kyc.op(op!(not_contains, &sanction_list, (gov_id, "idNumber"))); kyc.pub_op(op!(not_contains, &sanction_list, (gov_id, "idNumber")));
kyc.op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y)); kyc.pub_op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y));
kyc.op(op!( kyc.pub_op(op!(
eq, eq,
(gov_id, "socialSecurityNumber"), (gov_id, "socialSecurityNumber"),
(pay_stub, "socialSecurityNumber") (pay_stub, "socialSecurityNumber")
)); ));
kyc.op(op!(eq, (pay_stub, "startDate"), now_minus_1y)); kyc.pub_op(op!(eq, (pay_stub, "startDate"), now_minus_1y));
kyc kyc
} }

View file

@ -1,4 +1,3 @@
pub mod backend;
pub mod backends; pub mod backends;
pub mod frontend; pub mod frontend;
pub mod merkletree; pub mod merkletree;

View file

@ -171,6 +171,7 @@ pub struct Params {
pub max_signed_pod_values: usize, pub max_signed_pod_values: usize,
pub max_public_statements: usize, pub max_public_statements: usize,
pub max_statement_args: usize, pub max_statement_args: usize,
pub max_operation_args: usize,
} }
impl Params { impl Params {
@ -188,6 +189,7 @@ impl Default for Params {
max_signed_pod_values: 8, max_signed_pod_values: 8,
max_public_statements: 10, max_public_statements: 10,
max_statement_args: 5, max_statement_args: 5,
max_operation_args: 5,
} }
} }
} }
@ -220,6 +222,29 @@ pub trait SignedPod: fmt::Debug + DynClone {
// impl Clone for Box<dyn SignedPod> // impl Clone for Box<dyn SignedPod>
dyn_clone::clone_trait_object!(SignedPod); dyn_clone::clone_trait_object!(SignedPod);
/// This is a filler type that fulfills the SignedPod trait and always verifies. It's empty. This
/// can be used to simulate padding in a circuit.
#[derive(Debug, Clone)]
pub struct NoneSignedPod {}
impl SignedPod for NoneSignedPod {
fn verify(&self) -> bool {
true
}
fn id(&self) -> PodId {
PodId(NULL)
}
fn kvs(&self) -> HashMap<Hash, Value> {
HashMap::new()
}
fn pub_statements(&self) -> Vec<Statement> {
Vec::new()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
pub trait PodSigner { pub trait PodSigner {
fn sign(&mut self, params: &Params, kvs: &HashMap<Hash, Value>) -> Result<Box<dyn SignedPod>>; fn sign(&mut self, params: &Params, kvs: &HashMap<Hash, Value>) -> Result<Box<dyn SignedPod>>;
} }
@ -305,12 +330,38 @@ pub trait MainPod: fmt::Debug + DynClone {
// impl Clone for Box<dyn SignedPod> // impl Clone for Box<dyn SignedPod>
dyn_clone::clone_trait_object!(MainPod); dyn_clone::clone_trait_object!(MainPod);
/// This is a filler type that fulfills the MainPod trait and always verifies. It's empty. This
/// can be used to simulate padding in a circuit.
#[derive(Debug, Clone)]
pub struct NoneMainPod {}
impl MainPod for NoneMainPod {
fn verify(&self) -> bool {
true
}
fn id(&self) -> PodId {
PodId(NULL)
}
fn pub_statements(&self) -> Vec<Statement> {
Vec::new()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
// TODO: Figure out a way to signal which signed_pods entries and which main_pods statements need
// to be made public. Idea: introduce an operation called reveal, which the backend translates to
// CopyOf but moves copies that statement to a public slot?
#[derive(Debug)] #[derive(Debug)]
pub struct MainPodInputs<'a> { pub struct MainPodInputs<'a> {
pub signed_pods: &'a [&'a dyn SignedPod], pub signed_pods: &'a [&'a Box<dyn SignedPod>],
pub main_pods: &'a [&'a dyn MainPod], pub main_pods: &'a [&'a Box<dyn MainPod>],
pub statements: &'a [Statement], pub statements: &'a [Statement],
pub operations: &'a [Operation], pub operations: &'a [Operation],
/// Statements that need to be made public (they can come from input pods or input
/// statements)
pub public_statements: &'a [Statement],
} }
pub trait PodProver { pub trait PodProver {