Skip to content

Commit 8818a11

Browse files
authored
Merge pull request #2971 from o1-labs/martin/saffron-proof-of-storage-clean
Proof of Storage
2 parents 563901a + 97ee9f7 commit 8818a11

File tree

5 files changed

+176
-2
lines changed

5 files changed

+176
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

saffron/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mina-curves.workspace = true
2929
mina-poseidon.workspace = true
3030
o1-utils.workspace = true
3131
poly-commitment.workspace = true
32+
rand.workspace = true
3233
rayon.workspace = true
3334
rmp-serde.workspace = true
3435
serde.workspace = true

saffron/src/blob.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<G: CommitmentCurve> FieldBlob<G> {
9393
}
9494

9595
#[cfg(test)]
96-
mod blob_test_utils {
96+
pub mod test_utils {
9797
use proptest::prelude::*;
9898

9999
#[derive(Debug)]
@@ -171,10 +171,10 @@ mod tests {
171171

172172
use super::*;
173173
use ark_poly::Radix2EvaluationDomain;
174-
use blob_test_utils::*;
175174
use mina_curves::pasta::{Fp, Vesta};
176175
use once_cell::sync::Lazy;
177176
use proptest::prelude::*;
177+
use test_utils::*;
178178

179179
static SRS: Lazy<SRS<Vesta>> = Lazy::new(|| {
180180
if let Ok(srs) = std::env::var("SRS_FILEPATH") {

saffron/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ pub mod blob;
22
pub mod cli;
33
pub mod commitment;
44
pub mod env;
5+
pub mod proof;
56
pub mod utils;

saffron/src/proof.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
use crate::blob::FieldBlob;
2+
use ark_ec::AffineRepr;
3+
use ark_ff::{One, PrimeField, Zero};
4+
use ark_poly::{univariate::DensePolynomial, Polynomial, Radix2EvaluationDomain as D};
5+
use kimchi::curve::KimchiCurve;
6+
use mina_poseidon::FqSponge;
7+
use o1_utils::ExtendedDensePolynomial;
8+
use poly_commitment::{
9+
commitment::{absorb_commitment, BatchEvaluationProof, Evaluation},
10+
ipa::{OpeningProof, SRS},
11+
utils::DensePolynomialOrEvaluations,
12+
PolyComm,
13+
};
14+
use rand::rngs::OsRng;
15+
use tracing::instrument;
16+
17+
#[instrument(skip_all, level = "debug")]
18+
pub fn storage_proof<G: KimchiCurve, EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>>(
19+
srs: &SRS<G>,
20+
group_map: &G::Map,
21+
blob: FieldBlob<G>,
22+
evaluation_point: G::ScalarField,
23+
rng: &mut OsRng,
24+
) -> (G::ScalarField, OpeningProof<G>)
25+
where
26+
G::BaseField: PrimeField,
27+
{
28+
let alpha = {
29+
let mut sponge = EFqSponge::new(G::other_curve_sponge_params());
30+
for commitment in &blob.commitments {
31+
absorb_commitment(&mut sponge, commitment)
32+
}
33+
sponge.challenge()
34+
};
35+
let p = {
36+
let init = (DensePolynomial::zero(), G::ScalarField::one());
37+
blob.data
38+
.into_iter()
39+
.fold(init, |(acc_poly, curr_power), curr_poly| {
40+
(acc_poly + curr_poly.scale(curr_power), curr_power * alpha)
41+
})
42+
.0
43+
};
44+
let evaluation = p.evaluate(&evaluation_point);
45+
let opening_proof_sponge = {
46+
let mut sponge = EFqSponge::new(G::other_curve_sponge_params());
47+
// TODO: check and see if we need to also absorb the absorb the poly cm
48+
// see https://github.com/o1-labs/proof-systems/blob/feature/test-data-storage-commitments/data-storage/src/main.rs#L265-L269
49+
sponge.absorb_fr(&[evaluation]);
50+
sponge
51+
};
52+
let proof = srs.open(
53+
group_map,
54+
&[(
55+
DensePolynomialOrEvaluations::<<G as AffineRepr>::ScalarField, D<G::ScalarField>> ::DensePolynomial(
56+
&p,
57+
),
58+
PolyComm {
59+
chunks: vec![G::ScalarField::zero()],
60+
},
61+
)],
62+
&[evaluation_point],
63+
G::ScalarField::one(), // Single evaluation, so we don't care
64+
G::ScalarField::one(), // Single evaluation, so we don't care
65+
opening_proof_sponge,
66+
rng,
67+
);
68+
(evaluation, proof)
69+
}
70+
71+
#[instrument(skip_all, level = "debug")]
72+
pub fn verify_storage_proof<
73+
G: KimchiCurve,
74+
EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
75+
>(
76+
srs: &SRS<G>,
77+
group_map: &G::Map,
78+
commitment: PolyComm<G>,
79+
evaluation_point: G::ScalarField,
80+
evaluation: G::ScalarField,
81+
opening_proof: &OpeningProof<G>,
82+
rng: &mut OsRng,
83+
) -> bool
84+
where
85+
G::BaseField: PrimeField,
86+
{
87+
let mut opening_proof_sponge = EFqSponge::new(G::other_curve_sponge_params());
88+
opening_proof_sponge.absorb_fr(&[evaluation]);
89+
90+
srs.verify(
91+
group_map,
92+
&mut [BatchEvaluationProof {
93+
sponge: opening_proof_sponge.clone(),
94+
evaluation_points: vec![evaluation_point],
95+
polyscale: G::ScalarField::one(),
96+
evalscale: G::ScalarField::one(),
97+
evaluations: vec![Evaluation {
98+
commitment,
99+
evaluations: vec![vec![evaluation]],
100+
}],
101+
opening: opening_proof,
102+
combined_inner_product: evaluation,
103+
}],
104+
rng,
105+
)
106+
}
107+
108+
#[cfg(test)]
109+
mod tests {
110+
use super::*;
111+
use crate::{
112+
blob::test_utils::*,
113+
commitment::{commit_to_field_elems, fold_commitments},
114+
env,
115+
utils::encode_for_domain,
116+
};
117+
use ark_poly::{EvaluationDomain, Radix2EvaluationDomain};
118+
use ark_std::UniformRand;
119+
use kimchi::groupmap::GroupMap;
120+
use mina_curves::pasta::{Fp, Vesta, VestaParameters};
121+
use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge};
122+
use once_cell::sync::Lazy;
123+
use poly_commitment::{commitment::CommitmentCurve, ipa::SRS, SRS as _};
124+
use proptest::prelude::*;
125+
126+
static SRS: Lazy<SRS<Vesta>> = Lazy::new(|| {
127+
if let Ok(srs) = std::env::var("SRS_FILEPATH") {
128+
env::get_srs_from_cache(srs)
129+
} else {
130+
SRS::create(1 << 16)
131+
}
132+
});
133+
134+
static DOMAIN: Lazy<Radix2EvaluationDomain<Fp>> =
135+
Lazy::new(|| Radix2EvaluationDomain::new(SRS.size()).unwrap());
136+
137+
static GROUP_MAP: Lazy<<Vesta as CommitmentCurve>::Map> =
138+
Lazy::new(<Vesta as CommitmentCurve>::Map::setup);
139+
140+
proptest! {
141+
#![proptest_config(ProptestConfig::with_cases(5))]
142+
#[test]
143+
fn test_storage_prove_verify(BlobData(data) in BlobData::arbitrary()) {
144+
let mut rng = OsRng;
145+
let commitment = {
146+
let field_elems = encode_for_domain(&*DOMAIN, &data);
147+
let user_commitments = commit_to_field_elems(&*SRS, *DOMAIN, field_elems);
148+
let mut fq_sponge = DefaultFqSponge::<VestaParameters, PlonkSpongeConstantsKimchi>::new(
149+
mina_poseidon::pasta::fq_kimchi::static_params(),
150+
);
151+
fold_commitments(&mut fq_sponge, &user_commitments)
152+
};
153+
let blob = FieldBlob::<Vesta>::encode(&*SRS, *DOMAIN, &data);
154+
let evaluation_point = Fp::rand(&mut rng);
155+
let (evaluation, proof) = storage_proof::<
156+
Vesta,
157+
DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>,
158+
>(&*SRS, &*GROUP_MAP, blob, evaluation_point, &mut rng);
159+
let res = verify_storage_proof::<Vesta, DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>>(
160+
&*SRS,
161+
&*GROUP_MAP,
162+
commitment,
163+
evaluation_point,
164+
evaluation,
165+
&proof,
166+
&mut rng,
167+
);
168+
prop_assert!(res);
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)