# 以太坊 PrevRandao 與合約應用

By [Blockchain Builder](https://paragraph.com/@blockchain-builder) · 2023-05-03

---

（Cover designed by @freepik／Freepik）

> 作者：Irara @ imToken Labs 校稿：[Nic](https://medium.com/@twedusuck)、[Cyan](https://cyanho.medium.com/) @ imToken Labs，[FengYi](https://yufengyi.medium.com/) @ imToken Wallet

在區塊鏈領域，以太坊 Proof of Stake 共識機制已穩定運作了一段時間，本文將會介紹 PoS 中的一個關鍵概念： PrevRandao，**_它的核心作用是保證每個 Epoch 中選出的 Slot Proposer 是隨機的且很難被操縱_**。

在這篇文章中，您將會了解到 PrevRandao 的產生流程，它將如何透過社群的力量來達到安全且隨機，以及 Solidity 開發人員該如何將 PrevRandao 引入智能合約來設計好玩的 DeFi 應用。如果您仍是區塊鏈的新手，那這篇隨機數的文章也會是一個很好的開始！

> 本文是以太坊 Proof of Stake 的延伸文章，我們推薦您閱讀先前發佈的「[以太坊 PoS 與上海硬分叉總整理！](https://mirror.xyz/0x347c9872A2a1dE370D798f9FE96341A9A0E05af8/IZQZJzTCvdcRh1OIyevWSSOZv5eb0NKIulTxfIcOTu8)」文章，以了解 PoS 的相關概念。

1、隨機數與隨機性
---------

### 現實中的隨機事件

我們的生活充滿各種具有隨機特性的事件，例如：今天是否會下雨、股市行情會漲還是跌、上班路上是否遇到塞車等。這些隨機性為我們的生活帶來樂趣。

### 電腦與區塊鏈的隨機數難題

在電腦世界中，因電腦硬體的限制（例如：邏輯閘產生的偽隨機數其實會週期性循環等），我們難以在封閉的電腦與區塊鏈世界中創造真正隨機性的事件。這就是為什麼當我們玩一款隨機數設計不良的遊戲時，常常會有像是在半夜 12 點轉蛋特別容易抽中角色的都市傳說。為了盡可能產生足夠隨機的數字，我們就必須依賴「相對不可預測」的外部參數，例如：滑鼠位置、大氣中的雜訊、機械音頻等，輸入到電腦或區塊鏈網路中以產生真正意義上的隨機數。

### 由社群來產生隨機數用的參數

在區塊鏈領域中，我們不能像大部份的手遊公司，由一個黑箱來建立產生隨機數所需的外部參數。為了降低任何個人／組織提供特殊參數來操縱最後產生隨機數的可能性，我們需要找到越多位願意來參與的角色（只需有一位是誠實的），並共同輸入參數以產生隨機數。

試想一下，如果區塊挖礦獎勵、極稀有的 NFT，總是被同一個人得到，那還會有人願意消耗運算資源來加入區塊鏈網路、並且共同維護區塊鏈的生態系嗎？

![讓多位願意來參與的來提供產生隨機數的參數，只要有一位是誠實的，最後產出的隨機數就是可信的](https://storage.googleapis.com/papyrus_images/f5dbe1bbd4af5501abec43a5fc229289822c68967b2d11592a0cd2bfba86ad8c.png)

讓多位願意來參與的來提供產生隨機數的參數，只要有一位是誠實的，最後產出的隨機數就是可信的

### 參數的選擇

剛才提到的用來產生隨機數的外部參數，必須找到越多位願意來參與的角色共同產生。那麼，作為這些角色的我們，應該使用什麼數值作為參數呢？是使用電腦時間、還是我們所處位置的座標？相對的，您是否也相信對方真的提供當初說好的時間或座標，而不是加了料的數值？

所以，這世上有沒有一種參數符合「足夠隨機，且所有人都可驗證」的數值呢？

那麼，現在就讓我們介紹今日的主角 - PrevRandao 隨機數產生流程！

2、PrevRandao 隨機數及產生流程
---------------------

PrevRandao 隨機數在區塊鏈產生的目的，是為了公平地從為數眾多 Validator 中選出每個 Epoch 的 Slot Proposer，且每個 Validator 都有公平機會取得提交區塊的獎勵。

> PrevRandao 的命名原由
> 
> Randao 實際上為 Random（隨機）及 DAO（去中心化自治組織）的合體字，意旨是隨機數是由所有 DAO 的參與者來共同產生。而 PrevRandao 則是 Previous + Randao 二字的結合。
> 
> 根據 PrevRandao 演算法，每一個 Slot 的 Proposer 要揭示自己用的私鑰所簽署的 Randao Reveal 值，而 PrevRandao 就是由「前一個（Previous）」已知的 Randao Reveal 的值與目前的 Randao Reveal 值作 XOR 運算而得此名。

而前一節提到的「隨機，且所有人都可驗證其他人給的是誠實」參數，我們稱之為 Randao Reveal，它的產生方式其實相當簡單：

「在區塊鏈中每個 Slot Proposer，使用自己的 BLS 私鑰對 Epoch 編號進行簽名」即可成為參數。

數學公式為：Randao Reveal = BLS\_Sig( Epoch\_Num )

Epoch 編號是很公正的數值來源，也就是說任何人不能透過自己隨意產生的數值來影響結果；當隨機選出的 Slot Proposer 對 Epoch 編號進行簽名後，就可視為公正且可驗證的隨機數。任何人都可輕易地用公鑰來驗證：簽名者是否就是 Slot Proposer 本人。如此，就可解決「可驗證的參數」問題。

![對 Epoch 編號簽名的數值已是隨機數，來源：https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html](https://storage.googleapis.com/papyrus_images/1cdc0079b9fce5308f207180a53b78b4dea3bde13eda0eead9fae183d64c4f60.png)

對 Epoch 編號簽名的數值已是隨機數，來源：https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html

萬一被發現某個 Slot Proposer 給的 Randao Reveal 並非合法簽章，那我們就不要將之加入後續的 PrevRandao 產生過程就好；況且，惡意的 Slot Proposer 會得不到提交正確 Slot 的區塊獎勵！

在下面我們就來繼續說明 PrevRandao 演算法，以及如何去使用 Randao Reveal 參數！

> 註 1：我們常用的 [etherscan](https://etherscan.io/block/16666666#consensusinfo) 區塊鏈瀏覽器就可看到 PreRandao（Block Randomness）及 Randao Reveal 數值。
> 
> 註 2：BLS 數位簽章演算法，不同於 ECDSA，多個 BLS 的簽章可以合併成一個，大大降低簽章內容在網路中傳遞所佔用的頻寬，有興趣可參考 [Vitalik 介紹 BLS 影片](https://youtu.be/DpV0Hh9YajU)

### PreRandao 產生公式及詳細說明

*   數學公式為：PrevRandao\[n\] = PrevRandao\[n-1\] XOR SHA256(RandaoReveal\[n-1\])
    
*   註：這邊的 n 指的是當前的 Slot，而 n-1 則為上一個 Slot
    

說明：

*   每個 Slot 的 Proposer 各會公開一個 SHA256(RandaoReveal)
    

> 註：為何不直接公開 RandaoReveal，還要進行 SHA256 運算的原因：避免 XOR 運算時被惡意操作
> 
> XOR 惡意操作方法：假設看到 1100 值，若惡意節點希望產生 1111 值 = 1100 XOR ?，那惡意節點就公開 0011 就可達到（1100 XOR 0011 就會得到 1111）
> 
> 此時若公開的是 SHA256 運算結果，則惡意節點要找到符合 SHA256(1100) XOR SHA256(？) = 1111 的值，將會變得很困難

*   如果驗證失敗，則此 Slot Proposer 會得不到提交正確 Slot 的區塊獎勵，且公開的 SHA256(RandaoReveal) 會跳過不列入計算
    
*   最後，透過最上方的公式，將每一個 Slot 的 Slot PrevRandao 隨機數計算出來
    

![PrevRandao 演算法](https://storage.googleapis.com/papyrus_images/3a070d75bd39e2eb6db6527243a2e4a8b8e662af7847458da4941f8007f6af2f.png)

PrevRandao 演算法

經過了由社群產生的外部參數，以及由社群驗證不可作惡的產生過程，我們可以說每組 PrevRandao 足夠隨機且幾乎不可能被操作。接下來這個隨機數作為選擇每個 Slot 的 Proposer，甚至是拿來用作下一章節將介紹的智能合約「隨機事件」的重要數值。

3、在智能合約裡的應用
-----------

在上一章節，我們已經了解到每 12 秒由 Slot Proposer 們產生的 PrevRandao 的過程，且產生的值足夠隨機且幾乎不可能被操作；現在，我們即可將 PrevRandao 運用在智能合約之中，以下是一個簡單的範例：

    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >=0.8.0 <0.9.0;
    
    contract RandomNumber {
        function prevrandao(uint256 min, uint256 max) public view returns (uint256) {
            // Max 必須要大於 min 值
            require(max >= min, "Max variable must be greater than min variable.");
            // 使用 PrevRandao 建立的隨機數，每個 Slot（12 秒）會更新一次
            return uint256(keccak256(abi.encodePacked(block.prevrandao))) % (max - min + 1) + min;
        }
    
        function difficulty(uint256 min, uint256 max) public view returns (uint256) {
            require(max >= min, "Max variable must be greater than min variable.");
            // 使用 Difficulty（現應等同於 PrevRandao）建立的隨機數，每個 Slot（12 秒）會更新一次
            return uint256(keccak256(abi.encodePacked(block.difficulty))) % (max - min + 1) + min;
        }
    
        function customRandom(uint256 min, uint256 max) public view returns (uint256) {
            require(max >= min, "Max variable must be greater than min variable.");
            // 使用 PrevRandao 及其他區塊鏈參數建立的隨機數，每個 Slot（12 秒）會更新一次
            return uint256(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, block.number, block.coinbase, msg.sender))) % (max - min + 1) + min;
        }
    }
    

（此段智能合約已部署至 Goerli 測試鏈上：[https://goerli.etherscan.io/address/0x4239a11f6f4b54fe9f61972d88787414195ab375](https://goerli.etherscan.io/address/0x4239a11f6f4b54fe9f61972d88787414195ab375#readContract)）

> 註 1：在過去，Proof of Work 以太坊要藉由 Difficulty Opcode 來調整挖礦難度，並讓出塊時間維持在 12 秒；如今，Proof of Stake 以太坊已不需要此參數，為了可向下相容過去已部署並使用 Difficulty 的智能合約，Difficulty 將等同於 PrevRandao
> 
> 註 2：在 Solidity 中，keccak256() 雜湊函數只能輸入 Byte32 型別，所以需將 block.prevrandao 或 block.difficulty 這種 Uint256 型別透過 abi.encodePacked() 串接函數來進行轉換。另外，智能合約還可以添加，如：block.timestamp、block.number 等數值，來增加隨機數的亂度

接著，使用者只需輸入 min 最小值及 max 最大值，即可每 12 秒得到安全且足夠隨機的數值了！

![根據使用者輸入的 min、max 產生隨機數](https://storage.googleapis.com/papyrus_images/4bdacc5b8618b0593e9fc7e1bf09f13cea9c25f36c7886fb6ef99ffddd57d2ee.png)

根據使用者輸入的 min、max 產生隨機數

4、結語
----

在區塊鏈的世界中，產生真正隨機性的事件是非常困難的。為了能在區塊鏈網路中產生足夠隨機的隨機數，我們需要設計出一套可驗證且難以被影響的隨機數產生機制。PrevRandao 是以太坊 PoS 中一個重要的機制，它可保證每個 Epoch 中選出的 Slot Proposer 是隨機的且很難被操縱。

PrevRandao 透過眾多的 Slot Proposer 來共同輸入參數以產生隨機數，並讓多位 Slot Proposer 來提供這些參數。這可以有效地降低任何個人或組織操縱最後產生隨機數的可能性。

最後，對於 Solidity 開發人員來說，了解並信任 PrevRandao 的產生流程，可以很輕鬆的將其引入智能合約內，如此就可以設計出有趣的，諸如：大樂透、稀有 NFT 挖掘、養殖不同特色的虛擬寵物等 DeFi 應用。

感謝您的閱讀！

5、參考資料
------

*   中文
    
    *   Random Numbers & Bitwise
        
        *   [https://ithelp.ithome.com.tw/articles/10287325](https://ithelp.ithome.com.tw/articles/10287325)
            
*   英文
    
    *   Randomness
        
        *   [https://eth2book.info/bellatrix/part2/building\_blocks/randomness/](https://eth2book.info/bellatrix/part2/building_blocks/randomness/)
            
    *   Block Proposal - Random selection
        
        *   [https://ethereum.org/zh/developers/docs/consensus-mechanisms/pos/block-proposal/#random-selection](https://ethereum.org/zh/developers/docs/consensus-mechanisms/pos/block-proposal/#random-selection)
            
    *   Beacon Chain Spec
        
        *   [https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#randao](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#randao)
            
    *   Randao
        
        *   [https://github.com/randao/randao](https://github.com/randao/randao)
            
    *   EIP-4399
        
        *   [https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4399.md](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4399.md)
            
        *   [https://eips.ethereum.org/EIPS/eip-4399](https://eips.ethereum.org/EIPS/eip-4399)
            
        *   [https://ethereum-magicians.org/t/eip-4399-supplant-difficulty-opcode-with-random/7368](https://ethereum-magicians.org/t/eip-4399-supplant-difficulty-opcode-with-random/7368)

---

*Originally published on [Blockchain Builder](https://paragraph.com/@blockchain-builder/prevrandao)*
