为了账号安全,请及时绑定邮箱和手机立即绑定

目录

手机
阅读

扫一扫 手机阅读

区块链游戏 ForeverBird 开发实战

原价 ¥ 49.90

立即订阅 ¥ 49.90
2.智能合约调试
作者:赛班码农 更新时间:2019-05-06 12:07:16
学习知识要善于思考,思考,再思考。

—— 爱因斯

以太坊为智能合约提供了可信的执行环境,智能合约运行于以太链虚拟机,它是运行在可复制、共享的账本上的计算机程序,可以维持自己的状态,可以处理信息,接收、储存和发送数据。

智能合约就像一份协议或一个约定,这个协议由以太坊管理,部署之后大家原则上都不应修改协议内容,如果智能合约本身逻辑出现问题,将会造成严重的损失,所以实现智能合约时应该特别注意,避免出现漏洞。

本章使用上一章实例(Sample合约设置了一些漏洞)讲解常见的漏洞,讲解之前先介绍智能合约调试,使用调试能够查看合约状态,追溯执行逻辑,方便定位和分析问题。

调试

Remix提供了Debug功能,进行Debug之前先编译部署智能合约,部署运行方法请参考上一章。执行Sample合约的add操作,通过get函数可以查看当前余额,如下图所示。

图片描述

上面是执行了两次add操作,Debug第二次add,可以点击左边console区域的Debug按钮,也可以通过交易hash进行debug。(每次的转账或者执行合约就是一次交易,调用之后会返回一个交易hash,可以通过该hash查询交易结果、交易凭证等。)我们选择通过交易hash进行调试。点击add交易对应的Details按钮,如下图所示。

图片描述

切换到Debugger选项卡,然后输入交易hash,点击start就可以开始调试了。如果输入的hash正确,就会展示出交易和调试的相关UI,即debugger包含的主要功能,如下图所示。

图片描述

图片描述

图片描述

我们调试分析一下add函数的执行,首先查看Transaction,from标识合约的调用地址,to标识合约本身地址,hash就是本次交易的hash。

图片描述

然后,拖动滚动条到函数执行入口,点击Solidity Locals查看参数内容,点击Step detail可以查看每一步详情,可以查看当前gas剩余,每一步gas消耗等,如下图所示。

图片描述

为了追溯函数的执行分支,我们可以进行单步调试,如下图所示。

图片描述

当执行到users[msg.sender].count += 1;就产生了新的合约状态,如下图所示。

图片描述

单步执行到最后,就可以看到countamount都发生了改变。上面主要介绍了Debugger的简单调试方法,接下来我们讲解合约开发中常遇到的问题。

异常处理

我们再看一下这个合约,如果add函数传入的参数是-1,会发生什么问题呢?查看console区的交易details,交易执行结果为success。查看当前数据amount没有增加但count数量加1,如下图所示。

图片描述

图片描述

由以上可知,使用if...else...return false方式不能控制是否可以进行交易打包,不能还原合约到初始状态,逻辑判断之前的状态修改还是成功打包,这时我们的合约数据就出现了异常。针对这种情况采用的方式是使用require(amount > 0)require异常执行回退操作。关于异常处理相关内容请查看文档错误处理 - require

更改add中的判断逻辑为require(amount > 0),重新部署运行合约,然后使用debug调试流程,调用add(-1)会触发异常,require会执行回退操作,这时合约的状态数据保存不变,如下图所示。

图片描述

我们调试一下这个过程,执行到require(amount > 0)之前,可以看到合约的状态中count已经变为了2,继续执行就会发现会停留在require(amount > 0),说明这时已经产生了异常,VM会自动执行回退逻辑。

图片描述

我们对上面做个总结:

  • 合约需要条件判断,异常分支需要还原合约状态的情况下,要使用assertrequire
  • 使用if...else...return返回值和交易执行结果无关。

算法溢出

我们使用下面的合约进行测试,部署运行合约,代码如下:

contract Sample {
   mapping(address=>uint8) users;
   function add(uint8 amount) external {
        users[msg.sender] += amount;
   }

   function del(uint8 amount) external {
        uint8 old = users[msg.sender];
        uint8 n = old - amount;
        users[msg.sender] = n;
   }

   function get() public view returns (uint8) {
        return users[msg.sender];
    }
}

调用add函数,参数为123,然后调用get函数,获取数据为123,如下图所示。

图片描述

然后调用del函数,参数为124,然后再次执行get函数获取金额,这时候金额为255,如下图所示。

图片描述

这个结果超出了预期,对del进行debug分步追溯,点击debug单步调试,执行uint8 n = old - amount;之前,local数据如下:

图片描述

当执行完uint8 n = old - amount;数据如下图:

图片描述

由上图可以推测,智能合约的整型处理是循环方式,当uint8出现向下溢出时,实际数据会变为2^8+负数,即256+(-1)=255

我们再看一下,调用add加上2会出现什么情况,如下图所示。

图片描述

当加上一个数超过uint8的范围(即向上溢出)时,实际数据会变为mod 2^8之后数值,即257 mod 256 = 1。因此,智能合约实现数值的计算要进行范围判断,防止算法上下溢出。

关于算法溢出的文章请参考 Ethereum, Solidity and integer overflows

智能合约中预防算法溢出可以采用SafeMath库,SafeMath的GitHub下载路径为 SafeMath.sol,其详细介绍请参考文章 SafeMath to protect from overflows

本章主要讲解了智能合约管理和智能合约调试,以及常见的一些问题以及预防。关于智能合约的开发就讲解到这里,进一步的学习请参考以下资料:

}
立即订阅 ¥ 49.90

你正在阅读课程试读内容,订阅后解锁课程全部内容

区块链游戏 ForeverBird 开发实战
立即订阅 ¥ 49.90

举报

0/150
提交
取消
意见反馈 分销返利 帮助中心 APP下载
官方微信