我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes
这一讲,我们介绍EVM中的CALL
指令。CALL
指令可以被视为以太坊的核心,它允许合约之间进行交互,让区块链上的合约不再孤立。如果你不了解CALL
指令,请参考WTF Solidity教程第22讲。

CALL 指令
CALL
指令会创建一个子环境来执行其他合约的部分代码,发送ETH
,并返回数据。返回数据可以使用RETURNDATASIZE
和RETURNDATACOPY
获取。若执行成功,会将1
压入堆栈;否则,则压入0
。如果目标合约没有代码,仍将1
压入堆栈(视为成功)。如果账户ETH
余额小于要发送的ETH
数量,调用失败,但当前交易不会回滚。
它从堆栈中弹出7个参数,依次为:
gas
:为这次调用分配的gas量。to
:被调用合约的地址。value
:要发送的以太币数量,单位为wei
。mem_in_start
:输入数据(calldata)在内存的起始位置。mem_in_size
:输入数据的长度。mem_out_start
:返回数据(returnData)在内存的起始位置。mem_out_size
:返回数据的长度。
它的操作码为0xF1
,gas消耗比较复杂,包含内存扩展和代码执行等成本。
下面,我们在极简EVM中支持CALL
指令。由于CALL
指令比较复杂,我们进行了一些简化,主要包括以下几个步骤:读取calldata,更新ETH余额,根据目标地址代码创建evm子环境,执行evm子环境代码,读取返回值。
测试
测试用的以太坊账户状态:
在测试中,我们会使用第一个地址(0x9bbf
起始)调用第二个地址(0x1000
起始),运行上面的代码(PUSH1 0x42 PUSH1 0 MSTORE PUSH1 1 PUSH1 31 RETURN
),成功的话会返回0x42
。
测试字节码为6001601f5f5f6001731000000000000000000000000000000000000c425ff15f51
(PUSH1 1 PUSH1 31 PUSH0 PUSH0 PUSH1 1 PUSH20 1000000000000000000000000000000000000c42 PUSH0 CALL PUSH0 MLOAD),它会调用第二个地址上的代码,并发送1 wei
的以太坊,然后将内存中的返回值0x42
压入堆栈。
总结
这一讲,我们探讨了CALL
指令,它使得EVM上的合约可以调用其他合约,实现更复杂的功能。希望这一讲对您有所帮助!目前,我们已经学习了144个操作码中的137个(95%)!