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/mod.rs b/jolt-core/src/jolt/trace/mod.rs index 06b23aaf9..c9fa70683 100644 --- a/jolt-core/src/jolt/trace/mod.rs +++ b/jolt-core/src/jolt/trace/mod.rs @@ -2,14 +2,14 @@ use ark_ff::PrimeField; use super::{ instruction::JoltInstruction, - vm::{pc::ELFRow, read_write_memory::MemoryOp}, + vm::{bytecode::ELFRow, read_write_memory::MemoryOp}, }; trait JoltProvableTrace { type JoltInstructionEnum: JoltInstruction; fn to_jolt_instructions(&self) -> Vec; fn to_ram_ops(&self) -> Vec; - fn to_pc_trace(&self) -> ELFRow; + fn to_bytecode_trace(&self) -> ELFRow; fn to_circuit_flags(&self) -> Vec; } diff --git a/jolt-core/src/jolt/trace/rv.rs b/jolt-core/src/jolt/trace/rv.rs index 99ee9268f..4dddd8ea6 100644 --- a/jolt-core/src/jolt/trace/rv.rs +++ b/jolt-core/src/jolt/trace/rv.rs @@ -18,9 +18,9 @@ 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::vm::{pc::ELFRow, rv32i_vm::RV32I}; +use crate::jolt::instruction::{add::ADDInstruction, sub::SUBInstruction}; +use crate::jolt::vm::{bytecode::ELFRow, rv32i_vm::RV32I}; use common::{constants::REGISTER_COUNT, RV32InstructionFormat, RV32IM}; use super::JoltProvableTrace; @@ -843,7 +843,7 @@ impl JoltProvableTrace for RVTraceRow { } } - fn to_pc_trace(&self) -> ELFRow { + fn to_bytecode_trace(&self) -> ELFRow { ELFRow::new( self.pc.try_into().unwrap(), self.opcode as u64, @@ -1016,10 +1016,10 @@ mod tests { use std::convert; #[test] - fn to_pc_trace() { + fn to_bytecode_trace() { // ADD let add_row = RVTraceRow::RType(2, RV32IM::ADD, 12, 10, 11, 0, 35, 15, 20); - let add_pc = add_row.to_pc_trace(); + let add_pc = add_row.to_bytecode_trace(); let expected_pc = ELFRow::new(2, RV32IM::ADD as u64, 12, 10, 11, 0u64); assert_eq!(add_pc, expected_pc); @@ -1027,7 +1027,7 @@ mod tests { let imm = 20; let rd_update = 20 << 12; let lui_row = RVTraceRow::UType(0, RV32IM::LUI, 10, 0, rd_update, imm); - let lui_pc = lui_row.to_pc_trace(); + let lui_pc = lui_row.to_bytecode_trace(); let expected_pc = ELFRow::new(0, RV32IM::LUI as u64, 10, 0, 0, 20u64); assert_eq!(lui_pc, expected_pc); } @@ -1070,19 +1070,32 @@ mod tests { .into_iter() .map(|common| RVTraceRow::from_common(common)) .collect(); + let _: Vec = converted_trace + .iter() + .map(|row| row.to_bytecode_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/pc.rs b/jolt-core/src/jolt/vm/bytecode.rs similarity index 80% rename from jolt-core/src/jolt/vm/pc.rs rename to jolt-core/src/jolt/vm/bytecode.rs index 215b28024..ed8aa947b 100644 --- a/jolt-core/src/jolt/vm/pc.rs +++ b/jolt-core/src/jolt/vm/bytecode.rs @@ -1,11 +1,12 @@ -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}, + lasso::memory_checking::{MemoryCheckingProof, MemoryCheckingProver, MemoryCheckingVerifier}, poly::{ dense_mlpoly::{DensePolynomial, PolyCommitmentGens}, identity_poly::IdentityPolynomial, @@ -17,6 +18,20 @@ use crate::{ const ADDRESS_INCREMENT: usize = 1; +pub struct BytecodeProof +where + F: PrimeField, + G: CurveGroup, +{ + pub memory_checking_proof: MemoryCheckingProof< + G, + BytecodePolynomials, + BytecodeReadWriteOpenings, + BytecodeInitFinalOpenings, + >, + pub commitment: BytecodeCommitment, +} + #[derive(Debug, Clone, PartialEq)] pub struct ELFRow { /// Memory address as read from the ELF. @@ -57,6 +72,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 { @@ -122,7 +151,7 @@ impl FiveTuplePoly { } } -pub struct PCPolys> { +pub struct BytecodePolynomials> { _group: PhantomData, /// MLE of read/write addresses. For offline memory checking, each read is paired with a "virtual" write, /// so the read addresses and write addresses are the same. @@ -141,19 +170,19 @@ pub struct PCPolys> { t_final: DensePolynomial, } -impl> PCPolys { - pub fn new_program(mut program: Vec, mut trace: Vec) -> Self { - Self::validate_program(&program, &trace); - Self::preprocess(&mut program, &mut trace); +impl> BytecodePolynomials { + pub fn new(mut bytecode: Vec, mut trace: Vec) -> Self { + Self::validate_bytecode(&bytecode, &trace); + Self::preprocess(&mut bytecode, &mut trace); // Preprocessing should deal with padding. - assert!(is_power_of_two(program.len())); + assert!(is_power_of_two(bytecode.len())); assert!(is_power_of_two(trace.len())); let num_ops = trace.len().next_power_of_two(); - let code_size = program.len().next_power_of_two(); + let code_size = bytecode.len().next_power_of_two(); - println!("PCPolys::new_program num_ops {num_ops} code_size {code_size}"); + println!("BytecodePolynomials::new num_ops {num_ops} code_size {code_size}"); let mut a_read_write_usize: Vec = vec![0; num_ops]; let mut read_cts: Vec = vec![0; num_ops]; @@ -169,7 +198,7 @@ impl> PCPolys { } let v_read_write = FiveTuplePoly::from_elf(&trace); - let v_init_final = FiveTuplePoly::from_elf(&program); + let v_init_final = FiveTuplePoly::from_elf(&bytecode); let a_read_write = DensePolynomial::from_usize(&a_read_write_usize); let t_read = DensePolynomial::from_usize(&read_cts); @@ -185,37 +214,37 @@ impl> PCPolys { } } - fn validate_program(program: &Vec, trace: &Vec) { - let mut pc = program[0].address; + fn validate_bytecode(bytecode: &Vec, trace: &Vec) { + let mut pc = bytecode[0].address; assert!(pc < 10_000); - let mut program_map: HashMap = HashMap::new(); + let mut bytecode_map: HashMap = HashMap::new(); - for program_row in program.iter().skip(1) { - program_map.insert(program_row.address, program_row); - assert_eq!(program_row.address, pc + ADDRESS_INCREMENT); + for bytecode_row in bytecode.iter().skip(1) { + bytecode_map.insert(bytecode_row.address, bytecode_row); + assert_eq!(bytecode_row.address, pc + ADDRESS_INCREMENT); - pc = program_row.address; + pc = bytecode_row.address; } for trace_row in trace { assert_eq!( - **program_map + **bytecode_map .get(&trace_row.address) - .expect("couldn't find in program"), + .expect("couldn't find in bytecode"), *trace_row ); } } - fn preprocess(program: &mut Vec, trace: &mut Vec) { - // Program: Add single no_op instruction at adddress | ELF + 1 | - let no_op_address = program.last().unwrap().address + ADDRESS_INCREMENT; - program.push(ELFRow::no_op(no_op_address)); + fn preprocess(bytecode: &mut Vec, trace: &mut Vec) { + // Bytecode: Add single no_op instruction at adddress | ELF + 1 | + let no_op_address = bytecode.last().unwrap().address + ADDRESS_INCREMENT; + bytecode.push(ELFRow::no_op(no_op_address)); - // Program: Pad to nearest power of 2 - for _program_i in program.len()..program.len().next_power_of_two() { - program.push(ELFRow::no_op(0)); + // Bytecode: Pad to nearest power of 2 + for _ in bytecode.len()..bytecode.len().next_power_of_two() { + bytecode.push(ELFRow::no_op(0)); } // Trace: Pad to nearest power of 2 @@ -226,7 +255,7 @@ impl> PCPolys { } } -pub struct BatchedPCPolys { +pub struct BatchedBytecodePolynomials { /// Contains: /// - a_read_write, t_read, v_read_write combined_read_write: DensePolynomial, @@ -236,8 +265,8 @@ pub struct BatchedPCPolys { combined_init_final: DensePolynomial, } -pub struct ProgramCommitment { - generators: PCCommitmentGenerators, +pub struct BytecodeCommitment { + generators: BytecodeCommitmentGenerators, /// Combined commitment for: /// - a_read_write, t_read, v_read_write pub read_write_commitments: CombinedTableCommitment, @@ -247,18 +276,18 @@ pub struct ProgramCommitment { pub init_final_commitments: CombinedTableCommitment, } -pub struct PCCommitmentGenerators { +pub struct BytecodeCommitmentGenerators { pub gens_read_write: PolyCommitmentGens, pub gens_init_final: PolyCommitmentGens, } -impl BatchablePolynomials for PCPolys +impl BatchablePolynomials for BytecodePolynomials where F: PrimeField, G: CurveGroup, { - type BatchedPolynomials = BatchedPCPolys; - type Commitment = ProgramCommitment; + type BatchedPolynomials = BatchedBytecodePolynomials; + type Commitment = BytecodeCommitment; fn batch(&self) -> Self::BatchedPolynomials { let combined_read_write = DensePolynomial::merge(&vec![ @@ -288,12 +317,12 @@ where fn commit(batched_polys: &Self::BatchedPolynomials) -> Self::Commitment { let (gens_read_write, read_write_commitments) = batched_polys .combined_read_write - .combined_commit(b"BatchedPCPolys.read_write"); + .combined_commit(b"BatchedBytecodePolynomials.read_write"); let (gens_init_final, init_final_commitments) = batched_polys .combined_init_final - .combined_commit(b"BatchedPCPolys.init_final"); + .combined_commit(b"BatchedBytecodePolynomials.init_final"); - let generators = PCCommitmentGenerators { + let generators = BytecodeCommitmentGenerators { gens_read_write, gens_init_final, }; @@ -306,15 +335,13 @@ where } } -pub struct PCProof(PhantomData); - -impl MemoryCheckingProver> for PCProof +impl MemoryCheckingProver> for BytecodePolynomials where F: PrimeField, G: CurveGroup, { - type ReadWriteOpenings = PCReadWriteOpenings; - type InitFinalOpenings = PCInitFinalOpenings; + type ReadWriteOpenings = BytecodeReadWriteOpenings; + type InitFinalOpenings = BytecodeInitFinalOpenings; // [a, opcode, rd, rs1, rs2, imm, t] type MemoryTuple = [F; 7]; @@ -331,14 +358,14 @@ where fn read_leaves( &self, - polynomials: &PCPolys, + polynomials: &BytecodePolynomials, gamma: &F, tau: &F, ) -> Vec> { let num_ops = polynomials.a_read_write.len(); let read_fingerprints = (0..num_ops) .map(|i| { - >>::fingerprint( + >>::fingerprint( &[ polynomials.a_read_write[i], polynomials.v_read_write.opcode[i], @@ -357,14 +384,14 @@ where } fn write_leaves( &self, - polynomials: &PCPolys, + polynomials: &BytecodePolynomials, gamma: &F, tau: &F, ) -> Vec> { let num_ops = polynomials.a_read_write.len(); let read_fingerprints = (0..num_ops) .map(|i| { - >>::fingerprint( + >>::fingerprint( &[ polynomials.a_read_write[i], polynomials.v_read_write.opcode[i], @@ -383,14 +410,14 @@ where } fn init_leaves( &self, - polynomials: &PCPolys, + polynomials: &BytecodePolynomials, gamma: &F, tau: &F, ) -> Vec> { let memory_size = polynomials.v_init_final.opcode.len(); let init_fingerprints = (0..memory_size) .map(|i| { - >>::fingerprint( + >>::fingerprint( &[ F::from(i as u64), polynomials.v_init_final.opcode[i], @@ -409,14 +436,14 @@ where } fn final_leaves( &self, - polynomials: &PCPolys, + polynomials: &BytecodePolynomials, gamma: &F, tau: &F, ) -> Vec> { let memory_size = polynomials.v_init_final.opcode.len(); let init_fingerprints = (0..memory_size) .map(|i| { - >>::fingerprint( + >>::fingerprint( &[ F::from(i as u64), polynomials.v_init_final.opcode[i], @@ -439,7 +466,7 @@ where } } -impl MemoryCheckingVerifier> for PCProof +impl MemoryCheckingVerifier> for BytecodePolynomials where F: PrimeField, G: CurveGroup, @@ -495,7 +522,7 @@ where } } -pub struct PCReadWriteOpenings +pub struct BytecodeReadWriteOpenings where F: PrimeField, G: CurveGroup, @@ -510,14 +537,15 @@ where read_write_opening_proof: CombinedTableEvalProof, } -impl StructuredOpeningProof> for PCReadWriteOpenings +impl StructuredOpeningProof> + for BytecodeReadWriteOpenings where F: PrimeField, G: CurveGroup, { type Openings = (F, Vec, F); - fn open(polynomials: &PCPolys, opening_point: &Vec) -> Self::Openings { + fn open(polynomials: &BytecodePolynomials, opening_point: &Vec) -> Self::Openings { ( polynomials.a_read_write.evaluate(&opening_point), polynomials.v_read_write.evaluate(&opening_point), @@ -526,8 +554,8 @@ where } fn prove_openings( - polynomials: &BatchedPCPolys, - commitment: &ProgramCommitment, + polynomials: &BatchedBytecodePolynomials, + commitment: &BytecodeCommitment, opening_point: &Vec, openings: (F, Vec, F), transcript: &mut Transcript, @@ -560,7 +588,7 @@ where fn verify_openings( &self, - commitment: &ProgramCommitment, + commitment: &BytecodeCommitment, opening_point: &Vec, transcript: &mut Transcript, ) -> Result<(), ProofVerifyError> { @@ -580,7 +608,7 @@ where } } -pub struct PCInitFinalOpenings +pub struct BytecodeInitFinalOpenings where F: PrimeField, G: CurveGroup, @@ -595,14 +623,15 @@ where init_final_opening_proof: CombinedTableEvalProof, } -impl StructuredOpeningProof> for PCInitFinalOpenings +impl StructuredOpeningProof> + for BytecodeInitFinalOpenings where F: PrimeField, G: CurveGroup, { type Openings = (Vec, F); - fn open(polynomials: &PCPolys, opening_point: &Vec) -> Self::Openings { + fn open(polynomials: &BytecodePolynomials, opening_point: &Vec) -> Self::Openings { ( polynomials.v_init_final.evaluate(&opening_point), polynomials.t_final.evaluate(&opening_point), @@ -610,8 +639,8 @@ where } fn prove_openings( - polynomials: &BatchedPCPolys, - commitment: &ProgramCommitment, + polynomials: &BatchedBytecodePolynomials, + commitment: &BytecodeCommitment, opening_point: &Vec, openings: Self::Openings, transcript: &mut Transcript, @@ -641,7 +670,7 @@ where fn verify_openings( &self, - commitment: &ProgramCommitment, + commitment: &BytecodeCommitment, opening_point: &Vec, transcript: &mut Transcript, ) -> Result<(), ProofVerifyError> { @@ -703,14 +732,14 @@ mod tests { ELFRow::new(3, 16u64, 16u64, 16u64, 16u64, 16u64), ELFRow::new(2, 8u64, 8u64, 8u64, 8u64, 8u64), ]; - let polys: PCPolys = PCPolys::new_program(program, trace); + let polys: BytecodePolynomials = + BytecodePolynomials::new(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]; @@ -735,15 +764,15 @@ mod tests { ELFRow::new(3, 16u64, 16u64, 16u64, 16u64, 16u64), ELFRow::new(2, 8u64, 8u64, 8u64, 8u64, 8u64), ]; - let polys: PCPolys = PCPolys::new_program(program, trace); - let pc_prover: PCProof = PCProof(PhantomData::<_>); + let polys: BytecodePolynomials = + BytecodePolynomials::new(program, trace); 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 commitments = BytecodePolynomials::commit(&batched_polys); + let proof = polys.prove_memory_checking( &polys, &batched_polys, &commitments, @@ -752,7 +781,7 @@ mod tests { ); let mut transcript = Transcript::new(b"test_transcript"); - PCProof::verify_memory_checking(proof, &commitments, &mut transcript) + BytecodePolynomials::verify_memory_checking(proof, &commitments, &mut transcript) .expect("proof should verify"); } @@ -771,15 +800,15 @@ 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 polys: BytecodePolynomials = + BytecodePolynomials::new(program, trace); let batch = polys.batch(); - let commitments = PCPolys::commit(&batch); + let commitments = BytecodePolynomials::commit(&batch); 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 +817,7 @@ mod tests { ); let mut transcript = Transcript::new(b"test_transcript"); - PCProof::verify_memory_checking(proof, &commitments, &mut transcript) + BytecodePolynomials::verify_memory_checking(proof, &commitments, &mut transcript) .expect("should verify"); } @@ -807,7 +836,8 @@ mod tests { ELFRow::new(2, 8u64, 8u64, 8u64, 8u64, 8u64), ELFRow::new(5, 0u64, 0u64, 0u64, 0u64, 0u64), // no_op: shouldn't exist in pgoram ]; - let _polys: PCPolys = PCPolys::new_program(program, trace); + let _polys: BytecodePolynomials = + BytecodePolynomials::new(program, trace); } #[test] @@ -823,6 +853,7 @@ mod tests { ELFRow::new(3, 16u64, 16u64, 16u64, 16u64, 16u64), ELFRow::new(2, 8u64, 8u64, 8u64, 8u64, 8u64), ]; - let _polys: PCPolys = PCPolys::new_program(program, trace); + let _polys: BytecodePolynomials = + BytecodePolynomials::new(program, trace); } } 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..6fd90ef42 100644 --- a/jolt-core/src/jolt/vm/mod.rs +++ b/jolt-core/src/jolt/vm/mod.rs @@ -4,108 +4,131 @@ use merlin::Transcript; use std::any::TypeId; use strum::{EnumCount, IntoEnumIterator}; -use crate::lasso::{ - memory_checking::MemoryCheckingProver, - surge::Surge, -}; - use crate::jolt::{ instruction::{sltu::SLTUInstruction, JoltInstruction, Opcode}, subtable::LassoSubtable, }; use crate::poly::structured_poly::BatchablePolynomials; use crate::utils::{errors::ProofVerifyError, random::RandomTape}; +use crate::{ + lasso::{ + memory_checking::{MemoryCheckingProof, MemoryCheckingProver, MemoryCheckingVerifier}, + surge::{Surge, SurgeProof}, + }, + utils::math::Math, +}; +use self::bytecode::{ + BytecodeCommitment, BytecodeInitFinalOpenings, BytecodePolynomials, BytecodeProof, + BytecodeReadWriteOpenings, ELFRow, +}; use self::instruction_lookups::{InstructionLookups, InstructionLookupsProof}; -use self::read_write_memory::{MemoryCommitment, MemoryOp, ReadWriteMemory}; +use self::read_write_memory::{ + MemoryCommitment, MemoryInitFinalOpenings, MemoryOp, MemoryReadWriteOpenings, ReadWriteMemory, + ReadWriteMemoryProof, +}; + +struct JoltProof> { + instruction_lookups: InstructionLookupsProof, + read_write_memory: ReadWriteMemoryProof, + bytecode: BytecodeProof, + // TODO: r1cs +} pub trait Jolt, const C: usize, const M: usize> { type InstructionSet: JoltInstruction + Opcode + IntoEnumIterator + EnumCount; type Subtables: LassoSubtable + IntoEnumIterator + EnumCount + From + Into; - fn prove() { - // preprocess? - // emulate - // prove_program_code - // prove_memory - // prove_lookups - // prove_r1cs - unimplemented!("todo"); + fn prove( + mut bytecode: Vec, + mut bytecode_trace: Vec, + memory_trace: Vec, + memory_size: usize, + instructions: Vec, + ) -> JoltProof { + let mut transcript = Transcript::new(b"Jolt transcript"); + let mut random_tape = RandomTape::new(b"Jolt prover randomness"); + let bytecode_proof = + Self::prove_bytecode(bytecode, bytecode_trace, &mut transcript, &mut random_tape); + let memory_proof = + Self::prove_memory(memory_trace, memory_size, &mut transcript, &mut random_tape); + let instruction_lookups = + Self::prove_instruction_lookups(instructions, &mut transcript, &mut random_tape); + todo!("rics"); + JoltProof { + instruction_lookups, + read_write_memory: memory_proof, + bytecode: bytecode_proof, + } + } + + fn verify(proof: JoltProof) -> Result<(), ProofVerifyError> { + let mut transcript = Transcript::new(b"Jolt transcript"); + Self::verify_bytecode(proof.bytecode, &mut transcript)?; + Self::verify_memory(proof.read_write_memory, &mut transcript)?; + Self::verify_instruction_lookups(proof.instruction_lookups, &mut transcript)?; + todo!("r1cs"); } 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), + fn prove_bytecode( + mut bytecode: Vec, + mut trace: Vec, transcript: &mut Transcript, - ) { - // 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, - // ) + random_tape: &mut RandomTape, + ) -> BytecodeProof { + let polys: BytecodePolynomials = BytecodePolynomials::new(bytecode, trace); + let batched_polys = polys.batch(); + let commitment = BytecodePolynomials::commit(&batched_polys); + + let memory_checking_proof = polys.prove_memory_checking( + &polys, + &batched_polys, + &commitment, + transcript, + random_tape, + ); + BytecodeProof { + memory_checking_proof, + commitment, + } } - fn prove_memory(memory_trace: Vec, memory_size: usize, transcript: &mut Transcript) { + fn verify_bytecode( + proof: BytecodeProof, + transcript: &mut Transcript, + ) -> Result<(), ProofVerifyError> { + BytecodePolynomials::verify_memory_checking( + proof.memory_checking_proof, + &proof.commitment, + transcript, + ) + } + + fn prove_memory( + memory_trace: Vec, + memory_size: usize, + transcript: &mut Transcript, + random_tape: &mut RandomTape, + ) -> ReadWriteMemoryProof { const MAX_TRACE_SIZE: usize = 1 << 22; // TODO: Support longer traces assert!(memory_trace.len() <= MAX_TRACE_SIZE); @@ -114,15 +137,14 @@ pub trait Jolt, const C: usize, co let (memory, read_timestamps) = ReadWriteMemory::new(memory_trace, memory_size, transcript); let batched_polys = memory.batch(); - let commitments: MemoryCommitment = ReadWriteMemory::commit(&batched_polys); + let commitment: 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, + &commitment, transcript, - &mut random_tape, + random_tape, ); let timestamp_validity_lookups: Vec = read_timestamps @@ -134,6 +156,28 @@ pub trait Jolt, const C: usize, co let timestamp_validity_proof = >::new(timestamp_validity_lookups) .prove(transcript); + + ReadWriteMemoryProof { + memory_checking_proof, + commitment, + timestamp_validity_proof, + } + } + + fn verify_memory( + proof: ReadWriteMemoryProof, + transcript: &mut Transcript, + ) -> Result<(), ProofVerifyError> { + const MAX_TRACE_SIZE: usize = 1 << 22; + ReadWriteMemory::verify_memory_checking( + proof.memory_checking_proof, + &proof.commitment, + transcript, + )?; + >::verify( + proof.timestamp_validity_proof, + transcript, + ) } fn prove_r1cs() { @@ -141,7 +185,7 @@ pub trait Jolt, const C: usize, co } } +pub mod bytecode; pub mod instruction_lookups; -pub mod pc; pub mod read_write_memory; pub mod rv32i_vm; diff --git a/jolt-core/src/jolt/vm/read_write_memory.rs b/jolt-core/src/jolt/vm/read_write_memory.rs index 68bdc2898..965c33e87 100644 --- a/jolt-core/src/jolt/vm/read_write_memory.rs +++ b/jolt-core/src/jolt/vm/read_write_memory.rs @@ -5,7 +5,8 @@ use ark_ff::PrimeField; use merlin::Transcript; use crate::{ - lasso::memory_checking::{MemoryCheckingProver, MemoryCheckingVerifier}, + lasso::memory_checking::{MemoryCheckingProof, MemoryCheckingProver, MemoryCheckingVerifier}, + lasso::surge::SurgeProof, poly::{ dense_mlpoly::{DensePolynomial, PolyCommitmentGens}, identity_poly::IdentityPolynomial, @@ -16,6 +17,21 @@ use crate::{ }; use common::constants::{RAM_START_ADDRESS, REGISTER_COUNT}; +pub struct ReadWriteMemoryProof +where + F: PrimeField, + G: CurveGroup, +{ + pub memory_checking_proof: MemoryCheckingProof< + G, + ReadWriteMemory, + MemoryReadWriteOpenings, + MemoryInitFinalOpenings, + >, + pub commitment: MemoryCommitment, + pub timestamp_validity_proof: SurgeProof, +} + #[derive(Debug, PartialEq, Clone)] pub enum MemoryOp { Read(u64, u64), // (address, value) 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]