以太坊智能合約是以太坊概念中非常重要的一個概念,以太坊實現了基於 solidity 語言的以太坊虛擬機 EVM(Ethereum Virtual Machine),所以允許用戶在鏈上部屬自己的智能合約代碼,通過合約可以完成約定的合約。
智能合約是利用 EVM 運行,跑在鏈上的代碼最大的特性就是公開和不可竄改的特性。而如何在合約上生成一個可靠且具安全性的隨機數就是一個值得探討的問題。
PRNG 漏洞類型
開發者生成隨機數時,一般都會使用弱隨機數來生成(pseudo-random number generator)簡稱 PRNG
- 使用區塊資訊作為種子的 PRNG
- 基於過往區塊的 hash 作為 PRNG
- 基於過往區塊和私有種子的區塊哈希的 PRNG
- 易被搶占交易的 PRNG
智能合約,隨機數竟是可以預測的?
以 Fomo3D 合約的空投獎勵(Airdrop) 來說,隨機發送的獎勵所引入的參考訊息就是利用 block 區塊的資訊做為參考而生成的。
function airdrop()
private
view
returns(bool)
{
uint256 seed = uint256(keccak256(abi.encodePacked(
(block.timestamp).add
(block.difficulty).add
((uint256(keccak256(abi.encodePacked(block.coinbase)))) / (now)).add
(block.gaslimit).add
((uint256(keccak256(abi.encodePacked(msg.sender)))) / (now)).add
(block.number)
)));
if((seed - ((seed / 1000) * 1000)) < airDropTracker_)
return(true);
else
return(false);
}
這段 code 導致了薅羊毛事件,被以極小的成本刷走了大量的 airdop 走,具體來說就是預測了弱隨機數的結果,只有在機戶可能中獎時才出手。
Hacker 利用了以太坊的一個特徵,一個地址(account)建立一個合約,然而合約地址是可以透過規則計算得到的,任何人都可以根據已知訊息進行推算。
然而 Hacker 利用了 1000 個合約地址自動推算下一次新建合約地址,而該地址剛好是空投遊戲中獎數字的隨機種子,所以利用合約自動篩選出最有可能中獎的地址進行抽獎,以高概率的中獎機率刷走空投。
(至於為什麼是 1000 個又是另一個故事了,這邊先不說明)
我們可以知道在 PoW 挖礦時,礦工需要進行以下計算
BlockHash = Hash(Header+Nonce)
Check(BlockHash < Diff)
當 BlockHash 結果小於當前難度值時,代表找到了一個合法的 Nonce
而上面的 Fomo3D 空投獎勵中和挖礦機制類似,所以 Hacker 利用 msg.sender
作為 Nonce 來挖礦。
畢竟 發起者帳戶 + nonce
= 智能合約的地址。
這麼一來隨機數就並不是這麼隨機了,而是有心人士可以透過多方訊息提高中獎機率,也難怪稱做弱隨機數了。
如何在區塊鏈上設計不被預測的隨機數呢?
在智能合約內生成隨機數需要有更多層面的考量,為了避免隨機數可以被預測,有幾種作法。
1. External oracles: Oraclize
2. External oracles: BTCRelay
3. Signidice
4. Hash-Commit–reveal
這邊先講 Hash-Commit–reveal,先提交再揭示的作法,剩下的想知道可以在自己搜一下 XD
- 提交階段:一方提交加密內容給智能合約
- 揭示階段:一方宣布明文種子,智能合約驗證正確性,並利用種子生成隨機數
揭示方法的實現不該單一的依賴於任何一方,雖然玩家並不知道合約擁有者提交的原始種子,機會是相同的,但是合約擁有者也有可以身兼玩家,所以玩家並不能完全信任合約擁有者。
Randao 更好的實現了這個 commit reveal 的作法,從多方收集 hash 種子,並且每一方都獲得參與獎勵。沒有人知道其他人手上的種子是什麼,所以能保證結果真正的隨機,唯一的缺點就是如果有一方拒絕揭示種子,將會導致合約拒絕這次服務。
1. owner’s sha3(seed1)
2. player’s sha3(seed2)
3. a future blockhash
解決的問題
- 礦工激勵問題:礦工可以決定區塊哈希,但不知道合約擁有者和玩家們的種子
- 合約擁有者激勵問題:擁有者只知道自己的種子,但玩家的種子和未來區塊的種子是未知的。
- 解決當一個人既是合約擁有者又是礦工的情況,該能人決定區塊哈希並且知道合約擁有者的種子,但並不知道玩家的種子