Hyrax: Doubly-efficient zkSNARKs without trusted setup代码解析

1. 引言

官方代码实现参见:(主要基于python和C++,且已经较长时间未更新了)
https://github.com/hyraxZK

微软研究中心Setty 2019年论文《Spartan: Efficient and general-purpose zkSNARKs without trusted setup》中,有复用Hyrax论文中的一些基础协议,并在 https://github.com/microsoft/Spartan 库中使用Rust语言进行了相应实现。

本文主要针对https://github.com/microsoft/Spartan 库中Hyrax相关协议实现进行代码解析。

2. 主要库依赖

  • curve25519-dalek:定位为中间层,不做任何协议实现,其中的Ristretto主要是将non-prime-order Edwards curve封装为prime-order group。curve25519-dalek提供了group operations API on the Edwards and Montgomery forms of Curve25519, and on the prime-order Ristretto group。 curve25519-dalek is intended to provide a clean and safe mid-level API for use implementing a wide range of ECC-based crypto protocols, such as key agreement, signatures, anonymous credentials, rangeproofs, and zero-knowledge proof systems. 代码库对应为:https://github.com/dalek-cryptography/curve25519-dalek。代码实现质量很高,有针对该库做过一些研究。(如 博客 dalek-Curve25519 avx2并行计算学习笔记 等等。)
    其中的features simd_backend表示引入了并行计算,运行效率最高。
# The SIMD backend uses parallel formulas, using either AVX2 or AVX512-IFMA.
simd_backend = ["nightly", "u64_backend", "packed_simd"]
  • merlin:为composable proof transcripts for public-coin arguments of knowledge。为STROBE-based transcript construction for zero-knowledge proofs。实现了Fiat-Shamir transform,使用merlin可将interactive protocol转换为non-interactive protocol。可参见博客 Merlin——零知识证明(1)理论篇

  • digest:为https://github.com/RustCrypto/traits中的digest算法。

  • rand:provides utilities to generate random numbers, to convert them to useful types and distributions, and some randomness-related algorithms.

  • sha3:为https://github.com/RustCrypto中的hashes算法,实现的是SHA-3 (Keccak) hash function。

  • byteorder:provides convenience methods for encoding and decoding numbers in either big-endian or little-endian order。

  • rayon:为data-parallelism Rust库。非常轻量,很容易convert a sequential computation into a parallel one。(具体可参加博客 Rayon: data parallelism in Rust)

  • serde:Serde is a framework for serializing and deserializing Rust data structures efficiently and generically。

  • bincode:A compact encoder / decoder pair that uses a binary zero-fluff encoding scheme. The size of the encoded object will be the same or smaller than the size that the object takes up in memory in a running Rust program.

  • subtle:用于实现常量时间执行密码学操作的库。详细参见博客 Rust crate subtle:用于实现常量时间执行密码学操作的库 和 rust Trait subtle::ConditionallySelectable常量时间执行及应用举例。

  • rand_core:intended for use when implementing the core trait, RngCore.

  • zeroize:Securely zero memory while avoiding compiler optimizations。可参见博客 Pointproofs 学习笔记3——代码解析 3.0.2节内容。

  • itertools:Extra iterator adaptors, iterator methods, free functions, and macros。

  • colored:The most simple way to add colors in your terminal。

  • flate2:基于DEFLATE的stream压缩/解压缩算法。

  • criterion:Statistics-driven Micro-benchmarking in Rust。Criterion.rs helps you write fast code by detecting and measuring performance improvements or regressions, even small ones, quickly and accurately. You can optimize with confidence, knowing how each change affects the performance of your code.
    criterion的主要特征有:
    – Statistics: Statistical analysis detects if, and by how much, performance has changed since the last benchmark run。
    – Charts:支持使用gnuplot来生成detailed graphs of benchmark results。
    – Stable-compatible:Benchmark your code without installing nightly Rust。

# 如果设置为false,`cargo test`将会忽略传递给rustc的--test参数。
harness = true

3. Proof of knowledge of opening

Hyrax: Doubly-efficient zkSNARKs without trusted setup代码解析_第1张图片

