Chainlink VRF 深度解析与实战

背景

在区块链的去中心化应用中,随机性是一个常见但难以实现的需求。例如,区块链游戏需要随机决定战斗结果,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 的核心是一个两步流程:

  1. 请求随机数:智能合约(称为消费者合约)向 Chainlink VRF 协调器(Coordinator)发送请求,指定所需的随机数数量和其他参数

  2. 履行随机数请求:Chainlink 预言机在链下生成随机数,并使用私钥对其进行签名。然后,预言机将随机数和密码学证明一起发送到消费者合约。合约使用预言机的公钥验证证明,确保随机数未被篡改

核心组件

  • VRF 协调器:一个管理随机数请求和响应的智能合约,充当消费者合约和 Chainlink 预言机之间的桥梁

  • 订阅模型:在 VRF v2.5 中,用户通过 LINK 代币为订阅账户充值,用于支付随机数请求费用。每个订阅都有一个唯一 ID

  • 证明验证:Chainlink 预言机使用椭圆曲线密码学生成证明,消费者合约在接受随机数前会验证此证明,确保随机性来源可信

Chainlink VRF 的优势

  1. 可验证性:随机数附带密码学证明,任何人都可以验证其真实性

  2. 防篡改:链下生成随机数避免了链上攻击(如矿工操纵)

  3. 灵活性:支持多种区块链网络,并且可以请求多个随机数

  4. 易于集成:Chainlink 提供了详细的文档和库,开发者可以快速集成 VRF

典型用例

Chainlink VRF 的应用场景非常广泛,以下是一些典型案例:

  • 区块链游戏:为游戏中的随机事件(如抽卡、战斗结果)生成公平的随机数

  • NFT 铸造:随机分配稀有属性或决定 NFT 的生成顺序

  • 去中心化抽奖:确保获奖者选择过程公平透明

  • 随机分配:在去中心化金融(DeFi)或治理协议中随机选择参与者

集成 Chainlink VRF 的步骤

以下是如何在 Arbitrum Sepolia(或其他 EVM 兼容链)上集成 Chainlink VRF v2.5 的详细步骤:

