1. 引言
RISC Zero,设想了一个基于零知识证明的无限计算的未来。
2023年5月发布了RISC Zero zkVM v0.15版本,其中包含了一种重要feature:
- continuations。
在RISC Zero zkVM中,continuations为一种机制:
- 用于将大的程序,切分为多个更小的片段,使得这些小片段可独立计算和证明。
引入continuations机制的好处在于:
- 并行证明
- 启用暂停和恢复zkVM(类似于AWS Lambda上的“热启动”)
- 限定所需内存为固定值,该内存值与程序大小无关
本文重点关注的是,借助continuations机制:
- 不再限定程序 不超过某固定最大计算长度。
- 使得程序可根据需要,运行任意多的指令。
但实际如何实现无上限cycle count的呢?
具有无限可能——因RISC Zero zkVM是通用的,可运行可编译为RISC-V的程序(如Rust、C++、Go等等)。
而且,引入了continuations机制之后,RISC Zero zkVM可执行任意时长的程序,如:
- 在RISC Zero zkVM内,运行一个EVM引擎,并证明由某以太坊交易引起的状态改变。
相关zkEVM demo示例见:
2. 在RISC Zero zkVM中运行EVM
在2023年5月10日博客zkEVM vs zkVM: a Single Letter Can Make a Big Difference!中,Erik解释了RISC Zero zkVM与zkEVM的区别:
- 在zkVM中:可运行在计算机上任意软件,都可运行在zkVM中,不仅限于运行在以太坊上的软件。
EVM是运行在某计算机是的软件,这即意味着,该EVM也可运行在RISC Zero zkVM中,之前相关的研究成果有:
- Odra在2023年2月做了一个POC原型:在RISC Zero zkVM中实现了SputnikVM的solidity运行。
- zkPoEx:使用SputnikVM来生成proofs of exploits。
本文以revm EVM为例:
- revm为基于Rust的EVM interpreter crate。
与其它大多数Rust crates类似,revm crate也可运行在RISC Zero zkVM中。https://github.com/risc0/risc0/tree/main/examples/zkevm-demo示例中即运行的是revm crate:
- 当将其指向某以太坊交易时,将使用revm来执行该交易以计算新状态;
- 然后创建a receipt,其中包含:交易执行前后的状态差异,以及,该结果正确的ZKP proof。
在引入continuations之前,该流程仅适于小交易:
- 之前使用https://etherscan.io/tx/0x671a3b40ecb7d51b209e68392df2d38c098aae03febd3a88be0f1fa77725bbd7这笔小交易来展示,该小交易不会超过cycle上限限制。
引入continuations之后,不再有该限制:
- 如,可运行类似https://etherscan.io/tx/0x600d18676aef439ec6ba33d143b78878a520682be7fd8331c74bdf672988a2b1这样的heavy交易,所调用的合约使用了ecrecover预编译合约以及多个KECCAK256。
在M1 Max MacBook Pro上运行,用时约12分钟,内存12GB。- 用时取决于程序执行长度,但内存用量,与程序和操作系统等均无关。
- 借助continuations,所有以太坊交易都可在https://github.com/risc0/risc0/tree/main/examples/zkevm-demo](https://github.com/risc0/risc0/tree/main/examples/zkevm-demo)示例中运行并证明,且无对交易大小的任务限制。
3. 何为continuation?
continuations为一种机制:
- 用于将大的程序,切分为多个更小的片段,使得这些小片段可独立计算和证明。
- 以内存image(和prorgam counter)的Merkle tree方式,来 跟踪每个小片段的起始和终止zkVM状态。从而可对比某片段的终止状态,与下一片段的起始状态。
zkVM程序,基于cycle count(即po2
),自动切分为segment片段:
pub struct Segment {
pub po2: usize, //The number of cycles in powers of 2.
pub index: u32, //The index of this Segment within the Session
pub insn_cycles: usize, //The number of cycles used to execute instructions.
/* private fields */
}
若程序运行需要的cycle数多于单个segment所允许的数量,则会自动切分:
- 使用"session":来表示一系列片段(sequence of segments)。其中第一个片段由用户初始化,最后一个片段由用户终止(不同于,自动生成切分的终止)。
- 而“segment”:具有任意自动决定的边界,这些边界包含在per-segment cycle limit 范围内。
sessions表示的是语义上的边界,其起始和终止均由用户来指定。
一个session receipt中包含了多个segment receipt:
pub struct Receipt {
pub inner: InnerReceipt,
pub journal: Vec<u8>,
}
pub enum InnerReceipt {
Flat(SegmentReceipts),
Succinct(SuccinctReceipt),
Fake,
}
会确认session receipt的如下要素的有效性:
- 1)每个segment receipt是有效的。
- 2)每个sement的起始receipt,与前一segment的终止状态,匹配。
- 3)初始状态(或image_id)与validator的期待匹配(即,该session运行的是该validator想要验证的同一代码)。
4. Continuations机制的好处
引入 continuations机制最明显的好处是:
- 可运行任意长度程序。
除此之外,还有如下好处:
- 1)可并行化:小片段可分发给多个计算机并行执行,以降低负载减少延迟。会在证明之前,通过“execution”阶段将程序切分为片段,并确定每个片段的内容。这样使得所证明的每个片段,均与其它片段内容无关。证明片段的高强度工作,可分发给多台机器,相比于在单一机器上生成完整proof,这样可并行化并降低总体延迟。
- 2)暂停-恢复:借助continuations,zkVM程序可暂停并恢复。当描述片段时,其会持续,直到用户喊停。这可以是传统的中止,表示计算的中止。但其也可以是暂停,使得用户可"I want to do some computation now, and then come back at some later time and pick up where I left off."。
如,想象某zkML工作负载,其构造了一个ML模型,加载了权重,提供了输入数据,并计算了输出。在这个例子中,zkVM可在加载权重之后,在提供输入之前暂停。这有几个优势:- 首先,模型构建和权重加载可以在用户提供输入之前执行,从而减少输入和输出之间的延迟。
- 此外,这个初始设置阶段可以执行一次并暂停,然后针对不同的输入恢复多次,从而节省了重新执行共享设置的工作。
- 3)固定内存用量:在引入continuations之前,若某zkVM程序的cycle数是2倍,则其runtime和内存需求均约是2倍。引入continuations之后,内存用量仅取决于segment length,而不是总程序长度,因此,任意长度的程序执行,所需的内存用量都是固定的。
5. Continuations和Bonsai
Continuations是一个强大的工具。
RISC Zero团队想要尽可能简化continuations的复杂度,同时让ZKP更通用。为此当前正致力于Bonsai:
- 虽然通过并行化有可能大幅提高延迟,但zkVM代码中并没有构建程序片段的分配。
- 当前只支持flat continuations,而不支持rollup continuations。
- 通过flat continuations,每个片段都会生成自己的receipt。每个receipt都需要单独验证,虽然我们helper functions来证明整个程序,但所需的验证工作仍会随着执行时间的延长而增加。
- rollup continuations将使用proofs of verification来递归地rollup这些单独的segment proofs。结果是,只需要验证一个receipt,验证时长将是恒定的,与执行时间长短无关。
参考资料
[1] RISC Zero团队2023年5月22日博客 Using Continuations to Prove Any EVM Transaction