1 理论背景

本文主要解析基于论文《Polynomial Commitments∗》在代码库中的实现细节。
代码的实现流程基于论文《Marlin:Preprocessing zkSNARKs with Universal and Updatable SRS》中的6.1节和B.3。


2 相应代码实现

2.0 profiling不同曲线性能对比

zexebench-utilsprint-trace features支持对各个阶段的代码运行效率进行profiling。调用的方法可为:

cargo test end_to_end_test --features print-trace -- --nocaputre

为了便于对比,修改了程序,要求bls12-377bls12-381mnt6sw6每组曲线都对相同阶(如18)的多项式进行commit。

     end_to_end_test::<_, KZG_Bls12_377>().expect("test failed for bls12-377");end_to_end_test::<_, KZG_Bls12_381>().expect("test failed for bls12-381");end_to_end_test::<_, KZG_MNT6>().expect("test failed for MNT6");end_to_end_test::<_, KZG_SW6>().expect("test failed for SW6");

会有类似的运行结果:【由此可知,总体来说bls12-377的运算性能最高,bls12-377>bls12-381>mnt6>sw6】【evaluating polynomial的时间不是这样的,可能跟evaluate point有关系。固定在相同的点evaluate 试下。】



2.1 setup代码实现

 let (ck, vk) = SinglePC::setup(degree, rng)?;

https://github.com/scipr-lab/poly-commit/blob/master/src/single_pc/kzg10.rs中,setup代码实现流程如下:

     /// Constructs public parameters when given as input the maximum degree `degree`/// for the polynomial commitment scheme.fn setup<R: RngCore>(degree: usize,rng: &mut R,) -> Result<(Self::CommitterKey, Self::VerifierKey), Self::Error> {if degree < 1 {return Err(Error::UnsupportedDegree);}let setup_time = start_timer!(|| format!("Started KZG10::Setup with degree {}", degree));let beta = E::Fr::rand(rng);let g = E::G1Projective::rand(rng);let gamma_g = E::G1Projective::rand(rng);let h = E::G2Projective::rand(rng);let mut powers_of_beta = vec![E::Fr::one()];let mut cur = beta;// TODO: can optimize by computing// 1, beta, beta^2, beta^3, ..., beta^d and using those to compute arbitrary// powers of beta faster via a bit decomposition of each scalar.// But that seems like it would be slower? log(D) additions instead of// one multiplication.// Anyway it shouldn't be a bottleneck?// If we use MPC to run the setup, we can consider this optimization.for _ in 0..degree {powers_of_beta.push(cur);cur *= &beta;}let window_size = FixedBaseMSM::get_mul_window_size(degree + 1);let scalar_bits = E::Fr::size_in_bits();let g_time = start_timer!(|| "Generating powers of G");let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g);let mut powers_of_g = FixedBaseMSM::multi_scalar_mul::<E::G1Projective>(scalar_bits,window_size,&g_table,&powers_of_beta,);end_timer!(g_time);let gamma_g_time = start_timer!(|| "Generating powers of gamma * G");let gamma_g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, gamma_g);let mut powers_of_gamma_g = FixedBaseMSM::multi_scalar_mul::<E::G1Projective>(scalar_bits,window_size,&gamma_g_table,&powers_of_beta,);end_timer!(gamma_g_time);E::G1Projective::batch_normalization(powers_of_g.as_mut_slice());E::G1Projective::batch_normalization(powers_of_gamma_g.as_mut_slice());let ck = CommitterKey {powers_of_g: powers_of_g.into_iter().map(|e| e.into_affine()).collect(),powers_of_gamma_g: powers_of_gamma_g.into_iter().map(|e| e.into_affine()).collect(),degree,};let beta_h = h.mul(&beta).into_affine();let h = h.into_affine();let prepared_h = h.prepare();let prepared_beta_h = beta_h.prepare();let vk = VerifierKey {g: g.into_affine(),gamma_g: gamma_g.into_affine(),h,beta_h,prepared_h,prepared_beta_h,degree,};end_timer!(setup_time);Ok((ck, vk))}

2.2 随机多项式生成

let p = Polynomial::rand(degree, rng);pub use ff_fft::DensePolynomial as Polynomial;