1. 前置条件

  • 安装环境

    • 安装 Hardhat 或 Foundry 用于开发和部署智能合约

    • 安装 Chainlink 合约库(通过 npm 或直接导入)

    • 或者前面的两条都不用准备,直接使用 Remix

  • 准备 LINK 代币:用于支付 VRF 请求费用

  • 创建订阅(部署的时候我会给出详细步骤图解 

    1. 访问 Chainlink VRF 订阅页面   https://vrf.chain.link/

    2. 创建一个订阅账户并充值 LINK 代币

    3. 记录订阅 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/83610.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7. TypeScript接口

TypeScript 中的接口(Interfaces)用于定义对象的结构。它们允许开发者指定一个对象应具有哪些属性以及这些属性的类型。接口有助于确保对象遵循特定的结构,从而在整个应用中提供一致性,并提升代码的可维护性。 一、认识接口 Typ…

UE 新版渲染器输出视频

安装包解压到C盘 打开UE插件 Movie Render Queue 进入UE引擎在项目设置找到 libx264 aac mp4 影片渲染队列调用出 命令行编码器安装包路径,序列输出路径,定序器不能有中文

基于用户的协同过滤推荐算法实现(Java电商平台)

在电商平台中,基于用户的协同过滤推荐算法是一种常见的推荐系统方法。它通过分析用户之间的相似性来推荐商品。以下是一个简单的实现思路和示例代码,使用Java语言。 实现思路 数据准备:收集用户的评分数据,通常以用户-商品评分矩…

LeetCode - 904. 水果成篮

题目 904. 水果成篮 - 力扣(LeetCode) 思路 题目本质 你有一个整数数组,每个元素代表一种水果。你只能用两个篮子,每个篮子只能装一种水果。你要在数组中找一个最长的连续子数组,这个子数组里最多只包含两种不同的…

发现 Kotlin MultiPlatform 的一点小变化

最近发现 Kotlin 官方已经开始首推 Idea 的社区版的 KMP 插件了. 以前有网页创建 KMP 的项目的文档也消失了. 虽然有 Android Studio 的选项. 但是却不是在默认的位置上了. 足以说明官方是有意想让大家直接使用 Idea 社区版或者专业版 所以我直接在社区版上安装 KMP 插件. 尝试…

【Photoshop】金属字体制作

新建一个空白项目,选择横排文字工具,输入想要的文件建立文字图层 选择横排文字工具选择出文字内容,在通知栏出点击’拾色器‘,设置好需要的文字颜色 图层面板右下角点击‘添加图层样式’,选择斜面和浮雕 样式设置为内斜…

centos 7.9 升级ssh版本 7.4p1 升级到 8.2p1

centos 7.9 升级ssh版本 7.4p1 升级到 8.2p1 1、安装包下载2、安装telnet3、安装openssl-OpenSSL_1_1_1f.tar.gz4、安装openssh-8.2p1.tar.gz5、修改ssh服务的相关配置文件6、确定可以ssh连接服务器后,卸载telnet,因为telnet不安全 本文是离线环境下升级…

stm32---dma串口发送+fifo队列框架

之前分享了一个关于gd32的fifo框架,这次就用stm32仿照写一个,其实几乎一样,这次说的更详细点,我全文都写上了注释,大家直接cv模仿我的调用方式即可 uasrt.c #include "stm32f10x.h" // D…

【生产就曲篇】让应用可观测:Actuator监控端点与日志最佳实践

摘要 本文是《Spring Boot 实战派》系列的终章,我们将探讨如何让应用真正达到**“生产就绪” (Production-Ready)** 的标准。文章的核心是可观测性 (Observability),即从外部了解一个系统内部运行状态的能力。 我们将深度挖掘 Spring Boot Actuator 的…

操作系统知识(1)

操作系统的分类总结 1、批处理操作系统:单道批处理和多道批处理(主机与外设可并行) 2、分时操作系统:一个计算机系统与多个终端设备连接。将CPU的工作时间划分为许多很短的时间片,轮流为各个终端的用户服务。 3、实时操作系统:实时是指计算机对于外来信息能够以足…

一.Sharding分库分表-基因法+自定义多key分片实现多字段查询

前言 当下遇到这样一个场景,由于订单数据量达到千万级别,采用分库分表进行优化,根据订单的热查条件:order_no订单编号进行分表,但是这样带来一个问题,用户查询自己的订单怎么查?由于分片键使用…

【leetcode】543. 二叉树的直径

二叉树的直径 题目题解解释 题目 543. 二叉树的直径 给你一棵二叉树的根节点,返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 题解 …

AI基础知识(07):基于 PyTorch 的手写体识别案例手册

目录 实验介绍 实验对象 实验时间 实验流程 实验介绍 随着人工智能技术的飞速发展,图像识别技术在众多领域得到了广泛应用。手写体识别作为图像 识别的一个重要分支,其在教育、金融、医疗等领域具有广泛的应用前景。本实验旨在利用深度 学习框架 PyTorc…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

信号(瞬时)频率求解与仿真实践(2)

引言 本文是信号(瞬时)频率求解与仿真实践专题的第二篇文章,在上一篇博文 [1]信号(瞬时)频率求解与仿真实践(1)-CSDN博客中,我构建了信号瞬时频率求解的基本框架,并且比较详细地讨论了瞬时频率法。这篇博文探讨适用于信号瞬时频率求解的另一种…

Linux运行发布jar文件携带哪些参数

在 CentOS 8 上运行发布的 JAR 文件时,可以根据不同需求携带以下参数: 1. 基本运行方式 bash 复制 下载 java -jar your-application.jar 2. 常用 JVM 参数 参数说明-Xms256m初始堆内存大小(如 256MB)-Xmx1024m最大堆内存大小(如 1GB)-XX:MaxMetaspaceSize=256m元空间…

在GIS 工作流中实现数据处理(4)

结果输出与可视化 最后,我们将统计结果输出为一个 Excel 文件,并在 ArcMap 中对城市中心区域的土地利用情况进行可视化展示。 import pandas as pd# 将统计表格转换为 pandas DataFrame df pd.read_csv(statistics_table, sep"\t")# 输出为…

【术语解释】网络安全((SAST, DAST, SCA, IAST),Hadoop, Spark, Hive 的关系

## OWASP Top 10等 OWASP Top 10:OWASP (Open Worldwide Application Security Project,开放全球应用程序安全项目) Top 10 是一份由全球安全专家定期更新的报告,列出了当前 Web 应用程序面临的十大最关键安全风险。 它是一个广受认可的意识文…

NY197NY205美光闪存固态NY218NY226

NY197NY205美光闪存固态NY218NY226 美光科技作为全球领先的半导体存储解决方案供应商,其闪存固态硬盘(SSD)产品线一直备受业界关注。NY197、NY205、NY218和NY226是美光近期推出的几款重要固态硬盘型号,它们在性能、容量和适用场景…

MinHook 对.NET底层的 SendMessage 拦截真实案例反思

一:背景 1. 讲故事 上一篇我们说到了 minhook 的一个简单使用,这一篇给大家分享一个 minhook 在 dump 分析中的实战,先看下面的线程栈。 0:044> ~~[138c]s win32u!NtUserMessageCall0x14: 00007ffc5c891184 c3 ret 0:061&g…