LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 行情分析 > 技术实践 Chainlink 预言机网络搭建与数据交互

技术实践 Chainlink 预言机网络搭建与数据交互

2020-02-07 链闻 来源:火星财经

通过代码实践chainlink预言机网络的搭建,并使用预置或自定义的适配器实现智能合约与外部世界的数据交互。

原文标题:《Chainlink去中心化预言机桥接区块链与现实世界》
撰文:影无双

Chainlink是一个去中心化的预言机网络,它可以让区块链中的智能合约安全地访问外部世界的数据。在这个文章中,我们将探索chainlink预言机网络的搭建,并学习如何使用预置或自定义的适配器实现智能合约与外部世界数据的桥接。

智能合约被锁定在区块链里,与外部世界隔离开来。然而在许多应用中,智能合约的运行需要依赖于外部真实世界的信息。

以Ocean协议为例:只有当提供的数据被证明是可以使用时,数据提供商才可以得到代币奖励。因此一个可以桥接区块链和现实世界的预言机(Oracle)网络就非常必要了。

Chainlink是一个去中心化的Oracle网络,它可以让区块链中的智能合约安全地访问外部世界的数据:

在这个教程中,我们将探索chainlink网络的搭建以及其适配器的使用方法,我们在Kovan测试链搭建了一个用于概念验证的演示环境,所有的代码可以从这里[1]下载。我们使用trufflev5.0.3和Node.jsv8.11.1。

1、Chainlink架构概述

Chainlink网络的主要组成部分如下:

Chainlink预言机合约:预言机智能合约部署在区块链网络上,它接收来自合约的Link代币支付并向Chainlink节点分发事件 Chainlink节点:Chainlink节点是运行在区块链和外部数据源之间的链下中间件,它提供真实世界的数据,执行来自请求器合约的请求 Chainlink适配器:适配器是应用相关的软件,它负责与数据源交互并执行特定的任务。chainlink适配器可以部署在serverless平台,例如amazonlambda或Googlecloudfunctions

值得指出的是,每个来自请求器合约的请求都必须包含一个任务ID,用来唯一的标识一个特定的工作流水线。Chainlink节点依赖于任务ID来识别与数据源交互所需的适配器以及处理数据所需的工作流。

2、使用Chainlink内置的适配器

在这一部分,我们使用Chainlinkg预置的适配器来展示如何集成Chainlink并向其提交请求。

2.1安装Chainlink包

在项目根目录,执行如下命令安装chainlink包:

$npminstallgithub:smartcontractkit/chainlink--save

另外,Chainlink官方最近增加了一个新的NPM包用于Chainlink合约,可以如下命令安装:

$npminstallchainlink.js—save

2.2在Kovan测试链部署请求器合约

要访问Chainlink的预言机合约,需要构造一个用于发送Link代币并提交请求的请求器合约。

我们创建了一个请求器合约示例,可以在这里下载。

