RISC Zero zk-STARK证明系统代码解析

1. 引言

前序博客:

开源代码见:

当前支持的feature有:

Feature Target(s) Implies Description
cuda prove, std Turns on CUDA GPU acceleration for the prover. Requires CUDA toolkit to be installed.
metal macos prove, std Turns on Metal GPU acceleration for the prover.
prove all except rv32im std Enables the prover, incompatible within the zkvm guest.
std all Support for the Rust stdlib.
[features]
default = []
cuda = ["dep:cust", "prove", "risc0-sys/cuda"]
metal = ["dep:metal", "prove", "risc0-sys/metal"]
prove = [
  "dep:ff",
  "dep:lazy_static",
  "dep:ndarray",
  "dep:rand",
  "dep:rayon",
  "risc0-sys",
  "std",
]
std = ["anyhow/std"]

关键依赖库

[dependencies]
anyhow = {
    
     version = "1.0", default-features = false }
blake2 = {
    
     version = "0.10.6", default-features = false }
bytemuck = {
    
     version = "1.12", features = ["derive"] }
cust = {
    
     version = "0.3", optional = true }
digest = {
    
     version = "0.10", features = ["oid"] }
ff = {
    
     version = "0.13", features = ["derive", "bits"], optional = true }
hex = {
    
     version = "0.4.3", default-features = false, features = ["alloc"] }
lazy_static = {
    
     version = "1.4", optional = true }
metal = {
    
     version = "0.25", optional = true }
paste = "1.0"
rand_core = "0.6"
risc0-core = {
    
     workspace = true }
risc0-zkvm-platform = {
    
     workspace = true }
serde = {
    
     version = "1.0", default-features = false, features = ["derive"] }
tracing = {
    
     version = "0.1", default-features = false, features = [
  "attributes",
] }

[target.'cfg(not(target_os = "zkvm"))'.dependencies]
ndarray = {
    
     version = "0.15", features = ["rayon"], optional = true }
rand = {
    
     version = "0.8", optional = true }
rayon = {
    
     version = "1.5", optional = true }
risc0-sys = {
    
     workspace = true, optional = true }
sha2 = {
    
     version = "0.10", default-features = false, features = ["compress"] }

[dev-dependencies]
criterion = "0.5"
rand = {
    
     version = "0.8", features = ["small_rng"] }
serial_test = "2.0"
test-log = {
    
     version = "0.2", default-features = false, features = ["trace"] }
tracing-subscriber = {
    
     version = "0.3", features = ["env-filter"] }

关键依赖库有:

  • anyhow:基于std::error::Error构建的灵活具体错误类型库。
  • blake2:BLAKE2哈希函数。
  • bytemuck:对bytes的灵活处理库。
  • cust:GPU CUDA库。
  • digest:密码学哈希函数和消息认证码。
  • ff:zkcrypto的有限域库。非constant-time。
  • hex:数据十六进制编解码库。
  • lazy_static:代码执行时才初始化的静态变量。
  • metal:Unsafe Rust bindings for the Metal 3D Graphics API。针对Macos的Metal GPU。
  • paste:标识符拼接库。
  • rand_core:Core随机数生成器。
  • risc0-core:“risc0/core”
  • risc0-zkvm-platform:“risc0/zkvm/platform”
  • serde:序列化、反序列化库。
  • tracing:应用层tracing,用于手机结构化、基于事件的诊断信息。
  • ndarray:n维数组。
  • rand:随机数生成器和其它随机数功能。
  • rayon:并行库。
  • risc0-sys:“risc0/sys”
  • sha2:SHA-2哈希函数,包括SHA-224、SHA-256、SHA-384、SHA-512。
  • criterion:统计驱动的微benchmark库。
  • serial_test:支持序列化测试用例创建。
  • test-log:替代#[test]属性,支持在运行tests之前,对logging和(或)tracing框架进行初始化。
  • tracing-subscriber:用于实现和组成“tracing”订阅者的实用程序。

3. 哈希函数

RISC Zero zk-STARK证明系统中的哈希函数有:【见zkp/src/core/hash】

  • Poseidon:基于BabyBear有限域的Poseidon哈希函数。sbox为 x 7 x^7 x7
  • Poseidon2:基于BabyBear有限域的Poseidon2哈希函数。sbox为 x 7 x^7 x7
  • Poseidon_254:基于BN254 scalar域的Poseidon哈希函数。sbox为 x 8 x^8 x8
  • SHA:实现FIPS 180-4 SHA-256哈希算法。
  • blake2b:Blake2b哈希套件。