https://github.com/scipr-lab/zexe/blob/master/ff-fft/src/polynomial/dense.rs中的

 /// Outputs a polynomial of degree `d` where each coefficient is sampled uniformly at random/// from the field `F`.pub fn rand<R: Rng>(d: usize, rng: &mut R) -> Self {let mut random_coeffs = Vec::new();for _ in 0..(d + 1) {random_coeffs.push(F::rand(rng));}Self::from_coefficients_vec(random_coeffs)}/// Constructs a new polynomial from a list of coefficients.pub fn from_coefficients_vec(mut coeffs: Vec<F>) -> Self {// While there are zeros at the end of the coefficient vector, pop them off.while coeffs.last().map_or(false, |c| c.is_zero()) {coeffs.pop();}// Check that either the coefficients vec is empty or that the last coeff is non-zero.assert!(coeffs.last().map_or(true, |coeff| !coeff.is_zero()));Self { coeffs }}

2.3 commit代码实现

/// Outputs a commitment to `polynomial`.fn commit(ck: &Self::CommitterKey,polynomial: &Polynomial<E::Fr>,hiding_bound: Option<usize>,rng: Option<&mut dyn RngCore>,) -> Result<(Self::Commitment, Self::Randomness), Error> {Error::check_degree(polynomial.degree(), ck.max_degree())?;let commit_time = start_timer!(|| format!("Committing to polynomial of degree {} with hiding_bound: {:?}",polynomial.degree(),hiding_bound,));let mut skip_zeros = 0;while polynomial.coeffs[skip_zeros].is_zero() && skip_zeros < polynomial.coeffs.len() {skip_zeros += 1;}let from_mont_repr_time =start_timer!(|| "Converting plaintext polynomial from Montgomery repr");let plain_coeffs = polynomial.coeffs[skip_zeros..].par_iter().map(|s| s.into_repr()).collect::<Vec<_>>();end_timer!(from_mont_repr_time);let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly");let mut commitment =VariableBaseMSM::multi_scalar_mul(&ck.powers_of_g[skip_zeros..], &plain_coeffs);end_timer!(msm_time);let mut randomness = Randomness::empty();if let Some(hiding_degree) = hiding_bound {let mut rng = rng.ok_or(Error::MissingRng)?;let sample_random_poly_time = start_timer!(|| format!("Sampling a random polynomial of degree {}",hiding_degree));randomness = Randomness::rand(hiding_degree, &mut rng);if randomness.blinding_polynomial.degree() > ck.max_degree() {eprintln!("The hiding bound is too large for the commitment key.");Err(Error::PolynomialDegreeTooLarge { poly_degree: randomness.blinding_polynomial.degree(),max_degree: ck.max_degree(), })?;}end_timer!(sample_random_poly_time);}let from_mont_repr_time =start_timer!(|| "Converting random polynomial from Montgomery repr");let random_ints = randomness.blinding_polynomial.coeffs.par_iter().map(|s: &E::Fr| s.into_repr()).collect::<Vec<_>>();end_timer!(from_mont_repr_time);let msm_time = start_timer!(|| "MSM to compute commitment to random poly");let random_commitment =VariableBaseMSM::multi_scalar_mul(&ck.powers_of_gamma_g, random_ints.as_slice()).into_affine();end_timer!(msm_time);commitment.add_assign_mixed(&random_commitment);end_timer!(commit_time);Ok((Commitment(commitment.into()), randomness))}
impl<E: PairingEngine> PCRandomness for Randomness<E> {fn empty() -> Self {Self {blinding_polynomial: Polynomial::zero(),}}fn rand<R: RngCore>(d: usize, rng: &mut R) -> Self {let mut randomness = Randomness::empty();randomness.blinding_polynomial = Polynomial::rand(d + 1, rng);randomness}
}

2.4 选取evaluation point对多项式进行evaluate

let point = F::rand(rng);
let value = p.evaluate(point);
 /// Evaluates `self` at the given `point` in the field.pub fn evaluate(&self, point: F) -> F {if self.is_zero() {return F::zero();}let mut powers_of_point = vec![F::one()];let mut cur = point;for _ in 0..self.degree() {powers_of_point.push(cur);cur *= &point;}assert_eq!(powers_of_point.len(), self.coeffs.len());let zero = F::zero();powers_of_point.into_par_iter().zip(&self.coeffs).map(|(power, coeff)| power * coeff).reduce(|| zero, |a, b| a + &b)}

