本文来自于 8 月19 日 Chainlink 开发者社区中国负责人 Frank ,在 DApp Learning 分享会上对于 Chainlink 预言机的原理的讲解。
本文来自于 8 月19 日 Chainlink 开发者社区中国负责人 Frank ,在 DApp Learning 分享会上对于 Chainlink 预言机的原理的讲解,以下是这节分享会的总结内容。有兴趣的小伙伴可以结合视频一起学习:
区块链是一个封闭的确定性系统,每一笔交易都需要不同节点共识,只有超过一定数量的节点共识成功,交易才会被真正认可,并写入区块链。
因为对于外部 API 的调用并不是一个确定性操作,所以智能合约没有实现外部 API 调用的功能。除此之外,因为交易广播到其他节点时,各个节点执行交易的时间不一致,所以获取的数据可能不同。比如 A 节点执行一笔交易,其中调用了一个API Call,在把这个交易打包入块让别的节点去验证的时候,其他节点也需要执行这个 API call,执行时间跟打包交易的节点执行时间不一样,得到的结果不一定相同,从而共识失败,导致交易无法写入区块链中。
随机数在区块链应用中有大量的应用场景。
随机数的生成,同样是一个不确定的操作,因为随机数的生成是不可预知的,所以不同的节点,在执行时得到的随机数算法的时候,得到的结果肯定不一样,因此会造成交易执行结果不一致性从而无法达成共识。
通过外部预言机输入可验证的随机数,让链上智能合约只接受并且验证随机数,就可以保证交易执行的一致性。同时还可以实现随机数不可提前预知,并且可以通过证明保证其安全性。
由于智能合约无法调用外部 API 这一特性,所以诞生了预言机这个机制用来帮助智能合约获取外部数据,除了应用最广泛的价格数据以外,还包括一些天气数据,体育比赛数据,股票市场数据,交通数据,甚至包括总统选结果等数据。
除了提供数据,预言机广义上的功能也包括提供随机数和作为触发器实现智能合约执行,它们都算是链下的工具来和链上的合约进行交互。
在架构上,预言机分为两类,中心化预言机和去中心化预言机。最初的预言机解决方案中以中心化预言机为主。最简单的方式就是在链下搭建一个服务器,服务器从不同数据源获取数据。然后由开发人员在服务器中写一个脚本,根据时间间隔或者链上智能合约的状态,通过服务器把数据发送给链上合约。
虽然能够实现给链上合约发送“喂”数据的功能,但是中心化预言机有一个很大的问题是它存在单点失败的风险。用户在区块链上部署智能合约,本意是希望区块链网络中众多的节点来保证合约的安全性跟公平性。在这个前提下,对于外部数据,合约所依赖的数据却是通过一个中心化服务来输入,就会导致整体的安全性降低。就像木桶原理,合约的安全性、公平性在链上可以得到充足保证,每一个板都很长,唯独中心化预言机是短板,因此盛的水也变少了。
对于中心化预言机而言,网络故障或者宕机都会造成业务中断,导致用户无法及时获取数据。除此以外,中心化意味着运营方的单一,当中心化预言机发现一些 DeFi 协议或者 NFT 项目中依赖于它的数据,也不排除提供恶意数据获得利益的可能。上述情况的发生很难避免,我们称它为单点失败风险。
另一类是去中心化预言机,Chainlink 预言机就是一个去中心化预言机网络(Decentralized oracle network:DON)。
Chainlink 基于自身的预言机网络可以给链上的智能合约提供多项服务,比如说“喂价”,合约的自动化执行,生成可验证的随机数,获取任意外部的 API 数据,甚至能够把链上智能合约的一些计算或判断放到链下完成,然后返回结果,从而节省 gas 费。
Chainlink 预言机把中心化预言机节点替换为去中心化网络,在网络中有很多预言机节点,每个预言机节点都可以通过自己的渠道去获取数据,然后在去中心化网络中对获得的数据进行共识。这里的共识方式并不是 BFT,POS,POW 这个意义的共识,而是为了获取可靠的数据,比如说取平均数;或者类似于体育比赛里面,去掉一个最高分,去掉一个最低分,剩下的取平均数或者中位数,现在 Chainlink 采取的是中位数的共识方式。
Chainlink 把预言机通过去中心化的网络,在技术上避免了单点失败的风险。如同以太坊节点,当一个节点中断或者退出时,不会影响到整个网络的安全性和可用性。另外在这个数据上,由于采用多种数据源,所以不会被一个单方面数据源所操控,充分地利用了去中心化的优势。
Chainlink 对自身的去中心化预言机网络外推出了很多服务,第一个服务是 Chainlink Data Feeds,可以为链上智能合约喂价。
在 Chainlink Data Feeds 中,不同的预言机节点,通过自己的数据提供商获取价格数据,然后通过预言机网络对多个数据聚合后。比如一个 token 价格,A 节点报价是 $400,B 节点报价是 $399,C 节点报价是$401,预言机节点将所有的报价进行共识后,会把价格中位数 $400 传给链上的智能合约,完成此次喂价。
Data feeds 的业务流程涉及两个参与方,第一个是数据提供商,它们会使用自己的数据,或通过第三方获取相应的数据,之后输入到 Chainlink 预言机网络中的一个节点。另外一个参与方就是预言机节点,每个节点可以有一个或者多个数据提供商,每个提供商输入的数据都会在预言机网络里中进行共识,然后网络中会随机选取一个节点,由该节点提交数据到链上。
Data feeds 在链上会部署一个叫做 Aggregator 的合约,内部使用 Mapping 存储节点网络聚合后的中位数价格。对于用户合约,可以调用 Aggregator 函数,获得相应的价格数据。
Chainlink Data Feeds 支持多个不同的区块链网络,如BNB,Solana,Polygon等 ,可以在 Data Feeds 页面找到目前支持的网络和价格数据。同时对于每个价格数据,都有相应的参数控制数据的更新频率,以保证数据的实时性和有效性。
拿 ETH对USD 价格举例,这里有两个参数,第一个参数叫 Deviation threshold ( 波动阈值 )。如果当前聚合价格对比最近一次更新的价格,波动率超过0.5%,预言机需要立即更新 ETH 的当前价格。另一个是 heartbeat ( 心跳计时 ),距离上一次价格更新时间间隔超过 1 个小时的话,预言机网络就会进行新一轮的更新。需要说明的是,当 Deviation threshold 触发更新后,Heartbeat 会重置为 1 小时,重新开始计时。
从上面还可以看到提供价格数据的节点信息,包括节点的数量,节点提供的价格数据,以及节点运营商。可以看到很多大型机构参与了 Chainlink 预言机网络,包括 T-Mobile、SNZ、SyncNode 等,后续 Chainlink 还会进一步扩大合作机构,提供更加稳定和安全的去中心化的喂价服务。
Chainlink Data Feeds 主要包含了三个合约,第一个是 Consumer 合约 ( 用户合约 ),第二个是 Proxy 合约 ( 代理合约 ),第三个就是刚才提到的 Aggregator 合约 ( 聚合合约 )。Proxy 合约作为接口连接 Consumer 合约和 Aggregator 合约,屏蔽了 Aggregator 的复杂性,对外向用户提供一个统一的接口(latestRounddata),返回预言机网络最近一次的价格数据。
Data feeds 应用场景非常广泛,很多主流 DeFi 项目都集成了 Data Feeds。
最常见的一个应用场景是借贷协议,比如 Ethereum 上的 AAVE,Compound , BNB 上的 Venus。当用户在 AAVE 上存入一个 BTC 然后贷出来 USD 时, AAVE 需要知道 BTC 和 DAI 的兑换比例,才能决定给用户贷多少 USD。
第二个应用场景是合成资产,比如Synthetix(SNX),它可以让用户去交易一些主流资产,比如美股股票。用户合成资产时,协议肯定需要知道资产价格,这个价格就是通过 Data Feeds 获取的。
第三个应用场景是抵押型 Stablecoin 。抵押型 Stablecoin 要发行的话,需要抵押相应的资产。Stablecoin 的协议需要通过 Data Feeds 获取资产的价格后,才可以计算得出抵押资产的总价值,根据总价值决定 Stablecoin 的发行数量。
第四个是资产管理和衍生品的交易平台。像期权、期货交易平台,它们对价格都很敏感,需要提供稳定、准确的价格数据, Data Feeds 提供的数据完美的满足了它们的业务需求。
Chainlink 是一个开源项目,任何人都可以在 Github 下载分析,在其中可以找到 Data Feeds 的合约代码。Chainlink 合约从 solc0.4 开始迭代,到当前截稿日期为止,已经迭代到 solc0.8,所以在 contracts/src 目录中可以发现 v0.4、v0.5、v0.6、v0.7、v0.8 这几个子目录。
上面我们提到用户是通过调用 Proxy 来获取价格数据,对应的具体合约为 AggregatorProxy 代理合约。使用代理合约的目的在于预言网络根据需要会进行更新,因而预言机节点发送的数据格式也会有所不同。如果用户合约直接使用 Aggregator 合约读取价格数据,当数据格式发生变化时,有可能有服务中断的风险,即用户合约无法及时获得价格数据的风险。因此,基于上述考虑,Chainlink 官方针对于每一个价格对,会部署一个 Aggregator 合约,用户不直接使用 aggregator 合约,而是通过 Proxy 合约获取 aggregator 中的价格数据。
AggregatorProxy继承AggregatorProxyInterface 接口,实现了其中的 phaseAggregators、phaseId、proposedAggregator 等接口,同时还额外给用户提供了 latestAnswer、latestTimestamp、latestRound、latestRoundData 等接口,用以获取更新时间戳、轮次等。
上一个版本中,AggregatorProxy 对应的 Aggregator 的实现为 FluxAggregator,其中 Flux 为预言机网路共识机制名称。在 2021 年初,Chainlink 对预言机网络进行了升级,共识机制变更为 OCR(Off-chain Reporting) ,节点喂价效率提升了 10 倍。
OCR 和 Flux 的区别可以简单理解为,Flux 机制中,节点把数据提供商的数据统一上报到链上,在链上完成数据的聚合。我们知道,链上计算不仅需要消耗 gas ,而且会耗费很多时间。而在 OCR 机制中,数据在预言机节点网络中完成聚合,最后由其中一个节点上报到链上,节省了大量的 gas 和计算时间,效率因此得到大幅提升。
正如智能合约无法主动获取外界数据一样,也无法自己触发自己。在没有自动化工具之前,智能合约都是由开发者手动触发的。对于小型项目方,需要自己在服务器上写一段脚本,每次通过私钥去触发合约交易,这样不仅存在曾经提到的单节点风险,并且会占用团队的时间和资源。
除了手动触发以外,还可以通过 Bounty 模式实现智能合约的自动执行。挖过 YFI 的人都知道,曾经一段时间,YFI 需要用户手工点一下 Claim,才能把收益发到各个用户的钱包里,每次点击的人会发一些额外的奖励,这就是 Bounty 模式。有一个著名的项目叫 ETH Alarm Clock,可以给某个时间点去触发某个交易的人汇一些赏金。它的问题在于只有一个人能成功触发交易,而成功触发的人会获得所有奖励,这会导致 Gas 竞赛。为了成为成功触发的那个人,大家不断增加 Gas Fee ,导致了链的拥堵,就像是抢 NFT 一样。但是实际上,在这个场景中,只要一个人去调用函数就可以了,很多人抢会造成了不必要的浪费。
Chainlink Keepers 是的一个去中心化合约执行服务,可以实现链上合约的自动化执行。
开发团队可以注册一个 UpKeep,每个区块中都会去检测一下监控的的合约状态,如果符合预设条件,就调用函数,当然也可以不设置预设条件(相当于条件判断结果恒等于True),根据时间来调用特定合约中的特定函数。
Chainlink Keepers 可以在不需要输入的情况下,不断地根据预设的逻辑去检测智能合约的状态,如果满足的话就执行,不满足的话,等待下一次检测。
Chainlink Keepers 首先调用 UpKeep 合约里面的检测函数,执行函数中的条件判断,判断条件结果为 True 或者 False,如果是结果为 False 就跳过,在下一个区块重新检查。如果为True,会通过 UpKeep Registry 合约调用 UpKeep 合约的 PerformUpKeep 函数,用户可以在 PerformUpKeep 中定义具体的执行逻辑。
Chainlink Keepers 由三个合约组成,分别为 KeepersCompatible、KeepersRegistrar、KeepersRegistry。
KeepersCompatible 是用户合约,为了保证 KeepersRegistry 能成功回调,KeepersCompatible 合约需要实现两个函数,一个是 checkUpKeep,另一个是 performUpKeep。KeepersCompatible 只有在 KeepersRegistar 中注册成功后,预言机节点才能通过 checkUpKeep 判断是否需要执行 performUpKeep 。
具体注册、调用流程如下:
Chainlink 的第三个去中心化产品是可验证随机数 VRF。在VRF出现之前,主流的生成随机数的方法是根据当前区块中的交易生成 Hash,使用这个 Hash 作为种子生成随机数,由此引出了 MEV 问题。矿工可以选择性打包交易,得到他们想要的随机数,虽然成本比较高,但当利润回报很高时,矿工作恶的可能性也会相应提高很多。
在 Chainlink VRF 中,随机数是由预言机网络生成。用户传入一个种子给 VRF 合约,预言机 VRF 节点会使用节点私钥和种子生成一个随机数和 Proof ( 证明 ) 返回给 VRF 合约,VRF 合约 Proof 验证随机数的合法性,如果通过验证,就会把随机数返回给用户。跟单纯链下生成随机数不同的是,Chainlink VRF 生成的随机数可以通过 Proof 证明它是根据特定椭圆曲线算法算出来的,具有可验证性、独特性。
VRF 本身是一个随机算法的名字,在 1999 年由Micali、Rabin 和 Vadhan 首次提出 VRF这个概念。
相比其他算法,VRF 有三个主要特点:
VRF算法由三个函数构成:
VRF 这三个函数就是使用 Chainlink VRF 的三个步骤。预言机节点生成密钥对时,会把公钥公布并且保存在链上验证合约中,私钥保存在节点内部。之后用户发起一笔交易到 VRF 链上合约去请求随机数,合约在区块中生成 Event Log,并在其中记录用户传入的种子。预言机节点监听到这个 Event Log 后,生成随机数和 Proof 发回给 VRF 链上合约,合约验证通过则写入随机数到用户合约,否则不写入,这就是整个 VRF 的实现过程。
用户 Consumer 合约不直接跟预言机节点交互,而是通过 Coordinator 合约协助。就像 Keepers 一样,用户需要在 Consumer 合约中实现特定的函数,以便接受 Coordinator 合约回传的随机数。这里需要实现的回调接口为 FulfillRandWords,具体调用流程如下:
用户在 Consumer 合约中调用 Coordinator 合约的 RequestRandomWords 接口请求 VRF 随机数。Coordinator 合约收到请求后,生成 Event Log,并在其中写入 Meta data 等配置信息。链下预言机监控到出现 Event Log 后,提取里面的配置信息,结合当前的区块 Hash 算出一个种子,具体算法为用户 Seed 加区块哈希再生成一个新的哈希作为最终 Seed。
预言机节点根据种子和自己的私钥计算得到随机数 ( randomness ) 和 Proof ,然后调用 Coordinator 合约的 FullfillRandomWords 接口进行回传。Coordinator 合约对收到的随机数和 Proof 进行验证,验证成功以后通过 FulfillRandomWords 接口把随机数返回给 Consumer 合约,最终用户就收到这个随机数了。
至于随机数的使用,用户可以在 FulfillRandomWords 接口中实现,也可以编写另外的函数去实现。Coordinator 合约调用 Consumer 的 FulfillRandomWords 接口时有 callBack gas limit 限制,当 FulfillRandomWords 接口中消耗的 gas 超过这个 limit 时,Coordinator 会调用失败,导致无法传递随机数给i Consumer 合约,所以 FulfillRandomWords 接口中避免实现复杂的逻辑,最好只做一个随机数的存储,然后通过另外的接口去使用。
Chainlink VRF 目前最大的应用场景是 NFT Mint。NFT 需要创建、分发,创建的时候,每一个 NFT 的稀有度不同,分配的用户不同。根据业务需要,有可能还要设置白名单,白名单里面的 holder 空投不同的 NFT。比如无聊猿猴项目,空投 serum 血清给 Holder ,血清的随机空投就是用的 VRF 随机数。
另一个应用是抽奖。比如一些 IDO 平台,用户购买通证,然后去质押通证,平台会赠送白名单参与抽奖,奖励的大小就可以使用 VRF 进行选取。
具体的使用场景还有挺多的,除了加密货币领域,甚至在现实生活中,比如说摇号买房、摇号买车或者品牌营销抽奖,其实也都可以使用 VRF,相信在不远的将来,可验证随机数会成为随机数场景中的主流应用。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!