#[test]
  fn check_knowledgeproof() {
    let mut csprng: OsRng = OsRng;

    let gens_1 = MultiCommitGens::new(1, b"test-knowledgeproof");

    let x = Scalar::random(&mut csprng);
    let r = Scalar::random(&mut csprng);

    let mut random_tape = RandomTape::new(b"proof");
    let mut prover_transcript = Transcript::new(b"example");
    let (proof, committed_value) =
      KnowledgeProof::prove(&gens_1, &mut prover_transcript, &mut random_tape, &x, &r);

    let mut verifier_transcript = Transcript::new(b"example");
    assert!(proof
      .verify(&gens_1, &mut verifier_transcript, &committed_value)
      .is_ok());
  }
#[derive(Serialize, Deserialize, Debug)]
pub struct KnowledgeProof {
  alpha: CompressedGroup,
  z1: Scalar,
  z2: Scalar,
}

impl KnowledgeProof {
  fn protocol_name() -> &'static [u8] {
    b"knowledge proof"
  }

  pub fn prove(
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    random_tape: &mut RandomTape,
    x: &Scalar,
    r: &Scalar,
  ) -> (KnowledgeProof, CompressedGroup) {
    transcript.append_protocol_name(KnowledgeProof::protocol_name());

    // produce two random Scalars
    let t1 = random_tape.random_scalar(b"t1");
    let t2 = random_tape.random_scalar(b"t2");

    let C = x.commit(&r, gens_n).compress();
    C.append_to_transcript(b"C", transcript);

    let alpha = t1.commit(&t2, gens_n).compress();
    alpha.append_to_transcript(b"alpha", transcript);

    let c = transcript.challenge_scalar(b"c");

    let z1 = x * c + t1;
    let z2 = r * c + t2;

    (KnowledgeProof { alpha, z1, z2 }, C)
  }

  pub fn verify(
    &self,
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    C: &CompressedGroup,
  ) -> Result<(), ProofVerifyError> {
    transcript.append_protocol_name(KnowledgeProof::protocol_name());
    C.append_to_transcript(b"C", transcript);
    self.alpha.append_to_transcript(b"alpha", transcript);

    let c = transcript.challenge_scalar(b"c");

    let lhs = self.z1.commit(&self.z2, gens_n).compress();
    let rhs = (c * C.decompress().expect("Could not decompress C")
      + self
        .alpha
        .decompress()
        .expect("Could not decompress self.alpha"))
    .compress();

    if lhs == rhs {
      Ok(())
    } else {
      Err(ProofVerifyError)
    }
  }
}

4. proof of equality

Hyrax: Doubly-efficient zkSNARKs without trusted setup代码解析_第2张图片

#[test]
  fn check_equalityproof() {
    let mut csprng: OsRng = OsRng;

    let gens_1 = MultiCommitGens::new(1, b"test-equalityproof");
    let v1 = Scalar::random(&mut csprng);
    let v2 = v1;
    let s1 = Scalar::random(&mut csprng);
    let s2 = Scalar::random(&mut csprng);

    let mut random_tape = RandomTape::new(b"proof");
    let mut prover_transcript = Transcript::new(b"example");
    let (proof, C1, C2) = EqualityProof::prove(
      &gens_1,
      &mut prover_transcript,
      &mut random_tape,
      &v1,
      &s1,
      &v2,
      &s2,
    );

    let mut verifier_transcript = Transcript::new(b"example");
    assert!(proof
      .verify(&gens_1, &mut verifier_transcript, &C1, &C2)
      .is_ok());
  }
#[derive(Serialize, Deserialize, Debug)]
pub struct EqualityProof {
  alpha: CompressedGroup,
  z: Scalar,
}

