CYNIC

Posted on Mar 09, 2024Read on Mirror.xyz

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)。

知道了物理机的执行流程,现在来讲虚拟机。

为什么需要虚拟机?

首先是为了提供一个执行环境,以太坊为了达到其最初“世界计算机”的愿景,需要一个图灵完备的执行环境,来为编程提供支持。之所以用虚拟机而不是直接转换为机器码,在各个机器上执行,可能是考虑1、兼容性(可以类比JVM)2、执行环境可控(每一条指令都由虚拟机负责解释执行,这意味着可以对程序执行的每一个流程进行控制,虚拟机作为一个沙盒环境,对权限控制,调试都有诸多好处。尤其是在权限的控制中,可以避免一些恶意程序对原生系统的破坏)(最重要的,可能是因为原生的不能支持Gas的设计,Gas可以维持系统运行,防止DDoS攻击破坏系统安全)

虚拟机通常通过模拟不同的硬件设备来提供一个虚拟的计算机环境。不同的虚拟机可以模拟的硬件设备有所不同,但通常包括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的博客。

https://vitalik.ca/general/2022/08/04/zkevm.html

为什么需要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!

StarkNet