# 以太坊 PrevRandao 與合約應用 **Published by:** [Blockchain Builder](https://paragraph.com/@blockchain-builder/) **Published on:** 2023-05-03 **URL:** https://paragraph.com/@blockchain-builder/prevrandao ## Content (Cover designed by @freepik/Freepik)作者:Irara @ imToken Labs 校稿:Nic、Cyan @ imToken Labs,FengYi @ imToken Wallet在區塊鏈領域,以太坊 Proof of Stake 共識機制已穩定運作了一段時間,本文將會介紹 PoS 中的一個關鍵概念: PrevRandao,它的核心作用是保證每個 Epoch 中選出的 Slot Proposer 是隨機的且很難被操縱。 在這篇文章中,您將會了解到 PrevRandao 的產生流程,它將如何透過社群的力量來達到安全且隨機,以及 Solidity 開發人員該如何將 PrevRandao 引入智能合約來設計好玩的 DeFi 應用。如果您仍是區塊鏈的新手,那這篇隨機數的文章也會是一個很好的開始!本文是以太坊 Proof of Stake 的延伸文章,我們推薦您閱讀先前發佈的「以太坊 PoS 與上海硬分叉總整理!」文章,以了解 PoS 的相關概念。1、隨機數與隨機性現實中的隨機事件我們的生活充滿各種具有隨機特性的事件,例如:今天是否會下雨、股市行情會漲還是跌、上班路上是否遇到塞車等。這些隨機性為我們的生活帶來樂趣。電腦與區塊鏈的隨機數難題在電腦世界中,因電腦硬體的限制(例如:邏輯閘產生的偽隨機數其實會週期性循環等),我們難以在封閉的電腦與區塊鏈世界中創造真正隨機性的事件。這就是為什麼當我們玩一款隨機數設計不良的遊戲時,常常會有像是在半夜 12 點轉蛋特別容易抽中角色的都市傳說。為了盡可能產生足夠隨機的數字,我們就必須依賴「相對不可預測」的外部參數,例如:滑鼠位置、大氣中的雜訊、機械音頻等,輸入到電腦或區塊鏈網路中以產生真正意義上的隨機數。由社群來產生隨機數用的參數在區塊鏈領域中,我們不能像大部份的手遊公司,由一個黑箱來建立產生隨機數所需的外部參數。為了降低任何個人/組織提供特殊參數來操縱最後產生隨機數的可能性,我們需要找到越多位願意來參與的角色(只需有一位是誠實的),並共同輸入參數以產生隨機數。 試想一下,如果區塊挖礦獎勵、極稀有的 NFT,總是被同一個人得到,那還會有人願意消耗運算資源來加入區塊鏈網路、並且共同維護區塊鏈的生態系嗎?讓多位願意來參與的來提供產生隨機數的參數,只要有一位是誠實的,最後產出的隨機數就是可信的參數的選擇剛才提到的用來產生隨機數的外部參數,必須找到越多位願意來參與的角色共同產生。那麼,作為這些角色的我們,應該使用什麼數值作為參數呢?是使用電腦時間、還是我們所處位置的座標?相對的,您是否也相信對方真的提供當初說好的時間或座標,而不是加了料的數值? 所以,這世上有沒有一種參數符合「足夠隨機,且所有人都可驗證」的數值呢? 那麼,現在就讓我們介紹今日的主角 - 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萬一被發現某個 Slot Proposer 給的 Randao Reveal 並非合法簽章,那我們就不要將之加入後續的 PrevRandao 產生過程就好;況且,惡意的 Slot Proposer 會得不到提交正確 Slot 的區塊獎勵! 在下面我們就來繼續說明 PrevRandao 演算法,以及如何去使用 Randao Reveal 參數!註 1:我們常用的 etherscan 區塊鏈瀏覽器就可看到 PreRandao(Block Randomness)及 Randao Reveal 數值。 註 2:BLS 數位簽章演算法,不同於 ECDSA,多個 BLS 的簽章可以合併成一個,大大降低簽章內容在網路中傳遞所佔用的頻寬,有興趣可參考 Vitalik 介紹 BLS 影片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 演算法經過了由社群產生的外部參數,以及由社群驗證不可作惡的產生過程,我們可以說每組 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)註 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 產生隨機數4、結語在區塊鏈的世界中,產生真正隨機性的事件是非常困難的。為了能在區塊鏈網路中產生足夠隨機的隨機數,我們需要設計出一套可驗證且難以被影響的隨機數產生機制。PrevRandao 是以太坊 PoS 中一個重要的機制,它可保證每個 Epoch 中選出的 Slot Proposer 是隨機的且很難被操縱。 PrevRandao 透過眾多的 Slot Proposer 來共同輸入參數以產生隨機數,並讓多位 Slot Proposer 來提供這些參數。這可以有效地降低任何個人或組織操縱最後產生隨機數的可能性。 最後,對於 Solidity 開發人員來說,了解並信任 PrevRandao 的產生流程,可以很輕鬆的將其引入智能合約內,如此就可以設計出有趣的,諸如:大樂透、稀有 NFT 挖掘、養殖不同特色的虛擬寵物等 DeFi 應用。 感謝您的閱讀!5、參考資料中文Random Numbers & Bitwisehttps://ithelp.ithome.com.tw/articles/10287325英文Randomnesshttps://eth2book.info/bellatrix/part2/building_blocks/randomness/Block Proposal - Random selectionhttps://ethereum.org/zh/developers/docs/consensus-mechanisms/pos/block-proposal/#random-selectionBeacon Chain Spechttps://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#randaoRandaohttps://github.com/randao/randaoEIP-4399https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4399.mdhttps://eips.ethereum.org/EIPS/eip-4399https://ethereum-magicians.org/t/eip-4399-supplant-difficulty-opcode-with-random/7368 ## Publication Information - [Blockchain Builder](https://paragraph.com/@blockchain-builder/): Publication homepage - [All Posts](https://paragraph.com/@blockchain-builder/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@blockchain-builder): Subscribe to updates - [Twitter](https://twitter.com/oneleo760823): Follow on Twitter