您当前的位置: 首页 > 

mutourend

暂无认证

  • 1浏览

    0关注

    661博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Plonk代码解析

mutourend 发布时间:2021-02-13 21:36:45 ,浏览量:1

1. 引言

Gabizon等人2019年论文《PLONK: permutations over lagrange-bases for oecumenical noninteractive arguments of knowledge》。

本文主要针对的代码库为:

  • https://github.com/dusk-network/plonk 【rust语言实现,在v0.1.0版本配有examples】

Plonk论文中,constraint 类型分为:【可参考Module dusk_plonk::notes ::prove_verify 文档】

  • gate constraint:用于表示add,mul,and,xor等常规gate的运算关系。
  • copy constraint:用于表示wires之间的关系,which have equality, from the entire circuit。copy constraint可通过permutation argument来验证。本质上,可通过Verifier给出的随机值来验证wires是否是不重复的。
2. [kzg10] polynomial commitment scheme

主要见https://github.com/dusk-network/plonk/tree/master/src/commitment_scheme,采用的曲线为bls12-381。

  • BlsScalar:表示scalar field
  • G1AffineG1Projective:表示group point,实际commitment采用G1Affine来表示,而借助.into()函数可将G1Projective转换为G1Affine形式。

identity是指无穷远点 ( 0 , 1 , 1 ) (0,1,1) (0,1,1)。满足任意点与该identity相加均为该点。

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
/// Holds a commitment to a polynomial in a form of a `G1Affine` Bls12_381 point.
pub struct Commitment(
    /// The commitment is a group element.
    pub G1Affine,
);

https://github.com/dusk-network/plonk代码库中,实际实现是参考了 Kate等人2010年论文[kzg10]《Polynomial Commitments∗》中 不具有hiding属性的 3.2节“ P o l y C o m m i t D L PolyCommit_{DL} PolyCommitDL​” 的属性,只是实际实现,采用的不是 symmetric pairing e : G × G → G T e:\mathbb{G}\times\mathbb{G}\rightarrow\mathbb{G}_T e:G×G→GT​,而 unsymmetric pairing e : G 1 × G 2 → G T e:\mathbb{G}_1\times \mathbb{G}_2\rightarrow \mathbb{G}_T e:G1​×G2​→GT​。

2.1 [kzg10] 单个多项式polynomial commitment

以degree 为 t t t的单个多项式 ϕ ( x ) ∈ Z p [ x ] \phi(x)\in\mathbb{Z}_p[x] ϕ(x)∈Zp​[x]为例,构建polynomial commitment的数学背景为: ϕ ( x ) − ϕ ( z ) x − z \frac{\phi(x)-\phi(z)}{x-z} x−zϕ(x)−ϕ(z)​ 不存在余数 for z ∈ Z p z\in\mathbb{Z}_p z∈Zp​。 g g g为generator of G 1 \mathbb{G}_1 G1​, h h h为generator of G 2 \mathbb{G}_2 G2​。

  • S e t u p ( t ) → p p Setup(t)\rightarrow pp Setup(t)→pp:输出的public parameters p p pp pp中包含 Prover用于生成proof的commit_key 和 Verifier用于verify的opening_key。 public parameters 又称为 structured reference string (srs)。 选择 β ∈ R Z p \beta\in_R\mathbb{Z_p} β∈R​Zp​为secret key S K SK SK,相应的: commit_key为: ( g , g β , ⋯   , g β t ) ∈ G 1 t + 1 (g,g^{\beta},\cdots,g^{\beta^t})\in\mathbb{G}_1^{t+1} (g,gβ,⋯,gβt)∈G1t+1​ 【Prover使用】 opening_key为: g ∈ G 1 , ( h , h β ) ∈ G 2 2 g\in\mathbb{G}_1,(h,h^{\beta})\in\mathbb{G}_2^2 g∈G1​,(h,hβ)∈G22​ 【Verifier使用】
/// The Public Parameters can also be referred to as the Structured Reference String (SRS).
/// It is available to both the prover and verifier and allows the verifier to
/// efficiently verify and make claims about polynomials up to and including a configured degree.
#[derive(Debug, Clone)]
pub struct PublicParameters {
    /// Key used to generate proofs for composed circuits.
    pub commit_key: CommitKey,
    /// Key used to verify proofs for composed circuits.
    pub opening_key: OpeningKey,
}

	/// Setup generates the public parameters using a random number generator.
    /// This method will in most cases be used for testing and exploration.
    /// In reality, a `Trusted party` or a `Multiparty Computation` will used to generate the SRS.
    /// Returns an error if the configured degree is less than one.
    pub fn setup(
        max_degree: usize,
        mut rng: &mut R,
    ) -> Result {
        // Cannot commit to constants
        if max_degree < 1 {
            return Err(Error::DegreeIsZero);
        }

        // Generate the secret scalar beta
        let beta = util::random_scalar(&mut rng);

        // Compute powers of beta up to and including beta^max_degree
        let powers_of_beta = util::powers_of(&beta, max_degree);

        // Powers of G1 that will be used to commit to a specified polynomial
        let g = util::random_g1_point(&mut rng);
        let powers_of_g: Vec =
            util::slow_multiscalar_mul_single_base(&powers_of_beta, g);
        assert_eq!(powers_of_g.len(), max_degree + 1);

        // Normalise all projective points
        let mut normalised_g = vec![G1Affine::identity(); max_degree + 1];
        G1Projective::batch_normalize(&powers_of_g, &mut normalised_g);

        // Compute beta*G2 element and stored cached elements for verifying multiple proofs.
        let h: G2Affine = util::random_g2_point(&mut rng).into();
        let beta_h: G2Affine = (h * beta).into();

        Ok(PublicParameters {
            commit_key: CommitKey {
                powers_of_g: normalised_g,
            },
            opening_key: OpeningKey::new(g.into(), h, beta_h),
        })
    }
  • C o m m i t ( commit_key , ϕ ( x ) ) → C Commit(\text{commit\_key},\phi(x))\rightarrow \mathcal{C} Commit(commit_key,ϕ(x))→C:对多项式 ϕ ( x ) = ∑ j = 0 d e g ( ϕ ) ϕ j x j \phi(x)=\sum_{j=0}^{deg(\phi)}\phi_jx^j ϕ(x)=∑j=0deg(ϕ)​ϕj​xj进行commit,输出相应的commitment C = g ϕ ( β ) = ∏ j = 0 d e g ( ϕ ) ( g β j ) ϕ j \mathcal{C}=g^{\phi(\beta)}=\prod_{j=0}^{deg(\phi)}(g^{\beta^j})^{\phi_j} C=gϕ(β)=∏j=0deg(ϕ)​(gβj)ϕj​。
	/// Commits to a polynomial returning the corresponding `Commitment`.
    ///
    /// Returns an error if the polynomial's degree is more than the max degree of the commit key.
    pub fn commit(&self, polynomial: &Polynomial) -> Result {
        // Check whether we can safely commit to this polynomial
        self.check_commit_degree_is_within_bounds(polynomial.degree())?;

        // Compute commitment
        let commitment = msm_variable_base(&self.powers_of_g, &polynomial.coeffs);
        Ok(Commitment::from_projective(commitment))
    }

  • O p e n _ s i n g l e ( commit_key , ϕ ( x ) , v , z ) → π Open\_single(\text{commit\_key},\phi(x), v, z)\rightarrow \pi Open_single(commit_key,ϕ(x),v,z)→π:即证明 ϕ ( z ) = v \phi(z)=v ϕ(z)=v,输出为opening proof π \pi π。 实际的witness为 ψ ( x ) = ϕ ( x ) − ϕ ( z ) x − z \psi(x)=\frac{\phi(x)-\phi(z)}{x-z} ψ(x)=x−zϕ(x)−ϕ(z)​ 对应的commitment π = g ψ ( β ) \pi=g^{\psi(\beta)} π=gψ(β)。
	/// Creates an opening proof that a polynomial `p` was correctly evaluated at p(z) and produced the value
    /// `v`. ie v = p(z).
    /// Returns an error if the polynomials degree is too large.
    pub fn open_single(
        &self,
        polynomial: &Polynomial,
        value: &BlsScalar,
        point: &BlsScalar,
    ) -> Result {
        let witness_poly = self.compute_single_witness(polynomial, point);
        Ok(Proof {
            commitment_to_witness: self.commit(&witness_poly)?,
            evaluated_point: *value,
            commitment_to_polynomial: self.commit(polynomial)?,
        })
    }
	
	/// 这段注释有问题。。。。。。
	/// For a given polynomial `p` and a point `z`, compute the witness
    /// for p(z) using Ruffini's method for simplicity.
    /// The Witness is the quotient of f(x) - f(z) / x-z.
    /// However we note that the quotient polynomial is invariant under the value f(z)
    /// ie. only the remainder changes. We can therefore compute the witness as f(x) / x - z
    /// and only use the remainder term f(z) during verification.
    pub fn compute_single_witness(&self, polynomial: &Polynomial, point: &BlsScalar) -> Polynomial {
        // Computes `f(x) / x-z`, returning it as the witness poly
        polynomial.ruffini(*point)
    }
  • V e r i f y E v a l _ s i n g l e ( opening_key , π , z , v , C ) → 0  or  1 VerifyEval\_single(\text{opening\_key},\pi,z,v,\mathcal{C})\rightarrow 0\text{ or } 1 VerifyEval_single(opening_key,π,z,v,C)→0 or 1:Verifier根据Prover提供的proof和之前对polynomial的commitment C \mathcal{C} C 来确认相应的evaluation是否正确。 即仅需验证: e ( C / g v , h ) ⋅ e ( π , h ( z − β ) ) = 1 e(\mathcal{C}/g^v, h)\cdot e(\pi,h^{(z-\beta)})=1 e(C/gv,h)⋅e(π,h(z−β))=1 是否成立即可。
	/// Checks that a polynomial `p` was evaluated at a point `z` and returned the value specified `v`.
    /// ie. v = p(z).
    pub fn check(&self, point: BlsScalar, proof: Proof) -> bool {
        let inner_a: G1Affine =
            (proof.commitment_to_polynomial.0 - (self.g * proof.evaluated_point)).into();

        let inner_b: G2Affine = (self.beta_h - (self.h * point)).into();
        let prepared_inner_b = G2Prepared::from(-inner_b);

        let pairing = dusk_bls12_381::multi_miller_loop(&[
            (&inner_a, &self.prepared_h),
            (&proof.commitment_to_witness.0, &prepared_inner_b),
        ])
        .final_exponentiation();

        pairing == dusk_bls12_381::Gt::identity()
    }

[kzg10]单个多项式 polynomial commitment scheme示例为:

	#[test]
    fn test_basic_commit() {
        let degree = 25;
        let (proving_key, opening_key) = setup_test(degree);
        let point = BlsScalar::from(10);

        let poly = Polynomial::rand(degree, &mut rand::thread_rng());
        let value = poly.evaluate(&point);

        let proof = proving_key.open_single(&poly, &value, &point).unwrap();

        let ok = opening_key.check(point, proof);
        assert!(ok);
    }
2.2 batch open different polynomials at same points

proof 个数为 O ( 1 ) O(1) O(1),与points个数无关。 在这里插入图片描述

具体测试用例为:

	#[test]
    fn test_aggregate_witness() {
        let max_degree = 27;
        let (proving_key, opening_key) = setup_test(max_degree);
        let point = BlsScalar::from(10);

        // Committer's View
        let aggregated_proof = {
            // Compute secret polynomials and their evaluations
            let poly_a = Polynomial::rand(25, &mut rand::thread_rng());
            let poly_a_eval = poly_a.evaluate(&point);

            let poly_b = Polynomial::rand(26 + 1, &mut rand::thread_rng());
            let poly_b_eval = poly_b.evaluate(&point);

            let poly_c = Polynomial::rand(27, &mut rand::thread_rng());
            let poly_c_eval = poly_c.evaluate(&point);

            proving_key
                .open_multiple(
                    &[poly_a, poly_b, poly_c],
                    vec![poly_a_eval, poly_b_eval, poly_c_eval],
                    &point,
                    &mut Transcript::new(b"agg_flatten"),
                )
                .unwrap()
        };

        // Verifier's View
        let ok = {
            let flattened_proof = aggregated_proof.flatten(&mut Transcript::new(b"agg_flatten"));
            opening_key.check(point, flattened_proof)
        };

        assert!(ok);
    }
	/// Creates an opening proof that multiple polynomials were evaluated at the same point
    /// and that each evaluation produced the correct evaluation point.
    /// Returns an error if any of the polynomial's degrees are too large.
    pub fn open_multiple(
        &self,
        polynomials: &[Polynomial],
        evaluations: Vec,
        point: &BlsScalar,
        transcript: &mut Transcript,
    ) -> Result {
        // Commit to polynomials
        let mut polynomial_commitments = Vec::with_capacity(polynomials.len());
        for poly in polynomials.iter() {
            polynomial_commitments.push(self.commit(poly)?)
        }

        // Compute the aggregate witness for polynomials
        let witness_poly = self.compute_aggregate_witness(polynomials, point, transcript);

        // Commit to witness polynomial
        let witness_commitment = self.commit(&witness_poly)?;

        let aggregate_proof = AggregateProof {
            commitment_to_witness: witness_commitment,
            evaluated_points: evaluations,
            commitments_to_polynomials: polynomial_commitments,
        };
        Ok(aggregate_proof)
    }

	/// Computes a single witness for multiple polynomials at the same point, by taking
    /// a random linear combination of the individual witnesses.
    /// We apply the same optimisation mentioned in when computing each witness; removing f(z).
    pub(crate) fn compute_aggregate_witness(
        &self,
        polynomials: &[Polynomial],
        point: &BlsScalar,
        transcript: &mut Transcript,
    ) -> Polynomial {
        let challenge = transcript.challenge_scalar(b"aggregate_witness");
        let powers = util::powers_of(&challenge, polynomials.len() - 1);

        assert_eq!(powers.len(), polynomials.len());

        let numerator: Polynomial = polynomials
            .iter()
            .zip(powers.iter())
            .map(|(poly, challenge)| poly * challenge)
            .sum();
        numerator.ruffini(*point)
    }

Verifier 进行 flatten的主要作用为:

  • 获取相同的随机值 γ \gamma γ,计算 ( 1 , γ , ⋯   , γ t − 1 ) (1,\gamma,\cdots,\gamma^{t-1}) (1,γ,⋯,γt−1)。
  • flattened_poly_commitments 对应为 F F F,flattened_poly_evaluations对应为 v v v。 在这里插入图片描述
	/// Flattens an `AggregateProof` into a `Proof`.
    /// The transcript must have the same view as the transcript that was used to aggregate the witness in the proving stage.
    pub fn flatten(&self, transcript: &mut Transcript) -> Proof {
        let challenge = transcript.challenge_scalar(b"aggregate_witness");
        let powers = powers_of(&challenge, self.commitments_to_polynomials.len() - 1);

        // Flattened polynomial commitments using challenge
        let flattened_poly_commitments: G1Projective = self
            .commitments_to_polynomials
            .iter()
            .zip(powers.iter())
            .map(|(poly, challenge)| poly.0 * challenge)
            .sum();
        // Flattened evaluation points
        let flattened_poly_evaluations: BlsScalar = self
            .evaluated_points
            .iter()
            .zip(powers.iter())
            .map(|(eval, challenge)| eval * challenge)
            .fold(BlsScalar::zero(), |acc, current_val| acc + current_val);

        Proof {
            commitment_to_witness: self.commitment_to_witness,
            evaluated_point: flattened_poly_evaluations,
            commitment_to_polynomial: Commitment::from_projective(flattened_poly_commitments),
        }
    }
2.3 batch open different polynomials at different points 2.3.1 当proof 个数与points个数一致时

当proof 个数与points个数一致时,即为每个point的evaluation生成一个proof,对应示例为:

	#[test]
    fn test_batch_verification() {
        let degree = 25;
        let (proving_key, vk) = setup_test(degree);

        let point_a = BlsScalar::from(10);
        let point_b = BlsScalar::from(11);

        // Compute secret polynomial a
        let poly_a = Polynomial::rand(degree, &mut rand::thread_rng());
        let value_a = poly_a.evaluate(&point_a);
        let proof_a = proving_key
            .open_single(&poly_a, &value_a, &point_a)
            .unwrap();
        assert!(vk.check(point_a, proof_a));

        // Compute secret polynomial b
        let poly_b = Polynomial::rand(degree, &mut rand::thread_rng());
        let value_b = poly_b.evaluate(&point_b);
        let proof_b = proving_key
            .open_single(&poly_b, &value_b, &point_b)
            .unwrap();
        assert!(vk.check(point_b, proof_b));

        assert!(vk
            .batch_check(
                &[point_a, point_b],
                &[proof_a, proof_b],
                &mut Transcript::new(b""),
            )
            .is_ok());
    }

相应batch算法实现为:

  • V e r i f y E v a l _ b a t c h ( opening_key , π 1 , ⋯   , π n , z 1 , ⋯   , z n , v 1 . ⋯   , v n , C 1 , ⋯   , C n ) → 0  or  1 VerifyEval\_batch(\text{opening\_key},\pi_1,\cdots,\pi_n,z_1,\cdots,z_n,v_1.\cdots,v_n,\mathcal{C}_1,\cdots,\mathcal{C}_n)\rightarrow 0\text{ or } 1 VerifyEval_batch(opening_key,π1​,⋯,πn​,z1​,⋯,zn​,v1​.⋯,vn​,C1​,⋯,Cn​)→0 or 1:Verifier根据Prover提供的proofs π 1 , ⋯   , π n \pi_1,\cdots,\pi_n π1​,⋯,πn​和之前对polynomials f 1 , ⋯   , f n f_1,\cdots,f_n f1​,⋯,fn​的commitments C 1 , ⋯   , C n \mathcal{C}_1,\cdots,\mathcal{C}_n C1​,⋯,Cn​ 来确认相应的evaluations v 1 = f 1 ( z 1 ) , ⋯   , v n = f n ( z n ) v_1=f_1(z_1),\cdots,v_n=f_n(z_n) v1​=f1​(z1​),⋯,vn​=fn​(zn​)是否正确。 Verifier选择随机值 γ \gamma γ,并计算 ( 1 , γ , ⋯   , γ n − 1 ) (1,\gamma,\cdots,\gamma^{n-1}) (1,γ,⋯,γn−1)。 计算: C s u m = ∑ i = 1 n γ i − 1 ( C i + π i ⋅ z i ) − ∑ i = 1 n γ i − 1 v i = ∑ i = 1 n γ i − 1 β ( f i ( β ) − f i ( z i ) ) β − z i \mathcal{C}_{sum}=\sum_{i=1}^{n}\gamma^{i-1}(\mathcal{C}_{i}+\pi_{i}\cdot z_i)-\sum_{i=1}^n\gamma^{i-1}v_i=\sum_{i=1}^n\gamma^{i-1}\frac{\beta(f_i(\beta)-f_i(z_i))}{\beta-z_i} Csum​=∑i=1n​γi−1(Ci​+πi​⋅zi​)−∑i=1n​γi−1vi​=∑i=1n​γi−1β−zi​β(fi​(β)−fi​(zi​))​ π s u m = − ∑ i = 1 n γ i − 1 π i = − ∑ i = 1 n γ i − 1 f i ( β ) − f i ( z i ) β − z i \pi_{sum}=-\sum_{i=1}^{n}\gamma^{i-1}\pi_i=-\sum_{i=1}^{n}\gamma^{i-1}\frac{f_i(\beta)-f_i(z_i)}{\beta-z_i} πsum​=−∑i=1n​γi−1πi​=−∑i=1n​γi−1β−zi​fi​(β)−fi​(zi​)​ 即仅需验证: e ( C s u m , h ) ⋅ e ( π s u m , h β ) = 1 e(\mathcal{C}_{sum}, h)\cdot e(\pi_{sum},h^{\beta})=1 e(Csum​,h)⋅e(πsum​,hβ)=1 是否成立即可。
	/// Checks whether a batch of polynomials evaluated at different points, returned their specified value.
    pub fn batch_check(
        &self,
        points: &[BlsScalar],
        proofs: &[Proof],
        transcript: &mut Transcript,
    ) -> Result {
        let mut total_c = G1Projective::identity();
        let mut total_w = G1Projective::identity();

        let challenge = transcript.challenge_scalar(b"batch"); // XXX: Verifier can add their own randomness at this point
        let powers = util::powers_of(&challenge, proofs.len() - 1);
        // 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 = BlsScalar::zero();

        for ((proof, challenge), point) in proofs.iter().zip(powers).zip(points) {
            let mut c = G1Projective::from(proof.commitment_to_polynomial.0);
            let w = proof.commitment_to_witness.0;
            c += w * point;
            g_multiplier += challenge * proof.evaluated_point;

            total_c += c * challenge;
            total_w += w * challenge;
        }
        total_c -= self.g * g_multiplier;

        let affine_total_w = G1Affine::from(-total_w);
        let affine_total_c = G1Affine::from(total_c);

        let pairing = dusk_bls12_381::multi_miller_loop(&[
            (&affine_total_w, &self.prepared_beta_h),
            (&affine_total_c, &self.prepared_h),
        ])
        .final_exponentiation();

        if pairing != dusk_bls12_381::Gt::identity() {
            return Err(Error::PairingCheckFailure);
        };
        Ok(())
    }

实际,Plonk中针对的场景为,evaluation points中实际仅有2组不同,分别表示为 z , z ′ z,z' z,z′,具体示例为:【即proof 个数与不同的points个数一致】

	#[test]
    fn test_batch_with_aggregation() {
        let max_degree = 28;
        let (proving_key, opening_key) = setup_test(max_degree);
        let point_a = BlsScalar::from(10);
        let point_b = BlsScalar::from(11);

        // Committer's View
        let (aggregated_proof, single_proof) = {
            // Compute secret polynomial and their evaluations
            let poly_a = Polynomial::rand(25, &mut rand::thread_rng());
            let poly_a_eval = poly_a.evaluate(&point_a);

            let poly_b = Polynomial::rand(26, &mut rand::thread_rng());
            let poly_b_eval = poly_b.evaluate(&point_a);

            let poly_c = Polynomial::rand(27, &mut rand::thread_rng());
            let poly_c_eval = poly_c.evaluate(&point_a);

            let poly_d = Polynomial::rand(28, &mut rand::thread_rng());
            let poly_d_eval = poly_d.evaluate(&point_b);

            let aggregated_proof = proving_key
                .open_multiple(
                    &[poly_a, poly_b, poly_c],
                    vec![poly_a_eval, poly_b_eval, poly_c_eval],
                    &point_a,
                    &mut Transcript::new(b"agg_batch"),
                )
                .unwrap();

            let single_proof = proving_key
                .open_single(&poly_d, &poly_d_eval, &point_b)
                .unwrap();

            (aggregated_proof, single_proof)
        };

        // Verifier's View
        let ok = {
            let mut transcript = Transcript::new(b"agg_batch");
            let flattened_proof = aggregated_proof.flatten(&mut transcript);

            opening_key.batch_check(
                &[point_a, point_b],
                &[flattened_proof, single_proof],
                &mut transcript,
            )
        };

        assert!(ok.is_ok());
    }
3. 借助(I)FFT来加速多项式乘法运算 3.1 (I)FFT加速多项式乘法运算