impl EqualityProof {
  fn protocol_name() -> &'static [u8] {
    b"equality proof"
  }

  pub fn prove(
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    random_tape: &mut RandomTape,
    v1: &Scalar,
    s1: &Scalar,
    v2: &Scalar,
    s2: &Scalar,
  ) -> (EqualityProof, CompressedGroup, CompressedGroup) {
    transcript.append_protocol_name(EqualityProof::protocol_name());

    // produce a random Scalar
    let r = random_tape.random_scalar(b"r");

    let C1 = v1.commit(&s1, gens_n).compress();
    C1.append_to_transcript(b"C1", transcript);

    let C2 = v2.commit(&s2, gens_n).compress();
    C2.append_to_transcript(b"C2", transcript);

    let alpha = (r * gens_n.h).compress();
    alpha.append_to_transcript(b"alpha", transcript);

    let c = transcript.challenge_scalar(b"c");

    let z = c * (s1 - s2) + r;

    (EqualityProof { alpha, z }, C1, C2)
  }

  pub fn verify(
    &self,
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    C1: &CompressedGroup,
    C2: &CompressedGroup,
  ) -> Result<(), ProofVerifyError> {
    transcript.append_protocol_name(EqualityProof::protocol_name());
    C1.append_to_transcript(b"C1", transcript);
    C2.append_to_transcript(b"C2", transcript);
    self.alpha.append_to_transcript(b"alpha", transcript);

    let c = transcript.challenge_scalar(b"c");
    let rhs = {
      let C = C1.decompress().unwrap() - C2.decompress().unwrap();
      (c * C + self.alpha.decompress().unwrap()).compress()
    };

    let lhs = (self.z * gens_n.h).compress();

    if lhs == rhs {
      Ok(())
    } else {
      Err(ProofVerifyError)
    }
  }
}

5. proof of product

Hyrax: Doubly-efficient zkSNARKs without trusted setup代码解析_第3张图片

#[test]
  fn check_productproof() {
    let mut csprng: OsRng = OsRng;

    let gens_1 = MultiCommitGens::new(1, b"test-productproof");
    let x = Scalar::random(&mut csprng);
    let rX = Scalar::random(&mut csprng);
    let y = Scalar::random(&mut csprng);
    let rY = Scalar::random(&mut csprng);
    let z = x * y;
    let rZ = Scalar::random(&mut csprng);

    let mut random_tape = RandomTape::new(b"proof");
    let mut prover_transcript = Transcript::new(b"example");
    let (proof, X, Y, Z) = ProductProof::prove(
      &gens_1,
      &mut prover_transcript,
      &mut random_tape,
      &x,
      &rX,
      &y,
      &rY,
      &z,
      &rZ,
    );

    let mut verifier_transcript = Transcript::new(b"example");
    assert!(proof
      .verify(&gens_1, &mut verifier_transcript, &X, &Y, &Z)
      .is_ok());
  }
#[derive(Serialize, Deserialize, Debug)]
pub struct ProductProof {
  alpha: CompressedGroup,
  beta: CompressedGroup,
  delta: CompressedGroup,
  z: [Scalar; 5],
}

impl ProductProof {
  fn protocol_name() -> &'static [u8] {
    b"product proof"
  }

  pub fn prove(
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    random_tape: &mut RandomTape,
    x: &Scalar,
    rX: &Scalar,
    y: &Scalar,
    rY: &Scalar,
    z: &Scalar,
    rZ: &Scalar,
  ) -> (
    ProductProof,
    CompressedGroup,
    CompressedGroup,
    CompressedGroup,
  ) {
    transcript.append_protocol_name(ProductProof::protocol_name());

    // produce five random Scalar
    let b1 = random_tape.random_scalar(b"b1");
    let b2 = random_tape.random_scalar(b"b2");
    let b3 = random_tape.random_scalar(b"b3");
    let b4 = random_tape.random_scalar(b"b4");
    let b5 = random_tape.random_scalar(b"b5");

    let X = x.commit(&rX, gens_n).compress();
    X.append_to_transcript(b"X", transcript);

    let Y = y.commit(&rY, gens_n).compress();
    Y.append_to_transcript(b"Y", transcript);

    let Z = z.commit(&rZ, gens_n).compress();
    Z.append_to_transcript(b"Z", transcript);

    let alpha = b1.commit(&b2, gens_n).compress();
    alpha.append_to_transcript(b"alpha", transcript);

    let beta = b3.commit(&b4, gens_n).compress();
    beta.append_to_transcript(b"beta", transcript);

    let delta = {
      let gens_X = &MultiCommitGens {
        n: 1,
        G: vec![X.decompress().unwrap()],
        h: gens_n.h,
      };
      b3.commit(&b5, gens_X).compress()
    };
    delta.append_to_transcript(b"delta", transcript);

    let c = transcript.challenge_scalar(b"c");

    let z1 = b1 + c * x;
    let z2 = b2 + c * rX;
    let z3 = b3 + c * y;
    let z4 = b4 + c * rY;
    let z5 = b5 + c * (rZ - rX * y);
    let z = [z1, z2, z3, z4, z5];

    (
      ProductProof {
        alpha,
        beta,
        delta,
        z,
      },
      X,
      Y,
      Z,
    )
  }

  fn check_equality(
    P: &CompressedGroup,
    X: &CompressedGroup,
    c: &Scalar,
    gens_n: &MultiCommitGens,
    z1: &Scalar,
    z2: &Scalar,
  ) -> bool {
    let lhs = (P.decompress().unwrap() + c * X.decompress().unwrap()).compress();
    let rhs = z1.commit(&z2, gens_n).compress();

    lhs == rhs
  }

  pub fn verify(
    &self,
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    X: &CompressedGroup,
    Y: &CompressedGroup,
    Z: &CompressedGroup,
  ) -> Result<(), ProofVerifyError> {
    transcript.append_protocol_name(ProductProof::protocol_name());

    X.append_to_transcript(b"X", transcript);
    Y.append_to_transcript(b"Y", transcript);
    Z.append_to_transcript(b"Z", transcript);
    self.alpha.append_to_transcript(b"alpha", transcript);
    self.beta.append_to_transcript(b"beta", transcript);
    self.delta.append_to_transcript(b"delta", transcript);

    let z1 = self.z[0];
    let z2 = self.z[1];
    let z3 = self.z[2];
    let z4 = self.z[3];
    let z5 = self.z[4];

    let c = transcript.challenge_scalar(b"c");

    if ProductProof::check_equality(&self.alpha, &X, &c, &gens_n, &z1, &z2)
      && ProductProof::check_equality(&self.beta, &Y, &c, &gens_n, &z3, &z4)
      && ProductProof::check_equality(
        &self.delta,
        &Z,
        &c,
        &MultiCommitGens {
          n: 1,
          G: vec![X.decompress().unwrap()],
          h: gens_n.h,
        },
        &z3,
        &z5,
      )
    {
      Ok(())
    } else {
      Err(ProofVerifyError)
    }
  }
}

6. ZK vector dot-product proof

Hyrax: Doubly-efficient zkSNARKs without trusted setup代码解析_第4张图片

#[test]
  fn check_dotproductproof() {
    let mut csprng: OsRng = OsRng;

    let n = 1024;

    let gens_1 = MultiCommitGens::new(1, b"test-two");
    let gens_1024 = MultiCommitGens::new(n, b"test-1024");

    let mut x: Vec = Vec::new();
    let mut a: Vec = Vec::new();
    for _ in 0..n {
      x.push(Scalar::random(&mut csprng));
      a.push(Scalar::random(&mut csprng));
    }
    let y = DotProductProofLog::compute_dotproduct(&x, &a);
    let r_x = Scalar::random(&mut csprng);
    let r_y = Scalar::random(&mut csprng);

    let mut random_tape = RandomTape::new(b"proof");
    let mut prover_transcript = Transcript::new(b"example");
    let (proof, Cx, Cy) = DotProductProof::prove(
      &gens_1,
      &gens_1024,
      &mut prover_transcript,
      &mut random_tape,
      &x,
      &r_x,
      &a,
      &y,
      &r_y,
    );

    let mut verifier_transcript = Transcript::new(b"example");
    assert!(proof
      .verify(&gens_1, &gens_1024, &mut verifier_transcript, &a, &Cx, &Cy)
      .is_ok());
  }
#[derive(Debug, Serialize, Deserialize)]
pub struct DotProductProof {
  delta: CompressedGroup,
  beta: CompressedGroup,
  z: Vec,
  z_delta: Scalar,
  z_beta: Scalar,
}

