diff --git a/Cargo.toml b/Cargo.toml index 007d97a948..587b1b7e0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "provers/groth16", "provers/groth16/arkworks-adapter", "provers/groth16/circom-adapter", "examples/merkle-tree-cli", "examples/prove-miden", "provers/winterfell_adapter", "examples/shamir_secret_sharing", "examples/prove-verify-circom"] +members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "provers/groth16", "provers/groth16/arkworks-adapter", "provers/groth16/circom-adapter", "examples/merkle-tree-cli", "examples/prove-miden", "provers/winterfell_adapter", "examples/shamir_secret_sharing", "examples/prove-verify-circom", "provers/cairo/ffi"] exclude = ["ensure-no_std"] resolver = "2" diff --git a/README.md b/README.md index b8e75ae4ba..05c264771f 100644 --- a/README.md +++ b/README.md @@ -188,3 +188,4 @@ The following links, repos and projects have been important in the development o - [EthSTARK](https://github.com/starkware-libs/ethSTARK/tree/master) - [CAIRO whitepaper](https://eprint.iacr.org/2021/1063.pdf) - [Gnark](https://github.com/Consensys/gnark) +- [Constantine](https://github.com/mratsim/constantine) diff --git a/crypto/src/fiat_shamir/default_transcript.rs b/crypto/src/fiat_shamir/default_transcript.rs index 4279ea9013..9cac5b1bab 100644 --- a/crypto/src/fiat_shamir/default_transcript.rs +++ b/crypto/src/fiat_shamir/default_transcript.rs @@ -1,17 +1,32 @@ -use super::transcript::Transcript; -use alloc::borrow::ToOwned; +use super::is_transcript::IsTranscript; +use crate::alloc::borrow::ToOwned; +use core::marker::PhantomData; +use lambdaworks_math::{ + field::{element::FieldElement, traits::IsField}, + traits::ByteConversion, +}; use sha3::{Digest, Keccak256}; -pub struct DefaultTranscript { +pub struct DefaultTranscript { hasher: Keccak256, + phantom: PhantomData, } -impl Transcript for DefaultTranscript { - fn append(&mut self, new_data: &[u8]) { - self.hasher.update(&mut new_data.to_owned()); +impl DefaultTranscript +where + F: IsField, + FieldElement: ByteConversion, +{ + pub fn new(data: &[u8]) -> Self { + let mut res = Self { + hasher: Keccak256::new(), + phantom: PhantomData, + }; + res.append_bytes(data); + res } - fn challenge(&mut self) -> [u8; 32] { + pub fn sample(&mut self) -> [u8; 32] { let mut result_hash = [0_u8; 32]; result_hash.copy_from_slice(&self.hasher.finalize_reset()); result_hash.reverse(); @@ -20,37 +35,61 @@ impl Transcript for DefaultTranscript { } } -impl Default for DefaultTranscript { +impl Default for DefaultTranscript +where + F: IsField, + FieldElement: ByteConversion, +{ fn default() -> Self { - Self::new() + Self::new(&[]) } } -impl DefaultTranscript { - pub fn new() -> Self { - Self { - hasher: Keccak256::new(), - } +impl IsTranscript for DefaultTranscript +where + F: IsField, + FieldElement: ByteConversion, +{ + fn append_bytes(&mut self, new_bytes: &[u8]) { + self.hasher.update(&mut new_bytes.to_owned()); + } + + fn append_field_element(&mut self, element: &FieldElement) { + self.append_bytes(&element.to_bytes_be()); + } + + fn state(&self) -> [u8; 32] { + self.hasher.clone().finalize().into() + } + + fn sample_field_element(&mut self) -> FieldElement { + FieldElement::from_bytes_be(&self.sample()).unwrap() + } + + fn sample_u64(&mut self, upper_bound: u64) -> u64 { + u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound } } #[cfg(test)] mod tests { - use alloc::vec::Vec; - use super::*; + extern crate alloc; + use alloc::vec::Vec; + use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; + #[test] fn basic_challenge() { - let mut transcript = DefaultTranscript::new(); + let mut transcript = DefaultTranscript::::default(); let point_a: Vec = vec![0xFF, 0xAB]; let point_b: Vec = vec![0xDD, 0x8C, 0x9D]; - transcript.append(&point_a); // point_a - transcript.append(&point_b); // point_a + point_b + transcript.append_bytes(&point_a); // point_a + transcript.append_bytes(&point_b); // point_a + point_b - let challenge1 = transcript.challenge(); // Hash(point_a + point_b) + let challenge1 = transcript.sample(); // Hash(point_a + point_b) assert_eq!( challenge1, @@ -64,10 +103,10 @@ mod tests { let point_c: Vec = vec![0xFF, 0xAB]; let point_d: Vec = vec![0xDD, 0x8C, 0x9D]; - transcript.append(&point_c); // Hash(point_a + point_b) + point_c - transcript.append(&point_d); // Hash(point_a + point_b) + point_c + point_d + transcript.append_bytes(&point_c); // Hash(point_a + point_b) + point_c + transcript.append_bytes(&point_d); // Hash(point_a + point_b) + point_c + point_d - let challenge2 = transcript.challenge(); // Hash(Hash(point_a + point_b) + point_c + point_d) + let challenge2 = transcript.sample(); // Hash(Hash(point_a + point_b) + point_c + point_d) assert_eq!( challenge2, [ diff --git a/crypto/src/fiat_shamir/is_transcript.rs b/crypto/src/fiat_shamir/is_transcript.rs new file mode 100644 index 0000000000..a127539a52 --- /dev/null +++ b/crypto/src/fiat_shamir/is_transcript.rs @@ -0,0 +1,43 @@ +use lambdaworks_math::{ + field::{ + element::FieldElement, + traits::{IsField, IsSubFieldOf}, + }, + traits::AsBytes, +}; + +/// The functionality of a transcript to be used in the STARK Prove and Verify protocols. +pub trait IsTranscript { + /// Appends a field element to the transcript. + fn append_field_element(&mut self, element: &FieldElement); + /// Appends a bytes to the transcript. + fn append_bytes(&mut self, new_bytes: &[u8]); + /// Returns the inner state of the transcript that fully determines its outputs. + fn state(&self) -> [u8; 32]; + /// Returns a random field element. + fn sample_field_element(&mut self) -> FieldElement; + /// Returns a random index between 0 and `upper_bound`. + fn sample_u64(&mut self, upper_bound: u64) -> u64; + /// Returns a field element not contained in `lde_roots_of_unity_coset` or `trace_roots_of_unity`. + fn sample_z_ood>( + &mut self, + lde_roots_of_unity_coset: &[FieldElement], + trace_roots_of_unity: &[FieldElement], + ) -> FieldElement + where + FieldElement: AsBytes, + { + loop { + let value: FieldElement = self.sample_field_element(); + if !lde_roots_of_unity_coset + .iter() + .any(|x| x.clone().to_extension() == value) + && !trace_roots_of_unity + .iter() + .any(|x| x.clone().to_extension() == value) + { + return value; + } + } + } +} diff --git a/crypto/src/fiat_shamir/mod.rs b/crypto/src/fiat_shamir/mod.rs index 736301a5a4..378c490db3 100644 --- a/crypto/src/fiat_shamir/mod.rs +++ b/crypto/src/fiat_shamir/mod.rs @@ -1,4 +1,4 @@ pub mod default_transcript; +pub mod is_transcript; #[cfg(feature = "test_fiat_shamir")] pub mod test_transcript; -pub mod transcript; diff --git a/crypto/src/fiat_shamir/transcript.rs b/crypto/src/fiat_shamir/transcript.rs deleted file mode 100644 index 2c16575b74..0000000000 --- a/crypto/src/fiat_shamir/transcript.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub trait Transcript { - fn append(&mut self, new_data: &[u8]); - fn challenge(&mut self) -> [u8; 32]; -} diff --git a/provers/cairo/Cargo.toml b/provers/cairo/Cargo.toml index 60f220736a..bdc4829ec7 100644 --- a/provers/cairo/Cargo.toml +++ b/provers/cairo/Cargo.toml @@ -11,7 +11,7 @@ path = "src/main.rs" required-features = ["cli"] [lib] -name = "platinum_prover" +name = "cairo_platinum_prover" crate-type = ["cdylib", "rlib"] [dependencies] diff --git a/provers/cairo/README.md b/provers/cairo/README.md index a68ccbe088..e4651568c7 100644 --- a/provers/cairo/README.md +++ b/provers/cairo/README.md @@ -10,8 +10,18 @@ This prover is still in development and may contain bugs. It is not intended to Please check issues under security label, and wait for them to be resolved if they are relevant to your project. -## [Cairo Platinum Prover Docs](<[lambdaclass.github.io/lambdaworks/](https://github.com/lambdaclass/lambdaworks/blob/main/provers/cairo/README.md)>) +## [Cairo Platinum Prover Docs](https://lambdaclass.github.io/lambdaworks/starks/cairo.html) +## Proof file + +Currently, the .proof file contains the following elements concatenated: +- Proof size +- Proof +- Public inputs + +It uses the bincode as protocol to encode the structs of the files. Fields are stored as the bytes of canonical non-Montgomery form. It is in full big-endian form. Limbs are ordered in big-endian, and each u64 of the Montgomery field is in big-endian. + +⚠️: There may be changes to how this file represents the proof, always check here to ensure you have the latest encoding. We will add presets of verifications to the public inputs so you should not need to handle the file, just enable them. ### Cairo Platinum Prover - Introduction @@ -21,6 +31,10 @@ CLI currently runs with 100 bits of conjecturable security. Cairo / Cairo1 programs full integration is on the way. It can already be used generating a trace and a memory with the Cairo VM Runner, and fed to the prover with the prove command. +For using Cairo1, use the following [VM version](https://github.com/lambdaclass/cairo-vm/commit/070aeb9dbaf55875bf1cba2cef36fccafbb4851a) until it gets fixed in main + +Notice in this version you don't need the flag ```--proof_mode``` as it's enabled by default. + ### Usage: To prove Cairo programs, they first need to be compiled. For compilation you need to have `cairo-lang` or `docker` installed. diff --git a/provers/cairo/benches/criterion_prover.rs b/provers/cairo/benches/criterion_prover.rs index 86d7467db6..7a8e913087 100644 --- a/provers/cairo/benches/criterion_prover.rs +++ b/provers/cairo/benches/criterion_prover.rs @@ -1,9 +1,9 @@ +use cairo_platinum_prover::{ + air::generate_cairo_proof, cairo_layout::CairoLayout, runner::run::generate_prover_args, +}; use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; -use platinum_prover::{ - air::generate_cairo_proof, cairo_layout::CairoLayout, runner::run::generate_prover_args, -}; use stark_platinum_prover::proof::options::{ProofOptions, SecurityLevel}; pub mod functions; diff --git a/provers/cairo/benches/criterion_prover_70k.rs b/provers/cairo/benches/criterion_prover_70k.rs index 57bbeefac0..31f73e3a10 100644 --- a/provers/cairo/benches/criterion_prover_70k.rs +++ b/provers/cairo/benches/criterion_prover_70k.rs @@ -1,9 +1,9 @@ +use cairo_platinum_prover::cairo_layout::CairoLayout; +use cairo_platinum_prover::{air::generate_cairo_proof, runner::run::generate_prover_args}; use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode, }; -use platinum_prover::cairo_layout::CairoLayout; -use platinum_prover::{air::generate_cairo_proof, runner::run::generate_prover_args}; use stark_platinum_prover::proof::options::{ProofOptions, SecurityLevel}; pub mod functions; diff --git a/provers/cairo/benches/criterion_verifier.rs b/provers/cairo/benches/criterion_verifier.rs index de8379a53a..f4a82c957d 100644 --- a/provers/cairo/benches/criterion_verifier.rs +++ b/provers/cairo/benches/criterion_verifier.rs @@ -1,10 +1,10 @@ +use cairo_platinum_prover::air::{verify_cairo_proof, PublicInputs}; use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; use lambdaworks_math::{ field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, traits::Deserializable, }; -use platinum_prover::air::{verify_cairo_proof, PublicInputs}; use stark_platinum_prover::proof::{ options::{ProofOptions, SecurityLevel}, stark::StarkProof, diff --git a/provers/cairo/benches/criterion_verifier_70k.rs b/provers/cairo/benches/criterion_verifier_70k.rs index f4aafd58d1..6597d56e39 100644 --- a/provers/cairo/benches/criterion_verifier_70k.rs +++ b/provers/cairo/benches/criterion_verifier_70k.rs @@ -1,3 +1,4 @@ +use cairo_platinum_prover::air::{verify_cairo_proof, PublicInputs}; use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode, @@ -5,7 +6,6 @@ use criterion::{ use lambdaworks_math::{ field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, traits::Deserializable, }; -use platinum_prover::air::{verify_cairo_proof, PublicInputs}; use stark_platinum_prover::proof::{ options::{ProofOptions, SecurityLevel}, stark::StarkProof, diff --git a/provers/cairo/benches/functions/stark.rs b/provers/cairo/benches/functions/stark.rs index ab08338ee8..15f4611483 100644 --- a/provers/cairo/benches/functions/stark.rs +++ b/provers/cairo/benches/functions/stark.rs @@ -1,4 +1,4 @@ -use platinum_prover::{cairo_mem::CairoMemory, register_states::RegisterStates}; +use cairo_platinum_prover::{cairo_mem::CairoMemory, register_states::RegisterStates}; pub fn generate_cairo_trace(filename: &str) -> (RegisterStates, CairoMemory) { let base_dir = env!("CARGO_MANIFEST_DIR").to_string() + "/src/cairo_vm/test_data/"; diff --git a/provers/cairo/cairo_programs/cairo0/fibonacci_10000_loop.cairo b/provers/cairo/cairo_programs/cairo0/fibonacci_10000_loop.cairo new file mode 100644 index 0000000000..63ceaf213e --- /dev/null +++ b/provers/cairo/cairo_programs/cairo0/fibonacci_10000_loop.cairo @@ -0,0 +1,21 @@ + +// Looped fibonacci is more efficient +// than calling the fibo function with recursion +// For n = 5, it's 31 steps vs 49 steps +// This is useful to compare with other vms that are not validating the call stack for fibonacci + +func main{}() { + tempvar x0 = 0; + tempvar x1 = 1; + tempvar fib_acc = x0 + x1; + tempvar n = 10000; + loop: + tempvar x0 = x1; + tempvar x1 = fib_acc; + tempvar fib_acc = x0 + x1; + tempvar n = n - 1; + jmp loop if n != 0; + + assert fib_acc = 2287375788429092341882876480321135809824733217263858843173749298459021701670; + return (); +} diff --git a/provers/cairo/ffi/Cargo.toml b/provers/cairo/ffi/Cargo.toml new file mode 100644 index 0000000000..f7437cf02e --- /dev/null +++ b/provers/cairo/ffi/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "cairo-platinum-ffi" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# libc = "0.2" +stark-platinum-prover = { path = "../../stark" } +cairo-platinum-prover = { path = "../" } +bincode = { version = "2.0.0-rc.2", tag = "v2.0.0-rc.2", git = "https://github.com/bincode-org/bincode.git", features = ['serde'] } + +[lib] +crate-type = ["cdylib", "staticlib", "lib"] diff --git a/provers/cairo/ffi/fibo_5.proof b/provers/cairo/ffi/fibo_5.proof new file mode 100644 index 0000000000..fc112dec4c Binary files /dev/null and b/provers/cairo/ffi/fibo_5.proof differ diff --git a/provers/cairo/ffi/src/lib.rs b/provers/cairo/ffi/src/lib.rs new file mode 100644 index 0000000000..b5ea7e0a51 --- /dev/null +++ b/provers/cairo/ffi/src/lib.rs @@ -0,0 +1,71 @@ +use cairo_platinum_prover::air::CairoAIR; +use stark_platinum_prover::proof::options::ProofOptions; +use stark_platinum_prover::proof::options::SecurityLevel; +use stark_platinum_prover::transcript::StoneProverTranscript; +use stark_platinum_prover::verifier::{IsStarkVerifier, Verifier}; + +fn verify_cairo_proof_ffi(proof_bytes: &[u8], proof_options: &ProofOptions) -> bool { + let bytes = proof_bytes; + + // This logic is the same as main verify, with only error handling changing. In ffi, we simply return a false if the proof is invalid, instead of rising an error. + + // Proof len was stored as an u32, 4u8 needs to be read + let proof_len = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as usize; + + let bytes = &bytes[4..]; + if bytes.len() < proof_len { + return false; + } + + let Ok((proof, _)) = + bincode::serde::decode_from_slice(&bytes[0..proof_len], bincode::config::standard()) + else { + return false; + }; + let bytes = &bytes[proof_len..]; + + let Ok((pub_inputs, _)) = bincode::serde::decode_from_slice(bytes, bincode::config::standard()) + else { + return false; + }; + + Verifier::::verify( + &proof, + &pub_inputs, + proof_options, + StoneProverTranscript::new(&[]), + ) +} + +// Fibo 70k is 260 kb +// 2 MiB is more than enough +const MAX_PROOF_SIZE: usize = 1024 * 1024; + +/// WASM Function for verifying a proof with default 100 bits of security +#[no_mangle] +pub extern "C" fn verify_cairo_proof_ffi_100_bits( + proof_bytes: &[u8; MAX_PROOF_SIZE], + real_len: usize, +) -> bool { + let (real_proof_bytes, _) = proof_bytes.split_at(real_len); + + println!("Len: {:?} ", real_proof_bytes.len()); + let proof_options = ProofOptions::new_secure(SecurityLevel::Conjecturable100Bits, 3); + verify_cairo_proof_ffi(real_proof_bytes, &proof_options) +} + +#[cfg(test)] +mod tests { + use super::*; + + const PROOF: &[u8; 265809] = include_bytes!("../fibo_5.proof"); + + #[test] + fn fibo_5_proof_verifies() { + let mut proof_buffer = [0u8; super::MAX_PROOF_SIZE]; + let proof_size = PROOF.len(); + proof_buffer[..proof_size].clone_from_slice(PROOF); + let result = verify_cairo_proof_ffi_100_bits(&proof_buffer, proof_size); + assert!(result) + } +} diff --git a/provers/cairo/src/air.rs b/provers/cairo/src/air.rs index 7ab8777790..2d15f56729 100644 --- a/provers/cairo/src/air.rs +++ b/provers/cairo/src/air.rs @@ -3,6 +3,7 @@ use crate::transition_constraints::*; use cairo_vm::{air_public_input::MemorySegmentAddresses, without_std::collections::HashMap}; #[cfg(debug_assertions)] use itertools::Itertools; +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::{ errors::DeserializationError, field::{ @@ -18,7 +19,7 @@ use stark_platinum_prover::{ prover::{IsStarkProver, Prover, ProvingError}, trace::TraceTable, traits::AIR, - transcript::{IsStarkTranscript, StoneProverTranscript}, + transcript::StoneProverTranscript, verifier::{IsStarkVerifier, Verifier}, Felt252, }; @@ -763,7 +764,7 @@ impl AIR for CairoAIR { fn build_rap_challenges( &self, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Vec { let alpha_memory = transcript.sample_field_element(); let z_memory = transcript.sample_field_element(); diff --git a/provers/cairo/src/main.rs b/provers/cairo/src/main.rs index d797e7ee3e..b64a51f26e 100644 --- a/provers/cairo/src/main.rs +++ b/provers/cairo/src/main.rs @@ -1,8 +1,8 @@ +use cairo_platinum_prover::air::{generate_cairo_proof, verify_cairo_proof, PublicInputs}; +use cairo_platinum_prover::cairo_layout::CairoLayout; +use cairo_platinum_prover::runner::run::generate_prover_args; +use cairo_platinum_prover::runner::run::generate_prover_args_from_trace; use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; -use platinum_prover::air::{generate_cairo_proof, verify_cairo_proof, PublicInputs}; -use platinum_prover::cairo_layout::CairoLayout; -use platinum_prover::runner::run::generate_prover_args; -use platinum_prover::runner::run::generate_prover_args_from_trace; use stark_platinum_prover::proof::options::{ProofOptions, SecurityLevel}; use stark_platinum_prover::proof::stark::StarkProof; mod commands; diff --git a/provers/cairo/tests/wasm.rs b/provers/cairo/tests/wasm.rs index 0e820a4e8a..eb8fb66040 100644 --- a/provers/cairo/tests/wasm.rs +++ b/provers/cairo/tests/wasm.rs @@ -2,7 +2,7 @@ use wasm_bindgen_test::wasm_bindgen_test_configure; wasm_bindgen_test_configure!(run_in_browser); #[cfg(feature = "wasm")] -use platinum_prover::wasm_wrappers::verify_cairo_proof_wasm; +use cairo_platinum_prover::wasm_wrappers::verify_cairo_proof_wasm; #[cfg(feature = "wasm")] use stark_platinum_prover::proof::options::ProofOptions; diff --git a/provers/plonk/Cargo.toml b/provers/plonk/Cargo.toml index fe24503d5a..5f3784d489 100644 --- a/provers/plonk/Cargo.toml +++ b/provers/plonk/Cargo.toml @@ -8,5 +8,7 @@ edition = "2021" [dependencies] lambdaworks-math.workspace = true lambdaworks-crypto.workspace = true -serde = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +sha3 = { version = "0.10", default-features = false } +sha2 = { version = "0.10", default-features = false } diff --git a/provers/plonk/src/prover.rs b/provers/plonk/src/prover.rs index a120646370..3281c48db2 100644 --- a/provers/plonk/src/prover.rs +++ b/provers/plonk/src/prover.rs @@ -1,4 +1,4 @@ -use lambdaworks_crypto::fiat_shamir::transcript::Transcript; +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::errors::DeserializationError; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::traits::{AsBytes, Deserializable, IsRandomFieldElementGenerator}; @@ -638,20 +638,20 @@ where // Round 1 let round_1 = self.round_1(witness, common_preprocessed_input); - transcript.append(&round_1.a_1.as_bytes()); - transcript.append(&round_1.b_1.as_bytes()); - transcript.append(&round_1.c_1.as_bytes()); + transcript.append_bytes(&round_1.a_1.as_bytes()); + transcript.append_bytes(&round_1.b_1.as_bytes()); + transcript.append_bytes(&round_1.c_1.as_bytes()); // Round 2 // TODO: Handle error - let beta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - let gamma = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let beta = transcript.sample_field_element(); + let gamma = transcript.sample_field_element(); let round_2 = self.round_2(witness, common_preprocessed_input, beta, gamma); - transcript.append(&round_2.z_1.as_bytes()); + transcript.append_bytes(&round_2.z_1.as_bytes()); // Round 3 - let alpha = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let alpha = transcript.sample_field_element(); let round_3 = self.round_3( common_preprocessed_input, public_input, @@ -659,23 +659,23 @@ where &round_2, alpha, ); - transcript.append(&round_3.t_lo_1.as_bytes()); - transcript.append(&round_3.t_mid_1.as_bytes()); - transcript.append(&round_3.t_hi_1.as_bytes()); + transcript.append_bytes(&round_3.t_lo_1.as_bytes()); + transcript.append_bytes(&round_3.t_mid_1.as_bytes()); + transcript.append_bytes(&round_3.t_hi_1.as_bytes()); // Round 4 - let zeta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let zeta = transcript.sample_field_element(); let round_4 = self.round_4(common_preprocessed_input, &round_1, &round_2, zeta); - transcript.append(&round_4.a_zeta.to_bytes_be()); - transcript.append(&round_4.b_zeta.to_bytes_be()); - transcript.append(&round_4.c_zeta.to_bytes_be()); - transcript.append(&round_4.s1_zeta.to_bytes_be()); - transcript.append(&round_4.s2_zeta.to_bytes_be()); - transcript.append(&round_4.z_zeta_omega.to_bytes_be()); + transcript.append_field_element(&round_4.a_zeta); + transcript.append_field_element(&round_4.b_zeta); + transcript.append_field_element(&round_4.c_zeta); + transcript.append_field_element(&round_4.s1_zeta); + transcript.append_field_element(&round_4.s2_zeta); + transcript.append_field_element(&round_4.z_zeta_omega); // Round 5 - let upsilon = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let upsilon = transcript.sample_field_element(); let round_5 = self.round_5( common_preprocessed_input, &round_1, diff --git a/provers/plonk/src/setup.rs b/provers/plonk/src/setup.rs index 8511b61546..01f08588f5 100644 --- a/provers/plonk/src/setup.rs +++ b/provers/plonk/src/setup.rs @@ -3,8 +3,9 @@ use std::collections::HashMap; use crate::constraint_system::{get_permutation, ConstraintSystem, Variable}; use crate::test_utils::utils::{generate_domain, generate_permutation_coefficients}; use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; -use lambdaworks_crypto::fiat_shamir::transcript::Transcript; +use lambdaworks_crypto::fiat_shamir::{ + default_transcript::DefaultTranscript, is_transcript::IsTranscript, +}; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::field::{element::FieldElement, traits::IsField}; use lambdaworks_math::polynomial::Polynomial; @@ -132,27 +133,28 @@ pub fn setup>( pub fn new_strong_fiat_shamir_transcript( vk: &VerificationKey, public_input: &[FieldElement], -) -> DefaultTranscript +) -> DefaultTranscript where F: IsField, FieldElement: ByteConversion, CS: IsCommitmentScheme, CS::Commitment: AsBytes, { - let mut transcript = DefaultTranscript::new(); + let mut transcript = DefaultTranscript::default(); - transcript.append(&vk.s1_1.as_bytes()); - transcript.append(&vk.s2_1.as_bytes()); - transcript.append(&vk.s3_1.as_bytes()); - transcript.append(&vk.ql_1.as_bytes()); - transcript.append(&vk.qr_1.as_bytes()); - transcript.append(&vk.qm_1.as_bytes()); - transcript.append(&vk.qo_1.as_bytes()); - transcript.append(&vk.qc_1.as_bytes()); + transcript.append_bytes(&vk.s1_1.as_bytes()); + transcript.append_bytes(&vk.s2_1.as_bytes()); + transcript.append_bytes(&vk.s3_1.as_bytes()); + transcript.append_bytes(&vk.ql_1.as_bytes()); + transcript.append_bytes(&vk.qr_1.as_bytes()); + transcript.append_bytes(&vk.qm_1.as_bytes()); + transcript.append_bytes(&vk.qo_1.as_bytes()); + transcript.append_bytes(&vk.qc_1.as_bytes()); for value in public_input.iter() { - transcript.append(&value.to_bytes_be()); + transcript.append_field_element(value); } + transcript } diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index 17e8e59f1d..8393148dd5 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -1,5 +1,5 @@ use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_crypto::fiat_shamir::transcript::Transcript; +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::cyclic_group::IsGroup; use lambdaworks_math::field::element::FieldElement; use lambdaworks_math::field::traits::{IsFFTField, IsField, IsPrimeField}; @@ -36,27 +36,27 @@ impl> Verifier { { let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); - transcript.append(&p.a_1.as_bytes()); - transcript.append(&p.b_1.as_bytes()); - transcript.append(&p.c_1.as_bytes()); - let beta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - let gamma = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - transcript.append(&p.z_1.as_bytes()); - let alpha = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - transcript.append(&p.t_lo_1.as_bytes()); - transcript.append(&p.t_mid_1.as_bytes()); - transcript.append(&p.t_hi_1.as_bytes()); - let zeta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - transcript.append(&p.a_zeta.to_bytes_be()); - transcript.append(&p.b_zeta.to_bytes_be()); - transcript.append(&p.c_zeta.to_bytes_be()); - transcript.append(&p.s1_zeta.to_bytes_be()); - transcript.append(&p.s2_zeta.to_bytes_be()); - transcript.append(&p.z_zeta_omega.to_bytes_be()); - let upsilon = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + transcript.append_bytes(&p.a_1.as_bytes()); + transcript.append_bytes(&p.b_1.as_bytes()); + transcript.append_bytes(&p.c_1.as_bytes()); + let beta = transcript.sample_field_element(); + let gamma = transcript.sample_field_element(); + + transcript.append_bytes(&p.z_1.as_bytes()); + let alpha = transcript.sample_field_element(); + + transcript.append_bytes(&p.t_lo_1.as_bytes()); + transcript.append_bytes(&p.t_mid_1.as_bytes()); + transcript.append_bytes(&p.t_hi_1.as_bytes()); + let zeta = transcript.sample_field_element(); + + transcript.append_field_element(&p.a_zeta); + transcript.append_field_element(&p.b_zeta); + transcript.append_field_element(&p.c_zeta); + transcript.append_field_element(&p.s1_zeta); + transcript.append_field_element(&p.s2_zeta); + transcript.append_field_element(&p.z_zeta_omega); + let upsilon = transcript.sample_field_element(); [beta, gamma, alpha, zeta, upsilon] } diff --git a/provers/stark/src/examples/fibonacci_rap.rs b/provers/stark/src/examples/fibonacci_rap.rs index 263bc3170f..18084f86f4 100644 --- a/provers/stark/src/examples/fibonacci_rap.rs +++ b/provers/stark/src/examples/fibonacci_rap.rs @@ -10,8 +10,8 @@ use crate::{ proof::options::ProofOptions, trace::TraceTable, traits::AIR, - transcript::IsStarkTranscript, }; +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::{ field::{element::FieldElement, traits::IsFFTField}, helpers::resize_to_next_power_of_two, @@ -213,7 +213,7 @@ where fn build_rap_challenges( &self, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Vec> { vec![transcript.sample_field_element()] } diff --git a/provers/stark/src/fri/fri_decommit.rs b/provers/stark/src/fri/fri_decommit.rs index 8bc378ac82..cfdf5bf2f7 100644 --- a/provers/stark/src/fri/fri_decommit.rs +++ b/provers/stark/src/fri/fri_decommit.rs @@ -1,6 +1,4 @@ -pub use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_crypto::merkle_tree::proof::Proof; - use lambdaworks_math::field::element::FieldElement; use lambdaworks_math::field::traits::IsField; diff --git a/provers/stark/src/fri/mod.rs b/provers/stark/src/fri/mod.rs index cb32f5ca70..a841a8aea7 100644 --- a/provers/stark/src/fri/mod.rs +++ b/provers/stark/src/fri/mod.rs @@ -2,6 +2,7 @@ pub mod fri_commitment; pub mod fri_decommit; mod fri_functions; +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::field::traits::{IsFFTField, IsField}; use lambdaworks_math::traits::AsBytes; use lambdaworks_math::{ @@ -13,7 +14,6 @@ pub use lambdaworks_math::{ }; use crate::config::{BatchedMerkleTree, BatchedMerkleTreeBackend}; -use crate::transcript::IsStarkTranscript; use self::fri_commitment::FriLayer; use self::fri_decommit::FriDecommitment; @@ -22,7 +22,7 @@ use self::fri_functions::fold_polynomial; pub fn commit_phase, E: IsField>( number_layers: usize, p_0: Polynomial>, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, coset_offset: &FieldElement, domain_size: usize, ) -> ( diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index bb50d3fc80..7a5a4210e6 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -2,6 +2,7 @@ use std::marker::PhantomData; #[cfg(feature = "instruments")] use std::time::Instant; +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::fft::cpu::bit_reversing::{in_place_bit_reverse_permute, reverse_index}; use lambdaworks_math::fft::errors::FFTError; @@ -22,7 +23,6 @@ use crate::fri; use crate::proof::stark::{DeepPolynomialOpenings, PolynomialOpenings}; use crate::table::Table; use crate::trace::{columns2rows, LDETraceTable}; -use crate::transcript::IsStarkTranscript; use super::config::{BatchedMerkleTree, Commitment}; use super::constraints::evaluator::ConstraintEvaluator; @@ -194,7 +194,7 @@ pub trait IsStarkProver { fn interpolate_and_commit( trace: &TraceTable, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> ( Vec>>, Vec>>, @@ -270,7 +270,7 @@ pub trait IsStarkProver { air: &A, main_trace: &TraceTable, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Result, ProvingError> where FieldElement: AsBytes + Send + Sync, @@ -461,7 +461,7 @@ pub trait IsStarkProver { round_2_result: &Round2, round_3_result: &Round3, z: &FieldElement, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Round4 where FieldElement: AsBytes + Send + Sync, @@ -544,7 +544,7 @@ pub trait IsStarkProver { fn sample_query_indexes( number_of_queries: usize, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -793,7 +793,7 @@ pub trait IsStarkProver { main_trace: &TraceTable, pub_inputs: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, + mut transcript: impl IsTranscript, ) -> Result, ProvingError> where A: Send + Sync, diff --git a/provers/stark/src/traits.rs b/provers/stark/src/traits.rs index 0c6dcb14c3..d33343bb21 100644 --- a/provers/stark/src/traits.rs +++ b/provers/stark/src/traits.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::{ field::{ element::FieldElement, @@ -8,9 +9,7 @@ use lambdaworks_math::{ polynomial::Polynomial, }; -use crate::{ - constraints::transition::TransitionConstraint, domain::Domain, transcript::IsStarkTranscript, -}; +use crate::{constraints::transition::TransitionConstraint, domain::Domain}; use super::{ constraints::boundary::BoundaryConstraints, context::AirContext, frame::Frame, @@ -43,7 +42,7 @@ pub trait AIR { fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsTranscript, ) -> Vec> { Vec::new() } diff --git a/provers/stark/src/transcript.rs b/provers/stark/src/transcript.rs index b4d0c588cd..239c338d65 100644 --- a/provers/stark/src/transcript.rs +++ b/provers/stark/src/transcript.rs @@ -1,50 +1,14 @@ +use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; use lambdaworks_math::{ field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::{IsFFTField, IsField, IsSubFieldOf}, + element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + traits::IsFFTField, }, traits::{AsBytes, ByteConversion}, unsigned_integer::element::U256, }; use sha3::{Digest, Keccak256}; -/// The functionality of a transcript to be used in the STARK Prove and Verify protocols. -pub trait IsStarkTranscript { - /// Appends a field element to the transcript. - fn append_field_element(&mut self, element: &FieldElement); - /// Appends a bytes to the transcript. - fn append_bytes(&mut self, new_bytes: &[u8]); - /// Returns the inner state of the transcript that fully determines its outputs. - fn state(&self) -> [u8; 32]; - /// Returns a random field element. - fn sample_field_element(&mut self) -> FieldElement; - /// Returns a random index between 0 and `upper_bound`. - fn sample_u64(&mut self, upper_bound: u64) -> u64; - /// Returns a field element not contained in `lde_roots_of_unity_coset` or `trace_roots_of_unity`. - fn sample_z_ood>( - &mut self, - lde_roots_of_unity_coset: &[FieldElement], - trace_roots_of_unity: &[FieldElement], - ) -> FieldElement - where - FieldElement: AsBytes, - { - loop { - let value: FieldElement = self.sample_field_element(); - if !lde_roots_of_unity_coset - .iter() - .any(|x| x.clone().to_extension() == value) - && !trace_roots_of_unity - .iter() - .any(|x| x.clone().to_extension() == value) - { - return value; - } - } - } -} - /// A transcript implementing `IsStarkTranscript` and compatible with Stone (https://github.com/starkware-libs/stone-prover). pub struct StoneProverTranscript { state: [u8; 32], @@ -74,14 +38,6 @@ impl StoneProverTranscript { } } - fn keccak_hash(data: &[u8]) -> [u8; 32] { - let mut hasher = Keccak256::new(); - hasher.update(data); - let mut result_hash = [0_u8; 32]; - result_hash.copy_from_slice(&hasher.finalize_reset()); - result_hash - } - pub fn sample_block(&mut self, used_bytes: usize) -> Vec { let mut first_part: Vec = self.state.to_vec(); let mut counter_bytes: Vec = vec![0; 28] @@ -118,9 +74,17 @@ impl StoneProverTranscript { pub fn sample_big_int(&mut self) -> U256 { U256::from_bytes_be(&self.sample(32)).unwrap() } + + fn keccak_hash(data: &[u8]) -> [u8; 32] { + let mut hasher = Keccak256::new(); + hasher.update(data); + let mut result_hash = [0_u8; 32]; + result_hash.copy_from_slice(&hasher.finalize_reset()); + result_hash + } } -impl IsStarkTranscript for StoneProverTranscript { +impl IsTranscript for StoneProverTranscript { fn append_field_element(&mut self, element: &FieldElement) { let limbs = element.value().limbs; let mut bytes: [u8; 32] = [0; 32]; @@ -170,7 +134,7 @@ impl IsStarkTranscript for StoneProverTranscript { /// Returns a batch of size `size` of field elements sampled from the transcript `transcript`. pub fn batch_sample_challenges( size: usize, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Vec> where FieldElement: AsBytes, @@ -186,7 +150,7 @@ mod tests { element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, }; - use crate::transcript::{IsStarkTranscript, StoneProverTranscript}; + use crate::transcript::{IsTranscript, StoneProverTranscript}; use std::num::ParseIntError; diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index b345a92464..ea25dbe799 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -6,10 +6,8 @@ use super::{ proof::{options::ProofOptions, stark::StarkProof}, traits::AIR, }; -use crate::{ - config::Commitment, proof::stark::DeepPolynomialOpening, transcript::IsStarkTranscript, -}; -use lambdaworks_crypto::merkle_tree::proof::Proof; +use crate::{config::Commitment, proof::stark::DeepPolynomialOpening}; +use lambdaworks_crypto::{fiat_shamir::is_transcript::IsTranscript, merkle_tree::proof::Proof}; use lambdaworks_math::{ fft::cpu::bit_reversing::reverse_index, field::{ @@ -65,7 +63,7 @@ pub trait IsStarkVerifier { fn sample_query_indexes( number_of_queries: usize, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -78,7 +76,7 @@ pub trait IsStarkVerifier { air: &A, proof: &StarkProof, domain: &Domain, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsTranscript, ) -> Challenges where FieldElement: AsBytes, @@ -709,7 +707,7 @@ pub trait IsStarkVerifier { proof: &StarkProof, pub_input: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, + mut transcript: impl IsTranscript, ) -> bool where FieldElement: AsBytes + Sync + Send,