(I)FFT 仅适于 multiplicative subgroup of size that is a power-of-2。 具体代码见https://github.com/3for/plonk/blob/master/src/fft/ 目录:

/// Performs O(nlogn) multiplication of polynomials if F is smooth.
impl Mul for &'b Polynomial {
    type Output = Polynomial;

    #[inline]
    fn mul(self, other: &'a Polynomial) -> Polynomial {
        if self.is_zero() || other.is_zero() {
            Polynomial::zero()
        } else {
            let domain = EvaluationDomain::new(self.coeffs.len() + other.coeffs.len())
                .expect("field is not smooth enough to construct domain");
            let mut self_evals = Evaluations::from_vec_and_domain(domain.fft(&self.coeffs), domain);
            let other_evals = Evaluations::from_vec_and_domain(domain.fft(&other.coeffs), domain);
            self_evals *= &other_evals;
            self_evals.interpolate()
        }
    }
}
/// Defines a domain over which finite field (I)FFTs can be performed. Works
/// only for fields that have a large multiplicative subgroup of size that is
/// a power-of-2.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct EvaluationDomain {
    /// The size of the domain.
    pub size: u64,
    /// `log_2(self.size)`.
    pub log_size_of_group: u32,
    /// Size of the domain as a field element.
    pub size_as_field_element: BlsScalar,
    /// Inverse of the size in the field.
    pub size_inv: BlsScalar,
    /// A generator of the subgroup.
    pub group_gen: BlsScalar,
    /// Inverse of the generator of the subgroup.
    pub group_gen_inv: BlsScalar,
    /// Multiplicative generator of the finite field.
    pub generator_inv: BlsScalar,
}
3.2 coset FFT

在这里插入图片描述

coset FFT的主要作用是将n域的系数扩展至4n域内,从而加速 点值表示方式下 的求商运算 得 quotient polynomial: t ( X ) = z ( X ) − z H ( X ) z H ( X ) = f ( X ) X n − 1 t(X)=\frac{z(X)-z_H(X)}{z_H(X)}=\frac{f(X)}{X^n-1} t(X)=zH​(X)z(X)−zH​(X)​=Xn−1f(X)​。缺点是增加了Prover需要维护的信息量,由n扩充至4n。 在这里插入图片描述

假设order为 p p p的有限域内,相应的generator为 g g g,有 g p − 1 ≡ 1 m o d    p g^{p-1}\equiv 1\mod p gp−1≡1modp。 以domain_n n = 8 n=8 n=8为例,假设有 w 8 ≡ 1 m o d    p w^8\equiv 1\mod p w8≡1modp。 则相应的domain_4n 中有, v 32 ≡ 1 m o d    p v^{32}\equiv 1\mod p v32≡1modp。

let q_m_eval_4n =
            Evaluations::from_vec_and_domain(domain_4n.coset_fft(&selectors.q_m), domain_4n);

其中domain_n的q_m多项式:

  • 以系数表示为 ( c 0 , c 1 , c 2 , c 3 , c 4 , c 5 , c 6 , c 7 ) (c_0,c_1,c_2,c_3,c_4,c_5,c_6,c_7) (c0​,c1​,c2​,c3​,c4​,c5​,c6​,c7​);
  • 以点值表示为 ( ( 1 , q m 0 ) , ( w , q m 1 ) , ( w 2 , q m 2 ) , ⋯   , ( w 7 , q m 7 ) ) ((1,qm_0), (w,qm_1),(w^2,qm_2),\cdots,(w^7,qm_7)) ((1,qm0​),(w,qm1​),(w2,qm2​),⋯,(w7,qm7​))

coset_fft()函数是将系数扩充至domain_4n,多项式q_m_4n

  • 以系数表示为 ( c 0 , c 1 g , c 2 g 2 , c 3 g 3 , c 4 g 4 , c 5 g 5 , c 6 g 6 , c 7 g 7 , 0 , ⋯   , 0 ) (c_0,c_1g,c_2g^2,c_3g^3,c_4g^4,c_5g^5,c_6g^6,c_7g^7,0,\cdots,0) (c0​,c1​g,c2​g2,c3​g3,c4​g4,c5​g5,c6​g6,c7​g7,0,⋯,0)【总长度为8*4=32】
  • 以点值表示为 ( ( 1 , q m 4 n 0 ) , ( v , q m 4 n 1 ) , ⋯   , ( v 31 , q m 4 n 31 ) ) ((1,qm4n_0),(v,qm4n_1),\cdots,(v^{31},qm4n_{31})) ((1,qm4n0​),(v,qm4n1​),⋯,(v31,qm4n31​))。【代码中q_m_eval_4n即为相应的点值表示。】
// Compute 4n evaluations for X^n -1
v_h_coset_4n: domain_4n.compute_vanishing_poly_over_coset(domain.size() as u64),

compute_vanishing_poly_over_coset()中的 evaluations v_h 为: [ g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 g 8 − 1 g 8 v 8 − 1 g 8 v 16 − 1 g 8 v 24 − 1 ] \begin{bmatrix} g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1\\ g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1\\ g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1\\ g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1\\ g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1\\ g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1\\ g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1\\ g^8-1 & g^8v^8-1 & g^8v^{16}-1 & g^8v^{24}-1 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡​g8−1g8−1g8−1g8−1g8−1g8−1g8−1g8−1​g8v8−1g8v8−1g8v8−1g8v8−1g8v8−1g8v8−1g8v8−1g8v8−1​g8v16−1g8v16−1g8v16−1g8v16−1g8v16−1g8v16−1g8v16−1g8v16−1​g8v24−1g8v24−1g8v24−1g8v24−1g8v24−1g8v24−1g8v24−1g8v24−1​⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤​

相应的插值点为: [ 1 v v 2 v 3 v 4 v 5 v 6 v 7 v 8 v 9 v 10 v 11 v 12 v 13 v 14 v 15 v 16 v 17 v 18 v 19 v 20 v 21 v 22 v 23 v 24 v 25 v 26 v 27 v 28 v 29 v 30 v 31 ] \begin{bmatrix} 1 & v & v^2 & v^3\\ v^4 & v^5 & v^6 & v^7\\ v^8 & v^9 & v^{10} & v^{11}\\ v^{12} & v^{13} & v^{14} & v^{15}\\ v^{16} & v^{17} & v^{18} & v^{19}\\ v^{20} & v^{21} & v^{22} & v^{23}\\ v^{24} & v^{25} & v^{26} & v^{27}\\ v^{28} & v^{29} & v^{30} & v^{31} \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡​1v4v8v12v16v20v24v28​vv5v9v13v17v21v25v29​v2v6v10v14v18v22v26v30​v3v7v11v15v19v23v27v31​⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤​

4. permutation argument

具体代码实现见:https://github.com/3for/plonk/blob/master/src/permutation/permutation.rs

粒度在Variable(以此为键值),每个变量可为多个门的左侧输入、右侧输入、输出或者是Fourth值,这些信息维护在相应的Vec中。

/// Permutation provides the necessary state information and functions
/// to create the permutation polynomial. In the literature, Z(X) is the "accumulator",
/// this is what this codebase calls the permutation polynomial.  
#[derive(Debug)]
pub struct Permutation {
    // Maps a variable to the wires that it is associated to
    pub(crate) variable_map: HashMap,
}
/// The value is a reference to the actual value that was added to the constraint system
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
pub struct Variable(pub(crate) usize);

impl Into for Variable {
    fn into(self) -> (BlsScalar, Variable) {
        (BlsScalar::one(), self)
    }
}

/// Stores the data for a specific wire in an arithmetic circuit
/// This data is the gate index and the type of wire
/// Left(1) signifies that this wire belongs to the first gate and is the left wire
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum WireData {
    /// Left Wire of n'th gate
    Left(usize),
    /// Right Wire of n'th gate
    Right(usize),
    /// Output Wire of n'th gate
    Output(usize),
    /// Fourth Wire of n'th gate
    Fourth(usize),
}

假设总共有 n n n个gate,令 ω \omega ω为 n n n-th root of unity,即在scalar 域内,满足 ω n = 1 \omega^n=1 ωn=1。 令 H = { 1 , ω , ⋯   , ω n − 1 } H=\{1,\omega,\cdots,\omega^{n-1}\} H={1,ω,⋯,ωn−1},取 k 1 , k 2 , k 3 ∈ F k_1,k_2,k_3\in\mathbb{F} k1​,k2​,k3​∈F,满足 H , k 1 ⋅ H , k 2 ⋅ H , k 3 ⋅ H H,k_1\cdot H,k_2\cdot H, k_3\cdot H H,k1​⋅H,k2​⋅H,k3​⋅H 为distinct cosets of H H H in F ∗ \mathbb{F}^* F∗。

/// Constants used in the permutation argument to ensure that the wire subsets are disjoint.
pub(crate) const K1: BlsScalar = BlsScalar::from_raw([7, 0, 0, 0]);
pub(crate) const K2: BlsScalar = BlsScalar::from_raw([13, 0, 0, 0]);
pub(crate) const K3: BlsScalar = BlsScalar::from_raw([17, 0, 0, 0]);

以 n = 4 n=4 n=4为例,具体见test_permutation_compute_sigmas_only_left_wires,采用lagrange插值,插值点为 ( 1 , ω , ⋯   , ω 3 ) (1,\omega,\cdots,\omega^3) (1,ω,⋯,ω3),使得:

  • 对于Left wire有: L 0 = L ( 1 ) = 1 , L 1 = L ( ω ) = ω , L 2 = L ( ω 2 ) = ω 2 , L 3 = L ( ω 3 ) = ω 3 L_0=L(1)=1,L_1=L(\omega)=\omega,L_2=L(\omega^2)=\omega^2,L_3=L(\omega^3)=\omega^3 L0​=L(1)=1,L1​=L(ω)=ω,L2​=L(ω2)=ω2,L3​=L(ω3)=ω3。
  • 对于Right wire有: R 0 = R ( 1 ) = K 1 , R 1 = R ( ω ) = K 1 ω , R 2 = R ( ω 2 ) = K 1 ω 2 , R 3 = R ( ω 3 ) = K 1 ω 3 R_0=R(1)=K_1,R_1=R(\omega)=K_1\omega,R_2=R(\omega^2)=K_1\omega^2,R_3=R(\omega^3)=K_1\omega^3 R0​=R(1)=K1​,R1​=R(ω)=K1​ω,R2​=R(ω2)=K1​ω2,R3​=R(ω3)=K1​ω3。
  • 对于Output wire有: O 0 = O ( 1 ) = K 2 , O 1 = O ( ω ) = K 2 ω , O 2 = O ( ω 2 ) = K 2 ω 2 , O 3 = O ( ω 3 ) = K 2 ω 3 O_0=O(1)=K_2,O_1=O(\omega)=K_2\omega,O_2=O(\omega^2)=K_2\omega^2,O_3=O(\omega^3)=K_2\omega^3 O0​=O(1)=K2​,O1​=O(ω)=K2​ω,O2​=O(ω2)=K2​ω2,O3​=O(ω3)=K2​ω3。
  • 对于Fourth wire有: F 0 = F ( 1 ) = K 3 , F 1 = F ( ω ) = K 3 ω , F 2 = F ( ω 2 ) = K 3 ω 2 , F 3 = F ( ω 3 ) = K 3 ω 3 F_0=F(1)=K_3,F_1=F(\omega)=K_3\omega,F_2=F(\omega^2)=K_3\omega^2,F_3=F(\omega^3)=K_3\omega^3 F0​=F(1)=K3​,F1​=F(ω)=K3​ω,F2​=F(ω2)=K3​ω2,F3​=F(ω3)=K3​ω3。

permutation是根据实际各个wire与Variable之间的逻辑关系,进行了调整后再插值:

		let num_wire_mappings = 4;
		// !此处即为实际各个wire与Variable之间的逻辑关系!
        // Add four wire mappings
        perm.add_variables_to_map(var_zero, var_zero, var_five, var_nine, 0);
        perm.add_variables_to_map(var_zero, var_two, var_six, var_nine, 1);
        perm.add_variables_to_map(var_zero, var_three, var_seven, var_nine, 2);
        perm.add_variables_to_map(var_zero, var_four, var_eight, var_nine, 3);

        /*
        var_zero = {L0, R0, L1, L2, L3}
        var_two = {R1}
        var_three = {R2}
        var_four = {R3}
        var_five = {O0}
        var_six = {O1}
        var_seven = {O2}
        var_eight = {O3}
        var_nine = {F0, F1, F2, F3}
        Left_sigma = {R0, L2, L3, L0}
        Right_sigma = {L1, R1, R2, R3}
        Out_sigma = {O0, O1, O2, O3}
        Fourth_sigma = {F1, F2, F3, F0}
        */
        let sigmas = perm.compute_sigma_permutations(num_wire_mappings);
        let left_sigma = &sigmas[0];
        let right_sigma = &sigmas[1];
        let out_sigma = &sigmas[2];
        let fourth_sigma = &sigmas[3];

        // Check the left sigma polynomial
        assert_eq!(left_sigma[0], WireData::Right(0));
        assert_eq!(left_sigma[1], WireData::Left(2));
        assert_eq!(left_sigma[2], WireData::Left(3));
        assert_eq!(left_sigma[3], WireData::Left(0));

        // Check the right sigma polynomial
        assert_eq!(right_sigma[0], WireData::Left(1));
        assert_eq!(right_sigma[1], WireData::Right(1));
        assert_eq!(right_sigma[2], WireData::Right(2));
        assert_eq!(right_sigma[3], WireData::Right(3));

        // Check the output sigma polynomial
        assert_eq!(out_sigma[0], WireData::Output(0));
        assert_eq!(out_sigma[1], WireData::Output(1));
        assert_eq!(out_sigma[2], WireData::Output(2));
        assert_eq!(out_sigma[3], WireData::Output(3));

        // Check the output sigma polynomial
        assert_eq!(fourth_sigma[0], WireData::Fourth(1));
        assert_eq!(fourth_sigma[1], WireData::Fourth(2));
        assert_eq!(fourth_sigma[2], WireData::Fourth(3));
        assert_eq!(fourth_sigma[3], WireData::Fourth(0));

        let domain = EvaluationDomain::new(num_wire_mappings).unwrap();
        let w: Fr = domain.group_gen;
        let w_squared = w.pow(&[2, 0, 0, 0]);
        let w_cubed = w.pow(&[3, 0, 0, 0]);

        // Check the left sigmas have been encoded properly
        // Left_sigma = {R0, L2, L3, L0}
        // Should turn into {1 * K1, w^2, w^3, 1}
        let encoded_left_sigma = perm.compute_permutation_lagrange(left_sigma, &domain);
        assert_eq!(encoded_left_sigma[0], Fr::one() * &K1);
        assert_eq!(encoded_left_sigma[1], w_squared);
        assert_eq!(encoded_left_sigma[2], w_cubed);
        assert_eq!(encoded_left_sigma[3], Fr::one());

        // Check the right sigmas have been encoded properly
        // Right_sigma = {L1, R1, R2, R3}
        // Should turn into {w, w * K1, w^2 * K1, w^3 * K1}
        let encoded_right_sigma = perm.compute_permutation_lagrange(right_sigma, &domain);
        assert_eq!(encoded_right_sigma[0], w);
        assert_eq!(encoded_right_sigma[1], w * &K1);
        assert_eq!(encoded_right_sigma[2], w_squared * &K1);
        assert_eq!(encoded_right_sigma[3], w_cubed * &K1);

        // Check the output sigmas have been encoded properly
        // Out_sigma = {O0, O1, O2, O3}
        // Should turn into {1 * K2, w * K2, w^2 * K2, w^3 * K2}
        let encoded_output_sigma = perm.compute_permutation_lagrange(out_sigma, &domain);
        assert_eq!(encoded_output_sigma[0], Fr::one() * &K2);
        assert_eq!(encoded_output_sigma[1], w * &K2);
        assert_eq!(encoded_output_sigma[2], w_squared * &K2);
        assert_eq!(encoded_output_sigma[3], w_cubed * &K2);

        // Check the fourth sigmas have been encoded properly
        // Out_sigma = {F1, F2, F3, F0}
        // Should turn into {w * K3, w^2 * K3, w^3 * K3, 1 * K3}
        let encoded_fourth_sigma = perm.compute_permutation_lagrange(fourth_sigma, &domain);
        assert_eq!(encoded_fourth_sigma[0], w * &K3);
        assert_eq!(encoded_fourth_sigma[1], w_squared * &K3);
        assert_eq!(encoded_fourth_sigma[2], w_cubed * &K3);
        assert_eq!(encoded_fourth_sigma[3], K3);
	fn compute_permutation_lagrange(
        &self,
        sigma_mapping: &[WireData],
        domain: &EvaluationDomain,
    ) -> Vec {
        let roots: Vec = domain.elements().collect();

        let lagrange_poly: Vec = sigma_mapping
            .iter()
            .map(|x| match x {
                WireData::Left(index) => {
                    let root = &roots[*index];
                    *root
                }
                WireData::Right(index) => {
                    let root = &roots[*index];
                    K1 * root
                }
                WireData::Output(index) => {
                    let root = &roots[*index];
                    K2 * root
                }
                WireData::Fourth(index) => {
                    let root = &roots[*index];
                    K3 * root
                }
            })
            .collect();

        lagrange_poly
    }