而zkp/src/core中:

  • digest.rs中Digest:为256位数组表示。
  • ntt.rs:实现了抽象域的buffterfly、interpolate_ntt和evaluate_ntt等函数。【适用于Goldilocks、Babybear等域】
  • poly.rs:针对的是扩域的多项式evaluate、interpolate、divide函数。

4. 硬件抽象层

Hardware Abstraction Layer (HAL)用于对ZKP系统进行加速。具体见:zkp/src/hal。

  • cpu.rs:基于CPU实现的HAL
  • cuda.rs:基于GPU实现的HAL
  • dual.rs:
  • metal.rs:基于Metal GPU实现的HAL

5. 常量参数及其它中间层

5.1 常量参数

pub use risc0_core::field;

pub const MIN_CYCLES_PO2: usize = 13;
pub const MIN_CYCLES: usize = 1 << MIN_CYCLES_PO2; // 8K
pub const MAX_CYCLES_PO2: usize = 24;
pub const MAX_CYCLES: usize = 1 << MAX_CYCLES_PO2; // 16M

/// 50 FRI queries gives ~100 bits of conjectured security
pub const QUERIES: usize = 50;
pub const ZK_CYCLES: usize = QUERIES;
pub const MIN_PO2: usize = core::log2_ceil(1 + ZK_CYCLES);

/// Inverse of Reed-Solomon Expansion Rate
pub const INV_RATE: usize = 4;

const FRI_FOLD_PO2: usize = 4;

/// FRI folding factor is 2 ^ FRI_FOLD_PO2
pub const FRI_FOLD: usize = 1 << FRI_FOLD_PO2;

/// FRI continues until the degree of the FRI polynomial reaches FRI_MIN_DEGREE
#[cfg(not(target_os = "zkvm"))]
const FRI_MIN_DEGREE: usize = 256;

5.2 taps

zkp/src/taps.rs:供RISC-V递归中高效实现的TapData。

/// This class is an implementation detail and carefully built to be efficient
/// on RISC-V for use in recursion.
#[derive(Debug)]
pub struct TapData {
    
    
    // The offset in register group (reg #)
    pub offset: u16,
    // How many cycles back this tap is
    pub back: u16,
    // Which register group this tap is a part of
    pub group: usize,
    // Which combo this register is part of
    pub combo: u8,
    // How far to skip to next register
    pub skip: u8,
}

5.3 Merkle tree参数

zkp/src/merkle.rs:用于构建Merkle tree的参数。

/// The parameters of a merkle tree of prime field elements, including:
/// row_size - the number of leaves in the tree
/// col_size - the number of field elements associated with each leaf
/// queries - the number of queries that will be made to this merkle tree
/// layers - the number of levels on the merkle tree
/// top_layer - the index of the layer above which we check hashes only once
/// top_size - the number of hashes in the top layer
pub struct MerkleTreeParams {
    
    
    pub row_size: usize,
    pub col_size: usize,
    pub queries: usize,
    pub layers: usize,
    pub top_layer: usize,
    pub top_size: usize,
}

5.4 layout

zkp/src/layout.rs:提供符号化访问电路buffers的接口。

需使用layout_buffer宏来定义电路中具有layout信息的buffer。

5.5 adapter

zkp/src/adapter.rs:为电路,与,Prover/Verifier之间的,接口层。

实现的trait有:

  • CircuitStepHandler
  • CircuitStep
  • PolyFp:多项式系数为有限域。
  • PolyExt:多项式系数为扩域。
  • TapsProvider
  • CircuitInfo

6. executor

executor实现见:zkp/src/prove/executor.rs。

executor是基于CPU构建的:

pub struct CpuBuffer<T> {
    
    
    buf: Rc<RefCell<TrackedVec<T>>>,
    region: Region,
}

struct TrackedVec<T>(Vec<T>); //硬件内存
struct Region(usize, usize); //第一个为offset,第二个为size。

Executor结构定义见:【其中code表示控制指令,data表示execution trace数据,io表示电路输入输出,cycle表示对应第几个cycle,每个cycle的处理逻辑取决于handler。】

