Frontend work (#109)
This commit is contained in:
parent
7eeb595dc2
commit
9d60b0ec3a
9 changed files with 611 additions and 262 deletions
|
|
@ -5,6 +5,7 @@ use anyhow::{anyhow, Error, Result};
|
|||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::From;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, hash as h};
|
||||
|
||||
use crate::middleware::{
|
||||
|
|
@ -13,7 +14,7 @@ use crate::middleware::{
|
|||
hash_str, Hash, MainPodInputs, NativeOperation, NativePredicate, Params, PodId, PodProver,
|
||||
PodSigner, SELF,
|
||||
};
|
||||
use crate::middleware::{OperationType, Predicate};
|
||||
use crate::middleware::{OperationType, Predicate, KEY_SIGNER};
|
||||
|
||||
mod custom;
|
||||
mod operation;
|
||||
|
|
@ -77,6 +78,12 @@ impl From<&Value> for middleware::Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<middleware::Value> for Value {
|
||||
fn from(v: middleware::Value) -> Self {
|
||||
Self::Raw(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
|
@ -122,15 +129,19 @@ impl SignedPodBuilder {
|
|||
pub fn sign<S: PodSigner>(&self, signer: &mut S) -> Result<SignedPod> {
|
||||
let mut kvs = HashMap::new();
|
||||
let mut key_string_map = HashMap::new();
|
||||
let mut value_hash_map = HashMap::new();
|
||||
for (k, v) in self.kvs.iter() {
|
||||
let k_hash = hash_str(k);
|
||||
kvs.insert(k_hash, middleware::Value::from(v));
|
||||
let v_hash = middleware::Value::from(v);
|
||||
kvs.insert(k_hash, v_hash);
|
||||
key_string_map.insert(k_hash, k.clone());
|
||||
value_hash_map.insert(v_hash.into(), v.clone());
|
||||
}
|
||||
let pod = signer.sign(&self.params, &kvs)?;
|
||||
Ok(SignedPod {
|
||||
pod,
|
||||
key_string_map,
|
||||
value_hash_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -142,6 +153,8 @@ pub struct SignedPod {
|
|||
pub pod: Box<dyn middleware::Pod>,
|
||||
/// HashMap to store the reverse relation between key strings and key hashes
|
||||
pub key_string_map: HashMap<Hash, String>,
|
||||
/// HashMap to store the reverse relation between values and their hashes
|
||||
pub value_hash_map: HashMap<Hash, Value>,
|
||||
}
|
||||
|
||||
impl fmt::Display for SignedPod {
|
||||
|
|
@ -202,6 +215,8 @@ pub struct MainPodBuilder {
|
|||
pub public_statements: Vec<Statement>,
|
||||
// Internal state
|
||||
const_cnt: usize,
|
||||
key_table: HashMap<Hash, String>,
|
||||
pod_class_table: HashMap<PodId, PodClass>,
|
||||
}
|
||||
|
||||
impl fmt::Display for MainPodBuilder {
|
||||
|
|
@ -235,12 +250,26 @@ impl MainPodBuilder {
|
|||
operations: Vec::new(),
|
||||
public_statements: Vec::new(),
|
||||
const_cnt: 0,
|
||||
key_table: HashMap::new(),
|
||||
pod_class_table: HashMap::from_iter([(SELF, PodClass::Main)].into_iter()),
|
||||
}
|
||||
}
|
||||
pub fn add_signed_pod(&mut self, pod: &SignedPod) {
|
||||
self.input_signed_pods.push(pod.clone());
|
||||
pod.key_string_map.iter().for_each(|(hash, key)| {
|
||||
self.key_table.insert(hash.clone(), key.clone());
|
||||
});
|
||||
self.pod_class_table.insert(pod.id(), PodClass::Signed);
|
||||
}
|
||||
pub fn add_main_pod(&mut self, pod: MainPod) {
|
||||
self.pod_class_table.insert(pod.id(), PodClass::Main);
|
||||
pod.key_string_map.iter().for_each(|(hash, key)| {
|
||||
self.key_table.insert(hash.clone(), key.clone());
|
||||
});
|
||||
pod.pod_class_map.iter().for_each(|(pod_id, pod_class)| {
|
||||
self.pod_class_table
|
||||
.insert(pod_id.clone(), pod_class.clone());
|
||||
});
|
||||
self.input_main_pods.push(pod);
|
||||
}
|
||||
pub fn insert(&mut self, st_op: (Statement, Operation)) {
|
||||
|
|
@ -250,7 +279,11 @@ impl MainPodBuilder {
|
|||
}
|
||||
|
||||
/// Convert [OperationArg]s to [StatementArg]s for the operations that work with entries
|
||||
fn op_args_entries(&mut self, public: bool, args: &mut [OperationArg]) -> Vec<StatementArg> {
|
||||
fn op_args_entries(
|
||||
&mut self,
|
||||
public: bool,
|
||||
args: &mut [OperationArg],
|
||||
) -> Result<Vec<StatementArg>> {
|
||||
let mut st_args = Vec::new();
|
||||
for arg in args.iter_mut() {
|
||||
match arg {
|
||||
|
|
@ -270,7 +303,7 @@ impl MainPodBuilder {
|
|||
OperationType::Native(NativeOperation::NewEntry),
|
||||
vec![OperationArg::Entry(k.clone(), v.clone())],
|
||||
),
|
||||
);
|
||||
)?;
|
||||
*arg = OperationArg::Statement(value_of_st.clone());
|
||||
st_args.push(value_of_st.1[0].clone())
|
||||
}
|
||||
|
|
@ -283,14 +316,18 @@ impl MainPodBuilder {
|
|||
}
|
||||
};
|
||||
}
|
||||
st_args
|
||||
Ok(st_args)
|
||||
}
|
||||
|
||||
pub fn pub_op(&mut self, op: Operation) -> Statement {
|
||||
pub fn pub_op(&mut self, op: Operation) -> Result<Statement> {
|
||||
self.op(true, op)
|
||||
}
|
||||
|
||||
pub fn op(&mut self, public: bool, mut op: Operation) -> Statement {
|
||||
pub fn priv_op(&mut self, op: Operation) -> Result<Statement> {
|
||||
self.op(false, op)
|
||||
}
|
||||
|
||||
fn op(&mut self, public: bool, mut op: Operation) -> Result<Statement> {
|
||||
use NativeOperation::*;
|
||||
let Operation(op_type, ref mut args) = &mut op;
|
||||
// TODO: argument type checking
|
||||
|
|
@ -299,49 +336,96 @@ impl MainPodBuilder {
|
|||
None => Statement(Predicate::Native(NativePredicate::None), vec![]),
|
||||
NewEntry => Statement(
|
||||
Predicate::Native(NativePredicate::ValueOf),
|
||||
self.op_args_entries(public, args),
|
||||
self.op_args_entries(public, args)?,
|
||||
),
|
||||
CopyStatement => todo!(),
|
||||
EqualFromEntries => Statement(
|
||||
Predicate::Native(NativePredicate::Equal),
|
||||
self.op_args_entries(public, args),
|
||||
self.op_args_entries(public, args)?,
|
||||
),
|
||||
NotEqualFromEntries => Statement(
|
||||
Predicate::Native(NativePredicate::NotEqual),
|
||||
self.op_args_entries(public, args),
|
||||
self.op_args_entries(public, args)?,
|
||||
),
|
||||
GtFromEntries => Statement(
|
||||
Predicate::Native(NativePredicate::Gt),
|
||||
self.op_args_entries(public, args),
|
||||
self.op_args_entries(public, args)?,
|
||||
),
|
||||
LtFromEntries => Statement(
|
||||
Predicate::Native(NativePredicate::Lt),
|
||||
self.op_args_entries(public, args),
|
||||
self.op_args_entries(public, args)?,
|
||||
),
|
||||
TransitiveEqualFromStatements => todo!(),
|
||||
GtToNotEqual => todo!(),
|
||||
LtToNotEqual => todo!(),
|
||||
ContainsFromEntries => Statement(
|
||||
Predicate::Native(NativePredicate::Contains),
|
||||
self.op_args_entries(public, args),
|
||||
self.op_args_entries(public, args)?,
|
||||
),
|
||||
NotContainsFromEntries => Statement(
|
||||
Predicate::Native(NativePredicate::NotContains),
|
||||
self.op_args_entries(public, args),
|
||||
self.op_args_entries(public, args)?,
|
||||
),
|
||||
RenameContainedBy => todo!(),
|
||||
SumOf => todo!(),
|
||||
ProductOf => todo!(),
|
||||
MaxOf => todo!(),
|
||||
},
|
||||
_ => todo!(),
|
||||
OperationType::Custom(cpr) => {
|
||||
// All args should be statements to be pattern matched against statement templates.
|
||||
let args = args.iter().map(
|
||||
|a| match a {
|
||||
OperationArg::Statement(s) => middleware::Statement::try_from(s.clone()),
|
||||
_ => Err(anyhow!("Invalid argument {} to operation corresponding to custom predicate {:?}.", a, cpr))
|
||||
}
|
||||
).collect::<Result<Vec<_>>>()?;
|
||||
// Match these statements against the custom predicate definition
|
||||
let bindings = cpr.match_against(&args)?;
|
||||
let output_arg_values = (0..cpr.arg_len())
|
||||
.map(|i| {
|
||||
bindings.get(&i).cloned().ok_or(anyhow!(
|
||||
"Wildcard {} of custom predicate {:?} is unbound.",
|
||||
i,
|
||||
cpr
|
||||
))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let output_args = output_arg_values
|
||||
.chunks(2)
|
||||
.map(|chunk| {
|
||||
Ok(StatementArg::Key(AnchoredKey(
|
||||
Origin(
|
||||
self.pod_class_table
|
||||
.get(&PodId(chunk[0].into()))
|
||||
.cloned()
|
||||
.ok_or(anyhow!("Missing POD class value."))?,
|
||||
PodId(chunk[0].into()),
|
||||
),
|
||||
self.key_table
|
||||
.get(&chunk[1].into())
|
||||
.cloned()
|
||||
.ok_or(anyhow!("Missing key corresponding to hash."))?,
|
||||
)))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Statement(Predicate::Custom(cpr.clone()), output_args)
|
||||
}
|
||||
};
|
||||
self.operations.push(op);
|
||||
if public {
|
||||
self.public_statements.push(st.clone());
|
||||
}
|
||||
|
||||
// Add key-hash pairs in statement to table.
|
||||
(&st).1.iter().for_each(|arg| match arg {
|
||||
StatementArg::Key(AnchoredKey(_, key)) => {
|
||||
self.key_table.insert(hash_str(key), key.clone());
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
|
||||
self.statements.push(st);
|
||||
self.statements[self.statements.len() - 1].clone()
|
||||
Ok(self.statements[self.statements.len() - 1].clone())
|
||||
}
|
||||
|
||||
pub fn reveal(&mut self, st: &Statement) {
|
||||
|
|
@ -357,8 +441,29 @@ impl MainPodBuilder {
|
|||
operations: &self.operations,
|
||||
public_statements: &self.public_statements,
|
||||
};
|
||||
let (statements, operations, public_statements) = compiler.compile(inputs, params)?;
|
||||
let key_string_map = inputs
|
||||
.public_statements
|
||||
.iter()
|
||||
.flat_map(|s| &s.1)
|
||||
.flat_map(|arg| match arg {
|
||||
StatementArg::Key(AnchoredKey(_, key)) => Some((hash_str(key), key.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let pod_class_map = (&inputs)
|
||||
.public_statements
|
||||
.into_iter()
|
||||
.flat_map(|s| &s.1)
|
||||
.flat_map(|arg| match arg {
|
||||
StatementArg::Key(AnchoredKey(Origin(pod_class, pod_id), _)) => {
|
||||
Some((pod_id.clone(), pod_class.clone()))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let (statements, operations, public_statements) = compiler.compile(inputs, params)?;
|
||||
let inputs = MainPodInputs {
|
||||
signed_pods: &self.input_signed_pods.iter().map(|p| &p.pod).collect_vec(),
|
||||
main_pods: &self.input_main_pods.iter().map(|p| &p.pod).collect_vec(),
|
||||
|
|
@ -367,7 +472,11 @@ impl MainPodBuilder {
|
|||
public_statements: &public_statements,
|
||||
};
|
||||
let pod = prover.prove(&self.params, inputs)?;
|
||||
Ok(MainPod { pod })
|
||||
Ok(MainPod {
|
||||
pod,
|
||||
key_string_map,
|
||||
pod_class_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -375,6 +484,8 @@ impl MainPodBuilder {
|
|||
pub struct MainPod {
|
||||
pub pod: Box<dyn middleware::Pod>,
|
||||
// TODO: metadata
|
||||
pub key_string_map: HashMap<Hash, String>,
|
||||
pub pod_class_map: HashMap<PodId, PodClass>,
|
||||
}
|
||||
|
||||
impl fmt::Display for MainPod {
|
||||
|
|
@ -433,7 +544,7 @@ impl MainPodCompiler {
|
|||
|
||||
fn compile_op_arg(&self, op_arg: &OperationArg) -> Option<middleware::Statement> {
|
||||
match op_arg {
|
||||
OperationArg::Statement(s) => Some(self.compile_st(s)),
|
||||
OperationArg::Statement(s) => self.compile_st(s).ok(),
|
||||
OperationArg::Literal(_v) => {
|
||||
// OperationArg::Literal is a syntax sugar for the frontend. This is translated to
|
||||
// a new ValueOf statement and it's key used instead.
|
||||
|
|
@ -448,23 +559,23 @@ impl MainPodCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
fn compile_st(&self, st: &Statement) -> middleware::Statement {
|
||||
st.clone().try_into().unwrap()
|
||||
fn compile_st(&self, st: &Statement) -> Result<middleware::Statement> {
|
||||
st.clone().try_into()
|
||||
}
|
||||
|
||||
fn compile_op(&self, op: &Operation) -> middleware::Operation {
|
||||
fn compile_op(&self, op: &Operation) -> Result<middleware::Operation> {
|
||||
// TODO
|
||||
let mop_code: OperationType = op.0.clone();
|
||||
let mop_args =
|
||||
op.1.iter()
|
||||
.flat_map(|arg| self.compile_op_arg(arg).map(|s| s.try_into().unwrap()))
|
||||
.collect::<Vec<middleware::Statement>>();
|
||||
middleware::Operation::op(mop_code, &mop_args).unwrap()
|
||||
.flat_map(|arg| self.compile_op_arg(arg).map(|s| Ok(s.try_into()?)))
|
||||
.collect::<Result<Vec<middleware::Statement>>>()?;
|
||||
middleware::Operation::op(mop_code, &mop_args)
|
||||
}
|
||||
|
||||
fn compile_st_op(&mut self, st: &Statement, op: &Operation, params: &Params) -> Result<()> {
|
||||
let middle_st = self.compile_st(st);
|
||||
let middle_op = self.compile_op(op);
|
||||
let middle_st = self.compile_st(st)?;
|
||||
let middle_op = self.compile_op(op)?;
|
||||
let is_correct = middle_op.check(params, &middle_st)?;
|
||||
if !is_correct {
|
||||
// todo: improve error handling
|
||||
|
|
@ -504,7 +615,7 @@ impl MainPodCompiler {
|
|||
let public_statements = public_statements
|
||||
.iter()
|
||||
.map(|st| self.compile_st(st))
|
||||
.collect_vec();
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Ok((self.statements, self.operations, public_statements))
|
||||
}
|
||||
}
|
||||
|
|
@ -548,8 +659,8 @@ pub mod tests {
|
|||
use crate::backends::plonky2::mock_main::MockProver;
|
||||
use crate::backends::plonky2::mock_signed::MockSigner;
|
||||
use crate::examples::{
|
||||
great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder,
|
||||
zu_kyc_sign_pod_builders,
|
||||
eth_dos_pod_builder, eth_friend_signed_pod_builder, great_boy_pod_full_flow,
|
||||
tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
@ -563,19 +674,19 @@ pub mod tests {
|
|||
let mut signer = MockSigner {
|
||||
pk: "ZooGov".into(),
|
||||
};
|
||||
let gov_id = gov_id.sign(&mut signer).unwrap();
|
||||
let gov_id = gov_id.sign(&mut signer)?;
|
||||
println!("{}", gov_id);
|
||||
|
||||
let mut signer = MockSigner {
|
||||
pk: "ZooDeel".into(),
|
||||
};
|
||||
let pay_stub = pay_stub.sign(&mut signer).unwrap();
|
||||
let pay_stub = pay_stub.sign(&mut signer)?;
|
||||
println!("{}", pay_stub);
|
||||
|
||||
let mut signer = MockSigner {
|
||||
pk: "ZooOFAC".into(),
|
||||
};
|
||||
let sanction_list = sanction_list.sign(&mut signer).unwrap();
|
||||
let sanction_list = sanction_list.sign(&mut signer)?;
|
||||
println!("{}", sanction_list);
|
||||
|
||||
let kyc = zu_kyc_pod_builder(¶ms, &gov_id, &pay_stub, &sanction_list)?;
|
||||
|
|
@ -590,6 +701,35 @@ pub mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ethdos() -> Result<()> {
|
||||
let params = Params::default();
|
||||
|
||||
let mut alice = MockSigner { pk: "Alice".into() };
|
||||
let mut bob = MockSigner { pk: "Bob".into() };
|
||||
let mut charlie = MockSigner {
|
||||
pk: "Charlie".into(),
|
||||
};
|
||||
|
||||
// Alice attests that she is ETH friends with Charlie and Charlie
|
||||
// attests that he is ETH friends with Bob.
|
||||
let alice_attestation =
|
||||
eth_friend_signed_pod_builder(¶ms, charlie.pubkey().into()).sign(&mut alice)?;
|
||||
let charlie_attestation =
|
||||
eth_friend_signed_pod_builder(¶ms, bob.pubkey().into()).sign(&mut charlie)?;
|
||||
|
||||
let mut prover = MockProver {};
|
||||
let alice_bob_ethdos = eth_dos_pod_builder(
|
||||
¶ms,
|
||||
&alice_attestation,
|
||||
&charlie_attestation,
|
||||
&bob.pubkey().into(),
|
||||
)?
|
||||
.prove(&mut prover, ¶ms)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_front_great_boy() -> Result<()> {
|
||||
let great_boy = great_boy_pod_full_flow()?;
|
||||
|
|
@ -625,7 +765,7 @@ pub mod tests {
|
|||
|
||||
let mut builder = MainPodBuilder::new(¶ms);
|
||||
builder.add_signed_pod(&pod);
|
||||
builder.pub_op(op!(gt, (&pod, "num"), 5));
|
||||
builder.pub_op(op!(gt, (&pod, "num"), 5)).unwrap();
|
||||
|
||||
let mut prover = MockProver {};
|
||||
let false_pod = builder.prove(&mut prover, ¶ms).unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue