availableGas = availableGas - base
gas := availableGas - availableGas/64
if !callCost.IsUint64() || gas < callCost.Uint64() {
return 0, errNotEnoughGas
}
理由
目前,作为这些操作码一部分指定的 gas 只是一个最大值。并且由于 EIP-150 的行为,外部调用可能会被赋予比预期更少的 gas(比作为 CALL 一部分指定的 gas 更少),而当前调用的其余部分则被赋予足够的 gas 以继续并成功。事实上,由于使用 EIP-150,外部调用最多被赋予 G - Math.floor(G/64),其中 G 是 CALL 时的 gasleft(),当前调用的其余部分被赋予 Math.floor(G/64),这在许多情况下对于交易成功来说已经足够了。例如,当 G = 6,400,000 时,交易的其余部分将被赋予 100,000 gas,这在许多情况下已经足够了。
对于要求外部调用仅在有足够 gas 的情况下失败的合约来说,这是一个问题。智能合约钱包和一般的元交易中存在这种需求,其中执行交易的人不是执行数据的签名者。因为在这种情况下,合约需要确保调用完全按照签名用户的意图执行。
但对于简单的用例来说也是如此,例如检查合约是否通过 EIP-165 实现接口。 事实上,正如这样的 EIP 所指定的那样,supporstInterface 方法被限制为使用 30,000 gas,因此从理论上讲,可以确保 throw 不是由于 gas 不足造成的。不幸的是,由于不同的 CALL 操作码的行为方式,合约不能简单地依赖于指定的 gas 值。他们必须通过其他方式确保调用有足够的 gas。
事实上,如果调用者不确保为被调用者提供 30,000 gas 或更多 gas,则被调用者可能会因 gas 不足而 throw(而不是因为它不支持该接口),并且父调用将被赋予最多 476 gas 以继续。这将导致调用者错误地解释为被调用者未实现所讨论的接口。
虽然可以通过根据 EIP-150 在调用之前检查剩余的 gas 和所需的精确 gas 来强制执行此类要求(请参阅该 bug 报告 中提出的解决方案)或在调用之后(请参阅 此处 的本机元交易实现),如果 EVM 允许我们严格指定要给予 CALL 多少 gas,那么将会更好,这样合约实现就不需要密切遵循 EIP-150 行为和当前的 gas 价格。
其中 E 是从调用 gasleft() 到实际调用之间的操作所需的 gas 加上调用本身的 gas 成本。
虽然可以简单地高估 E 以防止在没有为当前调用提供足够 gas 的情况下执行调用,但最好让 EVM 自己完成精确的工作。随着 gas 价格的不断发展,拥有一种机制来确保将特定数量的 gas 传递给调用非常重要,因此可以使用此类机制,而无需依赖特定的 gas 价格。
此解决方案不需要计算 E 值,因此不依赖于特定的 gas 价格(EIP-150 的行为除外),因为如果给予调用的 gas 不足并因此而失败,则上述条件将始终失败,从而确保当前调用将回退。
但是,如果给出的 gas 较少并且外部调用 EARLY 回退或成功(因此调用后剩余的 gas > txGas / 63),则此检查仍然会通过。
如果作为 CALL 的一部分执行的代码由于针对所提供的 gas 进行检查而回退,则这可能是一个问题。就像元交易中的元交易一样。