pub struct Executor<F, C, S>
where
    F: Field,
    C: 'static + CircuitProveDef<F>,
    S: CircuitStepHandler<F::Elem>,
{
    
    
    pub circuit: &'static C,
    // Circuit Step Handler
    pub handler: S,
    // Control Instructions
    pub code: CpuBuffer<F::Elem>,
    // Number of columns used for control instructions
    code_size: usize,
    // Execution Trace Data
    pub data: CpuBuffer<F::Elem>,
    // Number of columns used for execution trace data
    data_size: usize,
    // Circuit inputs/outputs
    pub io: CpuBuffer<F::Elem>,
    // Power of 2
    pub po2: usize,
    // steps = 2^po2 is the total number of cycles in the zkVM execution
    pub steps: usize,
    // Indicates whether the guest program has already halted
    pub halted: bool,
    // Maximum allowable execution length of guest program
    max_po2: usize,
    // Counter for zkVM execution
    pub cycle: usize,
}

7. prover

7.1 IOP

pub struct WriteIOP<F: Field> {
    
    
    pub proof: Vec<u32>,
    pub rng: Box<dyn Rng<F>>,
}

7.2 MerkleTreeProver

MerkleTreeProver用于根据a matrix of values,生成一棵Merkle tree。

    /// matrix: `rows * cols`
    /// rows: `domain = steps * INV_RATE`, `steps` is always a power of 2.
    /// cols: `count = circuit_cols`
pub struct MerkleTreeProver<H: Hal> {
    
    
    params: MerkleTreeParams,

    // The retained matrix of values
    matrix: H::Buffer<H::Elem>,

    // A heap style array where node N has children 2*N and 2*N+1.  The size of
    // this buffer is (1 << (layers + 1)) and begins at offset 1 (zero is unused
    // to make indexing nicer).
    nodes: Vec<Digest>,

    // The root value
    root: Digest,
}

7.3 poly_group

PolyGroup表示一组多项式:

  • 具有相同的最大degree
  • evaluate的domain 比 degree 大 inv_Rate 倍。evaluation domain size应为power of 2,从而可使用NTT。
  • 具有一棵dense Merkle tree:
    • 其每个元素对应evaluation domain上的单个点。
    • 叶子节点哈希为在该点所有evaluation值的linear hash。
    • 即若有100个多项式,在 2 16 2^{16} 216个点上evaluate,则相应的Merkle tree有 2 16 2^{16} 216个元素,每个元素为100个evaluation值的哈希值。
pub struct PolyGroup<H: Hal> {
    
    
    pub coeffs: H::Buffer<H::Elem>,
    pub count: usize,
    pub evaluated: H::Buffer<H::Elem>,
    pub merkle: MerkleTreeProver<H>,
}

PolyGroup主要用于DEEP-ALI协议中,在proof生成过程中需要4个methods:

  • 1)Resolve queries,即make MerkleColProofs
  • 2)在随机选中点做多项式evaluations
  • 3)通过一组随机线性系数,混合多项式
  • 4)访问evaluation domain中的raw values,来对Constraint Polynomials进行evaluate。

PolyGroup有3个buffers:

  • 1)每个多项式的系数,用于evaluate和混合。coeffs字段。
  • 2)对Merkle proofs的‘col’ part的evaluated points。evaluted字段。
  • 3)Merkle tree自身。merkle字段。

PolyGroup有2大来源:

  • 1)steps of computations,即DEEP Polynomials。
    • computations的resulting steps必须由caller填充(可能以随机数据填充)。
    • 同时会额外对多项式shift,即 f ( x ) → f ( 3 ∗ x ) f(x)\rightarrow f(3*x) f(x)f(3x),即意味着,只要query的次数 少于 所附加的随机值,常规NTT evaluation domain不会公开任何原始信息,从而实现zero knowledge零知识属性。
  • 2)将单个higher degree polynomial切分为lower degree polynomials。

7.4 FRI Prove


struct ProveRoundInfo<H: Hal> {
    
    
    domain: usize,
    coeffs: H::Buffer<H::Elem>,
    merkle: MerkleTreeProver<H>,
}

7.5 grand product accumulation checks

RISC Zero中,使用grand product accumulation checks的用途有二:

  • 1)Memory permutation
  • 2)Lookup argument
/// Tracks grand product accumulations for PLONK-style permutation arguments.
pub struct Accum<E: Elem> {
    
    
    /// Total number of cycles in this run.
    cycles: usize,

    // We use two PLONK-style grand product accumulation checks;
    // one for the memory permutation and a second for a lookup table.
    // We have two `kinds`: memory and bytes.
    kinds: BTreeMap<String, Vec<E>>,
}

