Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement persistent commitments #543

Merged
merged 20 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion fcomm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ impl<F: LurkField + Serialize + DeserializeOwned> LurkPtr<F> {
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")
}
}
Expand Down
50 changes: 50 additions & 0 deletions src/cli/commitment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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
/// hide the original payload.
#[derive(Serialize, Deserialize)]
pub struct Commitment<F: LurkField> {
pub(crate) hidden: ZExprPtr<F>,
pub(crate) zstore: ZStore<F>,
}

impl<F: LurkField> HasFieldModulus for Commitment<F> {
fn field_modulus() -> String {
F::MODULUS.to_owned()
}
}

#[cfg(not(target_arch = "wasm32"))]
mod non_wasm {
use anyhow::Result;
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 super::Commitment;

impl<F: LurkField> Commitment<F> {
pub fn new(secret: F, payload: Ptr<F>, store: &mut Store<F>) -> Result<Self> {
let hidden = store.hide(secret, payload);
let mut zstore = Some(ZStore::<F>::default());
let hidden = store.get_z_expr(&hidden, &mut zstore)?.0;
Ok(Self {
hidden,
zstore: zstore.unwrap(),
})
}
}

impl<F: LurkField + Serialize> Commitment<F> {
#[inline]
pub fn persist(self, hash: &str) -> Result<()> {
FieldData::dump(self, commitment_path(hash))
}
}
}
74 changes: 74 additions & 0 deletions src/cli/field_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use anyhow::Result;

use serde::{de::DeserializeOwned, Deserialize, Serialize};

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct FieldData<T>(T);

pub(crate) trait HasFieldModulus {
fn field_modulus() -> String;
}

impl<'de, T: DeserializeOwned + HasFieldModulus> Deserialize<'de> for FieldData<T> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct Labeled {
label: String,
#[serde(with = "serde_bytes")]
bytes: Vec<u8>,
}
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))
}
}

impl<T: Serialize + HasFieldModulus> Serialize for FieldData<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
struct Labeled {
label: String,
#[serde(with = "serde_bytes")]
bytes: Vec<u8>,
}
arthurpaulino marked this conversation as resolved.
Show resolved Hide resolved
let bytes = bincode::serialize(&self.0).map_err(serde::ser::Error::custom)?;
let labeled = Labeled {
label: T::field_modulus(),
bytes,
};
labeled.serialize(serializer)
}
}

arthurpaulino marked this conversation as resolved.
Show resolved Hide resolved
#[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<T: Serialize + HasFieldModulus> FieldData<T> {
pub fn dump(t: T, path: PathBuf) -> Result<()> {
let bytes = bincode::serialize(&FieldData(t))?;
Ok(std::fs::write(path, bytes)?)
}
}

impl<T: DeserializeOwned + HasFieldModulus> FieldData<T> {
pub fn load(path: PathBuf) -> Result<T> {
let bytes = std::fs::read(path)?;
let fd: FieldData<T> = bincode::deserialize(&bytes)?;
Ok(fd.0)
}
}
}
162 changes: 89 additions & 73 deletions src/cli/lurk_proof.rs
Original file line number Diff line number Diff line change
@@ -1,61 +1,47 @@
use serde::{Deserialize, Serialize};

use anyhow::Result;

use lurk::{
eval::{
lang::{Coproc, Lang},
Status,
},
field::{LanguageField, LurkField},
coprocessor::Coprocessor,
eval::lang::{Coproc, Lang},
field::LurkField,
proof::nova,
public_parameters::public_params,
z_ptr::ZExprPtr,
z_ptr::{ZContPtr, ZExprPtr},
z_store::ZStore,
};

