本文由iOlite贊助創作。感謝您支持使SitePoint成為可能的合作夥伴。
Solidity 是一門相對較新的語言,由於沒有完美的代碼,它包含與代碼及其預期用途相關的問題。本文將指導您在使用隨機數作為以太坊智能合約輸入時的最佳實踐和陷阱。
關鍵要點
block.timestamp
和 block.difficulty
,礦工可以操縱這些值。 Solidity 隨機數生成
Solidity 無法創建隨機數。實際上,所有創建隨機數的算法都是偽隨機的——沒有語言能夠創建完全隨機的數。 Solidity 的問題在於復雜算法的成本太高,因此使用更基本的解決方案。除此之外,Solidity 代碼應該是確定性的,因為它將在多個節點上運行。我們需要一種能夠生成一次隨機數並在多個節點上使用它的算法。諸如時鐘時間之類的信息不可用於生成隨機數,因此我們必須尋找其他選項。作為開發人員,您應該意識到這個問題,因為攻擊者能夠在某些特定情況下預測結果。
最常用的算法之一是“線性同餘生成器”(LCG)。它是 oldest 算法之一,快速且易於理解。 LCG 對於嵌入式系統來說是一個不錯的選擇,因為它們只有有限的內存。但是,它不適用於密碼安全應用。儘管如此,它仍在智能合約中使用,因為快速算法在 gas 成本方面更便宜。
該算法本身執行以下步驟:
讓我們探討使用彩票智能合約示例創建隨機數的不同方法。用戶可以通過向合約發送 0.1 以太幣以及 0 到 250 之間的整數來加入彩票。
block.timestamp
& block.difficulty
每當礦工確認交易時,都會分配一個 block.timestamp
。我們的彩票合約的任何玩家都無法控制它。讓我們來看一下這段用於創建隨機數的代碼。
<code class="language-solidity">function random() private view returns (uint8) { return uint8(uint256(keccak256(block.timestamp, block.difficulty))%251); }</code>
此處查找 Gist。
這段代碼首先對區塊時間戳和難度進行哈希運算。接下來,我們將哈希值轉換為整數並將其除以 251 以獲得 0 到 250 之間的整數。但是,這段代碼的問題在於我們不應該相信礦工來選擇獲勝者。
我們需要更多任意數據來選擇我們的獲勝者。我們可以使用已進入我們的彩票智能合約的玩家的地址,但我們必須將其隱藏起來,因為他們可能會濫用它。由於所有信息都記錄在區塊鏈上,因此無法隱藏此信息。
可以提交給我們的彩票智能合約的數字。用戶必須將他們選擇的數字與他們的以太坊地址一起進行哈希運算。這給了我們一個相當隨機的數字。
話雖如此,隨機性是可以實現的,但您只需要使用預言機從區塊鏈外部獲取隨機數即可。使用外部數據的問題在於,證明該數字實際上是隨機的非常困難,並確保鏈下實體不會以任何方式操縱隨機數。這就是 Chainlink VRF 發揮作用的地方。 Chainlink VRF(可驗證隨機函數)是我們如何在 Solidity 中獲得可證明隨機數的方法。
Chainlink VRF 向區塊鏈添加一個事件,Chainlink 節點從中讀取該事件,並返回一個隨機數。通過所謂的 VRF 協調器進行鏈上隨機性檢查。這使用來自預言機的特定密鑰哈希和來自用戶的種子短語以及一些密碼學來確保數字是真正的隨機數。這樣,我們可以得到一個無偏見的隨機數。
開發人員需要考慮何時選擇獲勝者。諸如時鐘時間之類的信息在以太坊虛擬機中不可用,因為代碼將在多個節點上以不同的時間運行。這使得選擇獲勝者更加困難。一種方法是在您的智能合約中實現一個函數,該函數將關閉彩票並選擇獲勝者。這不像我們希望的那樣去中心化。合約的所有者可以在確定他們的朋友會獲勝時關閉彩票。我們要避免這種作弊行為。
更好的選擇是使用以太坊鬧鐘。它是一項允許調度交易在以太坊區塊鏈上的稍後時間執行的服務。這項服務完全是無需信任的,這意味著整個服務都作為智能合約運行。基本上,以太坊鬧鐘使用區塊編號來調度交易。注意,這並不意味著合約會自行啟動。它依賴於用戶對調用“選擇獲勝者”函數感興趣(以太獎勵)。當然,如果沒有人調用您的函數,您的彩票將失敗。
Random.org 提供一個 API,通過 JSON 提供隨機數據源。以太坊智能合約可以使用此數據源來饋送選擇隨機數的算法。由於安全性很重要,可以使用數字簽名。隨機數據將由 Random.org 簽名。您可以驗證數據的完整性,以便您可以證明它確實來自 Random.org 並且數據未被篡改。
RANDAO 是區塊鏈領域中的一個新項目,它完全專注於提供隨機數。他們結合使用預言機和智能合約來為您提供隨機數。但是,RANDAO 服務目前非常慢。如果您有一個經常使用的應用程序,這不是理想的選擇。
您還可以在代碼中使用監視器,該監視器檢查區塊編號,直到它與您設置的目標編號匹配。
<code class="language-solidity">function random() private view returns (uint8) { return uint8(uint256(keccak256(block.timestamp, block.difficulty))%251); }</code>
來源。 Gist。
iOlite 正在創建一個接受自然語言來創建智能合約的產品。它使用斯坦福自然語言處理 (NLP) 引擎,稱為快速適應引擎 (FAE)。 iOlite 依賴於 Solidity 專家進行社區培訓。 Solidity 專家(貢獻者)可以定義包含一個或多個句子的結構,並將其附加到相應的智能合約代碼。
斯坦福 NLP 引擎旨在理解複雜的語言。語言複雜程度取決於機器訓練量。經過適當的訓練後,該引擎將能夠創建複雜的智能合約。 FAE 能夠創建此類合約,因為複雜的合約實際上並不那麼複雜。專家可以將請求拆分為多個較小的代碼片段,並將其附加到一個句子。
當有人輸入多個句子時,它將查找相應的結構/句子來構建“複雜”合約。貢獻者將通過新結構的挖掘過程獲得 iOlite 代幣獎勵。
使用 iOlite 的好處是智能合約專家可以為您解決諸如隨機數生成之類的難題。您可以在 iOlite.io 找到更多信息。
結論
如您所見,生成真正的隨機輸入並非易事。不要依賴 block.timestamp
、now
和 block.blockhash
作為隨機性來源。一個好的解決方案包括組合幾個偽隨機數據輸入和使用預言機或智能合約以使其更可靠。您需要 100% 確定沒有人可以篡改輸入到智能合約中的數據。
請謹慎操作,並在實施隨機數生成邏輯之前三思而後行。
Solidity 中隨機數生成的常見問題解答 (FAQ)
Solidity(用於編寫以太坊智能合約的編程語言)沒有內置函數來生成隨機數。這是因為區塊鏈(以太坊的基礎技術)本質上是確定性的。這意味著給定一組輸入,輸出將始終相同。這種確定性對於維護區塊鏈的完整性和安全性至關重要。但是,它使生成真正的隨機數成為一項挑戰,因為隨機性的概念本質上是非確定性的。
開發人員使用幾種方法在 Solidity 中生成偽隨機數。一種常用方法是使用 keccak256 哈希函數,其輸入難以預測,例如當前區塊時間戳和區塊難度。另一種方法是使用預言機服務,該服務從鏈下源提供隨機數。但是,每種方法都有其自身的局限性和潛在安全風險。
雖然 keccak256 哈希函數可用於生成偽隨機數,但它存在一些潛在的安全風險。由於哈希函數的輸入(例如當前區塊時間戳和區塊難度)在區塊鏈上公開可用,因此惡意礦工可能會操縱這些值以影響隨機數生成的輸出。
預言機服務可以從鏈下源提供隨機數。這些服務充當區塊鍊和外部世界之間的橋樑,允許智能合約與區塊鏈本身不可用的數據進行交互。但是,使用預言機服務會引入一定程度的信任,因為智能合約必須依賴預言機來提供準確且無偏見的隨機數。
承諾-揭示方案是一種以去中心化和安全的方式生成隨機數的方法。在承諾-揭示方案中,參與者首先承諾一個秘密數字,然後所有秘密數字同時被揭示,並根據這些秘密生成一個隨機數。這種方法可以防止任何單個參與者能夠影響隨機數生成的輸出。
真正的隨機數對於許多類型的智能合約至關重要,例如用於機會遊戲、彩票和其他需要隨機性的應用程序的智能合約。如果這些合約中使用的隨機數可以預測或影響,則可能導致不公平的結果,甚至允許惡意行為者利用該合約。
Solidity 中的 blockhash 函數可用於生成偽隨機數。此函數返回給定區塊編號的哈希值,該值是不可預測的,並且隨每個區塊而變化。但是,此方法有其局限性。例如,blockhash 函數僅適用於最近的 256 個區塊,並且在挖掘之前無法知道未來區塊的 blockhash。
使用當前區塊時間戳作為生成隨機數的輸入有一個主要局限性。礦工對他們挖掘的區塊的時間戳具有一定的影響力,這意味著他們可能會操縱時間戳以影響隨機數生成的輸出。
RANDAO(隨機數 DAO)信標是一種去中心化且透明的生成隨機數的方法。 RANDAO 信標中的參與者承諾秘密數字,然後這些數字被揭示並組合起來生成一個隨機數。這種方法旨在防止任何單個參與者能夠影響隨機數生成的輸出。
正在進行關於改進 Solidity 和其他區塊鏈平台中隨機數生成的研究和提案。例如,以太坊 2.0(以太坊網絡的即將到來的升級)預計將包括一個內置的隨機數生成器。但是,在這些改進實施之前,開發人員必須繼續使用現有方法及其固有的局限性和潛在安全風險。
以上是堅固性陷阱:以太坊的隨機數生成的詳細內容。更多資訊請關注PHP中文網其他相關文章!