arnaucube
arnaucube/shockwave-plusPublic Self-hosted repository
shockwave-plus/shockwave_plus/src/lib.rs
shockwave_plus/src/lib.rsRaw
  1#![allow(non_snake_case)]
  2mod polynomial;
  3mod r1cs;
  4mod sumcheck;
  5
  6use ark_std::{end_timer, start_timer};
  7use serde::{Deserialize, Serialize};
  8use sumcheck::{SCPhase1Proof, SCPhase2Proof, SumCheckPhase1, SumCheckPhase2};
  9use tensor_pcs::{ecfft::GoodCurve, *};
 10
 11// Exports
 12pub use r1cs::R1CS;
 13
 14#[derive(Serialize, Deserialize)]
 15pub struct PartialSpartanProof<F: FieldExt> {
 16    pub z_comm: [u8; 32],
 17    pub sc_proof_1: SCPhase1Proof<F>,
 18    pub sc_proof_2: SCPhase2Proof<F>,
 19    pub z_eval_proof: TensorMLOpening<F>,
 20    pub v_A: F,
 21    pub v_B: F,
 22    pub v_C: F,
 23}
 24
 25pub struct FullSpartanProof<F: FieldExt> {
 26    pub partial_proof: PartialSpartanProof<F>,
 27    pub A_eval_proof: TensorMLOpening<F>,
 28    pub B_eval_proof: TensorMLOpening<F>,
 29    pub C_eval_proof: TensorMLOpening<F>,
 30}
 31
 32pub struct ShockwavePlus<F: FieldExt> {
 33    r1cs: R1CS<F>,
 34    pcs: TensorMultilinearPCS<F>,
 35}
 36
 37impl<F: FieldExt> ShockwavePlus<F> {
 38    pub fn new(r1cs: R1CS<F>, l: usize, good_curve: GoodCurve<F>, coset_offset: (F, F)) -> Self {
 39        let expansion_factor = 2;
 40
 41        let ecfft_config = rs_config::ecfft::gen_config_form_curve(good_curve, coset_offset);
 42
 43        let pcs_config = TensorRSMultilinearPCSConfig::<F> {
 44            expansion_factor,
 45            domain_powers: None,
 46            fft_domain: None,
 47            ecfft_config: Some(ecfft_config),
 48            l,
 49        };
 50
 51        let min_num_entries = r1cs.num_vars.next_power_of_two();
 52        let min_num_cols = pcs_config.num_cols(min_num_entries);
 53
 54        let max_num_entries = r1cs.z_len().next_power_of_two();
 55        let max_num_cols = pcs_config.num_cols(max_num_entries);
 56        // Make sure that there are enough columns to run the l queries
 57        assert!(min_num_cols > l);
 58
 59        assert_eq!(good_curve.k, (max_num_cols as f64).log2() as usize + 1);
 60
 61        let pcs = TensorMultilinearPCS::new(pcs_config);
 62
 63        Self { r1cs, pcs }
 64    }
 65
 66    pub fn prove(
 67        &self,
 68        r1cs_witness: &[F],
 69        r1cs_input: &[F],
 70        transcript: &mut Transcript<F>,
 71    ) -> (PartialSpartanProof<F>, Vec<F>) {
 72        // Compute the multilinear extension of the witness
 73        let witness_poly = SparseMLPoly::from_dense(r1cs_witness.to_vec());
 74        let Z = R1CS::construct_z(r1cs_witness, r1cs_input);
 75
 76        // Commit the witness polynomial
 77        let comm_witness_timer = start_timer!(|| "Commit witness");
 78        let committed_witness = self.pcs.commit(&witness_poly);
 79        let witness_comm = committed_witness.committed_tree.root;
 80        end_timer!(comm_witness_timer);
 81
 82        // Add the witness commitment to the transcript
 83        transcript.append_bytes(&witness_comm);
 84
 85        // ############################
 86        // Phase 1
 87        // ###################
 88
 89        let m = (self.r1cs.z_len() as f64).log2() as usize;
 90        let tau = transcript.challenge_vec(m);
 91
 92        let Az_poly = self.r1cs.A.mul_vector(&Z);
 93        let Bz_poly = self.r1cs.B.mul_vector(&Z);
 94        let Cz_poly = self.r1cs.C.mul_vector(&Z);
 95
 96        // Prove that the
 97        // Q(t) = \sum_{x \in {0, 1}^m} (Az_poly(x) * Bz_poly(x) - Cz_poly(x)) eq(t, x)
 98        // is a zero-polynomial using the sum-check protocol.
 99        // We evaluate Q(t) at $\tau$ and check that it is zero.
100
101        let rx = transcript.challenge_vec(m);
102
103        let sc_phase_1_timer = start_timer!(|| "Sumcheck phase 1");
104
105        let sc_phase_1 = SumCheckPhase1::new(
106            Az_poly.clone(),
107            Bz_poly.clone(),
108            Cz_poly.clone(),
109            tau.clone(),
110            rx.clone(),
111        );
112
113        let (sc_proof_1, (v_A, v_B, v_C)) = sc_phase_1.prove(&self.pcs, transcript);
114        end_timer!(sc_phase_1_timer);
115
116        transcript.append_fe(&v_A);
117        transcript.append_fe(&v_B);
118        transcript.append_fe(&v_C);
119
120        // Phase 2
121        let r = transcript.challenge_vec(3);
122
123        // T_2 should equal teh evaluations of the random linear combined polynomials
124
125        let ry = transcript.challenge_vec(m);
126        let sc_phase_2_timer = start_timer!(|| "Sumcheck phase 2");
127        let sc_phase_2 = SumCheckPhase2::new(
128            self.r1cs.A.clone(),
129            self.r1cs.B.clone(),
130            self.r1cs.C.clone(),
131            Z.clone(),
132            rx.clone(),
133            r.as_slice().try_into().unwrap(),
134            ry.clone(),
135        );
136
137        let sc_proof_2 = sc_phase_2.prove(&self.pcs, transcript);
138        end_timer!(sc_phase_2_timer);
139
140        let z_open_timer = start_timer!(|| "Open witness poly");
141        // Prove the evaluation of the polynomial Z(y) at ry
142        let z_eval_proof = self
143            .pcs
144            .open(&committed_witness, &witness_poly, &ry[1..], transcript);
145        end_timer!(z_open_timer);
146
147        // Prove the evaluation of the polynomials A(y), B(y), C(y) at ry
148
149        let rx_ry = vec![ry, rx].concat();
150        (
151            PartialSpartanProof {
152                z_comm: witness_comm,
153                sc_proof_1,
154                sc_proof_2,
155                z_eval_proof,
156                v_A,
157                v_B,
158                v_C,
159            },
160            rx_ry,
161        )
162    }
163
164    pub fn verify_partial(
165        &self,
166        partial_proof: &PartialSpartanProof<F>,
167        transcript: &mut Transcript<F>,
168    ) {
169        partial_proof.z_comm.append_to_transcript(transcript);
170
171        let A_mle = self.r1cs.A.to_ml_extension();
172        let B_mle = self.r1cs.B.to_ml_extension();
173        let C_mle = self.r1cs.C.to_ml_extension();
174
175        let m = (self.r1cs.z_len() as f64).log2() as usize;
176        let tau = transcript.challenge_vec(m);
177        let rx = transcript.challenge_vec(m);
178
179        transcript.append_fe(&partial_proof.sc_proof_1.blinder_poly_sum);
180        transcript.append_bytes(&partial_proof.sc_proof_1.blinder_poly_eval_proof.u_hat_comm);
181
182        let rho = transcript.challenge_fe();
183
184        let ex = SumCheckPhase1::verify_round_polys(&partial_proof.sc_proof_1, &rx, rho);
185
186        self.pcs.verify(
187            &partial_proof.sc_proof_1.blinder_poly_eval_proof,
188            transcript,
189        );
190
191        // The final eval should equal
192        let v_A = partial_proof.v_A;
193        let v_B = partial_proof.v_B;
194        let v_C = partial_proof.v_C;
195
196        let T_1_eq = EqPoly::new(tau);
197        let T_1 = (v_A * v_B - v_C) * T_1_eq.eval(&rx)
198            + rho * partial_proof.sc_proof_1.blinder_poly_eval_proof.y;
199        assert_eq!(T_1, ex);
200
201        transcript.append_fe(&v_A);
202        transcript.append_fe(&v_B);
203        transcript.append_fe(&v_C);
204
205        let r = transcript.challenge_vec(3);
206        let r_A = r[0];
207        let r_B = r[1];
208        let r_C = r[2];
209
210        let ry = transcript.challenge_vec(m);
211
212        transcript.append_fe(&partial_proof.sc_proof_2.blinder_poly_sum);
213        transcript.append_bytes(&partial_proof.sc_proof_2.blinder_poly_eval_proof.u_hat_comm);
214
215        let rho_2 = transcript.challenge_fe();
216
217        let T_2 =
218            (r_A * v_A + r_B * v_B + r_C * v_C) + rho_2 * partial_proof.sc_proof_2.blinder_poly_sum;
219        let final_poly_eval =
220            SumCheckPhase2::verify_round_polys(T_2, &partial_proof.sc_proof_2, &ry);
221
222        self.pcs.verify(
223            &partial_proof.sc_proof_2.blinder_poly_eval_proof,
224            transcript,
225        );
226
227        assert_eq!(partial_proof.z_eval_proof.x, ry[1..]);
228        let rx_ry = [rx, ry.clone()].concat();
229
230        let witness_eval = partial_proof.z_eval_proof.y;
231        let A_eval = A_mle.eval(&rx_ry);
232        let B_eval = B_mle.eval(&rx_ry);
233        let C_eval = C_mle.eval(&rx_ry);
234
235        self.pcs.verify(&partial_proof.z_eval_proof, transcript);
236
237        let witness_len = self.r1cs.num_vars.next_power_of_two();
238        let input = (0..self.r1cs.num_input)
239            .map(|i| (witness_len + i + 1, self.r1cs.public_input[i]))
240            .collect::<Vec<(usize, F)>>();
241
242        let input_poly =
243            SparseMLPoly::new(vec![vec![(witness_len, F::ONE)], input].concat(), ry.len());
244
245        let input_poly_eval = input_poly.eval(&ry);
246
247        let z_eval = (F::ONE - ry[0]) * witness_eval + input_poly_eval;
248
249        let T_opened = (r_A * A_eval + r_B * B_eval + r_C * C_eval) * z_eval
250            + rho_2 * partial_proof.sc_proof_2.blinder_poly_eval_proof.y;
251        assert_eq!(T_opened, final_poly_eval);
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use tensor_pcs::rs_config::good_curves::secp256k1::secp256k1_good_curve;
258
259    use super::*;
260
261    #[test]
262    fn test_shockwave_plus() {
263        type F = halo2curves::secp256k1::Fp;
264
265        let num_vars = 2usize.pow(6);
266        let num_input = 3;
267        let l = 2;
268
269        let (r1cs, witness) = R1CS::<F>::produce_synthetic_r1cs(num_vars, num_input);
270
271        let num_cols = det_num_cols(r1cs.z_len(), l);
272
273        let k = (num_cols as f64).log2() as usize;
274        let (good_curve, coset_offset) = secp256k1_good_curve(k + 1);
275        let ShockwavePlus = ShockwavePlus::new(r1cs.clone(), l, good_curve, coset_offset);
276        let mut prover_transcript = Transcript::new(b"bench");
277        let (partial_proof, _) =
278            ShockwavePlus.prove(&witness, &r1cs.public_input, &mut prover_transcript);
279
280        let mut verifier_transcript = Transcript::new(b"bench");
281        ShockwavePlus.verify_partial(&partial_proof, &mut verifier_transcript);
282    }
283}