add SignerPod.id computation (integrate kvs with MerkleTree) (#17)
- Add SignerPod.id computation (integrate kvs with MerkleTree). - Updates frontend::SignerPod to be a wrapper on top of backend::SignerPod with extra metadata (a keymap between hashes and their strings). - Get's rid of SignerPod.compile() since now the frontend::SignerPod uses the method `::new()` which internally calls backend::SignerPod::new which constructs the merkletree to use it's root as PodID.
This commit is contained in:
parent
5b455acbd6
commit
aafebfbcd5
3 changed files with 111 additions and 61 deletions
|
|
@ -1,3 +1,5 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use itertools::Itertools;
|
||||||
use plonky2::field::types::{Field, PrimeField64};
|
use plonky2::field::types::{Field, PrimeField64};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
@ -5,7 +7,7 @@ use std::io::{self, Write};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use strum_macros::FromRepr;
|
use strum_macros::FromRepr;
|
||||||
|
|
||||||
use crate::{Hash, Params, PodId, F, NULL};
|
use crate::{merkletree::MerkleTree, Hash, Params, PodId, F, NULL};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq)]
|
||||||
pub enum NativeStatement {
|
pub enum NativeStatement {
|
||||||
|
|
@ -69,10 +71,20 @@ impl Statement {
|
||||||
pub struct SignedPod {
|
pub struct SignedPod {
|
||||||
pub params: Params,
|
pub params: Params,
|
||||||
pub id: PodId,
|
pub id: PodId,
|
||||||
pub kvs: HashMap<Hash, Value>,
|
pub kvs: MerkleTree,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignedPod {
|
impl SignedPod {
|
||||||
|
pub fn new(params: &Params, kvs: HashMap<Hash, Value>) -> Result<Self> {
|
||||||
|
let mt = MerkleTree::new(kvs);
|
||||||
|
let root = mt.root()?;
|
||||||
|
Ok(Self {
|
||||||
|
params: *params,
|
||||||
|
id: PodId(root),
|
||||||
|
kvs: mt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_null(&self) -> bool {
|
pub fn is_null(&self) -> bool {
|
||||||
self.id.0 == NULL
|
self.id.0 == NULL
|
||||||
}
|
}
|
||||||
|
|
@ -101,15 +113,15 @@ impl MainPod {
|
||||||
mut input_signed_pods: Vec<SignedPod>,
|
mut input_signed_pods: Vec<SignedPod>,
|
||||||
input_main_pods: Vec<MainPod>,
|
input_main_pods: Vec<MainPod>,
|
||||||
mut statements: Vec<Statement>,
|
mut statements: Vec<Statement>,
|
||||||
) -> Self {
|
) -> Result<Self> {
|
||||||
Self::pad_statements(¶ms, &mut statements, params.max_statements);
|
Self::pad_statements(¶ms, &mut statements, params.max_statements);
|
||||||
Self::pad_input_signed_pods(¶ms, &mut input_signed_pods);
|
Self::pad_input_signed_pods(¶ms, &mut input_signed_pods)?;
|
||||||
Self {
|
Ok(Self {
|
||||||
params,
|
params,
|
||||||
id: PodId::default(), // TODO
|
id: PodId::default(), // TODO
|
||||||
input_signed_pods,
|
input_signed_pods,
|
||||||
statements,
|
statements,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_none(params: &Params) -> Statement {
|
fn statement_none(params: &Params) -> Statement {
|
||||||
|
|
@ -129,13 +141,9 @@ impl MainPod {
|
||||||
fill_pad(args, StatementArg::None, params.max_statement_args)
|
fill_pad(args, StatementArg::None, params.max_statement_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pad_input_signed_pods(params: &Params, pods: &mut Vec<SignedPod>) {
|
fn pad_input_signed_pods(params: &Params, pods: &mut Vec<SignedPod>) -> Result<()> {
|
||||||
let pod_none = SignedPod {
|
let pod_none = SignedPod::new(params, HashMap::new())?;
|
||||||
params: params.clone(),
|
Ok(fill_pad(pods, pod_none, params.max_input_signed_pods))
|
||||||
id: PodId::default(),
|
|
||||||
kvs: HashMap::new(),
|
|
||||||
};
|
|
||||||
fill_pad(pods, pod_none, params.max_input_signed_pods)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_signed_pods_statements(&self) -> Vec<Vec<Statement>> {
|
pub fn input_signed_pods_statements(&self) -> Vec<Vec<Statement>> {
|
||||||
|
|
@ -200,9 +208,11 @@ impl Printer {
|
||||||
|
|
||||||
pub fn fmt_signed_pod(&self, w: &mut dyn Write, pod: &SignedPod) -> io::Result<()> {
|
pub fn fmt_signed_pod(&self, w: &mut dyn Write, pod: &SignedPod) -> io::Result<()> {
|
||||||
writeln!(w, "SignedPod ({}):", pod.id)?;
|
writeln!(w, "SignedPod ({}):", pod.id)?;
|
||||||
// for (k, v) in pod.kvs.iter().sorted_by_key(|kv| kv.0) {
|
// Note: current version iterates sorting by keys of the kvs, but the merkletree defined at
|
||||||
// TODO: print in a stable order
|
// https://0xparc.github.io/pod2/merkletree.html will not need it since it will be
|
||||||
for (k, v) in pod.kvs.iter() {
|
// 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)?;
|
writeln!(w, " - {}: {}", k, v)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -279,18 +289,20 @@ mod tests {
|
||||||
use crate::frontend;
|
use crate::frontend;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_back_0() {
|
fn test_back_0() -> Result<()> {
|
||||||
let params = Params::default();
|
let params = Params::default();
|
||||||
let (front_gov_id, front_pay_stub, front_kyc) = frontend::tests::data_zu_kyc(params);
|
let (front_gov_id, front_pay_stub, front_kyc) = frontend::tests::data_zu_kyc(params)?;
|
||||||
let gov_id = front_gov_id.compile();
|
let gov_id = front_gov_id.pod; // get backend's pod
|
||||||
let pay_stub = front_pay_stub.compile();
|
let pay_stub = front_pay_stub.pod; // get backend's pod
|
||||||
let kyc = front_kyc.compile();
|
let kyc = front_kyc.compile()?;
|
||||||
// println!("{:#?}", kyc);
|
// println!("{:#?}", kyc);
|
||||||
|
|
||||||
let printer = Printer { skip_none: true };
|
let printer = Printer { skip_none: true };
|
||||||
let mut w = io::stdout();
|
let mut w = io::stdout();
|
||||||
printer.fmt_signed_pod(&mut w, &gov_id).unwrap();
|
printer.fmt_signed_pod(&mut w, &gov_id)?;
|
||||||
printer.fmt_signed_pod(&mut w, &pay_stub).unwrap();
|
printer.fmt_signed_pod(&mut w, &pay_stub)?;
|
||||||
printer.fmt_main_pod(&mut w, &kyc).unwrap();
|
printer.fmt_main_pod(&mut w, &kyc)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
@ -71,27 +72,42 @@ impl fmt::Display for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SignedPod is a wrapper on top of backend::SignedPod, which additionally stores the
|
||||||
|
/// string<-->hash relation of the keys.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SignedPod {
|
pub struct SignedPod {
|
||||||
pub params: Params,
|
pub pod: backend::SignedPod,
|
||||||
pub id: PodId,
|
// `string_key_map` is a hashmap to store the relation between key strings and key hashes
|
||||||
pub kvs: HashMap<String, Value>,
|
// TODO review if maybe store it as <Hash, String>, so that when iterating we can get each
|
||||||
|
// hash respective string
|
||||||
|
string_key_map: HashMap<String, crate::Hash>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignedPod {
|
impl SignedPod {
|
||||||
|
pub fn new(params: &Params, kvs: HashMap<String, Value>) -> Result<Self> {
|
||||||
|
let (hashed_kvs, string_key_map): (
|
||||||
|
HashMap<crate::Hash, backend::Value>,
|
||||||
|
HashMap<String, crate::Hash>,
|
||||||
|
) = kvs.iter().fold(
|
||||||
|
(HashMap::new(), HashMap::new()),
|
||||||
|
|(mut hashed_kvs, mut key_to_hash), (k, v)| {
|
||||||
|
let h = hash_str(k);
|
||||||
|
hashed_kvs.insert(h, backend::Value::from(v));
|
||||||
|
key_to_hash.insert(k.clone(), h);
|
||||||
|
(hashed_kvs, key_to_hash)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let pod = backend::SignedPod::new(params, hashed_kvs)?;
|
||||||
|
Ok(Self {
|
||||||
|
pod,
|
||||||
|
string_key_map,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn id(&self) -> PodId {
|
||||||
|
self.pod.id
|
||||||
|
}
|
||||||
pub fn origin(&self) -> Origin {
|
pub fn origin(&self) -> Origin {
|
||||||
Origin(PodType::Signed, self.id)
|
Origin(PodType::Signed, self.pod.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(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +235,7 @@ impl MainPod {
|
||||||
Origin(PodType::Main, self.id)
|
Origin(PodType::Main, self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(&self) -> backend::MainPod {
|
pub fn compile(&self) -> Result<backend::MainPod> {
|
||||||
MainPodCompiler::new(self).compile()
|
MainPodCompiler::new(self).compile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,7 +294,7 @@ impl<'a> MainPodCompiler<'a> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(mut self) -> backend::MainPod {
|
pub fn compile(mut self) -> Result<backend::MainPod> {
|
||||||
let MainPod {
|
let MainPod {
|
||||||
statements,
|
statements,
|
||||||
params,
|
params,
|
||||||
|
|
@ -291,7 +307,8 @@ impl<'a> MainPodCompiler<'a> {
|
||||||
panic!("too many statements");
|
panic!("too many statements");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let input_signed_pods = input_signed_pods.iter().map(|p| p.compile()).collect();
|
let input_signed_pods: Vec<backend::SignedPod> =
|
||||||
|
input_signed_pods.iter().map(|p| p.pod.clone()).collect();
|
||||||
backend::MainPod::new(params.clone(), input_signed_pods, vec![], self.statements)
|
backend::MainPod::new(params.clone(), input_signed_pods, vec![], self.statements)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -335,8 +352,12 @@ impl Printer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_signed_pod(&self, w: &mut dyn Write, pod: &SignedPod) -> io::Result<()> {
|
pub fn fmt_signed_pod(&self, w: &mut dyn Write, pod: &SignedPod) -> io::Result<()> {
|
||||||
writeln!(w, "SignedPod (id:{}):", pod.id)?;
|
writeln!(w, "SignedPod (id:{}):", pod.id())?;
|
||||||
for (k, v) in pod.kvs.iter().sorted_by_key(|kv| kv.0) {
|
// 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.pod.kvs.iter().sorted_by_key(|kv| kv.0) {
|
||||||
writeln!(w, " - {}: {}", k, v)?;
|
writeln!(w, " - {}: {}", k, v)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -346,7 +367,7 @@ impl Printer {
|
||||||
writeln!(w, "MainPod (id:{}):", pod.id)?;
|
writeln!(w, "MainPod (id:{}):", pod.id)?;
|
||||||
writeln!(w, " input_signed_pods:")?;
|
writeln!(w, " input_signed_pods:")?;
|
||||||
for in_pod in &pod.input_signed_pods {
|
for in_pod in &pod.input_signed_pods {
|
||||||
writeln!(w, " - {}", in_pod.id)?;
|
writeln!(w, " - {}", in_pod.id())?;
|
||||||
}
|
}
|
||||||
writeln!(w, " input_main_pods:")?;
|
writeln!(w, " input_main_pods:")?;
|
||||||
for in_pod in &pod.input_main_pods {
|
for in_pod in &pod.input_main_pods {
|
||||||
|
|
@ -394,25 +415,17 @@ pub mod tests {
|
||||||
(max_of, $($arg:expr),+) => { Statement(NativeStatement::max_of, args!($($arg),*)) };
|
(max_of, $($arg:expr),+) => { Statement(NativeStatement::max_of, args!($($arg),*)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_zu_kyc(params: Params) -> (SignedPod, SignedPod, MainPod) {
|
pub fn data_zu_kyc(params: Params) -> Result<(SignedPod, SignedPod, MainPod)> {
|
||||||
let mut kvs = HashMap::new();
|
let mut kvs = HashMap::new();
|
||||||
kvs.insert("idNumber".into(), "4242424242".into());
|
kvs.insert("idNumber".into(), "4242424242".into());
|
||||||
kvs.insert("dateOfBirth".into(), 1169909384.into());
|
kvs.insert("dateOfBirth".into(), 1169909384.into());
|
||||||
kvs.insert("socialSecurityNumber".into(), "G2121210".into());
|
kvs.insert("socialSecurityNumber".into(), "G2121210".into());
|
||||||
let gov_id = SignedPod {
|
let gov_id = SignedPod::new(¶ms, kvs)?;
|
||||||
params: params.clone(),
|
|
||||||
id: pod_id("1100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
kvs,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut kvs = HashMap::new();
|
let mut kvs = HashMap::new();
|
||||||
kvs.insert("socialSecurityNumber".into(), "G2121210".into());
|
kvs.insert("socialSecurityNumber".into(), "G2121210".into());
|
||||||
kvs.insert("startDate".into(), 1706367566.into());
|
kvs.insert("startDate".into(), 1706367566.into());
|
||||||
let pay_stub = SignedPod {
|
let pay_stub = SignedPod::new(¶ms, kvs)?;
|
||||||
params: params.clone(),
|
|
||||||
id: pod_id("2200000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
kvs,
|
|
||||||
};
|
|
||||||
|
|
||||||
let sanction_list = Value::MerkleTree(MerkleTree { root: 1 });
|
let sanction_list = Value::MerkleTree(MerkleTree { root: 1 });
|
||||||
let now_minus_18y: i64 = 1169909388;
|
let now_minus_18y: i64 = 1169909388;
|
||||||
|
|
@ -440,17 +453,19 @@ pub mod tests {
|
||||||
statements,
|
statements,
|
||||||
};
|
};
|
||||||
|
|
||||||
(gov_id, pay_stub, kyc)
|
Ok((gov_id, pay_stub, kyc))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_front_0() {
|
fn test_front_0() -> Result<()> {
|
||||||
let (gov_id, pay_stub, kyc) = data_zu_kyc(Params::default());
|
let (gov_id, pay_stub, kyc) = data_zu_kyc(Params::default())?;
|
||||||
|
|
||||||
let printer = Printer {};
|
let printer = Printer {};
|
||||||
let mut w = io::stdout();
|
let mut w = io::stdout();
|
||||||
printer.fmt_signed_pod(&mut w, &gov_id).unwrap();
|
printer.fmt_signed_pod(&mut w, &gov_id).unwrap();
|
||||||
printer.fmt_signed_pod(&mut w, &pay_stub).unwrap();
|
printer.fmt_signed_pod(&mut w, &pay_stub).unwrap();
|
||||||
printer.fmt_main_pod(&mut w, &kyc).unwrap();
|
printer.fmt_main_pod(&mut w, &kyc).unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use plonky2::hash::{
|
||||||
use plonky2::plonk::config::GenericConfig;
|
use plonky2::plonk::config::GenericConfig;
|
||||||
use plonky2::plonk::config::Hasher;
|
use plonky2::plonk::config::Hasher;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::iter::IntoIterator;
|
||||||
|
|
||||||
use crate::backend::Value;
|
use crate::backend::Value;
|
||||||
use crate::{Hash, C, D, F};
|
use crate::{Hash, C, D, F};
|
||||||
|
|
@ -28,6 +29,11 @@ pub struct MerkleTree {
|
||||||
tree: PlonkyMerkleTree<F, <C as GenericConfig<D>>::Hasher>,
|
tree: PlonkyMerkleTree<F, <C as GenericConfig<D>>::Hasher>,
|
||||||
// keyindex: key -> index mapping. This is just for the current plonky-tree wrapper
|
// keyindex: key -> index mapping. This is just for the current plonky-tree wrapper
|
||||||
keyindex: HashMap<Hash, usize>,
|
keyindex: HashMap<Hash, usize>,
|
||||||
|
// kvs are a field in the MerkleTree in order to be able to iterate over the keyvalues. This is
|
||||||
|
// specific of the current implementation (Plonky2's tree wrapper), in the next iteration this
|
||||||
|
// will not be needed since the tree implementation itself will offer the hashmap
|
||||||
|
// functionallity.
|
||||||
|
kvs: HashMap<Hash, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MerkleProof {
|
pub struct MerkleProof {
|
||||||
|
|
@ -44,7 +50,7 @@ impl MerkleTree {
|
||||||
// https://0xparc.github.io/pod2/merkletree.html will not need it since it will be
|
// 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
|
// deterministic based on the keys values not on the order of the keys when added into the
|
||||||
// tree.
|
// tree.
|
||||||
for (i, (k, v)) in kvs.into_iter().sorted_by_key(|kv| kv.0).enumerate() {
|
for (i, (k, v)) in kvs.clone().into_iter().sorted_by_key(|kv| kv.0).enumerate() {
|
||||||
let input: Vec<F> = [k.0, v.0].concat();
|
let input: Vec<F> = [k.0, v.0].concat();
|
||||||
let leaf = PoseidonHash::hash_no_pad(&input).elements;
|
let leaf = PoseidonHash::hash_no_pad(&input).elements;
|
||||||
leaves.push(leaf.into());
|
leaves.push(leaf.into());
|
||||||
|
|
@ -58,7 +64,11 @@ impl MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tree = PlonkyMerkleTree::<F, <C as GenericConfig<D>>::Hasher>::new(leaves, CAP_HEIGHT);
|
let tree = PlonkyMerkleTree::<F, <C as GenericConfig<D>>::Hasher>::new(leaves, CAP_HEIGHT);
|
||||||
Self { tree, keyindex }
|
Self {
|
||||||
|
tree,
|
||||||
|
keyindex,
|
||||||
|
kvs,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root(&self) -> Result<Hash> {
|
pub fn root(&self) -> Result<Hash> {
|
||||||
|
|
@ -114,6 +124,19 @@ impl MerkleTree {
|
||||||
println!("WARNING: MerkleTree::verify_nonexistence is currently a mock");
|
println!("WARNING: MerkleTree::verify_nonexistence is currently a mock");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> std::collections::hash_map::Iter<Hash, Value> {
|
||||||
|
self.kvs.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a MerkleTree {
|
||||||
|
type Item = (&'a Hash, &'a Value);
|
||||||
|
type IntoIter = std::collections::hash_map::Iter<'a, Hash, Value>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.kvs.iter()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue