Skip to content

Commit

Permalink
Stark: Stone prover compatibility end to end for Fibonacci AIR (#596)
Browse files Browse the repository at this point in the history
* add test

* make trace commitment SHARP compatible

* wip

* use powers of a single challenge for the boundary and transition coefficients

* add permutation to match sharp compatible commitments on the trace

* change trait bound from ByteConversion to Serializable

* minor refactor

* fmt, clippy

* move std feature to inner trait function in Serializable

* add IsStarkProver and IsStarkVerifier traits

* proof of concept

* composition poly breaker

* WIP: commitment composition poly works. Opens are broken.

* WIP Refactor open_trace_polys and open_composition_poly

* Refactor sample iotas

* Refactor sample iotas

* make fri a trait

* change trace ood evaluations in transcript

* wip

* sample gammas as power of a single challenge

* fix z fri sampling

* wip

* wip

* wip, broken

* Compiles but fibonacci_5 does not work

* Opens of query phase and OOD broken. Commit phase of FRI works.

* Dont append to the transcript when grinding factor is zero

* skip grinding factor when security bits is zero

* remove permutation function

* fmt

* fix standard verifier

* removes deep consistency check and openings of the first layer of fri for each query

* SHARP computes the trace and composition polynomial openings and their symmetric elements consistently

* Test symmetric elements in trace openings to compute deep composition polynomial

* Composition polynomial opening evaluations are splitted between symmetric and not. The authentication paths remain equal

* check openings in symmetric elements

* make verifier sharp compatible

* compute number of parts

* fix verify fri for original prover

* fix verify sym in stone prover

* rename

* rename file

* wip

* remove unnecessary variable

* wip

* move verifier

* move fri

* fix open

* move stone to prover

* remove file

* fmt

* clippy

* clippy

* remove redundant trait bounds

* remove custom serialization/deserialization and replace it with serde_cbor

* fmt

* clippy

* remove old files after merge from main

* fmt

* make field a type of IsStarkVerifier

* remove frame serialization

* separate compatibility test into individual tests

* remove redundant test

* add test case 2

* minor refactor. add docs

* minor refactor

* remove unnecessary method

* revert unintended changes to exercises

* clippy

* remove isFri trait

* move Prover definition to the top of the file

* update  docs and add unit test

* minor refactors. clippy

* remove unused trait method

* Move function only used for tests, to tests

---------

Co-authored-by: Agustin <[email protected]>
Co-authored-by: MauroFab <[email protected]>
  • Loading branch information
3 people authored Oct 17, 2023
1 parent 780b7dd commit a1f2fa7
Show file tree
Hide file tree
Showing 21 changed files with 2,054 additions and 2,598 deletions.
2 changes: 1 addition & 1 deletion exercises/message/src/starks/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ where
let timer4 = Instant::now();

#[allow(clippy::let_and_return)]
if !step_4_verify_deep_composition_polynomial(&air, proof, &domain, &challenges) {
if !step_4_verify_deep_composition_polynomial(&air, proof, &challenges) {
error!("DEEP Composition Polynomial verification failed");
return false;
}
Expand Down
8 changes: 4 additions & 4 deletions math/src/fft/cpu/bit_reversing.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/// In-place bit-reverse permutation algorithm. Requires input length to be a power of two.
pub fn in_place_bit_reverse_permute<E>(input: &mut [E]) {
for i in 0..input.len() {
let bit_reversed_index = reverse_index(&i, input.len() as u64);
let bit_reversed_index = reverse_index(i, input.len() as u64);
if bit_reversed_index > i {
input.swap(i, bit_reversed_index);
}
}
}

/// Reverses the `log2(size)` first bits of `i`
pub fn reverse_index(i: &usize, size: u64) -> usize {
pub fn reverse_index(i: usize, size: u64) -> usize {
if size == 1 {
*i
i
} else {
i.reverse_bits() >> (usize::BITS - size.trailing_zeros())
}
Expand All @@ -26,7 +26,7 @@ mod test {
fn bit_reverse_permutation_works() {
let mut reversed: Vec<usize> = Vec::with_capacity(16);
for i in 0..reversed.capacity() {
reversed.push(reverse_index(&i, reversed.capacity() as u64));
reversed.push(reverse_index(i, reversed.capacity() as u64));
}
assert_eq!(
reversed[..],
Expand Down
49 changes: 30 additions & 19 deletions math/src/polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,27 +227,24 @@ impl<F: IsField> Polynomial<FieldElement<F>> {
}
}

/// For the given polynomial, returns a tuple `(even, odd)` of polynomials
/// with the even and odd coefficients respectively.
/// Note that `even` and `odd` ARE NOT actually even/odd polynomials themselves.
/// Returns a vector of polynomials [p₀, p₁, ..., p_{d-1}], where d is `number_of_parts`, such that `self` equals
/// p₀(Xᵈ) + Xp₁(Xᵈ) + ... + X^(d-1)p_{d-1}(Xᵈ).
///
/// Example: if poly = 3 X^3 + X^2 + 2X + 1, then
/// `poly.even_odd_decomposition = (even, odd)` with
/// `even` = X + 1 and `odd` = 3X + 1.
///
/// In general, the decomposition satisfies the following:
/// `poly(x)` = `even(x^2)` + X * `odd(x^2)`
pub fn even_odd_decomposition(&self) -> (Self, Self) {
/// Example: if d = 2 and `self` is 3 X^3 + X^2 + 2X + 1, then `poly.break_in_parts(2)`
/// returns a vector with two polynomials `(p₀, p₁)`, where p₀ = X + 1 and p₁ = 3X + 2.
pub fn break_in_parts(&self, number_of_parts: usize) -> Vec<Self> {
let coef = self.coefficients();
let even_coef: Vec<FieldElement<F>> = coef.iter().step_by(2).cloned().collect();

// odd coeficients of poly are multiplied by beta
let odd_coef: Vec<FieldElement<F>> = coef.iter().skip(1).step_by(2).cloned().collect();

Polynomial::pad_with_zero_coefficients(
&Polynomial::new(&even_coef),
&Polynomial::new(&odd_coef),
)
let mut parts: Vec<Self> = Vec::with_capacity(number_of_parts);
for i in 0..number_of_parts {
let coeffs: Vec<_> = coef
.iter()
.skip(i)
.step_by(number_of_parts)
.cloned()
.collect();
parts.push(Polynomial::new(&coeffs));
}
parts
}
}

Expand Down Expand Up @@ -895,6 +892,20 @@ mod tests {
);
}

#[test]
fn break_in_parts() {
// p = 3 X^3 + X^2 + 2X + 1
let p = Polynomial::new(&[FE::new(1), FE::new(2), FE::new(1), FE::new(3)]);
let p0_expected = Polynomial::new(&[FE::new(1), FE::new(1)]);
let p1_expected = Polynomial::new(&[FE::new(2), FE::new(3)]);
let parts = p.break_in_parts(2);
assert_eq!(parts.len(), 2);
let p0 = &parts[0];
let p1 = &parts[1];
assert_eq!(p0, &p0_expected);
assert_eq!(p1, &p1_expected);
}

use proptest::prelude::*;
proptest! {
#[test]
Expand Down
1 change: 1 addition & 0 deletions provers/cairo-prover-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ name = "cairo-platinum-prover-cli"
path = "src/main.rs"

[dependencies]
serde_cbor = { version = "0.11.1"}
lambdaworks-math = { workspace = true , features = ["lambdaworks-serde"] }
stark-platinum-prover = { workspace = true, features = ["wasm"] }
cairo-platinum-prover = { workspace = true}
Expand Down
5 changes: 2 additions & 3 deletions provers/cairo-prover-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ fn main() {
};

let mut bytes = vec![];
let proof_bytes = proof.serialize();
let proof_bytes: Vec<u8> = serde_cbor::to_vec(&proof).unwrap();
bytes.extend(proof_bytes.len().to_be_bytes());
bytes.extend(proof_bytes);
bytes.extend(pub_inputs.serialize());
Expand Down Expand Up @@ -145,8 +145,7 @@ fn main() {
println!("Error reading proof from file: {}", args.proof_path);
return;
}
let Ok(proof) = StarkProof::<Stark252PrimeField>::deserialize(&bytes[0..proof_len])
else {
let Ok(proof) = serde_cbor::from_slice(&bytes[0..proof_len]) else {
println!("Error reading proof from file: {}", args.proof_path);
return;
};
Expand Down
4 changes: 2 additions & 2 deletions provers/cairo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ rayon = { version = "1.7.0", optional = true }
wasm-bindgen = { version = "0.2", optional = true }
serde-wasm-bindgen = { version = "0.5", optional = true }
web-sys = { version = "0.3.64", features = ['console'], optional = true }
serde_cbor = { version = "0.11.1", optional = true }
serde_cbor = { version = "0.11.1"}

[dev-dependencies]
hex = "0.4.3"
Expand All @@ -50,7 +50,7 @@ test_fiat_shamir = []
instruments = [] # This enables timing prints in prover and verifier
metal = ["lambdaworks-math/metal"]
parallel = ["dep:rayon"]
wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys", "dep:serde_cbor"]
wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys"]

[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
proptest = "1.2.0"
Expand Down
3 changes: 2 additions & 1 deletion provers/cairo/benches/criterion_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof<Stark252PrimeField
let mut bytes = program_content.as_slice();
let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap());
bytes = &bytes[8..];
let proof = StarkProof::<Stark252PrimeField>::deserialize(&bytes[0..proof_len]).unwrap();
let proof: StarkProof<Stark252PrimeField> =
serde_cbor::from_slice(&bytes[0..proof_len]).unwrap();
bytes = &bytes[proof_len..];

let public_inputs = PublicInputs::deserialize(bytes).unwrap();
Expand Down
3 changes: 2 additions & 1 deletion provers/cairo/benches/criterion_verifier_70k.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof<Stark252PrimeField
let mut bytes = program_content.as_slice();
let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap());
bytes = &bytes[8..];
let proof = StarkProof::<Stark252PrimeField>::deserialize(&bytes[0..proof_len]).unwrap();
let proof: StarkProof<Stark252PrimeField> =
serde_cbor::from_slice(&bytes[0..proof_len]).unwrap();
bytes = &bytes[proof_len..];

let public_inputs = PublicInputs::deserialize(bytes).unwrap();
Expand Down
53 changes: 6 additions & 47 deletions provers/cairo/src/air.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use stark_platinum_prover::{
context::AirContext,
frame::Frame,
proof::{options::ProofOptions, stark::StarkProof},
prover::{prove, ProvingError},
prover::{IsStarkProver, Prover, ProvingError},
trace::TraceTable,
traits::AIR,
transcript::{IsStarkTranscript, StoneProverTranscript},
verifier::verify,
verifier::{IsStarkVerifier, Verifier},
};

use crate::Felt252;
Expand Down Expand Up @@ -1254,7 +1254,7 @@ pub fn generate_cairo_proof(
pub_input: &PublicInputs,
proof_options: &ProofOptions,
) -> Result<StarkProof<Stark252PrimeField>, ProvingError> {
prove::<Stark252PrimeField, CairoAIR>(
Prover::prove::<CairoAIR>(
trace,
pub_input,
proof_options,
Expand All @@ -1270,7 +1270,7 @@ pub fn verify_cairo_proof(
pub_input: &PublicInputs,
proof_options: &ProofOptions,
) -> bool {
verify::<Stark252PrimeField, CairoAIR>(
Verifier::verify::<CairoAIR>(
proof,
pub_input,
proof_options,
Expand Down Expand Up @@ -1567,7 +1567,6 @@ mod test {
#[cfg(test)]
mod prop_test {
use lambdaworks_math::{
errors::DeserializationError,
field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField,
traits::{Deserializable, Serializable},
};
Expand Down Expand Up @@ -1652,7 +1651,7 @@ mod prop_test {

// The proof is generated and serialized.
let proof = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap();
let proof_bytes = proof.serialize();
let proof_bytes: Vec<u8> = serde_cbor::to_vec(&proof).unwrap();

// The trace and original proof are dropped to show that they are decoupled from
// the verifying process.
Expand All @@ -1661,49 +1660,9 @@ mod prop_test {

// At this point, the verifier only knows about the serialized proof, the proof options
// and the public inputs.
let proof = StarkProof::<Stark252PrimeField>::deserialize(&proof_bytes).unwrap();
let proof: StarkProof<Stark252PrimeField> = serde_cbor::from_slice(&proof_bytes).unwrap();

// The proof is verified successfully.
assert!(verify_cairo_proof(&proof, &pub_inputs, &proof_options));
}

#[test]
fn deserialize_should_not_panic_with_changed_and_sliced_bytes() {
let program_content = std::fs::read(cairo0_program_path("fibonacci_10.json")).unwrap();
let (main_trace, pub_inputs) =
generate_prover_args(&program_content, &None, CairoLayout::Plain).unwrap();

let proof_options = ProofOptions::default_test_options();

// The proof is generated and serialized.
let proof = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap();
let mut proof_bytes = proof.serialize();

// The trace and original proof are dropped to show that they are decoupled from
// the verifying process.
drop(main_trace);
drop(proof);

for byte in proof_bytes.iter_mut().take(21664) {
*byte = 255;
}
proof_bytes = proof_bytes[0..517].to_vec();

assert_eq!(
DeserializationError::InvalidAmountOfBytes,
StarkProof::<Stark252PrimeField>::deserialize(&proof_bytes)
.err()
.unwrap()
);
}

#[test]
fn deserialize_empty_proof_should_give_error() {
assert_eq!(
DeserializationError::InvalidAmountOfBytes,
StarkProof::<Stark252PrimeField>::deserialize(&[])
.err()
.unwrap()
);
}
}
40 changes: 3 additions & 37 deletions provers/cairo/src/tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use lambdaworks_math::{
errors::DeserializationError,
field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField,
traits::{Deserializable, Serializable},
};
use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField;
use stark_platinum_prover::{
debug::validate_trace,
domain::Domain,
Expand Down Expand Up @@ -299,7 +295,7 @@ fn deserialize_and_verify() {

// The proof is generated and serialized.
let proof = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap();
let proof_bytes = proof.serialize();
let proof_bytes: Vec<u8> = serde_cbor::to_vec(&proof).unwrap();

// The trace and original proof are dropped to show that they are decoupled from
// the verifying process.
Expand All @@ -308,38 +304,8 @@ fn deserialize_and_verify() {

// At this point, the verifier only knows about the serialized proof, the proof options
// and the public inputs.
let proof = StarkProof::<Stark252PrimeField>::deserialize(&proof_bytes).unwrap();
let proof: StarkProof<Stark252PrimeField> = serde_cbor::from_slice(&proof_bytes).unwrap();

// The proof is verified successfully.
assert!(verify_cairo_proof(&proof, &pub_inputs, &proof_options));
}

#[test]
fn deserialize_should_not_panic_with_changed_and_sliced_bytes() {
let program_content = std::fs::read(cairo0_program_path("fibonacci_10.json")).unwrap();
let (main_trace, pub_inputs) =
generate_prover_args(&program_content, &None, CairoLayout::Plain).unwrap();

let proof_options = ProofOptions::default_test_options();

// The proof is generated and serialized.
let proof = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap();
let mut proof_bytes = proof.serialize();

// The trace and original proof are dropped to show that they are decoupled from
// the verifying process.
drop(main_trace);
drop(proof);

for byte in proof_bytes.iter_mut().take(21664) {
*byte = 255;
}
proof_bytes = proof_bytes[0..517].to_vec();

assert_eq!(
DeserializationError::InvalidAmountOfBytes,
StarkProof::<Stark252PrimeField>::deserialize(&proof_bytes)
.err()
.unwrap()
);
}
4 changes: 2 additions & 2 deletions provers/stark/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ rayon = { version = "1.7.0", optional = true }
wasm-bindgen = { version = "0.2", optional = true }
serde-wasm-bindgen = { version = "0.5", optional = true }
web-sys = { version = "0.3.64", features = ['console'], optional = true }
serde_cbor = { version = "0.11.1", optional = true }
serde_cbor = { version = "0.11.1"}

[dev-dependencies]
hex = "0.4.3"
Expand All @@ -46,7 +46,7 @@ test_fiat_shamir = []
instruments = [] # This enables timing prints in prover and verifier
metal = ["lambdaworks-math/metal"]
parallel = ["dep:rayon"]
wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys", "dep:serde_cbor"]
wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys"]

[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
proptest = "1.2.0"
Expand Down
5 changes: 2 additions & 3 deletions provers/stark/src/constraints/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ use rayon::prelude::{
#[cfg(all(debug_assertions, not(feature = "parallel")))]
use crate::debug::check_boundary_polys_divisibility;
use crate::domain::Domain;
use crate::frame::Frame;
use crate::prover::evaluate_polynomial_on_lde_domain;
use crate::trace::TraceTable;
use crate::traits::AIR;
use crate::{frame::Frame, prover::evaluate_polynomial_on_lde_domain};

use super::{boundary::BoundaryConstraints, evaluation_table::ConstraintEvaluationTable};

Expand Down Expand Up @@ -272,7 +271,7 @@ fn evaluate_transition_exemptions<F: IsFFTField>(
domain: &Domain<F>,
) -> Vec<Vec<FieldElement<F>>>
where
FieldElement<F>: Send + Sync,
FieldElement<F>: Send + Sync + Serializable,
Polynomial<FieldElement<F>>: Send + Sync,
{
#[cfg(feature = "parallel")]
Expand Down
2 changes: 0 additions & 2 deletions provers/stark/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use super::traits::AIR;
pub struct Domain<F: IsFFTField> {
pub(crate) root_order: u32,
pub(crate) lde_roots_of_unity_coset: Vec<FieldElement<F>>,
pub(crate) lde_root_order: u32,
pub(crate) trace_primitive_root: FieldElement<F>,
pub(crate) trace_roots_of_unity: Vec<FieldElement<F>>,
pub(crate) coset_offset: FieldElement<F>,
Expand Down Expand Up @@ -46,7 +45,6 @@ impl<F: IsFFTField> Domain<F> {
Self {
root_order,
lde_roots_of_unity_coset,
lde_root_order,
trace_primitive_root,
trace_roots_of_unity,
blowup_factor,
Expand Down
Loading

0 comments on commit a1f2fa7

Please sign in to comment.