impl DotProductProof {
  fn protocol_name() -> &'static [u8] {
    b"dot product proof"
  }

  pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar {
    assert_eq!(a.len(), b.len());
    (0..a.len()).map(|i| a[i] * b[i]).sum()
  }

  pub fn prove(
    gens_1: &MultiCommitGens,
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    random_tape: &mut RandomTape,
    x_vec: &[Scalar],
    blind_x: &Scalar,
    a_vec: &[Scalar],
    y: &Scalar,
    blind_y: &Scalar,
  ) -> (DotProductProof, CompressedGroup, CompressedGroup) {
    transcript.append_protocol_name(DotProductProof::protocol_name());

    let n = x_vec.len();
    assert_eq!(x_vec.len(), a_vec.len());
    assert_eq!(gens_n.n, a_vec.len());
    assert_eq!(gens_1.n, 1);

    // produce randomness for the proofs
    let d_vec = random_tape.random_vector(b"d_vec", n);
    let r_delta = random_tape.random_scalar(b"r_delta");
    let r_beta = random_tape.random_scalar(b"r_beta");

    let Cx = x_vec.commit(&blind_x, gens_n).compress();
    Cx.append_to_transcript(b"Cx", transcript);

    let Cy = y.commit(&blind_y, gens_1).compress();
    Cy.append_to_transcript(b"Cy", transcript);

    let delta = d_vec.commit(&r_delta, gens_n).compress();
    delta.append_to_transcript(b"delta", transcript);

    let dotproduct_a_d = DotProductProof::compute_dotproduct(&a_vec, &d_vec);

    let beta = dotproduct_a_d.commit(&r_beta, gens_1).compress();
    beta.append_to_transcript(b"beta", transcript);

    let c = transcript.challenge_scalar(b"c");

    let z = (0..d_vec.len())
      .map(|i| c * x_vec[i] + d_vec[i])
      .collect::>();

    let z_delta = c * blind_x + r_delta;
    let z_beta = c * blind_y + r_beta;

    (
      DotProductProof {
        delta,
        beta,
        z,
        z_delta,
        z_beta,
      },
      Cx,
      Cy,
    )
  }

  pub fn verify(
    &self,
    gens_1: &MultiCommitGens,
    gens_n: &MultiCommitGens,
    transcript: &mut Transcript,
    a: &[Scalar],
    Cx: &CompressedGroup,
    Cy: &CompressedGroup,
  ) -> Result<(), ProofVerifyError> {
    assert_eq!(gens_n.n, a.len());
    assert_eq!(gens_1.n, 1);

    transcript.append_protocol_name(DotProductProof::protocol_name());
    Cx.append_to_transcript(b"Cx", transcript);
    Cy.append_to_transcript(b"Cy", transcript);
    self.delta.append_to_transcript(b"delta", transcript);
    self.beta.append_to_transcript(b"beta", transcript);

    let c = transcript.challenge_scalar(b"c");

    let mut result = c * Cx.decompress().unwrap() + self.delta.decompress().unwrap()
      == self.z.commit(&self.z_delta, gens_n);

    let dotproduct_z_a = DotProductProof::compute_dotproduct(&self.z, &a);
    result &= c * Cy.decompress().unwrap() + self.beta.decompress().unwrap()
      == dotproduct_z_a.commit(&self.z_beta, gens_1);

    if result {
      Ok(())
    } else {
      Err(ProofVerifyError)
    }
  }
}

7. proof of dot-product based on Bulletproofs

Hyrax: Doubly-efficient zkSNARKs without trusted setup代码解析_第5张图片
Hyrax: Doubly-efficient zkSNARKs without trusted setup代码解析_第6张图片

