背景以太坊中的智能合約可以為廣泛的應用提供動力,但由于區塊鏈的性質,智能合約缺乏必要的功能:互聯網連接。以太坊被設計為完全確定性,
背景
以太坊中的智能合約可以為廣泛的應用提供動力,但由于區塊鏈的性質,智能合約缺乏必要的功能:互聯網連接。
以太坊被設計為完全確定性,這意味著如果有人下載整個網絡歷史并重放它們,它們應該總是以相同的狀態結束。確定性是必要的,這樣節點就可以達成一致。
但是,互聯網具有不是確定性,在某個時間點查詢API的智能合約,不能保證以后查詢相同的API會得到相同的結果。 Web上的API和數據發生了變化。因此,智能合約本質上缺乏連通性。
oracle這個名字來源于這樣一個事實:從歷史上講,oracles是事實的來源。這就是我們所需要的事實。
對于智能合約來說,預言機就是每個智能合約的輸入參數。所有智能合約都繞不開預言機的輸入數據,輸入數據決定了智能合約的運行結果。通過向區塊鏈中添加具有所需信息的交易,智能合約可以運行并始終獲取相同的信息,因為數據都是從區塊中進行檢索。
解決方案
我們將創建一個oracle服務,該服務可以查詢JSON API并從API響應中檢索單個值。 oracle將保存所有請求和答案,并將擁有一組預定義的利益相關者。
利益相關者是運行node.js服務的帳戶,該服務查詢API并返回對oracle的響應。 oracle還具有必須接收的最小數量的相等響應,以確認所提供的答案是有效的。
這樣競爭方依賴于oracle來支持他們的合約,但是如果其中一方(節點)試圖去操縱結果,那就無法實現了。因為他們同意預定義了法定人數的等量答案結果。
oracle包含兩個組件。on-chain oracle(智能合約)和off-chain oracle服務(node.js服務器)。
on-chain oracle是一個智能合約,它有一個公共函數createRequest,接收URL,查詢和要檢索的屬性。然后啟動一個事件來提醒新鏈接oracle的新請求。
off-chain oracle由不同方部署的幾個node.js服務組成,這些服務將查詢API并將響應返回給合約。
on-chain Oracle會驗證是否已達到最小數量的相等響應,如果已達到,則會發出一個事件,表明其已就價值達成共識,以便查詢Oracle的客戶機智能合約知道其已收到響應。
On-chain Oracle實施
我們用約定的條款定義Oracle合同:最低法定人數和Oracle總數。對于這個例子,有三個利益相關者,為了達成共識,3個中的2個必須提供相同的答案。
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,它將保存請求:
// 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)
}
現在我們可以創建公共函數createRequest,客戶端智能合約(任何想要使用oracle服務的合同)都會調用:
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++;
}
該功能包含利益相關者之間協議的重要部分。 受信任參與最終解決方案的帳戶的地址。并且發出被off-chain oracle監聽的事件NewRequest。
//event that triggers oracle outside of the blockchain
event NewRequest (
uint id,
string urlToQuery,
string attributeToFetch
);
在監聽此事件后,off-chain Oracle將調用公共函數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
);
}
}
}
}
}
此函數將首先檢查調用者是否是預定義地址之一。 然后它會檢查oracle沒有投票,如果是,它將保存oracle答案。 然后它將檢查該答案是否至少由所需的最低法定人數提供。 如果是這樣,那么我們就結果達成一致,并將發出一個事件,即UpdatedRequest,以警告客戶合同結果。
//triggered when there's a consensus on the final result
event UpdatedRequest (
uint id,
string urlToQuery,
string attributeToFetch,
string agreedValue
);
Off-chain Oracle實施
這是更簡單的部分,它是可以監聽發出的區塊鏈事件和查詢API的任何服務。
off-chain Oracle使用web3監聽鏈上Oracle發出的事件,并查詢請求的api,解析檢索到的json以獲取請求的密鑰,并調用公共函數updateRequest。
您可以在github上找到off-chain oracle所有利益相關方應該部署的此服務的代碼。https://github.com/pedroduartecosta/blockchain-oracle/tree/master/off-chain-oracle
這種實現允許不依賴于一方作為唯一一個查詢API的真實來源,而是讓多方就一個結果達成一致。它也是一個非常靈活的服務,因為它可以查詢任何公共JSONAPI,允許在大量的用例中使用。(鏈三豐)