玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

news2024/11/20 7:08:57

错误处理

作为开发者的我们知道,我们所编写出来的程序难免会出现 bug ,而要做的是捕获异常,给用户抛出一个友好地错误提示。

而在 Solidity 中,根据状态恢复异常来处理错误,该异常将撤销在当前调用中对状态所做的所有修改,与此同时,还向调用者标记错误。

它有许多功能来解决在编译时或运行时可能发生的潜在问题。即使语法错误检查发生在编译时,运行时错误也很难捕捉,主要发生在合约执行过程中。一些运行时错误的例子包括除以0的类型错误,数组超出索引错误,等等。

实际上,Solidity的错误处理确保了原子性这一属性。当一个智能合约调用因错误而终止时,所有的状态变化(即对变量、余额等的改变)都会被恢复,一直到合约调用链。

Solidity 有三种处理错误的方式:requireassert*revert*

require

require(condition);
require(condition, "description");

require 语句声明了运行该函数的先决条件,即它声明了在执行代码之前应该满足的约束条件。它接受一个参数并在评估后返回一个布尔值,它也有一个自定义的字符串信息选项。如果是false,就会产生异常并终止执行。未使用的gas被返回给调用者,状态被逆转为原始状态。以下是require类型的异常被触发的一些情况。

  • 1、当require(条件表达式)被调用时,其条件表达式结果为false
  • 2、当一个被消息调用的函数没有正确结束时。
  • 3、当使用 new 关键字创建一个合约,而这个过程没有正常结束。
  • 4、当一个无代码的合约被定位到一个外部函数时。
  • 5、当使用公共getter方法将以太坊发送到合约时。
  • 6、当.transfer()方法失败时。
    • 6.1、当一个断言被调用时,其结果是假的。
    • 6.2、当一个函数的零初始化变量被调用时。
    • 6.3、当一个大值或负值被转换为一个枚举值时。
    • 6.4、当一个值被零除或模数化的时候。
    • 6.5、当在一个索引中访问一个数组,这个数组太大或者是负数。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract requireContract{
    
    function requireTest(uint256 _number) pure public {
        require(_number < 100, "number too big");
    }
 }

在这里插入图片描述

引入require语句的原因是为了使代码更具可读性。require 语句应在以下情况下使用。

  • 1、验证用户输入,正如我们上面所做的。
  • 2、在任何行动之前验证状态条件
  • 3、验证来自外部合同的响应
  • 4、检查溢出和欠溢出的情况

assert

assert语句也可以撤销所有的状态变化,但它会消耗事务中提供的所有气体,即使它没有被使用。这使得它不如revert或require宽松,因此使用频率较低。Assert是为 "真正糟糕的事情 "而存在的,应该只用于内部错误。

assert应该在以下情况下使用。

  • 1、检查常量,如this.balance > totalSupply
  • 2、执行后验证状态
  • 3、溢出和下溢检查,如果revert/require不合适的话

Solidity 在某些情况下自动创建assert型异常。

  • 1、被零除法或模数
  • 2、访问一个超出其界限的数组
  • 3、将过大或负数转换为枚数
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract assertContract{
    
    function assertDiv(int x,int y) pure public returns(int){
        assert(y == 0);
        return x / y;
    }
 }

在这里插入图片描述

revert

revert语句将停止执行并撤销所有的状态变化。剩余的gas将被退还给调用者(但到目前为止所使用的gas将消失)。revert允许返回一个值。这可以作为一个错误信息来澄清为什么执行revert语句。

有两种方式来触发revert

revert CustomError(arg1, arg2)
revert("description")

第一种是自定义的error类型(Solidity 0.8.4 出现的新类型)。

使用自定义错误实例通常会比传递一个字符串作为描述在消耗gas方面更便宜。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract revertContract{
    //自定义错误实例
    error WrongNumber(uint256 threshold, uint256 number);
    string public status;
    uint256 myThreshold = 100;

    function revertString(uint256 _number) public{
        status = "error";
        if(_number > myThreshold){
            revert("Number too big");
        }
        status = "success";
    }
    function revertCustomError(uint256 _number) public{
        status = "error";
        if(_number > myThreshold){
            revert WrongNumber({ threshold: myThreshold, number: _number});
        }
        status = "success";
    }

 }

在这里插入图片描述

在这里插入图片描述

三种方式处理错误消耗 gas 对比

  • 1、revert 语句,从示例可以看出最终耗费了 46607 gas

在这里插入图片描述

  • 2、revert 语句与自定义 error 相配合,反而比比上述消耗跟多的gas,它最终消耗了46733 gas

在这里插入图片描述

  • 3、 我们在示例中使用了assert语句,明显比revert语句消耗gas更少,它最终消耗了22077 gas

在这里插入图片描述

  • 4、 我们最后来看看equire语句。第一种情况是带了错误描述,而它却又比assert语句少耗费了gas,从示例可以看出它消耗了21898 gas

在这里插入图片描述

  • 5、而require第二种情况,便是只有条件表达式,可以看出在示例中它以上几种情况消耗gas最少的,它最终消耗了21605 gas

在这里插入图片描述