constructor()public{//SettheaddressfortheLINKtokeninKovannetwork.setLinkToken(0xa36085F69e2889c224210F603D836748e7dC0088);//SettheaddressoftheOraclecontractinKovannetwork.setOracle(0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e);}.../**CreatearequestandsendittodefaultOraclecontract*/functioncreateRequest(bytes32_jobId,string_url,string_path,int256_times)publiconlyOwnerreturns(bytes32requestId){//createrequestinstanceChainlink.Requestmemoryreq=newRequest(_jobId,this,this.fulfill.selector);//fillinthepass-inparametersreq.add("url",_url);req.add("path",_path);req.addInt("times",_times);//sendrequest&paymenttoChainlinkoraclerequestId=chainlinkRequestTo(getOracle(),req,ORACLE_PAYMENT);//emiteventmessageemitrequestCreated(msg.sender,_jobId,requestId);}

请求器合约中的关键函数是createRequest函数,它创建请求并设置必要的参数:

JobId:特定作业流水线的唯一标识符。可以在这里查看内置适配器的完整清单:https://docs.chain.link/docs/addresses-and-job-specs URL:可以返回JSON数据的WebAPI的访问端结点 path:JSON数据字段选择路径,用来声明使用数据中的哪一部分 times:数据倍乘系数。该操作将浮点数转换为整数,因为solidity智能合约仅接受整数

2.3在Kovan测试链部署请求器合约

执行如下命令在以太坊Kovan测试链部署请求器合约:

$trufflemigrate--networkkovan...Deploying'OceanRequester'-------------------------->transactionhash:0x6e228163e73828c58c8287fec72c551289516a1d8e9300aab5dcc99d848f6146>Blocks:0Seconds:16>contractaddress:0x04E4b02EA2662F5BF0189912e6092d317d6388F3>account:0x0E364EB0Ad6EB5a4fC30FC3D2C2aE8EBe75F245c>balance:2.703082875853937168>gasused:1439461>gasprice:10gwei>valuesent:0ETH>totalcost:0.01439461ETH>Savingartifacts------------------------------------->Totalcost:0.01439461ETH

2.4向请求器合约存入LINK代币

Chainlink官方提供了一些代币faucet。在Kovan测试链上可以访问https://kovan.chain.link/获取一些测试用的LINK代币。

只需要输入合约地址或钱包地址,Chainlink的faucet就会转100个LINK代币进去:

2.5从合约请求数据

我们创建了一个JavaScript脚本来与请求器合约交互,以便创建并提交请求给Chainlink网络。可以在这里下载JavaScript脚本。

contract("OceanRequester",(accounts)=>{constLinkToken=artifacts.require("LinkToken.sol");constOceanRequester=artifacts.require("OceanRequester.sol");constjobId=web3.utils.toHex("2c6578f488c843588954be403aba2deb");consturl="https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms;=USD,EUR,JPY";constpath="USD";consttimes=100;letlink,ocean;beforeEach(async()=>{link=awaitLinkToken.at("0xa36085F69e2889c224210F603D836748e7dC0088");ocean=awaitOceanRequester.at("0x04E4b02EA2662F5BF0189912e6092d317d6388F3");});describe("querytheinitialtokenbalance",()=>{it("createarequestandsendtoChainlink",async()=>{lettx=awaitocean.createRequest(jobId,url,path,times);request=h.decodeRunRequest(tx.receipt.rawLogs[3]);...data=awaitocean.getRequestResult(request.id)console.log("Requestisfulfilled.data:="+data)...});});});

上面的代码中,关键参数已经加粗显式。任务ID「2c6578f488c843588954be403aba2deb」标识了用于从URL提取JSON数据、拷贝指定字段值并转换化为SOlidity支持的uint256类型的Chainlink适配器。

例如,返回的JSON格式数据看起来像这样:

{USD":142.33,"EUR":126.69,"JPY":15765.39}

path参数设置为USD表示该字段的值需要提供给请求器合约。

我们可以运行该脚本像Chainlinkg网络提交请求并从指定的URL提取数据。Chainlinkg节点大概需要2秒钟来执行该请求,其中包含区块确认的时间。

3、使用自定义的Chainlink适配器

前面的部分看起来干净简洁。但是,Chainlink内置的适配器很有限,不能满足各种区块链应用的要求。因此,需要为不同的应用场景创建定制的适配器。

在这一部分,我们学习如何开发自己的适配器,并学习如何将其嵌入Chainlink体系中。可以在这里[2]找到一些外部适配器的参考实现,或者查看这里的指南[3]。

下图展示了包含外部适配器的Chainlink网络架构:

区块链开发者需要完成以下工作:

将预言机合约部署到区块链网络 开发定制适配器并部署到AWSlambda或GCPfunctions,提供用于交互的URL端结点 运行一个新的CHainlink节点并在该节点的配置中注册定制的适配器的URL端结点 在Chainlink节点中为该任务创建一个任务描述,以便其监听预言机合约并触发正确的工作流水线 在链上预言机合约中注册新的CHainlink节点 创建一个新的请求器合约来提交请求

下面我们逐步来实现。

3.1在Kovan测试链部署预言机合约

在我们的概念验证系统中,需要一个预言机合约与Chainlinkg节点交互。为此,我们在Kovan测试链上部署这个合约:https://github.com/oceanprotocol/Nautilus/blob/master/4-chainlink。

3_oracle_migration.js=====================Deploying'Oracle'------------------>transactionhash:0xd281b18c4be0be9b2bdbfed4bae090aab5c86027564f048785b1f971cf0b6f2c>Blocks:0Seconds:8>contractaddress:0x698EFB00F79E858724633e297d5188705512e506>account:0x0E364EB0Ad6EB5a4fC30FC3D2C2aE8EBe75F245c>balance:2.262907885853937168>gasused:1311430>gasprice:10gwei>valuesent:0ETH>totalcost:0.0131143ETH>Savingartifacts------------------------------------->Totalcost:0.0131143ETH

3.2创建一个新的外部适配器

在这个概念验证系统中,我们使用一个由OracleFinder开发的外部适配器CryptoCompareExternalAdapter。对应更一般性的应用,ThomasHodges创建了一个用NodeJS开发的外部适配器模板:https://github.com/thodges-gh/CL-EA-NodeJS-Template

$gitclonehttps://github.com/OracleFinder/CryptoCompareExternalAdapter.git$cdCryptoCompareExternalAdapter/$npminstall$zip-rchainlink-cloud-adapter.zip.

压缩文件chainlink-cloud-adapter.zip创建后就可以部署了。作为示例,我们将这个外部适配器部署到GoogleCloudFunctions。在登录之后,参考下图创建一个新的函数并上传chainlink-cloud-adapter.zip:

为这个外部适配器生成的URL访问端结点需要提供给chainlink节点:

https://us-central1-macro-mercury-234919.cloudfunctions.net/coinmarketcap-adapter

现在使用GoogleCloudFunctions的控制台,我们可以测试适配器以确保它可以正常运行:

现在,外部适配器已经在GoogleCloud平台运行起来,它等待执行来自Chainlink节点的请求。

3.3在新的chainlink节点中注册适配器url

我们需要运行一个新的chainlink节点,以便可以访问外部适配器,步骤如下:

安装Parity并接入Kovan网络:

$dockerpullparity/parity:stable$mkdir~/.parity-kovan$dockerrun-heth--nameeth-p8546:8546\-v~/.parity-kovan:/home/parity/.local/share/io.parity.ethereum/\-itparity/parity:stable--chain=kovan\--ws-interface=all--ws-origins="all"--light\--base-path/home/parity/.local/share/io.parity.ethereum/

创建Chainlink节点的管理账号

$dockerpullsmartcontract/chainlink:latest$mkdir-p~/.chainlink-kovan/tls$opensslreq-x509-out~/.chainlink-kovan/tls/server.crt-keyout~/.chainlink-kovan/tls/server.key\-newkeyrsa:2048-nodes-sha256\-subj'/CN=localhost'-extensionsEXT-config<(\printf"[dn]\nCN=localhost\n[req]\ndistinguished_name=dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

创建Chainlink节点的配置信息

echo"ROOT=/chainlinkLOG_LEVEL=debugETH_URL=ws://eth:8546ETH_CHAIN_ID=42MIN_OUTGOING_CONFIRMATIONS=2MIN_INCOMING_CONFIRMATIONS=0LINK_CONTRACT_ADDRESS=0xa36085F69e2889c224210F603D836748e7dC0088TLS_CERT_PATH=/chainlink/tls/server.crtTLS_KEY_PATH=/chainlink/tls/server.keyALLOW_ORIGINS=*">.env

运行chainlink节点

$dockerrun--linketh-p6689:6689-v~/.chainlink-kovan:/chainlink-it--env-file=.envsmartcontract/chainlinkn

访问https://localhost:6689打开Chainlink节点的GUI配置界面,使用前面创建的管理账号登入,仪表盘看起来像这样:

在chainlink节点中注册外部适配器

在Bridges选项卡,我们需要创建一个新的桥接器并填写桥接url:

结果看起来是这样:

3.4为外部适配器创建任务描述

在chainlink节点上,很重要的一个步骤是创建一个新的任务描述,参考:https://docs.chain.link/docs/job-specifications

有了任务描述,Chainlink节点可以监听来自预言机合约的事件消息并触发在任务描述中定义的流水线。我们的任务描述:

{"initiators":[{"type":"RunLog","params":{"address":"0x698efb00f79e858724633e297d5188705512e506"}}],"tasks":[{"type":"coinmarketcap","confirmations":0,"params":{}},{"type":"Copy","params":{}},{"type":"Multiply","params":{"times":100}},{"type":"EthUint256"},{"type":"EthTx"}]}

initiators用来设置触发chainlink节点的合约地址,tasks定义了该任务的作业流水线。

3.5在预言机合约中注册Chainlink节点

新的Chainlink节点必须要在之前部署的预言机合约中注册,这样它才能接受请求并执行任务。

可以在Chainlink节点的配置页面找到新的chainlink节点的账户地址:

我们使用一个JavaScript文件来注册该Chainlink节点:

constWeb3=require('web3')constweb3=newWeb3(newWeb3.providers.HttpProvider('https://kovan.infura.io/'))consth=require("chainlink-test-helpers");constscale=1e18;contract("Oracle",(accounts)=>{constOracle=artifacts.require("Oracle.sol");constchainlinkNode='0x79B80f3b6B06FD5516146af22E10df26dfDc5455';letoracle;beforeEach(async()=>{oracle=awaitOracle.at("0x698EFB00F79E858724633e297d5188705512e506");});describe("shouldregisterchainlinknode",()=>{it("registerchainlinknode",async()=>{awaitoracle.setFulfillmentPermission(chainlinkNode,true)letstatus=awaitoracle.getAuthorizationStatus(chainlinkNode)console.log("Chainlinknode'sstatusis:="+status)});});});

使用truffle运行上述脚本:

$truffletesttest/Oracle.Test.js--networkkovanUsingnetwork'kovan'Contract:OracleshouldregisterchainlinknodeChainlinknode'sstatusis:=true

状态为true表示chainlink节点已经注册成功,并被授予执行任务的权限。

3.6创建请求器合约以提交请求

为了向外部适配器提交请求,我们创建一个请求器合约contracts/requestGCP.sol来测试整个工作流。

functioncreateRequest(bytes32_jobId,string_coin,string_market)publiconlyOwnerreturns(bytes32requestId){//createrequestinstanceChainlink.Requestmemoryreq=newRequest(_jobId,this,this.fulfill.selector);//fillinthepass-inparametersreq.add("endpoint","price");req.add("fsym",_coin);req.add("tsyms",_market);req.add("copyPath",_market);//sendrequest&paymenttoChainlinkoracle(RequesterContractsendsthepayment)requestId=chainlinkRequestTo(getOracle(),req,ORACLE_PAYMENT);//emiteventmessageemitrequestCreated(msg.sender,_jobId,requestId);}

同样部署到Kovan测试链:

4_requestGCP_migration.js=========================Replacing'requestGCP'---------------------->transactionhash:0x978974b43d843606c42ce15c87fcc560a5c625497bf074f5ec0f337347438fdf>Blocks:0Seconds:16>contractaddress:0x6f73E784253aD72F0BA4164101860992dFC17Fe1>account:0x0E364EB0Ad6EB5a4fC30FC3D2C2aE8EBe75F245c>balance:2.248942845853937168>gasused:1396504>gasprice:10gwei>valuesent:0ETH>totalcost:0.01396504ETH>Savingartifacts------------------------------------>Totalcost:0.01396504ETH

利用faucet充值一些link代币:https://kovan.chain.link/

现在,我们可以请求外部适配器来访问链下数据。可以使用如下脚本:

constWeb3=require('web3')constweb3=newWeb3(newWeb3.providers.WebsocketProvider('ws://eth:8546'))consth=require("chainlink-test-helpers");constscale=1e18;contract("requestGCP",(accounts)=>{constLinkToken=artifacts.require("LinkToken.sol");constRequestGCP=artifacts.require("requestGCP.sol");constjobId=web3.utils.toHex("80c7e6908e714bf4a73170c287b9a18c");constcoin="ETH"constmarket="USD";constdefaultAccount=0x0e364eb0ad6eb5a4fc30fc3d2c2ae8ebe75f245c;letlink,ocean;beforeEach(async()=>{link=awaitLinkToken.at("0xa36085F69e2889c224210F603D836748e7dC0088");ocean=awaitRequestGCP.at("0x6f73E784253aD72F0BA4164101860992dFC17Fe1");});describe("shouldrequestdataandreceivecallback",()=>{letrequest;...it("createarequestandsendtoChainlink",async()=>{lettx=awaitocean.createRequest(jobId,coin,market);request=h.decodeRunRequest(tx.receipt.rawLogs[3]);console.log("requesthasbeensent.requestid:="+request.id)letdata=0lettimer=0while(data==0){data=awaitocean.getRequestResult(request.id)if(data!=0){console.log("Requestisfulfilled.data:="+data)}wait(1000)timer=timer+1console.log("waitingfor"+timer+"second")}});});});

用truffle运行该脚本:

truffletesttest/requestGCP.Test.js--networkkovan

运行了大约10秒钟,外部适配器完成该任务:

可以在chainlink节点的交易历史中找到该交易:

也可以在Googlecloudfunctions的仪表盘中找到该交易:

4、结语

Chainlink是一个重要的桥接区块链与现实世界的去中心化预言机网络。许多区块链应用可能都需要通过chainlink网络来访问现实世界中的数据流。

原文链接:BridgingBlockchaintotheRealWorldusingChainlink[4]

References

[1]这里:
https://github.com/oceanprotocol/Nautilus/tree/master/4-chainlink

[2]这里:
https://chainlinkadapters.com/

[3]指南:
https://chainlinkadapters.com/guides

[4]BridgingBlockchaintotheRealWorldusingChainlink:
https://blog.oceanprotocol.com/bridging-blockchain-to-the-real-world-using-chainlink-afcf19cfbb73

来源链接:mp.weixin.qq.com

—-

编译者/作者:链闻

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...