在运行时错误是最常遇到的情况,你知道 Error
与 Panic
的细微差别吗? 发生 Panic 错误真的会消耗所有的 gas 么,本文揭晓答案。
在上一篇介绍了编译时错误(由 Solidity 编译器生成的错误)之后,我们将在本文介绍运行时错误(与链上的合约交互时发生的错误)。
我们将看到,Solidity 可以生成 4 种主要类型的错误:Error(string)
、Panic(uint256)
、自定义 error
和 invalid
。本文将介绍每种错误的规则和语义。最后,我们将预测一下可能添加到 Solidity 编程语言中的新错误类型。
本文内容有:
在许多情况下, Solidity 合约代码都可能出现运行时错误。
与 Solidity 相关的一些标准运行时错误包括:
require()
时,参数结果为false。new
关键字创建合约失败,且进程无法正常结束。address.code.length
)合约指向外部函数时。(当通过 constructor
运行时,请注意 isContract()
失效)。view
)或pure
方法时发送 ethers (使用 msg.value
)。payable
的函数发送以太币 (即 msg.value
) 时。assert()
中的条件为 false
时。function
类型的零初始化变量时。enum
时。然而,我们会发现,在这份情况列表中,每种情况都属于特定的错误类别,取决于错误的原因。
Error(string)
→ 通过内置函数 require
和 revert
触发。Panic(uint256)
→ 通过内置函数 assert
触发,或在某些情况下由编译器创建。error
→ 通过 revert CustomError()
触发。invalid
操作码 → 通过汇编触发。示例 1:
Error(string)
的keccak256
哈希值为 0x08c379a0afcc32b1a39302f7cb8073359698411ab5fd6e3edb2c02c0b5fba8aa。
如果保留前 4 个字节,我们将得到 Error(string)
错误选择器为 0x08c379a0
。
示例 2:
Panic(uint256)
的 keccak256
哈希值是 0x4e487b71539e0164c9d29506cc725e49342bcac15e0927282bf30fedfe1c7268 。
如果保留前 4 个字节,我们将得到Panic(uint256)
错误选择器 0x4e487b71
。
下表总结了 Solidity 中不同类型的错误:
错误类型 | 错误签名 | bytes4 错误选择器 |
---|---|---|
String Error | Error(string) |
0x08c379a0 |
Panic | Panic(uint256) |
0x4e487b71 |
Custom Errors | 用户错误自定义 | 基于自定义错误的名称 及 参数(若有) |
Invalid | none | none |
让我们看看 Solidity 代码示例,以便更好地理解
pragma solidity ^ 0.8 .0;
contract SolidityErrors {
error InsufficientBalance(
uint256 amount,
uint256 balance
);
function testRevertErrorEmpty()
public
pure {
revert();
}
function testRevertError()
public
pure {
revert("something went wrong!");
}
function testPanicError(uint256 a)
public
pure
returns(uint256) {
return 10 / a;
}
function testCustomError(uint256 amount)
public
view {
revert InsufficientBalance(
amount,
address(this).balance
);
}
function testInvalid()
public
pure {
assembly {
invalid()
}
}
}
如果我们在 Remix 中调试交易,就会发现在操作码 REVERT
之前,有一个 4 字节的值被推入堆栈。这 4 个字节的值对应于正在抛出的错误类型的选择器。
<p align=center>在 Remix 中调试以找到每种 Solidity 错误类型的选择器 </p>
内置错误 Error(string)
用于 "回退并提示错误信息"。
在下列情况下会出现 Error(string)
异常(在参数 string
中提供了错误信息):
调用 require(x, "error message")
,其中 x
值为 false
,并且你提供了一条 error message
。
使用 revert()
或 revert("description")
时。
如果你执行外部函数调用,而调用的目标是不包含相应的代码。
如果你的合约通过公共函数(包括构造函数和回退函数)接收以太,而该公共函数未包含payable
修饰符。
如果你的合约通过公共 getter 函数接收以太币。
所提供的 string
会以调用函数 Error(string)
的方式进行 abi 编码。
当涉及到 Solidity 中的 Panic(uint256)
类型错误时,有一条唯一的规则需要牢记:
Panic(uint256)
错误不应出现在无错误的代码中。
如果你在开发和测试 Solidity 合约时遇到 Panic
类型的错误: 你应该修复你的代码!
已经部署在网络上的智能合约在运行时出现 "Panic" 错误,很可能是智能合约代码中的一个错误,或者是智能合约的设计要求没有被完全满足或正确。
在此案例中,必须修复 Solidity 代码中的错误,这样智能合约就不会在运行时再次出现同样的 "Panic" 错误。
Solidity 文档更强调了这一点:
正常运行的代码绝不会产生 "Panic",即使是无效的外部输入也不会。
如果发生了这种情况,那么你的合约中就有一个错误,你应该修复它。
让我们来看一个真实世界的例子,看看 "Panic "错误是如何发生和修复的。请看下面的 Solidity 代码段。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract PanicErrorExample {
function countTrailingZeroBytes(bytes32 data) public pure returns(uint256) {
uint256 index = 31;
// CHECK each bytes of the key, starting from the end (right to left)
// skip each empty bytes `0x00` to find the first non-empty byte
while (data[index] == 0x00) index--;
return 32 - (index + 1);
}
}
顾名思义,该函数用于计算bytes32
数据值末尾的0x00
零字节数。
例如,当提供下面的值作为 data
参数时,它将返回 4
:
data = 0xcafecafecafecafecafecafecafecafecafecafecafecafecafecafe00000000
result = 4
![img](https://img.learnblockchain.cn/2023/07/28/41478.png!l...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!