#[test]
  fn check_dotproductproof_log() {
    let mut csprng: OsRng = OsRng;

    let n = 1024;

    let gens = DotProductProofGens::new(n, b"test-1024");

    let x: Vec = (0..n).map(|_i| Scalar::random(&mut csprng)).collect();
    let a: Vec = (0..n).map(|_i| Scalar::random(&mut csprng)).collect();
    let y = DotProductProof::compute_dotproduct(&x, &a);

    let r_x = Scalar::random(&mut csprng);
    let r_y = Scalar::random(&mut csprng);

    let mut random_tape = RandomTape::new(b"proof");
    let mut prover_transcript = Transcript::new(b"example");
    let (proof, Cx, Cy) = DotProductProofLog::prove(
      &gens,
      &mut prover_transcript,
      &mut random_tape,
      &x,
      &r_x,
      &a,
      &y,
      &r_y,
    );

    let mut verifier_transcript = Transcript::new(b"example");
    assert!(proof
      .verify(n, &gens, &mut verifier_transcript, &a, &Cx, &Cy)
      .is_ok());
  }
}
pub struct DotProductProofGens {
  n: usize,
  pub gens_n: MultiCommitGens,
  pub gens_1: MultiCommitGens,
}

impl DotProductProofGens {
  pub fn new(n: usize, label: &[u8]) -> Self {
    let (gens_n, gens_1) = MultiCommitGens::new(n + 1, label).split_at_mut(n);
    DotProductProofGens { n, gens_n, gens_1 }
  }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct DotProductProofLog {
  bullet_reduction_proof: BulletReductionProof,
  delta: CompressedGroup,
  beta: CompressedGroup,
  z1: Scalar,
  z2: Scalar,
}

impl DotProductProofLog {
  fn protocol_name() -> &'static [u8] {
    b"dot product proof (log)"
  }

  pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar {
    assert_eq!(a.len(), b.len());
    (0..a.len()).map(|i| a[i] * b[i]).sum()
  }

  pub fn prove(
    gens: &DotProductProofGens,
    transcript: &mut Transcript,
    random_tape: &mut RandomTape,
    x_vec: &[Scalar],
    blind_x: &Scalar,
    a_vec: &[Scalar],
    y: &Scalar,
    blind_y: &Scalar,
  ) -> (DotProductProofLog, CompressedGroup, CompressedGroup) {
    transcript.append_protocol_name(DotProductProofLog::protocol_name());

    let n = x_vec.len();
    assert_eq!(x_vec.len(), a_vec.len());
    assert_eq!(gens.n, n);

    // produce randomness for generating a proof
    let d = random_tape.random_scalar(b"d");
    let r_delta = random_tape.random_scalar(b"r_delta");
    let r_beta = random_tape.random_scalar(b"r_delta");
    let blinds_vec = {
      let v1 = random_tape.random_vector(b"blinds_vec_1", 2 * n.log2());
      let v2 = random_tape.random_vector(b"blinds_vec_2", 2 * n.log2());
      (0..v1.len())
        .map(|i| (v1[i], v2[i]))
        .collect::>()
    };

    let Cx = x_vec.commit(&blind_x, &gens.gens_n).compress();
    Cx.append_to_transcript(b"Cx", transcript);

    let Cy = y.commit(&blind_y, &gens.gens_1).compress();
    Cy.append_to_transcript(b"Cy", transcript);

    let blind_Gamma = blind_x + blind_y;
    let (bullet_reduction_proof, _Gamma_hat, x_hat, a_hat, g_hat, rhat_Gamma) =
      BulletReductionProof::prove(
        transcript,
        &gens.gens_1.G[0],
        &gens.gens_n.G,
        &gens.gens_n.h,
        x_vec,
        a_vec,
        &blind_Gamma,
        &blinds_vec,
      );
    let y_hat = x_hat * a_hat;

    let delta = {
      let gens_hat = MultiCommitGens {
        n: 1,
        G: vec![g_hat],
        h: gens.gens_1.h,
      };
      d.commit(&r_delta, &gens_hat).compress()
    };
    delta.append_to_transcript(b"delta", transcript);

    let beta = d.commit(&r_beta, &gens.gens_1).compress();
    beta.append_to_transcript(b"beta", transcript);

    let c = transcript.challenge_scalar(b"c");

    let z1 = d + c * y_hat;
    let z2 = a_hat * (c * rhat_Gamma + r_beta) + r_delta;

    (
      DotProductProofLog {
        bullet_reduction_proof,
        delta,
        beta,
        z1,
        z2,
      },
      Cx,
      Cy,
    )
  }

