ChainLink 预言机学习

分类

中心化预言机

数据源 -> 中心化节点 -> 去中心化网络

去中心化预言机

多个数据节点形成去中心预言机网络,每个节点都会收集数据,达成共识后输入到区块链上的智能合约

  • 技术上避免了单点失败
  • 数据上通过网络对多个数据源进行验证

DataFeed

Data Feed 采用的是多层级数据聚合机制,简单来说就是数据会经过多层级的聚合处理 。首先价格数据聚合器也就是 CoinGecko、Coinmarketcap 这种的数据网站会在众多原始数据源中进行第一次聚合,这些数据聚合器会通过计算生成一个交易量加权平均价来确保交易数据的质量 ,接着就是 Chainlink 的节点运营商会从这些数据聚合器中获取一次价格数据并进行第二次聚合,通常来说会选取其中位数价格,但为什么是中位数呢?如果取平均值的话就会出现某个异常极端数据把最终价格给拉高或拉低的情况,因此从理论情况下,一般取中位数可以较好的保障预言机服务的可靠性。

那么最后一层数据聚合就是发生在去中心化预言机网络 DON 层面,所有节点运营商上传其获取的中位数数据以及节点签名并生成一个预言机报告(OCR)在链上发布,该报告涵盖了所有预言机节点上传的数据以及签名。预言机报告(OCR)每次在链上发布,都会先验证每个节点的签名,然后再对所有数据进行一次聚合(如提取中位数),并将其储存在参考合约中,数据一旦储存就难以再被篡改。

并且一个 DON 中至少需要有 2⁄3 的节点上传结果和签名,预言机报告(OCR)才会被链上接受,这类似于以太坊的权益证明共识机制:只有 2⁄3 的节点投票验证通过才可以出块。这种机制很大程度的保障了 Data Feed 最终数据的难以篡改。

因此根据个人理解,Data Feed 服务的数据经过了多重聚合处理并由更可靠的节点运营商以及共识机制进行传输,以此来保障数据的准确性和难以篡改性。

业务流程

数据源 -> 数据提供商 -> ChainLink 节点 -> ChainLink 合约 -> 用户合约

技术架构

  1. Consumer调用Proxy合约 latestRoundData()
  2. Proxy 调用Aggregator 合约 latestRoundData()
  3. Aggregator 返回Transmission 结果给Proxy
  4. Proxy返回结果给Consumer

为什么需要Proxy合约?

因为链下的预言机网络可能会升级

随机数生成器 (RNG) - 链上方案

为什么?

  • 区块链是一个确定的系统,所有结果都可以预测,没有随机性
  • 对于任一笔交易,每个节点都需达成同样结果(共识算法)
  • 恶意矿工选择性打包交易
  • 合约中的随机数生成被所操作区块影响
  1. 智能合约的随机数生成器
  2. 可验证公平的随机数
  3. 由链上数据和链下计算共同生成

使用步骤

  1. 用户合约 构造请求 RequestRandomwords, 给预言机合约 VRFCoordinator 发送随机数请求RequestRandomWords, 并生成随机数及proof,
  2. 预言机会监听EventLog, 预言机获取种子,生成随机数以及相关的proof 写回链上
  3. VRF 合约验证随机数是否由预言机按照约定算法生成,coordinate合约, 会根据注册的consume合约, 及request id, 调用用户合约
  4. 用户合约里override的 FullfillRandomWords 接受已验证的随机数

VRF 三个函数组成:

  1. 密钥生成函数(Key Gen)
    𝐺(𝑟)=>(𝑃𝐾,𝑆𝐾)G(r)=>(PK,SK),
    • PK: public key, 公钥
    • SK: secrete key 密钥
  2. 随机数生成函数 (Evaluate)
    𝐸(𝑆𝐾,𝑆𝑒𝑒𝑑)=>(𝑅𝑎𝑛𝑑𝑜𝑚𝑛𝑒𝑠𝑠,𝑃𝑟𝑜𝑜𝑓)E(SK,Seed)=>(Randomness,Proof)
    • seed: RNG的种子
    • Randomness: 随机数
    • Proof: 证明
  3. 验证函数 (Verfify)
    𝑉(𝑃𝐾,𝑠𝑒𝑒𝑑,𝑅𝑎𝑛𝑑𝑜𝑚𝑛𝑒𝑠𝑠,𝑃𝑟𝑜𝑜𝑓)V(PK,seed,Randomness,Proof) => true or false

