WTF Opcodes极简入门: 16. Return指令 我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。 推特:@0xAAScience 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes 这一讲,我们将介绍EVM中与返回数据(return)相关的3个指令: , ,和 。它们是Solidity中 关键字的基础。 返回数据 EVM的返回数据,通常称为 ,本质上是一个字节数组。它不遵循固定的数据结构,而是简单地表示为连续的字节。
我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes
这一讲,我们将介绍EVM中与返回数据(return)相关的3个指令: RETURN,RETURNDATASIZE,和RETURNDATACOPY。它们是Solidity中return关键字的基础。
EVM的返回数据,通常称为returnData,本质上是一个字节数组。它不遵循固定的数据结构,而是简单地表示为连续的字节。当合约函数需要返回复杂数据类型(如结构体或数组)时,这些数据将按照ABI规范被编码为字节,并存储在returnData中,供其他函数或合约访问。
为了支持这一特性,我们需要为我们的简化版EVM添加一个新属性以保存返回数据:
class EVM: def __init__(self): # ... 其他属性 ... self.returnData = bytearray()
0xF3returnData中,并终止当前的操作。此指令需要从堆栈中取出两个参数:内存的起始位置mem_offset和数据的长度length。def return_op(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[offset:offset + length]
0x3DreturnData的大小推入堆栈。def returndatasize(self): self.stack.append(len(self.returnData))
0x3EreturnData中的某段数据复制到内存中。此指令需要从堆栈中取出三个参数:内存的起始位置mem_offset,返回数据的起始位置return_offset,和数据的长度length。def returndatacopy(self): if len(self.stack) < 3: raise Exception('Stack underflow') mem_offset = self.stack.pop() return_offset = self.stack.pop() length = self.stack.pop() if return_offset + length > len(self.returnData): raise Exception("Invalid returndata size") # 扩展内存 if len(self.memory) < mem_offset + length: self.memory.extend([0] * (mem_offset + length - len(self.memory))) # 使用切片进行复制 self.memory[mem_offset:mem_offset + length] = self.returnData[return_offset:return_offset + length]
RETURN: 我们运行一个包含RETURN指令的字节码:60a26000526001601ff3(PUSH1 a2 PUSH1 0 MSTORE PUSH1 1 PUSH1 1f RETURN)。这个字节码将a2存在内存中,然后使用RETURN指令将a2复制到returnData中。
# RETURN code = b"\x60\xa2\x60\x00\x52\x60\x01\x60\x1f\xf3" evm = EVM(code) evm.run() print(evm.returnData.hex()) # output: a2
RETURNDATASIZE:我们将returnData设为aaaa,然后用RETURNDATASIZE将它的长度压入堆栈。
# RETURNDATASIZE code = b"\x3D" evm = EVM(code) evm.returnData = b"\xaa\xaa" evm.run() print(evm.stack) # output: 2
RETURNDATACOPY:我们将returnData设为aaaa,然后运行一个包含RETURNDATACOPY指令的字节码:60025F5F3E(PUSH1 2 PUSH0 PUSH0 RETURNDATACOPY),将返回数据存入内存。
# RETURNDATACOPY code = b"\x60\x02\x5F\x5F\x3E" evm = EVM(code) evm.returnData = b"\xaa\xaa" evm.run() print(evm.memory.hex()) # output: aaaa
这一讲,我们学习了EVM中与返回数据相关的3个指令:RETURN、RETURNDATASIZE,和RETURNDATACOPY,并通过代码示例为极简EVM添加了对这些指令的支持。目前,我们已经学习了144个操作码中的134个(93%)!