hyperplonk/hyperplonk/src/mock.rs
hyperplonk/src/mock.rsRaw
1// Copyright (c) 2023 Espresso Systems (espressosys.com)
2// This file is part of the HyperPlonk library.
3
4// You should have received a copy of the MIT License
5// along with the HyperPlonk library. If not, see <https://mit-license.org/>.
6
7use arithmetic::identity_permutation;
8use ark_ff::PrimeField;
9use ark_std::{log2, test_rng};
10
11use crate::{
12 custom_gate::CustomizedGates,
13 selectors::SelectorColumn,
14 structs::{HyperPlonkIndex, HyperPlonkParams},
15 witness::WitnessColumn,
16};
17
18pub struct MockCircuit<F: PrimeField> {
19 pub public_inputs: Vec<F>,
20 pub witnesses: Vec<WitnessColumn<F>>,
21 pub index: HyperPlonkIndex<F>,
22}
23
24impl<F: PrimeField> MockCircuit<F> {
25 /// Number of variables in a multilinear system
26 pub fn num_variables(&self) -> usize {
27 self.index.num_variables()
28 }
29
30 /// number of selector columns
31 pub fn num_selector_columns(&self) -> usize {
32 self.index.num_selector_columns()
33 }
34
35 /// number of witness columns
36 pub fn num_witness_columns(&self) -> usize {
37 self.index.num_witness_columns()
38 }
39}
40
41impl<F: PrimeField> MockCircuit<F> {
42 /// Generate a mock plonk circuit for the input constraint size.
43 pub fn new(num_constraints: usize, gate: &CustomizedGates) -> MockCircuit<F> {
44 let mut rng = test_rng();
45 let nv = log2(num_constraints);
46 let num_selectors = gate.num_selector_columns();
47 let num_witnesses = gate.num_witness_columns();
48 let log_n_wires = log2(num_witnesses);
49 let merged_nv = nv + log_n_wires;
50
51 let mut selectors: Vec<SelectorColumn<F>> = vec![SelectorColumn::default(); num_selectors];
52 let mut witnesses: Vec<WitnessColumn<F>> = vec![WitnessColumn::default(); num_witnesses];
53
54 for _cs_counter in 0..num_constraints {
55 let mut cur_selectors: Vec<F> = (0..(num_selectors - 1))
56 .map(|_| F::rand(&mut rng))
57 .collect();
58 let cur_witness: Vec<F> = (0..num_witnesses).map(|_| F::rand(&mut rng)).collect();
59 let mut last_selector = F::zero();
60 for (index, (coeff, q, wit)) in gate.gates.iter().enumerate() {
61 if index != num_selectors - 1 {
62 let mut cur_monomial = if *coeff < 0 {
63 -F::from((-coeff) as u64)
64 } else {
65 F::from(*coeff as u64)
66 };
67 cur_monomial = match q {
68 Some(p) => cur_monomial * cur_selectors[*p],
69 None => cur_monomial,
70 };
71 for wit_index in wit.iter() {
72 cur_monomial *= cur_witness[*wit_index];
73 }
74 last_selector += cur_monomial;
75 } else {
76 let mut cur_monomial = if *coeff < 0 {
77 -F::from((-coeff) as u64)
78 } else {
79 F::from(*coeff as u64)
80 };
81 for wit_index in wit.iter() {
82 cur_monomial *= cur_witness[*wit_index];
83 }
84 last_selector /= -cur_monomial;
85 }
86 }
87 cur_selectors.push(last_selector);
88 for i in 0..num_selectors {
89 selectors[i].append(cur_selectors[i]);
90 }
91 for i in 0..num_witnesses {
92 witnesses[i].append(cur_witness[i]);
93 }
94 }
95 let pub_input_len = ark_std::cmp::min(4, num_constraints);
96 let public_inputs = witnesses[0].0[0..pub_input_len].to_vec();
97
98 let params = HyperPlonkParams {
99 num_constraints,
100 num_pub_input: public_inputs.len(),
101 gate_func: gate.clone(),
102 };
103
104 let permutation = identity_permutation(merged_nv as usize, 1);
105 let index = HyperPlonkIndex {
106 params,
107 permutation,
108 selectors,
109 };
110
111 Self {
112 public_inputs,
113 witnesses,
114 index,
115 }
116 }
117
118 pub fn is_satisfied(&self) -> bool {
119 for current_row in 0..self.num_variables() {
120 let mut cur = F::zero();
121 for (coeff, q, wit) in self.index.params.gate_func.gates.iter() {
122 let mut cur_monomial = if *coeff < 0 {
123 -F::from((-coeff) as u64)
124 } else {
125 F::from(*coeff as u64)
126 };
127 cur_monomial = match q {
128 Some(p) => cur_monomial * self.index.selectors[*p].0[current_row],
129 None => cur_monomial,
130 };
131 for wit_index in wit.iter() {
132 cur_monomial *= self.witnesses[*wit_index].0[current_row];
133 }
134 cur += cur_monomial;
135 }
136 if !cur.is_zero() {
137 return false;
138 }
139 }
140
141 true
142 }
143}
144
145#[cfg(test)]
146mod test {
147 use super::*;
148 use crate::{errors::HyperPlonkErrors, HyperPlonkSNARK};
149 use ark_bls12_381::{Bls12_381, Fr};
150 use subroutines::{
151 pcs::{
152 prelude::{MultilinearKzgPCS, MultilinearUniversalParams},
153 PolynomialCommitmentScheme,
154 },
155 poly_iop::PolyIOP,
156 };
157
158 const SUPPORTED_SIZE: usize = 20;
159 const MIN_NUM_VARS: usize = 8;
160 const MAX_NUM_VARS: usize = 15;
161 const CUSTOM_DEGREE: [usize; 6] = [1, 2, 4, 8, 16, 32];
162
163 #[test]
164 fn test_mock_circuit_sat() {
165 for i in 1..10 {
166 let vanilla_gate = CustomizedGates::vanilla_plonk_gate();
167 let circuit = MockCircuit::<Fr>::new(1 << i, &vanilla_gate);
168 assert!(circuit.is_satisfied());
169
170 let jf_gate = CustomizedGates::jellyfish_turbo_plonk_gate();
171 let circuit = MockCircuit::<Fr>::new(1 << i, &jf_gate);
172 assert!(circuit.is_satisfied());
173
174 for num_witness in 2..10 {
175 for degree in CUSTOM_DEGREE {
176 let mock_gate = CustomizedGates::mock_gate(num_witness, degree);
177 let circuit = MockCircuit::<Fr>::new(1 << i, &mock_gate);
178 assert!(circuit.is_satisfied());
179 }
180 }
181 }
182 }
183
184 fn test_mock_circuit_zkp_helper(
185 nv: usize,
186 gate: &CustomizedGates,
187 pcs_srs: &MultilinearUniversalParams<Bls12_381>,
188 ) -> Result<(), HyperPlonkErrors> {
189 let circuit = MockCircuit::<Fr>::new(1 << nv, gate);
190 assert!(circuit.is_satisfied());
191
192 let index = circuit.index;
193 // generate pk and vks
194 let (pk, vk) =
195 <PolyIOP<Fr> as HyperPlonkSNARK<Bls12_381, MultilinearKzgPCS<Bls12_381>>>::preprocess(
196 &index, &pcs_srs,
197 )?;
198 // generate a proof and verify
199 let proof =
200 <PolyIOP<Fr> as HyperPlonkSNARK<Bls12_381, MultilinearKzgPCS<Bls12_381>>>::prove(
201 &pk,
202 &circuit.public_inputs,
203 &circuit.witnesses,
204 )?;
205
206 let verify =
207 <PolyIOP<Fr> as HyperPlonkSNARK<Bls12_381, MultilinearKzgPCS<Bls12_381>>>::verify(
208 &vk,
209 &circuit.public_inputs,
210 &proof,
211 )?;
212 assert!(verify);
213 Ok(())
214 }
215
216 #[test]
217 fn test_mock_circuit_zkp() -> Result<(), HyperPlonkErrors> {
218 let mut rng = test_rng();
219 let pcs_srs =
220 MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, SUPPORTED_SIZE)?;
221 for nv in MIN_NUM_VARS..MAX_NUM_VARS {
222 let vanilla_gate = CustomizedGates::vanilla_plonk_gate();
223 test_mock_circuit_zkp_helper(nv, &vanilla_gate, &pcs_srs)?;
224 }
225 for nv in MIN_NUM_VARS..MAX_NUM_VARS {
226 let tubro_gate = CustomizedGates::jellyfish_turbo_plonk_gate();
227 test_mock_circuit_zkp_helper(nv, &tubro_gate, &pcs_srs)?;
228 }
229 let nv = 5;
230 for num_witness in 2..10 {
231 for degree in CUSTOM_DEGREE {
232 let mock_gate = CustomizedGates::mock_gate(num_witness, degree);
233 test_mock_circuit_zkp_helper(nv, &mock_gate, &pcs_srs)?;
234 }
235 }
236
237 Ok(())
238 }
239
240 #[test]
241 fn test_mock_circuit_e2e() -> Result<(), HyperPlonkErrors> {
242 let mut rng = test_rng();
243 let pcs_srs =
244 MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, SUPPORTED_SIZE)?;
245 let nv = MAX_NUM_VARS;
246
247 let turboplonk_gate = CustomizedGates::jellyfish_turbo_plonk_gate();
248 test_mock_circuit_zkp_helper(nv, &turboplonk_gate, &pcs_srs)?;
249
250 Ok(())
251 }
252
253 #[test]
254 fn test_mock_long_selector_e2e() -> Result<(), HyperPlonkErrors> {
255 let mut rng = test_rng();
256 let pcs_srs =
257 MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, SUPPORTED_SIZE)?;
258 let nv = MAX_NUM_VARS;
259
260 let long_selector_gate = CustomizedGates::super_long_selector_gate();
261 test_mock_circuit_zkp_helper(nv, &long_selector_gate, &pcs_srs)?;
262
263 Ok(())
264 }
265}