技术架构

  1. 调用Consumer合约的函数请求随机数
  2. 用户合约调用Coordinator合约的函数请求随机数
  3. 将Preseed写入EventLogz
  4. 预言机读取EventLog中的 PreSeed 和 blockhash
  5. 预言机通过VRF生成随机数和Proof
  6. 预言机将 rc 和 proof 写入 Coordinator
  7. Coordinator进行验证, 并将随机数写入Consumer合约
  • 给智能合约提供价格数据
  • 从外部数据源获取
  • 外部数据源覆盖DEX和CEX市场
  • 和交易量相关的计算方法
  • 在多个区块链网络中都可以使用

使用场景

NFT创建和分发

  • 通过VRF给要生成的NFT分配随机属性
  • 给NFT collection 参与者随机分配稀有NFT

公平抽奖

  • 给孵化项目的参与者发放白名单
  • 合约不能自我执行
  • 智能合约是一种被动代理
  • 智能合约不能够独自开始处理一个流程,也不能基于预设条件来调用它们自己的函数
  • 智能合约中的状态变化只在另一个账户发送交易的时候才会发生。另一账户包括: 用户,预言机节点或其它智能合约

合约自动化执行旧有的两种方式:

  • 手动DevOps & 中心化服务器

开发人员通过一个中心化服务器去执行Solidity 的 Cron Job, 监控合约状态, 并且发送交易给链上合约

缺点

  • 单点失败风险

  • 占用团队资源和时间

  • Bounty 模式 (ETH Alarm Clock)

给交易触发的个人账户提供资金,交易执行成功即可获得经济奖励

缺点

  • 没有direct commitment
  • winner-take-all rewards
  • 增加链的拥挤程度

基于去中心化的预言机网络(DON), DON中的节点会主动调用注册的Job, 这些Job被称为UpKeep

它们可以被用来验证条件是否满足,并且基于预设逻辑给智能合约发送交易。可以被看作时一个主动的代理用户和合约交互。

如果链下模拟checkUpKeep返回的是true, DON就会给区块链发送交易,执行performUpKeep

使用步骤:

  1. 用户部署合约(一般叫upKeep),写好需要判断的预设条件
  2. 将合约地址注册到Chainlink的注册合约中
  3. Chainlink的链下预言机网络在每一个区块对用户合约进行检查
  1. Oracle检查CheckUpKeep,判断预设条件是否满足条件
  2. 如果CheckUpKeep条件不满足,则下个区块继续检查
  3. 如果CheckUpKeep条件满足,oracle则调用Automation Register 合约
  4. Automation Registry 合约调用 consumer 合约的函数 performUpKeep
  1. 在Registry中注册用户合约
  2. DON通过event log检测请求
  3. DON调用checkUpKeep()查看是否需要perform
  4. Registry检查用户合约
  5. DON通过Registry调用 performUpKeep
  6. Registry 在 consumer 调用 performUpKeep

链上合约

AutomationCompatible.sol 用户主要维护这个合约,继承AutomationCompatibleInterface 合约, 然后实现这两个函数

checkUpKeep(bytes calldata) external view override 
	returns (bool upKeepNeed, bytes memory)
{
	Check the predefined condition
	// 可以把计算量大的逻辑放这里,不更改合约状态的,可以得到其返回值
}

PerformUpKeep(calldata) override {
	Logics to perform
}

AutomationRegister.sol

register(config data) {
	PendingRequest
}

approve(config data) {
	Write upkeep into registry
}

AutomationRegistry

registerUpKeep (config data) {
	upKeeps[]
}

checkUpKeep (upkeep id) {
	Simulate checkUpkeep
}

performUpKeep(upkeep id, calldata) {
	call consumer contract
}

