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.

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