从上述的示例可以看出,使用require语句检查错误,最终消耗的gas是最少的,这也证明了很多项目使用require的原因了。

try catch 捕获异常

现在我们可以用它们来处理外部函数调用的失败,而不需要回滚整个事务(被调用函数中的状态变化仍然会被回滚,但调用函数中的变化不会)。

//CharitySplitter.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract CharitySplitter {
    address public owner;
    constructor (address _owner) {
        require(_owner != address(0), "no-owner-provided");
        owner = _owner;
    }
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "./CharitySplitter.sol";

contract CharitySplitterFactory {
    mapping (address => CharitySplitter) public charitySplitters;
    uint public errorCount;

    event ErrorHandled(string reason);
    event ErrorNotHandled(bytes reason);

    function createCharitySplitter(address charityOwner) public {
        try new CharitySplitter(charityOwner)
            returns (CharitySplitter newCharitySplitter)
        {
            charitySplitters[msg.sender] = newCharitySplitter;
        } catch {
            errorCount++;
        }
    }
}

在这里插入图片描述

try 关键词后面必须有一个表达式,代表外部函数调用或合约创建( new ContractName())。

在表达式上的错误不会被捕获(例如,如果它是一个复杂的表达式,还涉及内部函数调用),只有外部调用本身发生的revert 可以捕获。 接下来的 returns 部分(是可选的)声明了与外部调用返回的类型相匹配的返回变量。 在没有错误的情况下,这些变量被赋值,合约将继续执行第一个成功块内代码。 如果到达成功块的末尾,则在 catch 块之后继续执行。

Solidity 根据错误的类型,支持不同种类的捕获代码块:

  • catch Error(string memory reason) { ... }: 如果错误是由 revert("reasonString")require(false, "reasonString") (或导致这种异常的内部错误)引起的,则执行这个catch子句。
  • catch Panic(uint errorCode) { ... }: 如果错误是由 panic 引起的(如: assert 失败,除以0,无效的数组访问,算术溢出等),将执行这个catch子句。
  • catch (bytes memory lowLevelData) { ... }: 如果错误签名不符合任何其他子句,如果在解码错误信息时出现了错误,或者如果异常没有一起提供错误数据。在这种情况下,子句声明的变量提供了对低级错误数据的访问。
  • catch { ... }: 如果你对错误数据不感兴趣,你可以直接使用 catch { ... } (甚至是作为唯一的catch子句) 而不是前面几个catch子句。

有计划在未来支持其他类型的错误数据。 ErrorPanic 字符串目前是按原样解析的,不作为标识符处理。

为了捕捉所有的错误情况,你至少要有子句 catch { ... }catch (bytes memory lowLevelData) { ... }.

returnscatch 子句中声明的变量只在后面的块的范围内有效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/106540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[思维模式-9]:《如何系统思考》-5- 认识篇 - 改变开环、组合逻辑的线性思考,实施闭环、时序逻辑的动态思考。

目录 第1章 因果关系 1.1 因果关系 1.2 因果关系的特点 1.3 因果关系的类型 第2章 线性思考遇到的问题&#xff1a;开环思维、组合逻辑 2.1 开环系统 2.2 组合逻辑 2.3 线性关系 2.4 什么是线性思维&#xff1a;线性因果关系 2.5 线性思维的数学本质 2.6 线性思维的…

自动化药房出药升降机选型设计

一、 运动规划、运动参数的确定 1、 运动参数计算 运动参数主要通过速度规划确定&#xff0c;速度规划采用直线速度特性&#xff0c;如图所示。 运动方程为&#xff1a; 2、 X方向的速度和加速度的估算 已知参数&#xff1a; X方向行程:1…

stream_component_open函数分析

stream_component_open() 函数主要作用是打开 音频流或者视频流 对应的解码器&#xff0c;开启解码线程去解码。 流程图如下&#xff1a; stream_component_open() 的函数定义如下&#xff1a; /* open a given stream. Return 0 if OK */ static int stream_component_open(…

K8S知识点及dashboard操作

1.什么是K8S&#xff1f; K8S是一组服务器集群&#xff0c;可以在集群的各个节点上运行特定的容器。 K8S所管理的是&#xff1a;集群节点上的容器 特性&#xff1a; 自我修复&#xff0c;弹性伸缩&#xff08;根据实时服务器的并打情况&#xff0c;增加或收缩容器数量&…

网络编程套接字Socket(通过两个用例,逐行注释,详细理解)干活满满建议收藏

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言1.分类1.流套接字2.数据报套接字3.原始套接字2.Socket通信模型 3.UDP套接字编程1. DatagramSocket API1.构造方法1.DatagramSocket()2.DatagramSocket(int port)…

C语言之复合类型上卷(十八)(阴阳两极)

上一篇: C语言之内存管理&#xff08;十七&#xff09;&#xff08;转世灵童现世&#xff09; 逐梦编程&#xff0c;让中华屹立世界之巅。 简单的事情重复做,重复的事情用心做,用心的事情坚持做&#xff1b; 文章目录前言一、什么是结构体&#xff1f;二、结构体的定义及初始化…