应用场景

  • 自动复利 & yield (Yield harvesting and Compounding)
    存入资产,进行流动性挖矿,复利的方式收益会更高,复利需要周期性地把收益取出来,然后再stake进资产里。并进行周期性claim 或 stake. (synthetics跟alchemist)
  • Dex 限价单(DEX limit orders)
    Dex中没有order book, 资产价格通过两个token的比例进行计算。DEX就没有中心化的交易撮合引擎,来完成固定价格的订单。利用keepers的 checkUpkeep, 将成交逻辑写入到 performUpKeep中, 如果链下预言机判断成功了,就会触发这个performUpKeep, 完成订单的交易。实现DEX的挂单
  • 借贷平台清算(Liquidation)
    借贷平台需要周期性检查用户质押资产的价格,如果出现质押资产的价格低于用户所贷出的金额,或者低于某个警戒线,就需要将资产卖出,防止价格下跌导致清算不掉,出现更大的损失 (aave使用了keepers)
  • 动态NFT (Dynamic NFT)
    让NFT根据某些指标进行更新(Punks艺术品, 门票, 房地产, 消息, 证书, 质押证明)
    • 更新元数据

通过 Chainlink Function,开发者可以在去中心化预言机网络中自定义运算逻辑,从而在不将计算负担放在区块链网络上的情况下,满足复杂的智能合约需求。我们来看看 Chainlink Function 是如何运作的:

  1. 最终消费者(End Consumer)启动嵌入在 DApp 中的 Chainlink Function
  2. 该 DApp 向 Chainlink Function 智能合约发出请求。此请求包括 APl 端点、数据的任何转换以及加密凭据(如果有)
  3. 去中心化预言机网络 DON 不断监听 Chainlink Functions 智能合约。当它接收到请求时,每个节点都会独立地触发其运行环境以获取外部数据,在其上执行任何计算,井返回结果
  4. 节点使用 OCR2.0 就最终结果达成共识,然后选择一个节点将结果传回链上。如果该节点未能发布数据,则选择另一个节点将其传输到链上。最终的结果是高可靠性和信任最小化安全性的

简而言之,当 Chainlink Function 收到请求后,Chainlink Function 就会在去中心化预言机网络(DON)内通过每个节点独立执行开发者自定义的运算逻辑来获取和处理外部数据,然后利用 OCR 2.0 协议(Chainlink Data Feeds 底层共识协议)对最终数据达成共识并上传到链上。但需要注意的是,尽管 Chainlink Function 确保了数据传输的可靠性、安全性和难以篡改性,但它不能保证数据源的数据真实性,参与方需要对数据来源进行审慎评估。

使用流程

  1. 用户构建请求
  2. Functions Router合约保存记录并触发event
  3. Router合约锁定用户所订阅的余额
  4. DON节点完成请求
  5. Router合约将结果写回给用户的Consumer
  6. 扣除用户此次请求的费用

使用场景

  • 开发者可以使用 Chainlink Functions 连接到任何公共或私人数据 API,如获取最近的游戏或体育结果,或从 Token Terminal 提取 Web3 协议上的指标数据(如协议收入、用户费用、活跃用户、TVL)

  • 开发者可以通过让 Chainlink Function 获取数据并在智能合约中引用数据之前对其执行高级计算,如从社交媒体 API 检索数据,计算情绪,并在链上报告转换结果以触发操作(如用户收到限量版 NFT)

  • 开发者可以使用 Chainlink Functions 连接到受密码保护的物联网设备数据或企业系统,将 Web3 协议与现有技术和网络集成。例如,开发者可以从智能手表或智能污染传感器获取数据,或将智能合约连接到企业 ERP 系统(如 SAP)以构建供应链应用程序,或连接到 Stripe API 以检查用户的账户余额

  • 开发者可以与 Chainlink Functions 集成,将他们的智能合约连接到外部去中心化数据库,如 IPFS 和 Filecoin。这将允许开发者利用 DON 作为计算层和 IPFS 用于低成本去中心化存储的链下计算 dApp。例如,开发者可以通过使用 Chainlink 函数获取链下投票并将投票结果转发到链上以触发基于智能合约的操作,从而为 DAO 构建去中心化的链下投票系统

  • 开发人员可以连接到 Web2 应用程序并构建复杂运算逻辑的混合智能合约

  • 开发人员可以几乎从任何 Web2 系统(如 AWS S3、Firebase 或 Google Cloud Storage 甚至是特斯拉汽车)获取数据

  • 开发人员可以接入 Al 进行回应(例如,在 OpenAl 的 ChatGPT APl 或为 DeFi 交易生成建议的云提供商)

