背景以太坊中的智能合約可以為廣泛的應(yīng)用提供動(dòng)力,但由于區(qū)塊鏈的性質(zhì),智能合約缺乏必要的功能:互聯(lián)網(wǎng)連接。以太坊被設(shè)計(jì)為完全確定性,
背景
以太坊中的智能合約可以為廣泛的應(yīng)用提供動(dòng)力,但由于區(qū)塊鏈的性質(zhì),智能合約缺乏必要的功能:互聯(lián)網(wǎng)連接。
以太坊被設(shè)計(jì)為完全確定性,這意味著如果有人下載整個(gè)網(wǎng)絡(luò)歷史并重放它們,它們應(yīng)該總是以相同的狀態(tài)結(jié)束。確定性是必要的,這樣節(jié)點(diǎn)就可以達(dá)成一致。
但是,互聯(lián)網(wǎng)具有不是確定性,在某個(gè)時(shí)間點(diǎn)查詢API的智能合約,不能保證以后查詢相同的API會(huì)得到相同的結(jié)果。 Web上的API和數(shù)據(jù)發(fā)生了變化。因此,智能合約本質(zhì)上缺乏連通性。
oracle這個(gè)名字來(lái)源于這樣一個(gè)事實(shí):從歷史上講,oracles是事實(shí)的來(lái)源。這就是我們所需要的事實(shí)。
對(duì)于智能合約來(lái)說(shuō),預(yù)言機(jī)就是每個(gè)智能合約的輸入?yún)?shù)。所有智能合約都繞不開預(yù)言機(jī)的輸入數(shù)據(jù),輸入數(shù)據(jù)決定了智能合約的運(yùn)行結(jié)果。通過向區(qū)塊鏈中添加具有所需信息的交易,智能合約可以運(yùn)行并始終獲取相同的信息,因?yàn)閿?shù)據(jù)都是從區(qū)塊中進(jìn)行檢索。
解決方案
我們將創(chuàng)建一個(gè)oracle服務(wù),該服務(wù)可以查詢JSON API并從API響應(yīng)中檢索單個(gè)值。 oracle將保存所有請(qǐng)求和答案,并將擁有一組預(yù)定義的利益相關(guān)者。
利益相關(guān)者是運(yùn)行node.js服務(wù)的帳戶,該服務(wù)查詢API并返回對(duì)oracle的響應(yīng)。 oracle還具有必須接收的最小數(shù)量的相等響應(yīng),以確認(rèn)所提供的答案是有效的。
這樣競(jìng)爭(zhēng)方依賴于oracle來(lái)支持他們的合約,但是如果其中一方(節(jié)點(diǎn))試圖去操縱結(jié)果,那就無(wú)法實(shí)現(xiàn)了。因?yàn)樗麄兺忸A(yù)定義了法定人數(shù)的等量答案結(jié)果。
oracle包含兩個(gè)組件。on-chain oracle(智能合約)和off-chain oracle服務(wù)(node.js服務(wù)器)。
on-chain oracle是一個(gè)智能合約,它有一個(gè)公共函數(shù)createRequest,接收URL,查詢和要檢索的屬性。然后啟動(dòng)一個(gè)事件來(lái)提醒新鏈接oracle的新請(qǐng)求。
off-chain oracle由不同方部署的幾個(gè)node.js服務(wù)組成,這些服務(wù)將查詢API并將響應(yīng)返回給合約。
on-chain Oracle會(huì)驗(yàn)證是否已達(dá)到最小數(shù)量的相等響應(yīng),如果已達(dá)到,則會(huì)發(fā)出一個(gè)事件,表明其已就價(jià)值達(dá)成共識(shí),以便查詢Oracle的客戶機(jī)智能合約知道其已收到響應(yīng)。
On-chain Oracle實(shí)施
我們用約定的條款定義Oracle合同:最低法定人數(shù)和Oracle總數(shù)。對(duì)于這個(gè)例子,有三個(gè)利益相關(guān)者,為了達(dá)成共識(shí),3個(gè)中的2個(gè)必須提供相同的答案。
pragma solidity >=0.4.21 <0.6.0;
contract Oracle {
Request[] requests; //list of requests made to the contract
uint currentId = 0; //increasing request id
uint minQuorum = 2; //minimum number of responses to receive before declaring final result
uint totalOracleCount = 3; // Hardcoded oracle count
}
然后我們添加Request Struct,它將保存請(qǐng)求:
// defines a general api request
struct Request {
uint id; //request id
string urlToQuery; //API url
string attributeToFetch; //json attribute (key) to retrieve in the response
string agreedValue; //value from key
mapping(uint => string) anwers; //answers provided by the oracles
mapping(address => uint) quorum; //oracles which will query the answer (1=oracle hasn't voted, 2=oracle has voted)
}
現(xiàn)在我們可以創(chuàng)建公共函數(shù)createRequest,客戶端智能合約(任何想要使用oracle服務(wù)的合同)都會(huì)調(diào)用:
function createRequest (
string memory _urlToQuery,
string memory _attributeToFetch
)
public
{
uint lenght = requests.push(Request(currentId, _urlToQuery, _attributeToFetch, ""));
Request storage r = requests[lenght-1];
// Hardcoded oracles address
r.quorum[address(0x6c2339b46F41a06f09CA0051ddAD54D1e582bA77)] = 1;
r.quorum[address(0xb5346CF224c02186606e5f89EACC21eC25398077)] = 1;
r.quorum[address(0xa2997F1CA363D11a0a35bB1Ac0Ff7849bc13e914)] = 1;
// launch an event to be detected by oracle outside of blockchain
emit NewRequest (
currentId,
_urlToQuery,
_attributeToFetch
);
// increase request id
currentId++;
}
該功能包含利益相關(guān)者之間協(xié)議的重要部分。 受信任參與最終解決方案的帳戶的地址。并且發(fā)出被off-chain oracle監(jiān)聽的事件NewRequest。
//event that triggers oracle outside of the blockchain
event NewRequest (
uint id,
string urlToQuery,
string attributeToFetch
);
在監(jiān)聽此事件后,off-chain Oracle將調(diào)用公共函數(shù)updateRequest。
//called by the oracle to record its answer
function updateRequest (
uint _id,
string memory _valueRetrieved
) public {
Request storage currRequest = requests[_id];
//check if oracle is in the list of trusted oracles
//and if the oracle hasn't voted yet
if(currRequest.quorum[address(msg.sender)] == 1){
//marking that this address has voted
currRequest.quorum[msg.sender] = 2;
//iterate through "array" of answers until a position if free and save the retrieved value
uint tmpI = 0;
bool found = false;
while(!found) {
//find first empty slot
if(bytes(currRequest.anwers[tmpI]).length == 0){
found = true;
currRequest.anwers[tmpI] = _valueRetrieved;
}
tmpI++;
}
uint currentQuorum = 0;
//iterate through oracle list and check if enough oracles(minimum quorum)
//have voted the same answer has the current one
for(uint i = 0; i < totalOracleCount; i++){
bytes memory a = bytes(currRequest.anwers[i]);
bytes memory b = bytes(_valueRetrieved);
if(keccak256(a) == keccak256(b)){
currentQuorum++;
if(currentQuorum >= minQuorum){
currRequest.agreedValue = _valueRetrieved;
emit UpdatedRequest (
currRequest.id,
currRequest.urlToQuery,
currRequest.attributeToFetch,
currRequest.agreedValue
);
}
}
}
}
}
此函數(shù)將首先檢查調(diào)用者是否是預(yù)定義地址之一。 然后它會(huì)檢查oracle沒有投票,如果是,它將保存oracle答案。 然后它將檢查該答案是否至少由所需的最低法定人數(shù)提供。 如果是這樣,那么我們就結(jié)果達(dá)成一致,并將發(fā)出一個(gè)事件,即UpdatedRequest,以警告客戶合同結(jié)果。
//triggered when there's a consensus on the final result
event UpdatedRequest (
uint id,
string urlToQuery,
string attributeToFetch,
string agreedValue
);
Off-chain Oracle實(shí)施
這是更簡(jiǎn)單的部分,它是可以監(jiān)聽發(fā)出的區(qū)塊鏈?zhǔn)录筒樵傾PI的任何服務(wù)。
off-chain Oracle使用web3監(jiān)聽鏈上Oracle發(fā)出的事件,并查詢請(qǐng)求的api,解析檢索到的json以獲取請(qǐng)求的密鑰,并調(diào)用公共函數(shù)updateRequest。
您可以在github上找到off-chain oracle所有利益相關(guān)方應(yīng)該部署的此服務(wù)的代碼。https://github.com/pedroduartecosta/blockchain-oracle/tree/master/off-chain-oracle
這種實(shí)現(xiàn)允許不依賴于一方作為唯一一個(gè)查詢API的真實(shí)來(lái)源,而是讓多方就一個(gè)結(jié)果達(dá)成一致。它也是一個(gè)非常靈活的服務(wù),因?yàn)樗梢圆樵內(nèi)魏喂睯SONAPI,允許在大量的用例中使用。(鏈三豐)