美文网首页以太坊Solidity智能合约开发
智能合约编程/Dapp漏洞 -- 浮点数精度Floating P

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

作者: 末座少年 | 来源:发表于2019-03-11 12:05 被阅读0次

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

    攻击原理

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

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

    contract FunWithNumbers {

        uint constant public tokensPerEth = 10;

        uint constant public weiPerEth = 1e18;

        mapping(address => uint) public balances;

        functionbuyTokens()publicpayable{

            uint tokens = msg.value/weiPerEth*tokensPerEth; // convert wei to eth, then multiply by token rate        balances[msg.sender] += tokens;

        }

        functionsellTokens(uinttokens)public{

            require(balances[msg.sender] >= tokens);

            uint eth = tokens/tokensPerEth;

            balances[msg.sender] -= tokens;

            msg.sender.transfer(eth*weiPerEth); //    }

    }

    这个简单的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库看看是如何实现的。

    相关文章

      网友评论

        本文标题:智能合约编程/Dapp漏洞 -- 浮点数精度Floating P

        本文链接:https://www.haomeiwen.com/subject/gcbvpqtx.html