Login
欢迎来到未来世界

您现在的位置是: 首页 > 计算机 > 区块链

区块链

编写智能合约的注意事项(智能合约的定义)

区块链 加入收藏
下面由小编针对编写智能合约的注意事项为您答疑解惑,希望能给您带来有一些有效参考。轰动一时的BEC事件在币圈和链圈掀起了不小的反响,智能合约安全一度成为焦点话题。通过一个小小的整数溢出漏洞,黑客盗取了巨量的BECtoken,迫使交易所不得不暂时停止交易。一时间BEC价格呈现断崖式下跌。随后,SMTtoken的智能合约也因

下面由小编针对编写智能合约的注意事项为您答疑解惑,希望能给您带来有一些有效参考。

轰动一时的BEC事件在币圈和链圈掀起了不小的反响,智能合约安全一度成为焦点话题。

通过一个小小的整数溢出漏洞,黑客盗取了巨量的BEC token,迫使交易所不得不暂时停止交易。

一时间BEC价格呈现断崖式下跌。

随后,SMT token 的智能合约也因为相同的问题遭到了攻击。

两次事件都给项目发行方和token持有者造成了巨额的损失。

公开数据表明,大量已经部署的智能合约或多或少都存在着一定的安全风险和漏洞,BEC事件也只是冰山一角。

那么就ERC20合约而言,除了整数溢出漏洞以外,还有可能面临哪些风险呢?可重入若一个程序或子程序可以「在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错」,则称其为可重入(reentrant或re-entrancy)的。

在智能合约代码中,黑客可以利用fallback函数,来递归调用包含 call.value() 的函数,从合约中重复提取以太币。

通常造成该类事件的原因是余额校验不到位或余额更新不及时。

当ERC20代币合约中涉及以太币的转出,应当尤为注意。

以一个 withdraw 函数为例,这也是ERC20合约中经常出现的函数。

以下代码中首先进行余额校验,并向msg.sender地址转出以太币,再修改 balance 数组中余额值。

当函数执行到 msg.sender.call.value(amount)() 时,黑客就可以通过msg.sender的 fallback函数来重复调用 withdraw,进而重复执行 msg.sender.call.value(_amount)(),直到gas全部消耗完毕或者合约中的以太余额全部被取完。

于是就给黑客实现 reentrancy 攻击创造了条件。

因此,SECBIT实验室的工程师强烈建议智能合约开发者在转账之前做好余额校验工作,并且将余额计算放在转账之前处理。

另外,值得重视的是,除了 call.value() 以外,任何直接或者间接调用call方法的步骤,都有可能引起回调,从而引发重入的安全事件。

转账方式风险当然,杜绝上述问题的一个更好的方式就是不使用 call.value() 进行转账。

发起以太币转账的方式有三种transfer() ,send() 和 call.value()。

我们对这三种方式进行比较。

如上图所示,其中最安全的方式当属 transfer(),一旦转账失败,transfer() 会抛出异常直接触发 revert() 事件,而另外两者不会,需要开发人员手动处理返回值。

send() 与 transfer() 唯一的区别也在于返回值,通常我们可以认为addr.transfer(v) 就相当于require(addr.send(v))。

而call.value() 与另外两者一个最明显的区别在于gas的限制上面,call.value()允许消耗掉所有的gas。

但另外两种方式由于gas消耗限制到2300,不足以完成递归调用,这也是能够避免 reentrancy 攻击的原因,上述 withdraw 的代码可以按照以下的写法来实现。

所以,SECBIT实验室的工程师建议开发人员,在ERC20合约开发过程中,如果遇到以太币转出的情况,如非必要,尽量选择使用transfer()函数来完成。

避免混淆 tx.origin 和 msg.sendersolidity提供了两种标准的方式来获取合约调用方的地址,tx.origin 和 msg.sender,但是这两者的含义是不同的,其中 msg.sender 是指直接调用当前合约的调用方地址,tx.origin 是指发起本次调用的起始调用方地址。

比如合约(或外账户地址)A去调用合约B,合约B调用合约C。

此时在合约C中读取到的 msg.sender 即为合约B的地址,tx.origin 即为A的地址。

若从一个地址直接对合约发起调用,那么 msg.sender 和 tx.origin 是一样的,否则这两个地址就不一样。

由于合约无法决定外部调用的关系,开发人员又往往容易混淆两者的含义,进而留下隐患。

以两段真实的ERC20合约为例,第一段将 tx.origin 地址设为合约的owner地址,第二段将 msg.sender 设为合约的owner地址。

在智能合约开发过程中,一定先搞明白代码的实现意图,再选择使用 tx.origin 还是 msg.sender,使用 tx.origin 的时候要尤为慎重。

依赖时间戳或者块高度在 solidity 中,允许获取当前时间戳(或者说交易所在区块的区块高度)。

但是,这并不是安全的。

一方面,时间戳是打包交易时候由矿工设置的,存在一定的人为操作因素在里面,矿工完全可以对时间戳做轻微的改动:另一方面,我们不能完全排除以太坊未来会在出块时间上做出调整的可能性,因此通过块高度来预估时间是存在一定隐患的。

上述例子中,通过块高度来限制ERC20代币购买的时间段,假如在合约发布后,购买结束前的时间段内,以太坊平台的出块时间做出了调整,那么购买时效也会发生变动。

另外,千万不要使用这两个值来产生随机数。

因为在合约外部一定范围内(即同一个区块内)是可以获取到时间戳和块高度的,所以对于同一个区块中的合约来说,这两个值就变得不随机了,这便给黑客留下了可乘之机。

整数相除在solidity中,整数相除遵循向下取整的原则。

因此,当遇到不能整除的情况时,一定要谨慎处理。

如上述代码所示,计算结果的关系是`a == b * c + a % b`,而并非`a == b*c`。

在ERC20合约开发过程中,经常会遇到使用除法的场景,要当心除法取整的问题,避免引起数据前后不一致的麻烦。

关键字过时随着solidity不断的升级更新,老版本上的一些用法也逐渐被标记为过时然后废弃掉。

因此在合约开发和升级过程中,一定要当心过时的用法,避免造成不必要的损失。

下表展示了部分过时用法和其替代用法可供参考。

以上就是小编为您介绍的关于编写智能合约的注意事项的相关信息,本文到此结束。

图集详情底部广告位