From ca1be65b85f724789aecaf04a56e6f1acbbf83d4 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 6 Feb 2025 11:35:57 +0100 Subject: [PATCH] compute MainPod.id from pub_statements; and introduce the trait `ToFields` (#35) * compute MainPod.id from pub_statements; and introduce the trait `ToFields` Compute MainPod.id from pub_statements hash; for it, introduce the trait `ToFields` at the middleware and implement it for the Statement related types. * cleaner statements tofield iter Co-authored-by: Ahmad Afuni --------- Co-authored-by: Ahmad Afuni --- src/backends/mock_main.rs | 40 ++++++++++++-------- src/frontend.rs | 1 - src/middleware.rs | 77 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 19 deletions(-) diff --git a/src/backends/mock_main.rs b/src/backends/mock_main.rs index 9d4d0d4..412f0e3 100644 --- a/src/backends/mock_main.rs +++ b/src/backends/mock_main.rs @@ -1,9 +1,11 @@ use crate::middleware::{ - self, MainPod, MainPodInputs, NativeOperation, NativeStatement, NoneMainPod, NoneSignedPod, - Params, PodId, PodProver, SignedPod, Statement, StatementArg, + self, Hash, MainPod, MainPodInputs, NativeOperation, NativeStatement, NoneMainPod, + NoneSignedPod, Params, PodId, PodProver, SignedPod, Statement, StatementArg, ToFields, }; use anyhow::Result; use itertools::Itertools; +use plonky2::hash::poseidon::PoseidonHash; +use plonky2::plonk::config::Hasher; use std::any::Any; use std::io::{self, Write}; @@ -156,7 +158,7 @@ impl MockMainPod { } } - fn process_priavte_statements_operations( + fn process_private_statements_operations( params: &Params, statements: &[Statement], input_operations: &[middleware::Operation], @@ -207,12 +209,12 @@ impl MockMainPod { pub fn new(params: &Params, inputs: MainPodInputs) -> Result { // 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. + // the MainPodInputs type to accommodate 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); + Self::process_private_statements_operations(params, &statements, inputs.operations); let operations = Self::process_public_statements_operations(params, &statements, operations); @@ -225,12 +227,12 @@ impl MockMainPod { 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`). + // get the id out of the public statements + let id: PodId = PodId(hash_statements(&public_statements)?); + Ok(Self { params: params.clone(), - id: PodId::default(), // TODO + id, input_signed_pods, input_main_pods, input_statements, @@ -265,6 +267,14 @@ impl MockMainPod { } } +pub fn hash_statements(statements: &[middleware::Statement]) -> Result { + let field_elems = statements + .into_iter() + .flat_map(|statement| statement.clone().to_fields().0) + .collect::>(); + Ok(Hash(PoseidonHash::hash_no_pad(&field_elems).elements)) +} + impl MainPod for MockMainPod { fn verify(&self) -> bool { // TODO @@ -410,20 +420,20 @@ pub mod tests { fn test_mock_main_0() { let params = middleware::Params::default(); - let (gov_id, pay_stub) = frontend::tests::zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = frontend::tests::zu_kyc_sign_pod_builders(¶ms); let mut signer = MockSigner { pk: "ZooGov".into(), }; - let gov_id = gov_id.sign(&mut signer).unwrap(); + let gov_id_pod = gov_id_builder.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(¶ms, &gov_id, &pay_stub); + let pay_stub_pod = pay_stub_builder.sign(&mut signer).unwrap(); + let kyc_builder = frontend::tests::zu_kyc_pod_builder(¶ms, &gov_id_pod, &pay_stub_pod); let mut prover = MockProver {}; - let kyc = kyc.prove(&mut prover).unwrap(); - let pod = kyc.pod.into_any().downcast::().unwrap(); + let kyc_pod = kyc_builder.prove(&mut prover).unwrap(); + let pod = kyc_pod.pod.into_any().downcast::().unwrap(); let printer = Printer { skip_none: false }; let mut w = io::stdout(); diff --git a/src/frontend.rs b/src/frontend.rs index 8b41802..e04dd07 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -343,7 +343,6 @@ impl MainPodBuilder { }; let (statements, operations, public_statements) = compiler.compile(inputs)?; - // TODO: Add API to specify public/private statement 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(), diff --git a/src/middleware.rs b/src/middleware.rs index e38a91d..fbee7f6 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -17,6 +17,7 @@ use strum_macros::FromRepr; pub const KEY_SIGNER: &str = "_signer"; pub const KEY_TYPE: &str = "_type"; +pub const STATEMENT_ARG_F_LEN: usize = 8; /// F is the native field we use everywhere. Currently it's Goldilocks from plonky2 pub type F = GoldilocksField; @@ -74,6 +75,12 @@ impl fmt::Display for Value { #[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] pub struct Hash(pub [F; 4]); +impl ToFields for Hash { + fn to_fields(self) -> (Vec, usize) { + (self.0.to_vec(), 4) + } +} + impl Ord for Hash { fn cmp(&self, other: &Self) -> Ordering { Value(self.0).cmp(&Value(other.0)) @@ -117,6 +124,12 @@ impl FromHex for Hash { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct PodId(pub Hash); +impl ToFields for PodId { + fn to_fields(self) -> (Vec, usize) { + self.0.to_fields() + } +} + pub const SELF: PodId = PodId(Hash([F::ONE, F::ZERO, F::ZERO, F::ZERO])); impl fmt::Display for PodId { @@ -264,6 +277,12 @@ pub enum NativeStatement { MaxOf = 10, } +impl ToFields for NativeStatement { + fn to_fields(self) -> (Vec, usize) { + (vec![F::from_canonical_u64(self as u64)], 1) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct AnchoredKey(pub PodId, pub Hash); @@ -280,6 +299,36 @@ impl StatementArg { } } +impl ToFields for StatementArg { + fn to_fields(self) -> (Vec, usize) { + // NOTE: current version returns always the same amount of field elements in the returned + // vector, which means that the `None` case is padded with 8 zeroes, and the `Literal` case + // is padded with 4 zeroes. Since the returned vector will mostly be hashed (and reproduced + // in-circuit), we might be interested into reducing the length of it. If that's the case, + // we can check if it makes sense to make it dependant on the concrete StatementArg; that + // is, when dealing with a `None` it would be a single field element (zero value), and when + // dealing with `Literal` it would be of length 4. + let f = match self { + StatementArg::None => vec![F::ZERO; STATEMENT_ARG_F_LEN], + StatementArg::Literal(v) => { + let value_f = v.0.to_vec(); + [ + value_f.clone(), + vec![F::ZERO; STATEMENT_ARG_F_LEN - value_f.len()], + ] + .concat() + } + StatementArg::Key(ak) => { + let (podid_f, _) = ak.0.to_fields(); + let (hash_f, _) = ak.1.to_fields(); + [podid_f, hash_f].concat() + } + }; + assert_eq!(f.len(), STATEMENT_ARG_F_LEN); // sanity check + (f, STATEMENT_ARG_F_LEN) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Statement(pub NativeStatement, pub Vec); @@ -289,6 +338,25 @@ impl Statement { } } +impl ToFields for Statement { + fn to_fields(self) -> (Vec, usize) { + let (native_statement_f, native_statement_f_len) = self.0.to_fields(); + let (vec_statementarg_f, vec_statementarg_f_len) = self + .1 + .into_iter() + .map(|statement_arg| statement_arg.to_fields()) + .fold((Vec::new(), 0), |mut acc, (f, l)| { + acc.0.extend(f); + acc.1 += l; + acc + }); + ( + [native_statement_f, vec_statementarg_f].concat(), + native_statement_f_len + vec_statementarg_f_len, + ) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum NativeOperation { None = 0, @@ -350,9 +418,6 @@ impl MainPod for NoneMainPod { } } -// 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)] pub struct MainPodInputs<'a> { pub signed_pods: &'a [&'a Box], @@ -367,3 +432,9 @@ pub struct MainPodInputs<'a> { pub trait PodProver { fn prove(&mut self, params: &Params, inputs: MainPodInputs) -> Result>; } + +pub trait ToFields { + /// returns Vec representation of the type, and a usize indicating how many field elements + /// does the vector contain + fn to_fields(self) -> (Vec, usize); +}