Solidity中的Oracle客户端接口

下面是一个Solidity示例,演示如何使用API从Oraclize连续轮询ETH/USD价格并以可用的方式存储结果。:

  1. /*
  2. ETH/USD price ticker leveraging CryptoCompare API
  3. This contract keeps in storage an updated ETH/USD price,
  4. which is updated every 10 minutes.
  5. */
  6. pragma solidity ^0.4.1;
  7. import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
  8. /*
  9. "oraclize_" prepended methods indicate inheritance from "usingOraclize"
  10. */
  11. contract EthUsdPriceTicker is usingOraclize {
  12. uint public ethUsd;
  13. event newOraclizeQuery(string description);
  14. event newCallbackResult(string result);
  15. function EthUsdPriceTicker() payable {
  16. // signals TLSN proof generation and storage on IPFS
  17. oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
  18. // requests query
  19. queryTicker();
  20. }
  21. function __callback(bytes32 _queryId, string _result, bytes _proof) public {
  22. if (msg.sender != oraclize_cbAddress()) throw;
  23. newCallbackResult(_result);
  24. /*
  25. * parse the result string into an unsigned integer for on-chain use
  26. * uses inherited "parseInt" helper from "usingOraclize", allowing for
  27. * a string result such as "123.45" to be converted to uint 12345
  28. */
  29. ethUsd = parseInt(_result, 2);
  30. // called from callback since we're polling the price
  31. queryTicker();
  32. }
  33. function queryTicker() public payable {
  34. if (oraclize_getPrice("URL") > this.balance) {
  35. newOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
  36. } else {
  37. newOraclizeQuery("Oraclize query was sent, standing by for the answer..");
  38. // query params are (delay in seconds, datasource type, datasource argument)
  39. // specifies JSONPath, to fetch specific portion of JSON API result
  40. oraclize_query(60 * 10, "URL", "json(https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,GBP).USD");
  41. }
  42. }
  43. }

要与Oraclize集成,合约EthUsdPriceTicker必须是usingOraclize的子项;usingOraclize合约在oraclizeAPI文件中定义。数据请求是使用oraclize_query()函数生成的,该函数继承自usingOraclize合约。这是一个重载函数,至少需要两个参数:

  • 支持的数据源,例如URL,WolframAlpha,IPFS或计算

  • 给定数据源的参数,可能包括使用JSON或XML解析助手

价格查询在queryTicke()函数中执行。为了执行查询,Oraclize要求在以太网中支付少量费用,包括将结果传输和处理到callback()函数的gas成本以及随附的服务附加费。此数量取决于数据源,如果指定,则取决于所需的真实性证明类型。一旦检索到数据,callback()函数由Oraclize控制的帐户调用,该帐户被允许进行回调; 它传递响应值和唯一的queryId参数,作为示例,它可用于处理和跟踪来自Oraclize的多个挂起的回调。

