diff --git a/src/e2e/mod.rs b/src/e2e/mod.rs index 203f293..c09c3ea 100644 --- a/src/e2e/mod.rs +++ b/src/e2e/mod.rs @@ -7,6 +7,7 @@ use crate::zkvm_verifier::binding::{ }; use crate::zkvm_verifier::verifier::{verify_gkr_circuit, verify_zkvm_proof}; use ceno_mle::util::ceil_log2; +use ceno_transcript::BasicTranscript; use ff_ext::BabyBearExt4; use gkr_iop::gkr::{ layer::sumcheck_layer::{SumcheckLayer, SumcheckLayerProof}, @@ -284,6 +285,7 @@ pub fn parse_zkvm_proof_import( chip_proofs.push(ZKVMChipProofInput { idx: chip_id.clone(), num_instances: chip_proof.num_instances, + num_vars: num_var_with_rotation, record_r_out_evals_len, record_w_out_evals_len, record_lk_out_evals_len, @@ -329,8 +331,12 @@ pub fn inner_test_thread() { .expect("Failed to deserialize vk file"); let verifier = ZKVMVerifier::new(vk); - let zkvm_proof_input = parse_zkvm_proof_import(zkvm_proof, &verifier); + let zkvm_proof_input = parse_zkvm_proof_import(zkvm_proof.clone(), &verifier); + // let transcript = BasicTranscript::new(b"riscv"); + // verifier + // .verify_proof(zkvm_proof, transcript) + // .expect("ZKVM proof verification failed"); // OpenVM DSL let mut builder = AsmBuilder::::default(); @@ -347,7 +353,7 @@ pub fn inner_test_thread() { witness_stream.extend(zkvm_proof_input.write()); // Compile program - let options = CompilerOptions::default().with_cycle_tracker(); + let options = CompilerOptions::default(); let mut compiler = AsmCompiler::new(options.word_size); compiler.build(builder.operations); let asm_code = compiler.code(); diff --git a/src/zkvm_verifier/binding.rs b/src/zkvm_verifier/binding.rs index fdf7b72..534d661 100644 --- a/src/zkvm_verifier/binding.rs +++ b/src/zkvm_verifier/binding.rs @@ -119,7 +119,7 @@ impl Hintable for ZKVMProofInput { let witin_num_vars = self .chip_proofs .iter() - .map(|proof| ceil_log2(proof.num_instances).max(1)) + .map(|proof| proof.num_vars) .collect::>(); let witin_max_widths = self .chip_proofs @@ -130,7 +130,7 @@ impl Hintable for ZKVMProofInput { .chip_proofs .iter() .filter(|proof| proof.fixed_in_evals.len() > 0) - .map(|proof| ceil_log2(proof.num_instances).max(1)) + .map(|proof| proof.num_vars) .collect::>(); let fixed_max_widths = self .chip_proofs @@ -251,7 +251,11 @@ impl Hintable for TowerProofInput { pub struct ZKVMChipProofInput { pub idx: usize, + // this is the number of instructions before padding + // it's possible that an instruction has multiple rows. pub num_instances: usize, + // this is the number of variables of each polynomial in the witness matrix + pub num_vars: usize, // product constraints pub record_r_out_evals_len: usize, diff --git a/src/zkvm_verifier/verifier.rs b/src/zkvm_verifier/verifier.rs index 2a9286b..19d4733 100644 --- a/src/zkvm_verifier/verifier.rs +++ b/src/zkvm_verifier/verifier.rs @@ -13,6 +13,7 @@ use crate::basefold_verifier::mmcs::MmcsCommitmentVariable; use crate::basefold_verifier::query_phase::PointAndEvalsVariable; use crate::basefold_verifier::utils::pow_2; // use crate::basefold_verifier::verifier::batch_verify; +use crate::basefold_verifier::verifier::batch_verify; use crate::tower_verifier::program::verify_tower_proof; use crate::transcript::transcript_observe_label; use crate::zkvm_verifier::binding::{ @@ -301,11 +302,12 @@ pub fn verify_zkvm_proof>( }; builder.cycle_tracker_end("Verify chip proof"); + let point_clone: Array> = builder.eval(input_opening_point.clone()); let witin_round: RoundOpeningVariable = builder.eval(RoundOpeningVariable { - num_var: chip_proof.log2_num_instances.get_var(), + num_var: input_opening_point.len().get_var(), point_and_evals: PointAndEvalsVariable { point: PointVariable { - fs: input_opening_point.clone(), + fs: point_clone, }, evals: chip_proof.wits_in_evals, }, @@ -314,10 +316,10 @@ pub fn verify_zkvm_proof>( if chip_vk.get_cs().num_fixed() > 0 { let fixed_round: RoundOpeningVariable = builder.eval(RoundOpeningVariable { - num_var: chip_proof.log2_num_instances.get_var(), + num_var: input_opening_point.len().get_var(), point_and_evals: PointAndEvalsVariable { point: PointVariable { - fs: input_opening_point.clone(), + fs: input_opening_point, }, evals: chip_proof.fixed_in_evals, }, @@ -375,15 +377,14 @@ pub fn verify_zkvm_proof>( ); } - /* _debug batch_verify( builder, zkvm_proof_input.max_num_var, + zkvm_proof_input.max_width, rounds, zkvm_proof_input.pcs_proof, &mut challenger, ); - */ let empty_arr: Array> = builder.dyn_array(0); let initial_global_state = eval_ceno_expr_with_instance( @@ -503,6 +504,7 @@ pub fn verify_opcode_proof( let out_evals_len: Usize = builder.eval(record_evals.len() + logup_q_evals.len()); let out_evals: Array> = builder.dyn_array(out_evals_len.clone()); + // TODO: find a better way to concat two arrays builder .range(0, record_evals.len()) .for_each(|idx_vec, builder| { @@ -529,7 +531,7 @@ pub fn verify_opcode_proof( unipoly_extrapolator, ); - opening_evaluations[0].point.fs.clone() + builder.eval(opening_evaluations[0].point.fs.clone()) } pub fn verify_gkr_circuit( @@ -570,7 +572,7 @@ pub fn verify_gkr_circuit( builder.if_eq(has_rotation, Usize::from(1)).then(|builder| { let first = builder.get(&eval_and_dedup_points, 0); builder.assert_usize_eq(first.has_point, Usize::from(1)); // Rotation proof should have at least one point - let rt = first.point.fs.clone(); + let rt = builder.eval(first.point.fs.clone()); let RotationClaim { left_evals, @@ -590,6 +592,12 @@ pub fn verify_gkr_circuit( unipoly_extrapolator, ); + // extend eval_and_dedup_points by + // [ + // (left_evals, left_point), + // (right_evals, right_point), + // (target_evals, origin_point), + // ] let last_idx: Usize = builder.eval(eval_and_dedup_points.len() - Usize::from(1)); builder.set( &eval_and_dedup_points, @@ -636,6 +644,7 @@ pub fn verify_gkr_circuit( let alpha_idx: Usize = Usize::Var(Var::uninit(builder)); builder.assign(&alpha_idx, C::N::from_canonical_usize(0)); + // sigma = \sum_i alpha^i * evals_i builder .range(0, eval_and_dedup_points.len()) .for_each(|idx_vec, builder| { @@ -652,6 +661,8 @@ pub fn verify_gkr_circuit( builder.assign(&sigma, sigma.clone() + sub_sum); builder.assign(&alpha_idx, end_idx); }); + + // sigma = \sum_b sel(b) * zero_expr(b) let max_degree = builder.constant(C::F::from_canonical_usize(layer.max_expr_degree + 1)); let max_num_variables = builder.unsafe_cast_var_to_felt(gkr_proof.num_var_with_rotation.get_var()); @@ -666,6 +677,7 @@ pub fn verify_gkr_circuit( unipoly_extrapolator, ); + // check selector evaluations layer .out_sel_and_eval_exprs .iter() @@ -683,6 +695,7 @@ pub fn verify_gkr_circuit( ); }); + // TODO: we should store alpha_pows in a bigger array to avoid concatenating them let main_sumcheck_challenges_len: Usize = builder.eval(alpha_pows.len() + Usize::from(2)); let main_sumcheck_challenges: Array> = @@ -720,16 +733,14 @@ pub fn verify_gkr_circuit( .enumerate() .for_each(|(idx, pos)| { let val = builder.get(&main_evals, idx); - builder.set( - &claims, - *pos, - PointAndEvalVariable { - point: PointVariable { - fs: in_point.clone(), - }, - eval: val, + let new_point: Array> = builder.eval(in_point.clone()); + let new_point_eval = builder.eval(PointAndEvalVariable { + point: PointVariable { + fs: new_point, }, - ); + eval: val, + }); + builder.set_value(&claims, *pos, new_point_eval); }); } @@ -739,9 +750,8 @@ pub fn verify_gkr_circuit( .in_eval_expr .iter() .enumerate() - .map(|(poly, eval)| { - let PointAndEvalVariable { point, eval } = builder.get(&claims, *eval); - + .map(|(poly, pos)| { + let PointAndEvalVariable { point, eval } = builder.get(&claims, *pos); GKRClaimEvaluation { value: eval, point, @@ -775,6 +785,8 @@ pub fn verify_rotation( let max_num_variables = builder.unsafe_cast_var_to_felt(max_num_variables.get_var()); let max_degree: Felt = builder.constant(C::F::TWO); + // 0 = \sum_b alpha^i * sel(rx, b) * in(next(b)) - out(b) + // in(next(b)) = (1-b4) * in(0,b0,...) + b4 * in(1,b0,1-b1,b2,...) let (origin_point, expected_evaluation) = iop_verifier_state_verify( builder, challenger, @@ -937,6 +949,7 @@ pub fn evaluate_selector( } }; + // TODO: just return eval and check it with respect to evals let Expression::StructuralWitIn(wit_id, _, _, _) = expr else { panic!("Wrong selector expression format"); }; @@ -944,6 +957,7 @@ pub fn evaluate_selector( builder.set(evals, wit_id, eval); } +// TODO: make this as a function of BooleanHypercube pub fn get_rotation_points( builder: &mut Builder, _num_vars: usize, @@ -951,6 +965,8 @@ pub fn get_rotation_points( ) -> (Array>, Array>) { let left: Array> = builder.dyn_array(point.len()); let right: Array> = builder.dyn_array(point.len()); + // left = (0,s0,s1,s2,s3,...) + // right = (1,s0,1-s1,s2,s3,...) builder.range(0, 4).for_each(|idx_vec, builder| { let e = builder.get(point, idx_vec[0]); let dest_idx: Var = builder.eval(idx_vec[0] + RVar::from(1)); @@ -980,16 +996,16 @@ pub fn evaluate_gkr_expression( ) -> PointAndEvalVariable { match expr { EvalExpression::Zero => { - let point = builder.get(claims, 0).point.clone(); + let point = builder.get(claims, 0).point; let eval: Ext = builder.constant(C::EF::ZERO); PointAndEvalVariable { point, eval } } - EvalExpression::Single(i) => builder.get(claims, *i).clone(), + EvalExpression::Single(i) => builder.get(claims, *i), EvalExpression::Linear(i, c0, c1) => { let point = builder.get(claims, *i); - let eval = point.eval.clone(); - let point = point.point.clone(); + let eval = builder.eval(point.eval); + let point = point.point; let empty_arr: Array> = builder.dyn_array(0); let c0_eval = eval_ceno_expr_with_instance( @@ -1003,6 +1019,7 @@ pub fn evaluate_gkr_expression( PointAndEvalVariable { point, eval } } + // TODO: we can ignore this part since it's not used right now EvalExpression::Partition(parts, indices) => { assert!(izip!(indices.iter(), indices.iter().skip(1)).all(|(a, b)| a.0 < b.0)); let empty_arr: Array> = builder.dyn_array(0); @@ -1028,6 +1045,7 @@ pub fn evaluate_gkr_expression( // _debug // assert!(parts.iter().all(|part| part.point == parts[0].point)); + // FIXME: this is WRONG. we should use builder.dyn_array(); let mut new_point: Vec> = vec![]; builder .range(0, parts[0].point.fs.len()) @@ -1090,19 +1108,14 @@ pub fn extract_claim_and_point( .for_each(|(i, (_, out_evals))| { let evals = out_evals .iter() - .map(|out_eval| { - let r = evaluate_gkr_expression(builder, out_eval, claims, challenges); - r.eval - }) + .map(|out_eval| evaluate_gkr_expression(builder, out_eval, claims, challenges)) .collect_vec(); + + let point = evals.first().map(|claim| builder.eval(claim.point.clone())); let evals_arr: Array> = builder.dyn_array(evals.len()); for (j, e) in evals.iter().enumerate() { - builder.set(&evals_arr, j, *e); + builder.set_value(&evals_arr, j, e.eval); } - let point = out_evals.first().map(|out_eval| { - let r = evaluate_gkr_expression(builder, out_eval, claims, challenges); - r.point - }); if point.is_some() { builder.set( @@ -1147,6 +1160,7 @@ pub fn generate_layer_challenges( builder.set(&r, 0, alpha); builder.set(&r, 1, beta); + // TODO: skip if n_challenges <= 2 transcript_observe_label(builder, challenger, b"layer challenge"); let c = gen_alpha_pows(builder, challenger, Usize::from(n_challenges));