24-ETH-美鏈

聲明:本文是要點筆記,介紹和系列筆記均收錄在專題:區塊鏈技術與應用

美鏈事件

2018年4月發生的事件,美鏈是發行在以太坊上的代幣,這些代幣沒有自己的區塊鏈,而是以智能合約的形式運行在以太坊的EVM平臺上。發行這個代幣的智能合約,對應的是以太坊狀態樹的一個節點,這個節點有它自己的賬戶餘額,就相當於這個智能合約一共有多少個以太幣,就是發行這個代幣的智能合約它的資產有多少個以太幣,然後在這個合約裏每個賬戶上有多少個代幣,這個是作爲存儲樹中的變量,存儲在智能合約的賬戶裏。代幣的發行、轉賬、銷燬都是通過調用智能合約中的函數來實現的,這個也是跟以太坊上的以太幣不太一樣的地方,它不像以太坊一樣需要挖礦來維護一個底層的基礎鏈,像以太坊上每個賬戶有多少個以太幣,這個是直接保存在狀態樹中的變量,然後以太坊上面兩個賬戶轉賬是通過發佈一個交易到區塊鏈上,這個交易會打包到發佈的區塊鏈上面,而代幣發生轉賬的話實際上就是智能合約上面兩個賬戶之間發生轉賬,通過調用智能合約上的函數,就可以完成了。每個代幣都可以制定自己的發行規則,比如某個代幣是1個以太坊兌換100個代幣,那麼比如說從某個外部賬戶轉1個以太幣給這個智能合約,這個智能合約就可以給你在這個智能合約裏的代幣賬戶上發送100個代幣,每個代幣賬戶上有多少個代幣的信息都是維護在存儲樹裏面,發行這個代幣的智能合約的存儲樹裏面。

以太坊平臺的出現讓發行代幣提供了方便,包括以前說的 eos,這個在上線之前也是作爲以太坊上的代幣形式,上線的意思是有自己的基礎鏈了,不用依附在以太坊上了。以太坊發行代幣的標準爲ERC20(Ethereum Request for Comments)。

美鏈的 batchTransfer 的函數:

這個函數有兩個參數,第一個參數是一個數組,接收者地址的數組,第二個參數value是轉賬的金額,給每個人轉多少。

uint256 amount = uint256(cnt) * _value; // 計算一共要轉的總金額
require(cnt > 0 && cnt <= 20); // 檢查接收者的數目不超過20個
require(_value > 0 && balances[msg.sender] >= amount); // 檢查發起調用函數的這個賬戶是否有這麼多錢
balances[msg.sender] = balances[msg.sender].sub(amount); // 把發起調用的賬戶餘額減去amount
// 下面用一個循環給每個接收者接收 value 這麼多的代幣
...

那麼,問題出在哪?

問題出在:uint256 amount = uint256(cnt) * _value;, 當 value 的值很大的時候可能會發生溢出,那麼 amount 算出來可能是個很小的一個值,所以從調用者的代幣中減的時候是很小一部分的代幣,但還是給每個 receivers 增加那麼多 value 的代幣。最後系統中相當於多發行了許多的代幣。

攻擊細節

第0號參數是 _receivers 數組在參數列表中的位置,這裏是16進制的,0040對應的是4乘16=64,第一個參數出現在第64個字節的位置,一行有64個數字,1個16進制的數字需要用4個二進制的數字來表示,64乘4=256位,也就是說一行有32個字節。所以是從 [2] 行開始表示 receivers,表示了數組的長度是2,[3] [4]兩行是接受的地址。[1] 行表示value的值。注意,[1] 行的參數是80000...再乘以接收者2個,算出來的溢出正好是0。

紅框中可以看到每個地址都是接收了很大數量的代幣。

攻擊結果

攻擊使代幣的價格造成致命性的打擊,差不多快要歸零了。

代幣上市的交易所在發生攻擊後暫停提幣的功能,並且回滾了交易。

反思

在進行數學運算的時候一定要考慮溢出的可能性。solidity有一個safeMath庫,裏面提供的操作運算都會自動檢測有沒有出現溢出。

C語言裏,兩個數相乘會有一定的精度損失,再除以一個數,不一定會得到和另外一個數一模一樣的數。但是在solidity裏面是不存在的,因爲兩個數都是256位的整數,整數先進行乘法,再進行除法。

batchTransfer的加法和減法都用的safeMath庫,只有乘法不小心沒有使用,結果釀成了悲劇。曾經有人懷疑是不是故意的,但從事件的結果來看又不像使故意的。

mul() 函數中,先用a*b = c,再用c/a看看是否等於b,如果發生溢出的話,assert()會拋出異常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章