You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

432 lines
14 KiB

  1. #![allow(non_snake_case)]
  2. #![allow(non_camel_case_types)]
  3. #![allow(non_upper_case_globals)]
  4. pub mod merkletree;
  5. use merkletree::{Hash, MerkleTree};
  6. pub mod transcript;
  7. use transcript::Transcript;
  8. use ark_ff::PrimeField;
  9. use ark_poly::{
  10. univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, GeneralEvaluationDomain,
  11. };
  12. use ark_std::cfg_into_iter;
  13. use ark_std::marker::PhantomData;
  14. use ark_std::ops::Div;
  15. use ark_std::ops::Mul;
  16. use ark_std::{rand::Rng, UniformRand};
  17. // rho^-1
  18. const rho1: usize = 8; // WIP TODO parametrize
  19. // FRI low degree testing proof
  20. pub struct LDTProof<F: PrimeField> {
  21. degree: usize, // claimed degree
  22. commitments: Vec<F>,
  23. mtproofs: Vec<Vec<F>>,
  24. evals: Vec<F>,
  25. constants: [F; 2],
  26. }
  27. // FRI_LDT implements the FRI Low Degree Testing
  28. pub struct FRI_LDT<F: PrimeField, P: DenseUVPolynomial<F>, H: Hash<F>> {
  29. _f: PhantomData<F>,
  30. _poly: PhantomData<P>,
  31. _h: PhantomData<H>,
  32. }
  33. impl<F: PrimeField, P: DenseUVPolynomial<F>, H: Hash<F>> FRI_LDT<F, P, H> {
  34. pub fn new() -> Self {
  35. Self {
  36. _f: PhantomData,
  37. _poly: PhantomData,
  38. _h: PhantomData,
  39. }
  40. }
  41. fn split(p: &P) -> (P, P) {
  42. // TODO see if enable check, take in mind g(x) being d-1
  43. // let d = p.coeffs().len();
  44. // if (d != 0) && (d & (d - 1) != 0) {
  45. // println!("d={:?}", d);
  46. // panic!("d should be a power of 2");
  47. // }
  48. // let d = p.degree() + 1;
  49. let coeffs = p.coeffs();
  50. let odd: Vec<F> = coeffs.iter().step_by(2).cloned().collect();
  51. let even: Vec<F> = coeffs.iter().skip(1).step_by(2).cloned().collect();
  52. return (
  53. P::from_coefficients_vec(odd),
  54. P::from_coefficients_vec(even),
  55. );
  56. }
  57. // prove implements the proof generation for a FRI-low-degree-testing
  58. // pub fn prove(p: &P) -> (Vec<F>, Vec<Vec<F>>, Vec<F>, [F; 2]) {
  59. pub fn prove(p: &P) -> LDTProof<F> {
  60. // init transcript
  61. let mut transcript: Transcript<F> = Transcript::<F>::new();
  62. let d = p.degree();
  63. let mut commitments: Vec<F> = Vec::new();
  64. let mut mts: Vec<MerkleTree<F, H>> = Vec::new();
  65. // f_0(x) = fL_0(x^2) + x fR_0(x^2)
  66. let mut f_i1 = p.clone();
  67. // sub_order = |F_i| = rho^-1 * d
  68. let mut sub_order = d * rho1; // TMP, TODO this will depend on rho parameter
  69. let mut eval_sub_domain: GeneralEvaluationDomain<F> =
  70. GeneralEvaluationDomain::new(sub_order).unwrap();
  71. // V sets rand z \in \mathbb{F} challenge
  72. let (z_pos, z) = transcript.get_challenge_in_eval_domain(eval_sub_domain, b"get z");
  73. let mut f_is: Vec<P> = Vec::new();
  74. // evals = {f_i(z^{2^i}), f_i(-z^{2^i})} \forall i \in F_i
  75. let mut evals: Vec<F> = Vec::new();
  76. let mut mtproofs: Vec<Vec<F>> = Vec::new();
  77. let mut fL_i: P = P::from_coefficients_vec(Vec::new());
  78. let mut fR_i: P = P::from_coefficients_vec(Vec::new());
  79. let mut i = 0;
  80. while f_i1.degree() >= 1 {
  81. f_is.push(f_i1.clone());
  82. let alpha_i = transcript.get_challenge(b"get alpha_i");
  83. let subdomain_evaluations: Vec<F> = cfg_into_iter!(0..eval_sub_domain.size())
  84. .map(|k| f_i1.evaluate(&eval_sub_domain.element(k)))
  85. .collect();
  86. // commit to f_{i+1}(x) = fL_i(x) + alpha_i * fR_i(x), commit to the evaluation domain
  87. let (cm_i, mt_i) = MerkleTree::<F, H>::commit(&subdomain_evaluations);
  88. commitments.push(cm_i);
  89. mts.push(mt_i);
  90. transcript.add(b"root_i", &cm_i);
  91. // evaluate f_i(z^{2^i}), f_i(-z^{2^i}), and open their commitment
  92. let z_2i = z.pow([2_u64.pow(i as u32)]); // z^{2^i} // TODO check usage of .pow(u64)
  93. let neg_z_2i = z_2i.neg();
  94. let eval_i = f_i1.evaluate(&z_2i);
  95. evals.push(eval_i);
  96. transcript.add(b"f_i(z^{2^i})", &eval_i);
  97. let eval_i = f_i1.evaluate(&neg_z_2i);
  98. evals.push(eval_i);
  99. transcript.add(b"f_i(-z^{2^i})", &eval_i);
  100. // gen the openings in the commitment to f_i(z^(2^i))
  101. let mtproof = mts[i].open(F::from(z_pos as u32));
  102. mtproofs.push(mtproof);
  103. (fL_i, fR_i) = Self::split(&f_i1);
  104. // compute f_{i+1}(x) = fL_i(x) + alpha_i * fR_i(x)
  105. let aux = DensePolynomial::from_coefficients_slice(fR_i.coeffs());
  106. f_i1 = fL_i.clone() + P::from_coefficients_slice(aux.mul(alpha_i).coeffs());
  107. // prepare next subdomain
  108. sub_order = sub_order / 2;
  109. eval_sub_domain = GeneralEvaluationDomain::new(sub_order).unwrap();
  110. i += 1;
  111. }
  112. if fL_i.coeffs().len() != 1 {
  113. panic!("fL_i not constant");
  114. }
  115. if fR_i.coeffs().len() != 1 {
  116. panic!("fR_i not constant");
  117. }
  118. let constant_fL_l: F = fL_i.coeffs()[0].clone();
  119. let constant_fR_l: F = fR_i.coeffs()[0].clone();
  120. LDTProof {
  121. degree: p.degree(),
  122. commitments,
  123. mtproofs,
  124. evals,
  125. constants: [constant_fL_l, constant_fR_l],
  126. }
  127. }
  128. // verify implements the verification of a FRI-low-degree-testing proof
  129. pub fn verify(
  130. proof: LDTProof<F>,
  131. degree: usize, // expected degree
  132. ) -> bool {
  133. // init transcript
  134. let mut transcript: Transcript<F> = Transcript::<F>::new();
  135. if degree != proof.degree {
  136. println!("proof degree missmatch");
  137. return false;
  138. }
  139. // TODO check that log_2(evals/2) == degree, etc
  140. let sub_order = rho1 * degree;
  141. let eval_sub_domain: GeneralEvaluationDomain<F> =
  142. GeneralEvaluationDomain::new(sub_order).unwrap();
  143. let (z_pos, z) = transcript.get_challenge_in_eval_domain(eval_sub_domain, b"get z");
  144. if proof.commitments.len() != (proof.evals.len() / 2) {
  145. println!("sho commitments.len() != (evals.len() / 2) - 1");
  146. return false;
  147. }
  148. let mut i_z = 0;
  149. for i in (0..proof.evals.len()).step_by(2) {
  150. let alpha_i = transcript.get_challenge(b"get alpha_i");
  151. // take f_i(z^2) from evals
  152. let z_2i = z.pow([2_u64.pow(i_z as u32)]); // z^{2^i}
  153. let fi_z = proof.evals[i];
  154. let neg_fi_z = proof.evals[i + 1];
  155. // compute f_i^L(z^2), f_i^R(z^2) from the linear combination
  156. let L = (fi_z + neg_fi_z) * F::from(2_u32).inverse().unwrap();
  157. let R = (fi_z - neg_fi_z) * (F::from(2_u32) * z_2i).inverse().unwrap();
  158. // compute f_{i+1}(z^2) = f_i^L(z^2) + a_i f_i^R(z^2)
  159. let next_fi_z2 = L + alpha_i * R;
  160. // check: obtained f_{i+1}(z^2) == evals.f_{i+1}(z^2) (=evals[i+2])
  161. if i < proof.evals.len() - 2 {
  162. if next_fi_z2 != proof.evals[i + 2] {
  163. println!(
  164. "verify step i={}, should f_i+1(z^2) == evals.f_i+1(z^2) (=evals[i+2])",
  165. i
  166. );
  167. return false;
  168. }
  169. }
  170. transcript.add(b"root_i", &proof.commitments[i_z]);
  171. transcript.add(b"f_i(z^{2^i})", &proof.evals[i]);
  172. transcript.add(b"f_i(-z^{2^i})", &proof.evals[i + 1]);
  173. // check commitment opening
  174. if !MerkleTree::<F, H>::verify(
  175. proof.commitments[i_z],
  176. F::from(z_pos as u32),
  177. proof.evals[i],
  178. proof.mtproofs[i_z].clone(),
  179. ) {
  180. println!("verify step i={}, MT::verify failed", i);
  181. return false;
  182. }
  183. // last iteration, check constant values equal to the obtained f_i^L(z^{2^i}),
  184. // f_i^R(z^{2^i})
  185. if i == proof.evals.len() - 2 {
  186. if L != proof.constants[0] {
  187. println!("constant L not equal to the obtained one");
  188. return false;
  189. }
  190. if R != proof.constants[1] {
  191. println!("constant R not equal to the obtained one");
  192. return false;
  193. }
  194. }
  195. i_z += 1;
  196. }
  197. true
  198. }
  199. }
  200. pub struct FRI_PCS_Proof<F: PrimeField> {
  201. p_proof: LDTProof<F>,
  202. g_proof: LDTProof<F>,
  203. mtproof_y_index: F, // TODO maybe include index in the mtproof, this would be done at the MerkleTree impl level
  204. mtproof_y: Vec<F>,
  205. }
  206. // FRI_PCS implements the FRI Polynomial Commitment
  207. pub struct FRI_PCS<F: PrimeField, P: DenseUVPolynomial<F>, H: Hash<F>> {
  208. _F: PhantomData<F>,
  209. _poly: PhantomData<P>,
  210. _h: PhantomData<H>,
  211. }
  212. impl<F: PrimeField, P: DenseUVPolynomial<F>, H: Hash<F>> FRI_PCS<F, P, H>
  213. where
  214. for<'a, 'b> &'a P: Div<&'b P, Output = P>,
  215. {
  216. pub fn commit(p: &P) -> F {
  217. let (cm, _, _) = Self::tree_from_domain_evals(p);
  218. cm
  219. }
  220. pub fn rand_in_eval_domain<R: Rng>(rng: &mut R, deg: usize) -> F {
  221. let sub_order = deg * rho1;
  222. let eval_domain: GeneralEvaluationDomain<F> =
  223. GeneralEvaluationDomain::new(sub_order).unwrap();
  224. let size = eval_domain.size();
  225. let c = usize::rand(rng);
  226. let pos = c % size;
  227. eval_domain.element(pos)
  228. }
  229. fn tree_from_domain_evals(p: &P) -> (F, MerkleTree<F, H>, Vec<F>) {
  230. let d = p.degree();
  231. let sub_order = d * rho1;
  232. let eval_sub_domain: GeneralEvaluationDomain<F> =
  233. GeneralEvaluationDomain::new(sub_order).unwrap();
  234. let subdomain_evaluations: Vec<F> = cfg_into_iter!(0..eval_sub_domain.size())
  235. .map(|k| p.evaluate(&eval_sub_domain.element(k)))
  236. .collect();
  237. let (cm, mt) = MerkleTree::<F, H>::commit(&subdomain_evaluations);
  238. (cm, mt, subdomain_evaluations)
  239. }
  240. pub fn open(p: &P, r: F) -> (F, FRI_PCS_Proof<F>) {
  241. let y = p.evaluate(&r);
  242. let y_poly: P = P::from_coefficients_vec(vec![y]);
  243. let mut p_y: P = p.clone();
  244. p_y.sub_assign(&y_poly);
  245. // p_y = p_y - y_poly;
  246. let x_r: P = P::from_coefficients_vec(vec![-r, F::one()]);
  247. // g(x), quotient polynomial
  248. let g: P = p_y.div(&x_r);
  249. if p.degree() != g.degree() + 1 {
  250. panic!("ERR p.deg: {}, g.deg: {}", p.degree(), g.degree()); // TODO err
  251. }
  252. // proof for commitment
  253. // reconstruct commitment_mt
  254. let (_, commitment_mt, subdomain_evaluations) = Self::tree_from_domain_evals(&p);
  255. // find y in subdomain_evaluations
  256. let mut y_eval_index: F = F::zero();
  257. for i in 0..subdomain_evaluations.len() {
  258. if y == subdomain_evaluations[i] {
  259. y_eval_index = F::from(i as u64);
  260. break;
  261. }
  262. }
  263. let mtproof_y = commitment_mt.open(y_eval_index);
  264. let p_proof = FRI_LDT::<F, P, H>::prove(p);
  265. let g_proof = FRI_LDT::<F, P, H>::prove(&g);
  266. (
  267. y,
  268. FRI_PCS_Proof {
  269. p_proof,
  270. g_proof,
  271. mtproof_y_index: y_eval_index,
  272. mtproof_y,
  273. },
  274. )
  275. }
  276. pub fn verify(commitment: F, proof: FRI_PCS_Proof<F>, r: F, y: F) -> bool {
  277. let deg_p = proof.p_proof.degree;
  278. let deg_g = proof.g_proof.degree;
  279. if deg_p != deg_g + 1 {
  280. return false;
  281. }
  282. // obtain z from transcript
  283. let sub_order = rho1 * proof.p_proof.degree;
  284. let eval_sub_domain: GeneralEvaluationDomain<F> =
  285. GeneralEvaluationDomain::new(sub_order).unwrap();
  286. let mut transcript: Transcript<F> = Transcript::<F>::new();
  287. let (_, z) = transcript.get_challenge_in_eval_domain(eval_sub_domain, b"get z");
  288. // check g(z) == (f(z) - y) * (z-r)^-1
  289. let gz = proof.g_proof.evals[0];
  290. let fz = proof.p_proof.evals[0];
  291. let rhs = (fz - y) / (z - r);
  292. if gz != rhs {
  293. return false;
  294. }
  295. // check that commitment was for the given y
  296. if !MerkleTree::<F, H>::verify(commitment, proof.mtproof_y_index, y, proof.mtproof_y) {
  297. return false;
  298. }
  299. // check FRI-LDT for p(x)
  300. if !FRI_LDT::<F, P, H>::verify(proof.p_proof, deg_p) {
  301. return false;
  302. }
  303. // check FRI-LDT for g(x)
  304. if !FRI_LDT::<F, P, H>::verify(proof.g_proof, deg_p - 1) {
  305. return false;
  306. }
  307. return true;
  308. }
  309. }
  310. #[cfg(test)]
  311. mod tests {
  312. use super::*;
  313. use ark_ff::Field;
  314. use ark_std::UniformRand;
  315. // pub type Fr = ark_bn254::Fr; // scalar field
  316. use ark_bn254::Fr; // scalar field
  317. use ark_poly::univariate::DensePolynomial;
  318. use ark_poly::Polynomial;
  319. use ark_std::log2;
  320. use merkletree::Keccak256Hash;
  321. #[test]
  322. fn test_split() {
  323. let mut rng = ark_std::test_rng();
  324. let deg = 7;
  325. let p = DensePolynomial::<Fr>::rand(deg, &mut rng);
  326. assert_eq!(p.degree(), deg);
  327. type FRID = FRI_LDT<Fr, DensePolynomial<Fr>, Keccak256Hash<Fr>>;
  328. let (pL, pR) = FRID::split(&p);
  329. // check that f(z) == fL(x^2) + x * fR(x^2), for a rand z
  330. let z = Fr::rand(&mut rng);
  331. assert_eq!(
  332. p.evaluate(&z),
  333. pL.evaluate(&z.square()) + z * pR.evaluate(&z.square())
  334. );
  335. }
  336. #[test]
  337. fn test_prove() {
  338. let deg = 31;
  339. let p = DensePolynomial::<Fr>::rand(deg, &mut ark_std::test_rng());
  340. assert_eq!(p.degree(), deg);
  341. // println!("p {:?}", p);
  342. type LDT = FRI_LDT<Fr, DensePolynomial<Fr>, Keccak256Hash<Fr>>;
  343. let proof = LDT::prove(&p);
  344. // commitments contains the commitments to each f_0, f_1, ..., f_n, with n=log2(d)
  345. assert_eq!(proof.commitments.len(), log2(p.coeffs().len()) as usize);
  346. assert_eq!(proof.evals.len(), 2 * log2(p.coeffs().len()) as usize);
  347. let v = LDT::verify(proof, deg);
  348. assert!(v);
  349. }
  350. #[test]
  351. fn test_polynomial_commitment() {
  352. let deg = 31;
  353. let mut rng = ark_std::test_rng();
  354. let p = DensePolynomial::<Fr>::rand(deg, &mut rng);
  355. type PCS = FRI_PCS<Fr, DensePolynomial<Fr>, Keccak256Hash<Fr>>;
  356. let commitment = PCS::commit(&p);
  357. // Verifier set challenge in evaluation domain for the degree
  358. let r = PCS::rand_in_eval_domain(&mut rng, deg);
  359. let (claimed_y, proof) = PCS::open(&p, r);
  360. let v = PCS::verify(commitment, proof, r, claimed_y);
  361. assert!(v);
  362. }
  363. }