2.5 open代码实现

 let proof = SinglePC::open(&ck, &p, point, &rand)?;
 /// On input a polynomial `p` and a point `point`, outputs a proof for the same.fn open(ck: &Self::CommitterKey,p: &Polynomial<E::Fr>,point: E::Fr,randomness: &Self::Randomness,) -> Result<Self::Proof, Error> {Error::check_degree(p.degree(), ck.max_degree())?;let open_time = start_timer!(|| format!("Opening polynomial of degree {}", p.degree()));let eval_time = start_timer!(|| "Evaluating polynomial");let value = p.evaluate(point);end_timer!(eval_time);let witness_time = start_timer!(|| "Computing witness polynomial");let witness_polynomial = &(p - &Polynomial::from_coefficients_vec(vec![value]))/ &Polynomial::from_coefficients_vec(vec![-point, E::Fr::one()]);end_timer!(witness_time);let convert_time = start_timer!(|| "Converting witness polynomial from Montgomery repr");let witness_coeffs = witness_polynomial.coeffs.into_par_iter().map(|s| s.into_repr()).collect::<Vec<_>>();end_timer!(convert_time);let witness_comm_time = start_timer!(|| "Computing commitment to witness polynomial");let mut w = VariableBaseMSM::multi_scalar_mul(&ck.powers_of_g, &witness_coeffs);end_timer!(witness_comm_time);let mut random_v = E::Fr::zero();if randomness.is_hiding() {let random_p = &randomness.blinding_polynomial;let rand_eval_time = start_timer!(|| "Evaluating random polynomial");let random_value = random_p.evaluate(point);end_timer!(rand_eval_time);let witness_time = start_timer!(|| "Computing random witness polynomial");let random_witness_polynomial = &(random_p- &Polynomial::from_coefficients_vec(vec![random_value]))/ &Polynomial::from_coefficients_vec(vec![-point, E::Fr::one()]);end_timer!(witness_time);let random_witness_coeffs = random_witness_polynomial.coeffs.into_par_iter().map(|s| s.into_repr()).collect::<Vec<_>>();let witness_comm_time =start_timer!(|| "Computing commitment to random witness polynomial");w += &VariableBaseMSM::multi_scalar_mul(&ck.powers_of_gamma_g, &random_witness_coeffs);end_timer!(witness_comm_time);random_v = random_value;}end_timer!(open_time);Ok(Proof {w: w.into_affine(),random_v,})}

// 多项式除法的实现如下:

impl<'a, 'b, F: Field> Div<&'a DensePolynomial<F>> for &'b DensePolynomial<F> {type Output = DensePolynomial<F>;#[inline]fn div(self, divisor: &'a DensePolynomial<F>) -> DensePolynomial<F> {let a: DenseOrSparsePolynomial<_> = self.into();let b: DenseOrSparsePolynomial<_> = divisor.into();a.divide_with_q_and_r(&b).expect("division failed").0}
}/// Divide self by another (sparse or dense) polynomial, and returns the quotient and remainder.pub fn divide_with_q_and_r(&self, divisor: &Self) -> Option<(DensePolynomial<F>, DensePolynomial<F>)> {if self.is_zero() {Some((DensePolynomial::zero(), DensePolynomial::zero()))} else if divisor.is_zero() {panic!("Dividing by zero polynomial")} else if self.degree() < divisor.degree() {Some((DensePolynomial::zero(), self.clone().into()))} else {// Now we know that self.degree() >= divisor.degree();let mut quotient = vec![F::zero(); self.degree() - divisor.degree() + 1];let mut remainder: DensePolynomial<F> = self.clone().into();// Can unwrap here because we know self is not zero.let divisor_leading_inv = divisor.leading_coefficient().unwrap().inverse().unwrap();while !remainder.is_zero() && remainder.degree() >= divisor.degree() {let cur_q_coeff = *remainder.coeffs.last().unwrap() * &divisor_leading_inv;let cur_q_degree = remainder.degree() - divisor.degree();quotient[cur_q_degree] = cur_q_coeff;for (i, div_coeff) in divisor.iter_with_index() {remainder[cur_q_degree + i] -= &(cur_q_coeff * &div_coeff);}while let Some(true) = remainder.coeffs.last().map(|c| c.is_zero()) {remainder.coeffs.pop();}}Some((DensePolynomial::from_coefficients_vec(quotient), remainder))}}

2.6 check代码实现

 assert!(SinglePC::check(&vk, &comm, point, value, &proof)?,"proof was incorrect for max_degree = {}, polynomial_degree = {}, hiding_bound = {:?}",degree,p.degree(),hiding_bound,);
 /// Verifies that `value` is the evaluation at `x` of the polynomial/// committed inside `comm`.fn check(vk: &Self::VerifierKey,comm: &Self::Commitment,point: E::Fr,value: E::Fr,proof: &Self::Proof,) -> Result<bool, Error> {let check_time = start_timer!(|| "Checking evaluation");let inner = comm.0.into_projective()- &vk.g.into_projective().mul(&value)- &vk.gamma_g.into_projective().mul(&proof.random_v);let lhs = E::pairing(inner, vk.h);let inner = vk.beta_h.into_projective() - &vk.h.into_projective().mul(&point);let rhs = E::pairing(proof.w, inner);end_timer!(check_time, || format!("Result: {}", lhs == rhs));Ok(lhs == rhs)}

