以太坊上簡單實現Oracle預言機連結

買賣虛擬貨幣
背景以太坊中的智慧合約可以為廣泛的應用提供動力,但由於區塊鏈的性質,智慧合約缺乏必要的功能:網際網路連線。以太坊被設計為完全確定性,這意味著如果有人下載整個網路歷史並重放它們,它們應該總是以相同的狀態結束。確定性是必要的,這樣節點就可以達成一致。但是,網際網路具有不是確定性,在某個時間點查詢API的智慧合約,不能保證以後查詢相同的API會得到相同的結果。 Web上的API和資料發生了變化。因此,智慧合約本質上缺乏連通性。oracle這個名字來源於這樣一個事實:從歷史上講,oracles是事實的來源。這就是我們所需要的事實。對於智慧合約來說,預言機就是每個智慧合約的輸入引數。所有智慧合約都繞不開預言機的輸入資料,輸入資料決定了智慧合約的執行結果。透過向區塊鏈中新增具有所需資訊的交易,智慧合約可以執行並始終獲取相同的資訊,因為資料都是從區塊中進行檢索。
解決方案我們將建立一個oracle服務,該服務可以查詢JSON API並從API響應中檢索單個值。 oracle將儲存所有請求和答案,並將擁有一組預定義的利益相關者。利益相關者是執行node.js服務的帳戶,該服務查詢API並返回對oracle的響應。 oracle還具有必須接收的最小數量的相等響應,以確認所提供的答案是有效的。v這樣競爭方依賴於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,允許在大量的用例中使用。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;