揭秘EVM Opcodes

1. 引言

本文主要源自Macro团队的Gilbert在ETHNewYork 2022分享 Demystifying EVM Opcodes,同时结合evm.codes来理解。

下图摘自Evolution of the EVM Pt. 1
在这里插入图片描述
在这里插入图片描述

一个很赞的理解Opcodes的资料见EVM opcodes谜题:

在这里插入图片描述
学习EVM Opcodes,可成为更好的Solidity工程师。
更好的Solidity工程师,意味着:

  • 1)理解Solidity的设计原理。
  • 2)更好的为low-level code做准备。
  • 3)更深入的理解通用设计模式。
  • 4)更深入的理解智能合约在EVM中的运行机制。

2. 何为虚拟机?

在这里插入图片描述
在计算机科学中,“bytecode”为一种由源代码编译而来的计算机语言,其运行在虚拟机中。bytecode不是human-readable的,但确实computer-readable的。基本的流程为:
源代码-》bytecode-》Machine code

[Flow]
Source Code -> Bytecode -> Machine Code
Source Code: File written in programming language such as Java, Solidity. 
Bytecode: Compiled from source code and run on Virtual Machine such as JVM, EVM
Machine Code: Code that only operating system can read. Bytecode is converted to Machine Code and finally executed.

3. EVM介绍

3.1 EVM中的Opcode

与其它虚拟机采用二进制表示Opcode不同,为便于记忆和可读,EVM的所有Opcode都以单个字节来表示,并附加了人类可读名字。
EVM Opcode的基本语法为:
在这里插入图片描述

3.2 EVM中的Stack

EVM是Stack-Based的,执行完下图前三个指令后,相应stack中的内容见下图左侧:
在这里插入图片描述
SWAP2指令是指将stack中的“a,b,c” 转换为 “c,b,a”:【即“0x03,0x04,0x09” -> “0x09,0x04,0x03”】
在这里插入图片描述
ADD指令是指将stack中的top 2 值pop出来,相加后的结果再push回stack中:【0x04+0x03=0x07】
在这里插入图片描述
CALLER指令是指将 the 20-byte address of the caller account 推送到stack中。该账号为 the account that did the last call (except delegate call)。
在这里插入图片描述
在这里插入图片描述
stack中每个元素最多为32字节。当想要操作大于32字节的数据时,使用stack将非常复杂,此时可以考虑使用memory。

3.3 EVM中的Memory

Memory为在EVM中可访问的另一种数据结构,其是一个非常长的数组,其长度最低为0,最长可为任意值,不过事实上不会是任意长,因随着运行最终会out of gas。不过从技术上来说,未对Memory的长度做限制。

以MSTORE指令(向Memory写入数据)为例,首先往stack中推入某些数据:
在这里插入图片描述
MSTORE指令是指取stack中的top 2值,依次为offset和value值,在memory偏移offset个字节中存入相应的value值。上图中,0x20为offset(32个字节),0x03为value值(32字节):
在这里插入图片描述
MSTORE会从stack中pop出top2的2个值,然后值更新到memory中相应的位置:
在这里插入图片描述
MLOAD指令(从Memory中读取数据)是指取stack的top1为offset,从memory中相应的offset位置开始读取32字节:
在这里插入图片描述
MLOAD指令会从stack中pop顶端值为offset,然后再将从memory中对应offset读取的32字节推入stack中:
在这里插入图片描述
在这里插入图片描述
Memory很便宜,但其仅存在于单笔交易中,若需要跨多笔交易存储,此时需要使用Storage。

3.4 EVM中的Storage

storage操作方式与memory类似,memory很便宜,区块链上的storage非常昂贵,如:

  • 单个SSTORE操作需约2900~20000 gas
  • 单个MSTORE操作仅需约3+ gas

因此非必要不使用storage存储,因其非常昂贵。

memory像一个巨大的数组,而storage像key-value数据库。

4. 更简单的Trim语法表示

Trim为小众语言,但具有更易读特性。
Trim的S-Expressions为:
在这里插入图片描述

5. Solidity Opcodes表示

如Solidity中的原语与Opcode的对应关系类似有:

  • msg.sender->CALLER
  • msg.value->CALLVALUE
  • block.timestamp->TIMESTAMP
  • tx.origin->ORIGIN

在这里插入图片描述

5.1 Solidity payable关键字

payable为Solidity的feature,若想get paid from a function,需为该函数添加payable关键字。payable不是an evm related concept,而是a solidity related concept。若函数为标记payable关键字,solidity可借此来decide to block your functions from receiving Ether,因此,对应non-payble函数,存在一些粗略等价的opcodes。首先获取CALLVALUE值,判断其是否为0;若CALLVALUE不为0,则跳转到某处来revert该hashtag syntax,相应的hashtag synax在Trim中称为label,label为bytecode中的某个位置,因此很容易跳转到指定位置的代码,而不需要数字节数 或 手工输入相应的数字:
在这里插入图片描述
对于每个未标记payable的函数,都会有以上代码生成。因此,对于标记了payable的函数,不会生成以上代码,从而实际上可减少合约编译出来的code size:
在这里插入图片描述

5.2 Solidity Storage变量

Solidity Storage变量:Storage变量仅在使用时才编译,而不是在定义时就编译。因此,可定义任意多的Storage变量,但事实上并不会给编译的合约增加任何code。原因在于,Storage是key-value数据库,在使用之前,无需对key进行初始化。若想读某个之前从未用过的key,返回相应的值为0,并不为抛错误。
Solidity源代码中的变量为index spaced,与变量名无关。
在这里插入图片描述
当做合约升级的时候,应注意不要对合约中的storage变量重新排序,否则影响历史数据与变量的对应关系:
在这里插入图片描述

5.3 Solidity Compact Storage变量

若有多个连续storage变量,其size均小于256 bit(对应32byte,为EVM的word size)。Solidity会将这些小于256 bit的Storage变量压缩存储在同一storage slot中:
在这里插入图片描述
这样的压缩会带来一定的好处,如下图中,在同一storage slot 0中同时有 x , y x,y x,y变量,因此可用bit mask来选择指定的变量。
在这里插入图片描述
采用Compact Storage的主要好处在于:【第一次SLOAD某slot为cold SLOAD,后续对同一slot的SLOAD均为Hot SLOAD。】

  • Cold SLOAD需2100 gas
  • Hot SLOAD需100 gas

5.4 Solidity If Statement

Solidity中的If条件判断:
在这里插入图片描述
不过有趣的一点在于,在0.8.x Solidity版本时,x = x + 7;这样的引用并未优化,会对应有一次Hot SLOAD,若想避免这种情况,可引入本地变量来优化:uint tmp=x + 7; x = tmp;

5.5 Solidity External和Internal Function Calls

EVM中的外部调用 与 内部调用非常不同。事实上,EVM并没有内部调用的概念。所谓内部调用和外部调用,是Solidity层面的概念。

外部调用示例:【从FnCalls合约中外部调用token合约的transfer函数】
在这里插入图片描述
内部调用示例为:
在这里插入图片描述
EVM中的内部调用是指跳转到bytecode中的指定位置执行完成后,再跳转回去。上图为内部调用的伪代码。
下图与上图不同之处在于,内部调用的函数具有参数,即意味着在jump之前,需将函数参数push到stack中:
在这里插入图片描述

参考资料

[1] ETHNewYork 2022分享 Demystifying EVM Opcodes
[2] Explaining core system Ethereum Virtual Machine

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值