1. 引言
前序博客有:
- Nova: Recursive Zero-Knowledge Arguments from Folding Schemes学习笔记
- 基于cycle of curves的Nova证明系统(1)
- 基于cycle of curves的Nova证明系统(2)
微软团队2021年论文 《Nova: Recursive Zero-Knowledge Arguments from Folding Schemes》。
本文主要解析代码见:
其主要依赖的库有:
- bellperson(bellman)库:https://github.com/filecoin-project/bellperson
- ff 有限域库:https://github.com/zkcrypto/ff
- (Sha2)digest库:https://github.com/RustCrypto/traits
- SHA-3 (Keccak) 哈希函数库:https://github.com/RustCrypto/hashes
- rayon并行库:https://github.com/rayon-rs/rayon
- rand_core库(基础随机数生成器):https://github.com/rust-random/rand
- rand_chacha库(使用ChaCha算法的密码学安全的随机数生成器):https://github.com/rust-random/rand
- itertools库(批量数据遍历工具库):https://github.com/rust-itertools/itertools
- subtle库(constant-time密码学库):https://github.com/dalek-cryptography/subtle,可参看博客:
- pasta库(Pasta曲线对——Pallas和Vesta库):https://github.com/zcash/pasta_curves,可参看博客:Mina中的Pasta(Pallas和Vesta)曲线。
- neptune Poseidon哈希函数库(由Filecoin开发、已审计,支持BLS12-381/Pallas/Vesta曲线的scalar域的Poseidon哈希。 ):https://github.com/lurk-lab/neptune
- generic-array(通用数组类型库):https://github.com/fizyk20/generic-array
- num-bigint(大整数库):https://github.com/rust-num/num-bigint
- num-traits(通用数学numeric traits):https://github.com/rust-num/num-traits
- num-integer(整数trait和函数库):https://github.com/rust-num/num-integer
- serde(通用序列化、反序列化框架):https://github.com/serde-rs/serde
- bincode(结构体与字节之间相互转换的二进制序列化反序列化工具):https://github.com/bincode-org/bincode
- flate2(基于DEFLATE stream的streaming压缩解压缩库):https://github.com/rust-lang/flate2-rs
- bitvec(以bits来对内存寻址,即逐bit管理内存):https://github.com/ferrilab/bitvec
- byteorder(按big-endian或little-endian对数字进行编解码):https://github.com/BurntSushi/byteorder
- thiserror(derive(Error) 派生宏):https://github.com/dtolnay/thiserror
2. 各R1CS基本类型定义
基本类型定义见traits/mod.rs
:
- Base:某group的base域内元素。
- Scalar:某group的scalar域内元素。
- CompressedGroupElement:某group元素的压缩表示。
- GroupElement:对压缩group元素的解压缩表示。
- PreprocessedGroupElement:表示preprocessed group元素。
- RO:表示某电路友好的sponge类型:其消耗base域元素,挤出scalar域元素。【以RO来定义某哈希函数,实际采用了Poseidon哈希函数】
- ROCircuit:将RO表示为电路模式。
- TE:外化proof时所使用的通用Fiat-Shamir transcript。【实际采用了SHA-3(Keccak)哈希函数】
- CE:基于group scalar值的承诺机制。【实际采用了Pedersen承诺机制】
- ROConstants:与RO相关的哈希函数常量值。
- ROConstantsCircuit:与ROCircuit相关的哈希函数常量值。
相关结构定义见:src/r1cs.rs
中:
- 0)R1CS公共参数定义:【主要用于构建返回公共参数CommitmentKey,其长度取num_cons(约束数)、num_vars(witness W向量长度)、total_nz(A、B、C三个矩阵元素总数之和) 的最大值的next_power_of_two(),】
pub struct R1CS<G: Group> { _p: PhantomData<G>, }
- 1)标准R1CS结构中A、B、C矩阵定义:
- 矩阵A、B、C的列数为:num_vars + num_io + 1,与向量 Z = ( W , u , x ) Z=(W,u,x) Z=(W,u,x)的长度一致,即列号的取值范围为0~(num_vars + num_io)。
- 矩阵A、B、C的行数num_cons:即对应为总约束数。
/// A type that holds the shape of the R1CS matrices #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct R1CSShape<G: Group> { pub(crate) num_cons: usize, // 矩阵A、B、C的行数,即行号取值范围为0~num_cons-1 pub(crate) num_vars: usize, // witness $W$ 向量长度 pub(crate) num_io: usize, // public input/output $x$ 向量长度 pub(crate) A: Vec<(usize, usize, G::Scalar)>, // 矩阵A以一维向量表示,其中每个元素的格式为(行号,列号,值) pub(crate) B: Vec<(usize, usize, G::Scalar)>, pub(crate) C: Vec<(usize, usize, G::Scalar)>, }
- 2)R1CS instance-witness定义为:【对应Az * Bz = Cz】
/// A type that holds a witness for a given R1CS instance #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct R1CSWitness<G: Group> { W: Vec<G::Scalar>, } /// A type that holds an R1CS instance #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] pub struct R1CSInstance<G: Group> { pub(crate) comm_W: Commitment<G>, pub(crate) X: Vec<G::Scalar>, }
- 3)relaxed R1CS instance-witness定义为:【对应Az * Bz = u*Cz + E】
/// A type that holds a witness for a given Relaxed R1CS instance #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct RelaxedR1CSWitness<G: Group> { pub(crate) W: Vec<G::Scalar>, pub(crate) E: Vec<G::Scalar>, } /// A type that holds a Relaxed R1CS instance #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] pub struct RelaxedR1CSInstance<G: Group> { pub(crate) comm_W: Commitment<G>, pub(crate) comm_E: Commitment<G>, pub(crate) X: Vec<G::Scalar>, pub(crate) u: G::Scalar, }
当采用Spartan来压缩最终的IVC proof时,需将 ( u , x ) ~ \widetilde{(u,x)} (u,x) 以multilinear多项式表示,则要求 ( u , x ) (u,x) (u,x)填充后的长度为power of two,同时要求未填充的 x x x向量的长度为偶数:
// We require the number of public inputs/outputs to be even
if num_io % 2 != 0 {
return Err(NovaError::OddInputLength);
}
2.1 R1CSShape中关键函数
R1CSShape中关键函数有:
- 1)new():明确创建特定的R1CS矩阵,会对矩阵向量的行列号做是否越界判断。
- 2)multiply_vec():计算AZ、BZ、CZ矩阵与向量乘积,结果为三个向量值。
- 3)is_sat_relaxed(ck, U, W):判断一组relaxed R1CS instance-witness是否satisfiable,即需同时满足以下条件:
- 3.1)witness 中的W.W、W.E向量长度应分别为num_vars和num_cons,instance中的U.X向量长度应为num_io。
- 3.2)满足Az * Bz = u*Cz + E,其中z=(W.W, U.u, U.X)
- 3.3)承诺值匹配,即U.comm_W== commit(ck, W.W) 且 U.comm_E== commit(ck, W.E)。
- 4)is_sat(ck, U, W):判断一组标准 R1CS instance-witness是否satisfiable,即需同时满足以下条件:
- 4.1)witness 中的W.W向量长度应为num_vars,instance中的U.X向量长度应为num_io。
- 4.2)满足Az * Bz = Cz,其中z=(W.W, 1, U.X)
- 4.3)承诺值匹配,即U.comm_W== commit(ck, W.W)。
- 5)commit_T(ck, U1, W1, U2, W2):计算committed relaxed R1CS folding scheme中的交叉项T的承诺值。其中(U1, W1)为relaxed R1CS instance-witness,(U2, W2)为标准R1CS instance-witness,即有U2.u=1且W2.E为零向量。
- 5.1)计算: T = A Z 1 ∘ B Z 2 + A Z 2 ∘ B Z 1 − u 1 ⋅ C Z 2 − u 2 ⋅ C Z 1 T=AZ_1\circ BZ_2+AZ_2\circ BZ_1-u_1\cdot CZ_2-u_2\cdot CZ_1 T=AZ1∘BZ2+AZ2∘BZ1−u1⋅CZ2−u2⋅CZ1
- 5.2)计算T的承诺值commit(ck, T)。
- 6)pad():当采用Spartan来压缩最终的IVC proof时,需将
W
~
(
⋅
)
、
A
~
(
⋅
,
⋅
)
、
B
~
(
⋅
,
⋅
)
、
C
~
(
⋅
,
⋅
)
、
(
u
,
x
)
~
\widetilde{W}(\cdot)、\widetilde{A}(\cdot,\cdot)、\widetilde{B}(\cdot,\cdot)、\widetilde{C}(\cdot,\cdot)、\widetilde{(u,x)}
W
(⋅)、A
(⋅,⋅)、B
(⋅,⋅)、C
(⋅,⋅)、(u,x)
以multilinear多项式表示,要求将A、B、C矩阵的行列数均填充为power of two;要求将W向量的长度填充为power of two;要求将
(
u
,
x
)
(u,x)
(u,x)的长度填充为power of two。为便于表示,实际填充时,取 A、B、C矩阵行数 与 W向量长度 的最大值的next_power_of_two() 为m。
本pad()函数仅对R1CSShape即A、B、C矩阵的行列进行填充。若只需做行填充,则直接改变num_cons参数值即可;若需做列填充,则将A、B、C 第 “c > (num_vars-1)” 列之后的值的列号调整为"c + m - num_vars"。
2.2 R1CSWitness中关键函数
R1CSWitness中关键函数有:【供Prover调用】
- 1)new():根据R1CSShape和W向量构建R1CSWitness,要求R1CSShape的num_vars 必须等于 W向量的长度。
- 2)commit():对R1CSWitness的W向量进行承诺。
2.3 R1CSInstance中关键函数
R1CSInstance中关键函数有:【供Verifier和Prover调用】
- 1)new():根据R1CSShape、comm_W和X向量构建R1CSInstance,要求R1CSShape的num_io 必须等于 X向量的长度。
- 2)absorb_in_ro():将R1CSInstance absorb到RO中。注意:
- comm_W中的base域元素可直接absorb;
- x向量中的每个scalar域元素
需以4个64 bit表示,再逐个需scalar_as_base()
转换为base域再absorb。【此时需考虑无需考虑域转换时的溢出问题。】
2.4 RelaxedR1CSWitness中关键函数
RelaxedR1CSWitness中关键函数有:【供Prover调用】
- 1)default():设置RelaxedR1CSWitness中W和E均为零向量。
- 2)from_r1cs_witness():根据R1CSWitness设置RelaxedR1CSWitness,其中E为零向量。
- 3)commit():分别对W和E向量进行承诺。
- 4)fold(W1, W2, T, r):将R1CSWitness W2 fold到 RelaxedR1CSWitness (W1, E1) 中,其中T为上面算的cross term,r为Verifier发送的random challenge。输出为RelaxedR1CSWitness (W, E)。
实际就是计算: W = W 1 + r ⋅ W 2 , E = E 1 + r ⋅ E 2 W=W_1+r\cdot W_2,E=E_1+r\cdot E_2 W=W1+r⋅W2,E=E1+r⋅E2。 - 5)pad():分别基于R1CSShape的num_vars和num_cons,将W和E向量补零到相应的长度。
2.5 RelaxedR1CSInstance中关键函数
RelaxedR1CSInstance中关键函数有:【供Verifier 和 Prover调用】
- 1)default():RelaxedR1CSInstance中的comm_W和comm_E为默认群元素,u为scalar零值,X为长度为num_io的零向量。
- 2)from_r1cs_instance():根据R1CSInstance构建RelaxedR1CSInstance,其中u为scalar 一值。
- 3)from_r1cs_instance_unchecked():根据comm_W和X构建RelaxedR1CSInstance,其中u为scalar 一值。
- 4)fold(U1, U2, comm_T, r):将R1CSInstance U2 fold到 RelaxedR1CSInstance (X1, u1, comm_W_1, comm_E_1) 中,其中comm_T为上面算的cross term T的承诺值,r为Verifier发送的random challenge。输出为RelaxedR1CSInstance (X, u, comm_W, comm_E)。
实际就是计算: x = x 1 + r ⋅ x 2 , W ˉ = W ˉ 1 + r ⋅ W ˉ 2 , E ˉ = E ˉ 1 + r ⋅ T ˉ + r 2 ⋅ E ˉ 2 , u = u 1 + r ⋅ u 2 x=x_1+r\cdot x_2,\bar{W}=\bar{W}_1+r\cdot \bar{W}_2,\bar{E}=\bar{E}_1+r\cdot \bar{T}+r^2\cdot \bar{E}_2,u=u_1+r\cdot u_2 x=x1+r⋅x2,Wˉ=Wˉ1+r⋅Wˉ2,Eˉ=Eˉ1+r⋅Tˉ+r2⋅Eˉ2,u=u1+r⋅u2。【注意,此处有 u 2 = 1 , E 2 为零向量 u_2=1,E_2为零向量 u2=1,E2为零向量,所以实际计算为 E ˉ = E ˉ 1 + r ⋅ T ˉ , u = u 1 + r \bar{E}=\bar{E}_1+r\cdot \bar{T},u=u_1+r Eˉ=Eˉ1+r⋅Tˉ,u=u1+r】 - 5)to_transcript_bytes():将RelaxedR1CSInstance转换拼接为Vec<u8>表示。
- 6)absorb_in_ro():将RelaxedR1CSInstance absorb到RO中。注意:
- comm_E和comm_W中的base域元素可直接absorb;
- scalar域元素u需
scalar_as_base()
转换为base域再absorb;【此时无需考虑域转换时的溢出问题】 - x向量中的每个scalar域元素需以4个64 bit表示,再逐个
scalar_as_base()
转换为base域再absorb。【此时需考虑域转换时的溢出问题。】
3. Non-interactive folding scheme
详细见src/nifs.rs
:
- prove():用于将具有相同结构、相同commitment key的Relaxed R1CS instance-witness tuple
(U1, W1)
和 R1CS instance-witness tuple(U2, W2)
进行fold,返回 Relaxed R1CS instance-witness(U, W)
。 - verify():用于将具有相同结构、相同commitment key的Relaxed R1CS instance
U1
和 R1CS instanceU2
进行fold,返回 Relaxed R1CS instanceU
。
Non-interactive folding scheme测试用例见:
4. 基于folding scheme的IVC
构建一个基于folding scheme的IVC方案,其Prover需证明: z n = F n ( z 0 ) z_n=F^n(z_0) zn=Fn(z0)。
修订版Nova Prover算法分为三大块:
- 1)初始流程
- 2)base case step:即针对 i = 0 i=0 i=0的情况
- 3)recursive step(即non-base case):即针对 i ≥ 1 i\geq 1 i≥1的情况。
每个step,
F
′
F'
F′ 由
F
F
F 和 verifier circuit 组成。
其中:
- 1)params:【见下面结构体定义】为哈希运算所需的参数。
- 2) i i i:表示执行第i个step。
- 3) z 0 z_0 z0:初始值。
- 4) z i z_i zi:执行完第i个step的输出值。
- 5)U:running instance,为relaxed R1CS instance。
- 6)u:为the instance to be folded in。为R1CS instance。
- 7)T:对应上图中的 T ˉ \bar{T} Tˉ承诺值.。
- 8) u i . x u_i.x ui.x对应为“u.X0= Hash(params, U, i, z0, zi)”, h i + 1 h_{i+1} hi+1对应“u.X1= H(params, Unew, i+1, z0, z_{i+1})”。
- 9)为提升IVC效率,采用cycle of curves实现:【cycle of curves 知识可参看:Halo2学习笔记——背景资料之Elliptic curves(5)】
见src/circuit.rs
,
F
′
F'
F′对应结构体 NovaAugmentedCircuit
:
/// The augmented circuit F' in Nova that includes a step circuit F
/// and the circuit for the verifier in Nova's non-interactive folding scheme
pub struct NovaAugmentedCircuit<G: Group, SC: StepCircuit<G::Base>> {
params: NovaAugmentedCircuitParams,
ro_consts: ROConstantsCircuit<G>,
inputs: Option<NovaAugmentedCircuitInputs<G>>,
step_circuit: SC, // The function that is applied for each step
}
其中:
- 1)
F
′
F'
F′的输入 对应 结构体
NovaAugmentedCircuitInputs
:【其中T对应上图中的 T ˉ \bar{T} Tˉ承诺值。】pub struct NovaAugmentedCircuitInputs<G: Group> { params: G::Scalar, // Hash(Shape of u2, Gens for u2). Needed for computing the challenge. i: G::Base, z0: Vec<G::Base>, zi: Option<Vec<G::Base>>, U: Option<RelaxedR1CSInstance<G>>, u: Option<R1CSInstance<G>>, T: Option<Commitment<G>>, //承诺值,对应$\bar{T}$ }
- 2)
F
′
F'
F′的哈希函数
H
1
,
H
2
H_1,H_2
H1,H2 对应 结构体
ROConstantsCircuit
。 - 3)
F
′
F'
F′哈希运算(absorb入base域,squeeze出scalar域)中域转换时需要的参数:
pub struct NovaAugmentedCircuitParams { limb_width: usize, n_limbs: usize, is_primary_circuit: bool, // A boolean indicating if this is the primary circuit }
- 4)
F
′
F'
F′中
F
F
F 对应 结构体
SC: StepCircuit<G::Base>
:/// A helper trait for a step of the incremental computation (i.e., circuit for F) pub trait StepCircuit<F: PrimeField>: Send + Sync + Clone { /// Return the the number of inputs or outputs of each step /// (this method is called only at circuit synthesis time) /// `synthesize` and `output` methods are expected to take as /// input a vector of size equal to arity and output a vector of size equal to arity fn arity(&self) -> usize; /// Sythesize the circuit for a computation step and return variable /// that corresponds to the output of the step z_{i+1} fn synthesize<CS: ConstraintSystem<F>>( &self, cs: &mut CS, z: &[AllocatedNum<F>], ) -> Result<Vec<AllocatedNum<F>>, SynthesisError>; /// return the output of the step when provided with the step's input fn output(&self, z: &[F]) -> Vec<F>; }
4.1 NovaAugmentedCircuit中关键函数
F
′
F'
F′对应结构体 NovaAugmentedCircuit
的关键函数有:
- 1)new():为input relaxed R1CS instances创建新的verification circuit。
- 2)alloc_witness():依次给 F ′ F' F′的各输入参数赋值:params、i、z_0、z_i、U、u、T。
- 3)synthesize_base_case(u):对应“修订版Nova Prover算法——base case step”,即针对
i
=
0
i=0
i=0情况。其中u为R1CS instance。
- 3.1)若params.is_primary_circuit为true,则设置relaxed R1CS instance U_default中:W = E = 0, u = 0, X0 = X1 = 0。
- 3.2)若params.is_primary_circuit为false,则根据R1CS instance u来 设置relaxed R1CS instance U_default。
- 4)synthesize_non_base_case(params, i, z_0, z_i, U, u, T, arity):对应“修订版Nova Prover算法——recursive step(即non-base case)”,即针对
i
≥
1
i\geq 1
i≥1的情况。其中arity对应各z_i向量长度。
- 4.1)检查u.x[0] = Hash(params, U, i, z0, zi) 是否成立,结果为check_pass。
- 4.2)运行NIFS Verifier:与
src/nifs.rs
中的verify()函数思路一致,将具有相同结构、相同commitment key的Relaxed R1CS instanceU
和 R1CS instanceu
进行fold,返回 Relaxed R1CS instanceU_fold
。【本质为计算 U i + 1 U_{i+1} Ui+1。】
- 5)synthesize():依次做如下操作:
- 5.1)调用alloc_witness():依次给 F ′ F' F′的各输入参数赋值:params、i、z_0、z_i、U、u、T。
- 5.2)判断i值是否为0,若为0则is_base_case为true,否则为false。
- 5.3)调用synthesize_base_case(u):对应“修订版Nova Prover算法——base case step”,即针对 i = 0 i=0 i=0情况。其中u为R1CS instance。返回relaxed R1CS instance Unew_base(U_default)。
- 5.4)调用synthesize_non_base_case(params, i, z_0, z_i, U, u, T, arity):对应“修订版Nova Prover算法——recursive step(即non-base case)”,即针对
i
≥
1
i\geq 1
i≥1的情况。其中arity对应各z_i向量长度。返回Unew_non_base(
U_fold
) 和 check_non_base_pass(check_pass)。 - 5.5)要求:”check_non_base_pass必须为true“,否则 “is_base_case必须为true(表示在base case step)”。【采用
AllocatedBit::nor
((NOT a) AND (NOT b)
)来约束】 - 5.6)计算Unew(即计算U_{I+1}):若is_base_case为true,设置Unew为 Unew_base(U_default);否则设置Unew为Unew_non_base(
U_fold
)。 - 5.7)计算i_new=i+1。
- 5.8)计算z_{i+1}(即计算z_next):
- 5.8.1)若is_base_case为true,则设置z_input为z_0;否则设置z_input为z_i。
- 5.8.2)调用step_circuit.synthesize(z_input),本质是计算z_next=F(z_input)。
- 5.8.3)要求z_next向量长度必须等于arity。
- 5.9)计算u.X1= H(params, Unew, i+1, z0, z_{i+1})。
src/circuit.rs
中test_recursive_circuit()测试用例中的TrivialTestCircuit,对应
F
(
x
)
=
x
F(x)=x
F(x)=x计算。
5. Nova:基于Folding scheme的高速recursive SNARK
- 1)params.is_primary_circuit为true,对应待证明relation
R
1
\mathcal{R}_1
R1:
- 2)params.is_primary_circuit为false,对应待证明relation
R
2
\mathcal{R}_2
R2:
具体见src/lib.rs
中: - 1)Nova公共参数见
PublicParams
结构体:【有G2::Base=G1::Scalar。以G1 curve为primary,StepCircuit C1对应的计算基于G1::Scalar;以G2 curve为secondary,StepCircuit C2对应的计算基于G2::Scalar。】【要求 z0_primary.len() == pp.F_arity_primary 且 z0_secondary.len() == pp.F_arity_secondary】/// A type that holds public parameters of Nova #[derive(Serialize, Deserialize)] #[serde(bound = "")] pub struct PublicParams<G1, G2, C1, C2> where G1: Group<Base = <G2 as Group>::Scalar>, G2: Group<Base = <G1 as Group>::Scalar>, C1: StepCircuit<G1::Scalar>, C2: StepCircuit<G2::Scalar>, { F_arity_primary: usize, F_arity_secondary: usize, ro_consts_primary: ROConstants<G1>, // 对应H1函数,absorb G1 Base field元素,squeeze G1 Scalar field元素 ro_consts_circuit_primary: ROConstantsCircuit<G2>, // 验证H2计算。 ck_primary: CommitmentKey<G1>, // primary曲线上的commitment key r1cs_shape_primary: R1CSShape<G1>, // primary曲线上计算对应的R1CSShape ro_consts_secondary: ROConstants<G2>, // 对应H2函数,,absorb G2 Base field元素,squeeze G2 Scalar field元素 ro_consts_circuit_secondary: ROConstantsCircuit<G1>, // 验证H1计算。 ck_secondary: CommitmentKey<G2>, // secondary曲线上的commitment key r1cs_shape_secondary: R1CSShape<G2>, // secondary曲线上计算对应的R1CSShape augmented_circuit_params_primary: NovaAugmentedCircuitParams, // 域转换溢出处理参数等,其中is_primary_circuit应为true augmented_circuit_params_secondary: NovaAugmentedCircuitParams, // 域转换溢出处理参数等,其中is_primary_circuit应为false digest: G1::Scalar, // digest of everything else with this field set to G1::Scalar::ZERO _p_c1: PhantomData<C1>, _p_c2: PhantomData<C2>, }
- 2)IVC中基于cycle of curves的递归step对应 结构体
RecursiveSNARK
- r_W_primary和r_U_primary:对应 W i ( 1 ) , U i ( 1 ) \mathbb{W_i^{(1)}},\mathbb{U_i^{(1)}} Wi(1),Ui(1)。
- r_W_secondary和r_U_secondary:对应 W i ( 2 ) , U i ( 2 ) \mathbb{W_i^{(2)}},\mathbb{U_i^{(2)}} Wi(2),Ui(2)。
- l_w_secondary和l_u_secondary:对应 𝕨 i ( 2 ) _i^{(2)} i(2),𝕦 i ( 2 ) _i^{(2)} i(2)。
- zi_primary和zi_secondary:对应 z i ( 1 ) z_i^{(1)} zi(1), z i ( 2 ) z_i^{(2)} zi(2)。
/// A SNARK that proves the correct execution of an incremental computation #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct RecursiveSNARK<G1, G2, C1, C2> where G1: Group<Base = <G2 as Group>::Scalar>, G2: Group<Base = <G1 as Group>::Scalar>, C1: StepCircuit<G1::Scalar>, C2: StepCircuit<G2::Scalar>, { r_W_primary: RelaxedR1CSWitness<G1>, r_U_primary: RelaxedR1CSInstance<G1>, r_W_secondary: RelaxedR1CSWitness<G2>, r_U_secondary: RelaxedR1CSInstance<G2>, l_w_secondary: R1CSWitness<G2>, l_u_secondary: R1CSInstance<G2>, i: usize, zi_primary: Vec<G1::Scalar>, zi_secondary: Vec<G2::Scalar>, _p_c1: PhantomData<C1>, _p_c2: PhantomData<C2>, }
- 3)压缩最后一个step的IVC proof,对应
CompressedSNARK
结构体:【其中RelaxedR1CSSNARKTrait实现见src/spartan/pp.rs
。】/// A SNARK that proves the knowledge of a valid `RecursiveSNARK` #[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] pub struct CompressedSNARK<G1, G2, C1, C2, S1, S2> where G1: Group<Base = <G2 as Group>::Scalar>, G2: Group<Base = <G1 as Group>::Scalar>, C1: StepCircuit<G1::Scalar>, C2: StepCircuit<G2::Scalar>, S1: RelaxedR1CSSNARKTrait<G1>, S2: RelaxedR1CSSNARKTrait<G2>, { r_U_primary: RelaxedR1CSInstance<G1>, r_W_snark_primary: S1, r_U_secondary: RelaxedR1CSInstance<G2>, l_u_secondary: R1CSInstance<G2>, nifs_secondary: NIFS<G2>, f_W_snark_secondary: S2, zn_primary: Vec<G1::Scalar>, zn_secondary: Vec<G2::Scalar>, _p_c1: PhantomData<C1>, _p_c2: PhantomData<C2>, } /// A type that holds the prover key for `CompressedSNARK` #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct ProverKey<G1, G2, C1, C2, S1, S2> where G1: Group<Base = <G2 as Group>::Scalar>, G2: Group<Base = <G1 as Group>::Scalar>, C1: StepCircuit<G1::Scalar>, C2: StepCircuit<G2::Scalar>, S1: RelaxedR1CSSNARKTrait<G1>, S2: RelaxedR1CSSNARKTrait<G2>, { pk_primary: S1::ProverKey, pk_secondary: S2::ProverKey, _p_c1: PhantomData<C1>, _p_c2: PhantomData<C2>, } /// A type that holds the verifier key for `CompressedSNARK` #[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] pub struct VerifierKey<G1, G2, C1, C2, S1, S2> where G1: Group<Base = <G2 as Group>::Scalar>, G2: Group<Base = <G1 as Group>::Scalar>, C1: StepCircuit<G1::Scalar>, C2: StepCircuit<G2::Scalar>, S1: RelaxedR1CSSNARKTrait<G1>, S2: RelaxedR1CSSNARKTrait<G2>, { F_arity_primary: usize, F_arity_secondary: usize, ro_consts_primary: ROConstants<G1>, ro_consts_secondary: ROConstants<G2>, digest: G1::Scalar, vk_primary: S1::VerifierKey, vk_secondary: S2::VerifierKey, _p_c1: PhantomData<C1>, _p_c2: PhantomData<C2>, }
5.1 PublicParams中关键函数
PublicParams中关键函数有:
- 1)setup():创建PublicParams
- 2)num_constraints():返回 primary电路 和 secondary电路的约束数。
- 3)num_variables():返回 primary电路 和 secondary电路的witness变量数。
5.2 RecursiveSNARK中关键函数
RecursiveSNARK中关键函数有:
-
1)setup():针对 i = 0 i=0 i=0的情况,根据StepCircuit c_primary和StepCircuit c_secondary,以及z0_primary和z_0_secondary,构建 ( W 1 ( 1 ) , U 1 ( 1 ) ) (\mathbb{W_1^{(1)}},\mathbb{U_1^{(1)}}) (W1(1),U1(1))、 ( W 1 ( 2 ) , U 1 ( 2 ) ) (\mathbb{W_1^{(2)}},\mathbb{U_1^{(2)}}) (W1(2),U1(2))、 ( ( (𝕨 1 ( 2 ) _1^{(2)} 1(2),𝕦 1 ( 2 ) ) _1^{(2)}) 1(2))。
-
2)prove_step (
&mut self,
pp: &PublicParams<G1, G2, C1, C2>,
c_primary: &C1,
c_secondary: &C2,
z0_primary: VecG1::Scalar,
z0_secondary: VecG2::Scalar,):其中self为RecursiveSNARK
结构体。- 2.1)若self.i=0,则直接修改self.i=1,然后返回self。【对应base case step】
- 2.2)若self.i > 0,则根据 z i ( 1 ) , z i ( 2 ) z_i^{(1)},z_i^{(2)} zi(1),zi(2)、 ( W i ( 1 ) , U i ( 1 ) ) (\mathbb{W_i^{(1)}},\mathbb{U_i^{(1)}}) (Wi(1),Ui(1))、 ( W i ( 2 ) , U i ( 2 ) ) (\mathbb{W_i^{(2)}},\mathbb{U_i^{(2)}}) (Wi(2),Ui(2))、 ( ( (𝕨 i ( 2 ) _i^{(2)} i(2),𝕦 i ( 2 ) ) _i^{(2)}) i(2)),计算 z i + 1 ( 1 ) , z i + 1 ( 2 ) z_{i+1}^{(1)},z_{i+1}^{(2)} zi+1(1),zi+1(2)、 ( W i + 1 ( 1 ) , U i + 1 ( 1 ) ) (\mathbb{W_{i+1}^{(1)}},\mathbb{U_{i+1}^{(1)}}) (Wi+1(1),Ui+1(1))、 ( W i + 1 ( 2 ) , U i + 1 ( 2 ) ) (\mathbb{W_{i+1}^{(2)}},\mathbb{U_{i+1}^{(2)}}) (Wi+1(2),Ui+1(2))、 ( ( (𝕨 i + 1 ( 2 ) _{i+1}^{(2)} i+1(2),𝕦 i + 1 ( 2 ) ) _{i+1}^{(2)}) i+1(2))。【对应non-base case step,即recursive step】
-
3)verify(
&self,
pp: &PublicParams<G1, G2, C1, C2>,
num_steps: usize,
z0_primary: &[G1::Scalar],
z0_secondary: &[G2::Scalar],
):其中self为RecursiveSNARK
结构体,返回self.zi_primary和self.zi_secondary。
验证第num_steps的RecursiveSNARK
的正确性,要求:【(relaxed) R1CS instances U i ( 1 ) , U i ( 2 ) \mathbb{U_i^{(1)}},\mathbb{U_i^{(2)}} Ui(1),Ui(2)和𝕦 i ( 2 ) _i^{(2)} i(2)的public input/output数必须为2。】
5.3 CompressedSNARK中关键函数
对step i 的IVC proof
z
i
(
1
)
,
z
i
(
2
)
z_i^{(1)},z_i^{(2)}
zi(1),zi(2)、
(
W
i
(
1
)
,
U
i
(
1
)
)
(\mathbb{W_i^{(1)}},\mathbb{U_i^{(1)}})
(Wi(1),Ui(1))、
(
W
i
(
2
)
,
U
i
(
2
)
)
(\mathbb{W_i^{(2)}},\mathbb{U_i^{(2)}})
(Wi(2),Ui(2))、
(
(
(𝕨
i
(
2
)
_i^{(2)}
i(2),𝕦
i
(
2
)
)
_i^{(2)})
i(2)) 进行压缩,CompressedSNARK中关键函数有:【其中RelaxedR1CSSNARKTrait实现见src/spartan/pp.rs
和src/spartan/mod.rs
。】
- 1)setup():根据PublicParams构建ProverKey和VerifierKey。
- 2)prover():根据ProverKey,对RecursiveSNARK进行证明。返回CompressedSNARK结构。
证明过程主要分为两大块:- 2.1)compression without SNARKs:
- 2.2)compression with SNARKs:【可采用Spartan来压缩】
- 2.1)compression without SNARKs:
- 3)verify(
&self,
vk: &VerifierKey<G1, G2, C1, C2, S1, S2>,
num_steps: usize,
z0_primary: VecG1::Scalar,
z0_secondary: VecG2::Scalar,
) :其中self为prove()输出的CompressedSNARK结构。
Nova系列博客
- Nova: Recursive Zero-Knowledge Arguments from Folding Schemes学习笔记
- Nova 和 SuperNova:无需通用电路的通用机器执行证明系统
- Sangria:类似Nova folding scheme的relaxed PLONK for PLONK
- 基于Nova/SuperNova的zkVM
- SuperNova:为多指令虚拟机执行提供递归证明
- Lurk——Recursive zk-SNARKs编程语言
- Research Day 2023:Succinct ZKP最新进展
- 2023年 ZK Hack以及ZK Summit 亮点记
- 基于cycle of curves的Nova证明系统(1)
- 基于cycle of curves的Nova证明系统(2)