diff --git a/jolt-core/src/benches/bench.rs b/jolt-core/src/benches/bench.rs index 7b56e4806..dd6f2f75e 100644 --- a/jolt-core/src/benches/bench.rs +++ b/jolt-core/src/benches/bench.rs @@ -17,10 +17,10 @@ use crate::jolt::vm::instruction_lookups::InstructionLookupsProof; use crate::jolt::vm::rv32i_vm::{RV32IJoltVM, RV32I}; use crate::jolt::vm::Jolt; use crate::lasso::surge::Surge; -use crate::utils::math::Math; +use crate::utils::{math::Math, random::RandomTape}; use crate::{jolt::instruction::xor::XORInstruction, utils::gen_random_point}; use ark_curve25519::{EdwardsProjective, Fr}; -use ark_std::{test_rng}; +use ark_std::test_rng; use merlin::Transcript; use rand_chacha::rand_core::RngCore; @@ -152,12 +152,12 @@ fn rv32i_lookup_benchmarks() -> Vec<(tracing::Span, Box)> { println!("Running {:?}", ops.len()); let work = Box::new(|| { - let r: Vec = gen_random_point::(ops.len().log_2()); let mut prover_transcript = Transcript::new(b"example"); + let mut random_tape = RandomTape::new(b"test_tape"); let proof: InstructionLookupsProof = - RV32IJoltVM::prove_instruction_lookups(ops, r.clone(), &mut prover_transcript); + RV32IJoltVM::prove_instruction_lookups(ops, &mut prover_transcript, &mut random_tape); let mut verifier_transcript = Transcript::new(b"example"); - assert!(RV32IJoltVM::verify_instruction_lookups(proof, r, &mut verifier_transcript).is_ok()); + assert!(RV32IJoltVM::verify_instruction_lookups(proof, &mut verifier_transcript).is_ok()); }); vec![(tracing::info_span!("RV32IM"), work)] } diff --git a/jolt-core/src/jolt/trace/rv.rs b/jolt-core/src/jolt/trace/rv.rs index 99ee9268f..915c76cef 100644 --- a/jolt-core/src/jolt/trace/rv.rs +++ b/jolt-core/src/jolt/trace/rv.rs @@ -18,8 +18,8 @@ use crate::jolt::instruction::sltu::SLTUInstruction; use crate::jolt::instruction::sra::SRAInstruction; use crate::jolt::instruction::srl::SRLInstruction; use crate::jolt::instruction::xor::XORInstruction; -use crate::jolt::instruction::{add::ADDInstruction, sub::SUBInstruction}; use crate::jolt::instruction::JoltInstruction; +use crate::jolt::instruction::{add::ADDInstruction, sub::SUBInstruction}; use crate::jolt::vm::{pc::ELFRow, rv32i_vm::RV32I}; use common::{constants::REGISTER_COUNT, RV32InstructionFormat, RV32IM}; @@ -1070,19 +1070,32 @@ mod tests { .into_iter() .map(|common| RVTraceRow::from_common(common)) .collect(); + let _: Vec = converted_trace + .iter() + .map(|row| row.to_pc_trace()) + .collect(); let mut num_errors = 0; for row in &converted_trace { if let Err(e) = row.validate() { - // if row.opcode != RV32IM::SLLI { println!("Validation error: {} \n{:#?}\n\n", e, row); - // } num_errors += 1; } } println!("Total errors: {num_errors}"); } + #[test] + fn load_bytecode() { + use common::path::JoltPaths; + use common::serializable::Serializable; + + let bytecode_location = JoltPaths::bytecode_path("fibonacci"); + let instructions = Vec::::deserialize_from_file(&bytecode_location) + .expect("deserialization failed"); + let _: Vec = instructions.iter().map(|x| ELFRow::from(x)).collect(); + } + #[test] fn fib_e2e() { use crate::jolt::vm::rv32i_vm::RV32I; @@ -1115,13 +1128,18 @@ mod tests { .into_iter() .flat_map(|row| row.to_jolt_instructions()) .collect(); - let r: Vec = gen_random_point::(lookup_ops.len().log_2()); let mut prover_transcript = Transcript::new(b"example"); + let mut random_tape = RandomTape::new(b"test_tape"); + let proof: InstructionLookupsProof = - RV32IJoltVM::prove_instruction_lookups(lookup_ops, r.clone(), &mut prover_transcript); + RV32IJoltVM::prove_instruction_lookups( + lookup_ops, + &mut prover_transcript, + &mut random_tape, + ); let mut verifier_transcript = Transcript::new(b"example"); assert!( - RV32IJoltVM::verify_instruction_lookups(proof, r, &mut verifier_transcript).is_ok() + RV32IJoltVM::verify_instruction_lookups(proof, &mut verifier_transcript).is_ok() ); // Prove memory @@ -1141,7 +1159,6 @@ mod tests { let batched_polys = rw_memory.batch(); let commitments = ReadWriteMemory::commit(&batched_polys); - let mut random_tape = RandomTape::new(b"test_tape"); let proof = rw_memory.prove_memory_checking( &rw_memory, &batched_polys, diff --git a/jolt-core/src/jolt/vm/instruction_lookups.rs b/jolt-core/src/jolt/vm/instruction_lookups.rs index 8aa1bafa7..0758e8801 100644 --- a/jolt-core/src/jolt/vm/instruction_lookups.rs +++ b/jolt-core/src/jolt/vm/instruction_lookups.rs @@ -778,10 +778,11 @@ where pub fn prove_lookups( &self, - r: Vec, transcript: &mut Transcript, + random_tape: &mut RandomTape, ) -> InstructionLookupsProof { >::append_protocol_name(transcript, Self::protocol_name()); + let polynomials = self.polynomialize(); let batched_polys = polynomials.batch(); let commitment = InstructionPolynomials::commit(&batched_polys); @@ -790,7 +791,13 @@ where .E_commitment .append_to_transcript(b"comm_poly_row_col_ops_val", transcript); - let eq = EqPolynomial::new(r.to_vec()); + let r_eq = >::challenge_vector( + transcript, + b"Jolt instruction lookups", + self.ops.len().log_2(), + ); + + let eq = EqPolynomial::new(r_eq.to_vec()); let sumcheck_claim = Self::compute_sumcheck_claim(&self.ops, &polynomials.E_polys, &eq); >::append_scalar( @@ -799,7 +806,7 @@ where &sumcheck_claim, ); - let mut eq_poly = DensePolynomial::new(EqPolynomial::new(r).evals()); + let mut eq_poly = DensePolynomial::new(EqPolynomial::new(r_eq).evals()); let num_rounds = self.ops.len().log_2(); // TODO: compartmentalize all primary sumcheck logic @@ -814,8 +821,6 @@ where transcript, ); - let mut random_tape = RandomTape::new(b"proof"); - // Create a single opening proof for the flag_evals and memory_evals let sumcheck_openings = PrimarySumcheckOpenings::prove_openings( &batched_polys, @@ -823,7 +828,7 @@ where &r_primary_sumcheck, (E_evals, flag_evals), transcript, - &mut random_tape, + random_tape, ); let primary_sumcheck = PrimarySumcheck { @@ -838,7 +843,7 @@ where &batched_polys, &commitment, transcript, - &mut random_tape, + random_tape, ); InstructionLookupsProof { @@ -850,7 +855,6 @@ where pub fn verify( proof: InstructionLookupsProof, - r_eq: &[G::ScalarField], transcript: &mut Transcript, ) -> Result<(), ProofVerifyError> { >::append_protocol_name(transcript, Self::protocol_name()); @@ -860,6 +864,12 @@ where .E_commitment .append_to_transcript(b"comm_poly_row_col_ops_val", transcript); + let r_eq = >::challenge_vector( + transcript, + b"Jolt instruction lookups", + proof.primary_sumcheck.num_rounds, + ); + >::append_scalar( transcript, b"claim_eval_scalar_product", diff --git a/jolt-core/src/jolt/vm/mod.rs b/jolt-core/src/jolt/vm/mod.rs index a6497b432..83d174d94 100644 --- a/jolt-core/src/jolt/vm/mod.rs +++ b/jolt-core/src/jolt/vm/mod.rs @@ -4,9 +4,12 @@ use merlin::Transcript; use std::any::TypeId; use strum::{EnumCount, IntoEnumIterator}; -use crate::lasso::{ - memory_checking::MemoryCheckingProver, - surge::Surge, +use crate::{ + lasso::{ + memory_checking::{MemoryCheckingProof, MemoryCheckingProver, MemoryCheckingVerifier}, + surge::{Surge, SurgeProof}, + }, + utils::math::Math, }; use crate::jolt::{ @@ -17,7 +20,10 @@ use crate::poly::structured_poly::BatchablePolynomials; use crate::utils::{errors::ProofVerifyError, random::RandomTape}; use self::instruction_lookups::{InstructionLookups, InstructionLookupsProof}; -use self::read_write_memory::{MemoryCommitment, MemoryOp, ReadWriteMemory}; +use self::pc::{ELFRow, PCInitFinalOpenings, PCPolys, PCReadWriteOpenings, ProgramCommitment}; +use self::read_write_memory::{ + MemoryCommitment, MemoryInitFinalOpenings, MemoryOp, MemoryReadWriteOpenings, ReadWriteMemory, +}; pub trait Jolt, const C: usize, const M: usize> { type InstructionSet: JoltInstruction + Opcode + IntoEnumIterator + EnumCount; @@ -35,77 +41,75 @@ pub trait Jolt, const C: usize, co fn prove_instruction_lookups( ops: Vec, - r: Vec, transcript: &mut Transcript, + random_tape: &mut RandomTape, ) -> InstructionLookupsProof { let instruction_lookups = InstructionLookups::::new(ops); - instruction_lookups.prove_lookups(r, transcript) + instruction_lookups.prove_lookups(transcript, random_tape) } fn verify_instruction_lookups( proof: InstructionLookupsProof, - r: Vec, transcript: &mut Transcript, ) -> Result<(), ProofVerifyError> { InstructionLookups::::verify( - proof, &r, transcript, + proof, transcript, ) } fn prove_program_code( - program_code: &[u64], - access_sequence: &[usize], - code_size: usize, - contiguous_reads_per_access: usize, - r_mem_check: &(F, F), + mut program: Vec, + mut trace: Vec, transcript: &mut Transcript, + random_tape: &mut RandomTape, + ) -> ( + MemoryCheckingProof, PCReadWriteOpenings, PCInitFinalOpenings>, + ProgramCommitment, ) { - // let (gamma, tau) = r_mem_check; - // let hash_func = |a: &F, v: &F, t: &F| -> F { *t * gamma.square() + *v * *gamma + *a - tau }; - - // let m: usize = (access_sequence.len() * contiguous_reads_per_access).next_power_of_two(); - // // TODO(moodlezoup): resize access_sequence? - - // let mut read_addrs: Vec = Vec::with_capacity(m); - // let mut final_cts: Vec = vec![0; code_size]; - // let mut read_cts: Vec = Vec::with_capacity(m); - // let mut read_values: Vec = Vec::with_capacity(m); - - // for (j, code_address) in access_sequence.iter().enumerate() { - // debug_assert!(code_address + contiguous_reads_per_access <= code_size); - // debug_assert!(code_address % contiguous_reads_per_access == 0); - - // for offset in 0..contiguous_reads_per_access { - // let addr = code_address + offset; - // let counter = final_cts[addr]; - // read_addrs.push(addr); - // read_values.push(program_code[addr]); - // read_cts.push(counter); - // final_cts[addr] = counter + 1; - // } - // } - - // let E_poly: DensePolynomial = DensePolynomial::from_u64(&read_values); // v_ops - // let dim: DensePolynomial = DensePolynomial::from_usize(access_sequence); // a_ops - // let read_cts: DensePolynomial = DensePolynomial::from_usize(&read_cts); // t_read - // let final_cts: DensePolynomial = DensePolynomial::from_usize(&final_cts); // t_final - // let init_values: DensePolynomial = DensePolynomial::from_u64(program_code); // v_mem - - // let polys = PCPolys::new(dim, E_poly, init_values, read_cts, final_cts, 0); - // let (gens, commitments) = polys.commit::(); - - todo!("decide how to represent nested proofs, gens, commitments"); - // MemoryCheckingProof::>::prove( - // &polys, - // r_fingerprints, - // &gens, - // &mut transcript, - // &mut random_tape, - // ) + let polys: PCPolys = PCPolys::new_program(program, trace); + let batched_polys = polys.batch(); + let commitments = PCPolys::commit(&batched_polys); + + ( + polys.prove_memory_checking( + &polys, + &batched_polys, + &commitments, + transcript, + random_tape, + ), + commitments, + ) + } + + fn verify_program_code( + proof: MemoryCheckingProof< + G, + PCPolys, + PCReadWriteOpenings, + PCInitFinalOpenings, + >, + commitment: ProgramCommitment, + transcript: &mut Transcript, + ) -> Result<(), ProofVerifyError> { + PCPolys::verify_memory_checking(proof, &commitment, transcript) } - fn prove_memory(memory_trace: Vec, memory_size: usize, transcript: &mut Transcript) { + fn prove_memory( + memory_trace: Vec, + memory_size: usize, + transcript: &mut Transcript, + random_tape: &mut RandomTape, + ) -> ( + MemoryCheckingProof< + G, + ReadWriteMemory, + MemoryReadWriteOpenings, + MemoryInitFinalOpenings, + >, + SurgeProof, + ) { const MAX_TRACE_SIZE: usize = 1 << 22; // TODO: Support longer traces assert!(memory_trace.len() <= MAX_TRACE_SIZE); @@ -116,13 +120,12 @@ pub trait Jolt, const C: usize, co let batched_polys = memory.batch(); let commitments: MemoryCommitment = ReadWriteMemory::commit(&batched_polys); - let mut random_tape = RandomTape::new(b"proof"); - memory.prove_memory_checking( + let memory_checking_proof = memory.prove_memory_checking( &memory, &batched_polys, &commitments, transcript, - &mut random_tape, + random_tape, ); let timestamp_validity_lookups: Vec = read_timestamps @@ -134,6 +137,27 @@ pub trait Jolt, const C: usize, co let timestamp_validity_proof = >::new(timestamp_validity_lookups) .prove(transcript); + + (memory_checking_proof, timestamp_validity_proof) + } + + fn verify_memory( + memory_checking_proof: MemoryCheckingProof< + G, + ReadWriteMemory, + MemoryReadWriteOpenings, + MemoryInitFinalOpenings, + >, + commitment: MemoryCommitment, + transcript: &mut Transcript, + timestamp_validity_proof: SurgeProof, + ) -> Result<(), ProofVerifyError> { + const MAX_TRACE_SIZE: usize = 1 << 22; + ReadWriteMemory::verify_memory_checking(memory_checking_proof, &commitment, transcript)?; + >::verify( + timestamp_validity_proof, + transcript, + ) } fn prove_r1cs() { diff --git a/jolt-core/src/jolt/vm/pc.rs b/jolt-core/src/jolt/vm/pc.rs index 215b28024..99f9710c4 100644 --- a/jolt-core/src/jolt/vm/pc.rs +++ b/jolt-core/src/jolt/vm/pc.rs @@ -1,8 +1,9 @@ -use std::{collections::HashMap, marker::PhantomData}; - use ark_ec::CurveGroup; use ark_ff::PrimeField; use merlin::Transcript; +use std::{collections::HashMap, marker::PhantomData}; + +use common::ELFInstruction; use crate::{ lasso::memory_checking::{MemoryCheckingProver, MemoryCheckingVerifier}, @@ -57,6 +58,20 @@ impl ELFRow { } } +// TODO(JOLT-74): Consolidate ELFInstruction and ELFRow +impl From<&ELFInstruction> for ELFRow { + fn from(value: &ELFInstruction) -> Self { + Self::new( + value.address as usize, + value.opcode as u64, + value.rd.unwrap_or(0), + value.rs1.unwrap_or(0), + value.rs2.unwrap_or(0), + value.imm.unwrap_or(0) as u64, // imm is always cast to its 32-bit repr, signed or unsigned + ) + } +} + /// Polynomial representation of bytecode as expected by Jolt –– each bytecode address maps to a /// tuple, containing information about the instruction and its operands. pub struct FiveTuplePoly { @@ -306,9 +321,7 @@ where } } -pub struct PCProof(PhantomData); - -impl MemoryCheckingProver> for PCProof +impl MemoryCheckingProver> for PCPolys where F: PrimeField, G: CurveGroup, @@ -439,7 +452,7 @@ where } } -impl MemoryCheckingVerifier> for PCProof +impl MemoryCheckingVerifier> for PCPolys where F: PrimeField, G: CurveGroup, @@ -706,11 +719,10 @@ mod tests { let polys: PCPolys = PCPolys::new_program(program, trace); let (gamma, tau) = (&Fr::from(100), &Fr::from(35)); - let pc_prover: PCProof = PCProof(PhantomData::<_>); - let init_leaves: Vec> = pc_prover.init_leaves(&polys, gamma, tau); - let read_leaves: Vec> = pc_prover.read_leaves(&polys, gamma, tau); - let write_leaves: Vec> = pc_prover.write_leaves(&polys, gamma, tau); - let final_leaves: Vec> = pc_prover.final_leaves(&polys, gamma, tau); + let init_leaves: Vec> = polys.init_leaves(&polys, gamma, tau); + let read_leaves: Vec> = polys.read_leaves(&polys, gamma, tau); + let write_leaves: Vec> = polys.write_leaves(&polys, gamma, tau); + let final_leaves: Vec> = polys.final_leaves(&polys, gamma, tau); let init_leaves = &init_leaves[0]; let read_leaves = &read_leaves[0]; @@ -736,14 +748,13 @@ mod tests { ELFRow::new(2, 8u64, 8u64, 8u64, 8u64, 8u64), ]; let polys: PCPolys = PCPolys::new_program(program, trace); - let pc_prover: PCProof = PCProof(PhantomData::<_>); let mut transcript = Transcript::new(b"test_transcript"); let mut random_tape = RandomTape::new(b"test_tape"); let batched_polys = polys.batch(); let commitments = PCPolys::commit(&batched_polys); - let proof = pc_prover.prove_memory_checking( + let proof = polys.prove_memory_checking( &polys, &batched_polys, &commitments, @@ -752,7 +763,7 @@ mod tests { ); let mut transcript = Transcript::new(b"test_transcript"); - PCProof::verify_memory_checking(proof, &commitments, &mut transcript) + PCPolys::verify_memory_checking(proof, &commitments, &mut transcript) .expect("proof should verify"); } @@ -771,7 +782,6 @@ mod tests { ELFRow::new(4, 32u64, 32u64, 32u64, 32u64, 32u64), ]; - let pc_prover: PCProof = PCProof(PhantomData::<_>); let polys: PCPolys = PCPolys::new_program(program, trace); let batch = polys.batch(); let commitments = PCPolys::commit(&batch); @@ -779,7 +789,7 @@ mod tests { let mut transcript = Transcript::new(b"test_transcript"); let mut random_tape = RandomTape::new(b"test_tape"); - let proof = pc_prover.prove_memory_checking( + let proof = polys.prove_memory_checking( &polys, &batch, &commitments, @@ -788,7 +798,7 @@ mod tests { ); let mut transcript = Transcript::new(b"test_transcript"); - PCProof::verify_memory_checking(proof, &commitments, &mut transcript) + PCPolys::verify_memory_checking(proof, &commitments, &mut transcript) .expect("should verify"); } diff --git a/jolt-core/src/jolt/vm/rv32i_vm.rs b/jolt-core/src/jolt/vm/rv32i_vm.rs index ed1aa099b..48f993ca5 100644 --- a/jolt-core/src/jolt/vm/rv32i_vm.rs +++ b/jolt-core/src/jolt/vm/rv32i_vm.rs @@ -7,11 +7,11 @@ use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use super::Jolt; use crate::jolt::instruction::add::ADD32Instruction; use crate::jolt::instruction::{ - and::ANDInstruction, beq::BEQInstruction, bge::BGEInstruction, - bgeu::BGEUInstruction, blt::BLTInstruction, bltu::BLTUInstruction, bne::BNEInstruction, - jal::JALInstruction, jalr::JALRInstruction, or::ORInstruction, sll::SLLInstruction, - slt::SLTInstruction, sltu::SLTUInstruction, sra::SRAInstruction, srl::SRLInstruction, - sub::SUBInstruction, xor::XORInstruction, JoltInstruction, Opcode, + and::ANDInstruction, beq::BEQInstruction, bge::BGEInstruction, bgeu::BGEUInstruction, + blt::BLTInstruction, bltu::BLTUInstruction, bne::BNEInstruction, jal::JALInstruction, + jalr::JALRInstruction, or::ORInstruction, sll::SLLInstruction, slt::SLTInstruction, + sltu::SLTUInstruction, sra::SRAInstruction, srl::SRLInstruction, sub::SUBInstruction, + xor::XORInstruction, JoltInstruction, Opcode, }; use crate::jolt::subtable::{ and::AndSubtable, eq::EqSubtable, eq_abs::EqAbsSubtable, eq_msb::EqMSBSubtable, @@ -199,14 +199,13 @@ mod tests { RV32I::XOR(XORInstruction(rng.next_u32() as u64, rng.next_u32() as u64)), ]; - let r: Vec = gen_random_point::(ops.len().log_2()); let mut prover_transcript = Transcript::new(b"example"); + let mut random_tape = RandomTape::new(b"test_tape"); + let proof: InstructionLookupsProof = - RV32IJoltVM::prove_instruction_lookups(ops, r.clone(), &mut prover_transcript); + RV32IJoltVM::prove_instruction_lookups(ops, &mut prover_transcript, &mut random_tape); let mut verifier_transcript = Transcript::new(b"example"); - assert!( - RV32IJoltVM::verify_instruction_lookups(proof, r, &mut verifier_transcript).is_ok() - ); + assert!(RV32IJoltVM::verify_instruction_lookups(proof, &mut verifier_transcript).is_ok()); } #[test]