  pub fn verify(
    &self,
    n: usize,
    gens: &DotProductProofGens,
    transcript: &mut Transcript,
    a: &[Scalar],
    Cx: &CompressedGroup,
    Cy: &CompressedGroup,
  ) -> Result<(), ProofVerifyError> {
    assert_eq!(gens.n, n);
    assert_eq!(a.len(), n);

    transcript.append_protocol_name(DotProductProofLog::protocol_name());
    Cx.append_to_transcript(b"Cx", transcript);
    Cy.append_to_transcript(b"Cy", transcript);

    let Gamma = Cx.decompress().unwrap() + Cy.decompress().unwrap();

    let (g_hat, Gamma_hat, a_hat) = self
      .bullet_reduction_proof
      .verify(n, a, transcript, &Gamma, &gens.gens_n.G)
      .unwrap();

    self.delta.append_to_transcript(b"delta", transcript);
    self.beta.append_to_transcript(b"beta", transcript);

    let c = transcript.challenge_scalar(b"c");

    let c_s = &c;
    let beta_s = self.beta.decompress().unwrap();
    let a_hat_s = &a_hat;
    let delta_s = self.delta.decompress().unwrap();
    let z1_s = &self.z1;
    let z2_s = &self.z2;

    let lhs = ((Gamma_hat * c_s + beta_s) * a_hat_s + delta_s).compress();
    let rhs = ((g_hat + gens.gens_1.G[0] * a_hat_s) * z1_s + gens.gens_1.h * z2_s).compress();

    assert_eq!(lhs, rhs);

    if lhs == rhs {
      Ok(())
    } else {
      Err(ProofVerifyError)
    }
  }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BulletReductionProof {
  L_vec: Vec,
  R_vec: Vec,
}

impl BulletReductionProof {
  /// Create an inner-product proof.
  ///
  /// The proof is created with respect to the bases \\(G\\).
  ///
  /// The `transcript` is passed in as a parameter so that the
  /// challenges depend on the *entire* transcript (including parent
  /// protocols).
  ///
  /// The lengths of the vectors must all be the same, and must all be
  /// either 0 or a power of 2.
  pub fn prove(
    transcript: &mut Transcript,
    Q: &GroupElement,
    G_vec: &[GroupElement],
    H: &GroupElement,
    a_vec: &[Scalar],
    b_vec: &[Scalar],
    blind: &Scalar,
    blinds_vec: &[(Scalar, Scalar)],
  ) -> (
    BulletReductionProof,
    GroupElement,
    Scalar,
    Scalar,
    GroupElement,
    Scalar,
  ) {
    // Create slices G, H, a, b backed by their respective
    // vectors.  This lets us reslice as we compress the lengths
    // of the vectors in the main loop below.
    let mut G = &mut G_vec.to_owned()[..];
    let mut a = &mut a_vec.to_owned()[..];
    let mut b = &mut b_vec.to_owned()[..];

    // All of the input vectors must have a length that is a power of two.
    let mut n = G.len();
    assert!(n.is_power_of_two());
    let lg_n = n.log2();

    let G_factors: Vec = iter::repeat(Scalar::one()).take(n).collect();

    // All of the input vectors must have the same length.
    assert_eq!(G.len(), n);
    assert_eq!(a.len(), n);
    assert_eq!(b.len(), n);
    assert_eq!(G_factors.len(), n);
    assert_eq!(blinds_vec.len(), 2 * lg_n);

    //transcript.innerproduct_domain_sep(n as u64);

    let mut L_vec = Vec::with_capacity(lg_n);
    let mut R_vec = Vec::with_capacity(lg_n);
    let mut blinds_iter = blinds_vec.iter();
    let mut blind_fin = *blind;

    while n != 1 {
      n /= 2;
      let (a_L, a_R) = a.split_at_mut(n);
      let (b_L, b_R) = b.split_at_mut(n);
      let (G_L, G_R) = G.split_at_mut(n);

      let c_L = inner_product(&a_L, &b_R);
      let c_R = inner_product(&a_R, &b_L);

      let (blind_L, blind_R) = blinds_iter.next().unwrap();

      let L = GroupElement::vartime_multiscalar_mul(
        a_L
          .iter()
          .chain(iter::once(&c_L))
          .chain(iter::once(blind_L)),
        G_R.iter().chain(iter::once(Q)).chain(iter::once(H)),
      );

      let R = GroupElement::vartime_multiscalar_mul(
        a_R
          .iter()
          .chain(iter::once(&c_R))
          .chain(iter::once(blind_R)),
        G_L.iter().chain(iter::once(Q)).chain(iter::once(H)),
      );

      transcript.append_point(b"L", &L.compress());
      transcript.append_point(b"R", &R.compress());

      let u = transcript.challenge_scalar(b"u");
      let u_inv = u.invert().unwrap();

      for i in 0..n {
        a_L[i] = a_L[i] * u + u_inv * a_R[i];
        b_L[i] = b_L[i] * u_inv + u * b_R[i];
        G_L[i] = GroupElement::vartime_multiscalar_mul(&[u_inv, u], &[G_L[i], G_R[i]]);
      }

      blind_fin = blind_fin + blind_L * u * u + blind_R * u_inv * u_inv;

      L_vec.push(L.compress());
      R_vec.push(R.compress());

      a = a_L;
      b = b_L;
      G = G_L;
    }

    let Gamma_hat =
      GroupElement::vartime_multiscalar_mul(&[a[0], a[0] * b[0], blind_fin], &[G[0], *Q, *H]);

    (
      BulletReductionProof { L_vec, R_vec },
      Gamma_hat,
      a[0],
      b[0],
      G[0],
      blind_fin,
    )
  }

