From 35a94708646a6c87b89adf0f56a7e7af520f7547 Mon Sep 17 00:00:00 2001 From: martyall Date: Tue, 28 Jan 2025 00:31:37 -0800 Subject: [PATCH 1/2] add prove/verify storage proof --- Cargo.lock | 1 + saffron/Cargo.toml | 1 + saffron/src/blob.rs | 4 +- saffron/src/lib.rs | 1 + saffron/src/proof.rs | 169 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 saffron/src/proof.rs diff --git a/Cargo.lock b/Cargo.lock index 177f323176..f99bb2c77d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2670,6 +2670,7 @@ dependencies = [ "once_cell", "poly-commitment", "proptest", + "rand", "rayon", "rmp-serde", "serde", diff --git a/saffron/Cargo.toml b/saffron/Cargo.toml index 8b2ee4deba..19b8bde121 100644 --- a/saffron/Cargo.toml +++ b/saffron/Cargo.toml @@ -29,6 +29,7 @@ mina-curves.workspace = true mina-poseidon.workspace = true o1-utils.workspace = true poly-commitment.workspace = true +rand.workspace = true rayon.workspace = true rmp-serde.workspace = true serde.workspace = true diff --git a/saffron/src/blob.rs b/saffron/src/blob.rs index ea982d2fad..c5b8d01fb8 100644 --- a/saffron/src/blob.rs +++ b/saffron/src/blob.rs @@ -93,7 +93,7 @@ impl FieldBlob { } #[cfg(test)] -mod blob_test_utils { +pub mod test_utils { use proptest::prelude::*; #[derive(Debug)] @@ -171,10 +171,10 @@ mod tests { use super::*; use ark_poly::Radix2EvaluationDomain; - use blob_test_utils::*; use mina_curves::pasta::{Fp, Vesta}; use once_cell::sync::Lazy; use proptest::prelude::*; + use test_utils::*; static SRS: Lazy> = Lazy::new(|| { if let Ok(srs) = std::env::var("SRS_FILEPATH") { diff --git a/saffron/src/lib.rs b/saffron/src/lib.rs index 3cc3d39057..dbfe03d318 100644 --- a/saffron/src/lib.rs +++ b/saffron/src/lib.rs @@ -2,4 +2,5 @@ pub mod blob; pub mod cli; pub mod commitment; pub mod env; +pub mod proof; pub mod utils; diff --git a/saffron/src/proof.rs b/saffron/src/proof.rs new file mode 100644 index 0000000000..016849515b --- /dev/null +++ b/saffron/src/proof.rs @@ -0,0 +1,169 @@ +use crate::blob::FieldBlob; +use ark_ec::AffineRepr; +use ark_ff::{One, PrimeField, Zero}; +use ark_poly::{univariate::DensePolynomial, Polynomial, Radix2EvaluationDomain as D}; +use kimchi::curve::KimchiCurve; +use mina_poseidon::FqSponge; +use o1_utils::ExtendedDensePolynomial; +use poly_commitment::{ + commitment::{absorb_commitment, BatchEvaluationProof, Evaluation}, + ipa::{OpeningProof, SRS}, + utils::DensePolynomialOrEvaluations, + PolyComm, +}; +use rand::rngs::OsRng; +use tracing::instrument; + +#[instrument(skip_all, level = "debug")] +pub fn storage_proof>( + srs: &SRS, + group_map: &G::Map, + blob: FieldBlob, + evaluation_point: G::ScalarField, + rng: &mut OsRng, +) -> (G::ScalarField, OpeningProof) +where + G::BaseField: PrimeField, +{ + let alpha = { + let mut sponge = EFqSponge::new(G::other_curve_sponge_params()); + for commitment in &blob.commitments { + absorb_commitment(&mut sponge, commitment) + } + sponge.challenge() + }; + let p = { + let init = (DensePolynomial::zero(), G::ScalarField::one()); + blob.data + .into_iter() + .fold(init, |(acc_poly, curr_power), curr_poly| { + (acc_poly + curr_poly.scale(curr_power), curr_power * alpha) + }) + .0 + }; + let evaluation = p.evaluate(&evaluation_point); + let opening_proof_sponge = { + let mut sponge = EFqSponge::new(G::other_curve_sponge_params()); + sponge.absorb_fr(&[evaluation]); + sponge + }; + let proof = srs.open( + group_map, + &[( + DensePolynomialOrEvaluations::<::ScalarField, D> ::DensePolynomial( + &p, + ), + PolyComm { + chunks: vec![G::ScalarField::zero()], + }, + )], + &[evaluation_point], + G::ScalarField::one(), // Single polynomial, so we don't care + G::ScalarField::one(), // Single polynomial, so we don't care + opening_proof_sponge, + rng, + ); + (evaluation, proof) +} + +#[instrument(skip_all, level = "debug")] +pub fn verify_storage_proof< + G: KimchiCurve, + EFqSponge: Clone + FqSponge, +>( + srs: &SRS, + group_map: &G::Map, + commitment: PolyComm, + evaluation_point: G::ScalarField, + evaluation: G::ScalarField, + opening_proof: &OpeningProof, + rng: &mut OsRng, +) -> bool +where + G::BaseField: PrimeField, +{ + let mut opening_proof_sponge = EFqSponge::new(G::other_curve_sponge_params()); + opening_proof_sponge.absorb_fr(&[evaluation]); + + srs.verify( + group_map, + &mut [BatchEvaluationProof { + sponge: opening_proof_sponge.clone(), + evaluation_points: vec![evaluation_point], + polyscale: G::ScalarField::one(), + evalscale: G::ScalarField::one(), + evaluations: vec![Evaluation { + commitment, + evaluations: vec![vec![evaluation]], + }], + opening: opening_proof, + combined_inner_product: evaluation, + }], + rng, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + blob::test_utils::*, + commitment::{commit_to_field_elems, fold_commitments}, + env, + utils::encode_for_domain, + }; + use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; + use ark_std::UniformRand; + use kimchi::groupmap::GroupMap; + use mina_curves::pasta::{Fp, Vesta, VestaParameters}; + use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge}; + use once_cell::sync::Lazy; + use poly_commitment::{commitment::CommitmentCurve, ipa::SRS, SRS as _}; + use proptest::prelude::*; + + static SRS: Lazy> = Lazy::new(|| { + if let Ok(srs) = std::env::var("SRS_FILEPATH") { + env::get_srs_from_cache(srs) + } else { + SRS::create(1 << 16) + } + }); + + static DOMAIN: Lazy> = + Lazy::new(|| Radix2EvaluationDomain::new(SRS.size()).unwrap()); + + static GROUP_MAP: Lazy<::Map> = + Lazy::new(::Map::setup); + + proptest! { + #![proptest_config(ProptestConfig::with_cases(5))] + #[test] + fn test_storage_prove_verify(BlobData(data) in BlobData::arbitrary()) { + let mut rng = OsRng; + let commitment = { + let field_elems = encode_for_domain(&*DOMAIN, &data); + let user_commitments = commit_to_field_elems(&*SRS, *DOMAIN, field_elems); + let mut fq_sponge = DefaultFqSponge::::new( + mina_poseidon::pasta::fq_kimchi::static_params(), + ); + fold_commitments(&mut fq_sponge, &user_commitments) + }; + let blob = FieldBlob::::encode(&*SRS, *DOMAIN, &data); + let evaluation_point = Fp::rand(&mut rng); + let (evaluation, proof) = storage_proof::< + Vesta, + DefaultFqSponge, + >(&*SRS, &*GROUP_MAP, blob, evaluation_point, &mut rng); + let res = verify_storage_proof::>( + &*SRS, + &*GROUP_MAP, + commitment, + evaluation_point, + evaluation, + &proof, + &mut rng, + ); + prop_assert!(res); + } + } +} From 97ee9f7d193e8850e64f3521537dc5a01535527e Mon Sep 17 00:00:00 2001 From: martyall Date: Thu, 30 Jan 2025 10:52:29 -0800 Subject: [PATCH 2/2] Marc's PR comments: adjust some comments --- saffron/src/proof.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/saffron/src/proof.rs b/saffron/src/proof.rs index 016849515b..8429c3fb88 100644 --- a/saffron/src/proof.rs +++ b/saffron/src/proof.rs @@ -44,6 +44,8 @@ where let evaluation = p.evaluate(&evaluation_point); let opening_proof_sponge = { let mut sponge = EFqSponge::new(G::other_curve_sponge_params()); + // TODO: check and see if we need to also absorb the absorb the poly cm + // see https://github.com/o1-labs/proof-systems/blob/feature/test-data-storage-commitments/data-storage/src/main.rs#L265-L269 sponge.absorb_fr(&[evaluation]); sponge }; @@ -58,8 +60,8 @@ where }, )], &[evaluation_point], - G::ScalarField::one(), // Single polynomial, so we don't care - G::ScalarField::one(), // Single polynomial, so we don't care + G::ScalarField::one(), // Single evaluation, so we don't care + G::ScalarField::one(), // Single evaluation, so we don't care opening_proof_sponge, rng, );