Kakarot是使用Cairo语言在Starknet上实现的zkEVM。
原文作者:CYNIC
原文来源:mirror
TL;DR
要讲清楚什么是虚拟机,必须先讲当今主流的冯诺依曼架构下的计算机执行流程。运行在计算机上的种种程序,通常是由高级语言经过层层转化,最终生成机器可理解的机器码完成执行的。根据转化为机器码的方式不同,高级语言可以大致分为编译型语言与解释型语言。
编译型语言是指在代码的编写完成后,需要经过编译器的处理,将高级语言代码转换成机器码,生成可执行文件。一次编译就可以多次以较高效率执行。编译型语言的优点是因为在编译时已经将代码转换为机器码,因此执行速度快,并且可以在没有编译器的环境下运行程序,便于用户使用,不需要安装额外的软件。常见的编译型语言包括C,C++,Go等。
与编译型语言相对应的是解释型语言。解释型语言是指代码通过解释器逐行解释执行,直接运行在计算机上,每次运行都要重新进行翻译过程。解释型语言的优点是开发效率高,代码易于调试,但执行速度相对较慢。常见的解释型语言包括Python,JavaScript,Ruby等。
需要强调,语言从本质上并不区分编译型和解释型,只是在最初设计时会有一些倾向。C/C++绝大多数情况下是编译执行,但是也可以解释执行(Cint、Cling)。很多传统意义上的解释型语言,现在是编译成中间代码在虚拟机上执行(Python、Lua)。
知道了物理机的执行流程,现在来讲虚拟机。
虚拟机通常通过模拟不同的硬件设备来提供一个虚拟的计算机环境。不同的虚拟机可以模拟的硬件设备有所不同,但通常包括CPU、内存、硬盘、网络接口等。
以以太坊虚拟机EVM为例,EVM是一种基于堆栈的虚拟机,它被用于执行以太坊智能合约。EVM通过模拟CPU、内存、存储器和栈等硬件设备来提供一个虚拟的计算机环境。
具体来说,EVM是一种基于堆栈的虚拟机,它使用堆栈来存储数据和执行指令。EVM的指令集包括各种操作码,例如算术操作、逻辑操作、存储操作、跳转操作等,这些指令可以在EVM的堆栈上执行,从而完成智能合约的执行。
EVM模拟的内存和存储器是用于存储智能合约的状态和数据的设备。EVM将内存和存储器视为两个不同的区域,它可以通过读取和写入内存和存储器来访问智能合约的状态和数据。
EVM模拟的栈用于存储指令的操作数和结果。EVM的指令集中的大多数指令都是基于堆栈的,它们从栈中读取操作数并将结果推回栈中。
总之,EVM通过模拟CPU、内存、存储器和栈等硬件设备来提供一个虚拟的计算机环境,它可以执行智能合约的指令并存储智能合约的状态和数据。在实际运行中,EVM会将智能合约的字节码加载到内存中,并通过执行指令集来执行智能合约的逻辑。EVM实际取代的是上图中操作系统+硬件的部分。
EVM的设计过程,显然是自下而上的,先敲定了模拟的硬件环境(堆栈、内存),再根据对应的环境设计了自己的一套汇编指令集(Opcode)与字节码(Bytecode)。尽管汇编指令集是给人看的,但是涉及到很多底层知识,对开发者的要求较高,开发起来也较繁琐,所以需要高级语言,屏蔽晦涩繁琐的底层调用,为开发者提供更好的体验。EVM由于其汇编指令集的的定制化设计,很难直接利用传统的高级语言,索性重新一个新的高级语言以适配该虚拟机。以太坊社区为了EVM执行效率设计了两种编译型的高级语言——Solidity和Vyper。Solidity自不必强调,Vyper是Vitalik针对Solidity中存在的部分缺陷进行改进后设计的EVM高级语言,但是在社区没有获得很高的采用度,于是渐渐淡出历史舞台。
简单来讲,zkEVM就是运用零知识证明/有效性证明技术的EVM,让EVM的执行过程,可以通过零知识证明/有效性证明来更高效、低成本地验证,而不需要所有验证者都重新进行EVM的执行过程。
市场上的zkEVM产品众多,赛道火热,主要玩家包括Starknet, zkSync, Scroll, Taiko, Linea, Polygon zkEVM(原Polygon Hermez) 等,被vitalik分为了5类(1、2、2.5、3、4)。具体的内容可以查看Vitalik的博客。
https://vitalik.ca/general/2022/08/04/zkevm.html
这个问题需要从两方面来看。
最初的zk Rollup尝试,都只能实现较为简单的转账、交易功能,例如zkSync Lite, Loopring等。但是曾经沧海难为水,用惯了以太坊上图灵完备的EVM,当无法通过编程创造多样的应用时,人们便开始唿唤L2上的虚拟机。撰写智能合约的需求,是为一。
由于EVM中部分设计对于生成零知识证明/有效性证明不友好,部分玩家选择了在底层使用对于零知识证明/有效性证明友好的指令集,例如Starknet的Cairo Assembly和zkSync的Zinc Instruction。但是大家同时也都不愿放弃EVM庞大的用户生态,于是选择在上层兼容EVM,是3、4类zkEVM。还有部分玩家仍然坚持EVM传统指令集Opcode,将精力放在为Opcode生成更高效的证明上,是1、2类zkEVM。EVM的庞大生态,是为二。
为什么能够在虚拟机上再做一个虚拟机?这个事情对于计算机从业者而言是司空见惯的,但是对于不了解计算机的用户可能没那么显然。其实很好理解,这就好像搭积木,只要下层足够牢固(有图灵完备的执行环境),就可以无上限地往上层叠加积木。但是不论搭了多少层,最后的执行还是要交给最底层的物理硬件去处理,所以层数增高会导致效率的降低。同时,由于不同积木的设计不同(虚拟机设计不同),随着积木越搭越高,积木倒塌的可能性就越大(运行出错),也就需要更高的技术水平支撑。
Kakarot是在Starknet上用Cairo语言实现的一个EVM,以Cairo智能合约形式去模拟EVM中堆栈、内存、执行等内容。相对而言,实现EVM并不是什么难事,除了使用率最高的Go-Ethereum中用Golang编写的EVM,现存的还有使用Python, Java, JavaScript, Rust编写的EVM。
Kakarot zkEVM的技术难点在于,协议是作为Starknet链上合约存在的,这就带来了两个关键的问题。
kakarot协议由五个主要的组件组成(GitHub文档中写的是四个,未包含EOA,本文为了便于读者理解做了调整):
据kakarot CEO Elias Tazartes反馈,在团队的最新版本中,放弃了Account Resister的设计,改为直接使用一个31bytes的Starknet地址到20位EVM地址的mapping来保存对应关系。在未来,为了提高互操作性以及允许Starknet合约注册自己的EVM地址,可能会重新使用Account Register的设计。
按照Vitalik定义的zkEVM类型而言,Warp属于Type-4,而kakarot当前属于Type-2.5。
Warp是一个将Solidity代码转化为Cairo代码的转译器,之所以不叫编译器,大概是因为输出的Cairo仍是高级语言。通过Warp,Solidity开发者可以维持原先的开发状态,而不需要学习新的Cairo语言。对于很多项目方而言,Warp降低了进入Starknet生态的门槛,不需要使用Cairo重写大量的工程代码。
转译的思想虽然简单,但是兼容性也是最差的,有部分Solidity代码无法很好地翻译为Cairo,涉及到账户体系、密码算法等代码逻辑需要修改源代码才能完成迁移,具体的不支持特征可见Warp文档。例如,许多项目会对EOA账户与合约账户的执行逻辑进行区分,但是Starknet中所有账户都是合约账户,这部分的代码就需要修改后才能进行转译。
Warp是在高级语言层面的兼容,kakarot是在EVM层面的兼容。
EVM的全部重写,Opcode与Pre-compile的逐条实现,让kakarot拥有了更高原生的兼容性。毕竟,在相同的虚拟机(EVM)中执行,总是要比在不同的虚拟机(Cairo VM)中执行来得更兼容一些。Account Registry、Blockhash Registry更是巧妙地屏蔽了不同体系下的差异,把对用户的迁移摩擦降到最小。
感谢kakarot团队对本文提出的宝贵意见,特别是Elias Tazartes。Thank you, sir!