anyapi 应用页面

查看不同的数据提供商所提供的关于股票的数据,事件型数据,宏观经济数据,身份数据等等

自定义数据源: 市场,事件,金融以及支付数据的API => chainlink 节点 Aapp请求

AnyAPI 技术架构

  1. Consumer合约通过 Client合约的 rawRequest 构建请求
  2. Client合约进行序列化
  3. 再通过Link合约(ERC-677) 的 TransferAndCall 发送给 Operator合约
  4. Operator合约将请求写入 EventLog
  5. 预言机节点获得请求并且将数据写回到Consumer

TransferAndCall 接受一个请求,并也需要接收相应地佣金。再去监视被请求的一方,有没有执行这个请求方所发送的请求,如果被请求方完成了这个请求,才会将佣金打给被请求方

使用场景

  • 天气及航班延误数据: 为保险应用提供必要的数据参数
  • 温室气体排放数据: 提供碳市场所需的数据
  • 选举及体育比赛数据: 预测市场及动态 NFT
  • 资产及宏观经济数据: 为资产提供更多流动性

主要组件:

  • 源链(source chain)
    • sender合约: 发送跨链消息
    • Router合约(由chainlink部署) 负责发起跨链交互, router合约将指令路由到特定地址的OnRamp
    • OnRamp合约(由chainlink部署)
    • TokenPool合约(由chainlink部署)
  • 目标链(Destination Chain)
    • Receiver合约:接收跨链消息
    • Router合约(由chainlink部署)
    • OffRamp合约(由chainlink部署)
    • TokenPool合约(由chainlink部署)
    • Commit Store(由chainlink部署)
    • RMN 合约(由chainlink部署)
  • 链下部分(offchain)
    • commiting DON: 用于把信息从源链移到目标链
    • RMN: 监控committing DON转移的消息是否正确, 交易是否发生
    • Executing DON: 把转移到目标链的消息进行执行
      server 和 receiver由用户自己开发
  • sender: EOA或智能合约
  • receiver: EOA 或智能合约,合约需要实现ccipReceive功能
    其它组件由chainlinki开发且部署

跨链消息:

  • 转移token
  • 发送任何消息
  • 转移token并且发送数据

步骤:

最终性(Finality)

  • 链上发生的交易非常难或者说不可能被回退
  • 交易在完成后不能够被改变,回退或取消
  • 不变性
  • 在不同的链上不变性的定义也不同,会影响跨链的速度,并不是由跨链协议去决定的,而是由链本身的特性决定

Bless: RMN(风险管理网络)

  • 祝福(Blessing):每个风险管理节点会监控每个目标链上提交的消息的所有 Merkle 根。Committing DON 提交这些 Merkle 根。

    风险管理节点通过获取源链上的所有消息独立重建 Merkle 树。然后,它检查 Committing DON 提交的 Merkle 根与重建的 Merkle 树的根之间是否匹配。如果两个 Merkle 根匹配,则风险管理节点就会在目标链上祝福该根到风险管理合约。风险管理合约跟踪选票。当达到规定人数时,风险管理合约就会认可受祝福的Merkle 根。

  • 诅咒(Cursing):如果风险管理节点检测到异常,风险管理节点就会诅咒 CCIP 系统。达到指定投票数后,风险管理合约就会将 CCIP 认定为受诅咒的。 CCIP 将自动在该链上暂停并等待合约所有者在可能解除诅咒之前评估状况。风险管理节点暂停 CCIP 的情况有两种:

    • 最终性破缺: CCIP 链上发生了违反风险管理配置所设置的安全参数的深度重组。
    • **违反执行安全:**消息在目标链上发起执行,但源链上却没有任何与之匹配的交易。双重执行就属于这一类,因为 executing DON 只能执行一条消息一次。

Excute: Execute DON

CCIP 工作流

ccip 用例

跨链质押: 将资金质押在一条链,然后在另一条链基于第一条链的质押获取部分流动性

跨链治理: 将治理的提案和投票自动化分配至不容的链

跨链交易: 在多条链上交易资产

新的Dapp类型: 在安全性高的主网重写主要逻辑并质押资产,而通过效率比较高的L2进行计算和存储

企业端应用:验证去中心化,降低在跨链过程中,多方信息传递的风险

