Solidity中的Oracle客户端接口
下面是一个Solidity示例,演示如何使用API从Oraclize连续轮询ETH/USD价格并以可用的方式存储结果。:
/*
ETH/USD price ticker leveraging CryptoCompare API
This contract keeps in storage an updated ETH/USD price,
which is updated every 10 minutes.
*/
pragma solidity ^0.4.1;
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
/*
"oraclize_" prepended methods indicate inheritance from "usingOraclize"
*/
contract EthUsdPriceTicker is usingOraclize {
uint public ethUsd;
event newOraclizeQuery(string description);
event newCallbackResult(string result);
function EthUsdPriceTicker() payable {
// signals TLSN proof generation and storage on IPFS
oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
// requests query
queryTicker();
}
function __callback(bytes32 _queryId, string _result, bytes _proof) public {
if (msg.sender != oraclize_cbAddress()) throw;
newCallbackResult(_result);
/*
* parse the result string into an unsigned integer for on-chain use
* uses inherited "parseInt" helper from "usingOraclize", allowing for
* a string result such as "123.45" to be converted to uint 12345
*/
ethUsd = parseInt(_result, 2);
// called from callback since we're polling the price
queryTicker();
}
function queryTicker() public payable {
if (oraclize_getPrice("URL") > this.balance) {
newOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
} else {
newOraclizeQuery("Oraclize query was sent, standing by for the answer..");
// query params are (delay in seconds, datasource type, datasource argument)
// specifies JSONPath, to fetch specific portion of JSON API result
oraclize_query(60 * 10, "URL", "json(https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,GBP).USD");
}
}
}
要与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的接口,以及将发出请求的客户端合约:
pragma solidity ^0.4.11;
contract Oracle {
uint256 public divisor;
function initRequest(uint256 queryType, function(uint256) external onSuccess, function(uint256) external onFailure) public returns (uint256 id);
function addArgumentToRequestUint(uint256 id, bytes32 name, uint256 arg) public;
function addArgumentToRequestString(uint256 id, bytes32 name, bytes32 arg) public;
function executeRequest(uint256 id) public;
function getResponseUint(uint256 id, bytes32 name) public constant returns(uint256);
function getResponseString(uint256 id, bytes32 name) public constant returns(bytes32);
function getResponseError(uint256 id) public constant returns(bytes32);
function deleteResponse(uint256 id) public constant;
}
contract OracleB1IQClient {
Oracle private oracle;
event LogError(bytes32 description);
function OracleB1IQClient(address addr) public payable {
oracle = Oracle(addr);
getIntraday("IBM", now);
}
function getIntraday(bytes32 ric, uint256 timestamp) public {
uint256 id = oracle.initRequest(0, this.handleSuccess, this.handleFailure);
oracle.addArgumentToRequestString(id, "symbol", ric);
oracle.addArgumentToRequestUint(id, "timestamp", timestamp);
oracle.executeRequest(id);
}
function handleSuccess(uint256 id) public {
assert(msg.sender == address(oracle));
bytes32 ric = oracle.getResponseString(id, "symbol");
uint256 open = oracle.getResponseUint(id, "open");
uint256 high = oracle.getResponseUint(id, "high");
uint256 low = oracle.getResponseUint(id, "low");
uint256 close = oracle.getResponseUint(id, "close");
uint256 bid = oracle.getResponseUint(id, "bid");
uint256 ask = oracle.getResponseUint(id, "ask");
uint256 timestamp = oracle.getResponseUint(id, "timestamp");
oracle.deleteResponse(id);
// Do something with the price data..
}
function handleFailure(uint256 id) public {
assert(msg.sender == address(oracle));
bytes32 error = oracle.getResponseError(id);
oracle.deleteResponse(id);
emit LogError(error);
}
}
使用initRequest()函数启动数据请求,该函数除了两个回调函数之外,还允许指定查询类型(在此示例中,是对日内价格的请求)。 这将返回一个uint256标识符,然后可以使用该标识符提供其他参数。addArgumentToRequestString()函数用于指定RIC(Reuters Instrument Code),此处用于IBM股票,addArgumentToRequestUint()允许指定时间戳。现在,传入block.timestamp的别名将检索IBM的当前价格。然后由executeRequest()函数执行该请求。处理完请求后,oracle合约将使用查询标识符调用onSuccess回调函数,允许检索结果数据,否则在检索失败时使用错误代码进行onFailure回调。成功检索的可用字段包括开盘价,最高价,最低价,收盘价(OHLC)和买/卖价。
Reality Keys [14]允许使用POST请求对事实进行离线请求。响应以加密方式签名,允许在链上进行验证。在这里,请求使用blockr.io API在特定时间检查比特币区块链上的账户余额:
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()函数在智能合约中验证结果:
"machine_resolution_value" : "29665.80352",
"signature_v2" : {
"fact_hash" : "aadb3fa8e896e56bb13958947280047c0b4c3aa4ab8c07d41a744a79abf2926b",
"ethereum_address" : "6fde387af081c37d9ffa762b49d340e6ae213395",
"base_unit" : 1,
"signed_value" : "0000000000000000000000000000000000000000000000000000000000000001",
"sig_r" : "a2cd9dc040e393299b86b1c21cbb55141ef5ee868072427fc12e7cfaf8fd02d1",
"sig_s" : "8f3199b9c5696df34c5193afd0d690241291d251a5d7b5c660fa8fb310e76f80",
"sig_v" : 27
}
为了验证签名,ecrecover()可以确定数据确实由ethereum_address签名,如下所示。fact_hash和signed_value经过哈希处理,并将三个签名参数传递给ecrecover():
bytes32 result_hash = sha3(fact_hash, signed_value);
address signer_address = ecrecover(result_hash, sig_v, sig_r, sig_s);
assert(signer_address == ethereum_address);
uint256 result = uint256(signed_value) / base_unit;
// Do something with the result..