Starknet的EVM兼容之路:Kakarot zkEVM 101

TL;DR

  • 虚拟机是一个软件仿真的计算机系统,为程序提供执行环境。它可以模拟各种硬件设备,使程序在受控且兼容的环境中运行。以太坊虚拟机(EVM)是一种基于栈的虚拟机,用于执行以太坊智能合约。

  • zkEVM是一种集成了零知识证明/有效性证明技术的EVM。它允许使用零知识证明验证EVM的执行过程,而无需所有验证者重新执行EVM。市场上有各种zkEVM产品,每个产品都有自己的方法和设计。

  • 需要zkEVM的原因在于对在Layer 2上支持智能合约执行的虚拟机的需求。此外,一些项目选择使用zkEVM来利用EVM的广泛用户生态系统,并设计更友好于零知识证明的指令集。

  • Kakarot是使用Cairo语言在Starknet上实现的zkEVM。它以Cairo智能合约的形式模拟了EVM的堆栈、内存、执行和其他方面。Kakarot面临与Starknet帐户系统的兼容性、成本优化和稳定性等挑战,因为Cairo语言尚处于实验阶段。

  • Warp是将Solidity代码转换为Cairo代码的转换器,在高级语言级别提供兼容性。另一方面,Kakarot通过实现EVM的操作码和预编译,提供了在EVM级别的兼容性。

什么是虚拟机?

要讲清楚什么是虚拟机,必须先讲当今主流的冯诺依曼架构下的计算机执行流程。运行在计算机上的种种程序,通常是由高级语言经过层层转化,最终生成机器可理解的机器码完成执行的。根据转化为机器码的方式不同,高级语言可以大致分为编译型语言与解释型语言。

编译型语言是指在代码的编写完成后,需要经过编译器的处理,将高级语言代码转换成机器码,生成可执行文件。一次编译就可以多次以较高效率执行。编译型语言的优点是因为在编译时已经将代码转换为机器码,因此执行速度快,并且可以在没有编译器的环境下运行程序,便于用户使用,不需要安装额外的软件。常见的编译型语言包括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

简单来讲,zkEVM就是运用零知识证明/有效性证明技术的EVM,让EVM的执行过程,可以通过零知识证明/有效性证明来更高效、低成本地验证,而不需要所有验证者都重新进行EVM的执行过程。

市场上的zkEVM产品众多,赛道火热,主要玩家包括Starknet, zkSync, Scroll, Taiko, Linea, Polygon zkEVM(原Polygon Hermez) 等,被vitalik分为了5类(1、2、2.5、3、4)。具体的内容可以查看Vitalik的博客。

为什么需要zkEVM

这个问题需要从两方面来看。

最初的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:虚拟机上的虚拟机?

为什么能够在虚拟机上再做一个虚拟机?这个事情对于计算机从业者而言是司空见惯的,但是对于不了解计算机的用户可能没那么显然。其实很好理解,这就好像搭积木,只要下层足够牢固(有图灵完备的执行环境),就可以无上限地往上层叠加积木。但是不论搭了多少层,最后的执行还是要交给最底层的物理硬件去处理,所以层数增高会导致效率的降低。同时,由于不同积木的设计不同(虚拟机设计不同),随着积木越搭越高,积木倒塌的可能性就越大(运行出错),也就需要更高的技术水平支撑。

Kakarot是在Starknet上用Cairo语言实现的一个EVM,以Cairo智能合约形式去模拟EVM中堆栈、内存、执行等内容。相对而言,实现EVM并不是什么难事,除了使用率最高的Go-Ethereum中用Golang编写的EVM,现存的还有使用Python, Java, JavaScript, Rust编写的EVM。

Kakarot zkEVM的技术难点在于,协议是作为Starknet链上合约存在的,这就带来了两个关键的问题。

  1. 兼容性 Starknet使用的是和以太坊完全不同的账户体系,以太坊中账户分为EOA(外部拥有账户)和CA(合约账户),然而Starknet中支持原生的账户抽象,所有账户都是合约账户。同时,由于使用的密码学算法不同,用户无法使用同一个熵在Starknet中生成与以太坊相同的地址。

  2. 成本 由于kakarot zkEVM是作为合约存在于链上的,所以对于代码实现有着较高的要求,需要尽可能地面向Gas进行优化,降低交互成本。

  3. 稳定性 与使用Golang, Rust, Python等传统高级语言不同,Cairo语言仍然处于试验阶段,从Cairo 0到Cairo 1再到Cairo 2(或者如果你喜欢的话,Cairo 1 version 2),官方团队仍然在不断修改语言特性。同时,Cairo VM还未得到足够的测试,不排除后续大规模重写的可能。

kakarot协议由五个主要的组件组成(GitHub文档中写的是四个,未包含EOA,本文为了便于读者理解做了调整):

  • Kakarot (Core):负责执行以太坊形式的交易,同时为以太坊用户提供对应的Starknet账户

  • Contract Accounts:以太坊意义上的CA,负责存储合约的字节码、合约中变量状态

  • Externally Owned Accounts:以太坊意义上的EOA,负责将以太坊交易转发给Kakarot Core

  • Account Registry: 存储以太坊账户和Starknet账户的对应关系。

  • Blockhash Registry:Blockhash作为一个特殊的Opcode,需要过去的区块数据,而Kakarot无法在链上直接获取到数据。该组件存储block_number -> block_hash的映射关系,由管理员写入,提供给Kakarot Core。

据kakarot CEO Elias Tazartes反馈,在团队的最新版本中,放弃了Account Resister的设计,改为直接使用一个31bytes的Starknet地址到20位EVM地址的mapping来保存对应关系。在未来,为了提高互操作性以及允许Starknet合约注册自己的EVM地址,可能会重新使用Account Register的设计。

Starknet上兼容EVM:Warp与kakarot有什么差异

按照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 团队

感谢kakarot团队对本文提出的宝贵意见,特别是Elias Tazartes。Thank you, sir!

Subscribe to Starknet Astro
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.