金融数据提供商Thomson Reuters还为以太坊提供了一项名为BlockOne IQ的oracle服务,允许在私有或许可网络上运行的智能合约请求市场和参考数据[13]。下面是oracle的接口,以及将发出请求的客户端合约:

  1. pragma solidity ^0.4.11;
  2. contract Oracle {
  3. uint256 public divisor;
  4. function initRequest(uint256 queryType, function(uint256) external onSuccess, function(uint256) external onFailure) public returns (uint256 id);
  5. function addArgumentToRequestUint(uint256 id, bytes32 name, uint256 arg) public;
  6. function addArgumentToRequestString(uint256 id, bytes32 name, bytes32 arg) public;
  7. function executeRequest(uint256 id) public;
  8. function getResponseUint(uint256 id, bytes32 name) public constant returns(uint256);
  9. function getResponseString(uint256 id, bytes32 name) public constant returns(bytes32);
  10. function getResponseError(uint256 id) public constant returns(bytes32);
  11. function deleteResponse(uint256 id) public constant;
  12. }
  13. contract OracleB1IQClient {
  14. Oracle private oracle;
  15. event LogError(bytes32 description);
  16. function OracleB1IQClient(address addr) public payable {
  17. oracle = Oracle(addr);
  18. getIntraday("IBM", now);
  19. }
  20. function getIntraday(bytes32 ric, uint256 timestamp) public {
  21. uint256 id = oracle.initRequest(0, this.handleSuccess, this.handleFailure);
  22. oracle.addArgumentToRequestString(id, "symbol", ric);
  23. oracle.addArgumentToRequestUint(id, "timestamp", timestamp);
  24. oracle.executeRequest(id);
  25. }
  26. function handleSuccess(uint256 id) public {
  27. assert(msg.sender == address(oracle));
  28. bytes32 ric = oracle.getResponseString(id, "symbol");
  29. uint256 open = oracle.getResponseUint(id, "open");
  30. uint256 high = oracle.getResponseUint(id, "high");
  31. uint256 low = oracle.getResponseUint(id, "low");
  32. uint256 close = oracle.getResponseUint(id, "close");
  33. uint256 bid = oracle.getResponseUint(id, "bid");
  34. uint256 ask = oracle.getResponseUint(id, "ask");
  35. uint256 timestamp = oracle.getResponseUint(id, "timestamp");
  36. oracle.deleteResponse(id);
  37. // Do something with the price data..
  38. }
  39. function handleFailure(uint256 id) public {
  40. assert(msg.sender == address(oracle));
  41. bytes32 error = oracle.getResponseError(id);
  42. oracle.deleteResponse(id);
  43. emit LogError(error);
  44. }
  45. }

使用initRequest()函数启动数据请求,该函数除了两个回调函数之外,还允许指定查询类型(在此示例中,是对日内价格的请求)。 这将返回一个uint256标识符,然后可以使用该标识符提供其他参数。addArgumentToRequestString()函数用于指定RIC(Reuters Instrument Code),此处用于IBM股票,addArgumentToRequestUint()允许指定时间戳。现在,传入block.timestamp的别名将检索IBM的当前价格。然后由executeRequest()函数执行该请求。处理完请求后,oracle合约将使用查询标识符调用onSuccess回调函数,允许检索结果数据,否则在检索失败时使用错误代码进行onFailure回调。成功检索的可用字段包括开盘价,最高价,最低价,收盘价(OHLC)和买/卖价。

Reality Keys [14]允许使用POST请求对事实进行离线请求。响应以加密方式签名,允许在链上进行验证。在这里,请求使用blockr.io API在特定时间检查比特币区块链上的账户余额:

  1. wget -qO- https://www.realitykeys.com/api/v1/blockchain/new --post-data="chain=XBT&address=1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX&which_total=total_received&comparison=ge&value=1000&settlement_date=2015-09-23&objection_period_secs=604800&accept_terms_of_service=current&use_existing=1"

对于此示例,参数允许指定区块链,要查询的金额(总收到金额或最终余额)以及要与提供的值进行比较的结果,从而允许真或假的响应。除了“signature_v2”字段之外,生成的JSON对象还包括返回值,该字段允许使用ecrecover()函数在智能合约中验证结果:

  1. "machine_resolution_value" : "29665.80352",
  2. "signature_v2" : {
  3. "fact_hash" : "aadb3fa8e896e56bb13958947280047c0b4c3aa4ab8c07d41a744a79abf2926b",
  4. "ethereum_address" : "6fde387af081c37d9ffa762b49d340e6ae213395",
  5. "base_unit" : 1,
  6. "signed_value" : "0000000000000000000000000000000000000000000000000000000000000001",
  7. "sig_r" : "a2cd9dc040e393299b86b1c21cbb55141ef5ee868072427fc12e7cfaf8fd02d1",
  8. "sig_s" : "8f3199b9c5696df34c5193afd0d690241291d251a5d7b5c660fa8fb310e76f80",
  9. "sig_v" : 27
  10. }

为了验证签名,ecrecover()可以确定数据确实由ethereum_address签名,如下所示。fact_hash和signed_value经过哈希处理,并将三个签名参数传递给ecrecover():

  1. bytes32 result_hash = sha3(fact_hash, signed_value);
  2. address signer_address = ecrecover(result_hash, sig_v, sig_r, sig_s);
  3. assert(signer_address == ethereum_address);
  4. uint256 result = uint256(signed_value) / base_unit;
  5. // Do something with the result..