CCIP 架构: Token处理机制

  • Lock & Mint(Burn & Block)
    原生token转移到一个没有被发行过的区块链网络上,在另一个网络中,他们不是原生的token, 而是synthetic或者wrapped版本
  • Burn & Mint
    token在多条链上发行过(比如说稳定币, 合成资产,记息token, wrapped token等), 有一个可变的总量

lane

Chainlink CCIP通道是源和目标之间的一条独特通道blockchain 。通道是单向的。例如,以太坊主网 => Polygon主网 和Polygon主网 => 以太坊主网是两个不同的通道。

  • chainlink ccip 通过lane来连接不同的区块链网络
  • 每一个lane都有一个源链,和目的链
  • 从区块链链A到C的lane,和反方向从c到A的lane是不同的

去中心化预言机网络(DON)

Chainlink 去中心化的 预言机 网络(或称DON )运行 Chainlink OCR2。该协议分轮运行,在此期间可能会就观察到的数据值达成一致。该过程的输出结果是一份由法定参与者证明的报告。然后该报告由参与者之一在链上传输。没有任何一个参与者负责每一轮的传输,并且所有参与者都将尝试以循环方式进行传输,直到传输完成。在 CCIP 的背景下,一条 通道包含两个 OCR DON 委员会,用于监控源和目的地之间的交易blockchain :提交 DON执行 DON

chain selector

  • ccip 所支持的每一个区块链都有一个chain selector 和router
  • 用于发起跨链交互交易的合约
  • router合约:
    • 源链的router: 在转移token的时候,调用者必须允许router合约有权限使用调用者的token
    • 目标链的router: 当一个信息在目标链上被接收到,router合约将会将token或信息交付给receiver智能合约

fee

  • fees = gas cost + premium
  • 费用可以通过link 或者原生token支付,或者原生token的wrapped版本
  • smart execution
    • 在源链收取费用,费用包括在目的链上处理交易的gas消耗
      • 即使在目标链出现了gas spike, ccip也会调高gas price的方式保证跨链交易的执行

![[Pasted image 20241013150407.png]]

  1. 源头blockchain :
    1. 当发送方发起 USDC 转账时,USDC代币 池与 CCTP 交互合约 销毁 USDC代币 并指定 USDC代币 目的地的池地址, blockchain 作为铸造它们的授权调用者。
    2. CCTP 销毁指定 USDC代币 并发出关联的 CCTP 事件。
  2. 链下:
    1. Circle 证明服务监听源上的 CCTP 事件blockchain 。
    2. CCIP
      执行DON监听源上的相关CCTP事件blockchain 。当它捕获此类事件时,它会调用 Circle Attestation 服务 API 来请求证明。证明是在目的地铸造指定数量 USDC 的签名授权blockchain 。
  3. 在目标区块链上:
    1. executing don 提供
      OffRamp合约的证明。
    2. OffRamp合约 调用 USDC代币 包含要铸造的 USDC 数量、接收者地址和 Circle 证明。
    3. USDC代币 池调用 CCTP合约。 CCTP合约 在将指定的 USDC 金额mint到接收方之前验证证明签名。
    4. 如果 CCIP 消息中有数据并且接收方不是 EOA,则 OffRamp合约 通过router传输 CCIP 消息合约 至接收者。

sender合约

sender合约 负责启动 USDC 的转移代币 和数据。其工作原理如下:

  1. 初始化合约:
    • 在部署合约,配置router地址, LINK 合约 地址和 USDC合约 地址。
    • 这些地址对于与 CCIP 路由器交互和处理至关重要代币转移。
  2. sendMessagePayLINK函数:
    • 这函数 发送 USDC代币,编码的函数 stake函数以及向接收方提供的参数(受益人地址和金额)合约 在目标链上。
    • 使用EVM2AnyMessage结构构造 CCIP 消息。
    • 使用router的getFee计算必要的费用函数。
    • 确保合约 有足够的LINK 支付费用并批准router转移LINK 。
    • 通过执行 router 合约的ccipSend将 CCIP 消息发送到目标链函数。
    • 发出MessageSent事件。

staker合约

