前序博客:
开源代码见:
当前支持的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"] }
关键依赖库有:
#[test]
属性,支持在运行tests之前,对logging和(或)tracing框架进行初始化。RISC Zero zk-STARK证明系统中的哈希函数有:【见zkp/src/core/hash】
而zkp/src/core中:
Hardware Abstraction Layer (HAL)用于对ZKP系统进行加速。具体见:zkp/src/hal。
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;
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,
}
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,
}
zkp/src/layout.rs:提供符号化访问电路buffers的接口。
需使用layout_buffer
宏来定义电路中具有layout信息的buffer。
zkp/src/adapter.rs:为电路,与,Prover/Verifier之间的,接口层。
实现的trait有:
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,
}
pub struct WriteIOP<F: Field> {
pub proof: Vec<u32>,
pub rng: Box<dyn Rng<F>>,
}
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,
}
PolyGroup表示一组多项式:
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:
PolyGroup有3个buffers:
coeffs
字段。evaluted
字段。merkle
字段。PolyGroup有2大来源:
struct ProveRoundInfo<H: Hal> {
domain: usize,
coeffs: H::Buffer<H::Elem>,
merkle: MerkleTreeProver<H>,
}
RISC Zero中,使用grand product accumulation checks的用途有二:
/// 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!()
}
}
为某电路执行生成zero-knowledge proof。关键函数见:pub fn finalize
。
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,
}
pub struct ReadIOP<'a, F: Field> {
proof: &'a [u32],
rng: Box<dyn Rng<F>>,
}
/// 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>>,
}
/// 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,
}
见: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)
}