Skip to content

Commit

Permalink
Merge pull request #3005 from o1-labs/martin/saffron-commitment-type
Browse files Browse the repository at this point in the history
[saffron] add commitment type shared between user and provider
  • Loading branch information
dannywillems authored Feb 9, 2025
2 parents f15858d + 505b3b7 commit 3ae4516
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 82 deletions.
7 changes: 3 additions & 4 deletions saffron/e2e-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SRS_ARG=""
if [ $# -eq 2 ]; then
SRS_ARG="--srs-filepath $2"
fi
COMMITMENT_FILE="${INPUT_FILE%.*}_commitment.bin"
ENCODED_FILE="${INPUT_FILE%.*}.bin"
DECODED_FILE="${INPUT_FILE%.*}-decoded${INPUT_FILE##*.}"

Expand All @@ -21,9 +22,7 @@ if [ ! -f "$INPUT_FILE" ]; then
fi

# Compute commitment and capture last line
COMMITMENT=$(cargo run --release --bin saffron compute-commitment -i "$INPUT_FILE" $SRS_ARG | tee /dev/stderr | tail -n 1)


COMMITMENT=$(cargo run --release --bin saffron compute-commitment -i "$INPUT_FILE" -o "$COMMITMENT_FILE" $SRS_ARG | tee /dev/stderr | tail -n 1)

# Run encode with captured commitment
echo "Encoding $INPUT_FILE to $ENCODED_FILE"
Expand Down Expand Up @@ -66,7 +65,7 @@ echo "Comparing original and decoded files..."
if cmp -s "$INPUT_FILE" "$DECODED_FILE"; then
echo "✓ Success: Files are identical"
echo "Cleaning up temporary files..."
rm -f "$ENCODED_FILE" "$DECODED_FILE"
rm -f "$ENCODED_FILE" "$DECODED_FILE" "$COMMITMENT_FILE"
exit 0
else
echo "✗ Error: Files differ"
Expand Down
31 changes: 13 additions & 18 deletions saffron/src/blob.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
commitment::fold_commitments,
commitment::Commitment,
utils::{decode_into, encode_for_domain},
};
use ark_ff::PrimeField;
Expand All @@ -22,10 +22,7 @@ use tracing::{debug, debug_span, instrument};
pub struct FieldBlob<G: CommitmentCurve> {
pub n_bytes: usize,
pub domain_size: usize,
pub commitments: Vec<PolyComm<G>>,
pub folded_commitment: PolyComm<G>,
#[serde_as(as = "o1_utils::serialization::SerdeAs")]
pub alpha: G::ScalarField,
pub commitment: Commitment<G>,
#[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
pub data: Vec<DensePolynomial<G::ScalarField>>,
}
Expand All @@ -45,7 +42,7 @@ impl<G: KimchiCurve> FieldBlob<G> {
#[instrument(skip_all, level = "debug")]
pub fn encode<
D: EvaluationDomain<G::ScalarField>,
EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
EFqSponge: FqSponge<G::BaseField, G, G::ScalarField>,
>(
srs: &SRS<G>,
domain: D,
Expand All @@ -60,12 +57,10 @@ impl<G: KimchiCurve> FieldBlob<G> {
.map(|chunk| Evaluations::from_vec_and_domain(chunk.to_vec(), domain).interpolate())
.collect()
});

let commitments = commit_to_blob_data(srs, &data);

let (folded_commitment, alpha) = {
let commitment = {
let chunks = commit_to_blob_data(srs, &data);
let mut sponge = EFqSponge::new(G::other_curve_sponge_params());
fold_commitments(&mut sponge, &commitments)
Commitment::from_chunks(chunks, &mut sponge)
};

debug!(
Expand All @@ -77,9 +72,7 @@ impl<G: KimchiCurve> FieldBlob<G> {
FieldBlob {
n_bytes: bytes.len(),
domain_size,
commitments,
folded_commitment,
alpha,
commitment,
data,
}
}
Expand Down Expand Up @@ -124,6 +117,8 @@ mod tests {
use once_cell::sync::Lazy;
use proptest::prelude::*;

type VestaFqSponge = DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>;

static SRS: Lazy<SRS<Vesta>> = Lazy::new(|| {
if let Ok(srs) = std::env::var("SRS_FILEPATH") {
env::get_srs_from_cache(srs)
Expand All @@ -140,7 +135,7 @@ mod tests {
#![proptest_config(ProptestConfig::with_cases(20))]
#[test]
fn test_round_trip_blob_encoding(UserData(xs) in UserData::arbitrary())
{ let blob = FieldBlob::<Vesta>::encode::<_, DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>>(&*SRS, *DOMAIN, &xs);
{ let blob = FieldBlob::<Vesta>::encode::<_, VestaFqSponge>(&*SRS, *DOMAIN, &xs);
let bytes = rmp_serde::to_vec(&blob).unwrap();
let a = rmp_serde::from_slice(&bytes).unwrap();
// check that ark-serialize is behaving as expected
Expand All @@ -156,9 +151,9 @@ mod tests {
#[test]
fn test_user_and_storage_provider_commitments_equal(UserData(xs) in UserData::arbitrary())
{ let elems = encode_for_domain(&*DOMAIN, &xs);
let user_commitments = commit_to_field_elems(&*SRS, *DOMAIN, elems);
let blob = FieldBlob::<Vesta>::encode::<_, DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>>(&*SRS, *DOMAIN, &xs);
prop_assert_eq!(user_commitments, blob.commitments);
let user_commitments = commit_to_field_elems::<_, VestaFqSponge>(&*SRS, *DOMAIN, elems);
let blob = FieldBlob::<Vesta>::encode::<_, VestaFqSponge>(&*SRS, *DOMAIN, &xs);
prop_assert_eq!(user_commitments, blob.commitment);
}
}
}
3 changes: 3 additions & 0 deletions saffron/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub struct ComputeCommitmentArgs {
#[arg(long, short = 'i', value_name = "FILE", help = "input file")]
pub input: String,

#[arg(long, short = 'o', value_name = "FILE", help = "output file")]
pub output: String,

#[arg(long = "srs-filepath", value_name = "SRS_FILEPATH")]
pub srs_cache: Option<String>,
}
Expand Down
61 changes: 37 additions & 24 deletions saffron/src/commitment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ark_ec::AffineRepr;
use ark_ff::One;
use ark_poly::{Evaluations, Radix2EvaluationDomain as D};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use kimchi::curve::KimchiCurve;
use mina_poseidon::FqSponge;
use poly_commitment::{
Expand All @@ -9,28 +10,56 @@ use poly_commitment::{
PolyComm, SRS as _,
};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use tracing::instrument;

#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(bound = "G::ScalarField: CanonicalDeserialize + CanonicalSerialize")]
pub struct Commitment<G: CommitmentCurve> {
pub chunks: Vec<PolyComm<G>>,
#[serde_as(as = "o1_utils::serialization::SerdeAs")]
pub alpha: G::ScalarField,
pub folded: PolyComm<G>,
}

impl<G: KimchiCurve> Commitment<G> {
pub fn from_chunks<EFqSponge>(chunks: Vec<PolyComm<G>>, sponge: &mut EFqSponge) -> Self
where
EFqSponge: FqSponge<G::BaseField, G, G::ScalarField>,
{
let (folded, alpha) = fold_commitments(sponge, &chunks);
Self {
chunks,
alpha,
folded,
}
}
}

#[instrument(skip_all, level = "debug")]
pub fn commit_to_field_elems<G: CommitmentCurve>(
pub fn commit_to_field_elems<G: KimchiCurve, EFqSponge>(
srs: &SRS<G>,
domain: D<G::ScalarField>,
field_elems: Vec<Vec<G::ScalarField>>,
) -> Vec<PolyComm<G>> {
field_elems
) -> Commitment<G>
where
EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
{
let commitments = field_elems
.par_iter()
.map(|chunk| {
let evals = Evaluations::from_vec_and_domain(chunk.to_vec(), domain);
srs.commit_evaluations_non_hiding(domain, &evals)
})
.collect()
.collect();
let mut sponge = EFqSponge::new(G::other_curve_sponge_params());
Commitment::from_chunks(commitments, &mut sponge)
}

#[instrument(skip_all, level = "debug")]
pub fn fold_commitments<
G: AffineRepr,
EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
>(
fn fold_commitments<G: AffineRepr, EFqSponge: FqSponge<G::BaseField, G, G::ScalarField>>(
sponge: &mut EFqSponge,
commitments: &[PolyComm<G>],
) -> (PolyComm<G>, G::ScalarField) {
Expand All @@ -51,19 +80,3 @@ pub fn fold_commitments<
alpha,
)
}

pub fn user_commitment<
G: KimchiCurve,
EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
>(
srs: &SRS<G>,
domain: D<G::ScalarField>,
field_elems: Vec<Vec<G::ScalarField>>,
) -> PolyComm<G> {
let commitments = commit_to_field_elems(srs, domain, field_elems);
let (commitment, _) = {
let mut sponge = EFqSponge::new(G::other_curve_sponge_params());
fold_commitments(&mut sponge, &commitments)
};
commitment
}
66 changes: 42 additions & 24 deletions saffron/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rand::rngs::OsRng;
use saffron::{
blob::FieldBlob,
cli::{self, HexString},
commitment::user_commitment,
commitment::commit_to_field_elems,
env,
proof::{self, StorageProof},
utils,
Expand All @@ -22,7 +22,7 @@ use tracing::{debug, debug_span};

pub const DEFAULT_SRS_SIZE: usize = 1 << 16;

type FqSponge = DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>;
type VestaFqSponge = DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>;

fn get_srs(cache: Option<String>) -> (SRS<Vesta>, Radix2EvaluationDomain<Fp>) {
let res = match cache {
Expand Down Expand Up @@ -77,19 +77,22 @@ fn encode_file(args: cli::EncodeFileArgs) -> Result<()> {
let mut file = File::open(args.input)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
let blob = FieldBlob::<Vesta>::encode::<_, FqSponge>(&srs, domain, &buf);
args.assert_commitment
.into_iter()
.for_each(|asserted_commitment| {
let c = rmp_serde::from_slice(&asserted_commitment.0).unwrap();
if blob.folded_commitment != c {
panic!(
"commitment hash mismatch: asserted {}, computed {}",
asserted_commitment,
HexString(rmp_serde::encode::to_vec(&c).unwrap())
);
}
});
let blob = FieldBlob::<Vesta>::encode::<_, VestaFqSponge>(&srs, domain, &buf);
if let Some(asserted) = args.assert_commitment {
let asserted_commitment =
rmp_serde::from_slice(&asserted.0).expect("failed to decode asserted commitment");

assert_eq!(
blob.commitment.folded,
asserted_commitment,
"commitment mismatch: asserted {}, computed {}",
asserted,
HexString(
rmp_serde::encode::to_vec(&blob.commitment.folded)
.expect("failed to encode commitment")
)
);
};
debug!(output_file = args.output, "Writing encoded blob to file",);
let mut writer = File::create(args.output)?;
rmp_serde::encode::write(&mut writer, &blob)?;
Expand All @@ -98,13 +101,22 @@ fn encode_file(args: cli::EncodeFileArgs) -> Result<()> {

pub fn compute_commitment(args: cli::ComputeCommitmentArgs) -> Result<HexString> {
let (srs, domain_fp) = get_srs(args.srs_cache);
let mut file = File::open(args.input)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
let field_elems = utils::encode_for_domain(&domain_fp, &buf);
let commitment = user_commitment::<_, FqSponge>(&srs, domain_fp, field_elems);
let res = rmp_serde::to_vec(&commitment)?;
Ok(HexString(res))
let buf = {
let mut file = File::open(args.input)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
buf
};
let commitment = {
let field_elems = utils::encode_for_domain(&domain_fp, &buf);
commit_to_field_elems::<_, VestaFqSponge>(&srs, domain_fp, field_elems)
};
{
let mut writer = File::create(args.output)?;
rmp_serde::encode::write(&mut writer, &commitment)?;
}
let c = rmp_serde::encode::to_vec(&commitment.folded)?;
Ok(HexString(c))
}

pub fn storage_proof(args: cli::StorageProofArgs) -> Result<HexString> {
Expand All @@ -115,7 +127,13 @@ pub fn storage_proof(args: cli::StorageProofArgs) -> Result<HexString> {
let group_map = <Vesta as CommitmentCurve>::Map::setup();
let mut rng = OsRng;
let evaluation_point = utils::encode(&args.challenge.0);
proof::storage_proof::<Vesta, FqSponge>(&srs, &group_map, blob, evaluation_point, &mut rng)
proof::storage_proof::<Vesta, VestaFqSponge>(
&srs,
&group_map,
blob,
evaluation_point,
&mut rng,
)
};
let res = rmp_serde::to_vec(&proof)?;
Ok(HexString(res))
Expand All @@ -128,7 +146,7 @@ pub fn verify_storage_proof(args: cli::VerifyStorageProofArgs) -> Result<()> {
let evaluation_point = utils::encode(&args.challenge.0);
let proof: StorageProof<Vesta> = rmp_serde::from_slice(&args.proof.0)?;
let mut rng = OsRng;
let res = proof::verify_storage_proof::<Vesta, FqSponge>(
let res = proof::verify_storage_proof::<Vesta, VestaFqSponge>(
&srs,
&group_map,
commitment,
Expand Down
22 changes: 10 additions & 12 deletions saffron/src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ where
.fold(init, |(acc_poly, curr_power), curr_poly| {
(
acc_poly + curr_poly.scale(curr_power),
curr_power * blob.alpha,
curr_power * blob.commitment.alpha,
)
})
.0
Expand Down Expand Up @@ -119,7 +119,7 @@ where
mod tests {
use super::*;
use crate::{
commitment::{commit_to_field_elems, fold_commitments},
commitment::commit_to_field_elems,
env,
utils::{encode_for_domain, test_utils::UserData},
};
Expand All @@ -132,6 +132,8 @@ mod tests {
use poly_commitment::{commitment::CommitmentCurve, ipa::SRS, SRS as _};
use proptest::prelude::*;

type VestaFqSponge = DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>;

static SRS: Lazy<SRS<Vesta>> = Lazy::new(|| {
if let Ok(srs) = std::env::var("SRS_FILEPATH") {
env::get_srs_from_cache(srs)
Expand All @@ -151,24 +153,20 @@ mod tests {
#[test]
fn test_storage_prove_verify(UserData(data) in UserData::arbitrary()) {
let mut rng = OsRng;
let (commitment,_) = {
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::<VestaParameters, PlonkSpongeConstantsKimchi>::new(
mina_poseidon::pasta::fq_kimchi::static_params(),
);
fold_commitments(&mut fq_sponge, &user_commitments)
commit_to_field_elems::<_, VestaFqSponge>(&*SRS, *DOMAIN, field_elems)
};
let blob = FieldBlob::<Vesta>::encode::<_, DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>>(&*SRS, *DOMAIN, &data);
let blob = FieldBlob::<Vesta>::encode::<_, VestaFqSponge>(&*SRS, *DOMAIN, &data);
let evaluation_point = Fp::rand(&mut rng);
let proof = storage_proof::<
Vesta, DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>
Vesta, VestaFqSponge

>(&*SRS, &*GROUP_MAP, blob, evaluation_point, &mut rng);
let res = verify_storage_proof::<Vesta, DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>>(
let res = verify_storage_proof::<Vesta, VestaFqSponge>(
&*SRS,
&*GROUP_MAP,
commitment,
commitment.folded,
evaluation_point,
&proof,
&mut rng,
Expand Down

0 comments on commit 3ae4516

Please sign in to comment.