我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes
这一讲,我们将介绍EVM中与日志(Log)相关的5个指令::从LOG0到LOG4。日志是EVM中一个重要的概念,用于记录与合约交互的重要信息,是智能合约中事件(Event)的基础。这些记录永久保存在在区块链上,方便检索,但不会影响区块链的状态,是DApps和智能合约开发者的一个强大工具。
EVM中的日志和事件
在Solidity中,我们常常使用event来定义和触发事件。当这些事件被触发时,它们会生成日志,将数据永久存储在区块链上。日志分为主题(topic)和数据(data)。第一个主题通常是事件签名的哈希值,后面的主题是由indexed修饰的事件参数。如果你对event不了解,推荐阅读WTF Solidity的相应章节。
EVM中的LOG指令用于创建这些日志。指令LOG0到LOG4的区别在于它们包含的主题数量。例如,LOG0没有主题,而LOG4有四个主题。
为了在我们的极简EVM中支持日志功能,我们首先需要定义一个Log类来表示一个日志条目,他会记录发出日志的合约地址address,数据部分data,和主体部分topics:
然后,我们需要在EVM的初始化函数中增加一个logs列表,记录这些日志:
LOG指令
EVM中有五个Log指令:LOG0、LOG1、LOG2、LOG3和LOG4。它们的主要区别在于携带的主题数(topics):LOG0没有主题,而LOG4有四个。操作码从A0到A4,gas消耗由以下公式计算:
Log指令从堆栈中弹出2 + n的元素。其中前两个参数是内存开始位置mem_offset和数据长度length,n是主题的数量(取决于具体的LOG指令)。所以对于LOG1,我们会从堆栈中弹出3个元素:内存开始位置,数据长度,和一个主题。需要mem_offset的原因是日志的数据(data)部分存储在内存中,gas消耗低,而主题(topic)部分直接存储在堆栈上。
接下来,我们实现LOG指令:
最后,我们需要在run方法中为不同的LOG指令添加支持:
测试
测试LOG0
我们运行一个包含LOG0指令的字节码:60aa6000526001601fa0(PUSH1 aa PUSH1 0 MSTORE PUSH1 1 PUSH1 1f LOG0)。这个字节码将aa存在内存中,然后使用LOG0指令将aa输出到日志的数据部分。
测试LOG1
我们运行一个包含LOG1指令的字节码:60aa60005260116001601fa1(PUSH1 aa PUSH1 0 MSTORE PUSH 11 PUSH1 1 PUSH1 1f LOG1)。这个字节码将aa存在内存,然后将11压入堆栈,最后使用LOG1指令将aa输出到日志的数据部分,将11输出到日志的主题部分。
总结
这一讲,我们学习了EVM中与日志和事件相关的5个指令。这些指令在智能合约开发中扮演着关键角色,允许开发者在区块链上永久记录重要信息,同时不会影响区块链的状态。目前,我们已经学习了144个操作码中的131个!