针对区块链安全问题,成都安联技术团队每周都会对智能合约的安全漏洞进行分析和连载,希望帮助程序员写出更安全、更牢固的合约,防患于未然。
导语:“以言治骨角者,既斩之,又议之;治玉者,既有打磨,又有重磨,有精,但求其精。”宋珠熙
上次继承的变量名和真实的不一样,构造函数名失去了意义。
在编写智能契约的过程中,采用正确的构造函数声明,保持契约名与构造函数名一致,是保证构造函数特殊性和唯一性的前提,也是构建契约安全基础的重中之重。构造函数中的所有者权限既不能太强,也不能落入他人之手。继承的父函数变量和新声明的子类函数变量之间有细微的差别。当名称相同时,将调用父类的变量进行操作。只有分析和了解这些细节,才能防止开发中的失误,同时防止蜜罐以类似手段套取资金。规范的正确名称也体现了开发者对项目和安全性的尊重。
除四除五也放弃,浮点精度数无小事。
经过第十一期的讲解和学习,我们逐渐从常见的严重漏洞延伸到合同开发的细节和不易察觉的安全隐患,旨在倡导更详细的安全开发流程和专注于安全防范的“工匠精神”。在序列化的开始,我们提到了臭名昭著的整数溢出漏洞,并强调使用SafeMath库来检查溢出,以防止严重的后果。请往深处走:溢出漏洞类型综合分析。
然而,在合约的数学运算中,不仅存在溢出问题,还存在与精度密切相关的除法运算和浮点运算。说到浮点和精度,这是计算机最基本也是最有争议的话题。听说即使是最简单的计算器,精度也比超级计算器高。举个很实际的例子,你用主流编程语言算0.2 0.4。测试的话,可以使用Chrome和FireFox浏览器,按F12进入控制台,然后输入计算结果。结果变成了:
然后用最简单的计算器,手持计算器或者系统自带的计算器算出结果。肯定是0.6,对比一下就清楚了。所以我们可以说,计算精度的关键不在于它的频率和内存,而在于它是如何设计、表示和计算的。
在坚固性上,浮点和精度也有类似的争议。本期我们将讨论浮点计算带来的精度漏洞。
对于扎实,我们需要知道以下两个方面。
1.浮点型,固定长度浮点型
Solidity目前不支持浮点,也不完全支持定长浮点。
定长浮点型可以用来在Solidity中声明变量,但不能用来赋值。
Fixed/ufixed:表示有符号和无符号的固定位浮点数。关键字是ufixedMxN和ufixedMxN。
m表示该类型占用的位数,可以是8到256位,步长为8。
n代表小数点的个数,可以是0到80。
2.除法运算
除法的结果会是四比五,只取整数部分。例如,下面的运算显示了直接将5除以2和用分数表示5除以2之间的比较。
这显示所有除法的结果。如果有小数点,小数点后的部分将被丢弃,只取整数部分。
所以,官方的建议是,如果你需要更高的精度,请考虑使用乘法器,或者存储分子和分母。
想象一下,如果令牌运算的结果小于1Ether,那么0。XXX大约等于0。类似地,4.9个令牌将大约等于4,导致一定程度的准确度损失。由于代币的经济属性,精准度的损失相当于资产的损失,所以在频繁交易时会带来积少成多的问题。这让我想起了每个人都在讲的笑话
如上所述,Solidity的最新版本是0.4.25,仍然不支持浮点型或定长浮点型。一般浮点数在Solidity中用整数类型表示,比如ERC20中的Token。如果使用不正确的转换,可能会出现错误甚至漏洞。
由于Solidity中没有浮点类型,开发者需要使用标准的整数数据类型来实现自己的类型。在这个过程中,开发者可能会遇到一些陷阱。
让我们从一个代码示例开始(为了简单起见,忽略任何上溢/下溢问题)。
这份简单的代币买卖合同,在代币买卖上有一些明显的问题。虽然买卖代币的数学计算是正确的,但是缺少浮点数会给出错误的结果。例如,当使用uint toke时
Ether,最初的除法将导致结果为0,进而使最后的乘法结果也是0(eg:200 wei除以1e18weiPerEth等于0)。
同理,当销售代币时,数量少于10代币的情况下将会收到0 ether。事实上,这里四舍五入总是向下约等于最近的整数,所以比如销售29 tokens,将只能收获2 Ether。
这个合约的问题是精度只能到最近的Ether(即1e18 wei)。当面临更高的精度时,例如使用decimals扩展精度的ERC20代币,事情会变得很棘手。
保持智能合约的正确精确度非常重要,尤其是在处理反映经济决策的比例或者比率时。
解决方案:
1. 大分子
所以我们应该确保使用的任何比例或比率都在分数中拥有大分子。例如,我们tokensPerEth在示例中使用了费率。使用weiPerTokens这将是一个很大的数字,效果自然会更好。这样可以解决我们用msg.sender/weiPerTokens计算的代币数量问题。同时给出更精确的结果。
2. 运算顺序
要记住的另一个策略是注意操作的顺序。在上面的例子中,购买代币的计算是msg.value/weiPerEth*tokenPerEth。请注意,这里的除法发生在乘法之前。如果计算首先进行乘法,然后再进行除法,那么这个例子会达到更高的精度,例如修改为msg.value*tokenPerEth/weiPerEth。
3. 精度转换
最后,当为数字定义任意精度时,将变量转换为更高精度,执行所有数学运算,然后最后在需要时将其转换回输出精度不失为一个好主意。通常uint256使用它们(因为它们对于gas使用来说是最佳的),它们的范围约为60个数量级,其中一些可用于提升数学运算的精确度。
可能会出现这样的情况:最好先将所有变量高精度地保持稳定,然后在外部应用程序中转换回较低的精度(这实际上是ERC20代币合约中decimals变量的工作原理)。要了解如何完成此操作的示例以及要执行此操作的库,可查看Maker DAO DSMath。
从上面的漏洞修复手法我们可以看出,三个措施正好对应了精度的设计,精度的运算,精度的表示三个方面。从这个角度来看,智能合约运算中的精度问题其实追根究底也是计算机精度问题的延伸。但是由于区块链产业目前的经济属性,精度无疑成为安全的一个重要考量,在精度的问题上多下功夫,有益无害。根据官方的消息,Solidity或者其他开发语言在以后也会在浮点和精度的问题上做进一步的完善和提升。说明官方也在努力为这个产业安全性准确性的进行不断完善。我们在关注和投身这个产业的过程中,做好细节的完善,也是为其添砖加瓦的一种最好体现。
引用:
<1>: Solidity Security: https://blog.sigmaprime.io/solidity-security.html
<2>: Ethereum Safety: https://github.com/ethereum/wiki/wiki/Safety#beware-rounding-with-integer-division
<3>: 代码之谜(五)- 浮点数:http://justjavac.iteye.com/blog/1724438
<4>: Solidity学习 (3):https://blog.csdn.net/sunniy27/article/details/78806685
<5>: 以太坊智能合约 ―― 最佳安全开发指南:https://blog.csdn.net/laoerhan/article/details/81078760
(作者:成都链安科技,内容来自链得得内容开放平台“得得号”;本文仅代表作者观点,不代表链得得官方立场)
以上就是帮企客小编为大家带来的全部内容,希望可以帮助到大家。免责声明:本文为转载,非本网原创内容,不代表本网观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
如有疑问请发送邮件至:bangqikeconnect@gmail.com