staker合约 管理 USDC 的质押和赎回代币. 工作原理如下:

  1. 初始化合约:
    • 部署合约,定义 USDC代币 地址。
  2. stake函数:
    • 允许质押 USDC代币 代表受益人。
    • 将 USDC 从调用者( msg.sender )转移到合约,然后铸造等量的质押代币给受益人。
  3. redeem函数:
    • 允许受益人赎回其质押的代币 对于 USDC。
    • burn代币 并将等值的USDC转移给受益人。

Receiver 合约

处理传入的跨链消息,对其进行处理,并与 Staker 进行交互合约 代表受益人质押 USDC。操作方式如下:

  1. 初始化合约:

    • 部署合约,配置 router合约地址,USDC代币地址和 staker 合约地址。
  2. ccipReceive函数:

    • CCIP 路由器将消息传递到合约。
    • 验证发件人并处理消息,确保其来自在源链上正确的sender合约 。
  3. 处理消息:

    • 调用processMessage函数,这是Solidity的try/catch错误处理机制。
    • processMessage内部,它调用_ccipReceive函数 以进行进一步的消息处理。
  4. _ccipReceive函数:

    • 检查是否收到代币 是 USDC。如果不是,则revert。
    • stake合约的stake函数进行low-level调用, 使用编码函数的签名和来自所接收数据
    • 处理成功后发出MessageReceived事件。
  5. 错误处理:

    • 如果在处理过程中发生错误,则会执行 ccipReceive 内的 catch 块。
    • 失败消息的messageId将添加到s_failedMessages中,消息内容存储在s_messageContents中。
    • 发出MessageFailed事件,允许稍后识别和重新处理失败的消息。
  6. retryFailedMessage函数:

    • 允许合约 所有者重试失败的消息并恢复相关代币。
    • 将消息的错误代码更新为RESOLVED以防止多次重试。
    • 转移锁定的代币 与失败消息相关联,并发送给指定受益人作为逃生舱。
  7. getFailedMessages函数:

    • 检索要检查的失败消息的分页列表。
    solidity 复制代码
    	/// @notice Sends data and transfer tokens to receiver on the destination chain.
        /// @notice Pay for fees in LINK.
        /// @dev Assumes your contract has sufficient LINK to pay for CCIP fees.
        /// @param _destinationChainSelector The identifier (aka selector) for the destination blockchain.
        /// @param _beneficiary The address of the beneficiary of the staked tokens on the destination blockchain.
        /// @param _amount token amount.
        /// @return messageId The ID of the CCIP message that was sent.
        function sendMessagePayLINK(
            uint64 _destinationChainSelector,
            address _beneficiary,
            uint256 _amount
        )
            external
            onlyOwner
            validateDestinationChain(_destinationChainSelector)
            returns (bytes32 messageId)
        {
            address receiver = s_receivers[_destinationChainSelector];
            if (receiver == address(0))
                revert NoReceiverOnDestinationChain(_destinationChainSelector);
            if (_amount == 0) revert AmountIsZero();
            uint256 gasLimit = s_gasLimits[_destinationChainSelector];
            if (gasLimit == 0)
                revert NoGasLimitOnDestinationChain(_destinationChainSelector);
            // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
            // address(linkToken) means fees are paid in LINK
            Client.EVMTokenAmount[]
                memory tokenAmounts = new Client.EVMTokenAmount[](1);
            tokenAmounts[0] = Client.EVMTokenAmount({
                token: address(i_usdcToken),
                amount: _amount
            });
            // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
            Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
                receiver: abi.encode(receiver), // ABI-encoded receiver address
                data: abi.encodeWithSelector(
                    IStaker.stake.selector,
                    _beneficiary,
                    _amount
                ), // Encode the function selector and the arguments of the stake function
                tokenAmounts: tokenAmounts, // The amount and type of token being transferred
                extraArgs: Client._argsToBytes(
                    // Additional arguments, setting gas limit
                    Client.EVMExtraArgsV2({
                        gasLimit: gasLimit, // Gas limit for the callback on the destination chain
                        allowOutOfOrderExecution: true // Allows the message to be executed out of order relative to other messages from the same sender
                    })
                ),
                // Set the feeToken to a feeTokenAddress, indicating specific asset will be used for fees
                feeToken: address(i_linkToken)
            });

// Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message

搭建节点

