├── .gitignore ├── README.md ├── img ├── hypercube.png └── univarization.png └── univarization ├── Cargo.toml ├── README.md └── src ├── bcho_pcs.rs ├── bits.rs ├── groupsim.rs ├── kzg10.rs ├── lib.rs ├── mle ├── coeffs_sparse.rs ├── evals.rs ├── evals_sparse.rs └── mod.rs ├── ph23_pcs.rs ├── snark.rs ├── sumcheck.rs ├── transcript.rs ├── unipoly.rs ├── unisumcheck.rs └── zeromorph.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hello, Hypercube! 2 | 3 | ### Motivation 4 | 5 | This is a project as part of ZK Hack Istanbul 2023. zk-SNARKs have always been black boxs to most ZK developers, and our motivation is to deconstruct the black box into its base level primitives, showcasing the various techniques that can be used to transform a much complex constraints problem into simpler univariate polynomials that are much easier and efficient to dealt with. 6 | 7 | The goal is to implement cryptographic primitives to reduce sumcheck-based zk-SNARKS proving system into a univariate polynomial eventually. We call it "Hello, Hypercube!" because a multilinear polynomial can be represented as points on a boolean hypercube and reduced through evaluations on it partial sum (using sumcheck). 8 | 9 | Such reduction can be used in: 10 | - Sumcheck-based zk-SNARKS 11 | - Nova-like folding schemes 12 | - Lasso/Jolt zkVM systems 13 | - GKR-based protocols 14 | 15 | ### Overview 16 | 17 | ![Overview](./img/univarization.png) 18 | 19 | The above diagram shows the outline of this project. Starting from the top level, we have a ZK proving system (a simplified version of Spartan is implemented here). The proving system can be represented as a multilinear polynomial. We can think of the multilinear polynomial as a Boolean hypercube. For example, if the polynomial is of form $`f(x_1, x_2, x_3)`$, then the hypercube is of dimension 3. We can think of f as following: 20 | 21 | ``` 22 | f(0, 0, 0) = v_0 23 | f(0, 0, 1) = v_1 24 | f(0, 1, 0) = v_2 25 | f(0, 1, 1) = v_3 26 | f(1, 0, 0) = v_4 27 | f(1, 0, 1) = v_5 28 | f(1, 1, 0) = v_6 29 | f(1, 1, 1) = v_7 30 | ``` 31 | 32 | where $`v_i`$ is the value of the polynomial at the point $`(x_1, x_2, x_3)`$ = $`(i_1, i_2, i_3)`$. Point $`v_i`$ is each a vertex of the hypercube. We can then evaluate over the hypercube to get the partial sums from the sumcheck protocol. The purpose of evaluating the polynomial on a Boolean hypercube is that multilinear sumcheck is efficient for bitwise operations. 33 | 34 | Thinking of zk-SNARKs construction over hypercubes is very intuitive. For example, R1CS follows the general form of $`A\vec{z}\circ B\vec{z} = C\vec{z}`$, which we can decompose it into multiple polynomials as shown in the diagram below. Each polynomial is a hypercube, and to evalutate their relationships we just need to evaluate by folding the vertices of the hypercubes. 35 | 36 | ![hypercube](./img/hypercube.png) 37 | 38 | After the sumcheck protocol, we have reduced the polynomial to a simpler form where it is a polynomial dependent on the verifier's randomness during sumcheck. This is represented as $`f(\vec{r})`$ and is still a multilinear polynomial, though a much smaller one. We want to further reduce this to a univariate polynomial. Three ways that have been proposed in literature are [LogUp+](https://eprint.iacr.org/2023/1284.pdf), [Gemini](https://eprint.iacr.org/2022/420.pdf) and [ZeroMorph](https://eprint.iacr.org/2023/917.pdf). They each use different techniques, which are: 39 | 1. LogUp+: reduction of multivariate polynomial in evaluation form to univariate in evaluation form 40 | 2. Gemini: reduction of multivariate polynomial in coefficient form to univariate in coefficient form 41 | 3. ZeroMorph: reduction of multivariate polynomial in evaluation form to univariate in coefficient form 42 | 43 | It's much easier to deal with univariate polynomial using commitment schemes such as KZG10. Especially when a degree-N polynomial has really large degrees (i.e. N), the evaluation proofs of such polynomials become prohibitively expensive, which motivates the entire project. 44 | 45 | ### Running the code 46 | 47 | ``` 48 | cd univarization 49 | cargo test 50 | ``` 51 | 52 | ### Code Organization 53 | 54 | The main files showcasing our work are as follows (in the `univarization/src` folder): 55 | - `mle/*.rs`: representing polynomials using multilinear extension (MLE) 56 | - `unipoly.rs`: representing univariate polynomials 57 | - `sumcheck.rs`: sumcheck protocol on multilinear polynomials 58 | - `bcho_pcs.rs`: implementation of the Gemini univarization technique as mentioned above 59 | - `ph23_pcs.rs`: implementation of the LogUp+ technique as mentioned above 60 | - `zeromorph.rs`: implementation of the Zeromorph technique as mentioned above 61 | - `unipoly.rs`: implementation of univariate polynomials 62 | - `snark.rs`: implementation of a Spartan like proving system 63 | 64 | We use ark_bn254, ark_std and ark_ff in our implementations. 65 | 66 | File | blank | comment | code 67 | ------------------------|--------------|-------------|-------- 68 | unipoly.rs | 353 | 497 | 1023 69 | mle/mod.rs | 31 | 186 | 125 70 | mle/coeffs_sparse.rs | 132 | 157 | 709 71 | mle/evals_sparse.rs | 66 | 18 | 349 72 | mle/evals.rs | 58 | 36 | 272 73 | ph23_pcs.rs | 175 | 296 | 558 74 | zeromorph.rs | 194 | 235 | 530 75 | bcho_pcs.rs | 127 | 141 | 453 76 | sumcheck.rs | 165 | 86 | 418 77 | snark.rs | 93 | 67 | 388 78 | kzg10.rs | 97 | 163 | 293 79 | lib.rs | 45 | 9 | 140 80 | unisumcheck.rs | 37 | 12 | 130 81 | transcript.rs | 23 | 2 | 107 82 | bits.rs | 26 | 3 | 102 83 | groupsim.rs | 18 | 0 | 72 84 | **SUM**: | 1640 | 1908 | 5669 85 | 86 | ### Future Work 87 | 88 | - We look to benchmark the three approaches in the future. 89 | - Adding shifting (to the next). 90 | - Adding zero knowledge construction. 91 | 92 | ### Credits 93 | 94 | Our work is based on the following papers and libraries: 95 | - [LogUp+](https://eprint.iacr.org/2023/1284.pdf) 96 | - [Gemini](https://eprint.iacr.org/2022/420.pdf) 97 | - [ZeroMorph](https://eprint.iacr.org/2023/917.pdf) 98 | - [Arkworks](https://github.com/arkworks-rs) 99 | -------------------------------------------------------------------------------- /img/hypercube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gy001/hypercube/109c40fc380affcdd0b8ed819b9c5321c88d1e24/img/hypercube.png -------------------------------------------------------------------------------- /img/univarization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gy001/hypercube/109c40fc380affcdd0b8ed819b9c5321c88d1e24/img/univarization.png -------------------------------------------------------------------------------- /univarization/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "univarization" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | ark-std = { version = "0.3", features = ["std"] } 9 | ark-ec = { version = "0.3", default-features = false } 10 | ark-ff = { version = "0.3", default-features = false } 11 | ark-poly = { version = "0.3" } 12 | ark-poly-commit = { version = "0.3" } 13 | ark-bn254 = { version = "0.3.0" } 14 | sha3 = { version = "0.10", default-features = false } 15 | ff = { version = "0.13" , features = ["derive"]} 16 | log = { version = "0.4" } 17 | env_logger = { version = "0.10" } -------------------------------------------------------------------------------- /univarization/README.md: -------------------------------------------------------------------------------- 1 | ## Inside 2 | 3 | - Sumcheck implmentation (non-ZK) 4 | - MiniSpartan based on Sumcheck (non-ZK) 5 | - Multi-linear polymonial over integer domain 6 | - Univariate polynomial over \mathbb{H} domain and integer domain 7 | - Multi-variate PCS => Univariate PCS 8 | - Gemini: tensor-product argument 9 | - Logup+ ([PH23]): Section 5. 10 | 11 | ## TODOs 12 | 13 | - unipoly coeffs should be [f0, f1, ..., fn] ✅ 14 | - more comments, protocol description 15 | - clean up the space 16 | - towards zeromorph 17 | - batch kzg10 proofs 18 | - reduce number of openings by linearizing commitments 19 | - shift over hypercube as in hyperplonk 20 | - find primitive polynomial 21 | - linearization by mle 22 | - shift over pyramid-like subgroups ([PH23]) 23 | - add zk 24 | - realize kzg10 25 | 26 | 27 | -------------------------------------------------------------------------------- /univarization/src/bcho_pcs.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use crate::*; 4 | use crate::unipoly::UniPolynomial; 5 | use crate::mle::evals::*; 6 | use crate::kzg10::{KZG10PCS, Commitment}; 7 | use crate::transcript::Transcript; 8 | use log::debug; 9 | use std::rc::Rc; 10 | use std::result; 11 | 12 | use crate::groupsim::pairing; 13 | 14 | /// The protocol: 15 | /// 16 | /// P V 17 | /// 18 | /// compute h0(X), h1(X), ..., h{n-1}(X) 19 | /// 20 | /// h0(X) = f(X) 21 | /// k=1..n-1 22 | /// hi(X) = (h{i-1}(X) + h{i-1}(-X)) / 2 23 | /// + u_{i-1} * (h{i-1}(X) - h{i-1}(-X)) / 2X 24 | /// 25 | /// P -- [h0], [h1], ..., [h{n-1}] --> V 26 | /// P <--- β ------------------------ V 27 | /// 28 | /// P - h0(β), h1(β), ..., h{n-1}(β) -> V 29 | /// P - h0(-β), h1(-β), ..., h{n-1}(-β) -> V 30 | /// P <--- γ ------------------------ V 31 | /// 32 | /// compute g(X) 33 | /// 34 | /// g(X) = h0(X)(X-β^2) - B0(X) 35 | /// + γ * (h1(X) - B1(X)) 36 | /// + γ^2 * (h2(X) - B2(X)) 37 | /// ... 38 | /// + γ^{n-1} * (h{n-1}(X) - B{n-1}(X)) 39 | /// 40 | /// s.t. 41 | /// g(X) = q(X) * (X-β)(X+β)(X-β^2) 42 | /// 43 | /// P ---- Q=[q] -------------------> V 44 | /// P <--- ζ ---------------------- V 45 | /// 46 | /// compute r(X) 47 | /// 48 | /// r(X) = g_ζ(X) - (ζ - β^2)^2 * q(X) 49 | /// 50 | /// compute w(X) 51 | /// 52 | /// w(X) = r(X) / (X - ζ) 53 | /// 54 | /// P ---- W=[w] ---------------------> V 55 | /// 56 | /// Verification: 57 | /// 1. compute h1(β^2), h2(β^2), ..., h{n-1}(β^2) in O(n) 58 | /// 2. compute B0(ζ), B1(ζ), ..., B{n-1}(ζ) in O(n) 59 | /// 3. compute R 60 | /// 61 | /// R = ((ζ - β^2) * H0 - B0(ζ) * [1]) 62 | /// + γ * (H1 - B1(ζ) * [1]) 63 | /// + γ^2 * (H2 - B2(ζ) * [1]) 64 | /// ... 65 | /// + γ^{n-1} * (H{n-1} - B{n-1}(ζ) * [1]) 66 | /// - (ζ - β^2)^2 * Q 67 | /// 4. check e(R + ζ*W, [1]) ?= e(W, [τ]) 68 | 69 | pub struct MlePCSystem { 70 | kzg10: Rc, 71 | } 72 | 73 | pub struct EvalArgument { 74 | poly_cm_vec: Vec, 75 | evals_pos: Vec, 76 | evals_neg: Vec, 77 | evals_sq: Vec, 78 | evals_pos_proof: Vec, 79 | evals_neg_proof: Vec, 80 | evals_sq_proof: Vec, 81 | } 82 | 83 | pub struct EvalArgumentOpt{ 84 | h_cm_vec: Vec, 85 | h_eval_pos_vec: Vec, 86 | h_eval_neg_vec: Vec, 87 | q_commitment: Commitment, 88 | w_commitment: Commitment, 89 | } 90 | 91 | impl MlePCSystem { 92 | 93 | pub fn setup(kzg10: &Rc) -> Self { 94 | MlePCSystem { 95 | kzg10: kzg10.clone(), 96 | } 97 | } 98 | 99 | pub fn commit(&self, f_mle: &MleEvals) -> Commitment { 100 | let coeffs = f_mle.to_coeffs(); 101 | let f_uni = UniPolynomial::from_coeffs(&coeffs); 102 | let f_cm = self.kzg10.commit_uni_poly(&f_uni); 103 | f_cm 104 | } 105 | 106 | /// Prove the evaluation argument naively (for testing purpose) 107 | /// 108 | /// # Arguments 109 | /// 110 | /// - `f_cm`, a (univariate) commitment for f_mle, Commit(⟦f_mle⟧_n) 111 | /// - `f_mle`, a mle polynomial for f_mle 112 | /// - `us`, a vector of scalars, `us = [u0, u1, ..., u{n-1}]` 113 | /// - `tr`, a transcript for interaction 114 | /// 115 | /// # Returns 116 | /// 117 | /// - v, the value of `f_mle(u0, u1, ..., u{n-1})` 118 | /// - prf, evaluation argument 119 | /// 120 | pub fn prove_opt(&self, 121 | f_cm: &Commitment, 122 | f_mle: &MleEvals, 123 | us: &[Scalar], 124 | tr: &mut Transcript, 125 | ) -> (Scalar, EvalArgumentOpt) { 126 | assert_eq!(f_mle.num_var, us.len()); 127 | 128 | let n = us.len(); 129 | let v = f_mle.evaluate(us); 130 | tr.update_with_g1(&f_cm.group_element); 131 | tr.update_with_scalar_vec(us); 132 | 133 | // Round 1. 134 | 135 | let mut coeffs: Vec = f_mle.to_coeffs(); 136 | // println!("coeffs={}", scalar_vector_to_string(&coeffs)); 137 | 138 | let mut h_uni_cm_vec: Vec = Vec::new(); 139 | let mut h_uni_vec: Vec = Vec::new(); 140 | for i in 0..us.len() { 141 | 142 | let h_uni = UniPolynomial::from_coeffs(&coeffs); 143 | 144 | let h_cm = self.kzg10.commit_uni_poly(&h_uni); 145 | 146 | let coeffs_e: Vec = coeffs.iter().step_by(2).cloned().collect(); 147 | let coeffs_o: Vec = coeffs.iter().skip(1).step_by(2).cloned().collect(); 148 | // debug!("rd={}, coeffs_e={}", i, scalar_vector_to_string(&coeffs_e)); 149 | // debug!("rd={}, coeffs_o={}", i, scalar_vector_to_string(&coeffs_o)); 150 | // debug!("i={}, us[i]={}", i, ScalarExt::to_string(&us[i])); 151 | 152 | coeffs = coeffs_e.iter().zip(coeffs_o.iter()) 153 | .map(| (&e, &o) | e + us[i] * o).collect(); 154 | // println!("coeffs={}", scalar_vector_to_string(&coeffs)); 155 | tr.update_with_g1(&h_cm.group_element); 156 | h_uni_cm_vec.push(h_cm); 157 | h_uni_vec.push(h_uni); 158 | } 159 | 160 | assert_eq!(v, coeffs[0]); 161 | assert_eq!(coeffs.len(), 1); 162 | 163 | // Round 2. 164 | 165 | let beta = tr.generate_challenge(); 166 | debug!("beta={}", ScalarExt::to_string(&beta)); 167 | let beta_sq = beta * beta; 168 | 169 | let mut h_eval_pos_vec: Vec = Vec::new(); 170 | let mut h_eval_neg_vec: Vec = Vec::new(); 171 | let mut h_eval_sq_vec: Vec = Vec::new(); 172 | 173 | // TODO: compute in O(n) 174 | for i in 0..us.len() { 175 | let hi_uni = &h_uni_vec[i]; 176 | let h_eval_pos = hi_uni.evaluate(&beta); 177 | h_eval_pos_vec.push(h_eval_pos); 178 | 179 | let h_eval_neg = hi_uni.evaluate(&(-beta)); 180 | h_eval_neg_vec.push(h_eval_neg); 181 | if i != 0 { 182 | let h_eval_sq = hi_uni.evaluate(&beta_sq); 183 | h_eval_sq_vec.push(h_eval_sq); 184 | // debug!("rd={}, h_eval_sq={}", i, ScalarExt::to_string(&h_eval_sq)); 185 | } 186 | } 187 | h_eval_sq_vec.push(v); 188 | 189 | tr.update_with_scalar_vec(&h_eval_pos_vec); 190 | tr.update_with_scalar_vec(&h_eval_neg_vec); 191 | 192 | // Round 3. 193 | 194 | let gamma = tr.generate_challenge(); 195 | debug!("gamma={}", ScalarExt::to_string(&gamma)); 196 | 197 | let b_uni_vec = { 198 | let mut b_vec = Vec::new(); 199 | let b_uni = UniPolynomial::from_coeffs( 200 | &UniPolynomial::compute_coeffs_from_evals_slow( 201 | &[h_eval_pos_vec[0], h_eval_neg_vec[0]], &[beta, -beta])); 202 | b_vec.push(b_uni); 203 | for i in 1..us.len() { 204 | let b_uni = UniPolynomial::from_coeffs( 205 | &UniPolynomial::compute_coeffs_from_evals_slow( 206 | &[h_eval_pos_vec[i], h_eval_neg_vec[i], h_eval_sq_vec[i-1]], 207 | &[beta, -beta, beta_sq])); 208 | b_vec.push(b_uni); 209 | }; 210 | b_vec 211 | }; 212 | 213 | { // double check -- B_i(X) 214 | for i in 0..us.len() { 215 | assert_eq!(b_uni_vec[i].evaluate(&beta), h_eval_pos_vec[i]); 216 | assert_eq!(b_uni_vec[i].evaluate(&(-beta)), h_eval_neg_vec[i]); 217 | if i != 0 { 218 | assert_eq!(b_uni_vec[i].evaluate(&beta_sq), h_eval_sq_vec[i-1]); 219 | } 220 | } 221 | } 222 | 223 | let g_uni = { 224 | let mut g = UniPolynomial::zero(); 225 | let gi = &h_uni_vec[0] 226 | .sub(&b_uni_vec[0]) 227 | .mul(&UniPolynomial::from_coeffs(&[-beta_sq, Scalar::one()])); 228 | { // double check -- g(X) 229 | assert_eq!(gi.evaluate(&beta), Scalar::zero()); 230 | assert_eq!(gi.evaluate(&(-beta)), Scalar::zero()); 231 | assert_eq!(gi.evaluate(&beta_sq), Scalar::zero()); 232 | } 233 | 234 | g.add_inplace(&gi); 235 | 236 | for i in 1..us.len() { 237 | let gi = &h_uni_vec[i] 238 | .sub(&b_uni_vec[i]) 239 | .mul_scalar(&gamma.exp(i)); 240 | { // double check -- g(X) 241 | assert_eq!(gi.evaluate(&beta), Scalar::zero(), "failed at i={}", i); 242 | assert_eq!(gi.evaluate(&(-beta)), Scalar::zero(), "failed at i={}", i); 243 | assert_eq!(gi.evaluate(&beta_sq), Scalar::zero(), "failed at i={}", i); 244 | } 245 | g.add_inplace(&gi); 246 | } 247 | g 248 | }; 249 | 250 | { // double check -- g(X) 251 | assert_eq!(g_uni.evaluate(&beta), Scalar::zero()); 252 | assert_eq!(g_uni.evaluate(&(-beta)), Scalar::zero()); 253 | assert_eq!(g_uni.evaluate(&beta_sq), Scalar::zero()); 254 | } 255 | 256 | let d_uni = { 257 | let mut d = UniPolynomial::from_coeffs(&[-beta_sq, Scalar::one()]); 258 | d = d.mul(&UniPolynomial::from_coeffs(&[-beta, Scalar::one()])); 259 | d = d.mul(&UniPolynomial::from_coeffs(&[beta, Scalar::one()])); 260 | d 261 | }; 262 | 263 | { // double check -- d(X) 264 | assert_eq!(d_uni.evaluate(&beta), Scalar::zero()); 265 | assert_eq!(d_uni.evaluate(&(-beta)), Scalar::zero()); 266 | assert_eq!(d_uni.evaluate(&beta_sq), Scalar::zero()); 267 | } 268 | 269 | let (q_uni, _r_uni) = g_uni.div(&d_uni); 270 | 271 | { // double check -- q(X) 272 | assert_eq!(_r_uni, UniPolynomial::zero()); 273 | } 274 | 275 | let q_cm = self.kzg10.commit_uni_poly(&q_uni); 276 | 277 | tr.update_with_g1(&q_cm.group_element); 278 | 279 | // Round 4. 280 | 281 | let zeta = tr.generate_challenge(); 282 | 283 | let r_uni = { 284 | let mut r = UniPolynomial::zero(); 285 | 286 | let ri = &h_uni_vec[0] 287 | .sub_scalar(&b_uni_vec[0].evaluate(&zeta)) 288 | .mul_scalar(&(zeta - beta_sq)); 289 | r.add_inplace(&ri); 290 | 291 | for i in 1..n { 292 | let ri = &h_uni_vec[i] 293 | .sub_scalar(&b_uni_vec[i].evaluate(&zeta)) 294 | .mul_scalar(&gamma.exp(i)); 295 | r.add_inplace(&ri); 296 | } 297 | r.sub_inplace(&q_uni.mul_scalar( 298 | &((zeta - beta_sq) * (zeta - beta) * (zeta + beta)))); 299 | r 300 | }; 301 | 302 | { // double check 303 | assert_eq!(r_uni.evaluate(&zeta), Scalar::zero()); 304 | } 305 | 306 | let (w_uni, _r_uni) = r_uni.div_by_linear_divisor(&zeta); 307 | { 308 | assert_eq!(_r_uni, Scalar::zero()); 309 | } 310 | let w_cm = self.kzg10.commit_uni_poly(&w_uni); 311 | 312 | { 313 | assert_eq!(r_uni.evaluate(&beta), w_uni.evaluate(&beta) * (beta - zeta)); 314 | } 315 | 316 | tr.update_with_g1(&w_cm.group_element); 317 | 318 | { //double check 319 | 320 | let b_zeta_vec = { 321 | let domain = &[beta, -beta, beta*beta]; 322 | let mut b = Vec::new(); 323 | 324 | let bi = UniPolynomial::evaluate_from_evals(&[h_eval_pos_vec[0], h_eval_neg_vec[0]], &zeta, &[beta, -beta]); 325 | b.push(bi); 326 | for i in 1..n { 327 | let values = &[h_eval_pos_vec[i], h_eval_neg_vec[i], h_eval_sq_vec[i-1]]; 328 | let bi = UniPolynomial::evaluate_from_evals(values, &zeta, domain); 329 | b.push(bi); 330 | } 331 | b 332 | }; 333 | 334 | for i in 0..n { 335 | assert_eq!(b_zeta_vec[i], b_uni_vec[i].evaluate(&zeta)); 336 | } 337 | 338 | // compute R 339 | 340 | let C_r = { 341 | 342 | let mut r = h_uni_cm_vec[0].sub(&Commitment::commit(&b_zeta_vec[0])).mul_scalar(&(zeta - beta_sq)); 343 | for i in 1..n { 344 | let ri = h_uni_cm_vec[i].sub(&Commitment::commit(&b_zeta_vec[i])).mul_scalar(&gamma.exp(i)); 345 | r = r.add(&ri); 346 | } 347 | r = r.sub(&q_cm.mul_scalar(&((zeta - beta_sq) * (zeta - beta) * (zeta + beta)))); 348 | r 349 | }; 350 | 351 | assert_eq!(C_r.group_element, self.kzg10.commit_uni_poly(&r_uni).group_element); 352 | } 353 | 354 | (v, EvalArgumentOpt { 355 | h_cm_vec: h_uni_cm_vec, 356 | h_eval_pos_vec: h_eval_pos_vec, 357 | h_eval_neg_vec: h_eval_neg_vec, 358 | q_commitment: q_cm, 359 | w_commitment: w_cm, 360 | }) 361 | } 362 | 363 | /// Verify the evaluation argument efficiently 364 | /// 365 | /// # Arguments 366 | /// 367 | /// - `f_cm`, a (univariate) commitment for f_mle, Commit(⟦f_mle⟧_n) 368 | /// - `us`, a vector of scalars, u0, u1, ..., u{n-1} 369 | /// - `v`, the value of `f_mle(u0, u1, ..., u{n-1})` 370 | /// - `prf`, evaluation argument 371 | /// - `tr`, transcript of Fiat-Shamir scheme 372 | /// 373 | /// # Return 374 | /// 375 | /// - true if the evaluation argument is valid 376 | /// 377 | pub fn verify_opt(&self, 378 | // vk: &VerifierKey, 379 | f_cm: &Commitment, 380 | us: &[Scalar], 381 | v: &Scalar, 382 | prf: &EvalArgumentOpt, 383 | tr: &mut Transcript 384 | ) -> bool { 385 | let n = us.len(); 386 | 387 | let domain_size = pow_2(n); 388 | 389 | tr.update_with_g1(&f_cm.group_element); 390 | tr.update_with_scalar_vec(us); 391 | 392 | let EvalArgumentOpt{ 393 | h_cm_vec, 394 | h_eval_pos_vec: h_beta_vec, 395 | h_eval_neg_vec: h_mbeta_vec, 396 | q_commitment: q_cm, 397 | w_commitment: w_cm, 398 | } = prf; 399 | 400 | // Round 1. 401 | 402 | for i in 0..n { 403 | let h_cm = &h_cm_vec[i]; 404 | tr.update_with_g1(&h_cm.group_element); 405 | } 406 | 407 | // Round 2. 408 | 409 | let beta = tr.generate_challenge(); 410 | debug!("V> beta={}", ScalarExt::to_string(&beta)); 411 | 412 | let beta_sq = beta * beta; 413 | tr.update_with_scalar_vec(&h_beta_vec); 414 | tr.update_with_scalar_vec(&h_mbeta_vec); 415 | 416 | // Round 3. 417 | 418 | let gamma = tr.generate_challenge(); 419 | debug!("V> gamma={}", ScalarExt::to_string(&gamma)); 420 | 421 | tr.update_with_g1(&q_cm.group_element); 422 | 423 | // Round 4. 424 | 425 | let zeta = tr.generate_challenge(); 426 | 427 | debug!("V> zeta={}", ScalarExt::to_string(&zeta)); 428 | 429 | tr.update_with_g1(&w_cm.group_element); 430 | 431 | // Verify 432 | 433 | // 1. compute h1(β^2), h2(β^2), ..., h{n-1}(β^2) in O(n) 434 | 435 | let h_beta_sq_vec = { 436 | let mut h = Vec::new(); 437 | for i in 1..=n { 438 | let hi = ((h_beta_vec[i-1] + h_mbeta_vec[i-1])/Scalar::from(2)) 439 | + us[i-1] * (h_beta_vec[i-1] - h_mbeta_vec[i-1])/(Scalar::from(2) * beta); 440 | h.push(hi); 441 | } 442 | h 443 | }; 444 | 445 | // 2. compute B0(ζ), B1(ζ), ..., B{n-1}(ζ) in O(n) 446 | 447 | let b_zeta_vec = { 448 | let domain = &[beta, -beta, beta*beta]; 449 | let mut b = Vec::new(); 450 | 451 | let bi = UniPolynomial::evaluate_from_evals(&[h_beta_vec[0], h_mbeta_vec[0]], &zeta, &[beta, -beta]); 452 | b.push(bi); 453 | for i in 1..n { 454 | let values = &[h_beta_vec[i], h_mbeta_vec[i], h_beta_sq_vec[i-1]]; 455 | let bi = UniPolynomial::evaluate_from_evals(values, &zeta, domain); 456 | b.push(bi); 457 | } 458 | b 459 | }; 460 | 461 | // 3. check evaluation result 462 | 463 | let result_0 = h_beta_sq_vec[n-1] == *v; 464 | 465 | // 4. compute R 466 | 467 | let C_r = { 468 | 469 | let mut r = h_cm_vec[0].sub(&Commitment::commit(&b_zeta_vec[0])).mul_scalar(&(zeta - beta_sq)); 470 | for i in 1..n { 471 | let ri = h_cm_vec[i].sub(&Commitment::commit(&b_zeta_vec[i])).mul_scalar(&gamma.exp(i)); 472 | r = r.add(&ri); 473 | } 474 | r = r.sub(&q_cm.mul_scalar(&((zeta - beta_sq) * (zeta - beta) * (zeta + beta)))); 475 | r 476 | }; 477 | 478 | let h = &self.kzg10.srs.h; 479 | let h_tau = &self.kzg10.srs.h_tau; 480 | let C1 = C_r.add(&w_cm.mul_scalar(&zeta)); 481 | let lhs = pairing(&C1.group_element, h); 482 | let rhs = pairing(&w_cm.group_element, h_tau); 483 | 484 | let result_1 = lhs == rhs; 485 | assert_eq!(result_1, true); 486 | result_0 && result_1 487 | } 488 | 489 | /// Prove the evaluation argument naively (for testing purpose) 490 | /// 491 | /// # Arguments 492 | /// 493 | /// - `f_cm`, a (univariate) commitment for f_mle, Commit(⟦f_mle⟧_n) 494 | /// - `f_mle`, a mle polynomial for f_mle 495 | /// - `us`, a vector of scalars, `us = [u0, u1, ..., u{n-1}]` 496 | /// - `tr`, a transcript for interaction 497 | /// 498 | /// # Returns 499 | /// 500 | /// - v, the value of `f_mle(u0, u1, ..., u{n-1})` 501 | /// - prf, evaluation argument 502 | /// 503 | pub fn prove_plain(&self, 504 | f_cm: &Commitment, 505 | f_mle: &MleEvals, 506 | us: &[Scalar], 507 | tr: &mut Transcript, 508 | ) -> (Scalar, EvalArgument) { 509 | assert_eq!(f_mle.num_var, us.len()); 510 | 511 | let n = us.len(); 512 | let v = f_mle.evaluate(us); 513 | tr.update_with_g1(&f_cm.group_element); 514 | tr.update_with_scalar_vec(us); 515 | 516 | let mut coeffs: Vec = f_mle.to_coeffs(); 517 | println!("coeffs={}", scalar_vector_to_string(&coeffs)); 518 | 519 | // let mut domain_size = pow_2(n); 520 | let mut h_uni_cm_vec: Vec = Vec::new(); 521 | let mut h_uni_vec: Vec = Vec::new(); 522 | for i in (0..us.len()) { 523 | 524 | let h_uni = UniPolynomial::from_coeffs(&coeffs); 525 | 526 | let h_cm = self.kzg10.commit_uni_poly(&h_uni); 527 | 528 | let coeffs_e: Vec = coeffs.iter().step_by(2).cloned().collect(); 529 | let coeffs_o: Vec = coeffs.iter().skip(1).step_by(2).cloned().collect(); 530 | debug!("rd={}, coeffs_e={}", i, scalar_vector_to_string(&coeffs_e)); 531 | debug!("rd={}, coeffs_o={}", i, scalar_vector_to_string(&coeffs_o)); 532 | 533 | debug!("i={}, us[i]={}", i, ScalarExt::to_string(&us[i])); 534 | coeffs = coeffs_e.iter().zip(coeffs_o.iter()) 535 | .map(| (&e, &o) | e + us[i] * o).collect(); 536 | println!("coeffs={}", scalar_vector_to_string(&coeffs)); 537 | // debug!("rd={}, coeffs_next={}", i, scalar_vector_to_string(&coeffs)); 538 | // domain_size /= 2; 539 | tr.update_with_g1(&h_cm.group_element); 540 | h_uni_cm_vec.push(h_cm); 541 | h_uni_vec.push(h_uni); 542 | } 543 | 544 | assert_eq!(v, coeffs[0]); 545 | assert_eq!(coeffs.len(), 1); 546 | 547 | let beta = tr.generate_challenge(); 548 | debug!("beta={}", ScalarExt::to_string(&beta)); 549 | 550 | let mut f_eval_pos_vec: Vec = Vec::new(); 551 | let mut f_eval_pos_proof: Vec = Vec::new(); 552 | let mut f_eval_neg_vec: Vec = Vec::new(); 553 | let mut f_eval_neg_proof: Vec = Vec::new(); 554 | let mut f_eval_sq_vec: Vec = Vec::new(); 555 | let mut f_eval_sq_proof: Vec = Vec::new(); 556 | 557 | // TODO: compute in O(n) 558 | for i in 0..us.len() { 559 | let f_poly = &h_uni_vec[i]; 560 | let (f_eval_pos, eval_proof) = self.kzg10.prove_eval(&f_poly, &beta); 561 | 562 | // assert!(self.kzg10.verify(&f_cm, &eval_proof, &beta, &f_eval_pos)); 563 | f_eval_pos_vec.push(f_eval_pos); 564 | f_eval_pos_proof.push(eval_proof); 565 | 566 | let (f_eval_neg, eval_proof) = self.kzg10.prove_eval(&f_poly, &(-beta)); 567 | f_eval_neg_vec.push(f_eval_neg); 568 | f_eval_neg_proof.push(eval_proof); 569 | 570 | // debug!("rd={}, f_eval_pos={}", i, ScalarExt::to_string(&f_eval_pos)); 571 | // debug!("rd={}, f_eval_neg={}", i, ScalarExt::to_string(&f_eval_neg)); 572 | 573 | if i != 0 { 574 | let (f_eval_sq, eval_proof) = self.kzg10.prove_eval(&f_poly, &(beta * beta)); 575 | f_eval_sq_vec.push(f_eval_sq); 576 | f_eval_sq_proof.push(eval_proof); 577 | debug!("rd={}, f_eval_sq={}", i, ScalarExt::to_string(&f_eval_sq)); 578 | } 579 | } 580 | f_eval_sq_vec.push(v); 581 | 582 | (v, EvalArgument { 583 | poly_cm_vec: h_uni_cm_vec, 584 | evals_pos: f_eval_pos_vec, 585 | evals_pos_proof: f_eval_pos_proof, 586 | evals_neg: f_eval_neg_vec, 587 | evals_neg_proof: f_eval_neg_proof, 588 | evals_sq: f_eval_sq_vec, 589 | evals_sq_proof: f_eval_sq_proof, 590 | }) 591 | } 592 | 593 | /// Verify the evaluation argument naively 594 | /// 595 | /// # Arguments 596 | /// 597 | /// - `f_cm`, a (univariate) commitment for f_mle, Commit(⟦f_mle⟧_n) 598 | /// - `us`, a vector of scalars, `us = [u0, u1, ..., u{n-1}]` 599 | /// - `v`, the value of `f_mle(u0, u1, ..., u{n-1})` 600 | /// - `prf`, evaluation argument 601 | /// - `tr`, transcript of Fiat-Shamir scheme 602 | /// 603 | /// # Returns 604 | /// 605 | /// - true if the evaluation argument is valid 606 | /// 607 | pub fn verify_plain(&self, 608 | f_cm: &Commitment, 609 | us: &[Scalar], 610 | v: &Scalar, 611 | prf: &EvalArgument, 612 | tr: &mut Transcript, 613 | ) -> bool { 614 | 615 | let num_rounds = us.len(); 616 | let mut rho_vec = us.to_vec(); 617 | 618 | tr.update_with_g1(&f_cm.group_element); 619 | tr.update_with_scalar_vec(us); 620 | 621 | let h_cm_vec = &prf.poly_cm_vec; 622 | 623 | for i in (0..num_rounds) { 624 | let h_cm = &h_cm_vec[i]; 625 | tr.update_with_g1(&h_cm.group_element); 626 | } 627 | 628 | let beta = tr.generate_challenge(); 629 | debug!("beta={}", ScalarExt::to_string(&beta)); 630 | 631 | for i in 0..num_rounds { 632 | let rho = us[i]; 633 | let rhs = ((prf.evals_pos[i] + prf.evals_neg[i]) / Scalar::from(2)) + rho * (prf.evals_pos[i] - prf.evals_neg[i]) / (Scalar::from(2) * beta); 634 | debug!("rd={}, rhs={}", i, ScalarExt::to_string(&rhs)); 635 | debug!("rd={}, sq={}", i, ScalarExt::to_string(&prf.evals_sq[i])); 636 | assert_eq!(prf.evals_sq[i], rhs); 637 | } 638 | 639 | for i in 0..prf.evals_pos.len() { 640 | let b = self.kzg10.verify_eval( 641 | &prf.poly_cm_vec[i], 642 | &beta, 643 | &prf.evals_pos[i], 644 | &prf.evals_pos_proof[i], 645 | ); 646 | assert!(b, "failed at round {}", i); 647 | } 648 | 649 | for i in 0..prf.evals_neg.len() { 650 | let b = self.kzg10.verify_eval( 651 | &prf.poly_cm_vec[i], 652 | &(-beta), 653 | &prf.evals_neg[i], 654 | &prf.evals_neg_proof[i], 655 | ); 656 | assert!(b, "failed at round {}", i); 657 | } 658 | 659 | for i in 0..prf.evals_sq_proof.len() { 660 | let b = self.kzg10.verify_eval( 661 | &prf.poly_cm_vec[i+1], 662 | &(beta * beta), 663 | &prf.evals_sq[i], 664 | &prf.evals_sq_proof[i], 665 | ); 666 | assert!(b, "failed at round {}", i); 667 | } 668 | 669 | prf.evals_sq[num_rounds-1] == *v 670 | } 671 | } 672 | 673 | mod tests { 674 | use super::*; 675 | 676 | #[test] 677 | fn test_prove_verify_plain() { 678 | init_logger(); 679 | 680 | let rng = &mut ark_std::test_rng(); 681 | // f(X2, X1, X0) = 1 + X0 + 2*X1 + 0*X0X1 + 4*X2 682 | let f_vs = Scalar::from_usize_vector(&[1,2,3,4,5,6,7,8]); 683 | let f_mle = MleEvals::new(&f_vs); 684 | let rs = Scalar::from_usize_vector(&[1,2,3]); 685 | let mut tr = Transcript::new(); 686 | 687 | let kzg10_sys = Rc::new(KZG10PCS::setup(100, rng)); 688 | let mle_pcs = MlePCSystem::setup(&kzg10_sys); 689 | 690 | let f_cm = mle_pcs.commit(&f_mle); 691 | 692 | let (v, prf) = mle_pcs.prove_plain(&f_cm, &f_mle, &rs, &mut tr.clone()); 693 | debug!("v={}", ScalarExt::to_string(&v)); 694 | 695 | let b = mle_pcs.verify_plain(&f_cm, &rs, &v, &prf, &mut tr.clone()); 696 | assert!(b); 697 | } 698 | 699 | #[test] 700 | fn test_prove_verify_opt() { 701 | init_logger(); 702 | 703 | let rng = &mut ark_std::test_rng(); 704 | // f(X2, X1, X0) = 1 + X0 + 2*X1 + 0*X0X1 + 4*X2 705 | let f_vs = Scalar::from_usize_vector(&[1,2,3,4,5,6,7,8]); 706 | let f_mle = MleEvals::new(&f_vs); 707 | let rs = Scalar::from_usize_vector(&[1,2,3]); 708 | let mut tr = Transcript::new(); 709 | 710 | let kzg10_sys = Rc::new(KZG10PCS::setup(100, rng)); 711 | let mle_pcs = MlePCSystem::setup(&kzg10_sys); 712 | 713 | let f_cm = mle_pcs.commit(&f_mle); 714 | 715 | let (v, prf) = mle_pcs.prove_opt(&f_cm, &f_mle, &rs, &mut tr.clone()); 716 | debug!("v={}", ScalarExt::to_string(&v)); 717 | 718 | let b = mle_pcs.verify_opt(&f_cm, &rs, &v, &prf, &mut tr.clone()); 719 | assert!(b); 720 | } 721 | } -------------------------------------------------------------------------------- /univarization/src/bits.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | 4 | pub fn bits_nand(a: &Vec, b: &Vec) -> bool { 5 | a.iter().zip(b.iter()).all(|(a, b)| !(*a && *b)) 6 | } 7 | 8 | 9 | // NOTE: 6 = (1, 1, 0), big-endian 10 | pub fn bits_BE(i: usize, num_bits: usize) -> Vec { 11 | (0..num_bits) 12 | .map(|shift_amount| ((i & (1 << (num_bits - shift_amount - 1))) > 0)) 13 | .collect::>() 14 | } 15 | 16 | pub fn bits_LE(n: usize, num_bits: usize) -> Vec { 17 | let mut bits = bits_BE(n, num_bits); 18 | bits.reverse(); 19 | bits 20 | } 21 | 22 | 23 | // NOTE: (1, 1, 0) = 6, big-endian 24 | pub fn bits_BE_to_integer(bits: &[bool]) -> usize { 25 | let bits: Vec = bits.iter().map(|b| if *b {1} else {0}).collect(); 26 | bits.into_iter().fold(0, |acc, b| acc * 2 + b) 27 | } 28 | 29 | // NOTE: (1, 1, 0) = 3, little-endian 30 | pub fn bits_LE_to_integer(bits: &[bool]) -> usize { 31 | let bits: Vec = bits.iter().map(|b| if *b {1} else {0}).collect(); 32 | bits.into_iter().rev().fold(0, |acc, b| acc * 2 + b) 33 | } 34 | 35 | pub fn uint_LE_concat_3(g: usize, g_bits: usize, x: usize, x_bits: usize, y: usize, y_bits: usize) -> usize { 36 | assert!(g < pow_2(g_bits)); 37 | assert!(x < pow_2(x_bits)); 38 | assert!(y < pow_2(y_bits)); 39 | g + (x << g_bits) + (y << (g_bits + x_bits)) 40 | } 41 | 42 | pub fn uint_LE_split_3(gxy: usize, g_bits: usize, x_bits: usize, y_bits: usize) -> (usize, usize, usize) { 43 | let xy = gxy >> g_bits; 44 | let g = gxy - (xy << g_bits); 45 | let y = xy >> x_bits; 46 | let x = xy - (y << x_bits); 47 | (g, x, y) 48 | } 49 | 50 | pub fn bit_reverse(i: usize, k_log: usize) -> usize { 51 | let i_bits = bits_LE(i, k_log); 52 | let i_reversed = bits_BE_to_integer(&i_bits); 53 | i_reversed 54 | } 55 | 56 | pub fn scalar_from_bits_BE(i: usize, num_bits: usize) -> Vec { 57 | bits_BE(i, num_bits).iter().map(| &b | if b {Scalar::one()} else {Scalar::zero()}).collect() 58 | } 59 | 60 | pub fn scalar_from_bits_LE(i: usize, num_bits: usize) -> Vec { 61 | bits_LE(i, num_bits).iter().map(| &b | if b {Scalar::one()} else {Scalar::zero()}).collect() 62 | } 63 | 64 | pub fn bits_BE_to_scalar(bits: Vec) -> Scalar { 65 | let i = bits_BE_to_integer(&bits); 66 | Scalar::from(i as i64) 67 | } 68 | 69 | pub fn bits_LE_to_scalar(bits: Vec) -> Scalar { 70 | let i = bits_LE_to_integer(&bits); 71 | Scalar::from(i as i64) 72 | } 73 | 74 | 75 | 76 | 77 | 78 | #[cfg(test)] 79 | mod tests { 80 | use super::*; 81 | use std::collections::HashMap; 82 | 83 | #[test] 84 | fn test_bits_nand() { 85 | assert_eq!(bits_nand(&vec![true, true], &vec![true, true]), false); 86 | assert_eq!(bits_nand(&vec![true, true], &vec![true, false]), false); 87 | assert_eq!(bits_nand(&vec![true, true], &vec![false, true]), false); 88 | assert_eq!(bits_nand(&vec![true, true], &vec![false, false]), true); 89 | assert_eq!(bits_nand(&vec![true, false], &vec![true, true]), false); 90 | assert_eq!(bits_nand(&vec![true, false], &vec![true, false]), false); 91 | assert_eq!(bits_nand(&vec![true, false], &vec![false, true]), true); 92 | assert_eq!(bits_nand(&vec![true, false], &vec![false, false]), true); 93 | assert_eq!(bits_nand(&vec![false, true], &vec![true, true]), false); 94 | assert_eq!(bits_nand(&vec![false, true], &vec![true, false]), true); 95 | assert_eq!(bits_nand(&vec![false, true], &vec![false, true]), false); 96 | assert_eq!(bits_nand(&vec![false, true], &vec![false, false]), true); 97 | assert_eq!(bits_nand(&vec![false, false], &vec![true, true]), true); 98 | assert_eq!(bits_nand(&vec![false, false], &vec![true, false]), true); 99 | assert_eq!(bits_nand(&vec![false, false], &vec![false, true]), true); 100 | assert_eq!(bits_nand(&vec![false, false], &vec![false, false]), true); 101 | } 102 | 103 | 104 | #[test] 105 | fn test_bits_to_integer() { 106 | let bs = bits_BE(6, 3); 107 | assert_eq!(bits_BE_to_integer(&bs), 6); 108 | } 109 | 110 | #[test] 111 | fn test_bit_reverse() { 112 | let i_bits = bit_reverse(6, 3); 113 | assert_eq!(i_bits, 3); 114 | } 115 | 116 | #[test] 117 | fn test_uint_LE_concat_3_and_split_3() { 118 | let g = 2; 119 | let g_bits = 2; 120 | let x = 3; 121 | let x_bits = 2; 122 | let y = 4; 123 | let y_bits = 3; 124 | 125 | let concat = uint_LE_concat_3(g, g_bits, x, x_bits, y, y_bits); 126 | let (g_res, x_res, y_res) = uint_LE_split_3(concat, g_bits, x_bits, y_bits); 127 | assert_eq!(g, g_res); 128 | assert_eq!(x, x_res); 129 | assert_eq!(y, y_res); 130 | } 131 | } -------------------------------------------------------------------------------- /univarization/src/groupsim.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub struct G1 { 5 | pub(crate) x: Scalar, 6 | } 7 | 8 | #[derive(Debug, PartialEq, Clone)] 9 | pub struct G2 { 10 | pub(crate) x: Scalar, 11 | } 12 | 13 | #[derive(Debug, PartialEq, Clone)] 14 | pub struct GT { 15 | pub(crate) x: Scalar, 16 | } 17 | 18 | impl G1 { 19 | pub fn new(x: Scalar) -> Self { 20 | G1 { x: x } 21 | } 22 | 23 | pub fn identity() -> Self{ 24 | G1::new(Scalar::zero()) 25 | } 26 | 27 | pub fn generator() -> Self{ 28 | G1::new(Scalar::one()) 29 | } 30 | 31 | pub fn add(&self, other: &G1, neg: bool) -> G1 { 32 | if neg { 33 | G1::new(self.x - other.x) 34 | } else { 35 | G1::new(self.x + other.x) 36 | } 37 | } 38 | 39 | pub fn mul_int(&self, i: i64) -> G1 { 40 | G1::new(self.x * Scalar::from_i64(i)) 41 | } 42 | 43 | pub fn mul_scalar(&self, s: &Scalar) -> G1 { 44 | G1::new(self.x * s) 45 | } 46 | } 47 | 48 | impl G2 { 49 | pub fn new(x: Scalar) -> Self { 50 | G2 { x: x } 51 | } 52 | 53 | pub fn identity() -> Self{ 54 | G2::new(Scalar::zero()) 55 | } 56 | 57 | pub fn generator() -> Self{ 58 | G2::new(Scalar::one()) 59 | } 60 | 61 | pub fn add(&self, other: &G2, neg: bool) -> G2 { 62 | if neg { 63 | G2::new(self.x - other.x) 64 | } else { 65 | G2::new(self.x + other.x) 66 | } 67 | } 68 | 69 | pub fn mul_int(&self, i: i64) -> G2 { 70 | G2::new(self.x * Scalar::from_i64(i)) 71 | } 72 | 73 | pub fn mul_scalar(&self, s: &Scalar) -> G2 { 74 | G2::new(self.x * s) 75 | } 76 | } 77 | 78 | impl GT { 79 | pub fn new(x: Scalar) -> Self { 80 | GT { x: x } 81 | } 82 | 83 | pub fn add(&self, other: >) -> GT { 84 | GT::new(self.x + other.x) 85 | } 86 | } 87 | 88 | pub fn pairing(g1: &G1, g2: &G2) -> GT { 89 | GT::new(g1.x * g2.x) 90 | } -------------------------------------------------------------------------------- /univarization/src/kzg10.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | use crate::unipoly::UniPolynomial; 4 | use crate::groupsim::*; 5 | 6 | use log::{debug, info, warn}; 7 | 8 | // use ark_poly::domain; 9 | use ark_std::rand::Rng; 10 | use ark_ec::msm::{FixedBaseMSM, VariableBaseMSM}; 11 | use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; 12 | use ark_ff::ToBytes; 13 | use ark_std::{ 14 | io::{Read, Result as IoResult, Write}, 15 | vec::Vec, 16 | }; 17 | 18 | #[derive(Clone)] 19 | pub struct SRS { 20 | // secret: Scalar, 21 | pub max_degree: usize, 22 | pub g: G1, 23 | pub h: G2, 24 | pub h_tau: G2, 25 | pub powers_over_G1: Vec, 26 | pub powers_over_G2: Vec, 27 | } 28 | 29 | #[derive(Clone)] 30 | pub struct KZG10PCS { 31 | pub srs: SRS, 32 | } 33 | 34 | #[derive(Clone)] 35 | pub struct Commitment { 36 | pub group_element: G1, 37 | pub internal_values: Vec, // for DEBUG (TODO: to be removed) 38 | } 39 | 40 | impl Commitment { 41 | 42 | 43 | pub fn commit(v: &Scalar) -> Self { 44 | Commitment { 45 | group_element: G1::new(*v), 46 | internal_values: vec![*v], 47 | } 48 | } 49 | pub fn add(&self, rhs: &Self) -> Self { 50 | let mut cv: Vec = Vec::new(); 51 | let av = &self.internal_values; 52 | let bv = &rhs.internal_values; 53 | if av.len() >= bv.len() { 54 | let mut cv = av.clone(); 55 | for (i, bv) in bv.iter().enumerate() { 56 | cv[i] += bv; 57 | } 58 | } else { 59 | let mut cv = bv.clone(); 60 | for (i, av) in av.iter().enumerate() { 61 | cv[i] += av; 62 | } 63 | } 64 | 65 | Commitment{ 66 | group_element: self.group_element.add(&rhs.group_element, false), 67 | internal_values: cv, 68 | } 69 | } 70 | 71 | pub fn sub(&self, rhs: &Self) -> Self { 72 | let mut cv: Vec = Vec::new(); 73 | let av = &self.internal_values; 74 | let bv = &rhs.internal_values; 75 | 76 | if av.len() >= bv.len() { 77 | let mut cv = av.clone(); 78 | for (i, bv) in bv.iter().enumerate() { 79 | cv[i] -= bv; 80 | } 81 | } else { 82 | let mut cv = bv.clone(); 83 | for (i, av) in av.iter().enumerate() { 84 | cv[i] -= av; 85 | } 86 | } 87 | 88 | Commitment{ 89 | group_element: self.group_element.add(&rhs.group_element, true), 90 | internal_values: cv} 91 | } 92 | 93 | pub fn mul_scalar(&self, s: &Scalar) -> Self { 94 | let mut cv: Vec = Vec::new(); 95 | let av = &self.internal_values; 96 | for (i, av) in av.iter().enumerate() { 97 | cv.push((*s) * (*av)); 98 | } 99 | Commitment{ 100 | group_element: self.group_element.mul_scalar(s), 101 | internal_values: cv, 102 | } 103 | } 104 | } 105 | 106 | #[derive(Clone)] 107 | pub struct EvalArgument { 108 | pub(crate) commitment: Commitment, 109 | } 110 | 111 | #[derive(Clone)] 112 | pub struct DegreeBoundArgument { 113 | pub(crate) commitment: Commitment, 114 | degree_bound: usize, 115 | max_degree: usize, 116 | } 117 | 118 | /* 119 | // TODO: mock implementation of KZG10 120 | impl KZG10PCS { 121 | 122 | pub fn setup(max_degree: usize) -> Self { 123 | // let beta = Scalar::rand(rng); 124 | let beta = Scalar::from_u64(2); 125 | 126 | let srs = StructuralReferenceString { 127 | secret: beta, 128 | max_degree: max_degree, 129 | }; 130 | 131 | KZG10PCS { 132 | srs: srs, 133 | } 134 | } 135 | 136 | pub fn commit(&self, polynomial: &UniPolynomial) -> Commitment { 137 | 138 | assert!(polynomial.degree < self.srs.max_degree); 139 | 140 | let coeffs = &polynomial.coeffs; 141 | 142 | Commitment { 143 | values: coeffs.clone(), 144 | } 145 | } 146 | 147 | pub fn commit_poly(&self, polynomial: &FftUniPolynomial) -> Commitment { 148 | 149 | assert!(polynomial.degree < self.srs.max_degree); 150 | 151 | let coeffs = &polynomial.coeffs; 152 | 153 | Commitment { 154 | values: coeffs.clone(), 155 | } 156 | } 157 | 158 | pub fn commit_evals(&self, evals: &Vec, domain_size: usize) -> Commitment { 159 | assert!(domain_size.is_power_of_two()); 160 | let poly = FftUniPolynomial::from_evals_fft(&evals, domain_size); 161 | Commitment { 162 | values: poly.coeffs.clone(), 163 | } 164 | } 165 | 166 | pub fn commit_values(&self, values: &[Scalar]) -> Commitment { 167 | Commitment { 168 | values: values.to_vec(), 169 | } 170 | } 171 | 172 | pub fn open(&self, commitment: &Commitment, polynomial: &UniPolynomial) -> bool { 173 | let coeffs = &polynomial.coeffs; 174 | let s_vec = &commitment.values; 175 | coeffs.iter().zip(s_vec.iter()) 176 | .map(| (&c, &s) | c == s).fold(true, |acc, x| acc && x) 177 | } 178 | 179 | pub fn open_with_values(&self, commitment: &Commitment, values: &[Scalar]) -> bool { 180 | let s_vec = &commitment.values; 181 | assert_eq!(s_vec.len(), values.len()); 182 | s_vec.iter().zip(values.iter()) 183 | .map(| (&c, &s) | c == s).fold(true, |acc, x| acc && x) 184 | } 185 | 186 | pub fn prove_eval(&self, polynomial: &UniPolynomial, x: &Scalar) -> (Scalar, EvalArgument) { 187 | let result = polynomial.evaluate(x); 188 | (result, EvalArgument{eval_at_x: result}) 189 | } 190 | 191 | pub fn verify_eval(&self, commitment: &Commitment, eval_argument: &EvalArgument, x: &Scalar) -> bool { 192 | let coeffs = &commitment.values; 193 | let result = UniPolynomial::evaluate_from_coeffs(&coeffs, x); 194 | 195 | result == eval_argument.eval_at_x 196 | } 197 | 198 | pub fn prove(&self, polynomial: &FftUniPolynomial, x: &Scalar) -> (Scalar, EvalArgument) { 199 | let result = polynomial.evaluate(x); 200 | (result, EvalArgument{eval_at_x: result}) 201 | } 202 | 203 | pub fn verify(&self, 204 | cm: &Commitment, 205 | eval_argument: &EvalArgument, 206 | x: &Scalar, 207 | e: &Scalar, 208 | ) -> bool { 209 | let coeffs = &cm.values; 210 | let poly = FftUniPolynomial::from_coeffs_fft(&coeffs); 211 | let result = poly.evaluate(x); 212 | result == *e && 213 | result == eval_argument.eval_at_x 214 | } 215 | 216 | // Prove that deg(f) < degree_bound 217 | // NOTE: strictly less than 218 | pub fn prove_degree_bound(&self, 219 | commitment: &Commitment, 220 | polynomial: &FftUniPolynomial, 221 | degree_bound: usize, 222 | ) -> DegreeBoundArgument { 223 | assert!(polynomial.degree < degree_bound); 224 | DegreeBoundArgument { 225 | degree: polynomial.degree, 226 | } 227 | } 228 | 229 | // Prove that deg(f) < degree_bound 230 | // NOTE: strictly less than 231 | pub fn prove_degree_bound_2(&self, 232 | commitment: &Commitment, 233 | polynomial: &unipoly2::UniPolynomial, 234 | degree_bound: usize, 235 | ) -> DegreeBoundArgument { 236 | assert!(polynomial.degree < degree_bound); 237 | DegreeBoundArgument { 238 | degree: polynomial.degree, 239 | } 240 | } 241 | 242 | // Verify that deg(f) < degree_bound 243 | // NOTE: strictly less than 244 | pub fn verify_degree_bound(&self, 245 | commitment: &Commitment, 246 | degree_bound: usize, 247 | deg_argument: &DegreeBoundArgument 248 | ) -> bool { 249 | let coeffs = &commitment.values; 250 | // TODO: trim the leading zeros 251 | coeffs.len() <= degree_bound 252 | && deg_argument.degree < degree_bound 253 | } 254 | } 255 | */ 256 | 257 | impl ToBytes for DegreeBoundArgument { 258 | fn write(&self, writer: W) -> Result<(), ark_std::io::Error> { 259 | self.commitment.group_element.x.write(writer)?; 260 | Ok(()) 261 | } 262 | } 263 | 264 | // TODO: mock implementation of KZG10 265 | impl KZG10PCS { 266 | 267 | pub fn setup(max_degree: usize, rng: &mut R) -> Self 268 | { 269 | debug!("[setup] start to setup..."); 270 | 271 | // generate the trapdoor: τ 272 | let tau = Scalar::rand(rng); 273 | 274 | let g1 = G1::generator(); 275 | let g2 = G2::generator(); 276 | 277 | debug!("[setup] generate...ok."); 278 | 279 | let mut powers_of_tau = vec![Scalar::one()]; 280 | 281 | let mut cur = tau; 282 | for _ in 0..max_degree { 283 | powers_of_tau.push(cur); 284 | cur *= τ 285 | } 286 | 287 | let powers_of_g1: Vec = (0..max_degree).map(|i| g1.mul_scalar(&powers_of_tau[i])).collect(); 288 | let powers_of_g2: Vec = (0..max_degree).map(|i| g2.mul_scalar(&powers_of_tau[i])).collect(); 289 | 290 | debug!( 291 | "[setup] generate powers_of_g1...ok. max_degree = {}", 292 | max_degree 293 | ); 294 | 295 | let h_tau = g2.mul_scalar(&tau); 296 | let srs = SRS { 297 | g: g1, 298 | h: g2, 299 | h_tau: h_tau, 300 | max_degree, 301 | powers_over_G1: powers_of_g1, 302 | powers_over_G2: powers_of_g2, 303 | }; 304 | debug!("[setup] complete"); 305 | Self { 306 | srs: srs, 307 | } 308 | } 309 | 310 | // pub fn commit(&self, polynomial: &UniPolynomial) -> Commitment { 311 | 312 | // assert!(polynomial.degree < self.srs.max_degree); 313 | 314 | // let coeffs = &polynomial.coeffs; 315 | 316 | // Commitment { 317 | // values: coeffs.clone(), 318 | // } 319 | // } 320 | 321 | // Commit a polynomial in the form of a vector of coefficients 322 | pub fn commit_uni_poly(&self, poly: &UniPolynomial) -> Commitment { 323 | 324 | assert!(poly.degree < self.srs.max_degree); 325 | 326 | let coeffs = &poly.coeffs; 327 | 328 | let powers_of_g1 = &self.srs.powers_over_G1; 329 | 330 | let values: Vec = coeffs.iter().enumerate().map(|(i, c)| powers_of_g1[i].mul_scalar(c)).collect(); 331 | 332 | let mut g = G1::identity(); 333 | for i in 0..values.len() { 334 | g = g.add(&values[i], false); 335 | } 336 | 337 | Commitment { 338 | group_element: g, 339 | internal_values: coeffs.clone(), 340 | } 341 | } 342 | 343 | // Commit a polynomial in the form of a vector of evaluations 344 | // TODO: lagrange basis srs 345 | // pub fn commit_evals(&self, evals: &Vec, domain_size: usize) -> Commitment { 346 | // assert!(domain_size.is_power_of_two()); 347 | // let poly = FftUniPolynomial::from_evals_fft(&evals, domain_size); 348 | // Commitment { 349 | // values: poly.coeffs.clone(), 350 | // } 351 | // } 352 | 353 | pub fn commit_values(&self, values: &[Scalar]) -> Commitment { 354 | assert!(values.len() < self.srs.max_degree); 355 | 356 | let powers_of_g1 = &self.srs.powers_over_G1; 357 | 358 | let cm_vec: Vec = values.iter().enumerate().map(|(i, c)| powers_of_g1[i].mul_scalar(c)).collect(); 359 | 360 | let mut cm = G1::identity(); 361 | for i in 0..values.len() { 362 | cm = cm.add(&cm_vec[i], false); 363 | } 364 | Commitment { 365 | group_element: cm, 366 | internal_values: values.to_vec(), 367 | } 368 | } 369 | 370 | pub fn open_uni_poly(&self, cm: &Commitment, poly: &UniPolynomial) -> bool { 371 | let coeffs = &poly.coeffs; 372 | let s_vec = &cm.internal_values; 373 | coeffs.iter().zip(s_vec.iter()) 374 | .map(| (&c, &s) | c == s).fold(true, |acc, x| acc && x) 375 | } 376 | 377 | pub fn open_with_values(&self, cm: &Commitment, values: &[Scalar]) -> bool { 378 | let s_vec = &cm.internal_values; 379 | assert_eq!(s_vec.len(), values.len()); 380 | 381 | s_vec.iter().zip(values.iter()) 382 | .map(| (&c, &s) | c == s).fold(true, |acc, x| acc && x) 383 | } 384 | 385 | pub fn prove_eval(&self, poly: &UniPolynomial, u: &Scalar) -> (Scalar, EvalArgument) { 386 | let v = poly.evaluate(u); 387 | let (q, r) = poly.sub_scalar(&v).div_by_linear_divisor(u); 388 | assert_eq!(r, Scalar::zero()); 389 | assert_eq!(q.evaluate(&(*u + Scalar::one())) * (*u + Scalar::one() - u), poly.evaluate(&(*u+Scalar::one())) - v); 390 | let cm_q = self.commit_uni_poly(&q); 391 | (v, EvalArgument{ commitment: cm_q }) 392 | } 393 | 394 | // ([f]-v[1]) * 1 = [q] * ([τ] - u[1]) 395 | // ([f] - [v] + u[q]) * [1] = [q] * [τ] 396 | pub fn verify_eval(&self, cm: &Commitment, u: &Scalar, v: &Scalar, prf: &EvalArgument) -> bool { 397 | let f_cm = cm.group_element.clone(); 398 | let q_cm = prf.commitment.group_element.clone(); 399 | let v_cm = self.srs.g.mul_scalar(v); 400 | let uq_cm = q_cm.mul_scalar(u); 401 | let lhs = pairing(&(f_cm.add(&v_cm, true).add(&uq_cm, false)), &self.srs.h); 402 | let rhs = pairing(&q_cm, &self.srs.h_tau); 403 | lhs == rhs 404 | } 405 | 406 | // pub fn prove(&self, polynomial: &FftUniPolynomial, x: &Scalar) -> (Scalar, EvalArgument) { 407 | // let result = polynomial.evaluate(x); 408 | // (result, EvalArgument{eval_at_x: result}) 409 | // } 410 | 411 | // pub fn verify(&self, 412 | // cm: &Commitment, 413 | // eval_argument: &EvalArgument, 414 | // x: &Scalar, 415 | // e: &Scalar, 416 | // ) -> bool { 417 | // let coeffs = &cm.values; 418 | // let poly = FftUniPolynomial::from_coeffs_fft(&coeffs); 419 | // let result = poly.evaluate(x); 420 | // result == *e && 421 | // result == eval_argument.eval_at_x 422 | // } 423 | 424 | // Prove that deg(f) < degree_bound 425 | // ([f(τ)] * [τ^{D-d-1}] = [f * τ^{D-d-1}] * [1] 426 | // NOTE: strictly less than 427 | pub fn prove_degree_bound(&self, 428 | f: &UniPolynomial, 429 | deg_bound: usize, 430 | ) -> DegreeBoundArgument { 431 | assert!(f.degree < deg_bound); 432 | let x_uni = UniPolynomial::from_coeffs(&{ 433 | let mut coe = vec![Scalar::zero(); self.srs.max_degree - deg_bound - 1]; 434 | coe.push(Scalar::one()); 435 | coe 436 | }); 437 | let fx_uni = f.mul(&x_uni); 438 | let fx_cm = self.commit_uni_poly(&fx_uni); 439 | DegreeBoundArgument { 440 | commitment: fx_cm, 441 | degree_bound: deg_bound, 442 | max_degree: self.srs.max_degree, 443 | } 444 | } 445 | 446 | // Verify that deg(f) < degree_bound 447 | // NOTE: strictly less than 448 | pub fn verify_degree_bound(&self, 449 | f_cm: &Commitment, 450 | deg_bound: usize, 451 | deg_arg: &DegreeBoundArgument 452 | ) -> bool { 453 | assert_eq!(deg_arg.max_degree, self.srs.max_degree); 454 | 455 | let x_in_g2 = &self.srs.powers_over_G2[deg_arg.max_degree - deg_bound - 1]; 456 | let fx_cm = °_arg.commitment; 457 | let lhs = pairing(&f_cm.group_element, x_in_g2); 458 | let rhs = pairing(&fx_cm.group_element, &self.srs.h); 459 | lhs == rhs 460 | } 461 | 462 | pub fn prove_eval_and_deg(&self, 463 | f: &UniPolynomial, 464 | u: &Scalar, 465 | deg_bound: usize, 466 | ) -> (Scalar, EvalArgument, DegreeBoundArgument) { 467 | let (v, prf) = self.prove_eval(&f, &u); 468 | let deg_prf = self.prove_degree_bound(&f, deg_bound); 469 | (v, prf, deg_prf) 470 | } 471 | 472 | pub fn verify_eval_and_deg(&self, 473 | f_cm: &Commitment, 474 | u: &Scalar, 475 | v: &Scalar, 476 | deg_bound: usize, 477 | eval_prf: &EvalArgument, 478 | deg_prf: &DegreeBoundArgument, 479 | ) -> bool { 480 | self.verify_eval(&f_cm, &u, &v, &eval_prf) && 481 | self.verify_degree_bound(&f_cm, deg_bound, °_prf) 482 | } 483 | 484 | pub fn check_commitment(&self, cm: &Commitment, poly: &UniPolynomial) -> bool { 485 | let coeffs = &poly.coeffs; 486 | let s_vec = &cm.internal_values; 487 | coeffs.iter().zip(s_vec.iter()) 488 | .map(| (&c, &s) | c == s).fold(true, |acc, x| acc && x) 489 | } 490 | } 491 | 492 | 493 | 494 | mod tests { 495 | use crate::*; 496 | use super::*; 497 | 498 | #[test] 499 | fn test_kzg10_commit_open() { 500 | let mut rng = ark_std::test_rng(); 501 | 502 | let coeffs = Scalar::from_usize_vector(&[2, 0, 1]); 503 | let f = UniPolynomial::from_coeffs(&coeffs); 504 | let kzg10_pcs = KZG10PCS::setup(100, &mut rng); 505 | let f_cm = kzg10_pcs.commit_uni_poly(&f); 506 | let b = kzg10_pcs.open_uni_poly(&f_cm, &f); 507 | assert!(b); 508 | } 509 | 510 | #[test] 511 | fn test_kzg10_eval_prove_verify() { 512 | let mut rng = ark_std::test_rng(); 513 | 514 | // f(X) = 2* X^2 + 1 515 | // f(3) = 19 516 | 517 | let coeffs = Scalar::from_usize_vector(&[1, 0, 2]); 518 | let f = UniPolynomial::from_coeffs(&coeffs); 519 | let u = Scalar::from(3); 520 | println!("eval_at_0={}", f.evaluate(&(Scalar::from(0)))); 521 | println!("eval_at_1={}", f.evaluate(&(Scalar::from(1)))); 522 | println!("eval_at_2={}", f.evaluate(&(Scalar::from(2)))); 523 | 524 | let kzg10_pcs = KZG10PCS::setup(100, &mut rng); 525 | let f_cm = kzg10_pcs.commit_uni_poly(&f); 526 | 527 | let (eval_at_u, eval_prf) = kzg10_pcs.prove_eval(&f, &u); 528 | println!("eval_at_u={}", ScalarExt::to_string(&eval_at_u)); 529 | 530 | assert_eq!(eval_at_u, Scalar::from(19)); 531 | let b = kzg10_pcs.verify_eval(&f_cm, &u, &eval_at_u, &eval_prf); 532 | assert!(b); 533 | } 534 | 535 | #[test] 536 | fn test_kzg10_degree_bound_prove_verify() { 537 | let mut rng = ark_std::test_rng(); 538 | 539 | // f(X) = 2* X^2 + 1 540 | // f(3) = 19 541 | 542 | let coeffs = Scalar::from_usize_vector(&[1, 0, 2]); 543 | let f = UniPolynomial::from_coeffs(&coeffs); 544 | 545 | let kzg10_pcs = KZG10PCS::setup(100, &mut rng); 546 | let f_cm = kzg10_pcs.commit_uni_poly(&f); 547 | 548 | let deg_prf = kzg10_pcs.prove_degree_bound(&f, 3); 549 | let b = kzg10_pcs.verify_degree_bound(&f_cm, 3, °_prf); 550 | assert!(b); 551 | } 552 | } 553 | 554 | -------------------------------------------------------------------------------- /univarization/src/lib.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Display; 2 | use core::fmt; 3 | 4 | use ark_bn254::Fr; 5 | use ark_bn254::FrParameters; 6 | use ark_ec::group; 7 | use ark_std::{vec::Vec, One, Zero, UniformRand}; 8 | // use ark_std::rand::Rng; 9 | use ark_ff::{Field, PrimeField, FftField, FftParameters, FpParameters, BigInteger, BigInteger256, ToBytes}; 10 | use ark_std::rand::Rng; 11 | 12 | use env_logger::Env; 13 | 14 | pub type Scalar = Fr; 15 | // pub type G1 = as ark_ec::PairingEngine>::G1Affine; 16 | // pub type G2 = as ark_ec::PairingEngine>::G2Affine; 17 | // pub type G1Projective = as ark_ec::PairingEngine>::G1Projective; 18 | // pub type G2Projective = as ark_ec::PairingEngine>::G2Projective; 19 | 20 | pub type G1 = groupsim::G1; 21 | pub type G2 = groupsim::G2; 22 | pub type GT = groupsim::GT; 23 | 24 | pub mod groupsim; 25 | pub mod bits; 26 | pub mod unipoly; 27 | pub mod kzg10; // TODO: mock implementation of KZG10 28 | pub mod mle; 29 | pub mod sumcheck; 30 | pub mod transcript; 31 | 32 | pub mod unisumcheck; 33 | 34 | pub mod zeromorph; 35 | pub mod ph23_pcs; 36 | pub mod bcho_pcs; 37 | 38 | pub mod snark; 39 | 40 | pub mod babygkr; 41 | pub mod prodgkr; 42 | 43 | // Initialize the logger 44 | pub fn init_logger() { 45 | let env = Env::default() 46 | .filter_or("RUST_LOG", "debug"); // Set the default log level here 47 | 48 | env_logger::Builder::from_env(env) 49 | .format_timestamp(None) // Customize the log format if needed 50 | .try_init(); 51 | } 52 | 53 | pub fn log_2(n: usize) -> usize { 54 | assert_ne!(n, 0); 55 | 56 | if n.is_power_of_two() { 57 | (1usize.leading_zeros() - n.leading_zeros()) as usize 58 | } else { 59 | (0usize.leading_zeros() - n.leading_zeros()) as usize 60 | } 61 | } 62 | 63 | pub fn pow_2(n: usize) -> usize { 64 | // assert_ne!(n, 0); 65 | let p = (2 as u32).pow(n as u32); 66 | p as usize 67 | } 68 | 69 | pub fn scalar_modulus_half() -> Scalar{ 70 | let mut b = FrParameters::MODULUS; 71 | b.div2(); 72 | Scalar::from(b) 73 | } 74 | 75 | pub fn scalar_modulus() -> BigInteger256 { 76 | FrParameters::MODULUS 77 | } 78 | 79 | // impl Display for ScalarExt { 80 | 81 | // } 82 | 83 | pub trait ScalarExt: Sized + Copy + Zero + One + Eq + std::fmt::Debug + Display { 84 | fn from_u64(i: u64) -> Self; 85 | 86 | fn from_i64(i: i64) -> Self; 87 | 88 | fn two() -> Self; 89 | 90 | fn from_i64_vector(v: &[i64]) -> Vec; 91 | 92 | fn from_usize(v: usize) -> Self; 93 | 94 | fn from_usize_vector(v: &[usize]) -> Vec; 95 | 96 | fn rand_vector(n: usize, rng: &mut R) -> Vec; 97 | 98 | fn to_string(&self) -> String; 99 | 100 | fn to_bytes(&self) -> Vec; 101 | 102 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 103 | let str = ScalarExt::to_string(self); 104 | write!(f, "{}", str) 105 | } 106 | 107 | fn exp(&self, exp: usize) -> Self; 108 | } 109 | 110 | impl ScalarExt for Scalar { 111 | 112 | fn from_u64(i: u64) -> Self { 113 | Scalar::from(i as u64) 114 | } 115 | 116 | fn from_i64(i: i64) -> Self { 117 | Scalar::from(i as i64) 118 | } 119 | 120 | fn two() -> Self { 121 | Scalar::from(2 as u64) 122 | 123 | } 124 | 125 | fn from_i64_vector(v: &[i64]) -> Vec { 126 | v.iter().map(| &n | Scalar::from(n as i64)).collect() 127 | } 128 | 129 | 130 | fn from_usize(v: usize) -> Self{ 131 | Scalar::from(v as u64) 132 | } 133 | 134 | fn from_usize_vector(v: &[usize]) -> Vec { 135 | v.iter().map(| &n | Scalar::from(n as u64)).collect() 136 | } 137 | 138 | fn rand_vector(n: usize, rng: &mut R) -> Vec { 139 | (0..n).map(| _ | Fr::rand(rng)).collect() 140 | } 141 | 142 | fn to_string(&self) -> String{ 143 | let mut str = "".to_string(); 144 | if self > &scalar_modulus_half() { 145 | str.push_str(&(Scalar::zero() - self).into_repr().to_string()); 146 | str.push_str("-*"); 147 | } else { 148 | str.push_str(&self.into_repr().to_string()); 149 | } 150 | str 151 | } 152 | 153 | fn to_bytes(&self) -> Vec { 154 | let mut value = [0u8; 32]; 155 | let _ = self.into_repr().to_bytes_be().write(value.as_mut()); 156 | value.to_vec() 157 | } 158 | 159 | fn exp(&self, exp: usize) -> Self { 160 | assert_eq!(exp, (exp as u64) as usize); 161 | self.pow(&[exp as u64, 0, 0, 0]) 162 | } 163 | } 164 | 165 | pub fn scalar_vector_to_string(v_vec: &Vec) -> String { 166 | let mut str = "[".to_string(); 167 | for (i, v) in v_vec.iter().enumerate() { 168 | str.push_str(&format!("{}:", i)); 169 | str.push_str(&ScalarExt::to_string(v)); 170 | str.push_str(",\n"); 171 | } 172 | str.push_str("]\n"); 173 | str 174 | } 175 | 176 | mod tests { 177 | use super::*; 178 | #[test] 179 | fn test_scalar_field() { 180 | let a = (Scalar::from(0 as u32) - Scalar::from(1 as u32)).into_repr(); 181 | let _bigint_two = BigInteger256::from(2); 182 | let mut b = FrParameters::MODULUS; 183 | println!("b={}", b); 184 | b.div2(); 185 | println!("a={}", a); 186 | println!("b/2={}", b); 187 | if a < b { 188 | println!("a=b"); 191 | } 192 | } 193 | 194 | } -------------------------------------------------------------------------------- /univarization/src/mle/evals.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Index; 2 | use core::fmt::Display; 3 | use core::fmt; 4 | use std::ops::IndexMut; 5 | use core::cmp::min; 6 | use log::debug; 7 | use std::collections::HashMap; 8 | 9 | use crate::*; 10 | use crate::mle::*; 11 | use crate::mle::coeffs_sparse::*; 12 | 13 | /// MLE Polynomial with (non-sparse) evaluations over hypercube. 14 | #[derive(Debug, Clone)] 15 | pub struct MleEvals { 16 | pub evals: Vec, // Hello, hypercube! 17 | pub num_var: usize, 18 | } 19 | 20 | impl Display for MleEvals { 21 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | let terms: Vec = self 23 | .evals 24 | .iter() 25 | .enumerate() 26 | .map(|(i, term)| { 27 | format!("\n{}:{}", i, ScalarExt::to_string(term)) 28 | }) 29 | .collect(); 30 | 31 | write!(f, "Polynomial.evals[{}]", terms.join(",")) 32 | } 33 | } 34 | 35 | impl MleEvals { 36 | 37 | pub fn new(vs: &[Scalar]) -> Self { 38 | let vs_len = vs.len(); 39 | let mut evals = vs.to_vec(); 40 | let full_len = vs_len.next_power_of_two(); 41 | 42 | let num_var = log_2(full_len); 43 | 44 | let padded_len = full_len - vs_len; 45 | let padded_vec = vec![Scalar::zero(); padded_len]; 46 | 47 | evals.extend(padded_vec); 48 | 49 | Self { 50 | num_var: num_var, 51 | evals: evals, 52 | } 53 | } 54 | 55 | pub fn len(&self) -> usize { 56 | self.evals.len() 57 | } 58 | 59 | pub fn lift_vars(&self, prepend_num_var: usize) -> Self { 60 | let new_evals = (0..pow_2(self.num_var+prepend_num_var)).enumerate().map( 61 | |(k, _v)| { 62 | let k_high = k >> prepend_num_var; 63 | self.evals[k_high] 64 | } 65 | ).collect(); 66 | 67 | Self { 68 | num_var: self.num_var + prepend_num_var, 69 | evals: new_evals, 70 | } 71 | } 72 | 73 | pub fn expand_vars(&self, append_num_var: usize) -> Self { 74 | let new_evals = (0..pow_2(self.num_var+append_num_var)).enumerate().map( 75 | |(k, _v)| { 76 | let k_low = k & (pow_2(self.num_var) - 1); 77 | self.evals[k_low] 78 | } 79 | ).collect(); 80 | 81 | Self { 82 | num_var: self.num_var + append_num_var, 83 | evals: new_evals, 84 | } 85 | } 86 | 87 | // Fold the space from N-dim to (N-1)-dim 88 | // Partial evaluation at (Xn = r), and turns the MLE 89 | // from f(X0, X1, ..., Xn) to f(X0, X1, ..., X{n-1}) 90 | pub fn fold_into_half(&mut self, rho: &Scalar) { 91 | let half = self.len() / 2; 92 | for i in 0..half { 93 | self.evals[i] = (Scalar::one() - rho) * self.evals[i] 94 | + *rho * self.evals[i + half]; 95 | } 96 | self.num_var -= 1; 97 | self.evals.truncate(half); 98 | } 99 | 100 | /// Partial evaluate f(X0, X1, ..., X{n-1}) at 101 | /// 102 | /// ``` 103 | /// (X{n-k}, X{n-k+1}, ..., X{n-1} = (r{n-k}, r{n-k+1})), 104 | /// ``` 105 | /// and return f(X0, X1, ..., X{n-k-1}) 106 | /// 107 | /// NOTE: the length of `rs` might greater than num_var, if so, 108 | /// the right most chunk of `rs` are chosen 109 | pub fn partial_evaluate(&self, rs: &[Scalar]) -> Self { 110 | let num_var = self.num_var; 111 | let mut evals = self.evals.clone(); 112 | let num_round = min(num_var, rs.len()); 113 | let mut half = self.len() / 2; 114 | let rho_vec: Vec= rs.iter().cloned().rev().take(num_round).collect(); 115 | for rd in 0..num_round { 116 | for i in 0..half { 117 | evals[i] = (Scalar::one() - rho_vec[rd]) * evals[i] 118 | + rho_vec[rd] * evals[i + half]; 119 | } 120 | half = half/2; 121 | }; 122 | evals.truncate(pow_2(num_var - num_round)); 123 | Self { 124 | num_var: num_var - num_round, 125 | evals: evals, 126 | } 127 | } 128 | 129 | // Evaluate the polynomial at the point: (Xn,...,X1,X0) in O(n) 130 | pub fn evaluate(&self, rs: &[Scalar]) -> Scalar { 131 | assert_eq!(rs.len(), self.num_var); 132 | 133 | // chi is lagrange polynomials evaluated at rs 134 | let chi_vec = EqPolynomial::new(&rs.to_vec()).evals_over_hypercube(); 135 | 136 | assert_eq!(chi_vec.len(), self.evals.len()); 137 | (0..self.evals.len()).map(| i | chi_vec[i] * self.evals[i]).sum() 138 | } 139 | 140 | 141 | 142 | // Evaluate the polynomial at r with coefficients in O(n) 143 | // TODO: add check for the length of rs. 144 | // TODO: change the order of rs, so that it is consistent with (X0, X1, ..., Xn) 145 | pub fn evaluate_from_coeffs(coeffs: &[Scalar], rs: &[Scalar]) -> Scalar { 146 | assert!(coeffs.len().is_power_of_two()); 147 | 148 | let mut evals = coeffs.to_vec(); 149 | let mut rs = rs.to_vec(); 150 | let num_rounds = log_2(evals.len()); 151 | let mut half = evals.len(); 152 | 153 | for _i in 0..num_rounds { 154 | half = half / 2; 155 | let r = rs.pop().unwrap(); // TODO: let r = rs[i] 156 | for j in 0..half { 157 | evals[j] = evals[j*2] + r * evals[j*2+1]; 158 | } 159 | evals.truncate(half); 160 | } 161 | evals[0] 162 | } 163 | 164 | /// Divide the polynomial at the point: `[X_0, X_1, ..., X_{n-1}]` in O(N) (Linear!) 165 | /// 166 | /// Reference: Algorithm 8 in Appendix B, [XZZPS18] 167 | /// "Libra: Succinct Zero-Knowledge Proofs with Optimal Prover Computation" 168 | /// 169 | /// Returns `[q_0, q_1, ..., q_{n-1}]` where q_i is a MLE as `q_i(X0, X1, ..., X_{i-1})`. 170 | #[allow(dead_code)] 171 | pub fn decompose_by_div(poly: &MleEvals, point: &[Scalar]) -> Vec { 172 | let mut r = poly.evals.clone(); 173 | let mut quotients: Vec = vec![]; 174 | let num_var = poly.num_var; 175 | for i in (0..num_var).rev() { 176 | let mut q = vec![Scalar::zero(); pow_2(i)]; 177 | for j in 0..pow_2(i) { 178 | q[j] = r[j + pow_2(i)] - r[j]; 179 | r[j] = r[j] * (Scalar::one() - point[i]) + r[j + pow_2(i)] * point[i]; 180 | } 181 | quotients.insert(0, MleEvals { 182 | num_var: i, 183 | evals: q, 184 | }); 185 | } 186 | quotients 187 | } 188 | 189 | pub fn to_coeffs(&self) -> Vec { 190 | compute_coeffs_from_evals(&self.evals) 191 | } 192 | 193 | pub fn from_coeffs(coeffs: &[Scalar], num_var: usize) -> Self { 194 | assert!(pow_2(num_var)>=coeffs.len()); 195 | let evals = compute_evals_from_coeffs(num_var, coeffs); 196 | Self { 197 | num_var: num_var, 198 | evals: evals, 199 | } 200 | } 201 | } 202 | 203 | impl Index for MleEvals { 204 | type Output = Scalar; 205 | 206 | // TODO: inline 207 | fn index(&self, index: usize) -> &Self::Output { 208 | &(self.evals[index]) 209 | } 210 | } 211 | 212 | 213 | #[cfg(test)] 214 | #[allow(dead_code)] 215 | #[allow(unused)] 216 | #[allow(non_snake_case)] 217 | mod tests { 218 | use crate::unipoly::UniPolynomial; 219 | 220 | use super::*; 221 | 222 | #[test] 223 | fn test_is_valid() { 224 | // give me a test case for is_valid(): 225 | let coeffs = vec![ 226 | (0b101, Scalar::from(2)), 227 | (0b010, Scalar::from(3)), 228 | (0b001, Scalar::from(4)), 229 | (0b100, Scalar::from(9)), 230 | ]; 231 | let num_var = 3; 232 | let f_mle = MleCoeffs::new(coeffs.clone(), num_var); 233 | assert!(f_mle.is_valid()); 234 | 235 | let g_mle = MleCoeffs::new(coeffs.clone(), 2); 236 | assert!(!g_mle.is_valid()); 237 | } 238 | 239 | #[test] 240 | fn test_eq_evals_over_hypercube() { 241 | init_logger(); 242 | 243 | let vs = Scalar::from_usize_vector(&[1,2,3]); 244 | let eq = EqPolynomial::new(&vs); 245 | let evals = eq.evals_over_hypercube(); 246 | let evals_prime = eq.evals_over_hypercube_slow(); 247 | assert_eq!(evals, evals_prime); 248 | debug!("evals={}", scalar_vector_to_string(&evals)); 249 | } 250 | 251 | #[test] 252 | fn test_mle_evals_evaluate() { 253 | init_logger(); 254 | 255 | // f(X0, X1) = [2, 3, 4, 5] 256 | // f(X0, X1) = 2 + X0 + 2X1 257 | let vs = Scalar::from_usize_vector(&[2,3,4,5]); 258 | let mle = MleEvals::new(&vs); 259 | debug!("mle.coeffs={}", scalar_vector_to_string(&mle.to_coeffs())); 260 | 261 | // f(2, 3) = 2 + 2 + 2*3 = 10 262 | let r = Scalar::from_usize_vector(&[2,3]); 263 | let e = mle.evaluate(&r); 264 | assert_eq!(e, Scalar::from(10)); 265 | } 266 | 267 | #[test] 268 | fn test_partial_evaluate() { 269 | init_logger(); 270 | 271 | // f(X0, X1, X2) = 1 + X0 + 2X1 + 4X2 272 | let mle = MleEvals { 273 | num_var: 3, 274 | evals: vec![Scalar::from(1), Scalar::from(2), Scalar::from(3), Scalar::from(4), 275 | Scalar::from(5), Scalar::from(6), Scalar::from(7), Scalar::from(8)], 276 | }; 277 | let coeffs = mle.to_coeffs(); 278 | debug!("mle.coeffs={}", scalar_vector_to_string(&coeffs)); 279 | assert_eq!(&coeffs, &ScalarExt::from_i64_vector(&vec![1,1,2,0,4,0,0,0])); 280 | let rs = vec![Scalar::from(1), Scalar::from(2)]; 281 | 282 | // f(X0, 1, 2) = 11 + X0 283 | let mle_1_2 = mle.partial_evaluate(&rs); 284 | assert_eq!(mle_1_2.num_var, 1); 285 | assert_eq!(mle_1_2.evals, vec![Scalar::from(11), Scalar::from(12)]); 286 | 287 | // f(X0, X1, 2) = 9 + X0 + 2X1 = g(X0, X1) 288 | let mle_2 = mle.partial_evaluate(&[Scalar::from(2)]); 289 | assert_eq!(mle_2.num_var, 2); 290 | assert_eq!(mle_2.evals, vec![Scalar::from(9), Scalar::from(10),Scalar::from(11),Scalar::from(12)]); 291 | 292 | // g(X0, 1) = ? 293 | let mle_2_1 = mle_2.partial_evaluate(&[Scalar::from(1)]); 294 | assert_eq!(mle_2_1.evals, mle_1_2.evals); 295 | } 296 | 297 | #[test] 298 | fn test_partial_evaluate_2() { 299 | 300 | // f() = 5 301 | let mle = MleEvals { 302 | num_var: 0, 303 | evals: vec![Scalar::from(5)], 304 | }; 305 | let rs = vec![Scalar::from(1), Scalar::from(2)]; 306 | 307 | // f(1,2) = f() = 5 308 | let mle_1 = mle.partial_evaluate(&rs); 309 | assert_eq!(mle_1.num_var, 0); 310 | assert_eq!(mle_1.evals, vec![Scalar::from(5)]); 311 | } 312 | 313 | #[test] 314 | fn test_partial_evaluate_3() { 315 | let mle = MleEvals { 316 | num_var: 3, 317 | evals: vec![Scalar::from(1), Scalar::from(2), Scalar::from(3), Scalar::from(4), 318 | Scalar::from(5), Scalar::from(6), Scalar::from(7), Scalar::from(8)], 319 | }; 320 | // the actual rs = [-1, 1, 2] 321 | let rs = vec![Scalar::from(3), Scalar::from(-1), Scalar::from(1), Scalar::from(2)]; 322 | 323 | let mle_1 = mle.partial_evaluate(&rs); 324 | assert_eq!(mle_1.num_var, 0); 325 | 326 | let eval = mle.evaluate(rs.split_at(1).1); 327 | assert_eq!(mle_1.evals, vec![Scalar::from(10)]); 328 | assert_eq!(eval, Scalar::from(10)); 329 | } 330 | 331 | 332 | #[test] 333 | fn test_compute_coeffs_from_evals() { 334 | init_logger(); 335 | 336 | let evals = ScalarExt::from_usize_vector(&vec![1, 2, 3, 4, 5, 6, 7, 8]); 337 | let coeffs: Vec = ScalarExt::from_i64_vector(&vec![1, 1, 2, 0, 4, 0, 0, 0]); 338 | let result = compute_coeffs_from_evals(&evals); 339 | assert_eq!(&coeffs, &result); 340 | debug!("mle.coeffs={}", scalar_vector_to_string(&result)); 341 | } 342 | 343 | #[test] 344 | fn test_compute_evals_from_coeffs() { 345 | init_logger(); 346 | 347 | let coeffs: Vec = ScalarExt::from_i64_vector(&vec![1, 1, 2, 0, 4, 0, 0, 0]); 348 | let evals: Vec = ScalarExt::from_usize_vector(&vec![1, 2, 3, 4, 5, 6, 7, 8]); 349 | let result = compute_evals_from_coeffs(3, &coeffs); 350 | assert_eq!(&evals, &result); 351 | debug!("mle.evals={}", scalar_vector_to_string(&result)); 352 | } 353 | 354 | #[test] 355 | fn test_coeffs_evals_random() { 356 | init_logger(); 357 | let rng = &mut ark_std::test_rng(); 358 | let n = 20; 359 | let N = pow_2(n); 360 | let coeffs: Vec = ScalarExt::rand_vector(N, rng); 361 | let evals = compute_evals_from_coeffs(n, &coeffs); 362 | let coeffs_prime = compute_coeffs_from_evals(&evals); 363 | assert_eq!(&coeffs, &coeffs_prime); 364 | } 365 | 366 | } -------------------------------------------------------------------------------- /univarization/src/mle/evals_sparse.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use crate::mle::*; 3 | use crate::mle::evals::*; 4 | 5 | use core::ops::{IndexMut, Index}; 6 | use std::collections::HashMap; 7 | use core::cmp::max; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct MleEvalsSparse { 11 | pub sparse_evals: HashMap, 12 | pub num_var: usize, 13 | zero: Scalar, 14 | } 15 | 16 | impl MleEvalsSparse { 17 | 18 | pub fn new(vs: &HashMap, num_var: usize) -> Self { 19 | let vs_len = vs.len(); 20 | vs.iter().for_each(|(&k, &v)| { 21 | assert!(k < pow_2(num_var)); 22 | }); 23 | 24 | Self { 25 | num_var: num_var, 26 | sparse_evals: vs.clone(), 27 | zero: Scalar::zero(), 28 | } 29 | } 30 | 31 | pub fn from_evals(mle: &MleEvals) -> Self { 32 | let mut sparse_evals = HashMap::new(); 33 | for (k, v) in mle.evals.iter().enumerate() { 34 | if !v.is_zero() { 35 | sparse_evals.insert(k, *v); 36 | } 37 | } 38 | Self { 39 | num_var: mle.num_var, 40 | sparse_evals: sparse_evals, 41 | zero: Scalar::zero(), 42 | } 43 | } 44 | 45 | pub fn to_evals(&self) -> MleEvals { 46 | let mut evals = vec![Scalar::zero(); pow_2(self.num_var)]; 47 | for (k, v) in self.sparse_evals.iter() { 48 | evals[*k] = *v; 49 | } 50 | MleEvals { 51 | num_var: self.num_var, 52 | evals: evals, 53 | } 54 | } 55 | 56 | pub fn lift_vars(&mut self, prepend_num_var: usize) { 57 | let mut vs = HashMap::new(); 58 | self.sparse_evals.iter().for_each(|(k, v)| { 59 | let mut k_prime = *k; 60 | k_prime <<= prepend_num_var; 61 | for i in 0..pow_2(prepend_num_var) { 62 | vs.insert(k_prime + i, *v); 63 | } 64 | }); 65 | self.num_var += prepend_num_var; 66 | self.sparse_evals = vs; 67 | } 68 | 69 | pub fn expand_vars(&mut self, append_num_var: usize) { 70 | let mut vs = HashMap::new(); 71 | self.sparse_evals.iter().for_each(|(k, v)| { 72 | let mut k_prime = *k; 73 | for i in 0..pow_2(append_num_var) { 74 | vs.insert(k_prime + (i< usize { 82 | pow_2(self.num_var) 83 | } 84 | 85 | // TODO: refractor the code to avoid code duplication 86 | pub fn fold_into_half(&mut self, rho: &Scalar) { 87 | let half = self.len() / 2; 88 | let mut new_sparse_evals = self.sparse_evals.clone(); 89 | for (&k, v) in self.sparse_evals.iter() { 90 | if k >= half { 91 | let k_prime = k - half; 92 | let low = new_sparse_evals.entry(k_prime).or_insert(Scalar::zero()); 93 | let high = v; 94 | let u = (Scalar::one() - rho) * *low + *rho * *high; 95 | if u != Scalar::zero() { 96 | *low = u; 97 | } else { 98 | new_sparse_evals.remove(&k_prime); 99 | } 100 | new_sparse_evals.remove(&k); 101 | } else { 102 | if self.sparse_evals.get(&(k + half)) == None { 103 | *new_sparse_evals.entry(k).or_insert(Scalar::zero()) *= Scalar::one() - rho; 104 | } 105 | } 106 | } 107 | self.num_var -= 1; 108 | self.sparse_evals = new_sparse_evals; 109 | } 110 | 111 | pub fn partial_evaluate(&self, rs: &[Scalar]) -> Self { 112 | let num_var = self.num_var; 113 | let mut mle = self.clone(); 114 | let num_rounds = 115 | if num_var >= rs.len() { 116 | rs.len() 117 | } else { 118 | num_var 119 | }; 120 | rs.iter().rev().take(num_rounds).for_each(|r| { 121 | mle.fold_into_half(r); 122 | }); 123 | mle 124 | } 125 | 126 | pub fn partial_evaluate_in_place(&mut self, rs: &[Scalar]) -> usize { 127 | let num_var = self.num_var; 128 | if num_var == 0 { 129 | return 0; 130 | } else if num_var >= rs.len() { 131 | for r in rs.iter().rev() { 132 | self.fold_into_half(r); 133 | } 134 | return rs.len(); 135 | } else if num_var < rs.len() { 136 | for r in rs.iter().rev().take(num_var) { 137 | self.fold_into_half(r); 138 | } 139 | return num_var; 140 | } else { 141 | unreachable!(); 142 | } 143 | } 144 | 145 | // Evaluate the polynomial at the point: (X0, X1, ..., Xn) in O(n) 146 | pub fn evaluate(&self, rs: &[Scalar]) -> Scalar { 147 | assert_eq!(rs.len(), self.num_var); 148 | 149 | // chi is lagrange polynomials evaluated at rs 150 | let chi_vec = EqPolynomial::new(&rs.to_vec()).evals_over_hypercube(); 151 | 152 | let mut sum = Scalar::zero(); 153 | for (k, v) in self.sparse_evals.iter() { 154 | sum += chi_vec[*k] * v; 155 | } 156 | sum 157 | } 158 | } 159 | 160 | impl Index for MleEvalsSparse { 161 | type Output = Scalar; 162 | 163 | // TODO: inline 164 | fn index(&self, index: usize) -> &Self::Output { 165 | self.sparse_evals.get(&index).unwrap_or(&self.zero) 166 | } 167 | } 168 | 169 | impl IndexMut for MleEvalsSparse { 170 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 171 | self.sparse_evals.entry(index).or_insert(self.zero) 172 | } 173 | } 174 | 175 | impl MleEvalsSparse { 176 | 177 | // Concat two MLEs into one MLE with one more variable 178 | // f(X0, X1, ..., X{n+1}) = 179 | // (1 - X{n+1}}) * f1(X0, X1, ..., Xn) + X{n+1} * f2(X0, X1, ..., Xn) 180 | // NOTE: If the num_vars of f1 and f2 are not the same, then 181 | // the smaller one is padded with zeros. 182 | 183 | pub fn append(&self, other: &Self) -> Self { 184 | let num_var = max(self.num_var, other.num_var); 185 | let mut sparse_evals = self.sparse_evals.clone(); 186 | let half = pow_2(num_var); 187 | for (k, v) in other.sparse_evals.iter() { 188 | let en = sparse_evals.entry(*k + half).or_insert(Scalar::zero()); 189 | *en += *v; 190 | } 191 | 192 | Self { 193 | sparse_evals: sparse_evals, 194 | num_var: num_var + 1, 195 | zero: Scalar::zero(), 196 | } 197 | } 198 | 199 | pub fn add(&self, other: &Self) -> Self { 200 | let mut sparse_evals = self.sparse_evals.clone(); 201 | for (k, v) in other.sparse_evals.iter() { 202 | let en = sparse_evals.entry(*k).or_insert(Scalar::zero()); 203 | *en += *v; 204 | } 205 | 206 | Self { 207 | sparse_evals: sparse_evals, 208 | num_var: self.num_var, 209 | zero: Scalar::zero(), 210 | } 211 | } 212 | 213 | pub fn mul_scalar(&self, scalar: &Scalar) -> Self { 214 | let mut sparse_evals = self.sparse_evals.clone(); 215 | for (k, v) in sparse_evals.iter_mut() { 216 | *v *= *scalar; 217 | } 218 | 219 | Self { 220 | sparse_evals: sparse_evals, 221 | num_var: self.num_var, 222 | zero: Scalar::zero(), 223 | } 224 | } 225 | } 226 | 227 | #[cfg(test)] 228 | mod tests { 229 | use super::*; 230 | use std::collections::HashMap; 231 | 232 | #[test] 233 | fn test_to_from_evals() { 234 | let hs = HashMap::from( 235 | [ 236 | (0b1 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::one()), // 0b011 -> 1 237 | (0b0 + 0b1 * pow_2(1) + 0b1 * pow_2(2), Scalar::one()) // 0b110 -> 1 238 | ]); 239 | for (k, v) in hs.iter() { 240 | println!("hs: k={}, v={}", k, v); 241 | } 242 | 243 | let mut mle = MleEvalsSparse::new(&hs, 1+1+1); 244 | println!("eval={}", mle.evaluate(&[Scalar::from(1), Scalar::from(1), Scalar::from(0)])); 245 | println!("eval={}", mle.evaluate(&[Scalar::from(0), Scalar::from(1), Scalar::from(1)])); 246 | 247 | let mut evals = vec![Scalar::zero(); pow_2(mle.num_var)]; 248 | for (k, v) in mle.sparse_evals.iter() { 249 | println!("k={}, v={}", k, v); 250 | evals[*k] = *v; 251 | } 252 | println!("evals={}", scalar_vector_to_string(&evals)); 253 | 254 | let mle2 = mle.to_evals(); 255 | println!("mle2={}", scalar_vector_to_string(&mle2.evals)); 256 | 257 | let mle3 = MleEvalsSparse::from_evals(&mle2); 258 | for (k, v) in mle3.sparse_evals.iter() { 259 | println!("mle3: k={}, v={}", k, v); 260 | } 261 | } 262 | 263 | #[test] 264 | fn test_lift_vars() { 265 | let hs = HashMap::from( 266 | [ 267 | (0b1 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::one()), // 0b011 -> 1 268 | (0b0 + 0b1 * pow_2(1) + 0b1 * pow_2(2), Scalar::one()) // 0b110 -> 1 269 | ]); 270 | let mut mle = MleEvalsSparse::new(&hs, 3); 271 | mle.lift_vars(2); 272 | 273 | assert_eq!(mle.to_evals().evals[0b01100], Scalar::one()); 274 | assert_eq!(mle.to_evals().evals[0b11000], Scalar::one()); 275 | assert_eq!(mle.to_evals().evals[0b00000], Scalar::zero()); 276 | assert_eq!(mle.to_evals().evals[0b00110], Scalar::zero()); 277 | assert_eq!(mle.to_evals().evals[0b00011], Scalar::zero()); 278 | } 279 | 280 | #[test] 281 | fn test_expand_vars() { 282 | let hs = HashMap::from( 283 | [ 284 | (0b1 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::one()), // 0b011 -> 1 285 | (0b0 + 0b1 * pow_2(1) + 0b1 * pow_2(2), Scalar::one()) // 0b110 -> 1 286 | ]); 287 | let mut mle = MleEvalsSparse::new(&hs, 3); 288 | mle.expand_vars(2); 289 | 290 | assert_eq!(mle.to_evals().evals[0b01100], Scalar::one()); 291 | assert_eq!(mle.to_evals().evals[0b11000], Scalar::one()); 292 | assert_eq!(mle.to_evals().evals[0b00000], Scalar::zero()); 293 | assert_eq!(mle.to_evals().evals[0b00110], Scalar::zero()); 294 | assert_eq!(mle.to_evals().evals[0b00011], Scalar::zero()); 295 | } 296 | 297 | #[test] 298 | fn test_fold() { 299 | 300 | let mut rng = ark_std::test_rng(); 301 | 302 | let hs = HashMap::from( 303 | [ 304 | (0b0 + 0b0 * pow_2(1) + 0b0 * pow_2(2), Scalar::two()), // 0b011 -> 1 305 | (0b1 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::one()), // 0b011 -> 1 306 | (0b0 + 0b1 * pow_2(1) + 0b1 * pow_2(2), Scalar::one()), // 0b110 -> 1 307 | (0b0 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::from(9)), // 0b010 -> 1 308 | (0b0 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::from(9)) // 0b010 -> 1 309 | ]); 310 | let mut mle = MleEvalsSparse::new(&hs, 3); 311 | let mut mle_evals = mle.to_evals(); 312 | 313 | // sample a folding factor 314 | let rho = Scalar::rand(&mut rng); 315 | 316 | // fold two identical mles 317 | mle.fold_into_half(&rho); 318 | mle_evals.fold_into_half(&rho); 319 | 320 | // test the equality of two folded mles 321 | let rs = Scalar::rand_vector(2, &mut rng); 322 | assert_eq!(mle.evaluate(&rs), mle_evals.evaluate(&rs)); 323 | } 324 | 325 | #[test] 326 | fn test_fold_2() { 327 | 328 | let mut rng = ark_std::test_rng(); 329 | 330 | // Assume H(4) = 1, H(0) = 2, if rho = 2, then 331 | // (1-2) * H(0) + 2 * H(4) = 0 332 | // TEST: the keys of both H(0) and H(4) shall be removed 333 | let hs = HashMap::from( 334 | [ 335 | (0b0 + 0b0 * pow_2(1) + 0b0 * pow_2(2), Scalar::two()), // 0b000 -> 2 336 | (0b1 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::one()), // 0b011 -> 1 337 | (0b0 + 0b1 * pow_2(1) + 0b1 * pow_2(2), Scalar::one()), // 0b110 -> 1 338 | (0b0 + 0b1 * pow_2(1) + 0b0 * pow_2(2), Scalar::from(9)), // 0b010 -> 1 339 | (0b0 + 0b0 * pow_2(1) + 0b1 * pow_2(2), Scalar::from(1)) // 0b010 -> 1 340 | ]); 341 | let mut mle = MleEvalsSparse::new(&hs, 3); 342 | let mut mle_evals = mle.to_evals(); 343 | 344 | // use a specific folding factor 345 | let rho = Scalar::from(2); 346 | 347 | // fold the two mles 348 | mle.fold_into_half(&rho); 349 | mle_evals.fold_into_half(&rho); 350 | 351 | // test equality 352 | let rs = Scalar::rand_vector(2, &mut rng); 353 | assert_eq!(mle.evaluate(&rs), mle_evals.evaluate(&rs)); 354 | } 355 | 356 | #[test] 357 | fn test_partial_evaluate() { 358 | 359 | let rng = &mut ark_std::test_rng(); 360 | 361 | let mut evals = vec![Scalar::zero(); 8]; 362 | evals[0b011] = Scalar::rand(rng); 363 | evals[0b110] = Scalar::rand(rng); 364 | 365 | let rs = Scalar::rand_vector(3, rng); 366 | 367 | let mle = MleEvals::new(&evals); 368 | let e = mle.evaluate(&rs); 369 | 370 | println!("e={}", e); 371 | 372 | let hs = HashMap::from( 373 | [ 374 | (0b1 + 0b1 * pow_2(1) + 0b0 * pow_2(2), evals[0b011]), // 0b011 -> r1 375 | (0b0 + 0b1 * pow_2(1) + 0b1 * pow_2(2), evals[0b110]) // 0b110 -> r2 376 | ]); 377 | 378 | let mut mle2 = MleEvalsSparse::new(&hs, 3); 379 | 380 | mle2.partial_evaluate(&rs); 381 | println!("mle2={}", mle2.to_evals()); 382 | let mle3 = mle.partial_evaluate(&rs); 383 | println!("mle3={}", mle3); 384 | } 385 | 386 | #[test] 387 | fn test_add() { 388 | let mut sparse_evals1 = HashMap::new(); 389 | sparse_evals1.insert(1, Scalar::from(2)); 390 | sparse_evals1.insert(2, Scalar::from(8)); 391 | let mle1 = MleEvalsSparse { 392 | sparse_evals: sparse_evals1, 393 | num_var: 2, 394 | zero: Scalar::zero(), 395 | }; 396 | 397 | let mut sparse_evals2 = HashMap::new(); 398 | sparse_evals2.insert(1, Scalar::from(3)); 399 | sparse_evals2.insert(3, Scalar::from(5)); 400 | let mle2 = MleEvalsSparse { 401 | sparse_evals: sparse_evals2, 402 | num_var: 2, 403 | zero: Scalar::zero(), 404 | }; 405 | 406 | let result = mle1.add(&mle2); 407 | assert_eq!(result.sparse_evals.get(&1), Some(&Scalar::from(5))); 408 | assert_eq!(result.sparse_evals.get(&2), Some(&Scalar::from(8))); 409 | assert_eq!(result.sparse_evals.get(&3), Some(&Scalar::from(5))); 410 | assert_eq!(result.sparse_evals.get(&0), None); 411 | } 412 | 413 | #[test] 414 | fn test_mul_scalar() { 415 | let mut sparse_evals = HashMap::new(); 416 | sparse_evals.insert(1, Scalar::from(2)); 417 | sparse_evals.insert(2, Scalar::from(4)); 418 | let mle = MleEvalsSparse { 419 | sparse_evals: sparse_evals, 420 | num_var: 2, 421 | zero: Scalar::zero(), 422 | }; 423 | 424 | let scalar = Scalar::from(10); 425 | let result = mle.mul_scalar(&scalar); 426 | assert_eq!(result.sparse_evals.get(&1), Some(&Scalar::from(20))); 427 | assert_eq!(result.sparse_evals.get(&2), Some(&Scalar::from(40))); 428 | assert_eq!(result.sparse_evals.get(&0), None); 429 | } 430 | 431 | 432 | 433 | } -------------------------------------------------------------------------------- /univarization/src/mle/mod.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Index; 2 | use core::fmt::Display; 3 | use core::fmt; 4 | use std::ops::IndexMut; 5 | use core::cmp::min; 6 | use log::debug; 7 | use std::collections::HashMap; 8 | 9 | use crate::*; 10 | use crate::bits::*; 11 | 12 | pub mod evals; 13 | pub mod coeffs_sparse; 14 | pub mod evals_sparse; 15 | 16 | pub struct EqPolynomial { 17 | x_vec: Vec, 18 | } 19 | 20 | impl EqPolynomial { 21 | 22 | /// Returns a new eq polynomial with the given `x_vec`. 23 | /// 24 | /// The formal definition of *eq polynomial* is: 25 | /// 26 | /// ``` 27 | /// eq(X[], Y[]) = \prod_{0 <= i < N}((1 - X_i) * (1 - Y_i) + X_i * Y_i) 28 | /// ``` 29 | /// 30 | /// In practice, however, the first argument is a vector of random (Field) 31 | /// elements, generated by the verifier, and the second argument is always a vector 32 | /// of a bits of a scalar, i.e. 33 | /// 34 | /// ``` 35 | /// Y[] = [y_0, y_1, ..., y_{n-1}], 36 | /// ``` 37 | /// 38 | /// where y_i = 0 or 1. Thus, we can simplify the definition as: 39 | /// 40 | /// ``` 41 | /// eq_y(X[]) = \prod_{i}((1 - X_i) * (1 - y_i) + X_i * y_i) 42 | ///``` 43 | /// 44 | /// where 45 | /// 46 | /// ``` 47 | /// y = y_0 + y_1 * 2 + y_2 * 4 + ... + y_{n-1} * 2^{n-1}, 48 | /// ``` 49 | /// or, we say `bits_LE(y) = [y_0, y_1, ..., y_{n-1}]` 50 | /// 51 | /// e.g. if `n = 3`, and `y = 0b011`, then 52 | /// 53 | /// ``` 54 | /// Y[3] = [1, 1, 0] 55 | /// ``` 56 | /// 57 | /// and 58 | /// 59 | /// ``` 60 | /// eq_y(X_0,X_1,X_2) = X0 * X1 * (1 - X2) 61 | /// ``` 62 | /// 63 | /// # Arguments 64 | /// 65 | /// - x_vec: the vector of random (Field) elements from the verifier 66 | /// 67 | pub fn new(x_vec: &Vec) -> Self { 68 | EqPolynomial { 69 | x_vec: x_vec.to_owned(), 70 | } 71 | } 72 | 73 | 74 | /// Compute eq(r_vec, i_rec) in O(log N). 75 | /// 76 | /// Or, it returns a specific evaluation on some vertex of the hypercube. 77 | /// 78 | /// Remember that eq polynomial is a grand product of `(1-X_i)` or `X_i` 79 | /// according to the bits of `i_rec`, the length of which is `n = log(N)`. 80 | /// 81 | /// ``` 82 | /// eq_i(X[]) = \prod_{i}((1 - X_i) * (1 - B_i) + X_i * B_i) 83 | /// ``` 84 | /// where `B_i=bits_LE(i)` is the i-th bit of `i_rec`. 85 | /// 86 | /// 87 | pub fn eval(&self, i: usize) -> Scalar { 88 | let x_log_vec = &self.x_vec; 89 | let i_bits = bits_LE(i, x_log_vec.len()); 90 | 91 | // EQ = \prod_{i}((1 - x_i) * (1 - r_i) + x_i * r_i) 92 | (0..x_log_vec.len()).map(|i| 93 | if i_bits[i] {x_log_vec[i]} else {Scalar::from(1) - x_log_vec[i]} 94 | ).product() 95 | } 96 | 97 | /// Compute eq(r_vec, x_vec) in O(log N). 98 | /// 99 | /// ``` 100 | /// eq(X[], Y[]) = \prod_{i}((1 - X_i) * (1 - Y_i) + X_i * Y_i) 101 | /// ``` 102 | /// 103 | /// 104 | pub fn evaluate(&self, r_vec: &[Scalar]) -> Scalar { 105 | let x_vec = &self.x_vec; 106 | assert_eq!(x_vec.len(), r_vec.len()); 107 | 108 | // EQ = \prod_{i}((1 - x_i) * (1 - r_i) + x_i * r_i) 109 | x_vec.iter().zip(r_vec.iter()).map(|(&a, &b)| 110 | (Scalar::one() - a) * (Scalar::one() - b) + a * b 111 | ).product() 112 | } 113 | 114 | /// Compute all evaluations over the hypercube in O(N), from [Tha13]. 115 | /// 116 | /// 117 | /// ^ X1 118 | /// | 119 | /// | 120 | /// |e2 e3 121 | /// +-----------+ 122 | /// / /| 123 | /// e6 / | e7 / | 124 | /// +-----------+ | 125 | /// | | | | 126 | /// | + - - - -| + -----------------> X0 127 | /// | / e0 | / e1 128 | /// | |/ 129 | /// +-----------+ 130 | /// /e4 e5 131 | /// / 132 | /// ∟ X2 133 | /// 134 | /// If `n = 2^3 = 8`, and `x_vec = [r0, r1, r2]`, then 135 | /// ``` 136 | /// e0 = (1-r0)(1-r1)(1-r2) 137 | /// e1 = r0 (1-r1)(1-r2) 138 | /// e2 = (1-r0) r1 (1-r2) 139 | /// e3 = r0 r1 (1-r2) 140 | /// e0 = (1-r0)(1-r1) r2 141 | /// e1 = r0 (1-r1) r2 142 | /// e2 = (1-r0) r1 r2 143 | /// e3 = r0 r1 r2 144 | /// ``` 145 | /// Returns a vector of size `2^n`, where the i-th element is `e{i}` 146 | /// 147 | /// 148 | /// // Compute all evaluations over the hypercube in O(n), from [Tha13] 149 | // The computation is similar to the reverse of folding. 150 | // NOTE: Particularly, the cost is only `n` field multiplications. 151 | // 152 | // x_vec = [X0, X1, ..., Xn] 153 | // e.g. 154 | // e0 = (1-x0)(1-x1)(1-x2) 155 | // e1 = x0 (1-x1)(1-x2) 156 | // e2 = (1-x0) x1 (1-x2) 157 | // e3 = x1 x0 (1-x2) 158 | pub fn evals_over_hypercube(&self) -> Vec { 159 | let x_vec = &self.x_vec; 160 | 161 | let log_size = self.x_vec.len(); 162 | let full_size = pow_2(log_size); 163 | 164 | let mut evals = vec![Scalar::one(); full_size]; 165 | let mut half = 1; 166 | for i in 0..log_size { 167 | for j in 0..half { 168 | evals[j+half] = evals[j] * x_vec[i]; 169 | 170 | // Normally, we should have computed `evals[j]` via 171 | // evals[j] = evals[j] * (Scalar::one() - x_vec[i]) 172 | // However we can save one multiplication by computing 173 | // evals[j] = evals[j] * (Scalar::one() - x_vec[i]) 174 | // = evals[j] - evals[j] * x_vec[i] 175 | // = evals[j] - evals[j+half] 176 | // evals[j] = evals[j] * (Scalar::one() - x_vec[i]); 177 | evals[j] = evals[j] - evals[j+half]; 178 | } 179 | half *= 2; 180 | } 181 | evals 182 | } 183 | 184 | /// TODO: obsoleted, remove it 185 | pub fn evals_over_hypercube_rev(&self) -> Vec { 186 | let x_vec = &self.x_vec; 187 | 188 | let log_size = self.x_vec.len(); 189 | let full_size = pow_2(log_size); 190 | 191 | let mut evals = vec![Scalar::one(); full_size]; 192 | let mut s = 1; 193 | for i in 0..log_size { 194 | s *= 2; 195 | for j in (0..s).rev().step_by(2) { 196 | let v = evals[j/2]; 197 | evals[j] = v * x_vec[i]; 198 | evals[j-1] = v * (Scalar::one() - x_vec[i]); 199 | } 200 | } 201 | evals 202 | } 203 | 204 | /// Compute all evaluations over the hypercube in O(n*log(n)) 205 | /// NOTE: only for testing, not used in production 206 | pub fn evals_over_hypercube_slow(&self) -> Vec { 207 | let x_vec = &self.x_vec; 208 | 209 | let log_size = self.x_vec.len(); 210 | let full_size = pow_2(log_size); 211 | 212 | let mut evals = vec![Scalar::zero(); full_size]; 213 | 214 | for i in 0..full_size { 215 | let mut prod_acc = Scalar::one(); 216 | let i_bin = scalar_from_bits_LE(i, log_size); 217 | for j in 0..log_size { 218 | let b = i_bin[j]; 219 | 220 | let x = x_vec[j]; 221 | 222 | let eq_j = (Scalar::one() - x) * (Scalar::one() - b) + (x * b); 223 | prod_acc *= eq_j; 224 | } 225 | evals[i] = prod_acc; 226 | } 227 | evals 228 | } 229 | 230 | // TODO: 231 | pub fn to_evals() -> Vec { 232 | unimplemented!(); 233 | } 234 | } 235 | 236 | /// Interpolate the evaluations into coefficients over hypercube. 237 | /// The asymptotic complexity is O(N * log^2(N)). 238 | /// 239 | /// TODO: can we compute in place (without memory allocation)? 240 | /// 241 | /// The argument evals: the evaluations of the MLE over hypercube 242 | /// 243 | /// ``` 244 | /// evals = [0b000: e0 paired with (1-X0)(1-X1)(1-X2), 245 | /// 0b001: e1 paired with X0 (1-X1)(1-X2), 246 | /// 0b010: e2 paired with (1-X0) X1 (1-X2), 247 | /// 0b011: e3 paired with X0 X1 (1-X2), 248 | /// 0b100: e4 paired with (1-X0)(1-X1) X2 , 249 | /// 0b101: e5 paired with X0 (1-X1) X2 , 250 | /// 0b110: e6 paired with (1-X0) X1 X2 , 251 | /// 0b111: e7 paired with X0 X1 X2 , 252 | /// ] 253 | /// ``` 254 | /// Return coeffs: the coefficients of the MLE 255 | /// 256 | /// ``` 257 | /// coeffs = [0b000: c0 of constant term, 258 | /// 0b001: c1 of X0 , 259 | /// 0b010: c2 of X1 , 260 | /// 0b011: c3 of X0 X1 , 261 | /// 0b100: c4 of X2 , 262 | /// 0b101: c5 of X0 X2 , 263 | /// 0b110: c6 of X1 X2 , 264 | /// 0b111: c7 of X0 X1 X2 , 265 | /// ] 266 | /// ``` 267 | 268 | pub fn compute_coeffs_from_evals(evals: &Vec) -> Vec { 269 | let mut coeffs = evals.clone(); 270 | let len = coeffs.len(); 271 | assert!(len.is_power_of_two()); 272 | let num_var = log_2(len); 273 | 274 | let mut half = len / 2; 275 | for _i in 0..num_var { 276 | let b = len / half; 277 | for j in (0..b).step_by(2) { 278 | for k in 0..half { 279 | let a = coeffs[j*half + k]; 280 | coeffs[(j+1)*half + k] -= a; 281 | } 282 | } 283 | half = half / 2; 284 | }; 285 | coeffs 286 | } 287 | 288 | /// Compute all evaluations over hypercube from coefficients. 289 | /// The asymptotic complexity is O(N*log^2(N)). 290 | /// 291 | /// Arugment coeffs: the coefficients of the polynomial (non-sparse form) 292 | /// 293 | /// ``` 294 | /// coeffs = [0b000: c0 of constant term, 295 | /// 0b001: c1 of X0 , 296 | /// 0b010: c2 of X1 , 297 | /// 0b011: c3 of X0 X1 , 298 | /// 0b100: c4 of X2 , 299 | /// 0b101: c5 of X0 X2 , 300 | /// 0b110: c6 of X1 X2 , 301 | /// 0b111: c7 of X0 X1 X2 , 302 | /// ] 303 | /// ``` 304 | /// 305 | /// Return evals: the evaluations of the polynomial (non-sparse form) 306 | /// 307 | /// ``` 308 | /// evals = [0b000: e0 paired with (1-X0)(1-X1)(1-X2), 309 | /// 0b001: e1 paired with X0 (1-X1)(1-X2), 310 | /// 0b010: e2 paired with (1-X0) X1 (1-X2), 311 | /// 0b011: e3 paired with X0 X1 (1-X2), 312 | /// 0b100: e4 paired with (1-X0)(1-X1) X2 , 313 | /// 0b101: e5 paired with X0 (1-X1) X2 , 314 | /// 0b110: e6 paired with (1-X0) X1 X2 , 315 | /// 0b111: e7 paired with X0 X1 X2 , 316 | /// ] 317 | /// ``` 318 | /// 319 | pub fn compute_evals_from_coeffs(num_var: usize, coeffs: &[Scalar]) -> Vec { 320 | let len = pow_2(num_var); 321 | assert!(coeffs.len() <= len); 322 | let mut evals = coeffs.to_vec(); 323 | 324 | // Padding zeros to match the length of the hypercube 325 | let zeros = vec![Scalar::zero(); len - coeffs.len()]; 326 | evals.extend(zeros.into_iter()); 327 | 328 | // Initialize the position of folding 329 | let mut half = len / 2; // number of blocks 330 | 331 | for _i in 0..num_var { 332 | for j in 0..half { 333 | let s = len/half; // size of each block 334 | for k in 0..s/2 { // tranverse over the top-half of the block 335 | let a = evals[j*s + k]; 336 | evals[j*s + k + (s/2)] += a; 337 | } 338 | } 339 | half = half / 2; 340 | } 341 | evals 342 | } -------------------------------------------------------------------------------- /univarization/src/snark.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Zero; 2 | use ark_std::log2; 3 | use kzg10::Commitment; 4 | 5 | use crate::*; 6 | use crate::transcript::Transcript; 7 | use crate::sumcheck::{SumcheckSystem, SumcheckProof}; 8 | use crate::mle::{*, evals::*}; 9 | use crate::ph23_pcs::{MlePCSystem, EvalArgument as phEvalArgument}; 10 | 11 | #[derive(Clone)] 12 | pub struct Instance{ 13 | pub a_matrix: Vec>, 14 | pub b_matrix: Vec>, 15 | pub c_matrix: Vec>, // m * m 16 | pub io: Vec, 17 | pub m: usize, // m 18 | } 19 | 20 | impl Instance { 21 | fn new(a_matrix: Vec>, b_matrix: Vec>, c_matrix: Vec>, m: usize, io: Vec)-> Self{ 22 | let s = log_2(m); 23 | assert_eq!(pow_2(s), m); 24 | assert_eq!(io.len() + 1, m/2); 25 | assert_eq!(a_matrix.len(), m); 26 | assert_eq!(b_matrix.len(), m); 27 | assert_eq!(c_matrix.len(), m); 28 | //TODO: check each row 29 | 30 | Self{ 31 | a_matrix, 32 | b_matrix, 33 | c_matrix, 34 | m, 35 | io 36 | } 37 | } 38 | 39 | pub fn eval_matrix_a(&self, input: &Vec) -> Vec { 40 | eval_matrix(&self.a_matrix, input) 41 | } 42 | 43 | pub fn eval_matrix_b(&self, input: &Vec) -> Vec { 44 | eval_matrix(&self.b_matrix, input) 45 | } 46 | 47 | pub fn eval_matrix_c(&self, input: &Vec) -> Vec { 48 | eval_matrix(&self.c_matrix, input) 49 | } 50 | } 51 | 52 | 53 | #[derive(Clone)] 54 | pub struct ProveKey{ 55 | pub instance: Instance, 56 | pub params: MlePCSystem, 57 | } 58 | 59 | impl ProveKey { 60 | fn new(params: MlePCSystem, instance: Instance) -> Self { 61 | Self { 62 | params, instance 63 | } 64 | } 65 | } 66 | 67 | #[derive(Clone)] 68 | pub struct VerifyKey{ 69 | pub instance: Instance, 70 | pub params: MlePCSystem, 71 | } 72 | 73 | impl VerifyKey { 74 | fn new(params: MlePCSystem, instance: Instance) -> Self { 75 | Self { 76 | params, instance 77 | } 78 | } 79 | } 80 | 81 | #[derive(Clone)] 82 | pub struct KeyPair{ 83 | pub prove_key: ProveKey, 84 | pub verify_key: VerifyKey, 85 | } 86 | 87 | impl KeyPair{ 88 | pub fn generate(params: MlePCSystem, instance: Instance) -> Self { 89 | Self{ 90 | prove_key: ProveKey::new(params.clone(), instance.clone()), 91 | verify_key: VerifyKey::new(params, instance) 92 | } 93 | } 94 | 95 | pub fn prove_key(&self) -> ProveKey { 96 | self.prove_key.clone() 97 | } 98 | 99 | pub fn verify_key(&self) -> VerifyKey { 100 | self.verify_key.clone() 101 | } 102 | } 103 | 104 | pub struct MatrixEncode{ 105 | pub comm_a: Commitment, 106 | pub comm_b: Commitment, 107 | pub comm_c: Commitment, 108 | } 109 | 110 | pub struct MatrixEncodeProof { 111 | pub ea: Scalar, 112 | pub eb: Scalar, 113 | pub ec: Scalar, 114 | pub evala: phEvalArgument, 115 | pub evalb: phEvalArgument, 116 | pub evalc: phEvalArgument, 117 | } 118 | 119 | impl MatrixEncode { 120 | pub fn commit(matrix_a: &Vec>, matrix_b: &Vec>, matrix_c: &Vec>) -> Self{ 121 | let mut matrix_a = matrix_a.clone(); 122 | let mut matrix_b = matrix_b.clone(); 123 | let mut matrix_c = matrix_c.clone(); 124 | 125 | let pcs = MlePCSystem::setup(); 126 | 127 | let mut vec_a = Vec::new(); 128 | let mut vec_b = Vec::new(); 129 | let mut vec_c = Vec::new(); 130 | for i in 0..matrix_a.len() { 131 | vec_a.append(&mut matrix_a[i]); 132 | vec_b.append(&mut matrix_b[i]); 133 | vec_c.append(&mut matrix_c[i]); 134 | } 135 | let poly_a = MleEvals::new(vec_a.as_slice()); 136 | let poly_b = MleEvals::new(vec_b.as_slice()); 137 | let poly_c = MleEvals::new(vec_c.as_slice()); 138 | 139 | Self{ 140 | comm_a: pcs.commit(&poly_a), 141 | comm_b: pcs.commit(&poly_b), 142 | comm_c: pcs.commit(&poly_c), 143 | } 144 | 145 | } 146 | } 147 | 148 | pub struct SumcheckRound1Proof{ 149 | pub va: Scalar, 150 | pub vb: Scalar, 151 | pub vc: Scalar, 152 | pub value: Scalar, 153 | pub sumcheckproof: SumcheckProof, 154 | } 155 | 156 | impl SumcheckRound1Proof{ 157 | 158 | pub fn generate_proof(value_a: &Vec, value_b: &Vec, value_c: &Vec, eq_values: &Vec, tr: &mut Transcript) -> (Vec, Scalar, SumcheckProof) { 159 | assert_eq!(value_a.len(), value_b.len()); 160 | assert_eq!(value_a.len(), value_c.len()); 161 | assert_eq!(value_a.len(), eq_values.len()); 162 | 163 | let claim = Scalar::zero(); 164 | let poly_a = MleEvals::new(value_a.as_slice()); 165 | let poly_b = MleEvals::new(value_b.as_slice()); 166 | let poly_c = MleEvals::new(value_c.as_slice()); 167 | let poly_eq = MleEvals::new(eq_values.as_slice()); 168 | 169 | let g_func = |vs: Vec, _| { 170 | vs[3] * (vs[0] * vs[1] - vs[2]) 171 | }; 172 | SumcheckSystem::prove_cubic("sumcheck #1", &claim, vec![&poly_a, &poly_b, &poly_c, &poly_eq], g_func ,3, tr) 173 | } 174 | 175 | pub fn verify(&self, num_rounds: usize, tr: &mut Transcript) -> (Scalar, Vec) { 176 | let claim = Scalar::zero(); 177 | SumcheckSystem::verify(&claim, num_rounds, 3, &self.sumcheckproof, tr) 178 | } 179 | } 180 | 181 | pub struct SumcheckRound2Proof { 182 | 183 | pub value: Scalar, 184 | pub sumcheckproof: SumcheckProof, 185 | 186 | } 187 | 188 | impl SumcheckRound2Proof{ 189 | pub fn generate_proof(claim: Scalar, values: &Vec, input: &Vec, tr: &mut Transcript) ->(Vec, Scalar, SumcheckProof) { 190 | let poly = MleEvals::new(values.as_slice()); 191 | let poly_z = MleEvals::new(input.as_slice()); 192 | 193 | let g_func = |vs: Vec, _| { 194 | vs[1] * vs[0] 195 | }; 196 | SumcheckSystem::prove_cubic("sumcheck #2", &claim, vec![&poly, &poly_z], g_func , 3, tr) 197 | } 198 | 199 | pub fn verify(&self, claim: &Scalar, num_rounds: usize, tr: &mut Transcript) -> (Scalar, Vec){ 200 | SumcheckSystem::verify(claim, num_rounds,3, &self.sumcheckproof, tr) 201 | } 202 | } 203 | 204 | pub struct SNARKProof { 205 | pub v: Scalar, 206 | pub wit_comm: Commitment, 207 | pub proof1: SumcheckRound1Proof, 208 | pub proof2: SumcheckRound2Proof, 209 | pub ew: Scalar, // proof for witness commitment 210 | pub evalw: phEvalArgument,// proof for witness commitment 211 | pub ma: Scalar, 212 | pub mb: Scalar, 213 | pub mc: Scalar, 214 | pub eproof: MatrixEncodeProof, 215 | } 216 | 217 | impl SNARKProof{ 218 | 219 | pub fn generate_proof(params: &MlePCSystem, prove_key: &ProveKey, public: &[Scalar], witness: &[Scalar], encode: &MatrixEncode) -> Self{ 220 | 221 | let mut tr = Transcript::new_with_name(b"snark proof"); 222 | tr.update_with_scalar_vec(&public); 223 | // TODO: transcript adds more stuffs 224 | let s = log_2(prove_key.instance.m); 225 | let mut input = vec![Scalar::one()]; 226 | input.append(&mut public.to_vec()); 227 | input.append(&mut witness.to_vec()); 228 | 229 | assert_eq!(prove_key.instance.m/2, witness.len()); 230 | assert_eq!(prove_key.instance.m, input.len()); 231 | 232 | // step 1. PC.Commit(pp, w) 233 | let wit_poly = MleEvals::new(witness); 234 | let wit_comm = params.commit(&wit_poly); 235 | tr.update_with_g1(&wit_comm.group_element); 236 | 237 | // step 2. generate tau 238 | let tau = tr.generate_challenge_vector(s); 239 | 240 | // let tau_eq_2 = eval_eq_array(&tau); 241 | // change to: 242 | let tau_eq = EqPolynomial::new(&tau).evals_over_hypercube(); 243 | 244 | // step 3~5: sumcheck #1 245 | let value_a = prove_key.instance.eval_matrix_a(&input); 246 | let value_b = prove_key.instance.eval_matrix_b(&input); 247 | let value_c = prove_key.instance.eval_matrix_c(&input); 248 | 249 | {// double check 250 | for i in 0..value_a.len() { 251 | let v = value_a[i] * value_b[i] - value_c[i]; 252 | assert_eq!(v, Scalar::zero()); 253 | } 254 | } 255 | 256 | let (rx, eval_x, sumcheckproof1) = SumcheckRound1Proof::generate_proof(&value_a, &value_b, &value_c, &tau_eq, &mut tr); 257 | 258 | // step 6: eval_x = (va * vb - vc) * eq(rx, tau) 259 | // let va = eval_eq_r(&value_a, &rx); 260 | // let vb = eval_eq_r(&value_b, &rx); 261 | // let vc = eval_eq_r(&value_c, &rx); 262 | // let vr = eval_eq_r(&tau_eq, &rx); 263 | let va = MleEvals::new(value_a.as_slice()).evaluate(&rx); 264 | let vb = MleEvals::new(value_b.as_slice()).evaluate(&rx); 265 | let vc = MleEvals::new(value_c.as_slice()).evaluate(&rx); 266 | let vr = MleEvals::new(&tau_eq).evaluate(&rx); 267 | assert_eq!(eval_x, (va * vb - vc) * vr); 268 | 269 | let proof1 = SumcheckRound1Proof{ 270 | va, 271 | vb, 272 | vc, 273 | sumcheckproof: sumcheckproof1, 274 | value: eval_x, 275 | }; 276 | 277 | // step 8: generate ra, rb, rc 278 | let ra = tr.generate_challenge(); 279 | let rb = tr.generate_challenge(); 280 | let rc = tr.generate_challenge(); 281 | 282 | // step 9: generate claim2 = ra * xa + rb * xb + rc * xc 283 | let claim2 = ra * proof1.va + rb * proof1.vb + rc * proof1.vc; 284 | 285 | // step 10~11: sumcheck #2 286 | let rx_eq = EqPolynomial::new(&rx).evals_over_hypercube(); 287 | let eval_a = eval_matrix_col(&rx_eq, &prove_key.instance.a_matrix); 288 | let eval_b = eval_matrix_col(&rx_eq, &prove_key.instance.b_matrix); 289 | let eval_c = eval_matrix_col(&rx_eq, &prove_key.instance.c_matrix); 290 | let evals = eval_a.iter().zip(eval_b.iter().zip(eval_c.iter())).map(|(va, (vb, vc))|{ 291 | ra * va + rb * vb + rc * vc 292 | }).collect(); 293 | 294 | let (ry, eval_y, sumcheckproof2) = SumcheckRound2Proof::generate_proof(claim2, &evals, &input, &mut tr); 295 | 296 | let proof2 = SumcheckRound2Proof{ 297 | sumcheckproof: sumcheckproof2, 298 | value: eval_y, 299 | }; 300 | 301 | // step 12: v = w(ry[1..]) 302 | // let v = eval_eq_r(&witness.to_vec(), &ry[1..].to_vec()); 303 | let v = MleEvals::new(witness).evaluate(&ry[1..].to_vec()); 304 | tr.update_with_scalar(&v); 305 | 306 | // step 13: proof commit(w) 307 | let (ew, evalw) = params.prove_plain(&wit_comm, &wit_poly, &ry[1..], &mut tr); 308 | tr.update_with_scalar(&ew); 309 | // tr.update_with_scalar(&evalw); 310 | 311 | let ry_eq = EqPolynomial::new(&ry).evals_over_hypercube(); 312 | let ma = eval_matrix_row_col(&prove_key.instance.a_matrix, &rx_eq, &ry_eq); 313 | let mb = eval_matrix_row_col(&prove_key.instance.b_matrix, &rx_eq, &ry_eq); 314 | let mc = eval_matrix_row_col(&prove_key.instance.c_matrix, &rx_eq, &ry_eq); 315 | 316 | let mut matrix_a = prove_key.instance.a_matrix.clone(); 317 | let mut matrix_b = prove_key.instance.b_matrix.clone(); 318 | let mut matrix_c = prove_key.instance.c_matrix.clone(); 319 | 320 | let mut vec_a = Vec::new(); 321 | let mut vec_b = Vec::new(); 322 | let mut vec_c = Vec::new(); 323 | for i in 0..matrix_a.len() { 324 | vec_a.append(&mut matrix_a[i]); 325 | vec_b.append(&mut matrix_b[i]); 326 | vec_c.append(&mut matrix_c[i]); 327 | } 328 | let poly_a = MleEvals::new(vec_a.as_slice()); 329 | let poly_b = MleEvals::new(vec_b.as_slice()); 330 | let poly_c = MleEvals::new(vec_c.as_slice()); 331 | let mut rs = rx.clone(); 332 | let mut ry = ry.clone(); 333 | rs.append(&mut ry); 334 | 335 | let (ea, evala) = params.prove_plain(&encode.comm_a, &poly_a,&rs, &mut tr); 336 | let (eb, evalb) = params.prove_plain(&encode.comm_b, &poly_b,&rs, &mut tr); 337 | let (ec, evalc) = params.prove_plain(&encode.comm_c, &poly_c,&rs, &mut tr); 338 | let eproof = MatrixEncodeProof{ 339 | ea, 340 | eb, 341 | ec, 342 | evala, 343 | evalb, 344 | evalc, 345 | }; 346 | 347 | println!("test...final"); 348 | Self { 349 | v, 350 | wit_comm, 351 | proof1, 352 | proof2, 353 | ew, 354 | evalw, 355 | ma, 356 | mb, 357 | mc, 358 | eproof, 359 | } 360 | } 361 | 362 | pub fn verify(&self, params: &MlePCSystem, verify_key: &VerifyKey, public: &[Scalar], encode: &MatrixEncode) -> bool{ 363 | let mut tr = Transcript::new_with_name(b"snark proof"); 364 | 365 | // TODO: SHOULD feed ABC matrices into transcript 366 | tr.update_with_scalar_vec(&public); 367 | tr.update_with_g1(&self.wit_comm.group_element); 368 | 369 | let s = log_2(verify_key.instance.m); 370 | 371 | // step 2. generate tau 372 | let tau =tr.generate_challenge_vector(s); 373 | let tau_eq = EqPolynomial::new(&tau); 374 | 375 | // step 4~5: sumcheck #1 376 | let (eval_x, rx) = self.proof1.verify(s, &mut tr); 377 | let eq_eval = tau_eq.evaluate(&rx); 378 | 379 | // step 7: verify ex = (va * vb - vc)* eq(rx, tau) 380 | let rhs = (self.proof1.va * self.proof1.vb - self.proof1.vc) * eq_eval; 381 | let result1 = eval_x == rhs; 382 | println!("result1 = {}", result1); 383 | 384 | // step 8: generate ra, rb, rc 385 | let ra = tr.generate_challenge(); 386 | let rb = tr.generate_challenge(); 387 | let rc = tr.generate_challenge(); 388 | 389 | // step 9: generate claim2 = ra * xa + rb * xb + rc * xc 390 | let claim2 = ra * self.proof1.va + rb * self.proof1.vb + rc * self.proof1.vc; 391 | 392 | // step 10~11: sumcheck #2 393 | let (eval_y, ry) = self.proof2.verify(&claim2, s, &mut tr); 394 | 395 | // step 12 396 | tr.update_with_scalar(&self.v); 397 | 398 | // step 13~14: verify commit(w) 399 | let result3 = params.verify_plain(&self.wit_comm, &ry[1..], &self.ew, &self.evalw, &mut tr); 400 | println!("result3 = {}", result3); 401 | tr.update_with_scalar(&self.ew); 402 | 403 | // step 15: vz = (1-ry[0])w(ry[1..]) + ry[0]input(ry[1..]) 404 | let mut input = vec![Scalar::one()]; 405 | input.append(&mut public.to_vec()); 406 | let vio = MleEvals::new(&input).evaluate(&ry[1..]); 407 | let vz = (Scalar::one() - ry[0]) * vio + ry[0] * self.v; 408 | 409 | // step 16: v1 = A(rx, ry), v2 = B(rx, ry), v3 = C(rx, ry) 410 | let result2 = eval_y == (ra * self.ma + rb * self.mb + rc * self.mc) * vz; 411 | println!("result2 = {}", result2); 412 | 413 | let mut rs = rx.clone(); 414 | let mut ry = ry.clone(); 415 | rs.append(&mut ry); 416 | let rea = params.verify_plain(&encode.comm_a, &rs.as_slice(), &self.eproof.ea, &self.eproof.evala, &mut tr); 417 | let reb = params.verify_plain(&encode.comm_b, &rs.as_slice(), &self.eproof.eb, &self.eproof.evalb, &mut tr); 418 | let rec = params.verify_plain(&encode.comm_c, &rs.as_slice(), &self.eproof.ec, &self.eproof.evalc, &mut tr); 419 | 420 | println!("rea = {}, reb = {}, rec = {}", rea, reb, rec); 421 | 422 | rea && reb && rec 423 | } 424 | } 425 | 426 | // fn eval_eq_array(tau: &Vec) -> Vec{ 427 | // let base: usize = 2; 428 | // let len = tau.len(); 429 | // let pow_len = base.pow(len as u32); 430 | 431 | // let mut evals: Vec = vec![Scalar::one(); pow_len]; 432 | // let mut size = 1; 433 | // for i in 0..len { 434 | // let scalar = tau[len - i - 1]; 435 | // for j in 0..size { 436 | // evals[size + j] = scalar * &evals[j]; // eval * scalar 437 | // evals[j] = (Scalar::one() - &scalar) * &evals[j]; // eval * (1- scalar) 438 | // } 439 | // size *= 2; 440 | // } 441 | // evals 442 | // } 443 | 444 | // \prod (1-tau[i])(1-values[i]) + tau[i] * values[i] 445 | // fn eval_eq(tau: &Vec, values: &Vec) -> Scalar { 446 | // assert_eq!(tau.len(), values.len()); 447 | // tau.iter().zip(values.iter()).map(|(&a,&b)| a * b + (Scalar::one() - a) * (Scalar::one() - b)).product() 448 | // } 449 | 450 | 451 | // pub fn eval_eq_r(values: &Vec, r: &Vec) -> Scalar { 452 | 453 | // let m = values.len(); 454 | // let rm = r.len(); 455 | // assert_eq!(log2(m), rm as u32); 456 | 457 | // values.iter().zip(eval_eq_array(r).iter()).map(|(&v, &r)| v * r).sum() 458 | // } 459 | 460 | /// compute vector * matrix 461 | fn eval_matrix_col(rx_eq: &Vec, matrix: &Vec>) -> Vec { 462 | assert_eq!(rx_eq.len(), matrix.len()); 463 | let m = rx_eq.len(); 464 | let mut evals = vec![Scalar::zero(); m]; 465 | for i in 0..m { 466 | for j in 0..m { 467 | evals[j] += rx_eq[i]* matrix[i][j]; 468 | } 469 | } 470 | evals 471 | } 472 | 473 | fn eval_matrix_row_col(matrix: &Vec>, rx_eq: &Vec, ry_eq: &Vec) -> Scalar { 474 | 475 | assert_eq!(matrix.len(), rx_eq.len()); 476 | 477 | matrix.iter().zip(rx_eq.iter()).map(|(row, rx)|{ 478 | row.iter().zip(ry_eq.iter()).map(|(&v, &ry)| v * rx * ry).sum::() 479 | }).sum() 480 | } 481 | 482 | /// compute matrix * vector 483 | fn eval_matrix(matrix: &Vec>, values: &Vec) -> Vec { 484 | matrix.iter().map(|coeffs|{ 485 | assert_eq!(coeffs.len(), values.len()); 486 | coeffs.iter().zip(values.iter()).map(|(&coeff, &value)| coeff * value).sum() 487 | }).collect() 488 | } 489 | 490 | 491 | mod tests { 492 | use crate::*; 493 | use super::*; 494 | 495 | #[test] 496 | fn test_snark() { 497 | // input: 1, 2, 2, 498 | // witness: 2, 8, 7, 12 499 | let max_degree = 8; 500 | let params = MlePCSystem::setup(); 501 | // a * b = c 502 | // (b + c) * d = e 503 | // - a + e = f 504 | // f * d = g + 2 505 | // b * 1 = d 506 | // 4 * d = e 507 | let a_matrix = vec![ 508 | vec![Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 509 | vec![Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 510 | vec![Scalar::zero(), Scalar::zero() - Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero()], 511 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero()], 512 | vec![Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 513 | vec![Scalar::one().double().double(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 514 | vec![Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::one(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero()], 515 | vec![Scalar::zero(), Scalar::zero() - Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero()], 516 | ]; 517 | let b_matrix = vec![ 518 | vec![Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 519 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 520 | vec![Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 521 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 522 | vec![Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 523 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 524 | vec![Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 525 | vec![Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 526 | ]; 527 | let c_matrix = vec![ 528 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 529 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero()], 530 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero()], 531 | vec![Scalar::one().double(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one()], 532 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero(), Scalar::zero()], 533 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one(), Scalar::zero(), Scalar::zero()], 534 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one()], 535 | vec![Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::zero(), Scalar::one()], 536 | ]; 537 | let encode = MatrixEncode::commit(&a_matrix, &b_matrix, &c_matrix); 538 | let public = vec![Scalar::one(), Scalar::one().double(), Scalar::one().double()]; 539 | let witness = vec![Scalar::from_u64(2), Scalar::from_u64(8), Scalar::from_u64(7), Scalar::from_u64(12)]; 540 | let instance = Instance::new(a_matrix, b_matrix, c_matrix, 8, public.clone()); 541 | let keypair = KeyPair::generate(params.clone(), instance); 542 | let proof = SNARKProof::generate_proof(¶ms, &keypair.prove_key, &public.as_slice(), &witness.as_slice(), &encode); 543 | let result = proof.verify(¶ms, &keypair.verify_key, &public.as_slice(), &encode); 544 | println!("result = {}", result); 545 | 546 | } 547 | 548 | } -------------------------------------------------------------------------------- /univarization/src/sumcheck.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use crate::*; 4 | use crate::mle::{EqPolynomial, evals::MleEvals, coeffs_sparse::MleCoeffs}; 5 | use crate::unipoly::UniPolynomial; 6 | use crate::transcript::Transcript; 7 | use log::debug; 8 | 9 | // TODO: any setup for sumcheck? 10 | pub struct SumcheckSystem { } 11 | 12 | // TODO: do we need to store the degree of the polynomial? 13 | pub struct SumcheckProof { 14 | pub name: String, 15 | pub internal_unipolys: Vec>, 16 | } 17 | 18 | impl SumcheckSystem { 19 | 20 | // a simple sumcheck for summarize a vector 21 | // 22 | // prove: claimed_sum ?= ∑ f(x_vec) 23 | // return: 24 | // 1. randomness r_vec 25 | // 2. reduced sum 26 | // 3. proof 27 | 28 | // NOTE: `name` is useful for debugging, give me a better name pls. 29 | pub fn prove_single( 30 | name: &str, 31 | claimed_sum: &Scalar, 32 | f: &MleEvals, 33 | trans: &mut Transcript, 34 | ) -> (Vec, Scalar, SumcheckProof) { 35 | let rounds = f.num_var; 36 | let domain = [Scalar::zero(), Scalar::one()]; 37 | 38 | // TODO: do we need to truncate `poly`? 39 | let mut poly = MleEvals::new(&f.evals.clone()); 40 | let mut e = claimed_sum.to_owned(); 41 | 42 | // r_vec for collecting randomness generated in each round by RO 43 | let mut r_vec: Vec = Vec::new(); 44 | 45 | // ipoly_vec for collecting uni-polynomials generated in each round 46 | let mut ipoly_vec: Vec> = Vec::new(); 47 | 48 | let mut half = poly.len() / 2; 49 | 50 | for _rd in 0..rounds { 51 | debug!("rd={}, poly={}", _rd, poly); 52 | let mut eval_at_0 = Scalar::zero(); 53 | let mut eval_at_1 = Scalar::zero(); 54 | 55 | //start folding 56 | for i in 0..half { 57 | // f'(0) = ∑ f(low) 58 | eval_at_0 += poly[i]; 59 | // f'(1) = ∑ f(high) 60 | eval_at_1 += poly[i + half]; 61 | } 62 | 63 | // check f'(0) + f'(1) = f(r) 64 | assert_eq!(e, eval_at_0 + eval_at_1); 65 | 66 | trans.update_with_scalar(&eval_at_0); 67 | trans.update_with_scalar(&eval_at_1); 68 | debug!("eval_at_0={}", &eval_at_0); 69 | debug!("eval_at_1={}", &eval_at_1); 70 | 71 | // TODO: temporarily debugging 72 | // let r = Scalar::from_usize(_rd + 1); 73 | let r = trans.generate_challenge(); 74 | r_vec.insert(0, r); 75 | debug!("r={}", r); 76 | 77 | poly.fold_into_half(&r); 78 | half /= 2; 79 | 80 | let g_poly = [eval_at_0, eval_at_1]; 81 | // reduce the sum 82 | e = UniPolynomial::evaluate_from_evals(&g_poly, &r, &domain); 83 | // debug!("g={}", scalar_vector_to_string(&g_poly.evals)); 84 | // debug!("g.coeffs={}", scalar_vector_to_string(&g_poly.coeffs)); 85 | debug!("g[{}]={}", ScalarExt::to_string(&r), ScalarExt::to_string(&e)); 86 | // debug!("reduced sum={}", &e); 87 | ipoly_vec.push(g_poly.to_vec()); 88 | } 89 | 90 | (r_vec, e, SumcheckProof { 91 | name: String::from(name), 92 | internal_unipolys: ipoly_vec }) 93 | } 94 | 95 | pub fn verify_single( 96 | claimed_sum: &Scalar, 97 | num_rounds: usize, 98 | prf: &SumcheckProof, 99 | trans: &mut Transcript, 100 | ) -> (Scalar, Vec) { 101 | 102 | let mut e = *claimed_sum; 103 | let mut r_vec: Vec = Vec::new(); 104 | let ipoly_vec = &prf.internal_unipolys; 105 | let domain = [Scalar::zero(), Scalar::one()]; 106 | 107 | assert_eq!(num_rounds, ipoly_vec.len()); 108 | 109 | for _rd in 0..num_rounds { 110 | let ipoly = &ipoly_vec[_rd]; 111 | 112 | // every ipoly should be a linear polynomial 113 | assert_eq!(ipoly.len(), 2); 114 | 115 | let eval_at_0 = ipoly[0]; 116 | let eval_at_1 = ipoly[1]; 117 | 118 | assert_eq!(e, eval_at_0 + eval_at_1, 119 | "sumcheck [{}] failed at round {}", &prf.name, _rd); 120 | 121 | trans.update_with_scalar_vec(ipoly.as_slice()); 122 | 123 | // TODO: temporarily debugging 124 | // let r = Scalar::from_usize(_rd + 1); 125 | let r = trans.generate_challenge(); 126 | r_vec.insert(0, r); 127 | 128 | debug!("r[{}]={}", _rd, r); 129 | 130 | let eval_at_r = UniPolynomial::evaluate_from_evals(&ipoly, &r, &domain); 131 | // reduce the sum 132 | e = eval_at_r 133 | } 134 | // (reduced_sum, randomness vector from RO) 135 | (e, r_vec) 136 | } 137 | 138 | pub fn prove_product( 139 | name: &str, 140 | claimed_sum: &Scalar, 141 | f: &mut MleEvals, 142 | g: &mut MleEvals, 143 | trans: &mut Transcript, 144 | ) -> (Vec, Scalar, SumcheckProof) 145 | { 146 | assert_eq!(f.len(), g.len()); 147 | let mle_size = f.len(); 148 | let num_rounds = f.num_var; 149 | let domain = (0..=2).map(|i| Scalar::from_usize(i)).collect::>(); 150 | 151 | let mut e = claimed_sum.to_owned(); 152 | 153 | // r_vec for collecting randomness generated in each round by RO 154 | let mut r_vec: Vec = Vec::new(); 155 | 156 | // ipoly_vec for collecting uni-polynomials generated in each round 157 | let mut ipoly_vec: Vec> = Vec::new(); 158 | 159 | let mut half = mle_size / 2; 160 | 161 | debug!("==== sumcheck prove start ====="); 162 | 163 | // start sumchecking 164 | for _rd in 0..num_rounds { 165 | 166 | // g(0), g(1), g(2) 167 | let mut g_evals: Vec = vec![Scalar::zero(); 3]; 168 | 169 | //start folding 170 | for i in 0..half { 171 | 172 | // TODO: here we only support degree_bound = 3 173 | // generalize it to any degree later on, not hard as you can see 174 | 175 | // h(0) = ∑ f(low) * g(low) 176 | g_evals[0] += f[i] * g[i]; 177 | // h(1) = ∑ f(high) * g(high) 178 | g_evals[1] += f[half + i] * g[half + i]; 179 | // g(2) = ∑ (-f(low) + 2f(high)) * (-g(low) + 2g(high)) 180 | g_evals[2] += (Scalar::from(2) *f[half + i] - f[i]) * (Scalar::from(2) * g[half + i] - g[i]); 181 | } 182 | 183 | // check f'(0) + f'(1) = f(r) 184 | assert_eq!(e, g_evals[0] + g_evals[1]); 185 | println!("rd={}, g_evals={}", _rd, scalar_vector_to_string(&g_evals)); 186 | 187 | trans.update_with_scalar_vec(&g_evals); 188 | let r = trans.generate_challenge(); 189 | r_vec.insert(0, r); 190 | 191 | f.fold_into_half(&r); 192 | g.fold_into_half(&r); 193 | half /= 2; 194 | 195 | // reduce the sum 196 | e = UniPolynomial::evaluate_from_evals(&g_evals, &r, &domain); 197 | ipoly_vec.push(g_evals); 198 | } 199 | 200 | debug!("==== sumcheck prove end ====="); 201 | 202 | (r_vec, e, SumcheckProof { 203 | name: String::from(name), 204 | internal_unipolys: ipoly_vec }) 205 | } 206 | 207 | 208 | // G_func: compute the polynomial G(f_{0,i}, f_{1,i}, ..., f_{m,i}) horizontally 209 | // e.g. G(f1(x_vec), f2(x_vec), f3(x_vec), f4(x_vec)) 210 | // = f1(x_vec) * f2(x_vec) * f3(x_vec) + f1(x_vec) * f4(x_vec) 211 | pub fn prove_cubic( 212 | name: &str, 213 | claimed_sum: &Scalar, 214 | f_vec: Vec<&MleEvals>, 215 | G_func: Func, 216 | degree_bound: usize, 217 | trans: &mut Transcript, 218 | ) -> (Vec, Scalar, SumcheckProof) 219 | where 220 | Func: Fn(Vec, usize) -> Scalar, 221 | { 222 | assert!(f_vec.len() > 1); 223 | for f in f_vec.iter() { 224 | assert_eq!(f.num_var, f_vec[0].num_var) 225 | } 226 | let poly_size = f_vec[0].len(); 227 | let num_poly = f_vec.len(); 228 | let mut polys: Vec = Vec::new(); 229 | for i in 0..num_poly { 230 | polys.push(f_vec[i].clone()) 231 | } 232 | 233 | let num_rounds = f_vec[0].num_var; 234 | let domain = (0..=degree_bound).map(|i| Scalar::from_usize(i)).collect::>(); 235 | 236 | let mut e = claimed_sum.to_owned(); 237 | 238 | // TODO: should add degree bounds checking for G_func(f_vec) 239 | 240 | // r_vec for collecting randomness generated in each round by RO 241 | let mut r_vec: Vec = Vec::new(); 242 | 243 | // ipoly_vec for collecting uni-polynomials generated in each round 244 | let mut ipoly_vec: Vec> = Vec::new(); 245 | 246 | let mut half = poly_size / 2; 247 | 248 | debug!("==== sumcheck prove start ====="); 249 | 250 | // start sumchecking 251 | for _rd in 0..num_rounds { 252 | 253 | // g(0), g(1), g(2), ..., g(d) 254 | let mut g_evals: Vec = Vec::new(); 255 | for i in 0..=degree_bound { 256 | g_evals.push(Scalar::zero()); 257 | } 258 | 259 | //start folding 260 | for i in 0..half { 261 | 262 | // TODO: here we only support degree_bound = 3 263 | // generalize it to any degree later on, not hard as you can see 264 | 265 | // h(0) = ∑ f(low) 266 | g_evals[0] += G_func((0..num_poly).map(|k| polys[k][i]).collect::>(), num_poly); 267 | // h(1) = ∑ f(high) 268 | g_evals[1] += G_func((0..num_poly).map(|k| polys[k][half + i]).collect(), num_poly); 269 | 270 | // g(2) = ∑ (-f(low) + 2f(high)) 271 | g_evals[2] += G_func((0..num_poly).map( 272 | |k| Scalar::from(2) * polys[k][half + i] - polys[k][i]).collect(), num_poly 273 | ); 274 | 275 | // F(3) = ∑ (-2f(low) + 3f(high)) 276 | g_evals[3] += G_func((0..num_poly).map( 277 | |k| Scalar::from(3) * polys[k][half + i] - Scalar::from(2) * polys[k][i]).collect(), num_poly 278 | ); 279 | } 280 | 281 | // check f'(0) + f'(1) = f(r) 282 | assert_eq!(e, g_evals[0] + g_evals[1], "sumcheck failed at round {}", _rd); 283 | println!("rd={}, g_evals={}", _rd, scalar_vector_to_string(&g_evals)); 284 | 285 | trans.update_with_scalar_vec(&g_evals); 286 | let r = trans.generate_challenge(); 287 | r_vec.insert(0, r); 288 | 289 | for i in 0..num_poly { 290 | polys[i].fold_into_half(&r); 291 | } 292 | half /= 2; 293 | // println!("poly[0] = {}, poly[1] = {}, poly[2] = {}, poly[3] = {}", polys[0],polys[1],polys[2], polys[3]); 294 | 295 | // reduce the sum 296 | e = UniPolynomial::evaluate_from_evals(&g_evals, &r, &domain); 297 | ipoly_vec.push(g_evals); 298 | } 299 | 300 | debug!("==== sumcheck prove end ====="); 301 | 302 | (r_vec, e, SumcheckProof { 303 | name: String::from(name), 304 | internal_unipolys: ipoly_vec }) 305 | } 306 | 307 | // NOTE: verify() does not return boolean, 308 | // but the reduced sum and randomness vector. 309 | // It is only parially verified. 310 | pub fn verify( 311 | claimed_sum: &Scalar, 312 | num_rounds: usize, 313 | degree_bound: usize, 314 | prf: &SumcheckProof, 315 | trans: &mut Transcript, 316 | ) -> (Scalar, Vec) { 317 | 318 | // TODO: use `log::trace!()` instead of `debug!()` 319 | debug!("==== sumcheck verify begin ====="); 320 | 321 | let mut e = *claimed_sum; 322 | let mut r_vec: Vec = Vec::new(); 323 | let ipoly_vec = &prf.internal_unipolys; 324 | let domain = (0..=degree_bound).map(|i| Scalar::from_usize(i)).collect::>(); 325 | 326 | assert_eq!(num_rounds, ipoly_vec.len()); 327 | 328 | for _rd in 0..num_rounds { 329 | let ipoly = &ipoly_vec[_rd]; 330 | 331 | // every ipoly's domain should be equal to degree+1 332 | assert_eq!(ipoly.len(), degree_bound + 1); 333 | 334 | let eval_at_0 = ipoly[0]; 335 | let eval_at_1 = ipoly[1]; 336 | 337 | assert_eq!(e, eval_at_0 + eval_at_1, 338 | "sumcheck [{}] failed at round {}", &prf.name, _rd); 339 | 340 | trans.update_with_scalar_vec(ipoly.as_slice()); 341 | let r = trans.generate_challenge(); 342 | r_vec.insert(0, r); 343 | debug!("r[{}]={}", _rd, r); 344 | 345 | let eval_at_r = UniPolynomial::evaluate_from_evals(&ipoly, &r, &domain); 346 | // reduce the sum 347 | e = eval_at_r 348 | } 349 | // (reduced_sum, randomness vector from RO) 350 | (e, r_vec) 351 | } 352 | 353 | pub fn prove_G_polymonial( 354 | name: &str, 355 | claimed_sum: &Scalar, 356 | f_vec: &[MleEvals], 357 | G_func: Func, 358 | degree_bound: usize, 359 | trans: &mut Transcript, 360 | ) -> (Vec, Scalar, SumcheckProof) 361 | where 362 | Func: Fn(Vec, usize) -> Scalar, 363 | { 364 | assert!(f_vec.len() > 1); 365 | for f in f_vec.iter() { 366 | assert_eq!(f.num_var, f_vec[0].num_var) 367 | } 368 | let poly_size = f_vec[0].len(); 369 | let num_poly = f_vec.len(); 370 | let mut polys: Vec = Vec::new(); 371 | for i in 0..num_poly { 372 | polys.push(f_vec[i].clone()) 373 | } 374 | let domain = (0..=degree_bound).map(|i| Scalar::from_usize(i)).collect::>(); 375 | 376 | let num_rounds = f_vec[0].num_var; 377 | 378 | let mut e = claimed_sum.to_owned(); 379 | 380 | // TODO: should add degree bounds checking for G_func(f_vec) 381 | 382 | // r_vec for collecting randomness generated in each round by RO 383 | let mut r_vec: Vec = Vec::new(); 384 | 385 | // ipoly_vec for collecting uni-polynomials generated in each round 386 | let mut ipoly_vec: Vec> = Vec::new(); 387 | 388 | let mut half = poly_size / 2; 389 | 390 | debug!("==== sumcheck prove start ====="); 391 | 392 | // start sumchecking 393 | for _rd in 0..num_rounds { 394 | 395 | // g(0), g(1), g(2), ..., g(d) 396 | let mut g_evals: Vec = Vec::new(); 397 | 398 | // g(i) = ∑ (g_low * f(low,i) + g_high * f(high,i)) 399 | let mut g_fold_factors_high: Vec = Vec::new(); 400 | let mut g_fold_factors_low: Vec = Vec::new(); 401 | for i in 0..=degree_bound { 402 | g_evals.push(Scalar::zero()); 403 | g_fold_factors_high.push(Scalar::from_i64(i as i64)); 404 | g_fold_factors_low.push(Scalar::from_i64((i as i64) - 1)); 405 | } 406 | 407 | //start folding 408 | for i in 0..half { 409 | 410 | // support arbitrary degree bound 411 | for d in 0..=degree_bound { 412 | 413 | // h(d) = ∑ [ g_factor_low * f(low) + g_factor_high * f(high) ] 414 | let g_row = (0..num_poly).map(|k| 415 | g_fold_factors_high[d] * polys[k][half + i] // high 416 | - g_fold_factors_low[d] * polys[k][i] // low 417 | ).collect::>(); 418 | g_evals[d] += G_func(g_row, num_poly); 419 | } 420 | } 421 | 422 | // check f'(0) + f'(1) = f(r) 423 | assert_eq!(e, g_evals[0] + g_evals[1]); 424 | println!("rd={}, g_evals={}", _rd, scalar_vector_to_string(&g_evals)); 425 | 426 | trans.update_with_scalar_vec(&g_evals); 427 | let r = trans.generate_challenge(); 428 | r_vec.insert(0, r); 429 | 430 | for i in 0..num_poly { 431 | polys[i].fold_into_half(&r); 432 | } 433 | half /= 2; 434 | // println!("poly[0] = {}, poly[1] = {}, poly[2] = {}, poly[3] = {}", polys[0],polys[1],polys[2], polys[3]); 435 | 436 | // reduce the sum 437 | e = UniPolynomial::evaluate_from_evals(&g_evals, &r, &domain); 438 | ipoly_vec.push(g_evals); 439 | 440 | } 441 | 442 | debug!("==== sumcheck prove end ====="); 443 | 444 | (r_vec, e, SumcheckProof { 445 | name: String::from(name), 446 | internal_unipolys: ipoly_vec }) 447 | } 448 | 449 | } 450 | 451 | 452 | mod tests { 453 | use super::*; 454 | 455 | #[test] 456 | fn test_sumcheck_single_prove_verify_0() { 457 | init_logger(); 458 | 459 | // let rng = &mut ark_std::test_rng(); 460 | // let vs = Scalar::rand_vector(vector_size[i], rng); 461 | 462 | let vs = Scalar::from_usize_vector(&[1,2,3,4]); 463 | let f = MleEvals::new(&vs); 464 | let mut tr = Transcript::new_with_name(b"test"); 465 | 466 | let num_rounds = f.num_var; 467 | let sum = vs.iter().sum::(); 468 | 469 | let (r_vec, re, prf) = SumcheckSystem::prove_single("test", &sum, &f, &mut tr.clone()); 470 | 471 | debug!("r_vec={}", scalar_vector_to_string(&r_vec)); 472 | debug!("reduced_sum={}", ScalarExt::to_string(&re)); 473 | 474 | let (re_prime, r_vec_prime) = SumcheckSystem::verify_single(&sum, num_rounds, &prf, &mut tr.clone()); 475 | assert_eq!(re, re_prime); 476 | 477 | assert_eq!(r_vec, r_vec_prime); 478 | 479 | // final verification 480 | // let rx: Vec = r_vec.into_iter().rev().collect(); 481 | assert_eq!(f.evaluate(&r_vec), re); 482 | } 483 | 484 | 485 | #[test] 486 | fn test_sumcheck_single_prove_verify() { 487 | init_logger(); 488 | 489 | let rng = &mut ark_std::test_rng(); 490 | let vector_size = [2,4,8,16,32,64,128,256,512,1024]; 491 | 492 | for i in 0..vector_size.len() { 493 | let vs = Scalar::rand_vector(vector_size[i], rng); 494 | 495 | // let vs = Scalar::from_usize_vector(&[1,2,3,4]); 496 | let f = MleEvals::new(&vs); 497 | let mut tr = Transcript::new_with_name(b"test"); 498 | 499 | let num_rounds = f.num_var; 500 | let sum = vs.iter().sum::(); 501 | 502 | let (r_vec, re, prf) = SumcheckSystem::prove_single("test", &sum, &f, &mut tr.clone()); 503 | 504 | debug!("r_vec={}", scalar_vector_to_string(&r_vec)); 505 | debug!("reduced_sum={}", ScalarExt::to_string(&re)); 506 | 507 | let (re_prime, r_vec_prime) = SumcheckSystem::verify_single(&sum, num_rounds, &prf, &mut tr.clone()); 508 | assert_eq!(re, re_prime); 509 | 510 | assert_eq!(r_vec, r_vec_prime); 511 | 512 | // final verification 513 | assert_eq!(f.evaluate(&r_vec), re); 514 | } 515 | } 516 | 517 | #[test] 518 | fn test_sumcheck_product_prove_verify() { 519 | let rng = &mut ark_std::test_rng(); 520 | let mut tr = Transcript::new_with_name(b"test"); 521 | 522 | let f_vec = Scalar::from_usize_vector(&[2,2,3,4]); 523 | let g_vec = Scalar::from_usize_vector(&[3,3,2,4]); 524 | let mut f = MleEvals::new(&f_vec); 525 | let mut g = MleEvals::new(&g_vec); 526 | 527 | let sum = f_vec.iter().enumerate().map( 528 | |(i, v0)| *v0 * g_vec[i]).sum::(); 529 | 530 | let (rs, re, prf) = SumcheckSystem::prove_product("test", &sum, 531 | &mut f.clone(), &mut g.clone(), &mut tr.clone()); 532 | assert_eq!(re, f.evaluate(&rs)*g.evaluate(&rs)); 533 | 534 | let (re_prime, rs_prime) = SumcheckSystem::verify(&sum, f.num_var, 2, &prf, &mut tr.clone()); 535 | assert_eq!(re_prime, re); 536 | assert_eq!(rs_prime, rs); 537 | 538 | } 539 | 540 | #[test] 541 | fn test_sumcheck_cubic_prove_verify() { 542 | init_logger(); 543 | 544 | let rng = &mut ark_std::test_rng(); 545 | let vector_size = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]; 546 | 547 | for i in 0..vector_size.len() { 548 | 549 | let f0_vec = Scalar::rand_vector(vector_size[i], rng); 550 | let f1_vec = Scalar::rand_vector(vector_size[i], rng); 551 | let f2_vec = Scalar::rand_vector(vector_size[i], rng); 552 | let f0 = MleEvals::new(&f0_vec); 553 | let f1 = MleEvals::new(&f1_vec); 554 | let f2 = MleEvals::new(&f2_vec); 555 | 556 | let tr = Transcript::new_with_name(b"test"); 557 | 558 | let G_func = |vs: Vec, size: usize| { 559 | vs[0] * vs[1] * vs[2] 560 | }; 561 | 562 | let num_rounds = f0.num_var; 563 | let sum = f0_vec.iter().enumerate().map( 564 | |(i, v0)| *v0 * f1_vec[i] * f2_vec[i]).sum::(); 565 | 566 | debug!("sum={}", sum); 567 | 568 | let (r_vec, re, prf) = SumcheckSystem::prove_cubic("test", &sum, 569 | vec![&f0, &f1, &f2], &G_func, 3, &mut tr.clone()); 570 | 571 | debug!("r_vec={}", scalar_vector_to_string(&r_vec)); 572 | debug!("reduced_sum={}", ScalarExt::to_string(&re)); 573 | 574 | let (re_prime, r_vec_prime) = SumcheckSystem::verify(&sum, num_rounds, 3, &prf, &mut tr.clone()); 575 | assert_eq!(re, re_prime); 576 | assert_eq!(r_vec, r_vec_prime); 577 | 578 | // final verification 579 | assert_eq!(re, G_func(vec![f0.evaluate(&r_vec), f1.evaluate(&r_vec), f2.evaluate(&r_vec)], 3)); 580 | } 581 | } 582 | 583 | #[test] 584 | fn test_sumcheck_cubic_prove_verify_2() { 585 | init_logger(); 586 | 587 | let rng = &mut ark_std::test_rng(); 588 | let vector_size = [8]; 589 | 590 | for i in 0..vector_size.len() { 591 | 592 | let f0_vec = Scalar::rand_vector(vector_size[i], rng); 593 | let f1_vec = Scalar::rand_vector(vector_size[i], rng); 594 | let f2_vec = Scalar::rand_vector(vector_size[i], rng); 595 | let f0 = MleEvals::new(&f0_vec); 596 | let f1 = MleEvals::new(&f1_vec); 597 | let f2 = MleEvals::new(&f2_vec); 598 | 599 | let tr = Transcript::new_with_name(b"test"); 600 | 601 | let G_func = |vs: Vec, size: usize| { 602 | vs[0] * vs[1] * vs[2] 603 | }; 604 | 605 | let num_rounds = f0.num_var; 606 | let sum = f0_vec.iter().enumerate().map( 607 | |(i, v0)| *v0 * f1_vec[i] * f2_vec[i]).sum::(); 608 | 609 | debug!("sum={}", sum); 610 | 611 | let (r_vec, re, prf) = SumcheckSystem::prove_cubic("test", &sum, 612 | vec![&f0, &f1, &f2], &G_func, 3, &mut tr.clone()); 613 | 614 | debug!("r_vec={}", scalar_vector_to_string(&r_vec)); 615 | debug!("reduced_sum={}", ScalarExt::to_string(&re)); 616 | 617 | let (re_prime, r_vec_prime) = SumcheckSystem::verify(&sum, num_rounds, 3, &prf, &mut tr.clone()); 618 | assert_eq!(re, re_prime); 619 | assert_eq!(r_vec, r_vec_prime); 620 | 621 | // final verification 622 | assert_eq!(re, G_func(vec![f0.evaluate(&r_vec), f1.evaluate(&r_vec), f2.evaluate(&r_vec)], 3)); 623 | } 624 | } 625 | 626 | #[test] 627 | fn test_sumcheck_G_prove_verify() { 628 | init_logger(); 629 | 630 | let rng = &mut ark_std::test_rng(); 631 | let vector_size = [8]; 632 | 633 | for i in 0..vector_size.len() { 634 | 635 | let f0_vec = Scalar::rand_vector(vector_size[i], rng); 636 | let f1_vec = Scalar::rand_vector(vector_size[i], rng); 637 | let f2_vec = Scalar::rand_vector(vector_size[i], rng); 638 | let f0 = MleEvals::new(&f0_vec); 639 | let f1 = MleEvals::new(&f1_vec); 640 | let f2 = MleEvals::new(&f2_vec); 641 | 642 | let mut tr = Transcript::new_with_name(b"test"); 643 | 644 | let G_func = |vs: Vec, size: usize| { 645 | vs[0] * vs[1] * vs[2] 646 | }; 647 | 648 | let num_rounds = f0.num_var; 649 | let sum = f0_vec.iter().enumerate().map( 650 | |(i, v0)| *v0 * f1_vec[i] * f2_vec[i]).sum::(); 651 | 652 | debug!("sum={}", sum); 653 | 654 | let (r_vec, re, prf) = SumcheckSystem::prove_G_polymonial("test", &sum, 655 | &[f0.clone(), f1.clone(), f2.clone()], &G_func, 3, &mut tr.clone()); 656 | 657 | debug!("r_vec={}", scalar_vector_to_string(&r_vec)); 658 | debug!("reduced_sum={}", ScalarExt::to_string(&re)); 659 | 660 | let (re_prime, r_vec_prime) = SumcheckSystem::verify(&sum, num_rounds, 3, &prf, &mut tr.clone()); 661 | assert_eq!(re, re_prime); 662 | assert_eq!(r_vec, r_vec_prime); 663 | 664 | // final verification 665 | assert_eq!(re, G_func(vec![f0.evaluate(&r_vec), f1.evaluate(&r_vec), f2.evaluate(&r_vec)], 3)); 666 | } 667 | } 668 | 669 | } -------------------------------------------------------------------------------- /univarization/src/transcript.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use crate::*; 4 | use sha3::{Digest, Keccak256}; 5 | 6 | #[derive(Debug, Clone)] 7 | pub struct Transcript { 8 | //uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; 9 | //here just represent the highest byte 0x1f 10 | pub FR_MASK: u8, 11 | pub DST_0: [u8; 4], 12 | pub DST_1: [u8; 4], 13 | pub DST_CHALLENGE: [u8; 4], 14 | 15 | pub state_0: [u8; 32], 16 | pub state_1: [u8; 32], 17 | pub challenge_counter: u32, 18 | } 19 | 20 | impl Transcript { 21 | 22 | pub fn new() -> Self { 23 | Transcript { 24 | FR_MASK: 0x1f, 25 | DST_0: [0u8; 4], 26 | DST_1: [0u8, 0u8, 0u8, 1u8], 27 | DST_CHALLENGE: [0u8, 0u8, 0u8, 2u8], 28 | state_0: [0u8; 32], 29 | state_1: [0u8; 32], 30 | challenge_counter: 0, 31 | } 32 | } 33 | 34 | pub fn new_with_name(name: &[u8]) -> Self { 35 | let mut res = Transcript::new(); 36 | res.update_with_u256(&name); 37 | res 38 | } 39 | 40 | pub fn update_with_u256(&mut self, value: impl AsRef<[u8]>) { 41 | let old_state_0: [u8; 32] = self.state_0.clone(); 42 | 43 | let mut hasher = Keccak256::new(); 44 | hasher.update(&self.DST_0); 45 | hasher.update(&old_state_0); 46 | hasher.update(&self.state_1); 47 | hasher.update(&value); 48 | self.state_0 = <[u8; 32]>::from(hasher.finalize_reset()); 49 | 50 | hasher.update(&self.DST_1); 51 | hasher.update(&old_state_0); 52 | hasher.update(&self.state_1); 53 | hasher.update(&value); 54 | self.state_1 = <[u8; 32]>::from(hasher.finalize_reset()); 55 | } 56 | 57 | pub fn update_with_scalar(&mut self, a: &Scalar) { 58 | let a_bytes = a.to_bytes(); 59 | self.update_with_u256(a_bytes); 60 | } 61 | 62 | pub fn update_with_g1(&mut self, a: &G1) { 63 | let a_bytes = a.x.to_bytes(); 64 | self.update_with_u256(a_bytes); 65 | } 66 | 67 | pub fn update_with_g2(&mut self, a: &G2) { 68 | let a_bytes = a.x.to_bytes(); 69 | self.update_with_u256(a_bytes); 70 | } 71 | 72 | pub fn update_with_scalar_vec(&mut self, a_vec: &[Scalar]) { 73 | for a in a_vec.iter() { 74 | self.update_with_u256(a.to_bytes()); 75 | } 76 | } 77 | 78 | fn change_u32_to_bytes(&value: &u32) -> [u8; 4] { 79 | let musk = 0x000f as u32; 80 | let mut res = [0u8; 4]; 81 | let mut val = value.clone(); 82 | for i in 0..4 { 83 | res[4 - i - 1] = (val & musk) as u8; 84 | val >>= 8; 85 | } 86 | res 87 | } 88 | 89 | pub fn generate_challenge(&mut self) -> Scalar { 90 | let mut hasher = Keccak256::new(); 91 | hasher.update(&self.DST_CHALLENGE); 92 | hasher.update(&self.state_0); 93 | hasher.update(&self.state_1); 94 | let cc = Transcript::change_u32_to_bytes(&self.challenge_counter); 95 | hasher.update(cc); 96 | let mut query = <[u8; 32]>::from(hasher.finalize_reset()); 97 | 98 | self.challenge_counter += 1; 99 | query[0] = self.FR_MASK & query[0]; 100 | Scalar::from_be_bytes_mod_order(&query) 101 | } 102 | 103 | pub fn generate_challenge_vector(&mut self, size: usize) -> Vec { 104 | let mut c_vec = vec![Scalar::zero(); size]; 105 | for i in 0..size { 106 | c_vec[i] = self.generate_challenge(); 107 | } 108 | c_vec 109 | } 110 | } 111 | 112 | mod tests { 113 | use super::*; 114 | 115 | #[test] 116 | fn test_transcript_update() { 117 | let mut tr = Transcript::new(); 118 | tr.update_with_scalar(&Scalar::from(0)); 119 | tr.update_with_scalar(&Scalar::from(1)); 120 | let alpha = tr.generate_challenge(); 121 | println!("alpha={}", ScalarExt::to_string(&alpha)); 122 | 123 | tr.update_with_u256([1,2,3,4,5]); 124 | let beta = tr.generate_challenge(); 125 | println!("beta={}", ScalarExt::to_string(&beta)); 126 | 127 | let gamma_vec = tr.generate_challenge_vector(2); 128 | println!("gamma={}", scalar_vector_to_string(&gamma_vec)); 129 | } 130 | } 131 | 132 | 133 | -------------------------------------------------------------------------------- /univarization/src/unisumcheck.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use crate::*; 4 | 5 | use crate::unipoly::UniPolynomial; 6 | use crate::kzg10::{KZG10PCS, Commitment}; 7 | use crate::transcript::Transcript; 8 | 9 | // univariate sumcheck system 10 | // public input: a_cm, b_cm, u 11 | // witnesses: a_vec, b_vec 12 | // argument: ?= u 13 | // a(X) * b(X) ?= q(X) * z_H(X) + X * g(X) + u/N 14 | 15 | #[derive(Clone)] 16 | pub struct UniSumcheckSystem { 17 | kzg10: KZG10PCS, 18 | } 19 | 20 | #[derive(Clone)] 21 | pub struct UniSumcheckArgPlain { 22 | domain_size: usize, 23 | q_commitment: Commitment, 24 | g_commitment: Commitment, 25 | a_eval: Scalar, 26 | b_eval: Scalar, 27 | q_eval: Scalar, 28 | g_eval: Scalar, 29 | a_eval_proof: kzg10::EvalArgument, 30 | b_eval_proof: kzg10::EvalArgument, 31 | q_eval_proof: kzg10::EvalArgument, 32 | g_eval_proof: kzg10::EvalArgument, 33 | // q_deg_proof: kzg10::DegreeBoundArgument, 34 | g_deg_proof: kzg10::DegreeBoundArgument, 35 | } 36 | 37 | impl UniSumcheckSystem { 38 | 39 | pub fn setup(kzg10: &KZG10PCS) -> Self { 40 | UniSumcheckSystem { 41 | kzg10: kzg10.clone(), 42 | } 43 | } 44 | 45 | pub fn prove_plain(&self, 46 | a_cm: &Commitment, 47 | b_cm: &Commitment, 48 | a_uni: &UniPolynomial, 49 | b_uni: &UniPolynomial, 50 | c: &Scalar, 51 | tr: &mut Transcript, 52 | ) -> UniSumcheckArgPlain { 53 | assert_eq!(a_uni.coeffs.len(), b_uni.coeffs.len()); 54 | let n = a_uni.coeffs.len(); 55 | let domain_size = n.next_power_of_two(); 56 | 57 | tr.update_with_g1(&a_cm.group_element); 58 | tr.update_with_g1(&b_cm.group_element); 59 | tr.update_with_scalar(&c); 60 | 61 | 62 | let ab_prod_uni = a_uni.mul(&b_uni); 63 | let zH_uni = UniPolynomial::vanishing_polynomial_fft(domain_size); 64 | 65 | let (q_uni, r_uni) = ab_prod_uni.div(&zH_uni); 66 | 67 | let mut coeffs = r_uni.coeffs.clone(); 68 | coeffs.remove(0); 69 | let g_uni = UniPolynomial::from_coeffs(&coeffs); 70 | 71 | let q_cm = self.kzg10.commit_uni_poly(&q_uni); 72 | let g_cm = self.kzg10.commit_uni_poly(&g_uni); 73 | 74 | tr.update_with_g1(&q_cm.group_element); 75 | tr.update_with_g1(&g_cm.group_element); 76 | 77 | let zeta = tr.generate_challenge(); 78 | // let zeta = Scalar::one(); 79 | 80 | // TODO: adopt batched evaluation argument 81 | // TODO: adopt linearization technique 82 | let (a_zeta, a_eval_prf) = self.kzg10.prove_eval(&a_uni, &zeta); 83 | let (b_zeta, b_eval_prf) = self.kzg10.prove_eval(&b_uni, &zeta); 84 | let (q_zeta, q_eval_prf) = self.kzg10.prove_eval(&q_uni, &zeta); 85 | let (g_zeta, g_eval_prf) = self.kzg10.prove_eval(&g_uni, &zeta); 86 | // let q_degree = a_uni.degree + b_uni.degree - domain_size; 87 | // let q_deg_prf = self.kzg10.prove_degree_bound(&q_uni, q_degree+1); 88 | let g_degree_bound = domain_size - 1; 89 | let g_deg_prf = self.kzg10.prove_degree_bound(&g_uni, g_degree_bound); 90 | 91 | let zH_zeta = zH_uni.evaluate(&zeta); 92 | 93 | assert_eq!(a_zeta * b_zeta, 94 | q_zeta * zH_zeta + zeta * g_zeta + (*c / Scalar::from_usize(domain_size))); 95 | 96 | UniSumcheckArgPlain { 97 | domain_size: domain_size, 98 | q_commitment: q_cm, 99 | g_commitment: g_cm, 100 | a_eval: a_zeta, 101 | b_eval: b_zeta, 102 | q_eval: q_zeta, 103 | g_eval: g_zeta, 104 | a_eval_proof: a_eval_prf, 105 | b_eval_proof: b_eval_prf, 106 | q_eval_proof: q_eval_prf, 107 | g_eval_proof: g_eval_prf, 108 | g_deg_proof: g_deg_prf, 109 | } 110 | } 111 | 112 | pub fn verify_plain(&self, 113 | a_cm: &Commitment, 114 | b_cm: &Commitment, 115 | c: &Scalar, 116 | prf: &UniSumcheckArgPlain, 117 | tr: &mut Transcript, 118 | ) -> bool 119 | { 120 | let domain_size = prf.domain_size; 121 | 122 | tr.update_with_g1(&a_cm.group_element); 123 | tr.update_with_g1(&b_cm.group_element); 124 | tr.update_with_scalar(&c); 125 | 126 | let q_cm = prf.q_commitment.clone(); 127 | let g_cm = prf.g_commitment.clone(); 128 | 129 | let zH_uni = UniPolynomial::vanishing_polynomial_fft(domain_size); 130 | 131 | tr.update_with_g1(&q_cm.group_element); 132 | tr.update_with_g1(&g_cm.group_element); 133 | 134 | let zeta = tr.generate_challenge(); 135 | 136 | assert!(self.kzg10.verify_eval(&a_cm, &zeta, &prf.a_eval, &prf.a_eval_proof)); 137 | assert!(self.kzg10.verify_eval(&b_cm, &zeta, &prf.b_eval, &prf.b_eval_proof)); 138 | assert!(self.kzg10.verify_eval(&q_cm, &zeta, &prf.q_eval, &prf.q_eval_proof)); 139 | assert!(self.kzg10.verify_eval(&g_cm, &zeta, &prf.g_eval, &prf.g_eval_proof)); 140 | assert!(self.kzg10.verify_degree_bound(&g_cm, domain_size - 1, &prf.g_deg_proof)); 141 | 142 | let zH_eval = zH_uni.evaluate(&zeta); 143 | 144 | assert_eq!(prf.a_eval * prf.b_eval, 145 | prf.q_eval * zH_eval + zeta * prf.g_eval + (*c / Scalar::from_usize(domain_size))); 146 | 147 | true 148 | } 149 | } 150 | 151 | mod tests { 152 | use super::*; 153 | use crate::*; 154 | 155 | #[test] 156 | fn test_prove_verify() { 157 | init_logger(); 158 | 159 | let rng = &mut ark_std::test_rng(); 160 | let a = Scalar::from_i64_vector(&vec![1, 2, 3, 4]); 161 | let b = Scalar::from_i64_vector(&vec![2, 3, 1, 2]); 162 | 163 | // sum = 19 164 | let c: Scalar = (0..a.len()).map(|i| a[i] * b[i]).sum(); 165 | 166 | let kzg10 = KZG10PCS::setup(100, rng); 167 | let sumcheck_sys = UniSumcheckSystem::setup(&kzg10); 168 | let a_uni = UniPolynomial::from_evals(&a); 169 | let b_uni = UniPolynomial::from_evals(&b); 170 | 171 | let a_cm = kzg10.commit_uni_poly(&a_uni); 172 | let b_cm = kzg10.commit_uni_poly(&b_uni); 173 | let tr = Transcript::new(); 174 | 175 | let prf = sumcheck_sys.prove_plain(&a_cm, &b_cm, &a_uni, &b_uni, &c, &mut tr.clone()); 176 | let b = sumcheck_sys.verify_plain(&a_cm, &b_cm, &c, &prf, &mut tr.clone()); 177 | assert!(b); 178 | } 179 | } -------------------------------------------------------------------------------- /univarization/src/zeromorph.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::unipoly::UniPolynomial; 3 | use crate::mle::{*, evals::*, coeffs_sparse::*}; 4 | use crate::kzg10::{Commitment, KZG10PCS, DegreeBoundArgument}; 5 | use crate::transcript::Transcript; 6 | use crate::*; 7 | use log::{debug, info, warn}; 8 | 9 | // Main Equation: 10 | // 11 | // ⟦f⟧_n - v * 𝚽_n(X) = ∑_k (X^{2^k} * 𝚽_{n-k-1}(X^{2^{k+1}}) - u_k * 𝚽_{n-k}(X^{2^k}) * ⟦q_k⟧_k 12 | // 13 | // where v = f(u_0, u_1, ..., u_{n-1}) 14 | // and q_k = q_k(u_0, u_1, ..., u_k) s.t. 15 | // 16 | // f(X_0, X_1, ..., X_{n-1}) - v = ∑_k (X_k - u_k) * q_k(X_0, X_1, ..., X_{k-1}) (1️⃣) 17 | // 18 | // We apply ⟦·⟧_n to the both sides of (1️⃣), and get the main equation. 19 | // 20 | // 21 | // f(X_0, X_1, ..., X_{n-1}) - f(u_0, u_1, ..., u_{n-1}) = 22 | // (X_{n-1} - u_{n-1}) * q_{n-1}(X_0, X_1, ..., X_{n-2}) 23 | // + (X_{n-2} - u_{n-2}) * q_{n-2}(X_0, X_1, ..., X_{n-3}) 24 | // ... 25 | // + (X_1 - u_1) * q_1(X_0) 26 | // + (X_0 - u_0) * q_0() 27 | // 28 | 29 | pub struct MlePCSystem { 30 | kzg10: KZG10PCS, 31 | } 32 | 33 | pub struct EvalArgumentSimple { 34 | q_commitment_vec: Vec, 35 | deg_proof_vec: Vec, 36 | eval_proof: kzg10::EvalArgument, 37 | } 38 | 39 | pub struct EvalArgumentOpt { 40 | q_commitment_vec: Vec, 41 | q_hat_commitment: Commitment, 42 | eval_proof: kzg10::EvalArgument, 43 | } 44 | 45 | impl MlePCSystem { 46 | 47 | pub fn setup_with_rng(rng: &mut R) -> Self { 48 | let kzg10 = KZG10PCS::setup(64, rng); 49 | MlePCSystem { kzg10: kzg10 } 50 | } 51 | 52 | // @return: a univariate commitment for Commit(⟦f_mle⟧_n) 53 | pub fn commit(&self, f_mle: &MleEvals) -> Commitment { 54 | self.kzg10 55 | .commit_values(&f_mle.evals) 56 | } 57 | 58 | // The prover proves that f_mle(u0, u1, ..., u{n-1}) = v, that is mapped the goal of 59 | // the univariate polynomial, G_z(X), evaluates to ZERO at z, where 60 | // 61 | // G_z(X) = ⟦f_mle⟧_n - v * 𝚽_n(z) - ∑_k(z^{2^k}𝚽_{n-k-1}(z^{2^{k+1}}) - uk * 𝚽_{n-k}(z^{2^k})) * ⟦q_k⟧_k 62 | // ^^^^^^^^ ^^^^^^^ 63 | // = G0(X) = G1(X), ..., Gk(X) 64 | // 65 | // @param: f_cm, a (univariate) commitment for f_mle, Commit(⟦f_mle⟧_n) 66 | // @param: f_mle, a mle polynomial for f_mle 67 | // @param: us, a vector of scalars, u0, u1, ..., u{n-1} 68 | // @param: tr, transcript of Fiat-Shamir scheme 69 | // @return: (v, prf) 70 | // v, the value of f_mle(u0, u1, ..., u{n-1}) 71 | // prf, evaluation argument 72 | pub fn prove_plain( 73 | &self, 74 | f_mle_cm: &Commitment, 75 | f_mle: &MleEvals, 76 | us: &[Scalar], 77 | tr: &mut Transcript, 78 | ) -> (Scalar, EvalArgumentSimple) 79 | { 80 | info!("🔽 proving zeromorph isomorphism start ..."); 81 | let n = f_mle.num_var; 82 | 83 | // let coeffs: Vec<(usize, Scalar)> = mle.compute_coeffs().into_iter().enumerate().filter(|(_i, c)| !c.is_zero()).collect(); 84 | // let mle = MLEPolynomial::new(coeffs, n); 85 | let f_uni = U_from_evals(n, &f_mle); 86 | 87 | debug!("f_uni.coeffs={}", scalar_vector_to_string(&f_uni.coeffs)); 88 | 89 | let v = f_mle.evaluate(us); 90 | 91 | let f_mle_cof = MleCoeffs::from_coeffs(&f_mle.to_coeffs(), n); 92 | 93 | // compute quotients {q_k}, where q_i = f(X0, X1, ..., X{k-1}) 94 | let q_vec = f_mle_cof.decompose_by_division_alt(us); 95 | 96 | // initialize the commitments of quotients 97 | let mut q_cm_vec = Vec::new(); 98 | let mut q_uni_vec = Vec::new(); 99 | 100 | for i in 0..n { 101 | assert_eq!(i, q_vec[i].num_var()); 102 | let qi_uni = U(i, &q_vec[i]); 103 | let qi_cm = self.kzg10.commit_values(&qi_uni.coefficients()); 104 | q_cm_vec.push(qi_cm); 105 | q_uni_vec.push(qi_uni); 106 | } 107 | 108 | for i in 0..q_cm_vec.len() { 109 | tr.update_with_g1(&q_cm_vec[i].group_element); 110 | } 111 | 112 | let mut deg_prf_vec = Vec::new(); 113 | // TODO: use a new kzg10 114 | let f_deg_prf = self.kzg10.prove_degree_bound(&f_uni, pow_2(n)); 115 | 116 | for i in 0..n { 117 | let qi_deg_prf = self.kzg10.prove_degree_bound(&q_uni_vec[i], pow_2(i)); 118 | deg_prf_vec.push(qi_deg_prf); 119 | } 120 | deg_prf_vec.push(f_deg_prf); 121 | 122 | for i in 0..deg_prf_vec.len() { 123 | // TODO: add support degree_bound proofs 124 | tr.update_with_g1(°_prf_vec[i].commitment.group_element); 125 | } 126 | 127 | let zeta = tr.generate_challenge(); 128 | 129 | let ph_n = Phi(n, 1).evaluate(&zeta); 130 | 131 | // Compute G_z(X) = G0(X) - v * Phi_n(z) - ∑_k (c_k * Gk(X)) 132 | // s.t. G_z(z) == 0 133 | let mut g_uni = f_uni.sub_scalar(&(ph_n * v)); // G0(X) 134 | for k in 0..n { 135 | let c_k = zeta.exp(pow_2(k)) * Phi(n-k-1, pow_2(k+1)).evaluate(&zeta) - us[k] * Phi(n-k, pow_2(k)).evaluate(&zeta); 136 | let q_k_uni = q_uni_vec[k].mul_scalar(&c_k); 137 | g_uni = g_uni.sub(&q_k_uni); 138 | } 139 | 140 | let (eval_at_zeta, eval_prf) = self.kzg10.prove_eval(&g_uni, &zeta); 141 | 142 | assert_eq!(&eval_at_zeta, &Scalar::zero()); 143 | 144 | 145 | { // verify 146 | // let z_cm = self.kzg10.commit_poly(&g_uni); 147 | let v_cm = self.kzg10.commit_values(&UniPolynomial::from_coeffs(&[ph_n * v]).coefficients()); 148 | let mut z_cm = f_mle_cm.clone(); 149 | z_cm = z_cm.sub(&v_cm); 150 | for i in 0..n { 151 | let mut q_cm = q_cm_vec[i].clone(); 152 | let c = zeta.exp(pow_2(i)) * Phi(n-i-1, pow_2(i+1)).evaluate(&zeta) - us[i] * Phi(n-i, pow_2(i)).evaluate(&zeta); 153 | z_cm = z_cm.sub(&q_cm.mul_scalar(&c)); 154 | } 155 | 156 | // 157 | assert_eq!(g_uni.evaluate(&zeta), Scalar::zero()); 158 | info!("👀 z(zeta) == 0 ✅"); 159 | 160 | 161 | let _b = self.kzg10.open_with_values(&v_cm, &[v]); 162 | for i in 0..n { 163 | let _b = self.kzg10.open_with_values(&q_cm_vec[i], &q_uni_vec[i].coeffs); 164 | } 165 | 166 | // let mut g_cm = self.kzg10.commit_values(&[Scalar::zero()]); 167 | // for i in 0..n { 168 | // let c = zeta.exp(pow_2(i)) * phi(n-i-1, pow_2(i+1)).evaluate(&zeta) - xs[i] * phi(n-i, pow_2(i)).evaluate(&zeta); 169 | // g_cm = g_cm.add(&q_cm_vec[i].mul_scalar(&c)); 170 | // } 171 | 172 | let b = self.kzg10.open_with_values(&f_mle_cm, &f_uni.coeffs); 173 | println!("f_cm={}", scalar_vector_to_string(&f_mle_cm.internal_values)); 174 | println!("f_uni.coeffs={}", scalar_vector_to_string(&f_uni.coeffs)); 175 | println!("open(f(X), [f(X)])={}", b); 176 | 177 | let b = self.kzg10.open_with_values(&z_cm, &g_uni.coeffs); 178 | assert_eq!(b, true); 179 | info!("👀 open(z(X), [z(X)]) == true ✅"); 180 | } 181 | info!("🔼 proving zeromorph isomorphism end"); 182 | 183 | (v, EvalArgumentSimple { 184 | q_commitment_vec: q_cm_vec, 185 | deg_proof_vec: deg_prf_vec, 186 | eval_proof: eval_prf, 187 | }) 188 | } 189 | 190 | // Verify the evaluation argument of f_mle(u0, u1, ..., u{n-1}) = v 191 | // @param: f_cm, a (univariate) commitment for ⟦f_mle⟧_n 192 | // @param: us, a vector of scalars, (u0, u1, ..., u{n-1}) 193 | // @param: v, a scalar, v = f_mle(u0, u1, ..., u{n-1}) 194 | // @param: num_var, the number of variables of f_mle 195 | // @param: prf, evaluation argument 196 | // @param: tr, transcript of Fiat-Shamir scheme 197 | // @return: true if the proof is valid 198 | pub fn verify_plain( 199 | &self, 200 | f_cm: &Commitment, 201 | us: &[Scalar], 202 | v: &Scalar, 203 | num_var: usize, 204 | prf: &EvalArgumentSimple, 205 | tr: &mut Transcript, 206 | ) -> bool 207 | { 208 | let n = num_var; 209 | assert_eq!(n, us.len()); 210 | 211 | let q_cm_vec = &prf.q_commitment_vec; 212 | for q in q_cm_vec { 213 | tr.update_with_g1(&q.group_element); 214 | } 215 | 216 | let deg_prf_vec = &prf.deg_proof_vec; 217 | for p in deg_prf_vec { 218 | // TODO: add support degree_bound proofs 219 | tr.update_with_g1(&p.commitment.group_element); 220 | } 221 | 222 | let eval_prf = &prf.eval_proof; 223 | 224 | let zeta = tr.generate_challenge(); 225 | 226 | let ph_n = Phi(n, 1).evaluate(&zeta); 227 | 228 | let v_cm = self.kzg10.commit_values(&UniPolynomial::from_coeffs(&[ph_n * v]).coefficients()); 229 | let mut z_cm = f_cm.clone(); 230 | z_cm = z_cm.sub(&v_cm); 231 | for i in 0..n { 232 | let mut q_cm = q_cm_vec[i].clone(); 233 | let c = zeta.exp(pow_2(i)) * Phi(n-i-1, pow_2(i+1)).evaluate(&zeta) - us[i] * Phi(n-i, pow_2(i)).evaluate(&zeta); 234 | z_cm = z_cm.sub(&q_cm.mul_scalar(&c)); 235 | } 236 | 237 | let checked = self.kzg10.verify_eval(&z_cm, &zeta, &Scalar::zero(), eval_prf); 238 | // assert!(checked); 239 | info!("👀 z(zeta) == 0 ✅"); 240 | checked 241 | } 242 | 243 | // @param: f_mle_cm, a (univariate) commitment for f_mle, Commit( [[f_mle]]_n ) 244 | // @param: f_mle, a mle polynomial for f_mle 245 | // @param: us, a vector of scalars, u0, u1, ..., u{n-1} 246 | // @param: tr, transcript of Fiat-Shamir scheme 247 | // @return: (v, prf) 248 | // v, the value of f_mle(u0, u1, ..., u{n-1}) 249 | // prf, evaluation argument 250 | pub fn prove_opt( 251 | &self, 252 | f_mle_cm: &Commitment, 253 | f_mle: &MleEvals, 254 | us: &[Scalar], 255 | tr: &mut Transcript, 256 | ) -> (Scalar, EvalArgumentOpt) 257 | { 258 | info!("🔽 proving zeromorph isomorphism start ..."); 259 | let n = f_mle.num_var; 260 | 261 | let f_uni = U_from_evals(n, &f_mle); 262 | // check: f_cm == Commit(f_uni) 263 | 264 | let v = f_mle.evaluate(us); 265 | 266 | // Round 1: 267 | 268 | // transform f_mle into coefficient form 269 | let f_mle_cof = MleCoeffs::from_coeffs(&f_mle.to_coeffs(), n); 270 | 271 | // compute quotients {q_k}, where q_k = f(X0, X1, ..., X{k-1}) 272 | let q_vec = f_mle_cof.decompose_by_division_alt(us); 273 | 274 | // univariatise {⟦q_k⟧_k} and commit to them 275 | let (q_cm_vec, q_uni_vec) = { 276 | let mut q_cm_vec = Vec::new(); 277 | let mut q_uni_vec = Vec::new(); 278 | 279 | for i in 0..n { 280 | assert_eq!(i, q_vec[i].num_var()); 281 | let qi_uni = U(i, &q_vec[i]); 282 | let qi_cm = self.kzg10.commit_values(&qi_uni.coefficients()); 283 | q_cm_vec.push(qi_cm); 284 | q_uni_vec.push(qi_uni); 285 | } 286 | (q_cm_vec, q_uni_vec) 287 | }; 288 | 289 | // update transcript with {Commit(⟦q_k⟧_k)} 290 | for q_cm in q_cm_vec.iter() { 291 | tr.update_with_g1(&q_cm.group_element); 292 | } 293 | 294 | // Round 2: 295 | 296 | // retrieve a challenge to aggregate {q_k} 297 | let beta = tr.generate_challenge(); 298 | 299 | // compute the aggregated polynomial, q_hat(X) = ∑_k (beta^k * q_k(X)) 300 | let q_hat_uni = { 301 | let mut coeffs = vec![Scalar::zero(); pow_2(n)]; 302 | coeffs.push(Scalar::one()); 303 | let mut q_hat = UniPolynomial::zero(); 304 | let mut powers_of_beta = Scalar::one(); 305 | for k in 0..n { 306 | // construct unipoly: X^{2^n - 2^k} 307 | let x_deg_2_to_n_uni = UniPolynomial::from_coeffs(&coeffs[pow_2(k)..]); 308 | q_hat = q_hat.add(&q_uni_vec[k].mul(&x_deg_2_to_n_uni).mul_scalar(&powers_of_beta)); 309 | powers_of_beta *= beta; 310 | } 311 | q_hat 312 | }; 313 | 314 | // commit to q_hat(X) 315 | let q_hat_cm = self.kzg10.commit_uni_poly(&q_hat_uni); 316 | 317 | tr.update_with_g1(&q_hat_cm.group_element); 318 | 319 | // Round 3: 320 | 321 | // retrieve a challenge zeta (ζ) to check polynomial identies 322 | let zeta = tr.generate_challenge(); 323 | 324 | // construct G(X), and prove it is a zero polynomial 325 | // 326 | // G(X) = f(X) - v * 𝚽_n(X) 327 | // - ∑_k ( X^{2^k} * 𝚽_{n-k-1}(X^{2^{k+1}}) - u_k * 𝚽_{n-k}(X^{2^k}) ) * q_k(X) 328 | // 329 | // use linearization trick, partially instantiate G(X) at X = ζ (zeta) 330 | // 331 | // G_ζ(X) = f(X) - v * 𝚽_n(ζ) 332 | // - ∑_k ( ζ^{2^k} * 𝚽_{n-k-1}(ζ^{2^{k+1}}) - u_k * 𝚽_{n-k}(ζ^{2^k}) ) * q_k(X) 333 | // = f(X) - v_𝚽 - ∑_k c_k * q_k(X) 334 | 335 | 336 | // compute v * 𝚽_n(ζ) 337 | let phi_n = Phi(n, 1).evaluate(&zeta); 338 | 339 | // compute G_ζ(X) = f(X) - v * 𝚽_n(ζ) - ∑_k c_k * q_k(X) 340 | let g_zeta_uni = { 341 | let mut g_uni = f_uni.sub_scalar(&(phi_n * v)); 342 | for k in 0..n { 343 | let c_k = zeta.exp(pow_2(k)) * Phi(n-k-1, pow_2(k+1)).evaluate(&zeta) 344 | - us[k] * Phi(n-k, pow_2(k)).evaluate(&zeta); 345 | let q_k_uni = q_uni_vec[k].mul_scalar(&c_k); 346 | g_uni = g_uni.sub(&q_k_uni); 347 | } 348 | g_uni 349 | }; 350 | 351 | println!("g_zeta(zeta)={}", g_zeta_uni.evaluate(&zeta)); 352 | // println!("g_zeta_uni={}", scalar_vector_to_string(&g_zeta_uni.coeffs)); 353 | 354 | 355 | // Construct H(X), and prove it is a zero polynomial 356 | // 357 | // H(X) = q_hat(X) - ∑_k ( beta^{k} * X^{2^n - 2^k} * q_k(X) ) 358 | // 359 | // H_ζ(X) = q_hat(X) - ∑_k ( beta^{k} * ζ^{2^n - 2^k} * q_k(X) ) 360 | 361 | // compute H_ζ(X) = q_hat(X) - ∑_k e_k * q_k(X) 362 | let h_zeta_uni = { 363 | let mut h_uni = q_hat_uni.clone(); 364 | for k in 0..n { 365 | let e_k = beta.exp(k) * zeta.exp(pow_2(n) - pow_2(k)); 366 | println!("e_k={}", ScalarExt::to_string(&e_k)); 367 | let q_k_uni = q_uni_vec[k].mul_scalar(&e_k); 368 | h_uni = h_uni.sub(&q_k_uni); 369 | } 370 | h_uni 371 | }; 372 | 373 | println!("h_zeta(zeta)={}", h_zeta_uni.evaluate(&zeta)); 374 | 375 | // Round 4: 376 | 377 | // retrieve a challenge alpha (α) to aggregate H_ζ(X) and G_ζ(X) 378 | let alpha = tr.generate_challenge(); 379 | 380 | let a_uni = g_zeta_uni.mul_scalar(&alpha).add(&h_zeta_uni); 381 | // 382 | let (e, e_pf, _d_pf) = self.kzg10.prove_eval_and_deg(&a_uni, &zeta, pow_2(n)); 383 | 384 | assert_eq!(e, Scalar::zero()); 385 | 386 | tr.update_with_g1(&e_pf.commitment.group_element); 387 | 388 | { 389 | // actions by verifier 390 | 391 | // compute Commit(v * 𝚽_n(z); 0) 392 | let v_cm = self.kzg10.commit_values(&UniPolynomial::from_coeffs(&[phi_n * v]).coefficients()); 393 | 394 | assert!(self.kzg10.check_commitment( 395 | &v_cm, 396 | &UniPolynomial::from_coeffs(&[phi_n * v]) 397 | )); 398 | 399 | // compute Commit(G_ζ(X)) 400 | let g_zeta_cm = { 401 | let mut g_cm = f_mle_cm.sub(&v_cm); 402 | for k in 0..n { 403 | let c_k = zeta.exp(pow_2(k)) * Phi(n-k-1, pow_2(k+1)).evaluate(&zeta) 404 | - us[k] * Phi(n-k, pow_2(k)).evaluate(&zeta); 405 | let q_k_cm = q_cm_vec[k].mul_scalar(&c_k); 406 | g_cm = g_cm.sub(&q_k_cm); 407 | } 408 | g_cm 409 | }; 410 | 411 | println!("g_zeta_uni={}", scalar_vector_to_string(&g_zeta_uni.coeffs)); 412 | 413 | assert!(self.kzg10.check_commitment( 414 | &g_zeta_cm, 415 | &g_zeta_uni, 416 | )); 417 | 418 | // compute Commit(H_ζ(X)) 419 | let h_zeta_cm = { 420 | let mut h_cm = q_hat_cm.clone(); 421 | for k in 0..n { 422 | let e_k = beta.exp(k) * zeta.exp(pow_2(n) - pow_2(k)); 423 | let q_k_cm = q_cm_vec[k].mul_scalar(&e_k); 424 | h_cm = h_cm.sub(&q_k_cm); 425 | } 426 | h_cm 427 | }; 428 | 429 | assert!(self.kzg10.check_commitment( 430 | &h_zeta_cm, 431 | &h_zeta_uni, 432 | )); 433 | 434 | let a_cm = g_zeta_cm.mul_scalar(&alpha).add(&h_zeta_cm); 435 | 436 | let b = self.kzg10.verify_eval(&a_cm, &zeta, &Scalar::zero(), &e_pf); 437 | assert!(b); 438 | } 439 | 440 | (v, EvalArgumentOpt { 441 | q_commitment_vec: q_cm_vec, 442 | q_hat_commitment: q_hat_cm, 443 | eval_proof: e_pf, 444 | }) 445 | } 446 | 447 | pub fn verify_opt(&self, 448 | mle_cm: &Commitment, 449 | us: &[Scalar], 450 | v: &Scalar, 451 | prf: &EvalArgumentOpt, 452 | tr: &mut Transcript, 453 | ) -> bool 454 | { 455 | // Round 1: 456 | 457 | let n = us.len(); 458 | // unpack the elements from the proof and update the transcript 459 | let q_cm_vec = &prf.q_commitment_vec; 460 | for q in q_cm_vec { 461 | tr.update_with_g1(&q.group_element); 462 | } 463 | 464 | // Round 2: 465 | 466 | let beta = tr.generate_challenge(); 467 | 468 | let q_hat_cm = &prf.q_hat_commitment; 469 | tr.update_with_g1(&q_hat_cm.group_element); 470 | 471 | let eval_prf = &prf.eval_proof; 472 | 473 | // Round 3: 474 | 475 | // retrieve the challenge zeta (ζ) 476 | let zeta = tr.generate_challenge(); 477 | 478 | let phi_n = Phi(n, 1).evaluate(&zeta); 479 | 480 | // compute Commit(v * 𝚽_n(z); 0) 481 | let v_cm = self.kzg10.commit_values( 482 | &UniPolynomial::from_coeffs(&[phi_n * v]).coefficients()); 483 | 484 | // compute Commit(G_ζ(X)) 485 | let g_zeta_cm = { 486 | let mut g_cm = mle_cm.sub(&v_cm); 487 | for k in 0..n { 488 | let c_k = zeta.exp(pow_2(k)) * Phi(n-k-1, pow_2(k+1)).evaluate(&zeta) 489 | - us[k] * Phi(n-k, pow_2(k)).evaluate(&zeta); 490 | let q_k_cm = q_cm_vec[k].mul_scalar(&c_k); 491 | g_cm = g_cm.sub(&q_k_cm); 492 | } 493 | g_cm 494 | }; 495 | 496 | // compute Commit(H_ζ(X)) 497 | let h_zeta_cm = { 498 | let mut h_cm = q_hat_cm.clone(); 499 | for k in 0..n { 500 | let e_k = beta.exp(k) * zeta.exp(pow_2(n) - pow_2(k)); 501 | let q_k_cm = q_cm_vec[k].mul_scalar(&e_k); 502 | h_cm = h_cm.sub(&q_k_cm); 503 | } 504 | h_cm 505 | }; 506 | 507 | // Round 4: 508 | 509 | // retrieve a challenge alpha (α) to aggregate C(H_ζ(X)) and C(G_ζ(X)) 510 | let alpha = tr.generate_challenge(); 511 | 512 | let a_cm = g_zeta_cm.mul_scalar(&alpha).add(&h_zeta_cm); 513 | 514 | let b = self.kzg10.verify_eval(&a_cm, &zeta, &Scalar::zero(), eval_prf); 515 | b 516 | } 517 | } 518 | 519 | #[allow(non_snake_case)] 520 | pub fn U(num_var: usize, mle: &MleCoeffs) -> UniPolynomial { 521 | assert!(num_var >= mle.num_var()); 522 | let coeffs = mle.coefficients(); 523 | let evals = compute_evals_from_coeffs(num_var, &coeffs); 524 | UniPolynomial::from_coeffs(&evals) 525 | } 526 | 527 | #[allow(non_snake_case)] 528 | pub fn U_from_evals(num_var: usize, mle: &MleEvals) -> UniPolynomial { 529 | assert!(num_var >= mle.num_var); 530 | UniPolynomial::from_coeffs(&mle.evals) 531 | } 532 | 533 | // Compute the periodic polynomial Phi(X^d) 534 | // Phi_n(X) = 1 + X + X^2 + X^3 + ... + X^(2^n-1) 535 | // Phi(X^d) = 1 + X^d + X^2d + X^4d + ... + X^(2^(n-1))d 536 | #[allow(non_snake_case)] 537 | pub fn Phi(num_var: usize, d: usize) -> UniPolynomial { 538 | let mut coeffs = vec![Scalar::zero(); pow_2(num_var) * d]; 539 | for i in 0..pow_2(num_var) { 540 | coeffs[i*d] = Scalar::one(); 541 | 542 | } 543 | UniPolynomial::from_coeffs(&coeffs) 544 | } 545 | 546 | #[cfg(test)] 547 | mod tests { 548 | use super::*; 549 | // use std::collections::HashMap; 550 | 551 | 552 | #[test] 553 | fn test_phi() { 554 | 555 | // f1(X) = 1 + X + X^2 + X^3 + X^4 + X^5 + X^6 + X^7 556 | let f1_uni = Phi(3, 1); 557 | println!("f_uni.coeffs={}", scalar_vector_to_string(&f1_uni.coeffs)); 558 | 559 | // f2(X) = X - 1; 560 | let f2_uni = UniPolynomial::from_coeffs(&vec![-Scalar::from(1), Scalar::from(1)]); 561 | 562 | // f1(X)*f2(X) = X^8 - 1 563 | let f_uni = f2_uni.mul(&f1_uni); 564 | 565 | assert_eq!(f_uni, UniPolynomial::from_coeffs(&{ 566 | let mut coeffs = vec![Scalar::zero(); 8]; 567 | coeffs[0] = -Scalar::one(); 568 | coeffs.push(Scalar::one()); 569 | coeffs 570 | })); 571 | assert_eq!(f_uni.degree, 8); 572 | 573 | println!("f_uni.coeffs={}", scalar_vector_to_string(&f_uni.coeffs)); 574 | } 575 | 576 | 577 | #[test] 578 | fn test_map_mle_to_uni() { 579 | 580 | // f(x2, x1, x0) = 4*x0 + 3x1 + x0*x1 + 2*x0x2 + 5*x0*x1*x2 581 | 582 | let f1_mle = MleCoeffs::new(vec![ 583 | (0b001, Scalar::from(4)), 584 | (0b010, Scalar::from(3)), 585 | (0b011, Scalar::from(1)), 586 | (0b101, Scalar::from(2)), 587 | (0b111, Scalar::from(5)), 588 | ], 3); 589 | 590 | // f[0,0,0] = 0 591 | // f[0,0,1] = 4 592 | // f[0,1,0] = 3 593 | // f[0,1,1] = 4+3+1 = 8 594 | // f[1,0,0] = 0 595 | // f[1,0,1] = 4+2 = 6 596 | // f[1,1,0] = 3 597 | // f[1,1,1] = 4+3+1+2+5 = 15 598 | 599 | let f1_mle_coeffs = f1_mle.coefficients(); 600 | let f1_mle_evals = mle::compute_evals_from_coeffs(3, &f1_mle_coeffs); 601 | // println!("f1_mle.evals={}", scalar_vector_to_string(&f1_mle_evals)); 602 | 603 | // f2(X0, X1) = 4*X0 + 3*X1 + 1*X0X1 604 | // f2.evals[00, 01, 10, 11] = [0, 4, 3, 8] 605 | let f2_mle = MleCoeffs::new({ 606 | let mut coe: Vec<(usize, Scalar)> = f1_mle_coeffs.clone().into_iter().enumerate().collect(); 607 | coe.truncate(4); 608 | coe}, 2); 609 | 610 | println!("f2_mle.evals={}", scalar_vector_to_string(&mle::compute_evals_from_coeffs(3, &f2_mle.coefficients()))); 611 | 612 | let f1_uni = U(3, &f1_mle); 613 | println!("f1_uni={}", scalar_vector_to_string(&f1_uni.coeffs)); 614 | assert_eq!(&f1_uni.coeffs, &f1_mle_evals); 615 | 616 | let f2_uni = U(4, &f2_mle); 617 | assert_eq!(f2_uni.coeffs, (0..4).map(|_i| f2_mle.evaluations()).collect::>>().concat()); 618 | println!("f2_uni={}", scalar_vector_to_string(&f2_uni.coeffs)); 619 | 620 | } 621 | 622 | #[test] 623 | fn lemma_2_5_1() { 624 | 625 | // define a constant polynomial 626 | // mle = 2 + 0*X0 + 0*X1 + 0*X0X1 627 | let mle = MleCoeffs::new( 628 | vec![ 629 | (0b00, Scalar::from(2)), 630 | ] 631 | .into_iter() 632 | .collect(), 2); 633 | // compute f = [[ mle ]] = 2 + 2X + 2X^2 + 2X^3 634 | let f = U(2, &mle); 635 | // compute g = Phi_2(X) = 1 + X + X^2 + X^3 636 | let g = Phi(2, 1); 637 | // f == g * 2 638 | assert_eq!(f.coeffs, g.mul_scalar(&Scalar::from(2)).coeffs); 639 | } 640 | 641 | #[test] 642 | fn lemma_2_5_2() { 643 | 644 | let n = 4; 645 | let k = 2; 646 | 647 | let mut rng = ark_std::test_rng(); 648 | 649 | // define a simple MLE 650 | let mle = MleCoeffs::new(vec![ 651 | (0b00, Scalar::from(2)), 652 | (0b01, Scalar::from(3)), 653 | (0b10, Scalar::from(4)), 654 | (0b11, Scalar::from(5)), 655 | ] 656 | .into_iter() 657 | .collect(), 2); 658 | // compute f(X) = [[ mle ]] 659 | let uni_f = U(n, &mle); 660 | 661 | // compute f_k(X) = [[ mle ]]_k 662 | let uni_f_k = U(k, &mle); 663 | 664 | let ph = Phi(n-k, pow_2(k)); 665 | // println!("^f={}", &uni_f); 666 | let uni_g = ph.mul(&uni_f_k); 667 | // println!("^g={}", &uni_g); 668 | 669 | // test \hat{f} ?= \hat{g} 670 | let zeta = Scalar::rand(&mut rng); 671 | assert_eq!(uni_f.evaluate(&zeta), uni_g.evaluate(&zeta)); 672 | } 673 | 674 | 675 | #[test] 676 | fn lemma_2_5_3() { 677 | 678 | let n = 4; 679 | let k = 2; 680 | 681 | let mut rng = ark_std::test_rng(); 682 | 683 | // define a simple MLE 684 | let mut mle = MleCoeffs::new(vec![ 685 | (0b00, Scalar::from(2)), 686 | (0b01, Scalar::from(3)), 687 | (0b10, Scalar::from(4)), 688 | (0b11, Scalar::from(5)), 689 | ] 690 | .into_iter() 691 | .collect(),2); 692 | // 693 | let uni_f = U(n, &mle); 694 | 695 | mle.expand_vars(1); 696 | 697 | let uni_xf = U(n, &mle.mul(&MleCoeffs::new(vec![ 698 | (0b100, Scalar::from(1)) 699 | ] 700 | .into_iter() 701 | .collect(),3))); 702 | 703 | // g(X) = X^{2^k} + 1 704 | let g = UniPolynomial { degree: pow_2(k), coeffs: { 705 | let mut zeros = vec![Scalar::zero(); pow_2(k)]; 706 | zeros[0] = Scalar::one(); 707 | zeros.push(Scalar::one()); 708 | zeros 709 | }}; 710 | let uni_g = uni_xf.mul(&g); 711 | println!("^g={}", &uni_g); 712 | 713 | let h = UniPolynomial { degree: pow_2(k), coeffs: { 714 | let mut zeros = vec![Scalar::zero(); pow_2(k)]; 715 | zeros.push(Scalar::one()); 716 | zeros 717 | }}; 718 | 719 | let uni_h = uni_f.mul(&h); 720 | println!("^h={}", &uni_h); 721 | 722 | // test \hat{f} ?= \hat{g} 723 | let zeta = Scalar::rand(&mut rng); 724 | assert_eq!(uni_g.evaluate(&zeta), uni_h.evaluate(&zeta)); 725 | } 726 | 727 | 728 | #[test] 729 | fn corollary_2_5_3_1() { 730 | 731 | let n = 4; 732 | let k = 2; 733 | 734 | let mut rng = ark_std::test_rng(); 735 | 736 | // f1(X0, X1, X2) = X2 737 | let f1_mle = &MleCoeffs::new(vec![ 738 | (0b100, Scalar::from(1)) 739 | ] 740 | .into_iter() 741 | .collect(), 3); 742 | 743 | let f1_uni = U(n, &f1_mle); 744 | 745 | // f2(X) = X^{2^k} + 1 746 | let f2_uni = UniPolynomial { degree: pow_2(k), coeffs: { 747 | let mut zeros = vec![Scalar::zero(); pow_2(k)]; 748 | zeros[0] = Scalar::one(); 749 | zeros.push(Scalar::one()); 750 | zeros 751 | }}; 752 | 753 | // f3(X) = X - 1 754 | 755 | let f3_uni = UniPolynomial { degree: 1, 756 | coeffs: vec![-Scalar::one(), Scalar::one()] }; 757 | 758 | let f_uni = f1_uni.mul(&f2_uni).mul(&f3_uni); 759 | 760 | println!("^f={}", &f_uni); 761 | 762 | // g1(X) = X^{2^k} 763 | let g1_uni = UniPolynomial { degree: pow_2(k), coeffs: { 764 | let mut zeros = vec![Scalar::zero(); pow_2(k)]; 765 | zeros.push(Scalar::one()); 766 | zeros 767 | }}; 768 | 769 | // g2(X) = X^{2^n} - 1 770 | let g2_uni = UniPolynomial { degree: pow_2(n), coeffs: { 771 | let mut zeros = vec![Scalar::zero(); pow_2(n)]; 772 | zeros[0] = - Scalar::one(); 773 | zeros.push(Scalar::one()); 774 | zeros 775 | }}; 776 | 777 | let g_uni = g1_uni.mul(&g2_uni); 778 | 779 | println!("^g_uni={}", &g_uni); 780 | 781 | // test \hat{f} ?= \hat{g} 782 | let zeta = Scalar::rand(&mut rng); 783 | assert_eq!(f_uni.evaluate(&zeta), g_uni.evaluate(&zeta)); 784 | } 785 | 786 | 787 | #[test] 788 | fn corollary_2_5_3_2() { 789 | 790 | let n = 4; 791 | let k = 2; 792 | 793 | let mut rng = ark_std::test_rng(); 794 | 795 | // define a simple MLE 796 | let mut f_mle = MleCoeffs::new(vec![ 797 | (0b00, Scalar::from(2)), 798 | (0b01, Scalar::from(3)), 799 | (0b10, Scalar::from(4)), 800 | (0b11, Scalar::from(5)), 801 | ].into_iter().collect(), 802 | k); 803 | 804 | // 805 | let f1_uni_deg_k = U(k, &f_mle); 806 | 807 | f_mle.expand_vars(1); 808 | 809 | let xf_uni = U(n, &f_mle.mul(&&MleCoeffs::new(vec![ 810 | (0b100, Scalar::from(1)) 811 | ], k+1))); 812 | 813 | 814 | println!("^xf_uni={}", &xf_uni); 815 | 816 | // g1(X) = X^{2^k} 817 | let g1_uni = UniPolynomial { degree: pow_2(k), coeffs: { 818 | let mut zeros = vec![Scalar::zero(); pow_2(k)]; 819 | zeros.push(Scalar::one()); 820 | zeros 821 | }}; 822 | 823 | // g2(X) = Phi_{n-k-1}(X^{2^{k+1}}) 824 | let g2_uni = Phi(n-k-1, pow_2(k+1)); 825 | 826 | let g_uni = g1_uni.mul(&g2_uni).mul(&f1_uni_deg_k); 827 | 828 | // test \hat{f} ?= \hat{g} 829 | let zeta = Scalar::rand(&mut rng); 830 | assert_eq!(g_uni.evaluate(&zeta), xf_uni.evaluate(&zeta)); 831 | } 832 | /* 833 | #[test] 834 | fn test_evaluate() { 835 | let coeffs = vec![ 836 | (0b101, Scalar::from(2)), 837 | (0b010, Scalar::from(3)), 838 | (0b001, Scalar::from(4)), 839 | ].into_iter().collect(); 840 | let num_var = 3; 841 | let poly = MLEPolynomial{ 842 | num_var: num_var, 843 | coeffs: coeffs, 844 | }; 845 | 846 | // Test evaluation at different points 847 | let point1 = vec![Scalar::from(1), Scalar::from(0), Scalar::from(1)]; 848 | assert_eq!(poly.evaluate(&point1), Scalar::from(6)); 849 | 850 | let point2 = vec![Scalar::from(0), Scalar::from(1), Scalar::from(1)]; 851 | assert_eq!(poly.evaluate(&point2), Scalar::from(7)); 852 | 853 | let point3 = vec![Scalar::from(1), Scalar::from(1), Scalar::from(1)]; 854 | assert_eq!(poly.evaluate(&point3), Scalar::from(9)); 855 | 856 | let point4 = vec![Scalar::from(2), Scalar::from(2), Scalar::from(2)]; 857 | assert_eq!(poly.evaluate(&point4), Scalar::from(22)); 858 | } 859 | 860 | // #[test] 861 | // fn test_compute_quotients_2() { 862 | // // Create a MLE instance with coefficients 863 | // let coeffs = vec![ 864 | // (0b001, Scalar::from(4)), 865 | // (0b010, Scalar::from(3)), 866 | // (0b011, Scalar::from(1)), 867 | // (0b101, Scalar::from(2)), 868 | // (0b111, Scalar::from(5)), 869 | // ]; 870 | // let num_var = 3; 871 | // let poly = MLEPolynomial::new(coeffs, num_var); 872 | 873 | // // Test compute_quotients at different points 874 | // let point1 = vec![Scalar::from(1), Scalar::from(2), Scalar::from(1)]; 875 | // let quotients1 = poly.compute_quotients(&point1); 876 | // assert_eq!(quotients1.len(), 3); 877 | // println!("quotients1[0]={}", quotients1[0]); 878 | // println!("quotients1[1]={}", quotients1[1]); 879 | // println!("quotients1[2]={}", quotients1[2]); 880 | 881 | // let quotients2 = poly.compute_quotients_alt(&point1); 882 | // assert_eq!(quotients2.len(), 3); 883 | // println!("quotients2[0]={}", quotients2[0]); 884 | // println!("quotients2[1]={}", quotients2[1]); 885 | // println!("quotients2[2]={}", quotients2[2]); 886 | 887 | // // assert_eq!(quotients1[0].coeffs, vec![(0b001, Scalar::from(2))].into_iter().collect()); 888 | // // assert_eq!(quotients1[1].coeffs, vec![(0b000, Scalar::from(3))].into_iter().collect()); 889 | // // assert_eq!(quotients1[2].coeffs, vec![(0b000, Scalar::from(6))].into_iter().collect()); 890 | 891 | // // let point2 = vec![Scalar::from(2), Scalar::from(2), Scalar::from(2)]; 892 | // // let quotients2 = poly.compute_quotients(&point2); 893 | // // assert_eq!(quotients2.len(), 3); 894 | // // assert_eq!(quotients2[0].coeffs, vec![(0b001, Scalar::from(2))].into_iter().collect()); 895 | // // assert_eq!(quotients2[1].coeffs, vec![(0b000, Scalar::from(3))].into_iter().collect()); 896 | // // assert_eq!(quotients2[2].coeffs, vec![(0b000, Scalar::from(8))].into_iter().collect()); 897 | // } 898 | 899 | 900 | #[test] 901 | fn test_mle_div_mul() { 902 | let rng = &mut ark_std::test_rng(); 903 | let num_var = 5; 904 | let poly1 = MLEPolynomial { 905 | num_var: num_var, 906 | coeffs: (0..pow_2(num_var)) 907 | .map(|i| (i, Scalar::rand(rng))) 908 | .collect(), 909 | }; 910 | let div2: Divisor = (3, Scalar::rand(rng)); 911 | let (quo, rem) = poly1.div(&div2); 912 | let poly2 = quo 913 | .mul(&MLEPolynomial::from_divisor(num_var, &div2)) 914 | .add(&rem); 915 | assert_eq!(poly1, poly2); 916 | } 917 | */ 918 | 919 | #[test] 920 | fn test_prove_verify_plain() { 921 | init_logger(); 922 | 923 | let mut rng = ark_std::test_rng(); 924 | let vs = Scalar::from_usize_vector(&[1,2,3,4]); 925 | let f_mle = MleEvals::new(&vs); 926 | println!("num_var={}", f_mle.num_var); 927 | 928 | let pcs = MlePCSystem::setup_with_rng(&mut rng); 929 | let f_cm = pcs.commit(&f_mle); 930 | 931 | let us = Scalar::from_usize_vector(&[3,2]); 932 | let tr = Transcript::new(); 933 | let (v, prf) = pcs.prove_plain(&f_cm, &f_mle, &us, &mut tr.clone()); 934 | 935 | let verified = pcs.verify_plain(&f_cm, &us, &v, f_mle.num_var, &prf, &mut tr.clone()); 936 | assert!(verified); 937 | } 938 | 939 | #[test] 940 | fn test_prove_verify_opt() { 941 | init_logger(); 942 | 943 | let mut rng = ark_std::test_rng(); 944 | let vs = Scalar::from_usize_vector(&[1,2,3,4]); 945 | let f_mle = MleEvals::new(&vs); 946 | println!("num_var={}", f_mle.num_var); 947 | 948 | let pcs = MlePCSystem::setup_with_rng(&mut rng); 949 | let f_cm = pcs.commit(&f_mle); 950 | 951 | let us = Scalar::from_usize_vector(&[3,2]); 952 | let tr = Transcript::new(); 953 | let (v, prf) = pcs.prove_opt(&f_cm, &f_mle, &us, &mut tr.clone()); 954 | 955 | let verified = pcs.verify_opt(&f_cm, &us, &v, &prf, &mut tr.clone()); 956 | assert!(verified); 957 | } 958 | } 959 | 960 | --------------------------------------------------------------------------------