USB TO SPI(上海同旺电子)调试器调试MCP3201 A/D 转换器

所需设备&#xff1a; 1、USB TO SPI(上海同旺电子)&#xff1b; 2、MCP3201 12 位A/D 转换器; 特性 • 12 位分辨率 • 1 LSB DNL &#xff08;最大值&#xff09; • 1 LSB INL &#xff08;最大值&#xff09;&#xff08;MCP3201-B&#xff09; • 2 LSB INL &#xff…

pdf文件太大怎么变小,如何压缩pdf大小

pdf文件太大怎么变小&#xff1f;如果你是Windows电脑&#xff0c;可以使用PDF编辑器来减小PDF文件的大小&#xff0c;比如这款出色的PDF压缩工具-易我PDF编辑器&#xff0c;它的“压缩”功能提供了两种减小文件大小的方法&#xff0c;这使得它既适合那些只想获得更小的PDF的人…

【vscode】c++程序的自动编译及调试(环境centos)

目录1.新增配置文件&#xff08;1&#xff09;c_cpp_properties.json&#xff08;2&#xff09;files.associations&#xff08;3&#xff09;tasks.json(4)CMakeLists.txt2.断点调试1.新增配置文件 VS Code的配置文件一般是指特定目录下的JSON文件。所谓JSON是一种文本格式&a…

LCF-ATEPC(2020 Elsevier)面向中文的方面级提取和分类

论文题目&#xff08;Title&#xff09;&#xff1a;A Multi-task Learning Model for Chinese-oriented Aspect Polarity Classification and Aspect Term Extraction &#xff08;面向中文的方面极性分类和方面项提取的多任务学习模型) 研究问题&#xff08;Question&#…

适用于 Windows 10/11 电脑 的 5 大好用的离线录屏软件

屏幕录制应用程序可以数字记录出现在任何设备或 PC 屏幕上的内容&#xff0c;并同时以高清流式传输音频和视频。 因此&#xff0c;他们帮助创建营销视频、跟踪客户行为、设计产品演示、监控员工活动、录制教育内容、网络研讨会内容和业务会议内容。 现在您已经意识到屏幕录…

VS系列多通道振弦传感器无线采发仪的数据发送说明

每次设备启动后会将采集到的传感器数据进行内部存储&#xff0c;并在设置好的时间间隔将数据发送出去&#xff0c;通过修改“数据发送方式”参数&#xff0c;监测数据可由数据接口输出也可经由无线网络发送。在发送监测数据时&#xff0c;可通过修改“数据包协议”参数来设置所…

函数和数组习题

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C语言基础习题】 文章目录知识点习题2.实现一个整型数组的冒泡排序&#xff08;编程体&#xff09;。3.编程题&#xff1a;创建一个整型…

springcloud,springboot各个版本之间的关系

1 版本关系 在实际的开发中如果要自己搭建矿建&#xff0c;发现springcloud&#xff0c;springboot的版本可能是首先需要确定的&#xff0c;那么他们之间的关系是什么呢&#xff1f;看官网&#xff0c;地址 Spring Cloud 左侧是cloud的版本&#xff0c;右侧是对应的文档&…

Splunk Window 客户端迁移

最近客户的Splunk deployment server 要迁移,伴随着client 端的配置也要相应的调整: 先看一下架构: 看一下主要的参数: Summary of key terminology Heres a recap of the key definitions: TermMeaningdeployment serverA Splunk Enterprise instance that acts as a c…

Java中的日期与时间

Java中的日期与时间\huge{Java中的日期与时间}Java中的日期与时间 JavaJavaJava中有很多类是专门用于描述日期类的。 Date类 DateDateDate类&#xff1a;用于表示当前所在系统的日期时间信息。 Date类的构造器 示例&#xff1a; Date d new Date(); System.out.println(d);…

12月第3周榜单丨B站UP主排行榜(飞瓜数据B站)发布!

飞瓜轻数发布2022年12月12日-12月18日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营能力强的…

Redis高级篇

redis的四个问题&#xff1a; 1.Redis是基于内存存储,服务重启可能会丢失数据; 2.并发能力问题&#xff1a;单节点Redis能力虽然不错,但也无法满足如618这种高并发的场景(618并发 数量达到数十万甚至上百万); 3.如果reids宕机,服务不可用,则需要一种自动的故障恢复手段; 4.存…

自学Python可以找到工作吗?

自学Python可以找到工作吗&#xff1f;自学Python找工作主要看自己的学习能力&#xff0c;自学能力很强学完并精通当然可以工作&#xff0c;不过对于大多数人而言一般都挺难&#xff0c;学习不成系统&#xff0c;遇到问题没人解决很容易放弃半途而废。 学Python能干很多很多事…

NodeJS安装-Vue模块化项目构建

NodeJS安装-Vue模块化项目构建 一、环境准备&#xff08;NodeJS安装&#xff09; 1. 安装NodeJS 官网自行下载&#xff0c;并安装 2. 配置npm的全局安装路径 npm config set prefix "D:\soft_install\dev\qianduan_dir\nodejs"3. 切换npm的淘宝镜像&#xff0c…