跳到主要内容

WTF Opcodes极简入门: 17. Revert指令

我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。

推特:@0xAA_Science

社区:Discord微信群官网 wtf.academy

所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes


这一讲,我们将介绍EVM中与异常处理相关的2个指令: REVERTINVALID。当它们被触发时,交易会回滚。

交易状态

我们需要在咱们的极简evm中跟踪交易的状态success,默认为True,当交易失败回滚时变为False,只有当successTrue时继续执行opcodes,否则结束交易:

class EVM:
def __init__(self):
# ... 其他属性 ...
self.success = True

def run(self):
while self.pc < len(self.code) and self.success:
op = self.next_instruction()
# ... 指令操作 ...

REVERT

当合约运行出错,或者达到了某种条件需要终止执行并返回错误信息时,可以使用REVERT指令。REVERT指令会终止交易的执行,返回一个错误消息,并且所有状态更改(例如资金转移、存储值的更改等)都不会生效。它会从堆栈中弹出两个参数:内存中错误消息的起始位置mem_offset和错误消息的长度length。它的操作码为0xFD,gas消耗为内存扩展消耗。

def revert(self):
if len(self.stack) < 2:
raise Exception('Stack underflow')
mem_offset = self.stack.pop()
length = self.stack.pop()

# 拓展内存
if len(self.memory) < mem_offset + length:
self.memory.extend([0] * (mem_offset + length - len(self.memory)))

self.returnData = self.memory[mem_offset:mem_offset+length]
self.success = False

INVALID

INVALID是EVM中用来表示无效操作的指令。当EVM遇到无法识别的操作码时,或者在故意触发异常的情境下,它会执行INVALID指令,导致所有状态更改都不会生效,并且消耗掉所有的gas。它确保了当合约试图执行未定义的操作时,不会无所作为或产生不可预测的行为,而是会安全地停止执行,对EVM的安全至关重要。它的操作码为0xFE,gas消耗为所有剩余的gas。

def invalid(self):
self.success = False

测试

  1. REVERT: 我们运行一个包含REVERT指令的字节码:60aa6000526001601ffd(PUSH1 aa PUSH1 0 MSTORE PUSH1 1 PUSH1 1f REVERT)。这个字节码将aa存在内存中,然后使用REVERT指令将交易回滚,并将aa复制到returnData中。
```python
# REVERT
code = b"\x60\xa2\x60\x00\x52\x60\x01\x60\x1f\xfd"
evm = EVM(code)
evm.run()
print(evm.returnData.hex())
# output: a2
```

总结

这一讲,我们学习了EVM中与异常处理相关的2个指令:REVERTINVALID,并通过代码示例为极简EVM添加了对这些指令的支持。异常处理是任何程序或合约执行的重要部分,而这两个指令是Solidity中的requireerrorassert关键字的基础。目前,我们已经学习了144个操作码中的136个(94%)!