我们以Chainlink获取的TestnetConsumer合约中的一个requestEthereumPrice 方法为例来非常简单谈一下催促号召的流程。这个函数定义如下:function requestEthereumPrice(address _oracle, string _jobId)publiconlyOwner {Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(_jobId), this, this.fulfillEthereumPrice.selector);req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETHtsyms=USD");req.add("path", "USD");req.addInt("times", 100);sendChainlinkRequestTo(_oracle, req, ORACLE_PAYMENT); }它所构建的功能就就是指登录的API(cryptocompare)提供ETH/USD的交易价格。函数起源于的参数是登录的oracle地址和jobId。将一些佩的催促参数组好后,调用sendChainlinkRequestTo 方法将催促收到。
sendChainlinkRequestTo是定义在Chainlink获取的库中的一个模块方法,定义如下:/** * @notice 向登录的oracle地址创立一个催促 * @dev 创立并存储一个催促ID, 减少本地的nonce值, 并用于`transferAndCall` 方法发送到LINK, * 创立到目标oracle合约地址的催促 * 收到 ChainlinkRequested 事件. * @param _oracle 发送到催促至的oracle地址 * @param _req 已完成初始化的Chainlink催促 * @param _payment 催促发送到的LINK数量 * @return 催促 ID */ function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment)internalreturns (bytes32 requestId) {requestId = keccak256(abi.encodePacked(this, requests));_req.nonce = requests;pendingRequests[requestId] = _oracle;emit ChainlinkRequested(requestId);require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle");requests += 1; return requestId; }其中link.transferAndCall方法即是ERC677定义的token账户方法,与ERC20的transfer方法比起,它多了一个data字段,可以在账户的同时装载数据。这里就将之前包好的催促数据放到了data字段,追随账户一起发送到了oracle合约。transferAndCall 方法定义如下:/** * @dev 将token和额外数据一起移往给一个合约地址 * @param _to 移往到的目的地址 * @param _value 移往数量 * @param _data 传送给接管合约的额外数据 */ function transferAndCall(address _to, uint _value, bytes _data)publicreturns (bool success) {super.transfer(_to, _value);Transfer(msg.sender, _to, _value, _data);if (isContract(_to)) { contractFallback(_to, _value, _data);}return true; }其中的Transfer(msg.sender, _to, _value, _data);是收到一个事件日志:event Transfer(address indexed from, address indexed to, uint value, bytes data);将这次账户的详细信息(发送到方、接管方、金额、数据)记录到日志中。
Oracle合约在接到账户之后,网卓新闻网,不会启动时onTokenTransfer方法,该方法不会检查账户的有效性,并通过收到OracleRequest事件记录更加详尽的数据信息:event OracleRequest(bytes32 indexed specId,address requester,bytes32 requestId,uint256 payment,address callbackAddr,bytes4 callbackFunctionId,uint256 cancelExpiration,uint256 dataVersion,bytes data );这个日志不会在oracle合约的日志中寻找,如图中下方右图。链下的节点不会订阅者该主题的日志,在提供到记录的日志信息之后,节点不会解析出有催促的明确信息,通过网络的API调用,提供到催促的结果。之后通过递交事务的方式,调用Oracle合约中的fulfillOracleRequest方法,将数据递交到链上。fulfillOracleRequest定义如下:/** * @notice 由Chainlink节点调用来已完成催促 * @dev 递交的参数必需是`oracleRequest`方法所记录的哈希参数 * 将不会调用消息传递地址的消息传递函数,`require`检查时会报错,以便节点可以取得报酬 * @param _requestId 催促ID必需与请求者所给定 * @param _payment 为Oracle派发缴付金额 (以wei为单位) * @param _callbackAddress 已完成方法的消息传递地址 * @param _callbackFunctionId 已完成方法的消息传递函数 * @param _expiration 请求者可以中止之前节点不应号召的届满时间 * @param _data 回到给消费者合约的数据 * @return 外部调用顺利的状态值 */ function fulfillOracleRequest(bytes32 _requestId,uint256 _payment,address _callbackAddress,bytes4 _callbackFunctionId,uint256 _expiration,bytes32 _data )externalonlyAuthorizedNodeisValidRequest(_requestId)returns (bool) {bytes32 paramsHash = keccak256( abi.encodePacked(_payment,_callbackAddress,_callbackFunctionId,_expiration ));require(commitments[_requestId] == paramsHash, "Params do not match request ID");withdrawableTokens = withdrawableTokens.add(_payment);delete commitments[_requestId];require(gasleft() = MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solhint-disable-line avoid-low-level-calls }这个方法不会在展开一系列的检验之后,不会将结果通过之前记录的消息传递地址与消息传递函数,回到给消费者合约:_callbackAddress.call(_callbackFunctionId, _requestId, _data);这样一次催促就全部已完成了。
总结本文从应验机的概念开始,通过一个非常简单的提供ETH价格的例子,介绍了催促/号召模式的Chainlink应验机的基本过程,期望对你解读应验机与Chainlink的运营原理有所协助。
本文来源:OD体育-www.bjsanda.net