From ec3c94fa504ad947e5982987bf2eba27c1e9fe68 Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Wed, 28 Feb 2024 10:19:28 -0500 Subject: [PATCH 1/5] Move instantiation of group generators (i.e. Hyrax commitment key) from Jolt::prove to Jolt::preprocess --- jolt-core/Cargo.toml | 3 +- jolt-core/src/benches/bench.rs | 92 +++++---- jolt-core/src/jolt/vm/bytecode.rs | 177 ++++++++++-------- jolt-core/src/jolt/vm/instruction_lookups.rs | 54 +++--- jolt-core/src/jolt/vm/mod.rs | 111 ++++++++--- jolt-core/src/jolt/vm/read_write_memory.rs | 42 +++-- jolt-core/src/jolt/vm/rv32i_vm.rs | 64 ++++--- .../src/jolt/vm/timestamp_range_check.rs | 43 +++-- jolt-core/src/lasso/memory_checking.rs | 17 +- jolt-core/src/lasso/surge.rs | 58 +++--- jolt-core/src/poly/dense_mlpoly.rs | 14 +- jolt-core/src/poly/hyrax.rs | 18 +- jolt-core/src/poly/pedersen.rs | 38 ++-- jolt-core/src/poly/structured_poly.rs | 14 +- 14 files changed, 433 insertions(+), 312 deletions(-) diff --git a/jolt-core/Cargo.toml b/jolt-core/Cargo.toml index 18e4100ae..7dfc323ad 100644 --- a/jolt-core/Cargo.toml +++ b/jolt-core/Cargo.toml @@ -69,12 +69,11 @@ witness = { git = "https://github.com/sragss/circom-witness-rs", branch = "non-r ruint = "1.11.1" spartan2 = { git = "https://github.com/a16z/Spartan2.git" } ark-bn254 = "0.4.0" - +lazy_static = "1.4.0" [build-dependencies] common = { path = "../common" } - [lib] name = "liblasso" path = "src/lib.rs" diff --git a/jolt-core/src/benches/bench.rs b/jolt-core/src/benches/bench.rs index 220578856..ef553e2e4 100644 --- a/jolt-core/src/benches/bench.rs +++ b/jolt-core/src/benches/bench.rs @@ -9,7 +9,7 @@ use crate::jolt::vm::read_write_memory::{ use crate::jolt::vm::rv32i_vm::{RV32IJoltVM, C, M, RV32I}; use crate::jolt::vm::Jolt; use crate::poly::dense_mlpoly::bench::{init_commit_bench, run_commit_bench}; -use ark_bn254::{G1Projective, Fr}; +use ark_bn254::{Fr, G1Projective}; use common::constants::MEMORY_OPS_PER_INSTRUCTION; use common::ELFInstruction; use criterion::black_box; @@ -57,9 +57,9 @@ fn prove_e2e_except_r1cs( ) -> Vec<(tracing::Span, Box)> { let mut rng = rand::rngs::StdRng::seed_from_u64(1234567890); - let memory_size = memory_size.unwrap_or(1 << 22); // 4,194,304 = 4 MB - let bytecode_size = bytecode_size.unwrap_or(1 << 16); // 65,536 = 64 kB - let num_cycles = num_cycles.unwrap_or(1 << 16); // 65,536 + let memory_size = memory_size.unwrap_or(1 << 22); // 4,194,304 = 4 MB + let bytecode_size = bytecode_size.unwrap_or(1 << 16); // 65,536 = 64 kB + let num_cycles = num_cycles.unwrap_or(1 << 16); // 65,536 let ops: Vec = std::iter::repeat_with(|| RV32I::random_instruction(&mut rng)) .take(num_cycles) @@ -74,14 +74,20 @@ fn prove_e2e_except_r1cs( .collect(); let bytecode_trace = random_bytecode_trace(&bytecode_rows, num_cycles, &mut rng); - let work = Box::new(|| { - let mut transcript = Transcript::new(b"example"); - let _: (_, BytecodePolynomials, _) = - RV32IJoltVM::prove_bytecode(bytecode_rows, bytecode_trace, &mut transcript); + let generators = RV32IJoltVM::preprocess(bytecode_size, memory_size, num_cycles); + let mut transcript = Transcript::new(b"example"); + + let work = Box::new(move || { + let _: (_, BytecodePolynomials, _) = RV32IJoltVM::prove_bytecode( + bytecode_rows, + bytecode_trace, + &generators, + &mut transcript, + ); let _: (_, ReadWriteMemory, _) = - RV32IJoltVM::prove_memory(bytecode, memory_trace, &mut transcript); + RV32IJoltVM::prove_memory(bytecode, memory_trace, &generators, &mut transcript); let _: (_, InstructionPolynomials, _) = - RV32IJoltVM::prove_instruction_lookups(ops, &mut transcript); + RV32IJoltVM::prove_instruction_lookups(ops, &generators, &mut transcript); }); vec![( tracing::info_span!("prove_bytecode + prove_memory + prove_instruction_lookups"), @@ -103,10 +109,16 @@ fn prove_bytecode( .collect(); let bytecode_trace = random_bytecode_trace(&bytecode_rows, num_cycles, &mut rng); - let work = Box::new(|| { - let mut transcript = Transcript::new(b"example"); - let _: (_, BytecodePolynomials, _) = - RV32IJoltVM::prove_bytecode(bytecode_rows, bytecode_trace, &mut transcript); + let generators = RV32IJoltVM::preprocess(bytecode_size, 1, num_cycles); + let mut transcript = Transcript::new(b"example"); + + let work = Box::new(move || { + let _: (_, BytecodePolynomials, _) = RV32IJoltVM::prove_bytecode( + bytecode_rows, + bytecode_trace, + &generators, + &mut transcript, + ); }); vec![(tracing::info_span!("prove_bytecode"), work)] } @@ -127,10 +139,12 @@ fn prove_memory( .collect(); let memory_trace = random_memory_trace(&bytecode, memory_size, num_cycles, &mut rng); - let work = Box::new(|| { + let generators = RV32IJoltVM::preprocess(bytecode_size, memory_size, num_cycles); + + let work = Box::new(move || { let mut transcript = Transcript::new(b"example"); let _: (_, ReadWriteMemory, _) = - RV32IJoltVM::prove_memory(bytecode, memory_trace, &mut transcript); + RV32IJoltVM::prove_memory(bytecode, memory_trace, &generators, &mut transcript); }); vec![(tracing::info_span!("prove_memory"), work)] } @@ -143,10 +157,12 @@ fn prove_instruction_lookups(num_cycles: Option) -> Vec<(tracing::Span, B .take(num_cycles) .collect(); - let work = Box::new(|| { - let mut transcript = Transcript::new(b"example"); + let generators = RV32IJoltVM::preprocess(1, 1, num_cycles); + let mut transcript = Transcript::new(b"example"); + + let work = Box::new(move || { let _: (_, InstructionPolynomials, _) = - RV32IJoltVM::prove_instruction_lookups(ops, &mut transcript); + RV32IJoltVM::prove_instruction_lookups(ops, &generators, &mut transcript); }); vec![(tracing::info_span!("prove_instruction_lookups"), work)] } @@ -225,13 +241,17 @@ fn prove_example(example_name: &str) -> Vec<(tracing::Span, Box)> .flat_map(|row| row.to_circuit_flags::()) .collect::>(); - let (jolt_proof, jolt_commitments) = >::prove( - bytecode, - bytecode_trace, - memory_trace, - instructions_r1cs, - circuit_flags, - ); + let generators = RV32IJoltVM::preprocess(1 << 20, 1 << 20, 1 << 22); + + let (jolt_proof, jolt_commitments) = + >::prove( + bytecode, + bytecode_trace, + memory_trace, + instructions_r1cs, + circuit_flags, + generators, + ); assert!(RV32IJoltVM::verify(jolt_proof, jolt_commitments).is_ok()); }; @@ -284,7 +304,6 @@ fn fibonacci() -> Vec<(tracing::Span, Box)> { let memory_trace: Vec<[MemoryOp; MEMORY_OPS_PER_INSTRUCTION]> = converted_trace .clone() .into_iter() - .map(|row| row.to_ram_ops().try_into().unwrap()) .collect(); let circuit_flags = converted_trace @@ -292,14 +311,17 @@ fn fibonacci() -> Vec<(tracing::Span, Box)> { .flat_map(|row| row.to_circuit_flags::()) .collect::>(); - let (jolt_proof, jolt_commitments) = >::prove( - bytecode, - bytecode_trace, - memory_trace, - instructions_r1cs, - circuit_flags, - ); - + let generators = RV32IJoltVM::preprocess(1 << 20, 1 << 20, 1 << 22); + + let (jolt_proof, jolt_commitments) = + >::prove( + bytecode, + bytecode_trace, + memory_trace, + instructions_r1cs, + circuit_flags, + generators, + ); assert!(RV32IJoltVM::verify(jolt_proof, jolt_commitments).is_ok()); }; diff --git a/jolt-core/src/jolt/vm/bytecode.rs b/jolt-core/src/jolt/vm/bytecode.rs index b947ebfc9..e87521ff7 100644 --- a/jolt-core/src/jolt/vm/bytecode.rs +++ b/jolt-core/src/jolt/vm/bytecode.rs @@ -7,9 +7,8 @@ use std::{collections::HashMap, marker::PhantomData}; use crate::jolt::trace::{rv::RVTraceRow, JoltProvableTrace}; use crate::poly::eq_poly::EqPolynomial; -use crate::poly::hyrax::{HyraxCommitment, HyraxGenerators}; -use crate::poly::pedersen::PedersenInit; -use crate::utils::math::Math; +use crate::poly::hyrax::matrix_dimensions; +use crate::poly::pedersen::PedersenGenerators; use common::constants::{BYTES_PER_INSTRUCTION, RAM_START_ADDRESS, REGISTER_COUNT}; use common::RV32IM; use common::{to_ram_address, ELFInstruction}; @@ -26,7 +25,7 @@ use crate::{ subprotocols::batched_commitment::{ BatchedPolynomialCommitment, BatchedPolynomialOpeningProof, }, - utils::{errors::ProofVerifyError, is_power_of_two}, + utils::{errors::ProofVerifyError, is_power_of_two, math::Math}, }; pub type BytecodeProof = MemoryCheckingProof< @@ -406,6 +405,17 @@ impl> BytecodePolynomials { trace.push(ELFRow::no_op(no_op_address)); } } + + /// Computes the maximum number of group generators needed to commit to bytecode + /// polynomials using Hyrax, given the maximum bytecode size and maximum trace length. + pub fn num_generators(max_bytecode_size: usize, max_trace_length: usize) -> usize { + // a_read_write, t_read, v_read_write (opcode, rs1, rs2, rd, imm) + let read_write_num_vars = (max_trace_length * 7).log_2(); + // t_final, v_init_final (opcode, rs1, rs2, rd, imm) + let init_final_num_vars = (max_bytecode_size * 6).log_2(); + let max_num_vars = std::cmp::max(read_write_num_vars, init_final_num_vars); + matrix_dimensions(max_num_vars).1.pow2() + } } pub struct BatchedBytecodePolynomials { @@ -463,25 +473,22 @@ where } #[tracing::instrument(skip_all, name = "BytecodePolynomials::commit")] - fn commit(batched_polys: &Self::BatchedPolynomials, initializer: &PedersenInit) -> Self::Commitment { + fn commit( + batched_polys: &Self::BatchedPolynomials, + pedersen_generators: &PedersenGenerators, + ) -> Self::Commitment { let read_write_commitments = batched_polys .combined_read_write - .combined_commit(initializer); + .combined_commit(pedersen_generators); let init_final_commitments = batched_polys .combined_init_final - .combined_commit(initializer); + .combined_commit(pedersen_generators); Self::Commitment { read_write_commitments, init_final_commitments, } } - - fn max_generator_size(batched_polys: &Self::BatchedPolynomials) -> usize { - let read_write_num_vars = batched_polys.combined_read_write.get_num_vars(); - let init_final_num_vars = batched_polys.combined_init_final.get_num_vars(); - std::cmp::max(read_write_num_vars, init_final_num_vars) - } } impl MemoryCheckingProver> for BytecodePolynomials @@ -515,76 +522,84 @@ where let num_ops = polynomials.a_read_write.len(); let memory_size = polynomials.v_init_final.opcode.len(); - let read_fingerprints = (0..num_ops).into_par_iter().map(|i| { - >>::fingerprint( - &[ - polynomials.a_read_write[i], - polynomials.v_read_write.opcode[i], - polynomials.v_read_write.rd[i], - polynomials.v_read_write.rs1[i], - polynomials.v_read_write.rs2[i], - polynomials.v_read_write.imm[i], - polynomials.t_read[i], - ], - gamma, - tau, - ) - }) - .collect(); + let read_fingerprints = (0..num_ops) + .into_par_iter() + .map(|i| { + >>::fingerprint( + &[ + polynomials.a_read_write[i], + polynomials.v_read_write.opcode[i], + polynomials.v_read_write.rd[i], + polynomials.v_read_write.rs1[i], + polynomials.v_read_write.rs2[i], + polynomials.v_read_write.imm[i], + polynomials.t_read[i], + ], + gamma, + tau, + ) + }) + .collect(); let read_leaves = DensePolynomial::new(read_fingerprints); - let init_fingerprints = (0..memory_size).into_par_iter().map(|i| { - >>::fingerprint( - &[ - F::from(i as u64), - polynomials.v_init_final.opcode[i], - polynomials.v_init_final.rd[i], - polynomials.v_init_final.rs1[i], - polynomials.v_init_final.rs2[i], - polynomials.v_init_final.imm[i], - F::zero(), - ], - gamma, - tau, - ) - }) - .collect(); + let init_fingerprints = (0..memory_size) + .into_par_iter() + .map(|i| { + >>::fingerprint( + &[ + F::from(i as u64), + polynomials.v_init_final.opcode[i], + polynomials.v_init_final.rd[i], + polynomials.v_init_final.rs1[i], + polynomials.v_init_final.rs2[i], + polynomials.v_init_final.imm[i], + F::zero(), + ], + gamma, + tau, + ) + }) + .collect(); let init_leaves = DensePolynomial::new(init_fingerprints); - let write_fingerprints = (0..num_ops).into_par_iter().map(|i| { - >>::fingerprint( - &[ - polynomials.a_read_write[i], - polynomials.v_read_write.opcode[i], - polynomials.v_read_write.rd[i], - polynomials.v_read_write.rs1[i], - polynomials.v_read_write.rs2[i], - polynomials.v_read_write.imm[i], - polynomials.t_read[i] + F::one(), - ], - gamma, - tau, - ) - }) - .collect(); + let write_fingerprints = (0..num_ops) + .into_par_iter() + .map(|i| { + >>::fingerprint( + &[ + polynomials.a_read_write[i], + polynomials.v_read_write.opcode[i], + polynomials.v_read_write.rd[i], + polynomials.v_read_write.rs1[i], + polynomials.v_read_write.rs2[i], + polynomials.v_read_write.imm[i], + polynomials.t_read[i] + F::one(), + ], + gamma, + tau, + ) + }) + .collect(); let write_leaves = DensePolynomial::new(write_fingerprints); - let final_fingerprints = (0..memory_size).into_par_iter().map(|i| { - >>::fingerprint( - &[ - F::from(i as u64), - polynomials.v_init_final.opcode[i], - polynomials.v_init_final.rd[i], - polynomials.v_init_final.rs1[i], - polynomials.v_init_final.rs2[i], - polynomials.v_init_final.imm[i], - polynomials.t_final[i], - ], - gamma, - tau, - ) - }) - .collect(); + let final_fingerprints = (0..memory_size) + .into_par_iter() + .map(|i| { + >>::fingerprint( + &[ + F::from(i as u64), + polynomials.v_init_final.opcode[i], + polynomials.v_init_final.rd[i], + polynomials.v_init_final.rs1[i], + polynomials.v_init_final.rs2[i], + polynomials.v_init_final.imm[i], + polynomials.t_final[i], + ], + gamma, + tau, + ) + }) + .collect(); let final_leaves = DensePolynomial::new(final_fingerprints); ( @@ -866,8 +881,8 @@ mod tests { let mut transcript = Transcript::new(b"test_transcript"); let batched_polys = polys.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(10, b"test"); - let commitments = BytecodePolynomials::commit(&batched_polys, &initializer); + let generators = PedersenGenerators::new(10, b"test"); + let commitments = BytecodePolynomials::commit(&batched_polys, &generators); let proof = polys.prove_memory_checking(&polys, &batched_polys, &mut transcript); let mut transcript = Transcript::new(b"test_transcript"); @@ -893,8 +908,8 @@ mod tests { let polys: BytecodePolynomials = BytecodePolynomials::new(program, trace); let batch = polys.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(8, b"test"); - let commitments = BytecodePolynomials::commit(&batch, &initializer); + let generators = PedersenGenerators::new(8, b"test"); + let commitments = BytecodePolynomials::commit(&batch, &generators); let mut transcript = Transcript::new(b"test_transcript"); diff --git a/jolt-core/src/jolt/vm/instruction_lookups.rs b/jolt-core/src/jolt/vm/instruction_lookups.rs index 848619733..af73be1cd 100644 --- a/jolt-core/src/jolt/vm/instruction_lookups.rs +++ b/jolt-core/src/jolt/vm/instruction_lookups.rs @@ -1,19 +1,18 @@ use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use itertools::interleave; +use itertools::{interleave, max}; use merlin::Transcript; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; +use rayon::prelude::*; use std::any::TypeId; use std::marker::PhantomData; use strum::{EnumCount, IntoEnumIterator}; use tracing::trace_span; -use rayon::prelude::*; - use crate::lasso::memory_checking::MultisetHashes; -use crate::poly::hyrax::HyraxGenerators; -use crate::poly::pedersen::PedersenInit; +use crate::poly::hyrax::{matrix_dimensions, HyraxGenerators}; +use crate::poly::pedersen::PedersenGenerators; use crate::utils::{mul_0_1_optimized, split_poly_flagged}; use crate::{ jolt::{ @@ -52,11 +51,11 @@ where /// `m` (# lookups). pub dim: Vec>, - /// `C` sized vector of `DensePolynomials` whose evaluations correspond to + /// `NUM_MEMORIES` sized vector of `DensePolynomials` whose evaluations correspond to /// read access counts to the memory. Each `DensePolynomial` has size `m` (# lookups). pub read_cts: Vec>, - /// `C` sized vector of `DensePolynomials` whose evaluations correspond to + /// `NUM_MEMORIES` sized vector of `DensePolynomials` whose evaluations correspond to /// final access counts to the memory. Each `DensePolynomial` has size M, AKA subtable size. pub final_cts: Vec>, @@ -133,16 +132,13 @@ where } #[tracing::instrument(skip_all, name = "InstructionPolynomials::commit")] - fn commit(batched_polys: &Self::BatchedPolynomials, initializer: &PedersenInit) -> Self::Commitment { - let dim_read_commitment = batched_polys - .batched_dim_read - .combined_commit(initializer); - let final_commitment = batched_polys - .batched_final - .combined_commit(initializer); - let E_flag_commitment = batched_polys - .batched_E_flag - .combined_commit(initializer); + fn commit( + batched_polys: &Self::BatchedPolynomials, + generators: &PedersenGenerators, + ) -> Self::Commitment { + let dim_read_commitment = batched_polys.batched_dim_read.combined_commit(generators); + let final_commitment = batched_polys.batched_final.combined_commit(generators); + let E_flag_commitment = batched_polys.batched_E_flag.combined_commit(generators); Self::Commitment { dim_read_commitment, @@ -150,14 +146,6 @@ where E_flag_commitment, } } - - fn max_generator_size(batched_polys: &Self::BatchedPolynomials) -> usize { - let dim_read_num_vars = batched_polys.batched_dim_read.get_num_vars(); - let final_num_vars = batched_polys.batched_final.get_num_vars(); - let E_flag_num_vars = batched_polys.batched_E_flag.get_num_vars(); - - std::cmp::max(std::cmp::max(dim_read_num_vars, final_num_vars), E_flag_num_vars) - } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] @@ -808,6 +796,7 @@ where #[tracing::instrument(skip_all, name = "InstructionLookups::prove_lookups")] pub fn prove_lookups( &self, + generators: &PedersenGenerators, transcript: &mut Transcript, ) -> ( InstructionLookupsProof, @@ -818,8 +807,7 @@ where let polynomials = self.polynomialize(); let batched_polys = polynomials.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(InstructionPolynomials::::max_generator_size(&batched_polys), b"LassoV1"); - let commitment = InstructionPolynomials::commit(&batched_polys, &initializer); + let commitment = InstructionPolynomials::commit(&batched_polys, generators); commitment .E_flag_commitment @@ -1439,6 +1427,18 @@ where subtable_lookup_indices } + /// Computes the maximum number of group generators needed to commit to instruction + /// lookup polynomials using Hyrax, given the maximum trace length. + pub fn num_generators(max_trace_length: usize) -> usize { + let dim_read_num_vars = (max_trace_length * (C + Self::NUM_MEMORIES)).log_2(); + let final_num_vars = (M * Self::NUM_MEMORIES).log_2(); + let E_flag_num_vars = + (max_trace_length * (Self::NUM_MEMORIES + Self::NUM_INSTRUCTIONS)).log_2(); + + let max_num_vars = max([dim_read_num_vars, final_num_vars, E_flag_num_vars]).unwrap(); + matrix_dimensions(max_num_vars).1.pow2() + } + /// Maps an index [0, NUM_MEMORIES) -> [0, NUM_SUBTABLES) fn memory_to_subtable_index(i: usize) -> usize { i / C diff --git a/jolt-core/src/jolt/vm/mod.rs b/jolt-core/src/jolt/vm/mod.rs index 0342a5367..9045c034e 100644 --- a/jolt-core/src/jolt/vm/mod.rs +++ b/jolt-core/src/jolt/vm/mod.rs @@ -2,17 +2,19 @@ use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_std::log2; use circom_scotia::r1cs; +use itertools::max; use merlin::Transcript; use rayon::prelude::*; use std::any::TypeId; use strum::{EnumCount, IntoEnumIterator}; -use crate::{jolt::{ +use crate::jolt::{ instruction::{JoltInstruction, Opcode}, subtable::LassoSubtable, vm::timestamp_range_check::TimestampValidityProof, -}, poly::{hyrax::HyraxGenerators, pedersen::PedersenInit}}; +}; use crate::lasso::memory_checking::{MemoryCheckingProver, MemoryCheckingVerifier}; +use crate::poly::pedersen::PedersenGenerators; use crate::poly::structured_poly::BatchablePolynomials; use crate::r1cs::snark::R1CSProof; use crate::utils::errors::ProofVerifyError; @@ -36,7 +38,7 @@ where bytecode: BytecodeProof, read_write_memory: ReadWriteMemoryProof, instruction_lookups: InstructionLookupsProof, - r1cs: R1CSProof + r1cs: R1CSProof, } pub struct JoltPolynomials @@ -59,6 +61,40 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize type InstructionSet: JoltInstruction + Opcode + IntoEnumIterator + EnumCount; type Subtables: LassoSubtable + IntoEnumIterator + EnumCount + From + Into; + #[tracing::instrument(skip_all, name = "Jolt::preprocess")] + fn preprocess( + max_bytecode_size: usize, + max_memory_address: usize, + max_trace_length: usize, + ) -> PedersenGenerators { + // TODO(moodlezoup): move more stuff into preprocessing + + let num_bytecode_generators = + BytecodePolynomials::::num_generators(max_bytecode_size, max_trace_length); + let num_read_write_memory_generators = + ReadWriteMemory::::num_generators(max_memory_address, max_trace_length); + let timestamp_range_check_generators = + TimestampValidityProof::::num_generators(max_trace_length); + let num_instruction_lookup_generators = InstructionLookups::< + F, + G, + Self::InstructionSet, + Self::Subtables, + C, + M, + >::num_generators(max_trace_length); + + let max_num_generators = max([ + num_bytecode_generators, + num_read_write_memory_generators, + timestamp_range_check_generators, + num_instruction_lookup_generators, + ]) + .unwrap(); + + PedersenGenerators::new(max_num_generators, b"Jolt v1 Hyrax generators") + } + #[tracing::instrument(skip_all, name = "Jolt::prove")] fn prove( bytecode: Vec, @@ -66,42 +102,53 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize memory_trace: Vec<[MemoryOp; MEMORY_OPS_PER_INSTRUCTION]>, instructions: Vec, circuit_flags: Vec, + generators: PedersenGenerators, ) -> (JoltProof, JoltCommitments) { let mut transcript = Transcript::new(b"Jolt transcript"); let bytecode_rows: Vec = bytecode.iter().map(ELFRow::from).collect(); - let (bytecode_proof, bytecode_polynomials, bytecode_commitment) = - Self::prove_bytecode(bytecode_rows.clone(), bytecode_trace.clone(), &mut transcript); - + let (bytecode_proof, bytecode_polynomials, bytecode_commitment) = Self::prove_bytecode( + bytecode_rows.clone(), + bytecode_trace.clone(), + &generators, + &mut transcript, + ); // - prove_r1cs() memory_trace R1CS is not 2-padded // - prove_memory() memory_trace is 2-padded let mut padded_memory_trace = memory_trace.clone(); - padded_memory_trace.resize(memory_trace.len().next_power_of_two(), std::array::from_fn(|_| MemoryOp::no_op())); + padded_memory_trace.resize( + memory_trace.len().next_power_of_two(), + std::array::from_fn(|_| MemoryOp::no_op()), + ); + + let (memory_proof, memory_polynomials, memory_commitment) = Self::prove_memory( + bytecode.clone(), + padded_memory_trace, + &generators, + &mut transcript, + ); - let (memory_proof, memory_polynomials, memory_commitment) = - Self::prove_memory(bytecode.clone(), padded_memory_trace, &mut transcript); - let ( instruction_lookups_proof, instruction_lookups_polynomials, instruction_lookups_commitment, - ) = Self::prove_instruction_lookups(instructions.clone(), &mut transcript); - + ) = Self::prove_instruction_lookups(instructions.clone(), &generators, &mut transcript); let r1cs_proof = Self::prove_r1cs( - instructions, - bytecode_rows, - bytecode_trace, - bytecode, + instructions, + bytecode_rows, + bytecode_trace, + bytecode, memory_trace.into_iter().flatten().collect(), - circuit_flags, - &mut transcript); + circuit_flags, + &mut transcript, + ); let jolt_proof = JoltProof { bytecode: bytecode_proof, read_write_memory: memory_proof, instruction_lookups: instruction_lookups_proof, - r1cs: r1cs_proof + r1cs: r1cs_proof, }; let _jolt_polynomials = JoltPolynomials { bytecode: bytecode_polynomials, @@ -132,7 +179,10 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize commitments.instruction_lookups, &mut transcript, )?; - proof.r1cs.verify().map_err(|e| ProofVerifyError::SpartanError(e.to_string()))?; + proof + .r1cs + .verify() + .map_err(|e| ProofVerifyError::SpartanError(e.to_string()))?; Ok(()) } @@ -140,6 +190,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize #[tracing::instrument(skip_all, name = "Jolt::prove_instruction_lookups")] fn prove_instruction_lookups( ops: Vec, + generators: &PedersenGenerators, transcript: &mut Transcript, ) -> ( InstructionLookupsProof, @@ -148,7 +199,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize ) { let instruction_lookups = InstructionLookups::::new(ops); - instruction_lookups.prove_lookups(transcript) + instruction_lookups.prove_lookups(generators, transcript) } fn verify_instruction_lookups( @@ -165,6 +216,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize fn prove_bytecode( bytecode_rows: Vec, trace: Vec, + generators: &PedersenGenerators, transcript: &mut Transcript, ) -> ( BytecodeProof, @@ -173,8 +225,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize ) { let polys: BytecodePolynomials = BytecodePolynomials::new(bytecode_rows, trace); let batched_polys = polys.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(BytecodePolynomials::::max_generator_size(&batched_polys), b"LassoV1"); - let commitment = BytecodePolynomials::commit(&batched_polys, &initializer); + let commitment = BytecodePolynomials::commit(&batched_polys, &generators); let proof = polys.prove_memory_checking(&polys, &batched_polys, transcript); (proof, polys, commitment) @@ -192,6 +243,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize fn prove_memory( bytecode: Vec, memory_trace: Vec<[MemoryOp; MEMORY_OPS_PER_INSTRUCTION]>, + generators: &PedersenGenerators, transcript: &mut Transcript, ) -> ( ReadWriteMemoryProof, @@ -200,8 +252,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize ) { let (memory, read_timestamps) = ReadWriteMemory::new(bytecode, memory_trace, transcript); let batched_polys = memory.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(ReadWriteMemory::::max_generator_size(&batched_polys), b"LassoV1"); - let commitment: MemoryCommitment = ReadWriteMemory::commit(&batched_polys, &initializer); + let commitment: MemoryCommitment = ReadWriteMemory::commit(&batched_polys, &generators); let memory_checking_proof = memory.prove_memory_checking(&memory, &batched_polys, transcript); @@ -211,6 +262,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize &memory, &batched_polys, &commitment, + &generators, transcript, ); @@ -323,10 +375,10 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize assert_eq!(lookup_outputs.len(), TRACE_LEN); assert_eq!(circuit_flags.len(), TRACE_LEN * N_FLAGS); - // let padded_trace_len = next power of 2 from trace_eln + // let padded_trace_len = next power of 2 from trace_eln let PADDED_TRACE_LEN = TRACE_LEN.next_power_of_two(); - // pad each of the above vectors to be of length PADDED_TRACE_LEN * their multiple + // pad each of the above vectors to be of length PADDED_TRACE_LEN * their multiple prog_a_rw.resize(PADDED_TRACE_LEN, Default::default()); prog_v_rw.resize(PADDED_TRACE_LEN * 6, Default::default()); memreg_a_rw.resize(PADDED_TRACE_LEN * 7, Default::default()); @@ -338,7 +390,10 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize lookup_outputs.resize(PADDED_TRACE_LEN, Default::default()); let mut circuit_flags_padded = circuit_flags.clone(); - circuit_flags_padded.extend(vec![F::from(0_u64); PADDED_TRACE_LEN * N_FLAGS - circuit_flags.len()]); + circuit_flags_padded.extend(vec![ + F::from(0_u64); + PADDED_TRACE_LEN * N_FLAGS - circuit_flags.len() + ]); // circuit_flags.resize(PADDED_TRACE_LEN * N_FLAGS, Default::default()); let inputs = vec![ diff --git a/jolt-core/src/jolt/vm/read_write_memory.rs b/jolt-core/src/jolt/vm/read_write_memory.rs index 2057fa580..0eb129f43 100644 --- a/jolt-core/src/jolt/vm/read_write_memory.rs +++ b/jolt-core/src/jolt/vm/read_write_memory.rs @@ -1,21 +1,25 @@ -use std::cmp::max; -use std::marker::PhantomData; - use ark_ec::CurveGroup; use ark_ff::PrimeField; use merlin::Transcript; use rand::rngs::StdRng; use rand_core::RngCore; use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use std::cmp::max; #[cfg(test)] use std::collections::HashSet; +use std::marker::PhantomData; use crate::{ lasso::memory_checking::{ MemoryCheckingProof, MemoryCheckingProver, MemoryCheckingVerifier, MultisetHashes, }, poly::{ - dense_mlpoly::DensePolynomial, eq_poly::EqPolynomial, hyrax::HyraxGenerators, identity_poly::IdentityPolynomial, pedersen::PedersenInit, structured_poly::{BatchablePolynomials, StructuredOpeningProof} + dense_mlpoly::DensePolynomial, + eq_poly::EqPolynomial, + hyrax::matrix_dimensions, + identity_poly::IdentityPolynomial, + pedersen::PedersenGenerators, + structured_poly::{BatchablePolynomials, StructuredOpeningProof}, }, subprotocols::batched_commitment::{ BatchedPolynomialCommitment, BatchedPolynomialOpeningProof, @@ -471,6 +475,17 @@ impl> ReadWriteMemory { to_f_vec(&t_read), ] } + + /// Computes the maximum number of group generators needed to commit to read-write + /// memory polynomials using Hyrax, given the maximum memory address and maximum trace length. + pub fn num_generators(max_memory_address: usize, max_trace_length: usize) -> usize { + // { rs1, rs2, rd, ram_byte_1, ram_byte_2, ram_byte_3, ram_byte_4 } x { a_read, a_write, v_read, v_write, t_read_write } + let read_write_num_vars = (max_trace_length * MEMORY_OPS_PER_INSTRUCTION * 5).log_2(); + // v_init, v_final, t_final + let init_final_num_vars = (max_memory_address * 3).log_2(); + let max_num_vars = std::cmp::max(read_write_num_vars, init_final_num_vars); + matrix_dimensions(max_num_vars).1.pow2() + } } pub struct BatchedMemoryPolynomials { @@ -520,25 +535,22 @@ where } #[tracing::instrument(skip_all, name = "ReadWriteMemory::commit")] - fn commit(batched_polys: &Self::BatchedPolynomials, initializer: &PedersenInit) -> Self::Commitment { + fn commit( + batched_polys: &Self::BatchedPolynomials, + pedersen_generators: &PedersenGenerators, + ) -> Self::Commitment { let read_write_commitments = batched_polys .batched_read_write - .combined_commit(initializer); + .combined_commit(pedersen_generators); let init_final_commitments = batched_polys .batched_init_final - .combined_commit(initializer); + .combined_commit(pedersen_generators); Self::Commitment { read_write_commitments, init_final_commitments, } } - - fn max_generator_size(batched_polys: &Self::BatchedPolynomials) -> usize { - let read_write_num_vars = batched_polys.batched_read_write.get_num_vars(); - let init_final_num_vars = batched_polys.batched_init_final.get_num_vars(); - std::cmp::max(read_write_num_vars, init_final_num_vars) - } } pub struct MemoryReadWriteOpenings @@ -910,8 +922,8 @@ mod tests { let (rw_memory, _): (ReadWriteMemory, _) = ReadWriteMemory::new(bytecode, memory_trace, &mut transcript); let batched_polys = rw_memory.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(18, b"test"); - let commitments = ReadWriteMemory::commit(&batched_polys, &initializer); + let generators = PedersenGenerators::new(1 << 10, b"test"); + let commitments = ReadWriteMemory::commit(&batched_polys, &generators); let proof = rw_memory.prove_memory_checking(&rw_memory, &batched_polys, &mut transcript); diff --git a/jolt-core/src/jolt/vm/rv32i_vm.rs b/jolt-core/src/jolt/vm/rv32i_vm.rs index 8ac4252ff..731a9fe1e 100644 --- a/jolt-core/src/jolt/vm/rv32i_vm.rs +++ b/jolt-core/src/jolt/vm/rv32i_vm.rs @@ -143,19 +143,19 @@ where mod tests { use ark_curve25519::{EdwardsProjective, Fr}; use common::constants::MEMORY_OPS_PER_INSTRUCTION; + use common::{path::JoltPaths, serializable::Serializable, ELFInstruction}; use itertools::Itertools; use merlin::Transcript; use rand_core::SeedableRng; use std::collections::HashSet; - use common::{path::JoltPaths, serializable::Serializable, ELFInstruction}; use crate::jolt::instruction::{add::ADDInstruction, JoltInstruction}; use crate::jolt::trace::{rv::RVTraceRow, JoltProvableTrace}; use crate::jolt::vm::bytecode::ELFRow; - use crate::jolt::vm::MemoryOp; use crate::jolt::vm::rv32i_vm::{Jolt, RV32IJoltVM, C, M, RV32I}; - use strum::{EnumCount, IntoEnumIterator}; + use crate::jolt::vm::MemoryOp; use std::sync::Mutex; + use strum::{EnumCount, IntoEnumIterator}; // If multiple tests try to read the same trace artifacts simultaneously, they will fail lazy_static::lazy_static! { @@ -173,9 +173,12 @@ mod tests { let mut prover_transcript = Transcript::new(b"example"); + let generators = RV32IJoltVM::preprocess(1 << 20, 1 << 20, 1 << 22); + let (proof, _, commitment) = >::prove_instruction_lookups( ops, + &generators, &mut prover_transcript, ); let mut verifier_transcript = Transcript::new(b"example"); @@ -293,17 +296,17 @@ mod tests { .collect(); let instructions_r1cs: Vec = converted_trace - .clone() - .into_iter() - .flat_map(|row| { - let instructions = row.to_jolt_instructions(); - if instructions.is_empty() { - vec![ADDInstruction::<32>(0_u64, 0_u64).into()] - } else { - instructions - } - }) - .collect(); + .clone() + .into_iter() + .flat_map(|row| { + let instructions = row.to_jolt_instructions(); + if instructions.is_empty() { + vec![ADDInstruction::<32>(0_u64, 0_u64).into()] + } else { + instructions + } + }) + .collect(); let memory_trace: Vec<[MemoryOp; MEMORY_OPS_PER_INSTRUCTION]> = converted_trace .clone() @@ -315,12 +318,14 @@ mod tests { .flat_map(|row| row.to_circuit_flags::()) .collect::>(); + let generators = RV32IJoltVM::preprocess(1 << 20, 1 << 20, 1 << 20); let (proof, commitments) = >::prove( - bytecode, - bytecode_trace, - memory_trace, - instructions_r1cs, - circuit_flags + bytecode, + bytecode_trace, + memory_trace, + instructions_r1cs, + circuit_flags, + generators, ); let verify_result = RV32IJoltVM::verify(proof, commitments); assert!(verify_result.is_ok()); @@ -350,7 +355,7 @@ mod tests { .map(|row| row.to_bytecode_trace()) .collect(); - // R1CS expects a single lookup instruction per + // R1CS expects a single lookup instruction per let instructions_r1cs: Vec = converted_trace .clone() .into_iter() @@ -436,14 +441,17 @@ mod tests { .flat_map(|row| row.to_circuit_flags::()) .collect::>(); - let (jolt_proof, jolt_commitments) = >::prove( - bytecode, - bytecode_trace, - memory_trace, - instructions_r1cs, - circuit_flags, - ); + let generators = RV32IJoltVM::preprocess(1 << 20, 1 << 20, 1 << 20); + let (jolt_proof, jolt_commitments) = + >::prove( + bytecode, + bytecode_trace, + memory_trace, + instructions_r1cs, + circuit_flags, + generators, + ); assert!(RV32IJoltVM::verify(jolt_proof, jolt_commitments).is_ok()); } -} \ No newline at end of file +} diff --git a/jolt-core/src/jolt/vm/timestamp_range_check.rs b/jolt-core/src/jolt/vm/timestamp_range_check.rs index 066fc167f..2e7a8c6a8 100644 --- a/jolt-core/src/jolt/vm/timestamp_range_check.rs +++ b/jolt-core/src/jolt/vm/timestamp_range_check.rs @@ -14,7 +14,12 @@ use crate::{ MemoryCheckingProof, MemoryCheckingProver, MemoryCheckingVerifier, MultisetHashes, }, poly::{ - dense_mlpoly::DensePolynomial, eq_poly::EqPolynomial, hyrax::HyraxGenerators, identity_poly::IdentityPolynomial, pedersen::PedersenInit, structured_poly::{BatchablePolynomials, StructuredOpeningProof} + dense_mlpoly::DensePolynomial, + eq_poly::EqPolynomial, + hyrax::matrix_dimensions, + identity_poly::IdentityPolynomial, + pedersen::PedersenGenerators, + structured_poly::{BatchablePolynomials, StructuredOpeningProof}, }, subprotocols::{ batched_commitment::{BatchedPolynomialCommitment, BatchedPolynomialOpeningProof}, @@ -22,9 +27,7 @@ use crate::{ BatchedGrandProductArgument, BatchedGrandProductCircuit, GrandProductCircuit, }, }, - utils::{ - errors::ProofVerifyError, math::Math, mul_0_1_optimized, transcript::ProofTranscript - }, + utils::{errors::ProofVerifyError, math::Math, mul_0_1_optimized, transcript::ProofTranscript}, }; use super::read_write_memory::{ @@ -188,13 +191,11 @@ where } #[tracing::instrument(skip_all, name = "RangeCheckPolynomials::commit")] - fn commit(batched_polys: &Self::BatchedPolynomials, initializer: &PedersenInit) -> Self::Commitment { - batched_polys.combined_commit(initializer) - } - - fn max_generator_size(batched_polys: &Self::BatchedPolynomials) -> usize { - let batch_num_vars = batched_polys.get_num_vars(); - batch_num_vars + fn commit( + batched_polys: &Self::BatchedPolynomials, + pedersen_generators: &PedersenGenerators, + ) -> Self::Commitment { + batched_polys.combined_commit(pedersen_generators) } } @@ -588,13 +589,14 @@ where memory_polynomials: &ReadWriteMemory, batched_memory_polynomials: &BatchedMemoryPolynomials, memory_commitment: &MemoryCommitment, + generators: &PedersenGenerators, transcript: &mut Transcript, ) -> Self { let range_check_polys: RangeCheckPolynomials = RangeCheckPolynomials::new(read_timestamps); let batched_range_check_polys = range_check_polys.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(RangeCheckPolynomials::::max_generator_size(&batched_range_check_polys), b"LassoV1"); - let range_check_commitment = RangeCheckPolynomials::commit(&batched_range_check_polys, &initializer); + let range_check_commitment = + RangeCheckPolynomials::commit(&batched_range_check_polys, &generators); let (batched_grand_product, multiset_hashes, r_grand_product) = TimestampValidityProof::prove_grand_products(&range_check_polys, transcript); @@ -810,6 +812,13 @@ where Ok(()) } + /// Computes the maximum number of group generators needed to commit to timestamp + /// range-check polynomials using Hyrax, given the maximum trace length. + pub fn num_generators(max_trace_length: usize) -> usize { + let batch_num_vars = (max_trace_length * MEMORY_OPS_PER_INSTRUCTION * 4).log_2(); + matrix_dimensions(batch_num_vars).1.pow2() + } + fn protocol_name() -> &'static [u8] { b"Timestamp validity proof memory checking" } @@ -841,14 +850,18 @@ mod tests { let (rw_memory, read_timestamps): (ReadWriteMemory, _) = ReadWriteMemory::new(bytecode, memory_trace, &mut transcript); let batched_polys = rw_memory.batch(); - let initializer: PedersenInit = HyraxGenerators::new_initializer(ReadWriteMemory::::max_generator_size(&batched_polys), b"LassoV1"); - let commitments = ReadWriteMemory::commit(&batched_polys, &initializer); + let generators = PedersenGenerators::new( + 1 << 10, + b"Test generators", + ); + let commitments = ReadWriteMemory::commit(&batched_polys, &generators); let mut timestamp_validity_proof = TimestampValidityProof::prove( read_timestamps, &rw_memory, &batched_polys, &commitments, + &generators, &mut transcript, ); diff --git a/jolt-core/src/lasso/memory_checking.rs b/jolt-core/src/lasso/memory_checking.rs index 5082c7294..b756dee23 100644 --- a/jolt-core/src/lasso/memory_checking.rs +++ b/jolt-core/src/lasso/memory_checking.rs @@ -460,7 +460,7 @@ where mod tests { use std::collections::HashSet; - use crate::poly::pedersen::PedersenInit; + use crate::poly::pedersen::PedersenGenerators; use super::*; use ark_curve25519::{EdwardsProjective, Fr}; @@ -510,10 +510,7 @@ mod tests { fn batch(&self) -> Self::BatchedPolynomials { unimplemented!() } - fn commit(_batched_polys: &Self::BatchedPolynomials, initializer: &PedersenInit) -> Self::Commitment { - unimplemented!() - } - fn max_generator_size(_batched_polys: &Self::BatchedPolynomials) -> usize { + fn commit(_batched_polys: &Self::BatchedPolynomials, generators: &PedersenGenerators) -> Self::Commitment { unimplemented!() } } @@ -713,10 +710,7 @@ mod tests { fn batch(&self) -> Self::BatchedPolynomials { unimplemented!() } - fn commit(_batched_polys: &Self::BatchedPolynomials, _generator: &PedersenInit) -> Self::Commitment { - unimplemented!() - } - fn max_generator_size(_batched_polys: &Self::BatchedPolynomials) -> usize { + fn commit(_batched_polys: &Self::BatchedPolynomials, generators: &PedersenGenerators) -> Self::Commitment { unimplemented!() } } @@ -962,10 +956,7 @@ mod tests { fn batch(&self) -> Self::BatchedPolynomials { unimplemented!() } - fn commit(_batched_polys: &Self::BatchedPolynomials, _initializer: &PedersenInit) -> Self::Commitment { - unimplemented!() - } - fn max_generator_size(_batched_polys: &Self::BatchedPolynomials) -> usize { + fn commit(_batched_polys: &Self::BatchedPolynomials, generators: &PedersenGenerators) -> Self::Commitment { unimplemented!() } } diff --git a/jolt-core/src/lasso/surge.rs b/jolt-core/src/lasso/surge.rs index af9ef1b86..6578015ea 100644 --- a/jolt-core/src/lasso/surge.rs +++ b/jolt-core/src/lasso/surge.rs @@ -1,25 +1,25 @@ -use std::marker::{PhantomData, Sync}; - use ark_ec::CurveGroup; use ark_ff::PrimeField; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use merlin::Transcript; use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use std::marker::{PhantomData, Sync}; use crate::{ jolt::instruction::JoltInstruction, lasso::memory_checking::{MemoryCheckingProof, MemoryCheckingProver, MemoryCheckingVerifier}, poly::{ - dense_mlpoly::DensePolynomial, eq_poly::EqPolynomial, hyrax::HyraxGenerators, identity_poly::IdentityPolynomial, pedersen::PedersenInit, structured_poly::{BatchablePolynomials, StructuredOpeningProof} + dense_mlpoly::DensePolynomial, + eq_poly::EqPolynomial, + hyrax::matrix_dimensions, + identity_poly::IdentityPolynomial, + pedersen::PedersenGenerators, + structured_poly::{BatchablePolynomials, StructuredOpeningProof}, }, subprotocols::{ batched_commitment::{BatchedPolynomialCommitment, BatchedPolynomialOpeningProof}, sumcheck::SumcheckInstanceProof, }, - utils::{ - errors::ProofVerifyError, math::Math, mul_0_1_optimized, - transcript::ProofTranscript, - }, + utils::{errors::ProofVerifyError, math::Math, mul_0_1_optimized, transcript::ProofTranscript}, }; pub struct SurgePolys> { @@ -70,16 +70,17 @@ where } #[tracing::instrument(skip_all, name = "SurgePolys::commit")] - fn commit(batched_polys: &Self::BatchedPolynomials, initializer: &PedersenInit) -> Self::Commitment { + fn commit( + batched_polys: &Self::BatchedPolynomials, + pedersen_generators: &PedersenGenerators, + ) -> Self::Commitment { let dim_read_commitment = batched_polys .batched_dim_read - .combined_commit(initializer); + .combined_commit(pedersen_generators); let final_commitment = batched_polys .batched_final - .combined_commit(initializer); - let E_commitment = batched_polys - .batched_E - .combined_commit(initializer); + .combined_commit(pedersen_generators); + let E_commitment = batched_polys.batched_E.combined_commit(pedersen_generators); Self::Commitment { dim_read_commitment, @@ -87,14 +88,6 @@ where E_commitment, } } - - fn max_generator_size(batched_polys: &Self::BatchedPolynomials) -> usize { - let dim_read_num_vars = batched_polys.batched_dim_read.get_num_vars(); - let final_num_vars = batched_polys.batched_final.get_num_vars(); - let E_num_vars = batched_polys.batched_E.get_num_vars(); - - std::cmp::max(std::cmp::max(dim_read_num_vars, final_num_vars), E_num_vars) - } } type PrimarySumcheckOpenings = Vec; @@ -520,6 +513,7 @@ where } #[tracing::instrument(skip_all, name = "Surge::new")] + // TODO(moodlezoup): we can turn M back into a const generic pub fn new(ops: Vec, M: usize) -> Self { let num_lookups = ops.len().next_power_of_two(); let instruction = Instruction::default(); @@ -555,6 +549,18 @@ where b"Surge" } + /// Computes the maximum number of group generators needed to commit to Surge polynomials + /// using Hyrax, given `M` and the maximum number of lookups. + pub fn num_generators(M: usize, max_num_lookups: usize) -> usize { + let dim_read_num_vars = (max_num_lookups * 2 * C).log_2(); + let final_num_vars = (M * C).log_2(); + let E_num_vars = (max_num_lookups * Self::num_memories()).log_2(); + + let max_num_vars = + std::cmp::max(std::cmp::max(dim_read_num_vars, final_num_vars), E_num_vars); + matrix_dimensions(max_num_vars).1.pow2() + } + #[tracing::instrument(skip_all, name = "Surge::prove")] pub fn prove(&self, transcript: &mut Transcript) -> SurgeProof { >::append_protocol_name(transcript, Self::protocol_name()); @@ -562,8 +568,9 @@ where // TODO(sragss): Move upstream let polynomials = self.construct_polys(); let batched_polys = polynomials.batch(); - let initializer = HyraxGenerators::new_initializer(SurgePolys::::max_generator_size(&batched_polys), b"LassoV1"); - let commitment = SurgePolys::commit(&batched_polys, &initializer); + let pedersen_generators = + PedersenGenerators::new(Self::num_generators(self.M, self.num_lookups), b"LassoV1"); + let commitment = SurgePolys::commit(&batched_polys, &pedersen_generators); let num_rounds = self.num_lookups.log_2(); let instruction = Instruction::default(); @@ -575,7 +582,8 @@ where b"primary_sumcheck", num_rounds, ); - let eq: DensePolynomial = DensePolynomial::new(EqPolynomial::new(r_primary_sumcheck.to_vec()).evals()); + let eq: DensePolynomial = + DensePolynomial::new(EqPolynomial::new(r_primary_sumcheck.to_vec()).evals()); let sumcheck_claim: F = Self::compute_primary_sumcheck_claim(&polynomials, &eq, self.M); >::append_scalar( diff --git a/jolt-core/src/poly/dense_mlpoly.rs b/jolt-core/src/poly/dense_mlpoly.rs index 664445f4c..f3df242ed 100644 --- a/jolt-core/src/poly/dense_mlpoly.rs +++ b/jolt-core/src/poly/dense_mlpoly.rs @@ -3,7 +3,7 @@ use crate::poly::eq_poly::EqPolynomial; use crate::utils::{self, compute_dotproduct, compute_dotproduct_low_optimized, mul_0_1_optimized}; use super::hyrax::{HyraxCommitment, HyraxGenerators}; -use super::pedersen::PedersenInit; +use super::pedersen::PedersenGenerators; use crate::subprotocols::batched_commitment::BatchedPolynomialCommitment; use crate::utils::math::Math; use ark_ec::CurveGroup; @@ -226,11 +226,14 @@ impl DensePolynomial { DensePolynomial::new(Z) } - pub fn combined_commit(&self, initializer: &PedersenInit) -> BatchedPolynomialCommitment + pub fn combined_commit( + &self, + pedersen_generators: &PedersenGenerators, + ) -> BatchedPolynomialCommitment where G: CurveGroup, { - let generators = HyraxGenerators::new(self.get_num_vars(), initializer); + let generators = HyraxGenerators::new(self.get_num_vars(), pedersen_generators); let joint_commitment = HyraxCommitment::commit(&self, &generators); BatchedPolynomialCommitment { generators, @@ -316,8 +319,9 @@ pub mod bench { log_size: usize, ) -> (HyraxGenerators, DensePolynomial) { let evals: Vec = gen_random_point::(1 << log_size); - let initializer = HyraxGenerators::new_initializer(1 << log_size, b"test_gens"); - let gens = HyraxGenerators::new(log_size, &initializer); + + let pedersen_generators = PedersenGenerators::new(1 << log_size, b"test_gens"); + let gens = HyraxGenerators::new(log_size, &pedersen_generators); let poly = DensePolynomial::new(evals.clone()); (gens, poly) } diff --git a/jolt-core/src/poly/hyrax.rs b/jolt-core/src/poly/hyrax.rs index 3d69eeb1a..6f92177d7 100644 --- a/jolt-core/src/poly/hyrax.rs +++ b/jolt-core/src/poly/hyrax.rs @@ -1,7 +1,6 @@ -use crate::poly::eq_poly::EqPolynomial; - use super::dense_mlpoly::DensePolynomial; -use super::pedersen::{PedersenCommitment, PedersenGenerators, PedersenInit}; +use super::pedersen::{PedersenCommitment, PedersenGenerators}; +use crate::poly::eq_poly::EqPolynomial; use crate::utils::errors::ProofVerifyError; use crate::utils::math::Math; use crate::utils::transcript::{AppendToTranscript, ProofTranscript}; @@ -28,16 +27,11 @@ pub struct HyraxGenerators { impl HyraxGenerators { // the number of variables in the multilinear polynomial - pub fn new(num_vars: usize, initializer: &PedersenInit) -> Self { + pub fn new(num_vars: usize, pedersen_generators: &PedersenGenerators) -> Self { let (_left, right) = matrix_dimensions(num_vars); - let gens = initializer.sample(right.pow2()); + let gens = pedersen_generators.clone_n(right.pow2()); HyraxGenerators { gens } } - - pub fn new_initializer(num_vars: usize, label: &[u8]) -> PedersenInit { - let max_len = matrix_dimensions(num_vars).1.pow2(); - PedersenInit::new(max_len, label) - } } pub struct HyraxCommitment { @@ -210,8 +204,8 @@ mod tests { let eval = poly.evaluate(&r); assert_eq!(eval, G::ScalarField::from(28u64)); - let initializer = PedersenInit::new(1 << 8, b"test-two"); - let gens = HyraxGenerators::::new(poly.get_num_vars(), &initializer); + let pedersen_generators = PedersenGenerators::new(1 << 8, b"test-two"); + let gens = HyraxGenerators::::new(poly.get_num_vars(), &pedersen_generators); let poly_commitment = HyraxCommitment::commit(&poly, &gens); let mut prover_transcript = Transcript::new(b"example"); diff --git a/jolt-core/src/poly/pedersen.rs b/jolt-core/src/poly/pedersen.rs index ce9777a3c..19c26e693 100644 --- a/jolt-core/src/poly/pedersen.rs +++ b/jolt-core/src/poly/pedersen.rs @@ -11,12 +11,13 @@ use ark_ec::VariableBaseMSM; #[cfg(not(feature = "ark-msm"))] use crate::msm::VariableBaseMSM; -pub struct PedersenInit { - pub generators: Vec +#[derive(Clone)] +pub struct PedersenGenerators { + pub generators: Vec, } -impl PedersenInit { - #[tracing::instrument] +impl PedersenGenerators { + #[tracing::instrument(skip_all, name = "PedersenGenerators::new")] pub fn new(len: usize, label: &[u8]) -> Self { let mut shake = Shake256::default(); shake.input(label); @@ -30,26 +31,25 @@ impl PedersenInit { let mut rng = ChaCha20Rng::from_seed(seed); let mut generators: Vec = Vec::new(); - for _ in 0..len{ + for _ in 0..len { generators.push(G::rand(&mut rng)); } - Self { - generators, - } - - } - - pub fn sample(&self, n: usize) -> PedersenGenerators { - assert!(self.generators.len() >= n, "Insufficient number of generators for sampling: required {}, available {}", n, self.generators.len()); - let sample = self.generators[0..n].into(); - PedersenGenerators { generators: sample } + Self { generators } } -} -#[derive(Clone)] -pub struct PedersenGenerators { - pub generators: Vec, + pub fn clone_n(&self, n: usize) -> PedersenGenerators { + assert!( + self.generators.len() >= n, + "Insufficient number of generators for clone_n: required {}, available {}", + n, + self.generators.len() + ); + let slice = &self.generators[..n]; + PedersenGenerators { + generators: slice.into(), + } + } } pub trait PedersenCommitment: Sized { diff --git a/jolt-core/src/poly/structured_poly.rs b/jolt-core/src/poly/structured_poly.rs index 7c9885c88..e58955c25 100644 --- a/jolt-core/src/poly/structured_poly.rs +++ b/jolt-core/src/poly/structured_poly.rs @@ -2,17 +2,16 @@ use ark_ec::CurveGroup; use ark_ff::PrimeField; use merlin::Transcript; +use super::pedersen::PedersenGenerators; use crate::{ subprotocols::batched_commitment::BatchedPolynomialOpeningProof, - utils::{errors::ProofVerifyError}, + utils::errors::ProofVerifyError, }; -use super::{hyrax::HyraxGenerators, pedersen::PedersenInit}; - /// Encapsulates the pattern of a collection of related polynomials (e.g. those used to /// prove instruction lookups in Jolt) that can be "batched" for more efficient /// commitments/openings. -pub trait BatchablePolynomials { +pub trait BatchablePolynomials { /// The batched form of these polynomials. type BatchedPolynomials; /// The batched commitment to these polynomials. @@ -22,9 +21,10 @@ pub trait BatchablePolynomials { /// uses `DensePolynomial::merge` to combine polynomials of the same size. fn batch(&self) -> Self::BatchedPolynomials; /// Commits to batched polynomials, typically using `DensePolynomial::combined_commit`. - fn commit(batched_polys: &Self::BatchedPolynomials, initalizer: &PedersenInit) -> Self::Commitment; - - fn max_generator_size(batched_polys: &Self::BatchedPolynomials) -> usize; + fn commit( + batched_polys: &Self::BatchedPolynomials, + generators: &PedersenGenerators, + ) -> Self::Commitment; } /// Encapsulates the pattern of opening a batched polynomial commitment at a single point. From 626d4a10bdb5e696fafd9441593f411d90b45c5a Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Fri, 23 Feb 2024 18:38:41 -0500 Subject: [PATCH 2/5] Point to algebra fork and use optimized F::from_u64 --- Cargo.toml | 11 ++++++++--- jolt-core/Cargo.toml | 6 +++--- jolt-core/src/lib.rs | 1 + jolt-core/src/poly/dense_mlpoly.rs | 10 ++++++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8124552d6..f2c7416fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ authors = [ # authors who contributed to Lasso "Michael Zhu ", "Sam Ragsdale ", - "Noah Citron " + "Noah Citron ", ] edition = "2021" description = "The lookup singularity. Based on Spartan; built on Arkworks." @@ -27,7 +27,7 @@ members = [ "examples/fibonacci", "examples/sha2-ex", "examples/sha3-ex", - "integration-tests", + "integration-tests", ] [profile.release] @@ -38,4 +38,9 @@ lto = "fat" [profile.build-fast] inherits = "release" incremental = true -lto = "off" \ No newline at end of file +lto = "off" + +[patch.crates-io] +ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64" } +ark-ec = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64" } +ark-serialize = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64" } diff --git a/jolt-core/Cargo.toml b/jolt-core/Cargo.toml index 7dfc323ad..eafae4de5 100644 --- a/jolt-core/Cargo.toml +++ b/jolt-core/Cargo.toml @@ -20,9 +20,9 @@ keywords = ["zkSNARKs", "cryptography", "proofs"] [dependencies] ark-curve25519 = "0.4.0" -ark-ec = { version = "0.4.2", default-features = false } -ark-ff = { version = "0.4.2", default-features = false } -ark-serialize = { version = "0.4.2", default-features = false, features = [ +ark-ec = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64", default-features = false } +ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64", default-features = false } +ark-serialize = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64", default-features = false, features = [ "derive", ] } ark-std = { version = "0.4.0", default-features = false } diff --git a/jolt-core/src/lib.rs b/jolt-core/src/lib.rs index bd9ca0cd7..cb2c4b80c 100644 --- a/jolt-core/src/lib.rs +++ b/jolt-core/src/lib.rs @@ -6,6 +6,7 @@ #![allow(incomplete_features)] #![feature(generic_const_exprs)] #![feature(iter_next_chunk)] +#![allow(long_running_const_eval)] pub mod benches; pub mod jolt; diff --git a/jolt-core/src/poly/dense_mlpoly.rs b/jolt-core/src/poly/dense_mlpoly.rs index f3df242ed..3e9f0cee2 100644 --- a/jolt-core/src/poly/dense_mlpoly.rs +++ b/jolt-core/src/poly/dense_mlpoly.rs @@ -241,16 +241,22 @@ impl DensePolynomial { } } + #[tracing::instrument(skip_all, name = "DensePolynomial::from")] pub fn from_usize(Z: &[usize]) -> Self { DensePolynomial::new( (0..Z.len()) - .map(|i| F::from(Z[i] as u64)) + .map(|i| F::from_u64(Z[i] as u64).unwrap()) .collect::>(), ) } + #[tracing::instrument(skip_all, name = "DensePolynomial::from")] pub fn from_u64(Z: &[u64]) -> Self { - DensePolynomial::new((0..Z.len()).map(|i| F::from(Z[i])).collect::>()) + DensePolynomial::new( + (0..Z.len()) + .map(|i| F::from_u64(Z[i]).unwrap()) + .collect::>(), + ) } } From bb306527d02a03291fbba6fccebea73aecd4b67e Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Sat, 24 Feb 2024 13:36:56 -0500 Subject: [PATCH 3/5] Sprinkle in from_u64 in more places --- jolt-core/src/jolt/instruction/srl.rs | 4 +-- jolt-core/src/jolt/instruction/test.rs | 4 ++- jolt-core/src/jolt/subtable/and.rs | 4 +-- jolt-core/src/jolt/subtable/identity.rs | 4 +-- jolt-core/src/jolt/subtable/lt_abs.rs | 2 +- jolt-core/src/jolt/subtable/ltu.rs | 2 +- jolt-core/src/jolt/subtable/or.rs | 4 +-- jolt-core/src/jolt/subtable/sll.rs | 6 ++-- jolt-core/src/jolt/subtable/sra_sign.rs | 10 +++--- jolt-core/src/jolt/subtable/srl.rs | 8 +++-- .../src/jolt/subtable/truncate_overflow.rs | 4 +-- jolt-core/src/jolt/subtable/xor.rs | 4 +-- jolt-core/src/jolt/subtable/zero_lsb.rs | 6 ++-- jolt-core/src/jolt/vm/bytecode.rs | 31 ++++++++++--------- jolt-core/src/jolt/vm/instruction_lookups.rs | 2 +- jolt-core/src/jolt/vm/mod.rs | 24 +++++++++----- jolt-core/src/jolt/vm/read_write_memory.rs | 12 ++++--- .../src/jolt/vm/timestamp_range_check.rs | 7 +++-- jolt-core/src/lasso/surge.rs | 2 +- jolt-core/src/poly/identity_poly.rs | 2 +- jolt-core/src/poly/unipoly.rs | 2 +- jolt-core/src/utils/instruction_utils.rs | 2 +- 22 files changed, 85 insertions(+), 61 deletions(-) diff --git a/jolt-core/src/jolt/instruction/srl.rs b/jolt-core/src/jolt/instruction/srl.rs index 80072884f..ec1a65ee3 100644 --- a/jolt-core/src/jolt/instruction/srl.rs +++ b/jolt-core/src/jolt/instruction/srl.rs @@ -79,8 +79,8 @@ mod test { #[test] fn srl_instruction_e2e() { let mut rng = test_rng(); - const C: usize = 3; - const M: usize = 1 << 22; + const C: usize = 4; + const M: usize = 1 << 16; const WORD_SIZE: usize = 32; for _ in 0..256 { diff --git a/jolt-core/src/jolt/instruction/test.rs b/jolt-core/src/jolt/instruction/test.rs index 6a6427910..82a835664 100644 --- a/jolt-core/src/jolt/instruction/test.rs +++ b/jolt-core/src/jolt/instruction/test.rs @@ -7,6 +7,8 @@ /// 4. Checks that the result equals the expected value, given by the `lookup_output` macro_rules! jolt_instruction_test { ($instr:expr) => { + use ark_ff::PrimeField; + let materialized_subtables: Vec<_> = $instr .subtables::(C) .iter() @@ -23,7 +25,7 @@ macro_rules! jolt_instruction_test { } let actual = $instr.combine_lookups(&subtable_values, C, M); - let expected = Fr::from($instr.lookup_entry()); + let expected = Fr::from_u64($instr.lookup_entry()).unwrap(); assert_eq!(actual, expected, "{:?}", $instr); }; diff --git a/jolt-core/src/jolt/subtable/and.rs b/jolt-core/src/jolt/subtable/and.rs index 0153a489e..fbd489dc0 100644 --- a/jolt-core/src/jolt/subtable/and.rs +++ b/jolt-core/src/jolt/subtable/and.rs @@ -26,7 +26,7 @@ impl LassoSubtable for AndSubtable { // Materialize table entries in order where (x | y) ranges 0..M for idx in 0..M { let (x, y) = split_bits(idx, bits_per_operand); - let row = F::from((x & y) as u64); + let row = F::from_u64((x & y) as u64).unwrap(); entries.push(row); } entries @@ -42,7 +42,7 @@ impl LassoSubtable for AndSubtable { for i in 0..b { let x = x[b - i - 1]; let y = y[b - i - 1]; - result += F::from(1u64 << i) * x * y; + result += F::from_u64(1u64 << i).unwrap() * x * y; } result } diff --git a/jolt-core/src/jolt/subtable/identity.rs b/jolt-core/src/jolt/subtable/identity.rs index e42a8befb..ee039f35e 100644 --- a/jolt-core/src/jolt/subtable/identity.rs +++ b/jolt-core/src/jolt/subtable/identity.rs @@ -18,13 +18,13 @@ impl IdentitySubtable { impl LassoSubtable for IdentitySubtable { fn materialize(&self, M: usize) -> Vec { - (0..M).map(|i| F::from(i as u64)).collect() + (0..M).map(|i| F::from_u64(i as u64).unwrap()).collect() } fn evaluate_mle(&self, point: &[F]) -> F { let mut result = F::zero(); for i in 0..point.len() { - result += F::from(1u64 << i) * point[point.len() - 1 - i]; + result += F::from_u64(1u64 << i).unwrap() * point[point.len() - 1 - i]; } result } diff --git a/jolt-core/src/jolt/subtable/lt_abs.rs b/jolt-core/src/jolt/subtable/lt_abs.rs index 4c76f0cf7..2973280f4 100644 --- a/jolt-core/src/jolt/subtable/lt_abs.rs +++ b/jolt-core/src/jolt/subtable/lt_abs.rs @@ -49,7 +49,7 @@ impl LassoSubtable for LtAbsSubtable { // Skip i=0 for i in 1..b { result += (F::one() - x[i]) * y[i] * eq_term; - eq_term *= F::one() - x[i] - y[i] + F::from(2u64) * x[i] * y[i]; + eq_term *= F::one() - x[i] - y[i] + F::from_u64(2u64).unwrap() * x[i] * y[i]; } result } diff --git a/jolt-core/src/jolt/subtable/ltu.rs b/jolt-core/src/jolt/subtable/ltu.rs index d26628a42..48a239b7a 100644 --- a/jolt-core/src/jolt/subtable/ltu.rs +++ b/jolt-core/src/jolt/subtable/ltu.rs @@ -42,7 +42,7 @@ impl LassoSubtable for LtuSubtable { let mut eq_term = F::one(); for i in 0..b { result += (F::one() - x[i]) * y[i] * eq_term; - eq_term *= F::one() - x[i] - y[i] + F::from(2u64) * x[i] * y[i]; + eq_term *= F::one() - x[i] - y[i] + F::from_u64(2u64).unwrap() * x[i] * y[i]; } result } diff --git a/jolt-core/src/jolt/subtable/or.rs b/jolt-core/src/jolt/subtable/or.rs index 419fb9c38..8bea07730 100644 --- a/jolt-core/src/jolt/subtable/or.rs +++ b/jolt-core/src/jolt/subtable/or.rs @@ -26,7 +26,7 @@ impl LassoSubtable for OrSubtable { // Materialize table entries in order where (x | y) ranges 0..M for idx in 0..M { let (x, y) = split_bits(idx, bits_per_operand); - let row = F::from((x | y) as u64); + let row = F::from_u64((x | y) as u64).unwrap(); entries.push(row); } entries @@ -42,7 +42,7 @@ impl LassoSubtable for OrSubtable { for i in 0..b { let x = x[b - i - 1]; let y = y[b - i - 1]; - result += F::from(1u64 << i) * (x + y - x * y); + result += F::from_u64(1u64 << i).unwrap() * (x + y - x * y); } result } diff --git a/jolt-core/src/jolt/subtable/sll.rs b/jolt-core/src/jolt/subtable/sll.rs index 7606116c2..1af4fd711 100644 --- a/jolt-core/src/jolt/subtable/sll.rs +++ b/jolt-core/src/jolt/subtable/sll.rs @@ -42,7 +42,7 @@ impl LassoSubta .checked_shr(suffix_length as u32) .unwrap_or(0); - entries.push(F::from(row as u64)); + entries.push(F::from_u64(row as u64).unwrap()); } entries } @@ -64,7 +64,7 @@ impl LassoSubta let k_bits = (k as usize) .get_bits(log_WORD_SIZE) .iter() - .map(|bit| F::from(*bit as u64)) + .map(|bit| F::from_u64(*bit as u64).unwrap()) .collect::>(); // big-endian let mut eq_term = F::one(); @@ -84,7 +84,7 @@ impl LassoSubta let shift_x_by_k = (0..m_prime) .enumerate() - .map(|(j, _)| F::from(1_u64 << (j + k)) * x[b - 1 - j]) + .map(|(j, _)| F::from_u64(1_u64 << (j + k)).unwrap() * x[b - 1 - j]) .fold(F::zero(), |acc, val| acc + val); result += eq_term * shift_x_by_k; diff --git a/jolt-core/src/jolt/subtable/sra_sign.rs b/jolt-core/src/jolt/subtable/sra_sign.rs index c15f79c8c..68321e2ca 100644 --- a/jolt-core/src/jolt/subtable/sra_sign.rs +++ b/jolt-core/src/jolt/subtable/sra_sign.rs @@ -31,15 +31,15 @@ impl LassoSubtable for SraSignSubtable for idx in 0..M { let (x, y) = split_bits(idx, operand_chunk_width); - let x_sign = F::from(((x >> sign_bit_index) & 1) as u64); + let x_sign = F::from_u64(((x >> sign_bit_index) & 1) as u64).unwrap(); let row = (0..(y % WORD_SIZE) as u32) .into_iter() .fold(F::zero(), |acc, i: u32| { - acc + F::from(1_u64 << (WORD_SIZE as u32 - 1 - i)) * x_sign + acc + F::from_u64(1_u64 << (WORD_SIZE as u32 - 1 - i)).unwrap() * x_sign }); - entries.push(F::from(row)); + entries.push(row); } entries } @@ -64,7 +64,7 @@ impl LassoSubtable for SraSignSubtable let k_bits = (k as usize) .get_bits(log_WORD_SIZE) .iter() - .map(|bit| F::from(*bit as u64)) + .map(|bit| F::from(*bit)) .collect::>(); // big-endian let mut eq_term = F::one(); @@ -75,7 +75,7 @@ impl LassoSubtable for SraSignSubtable } let x_sign_upper = (0..k).into_iter().fold(F::zero(), |acc, i| { - acc + F::from(1_u64 << (WORD_SIZE - 1 - i)) * x_sign + acc + F::from_u64(1_u64 << (WORD_SIZE - 1 - i)).unwrap() * x_sign }); result += eq_term * x_sign_upper; diff --git a/jolt-core/src/jolt/subtable/srl.rs b/jolt-core/src/jolt/subtable/srl.rs index 2bace711f..08aa36336 100644 --- a/jolt-core/src/jolt/subtable/srl.rs +++ b/jolt-core/src/jolt/subtable/srl.rs @@ -39,7 +39,7 @@ impl LassoSubta .checked_shr((y % WORD_SIZE) as u32) .unwrap_or(0); - entries.push(F::from(row as u64)); + entries.push(F::from_u64(row as u64).unwrap()); } entries } @@ -61,7 +61,7 @@ impl LassoSubta let k_bits = (k as usize) .get_bits(log_WORD_SIZE) .iter() - .map(|bit| F::from(*bit as u64)) + .map(|bit| F::from(*bit)) .collect::>(); // big-endian let mut eq_term = F::one(); @@ -86,7 +86,9 @@ impl LassoSubta let shift_x_by_k = (m..chunk_length) .enumerate() - .map(|(_, j)| F::from(1_u64 << (b * CHUNK_INDEX + j - k)) * x[b - 1 - j]) + .map(|(_, j)| { + F::from_u64(1_u64 << (b * CHUNK_INDEX + j - k)).unwrap() * x[b - 1 - j] + }) .fold(F::zero(), |acc, val: F| acc + val); result += eq_term * shift_x_by_k; diff --git a/jolt-core/src/jolt/subtable/truncate_overflow.rs b/jolt-core/src/jolt/subtable/truncate_overflow.rs index 24eebf7ce..e8cd6aa33 100644 --- a/jolt-core/src/jolt/subtable/truncate_overflow.rs +++ b/jolt-core/src/jolt/subtable/truncate_overflow.rs @@ -31,7 +31,7 @@ impl LassoSubtable let mut entries: Vec = Vec::with_capacity(M); for idx in 0..M { let (_, lower_bits) = split_bits(idx, cutoff); - let row = F::from(lower_bits as u64); + let row = F::from_u64(lower_bits as u64).unwrap(); entries.push(row); } entries @@ -43,7 +43,7 @@ impl LassoSubtable let mut result = F::zero(); for i in 0..cutoff { - result += F::from(1u64 << i) * point[point.len() - 1 - i]; + result += F::from_u64(1u64 << i).unwrap() * point[point.len() - 1 - i]; } result } diff --git a/jolt-core/src/jolt/subtable/xor.rs b/jolt-core/src/jolt/subtable/xor.rs index 1363e6a72..ec51ea963 100644 --- a/jolt-core/src/jolt/subtable/xor.rs +++ b/jolt-core/src/jolt/subtable/xor.rs @@ -26,7 +26,7 @@ impl LassoSubtable for XorSubtable { // Materialize table entries in order where (x | y) ranges 0..M for idx in 0..M { let (x, y) = split_bits(idx, bits_per_operand); - let row = F::from((x ^ y) as u64); + let row = F::from_u64((x ^ y) as u64).unwrap(); entries.push(row); } entries @@ -42,7 +42,7 @@ impl LassoSubtable for XorSubtable { for i in 0..b { let x = x[b - i - 1]; let y = y[b - i - 1]; - result += F::from(1u64 << i) * ((F::one() - x) * y + x * (F::one() - y)); + result += F::from_u64(1u64 << i).unwrap() * ((F::one() - x) * y + x * (F::one() - y)); } result } diff --git a/jolt-core/src/jolt/subtable/zero_lsb.rs b/jolt-core/src/jolt/subtable/zero_lsb.rs index a3a7aaed1..60390b4c0 100644 --- a/jolt-core/src/jolt/subtable/zero_lsb.rs +++ b/jolt-core/src/jolt/subtable/zero_lsb.rs @@ -19,14 +19,16 @@ impl ZeroLSBSubtable { impl LassoSubtable for ZeroLSBSubtable { fn materialize(&self, M: usize) -> Vec { // always set LSB to 0 - (0..M).map(|i| F::from((i - (i % 2)) as u64)).collect() + (0..M) + .map(|i| F::from_u64((i - (i % 2)) as u64).unwrap()) + .collect() } fn evaluate_mle(&self, point: &[F]) -> F { let mut result = F::zero(); // skip LSB for i in 1..point.len() { - result += F::from(1u64 << i) * point[point.len() - 1 - i]; + result += F::from_u64(1u64 << i).unwrap() * point[point.len() - 1 - i]; } result } diff --git a/jolt-core/src/jolt/vm/bytecode.rs b/jolt-core/src/jolt/vm/bytecode.rs index e87521ff7..1be5e6026 100644 --- a/jolt-core/src/jolt/vm/bytecode.rs +++ b/jolt-core/src/jolt/vm/bytecode.rs @@ -179,11 +179,11 @@ impl FiveTuplePoly { let mut imms = Vec::with_capacity(len); for row in elf { - opcodes.push(F::from(row.opcode)); - rds.push(F::from(row.rd)); - rs1s.push(F::from(row.rs1)); - rs2s.push(F::from(row.rs2)); - imms.push(F::from(row.imm)); + opcodes.push(F::from_u64(row.opcode).unwrap()); + rds.push(F::from_u64(row.rd).unwrap()); + rs1s.push(F::from_u64(row.rs1).unwrap()); + rs2s.push(F::from_u64(row.rs2).unwrap()); + imms.push(F::from_u64(row.imm).unwrap()); } // Padding for _ in elf.len()..len { @@ -232,11 +232,11 @@ impl FiveTuplePoly { // let mut circuit_flags = Vec::with_capacity(len * 15); for row in elf { - opcodes.push(F::from(row.opcode)); - rds.push(F::from(row.rd)); - rs1s.push(F::from(row.rs1)); - rs2s.push(F::from(row.rs2)); - imms.push(F::from(row.imm)); + opcodes.push(F::from_u64(row.opcode).unwrap()); + rds.push(F::from_u64(row.rd).unwrap()); + rs1s.push(F::from_u64(row.rs1).unwrap()); + rs2s.push(F::from_u64(row.rs2).unwrap()); + imms.push(F::from_u64(row.imm).unwrap()); // circuit_flags.push(row.circuit_flags_packed::()); } @@ -345,8 +345,11 @@ impl> BytecodePolynomials { } // create a closure to convert usize to F vector - let to_f_vec = - |vec: &Vec| -> Vec { vec.iter().map(|x| F::from(*x as u64)).collect() }; + let to_f_vec = |vec: &Vec| -> Vec { + vec.iter() + .map(|x| F::from_u64(*x as u64).unwrap()) + .collect() + }; let v_read_write = FiveTuplePoly::from_elf_r1cs(&trace); @@ -547,7 +550,7 @@ where .map(|i| { >>::fingerprint( &[ - F::from(i as u64), + F::from_u64(i as u64).unwrap(), polynomials.v_init_final.opcode[i], polynomials.v_init_final.rd[i], polynomials.v_init_final.rs1[i], @@ -587,7 +590,7 @@ where .map(|i| { >>::fingerprint( &[ - F::from(i as u64), + F::from_u64(i as u64).unwrap(), polynomials.v_init_final.opcode[i], polynomials.v_init_final.rd[i], polynomials.v_init_final.rs1[i], diff --git a/jolt-core/src/jolt/vm/instruction_lookups.rs b/jolt-core/src/jolt/vm/instruction_lookups.rs index af73be1cd..fb90beed5 100644 --- a/jolt-core/src/jolt/vm/instruction_lookups.rs +++ b/jolt-core/src/jolt/vm/instruction_lookups.rs @@ -487,7 +487,7 @@ where .flat_map_iter(|(subtable_index, subtable)| { let init_fingerprints: Vec = (0..M) .map(|i| { - let a = &F::from(i as u64); + let a = &F::from_u64(i as u64).unwrap(); let v = &subtable[i]; // let t = F::zero(); // Compute h(a,v,t) where t == 0 diff --git a/jolt-core/src/jolt/vm/mod.rs b/jolt-core/src/jolt/vm/mod.rs index 9045c034e..0072297a8 100644 --- a/jolt-core/src/jolt/vm/mod.rs +++ b/jolt-core/src/jolt/vm/mod.rs @@ -310,12 +310,17 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize // Add circuit_flags_packed to prog_v_rw. Pack them in little-endian order. let span = tracing::span!(tracing::Level::INFO, "pack_flags"); let _enter = span.enter(); - let precomputed_powers: Vec = (0..N_FLAGS).map(|i| F::from(2u64.pow(i as u32))).collect(); - let packed_flags: Vec = circuit_flags.par_chunks(N_FLAGS).map(|x| { - x.iter().enumerate().fold(F::zero(), |packed, (i, flag)| { - packed + *flag * precomputed_powers[N_FLAGS - 1 - i] + let precomputed_powers: Vec = (0..N_FLAGS) + .map(|i| F::from_u64(2u64.pow(i as u32)).unwrap()) + .collect(); + let packed_flags: Vec = circuit_flags + .par_chunks(N_FLAGS) + .map(|x| { + x.iter().enumerate().fold(F::zero(), |packed, (i, flag)| { + packed + *flag * precomputed_powers[N_FLAGS - 1 - i] + }) }) - }).collect(); + .collect(); prog_v_rw.extend(packed_flags); drop(_enter); drop(span); @@ -349,7 +354,12 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize let [chunks_x, chunks_y] = op.operand_chunks(C, log_M); chunks_x.into_iter().zip(chunks_y.into_iter()) }) - .map(|(x, y)| (F::from(x as u64), F::from(y as u64))) + .map(|(x, y)| { + ( + F::from_u64(x as u64).unwrap(), + F::from_u64(y as u64).unwrap(), + ) + }) .unzip(); let mut chunks_query = instructions @@ -391,7 +401,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize let mut circuit_flags_padded = circuit_flags.clone(); circuit_flags_padded.extend(vec![ - F::from(0_u64); + F::zero(); PADDED_TRACE_LEN * N_FLAGS - circuit_flags.len() ]); // circuit_flags.resize(PADDED_TRACE_LEN * N_FLAGS, Default::default()); diff --git a/jolt-core/src/jolt/vm/read_write_memory.rs b/jolt-core/src/jolt/vm/read_write_memory.rs index 0eb129f43..ba1e50448 100644 --- a/jolt-core/src/jolt/vm/read_write_memory.rs +++ b/jolt-core/src/jolt/vm/read_write_memory.rs @@ -453,8 +453,12 @@ impl> ReadWriteMemory { drop(_enter); drop(span); - let to_f_vec = - |v: &Vec| -> Vec { v.into_par_iter().map(|i| F::from(*i)).collect::>() }; + // create a closure to convert u64 to F vector + let to_f_vec = |v: &Vec| -> Vec { + v.par_iter() + .map(|i| F::from_u64(*i).unwrap()) + .collect::>() + }; let un_remap_address = |a: &Vec| { a.iter() @@ -786,14 +790,14 @@ where let init_fingerprints = (0..self.memory_size) .into_par_iter() - .map(|i| /* 0 * gamma^2 + */ mul_0_optimized(&polynomials.v_init[i], gamma) + F::from(i as u64) - *tau) + .map(|i| /* 0 * gamma^2 + */ mul_0_optimized(&polynomials.v_init[i], gamma) + F::from_u64(i as u64).unwrap() - *tau) .collect(); let final_fingerprints = (0..self.memory_size) .into_par_iter() .map(|i| { mul_0_optimized(&polynomials.t_final[i], &gamma_squared) + mul_0_optimized(&polynomials.v_final[i], gamma) - + F::from(i as u64) + + F::from_u64(i as u64).unwrap() - *tau }) .collect(); diff --git a/jolt-core/src/jolt/vm/timestamp_range_check.rs b/jolt-core/src/jolt/vm/timestamp_range_check.rs index 2e7a8c6a8..729231f44 100644 --- a/jolt-core/src/jolt/vm/timestamp_range_check.rs +++ b/jolt-core/src/jolt/vm/timestamp_range_check.rs @@ -329,7 +329,8 @@ where let read_fingerprints_0: Vec = (0..M) .into_par_iter() .map(|j| { - let read_timestamp = F::from(polynomials.read_timestamps[i][j]); + let read_timestamp = + F::from_u64(polynomials.read_timestamps[i][j]).unwrap(); polynomials.read_cts_read_timestamp[i][j] * gamma_squared + read_timestamp * gamma + read_timestamp @@ -345,7 +346,7 @@ where .into_par_iter() .map(|j| { let global_minus_read = - F::from(j as u64 - polynomials.read_timestamps[i][j]); + F::from_u64(j as u64 - polynomials.read_timestamps[i][j]).unwrap(); polynomials.read_cts_global_minus_read[i][j] * gamma_squared + global_minus_read * gamma + global_minus_read @@ -369,7 +370,7 @@ where let init_fingerprints = (0..M) .into_par_iter() .map(|i| { - let index = F::from(i as u64); + let index = F::from_u64(i as u64).unwrap(); // 0 * gamma^2 + index * gamma + index - tau }) diff --git a/jolt-core/src/lasso/surge.rs b/jolt-core/src/lasso/surge.rs index 6578015ea..197d6a8fa 100644 --- a/jolt-core/src/lasso/surge.rs +++ b/jolt-core/src/lasso/surge.rs @@ -366,7 +366,7 @@ where .map(|i| { // 0 * gamma^2 + mul_0_1_optimized(&self.materialized_subtables[subtable_index][i], gamma) - + F::from(i as u64) + + F::from_u64(i as u64).unwrap() - *tau }) .collect(); diff --git a/jolt-core/src/poly/identity_poly.rs b/jolt-core/src/poly/identity_poly.rs index 0464ba56b..bb150be6b 100644 --- a/jolt-core/src/poly/identity_poly.rs +++ b/jolt-core/src/poly/identity_poly.rs @@ -15,7 +15,7 @@ impl IdentityPolynomial { let len = r.len(); assert_eq!(len, self.size_point); (0..len) - .map(|i| F::from((len - i - 1).pow2() as u64) * r[i]) + .map(|i| F::from_u64((len - i - 1).pow2() as u64).unwrap() * r[i]) .sum() } } diff --git a/jolt-core/src/poly/unipoly.rs b/jolt-core/src/poly/unipoly.rs index 08db152a0..c6bc27072 100644 --- a/jolt-core/src/poly/unipoly.rs +++ b/jolt-core/src/poly/unipoly.rs @@ -33,7 +33,7 @@ impl UniPoly { fn vandermonde_interpolation(evals: &[F]) -> Vec { let n = evals.len(); - let xs: Vec = (0..n).map(|x| F::from(x as u64)).collect(); + let xs: Vec = (0..n).map(|x| F::from_u64(x as u64).unwrap()).collect(); let mut vandermonde: Vec> = Vec::with_capacity(n); for i in 0..n { diff --git a/jolt-core/src/utils/instruction_utils.rs b/jolt-core/src/utils/instruction_utils.rs index aca80f775..7457b301d 100644 --- a/jolt-core/src/utils/instruction_utils.rs +++ b/jolt-core/src/utils/instruction_utils.rs @@ -13,7 +13,7 @@ pub fn concatenate_lookups(vals: &[F], C: usize, operand_bits: us let mut sum = F::zero(); let mut weight = F::one(); - let shift = F::from(1u64 << operand_bits); + let shift = F::from_u64(1u64 << operand_bits).unwrap(); for i in 0..C { sum += weight * vals[C - i - 1]; weight *= shift; From 38f9d7aa4ccb9ebb5bae7dea71167e85318d8b0b Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Wed, 28 Feb 2024 11:55:09 -0500 Subject: [PATCH 4/5] build-fast/release -> release/extra-fast profiles --- Cargo.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f2c7416fc..aec84c4c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,12 +33,13 @@ members = [ [profile.release] debug = 1 codegen-units = 1 -lto = "fat" +lto = "off" +incremental = true -[profile.build-fast] +[profile.extra-fast] inherits = "release" -incremental = true -lto = "off" +lto = "fat" +incremental = false [patch.crates-io] ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64" } From e5d1592230334c34e5a32d1fddf188b94cbfc27b Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Wed, 28 Feb 2024 12:03:08 -0500 Subject: [PATCH 5/5] address comments --- jolt-core/Cargo.toml | 6 +++--- jolt-core/src/jolt/subtable/eq_msb.rs | 2 +- jolt-core/src/jolt/subtable/gt_msb.rs | 2 +- jolt-core/src/jolt/subtable/sra_sign.rs | 2 +- jolt-core/src/jolt/subtable/srl.rs | 2 +- jolt-core/src/jolt/vm/mod.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/jolt-core/Cargo.toml b/jolt-core/Cargo.toml index eafae4de5..7dfc323ad 100644 --- a/jolt-core/Cargo.toml +++ b/jolt-core/Cargo.toml @@ -20,9 +20,9 @@ keywords = ["zkSNARKs", "cryptography", "proofs"] [dependencies] ark-curve25519 = "0.4.0" -ark-ec = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64", default-features = false } -ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64", default-features = false } -ark-serialize = { git = "https://github.com/a16z/arkworks-algebra", branch = "optimize/field-from-u64", default-features = false, features = [ +ark-ec = { version = "0.4.2", default-features = false } +ark-ff = { version = "0.4.2", default-features = false } +ark-serialize = { version = "0.4.2", default-features = false, features = [ "derive", ] } ark-std = { version = "0.4.0", default-features = false } diff --git a/jolt-core/src/jolt/subtable/eq_msb.rs b/jolt-core/src/jolt/subtable/eq_msb.rs index f7832a1d4..33e8f6728 100644 --- a/jolt-core/src/jolt/subtable/eq_msb.rs +++ b/jolt-core/src/jolt/subtable/eq_msb.rs @@ -28,7 +28,7 @@ impl LassoSubtable for EqMSBSubtable { for idx in 0..M { let (x, y) = split_bits(idx, bits_per_operand); let row = (x & high_bit) == (y & high_bit); - entries.push(F::from(row)); + entries.push(if row { F::one() } else { F::zero() }); } entries } diff --git a/jolt-core/src/jolt/subtable/gt_msb.rs b/jolt-core/src/jolt/subtable/gt_msb.rs index 56fe9550d..97ad79fa3 100644 --- a/jolt-core/src/jolt/subtable/gt_msb.rs +++ b/jolt-core/src/jolt/subtable/gt_msb.rs @@ -28,7 +28,7 @@ impl LassoSubtable for GtMSBSubtable { for idx in 0..M { let (x, y) = split_bits(idx, bits_per_operand); let row = (x & high_bit) > (y & high_bit); - entries.push(F::from(row)); + entries.push(if row { F::one() } else { F::zero() }); } entries } diff --git a/jolt-core/src/jolt/subtable/sra_sign.rs b/jolt-core/src/jolt/subtable/sra_sign.rs index 68321e2ca..08a0eeeab 100644 --- a/jolt-core/src/jolt/subtable/sra_sign.rs +++ b/jolt-core/src/jolt/subtable/sra_sign.rs @@ -64,7 +64,7 @@ impl LassoSubtable for SraSignSubtable let k_bits = (k as usize) .get_bits(log_WORD_SIZE) .iter() - .map(|bit| F::from(*bit)) + .map(|bit| if *bit { F::one() } else { F::zero() }) .collect::>(); // big-endian let mut eq_term = F::one(); diff --git a/jolt-core/src/jolt/subtable/srl.rs b/jolt-core/src/jolt/subtable/srl.rs index 08aa36336..5bc217707 100644 --- a/jolt-core/src/jolt/subtable/srl.rs +++ b/jolt-core/src/jolt/subtable/srl.rs @@ -61,7 +61,7 @@ impl LassoSubta let k_bits = (k as usize) .get_bits(log_WORD_SIZE) .iter() - .map(|bit| F::from(*bit)) + .map(|bit| if *bit { F::one() } else { F::zero() }) .collect::>(); // big-endian let mut eq_term = F::one(); diff --git a/jolt-core/src/jolt/vm/mod.rs b/jolt-core/src/jolt/vm/mod.rs index 0072297a8..98f6b0f17 100644 --- a/jolt-core/src/jolt/vm/mod.rs +++ b/jolt-core/src/jolt/vm/mod.rs @@ -426,7 +426,7 @@ pub trait Jolt<'a, F: PrimeField, G: CurveGroup, const C: usize fn compute_lookup_outputs(instructions: &Vec) -> Vec { instructions .par_iter() - .map(|op| F::from(op.lookup_entry())) + .map(|op| F::from_u64(op.lookup_entry()).unwrap()) .collect() } }