我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes
这一讲,我们介绍EVM中的GAS
指令,并介绍以太坊的Gas机制。
什么是Gas
在EVM中,交易和执行智能合约需要消耗计算资源。为了防止用户恶意的滥用网络资源和补偿验证者所消耗的计算能源,以太坊引入了一种称为Gas的计费机制,使每一笔交易都有一个关联的成本。
在发起交易时,用户设定一个最大Gas数量(gasLimit
)和每单位Gas的价格(gasPrice
)。如果交易执行超出了gasLimit
,交易会回滚,但已消耗的Gas不会退还。
Gas规则
以太坊上的Gas用gwei
衡量,它是ETH
的子单位,1 ETH = 10^9 gwei
。一笔交易的Gas成本等于每单位gas价格乘以交易的gas消耗,即gasPrice * gasUsed
。gas价格会随着时间的推移而变化,具体取决于当前对区块空间的需求。gas消耗由很多因素决定,并且每个以太坊版本都会有所改动,下面总结下:
-
calldata
大小:calldata
中的每个字节都需要花费gas,交易数据的大小越大,gas消耗就越高。calldata
每个零字节花费4
Gas,每个非零字节花费16
Gas(伊斯坦布尔硬分叉之前为 64 个)。 -
内在gas:每笔交易的内在成本为21000 Gas。除了交易成本之外,创建合约还需要 32000 Gas。该成本是在任何操作码执行之前从交易中支付的。
-
opcode
固定成本:每个操作码在执行时都有固定的成本,以Gas为单位。对于所有执行,该成本都是相同的。比如每个ADD
指令消耗3
Gas。 -
opcode
动态成本:一些指令消耗更多的计算资源取决于其参数。因此,除了固定成本之外,这些指令还具有动态成本。比如SHA3
指令消耗的Gas随参数长度增长。 -
内存拓展成本:在EVM中,合约可以使用操作码访问内存。当首次访问特定偏移量的内存(读取或写入)时,内存可能会触发扩展,产生gas消耗。比如
MLOAD
或RETURN
。 -
访问集成本:对于每个外部交易,EVM会定义一个访问集,记录交易过程中访问过的合约地址和存储槽(slot)。访问成本根据数据是否已经被访问过(热)或是首次被访问(冷)而有所不同。
-
Gas退款:
SSTORE
的一些操作(比如清除存储)可以触发Gas退款。退款会在交易结束时执行,上限为总Gas消耗的20%(从伦敦硬分叉开始)。
更详细的Gas消耗信息可以参考evm.codes。
GAS指令
EVM中的GAS
指令会将当前交易的剩余Gas
压入堆栈。它的操作码为0x5A
,gas消耗为2
。
下面,我们在极简EVM中实现GAS
。出于教学目的,目前我们仅实现部分opcode
的固定成本,其他的未来实现。
首先,我们需要在EVM
中添加一个gasUsed
属性,用于记录已经消耗的Gas:
接下来,我们需要定义每个指令的固定成本:
在每一个操作码的实现中更新Gas消耗,比如PUSH
指令:
最后,在每个操作码执行后检查Gas是否被耗尽:
测试
总结
这一讲,我们介绍了以太坊的Gas机制以及GAS
操作码。Gas机制确保了以太坊网络的计算资源不被恶意代码滥用。通过GAS
指令,智能合约可以实时地查询还剩下多少Gas,从而做出相应的决策。
至此,EVM中的144个操作码我们全部学习完了!相信你对EVM的理解一定有了质的飞跃,恭喜你!