From 09af891bb6250b91059f329e95a1012046e4152f Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Tue, 18 Jul 2023 17:53:22 -0300 Subject: [PATCH 01/20] meta commit and hide --- src/cli/commitment.rs | 42 ++++++++++++++++++++ src/cli/field_data.rs | 27 +++++++++++++ src/cli/lurk_proof.rs | 49 +++++++++++------------ src/cli/mod.rs | 2 + src/cli/paths.rs | 24 +++++++---- src/cli/repl.rs | 92 ++++++++++++++++++++++++++++--------------- 6 files changed, 171 insertions(+), 65 deletions(-) create mode 100644 src/cli/commitment.rs create mode 100644 src/cli/field_data.rs diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs new file mode 100644 index 0000000000..bc7432f00b --- /dev/null +++ b/src/cli/commitment.rs @@ -0,0 +1,42 @@ +use std::{fs::File, io::BufWriter}; + +use anyhow::Result; + +use lurk::{field::LurkField, ptr::Ptr, store::Store, z_ptr::ZExprPtr, z_store::ZStore}; +use serde::{Deserialize, Serialize}; + +use super::{field_data::FieldData, paths::commitment_path}; + +#[derive(Serialize, Deserialize)] +pub struct Commitment { + pub hash: F, + secret: F, + payload: ZExprPtr, + zstore: ZStore, +} + +impl Commitment { + pub fn new(secret: F, payload: Ptr, store: &mut Store) -> Result { + let payload = store.hide(secret, payload); + let mut zstore = Some(ZStore::::default()); + let payload = store.get_z_expr(&payload, &mut zstore)?.0; + let hash = payload.value().clone(); + let zstore = zstore.unwrap(); + Ok(Self { + hash, + secret, + payload, + zstore, + }) + } + + pub fn persist(&self, hash: &str) -> Result<()> { + let fd = &FieldData::wrap::>(self)?; + bincode::serialize_into(BufWriter::new(&File::create(commitment_path(hash))?), fd)?; + Ok(()) + } + + pub fn load(hash: &str) -> Result { + todo!() + } +} diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs new file mode 100644 index 0000000000..ea08d264a2 --- /dev/null +++ b/src/cli/field_data.rs @@ -0,0 +1,27 @@ +use anyhow::Result; +use serde::{Deserialize, Serialize}; + +use lurk::field::{LanguageField, LurkField}; + +/// A wrapper for data whose deserialization depends on a certain LurkField +#[derive(Serialize, Deserialize)] +pub struct FieldData { + field: LanguageField, + data: Vec, +} + +#[allow(dead_code)] +impl FieldData { + #[inline] + pub fn wrap(t: &T) -> Result { + Ok(Self { + field: F::FIELD, + data: bincode::serialize(t)?, + }) + } + + #[inline] + pub fn extract<'a, T: Deserialize<'a>>(&'a self) -> Result { + Ok(bincode::deserialize(&self.data)?) + } +} diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index b713bf5f7d..d3d9ca0e01 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -1,3 +1,5 @@ +use std::{fs::File, io::BufWriter}; + use serde::{Deserialize, Serialize}; use anyhow::Result; @@ -7,35 +9,17 @@ use lurk::{ lang::{Coproc, Lang}, Status, }, - field::{LanguageField, LurkField}, + field::LurkField, proof::nova, public_parameters::public_params, z_ptr::ZExprPtr, z_store::ZStore, }; -/// A wrapper for data whose deserialization depends on a certain LurkField -#[derive(Serialize, Deserialize)] -pub struct FieldData { - field: LanguageField, - data: Vec, -} - -#[allow(dead_code)] -impl FieldData { - #[inline] - pub fn wrap(t: &T) -> Result { - Ok(Self { - field: F::FIELD, - data: bincode::serialize(t)?, - }) - } - - #[inline] - pub fn open<'a, T: Deserialize<'a>>(&'a self) -> Result { - Ok(bincode::deserialize(&self.data)?) - } -} +use super::{ + field_data::FieldData, + paths::{proof_meta_path, proof_path}, +}; /// Carries extra information to help with visualization, experiments etc #[derive(Serialize, Deserialize)] @@ -51,6 +35,14 @@ pub struct LurkProofMeta { pub zstore: ZStore, } +impl LurkProofMeta { + pub fn persist(&self, id: &str) -> Result<()> { + let fd = &FieldData::wrap::>(self)?; + bincode::serialize_into(BufWriter::new(&File::create(proof_meta_path(id))?), fd)?; + Ok(()) + } +} + type F = pasta_curves::pallas::Scalar; // TODO: generalize this /// Minimal data structure containing just enough for proof verification @@ -67,6 +59,12 @@ pub enum LurkProof<'a> { } impl<'a> LurkProof<'a> { + pub fn persist(&self, id: &str) -> Result<()> { + let fd = &FieldData::wrap::>(self)?; + bincode::serialize_into(BufWriter::new(&File::create(proof_path(id))?), fd)?; + Ok(()) + } + #[allow(dead_code)] fn verify(self) -> Result { match self { @@ -96,12 +94,11 @@ impl<'a> LurkProof<'a> { #[cfg(not(target_arch = "wasm32"))] pub fn verify_proof(proof_id: &str) -> Result<()> { - use super::paths::proof_path; - use std::{fs::File, io::BufReader}; + use std::io::BufReader; let file = File::open(proof_path(proof_id))?; let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - let lurk_proof: LurkProof = fd.open()?; + let lurk_proof: LurkProof = fd.extract()?; Self::print_verification(proof_id, lurk_proof.verify()?); Ok(()) } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index fbc22aff00..9c208296f0 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,3 +1,5 @@ +mod commitment; +mod field_data; mod lurk_proof; mod paths; mod repl; diff --git a/src/cli/paths.rs b/src/cli/paths.rs index 31708f6059..01993639ed 100644 --- a/src/cli/paths.rs +++ b/src/cli/paths.rs @@ -23,8 +23,13 @@ pub fn proofs_dir() -> PathBuf { } #[cfg(not(target_arch = "wasm32"))] -pub fn lurk_leaf_dirs() -> [PathBuf; 1] { - [proofs_dir()] +pub fn commits_dir() -> PathBuf { + lurk_dir().join(Path::new("commits")) +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn lurk_leaf_dirs() -> [PathBuf; 2] { + [proofs_dir(), commits_dir()] } #[cfg(not(target_arch = "wasm32"))] @@ -35,6 +40,16 @@ pub fn create_lurk_dirs() -> Result<()> { Ok(()) } +#[cfg(not(target_arch = "wasm32"))] +pub fn repl_history() -> PathBuf { + lurk_dir().join(Path::new("repl-history")) +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn commitment_path(hash: &str) -> PathBuf { + commits_dir().join(Path::new(hash)) +} + #[cfg(not(target_arch = "wasm32"))] pub fn proof_path(name: &str) -> PathBuf { proofs_dir().join(Path::new(name)).with_extension("proof") @@ -44,8 +59,3 @@ pub fn proof_path(name: &str) -> PathBuf { pub fn proof_meta_path(name: &str) -> PathBuf { proofs_dir().join(Path::new(name)).with_extension("meta") } - -#[cfg(not(target_arch = "wasm32"))] -pub fn repl_history() -> PathBuf { - lurk_dir().join(Path::new("repl-history")) -} diff --git a/src/cli/repl.rs b/src/cli/repl.rs index b6305aedd5..3214f2b273 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -5,6 +5,7 @@ use std::{fs::read_to_string, process, time::Instant}; use anyhow::{bail, Context, Result}; +use ff::Field; use log::info; use rustyline::{ @@ -36,14 +37,9 @@ use lurk::{ z_store::ZStore, }; +use super::commitment::Commitment; #[cfg(not(target_arch = "wasm32"))] -use std::{fs::File, io::BufWriter}; - -#[cfg(not(target_arch = "wasm32"))] -use super::{ - lurk_proof::{LurkProof, LurkProofMeta}, - paths::{proof_meta_path, proof_path}, -}; +use super::lurk_proof::{LurkProof, LurkProofMeta}; #[derive(Completer, Helper, Highlighter, Hinter)] struct InputValidator { @@ -166,8 +162,6 @@ impl Repl { #[cfg(not(target_arch = "wasm32"))] pub fn prove_last_frames(&mut self) -> Result<()> { - use crate::cli::lurk_proof::FieldData; - match self.evaluation.as_mut() { None => bail!("No evaluation to prove"), Some(Evaluation { @@ -211,37 +205,30 @@ impl Repl { assert_eq!(self.rc * num_steps, n_frames); assert!(proof.verify(&pp, num_steps, &public_inputs, &public_outputs)?); - let lurk_proof_wrap = FieldData::wrap::>(&LurkProof::Nova { + let lurk_proof = &LurkProof::Nova { proof, public_inputs, public_outputs, num_steps, rc: self.rc, lang: (*self.lang).clone(), - })?; - - let lurk_proof_meta_wrap = - FieldData::wrap::>(&LurkProofMeta { - iterations: *iterations, - evaluation_cost: *cost, - generation_cost: generation.duration_since(start).as_nanos(), - compression_cost: compression.duration_since(generation).as_nanos(), - status, - expression, - environment, - result, - zstore: zstore.unwrap(), - })?; + }; + + let lurk_proof_meta = &LurkProofMeta { + iterations: *iterations, + evaluation_cost: *cost, + generation_cost: generation.duration_since(start).as_nanos(), + compression_cost: compression.duration_since(generation).as_nanos(), + status, + expression, + environment, + result, + zstore: zstore.unwrap(), + }; let id = &format!("{}", timestamp()); - bincode::serialize_into( - BufWriter::new(&File::create(proof_path(id))?), - &lurk_proof_wrap, - )?; - bincode::serialize_into( - BufWriter::new(&File::create(proof_meta_path(id))?), - &lurk_proof_meta_wrap, - )?; + lurk_proof.persist(id)?; + lurk_proof_meta.persist(id)?; println!("Proof ID: \"{id}\""); Ok(()) } @@ -250,6 +237,19 @@ impl Repl { } } + fn hide(&mut self, secret: F, payload: Ptr) -> Result<()> { + let commitment = Commitment::new(secret, payload, &mut self.store)?; + let hash = &format!("0x{}", commitment.hash.hex_digits()); + commitment.persist(hash)?; + println!("Data: {}\nHash: {hash}", payload.fmt_to_string(&self.store)); + Ok(()) + } + + fn fetch(&mut self, hash: &str) -> Result<()> { + let commitment: Commitment = Commitment::load(hash)?; + Ok(()) + } + #[inline] fn eval_expr(&mut self, expr_ptr: Ptr) -> Result<(IO, usize, Vec>)> { Ok(Evaluator::new(expr_ptr, self.env, &mut self.store, self.limit, &self.lang).eval()?) @@ -416,6 +416,34 @@ impl Repl { process::exit(1); } } + "lurk.commit" => { + let first = self.peek1(cmd, args)?; + let (first_io, ..) = self.eval_expr(first)?; + self.hide(F::ZERO, first_io.expr)?; + } + "lurk.hide" => { + let (first, second) = self.peek2(cmd, args)?; + let (first_io, ..) = self + .eval_expr(first) + .with_context(|| "evaluating first arg")?; + let (second_io, ..) = self + .eval_expr(second) + .with_context(|| "evaluating second arg")?; + let Some(secret) = self.store.fetch_num(&first_io.expr) else { + bail!("Secret must be a number. Got {}", first_io.expr.fmt_to_string(&self.store)) + }; + self.hide(secret.into_scalar(), second_io.expr)?; + } + "fetch" => { + let first = self.peek1(cmd, args)?; + let (first_io, ..) = self + .eval_expr(first) + .with_context(|| "evaluating first arg")?; + let Some(hash) = self.store.fetch_comm(&first) else { + bail!("Hash must be a number. Got {}", first.fmt_to_string(&self.store)) + }; + // self.fetch(&format!("0x{}", hash.into_scalar().hex_digits()))?; + } "clear" => self.env = self.store.nil(), "set-env" => { // The state's env is set to the result of evaluating the first argument. From 076b70dcf652030f4185b229487b91903b273537 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Tue, 18 Jul 2023 19:00:05 -0300 Subject: [PATCH 02/20] progress on fetch --- src/cli/commitment.rs | 29 ++++++++++++++++++++--------- src/cli/field_data.rs | 2 +- src/cli/repl.rs | 13 +++++++------ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index bc7432f00b..720341b0b3 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -1,6 +1,6 @@ -use std::{fs::File, io::BufWriter}; +use std::{fs::File, io::{BufWriter, BufReader}}; -use anyhow::Result; +use anyhow::{Result, bail}; use lurk::{field::LurkField, ptr::Ptr, store::Store, z_ptr::ZExprPtr, z_store::ZStore}; use serde::{Deserialize, Serialize}; @@ -11,21 +11,21 @@ use super::{field_data::FieldData, paths::commitment_path}; pub struct Commitment { pub hash: F, secret: F, - payload: ZExprPtr, + hidden: ZExprPtr, zstore: ZStore, } -impl Commitment { +impl<'a, F: LurkField + Serialize + Deserialize<'a>> Commitment { pub fn new(secret: F, payload: Ptr, store: &mut Store) -> Result { - let payload = store.hide(secret, payload); + let hidden = store.hide(secret, payload); let mut zstore = Some(ZStore::::default()); - let payload = store.get_z_expr(&payload, &mut zstore)?.0; - let hash = payload.value().clone(); + let hidden = store.get_z_expr(&hidden, &mut zstore)?.0; + let hash = *hidden.value(); let zstore = zstore.unwrap(); Ok(Self { hash, secret, - payload, + hidden, zstore, }) } @@ -37,6 +37,17 @@ impl Commitment { } pub fn load(hash: &str) -> Result { - todo!() + let file = File::open(commitment_path(hash))?; + let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; + if fd.field != F::FIELD { + bail!("Invalid field: {}. Expected {}", &fd.field, &F::FIELD) + } else { + let commitment: Commitment = fd.extract()?; + if format!("0x{}", commitment.hash.hex_digits()) == hash { + Ok(commitment) + } else { + bail!("Hash mismatch. Corrupted commitment file.") + } + } } } diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index ea08d264a2..6b0ae9da47 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -6,7 +6,7 @@ use lurk::field::{LanguageField, LurkField}; /// A wrapper for data whose deserialization depends on a certain LurkField #[derive(Serialize, Deserialize)] pub struct FieldData { - field: LanguageField, + pub field: LanguageField, data: Vec, } diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 3214f2b273..876965667b 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -247,6 +247,7 @@ impl Repl { fn fetch(&mut self, hash: &str) -> Result<()> { let commitment: Commitment = Commitment::load(hash)?; + println!("Data for 0x{hash} is now available"); Ok(()) } @@ -436,13 +437,13 @@ impl Repl { } "fetch" => { let first = self.peek1(cmd, args)?; - let (first_io, ..) = self - .eval_expr(first) + let n = self.store.lurk_sym("num"); + let expr = self.store.list(&[n, first]); + let (expr_io, ..) = self + .eval_expr(expr) .with_context(|| "evaluating first arg")?; - let Some(hash) = self.store.fetch_comm(&first) else { - bail!("Hash must be a number. Got {}", first.fmt_to_string(&self.store)) - }; - // self.fetch(&format!("0x{}", hash.into_scalar().hex_digits()))?; + let hash = self.store.fetch_num(&expr_io.expr).expect("must be a number"); + self.fetch(&format!("0x{}", hash.into_scalar().hex_digits()))?; } "clear" => self.env = self.store.nil(), "set-env" => { From 2fce9edf35f36e32182fb91080ca886e44968df2 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Tue, 18 Jul 2023 19:05:20 -0300 Subject: [PATCH 03/20] resolve lifetime issue --- src/cli/commitment.rs | 19 ++----------------- src/cli/repl.rs | 23 ++++++++++++++++++++--- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 720341b0b3..3450ffd93a 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -1,6 +1,6 @@ -use std::{fs::File, io::{BufWriter, BufReader}}; +use std::{fs::File, io::BufWriter}; -use anyhow::{Result, bail}; +use anyhow::Result; use lurk::{field::LurkField, ptr::Ptr, store::Store, z_ptr::ZExprPtr, z_store::ZStore}; use serde::{Deserialize, Serialize}; @@ -35,19 +35,4 @@ impl<'a, F: LurkField + Serialize + Deserialize<'a>> Commitment { bincode::serialize_into(BufWriter::new(&File::create(commitment_path(hash))?), fd)?; Ok(()) } - - pub fn load(hash: &str) -> Result { - let file = File::open(commitment_path(hash))?; - let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - if fd.field != F::FIELD { - bail!("Invalid field: {}. Expected {}", &fd.field, &F::FIELD) - } else { - let commitment: Commitment = fd.extract()?; - if format!("0x{}", commitment.hash.hex_digits()) == hash { - Ok(commitment) - } else { - bail!("Hash mismatch. Corrupted commitment file.") - } - } - } } diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 876965667b..0787111e1d 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -1,3 +1,5 @@ +use std::fs::File; +use std::io::BufReader; use std::path::Path; use std::sync::Arc; @@ -38,8 +40,10 @@ use lurk::{ }; use super::commitment::Commitment; +use super::field_data::FieldData; #[cfg(not(target_arch = "wasm32"))] use super::lurk_proof::{LurkProof, LurkProofMeta}; +use super::paths::commitment_path; #[derive(Completer, Helper, Highlighter, Hinter)] struct InputValidator { @@ -246,8 +250,18 @@ impl Repl { } fn fetch(&mut self, hash: &str) -> Result<()> { - let commitment: Commitment = Commitment::load(hash)?; - println!("Data for 0x{hash} is now available"); + let file = File::open(commitment_path(hash))?; + let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; + if fd.field != F::FIELD { + bail!("Invalid field: {}. Expected {}", &fd.field, &F::FIELD) + } else { + let commitment: Commitment = fd.extract()?; + if format!("0x{}", commitment.hash.hex_digits()) != hash { + bail!("Hash mismatch. Corrupted commitment file.") + } else { + println!("Data for {hash} is now available"); + } + } Ok(()) } @@ -442,7 +456,10 @@ impl Repl { let (expr_io, ..) = self .eval_expr(expr) .with_context(|| "evaluating first arg")?; - let hash = self.store.fetch_num(&expr_io.expr).expect("must be a number"); + let hash = self + .store + .fetch_num(&expr_io.expr) + .expect("must be a number"); self.fetch(&format!("0x{}", hash.into_scalar().hex_digits()))?; } "clear" => self.env = self.store.nil(), From 68d26bd5fae156a6ba3c12657d9eb43f1b9055a4 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Tue, 18 Jul 2023 19:47:14 -0300 Subject: [PATCH 04/20] finish fetch --- fcomm/src/lib.rs | 2 +- src/cli/commitment.rs | 24 +++++---- src/cli/lurk_proof.rs | 10 ++-- src/cli/repl.rs | 75 ++++++++++++++++------------ src/store.rs | 110 ++++++++++++++++++++++-------------------- 5 files changed, 119 insertions(+), 102 deletions(-) diff --git a/fcomm/src/lib.rs b/fcomm/src/lib.rs index 09511d7133..3c355c700a 100644 --- a/fcomm/src/lib.rs +++ b/fcomm/src/lib.rs @@ -555,7 +555,7 @@ impl LurkPtr { LurkPtr::ZStorePtr(z_store_ptr) => { let z_store = &z_store_ptr.z_store; let z_ptr = z_store_ptr.z_ptr; - s.intern_z_expr_ptr(z_ptr, z_store) + s.intern_z_expr_ptr(&z_ptr, z_store) .expect("failed to intern z_ptr") } } diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 3450ffd93a..50b67dc458 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -1,36 +1,34 @@ -use std::{fs::File, io::BufWriter}; - use anyhow::Result; use lurk::{field::LurkField, ptr::Ptr, store::Store, z_ptr::ZExprPtr, z_store::ZStore}; use serde::{Deserialize, Serialize}; -use super::{field_data::FieldData, paths::commitment_path}; - +/// Holds data for commitments. +/// +/// WARNING: CONTAINS PRIVATE DATA #[derive(Serialize, Deserialize)] pub struct Commitment { - pub hash: F, - secret: F, - hidden: ZExprPtr, - zstore: ZStore, + pub hidden: ZExprPtr, + pub zstore: ZStore, } impl<'a, F: LurkField + Serialize + Deserialize<'a>> Commitment { + #[allow(dead_code)] pub fn new(secret: F, payload: Ptr, store: &mut Store) -> Result { let hidden = store.hide(secret, payload); let mut zstore = Some(ZStore::::default()); let hidden = store.get_z_expr(&hidden, &mut zstore)?.0; - let hash = *hidden.value(); - let zstore = zstore.unwrap(); Ok(Self { - hash, - secret, hidden, - zstore, + zstore: zstore.unwrap(), }) } + #[cfg(not(target_arch = "wasm32"))] pub fn persist(&self, hash: &str) -> Result<()> { + use super::{field_data::FieldData, paths::commitment_path}; + use std::{fs::File, io::BufWriter}; + let fd = &FieldData::wrap::>(self)?; bincode::serialize_into(BufWriter::new(&File::create(commitment_path(hash))?), fd)?; Ok(()) diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index d3d9ca0e01..cbd3ab40b0 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -1,5 +1,3 @@ -use std::{fs::File, io::BufWriter}; - use serde::{Deserialize, Serialize}; use anyhow::Result; @@ -16,6 +14,10 @@ use lurk::{ z_store::ZStore, }; +#[cfg(not(target_arch = "wasm32"))] +use std::{fs::File, io::BufReader, io::BufWriter}; + +#[cfg(not(target_arch = "wasm32"))] use super::{ field_data::FieldData, paths::{proof_meta_path, proof_path}, @@ -36,6 +38,7 @@ pub struct LurkProofMeta { } impl LurkProofMeta { + #[cfg(not(target_arch = "wasm32"))] pub fn persist(&self, id: &str) -> Result<()> { let fd = &FieldData::wrap::>(self)?; bincode::serialize_into(BufWriter::new(&File::create(proof_meta_path(id))?), fd)?; @@ -59,6 +62,7 @@ pub enum LurkProof<'a> { } impl<'a> LurkProof<'a> { + #[cfg(not(target_arch = "wasm32"))] pub fn persist(&self, id: &str) -> Result<()> { let fd = &FieldData::wrap::>(self)?; bincode::serialize_into(BufWriter::new(&File::create(proof_path(id))?), fd)?; @@ -94,8 +98,6 @@ impl<'a> LurkProof<'a> { #[cfg(not(target_arch = "wasm32"))] pub fn verify_proof(proof_id: &str) -> Result<()> { - use std::io::BufReader; - let file = File::open(proof_path(proof_id))?; let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; let lurk_proof: LurkProof = fd.extract()?; diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 0787111e1d..a5dac4f681 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -1,5 +1,3 @@ -use std::fs::File; -use std::io::BufReader; use std::path::Path; use std::sync::Arc; @@ -7,7 +5,6 @@ use std::{fs::read_to_string, process, time::Instant}; use anyhow::{bail, Context, Result}; -use ff::Field; use log::info; use rustyline::{ @@ -39,11 +36,8 @@ use lurk::{ z_store::ZStore, }; -use super::commitment::Commitment; -use super::field_data::FieldData; #[cfg(not(target_arch = "wasm32"))] use super::lurk_proof::{LurkProof, LurkProofMeta}; -use super::paths::commitment_path; #[derive(Completer, Helper, Highlighter, Hinter)] struct InputValidator { @@ -241,24 +235,33 @@ impl Repl { } } + #[cfg(not(target_arch = "wasm32"))] fn hide(&mut self, secret: F, payload: Ptr) -> Result<()> { + use super::commitment::Commitment; + let commitment = Commitment::new(secret, payload, &mut self.store)?; - let hash = &format!("0x{}", commitment.hash.hex_digits()); + let hash = &format!("0x{}", commitment.hidden.value().hex_digits()); commitment.persist(hash)?; println!("Data: {}\nHash: {hash}", payload.fmt_to_string(&self.store)); Ok(()) } + #[cfg(not(target_arch = "wasm32"))] fn fetch(&mut self, hash: &str) -> Result<()> { + use super::{commitment::Commitment, field_data::FieldData, paths::commitment_path}; + use std::{fs::File, io::BufReader}; + let file = File::open(commitment_path(hash))?; let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; if fd.field != F::FIELD { bail!("Invalid field: {}. Expected {}", &fd.field, &F::FIELD) } else { let commitment: Commitment = fd.extract()?; - if format!("0x{}", commitment.hash.hex_digits()) != hash { + if format!("0x{}", commitment.hidden.value().hex_digits()) != hash { bail!("Hash mismatch. Corrupted commitment file.") } else { + self.store + .intern_z_expr_ptr(&commitment.hidden, &commitment.zstore); println!("Data for {hash} is now available"); } } @@ -432,35 +435,45 @@ impl Repl { } } "lurk.commit" => { - let first = self.peek1(cmd, args)?; - let (first_io, ..) = self.eval_expr(first)?; - self.hide(F::ZERO, first_io.expr)?; + #[cfg(not(target_arch = "wasm32"))] + { + let first = self.peek1(cmd, args)?; + let (first_io, ..) = self.eval_expr(first)?; + self.hide(ff::Field::ZERO, first_io.expr)?; + } } "lurk.hide" => { - let (first, second) = self.peek2(cmd, args)?; - let (first_io, ..) = self - .eval_expr(first) - .with_context(|| "evaluating first arg")?; - let (second_io, ..) = self - .eval_expr(second) - .with_context(|| "evaluating second arg")?; - let Some(secret) = self.store.fetch_num(&first_io.expr) else { + #[cfg(not(target_arch = "wasm32"))] + { + let (first, second) = self.peek2(cmd, args)?; + let (first_io, ..) = self + .eval_expr(first) + .with_context(|| "evaluating first arg")?; + let (second_io, ..) = self + .eval_expr(second) + .with_context(|| "evaluating second arg")?; + let Some(secret) = self.store.fetch_num(&first_io.expr) else { bail!("Secret must be a number. Got {}", first_io.expr.fmt_to_string(&self.store)) }; - self.hide(secret.into_scalar(), second_io.expr)?; + self.hide(secret.into_scalar(), second_io.expr)?; + } } "fetch" => { - let first = self.peek1(cmd, args)?; - let n = self.store.lurk_sym("num"); - let expr = self.store.list(&[n, first]); - let (expr_io, ..) = self - .eval_expr(expr) - .with_context(|| "evaluating first arg")?; - let hash = self - .store - .fetch_num(&expr_io.expr) - .expect("must be a number"); - self.fetch(&format!("0x{}", hash.into_scalar().hex_digits()))?; + #[cfg(not(target_arch = "wasm32"))] + { + let first = self.peek1(cmd, args)?; + let n = self.store.lurk_sym("num"); + let expr = self.store.list(&[n, first]); + let (expr_io, ..) = self + .eval_expr(expr) + .with_context(|| "evaluating first arg")?; + let hash = self + .store + .fetch_num(&expr_io.expr) + .expect("must be a number"); + #[cfg(not(target_arch = "wasm32"))] + self.fetch(&format!("0x{}", hash.into_scalar().hex_digits()))?; + } } "clear" => self.env = self.store.nil(), "set-env" => { diff --git a/src/store.rs b/src/store.rs index 95c8c97954..5de0450b0f 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1441,26 +1441,30 @@ impl Store { } } - pub fn intern_z_expr_ptr(&mut self, z_ptr: ZExprPtr, z_store: &ZStore) -> Option> { - if let Some(ptr) = self.fetch_z_expr_ptr(&z_ptr) { + pub fn intern_z_expr_ptr( + &mut self, + z_ptr: &ZExprPtr, + z_store: &ZStore, + ) -> Option> { + if let Some(ptr) = self.fetch_z_expr_ptr(z_ptr) { Some(ptr) } else { use ZExpr::*; - match (z_ptr.tag(), z_store.get_expr(&z_ptr)) { + match (z_ptr.tag(), z_store.get_expr(z_ptr)) { (ExprTag::Nil, Some(Nil)) => { let ptr = self.lurk_sym("nil"); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) } (ExprTag::Cons, Some(Cons(car, cdr))) => { - let car = self.intern_z_expr_ptr(car, z_store)?; - let cdr = self.intern_z_expr_ptr(cdr, z_store)?; + let car = self.intern_z_expr_ptr(&car, z_store)?; + let cdr = self.intern_z_expr_ptr(&cdr, z_store)?; let ptr = self.intern_cons(car, cdr); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) } (ExprTag::Comm, Some(Comm(secret, payload))) => { - let payload = self.intern_z_expr_ptr(payload, z_store)?; + let payload = self.intern_z_expr_ptr(&payload, z_store)?; let ptr = self.intern_comm(secret, payload); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) @@ -1471,8 +1475,8 @@ impl Store { Some(ptr) } (ExprTag::Str, Some(Str(strcar, strcdr))) => { - let strcar = self.intern_z_expr_ptr(strcar, z_store)?; - let strcdr = self.intern_z_expr_ptr(strcdr, z_store)?; + let strcar = self.intern_z_expr_ptr(&strcar, z_store)?; + let strcdr = self.intern_z_expr_ptr(&strcdr, z_store)?; let ptr = self.intern_strcons(strcar, strcdr); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) @@ -1483,15 +1487,15 @@ impl Store { Some(ptr) } (ExprTag::Sym, Some(Sym(symcar, symcdr))) => { - let symcar = self.intern_z_expr_ptr(symcar, z_store)?; - let symcdr = self.intern_z_expr_ptr(symcdr, z_store)?; + let symcar = self.intern_z_expr_ptr(&symcar, z_store)?; + let symcdr = self.intern_z_expr_ptr(&symcdr, z_store)?; let ptr = self.intern_symcons(symcar, symcdr); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) } (ExprTag::Key, Some(Key(keycar, keycdr))) => { - let keycar = self.intern_z_expr_ptr(keycar, z_store)?; - let keycdr = self.intern_z_expr_ptr(keycdr, z_store)?; + let keycar = self.intern_z_expr_ptr(&keycar, z_store)?; + let keycdr = self.intern_z_expr_ptr(&keycdr, z_store)?; let ptr = self.intern_keycons(keycar, keycdr); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) @@ -1504,8 +1508,8 @@ impl Store { (ExprTag::Char, Some(Char(x))) => Some(x.into()), (ExprTag::U64, Some(UInt(x))) => Some(self.intern_uint(x)), (ExprTag::Thunk, Some(Thunk(value, continuation))) => { - let value = self.intern_z_expr_ptr(value, z_store)?; - let continuation = self.intern_z_cont_ptr(continuation, z_store)?; + let value = self.intern_z_expr_ptr(&value, z_store)?; + let continuation = self.intern_z_cont_ptr(&continuation, z_store)?; let ptr = self.intern_thunk(expr::Thunk { value, continuation, @@ -1521,9 +1525,9 @@ impl Store { closed_env, }), ) => { - let arg = self.intern_z_expr_ptr(arg, z_store)?; - let body = self.intern_z_expr_ptr(body, z_store)?; - let env = self.intern_z_expr_ptr(closed_env, z_store)?; + let arg = self.intern_z_expr_ptr(&arg, z_store)?; + let body = self.intern_z_expr_ptr(&body, z_store)?; + let env = self.intern_z_expr_ptr(&closed_env, z_store)?; let ptr = self.intern_fun(arg, body, env); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) @@ -1549,13 +1553,13 @@ impl Store { pub fn intern_z_cont_ptr( &mut self, - z_ptr: ZContPtr, + z_ptr: &ZContPtr, z_store: &ZStore, ) -> Option> { use ZCont::*; let tag: ContTag = z_ptr.tag(); - if let Some(cont) = z_store.get_cont(&z_ptr) { + if let Some(cont) = z_store.get_cont(z_ptr) { let continuation = match cont { Outermost => Continuation::Outermost, Dummy => Continuation::Dummy, @@ -1564,48 +1568,48 @@ impl Store { saved_env, continuation, } => Continuation::Call0 { - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Call { saved_env, unevaled_arg, continuation, } => Continuation::Call { - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - unevaled_arg: self.intern_z_expr_ptr(unevaled_arg, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + unevaled_arg: self.intern_z_expr_ptr(&unevaled_arg, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Call2 { saved_env, function, continuation, } => Continuation::Call2 { - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - function: self.intern_z_expr_ptr(function, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + function: self.intern_z_expr_ptr(&function, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Tail { saved_env, continuation, } => Continuation::Tail { - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Error => Continuation::Error, Lookup { saved_env, continuation, } => Continuation::Lookup { - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Unop { operator, continuation, } => Continuation::Unop { operator, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Binop { operator, @@ -1614,9 +1618,9 @@ impl Store { continuation, } => Continuation::Binop { operator, - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - unevaled_args: self.intern_z_expr_ptr(unevaled_args, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + unevaled_args: self.intern_z_expr_ptr(&unevaled_args, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Binop2 { operator, @@ -1624,15 +1628,15 @@ impl Store { continuation, } => Continuation::Binop2 { operator, - evaled_arg: self.intern_z_expr_ptr(evaled_arg, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + evaled_arg: self.intern_z_expr_ptr(&evaled_arg, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, If { unevaled_args, continuation, } => Continuation::If { - unevaled_args: self.intern_z_expr_ptr(unevaled_args, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + unevaled_args: self.intern_z_expr_ptr(&unevaled_args, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Let { var, @@ -1640,10 +1644,10 @@ impl Store { saved_env, continuation, } => Continuation::Let { - var: self.intern_z_expr_ptr(var, z_store)?, - body: self.intern_z_expr_ptr(body, z_store)?, - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + var: self.intern_z_expr_ptr(&var, z_store)?, + body: self.intern_z_expr_ptr(&body, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, LetRec { var, @@ -1651,13 +1655,13 @@ impl Store { saved_env, continuation, } => Continuation::LetRec { - var: self.intern_z_expr_ptr(var, z_store)?, - body: self.intern_z_expr_ptr(body, z_store)?, - saved_env: self.intern_z_expr_ptr(saved_env, z_store)?, - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + var: self.intern_z_expr_ptr(&var, z_store)?, + body: self.intern_z_expr_ptr(&body, z_store)?, + saved_env: self.intern_z_expr_ptr(&saved_env, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, Emit { continuation } => Continuation::Emit { - continuation: self.intern_z_cont_ptr(continuation, z_store)?, + continuation: self.intern_z_cont_ptr(&continuation, z_store)?, }, }; @@ -1892,10 +1896,10 @@ impl ZStore { let mut store = Store::new(); for ptr in self.expr_map.keys() { - store.intern_z_expr_ptr(*ptr, self); + store.intern_z_expr_ptr(ptr, self); } for ptr in self.cont_map.keys() { - store.intern_z_cont_ptr(*ptr, self); + store.intern_z_cont_ptr(ptr, self); } store } @@ -1904,12 +1908,12 @@ impl ZStore { let mut store = Store::new(); for ptr in self.expr_map.keys() { - store.intern_z_expr_ptr(*ptr, self); + store.intern_z_expr_ptr(ptr, self); } for ptr in self.cont_map.keys() { - store.intern_z_cont_ptr(*ptr, self); + store.intern_z_cont_ptr(ptr, self); } - match store.intern_z_expr_ptr(*z_ptr, self) { + match store.intern_z_expr_ptr(z_ptr, self) { Some(ptr_ret) => Ok((store, ptr_ret)), None => Err(Error("Couldn't find given ZExprPtr".into())), } From 4d4b44d0f30ae51c75aadf70559b4a241fe26b06 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Tue, 18 Jul 2023 23:19:08 -0300 Subject: [PATCH 05/20] improve Commitment implementations; open meta command that prints data --- src/cli/commitment.rs | 4 +++- src/cli/repl.rs | 50 +++++++++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 50b67dc458..d38d1fb9b4 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -12,7 +12,7 @@ pub struct Commitment { pub zstore: ZStore, } -impl<'a, F: LurkField + Serialize + Deserialize<'a>> Commitment { +impl Commitment { #[allow(dead_code)] pub fn new(secret: F, payload: Ptr, store: &mut Store) -> Result { let hidden = store.hide(secret, payload); @@ -23,7 +23,9 @@ impl<'a, F: LurkField + Serialize + Deserialize<'a>> Commitment { zstore: zstore.unwrap(), }) } +} +impl Commitment { #[cfg(not(target_arch = "wasm32"))] pub fn persist(&self, hash: &str) -> Result<()> { use super::{field_data::FieldData, paths::commitment_path}; diff --git a/src/cli/repl.rs b/src/cli/repl.rs index a5dac4f681..6f1675944e 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -247,7 +247,7 @@ impl Repl { } #[cfg(not(target_arch = "wasm32"))] - fn fetch(&mut self, hash: &str) -> Result<()> { + fn fetch(&mut self, hash: &str, print_data: bool) -> Result<()> { use super::{commitment::Commitment, field_data::FieldData, paths::commitment_path}; use std::{fs::File, io::BufReader}; @@ -260,9 +260,15 @@ impl Repl { if format!("0x{}", commitment.hidden.value().hex_digits()) != hash { bail!("Hash mismatch. Corrupted commitment file.") } else { - self.store - .intern_z_expr_ptr(&commitment.hidden, &commitment.zstore); - println!("Data for {hash} is now available"); + let data = self + .store + .intern_z_expr_ptr(&commitment.hidden, &commitment.zstore) + .unwrap(); + if print_data { + println!("{}", data.fmt_to_string(&self.store)); + } else { + println!("Data for {hash} is now available"); + } } } Ok(()) @@ -310,6 +316,21 @@ impl Repl { } } + #[allow(dead_code)] + fn get_comm_hash(&mut self, cmd: &str, args: &Ptr) -> Result { + let first = self.peek1(cmd, args)?; + let n = self.store.lurk_sym("num"); + let expr = self.store.list(&[n, first]); + let (expr_io, ..) = self + .eval_expr(expr) + .with_context(|| "evaluating first arg")?; + let hash = self + .store + .fetch_num(&expr_io.expr) + .expect("must be a number"); + Ok(format!("0x{}", hash.into_scalar().hex_digits())) + } + fn handle_meta_cases(&mut self, cmd: &str, args: &Ptr, pwd_path: &Path) -> Result<()> { match cmd { "def" => { @@ -461,18 +482,15 @@ impl Repl { "fetch" => { #[cfg(not(target_arch = "wasm32"))] { - let first = self.peek1(cmd, args)?; - let n = self.store.lurk_sym("num"); - let expr = self.store.list(&[n, first]); - let (expr_io, ..) = self - .eval_expr(expr) - .with_context(|| "evaluating first arg")?; - let hash = self - .store - .fetch_num(&expr_io.expr) - .expect("must be a number"); - #[cfg(not(target_arch = "wasm32"))] - self.fetch(&format!("0x{}", hash.into_scalar().hex_digits()))?; + let hash = self.get_comm_hash(cmd, args)?; + self.fetch(&hash, false)?; + } + } + "lurk.open" => { + #[cfg(not(target_arch = "wasm32"))] + { + let hash = self.get_comm_hash(cmd, args)?; + self.fetch(&hash, true)?; } } "clear" => self.env = self.store.nil(), From 4cd39145e9f2a05081953bef19abecf8215fe39a Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Tue, 18 Jul 2023 23:23:34 -0300 Subject: [PATCH 06/20] add pub(crate) --- src/cli/commitment.rs | 4 ++-- src/cli/field_data.rs | 2 +- src/cli/lurk_proof.rs | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index d38d1fb9b4..1f126ea7fe 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; /// WARNING: CONTAINS PRIVATE DATA #[derive(Serialize, Deserialize)] pub struct Commitment { - pub hidden: ZExprPtr, - pub zstore: ZStore, + pub(crate) hidden: ZExprPtr, + pub(crate) zstore: ZStore, } impl Commitment { diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index 6b0ae9da47..ee7e0428e3 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -6,7 +6,7 @@ use lurk::field::{LanguageField, LurkField}; /// A wrapper for data whose deserialization depends on a certain LurkField #[derive(Serialize, Deserialize)] pub struct FieldData { - pub field: LanguageField, + pub(crate) field: LanguageField, data: Vec, } diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index cbd3ab40b0..87b800a874 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -26,15 +26,15 @@ use super::{ /// Carries extra information to help with visualization, experiments etc #[derive(Serialize, Deserialize)] pub struct LurkProofMeta { - pub iterations: usize, - pub evaluation_cost: u128, - pub generation_cost: u128, - pub compression_cost: u128, - pub status: Status, - pub expression: ZExprPtr, - pub environment: ZExprPtr, - pub result: ZExprPtr, - pub zstore: ZStore, + pub(crate) iterations: usize, + pub(crate) evaluation_cost: u128, + pub(crate) generation_cost: u128, + pub(crate) compression_cost: u128, + pub(crate) status: Status, + pub(crate) expression: ZExprPtr, + pub(crate) environment: ZExprPtr, + pub(crate) result: ZExprPtr, + pub(crate) zstore: ZStore, } impl LurkProofMeta { From c1f3fb885b6db4d08e717d2db0c1c88c4fc60cac Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 10:12:04 -0300 Subject: [PATCH 07/20] enclose non-wasm code in particular modules --- src/cli/commitment.rs | 55 +++++++++++---------- src/cli/lurk_proof.rs | 109 ++++++++++++++++++++---------------------- 2 files changed, 84 insertions(+), 80 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 1f126ea7fe..a804d3ec84 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -1,38 +1,45 @@ -use anyhow::Result; - -use lurk::{field::LurkField, ptr::Ptr, store::Store, z_ptr::ZExprPtr, z_store::ZStore}; +use lurk::{field::LurkField, z_ptr::ZExprPtr, z_store::ZStore}; use serde::{Deserialize, Serialize}; /// Holds data for commitments. /// -/// WARNING: CONTAINS PRIVATE DATA +/// **Warning**: Holds private data. The `zstore` attribute contains the secret +/// used to hide the original payload. #[derive(Serialize, Deserialize)] pub struct Commitment { pub(crate) hidden: ZExprPtr, pub(crate) zstore: ZStore, } -impl Commitment { - #[allow(dead_code)] - pub fn new(secret: F, payload: Ptr, store: &mut Store) -> Result { - let hidden = store.hide(secret, payload); - let mut zstore = Some(ZStore::::default()); - let hidden = store.get_z_expr(&hidden, &mut zstore)?.0; - Ok(Self { - hidden, - zstore: zstore.unwrap(), - }) - } -} +#[cfg(not(target_arch = "wasm32"))] +mod cli { + use anyhow::Result; + use lurk::{field::LurkField, ptr::Ptr, store::Store, z_store::ZStore}; + use serde::Serialize; + use std::{fs::File, io::BufWriter}; -impl Commitment { - #[cfg(not(target_arch = "wasm32"))] - pub fn persist(&self, hash: &str) -> Result<()> { - use super::{field_data::FieldData, paths::commitment_path}; - use std::{fs::File, io::BufWriter}; + use crate::cli::{field_data::FieldData, paths::commitment_path}; + + use super::Commitment; + + impl Commitment { + #[allow(dead_code)] + pub fn new(secret: F, payload: Ptr, store: &mut Store) -> Result { + let hidden = store.hide(secret, payload); + let mut zstore = Some(ZStore::::default()); + let hidden = store.get_z_expr(&hidden, &mut zstore)?.0; + Ok(Self { + hidden, + zstore: zstore.unwrap(), + }) + } + } - let fd = &FieldData::wrap::>(self)?; - bincode::serialize_into(BufWriter::new(&File::create(commitment_path(hash))?), fd)?; - Ok(()) + impl Commitment { + pub fn persist(&self, hash: &str) -> Result<()> { + let fd = &FieldData::wrap::>(self)?; + bincode::serialize_into(BufWriter::new(&File::create(commitment_path(hash))?), fd)?; + Ok(()) + } } } diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index 87b800a874..ae36118aa0 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -1,7 +1,5 @@ use serde::{Deserialize, Serialize}; -use anyhow::Result; - use lurk::{ eval::{ lang::{Coproc, Lang}, @@ -9,20 +7,10 @@ use lurk::{ }, field::LurkField, proof::nova, - public_parameters::public_params, z_ptr::ZExprPtr, z_store::ZStore, }; -#[cfg(not(target_arch = "wasm32"))] -use std::{fs::File, io::BufReader, io::BufWriter}; - -#[cfg(not(target_arch = "wasm32"))] -use super::{ - field_data::FieldData, - paths::{proof_meta_path, proof_path}, -}; - /// Carries extra information to help with visualization, experiments etc #[derive(Serialize, Deserialize)] pub struct LurkProofMeta { @@ -37,15 +25,6 @@ pub struct LurkProofMeta { pub(crate) zstore: ZStore, } -impl LurkProofMeta { - #[cfg(not(target_arch = "wasm32"))] - pub fn persist(&self, id: &str) -> Result<()> { - let fd = &FieldData::wrap::>(self)?; - bincode::serialize_into(BufWriter::new(&File::create(proof_meta_path(id))?), fd)?; - Ok(()) - } -} - type F = pasta_curves::pallas::Scalar; // TODO: generalize this /// Minimal data structure containing just enough for proof verification @@ -61,47 +40,65 @@ pub enum LurkProof<'a> { }, } -impl<'a> LurkProof<'a> { - #[cfg(not(target_arch = "wasm32"))] - pub fn persist(&self, id: &str) -> Result<()> { - let fd = &FieldData::wrap::>(self)?; - bincode::serialize_into(BufWriter::new(&File::create(proof_path(id))?), fd)?; - Ok(()) +#[cfg(not(target_arch = "wasm32"))] +mod cli { + use crate::cli::{ + field_data::FieldData, + paths::{proof_meta_path, proof_path}, + }; + use anyhow::Result; + use lurk::{field::LurkField, public_parameters::public_params}; + use serde::Serialize; + use std::{fs::File, io::BufReader, io::BufWriter}; + + use super::{LurkProof, LurkProofMeta}; + + impl LurkProofMeta { + pub fn persist(&self, id: &str) -> Result<()> { + let fd = &FieldData::wrap::>(self)?; + bincode::serialize_into(BufWriter::new(&File::create(proof_meta_path(id))?), fd)?; + Ok(()) + } } - #[allow(dead_code)] - fn verify(self) -> Result { - match self { - Self::Nova { - proof, - public_inputs, - public_outputs, - num_steps, - rc, - lang, - } => { - log::info!("Loading public parameters"); - let pp = public_params(rc, std::sync::Arc::new(lang))?; - Ok(proof.verify(&pp, num_steps, &public_inputs, &public_outputs)?) + impl<'a> LurkProof<'a> { + pub fn persist(&self, id: &str) -> Result<()> { + let fd = &FieldData::wrap::>(self)?; + bincode::serialize_into(BufWriter::new(&File::create(proof_path(id))?), fd)?; + Ok(()) + } + + fn verify(self) -> Result { + match self { + Self::Nova { + proof, + public_inputs, + public_outputs, + num_steps, + rc, + lang, + } => { + log::info!("Loading public parameters"); + let pp = public_params(rc, std::sync::Arc::new(lang))?; + Ok(proof.verify(&pp, num_steps, &public_inputs, &public_outputs)?) + } } } - } - #[allow(dead_code)] - fn print_verification(proof_id: &str, success: bool) { - if success { - println!("✓ Proof \"{proof_id}\" verified"); - } else { - println!("✗ Proof \"{proof_id}\" failed on verification"); + fn print_verification(proof_id: &str, success: bool) { + if success { + println!("✓ Proof \"{proof_id}\" verified"); + } else { + println!("✗ Proof \"{proof_id}\" failed on verification"); + } } - } - #[cfg(not(target_arch = "wasm32"))] - pub fn verify_proof(proof_id: &str) -> Result<()> { - let file = File::open(proof_path(proof_id))?; - let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - let lurk_proof: LurkProof = fd.extract()?; - Self::print_verification(proof_id, lurk_proof.verify()?); - Ok(()) + pub fn verify_proof(proof_id: &str) -> Result<()> { + let file = File::open(proof_path(proof_id))?; + let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; + let lurk_proof: LurkProof = fd.extract()?; + Self::print_verification(proof_id, lurk_proof.verify()?); + Ok(()) + } } } From af42e1bafb1eff5bd27157bf6cd3078af3ca0b8a Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 10:31:31 -0300 Subject: [PATCH 08/20] improved remarks on private data --- src/cli/commitment.rs | 4 ++-- src/cli/lurk_proof.rs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index a804d3ec84..8730877310 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize}; /// Holds data for commitments. /// -/// **Warning**: Holds private data. The `zstore` attribute contains the secret -/// used to hide the original payload. +/// **Warning**: holds private data. The `ZStore` contains the secret used to +/// hide the original payload. #[derive(Serialize, Deserialize)] pub struct Commitment { pub(crate) hidden: ZExprPtr, diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index ae36118aa0..2ac2c7c007 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -11,7 +11,11 @@ use lurk::{ z_store::ZStore, }; -/// Carries extra information to help with visualization, experiments etc +/// Carries extra information to help with visualization, experiments etc. +/// +/// Note: the `ZStore` in this struct only has enough data to recover the meaning +/// of the claim being proven: `expression`, when evaluated in the context of +/// `environment`, is reduced to `result`. It doesn't contain private data. #[derive(Serialize, Deserialize)] pub struct LurkProofMeta { pub(crate) iterations: usize, From 5fe6b6a7c9b1682fe1a84c0aa58b3ac4c7bf911e Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 11:03:44 -0300 Subject: [PATCH 09/20] better generalizations in lurk_proof --- src/cli/lurk_proof.rs | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index 2ac2c7c007..f80bc9ead7 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use lurk::{ + coprocessor::Coprocessor, eval::{ lang::{Coproc, Lang}, Status, @@ -29,11 +30,14 @@ pub struct LurkProofMeta { pub(crate) zstore: ZStore, } -type F = pasta_curves::pallas::Scalar; // TODO: generalize this +type Pallas = pasta_curves::pallas::Scalar; // TODO: generalize this /// Minimal data structure containing just enough for proof verification #[derive(Serialize, Deserialize)] -pub enum LurkProof<'a> { +pub enum LurkProof<'a, F: LurkField> +where + Coproc: Coprocessor, +{ Nova { proof: nova::Proof<'a, Coproc>, public_inputs: Vec, @@ -51,11 +55,14 @@ mod cli { paths::{proof_meta_path, proof_path}, }; use anyhow::Result; - use lurk::{field::LurkField, public_parameters::public_params}; + use lurk::{ + coprocessor::Coprocessor, eval::lang::Coproc, field::LurkField, + public_parameters::public_params, + }; use serde::Serialize; use std::{fs::File, io::BufReader, io::BufWriter}; - use super::{LurkProof, LurkProofMeta}; + use super::{LurkProof, LurkProofMeta, Pallas}; impl LurkProofMeta { pub fn persist(&self, id: &str) -> Result<()> { @@ -65,13 +72,26 @@ mod cli { } } - impl<'a> LurkProof<'a> { + impl<'a, F: LurkField + Serialize> LurkProof<'a, F> + where + Coproc: Coprocessor, + { pub fn persist(&self, id: &str) -> Result<()> { - let fd = &FieldData::wrap::>(self)?; + let fd = &FieldData::wrap::>(self)?; bincode::serialize_into(BufWriter::new(&File::create(proof_path(id))?), fd)?; Ok(()) } + fn print_verification(proof_id: &str, success: bool) { + if success { + println!("✓ Proof \"{proof_id}\" verified"); + } else { + println!("✗ Proof \"{proof_id}\" failed on verification"); + } + } + } + + impl<'a> LurkProof<'a, Pallas> { fn verify(self) -> Result { match self { Self::Nova { @@ -89,18 +109,10 @@ mod cli { } } - fn print_verification(proof_id: &str, success: bool) { - if success { - println!("✓ Proof \"{proof_id}\" verified"); - } else { - println!("✗ Proof \"{proof_id}\" failed on verification"); - } - } - pub fn verify_proof(proof_id: &str) -> Result<()> { let file = File::open(proof_path(proof_id))?; let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - let lurk_proof: LurkProof = fd.extract()?; + let lurk_proof: LurkProof = fd.extract()?; Self::print_verification(proof_id, lurk_proof.verify()?); Ok(()) } From d87c21d144227c18e4150a5bb416b67c54e6d5c1 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 11:19:32 -0300 Subject: [PATCH 10/20] safer extraction of FieldData --- src/cli/field_data.rs | 7 +++++-- src/cli/lurk_proof.rs | 4 ++-- src/cli/mod.rs | 2 +- src/cli/repl.rs | 26 +++++++++++--------------- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index ee7e0428e3..d2a5272689 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; use lurk::field::{LanguageField, LurkField}; @@ -21,7 +21,10 @@ impl FieldData { } #[inline] - pub fn extract<'a, T: Deserialize<'a>>(&'a self) -> Result { + pub fn extract<'a, F: LurkField, T: Deserialize<'a>>(&'a self) -> Result { + if self.field != F::FIELD { + bail!("Invalid field: {}. Expected {}", &self.field, &F::FIELD) + } Ok(bincode::deserialize(&self.data)?) } } diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index f80bc9ead7..e5cd71c38c 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -109,10 +109,10 @@ mod cli { } } - pub fn verify_proof(proof_id: &str) -> Result<()> { + pub fn verify_proof(proof_id: &str) -> Result<()> { let file = File::open(proof_path(proof_id))?; let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - let lurk_proof: LurkProof = fd.extract()?; + let lurk_proof = fd.extract::>()?; Self::print_verification(proof_id, lurk_proof.verify()?); Ok(()) } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 9c208296f0..3bd4706383 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -378,7 +378,7 @@ pub fn parse_and_run() -> Result<()> { #[cfg(not(target_arch = "wasm32"))] { use crate::cli::lurk_proof::LurkProof; - LurkProof::verify_proof(&verify_args.proof_id)?; + LurkProof::verify_proof::(&verify_args.proof_id)?; } Ok(()) } diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 6f1675944e..6c8eac0f34 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -253,22 +253,18 @@ impl Repl { let file = File::open(commitment_path(hash))?; let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - if fd.field != F::FIELD { - bail!("Invalid field: {}. Expected {}", &fd.field, &F::FIELD) + let commitment = fd.extract::>()?; + if format!("0x{}", commitment.hidden.value().hex_digits()) != hash { + bail!("Hash mismatch. Corrupted commitment file.") } else { - let commitment: Commitment = fd.extract()?; - if format!("0x{}", commitment.hidden.value().hex_digits()) != hash { - bail!("Hash mismatch. Corrupted commitment file.") + let data = self + .store + .intern_z_expr_ptr(&commitment.hidden, &commitment.zstore) + .unwrap(); + if print_data { + println!("{}", data.fmt_to_string(&self.store)); } else { - let data = self - .store - .intern_z_expr_ptr(&commitment.hidden, &commitment.zstore) - .unwrap(); - if print_data { - println!("{}", data.fmt_to_string(&self.store)); - } else { - println!("Data for {hash} is now available"); - } + println!("Data for {hash} is now available"); } } Ok(()) @@ -527,7 +523,7 @@ impl Repl { first.fmt_to_string(&self.store) ), Some(proof_id) => { - LurkProof::verify_proof(&proof_id)?; + LurkProof::verify_proof::(&proof_id)?; } } } From a4cb3e4adadac33ae4450b6e854af17380f89fb9 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 11:22:18 -0300 Subject: [PATCH 11/20] cleaner user feedback on fetch --- src/cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 6c8eac0f34..25ffca226f 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -264,7 +264,7 @@ impl Repl { if print_data { println!("{}", data.fmt_to_string(&self.store)); } else { - println!("Data for {hash} is now available"); + println!("Data is now available"); } } Ok(()) From b79a72da7466baaeaa6e8b0bc04162e9ba769295 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 11:43:58 -0300 Subject: [PATCH 12/20] remove unnecessary allow(dead_code) flag --- src/cli/commitment.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 8730877310..77e67c30b8 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -23,7 +23,6 @@ mod cli { use super::Commitment; impl Commitment { - #[allow(dead_code)] pub fn new(secret: F, payload: Ptr, store: &mut Store) -> Result { let hidden = store.hide(secret, payload); let mut zstore = Some(ZStore::::default()); From 55be212b9f4148f9e77cfbe45ffcacf3e6061fc0 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 13:32:34 -0300 Subject: [PATCH 13/20] clean up paths.rs --- src/cli/commitment.rs | 2 +- src/cli/lurk_proof.rs | 2 +- src/cli/mod.rs | 2 +- src/cli/paths.rs | 85 +++++++++++++++++++------------------------ src/cli/repl.rs | 4 +- 5 files changed, 43 insertions(+), 52 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 77e67c30b8..1cd71d7076 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -18,7 +18,7 @@ mod cli { use serde::Serialize; use std::{fs::File, io::BufWriter}; - use crate::cli::{field_data::FieldData, paths::commitment_path}; + use crate::cli::{field_data::FieldData, paths::cli::commitment_path}; use super::Commitment; diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index e5cd71c38c..757ccd72e9 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -52,7 +52,7 @@ where mod cli { use crate::cli::{ field_data::FieldData, - paths::{proof_meta_path, proof_path}, + paths::cli::{proof_meta_path, proof_path}, }; use anyhow::Result; use lurk::{ diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 3bd4706383..4e75ddd4b4 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -363,7 +363,7 @@ struct VerifyArgs { /// Parses CLI arguments and continues the program flow accordingly pub fn parse_and_run() -> Result<()> { #[cfg(not(target_arch = "wasm32"))] - paths::create_lurk_dirs()?; + paths::cli::create_lurk_dirs()?; if let Ok(repl_cli) = ReplCli::try_parse() { repl_cli.run() diff --git a/src/cli/paths.rs b/src/cli/paths.rs index 01993639ed..258016bd2f 100644 --- a/src/cli/paths.rs +++ b/src/cli/paths.rs @@ -1,61 +1,52 @@ #[cfg(not(target_arch = "wasm32"))] -use anyhow::Result; +pub mod cli { + use anyhow::Result; -#[cfg(not(target_arch = "wasm32"))] -use std::{ - fs, - path::{Path, PathBuf}, -}; + use std::{ + fs, + path::{Path, PathBuf}, + }; -#[cfg(not(target_arch = "wasm32"))] -fn home_dir() -> PathBuf { - home::home_dir().expect("missing home directory") -} + fn home_dir() -> PathBuf { + home::home_dir().expect("missing home directory") + } -#[cfg(not(target_arch = "wasm32"))] -pub fn lurk_dir() -> PathBuf { - home_dir().join(Path::new(".lurk")) -} + pub fn lurk_dir() -> PathBuf { + home_dir().join(Path::new(".lurk")) + } -#[cfg(not(target_arch = "wasm32"))] -pub fn proofs_dir() -> PathBuf { - lurk_dir().join(Path::new("proofs")) -} + pub fn proofs_dir() -> PathBuf { + lurk_dir().join(Path::new("proofs")) + } -#[cfg(not(target_arch = "wasm32"))] -pub fn commits_dir() -> PathBuf { - lurk_dir().join(Path::new("commits")) -} + pub fn commits_dir() -> PathBuf { + lurk_dir().join(Path::new("commits")) + } -#[cfg(not(target_arch = "wasm32"))] -pub fn lurk_leaf_dirs() -> [PathBuf; 2] { - [proofs_dir(), commits_dir()] -} + pub fn lurk_leaf_dirs() -> [PathBuf; 2] { + [proofs_dir(), commits_dir()] + } -#[cfg(not(target_arch = "wasm32"))] -pub fn create_lurk_dirs() -> Result<()> { - for dir in lurk_leaf_dirs() { - fs::create_dir_all(dir)?; + pub fn create_lurk_dirs() -> Result<()> { + for dir in lurk_leaf_dirs() { + fs::create_dir_all(dir)?; + } + Ok(()) } - Ok(()) -} -#[cfg(not(target_arch = "wasm32"))] -pub fn repl_history() -> PathBuf { - lurk_dir().join(Path::new("repl-history")) -} + pub fn repl_history() -> PathBuf { + lurk_dir().join(Path::new("repl-history")) + } -#[cfg(not(target_arch = "wasm32"))] -pub fn commitment_path(hash: &str) -> PathBuf { - commits_dir().join(Path::new(hash)) -} + pub fn commitment_path(hash: &str) -> PathBuf { + commits_dir().join(Path::new(hash)) + } -#[cfg(not(target_arch = "wasm32"))] -pub fn proof_path(name: &str) -> PathBuf { - proofs_dir().join(Path::new(name)).with_extension("proof") -} + pub fn proof_path(name: &str) -> PathBuf { + proofs_dir().join(Path::new(name)).with_extension("proof") + } -#[cfg(not(target_arch = "wasm32"))] -pub fn proof_meta_path(name: &str) -> PathBuf { - proofs_dir().join(Path::new(name)).with_extension("meta") + pub fn proof_meta_path(name: &str) -> PathBuf { + proofs_dir().join(Path::new(name)).with_extension("meta") + } } diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 25ffca226f..d7cd951398 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -248,7 +248,7 @@ impl Repl { #[cfg(not(target_arch = "wasm32"))] fn fetch(&mut self, hash: &str, print_data: bool) -> Result<()> { - use super::{commitment::Commitment, field_data::FieldData, paths::commitment_path}; + use super::{commitment::Commitment, field_data::FieldData, paths::cli::commitment_path}; use std::{fs::File, io::BufReader}; let file = File::open(commitment_path(hash))?; @@ -644,7 +644,7 @@ impl Repl { })); #[cfg(not(target_arch = "wasm32"))] - let history_path = &crate::cli::paths::repl_history(); + let history_path = &crate::cli::paths::cli::repl_history(); #[cfg(not(target_arch = "wasm32"))] if history_path.exists() { From d66321ef55d079f0e00cd096b83e805c0493435a Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 20:52:08 -0300 Subject: [PATCH 14/20] review suggestions + revamp --- Cargo.lock | 10 +++ Cargo.toml | 1 + src/cli/commitment.rs | 20 +++-- src/cli/field_data.rs | 86 +++++++++++++++----- src/cli/lurk_proof.rs | 76 +++++++++--------- src/cli/mod.rs | 4 +- src/cli/paths.rs | 6 +- src/cli/repl.rs | 181 ++++++++++++++++++++++-------------------- src/z_data/z_ptr.rs | 4 + 9 files changed, 231 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db8e07426c..c51481d449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1497,6 +1497,7 @@ dependencies = [ "rustyline", "rustyline-derive", "serde", + "serde_bytes", "serde_json", "serde_repr", "sha2", @@ -2426,6 +2427,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.167" diff --git a/Cargo.toml b/Cargo.toml index 39a0b231a6..ff404b5ebf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ rand_xorshift = "0.3.0" rayon = "1.7.0" rustyline-derive = "0.8.0" serde = { workspace = true, features = ["derive"] } +serde_bytes = "0.11.12" serde_json = { workspace = true } serde_repr = "0.1.10" tap = "1.0.1" diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 1cd71d7076..6523527b7f 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -1,6 +1,8 @@ use lurk::{field::LurkField, z_ptr::ZExprPtr, z_store::ZStore}; use serde::{Deserialize, Serialize}; +use super::field_data::HasFieldModulus; + /// Holds data for commitments. /// /// **Warning**: holds private data. The `ZStore` contains the secret used to @@ -11,14 +13,19 @@ pub struct Commitment { pub(crate) zstore: ZStore, } +impl HasFieldModulus for Commitment { + fn field_modulus() -> String { + F::MODULUS.to_owned() + } +} + #[cfg(not(target_arch = "wasm32"))] -mod cli { +mod non_wasm { use anyhow::Result; use lurk::{field::LurkField, ptr::Ptr, store::Store, z_store::ZStore}; use serde::Serialize; - use std::{fs::File, io::BufWriter}; - use crate::cli::{field_data::FieldData, paths::cli::commitment_path}; + use crate::cli::{field_data::FieldData, paths::non_wasm::commitment_path}; use super::Commitment; @@ -35,10 +42,9 @@ mod cli { } impl Commitment { - pub fn persist(&self, hash: &str) -> Result<()> { - let fd = &FieldData::wrap::>(self)?; - bincode::serialize_into(BufWriter::new(&File::create(commitment_path(hash))?), fd)?; - Ok(()) + #[inline] + pub fn persist(self, hash: &str) -> Result<()> { + FieldData::dump(self, commitment_path(hash)) } } } diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index d2a5272689..b38cff63b0 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -1,30 +1,74 @@ -use anyhow::{bail, Result}; -use serde::{Deserialize, Serialize}; +use anyhow::Result; -use lurk::field::{LanguageField, LurkField}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; -/// A wrapper for data whose deserialization depends on a certain LurkField -#[derive(Serialize, Deserialize)] -pub struct FieldData { - pub(crate) field: LanguageField, - data: Vec, +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct FieldData(T); + +pub(crate) trait HasFieldModulus { + fn field_modulus() -> String; +} + +impl<'de, T: DeserializeOwned + HasFieldModulus> Deserialize<'de> for FieldData { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Labeled { + label: String, + #[serde(with = "serde_bytes")] + bytes: Vec, + } + let level1_struct = Labeled::deserialize(deserializer)?; + if level1_struct.label != T::field_modulus() { + return Err(serde::de::Error::custom("Field mismatch")); + }; + let t: T = + bincode::deserialize(&level1_struct.bytes[..]).map_err(serde::de::Error::custom)?; + Ok(FieldData(t)) + } } -#[allow(dead_code)] -impl FieldData { - #[inline] - pub fn wrap(t: &T) -> Result { - Ok(Self { - field: F::FIELD, - data: bincode::serialize(t)?, - }) +impl Serialize for FieldData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + #[derive(Serialize)] + struct Labeled { + label: String, + #[serde(with = "serde_bytes")] + bytes: Vec, + } + let bytes = bincode::serialize(&self.0).map_err(serde::ser::Error::custom)?; + let labeled = Labeled { + label: T::field_modulus(), + bytes, + }; + labeled.serialize(serializer) + } +} + +#[cfg(not(target_arch = "wasm32"))] +pub mod non_wasm { + use super::{FieldData, HasFieldModulus}; + use anyhow::Result; + use serde::{de::DeserializeOwned, Serialize}; + use std::path::PathBuf; + + impl FieldData { + pub fn dump(t: T, path: PathBuf) -> Result<()> { + let bytes = bincode::serialize(&FieldData(t))?; + Ok(std::fs::write(path, bytes)?) + } } - #[inline] - pub fn extract<'a, F: LurkField, T: Deserialize<'a>>(&'a self) -> Result { - if self.field != F::FIELD { - bail!("Invalid field: {}. Expected {}", &self.field, &F::FIELD) + impl FieldData { + pub fn load(path: PathBuf) -> Result { + let bytes = std::fs::read(path)?; + let fd: FieldData = bincode::deserialize(&bytes)?; + Ok(fd.0) } - Ok(bincode::deserialize(&self.data)?) } } diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index 757ccd72e9..dc2a942e9e 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -2,16 +2,15 @@ use serde::{Deserialize, Serialize}; use lurk::{ coprocessor::Coprocessor, - eval::{ - lang::{Coproc, Lang}, - Status, - }, + eval::lang::{Coproc, Lang}, field::LurkField, proof::nova, - z_ptr::ZExprPtr, + z_ptr::{ZContPtr, ZExprPtr}, z_store::ZStore, }; +use super::field_data::HasFieldModulus; + /// Carries extra information to help with visualization, experiments etc. /// /// Note: the `ZStore` in this struct only has enough data to recover the meaning @@ -20,16 +19,21 @@ use lurk::{ #[derive(Serialize, Deserialize)] pub struct LurkProofMeta { pub(crate) iterations: usize, - pub(crate) evaluation_cost: u128, - pub(crate) generation_cost: u128, - pub(crate) compression_cost: u128, - pub(crate) status: Status, - pub(crate) expression: ZExprPtr, - pub(crate) environment: ZExprPtr, - pub(crate) result: ZExprPtr, + pub(crate) expr: ZExprPtr, + pub(crate) env: ZExprPtr, + pub(crate) cont: ZContPtr, + pub(crate) expr_out: ZExprPtr, + pub(crate) env_out: ZExprPtr, + pub(crate) cont_out: ZContPtr, pub(crate) zstore: ZStore, } +impl HasFieldModulus for LurkProofMeta { + fn field_modulus() -> String { + F::MODULUS.to_owned() + } +} + type Pallas = pasta_curves::pallas::Scalar; // TODO: generalize this /// Minimal data structure containing just enough for proof verification @@ -48,11 +52,20 @@ where }, } +impl<'a, F: LurkField> HasFieldModulus for LurkProof<'a, F> +where + Coproc: Coprocessor, +{ + fn field_modulus() -> String { + F::MODULUS.to_owned() + } +} + #[cfg(not(target_arch = "wasm32"))] -mod cli { +mod non_wasm { use crate::cli::{ field_data::FieldData, - paths::cli::{proof_meta_path, proof_path}, + paths::non_wasm::{proof_meta_path, proof_path}, }; use anyhow::Result; use lurk::{ @@ -60,15 +73,13 @@ mod cli { public_parameters::public_params, }; use serde::Serialize; - use std::{fs::File, io::BufReader, io::BufWriter}; use super::{LurkProof, LurkProofMeta, Pallas}; impl LurkProofMeta { - pub fn persist(&self, id: &str) -> Result<()> { - let fd = &FieldData::wrap::>(self)?; - bincode::serialize_into(BufWriter::new(&File::create(proof_meta_path(id))?), fd)?; - Ok(()) + #[inline] + pub fn persist(self, id: &str) -> Result<()> { + FieldData::dump(self, proof_meta_path(id)) } } @@ -76,18 +87,9 @@ mod cli { where Coproc: Coprocessor, { - pub fn persist(&self, id: &str) -> Result<()> { - let fd = &FieldData::wrap::>(self)?; - bincode::serialize_into(BufWriter::new(&File::create(proof_path(id))?), fd)?; - Ok(()) - } - - fn print_verification(proof_id: &str, success: bool) { - if success { - println!("✓ Proof \"{proof_id}\" verified"); - } else { - println!("✗ Proof \"{proof_id}\" failed on verification"); - } + #[inline] + pub fn persist(self, id: &str) -> Result<()> { + FieldData::dump(self, proof_path(id)) } } @@ -109,11 +111,13 @@ mod cli { } } - pub fn verify_proof(proof_id: &str) -> Result<()> { - let file = File::open(proof_path(proof_id))?; - let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - let lurk_proof = fd.extract::>()?; - Self::print_verification(proof_id, lurk_proof.verify()?); + pub fn verify_proof(proof_id: &str) -> Result<()> { + let lurk_proof: LurkProof<'_, Pallas> = FieldData::load(proof_path(proof_id))?; + if lurk_proof.verify()? { + println!("✓ Proof \"{proof_id}\" verified"); + } else { + println!("✗ Proof \"{proof_id}\" failed on verification"); + } Ok(()) } } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4e75ddd4b4..0fbda97f30 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -363,7 +363,7 @@ struct VerifyArgs { /// Parses CLI arguments and continues the program flow accordingly pub fn parse_and_run() -> Result<()> { #[cfg(not(target_arch = "wasm32"))] - paths::cli::create_lurk_dirs()?; + paths::non_wasm::create_lurk_dirs()?; if let Ok(repl_cli) = ReplCli::try_parse() { repl_cli.run() @@ -378,7 +378,7 @@ pub fn parse_and_run() -> Result<()> { #[cfg(not(target_arch = "wasm32"))] { use crate::cli::lurk_proof::LurkProof; - LurkProof::verify_proof::(&verify_args.proof_id)?; + LurkProof::verify_proof(&verify_args.proof_id)?; } Ok(()) } diff --git a/src/cli/paths.rs b/src/cli/paths.rs index 258016bd2f..e416eada26 100644 --- a/src/cli/paths.rs +++ b/src/cli/paths.rs @@ -1,5 +1,5 @@ #[cfg(not(target_arch = "wasm32"))] -pub mod cli { +pub mod non_wasm { use anyhow::Result; use std::{ @@ -38,8 +38,8 @@ pub mod cli { lurk_dir().join(Path::new("repl-history")) } - pub fn commitment_path(hash: &str) -> PathBuf { - commits_dir().join(Path::new(hash)) + pub fn commitment_path(name: &str) -> PathBuf { + commits_dir().join(Path::new(name)) } pub fn proof_path(name: &str) -> PathBuf { diff --git a/src/cli/repl.rs b/src/cli/repl.rs index d7cd951398..6cee784354 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -1,7 +1,7 @@ use std::path::Path; use std::sync::Arc; -use std::{fs::read_to_string, process, time::Instant}; +use std::{fs::read_to_string, process}; use anyhow::{bail, Context, Result}; @@ -100,7 +100,6 @@ impl Backend { struct Evaluation { frames: Vec, Witness, Coproc>>, iterations: usize, - cost: u128, } #[allow(dead_code)] @@ -130,15 +129,6 @@ fn pad(a: usize, m: usize) -> usize { (a + m - 1) / m * m } -#[allow(dead_code)] -fn timestamp() -> u128 { - use std::time::{SystemTime, UNIX_EPOCH}; - SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("We're after UNIX_EPOCH") - .as_nanos() -} - type F = pasta_curves::pallas::Scalar; // TODO: generalize this impl Repl { @@ -160,74 +150,92 @@ impl Repl { #[cfg(not(target_arch = "wasm32"))] pub fn prove_last_frames(&mut self) -> Result<()> { + use ff::Field; + + use crate::cli::paths::non_wasm::proof_path; + match self.evaluation.as_mut() { None => bail!("No evaluation to prove"), - Some(Evaluation { - frames, - iterations, - cost, - }) => match self.backend { + Some(Evaluation { frames, iterations }) => match self.backend { Backend::Nova => { - // padding the frames, if needed - let mut n_frames = frames.len(); - let n_pad = pad(n_frames, self.rc) - n_frames; - if n_pad != 0 { - frames.extend(vec![frames[n_frames - 1].clone(); n_pad]); - n_frames = frames.len(); - } - - let prover = NovaProver::new(self.rc, (*self.lang).clone()); - - info!("Loading public parameters"); - let pp = public_params(self.rc, self.lang.clone())?; - info!("Hydrating the store"); self.store.hydrate_scalar_cache(); // saving to avoid clones let input = &frames[0].input; let output = &frames[*iterations].output; - let status = output.cont.into(); let mut zstore = Some(ZStore::::default()); - let expression = self.store.get_z_expr(&input.expr, &mut zstore)?.0; - let environment = self.store.get_z_expr(&input.env, &mut zstore)?.0; - let result = self.store.get_z_expr(&output.expr, &mut zstore)?.0; - - info!("Proving and compressing"); - let start = Instant::now(); - let (proof, public_inputs, public_outputs, num_steps) = - prover.prove(&pp, frames, &mut self.store, self.lang.clone())?; - let generation = Instant::now(); - let proof = proof.compress(&pp)?; - let compression = Instant::now(); - assert_eq!(self.rc * num_steps, n_frames); - assert!(proof.verify(&pp, num_steps, &public_inputs, &public_outputs)?); - - let lurk_proof = &LurkProof::Nova { - proof, - public_inputs, - public_outputs, - num_steps, - rc: self.rc, - lang: (*self.lang).clone(), - }; - - let lurk_proof_meta = &LurkProofMeta { - iterations: *iterations, - evaluation_cost: *cost, - generation_cost: generation.duration_since(start).as_nanos(), - compression_cost: compression.duration_since(generation).as_nanos(), - status, - expression, - environment, - result, - zstore: zstore.unwrap(), - }; - - let id = &format!("{}", timestamp()); - lurk_proof.persist(id)?; - lurk_proof_meta.persist(id)?; - println!("Proof ID: \"{id}\""); + let expr = self.store.get_z_expr(&input.expr, &mut zstore)?.0; + let env = self.store.get_z_expr(&input.env, &mut zstore)?.0; + let cont = self.store.get_z_cont(&input.cont, &mut zstore)?.0; + let expr_out = self.store.get_z_expr(&output.expr, &mut zstore)?.0; + let env_out = self.store.get_z_expr(&output.env, &mut zstore)?.0; + let cont_out = self.store.get_z_cont(&output.cont, &mut zstore)?.0; + + let proof_id = [ + expr.parts(), + env.parts(), + cont.parts(), + expr_out.parts(), + env_out.parts(), + cont_out.parts(), + ] + .iter() + .fold(F::ZERO, |acc, (a, b)| { + self.store.poseidon_cache.hash3(&[acc, *a, *b]) + }) + .hex_digits(); + + let proof_path = proof_path(&proof_id); + if proof_path.exists() { + info!("Proof already cached"); + // TODO: make sure that the proof file is not corrupted + } else { + info!("Proof not cached"); + // padding the frames, if needed + let mut n_frames = frames.len(); + let n_pad = pad(n_frames, self.rc) - n_frames; + if n_pad != 0 { + frames.extend(vec![frames[n_frames - 1].clone(); n_pad]); + n_frames = frames.len(); + } + + info!("Loading public parameters"); + let pp = public_params(self.rc, self.lang.clone())?; + + let prover = NovaProver::new(self.rc, (*self.lang).clone()); + + info!("Proving and compressing"); + let (proof, public_inputs, public_outputs, num_steps) = + prover.prove(&pp, frames, &mut self.store, self.lang.clone())?; + let proof = proof.compress(&pp)?; + assert_eq!(self.rc * num_steps, n_frames); + assert!(proof.verify(&pp, num_steps, &public_inputs, &public_outputs)?); + + let lurk_proof = LurkProof::Nova { + proof, + public_inputs, + public_outputs, + num_steps, + rc: self.rc, + lang: (*self.lang).clone(), + }; + + let lurk_proof_meta = LurkProofMeta { + iterations: *iterations, + expr, + env, + cont, + expr_out, + env_out, + cont_out, + zstore: zstore.unwrap(), + }; + + lurk_proof.persist(&proof_id)?; + lurk_proof_meta.persist(&proof_id)?; + } + println!("Proof ID: \"{proof_id}\""); Ok(()) } Backend::SnarkPackPlus => todo!(), @@ -240,28 +248,31 @@ impl Repl { use super::commitment::Commitment; let commitment = Commitment::new(secret, payload, &mut self.store)?; - let hash = &format!("0x{}", commitment.hidden.value().hex_digits()); + let hash = &commitment.hidden.value().hex_digits(); commitment.persist(hash)?; - println!("Data: {}\nHash: {hash}", payload.fmt_to_string(&self.store)); + println!( + "Data: {}\nHash: 0x{hash}", + payload.fmt_to_string(&self.store) + ); Ok(()) } #[cfg(not(target_arch = "wasm32"))] fn fetch(&mut self, hash: &str, print_data: bool) -> Result<()> { - use super::{commitment::Commitment, field_data::FieldData, paths::cli::commitment_path}; - use std::{fs::File, io::BufReader}; + use super::{ + commitment::Commitment, field_data::FieldData, paths::non_wasm::commitment_path, + }; - let file = File::open(commitment_path(hash))?; - let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?; - let commitment = fd.extract::>()?; - if format!("0x{}", commitment.hidden.value().hex_digits()) != hash { + let commitment: Commitment = FieldData::load(commitment_path(hash))?; + if commitment.hidden.value().hex_digits() != hash { bail!("Hash mismatch. Corrupted commitment file.") } else { - let data = self + let comm = self .store .intern_z_expr_ptr(&commitment.hidden, &commitment.zstore) .unwrap(); if print_data { + let data = self.store.fetch_comm(&comm).unwrap().1; println!("{}", data.fmt_to_string(&self.store)); } else { println!("Data is now available"); @@ -324,7 +335,7 @@ impl Repl { .store .fetch_num(&expr_io.expr) .expect("must be a number"); - Ok(format!("0x{}", hash.into_scalar().hex_digits())) + Ok(hash.into_scalar().hex_digits()) } fn handle_meta_cases(&mut self, cmd: &str, args: &Ptr, pwd_path: &Path) -> Result<()> { @@ -523,7 +534,7 @@ impl Repl { first.fmt_to_string(&self.store) ), Some(proof_id) => { - LurkProof::verify_proof::(&proof_id)?; + LurkProof::verify_proof(&proof_id)?; } } } @@ -548,10 +559,8 @@ impl Repl { } fn eval_expr_and_memoize(&mut self, expr_ptr: Ptr) -> Result<(IO, usize)> { - let start = Instant::now(); let frames = Evaluator::new(expr_ptr, self.env, &mut self.store, self.limit, &self.lang) .get_frames()?; - let cost = Instant::now().duration_since(start).as_nanos(); let last_idx = frames.len() - 1; let last_frame = &frames[last_idx]; @@ -561,11 +570,7 @@ impl Repl { // FIXME: proving is not working for incomplete computations if last_frame.is_complete() { - self.evaluation = Some(Evaluation { - frames, - iterations, - cost, - }) + self.evaluation = Some(Evaluation { frames, iterations }) } else { iterations += 1; } @@ -644,7 +649,7 @@ impl Repl { })); #[cfg(not(target_arch = "wasm32"))] - let history_path = &crate::cli::paths::cli::repl_history(); + let history_path = &crate::cli::paths::non_wasm::repl_history(); #[cfg(not(target_arch = "wasm32"))] if history_path.exists() { diff --git a/src/z_data/z_ptr.rs b/src/z_data/z_ptr.rs index 6279b639de..eb611d3705 100644 --- a/src/z_data/z_ptr.rs +++ b/src/z_data/z_ptr.rs @@ -101,6 +101,10 @@ impl ZPtr { &self.1 } + pub fn parts(&self) -> (F, F) { + (self.tag_field(), self.1) + } + // TODO: Create a permanent format for ZPtr strings/ZIDs /// Converts the ZPtr to a base32-encoded string pub fn to_base32(&self) -> String { From b0a0fde98b7c8aa7b578025f36d6e0d0d36e6fc6 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 23:11:01 -0300 Subject: [PATCH 15/20] encoding proof claim as Lurk data --- src/cli/field_data.rs | 19 +++++------- src/cli/repl.rs | 70 ++++++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index b38cff63b0..613489ed38 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -9,17 +9,18 @@ pub(crate) trait HasFieldModulus { fn field_modulus() -> String; } +#[derive(Deserialize, Serialize)] +struct Labeled { + label: String, + #[serde(with = "serde_bytes")] + bytes: Vec, +} + impl<'de, T: DeserializeOwned + HasFieldModulus> Deserialize<'de> for FieldData { fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { - #[derive(Deserialize)] - struct Labeled { - label: String, - #[serde(with = "serde_bytes")] - bytes: Vec, - } let level1_struct = Labeled::deserialize(deserializer)?; if level1_struct.label != T::field_modulus() { return Err(serde::de::Error::custom("Field mismatch")); @@ -35,12 +36,6 @@ impl Serialize for FieldData { where S: serde::Serializer, { - #[derive(Serialize)] - struct Labeled { - label: String, - #[serde(with = "serde_bytes")] - bytes: Vec, - } let bytes = bincode::serialize(&self.0).map_err(serde::ser::Error::custom)?; let labeled = Labeled { label: T::field_modulus(), diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 6cee784354..ee93268e1f 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -148,11 +148,46 @@ impl Repl { } } + #[allow(dead_code)] + fn proof_claim( + store: &mut Store, + exprs: (Ptr, Ptr), + envs: (Ptr, Ptr), + conts: ((F, F), (F, F)), + ) -> Ptr { + let expr_key = store.key("expr"); + let env_key = store.key("env"); + let cont_key = store.key("cont"); + let expr_out_key = store.key("expr-out"); + let env_out_key = store.key("env-out"); + let cont_out_key = store.key("cont-out"); + let cont_tag = store.num(Num::Scalar(conts.0 .0)); + let cont_val = store.num(Num::Scalar(conts.0 .1)); + let cont = store.cons(cont_tag, cont_val); + let cont_out_tag = store.num(Num::Scalar(conts.1 .0)); + let cont_out_val = store.num(Num::Scalar(conts.1 .1)); + let cont_out = store.cons(cont_out_tag, cont_out_val); + store.list(&[ + expr_key, + exprs.0, + env_key, + envs.0, + cont_key, + cont, + expr_out_key, + exprs.1, + env_out_key, + envs.1, + cont_out_key, + cont_out, + ]) + } + #[cfg(not(target_arch = "wasm32"))] pub fn prove_last_frames(&mut self) -> Result<()> { use ff::Field; - use crate::cli::paths::non_wasm::proof_path; + use crate::cli::{commitment::Commitment, paths::non_wasm::proof_path}; match self.evaluation.as_mut() { None => bail!("No evaluation to prove"), @@ -172,21 +207,19 @@ impl Repl { let env_out = self.store.get_z_expr(&output.env, &mut zstore)?.0; let cont_out = self.store.get_z_cont(&output.cont, &mut zstore)?.0; - let proof_id = [ - expr.parts(), - env.parts(), - cont.parts(), - expr_out.parts(), - env_out.parts(), - cont_out.parts(), - ] - .iter() - .fold(F::ZERO, |acc, (a, b)| { - self.store.poseidon_cache.hash3(&[acc, *a, *b]) - }) - .hex_digits(); + let expr_out_str = output.expr.fmt_to_string(&self.store); + + let proof_claim = Self::proof_claim( + &mut self.store, + (input.expr, output.expr), + (input.env, output.env), + (cont.parts(), cont_out.parts()), + ); + let commitment = Commitment::new(F::ZERO, proof_claim, &mut self.store)?; + let proof_id = &commitment.hidden.value().hex_digits(); + + let proof_path = proof_path(proof_id); - let proof_path = proof_path(&proof_id); if proof_path.exists() { info!("Proof already cached"); // TODO: make sure that the proof file is not corrupted @@ -232,10 +265,11 @@ impl Repl { zstore: zstore.unwrap(), }; - lurk_proof.persist(&proof_id)?; - lurk_proof_meta.persist(&proof_id)?; + lurk_proof.persist(proof_id)?; + lurk_proof_meta.persist(proof_id)?; + commitment.persist(proof_id)?; } - println!("Proof ID: \"{proof_id}\""); + println!("Result: {expr_out_str}\nProof ID: \"{proof_id}\""); Ok(()) } Backend::SnarkPackPlus => todo!(), From e1aa3ccb7043dc6bc30dfbae7b622855de99c815 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 19 Jul 2023 23:15:10 -0300 Subject: [PATCH 16/20] clean up --- src/cli/repl.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cli/repl.rs b/src/cli/repl.rs index ee93268e1f..4f98e4abb3 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -207,8 +207,6 @@ impl Repl { let env_out = self.store.get_z_expr(&output.env, &mut zstore)?.0; let cont_out = self.store.get_z_cont(&output.cont, &mut zstore)?.0; - let expr_out_str = output.expr.fmt_to_string(&self.store); - let proof_claim = Self::proof_claim( &mut self.store, (input.expr, output.expr), @@ -269,7 +267,7 @@ impl Repl { lurk_proof_meta.persist(proof_id)?; commitment.persist(proof_id)?; } - println!("Result: {expr_out_str}\nProof ID: \"{proof_id}\""); + println!("Proof ID: \"{proof_id}\""); Ok(()) } Backend::SnarkPackPlus => todo!(), From 52b4d2babf1c0ceb24234e0f26735193e3509cad Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Thu, 20 Jul 2023 00:07:31 -0300 Subject: [PATCH 17/20] document field_data --- src/cli/commitment.rs | 4 +-- src/cli/field_data.rs | 63 ++++++++++++++++++++++++++----------------- src/cli/lurk_proof.rs | 8 +++--- src/cli/repl.rs | 4 +-- 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 6523527b7f..eb6a7d52a2 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -25,7 +25,7 @@ mod non_wasm { use lurk::{field::LurkField, ptr::Ptr, store::Store, z_store::ZStore}; use serde::Serialize; - use crate::cli::{field_data::FieldData, paths::non_wasm::commitment_path}; + use crate::cli::{field_data::non_wasm::dump, paths::non_wasm::commitment_path}; use super::Commitment; @@ -44,7 +44,7 @@ mod non_wasm { impl Commitment { #[inline] pub fn persist(self, hash: &str) -> Result<()> { - FieldData::dump(self, commitment_path(hash)) + dump(self, commitment_path(hash)) } } } diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index 613489ed38..3ecd3b9ee7 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -2,16 +2,38 @@ use anyhow::Result; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] -pub(crate) struct FieldData(T); +// This module implements a 2-step serde protocol for data that is parametrized +// on an arithmetic field in order to be properly deserialized. +// +// First, we serialize it to a vector of bytes. Then, we wrap the vector with a +// struct that contains the field modulus, which, in turn, is serialized to a +// vector of bytes. +// +// When deserializing, we unwrap the first layer and double check the field +// modulus for consistency. If everything goes well, we further unwrap the second +// layer of bytes. pub(crate) trait HasFieldModulus { fn field_modulus() -> String; } +#[allow(dead_code)] +pub(crate) fn to_bytes(t: T) -> Result> { + Ok(bincode::serialize(&FieldData(t))?) +} + +#[allow(dead_code)] +pub(crate) fn from_bytes(bytes: &[u8]) -> Result { + let FieldData(data) = bincode::deserialize(bytes)?; + Ok(data) +} + +#[derive(Debug, PartialEq, Eq)] +struct FieldData(T); + #[derive(Deserialize, Serialize)] -struct Labeled { - label: String, +struct FieldDataWrap { + field_modulus: String, #[serde(with = "serde_bytes")] bytes: Vec, } @@ -21,12 +43,11 @@ impl<'de, T: DeserializeOwned + HasFieldModulus> Deserialize<'de> for FieldData< where D: serde::Deserializer<'de>, { - let level1_struct = Labeled::deserialize(deserializer)?; - if level1_struct.label != T::field_modulus() { + let fdw = FieldDataWrap::deserialize(deserializer)?; + if fdw.field_modulus != T::field_modulus() { return Err(serde::de::Error::custom("Field mismatch")); }; - let t: T = - bincode::deserialize(&level1_struct.bytes[..]).map_err(serde::de::Error::custom)?; + let t: T = bincode::deserialize(&fdw.bytes).map_err(serde::de::Error::custom)?; Ok(FieldData(t)) } } @@ -36,34 +57,26 @@ impl Serialize for FieldData { where S: serde::Serializer, { - let bytes = bincode::serialize(&self.0).map_err(serde::ser::Error::custom)?; - let labeled = Labeled { - label: T::field_modulus(), - bytes, + let fdw = FieldDataWrap { + field_modulus: T::field_modulus(), + bytes: bincode::serialize(&self.0).map_err(serde::ser::Error::custom)?, }; - labeled.serialize(serializer) + fdw.serialize(serializer) } } #[cfg(not(target_arch = "wasm32"))] pub mod non_wasm { - use super::{FieldData, HasFieldModulus}; + use super::{from_bytes, to_bytes, HasFieldModulus}; use anyhow::Result; use serde::{de::DeserializeOwned, Serialize}; use std::path::PathBuf; - impl FieldData { - pub fn dump(t: T, path: PathBuf) -> Result<()> { - let bytes = bincode::serialize(&FieldData(t))?; - Ok(std::fs::write(path, bytes)?) - } + pub(crate) fn dump(t: T, path: PathBuf) -> Result<()> { + Ok(std::fs::write(path, to_bytes(t)?)?) } - impl FieldData { - pub fn load(path: PathBuf) -> Result { - let bytes = std::fs::read(path)?; - let fd: FieldData = bincode::deserialize(&bytes)?; - Ok(fd.0) - } + pub(crate) fn load(path: PathBuf) -> Result { + from_bytes(&std::fs::read(path)?) } } diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index dc2a942e9e..d7dff0f951 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -64,7 +64,7 @@ where #[cfg(not(target_arch = "wasm32"))] mod non_wasm { use crate::cli::{ - field_data::FieldData, + field_data::non_wasm::{dump, load}, paths::non_wasm::{proof_meta_path, proof_path}, }; use anyhow::Result; @@ -79,7 +79,7 @@ mod non_wasm { impl LurkProofMeta { #[inline] pub fn persist(self, id: &str) -> Result<()> { - FieldData::dump(self, proof_meta_path(id)) + dump(self, proof_meta_path(id)) } } @@ -89,7 +89,7 @@ mod non_wasm { { #[inline] pub fn persist(self, id: &str) -> Result<()> { - FieldData::dump(self, proof_path(id)) + dump(self, proof_path(id)) } } @@ -112,7 +112,7 @@ mod non_wasm { } pub fn verify_proof(proof_id: &str) -> Result<()> { - let lurk_proof: LurkProof<'_, Pallas> = FieldData::load(proof_path(proof_id))?; + let lurk_proof: LurkProof<'_, Pallas> = load(proof_path(proof_id))?; if lurk_proof.verify()? { println!("✓ Proof \"{proof_id}\" verified"); } else { diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 4f98e4abb3..34e6bddd4a 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -292,10 +292,10 @@ impl Repl { #[cfg(not(target_arch = "wasm32"))] fn fetch(&mut self, hash: &str, print_data: bool) -> Result<()> { use super::{ - commitment::Commitment, field_data::FieldData, paths::non_wasm::commitment_path, + commitment::Commitment, field_data::non_wasm::load, paths::non_wasm::commitment_path, }; - let commitment: Commitment = FieldData::load(commitment_path(hash))?; + let commitment: Commitment = load(commitment_path(hash))?; if commitment.hidden.value().hex_digits() != hash { bail!("Hash mismatch. Corrupted commitment file.") } else { From 1d9deaea4b38b90e39bcd947b87d86e4bc5395dd Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Thu, 20 Jul 2023 07:44:55 -0300 Subject: [PATCH 18/20] add tests for field_data --- src/cli/field_data.rs | 119 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 16 deletions(-) diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index 3ecd3b9ee7..381a7075e6 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -6,28 +6,44 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; // on an arithmetic field in order to be properly deserialized. // // First, we serialize it to a vector of bytes. Then, we wrap the vector with a -// struct that contains the field modulus, which, in turn, is serialized to a -// vector of bytes. +// struct that contains the field modulus, which, in turn, is serialized to the +// final vector of bytes. // -// When deserializing, we unwrap the first layer and double check the field +// When deserializing, we unwrap the vector of bytes and double check the field // modulus for consistency. If everything goes well, we further unwrap the second -// layer of bytes. +// vector of bytes. pub(crate) trait HasFieldModulus { fn field_modulus() -> String; } #[allow(dead_code)] -pub(crate) fn to_bytes(t: T) -> Result> { +pub(crate) fn ser(t: T) -> Result> { Ok(bincode::serialize(&FieldData(t))?) } #[allow(dead_code)] -pub(crate) fn from_bytes(bytes: &[u8]) -> Result { +pub(crate) fn de(bytes: &[u8]) -> Result { let FieldData(data) = bincode::deserialize(bytes)?; Ok(data) } +#[cfg(not(target_arch = "wasm32"))] +pub mod non_wasm { + use super::{de, ser, HasFieldModulus}; + use anyhow::Result; + use serde::{de::DeserializeOwned, Serialize}; + use std::path::PathBuf; + + pub(crate) fn dump(t: T, path: PathBuf) -> Result<()> { + Ok(std::fs::write(path, ser(t)?)?) + } + + pub(crate) fn load(path: PathBuf) -> Result { + de(&std::fs::read(path)?) + } +} + #[derive(Debug, PartialEq, Eq)] struct FieldData(T); @@ -65,18 +81,89 @@ impl Serialize for FieldData { } } -#[cfg(not(target_arch = "wasm32"))] -pub mod non_wasm { - use super::{from_bytes, to_bytes, HasFieldModulus}; - use anyhow::Result; - use serde::{de::DeserializeOwned, Serialize}; - use std::path::PathBuf; +#[cfg(test)] +mod tests { + use ff::Field; + use lurk::field::LurkField; + use pasta_curves::Fq; + use serde::{Deserialize, Serialize}; - pub(crate) fn dump(t: T, path: PathBuf) -> Result<()> { - Ok(std::fs::write(path, to_bytes(t)?)?) + use super::{de, ser, HasFieldModulus}; + + #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] + struct Struct { + str: String, + int: i32, + ff: F, } - pub(crate) fn load(path: PathBuf) -> Result { - from_bytes(&std::fs::read(path)?) + #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] + enum Enum1 { + CaseStr(String), + CaseInt(i32), + CaseFF(F), + } + + impl HasFieldModulus for Struct { + fn field_modulus() -> String { + F::MODULUS.to_string() + } + } + + impl HasFieldModulus for Enum1 { + fn field_modulus() -> String { + F::MODULUS.to_string() + } + } + + #[test] + fn struct_roundtrips() { + let s = Struct { + str: "hi".into(), + int: 42, + ff: Fq::double(&Fq::ONE), + }; + assert_eq!(s, de(&ser(s.clone()).unwrap()).unwrap()) + } + + #[test] + fn enum1_roundtrips() { + let e11 = Enum1::CaseStr("bye".into()); + let e12 = Enum1::CaseInt(11); + let e13 = Enum1::CaseFF(Fq::double(&Fq::double(&Fq::ONE))); + for e in [e11, e12, e13] { + assert_eq!(e, de(&ser(e.clone()).unwrap()).unwrap()); + } + } + + /// An enum can be deserialized to another, if the enum constructor has the + /// same index and uses the same inner data + #[test] + fn stable_enum() { + #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] + enum Enum2 { + CaseStr(String), + CaseInt(i32), + CaseFF(F), + Foo, + } + + impl HasFieldModulus for Enum2 { + fn field_modulus() -> String { + F::MODULUS.to_string() + } + } + let e11 = Enum1::CaseStr("bye".into()); + let e12 = Enum1::CaseInt(11); + let e13 = Enum1::CaseFF(Fq::double(&Fq::double(&Fq::ONE))); + + let e21 = Enum2::CaseStr("bye".into()); + let e22 = Enum2::CaseInt(11); + let e23 = Enum2::CaseFF(Fq::double(&Fq::double(&Fq::ONE))); + + for (e1, e2) in [(e11, e21), (e12, e22), (e13, e23)] { + assert_eq!(e2.clone(), de(&ser(e1.clone()).unwrap()).unwrap()); + assert_eq!(e1, de(&ser(e2).unwrap()).unwrap()); + } } } From e3c7ca9c482d6e836e94be8cb588d74975f719bb Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Thu, 20 Jul 2023 07:49:52 -0300 Subject: [PATCH 19/20] change constructor names on Enum2 for a better POC test --- src/cli/field_data.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cli/field_data.rs b/src/cli/field_data.rs index 381a7075e6..e068bea7c8 100644 --- a/src/cli/field_data.rs +++ b/src/cli/field_data.rs @@ -142,9 +142,9 @@ mod tests { fn stable_enum() { #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] enum Enum2 { - CaseStr(String), - CaseInt(i32), - CaseFF(F), + CaseStr2(String), + CaseInt2(i32), + CaseFF2(F), Foo, } @@ -157,9 +157,9 @@ mod tests { let e12 = Enum1::CaseInt(11); let e13 = Enum1::CaseFF(Fq::double(&Fq::double(&Fq::ONE))); - let e21 = Enum2::CaseStr("bye".into()); - let e22 = Enum2::CaseInt(11); - let e23 = Enum2::CaseFF(Fq::double(&Fq::double(&Fq::ONE))); + let e21 = Enum2::CaseStr2("bye".into()); + let e22 = Enum2::CaseInt2(11); + let e23 = Enum2::CaseFF2(Fq::double(&Fq::double(&Fq::ONE))); for (e1, e2) in [(e11, e21), (e12, e22), (e13, e23)] { assert_eq!(e2.clone(), de(&ser(e1.clone()).unwrap()).unwrap()); From 46bf08024ed0321547746fa71c1dbb296cc3fbd1 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Thu, 20 Jul 2023 11:22:26 -0300 Subject: [PATCH 20/20] fix docstring for LurkProofMeta --- src/cli/lurk_proof.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cli/lurk_proof.rs b/src/cli/lurk_proof.rs index d7dff0f951..2225fbc962 100644 --- a/src/cli/lurk_proof.rs +++ b/src/cli/lurk_proof.rs @@ -14,8 +14,9 @@ use super::field_data::HasFieldModulus; /// Carries extra information to help with visualization, experiments etc. /// /// Note: the `ZStore` in this struct only has enough data to recover the meaning -/// of the claim being proven: `expression`, when evaluated in the context of -/// `environment`, is reduced to `result`. It doesn't contain private data. +/// of the claim being proven: `expr`, when evaluated in the context of `env` and +/// continuation `cont`, is reduced to `expr_out`, resulting on environment +/// `env_out` and continuation `cont_out`. It doesn't contain private data. #[derive(Serialize, Deserialize)] pub struct LurkProofMeta { pub(crate) iterations: usize,