我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes
在这一讲,我们将介绍EVM中用于内存(Memory)操作的4个指令,包括MSTORE
,MSTORE8
,MLOAD
,和MSIZE
。我们将在用Python写的极简版EVM中添加对这些操作的支持。
EVM中的内存
我们在第一讲介绍了EVM的内存,它是一个线性寻址存储器,类似一个动态的字节数组,可以根据需求动态扩展。它的另一个特点就是易失性,交易结束时所有数据都会被清零。它支持以8或256 bit写入(MSTORE8
/MSTORE
),但只支持以256 bit取(MLOAD
)。

我们可以用Python内置的bytearray
来代表内存:
内存的读写比存储(Storage)的读写要便宜的多,每次读写有固定费用3 gas,另外如果首次访问了新的内存位置(内存拓展),则需要付额外的费用(由当前偏移量和历史最大偏移量决定),计算方法见链接。
MSTORE (内存写)
MSTORE
指令用于将一个256位(32字节)的值存储到内存中。它从堆栈中弹出两个元素,第一个元素为内存的地址(偏移量 offset),第二个元素为存储的值(value)。操作码是0x52
,gas消耗根据实际内存使用情况计算(3+X)。
我们在run()
函数中添加对MSTORE
指令的处理:
现在,我们可以尝试运行一个包含MSTORE
指令的字节码:0x6002602052
(PUSH1 2 PUSH1 0x20 MSTORE)。这个字节码将2
和0x20
(32)推入堆栈,然后进行MSTORE
,将2
存到偏移量为0x20
的地方。
MSTORE8 (内存8位写)
MSTORE8
指令用于将一个8位(1字节)的值存储到内存中。与MSTORE
类似,但只使用最低8位。操作码是0x53
,gas消耗根据实际内存使用情况计算(3+X)。
我们在run()
函数中添加对MSTORE8
指令的处理:
现在,我们可以尝试运行一个包含MSTORE8
指令的字节码:0x6002602053
(PUSH1 2 PUSH1 0x20 MSTORE8)。这个字节码将2
和0x20
(32)推入堆栈,然后进行MSTORE8
,将2
存到偏移量为0x20
的地方。
MLOAD (内存读)
MLOAD
指令从内存中加载一个256位的值并推入堆栈。它从堆栈中弹出一个元素,从该元素表示的内存地址中加载32字节,并将其推入堆栈。操作码是0x51
,gas消耗根据实际内存使用情况计算(3+X)。
我们在run()
函数中添加对MLOAD
指令的处理:
现在,我们可以尝试运行一个包含MLOAD
指令的字节码:0x6002602052602051
(PUSH1 2 PUSH1 0x20 MSTORE PUSH1 0x20 MLOAD)。这个字节码将2
和0x20
(32)推入堆栈,然后进行MSTORE
,将2
存到偏移量为0x20
的地方;然后将0x20
推入堆栈,然后进行MLOAD
,将刚才存储在内存的值读取出来。
MSIZE (内存大小)
MSIZE
指令将当前的内存大小(以字节为单位)压入堆栈。操作码是0x59
,gas消耗为2。
总结
这一讲,我们介绍了EVM中的内存操作指令,并在极简版EVM中添加了对它们的支持。这些操作允许我们在EVM的内存中存储和读取值,为更复杂的合约逻辑提供基础。
课后习题: 写出0x6002602053602051
对应的指令形式,并给出运行后的堆栈和内存状态。