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(3∗x),即意味着,只要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系列博客
- RISC0:Towards a Unified Compilation Framework for Zero Knowledge
- Risc Zero ZKVM:zk-STARKs + RISC-V
- 2023年 ZK Hack以及ZK Summit 9 亮点记
- RISC Zero zkVM 白皮书
- Risc0:使用Continunations来证明任意EVM交易
- Zeth:首个Type 0 zkEVM
- RISC Zero项目简介
- RISC Zero zkVM性能指标
- Continuations:扩展RISC Zero zkVM支持(无限)大计算
- A summary on the FRI low degree test前2页导读
- Reed-Solomon Codes及其与RISC Zero zkVM的关系
- RISC Zero zkVM架构
- RISC-V与RISC Zero zkVM的关系
- 有限域的Fast Multiplication和Modular Reduction算法实现
- RISC Zero的Bonsai证明服务
- RISC Zero ZKP协议中的商多项式
- FRI的Commit、Query以及FRI Batching内部机制
- RISC Zero的手撕STARK
- RISC Zero zkVM guest程序优化技巧 及其 与物理CPU的关键差异
- ZK*FM:RISC Zero zkVM的形式化验证
- Zirgen MLIR:RISC-Zero的ZK-circuits形式化验证
- 以RISC Zero ZK Fraud Proof赋能Optimistic Rollups
- zkSummit10 亮点记
- 技术探秘:在RISC Zero中验证FHE——由隐藏到证明:FHE验证的ZK路径(1)
- 技术探秘:在RISC Zero中验证FHE——RISC Zero应用的DevOps(2)
- RISC Zero STARK证明系统时序图及规范
- RISC Zero zkVM Host & Guest 101