背景
在區塊鏈的去中心化應用中,隨機性是一個常見但難以實現的需求。例如,區塊鏈游戲需要隨機決定戰斗結果,NFT 項目需要隨機分配稀有屬性,去中心化抽獎需要公平選擇獲獎者。然而,傳統的鏈上隨機數生成方法(如使用 block.timestamp 或 blockhash)存在嚴重缺陷:這些值可被礦工或惡意節點預測和操縱,導致隨機性不可靠,容易引發不公平或安全問題。此外,鏈上生成隨機數的計算成本高,且無法提供可驗證的公平性。
為解決這些問題,Chainlink 推出了 VRF(Verifiable Random Function,可驗證隨機函數)。Chainlink VRF 通過鏈下生成隨機數并結合密碼學證明,確保隨機性既安全又可公開驗證,同時避免了鏈上隨機性生成的高成本和潛在攻擊風險。這使得 VRF 成為區塊鏈應用中實現公平、透明隨機性的標準解決方案。
什么是 Chainlink VRF?
Chainlink VRF(Verifiable Random Function,可驗證隨機函數)是一種為區塊鏈應用設計的可證明公平且防篡改的隨機數生成器。它通過密碼學手段確保隨機數的生成過程安全、可驗證且不可預測。Chainlink VRF 是區塊鏈游戲、NFT 鑄造、抽獎系統以及其他需要隨機邏輯的去中心化應用的理想選擇。
與傳統的偽隨機數生成器不同,這些方法容易受到礦工操縱,Chainlink VRF 提供了更安全、透明的隨機數解決方案,廣泛應用于區塊鏈網絡。
Chainlink VRF 的工作原理
Chainlink VRF 的核心是一個兩步流程:
-
請求隨機數:智能合約(稱為消費者合約)向 Chainlink VRF 協調器(Coordinator)發送請求,指定所需的隨機數數量和其他參數
-
履行隨機數請求:Chainlink 預言機在鏈下生成隨機數,并使用私鑰對其進行簽名。然后,預言機將隨機數和密碼學證明一起發送到消費者合約。合約使用預言機的公鑰驗證證明,確保隨機數未被篡改
核心組件
-
VRF 協調器:一個管理隨機數請求和響應的智能合約,充當消費者合約和 Chainlink 預言機之間的橋梁
-
訂閱模型:在 VRF v2.5 中,用戶通過 LINK 代幣為訂閱賬戶充值,用于支付隨機數請求費用。每個訂閱都有一個唯一 ID
-
證明驗證:Chainlink 預言機使用橢圓曲線密碼學生成證明,消費者合約在接受隨機數前會驗證此證明,確保隨機性來源可信
Chainlink VRF 的優勢
-
可驗證性:隨機數附帶密碼學證明,任何人都可以驗證其真實性
-
防篡改:鏈下生成隨機數避免了鏈上攻擊(如礦工操縱)
-
靈活性:支持多種區塊鏈網絡,并且可以請求多個隨機數
-
易于集成:Chainlink 提供了詳細的文檔和庫,開發者可以快速集成 VRF
典型用例
Chainlink VRF 的應用場景非常廣泛,以下是一些典型案例:
-
區塊鏈游戲:為游戲中的隨機事件(如抽卡、戰斗結果)生成公平的隨機數
-
NFT 鑄造:隨機分配稀有屬性或決定 NFT 的生成順序
-
去中心化抽獎:確保獲獎者選擇過程公平透明
-
隨機分配:在去中心化金融(DeFi)或治理協議中隨機選擇參與者
集成 Chainlink VRF 的步驟
以下是如何在 Arbitrum Sepolia(或其他 EVM 兼容鏈)上集成 Chainlink VRF v2.5 的詳細步驟:
1. 前置條件
-
安裝環境:
-
安裝 Hardhat 或 Foundry 用于開發和部署智能合約
-
安裝 Chainlink 合約庫(通過 npm 或直接導入)
-
或者前面的兩條都不用準備,直接使用 Remix
-
-
準備 LINK 代幣:用于支付 VRF 請求費用
-
創建訂閱(部署的時候我會給出詳細步驟圖解?):
-
訪問 Chainlink VRF 訂閱頁面? ?https://vrf.chain.link/
-
創建一個訂閱賬戶并充值 LINK 代幣
-
記錄訂閱 ID
-
2. 編寫智能合約
以下官方展示如何使用 Chainlink VRF v2.5 生成隨機數,獲取家族名稱的案例:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;import {VRFConsumerBaseV2Plus} from "@chainlink/contracts@1.4.0/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts@1.4.0/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";// 使用隨機數模擬擲20面骰子的Chainlink VRF消費者合約
contract VRFD20 is VRFConsumerBaseV2Plus {// 表示骰子正在擲的狀態uint256 private constant ROLL_IN_PROGRESS = 42;// 你的訂閱IDuint256 public s_subscriptionId;// arbitrum-sepolia 網絡的VRF協調者地址address public vrfCoordinator = 0x5CE8D5A2BC84beb22a398CCA51996F7930313D61;// 使用的gas通道,指定最大gas價格bytes32 public s_keyHash =0x1770bdc7eec7771f7ba4ffd640f34260d7f095b79c92d34a5b2551d6f6cfd2be;// 回調函數的gas限制,存儲每個隨機數約需20000 gasuint32 public callbackGasLimit = 40000;// 請求確認數,默認為3uint16 public requestConfirmations = 3;// 請求的隨機數數量,最大不超過 VRFCoordinatorV2_5.MAX_NUM_WORDSuint32 public numWords = 1;// 映射:請求ID到擲骰者地址mapping(uint256 => address) private s_rollers;// 映射:擲骰者地址到VRF結果mapping(address => uint256) private s_results;// 事件:骰子已擲出event DiceRolled(uint256 indexed requestId, address indexed roller);// 事件:骰子結果已返回event DiceLanded(uint256 indexed requestId, uint256 indexed result);// 構造函數,繼承VRFConsumerBaseV2Plusconstructor(uint256 subscriptionId) VRFConsumerBaseV2Plus(vrfCoordinator) {s_subscriptionId = subscriptionId;}// 請求隨機數,模擬擲骰子function rollDice(address roller) public onlyOwner returns (uint256 requestId) {// 確保未擲過骰子require(s_results[roller] == 0, "Already rolled");// 請求隨機數requestId = s_vrfCoordinator.requestRandomWords(VRFV2PlusClient.RandomWordsRequest({keyHash: s_keyHash,subId: s_subscriptionId,requestConfirmations: requestConfirmations,callbackGasLimit: callbackGasLimit,numWords: numWords,extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false}))}));s_rollers[requestId] = roller;s_results[roller] = ROLL_IN_PROGRESS;emit DiceRolled(requestId, roller);}// VRF協調者回調函數,返回隨機數function fulfillRandomWords(uint256 requestId,uint256[] calldata randomWords) internal override {// 計算20面骰子結果uint256 d20Value = (randomWords[0] % 20) + 1;s_results[s_rollers[requestId]] = d20Value;emit DiceLanded(requestId, d20Value);}// 獲取玩家的家族名稱function house(address player) public view returns (string memory) {// 確保已擲骰子require(s_results[player] != 0, "Dice not rolled");// 確保擲骰完成require(s_results[player] != ROLL_IN_PROGRESS, "Roll in progress");return _getHouseName(s_results[player]);}// 根據ID獲取家族名稱function _getHouseName(uint256 id) private pure returns (string memory) {string[20] memory houseNames = ["Targaryen","Lannister","Stark","Tyrell","Baratheon","Martell","Tully","Bolton","Greyjoy","Arryn","Frey","Mormont","Tarley","Dayne","Umber","Valeryon","Manderly","Clegane","Glover","Karstark"];return houseNames[id - 1];}
}
訪問 Chainlink VRF 訂閱頁面:
創建一個訂閱賬戶的第一步(點擊Create Subscription):交易請求
創建一個訂閱賬戶的最后一步(自動彈出):請求簽名
訂閱賬戶創建成功可以看到:
點擊進入你的訂閱賬戶:復制 Subscription ID,部署合約要用到
部署合約:填入?? Subscription ID
?
部署成功之后, 在你的訂閱賬戶添加一個消費者:復制你的合約地址,然后點擊?Add consumer ,確認交易請求
成功之后,添加 Link 代幣做為費用支出,(因為每次請求隨機數都要消耗 Link 代幣)? 點擊 Fund subscription 后確認交易:
做完之后,你會看到消費者界面:
我們來擲色子,請求一下隨機數:
?
OK,我們來驗證結果,可以看到已經獲取到了家族名稱,證明隨機數請求成功!
?
代碼說明
-
合約繼承:合約繼承 VRFConsumerBaseV2Plus,這是 Chainlink 提供的基合約,用于處理 VRF 請求和回調
-
構造函數:初始化 VRF 協調器地址和訂閱 ID
-
請求隨機數:requestRandomNumber 函數向 VRF 協調器發送請求,指定 keyHash、訂閱 ID、gas 限制等參數
-
接收隨機數:fulfillRandomWords 是回調函數,由 VRF 協調器調用,將生成的隨機數存儲在 randomWords 變量中
-
參數說明:
-
vrfCoordinator:VRF 協調器地址,需根據網絡選擇
-
keyHash:標識預言機的公鑰哈希,決定 gas 價格
-
callbackGasLimit:回調函數的最大 gas 消耗
-
requestConfirmations:等待的區塊確認數,確保隨機數的安全性
-
numWords:請求的隨機數數量
-
注意事項
-
網絡選擇:確保 VRF 協調器地址和 keyHash 與目標網絡匹配
-
LINK 余額:訂閱賬戶需有足夠的 LINK 代幣支付費用
-
Gas 優化:根據回調邏輯的復雜性調整 callbackGasLimit,避免請求失敗
-
安全考慮:限制 requestRandomNumber 的調用權限,防止未經授權的請求
?
實際案例:NFT 隨機屬性
以下是一個簡單的 NFT 合約,展示如何使用 Chainlink VRF 為 NFT 分配隨機屬性:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;import {VRFConsumerBaseV2Plus} from "@chainlink/contracts@1.4.0/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts@1.4.0/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";contract RandomNFT is ERC721, VRFConsumerBaseV2Plus {address vrfCoordinator = 0x5CE8D5A2BC84beb22a398CCA51996F7930313D61;uint256 s_subscriptionId;uint32 callbackGasLimit = 200000;uint16 requestConfirmations = 3;uint32 numWords = 1;bytes32 keyHash =0x1770bdc7eec7771f7ba4ffd640f34260d7f095b79c92d34a5b2551d6f6cfd2be;uint256 public tokenId;mapping(uint256 => uint256) public tokenToAttribute;constructor(uint256 subscriptionId) ERC721("RandomNFT", "RNFT") VRFConsumerBaseV2Plus(vrfCoordinator) {s_subscriptionId = subscriptionId;}function mintNFT() external {s_vrfCoordinator.requestRandomWords(VRFV2PlusClient.RandomWordsRequest({keyHash: keyHash,subId: s_subscriptionId,requestConfirmations: requestConfirmations,callbackGasLimit: callbackGasLimit,numWords: numWords,extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false}))}));_safeMint(msg.sender, tokenId);tokenId++;}function fulfillRandomWords(uint256 /* requestId */,uint256[] calldata randomWords) internal override {uint256 attribute = randomWords[0] % 100; // 生成 0-99 的隨機屬性tokenToAttribute[tokenId - 1] = attribute;}
}
代碼說明
-
NFT 鑄造:用戶調用 mintNFT 函數鑄造 NFT 并觸發 VRF 請求
-
隨機屬性:隨機數生成后,fulfillRandomWords 將其轉換為 0-99 的屬性值并存儲
-
擴展性:可以根據需求調整隨機數的范圍或添加更多屬性
這里的合約我就不帶著大家去操作了, 我已經私下操作成功,留給大家自由發揮,步驟跟前面是一樣的,這里展示的是簡單版本的NFT,如果你的NFT是有元數據的,那就隨機元數據,利用好 requestId,使用基本是一樣的,請大家靈活使用
總結
Chainlink VRF 提供了一種安全、透明、可驗證的隨機數生成方案,為區塊鏈應用的公平性和可信度提供了強有力的支持。通過其訂閱模型和密碼學證明,開發者可以輕松集成隨機性,滿足游戲、NFT、抽獎等多種場景的需求。
截至 2025 年,Chainlink VRF 已在區塊鏈生態中取得了顯著成就。根據 Chainlink 官方數據,VRF 已處理超過 數百萬次隨機數請求,支持了數百個去中心化應用,涵蓋 NFT 項目、區塊鏈游戲和 DeFi 協議。這些應用利用 VRF 的公平性吸引了大量用戶,推動了區塊鏈生態的增長。例如,NFT 項目通過 VRF 實現的隨機屬性分配顯著提升了用戶信任和參與度。此外,Chainlink VRF 的多鏈支持使其成為跨鏈應用的首選隨機性解決方案,覆蓋以太坊、Arbitrum、Polygon、BNB Chain 等主流網絡。
Chainlink VRF 的影響不僅體現在技術層面,還推動了區塊鏈行業的標準化。它的可驗證性和防篡改特性為去中心化應用的公平性樹立了標桿,特別是在高價值的 NFT 和游戲領域。未來,隨著區塊鏈技術的進一步普及,Chainlink VRF 有望在更多領域(如元宇宙、去中心化治理、預測市場)發揮作用。Chainlink 團隊也在不斷優化 VRF 的性能,例如降低 gas 成本、支持更多鏈上場景,將進一步擴大其應用范圍和影響力。
官方文檔:???https://docs.chain.link/vrf/v2-5/getting-started
訂閱VRF:???https://vrf.chain.link/
水龍頭:???? ??https://faucets.chain.link/
代碼倉庫:???https://github.com/BraisedSix/chainlink-learn