Solidity Staticcall EIP 214

本文详细介绍了以太坊中的staticcall操作,解释了其与常规call的区别,及其在防止状态变更中的应用。同时,文章也讨论了staticcall的安全性问题,如拒绝服务攻击和重入攻击,并提供了代码示例来说明其使用方法。

Staticcall 类似于常规的以太坊调用,不同的是,如果发生状态更改,它将回滚。它不能用于转账以太币。无论是 EVM 操作码、Yul 汇编函数还是 Solidity 内置函数,都称为 staticcall。

EIP 214

Staticcall 是在 EIP 214 中引入的,该提案于 2017 年作为 Byzantium 硬分叉的一部分被添加到以太坊中。对于那些只需要返回值而不希望有状态更改的函数,它增加了一层安全性。如果被调用的合约导致状态更改,Staticcall 将回滚,这些状态更改包括:记录事件、发送以太币、创建合约、销毁合约或设置存储变量。读取存储变量是可以的。只要不导致状态更改,不转账以太币的调用也是允许的。

View 函数

Solidity 将调用外部合约的 view 函数编译为在底层使用 staticcall。

以下是一个简单的示例:

contract ERC20User {
    IERC20 public token;

    // 其余代码

    function myBalance() public view returns (uint256 balance) {
        balance = token.balanceOf(address(this));
    }

    function myBalanceLowLevelEquivalent() public view returns (uint256 balance) {
        (bool ok, bytes memory result) = token.staticcall(abi.encodeWithSignature("balanceOf(address)", address(this)));
        require(ok);

        balance = abi.decode(result, (uint256));

    } 
}

如果 balanceOf 更改了状态,代码将回滚。

元参数

可以以下方式指定转发的 gas 数量。转发的 gas 数量受 EIP 150 中描述的 63/64 规则限制。

staticcall{gas: gasAmount}(abiEncodedArguments);

攻击向量

尽管 staticcall 在某种程度上比常规调用“更安全”,但这并不意味着在使用时可以忽视安全性。

拒绝服务

Staticcall 仍然会受到 gas 消耗型拒绝服务攻击的影响。考虑上述 ERC20 代币在 balanceOf 函数中放入一个无限循环的情况。根据 EIP 150,调用合约仍然会有剩余的 gas,但只有原始数量的 1/64。

只读重入

Staticcall 的另一个攻击向量是获得了错误的值。在 只读重入攻击 中,代币值通过闪电贷被临时操纵,因此任何查看值(通过 staticcall)的智能合约都可能因预言机操纵而被攻击。

Yul 汇编中的 Staticcall

在 Yul 汇编中,staticcall 的参数如下:

let ok := staticcall(gas, addressToCall, in, insize, out, outsize)

参数 out 和 outsize 是内存中返回值将被复制到的区域。然而,这两个值通常设置为零,并使用“returndatacopy”和“returndatasize”来灵活处理可变长度的返回值。

这些操作码在 EIP 211 中引入,消除了预测其他智能合约返回值长度的需求。

参见以下示例:

returndatacopy(0, 0, returndatasize())

变量 in 和 insize 指向内存中调用外部合约的(通常是 abi 编码的)数据部分。

如果 staticcall 成功,变量 ok 返回 true;如果回滚,则返回 false。回滚不会向上传递。

与预编译智能合约的使用

Staticcall 是与 以太坊预编译合约(地址 0x01 到 0x09)交互的适当方式,因为它们都不会更改状态。

以下示例将计算一个 uint 的 SHA256。请注意,此预编译直接哈希数据,而不应进行 abi 编码

 function getSha256(uint256 x) public view returns (bytes32 hashOut) {
    (bool ok, bytes memory result) = address(0x02).staticcall(abi.encode(x));
    require(ok);

    hashOut = abi.decode(result, (bytes32));
} 

了解更多

参见我们的专家 Solidity 开发者训练营 以了解更多高级以太坊开发概念。我们还提供 免费的 Solidity 课程

最初发布于 2023 年 4 月 10 日

  • 原文链接: rareskills.io/post/solid...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/