当前位置:   article > 正文

智能合约编程/Dapp漏洞 -- 浮点数精度Floating Points and Precision

floating point stalls是什么

到Solidity v0.4.24为止,Solidity并不支持定点数和浮点数。这意味着浮点数的表示必须要用Solidity的整数来表示。如果实现不正确的话,会导致合约漏洞。

攻击原理

由于Solidity里并没有浮点数,程序员需要用标准的整型来自己实现浮点类型。在这个过程中,有一系列的陷阱,这里会介绍其中几个。

下面是程序示例 (简单起见,请忽略其中可能的over/under flow 问题)

  1. contract FunWithNumbers {
  2.     uint constant public tokensPerEth = 10;
  3.     uint constant public weiPerEth = 1e18;
  4.     mapping(address => uint) public balances;
  5.     function buyTokens() public payable {
  6.         uint tokens = msg.value/weiPerEth*tokensPerEth; // convert wei to eth, then multiply by token rate
  7.         balances[msg.sender] += tokens;
  8.     }
  9.     function sellTokens(uint tokens) public {
  10.         require(balances[msg.sender] >= tokens);
  11.         uint eth = tokens/tokensPerEth;
  12.         balances[msg.sender] -= tokens;
  13.         msg.sender.transfer(eth*weiPerEth); //
  14.     }
  15. }

 

这个简单的token买卖合约有几个明显的问题。尽管计算公式是对的,但是由于Solidity缺乏浮点数支持会导致错误的结果。比如在第7行买tokens on line [7], 如果买的数额小于1 ether的话,最初的除法结果是0,导致最后的结果也是0。同样的,在卖tokens的时候小于10最后结果也会是0。实际上,取整一直是向下取整,所以卖29tokens,最终结果是2 token.

这个合约的问题在于精度在最接近的ether (i.e. 1e18 wei)。如果在ERC20合约里需要高精度的话,情况会很复杂。

防护方式

保持正确的精度在智能合约编程中是非常重要的,特别是在处理比率和兑换率的场合。你必须尽量保证分子比较大。比如在例子中,我们使用了兑换率tokensPerEth。此处最好使用weiPerTokens以保证分子是个比较大的数。在想获取token的数量的时候使用msg.value/weiPerTokens.这样会保证高精度

另外一个要注意的是关于操作的次序问题。在上面的例子中,在计算购买tokens的数额时,使用了msg.value/weiPerEth*tokenPerEth。必须注意这里除法在乘法之前。最好把乘法放在除法之前比如msg.value*tokenPerEth/weiPerEth.

最后,为了执行数学运算,最好定义一个虚拟的精度数,把所有的变量都变换成一个高精度数,只有在输出的时候在把他们变换回来。一般,我们会使用uint256类型(可以省Gas费),范围大致有大概60位其中有些位可以用来保持精度。在Solidity合约中最好把所有的变量都变换成一个高精度数,在外部的app中的时候在把他们变换回来。 这也是ERC20中Decimal类型工作的方式。建议去学习Maker DAO合约的DSMath库看看是如何实现的。

转载于:https://my.oschina.net/gavinzheng731/blog/3019752

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/857137
推荐阅读
相关标签
  

闽ICP备14008679号