其实现了adapter中的CircuitStepHandler trait:

impl<'a, F: Field> CircuitStepHandler<F::Elem> for Handler<'a, F> {
    
    
    /// Performs an extern call
    fn call(
        &mut self,
        cycle: usize,
        // The name of the extern call to perform.
        // Examples include getMajor, ramRead, syscall, etc
        name: &str,
        // This is an extra string argument that is only used by the `log` extern call.
        extra: &str,
        args: &[F::Elem],
        outs: &mut [F::Elem],
    ) -> Result<()> {
    
    
        assert!(cycle < self.cycles);
        match name {
    
    
            "plonkWriteAccum" => {
    
    
                assert_eq!(args.len(), F::ExtElem::EXT_SIZE);
                let elem = F::ExtElem::from_subelems(args.iter().copied());
                let ptr = self.get_ptr(extra);
                // Already checked that our cycle number is in range, so this offset is in the
                // buffer.
                unsafe {
    
     ptr.add(cycle).write(elem) };
            }
            "plonkReadAccum" => {
    
    
                assert_eq!(outs.len(), F::ExtElem::EXT_SIZE);
                let ptr = self.get_ptr(extra);
                // Already checked that our cycle number is in range, so this offset is in the
                // buffer.
                let elem = unsafe {
    
     ptr.add(cycle).read() };
                outs.clone_from_slice(elem.subelems());
            }
            _ => panic!("Unknown accum operation {name}"),
        }
        Ok(())
    }

    fn sort(&mut self, _name: &str) {
    
    
        unimplemented!()
    }
}

7.6 zk-STARK prover

为某电路执行生成zero-knowledge proof。关键函数见:pub fn finalize<C>(mut self, globals: &[&H::Buffer<H::Elem>], circuit_hal: &C) -> Vec<u32>

pub struct Prover<'a, H: Hal> {
    
    
    hal: &'a H,
    taps: &'a TapSet<'a>,
    iop: WriteIOP<H::Field>,
    groups: Vec<Option<PolyGroup<H>>>,
    cycles: usize,
    po2: usize,
}

8. verifier

8.1 IOP

pub struct ReadIOP<'a, F: Field> {
    
    
    proof: &'a [u32],
    rng: Box<dyn Rng<F>>,
}

8.2 MerkleTreeVerifier

/// A struct against which we verify merkle branches, consisting of the
/// parameters of the Merkle tree and top - the vector of hash values in the top
/// row of the tree, above which we verify only once.
pub(crate) struct MerkleTreeVerifier<'a> {
    
    
    params: MerkleTreeParams,

    // Conceptually, the merkle tree here is twice as long as the
    // "top" row (params.top_size), minus element #0.  The children of
    // the entry at virtual index i are stored at 2*i and 2*i+1.  The
    // root of the tree is at virtual element #1.

    // "top" is a reference, top_size long, to the top row.  This
    // contains the virtual indexes [top_size..top_size*2).
    top: &'a [Digest],

    // These are the rest of the tree.  These have the virtual indexes [1, top_size).
    #[allow(clippy::vec_box)]
    rest: Vec<Box<Digest>>,
}

8.3 FRI Verifier

/// VerifyRoundInfo contains the data against which the queries for a particular
/// round are checked. This includes the Merkle tree top row data, as well as
/// the size of the domain of the polynomial, and the mixing parameter.
struct VerifyRoundInfo<'a, F: Field> {
    
    
    domain: usize,
    merkle: MerkleTreeVerifier<'a>,
    mix: F::ExtElem,
}

8.4 zk-STARK verifier

见:zkp/src/verify/mod.rs。

/// Verify a seal is valid for the given circuit, and code checking function.
#[must_use]
#[tracing::instrument(skip_all)]
pub fn verify<F, C, CheckCode>(
    circuit: &C,
    suite: &HashSuite<F>,
    seal: &[u32],
    check_code: CheckCode,
) -> Result<(), VerificationError>
where
    F: Field,
    C: CircuitCoreDef<F>,
    CheckCode: Fn(u32, &Digest) -> Result<(), VerificationError>,
{
    
    
    Verifier::<F, C>::new(circuit, suite).verify(seal, check_code)
}

RISC Zero系列博客

猜你喜欢

转载自blog.csdn.net/mutourend/article/details/135302318
今日推荐