test_correct_permutation_poly中:【仍然以 n = 4 n=4 n=4为例, ω 4 = 1 \omega^4=1 ω4=1】

  • numerator_components为: ( n 0 , n 1 , n 2 , n 3 ) (n_0,n_1,n_2,n_3) (n0​,n1​,n2​,n3​)
  • denominator_components为: ( d 0 , d 1 , d 2 , d 3 ) (d_0,d_1,d_2,d_3) (d0​,d1​,d2​,d3​)
  • 根据permutation,有: n 0 n 1 n 2 n 3 d 0 d 1 d 2 d 3 = 1 \frac{n_0n_1n_2n_3}{d_0d_1d_2d_3}=1 d0​d1​d2​d3​n0​n1​n2​n3​​=1。
  • z_vec为: ( 1 , n 0 d 0 , n 0 n 1 d 0 d 1 , n 0 n 1 n 2 d 0 d 1 d 2 ) (1,\frac{n_0}{d_0},\frac{n_0n_1}{d_0d_1},\frac{n_0n_1n_2}{d_0d_1d_2}) (1,d0​n0​​,d0​d1​n0​n1​​,d0​d1​d2​n0​n1​n2​​)
  • z_poly多项式的点值表示为: ( ( 1 , 1 ) , ( ω , n 0 d 0 ) , ( ω 2 , n 0 n 1 d 0 d 1 ) , ( ω 3 , n 0 n 1 n 2 d 0 d 1 d 2 ) ) = ( ( 1 , Z ( 1 ) ) , ( ω , Z ( ω ) ) , ( ω 2 , Z ( ω 2 ) ) , ( ω 3 , Z ( ω 3 ) ) ) ((1,1),(\omega,\frac{n_0}{d_0}),(\omega^2,\frac{n_0n_1}{d_0d_1}),(\omega^3,\frac{n_0n_1n_2}{d_0d_1d_2}))=((1,Z(1)),(\omega,Z(\omega)),(\omega^2,Z(\omega^2)),(\omega^3,Z(\omega^3))) ((1,1),(ω,d0​n0​​),(ω2,d0​d1​n0​n1​​),(ω3,d0​d1​d2​n0​n1​n2​​))=((1,Z(1)),(ω,Z(ω)),(ω2,Z(ω2)),(ω3,Z(ω3)))。【compute_permutation_poly() 函数实际计算的即为z_poly多项式。】
  • 从而有 Z ( ω i ) Z ( ω i + 1 ) = d i n i \frac{Z(\omega^i)}{Z(\omega^{i+1})}=\frac{d_i}{n_i} Z(ωi+1)Z(ωi)​=ni​di​​,for 1 ≤ i < n 1\leq i BlsScalar::zero(), _ => unreachable!(), }) + qrange * (delta(c - four * d) + delta(b - four * c) + delta(a - four * b) + delta(d_next - four * a)); assert_eq!(k, BlsScalar::zero(), "Check failed at gate {}", i,);

    其中delta函数用于保证f值仅为0 或 1 或 2 或 3,使得最终delta函数的输出为0值:【原因在于logic_gate中是同时对两个bit进行and或xor操作。与ebfull/halo中的xor方案不同,ebfull中是对单个bit进行and或xor操作。】

    		// Computes f(f-1)(f-2)(f-3)
            let delta = |f: BlsScalar| -> BlsScalar {
                let f_1 = f - BlsScalar::one();
                let f_2 = f - BlsScalar::from(2);
                let f_3 = f - BlsScalar::from(3);
                f * f_1 * f_2 * f_3
            };
    

    具体的结构体设计为:

    /// A composer is a circuit builder
    /// and will dictate how a circuit is built
    /// We will have a default Composer called `StandardComposer`
    #[derive(Debug)]
    pub struct StandardComposer {
        // n represents the number of arithmetic gates in the circuit
        pub(crate) n: usize,
    
        // Selector vectors
        //
        // Multiplier selector
        pub(crate) q_m: Vec,
        // Left wire selector
        pub(crate) q_l: Vec,
        // Right wire selector
        pub(crate) q_r: Vec,
        // Output wire selector
        pub(crate) q_o: Vec,
        // Fourth wire selector
        pub(crate) q_4: Vec,
        // Constant wire selector
        pub(crate) q_c: Vec,
        // Arithmetic wire selector
        pub(crate) q_arith: Vec,
        // Range selector
        pub(crate) q_range: Vec,
        // Logic selector
        pub(crate) q_logic: Vec,
        // Fixed base group addition selector
        pub(crate) q_fixed_group_add: Vec,
        // Variable base group addition selector
        pub(crate) q_variable_group_add: Vec,
    
        /// Public inputs vector
        pub public_inputs: Vec,
    
        // Witness vectors
        pub(crate) w_l: Vec,
        pub(crate) w_r: Vec,
        pub(crate) w_o: Vec,
        pub(crate) w_4: Vec,
    
        /// A zero variable that is a part of the circuit description.
        /// We reserve a variable to be zero in the system
        /// This is so that when a gate only uses three wires, we set the fourth wire to be
        /// the variable that references zero
        pub(crate) zero_var: Variable,
    
        // These are the actual variable values
        // N.B. They should not be exposed to the end user once added into the composer
        pub(crate) variables: HashMap,
    
        pub(crate) perm: Permutation,
    }
    

    对于常规的3-wire门,表示为:【其中 p i pi pi 表示 public inputs。】

    	/// Adds a width-3 poly gate.
        /// This gate gives total freedom to the end user to implement the corresponding
        /// circuits in the most optimized way possible because the under has access to the
        /// whole set of variables, as well as selector coefficients that take part in the
        /// computation of the gate equation.
        ///
        /// The final constraint added will force the following:
        /// `(a * b) * q_m + a * q_l + b * q_r + q_c + PI + q_o * c = 0`.
        pub fn poly_gate(
            &mut self,
            a: Variable,
            b: Variable,
            c: Variable,
            q_m: BlsScalar,
            q_l: BlsScalar,
            q_r: BlsScalar,
            q_o: BlsScalar,
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> (Variable, Variable, Variable) {
            self.w_l.push(a);
            self.w_r.push(b);
            self.w_o.push(c);
            self.w_4.push(self.zero_var);
            self.q_l.push(q_l);
            self.q_r.push(q_r);
    
            // Add selector vectors
            self.q_m.push(q_m);
            self.q_o.push(q_o);
            self.q_c.push(q_c);
            self.q_4.push(BlsScalar::zero());
            self.q_arith.push(BlsScalar::one());
    
            self.q_range.push(BlsScalar::zero());
            self.q_logic.push(BlsScalar::zero());
            self.q_fixed_group_add.push(BlsScalar::zero());
            self.q_variable_group_add.push(BlsScalar::zero());
    
            self.public_inputs.push(pi);
    
            self.perm
                .add_variables_to_map(a, b, c, self.zero_var, self.n);
            self.n += 1;
    
            (a, b, c)
        }
    

    支持的constraint类型有:

    • 1)constant constraint
    • 2)equal constraint
    • 3)dummy constraint
    • 4)multiply constraint
    • 5)add constraint
    • 6)boolean constraint
    • 7)fixed group add constraint
    • 8)xor logic constraint
    • 9)and logic constraint
    • 10)range constraint
    5.1 constant constraint

    constant constraint:即某变量值等于某常数,如a变量满足 a - constant + pi = 0

    	/// Adds a gate which is designed to constrain a `Variable` to have
        /// a specific constant value which is sent as a `BlsScalar`.
        pub fn constrain_to_constant(&mut self, a: Variable, constant: BlsScalar, pi: BlsScalar) {
            self.poly_gate(
                a,
                a,
                a,
                BlsScalar::zero(),
                BlsScalar::one(),
                BlsScalar::zero(),
                BlsScalar::zero(),
                -constant,
                pi,
            );
        }
    

    constant constraint可用于约束circuit中的某个point为某个特定的public point:【验证public info】

    /// Represents a JubJub point in the circuit
    #[derive(Debug, Clone, Copy)]
    pub struct Point {
        x: Variable,
        y: Variable,
    }
    	/// Asserts that a point in the circuit is equal to a known public point
        pub fn assert_equal_public_point(
            &mut self,
            point: Point,
            public_point: dusk_jubjub::JubJubAffine,
        ) {
            self.constrain_to_constant(point.x, BlsScalar::zero(), -public_point.get_x());
            self.constrain_to_constant(point.y, BlsScalar::zero(), -public_point.get_y());
        }
    
    5.2 equal constraint

    equal constraint:即某两个变量相等,a = b

    	/// Asserts that two variables are the same
        // XXX: Instead of wasting a gate, we can use the permutation polynomial to do this
        pub fn assert_equal(&mut self, a: Variable, b: Variable) {
            self.poly_gate(
                a,
                b,
                self.zero_var,
                BlsScalar::zero(),
                BlsScalar::one(),
                -BlsScalar::one(),
                BlsScalar::zero(),
                BlsScalar::zero(),
                BlsScalar::zero(),
            );
        }
    

    equal constraint可用于约束circuit中的某两个point是相等的:【验证private info】

    	/// Asserts that a point in the circuit is equal to another point in the circuit
        pub fn assert_equal_point(&mut self, point_a: Point, point_b: Point) {
            self.assert_equal(point_a.x, point_b.x);
            self.assert_equal(point_b.y, point_b.y);
        }
    
    5.3 dummy constraint

    对于有 n n n个gate的circuit,其constraint数量为 n + 3 n+3 n+3,其中那3个为:

    • 1)默认设置circuit的第一个变量值为0。
    		// Reserve the first variable to be zero
            composer.zero_var = composer.add_witness_to_circuit_description(BlsScalar::zero());
    
    • 2)为witness polynomials的blinding属性引入了2个dummy constraint—— 一个constraint用于保证selector polynomials不全为 zero polynomial;另一个constraint用于保证permutation polynomial不为identity polynomial。 目前是通过dummy constraint的方式来为witness polynomials添加blinding factor:
    	/// This function is used to add a blinding factor to the witness polynomials
        /// XXX: Split this into two separate functions and document
        /// XXX: We could add another section to add random witness variables, with selector polynomials all zero
        pub fn add_dummy_constraints(&mut self) {
            // Add a dummy constraint so that we do not have zero polynomials
            self.q_m.push(BlsScalar::from(1));
            self.q_l.push(BlsScalar::from(2));
            self.q_r.push(BlsScalar::from(3));
            self.q_o.push(BlsScalar::from(4));
            self.q_c.push(BlsScalar::from(4));
            self.q_4.push(BlsScalar::one());
            self.q_arith.push(BlsScalar::one());
            self.q_range.push(BlsScalar::zero());
            self.q_logic.push(BlsScalar::zero());
            self.q_fixed_group_add.push(BlsScalar::zero());
            self.q_variable_group_add.push(BlsScalar::zero());
            self.public_inputs.push(BlsScalar::zero());
            let var_six = self.add_input(BlsScalar::from(6));
            let var_one = self.add_input(BlsScalar::from(1));
            let var_seven = self.add_input(BlsScalar::from(7));
            let var_min_twenty = self.add_input(-BlsScalar::from(20));
            self.w_l.push(var_six);
            self.w_r.push(var_seven);
            self.w_o.push(var_min_twenty);
            self.w_4.push(var_one);
            self.perm
                .add_variables_to_map(var_six, var_seven, var_min_twenty, var_one, self.n);
            self.n += 1;
            //Add another dummy constraint so that we do not get the identity permutation
            self.q_m.push(BlsScalar::from(1));
            self.q_l.push(BlsScalar::from(1));
            self.q_r.push(BlsScalar::from(1));
            self.q_o.push(BlsScalar::from(1));
            self.q_c.push(BlsScalar::from(127));
            self.q_4.push(BlsScalar::zero());
            self.q_arith.push(BlsScalar::one());
            self.q_range.push(BlsScalar::zero());
            self.q_logic.push(BlsScalar::zero());
            self.q_fixed_group_add.push(BlsScalar::zero());
            self.q_variable_group_add.push(BlsScalar::zero());
            self.public_inputs.push(BlsScalar::zero());
            self.w_l.push(var_min_twenty);
            self.w_r.push(var_six);
            self.w_o.push(var_seven);
            self.w_4.push(self.zero_var);
            self.perm
                .add_variables_to_map(var_min_twenty, var_six, var_seven, self.zero_var, self.n);
            self.n += 1;
        }
    

    即对于空circuit,默认3个constraint,相应的测试用例为:

    	#[test]
        /// Tests that a circuit initially has 3 gates
        fn test_initial_circuit_size() {
            let composer: StandardComposer = StandardComposer::new();
            // Circuit size is n+3 because
            // - We have an extra gate which forces the first witness to be zero. This is used when the advice wire is not being used.
            // - We have two gates which ensure that the permutation polynomial is not the identity and
            // - Another gate which ensures that the selector polynomials are not all zeroes
            assert_eq!(3, composer.circuit_size());
            composer.check_circuit_satisfied(); //打印调试信息。feature中增加"print-trace"
        }
    

    相应的circuit gate satisfied 打印信息为:

    --------------------------------------------
    
                #Gate Index = 0
                #Selector Polynomials:
    
                - qm -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - ql -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - qr -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q4 -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - qo -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - qc -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_arith -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - q_range -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_logic -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_fixed_group_add -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_variable_group_add -> 0000000000000000000000000000000000000000000000000000000000000000
    
                # Witness polynomials:
    
                - w_l -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - w_r -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - w_o -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - w_4 -> 0000000000000000000000000000000000000000000000000000000000000000
    
    --------------------------------------------
    
                #Gate Index = 1
                #Selector Polynomials:
    
                - qm -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - ql -> 0200000000000000000000000000000000000000000000000000000000000000
    
                - qr -> 0300000000000000000000000000000000000000000000000000000000000000
    
                - q4 -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - qo -> 0400000000000000000000000000000000000000000000000000000000000000
    
                - qc -> 0400000000000000000000000000000000000000000000000000000000000000
    
                - q_arith -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - q_range -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_logic -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_fixed_group_add -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_variable_group_add -> 0000000000000000000000000000000000000000000000000000000000000000
    
                # Witness polynomials:
    
                - w_l -> 0600000000000000000000000000000000000000000000000000000000000000
    
                - w_r -> 0700000000000000000000000000000000000000000000000000000000000000
    
                - w_o -> edfffffffefffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73
    
                - w_4 -> 0100000000000000000000000000000000000000000000000000000000000000
    
    --------------------------------------------
    
                #Gate Index = 2
                #Selector Polynomials:
    
                - qm -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - ql -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - qr -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - q4 -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - qo -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - qc -> 7f00000000000000000000000000000000000000000000000000000000000000
    
                - q_arith -> 0100000000000000000000000000000000000000000000000000000000000000
    
                - q_range -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_logic -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_fixed_group_add -> 0000000000000000000000000000000000000000000000000000000000000000
    
                - q_variable_group_add -> 0000000000000000000000000000000000000000000000000000000000000000
    
                # Witness polynomials:
    
                - w_l -> edfffffffefffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73
    
                - w_r -> 0600000000000000000000000000000000000000000000000000000000000000
    
                - w_o -> 0700000000000000000000000000000000000000000000000000000000000000
    
                - w_4 -> 0000000000000000000000000000000000000000000000000000000000000000
    
    
    5.4 multiply constraint

    支持left wire、right wire、output wire和fourth wire的加法门。 对于mul gate, q l , q r q_l,q_r ql​,qr​恒为0。

    	/// Adds a width-3 add gate to the circuit linking the product of the
        /// provided inputs scaled by the selector coefficient `q_m` with the output
        /// provided scaled by `q_o`.
        ///
        /// Note that this gate requires to provide the actual result of the gate
        /// (output wire) since it will just add a `mul constraint` to the circuit.
        pub fn mul_gate(
            &mut self,
            a: Variable,
            b: Variable,
            c: Variable,
            q_m: BlsScalar,
            q_o: BlsScalar,
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            self.big_mul_gate(a, b, c, None, q_m, q_o, q_c, BlsScalar::zero(), pi)
        }
    
        /// Adds a width-4 `big_mul_gate` with the left, right and fourth inputs
        /// and it's scaling factors, computing & returning the output (result)
        /// `Variable` and adding the corresponding mul constraint.
        ///
        /// This type of gate is usually used when we need to have
        /// the largest amount of performance and the minimum circuit-size
        /// possible. Since it allows the end-user to setup all of the selector
        /// coefficients.
        ///
        /// Forces `q_m * (w_l * w_r) + w_4 * q_4 + q_c + PI = q_o * w_o`.
        ///
        /// `{w_l, w_r, w_o, w_4} = {a, b, c, d}`
        // XXX: Maybe make these tuples instead of individual field?
        pub fn big_mul_gate(
            &mut self,
            a: Variable,
            b: Variable,
            c: Variable,
            d: Option,
            q_m: BlsScalar,
            q_o: BlsScalar,
            q_c: BlsScalar,
            q_4: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            // Check if advice wire has a value
            let d = match d {
                Some(var) => var,
                None => self.zero_var,
            };
    
            self.w_l.push(a);
            self.w_r.push(b);
            self.w_o.push(c);
            self.w_4.push(d);
    
            // For a mul gate q_L and q_R is zero
            self.q_l.push(BlsScalar::zero());
            self.q_r.push(BlsScalar::zero());
    
            // Add selector vectors
            self.q_m.push(q_m);
            self.q_o.push(q_o);
            self.q_c.push(q_c);
            self.q_4.push(q_4);
            self.q_arith.push(BlsScalar::one());
    
            self.q_range.push(BlsScalar::zero());
            self.q_logic.push(BlsScalar::zero());
            self.q_fixed_group_add.push(BlsScalar::zero());
            self.q_variable_group_add.push(BlsScalar::zero());
    
            self.public_inputs.push(pi);
    
            self.perm.add_variables_to_map(a, b, c, d, self.n);
    
            self.n += 1;
    
            c
        }
    

    以及

    	/// Adds a simple and basic addition to the circuit between to `Variable`s
        /// returning the resulting `Variable`.
        pub fn mul(
            &mut self,
            q_m: BlsScalar,
            a: Variable,
            b: Variable,
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            self.big_mul(q_m, a, b, None, q_c, pi)
        }
    
        /// Adds a width-4 `big_mul_gate` with the left, right and fourth inputs
        /// and it's scaling factors, computing & returning the output (result)
        /// `Variable` and adding the corresponding mul constraint.
        ///
        /// This type of gate is usually used when we don't need to have
        /// the largest amount of performance and the minimum circuit-size
        /// possible. Since it defaults some of the selector coeffs = 0 in order
        /// to reduce the verbosity and complexity.
        ///
        /// Forces `q_m * (w_l * w_r) + w_4 * q_4 + q_c + PI = w_o(computed by the gate)`.
        ///
        /// `{w_l, w_r, w_4} = {a, b, d}`
        // XXX: This API is not consistent. It should use tuples and not individual fields
        pub fn big_mul(
            &mut self,
            q_m: BlsScalar,
            a: Variable,
            b: Variable,
            q_4_d: Option,
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            let q_o = -BlsScalar::one();
    
            // Check if advice wire is available
            let (q_4, d) = match q_4_d {
                Some((q_4, var)) => (q_4, var),
                None => (BlsScalar::zero(), self.zero_var),
            };
    
            // Compute output wire
            let a_eval = self.variables[&a];
            let b_eval = self.variables[&b];
            let d_eval = self.variables[&d];
            let c_eval = (q_m * a_eval * b_eval) + (q_4 * d_eval) + q_c + pi;
            let c = self.add_input(c_eval);
    
            self.big_mul_gate(a, b, c, Some(d), q_m, q_o, q_c, q_4, pi)
        }
    
    5.5 add constraint

    支持left wire、right wire、output wire和fourth wire的加法门。 对于add gate, q m q_m qm​恒为0。

    	/// Adds a width-3 add gate to the circuit, linking the addition of the
        /// provided inputs, scaled by the selector coefficients with the output
        /// provided.
        pub fn add_gate(
            &mut self,
            a: Variable,
            b: Variable,
            c: Variable,
            q_l: BlsScalar,
            q_r: BlsScalar,
            q_o: BlsScalar,
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            self.big_add_gate(a, b, c, None, q_l, q_r, q_o, BlsScalar::zero(), q_c, pi)
        }
    
    	/// Adds a width-4 add gate to the circuit and it's corresponding
        /// constraint.
        ///
        /// This type of gate is usually used when we need to have
        /// the largest amount of performance and the minimum circuit-size
        /// possible. Since it allows the end-user to set every selector coefficient
        /// as scaling value on the gate eq.
        pub fn big_add_gate(
            &mut self,
            a: Variable,
            b: Variable,
            c: Variable,
            d: Option,
            q_l: BlsScalar,
            q_r: BlsScalar,
            q_o: BlsScalar,
            q_4: BlsScalar,
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            // Check if advice wire has a value
            let d = match d { //若为None,则解析为零变量。
                Some(var) => var,
                None => self.zero_var,
            };
    
            self.w_l.push(a);
            self.w_r.push(b);
            self.w_o.push(c);
            self.w_4.push(d);
    
            // For an add gate, q_m is zero
            self.q_m.push(BlsScalar::zero());
    
            // Add selector vectors
            self.q_l.push(q_l);
            self.q_r.push(q_r);
            self.q_o.push(q_o);
            self.q_c.push(q_c);
            self.q_4.push(q_4);
            self.q_arith.push(BlsScalar::one());
            self.q_range.push(BlsScalar::zero());
            self.q_logic.push(BlsScalar::zero());
            self.q_fixed_group_add.push(BlsScalar::zero());
            self.q_variable_group_add.push(BlsScalar::zero());
    
            self.public_inputs.push(pi);
    
            self.perm.add_variables_to_map(a, b, c, d, self.n);
    
            self.n += 1;
    
            c
        }
    

    以及

    	/// Adds a `big_addition_gate` with the left and right inputs
        /// and it's scaling factors, computing & returning the output (result)
        /// `Variable`, and adding the corresponding addition constraint.
        ///
        /// This type of gate is usually used when we don't need to have
        /// the largest amount of performance as well as the minimum circuit-size
        /// possible. Since it defaults some of the selector coeffs = 0 in order
        /// to reduce the verbosity and complexity.
        ///
        /// Forces `q_l * w_l + q_r * w_r + q_c + PI = w_o(computed by the gate)`.
        pub fn add(
            &mut self,
            q_l_a: (BlsScalar, Variable),
            q_r_b: (BlsScalar, Variable),
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            self.big_add(q_l_a, q_r_b, None, q_c, pi)
        }
    
        /// Adds a `big_addition_gate` with the left, right and fourth inputs
        /// and it's scaling factors, computing & returning the output (result)
        /// `Variable` and adding the corresponding addition constraint.
        ///
        /// This type of gate is usually used when we don't need to have
        /// the largest amount of performance and the minimum circuit-size
        /// possible. Since it defaults some of the selector coeffs = 0 in order
        /// to reduce the verbosity and complexity.
        ///
        /// Forces `q_l * w_l + q_r * w_r + q_4 * w_4 + q_c + PI = w_o(computed by the gate)`.
        pub fn big_add(
            &mut self,
            q_l_a: (BlsScalar, Variable),
            q_r_b: (BlsScalar, Variable),
            q_4_d: Option,
            q_c: BlsScalar,
            pi: BlsScalar,
        ) -> Variable {
            // Check if advice wire is available
            let (q_4, d) = match q_4_d {
                Some((q_4, var)) => (q_4, var),
                None => (BlsScalar::zero(), self.zero_var),
            };
    
            let (q_l, a) = q_l_a;
            let (q_r, b) = q_r_b;
    
            let q_o = -BlsScalar::one();
    
            // Compute the output wire
            let a_eval = self.variables[&a];
            let b_eval = self.variables[&b];
            let d_eval = self.variables[&d];
            let c_eval = (q_l * a_eval) + (q_r * b_eval) + (q_4 * d_eval) + q_c + pi;
            let c = self.add_input(c_eval);
    
            self.big_add_gate(a, b, c, Some(d), q_l, q_r, q_o, q_4, q_c, pi)
        }
    
    
    5.6 boolean constraint 又称为 binary constraint

    boolean constraint 又名 binary constraint,是指约束变量值要么为“0”,要么为“1”。 核心思想就是:若 a ( 1 − a ) = 0 a(1-a)=0 a(1−a)=0,即 a a a为0或1。

    	/// Adds a boolean constraint (also known as binary constraint) where
        /// the gate eq. will enforce that the `Variable` received is either `0`
        /// or `1` by adding a constraint in the circuit.
        ///
        /// Note that using this constraint with whatever `Variable` that is not
        /// representing a value equalling 0 or 1, will always force the equation to fail.
        pub fn boolean_gate(&mut self, a: Variable) -> Variable {
            self.w_l.push(a);
            self.w_r.push(a);
            self.w_o.push(a);
            self.w_4.push(self.zero_var);
    
            self.q_m.push(BlsScalar::one());
            self.q_l.push(BlsScalar::zero());
            self.q_r.push(BlsScalar::zero());
            self.q_o.push(-BlsScalar::one());
            self.q_c.push(BlsScalar::zero());
            self.q_4.push(BlsScalar::zero());
            self.q_arith.push(BlsScalar::one());
    
            self.q_range.push(BlsScalar::zero());
            self.q_logic.push(BlsScalar::zero());
            self.q_fixed_group_add.push(BlsScalar::zero());
            self.q_variable_group_add.push(BlsScalar::zero());
    
            self.public_inputs.push(BlsScalar::zero());
    
            self.perm
                .add_variables_to_map(a, a, a, self.zero_var, self.n);
    
            self.n += 1;
    
            a
        }
    
    5.7 fixed group add constraint
    #[derive(Debug, Clone, Copy)]
    /// Contains all of the components needed to verify that a bit scalar multiplication was computed correctly
    pub(crate) struct WnafRound {
        /// This is the accumulated x coordinate point that we wish to add (so far.. depends on where you are in the scalar mul)
        /// it is linked to the wnaf entry, so must not be revealed
        pub acc_x: Variable,
        /// This is the accumulated y coordinate
        pub acc_y: Variable,
    
        /// This is the wnaf accumulated entry
        /// For all intents and purposes, you can think of this as the secret bit
        pub accumulated_bit: Variable,
    
        /// This is the multiplication of x_\alpha * y_\alpha
        /// we need this as a distinct wire, so that the degree of the polynomial does not go over 4
        pub xy_alpha: Variable,
        /// This is the possible x co-ordinate of the wnaf point we are going to add
        /// Actual x-co-ordinate = b_i * x_\beta
        pub x_beta: BlsScalar,
        /// This is the possible y co-ordinate of the wnaf point we are going to add
        /// Actual y coordinate = (b_i)^2 [y_\beta -1] + 1
        pub y_beta: BlsScalar,
        /// This is the multiplication of x_\beta * y_\beta
        pub xy_beta: BlsScalar,
    }
    
    	/// Fixed group addition of a jubjub point
        pub(crate) fn fixed_group_add(&mut self, wnaf_round: WnafRound) {
            self.w_l.push(wnaf_round.acc_x);
            self.w_r.push(wnaf_round.acc_y);
            self.w_o.push(wnaf_round.xy_alpha);
            self.w_4.push(wnaf_round.accumulated_bit);
    
            self.q_l.push(wnaf_round.x_beta);
            self.q_r.push(wnaf_round.y_beta);
    
            self.q_c.push(wnaf_round.xy_beta);
            self.q_o.push(BlsScalar::zero());
            self.q_fixed_group_add.push(BlsScalar::one());
            self.q_variable_group_add.push(BlsScalar::zero());
    
            self.q_m.push(BlsScalar::zero());
            self.q_4.push(BlsScalar::zero());
            self.q_arith.push(BlsScalar::zero());
            self.q_range.push(BlsScalar::zero());
            self.q_logic.push(BlsScalar::zero());
    
            self.public_inputs.push(BlsScalar::zero());
    
            self.perm.add_variables_to_map(
                wnaf_round.acc_x,
                wnaf_round.acc_y,
                wnaf_round.xy_alpha,
                wnaf_round.accumulated_bit,
                self.n,
            );
    
            self.n += 1;
        }
    
    5.8 xor logic constraint

    logic_gate中是同时对两个bit进行and或xor操作。具体见https://github.com/dusk-network/plonk/blob/master/src/proof_system/widget/logic/proverkey.rscompute_quotient_i() 函数:

        pub(crate) fn compute_quotient_i(
            &self,
            index: usize,
            logic_separation_challenge: &BlsScalar,
            w_l_i: &BlsScalar,
            w_l_i_next: &BlsScalar,
            w_r_i: &BlsScalar,
            w_r_i_next: &BlsScalar,
            w_o_i: &BlsScalar,
            w_4_i: &BlsScalar,
            w_4_i_next: &BlsScalar,
        ) -> BlsScalar {
            let four = BlsScalar::from(4);
    
            let q_logic_i = &self.q_logic.1[index];
            let q_c_i = &self.q_c.1[index];
    
            let kappa = logic_separation_challenge.square();
            let kappa_sq = kappa.square();
            let kappa_cu = kappa_sq * kappa;
            let kappa_qu = kappa_cu * kappa;
    
            let a = w_l_i_next - four * w_l_i;
            let c_0 = delta(a);
    
            let b = w_r_i_next - four * w_r_i;
            let c_1 = delta(b) * kappa;
    
            let d = w_4_i_next - four * w_4_i;
            let c_2 = delta(d) * kappa_sq;
    
            let w = w_o_i;
            let c_3 = (w - a * b) * kappa_cu;
    
            let c_4 = delta_xor_and(&a, &b, w, &d, &q_c_i) * kappa_qu;
    
            q_logic_i * (c_3 + c_0 + c_1 + c_2 + c_4) * logic_separation_challenge
        }
    

    其中的:

    • c_0,c_1,c_2,c_3 constraint:是约束wire的相应取值仅允许为 [ 0 , 1 , 2 , 3 ] [0, 1, 2, 3] [0,1,2,3] 中之一。
    // Computes f(f-1)(f-2)(f-3)
    fn delta(f: BlsScalar) -> BlsScalar {
        let f_1 = f - BlsScalar::one();
        let f_2 = f - BlsScalar::from(2);
        let f_3 = f - BlsScalar::from(3);
        f * f_1 * f_2 * f_3
    }
    
    • c_4 constraint:
    // The identity we want to check is q_logic * A = 0
    // A = B + E
    // B = q_c * [9c - 3(a+b)]
    // E = 3(a+b+c) - 2F
    // F = w[w(4w - 18(a+b) + 81) + 18(a^2 + b^2) - 81(a+b) + 83]
    #[allow(non_snake_case)]
    fn delta_xor_and(
        a: &BlsScalar,
        b: &BlsScalar,
        w: &BlsScalar,
        c: &BlsScalar,
        q_c: &BlsScalar,
    ) -> BlsScalar {
        let nine = BlsScalar::from(9);
        let two = BlsScalar::from(2);
        let three = BlsScalar::from(3);
        let four = BlsScalar::from(4);
        let eighteen = BlsScalar::from(18);
        let eighty_one = BlsScalar::from(81);
        let eighty_three = BlsScalar::from(83);
    
        let F = w
            * (w * (four * w - eighteen * (a + b) + eighty_one) + eighteen * (a.square() + b.square())
                - eighty_one * (a + b)
                + eighty_three);
        let E = three * (a + b + c) - (two * F);
        let B = q_c * ((nine * c) - three * (a + b));
        B + E
    }
    
    	/// Adds a logical XOR gate that performs the XOR between two values for the
        /// specified first `num_bits` returning a `Variable` holding the result.
        ///
        /// # Panics
        ///
        /// If the `num_bits` specified in the fn params is odd.
        pub fn xor_gate(&mut self, a: Variable, b: Variable, num_bits: usize) -> Variable {
            self.logic_gate(a, b, num_bits, true)
        }
    
    
    5.9 and logic constraint

    logic_gate中是同时对两个bit进行and或xor操作。 and logic constraint 具体与 xor logic constraint 类似,主要差别在于:

    • w_o 表示的逻辑分别为and或xor的结果值。
    			// The `out_quad` is the result of the bitwise ops `&` or `^` between
                // the left and right quads. The op is decided with a boolean flag set
                // as input of the function.
                let out_quad_fr = match is_xor_gate {
                    true => BlsScalar::from((left_quad ^ right_quad) as u64),
                    false => BlsScalar::from((left_quad & right_quad) as u64),
                };
    
    • q_cq_logic的取值不同:
    			match is_xor_gate {
                    true => {
                        self.q_c.push(-BlsScalar::one());
                        self.q_logic.push(-BlsScalar::one());
                    }
                    false => {
                        self.q_c.push(BlsScalar::one());
                        self.q_logic.push(BlsScalar::one());
                    }
                };
    
    	/// Adds a logical AND gate that performs the bitwise AND between two values
        /// for the specified first `num_bits` returning a `Variable` holding the result.
        ///
        /// # Panics
        ///
        /// If the `num_bits` specified in the fn params is odd.
        pub fn and_gate(&mut self, a: Variable, b: Variable, num_bits: usize) -> Variable {
            self.logic_gate(a, b, num_bits, false)
        }
    
    5.10 range constraint

    具体见https://github.com/dusk-network/plonk/blob/master/src/constraint_system/range.rspub fn range_gate(&mut self, witness: Variable, num_bits: usize) 函数,对应的constraints数量为: num_bits/8+1

    以32bit为例,相应的constraint数量为32/8+1=5

    6. proof system

    实际代码中,设计了六种不同类型的gate,分别为:

    • arithmetic gate
    • logic gate
    • range gate
    • ecc fixed base curve addition gate
    • ecc variable base curve addition gate
    • permutation check
    6.1 preprocess
    • 借助pad函数,通过附加零变量和零值的方式,使得circuit的constraint数量为power of two。
    6.2 Prover端
    /// Prover composes a circuit and builds a proof
    #[allow(missing_debug_implementations)]
    pub struct Prover {
        /// ProverKey which is used to create proofs about a specific PLONK circuit
        pub prover_key: Option,
    
        pub(crate) cs: StandardComposer,
        /// Store the messages exchanged during the preprocessing stage
        /// This is copied each time, we make a proof
        pub preprocessed_transcript: Transcript,
    }
    
    /// PLONK circuit proving key
    #[derive(Debug, PartialEq, Eq, Clone)]
    pub struct ProverKey {
        /// Circuit size
        pub n: usize,
        /// ProverKey for arithmetic gate
        pub arithmetic: arithmetic::ProverKey,
        /// ProverKey for logic gate
        pub logic: logic::ProverKey,
        /// ProverKey for range gate
        pub range: range::ProverKey,
        /// ProverKey for fixed base curve addition gates
        pub fixed_base: ecc::scalar_mul::fixed_base::ProverKey,
        /// ProverKey for permutation checks
        pub permutation: permutation::ProverKey,
        /// ProverKey for variable base curve addition gates
        pub variable_base: ecc::curve_addition::ProverKey,
        // Pre-processes the 4n Evaluations for the vanishing polynomial, so they do not
        // need to be computed at the proving stage.
        // Note: With this, we can combine all parts of the quotient polynomial in their evaluation phase and
        // divide by the quotient polynomial without having to perform IFFT
        pub(crate) v_h_coset_4n: Evaluations,
    }
    

    实际实现时,根据circuit中gate类型分类不同,分别实现了不同的ProverKey

    • 1)对于 arithmetic gate,有:
    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct ProverKey {
        pub q_m: (Polynomial, Evaluations),
        pub q_l: (Polynomial, Evaluations),
        pub q_r: (Polynomial, Evaluations),
        pub q_o: (Polynomial, Evaluations),
        pub q_c: (Polynomial, Evaluations),
        pub q_4: (Polynomial, Evaluations),
        pub q_arith: (Polynomial, Evaluations),
    }
    

    Round 3 第三轮 quotient polynomial 计算对应表示为:

    (a(x)b(x)q_M(x) + a(x)q_L(x) + b(X)q_R(x) + c(X)q_O(X) + d(x)q_4(X) + Q_C(X)) * Q_Arith(X)
    

    Round 4 第四轮 linearisation polynomial 计算对应表示为:

     (a_eval * b_eval * q_m_poly + a_eval * q_l + b_eval * q_r + c_eval * q_o + d_eval * q_4 + q_c) * q_arith_eval
    
    • 2)对于 logic gate,有: 【 logic gate详细设计思想参见 AztecProtocol中代码实现:
    /*
     * Hoo boy, AND and XOR polynomials!
     * This transition constraint evaluates either an AND or an XOR relationship (but not an or in sight) between the
     *accumulating sums of three base-4 variables...
     *
     * Ok, so we want to evaluate a ^ b = c OR a & b = c . We can create a | b from a | b = (a ^ b) + (a & b)
     *
     * We also want the output memory cell to represent the actual result of the AND / XOR operation,
     * instead of a collection of bits / quads that need to be summed together. Who has time for that?
     *
     * We can also be super sneaky and evaluate both AND and XOR operations with a single selector polynomial.
     *
     * Let's call this selector 'S', it takes values in { -1, 0, 1}
     *
     * If S = -1, we're evaluating a XOR op
     * If S = 1, we're evaluating an AND op
     * If S = 0, we're evaluating nothing! This constraint is turned off
     *
     * We use 3 columns of program memory to represent accumulating sums of a, b, c.
     *
     * For example, we can represent a 32-bit 'A' via its quads
     *
     *      15
     *      ===
     *      \          i
     * A =  /    a  . 4
     *      ===   i
     *     i = 0
     *
     * In program memory, we place an accumulating base-4 sum of A {A_0, ..., A_15}, where
     *
     *         i
     *        ===
     *        \                  j
     * A   =  /    a         .  4
     *  i     ===   (15 - j)
     *       j = 0
     *
     *
     * From this, we can extract a quad by validating that
     *
     *
     *  A      - 4 . A  ϵ [0, 1, 2, 3]
     *   i + 1        i
     *
     * Once we have validated the above, we can then extract an accumulator's implicit quad via:
     *
     *  a  =  A      - 4 . A  ϵ [0, 1, 2, 3]
     *   i     i + 1        i
     *
     *
     * But of course it's not so simple! An AND/XOR polynomial identity with two input quads (plus selector) has a degree
     *of 8. To constrain the degree of our quotient polynomial T(X) we want our identity to have a degree of 5
     *
     * We also have a spare column to work with, which we can use to store
     *
     *
     *  w = a  * b
     *       i    i
     *
     * For the polynomial identity, we use the following notation:
     *
     *  (1) 'a' is the current round quad attributed to our operand a
     *  (2) 'b' is the current round quad attributed to our operand b
     *  (3) 'c' is the current round quad attributed to our output c
     *  (4) 'w' = a * b
     *  (5) 's' is the AND/XOR selector polynomial round value.
     *
     * The polynomial identity we're evaluating is... wait for it...
     *
     *                                                                                             2    2
     * s ⋅ (s ⋅ (9 ⋅ c - 3 ⋅ (a + b)) + 3 ⋅ (c + a + b) + w ⋅ (w ⋅ (4 ⋅ w - 18 ⋅ (a + b) + 81) + 18 ⋅ (a  + b ) - 81 ⋅ (a +
     *b) + 83))
     *
     * =
     *
     * 0 mod Z_H
     *
     * To simplify things, we *could* frankenstein integers out of the 4th roots of unity to make this simpler,
     * but then integer multiplication would be horrible.
     * So really, it's a question of picking ones poison, and blaming the Babylonians
     * for creating their number system out of the integers instead of a nice cyclic group.
     *
     * In addition to this nonsense, we also need to verify the following:
     *
     *  (1) a is in the set { 0, 1, 2, 3 }
     *  (2) b is in the set { 0, 1, 2, 3 }
     *  (3) c is in the set { 0, 1, 2, 3 }
     *  (4) w = a * c
     *
     *
     * We place our accumulating sums (A, B, C) in program memory in the following sequence:
     *
     *                  +-----+-----+-----+-----+
     *                  |  1  |  2  |  3  |  4  |
     *                  +-----+-----+-----+-----+
     * you are here --> | 0   | 0   | w1  | 0   |
     *                  | A1  | B1  | w2  | C1  |
     *                  | A2  | B2  | w3  | C2  |
     *                  | ... | ... | ... | ... |
     *                  | An  | Bn  | --- | Cn  | --> exit
     *                  +-----+-----+-----+-----+
     *
     *
     **/
    

    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct ProverKey {
        pub q_c: (Polynomial, Evaluations),
        pub q_logic: (Polynomial, Evaluations),
    }
    

    logic gate的逻辑为q_logic=1表示为two bits and gate,q_logic=-1表示为two bits xor gate。

    • 对于 two bits and gate,有q_c=1c=a&b
    • 对于 two bits xor gate,有q_c=-1c=a^b

    Round 3 第三轮 quotient polynomial 计算对应表示为:【w=a*b

    // The identity we want to check is q_logic * A = 0
    // A = B + E
    // B = q_c * [9c - 3(a+b)]
    // E = 3(a+b+c) - 2F
    // F = w[w(4w - 18(a+b) + 81) + 18(a^2 + b^2) - 81(a+b) + 83]
    

    Round 4 第四轮 linearisation polynomial 计算对应表示为:【相应的evaluation * q_logic_poly

    
    
    • 3)对于 range gate,有: 【 range gate 详细设计思想参见 AztecProtocol中代码实现:
    /*
     * The range constraint accumulates base 4 values into a sum.
     * We do this by evaluating a kind of 'raster scan', where we compare adjacent elements
     * and validate that their differences map to a base for value  *
     * Let's say that we want to perform a 32-bit range constraint in 'x'.
     * We can represent x via 16 constituent base-4 'quads' {q_0, ..., q_15}:
     *
     *      15
     *      ===
     *      \          i
     * x =  /    q  . 4
     *      ===   i
     *     i = 0
     *
     * In program memory, we place an accumulating base-4 sum of x {a_0, ..., a_15}, where
     *
     *         i
     *        ===
     *        \                  j
     * a   =  /    q         .  4
     *  i     ===   (15 - j)
     *       j = 0
     *
     *
     * From this, we can use our range transition constraint to validate that
     *
     *
     *  a      - 4 . a  ϵ [0, 1, 2, 3]
     *   i + 1        i
     *
     *
     * We place our accumulating sums in program memory in the following sequence:
     *
     * +-----+-----+-----+-----+
     * |  A  |  B  |  C  |  D  |
     * +-----+-----+-----+-----+
     * | a3  | a2  | a1  | 0   |
     * | a7  | a6  | a5  | a4  |
     * | a11 | a10 | a9  | a8  |
     * | a15 | a14 | a13 | a12 |
     * | --- | --- | --- | a16 |
     * +-----+-----+-----+-----+
     *
     * Our range transition constraint on row 'i'
     * performs our base-4 range check on the follwing pairs:
     *
     * (D_{i}, C_{i}), (C_{i}, B_{i}), (B_{i}, A_{i}), (A_{i}, D_{i+1})
     *
     * We need to start our raster scan at zero, so we simplify matters and just force the first value
     * to be zero.
     *
     * The output will be in the 4th column of an otherwise unused row. Assuming this row can
     * be used for a width-3 standard gate, the total number of gates for an n-bit range constraint
     * is (n / 8) gates
     *
     **/
    

    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct ProverKey {
        pub q_range: (Polynomial, Evaluations),
    }
    

    Round 3 第三轮 quotient polynomial 计算对应表示为:

     Delta([c(X) - 4 * d(X)]) + Delta([b(X) - 4 * c(X)]) + Delta([a(X) - 4 * b(X)]) + Delta([d(Xg) - 4 * a(X)]) * Q_Range(X)
    

    Round 4 第四轮 linearisation polynomial 计算对应表示为:

    Delta([c_eval - 4 * d_eval]) + Delta([b_eval - 4 * c_eval]) + Delta([a_eval - 4 * b_eval]) + Delta([d_next_eval - 4 * a_eval]) * Q_Range(X)
    
    • 4)对于 ecc fixed base curve addition gate,有:
    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct ProverKey {
        pub q_l: (Polynomial, Evaluations),
        pub q_r: (Polynomial, Evaluations),
        pub q_c: (Polynomial, Evaluations),
        pub q_fixed_group_add: (Polynomial, Evaluations),
    }
    

    Round 3 第三轮 quotient polynomial 计算对应表示为:

    
    

    Round 4 第四轮 linearisation polynomial 计算对应表示为:

    
    
    • 5)对于 ecc variable base curve addition gate,有:
    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct ProverKey {
        pub q_variable_group_add: (Polynomial, Evaluations),
    }
    

    Round 3 第三轮 quotient polynomial 计算对应表示为:

    
    

    Round 4 第四轮 linearisation polynomial 计算对应表示为:

    
    
    • 6)对于permutation check,主要用于验证circuit中wire之间的约束关系,有:
    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct ProverKey {
        pub left_sigma: (Polynomial, Evaluations),
        pub right_sigma: (Polynomial, Evaluations),
        pub out_sigma: (Polynomial, Evaluations),
        pub fourth_sigma: (Polynomial, Evaluations),
        pub linear_evaluations: Evaluations, // Evaluations of f(x) = X [XXX: Remove this and benchmark if it makes a considerable difference -- These are just the domain elements]
    }
    

    Round 3 第三轮 quotient polynomial 计算对应表示为:

    // 分子
    // (a(x) + beta * X + gamma) (b(X) + beta * k1 * X + gamma) (c(X) + beta * k2 * X + gamma)(d(X) + beta * k3 * X + gamma)z(X) * alpha
    
    // 分母
    // (a(x) + beta* Sigma1(X) + gamma) (b(X) + beta * Sigma2(X) + gamma) (c(X) + beta * Sigma3(X) + gamma)(d(X) + beta * Sigma4(X) + gamma) Z(X.omega) * alpha
    
    // 常量
    // L_1(X)[Z(X) - 1]
    

    Round 4 第四轮 linearisation polynomial 计算对应表示为:

    // 分子
    // (a_eval + beta * z_challenge + gamma)(b_eval + beta * K1 * z_challenge + gamma)(c_eval + beta * K2 * z_challenge + gamma) * alpha* z(X)
    
    // 分母
    // -(a_eval + beta * sigma_1 + gamma)(b_eval + beta * sigma_2 + gamma) (c_eval + beta * sigma_3 + gamma) * beta *z_eval * alpha^2 * Sigma_4(X)
    
    // 常量
    // Evaluate l_1(z)
    
    6.3 Verifier端
    /// Verifier verifies a proof
    #[allow(missing_debug_implementations)]
    pub struct Verifier {
        /// VerificationKey which is used to verify a specific PLONK circuit
        pub verifier_key: Option,
    
        pub(crate) cs: StandardComposer,
        /// Store the messages exchanged during the preprocessing stage
        /// This is copied each time, we make a proof, so that we can use the same verifier to
        /// Verify multiple proofs from the same circuit. If this is not copied, then the verification procedure will modify
        /// the transcript, making it unusable for future proofs.
        pub preprocessed_transcript: Transcript,
    }
    
    /// PLONK circuit verification key
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct VerifierKey {
        /// Circuit size
        pub n: usize,
        /// VerifierKey for arithmetic gates
        pub arithmetic: arithmetic::VerifierKey,
        /// VerifierKey for logic gates
        pub logic: logic::VerifierKey,
        /// VerifierKey for range gates
        pub range: range::VerifierKey,
        /// VerifierKey for fixed base curve addition gates
        pub fixed_base: ecc::scalar_mul::fixed_base::VerifierKey,
        /// VerifierKey for variable base curve addition gates
        pub variable_base: ecc::curve_addition::VerifierKey,
        /// VerifierKey for permutation checks
        pub permutation: permutation::VerifierKey,
    }
    

    实际实现时,根据circuit中gate类型分类不同,分别实现了不同的VerifierKey

    • 1)对于 arithmetic gate,有:
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct VerifierKey {
        pub q_m: Commitment,
        pub q_l: Commitment,
        pub q_r: Commitment,
        pub q_o: Commitment,
        pub q_c: Commitment,
        pub q_4: Commitment,
        pub q_arith: Commitment,
    }
    
    • 2)对于 logic gate,有:
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct VerifierKey {
        pub q_c: Commitment,
        pub q_logic: Commitment,
    }
    
    • 3)对于 range gate,有:
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct VerifierKey {
        pub q_range: Commitment,
    }
    
    • 4)对于 ecc fixed base curve addition gate,有:
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct VerifierKey {
        pub q_l: Commitment,
        pub q_r: Commitment,
        pub q_fixed_group_add: Commitment,
    }
    
    • 5)对于 ecc variable base curve addition gate,有:
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct VerifierKey {
        pub q_variable_group_add: Commitment,
    }
    
    • 6)对于permutation check,主要用于验证circuit中wire之间的约束关系,有:
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct VerifierKey {
        pub left_sigma: Commitment,
        pub right_sigma: Commitment,
        pub out_sigma: Commitment,
        pub fourth_sigma: Commitment,
    }
    
    6.4 proof

    proof的组成有:【根本目的是使得Verifier可 verify() 通过。】

    • witness polynomials 的commitments 及 相应的evaluations;
    • permutation polynomials 的commitments 及 相应的evaluations;
    • quotient polynomials 的commitments 及 相应的evaluations;
    • shifted polynomials 的commitments 及 相应的evaluations;
    • opening polynomials 的commitments 及 相应的evaluations。
    /// A Proof is a composition of `Commitments` to the witness, permutation,
    /// quotient, shifted and opening polynomials as well as the
    /// `ProofEvaluations`.
    ///
    /// It's main goal is to have a `verify()` method attached which contains the
    /// logic of the operations that the `Verifier` will need to do in order to
    /// formally verify the `Proof`.
    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct Proof {
        /// Commitment to the witness polynomial for the left wires.
        pub a_comm: Commitment,
        /// Commitment to the witness polynomial for the right wires.
        pub b_comm: Commitment,
        /// Commitment to the witness polynomial for the output wires.
        pub c_comm: Commitment,
        /// Commitment to the witness polynomial for the fourth wires.
        pub d_comm: Commitment,
    
        /// Commitment to the permutation polynomial.
        pub z_comm: Commitment,
    
        /// Commitment to the quotient polynomial.
        pub t_1_comm: Commitment,
        /// Commitment to the quotient polynomial.
        pub t_2_comm: Commitment,
        /// Commitment to the quotient polynomial.
        pub t_3_comm: Commitment,
        /// Commitment to the quotient polynomial.
        pub t_4_comm: Commitment,
    
        /// Commitment to the opening polynomial.
        pub w_z_comm: Commitment,
        /// Commitment to the shifted opening polynomial.
        pub w_zw_comm: Commitment,
        /// Subset of all of the evaluations added to the proof.
        pub evaluations: ProofEvaluations,
    }
    
    /// Proof Evaluations is a subset of all of the evaluations. These evaluations will be added to the proof
    #[derive(Debug, Eq, PartialEq, Clone)]
    pub struct ProofEvaluations {
        // Evaluation of the witness polynomial for the left wire at `z`
        pub a_eval: BlsScalar,
        // Evaluation of the witness polynomial for the right wire at `z`
        pub b_eval: BlsScalar,
        // Evaluation of the witness polynomial for the output wire at `z`
        pub c_eval: BlsScalar,
        // Evaluation of the witness polynomial for the fourth wire at `z`
        pub d_eval: BlsScalar,
        //
        pub a_next_eval: BlsScalar,
        //
        pub b_next_eval: BlsScalar,
        // Evaluation of the witness polynomial for the fourth wire at `z * root of unity`
        pub d_next_eval: BlsScalar,
        // Evaluation of the arithmetic selector polynomial at `z`
        pub q_arith_eval: BlsScalar,
        //
        pub q_c_eval: BlsScalar,
        //
        pub q_l_eval: BlsScalar,
        //
        pub q_r_eval: BlsScalar,
        // Evaluation of the left sigma polynomial at `z`
        pub left_sigma_eval: BlsScalar,
        // Evaluation of the right sigma polynomial at `z`
        pub right_sigma_eval: BlsScalar,
        // Evaluation of the out sigma polynomial at `z`
        pub out_sigma_eval: BlsScalar,
    
        // Evaluation of the linearisation sigma polynomial at `z`
        pub lin_poly_eval: BlsScalar,
    
        // (Shifted) Evaluation of the permutation polynomial at `z * root of unity`
        pub perm_eval: BlsScalar,
    }
    
关注
打赏
1664532908
查看更多评论
立即登录/注册

微信扫码登录

0.0871s