/// A wrapper for data whose deserialization depends on a certain LurkField
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.
#[derive(Serialize, Deserialize)]
pub struct FieldData {
field: LanguageField,
data: Vec<u8>,
pub struct LurkProofMeta<F: LurkField> {
pub(crate) iterations: usize,
pub(crate) expr: ZExprPtr<F>,
pub(crate) env: ZExprPtr<F>,
pub(crate) cont: ZContPtr<F>,
pub(crate) expr_out: ZExprPtr<F>,
pub(crate) env_out: ZExprPtr<F>,
pub(crate) cont_out: ZContPtr<F>,
pub(crate) zstore: ZStore<F>,
}

#[allow(dead_code)]
impl FieldData {
#[inline]
pub fn wrap<F: LurkField, T: Serialize>(t: &T) -> Result<Self> {
Ok(Self {
field: F::FIELD,
data: bincode::serialize(t)?,
})
}

#[inline]
pub fn open<'a, T: Deserialize<'a>>(&'a self) -> Result<T> {
Ok(bincode::deserialize(&self.data)?)
impl<F: LurkField> HasFieldModulus for LurkProofMeta<F> {
fn field_modulus() -> String {
F::MODULUS.to_owned()
}
}

/// Carries extra information to help with visualization, experiments etc
#[derive(Serialize, Deserialize)]
pub struct LurkProofMeta<F: LurkField> {
pub iterations: usize,
pub evaluation_cost: u128,
pub generation_cost: u128,
pub compression_cost: u128,
pub status: Status,
pub expression: ZExprPtr<F>,
pub environment: ZExprPtr<F>,
pub result: ZExprPtr<F>,
pub zstore: ZStore<F>,
}

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<F>: Coprocessor<Pallas>,
{
Nova {
proof: nova::Proof<'a, Coproc<F>>,
public_inputs: Vec<F>,
Expand All @@ -66,43 +52,73 @@ pub enum LurkProof<'a> {
},
}

impl<'a> LurkProof<'a> {
#[allow(dead_code)]
fn verify(self) -> Result<bool> {
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, F: LurkField> HasFieldModulus for LurkProof<'a, F>
where
Coproc<F>: Coprocessor<Pallas>,
{
fn field_modulus() -> String {
F::MODULUS.to_owned()
}
}

#[cfg(not(target_arch = "wasm32"))]
mod non_wasm {
use crate::cli::{
field_data::FieldData,
paths::non_wasm::{proof_meta_path, proof_path},
};
use anyhow::Result;
use lurk::{
coprocessor::Coprocessor, eval::lang::Coproc, field::LurkField,
public_parameters::public_params,
};
use serde::Serialize;

use super::{LurkProof, LurkProofMeta, Pallas};

impl<F: LurkField + Serialize> LurkProofMeta<F> {
#[inline]
pub fn persist(self, id: &str) -> Result<()> {
FieldData::dump(self, proof_meta_path(id))
}
}

#[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");
impl<'a, F: LurkField + Serialize> LurkProof<'a, F>
where
Coproc<F>: Coprocessor<Pallas>,
{
#[inline]
pub fn persist(self, id: &str) -> Result<()> {
FieldData::dump(self, proof_path(id))
}
}

#[cfg(not(target_arch = "wasm32"))]
pub fn verify_proof(proof_id: &str) -> Result<()> {
use super::paths::proof_path;
use std::{fs::File, io::BufReader};
impl<'a> LurkProof<'a, Pallas> {
fn verify(self) -> Result<bool> {
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)?)
}
}
}

let file = File::open(proof_path(proof_id))?;
let fd: FieldData = bincode::deserialize_from(BufReader::new(file))?;
let lurk_proof: LurkProof = fd.open()?;
Self::print_verification(proof_id, lurk_proof.verify()?);
Ok(())
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(())
}
}
}
4 changes: 3 additions & 1 deletion src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod commitment;
mod field_data;
mod lurk_proof;
mod paths;
mod repl;
Expand Down Expand Up @@ -361,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::non_wasm::create_lurk_dirs()?;

if let Ok(repl_cli) = ReplCli::try_parse() {
repl_cli.run()
Expand Down
Loading