  /// Computes three vectors of verification scalars \\([u\_{i}^{2}]\\), \\([u\_{i}^{-2}]\\) and \\([s\_{i}]\\) for combined multiscalar multiplication
  /// in a parent protocol. See [inner product protocol notes](index.html#verification-equation) for details.
  /// The verifier must provide the input length \\(n\\) explicitly to avoid unbounded allocation within the inner product proof.
  fn verification_scalars(
    &self,
    n: usize,
    transcript: &mut Transcript,
  ) -> Result<(Vec, Vec, Vec), ProofVerifyError> {
    let lg_n = self.L_vec.len();
    if lg_n >= 32 {
      // 4 billion multiplications should be enough for anyone
      // and this check prevents overflow in 1< Result<(GroupElement, GroupElement, Scalar), ProofVerifyError> {
    let (u_sq, u_inv_sq, s) = self.verification_scalars(n, transcript)?;

    let Ls = self
      .L_vec
      .iter()
      .map(|p| p.decompress().ok_or(ProofVerifyError))
      .collect::, _>>()?;

    let Rs = self
      .R_vec
      .iter()
      .map(|p| p.decompress().ok_or(ProofVerifyError))
      .collect::, _>>()?;

    let G_hat = GroupElement::vartime_multiscalar_mul(s.iter(), G.iter());
    let a_hat = inner_product(a, &s);

    let Gamma_hat = GroupElement::vartime_multiscalar_mul(
      u_sq
        .iter()
        .chain(u_inv_sq.iter())
        .chain(iter::once(&Scalar::one())),
      Ls.iter().chain(Rs.iter()).chain(iter::once(Gamma)),
    );

    Ok((G_hat, Gamma_hat, a_hat))
  }
}

/// Computes an inner product of two vectors
/// \\[
///    {\langle {\mathbf{a}}, {\mathbf{b}} \rangle} = \sum\_{i=0}^{n-1} a\_i \cdot b\_i.
/// \\]
/// Panics if the lengths of \\(\mathbf{a}\\) and \\(\mathbf{b}\\) are not equal.
pub fn inner_product(a: &[Scalar], b: &[Scalar]) -> Scalar {
  let mut out = Scalar::zero();
  if a.len() != b.len() {
    panic!("inner_product(a,b): lengths of vectors do not match");
  }
  for i in 0..a.len() {
    out += a[i] * b[i];
  }
  out
}

你可能感兴趣的:(零知识证明)