深度剖析以太坊虚拟机(EVM)的未来:Ewasm
以太坊是具有内置图灵完备编程语言的区块链。任何人都可以使用以太坊智能合约来创建去中心化应用程序。“以太坊虚拟机(EVM)是以太坊处理智能合约部署和执行的一部分”(Anto...
以太坊是具有内置图灵完备编程语言的区块链。任何人都可以使用以太坊智能合约来创建去中心化应用程序。“以太坊虚拟机(EVM)是以太坊处理智能合约部署和执行的一部分”(Antonopoulos and Wood,2018)。EVM由基于堆栈的架构组成。为了部署智能合约,必须首先将所有高级以太坊智能合约代码编译为机器可读代码(称为字节码)。然后,EVM通过后进先出堆栈安排来处理此字节码代码(一系列单字节操作码和可选参数)。此操作类似于Java虚拟机(JVM),其中每条指令都以单字节操作码和参数开始,如果有参数的话,则占用后面的未对齐的字节,值按大端排序给出(Scott,2009)。
本文的首要目标是解释以太坊基于堆栈的EVM的内部工作原理。解释了这些EVM基础知识,我们将开始看到以太坊有机地遵循了基于堆栈的虚拟机二进制指令格式,使其有望过渡到WebAssembly的光明未来。让我们带着了解这些的目的,首先看一下WebAssembly。WebAssembly(简称Wasm)WebAssembly(Wasm)是一种可以在现代Web浏览器中运行新型代码。它带来了新功能并在性能上有重大进展。Wasm旨在成为C,C++和Rust等低级源语言的有效编译目标(MDN Web Docs,2019)。以太坊Web Assembly(简称Ewasm)
为什么选择EwasmEVM的当前架构是释放其原始(raw)性能的最大障碍之一(GitHub EIP48,2019)。例如,虽然256位字长可促进本机哈希和椭圆曲线运算(Antonopoulos and Wood,2018),但也使从EVM操作码转换成硬件指令的过程难度过大,远超所需。提供更接近硬件映射的架构将大大提高以太坊的性能(GitHub EIP48,2019)。除了性能增强方面,Ewasm项目的设计目标之一是还支持跨多种语言和工具进行智能合约开发,即把LLVM,C,C++,Rust,JavaScript纳入开发周期。
智能合约通过使用原始EVM架构逐步创建、编译和部署以太坊智能合约,来了解一下EVM的基本要件。以太坊智能合约就像以太坊执行环境中的“自治代理”,当这个合约被消息或交易“触发”时,总是会执行特定的代码段,并直接控制自己的以太币余额和自己的键/值存储,从而跟踪持久变量(Buterin,2013)。诸如Solidity,Vyper和Lity之类的每种更高级别的智能合约源编程语言都维护着自己的编译器。智能合约的源代码可以编译为各种输出。包括但不限于应用程序二进制接口(ABI),字节码流和操作码。智能合约编译
在本地计算机上安装编译器之前,建议您检查基于Web的新编译器,例如SecondState的BUIDL环境。这将为您节省大量时间。让我们使用简单的存储源代码,并使用SecondState的BUIDL环境对其进行编译,如下图所示。
单击编译按钮将立即生成智能合约的ABI和字节码,如下所示。
分析部署字节码
如果我们查看字节码中的前4条指令,则会看到以下内容。
608060405234
如果我们查看以太坊黄皮书第30页上引用的这些值的助记符表示,我们将看到第一个指令(60)为PUSH1。
δ
助记符右侧标有δ的列表示要通过PUSH1指令(在本例中为0)从堆栈中删除的item数。α
下一个标记为α的列,代表要添加的其他item数通过PUSH1指令放在堆栈上。在这种情况下为1;一个字节0x80。在下面的示例中,我们现在可以看到第一条指令(0x60)PUSH1将值0x80压入了堆栈,第二条指令(0x60)PUSH1将值0x40压入了堆栈。
60806040…
PUSH1 0x80 PUSH1 0x40…
5234
如果再次查看助记符右侧的δ列,我们可以看到MSTORE将消耗堆栈顶部的两个item。总计,MSTORE将消耗堆栈中的前2个item,但将0个item放回堆栈中。
EVM的字长为256位(Antonopoulos and Wood,2018)。
这个“word”本身不是单词。比如,它可以是账户地址等。
MSTORE通过首先从堆栈顶部消耗当前条目来开始其操作;即一个地址会被指定在word在存储器中的存储位置。在这种情况下,地址位置为0x60。然后,MSTORE使用堆栈中的下一个条目并将其(0x80)保存到预先指定的地址(0x60)。在此阶段,堆栈上没有剩余的条目。下一条指令是(0x34)。它具有CALLVALUE的助记符表示形式。
608060405234
如果我们密切注意标记为α的列,作为其标准操作的一部分,我们将看到CALL VALUE将在堆栈中放置一项。但是,我们刚才提到堆栈当前为空,因此这引发了以下问题。CALL VALUE如何获取要放在堆栈中的数据?从黄皮书中可以看到,所有值在30s(0x30至0x3e)中的指令都与环境信息有关。在这种情况下,CALLVALUE从负责执行此字节码的消息调用中获取其所需的数据。与环境信息有关的另一个示例是指令0x33,它具有CALLER的助记符表示。CALLER指令能够自动获取启动字节码执行的以太坊帐户的地址。部署与运行时字节码在此刻,区分部署字节码和运行时字节码非常重要。如果查看附录A.1(获取部署字节码)和附录A.2(获取运行时字节码),您会注意到返回的字节码结果并不相同。运行时字节码是调用已部署的智能合约的功能时执行的字节码。
分析运行时字节码每个智能合约功能都可以标识为4字节功能签名(在运行时字节码内部)。要计算功能签名,我们要先获取功能名称。在我们的例子中,让我们从功能“ set”开始。
除了函数名称“ set”之外,我们还采用了函数的输入参数数据类型(用逗号分隔并用括号括起来)。例如,在我们的简单情况下,我们以文本集(uint256)结尾。注意:创建函数选择器文本时,请勿使用任何空格。获得此信息后,我们将创建sha3哈希的十六进制表示,并将其截断为仅4个字节。这是web3.js和web3.py中的示例。
以上两个命令都将返回以下签名0x60fe47b1,我们可以在部署和运行时字节码数据中轻松找到它们。
在这一阶段,我们了解了字节码中的每条指令是如何执行的(从堆栈中获取和获取信息,然后调用环境和内存等)。我们现在对调用代码如何使用环境信息执行状态转换功能等有了很好的了解,让我们继续以太坊的特定Ewasm实现细节。Ewasm实施
之前我们提到过,智能合约的源代码可以编译成各种输出。当然,从高级智能合约代码到Ewasm的路径是一项复杂的任务,可以通过不同的工具链采用多种多样的编译路径。
SecondState 的开发人员最近建立了一个Solidity到Ewasm的编译器,称为Soll。https://github.com/second-state/soll
在以下以太坊重要的会议召开前,Second State已完成了demo的原型,这一具有挑战性的任务:
Crosslink-在台北举行的全球领先的区块链研究人员和开发者会议
Devcon5-以太坊开发者国际会议,在日本大阪举行
Crosslink(2019年10月)
Second State 的开发 Hung-Ying-Ying 因为Soll编译器项目得到以太坊基金会奖金(与Vitalik Buterin一起在台北Crosslink活动中合照)。
如果您没有参加Devcon5,以下视频概述了Soll的操作。即在新的以太坊Ewasm测试网上部署以太坊ERC20 Solidity智能合约,并与之交互。https://www.youtube.com/watch?v=X-A6sP_HTy0
Devcon5大会非常适合分享SecondState 团队的最新Ewasm开发。在会议的第一天正式介绍Second State提出的以太坊1.X 与以太坊2.0的解决方案之后,又进行了一次非正式的公开demo和讨论。在demo后,与Solidity 团队的Christian Reitwiessner以及其他人进行的讨论中,展现了Second State在最佳协作以及减少未来Ewasm领域里不同开发人员和软件项目之间重复工作方面的最佳发展方向。
在成功完成了从Solidity到LLVM到Ewasm的原型编译之后,Second State 参考Christian 的宝贵意见,将努力实现执行Yul到llvm到Ewasm的编译路径。
Yul
Yul是以太坊特定的中间语言。以太坊Solidity编译器(可能还有Vyper编译器)将来的版本会全面支持Yul作为中间语言。Yul被设计为EVM 1.0,EVM 1.5和Ewasm都能使用。Yul的核心组件是函数、区块、变量、literals,for循环,if语句,switch语句,表达式以及对变量的赋值(Solidity-Yul,2019)。后端或目标是从Yul到特定字节码的转换器。每个后端都可以公开以后端名称为前缀的函数。Yul为提出的两个后端保留evm_和ewasm_前缀(Solidity-Yul,2019)。在这个领域已经完成了许多工作,因此,让我们简要了解一下从Solidity到Yul到Ewasm之路的已经熟知的部分。为Yul编译Solidity
Solidity编译器具有一个特殊的flag,可用于将Solidity源代码编译为Yul的中间表示(IR)。为了提供此功能的示例,我们在命令行中使用以太坊的Solidity编译器,从上方将简单存储示例编译为这种YUL IR格式。
为Ewasm编译Yul从Yul出发的路径的简单概述包括以下步骤。
将每个256位(32字节)变量拆分为4个单独的64位(8字节)变量。注意字节序差异。
创建一个库,该库使用内置的等效Ewasm作为用户定义的函数来实现每个EVM操作码,并使用常规的优化器。更多Solidity编译示例
如果把上述YUL IR代码交给Solidity编译器处理,Solidity可以生成漂亮的打印格式、二进制表达格式和文本表达格式。这可以通过以下命令实现
各个输出如下:
二进制表达格式
文本表达格式
内部工作机制的一个例子
Christian 在Devcon5演示的代码示例,展示了Ewasm风格的编译过程的内部工作原理。更具体地说,如此处显示,产生等同于MSTORE函数所要求的工作。如上面的demo,使用原始EVM,MSTORE接受2个参数(首先是标准的32字节地址,其次是256位(32字节)的单词)。但是,如您所见,以下代码显示了原始的Solidity智能合约256位变量被拆分为单独的64位变量。您还将注意到发生了字节序交换。
可在此网址获得:http://chriseth.github.io/notes/talks/yul_devcon5/#/11
字节序出于以下原因,在为Ewasm进行编译时,交换字节序(存储和从内存中检索字节的字节顺序)至关重要。以太坊虚拟机规范采用大端字节排序(Wood,2014)。但是,Web Assembly规范要求所有值都必须以Little Endian读写(webassembly.github.io,2019)。为了不贬低其他需要进行的设计工作和未来的艰苦工作,随着这项重要工作的进展,目前正在考虑/讨论其他细节。为了简洁起见,以下的附录B中概述了其中的一些细节。结论
智能合约代码和Ewasm之间有许多的路径。Yul的使用将为当前的以太坊编译器提供目标终端(target endpoints),并为llvm到Ewasm编译器提供入口。从Yul到llvm到Ewasm的编译器将为任何与Yul兼容的智能合约语言(如Solidity和Vyper)带来Wasm / Ewasm的基本优势。
使用Yul是一项重大胜利,因为它可以重复使用几乎所有的优化器组件(Reitwiessner,2019)。
除此之外,鉴于Rust等其他语言都将LLVM作为其主要的代码源后端(Rust-lang.github.io,2019),上述工具链路径将为其他编程语言成为以太坊的Ewasm智能合约生态系统的一部分打开大门 。
附录A 在命令行中编译简单存储智能合约
A.2 获取运行时字节码
A.3获取操作码
A.4 获取ABI
附录B 待考虑/讨论的议题:
1. 与1.x EVM的其余部分交互
除了典型的字节码操作,即堆栈,内存和存储访问之外,原始EVM还可以访问帐户信息,即地址和余额以及区块信息和当前gas价格(Antonopoulos和Wood,2018年)。对存储的访问有助于正确操作。例如,有效交易的执行始于对状态的不可撤销的更改:发件人帐户的nonce(Wood,2014年)。
2. 确定性行为
参考文献
1. Antonopoulos, A. and Wood, G. (2018). 《精通以太坊第一版》 O’Reilly Media.
2. Docs.ethhub.io. (2019). 以太坊 1.x — EthHub.
3. Ewasm. (2019).
4. GitHub EIP48. (2019). 以太坊 EIP48.
5. 网站:MDN Web Docs.
6. Reitwiessner, C. (2019). Yul,、eWasm与Solidity的进展与未来计划
7. Rust-lang.github.io.
8. Scott, M. (2009). 《编程语言论》第三版 Amsterdam: Elsevier/Morgan Kaufmann Pub.
9. Solidity — Yul. (2019). Yul — Solidity 0.5.12 文档.
10. Webassembly.github.io. (2019). WebAssembly 分类.
11. Wood, G., 2014.《以太坊黄皮书》 以太坊:一个安全的去中心化通用交易账本
更多推荐
所有评论(0)