From 7743f9a5ff04d74a9efefa1ed15ddfdc3199c664 Mon Sep 17 00:00:00 2001 From: Kunming Jiang Date: Wed, 11 Dec 2024 21:20:28 -0500 Subject: [PATCH] Decrease sparse commitment size --- circ_blocks/examples/zxc.rs | 33 +++-- spartan_parallel/examples/interface.rs | 17 ++- spartan_parallel/src/instance.rs | 179 ++++++++++++++++--------- spartan_parallel/src/lib.rs | 20 +-- spartan_parallel/src/r1csinstance.rs | 139 ++++++++++++++----- spartan_parallel/src/sparse_mlpoly.rs | 8 +- 6 files changed, 271 insertions(+), 125 deletions(-) diff --git a/circ_blocks/examples/zxc.rs b/circ_blocks/examples/zxc.rs index 977d6d31..880462ac 100644 --- a/circ_blocks/examples/zxc.rs +++ b/circ_blocks/examples/zxc.rs @@ -1308,16 +1308,29 @@ fn run_spartan_proof( println!("Generating Circuits..."); // -- // BLOCK INSTANCES + // block_inst is used by sumcheck. Every block has the same number of variables let (block_num_vars, block_num_cons, block_num_non_zero_entries, mut block_inst) = - Instance::gen_block_inst::( - block_num_instances_bound, - num_vars, - &ctk.args, - num_inputs_unpadded, - &block_num_phy_ops, - &block_num_vir_ops, - &ctk.num_vars_per_block, - &rtk.block_num_proofs, + Instance::gen_block_inst::( + block_num_instances_bound, + num_vars, + &ctk.args, + num_inputs_unpadded, + &block_num_phy_ops, + &block_num_vir_ops, + &ctk.num_vars_per_block, + &rtk.block_num_proofs, + ); + // block_inst is used by commitment. Every block has different number of variables + let (_, _, _, block_inst_for_commit) = + Instance::::gen_block_inst::( + block_num_instances_bound, + num_vars, + &ctk.args, + num_inputs_unpadded, + &block_num_phy_ops, + &block_num_vir_ops, + &ctk.num_vars_per_block, + &rtk.block_num_proofs, ); println!("Finished Block"); @@ -1355,7 +1368,7 @@ fn run_spartan_proof( println!("Comitting Circuits..."); // block_comm_map records the sparse_polys committed in each commitment // Note that A, B, C are committed separately, so sparse_poly[3*i+2] corresponds to poly C of instance i - let (block_comm_map, block_comm_list, block_decomm_list) = SNARK::multi_encode(&block_inst); + let (block_comm_map, block_comm_list, block_decomm_list) = SNARK::multi_encode(&block_inst_for_commit); println!("Finished Block"); let (pairwise_check_comm, pairwise_check_decomm) = SNARK::encode(&pairwise_check_inst); println!("Finished Pairwise"); diff --git a/spartan_parallel/examples/interface.rs b/spartan_parallel/examples/interface.rs index 4ba81798..44ee6e51 100644 --- a/spartan_parallel/examples/interface.rs +++ b/spartan_parallel/examples/interface.rs @@ -135,8 +135,21 @@ fn main() { println!("Generating Circuits..."); // -- // BLOCK INSTANCES + // block_inst is used by sumcheck. Every block has the same number of variables let (block_num_vars, block_num_cons, block_num_non_zero_entries, mut block_inst) = - Instance::gen_block_inst::( + Instance::gen_block_inst::( + block_num_instances_bound, + num_vars, + &ctk.args, + num_inputs_unpadded, + &block_num_phy_ops, + &block_num_vir_ops, + &ctk.num_vars_per_block, + &rtk.block_num_proofs, + ); + // block_inst is used by commitment. Every block has different number of variables + let (_, _, _, block_inst_for_commit) = + Instance::::gen_block_inst::( block_num_instances_bound, num_vars, &ctk.args, @@ -185,7 +198,7 @@ fn main() { println!("Comitting Circuits..."); // block_comm_map records the sparse_polys committed in each commitment // Note that A, B, C are committed separately, so sparse_poly[3*i+2] corresponds to poly C of instance i - let (block_comm_map, block_comm_list, block_decomm_list) = SNARK::multi_encode(&block_inst); + let (block_comm_map, block_comm_list, block_decomm_list) = SNARK::multi_encode(&block_inst_for_commit); println!("Finished Block"); let (pairwise_check_comm, pairwise_check_decomm) = SNARK::encode(&pairwise_check_inst); println!("Finished Pairwise"); diff --git a/spartan_parallel/src/instance.rs b/spartan_parallel/src/instance.rs index f758842c..9057dc81 100644 --- a/spartan_parallel/src/instance.rs +++ b/spartan_parallel/src/instance.rs @@ -1,10 +1,22 @@ use std::cmp::max; +use std::collections::HashMap; use crate::errors::R1CSError; use crate::math::Math; use crate::scalar::SpartanExtensionField; use crate::R1CSInstance; +const GROUP_POWER_SCALE: usize = 16; +// Group all instances with the similar num_vars (round to the next power of four) together +// Output.0 records the label of instances included within each commitment +fn next_group_size(val: usize) -> usize { + let mut base = 1; + while base < val { + base *= GROUP_POWER_SCALE; + } + return base; +} + /// `Instance` holds the description of R1CS matrices and a hash of the matrices #[derive(Clone)] pub struct Instance { @@ -254,7 +266,9 @@ impl Instance { /// - VMR3 = r^3 * VT /// - VMC = (1 or VMC[i-1]) * (tau - VA - VMR1 - VMR2 - VMR3) /// The final product is stored in X = MC[NV - 1] - pub fn gen_block_inst( + /// + /// If in COMMIT_MODE, commit instance by num_vars_per_block, rounded to the nearest power of four + pub fn gen_block_inst( num_instances: usize, num_vars: usize, args: &Vec< @@ -274,6 +288,24 @@ impl Instance { ) -> (usize, usize, usize, Instance) { assert_eq!(num_instances, args.len()); + let num_vars_padded_per_block = if COMMIT_MODE { + // If in commit mode, group all R1CS with num_vars within a power of 2^4 + // For every padded group size, mark the actual maximum num_vars of each group + let mut max_size_per_group: HashMap = HashMap::new(); + for num_vars in num_vars_per_block { + if let Some(val) = max_size_per_group.get(&next_group_size(*num_vars)) { + if num_vars.next_power_of_two() > *val { + max_size_per_group.insert(next_group_size(*num_vars), num_vars.next_power_of_two()); + } + } else { + max_size_per_group.insert(next_group_size(*num_vars), num_vars.next_power_of_two()); + } + } + num_vars_per_block.iter().map(|i| max_size_per_group.get(&next_group_size(*i)).unwrap().clone()).collect() + } else { + vec![num_vars; num_instances] + }; + if PRINT_SIZE { println!("\n\n--\nBLOCK INSTS"); println!( @@ -304,43 +336,43 @@ impl Instance { let V_VL = |b: usize, i: usize| io_width + 2 * num_phy_ops[b] + 4 * i + 2; let V_VT = |b: usize, i: usize| io_width + 2 * num_phy_ops[b] + 4 * i + 3; // in CHALLENGES, not used if !has_mem_op - let V_tau = num_vars; - let V_r = |i: usize| num_vars + i; + let V_tau = |b: usize| num_vars_padded_per_block[b]; + let V_r = |b: usize, i: usize| num_vars_padded_per_block[b] + i; // in BLOCK_W2 / INPUT_W2 - let V_input_dot_prod = |i: usize| { + let V_input_dot_prod = |b: usize, i: usize| { if i == 0 { V_input(0) } else { - 2 * num_vars + 2 + i + 2 * num_vars_padded_per_block[b] + 2 + i } }; - let V_output_dot_prod = |i: usize| 2 * num_vars + 2 + (num_inputs_unpadded - 1) + i; + let V_output_dot_prod = |b: usize, i: usize| 2 * num_vars_padded_per_block[b] + 2 + (num_inputs_unpadded - 1) + i; // in BLOCK_W2 / PHY_W2 - let V_PMR = |i: usize| 2 * num_vars + 2 * num_inputs_unpadded + 2 * i; - let V_PMC = |i: usize| 2 * num_vars + 2 * num_inputs_unpadded + 2 * i + 1; + let V_PMR = |b: usize, i: usize| 2 * num_vars_padded_per_block[b] + 2 * num_inputs_unpadded + 2 * i; + let V_PMC = |b: usize, i: usize| 2 * num_vars_padded_per_block[b] + 2 * num_inputs_unpadded + 2 * i + 1; // in BLOCK_W2 / VIR_W2 let V_VMR1 = - |b: usize, i: usize| 2 * num_vars + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i; + |b: usize, i: usize| 2 * num_vars_padded_per_block[b] + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i; let V_VMR2 = - |b: usize, i: usize| 2 * num_vars + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i + 1; + |b: usize, i: usize| 2 * num_vars_padded_per_block[b] + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i + 1; let V_VMR3 = - |b: usize, i: usize| 2 * num_vars + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i + 2; + |b: usize, i: usize| 2 * num_vars_padded_per_block[b] + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i + 2; let V_VMC = - |b: usize, i: usize| 2 * num_vars + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i + 3; + |b: usize, i: usize| 2 * num_vars_padded_per_block[b] + 2 * num_inputs_unpadded + 2 * num_phy_ops[b] + 4 * i + 3; // in BLOCK_W3 - let V_v = 3 * num_vars; - let V_x = 3 * num_vars + 1; - let V_pi = 3 * num_vars + 2; - let V_d = 3 * num_vars + 3; - let V_Pp = 3 * num_vars + 4; - let V_Pd = 3 * num_vars + 5; - let V_Vp = 3 * num_vars + 6; - let V_Vd = 3 * num_vars + 7; + let V_v = |b: usize| 3 * num_vars_padded_per_block[b]; + let V_x = |b: usize| 3 * num_vars_padded_per_block[b] + 1; + let V_pi = |b: usize| 3 * num_vars_padded_per_block[b] + 2; + let V_d = |b: usize| 3 * num_vars_padded_per_block[b] + 3; + let V_Pp = |b: usize| 3 * num_vars_padded_per_block[b] + 4; + let V_Pd = |b: usize| 3 * num_vars_padded_per_block[b] + 5; + let V_Vp = |b: usize| 3 * num_vars_padded_per_block[b] + 6; + let V_Vd = |b: usize| 3 * num_vars_padded_per_block[b] + 7; // in BLOCK_W3_SHIFTED - let V_sv = 4 * num_vars; - let V_spi = 4 * num_vars + 2; - let V_Psp = 4 * num_vars + 4; - let V_Vsp = 4 * num_vars + 6; + let V_sv = |b: usize| 4 * num_vars_padded_per_block[b]; + let V_spi = |b: usize| 4 * num_vars_padded_per_block[b] + 2; + let V_Psp = |b: usize| 4 * num_vars_padded_per_block[b] + 4; + let V_Vsp = |b: usize| 4 * num_vars_padded_per_block[b] + 6; // Variable used by printing let mut total_inst_commit_size = 0; @@ -385,8 +417,8 @@ impl Instance { C, counter, vec![(V_input(i), 1)], - vec![(V_r(i), 1)], - vec![(V_input_dot_prod(i), 1)], + vec![(V_r(b, i), 1)], + vec![(V_input_dot_prod(b, i), 1)], ); counter += 1; } @@ -398,8 +430,8 @@ impl Instance { C, counter, vec![(V_output(i), 1)], - vec![(V_r(i + num_inputs_unpadded - 1), 1)], - vec![(V_output_dot_prod(i), 1)], + vec![(V_r(b, i + num_inputs_unpadded - 1), 1)], + vec![(V_output_dot_prod(b, i), 1)], ); counter += 1; } @@ -411,7 +443,7 @@ impl Instance { counter, vec![], vec![], - vec![(V_valid, 1), (V_v, -1)], + vec![(V_valid, 1), (V_v(b), -1)], ); counter += 1; // x[k] @@ -421,14 +453,14 @@ impl Instance { C, counter, [ - vec![(V_tau, 1)], + vec![(V_tau(b), 1)], (0..2 * num_inputs_unpadded - 2) - .map(|i| (V_input_dot_prod(i), -1)) + .map(|i| (V_input_dot_prod(b, i), -1)) .collect(), ] .concat(), vec![(V_cnst, 1)], - vec![(V_x, 1)], + vec![(V_x(b), 1)], ); counter += 1; // D[k] = x[k] * (pi[k + 1] + (1 - v[k + 1])) @@ -437,9 +469,9 @@ impl Instance { B, C, counter, - vec![(V_x, 1)], - vec![(V_spi, 1), (V_cnst, 1), (V_sv, -1)], - vec![(V_d, 1)], + vec![(V_x(b), 1)], + vec![(V_spi(b), 1), (V_cnst, 1), (V_sv(b), -1)], + vec![(V_d(b), 1)], ); counter += 1; // pi[k] = v[k] * D[k] @@ -448,9 +480,9 @@ impl Instance { B, C, counter, - vec![(V_v, 1)], - vec![(V_d, 1)], - vec![(V_pi, 1)], + vec![(V_v(b), 1)], + vec![(V_d(b), 1)], + vec![(V_pi(b), 1)], ); counter += 1; @@ -469,9 +501,9 @@ impl Instance { B, C, counter, - vec![(V_r(1), 1)], + vec![(V_r(b, 1), 1)], vec![(V_PD(i), 1)], - vec![(V_PMR(i), 1)], + vec![(V_PMR(b, i), 1)], ); counter += 1; // PMC = (1 or PMC[i-1]) * (tau - PA - PMR) @@ -482,8 +514,8 @@ impl Instance { C, counter, vec![(V_cnst, 1)], - vec![(V_tau, 1), (V_PA(i), -1), (V_PMR(i), -1)], - vec![(V_PMC(i), 1)], + vec![(V_tau(b), 1), (V_PA(i), -1), (V_PMR(b, i), -1)], + vec![(V_PMC(b, i), 1)], ); } else { (A, B, C) = Instance::::gen_constr( @@ -491,9 +523,9 @@ impl Instance { B, C, counter, - vec![(V_PMC(i - 1), 1)], - vec![(V_tau, 1), (V_PA(i), -1), (V_PMR(i), -1)], - vec![(V_PMC(i), 1)], + vec![(V_PMC(b, i - 1), 1)], + vec![(V_tau(b), 1), (V_PA(i), -1), (V_PMR(b, i), -1)], + vec![(V_PMC(b, i), 1)], ); } counter += 1; @@ -509,10 +541,10 @@ impl Instance { vec![if num_phy_ops[b] == 0 { (V_cnst, 1) } else { - (V_PMC(num_phy_ops[b] - 1), 1) + (V_PMC(b, num_phy_ops[b] - 1), 1) }], - vec![(V_Psp, 1), (V_cnst, 1), (V_sv, -1)], - vec![(V_Pd, 1)], + vec![(V_Psp(b), 1), (V_cnst, 1), (V_sv(b), -1)], + vec![(V_Pd(b), 1)], ); counter += 1; // Pp @@ -521,9 +553,9 @@ impl Instance { B, C, counter, - vec![(V_v, 1)], - vec![(V_Pd, 1)], - vec![(V_Pp, 1)], + vec![(V_v(b), 1)], + vec![(V_Pd(b), 1)], + vec![(V_Pp(b), 1)], ); counter += 1; @@ -539,7 +571,7 @@ impl Instance { B, C, counter, - vec![(V_r(1), 1)], + vec![(V_r(b, 1), 1)], vec![(V_VD(b, i), 1)], vec![(V_VMR1(b, i), 1)], ); @@ -550,7 +582,7 @@ impl Instance { B, C, counter, - vec![(V_r(2), 1)], + vec![(V_r(b, 2), 1)], vec![(V_VL(b, i), 1)], vec![(V_VMR2(b, i), 1)], ); @@ -561,7 +593,7 @@ impl Instance { B, C, counter, - vec![(V_r(3), 1)], + vec![(V_r(b, 3), 1)], vec![(V_VT(b, i), 1)], vec![(V_VMR3(b, i), 1)], ); @@ -575,7 +607,7 @@ impl Instance { counter, vec![(V_cnst, 1)], vec![ - (V_tau, 1), + (V_tau(b), 1), (V_VA(b, i), -1), (V_VMR1(b, i), -1), (V_VMR2(b, i), -1), @@ -591,7 +623,7 @@ impl Instance { counter, vec![(V_VMC(b, i - 1), 1)], vec![ - (V_tau, 1), + (V_tau(b), 1), (V_VA(b, i), -1), (V_VMR1(b, i), -1), (V_VMR2(b, i), -1), @@ -615,8 +647,8 @@ impl Instance { } else { (V_VMC(b, num_vir_ops[b] - 1), 1) }], - vec![(V_Vsp, 1), (V_cnst, 1), (V_sv, -1)], - vec![(V_Vd, 1)], + vec![(V_Vsp(b), 1), (V_cnst, 1), (V_sv(b), -1)], + vec![(V_Vd(b), 1)], ); counter += 1; // Vp @@ -625,9 +657,9 @@ impl Instance { B, C, counter, - vec![(V_v, 1)], - vec![(V_Vd, 1)], - vec![(V_Vp, 1)], + vec![(V_v(b), 1)], + vec![(V_Vd(b), 1)], + vec![(V_Vp(b), 1)], ); counter += 1; @@ -679,13 +711,32 @@ impl Instance { println!("Total Cons Exec Size: {}", total_cons_exec_size); } + // Set num_cons of R1CS of the same num_vars to be the same + let num_cons_padded_per_block = { + if COMMIT_MODE { + let mut max_cons_per_group: HashMap = HashMap::new(); + for i in 0..num_instances { + if let Some(num_cons) = max_cons_per_group.get(&num_vars_padded_per_block[i]) { + if *num_cons < block_num_cons[i] { + max_cons_per_group.insert(num_vars_padded_per_block[i], block_num_cons[i]); + } + } else { + max_cons_per_group.insert(num_vars_padded_per_block[i], block_num_cons[i]); + } + } + num_vars_padded_per_block.iter().map(|i| max_cons_per_group.get(i).unwrap().clone()).collect() + } else { + block_num_cons + } + }; + let block_num_vars = 8 * num_vars; let block_inst = Instance::new( num_instances, block_max_num_cons, - block_num_cons, + num_cons_padded_per_block, block_num_vars, - vec![block_num_vars; num_instances], + num_vars_padded_per_block.into_iter().map(|i| 8 * i).collect(), &A_list, &B_list, &C_list, @@ -1058,7 +1109,7 @@ impl Instance { let pairwise_check_inst = Instance::new( 3, pairwise_check_max_num_cons, - pairwise_check_num_cons, + vec![pairwise_check_max_num_cons; 3], 4 * pairwise_check_num_vars, vec![4 * pairwise_check_num_vars; 3], &A_list, diff --git a/spartan_parallel/src/lib.rs b/spartan_parallel/src/lib.rs index 00943f77..0c30eb89 100644 --- a/spartan_parallel/src/lib.rs +++ b/spartan_parallel/src/lib.rs @@ -1957,9 +1957,9 @@ impl SNARK { let timer_eval = Timer::new("eval_sparse_polys"); // Per instance evaluation is unsorted - let inst_evals_list = block_inst_unsorted.inst.multi_evaluate(&rx, &ry); + let inst_evals_list = block_inst_unsorted.inst.multi_evaluate::(&rx, &ry); // RP-bound evaluation is sorted - let (_, inst_evals_bound_rp) = block_inst.inst.multi_evaluate_bound_rp(&rp, &rx, &ry); + let (_, inst_evals_bound_rp) = block_inst.inst.multi_evaluate_bound_rp::(&rp, &rx, &ry); timer_eval.stop(); for r in &inst_evals_list { @@ -1975,7 +1975,7 @@ impl SNARK { let r1cs_eval_proof_list = { let mut r1cs_eval_proof_list = Vec::new(); for i in 0..block_comm_list.len() { - let proof = R1CSEvalProof::prove( + let proof = R1CSEvalProof::prove::( &block_decomm_list[i].decomm, &rx, &ry, @@ -2076,11 +2076,11 @@ impl SNARK { let timer_eval = Timer::new("eval_sparse_polys"); // Per instance evaluation is unsorted - let inst_evals_list = pairwise_check_inst_unsorted.inst.multi_evaluate(&rx, &ry); + let inst_evals_list = pairwise_check_inst_unsorted.inst.multi_evaluate::(&rx, &ry); // RP-bound evaluation is sorted let (_, inst_evals_bound_rp) = pairwise_check_inst .inst - .multi_evaluate_bound_rp(&rp, &rx, &ry); + .multi_evaluate_bound_rp::(&rp, &rx, &ry); timer_eval.stop(); for r in &inst_evals_list { @@ -2093,7 +2093,7 @@ impl SNARK { let _: S = transcript.challenge_scalar(b"challenge_c2"); let r1cs_eval_proof = { - let proof = R1CSEvalProof::prove( + let proof = R1CSEvalProof::prove::( &pairwise_check_decomm.decomm, &rx, &ry, @@ -2210,7 +2210,7 @@ impl SNARK { timer_eval.stop(); let r1cs_eval_proof = { - let proof = R1CSEvalProof::prove( + let proof = R1CSEvalProof::prove::( &perm_root_decomm.decomm, &rx, &ry, @@ -2979,7 +2979,7 @@ impl SNARK { .collect(); for i in 0..block_comm_list.len() { - self.block_r1cs_eval_proof_list[i].verify( + self.block_r1cs_eval_proof_list[i].verify::( &block_comm_list[i].comm, &rx, &ry, @@ -3072,7 +3072,7 @@ impl SNARK { }) .collect(); - self.pairwise_check_r1cs_eval_proof.verify( + self.pairwise_check_r1cs_eval_proof.verify::( &pairwise_check_comm.comm, &rx, &ry, @@ -3159,7 +3159,7 @@ impl SNARK { S::append_field_to_transcript(tag, transcript, *val); } let [_, _, rx, ry] = perm_block_root_challenges; - self.perm_root_r1cs_eval_proof.verify( + self.perm_root_r1cs_eval_proof.verify::( &perm_root_comm.comm, &rx, &ry, diff --git a/spartan_parallel/src/r1csinstance.rs b/spartan_parallel/src/r1csinstance.rs index e158de82..0abf3f46 100644 --- a/spartan_parallel/src/r1csinstance.rs +++ b/spartan_parallel/src/r1csinstance.rs @@ -102,9 +102,6 @@ impl R1CSInstance { assert_eq!(B_list.len(), C_list.len()); // no errors, so create polynomials - let num_poly_vars_x = max_num_cons.log_2(); - let num_poly_vars_y = max_num_vars.log_2(); - let mut poly_A_list = Vec::new(); let mut poly_B_list = Vec::new(); let mut poly_C_list = Vec::new(); @@ -114,6 +111,9 @@ impl R1CSInstance { let mut mat_C = Vec::new(); for inst in 0..A_list.len() { + let num_poly_vars_x = num_cons[inst].log_2(); + let num_poly_vars_y = num_vars[inst].log_2(); + let A = &A_list[inst]; let B = &B_list[inst]; let C = &C_list[inst]; @@ -389,21 +389,45 @@ impl R1CSInstance { (evals_A_list, evals_B_list, evals_C_list) } - pub fn multi_evaluate(&self, rx: &[S], ry: &[S]) -> Vec { + // If IS_BLOCK, ry is truncated starting at the third entry + pub fn multi_evaluate(&self, rx: &[S], ry: &[S]) -> Vec { let mut eval_list = Vec::new(); // Evaluate each individual poly on [rx, ry] for i in 0..self.num_instances { + let num_cons = self.num_cons[i]; + let num_vars = self.num_vars[i]; + let rx_header = rx[..rx.len() - min(rx.len(), num_cons.log_2())].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let rx_short = &rx[rx.len() - min(rx.len(), num_cons.log_2())..]; + let ry_skip_len = ry.len() - min(ry.len(), num_vars.log_2()); + let (ry_header, ry_short) = { + if IS_BLOCK { + let ry_header = ry[3..3 + ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = [ry[..3].to_vec(), ry[3 + ry_skip_len..].to_vec()].concat(); + (ry_header, ry_short) + } else { + let ry_header = ry[0..ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = ry[ry_skip_len..].to_vec(); + (ry_header, ry_short) + } + }; + let evals = SparseMatPolynomial::multi_evaluate( &[&self.A_list[i], &self.B_list[i], &self.C_list[i]], - rx, - ry, + rx_short, + &ry_short, ); - eval_list.extend(evals.clone()); + eval_list.extend(evals.into_iter().map(|i| rx_header * ry_header * i)); } eval_list } - pub fn multi_evaluate_bound_rp( + pub fn multi_evaluate_bound_rp( &self, rp: &[S], rx: &[S], @@ -418,11 +442,35 @@ impl R1CSInstance { let mut eval_list = Vec::new(); // Evaluate each individual poly on [rx, ry] for i in 0..self.num_instances { + let num_cons = self.num_cons[i]; + let num_vars = self.num_vars[i]; + let rx_header = rx[..rx.len() - min(rx.len(), num_cons.log_2())].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let rx_short = &rx[rx.len() - min(rx.len(), num_cons.log_2())..]; + let ry_skip_len = ry.len() - min(ry.len(), num_vars.log_2()); + let (ry_header, ry_short) = { + if IS_BLOCK { + let ry_header = ry[3..3 + ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = [ry[..3].to_vec(), ry[3 + ry_skip_len..].to_vec()].concat(); + (ry_header, ry_short) + } else { + let ry_header = ry[0..ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = ry[ry_skip_len..].to_vec(); + (ry_header, ry_short) + } + }; + let evals = SparseMatPolynomial::multi_evaluate( &[&self.A_list[i], &self.B_list[i], &self.C_list[i]], - rx, - ry, + rx_short, + &ry_short, ); + let evals: Vec = evals.into_iter().map(|i| rx_header * ry_header * i).collect(); eval_list.extend(evals.clone()); a_evals.push(evals[0]); b_evals.push(evals[1]); @@ -449,16 +497,6 @@ impl R1CSInstance { (evals[0], evals[1], evals[2]) } - // Group all instances with the similar num_vars (round to the next power of four) together - // Output.0 records the label of instances included within each commitment - pub fn next_power_of_four(val: usize) -> usize { - let mut base = 1; - while base < val { - base *= 4; - } - return base; - } - pub fn multi_commit( &self, ) -> ( @@ -472,9 +510,11 @@ impl R1CSInstance { let mut max_num_cons_list: Vec = Vec::new(); let mut max_num_vars_list: Vec = Vec::new(); - // Group the instances based on number of variables, separated by orders of 2^256 + // Group the instances based on number of variables, which are already orders of 2^4 for i in 0..self.num_instances { - let var_len = Self::next_power_of_four(self.num_vars[i]); + println!("I: {}, NUM_CONS: {}, NUM_VARS: {}", i, self.num_cons[i], self.num_vars[i]); + + let var_len = self.num_vars[i]; // A_list, B_list, C_list if let Some(index) = vars_size.get(&var_len) { label_map[*index].push(3 * i); @@ -488,12 +528,8 @@ impl R1CSInstance { } else { let next_label = vars_size.len(); vars_size.insert(var_len, next_label); - label_map.push(vec![3 * i]); - sparse_polys_list.push(vec![&self.A_list[i]]); - label_map.push(vec![3 * i + 1]); - sparse_polys_list.push(vec![&self.B_list[i]]); - label_map.push(vec![3 * i + 2]); - sparse_polys_list.push(vec![&self.C_list[i]]); + label_map.push(vec![3 * i, 3 * i + 1, 3 * i + 2]); + sparse_polys_list.push(vec![&self.A_list[i], &self.B_list[i], &self.C_list[i]]); max_num_cons_list.push(self.num_cons[i]); max_num_vars_list.push(self.num_vars[i]); } @@ -553,7 +589,8 @@ pub struct R1CSEvalProof { } impl R1CSEvalProof { - pub fn prove( + // If is BLOCK, separate the first 3 entries of ry out (corresponding to the 5 segments of witnesses) + pub fn prove( decomm: &R1CSDecommitment, rx: &[S], // point at which the polynomial is evaluated ry: &[S], @@ -564,19 +601,36 @@ impl R1CSEvalProof { let timer = Timer::new("R1CSEvalProof::prove"); println!("RX_LEN: {}, RY_LEN: {}", rx.len(), ry.len()); println!("NUM_CONS: {}, NUM_VARS: {}", decomm.num_cons, decomm.num_vars); - let rx_header = rx[..rx.len() - min(rx.len(), decomm.num_cons.log_2())].iter().fold( + let rx_skip_len = rx.len() - min(rx.len(), decomm.num_cons.log_2()); + let rx_header = rx[..rx_skip_len].iter().fold( S::field_one(), |c, i| c * (S::field_one() - i.clone()) ); - let rx_short = &rx[rx.len() - min(rx.len(), decomm.num_cons.log_2())..]; + let rx_short = &rx[rx_skip_len..]; + let ry_skip_len = ry.len() - min(ry.len(), decomm.num_vars.log_2()); + let (ry_header, ry_short) = { + if IS_BLOCK { + let ry_header = ry[3..3 + ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = [ry[..3].to_vec(), ry[3 + ry_skip_len..].to_vec()].concat(); + (ry_header, ry_short) + } else { + let ry_header = ry[0..ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = ry[ry_skip_len..].to_vec(); + (ry_header, ry_short) + } + }; // let ry_short = &ry[..min(ry.len(), decomm.num_vars.log_2())]; let proof = - SparseMatPolyEvalProof::prove(&decomm.dense, rx_header, rx_short, ry, evals, transcript, random_tape); + SparseMatPolyEvalProof::prove(&decomm.dense, rx_header * ry_header, rx_short, &ry_short, evals, transcript, random_tape); timer.stop(); R1CSEvalProof { proof } } - pub fn verify( + pub fn verify( &self, comm: &R1CSCommitment, rx: &[S], // point at which the R1CS matrix polynomials are evaluated @@ -588,7 +642,22 @@ impl R1CSEvalProof { S::field_one(), |c, i| c * (S::field_one() - i.clone()) ); let rx_short = &rx[rx.len() - min(rx.len(), comm.num_cons.log_2())..]; - // let ry_short = &ry[..min(ry.len(), comm.num_vars.log_2())]; - self.proof.verify(&comm.comm, rx_header, rx_short, ry, evals, transcript) + let ry_skip_len = ry.len() - min(ry.len(), comm.num_vars.log_2()); + let (ry_header, ry_short) = { + if IS_BLOCK { + let ry_header = ry[3..3 + ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = [ry[..3].to_vec(), ry[3 + ry_skip_len..].to_vec()].concat(); + (ry_header, ry_short) + } else { + let ry_header = ry[0..ry_skip_len].iter().fold( + S::field_one(), |c, i| c * (S::field_one() - i.clone()) + ); + let ry_short = ry[ry_skip_len..].to_vec(); + (ry_header, ry_short) + } + }; + self.proof.verify(&comm.comm, rx_header * ry_header, rx_short, &ry_short, evals, transcript) } } diff --git a/spartan_parallel/src/sparse_mlpoly.rs b/spartan_parallel/src/sparse_mlpoly.rs index 2cf16951..fd75da64 100644 --- a/spartan_parallel/src/sparse_mlpoly.rs +++ b/spartan_parallel/src/sparse_mlpoly.rs @@ -30,9 +30,9 @@ impl SparseMatEntry { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SparseMatPolynomial { - num_vars_x: usize, - num_vars_y: usize, - M: Vec>, + pub num_vars_x: usize, + pub num_vars_y: usize, + pub M: Vec>, } pub struct Derefs { @@ -748,7 +748,7 @@ impl HashLayerProof { S::append_field_vector_to_transcript(b"claim_evals_ops", transcript, &evals_ops); let challenges_ops = transcript.challenge_vector(b"challenge_combine_n_to_one", evals_ops.len().log_2()); - + let mut poly_evals_ops = DensePolynomial::new(evals_ops); for i in (0..challenges_ops.len()).rev() { poly_evals_ops.bound_poly_var_bot(&challenges_ops[i]);