Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
20 changes: 6 additions & 14 deletions benches/multi_stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ fn build_witness(num_adds: usize, system: &System<U32CS>) -> SystemWitness {
}

/// Build claims for the first `num_adds` additions (same PRNG seed as `build_witness`).
fn build_claims(num_adds: usize) -> Vec<[Val; 4]> {
fn build_claims(num_adds: usize) -> Vec<Vec<Val>> {
let f = Val::from_u32;
let mut a: u32 = 0xdead_beef;
let mut b: u32 = 0xcafe_babe;
Expand All @@ -191,7 +191,7 @@ fn build_claims(num_adds: usize) -> Vec<[Val; 4]> {
let x = a;
let y = b;
let (z, _carry) = x.overflowing_add(y);
claims.push([f(1), f(x), f(y), f(z)]);
claims.push(vec![f(1), f(x), f(y), f(z)]);
}
claims
}
Expand Down Expand Up @@ -223,15 +223,12 @@ fn bench_prove(c: &mut Criterion) {
for log_height in [12, 13, 14] {
let num_adds = 1 << log_height;
let claims = build_claims(num_adds);
let claim_refs: Vec<&[Val]> = claims.iter().map(|c| c.as_slice()).collect();
group.bench_function(
BenchmarkId::new("u32_add", format!("2^{log_height}")),
|b| {
b.iter_batched(
|| build_witness(num_adds, &system),
|witness| {
system.prove_multiple_claims(fri_parameters, &key, &claim_refs, witness)
},
|| (build_witness(num_adds, &system), claims.clone()),
|(witness, claims)| system.prove(fri_parameters, &key, claims, witness),
criterion::BatchSize::LargeInput,
);
},
Expand Down Expand Up @@ -263,17 +260,12 @@ fn bench_verify(c: &mut Criterion) {
for log_height in [12, 13, 14] {
let num_adds = 1 << log_height;
let claims = build_claims(num_adds);
let claim_refs: Vec<&[Val]> = claims.iter().map(|c| c.as_slice()).collect();
let witness = build_witness(num_adds, &system);
let proof = system.prove_multiple_claims(fri_parameters, &key, &claim_refs, witness);
let proof = system.prove(fri_parameters, &key, claims, witness);
group.bench_function(
BenchmarkId::new("u32_add", format!("2^{log_height}")),
|b| {
b.iter(|| {
system
.verify_multiple_claims(fri_parameters, &claim_refs, &proof)
.unwrap()
});
b.iter(|| system.verify(fri_parameters, &proof).unwrap());
},
);
}
Expand Down
6 changes: 3 additions & 3 deletions examples/lookup_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ fn main() {
);

// Claim: [even_index=0, input=4, expected_output=1] — is_even(4) should be 1
let claim = &[f(0), f(4), f(1)];
let claim = vec![f(0), f(4), f(1)];
let fri_parameters = FriParameters {
log_final_poly_len: 0,
max_log_arity: 1,
Expand All @@ -150,8 +150,8 @@ fn main() {
query_proof_of_work_bits: 0,
};

let proof = system.prove(fri_parameters, &key, claim, witness);
system.verify(fri_parameters, claim, &proof).unwrap();
let proof = system.prove(fri_parameters, &key, vec![claim], witness);
system.verify(fri_parameters, &proof).unwrap();
println!("Lookup proof verified successfully!");

let bytes = proof.to_bytes().expect("serialization failed");
Expand Down
7 changes: 2 additions & 5 deletions examples/preprocessed_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,8 @@ fn main() {
query_proof_of_work_bits: 0,
};

let no_claims: &[&[Val]] = &[];
let proof = system.prove_multiple_claims(fri_parameters, &key, no_claims, witness);
system
.verify_multiple_claims(fri_parameters, no_claims, &proof)
.unwrap();
let proof = system.prove(fri_parameters, &key, vec![], witness);
system.verify(fri_parameters, &proof).unwrap();
println!("Preprocessed proof verified successfully!");

let bytes = proof.to_bytes().expect("serialization failed");
Expand Down
7 changes: 2 additions & 5 deletions examples/simple_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,10 @@ fn main() {
};

// Prove
let no_claims = &[];
let proof = system.prove_multiple_claims(fri_parameters, &key, no_claims, witness);
let proof = system.prove(fri_parameters, &key, vec![], witness);

// Verify
system
.verify_multiple_claims(fri_parameters, no_claims, &proof)
.unwrap();
system.verify(fri_parameters, &proof).unwrap();
println!("Proof verified successfully!");

// Show proof size
Expand Down
26 changes: 22 additions & 4 deletions src/lookup.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use p3_air::{Air, BaseAir, ExtensionBuilder, WindowAccess};
use p3_field::{PrimeCharacteristicRing, batch_multiplicative_inverse};
use p3_field::{Field, PrimeCharacteristicRing, batch_multiplicative_inverse};
use p3_matrix::{Matrix, dense::RowMajorMatrix};
use p3_maybe_rayon::prelude::*;

Expand Down Expand Up @@ -74,6 +74,24 @@ impl<A: BaseAir<Val>> LookupAir<A> {
}
}

/// Computes the accumulator contribution from a list of claims with multiplicities.
///
/// Both the prover and verifier use this to initialize the lookup accumulator from
/// external claims before running the circuit-level lookup argument.
pub(crate) fn claims_accumulator(
lookup_argument_challenge: ExtVal,
fingerprint_challenge: ExtVal,
claims: &[(Vec<Val>, u64)],
) -> ExtVal {
let mut acc = ExtVal::ZERO;
for (claim, multiplicity) in claims {
let message =
lookup_argument_challenge + fingerprint(&fingerprint_challenge, claim.iter().cloned());
acc += message.inverse() * ExtVal::from_u64(*multiplicity);
}
acc
}

/// Computes a fingerprint of the coefficients using Horner's method.
#[inline]
pub(crate) fn fingerprint<F, I, Iter>(r: &F, coeffs: Iter) -> F
Expand Down Expand Up @@ -414,15 +432,15 @@ mod tests {
],
&system,
);
let claim = &[f(0), f(4), f(1)];
let claims = vec![vec![f(0), f(4), f(1)]];
let fri_parameters = FriParameters {
log_final_poly_len: 0,
max_log_arity: 1,
num_queries: 64,
commit_proof_of_work_bits: 0,
query_proof_of_work_bits: 0,
};
let proof = system.prove(fri_parameters, &key, claim, witness);
system.verify(fri_parameters, claim, &proof).unwrap();
let proof = system.prove(fri_parameters, &key, claims, witness);
system.verify(fri_parameters, &proof).unwrap();
}
}
64 changes: 36 additions & 28 deletions src/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ use std::ops::Deref;

use crate::{
builder::folder::ProverConstraintFolder,
lookup::{Lookup, fingerprint},
lookup::{Lookup, claims_accumulator},
system::{ProverKey, System, SystemWitness},
types::{
Challenger, Commitment, Domain, EvaluationsOnDomain, ExtVal, FriParameters, PackedExtVal,
Expand All @@ -170,8 +170,7 @@ use p3_air::{Air, BaseAir, RowWindow};
use p3_challenger::{CanObserve, FieldChallenger};
use p3_commit::{LagrangeSelectors, OpenedValuesForRound, Pcs as PcsTrait, PolynomialSpace};
use p3_field::{
BasedVectorSpace, Field, PackedValue, PrimeCharacteristicRing,
extension::BinomialExtensionField,
BasedVectorSpace, PackedValue, PrimeCharacteristicRing, extension::BinomialExtensionField,
};
use p3_matrix::{Matrix, dense::RowMajorMatrix};
use p3_maybe_rayon::prelude::*;
Expand Down Expand Up @@ -203,6 +202,13 @@ pub struct Proof {
pub preprocessed_opened_values: Option<OpenedValuesForRound<ExtVal>>,
pub stage_1_opened_values: OpenedValuesForRound<ExtVal>,
pub stage_2_opened_values: OpenedValuesForRound<ExtVal>,
/// Claims with multiplicities bound to this proof. Observed into the Fiat-Shamir
/// transcript before lookup challenges are sampled. The verifier reads them directly.
pub claims: Vec<(Vec<Val>, u64)>,
/// Remaining claims with multiplicities that must become the `claims` of the next
/// proof shard. The final lookup accumulator equals the accumulator derived from these,
/// and the next shard's initial accumulator (from its own claims) must match.
pub remaining_claims: Vec<(Vec<Val>, u64)>,
}

impl Proof {
Expand All @@ -223,29 +229,30 @@ impl Proof {
}

impl<A: BaseAir<Val> + for<'a> Air<ProverConstraintFolder<'a>>> System<A> {
/// Generates a STARK proof for the system with a single claim.
///
/// This is a convenience wrapper around [`Self::prove_multiple_claims`].
/// Generates a full STARK proof binding the given claims (all with multiplicity 1).
pub fn prove(
&self,
fri_parameters: FriParameters,
key: &ProverKey,
claim: &[Val],
claims: Vec<Vec<Val>>,
witness: SystemWitness,
) -> Proof {
self.prove_multiple_claims(fri_parameters, key, &[claim], witness)
let claims = claims.into_iter().map(|c| (c, 1u64)).collect();
self.partially_prove(fri_parameters, key, claims, vec![], witness)
}

/// Generates a STARK proof for the system with multiple claims.
/// Generates a partial STARK proof (proof shard).
///
/// Each claim is a slice of field elements that is observed by the challenger
/// before lookup challenges are sampled, binding the proof to the claimed values.
#[tracing::instrument(level = "info", skip_all, name = "stark/prove")]
pub fn prove_multiple_claims(
/// `claims` with multiplicities are observed into the Fiat-Shamir transcript before
/// lookup challenges are sampled. `remaining_claims` encode the obligations passed to
/// the next shard: the next shard's `claims` must equal these (so their accumulators
/// match), and this shard's final accumulator equals the accumulator derived from them.
pub fn partially_prove(
&self,
fri_parameters: FriParameters,
key: &ProverKey,
claims: &[&[Val]],
claims: Vec<(Vec<Val>, u64)>,
remaining_claims: Vec<(Vec<Val>, u64)>,
witness: SystemWitness,
) -> Proof {
// initialize pcs and challenger
Expand Down Expand Up @@ -279,11 +286,15 @@ impl<A: BaseAir<Val> + for<'a> Air<ProverConstraintFolder<'a>>> System<A> {
challenger.observe(Val::from_usize(*log_degree));
}

// observe the claims
// this has to be done before generating the lookup argument challenge
// otherwise the lookup argument can be attacked
for claim in claims {
challenger.observe_slice(claim);
// observe claims and remaining claims before lookup challenges are sampled;
// this is required for soundness of the lookup argument
for (values, multiplicity) in &claims {
challenger.observe_slice(values);
challenger.observe(Val::from_u64(*multiplicity));
}
for (values, multiplicity) in &remaining_claims {
challenger.observe_slice(values);
challenger.observe(Val::from_u64(*multiplicity));
}

// generate lookup challenges
Expand All @@ -292,13 +303,7 @@ impl<A: BaseAir<Val> + for<'a> Air<ProverConstraintFolder<'a>>> System<A> {
let fingerprint_challenge: ExtVal = challenger.sample_algebra_element();
challenger.observe_algebra_element(fingerprint_challenge);

// construct the accumulator from the claims
let mut acc = ExtVal::ZERO;
for claim in claims {
let message = lookup_argument_challenge
+ fingerprint(&fingerprint_challenge, claim.iter().cloned());
acc += message.inverse();
}
let acc = claims_accumulator(lookup_argument_challenge, fingerprint_challenge, &claims);

// Cost: "Lookup trace construction" — fingerprint (Horner), batch
// inversion, and accumulator update. Total: Σ n_i·L_i extension field ops.
Expand Down Expand Up @@ -335,6 +340,7 @@ impl<A: BaseAir<Val> + for<'a> Air<ProverConstraintFolder<'a>>> System<A> {
debug_assert_eq!(intermediate_accumulators.len(), self.circuits.len());
debug_assert_eq!(log_degrees.len(), self.circuits.len());
let mut quotient_degrees = vec![];
let mut curr_acc = acc;
let quotient_evaluations = self
.circuits
.iter()
Expand Down Expand Up @@ -384,7 +390,7 @@ impl<A: BaseAir<Val> + for<'a> Air<ProverConstraintFolder<'a>>> System<A> {
let stage_2_public_values = [
lookup_argument_challenge,
fingerprint_challenge,
acc,
curr_acc,
*next_acc,
];
let quotient_values = quotient_values(
Expand All @@ -408,7 +414,7 @@ impl<A: BaseAir<Val> + for<'a> Air<ProverConstraintFolder<'a>>> System<A> {
let quotient_sub_domains = quotient_domain.split_domains(quotient_degree);
// need to save the quotient degree for later
quotient_degrees.push(quotient_degree);
acc = *next_acc;
curr_acc = *next_acc;
quotient_sub_domains
.into_iter()
.zip(quotient_sub_evaluations)
Expand Down Expand Up @@ -477,6 +483,8 @@ impl<A: BaseAir<Val> + for<'a> Air<ProverConstraintFolder<'a>>> System<A> {
preprocessed_opened_values,
stage_1_opened_values,
stage_2_opened_values,
claims,
remaining_claims,
}
}
}
Expand Down
17 changes: 5 additions & 12 deletions src/test_circuits/blake3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2321,9 +2321,6 @@ mod tests {

let (_traces, witness) = claims.witness(&system);

let claims_slice: Vec<&[Val]> = claims.claims.iter().map(|v| v.as_slice()).collect();
let claims_slice: &[&[Val]] = &claims_slice;

let fri_parameters = FriParameters {
log_final_poly_len: 0,
max_log_arity: 1,
Expand All @@ -2332,10 +2329,9 @@ mod tests {
query_proof_of_work_bits: 0,
};

let proof =
system.prove_multiple_claims(fri_parameters, &prover_key, claims_slice, witness);
let proof = system.prove(fri_parameters, &prover_key, claims.claims, witness);
system
.verify_multiple_claims(fri_parameters, claims_slice, &proof)
.verify(fri_parameters, &proof)
.expect("verification issue");
}

Expand Down Expand Up @@ -2495,9 +2491,7 @@ mod tests {
);

let (_traces, witness) = claims.witness(&system);

let claims_slice: Vec<&[Val]> = claims.claims.iter().map(|v| v.as_slice()).collect();
let claims_slice: &[&[Val]] = &claims_slice;
let claim_vecs = claims.claims.clone();

let fri_parameters = FriParameters {
log_final_poly_len: 0,
Expand All @@ -2507,10 +2501,9 @@ mod tests {
query_proof_of_work_bits: 0,
};

let proof =
system.prove_multiple_claims(fri_parameters, &prover_key, claims_slice, witness);
let proof = system.prove(fri_parameters, &prover_key, claim_vecs, witness);
system
.verify_multiple_claims(fri_parameters, claims_slice, &proof)
.verify(fri_parameters, &proof)
.expect("verification issue");
}

Expand Down
17 changes: 8 additions & 9 deletions src/test_circuits/byte_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,20 @@ mod tests {
};
let witness = calls.witness(&system);
let f = Val::from_u32;
let claim1 = &[f(0), f(10), f(5), f(10 ^ 5)];
let claim2 = &[f(1), f(30), f(20), f(30 & 20)];
let claim3 = &[f(2), f(100), f(40), f(100 | 40)];
let claim4 = &[f(3), f(200), f(100)];
let claims: &[&[Val]] = &[claim1, claim2, claim3, claim4];
let claims = vec![
vec![f(0), f(10), f(5), f(10 ^ 5)],
vec![f(1), f(30), f(20), f(30 & 20)],
vec![f(2), f(100), f(40), f(100 | 40)],
vec![f(3), f(200), f(100)],
];
let fri_parameters = FriParameters {
log_final_poly_len: 0,
max_log_arity: 1,
num_queries: 64,
commit_proof_of_work_bits: 0,
query_proof_of_work_bits: 0,
};
let proof = system.prove_multiple_claims(fri_parameters, &key, claims, witness);
system
.verify_multiple_claims(fri_parameters, claims, &proof)
.unwrap();
let proof = system.prove(fri_parameters, &key, claims, witness);
system.verify(fri_parameters, &proof).unwrap();
}
}
Loading