我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes
在这一讲,我们将探索EVM中与交易(Transaction)上下文相关的4个指令,包括ADDRESS
, ORIGIN
, CALLER
等。我们能利用这些指令访问当前交易或调用者的信息。
交易的基本结构

在深入学习这些指令之前,让我们先了解以太坊交易的基本结构。每一笔以太坊交易都包含以下属性:
nonce
:一个与发送者账户相关的数字,表示该账户已发送的交易数。gasPrice
:交易发送者愿意支付的单位gas价格。gasLimit
:交易发送者为这次交易分配的最大gas数量。to
:交易的接收者地址。当交易为合约创建时,这一字段为空。value
:以wei为单位的发送金额。data
:附带的数据,通常为合约调用的输入数据(calldata)或新合约的初始化代码(initcode)。v, r, s
:与交易签名相关的三个值。
在此基础上,我们可以在极简EVM中添加一个交易类,除了上述的信息以外,我们还把一些交易上下文分信息包含其中,包含当前调用者caller
,原始发送者origin
(签名者),和执行合约地址,thisAddr
(Solidity中的address(this)
):
当初始化evm对象时,需要传入Transaction
对象:
交易指令
1. ADDRESS
- 操作码:
0x30
- gas消耗: 2
- 功能:将当前执行合约的地址压入堆栈。
- 使用场景:当合约需要知道自己的地址时使用。
2. ORIGIN
- 操作码:
0x32
- gas消耗: 2
- 功能:将交易的原始发送者(即签名者)地址压入堆栈。
- 使用场景:区分合约调用者与交易发起者。
3. CALLER
- 操作码:
0x33
- gas消耗: 2
- 功能:将直接调用当前合约的地址压入堆栈。
- 使用场景:当合约需要知道是谁调用了它时使用。
4. CALLVALUE
- 操作码:
0x34
- gas消耗: 2
- 功能:将发送给合约的ether的数量(以wei为单位)压入堆栈。
- 使用场景:当合约需要知道有多少以太币被发送时使用。
5. CALLDATALOAD
- 操作码:
0x35
- gas消耗: 3
- 功能:从交易或合约调用的
data
字段加载数据。它从堆栈中弹出calldata的偏移量(offset
),然后从calldata的offset
位置读取32字节的数据并压入堆栈。如果calldata剩余不足32字节,则补0。 - 使用场景:读取传入的数据。
6. CALLDATASIZE
- 操作码:
0x36
- gas消耗:2
- 功能:获取交易或合约调用的
data
字段的字节长度,并压入堆栈。 - 使用场景:在读取数据之前检查大小。
7. CALLDATACOPY
- 操作码:
0x37
- gas消耗:3 + 3 * 数据长度 + 内存扩展成本
- 功能:将
data
中的数据复制到内存中。它会从堆栈中弹出3个参数(mem_offset, calldata_offset, length),分别对应写到内存的偏移量,读取calldata的偏移量和长度。 - 使用场景:将输入数据复制到内存。
8. CODESIZE
- 操作码:
0x38
- gas消耗: 2
- 功能:获取当前合约代码的字节长度,然后压入堆栈。
- 使用场景:当合约需要访问自己的字节码时使用。
9. CODECOPY
- 操作码:
0x39
- gas消耗:3 + 3 * 数据长度 + 内存扩展成本
- 功能:复制合约的代码到EVM的内存中。它从堆栈中弹出三个参数:目标内存的开始偏移量(
mem_offset
)、代码的开始偏移量(code_offset
)、以及要复制的长度(length
)。 - 使用场景:当合约需要读取自己的部分字节码时使用。
10. GASPRICE
- 操作码:
0x3A
- gas消耗:2
- 功能:获取交易的gas价格,并压入堆栈。
- 使用场景:当合约需要知道当前交易的gas价格时使用。
总结
在这一讲,我们详细介绍了EVM中与交易有关的10个指令。这些指令为智能合约提供了与其环境交互的能力,使其能够访问调用者,calldata,和代码等信息。目前,我们已经学习了144个操作码中的126个!