前言 
在上一篇博客的基础上,本文将详细介绍Chainlink获取链下数据的方法。
本文需要在Remix上部署三个智能合约,分别是LinkToken.sol、Operator.sol和ATestnetConsumer.sol。而LinkToken合约的部署流程已在上文详细给出,这里我写一下跳转链接:【Chainlink】基于私链运行一个Chainlink预言机节点 | Keike1 (keikei99.github.io) 。
区块链上的用户作为数据获取方,需要从链下的数据提供方获取数据,而区块链本身不具备获取现实世界数据的能力,因此需要使用Chainlink预言机作为连接链上和链下之间的桥梁。下面我们将模拟用户获取数据的这个过程。
部署Operator.sol合约 
Operator.sol 
1 2 3 pragma solidity ^0.7 .6 ; import  "@chainlink/contracts/src/v0.7/Operator.sol" ;
部署完要记得把合约地址记录下来
1 Operator: 0xD82f08722b8e24DA3ab608AB11E230dB0E0a5829 
随后调用setAuthorizedSenders方法,填自己的账户地址,注意要以数组形式传递。如
1 ["0x0688AbDa371A869DcBb56d2E946A6898660ECDef"] 
可以用isAuthorizedSender来判断是否设置成功
在Chainlink Operator中创建新的Job 
写入以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 name  = "test" schemaVersion  = 1 type  = "directrequest" contractAddress  = "YOUR_OPERATOR_CONTRACT_ADDRESS" maxTaskDuration  = "0s" minIncomingConfirmations  = 0 observationSource  = """     decode_log   [type="ethabidecodelog"                   abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"                   data="$(jobRun.logData)"                   topics="$(jobRun.logTopics)"]     decode_cbor  [type="cborparse" data="$(decode_log.data)"]     fetch        [type="http" method=GET url="$(decode_cbor.get)" allowUnrestrictedNetworkAccess="true"]     parse        [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"]     multiply     [type="multiply" input="$(parse)" times="$(decode_cbor.times)"]     encode_data  [type="ethabiencode" abi="(bytes32 requestId, uint256 value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(multiply) }"]     encode_tx    [type="ethabiencode"                   abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)"                   data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\":   $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}"                   ]     submit_tx    [type="ethtx" to="YOUR_OPERATOR_CONTRACT_ADDRESS" data="$(encode_tx)"]     decode_log -> decode_cbor -> fetch -> parse -> multiply -> encode_data -> encode_tx -> submit_tx """ 
注意将以上的两个 YOUR_OPERATOR_CONTRACT_ADDRESS改为你的Operator合约地址。
随后复制系统给出的externalJobId备用。
1 JobId: e22cd821-ecb1-405f-9525-d273acc8d66e 
部署ATestnetConsumer合约 
ATestnetConsumer.sol 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 pragma solidity ^0.8 .7 ; import  "@chainlink/contracts/src/v0.8/ChainlinkClient.sol" ;import  "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol" ;contract ATestnetConsumer  is ChainlinkClient , ConfirmedOwner  {     using Chainlink  for  Chainlink .Request ;     uint256 private constant ORACLE_PAYMENT  = (1  * LINK_DIVISIBILITY ) / 10 ;      uint256 public currentPrice;     event RequestEthereumPriceFulfilled (         bytes32 indexed requestId,         uint256 indexed price     );          constructor (ConfirmedOwner (msg.sender ) {         setChainlinkToken (0x779877A7B0D9E8603169DdbD7836e478b4624789 );     }     function  requestEthereumPrice (         address _oracle,         string memory _jobId      ) public onlyOwner {        Chainlink .Request  memory req = buildChainlinkRequest (             stringToBytes32 (_jobId),             address (this ),             this .fulfillEthereumPrice .selector          );         req.add (             "get" ,             "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD"          );         req.add ("path" , "USD" );         req.addInt ("times" , 100 );         sendChainlinkRequestTo (_oracle, req, ORACLE_PAYMENT );     }     function  fulfillEthereumPrice (         bytes32 _requestId,         uint256 _price      ) public recordChainlinkFulfillment (_requestId ) {        emit RequestEthereumPriceFulfilled (_requestId, _price);         currentPrice = _price;     }     function  getChainlinkToken (         return  chainlinkTokenAddress ();     }     function  withdrawLink (         LinkTokenInterface  link = LinkTokenInterface (chainlinkTokenAddress ());         require (             link.transfer (msg.sender , link.balanceOf (address (this ))),             "Unable to transfer"          );     }     function  cancelRequest (         bytes32 _requestId,         uint256 _payment,         bytes4 _callbackFunctionId,         uint256 _expiration      ) public onlyOwner {        cancelChainlinkRequest (             _requestId,             _payment,             _callbackFunctionId,             _expiration         );     }     function  stringToBytes32 (         string memory source      ) private pure returns (bytes32 result) {        bytes memory tempEmptyStringTest = bytes (source);         if  (tempEmptyStringTest.length  == 0 ) {             return  0x0 ;         }         assembly {                          result := mload (add (source, 32 ))         }     } } 
这里注意将第29行的setChainlinkToken的参数改为之前部署的LinkToken合约地址 
私有链中可以有多种代币,这里我们使用Metamask去管理我们的账户余额以方便我们在Remix部署合约。
打开Metamask,点击Assets,点击Import tokens
然后填入你的LinkToken合约地址和它的代币符号LINK就行。
利用LinkToken合约向ATestnetConsumer合约转账 
这里我转入了1个LINK,根据实际需要转就可以了。转账以后,去原来部署的ATestnetConsumer合约中调用requestEthereumPrice方法。
_oracle填入Operator合约地址,_jobId填入刚刚在控制台得到的JobId。
JobId要去掉中间的横线!!! 
JobId要去掉中间的横线!!! 
JobId要去掉中间的横线!!! 
最后点击currentPrice,ETH/USD的汇率就出现了。
至此,我们实现了在区块链中获取链下的价格数据。在实际的开发中,可以将这种单一的数据拓展为特定的业务数据,以实现区块链与预言机在特定领域的应用。
基本请求模型–单预言机工作模式 - 知乎 (zhihu.com)