Chainlink节点通过与以太坊客户端节点连接,通过连接的以太坊客户端来获取链上的用户预言机请求,通过提交交易将获取到的数据写入到链上。所以Chainlink节点必须要依靠一个以太坊客户端节点才能发挥作用。您可以自己搭建以太坊客户端节点,也可以选择以太坊RPC服务提供方的服务,只需要一个WebSocket链接提供给Chainlink节点即可。

创建oracle合约

Oracle合约是Chainlink节点在区块链上的,它与用户合约直接进行沟通,接收用户的预言机请求,将请求结果通过用户注册的回调函数,写入到用户的合约中。

Oracle合约无需自己编写,我们直接部署Chainlink开源的Oracle合约代码即可。如果用remix部署,只需新建一个包含下面两行代码的文件即可:

pragma solidity 0.6.6; import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.6/Oracle.sol";

需要注意的是,oracle合约会暂时接受用户发起Chainlink请求是所支付的LINK费用,所以请务必妥善保存合约owner的私钥。

部署好Oracle合约之后,需要将开放权限给节点的地址,允许节点可以提交事务。

创建Jobs

按照 文档中的例子,在节点中添加Job(作业)。

打开节点管理界面的Jobs标签页,点击New Job添加新的Job,将每个类型的Job的JSON拷贝到文本框中,用刚刚部署好的Oracle合约地址,替换JSON中的YOUR_ORACLE_CONTRACT_ADDRESS字段。每创建成功一个Job,都会生成一个Job ID,在Jobs标签页可以找到所有的Jobs。

测试节点可用性

接下来我们测试一下我们部署的Chainlink服务的可用性。

pragma solidity ^0.6.0;

import "https://raw.githubusercontent.com/smartcontractkit/chainlink/develop/evm-contracts/src/v0.6/ChainlinkClient.sol";

contract APIConsumer is ChainlinkClient {
  
    uint256 public ethereumPrice;
    
    address private oracle;
    bytes32 private jobId;
    uint256 private fee;
    
    constructor() public {
        setPublicChainlinkToken();
	// 填入我们刚刚部署好的oracle地址
        oracle = 0x5F66a231a29CE1513dc6c16407fDCe9D0aEE2cB0;
	// 填入我们刚刚创建的地址
        jobId = "4b9b6e7d89154a8d855affed3985aafd";
	// 支付给Oracle的最小费用,可用在Configuration页面MINIMUM_CONTRACT_PAYMENT字段查看到
        fee = 1 * 10 ** 18; // 1 LINK
    }
    
    function requestEthereumPrice() public returns (bytes32 requestId) 
    {
        Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
        
        // 设置所要请求的API地址
        request.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD");
        
        // API返回结果的解析路径
        request.add("path", "USD");
        
        // 为去除小数点将结果乘以的倍数
        request.addInt("times", 100);
        
        // 发送请求
        return sendChainlinkRequestTo(oracle, request, fee);
    }
    
    function fulfill(bytes32 _requestId, uint256 _price) public recordChainlinkFulfillment(_requestId)
    {
        ethereumPrice = _price;
    }
}

部署好用户合约之后,向用户合约转入LINK,调用requestEthereumPrice方法,就可以测试我们的Chainlink预言机节点是否可以正常工作,https://t.me/gtokentool

相关推荐
Wx120不知道取啥名20 分钟前
C语言之长整型有符号数与短整型有符号数转换
c语言·开发语言·单片机·mcu·算法·1024程序员节
Python私教1 小时前
Flutter颜色和主题
开发语言·javascript·flutter
代码吐槽菌1 小时前
基于SSM的汽车客运站管理系统【附源码】
java·开发语言·数据库·spring boot·后端·汽车
Ws_1 小时前
蓝桥杯 python day01 第一题
开发语言·python·蓝桥杯
zdkdchao1 小时前
jdk,openjdk,oraclejdk
java·开发语言
神雕大侠mu2 小时前
函数式接口与回调函数实践
开发语言·python
Y.O.U..2 小时前
STL学习-容器适配器
开发语言·c++·学习·stl·1024程序员节
小魏冬琅2 小时前
探索面向对象的高级特性与设计模式(2/5)
java·开发语言
lihao lihao2 小时前
C++stack和queue的模拟实现
开发语言·c++
TT哇3 小时前
【Java】数组的定义与使用
java·开发语言·笔记