***回顾
1 2 3 4 5 6 7 | 2016年6月,以太坊最大众筹项目The DAO被***,***获得超过350万个以太币,最终导致以太坊分叉为ETH和ETC。 2016年拒绝服务***:GovernMental's 1100 ETH 2016年KotET(“纷争时代”)合约遭受***。 2017年Parity钱包,遭受delecate call注入,销毁了合约。损失513,774.16 Ether 2017年ANT Token遭受重入漏洞。 2017年Simoleon合约被***。***通过部署***合约获得了超过700万的token,从57万账户中脱颖而出,一举成为该合约token的第四大持有者。 2018年BEC代币遭到袭击,***手法被披露的24小时内,就有30多个合约遭受*** |
溢出
孔子曾经说过 过犹不及
。做事情都有限度,一旦超过了限度就会适得其反。理解溢出问题最好的是在千禧之年爆发的千年虫事件。过去,由于计算机程序中使用两个数字来表示年份,如1998年被表示为“98”、1999年被表示为“99”;而2000年被表示为“00”,这样将会导致某些程序在计算时得到不正确的结果,如把“00”误解为1900年。在嵌入式系统中可能存在同样的问题,这有可能导致设备停止运转或者发生更加灾难性的后果。
solidity中的溢出问题
下面是一个简单的函数,其功能是将桉树加1.例如传递4,返回5。传递200,返回201。但是里面暗藏着陷阱。例如当传递255的时候,会返回0…这就是溢出。这是由于uint8的最大值为255,在内存中:为1111 1111
。一旦加1之后,变为了
1 0000 0000
,但是最大的位数为8位。截断之后,变为了0000 0000
因此返回的结果为0。 1 2 3 | function add(uint8 a) public pure returns(uint8){ return a+1; } |
safeMath避免溢出问题
因此我们可以看到,对于4则运算,很容易的发生溢出问题。OpenZeppelin 建立了一个叫做 SafeMath 的 库。这就规避掉溢出问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | library SafeMath { function add(uint a, uint b) internal pure returns (uint c) { c = a + b; require(c >= a); } function sub(uint a, uint b) internal pure returns (uint c) { require(b <= a); c = a - b; } function mul(uint a, uint b) internal pure returns (uint c) { c = a * b; require(a == 0 || c / a == b); } function div(uint a, uint b) internal pure returns (uint c) { require(b > 0); c = a / b; } } |
BEC代币***全纪实
如下为BEC代币的源代码抽离出来的部分。2018年4月份BEC代币遭到***的溢出袭击。***为自己的两个账号转移了2^255次方的代币。导致市场的恐慌,币价一度一文不值。***手法被披露的24小时内,就有类似30多个合约遭受***
在TokenExample合约中,有一个batchTransfer函数。此函数的功能为对账户进行转账操作。第一个参数为动态长度地址,明确要转账的账户。第二个参数为转账的金额。 要转账成功,必须要保证在balance资金表中,发送者必须有超过总金额(账户数量转账金额)。但是 uint256 amount = uint256(cnt) value;这段代码并没有做安全的乘法,导致可能会发生溢出***。当***调用合约的时候,在remix中:
输入地址数组以及:[“0xb4D30Cac5124b46C2Df0CF3e3e1Be05f42119033”,“0x0e823fFE018727585EaF5Bc769Fa80472F76C3d7”],以及value"0x8000000000000000000000000000000000000000000000000000000000000000"即2*255,使得amount=2^255 * 2,超出uint256类型的范围[0,2**256-1],溢出为0,发送者账户余额不减少,并且,本例中,发送者的代币可以为零,实现"无中生有"。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | library SafeMath{ function sub(uint256 a,uint256 b) internal pure returns(uint256){ assert(b<=a); return a-b; } function add(uint256 a,uint256 b) internal pure returns(uint256 c){ c=a+b; assert(c>=a); return c; } } contract TokenExample{ //使用safemath库 using SafeMath for uint256; //资金表 mapping(address=>uint256) public balance; function batchTransfer(address[] _receivers,uint256 _value) public returns(bool){ //要转移的地址的数量 uint cnt = _receivers.length; //转账总金额 uint256 amount = uint256(cnt)*_value; //判断转账地址必须大于0 require(cnt >0 && cnt <=20); //判断发送者拥有的金额必须大于转账的总金额 require(_value >0 && balance[msg.sender]>=amount); //发送者账户金额减少 20 balance[msg.sender] = balance[msg.sender].sub(amount); //接受者金额增加 10 for(uint i =0 ;i |
本文链接:
版权声明: 本博客所有文章除特别声明外,均采用 许可协议。转载请注明出处!