Halo代码解析

Bowe等人2019年论文《Halo: Recursive Proof Composition without a Trusted Setup》。

代码实现参见:

  • https://github.com/zcash/halo2 【与Halo论文中的Tweedledum和Tweedledee曲线匹配】
  • https://github.com/mir-protocol/plonky
  • https://github.com/ebfull/halo 【与Halo论文中的曲线不一致。】

本博文解析主要针对 https://github.com/ebfull/halo中代码进行解析。

1. Verifier challenge生成

在Halo论文中,使用 G \mathbb{G} G来表示a group of prime order p p p F p \mathbb{F}_p Fp表示其scalar field,也可简化为用 F \mathbb{F} F来表示scalar field。
Halo论文中的verifier challenge取值范围不是来自于所有的 F x \mathbb{F}^x Fx,而是来自于challenge space I ⊂ F x \mathbb{I}\subset \mathbb{F}^x IFx,size为 2 λ 2^{\lambda} 2λ。论文中 λ = 128 \lambda=128 λ=128,即相应的Verifier challenge space I \mathbb{I} I的size为 2 128 2^{128} 2128,即Verifier challenge的取值范围应为 ( 0 , 2 128 − 1 ] (0,2^{128}-1] (0,21281]
实际实现时,采用了Abdelrahaman 等人2019年论文《Design of Symmetric-Key Primitives for Advanced Cryptographic Protocols》中的Rescue算法(algebraic hash function for prime fields)来获取Verifier challenges,同时采用Bertoni等人2011年论文《Duplexing the sponge: Single-pass authenticated encryption and other applications》中的duplex sponge来实例化构建Rescue中所需使用sponge函数——Prover messages are “absorbed” and Verifier challenges are “squeezed”。

1.1 Rescue算法实现

Rescue的算法实现见https://github.com/ebfull/halo/blob/master/src/rescue.rs

  • new
pub fn new() -> Self {
        let mds_matrix = generate_mds_matrix();

        // To use Rescue as a permutation, fix the master key to zero
        let key_schedule = generate_key_schedule([F::zero(); RESCUE_M], &mds_matrix);

        Rescue {
            sponge: SpongeState::Absorbing([None; SPONGE_RATE]),
            state: [F::zero(); RESCUE_M],
            mds_matrix,
            key_schedule,
        }
    }
  • absorb
pub fn absorb(&mut self, val: F) {
        match self.sponge {
            SpongeState::Absorbing(ref mut input) => {
                for entry in input.iter_mut() {
                    if entry.is_none() {
                        *entry = Some(val);
                        return;
                    }
                }

                // We've already absorbed as many elements as we can
                let _ = rescue_duplex(&mut self.state, input, &self.mds_matrix, &self.key_schedule);
                self.sponge = SpongeState::absorb(val);
            }
            SpongeState::Squeezing(_) => {
                // Drop the remaining output elements
                self.sponge = SpongeState::absorb(val);
            }
        }
    }
  • squeeze
pub fn squeeze(&mut self) -> F {
        loop {
            match self.sponge {
                SpongeState::Absorbing(input) => {
                    self.sponge = SpongeState::Squeezing(rescue_duplex(
                        &mut self.state,
                        &input,
                        &self.mds_matrix,
                        &self.key_schedule,
                    ));
                }
                SpongeState::Squeezing(ref mut output) => {
                    for entry in output.iter_mut() {
                        if let Some(e) = entry.take() {
                            return e;
                        }
                    }

                    // We've already squeezed out all available elements
                    self.sponge = SpongeState::Absorbing([None; SPONGE_RATE]);
                }
            }
        }
    }

1.2 Verifier challenge生成

1)调用Rescue算法中的squeeze算法,get_challenge函数中squeeze出来的值大于 2 128 2^{128} 2128,然后get_lower_128()取其中的低128位,此时值仍然不满足Verifier challenge要求的 ( 0 , 2 128 − 1 ] (0,2^{128}-1] (0,21281],方法是将128位的challenge的最高位置1,此时范围为 ( 2 127 , 2 129 − 1 ] (2^{127},2^{129}-1] (2127,21291]
其实get_challengeget_challenge_scalar 返回的是 u i 2 u_i^2 ui2,是因为在Halo 协议中,Verifier需计算 [ u i 2 ] L [u_i^2]L [ui2]L [ u i − 2 ] R [u_i^{-2}]R [ui2]R,优化依据参见Halo论文6.3节“Other Optimizations”。

fn get_challenge(transcript: &mut Rescue) -> F2 {
    let challenge = transcript.squeeze();
    let challenge = challenge.get_lower_128();

    let challenge = challenge | (1 << 127); //此时范围为$(2^128,2^{129}-1]$

    F2::from_u128(challenge)
}

2)将squeeze出来处理后的低128位,来自于Field F1,需要将其转换为F2 Field内的scalar值:

pub fn get_challenge_scalar(challenge: F1) -> F2 {
    let challenge = challenge.get_lower_128();

    let mut acc = F2::one();
    acc = acc + acc;
    acc = acc + F2::one();

    for i in 1..64 {
        let should_negate = (challenge >> (i * 2)) & 1 == 1;
        let should_endo = (challenge >> (i * 2 + 1)) & 1 == 1;

        acc = acc + &acc;
        if should_negate {
            acc = acc - &F2::one();
        } else {
            acc = acc + &F2::one();
        }

        if should_endo {
            acc = acc * &F2::BETA;
        }
    }

    acc
}

3)注意论文中 u 1 , u 2 , ⋯   , u k u_1,u_2,\cdots,u_k u1,u2,,uk的取值范围为 ( 0 , 2 128 − 1 ] (0,2^{128}-1] (0,21281],而get_challenge返回的值范围在 ( 2 1 27 , 2 129 − 1 ] (2^127,2^{129}-1] (2127,21291],调用get_challenge_scalar返回的scalar值在(0,2^{129}-1],需要保证该scalar值的sqrt值存在,最后sqrt的结果即为相应的Verifier challenge u i u_i ui

let (challenge, challenge_sq, challenge_sq_packed) = loop {
                let mut transcript = transcript.clone();
                transcript.absorb(forkvalue);
                let challenge_sq_packed = get_challenge::<_, C::Scalar>(&mut transcript);
                let challenge_sq: C::Scalar = get_challenge_scalar(challenge_sq_packed);
                match challenge_sq.sqrt().to_option() {
                    Some(challenge) => {
                        break (challenge, challenge_sq, challenge_sq_packed);
                    }
                    None => {
                        forkvalue = forkvalue + &C::Base::one();
                        forkvalue_u8 += 1;
                    }
                }
            };

2. main argument实现

完整协议参见Halo论文Figure 1,以及博客 Halo: Recursive Proof Composition without a Trusted Setup 学习笔记 第4节内容。

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