合約安全增強(qiáng): 溢出和下溢
什么是 溢出 (overflow)
?
假設(shè)我們有一個(gè) uint8
, 只能存儲(chǔ)8 bit
數(shù)據(jù)。這意味著我們能存儲(chǔ)的最大數(shù)字就是二進(jìn)制 11111111
(或者說十進(jìn)制的 2^8 - 1 = 255
).
來看看下面的代碼表窘。最后 number
將會(huì)是什么值?
uint8 number = 255;
number++;
在這個(gè)例子中,我們導(dǎo)致了溢出 — 雖然我們加了1, 但是number
出乎意料地等于 0
了荐健。
下溢(underflow)
也類似,如果你從一個(gè)等于 0
的 uint8
減去 1
, 它將變成 255
(因?yàn)?uint
是無符號(hào)的琳袄,其不能等于負(fù)數(shù))江场。
使用 SafeMath
為了防止這些情況,OpenZeppelin
建立了一個(gè)叫做 SafeMath
的 庫(library)
挚歧,默認(rèn)情況下可以防止這些問題。
一個(gè)庫
是 Solidity
中一種特殊的合約吁峻。其中一個(gè)有用的功能是給原始數(shù)據(jù)類型增加一些方法滑负。
比如,使用 SafeMath
庫的時(shí)候用含,我們將使用 using SafeMath for uint256
這樣的語法矮慕。 SafeMath
庫有四個(gè)方法 — add
, sub
啄骇, mul
痴鳄, 以及 div
。現(xiàn)在我們可以這樣來讓 uint256
調(diào)用這些方法:
using SafeMath for uint256;
uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10
來看看 SafeMath
的部分代碼:
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
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) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
首先我們有了 library
關(guān)鍵字, 庫允許我們使用 using
關(guān)鍵字缸夹,它可以自動(dòng)把庫的所有方法添加給一個(gè)數(shù)據(jù)類型:
using SafeMath for uint;
// 這下我們可以為任何 uint 調(diào)用這些方法了
uint test = 2;
test = test.mul(3); // test 等于 6 了
test = test.add(5); // test 等于 11 了
assert 和 require
assert
和 require
相似痪寻,若結(jié)果為否它就會(huì)拋出錯(cuò)誤螺句。 assert
和 require
區(qū)別在于,require
若失敗則會(huì)返還給用戶剩下的 gas
橡类, assert
則不會(huì)蛇尚。所以大部分情況下,你寫代碼的時(shí)候會(huì)比較喜歡 require
顾画,assert
只在代碼可能出現(xiàn)嚴(yán)重錯(cuò)誤的時(shí)候使用取劫,比如 uint
溢出。