2.7 batch_check代码实现

 fn batch_check<R: RngCore>(vk: &Self::VerifierKey,commitments: &[Self::Commitment],points: &[E::Fr],values: &[E::Fr],proofs: &[Self::Proof],rng: &mut R,) -> Result<bool, Self::Error> {let check_time =start_timer!(|| format!("Checking {} evaluation proofs", commitments.len()));let g = vk.g.into_projective();let gamma_g = vk.gamma_g.into_projective();let mut total_c = <E::G1Projective as ProjectiveCurve>::zero();let mut total_w = <E::G1Projective as ProjectiveCurve>::zero();let combination_time = start_timer!(|| "Combining commitments and proofs");let mut randomizer = E::Fr::one();// Instead of multiplying g and gamma_g in each turn, we simply accumulate// their coefficients and perform a final multiplication at the end.let mut g_multiplier = E::Fr::zero();let mut gamma_g_multiplier = E::Fr::zero();for (((c, z), v), proof) in commitments.iter().zip(points).zip(values).zip(proofs) {let mut c = c.0.into_projective();let w = proof.w.into_projective();c += &w.mul(z);g_multiplier += &(randomizer * &v);gamma_g_multiplier += &(randomizer * &proof.random_v);total_c += &c.mul(&randomizer);total_w += &w.mul(&randomizer);// We don't need to sample randomizers from the full field,// only from 128-bit strings.randomizer = u128::rand(rng).into();}total_c -= &g.mul(&g_multiplier);total_c -= &gamma_g.mul(&gamma_g_multiplier);end_timer!(combination_time);let to_affine_time = start_timer!(|| "Converting results to affine for pairing");let mut to_affine = [-total_w, total_c];E::G1Projective::batch_normalization(&mut to_affine);let [total_w, total_c] = to_affine;let total_w = total_w.into_affine();let total_c = total_c.into_affine();end_timer!(to_affine_time);let pairing_time = start_timer!(|| "Performing product of pairings");let result = E::product_of_pairings(&[(&total_w.prepare(), &vk.prepared_beta_h),(&total_c.prepare(), &vk.prepared_h),]) == E::Fqk::one();end_timer!(pairing_time);end_timer!(check_time, || format!("Result: {}", result));Ok(result)}

另关注下,Halo中polynomial commitment的实现细节。

借用博客polynomial commitment及实现方式对比3.2节内容:

  • 论文《Constant-Size Commitments to Polynomials and Their Applications》:
    provided protocols to commit to polynomials and then evaluate them at a given point in a verifiable way. Their protocols only require a constant number of commitments but security relies on pairing assumptions。
    基于的数学原理为:(论文《Sonic: Zero-Knowledge SNARKs from Linear-Size Universal and Updatable Structured Reference Strings》中的polynomial commitment也是基于此原理。)

  • 论文《Efficient Zero-Knowledge Arguments for Arithmetic Circuits in the Discrete Log Setting》:
    Our polynomial commitment protocol has square root communication complexity but
    relies solely on the discrete logarithm assumption.

    基于的原理是:(将系数拆分为矩阵,每行作为一个vector,将polynomial commitment拆分由vector commitment组成。)

参考资料:
[1] 论文《Polynomial Commitments∗》
[2] 代码库:https://github.com/scipr-lab/poly-commit
[3] 博客polynomial commitment及实现方式对比
[4] 博客椭圆曲线形式下的Pedersen commitment——vector commitmnt和polynomial commitment

Polynomial Commitments代码实现【1】——scipr-lab/poly-commit(含不同曲线性能对比)相关推荐

  1. Functional Commitment Schemes: From Polynomial Commitments to Pairing-Based Accumulators学习笔记

    1. 背景知识 Benoˆıt Libert, Somindu C. Ramanna 和 Moti Yung 2016年论文 <Functional Commitment Schemes: Fr ...

  2. RedShift: Transparent SNARKs from List Polynomial Commitments学习笔记

    1. 引言 纽约大学Kattis和Matter Labs团队2019年论文<RedShift: Transparent SNARKs from List Polynomial Commitmen ...

  3. 论文笔记Dory:Arguments for Inner Products and Polynomial Commitments

    Lee, J. (2021). Dory: Efficient, Transparent Arguments for Generalised Inner Products and Polynomial ...

  4. python 数据比对 函数_1行代码实现Python数据分析:图表美观清晰,自带对比功能丨开源...

    原标题:1行代码实现Python数据分析:图表美观清晰,自带对比功能丨开源

  5. 资源:代码舞动动画 提供gif图片(含程序、源码、下载地址)

    资源:代码舞动动画 提供gif图片(含程序.源码.下载地址) 案例 · 教程 · 地址: 3行代码 为你的网站博客添加萌萌哒可爱二次元女动漫玩偶人物(看板娘) 抖音上爆红的美女动态代码图如何实现? 以 ...

  6. SCI期刊写作必备(二):代码|手把手绘制目标检测领域YOLO论文常见的性能对比折线图,一键生成YOLOv7等主流论文同款图表,包含多种不同功能风格对比图表

    绘制一个原创属于自己的YOLO模型性能对比图表 具体绘制操作参考:(附Python代码,直接一键生成,精度对比图表代码 ) 只需要改动为自己的mAP.Params.FPS.GFlops等数值即可,一键 ...

  7. Node四种动态加载JS代码方法性能对比

    背景 我们运行node程序的时候,一般情况下,js代码都是事先写好在js文件里,然后启动的时候加载到内存中执行的.在一些特殊的需求下,我们可能会执行一些动态的js代码. 四种方法 目前我能想到的方法有 ...

  8. Pytorch优化器全总结(四)常用优化器性能对比 含代码

    目录 写在前面 一.优化器介绍 1.SGD+Momentum 2.Adagrad 3.Adadelta 4.RMSprop 5.Adam 6.Adamax 7.AdaW 8.L-BFGS 二.优化器对 ...

  9. 1行代码实现Python数据分析:图表美观清晰,自带对比功能丨开源

    公众号关注 "视学算法" 设为 "星标",DLCV消息即可送达! 萧箫 发自 凹非寺 转自 | 量子位 你是否也在朋友圈看过这样的小广告: 「你要悄悄学Pyth ...

  10. 计数排序、桶排序和基数排序的运算性能对比及总结区别(附python代码)

    首先证明一波排序算法的运算性能,如下图.对于50万个数据的无序列表,时间复杂度为的桶排序和计数排序明显比复杂度为的归并排序和快速排序性能好至少一个数量级. 1. 计数排序  1.1 基本原理:首先确定 ...

最新文章

  1. UTF-8 CPP的使用
  2. sys_guid 点滴用法
  3. 【网络安全】ollvm反混淆学习
  4. BZOJ 1101 Luogu P3455 POI 2007 Zap (莫比乌斯反演+数论分块)
  5. 深度学习总结:continuous actions和asyncronous advanteage actor-critic
  6. 复杂性思维中文第二版 附录 A、算法分析
  7. Pydiction : VIM上的PYTHON代码自动补全插件
  8. 一个大胖鲸-Docker(2):简单的几个docker命令
  9. wdinow 下 使用 docker 安装  code-server (web 版 vscode)
  10. Javascript预解析、作用域、作用域链
  11. POJ3979 分数加减法【水题】
  12. 30.卷1(套接字联网API)---客户/服务器程序设计规范
  13. 基于麻雀算法优化的Tsallis相对熵图像多阈值分割 -附代码
  14. linux标准mib,Linux系统中测试你的MIB值
  15. 支付系统行号(又称联行号)表下载
  16. [含论文+答辩PPT+任务书+中期检查表+源码等]S2SH洋酒销售系统|商城
  17. 揭秘 | Akuna工作体验大揭秘
  18. Frame Relay - 简单介绍及基本配置
  19. SolrCloud 初体验
  20. PE文件格式”1.9版 完整译文

热门文章

  1. 【5G核心网】 Network slicing 网络切片
  2. DCMTK实现Dicom CT图片读取CT值图像
  3. Mac Finder不显示侧边栏
  4. c语言用循环转换单词首字母,用c++实现将文本每个单词首字母转换为大写
  5. linux drop cache权限不够,Linux drop_caches
  6. K60解锁以及IAR Missing or malformed ...FlashK60Dxxx128K.flash错误的修改
  7. 英语----形容词和副词
  8. [日常] Apache Order Deny,Allow的用法
  9. ArcGis批量裁剪栅格图层
  10. 小米手机系统wifi服务器,手机时间变慢,小米高管科普:3招即可解决