1. 程式人生 > >區塊鏈Oracle原理及實現

區塊鏈Oracle原理及實現

區塊鏈本身是封閉的。區塊鏈的確定性模型基於這樣一個事實:在交易執行時區塊鏈不能執行任何來自外部的邏輯,所有的外部資料只能通過交易進入到系統中。預言機/Oracle就是通過交易為智慧合約提供可信資料的服務。Oracle雖然聽起來神祕,但實現並不複雜,在這篇文章裡,我們將介紹預言機的作用以及運作原理,並通過天氣資料預言機WeatherOracle的完整實現過程,來幫助 你快速掌握區塊鏈預言機/Oracle的精髓。

要快速掌握區塊鏈開發,推薦匯智網的區塊鏈開發線上互動課程

1、為什麼智慧合約需要預言機/Oracle?

在智慧合約中執行的邏輯不可以執行區塊鏈之外的任何操作,例如它不可以訪問網際網路上的web服務。外部資料進入智慧合約的唯一方法是將其置入一個交易中,通過向系統傳送一個新的交易來觸發區塊鏈狀態的更新。

試著考慮一下,如果智慧合約在執行時可以訪問外部的一個API來獲取資料,會出現什麼情況?

如果今天部署這個合約,那麼API可能會返回如下的資料:

{ "foo": "bar" }

但是明天再部署時,API可能就會返回新的資料,例如:

{ "foo": "baz" }

那麼可以想像,一個月以後如果有人進行以太坊區塊鏈的同步,這個智慧合約就會被執行,但是API的響應資料是和一個月之前不同的,這就會導致新同步的區塊鏈狀態不同於之前已經存在的節點狀態。

這就不再是完全自確定的區塊鏈了。經歷相同的同步過程,我的區塊鏈和你的區塊鏈卻不一樣!

讓我們再換個說法:給定一組區塊,一個節點必須能夠從零開始重現區塊鏈的最終狀態,而無需網際網路連線。

那麼這一點對於智慧合約的開發者意味著什麼?Oralce(預言機),開發者必須構造一個預言機來和實現智慧合約與外部世界的互動。

2、如何實現一個簡單的預言機/Oracle?

現在讓我們建立一個簡單的預言機/Oracle,來將外部的天氣資料傳入智慧合約:

oracle arch

在最底層的區塊鏈平臺,我們需要部署一個智慧合約,這個合約有一個方法updateWeather()用來更新天氣狀態,只有在合約白名單裡的地址才可以呼叫這個方法。updateWeather方法接受天氣資料作為引數,同時觸發一個以太坊合約事件並將天氣資料作為事件的引數,這樣JavaScript應用就可以訂閱這個事件並獲得非同步通知了。

同時我們將建立兩個nodejs程序,其中之一就是預言機/Oracle,它的實現邏輯就是週期性地輪詢第三方天氣API來獲取天氣資料,然後將天氣資料提交給智慧合約以便進行歷史審計。

另一個nodejs程序則負責訂閱智慧合約的天氣事件,然後在控制檯輸出事件引數。正如之前所述,每當預言機/Oracle呼叫合約的updateWeather()方法時,都會觸發天氣事件。

需要指出的是,為了便於理解預言機的核心實現思路,下面的程式碼進行了簡化,剔除了必要的錯誤處理,因此並不適用於生產環境。

原始碼在這裡:

接下來我們詳細講解這個簡單的預言機的實現。

3、預言機智慧合約實現

智慧合約有一個公開的oracleAddress狀態變數,用來表示允許呼叫智慧合約的updateWeather 方法的賬戶地址,我們在建構函式中對其進行賦值:

contract WeatherOracle {  
  address public oracleAddress;
  
  constructor (address _oracleAddress) public {
    oracleAddress = _oracleAddress;
  }
  
  // ...
}

接下來我們要定義天氣事件,這個事件將在weatherUpdate()呼叫成功時觸發。同樣為了簡化,我們讓這個事件簡單的附帶一個表示溫度的字串引數。

event WeatherUpdate (string temperature);

最後我們要實現updateWeather()方法。它的可見性為public,意思是可以從外部呼叫這個方法:

function updateWeather (string temperature) public {
    require(msg.sender == oracleAddress);
    emit WeatherUpdate (temperature);
  }

請注意require語句。只有當呼叫地址(msg.sender)和白名單地址(oracleAddress)一致時才允許繼續執行該方法,否則將回滾交易。

好了,就這麼簡單。

4、預言機服務

我們的預言機就是一個簡單的nodejs服務。它使用request庫來呼叫外部天氣API,解析API的響應,然後構造並提交交易給智慧合約,然後等一會兒,重複上面的工作,如此周而復始。

讓我們從訪問API開始,我們將API的地址放在一個環境變數裡,以便在開發/生產環境切換時避免修改原始碼:

const options = { uri: process.env.WEATHER_URL, json: true };
const start = () => {
  request(options)
  .then(parseData)
  .then(updateWeather)
  .then(restart)
  .catch(error);
};

下面的程式碼用來解析API的響應結果:

const parseData = (body) => {
  return new Promise((resolve, reject) => {
    const temperature = body.main.temp.toString();
    resolve({ temperature });
  });
};

現在要做的就是構造一個呼叫智慧合約的updateWeather()方法的以太坊交易。注意account()是一個非同步方法,它的作用是載入一個以太坊賬戶,contract是一個js物件,它包含了之前部署的WeatherOracle智慧合約的部署地址和ABI介面資料。這些與智慧合約相關的函式都來自於著名的web3開發包:)

const updateWeather = ({ temperature }) => {
  return new Promise((resolve, reject) => {
    account().then(account => {
      contract.updateWeather(temperature, { from: account }, (err, res) => {
        resolve(res);
      });
    });
  });
};

最後,我們只需要在指定超時後重新啟動這個過程即可。 wait()函式將在指定的超時時間之後解析。

const restart = () => {
  wait(process.env.TIMEOUT).then(start);
};

搞定了!上面的程式碼實現了一個簡單服務,它可以從API獲取資料,然後再輸入智慧合約。

注意:

  • 當我們構造以太坊交易時,我們使用{from:account}來指定呼叫賬戶,account所指向的這個賬戶需要有一些以太幣來支付交易的手續費。
  • 我們使用環境變數來配置一個私鑰,用來例項化account物件。這個私鑰必須是用來部署 WeatherOracle智慧合約時傳入的那個白名單地址所對應的私鑰。

5、天氣事件的利用服務

這是另一個簡單的nodejs服務。同樣,contract是一個包含了合約的部署地址和ABI資訊的js物件,呼叫WeatherUpdate並傳入一個回撥就是我們訂閱天氣事件的所有程式碼:

const consume = () => {
  contract.WeatherUpdate((error, result) => {
    console.log("NEW WEATHER DATA EVENT ON SMART CONTRACT");
    console.log("BLOCK NUMBER: ");
    console.log("  " + result.blockNumber)
    console.log("WEATHER DATA: ");
    console.log(result.args);
    console.log("\n");
  });
}

當這個服務執行時,隨著交易成功入塊上鍊,它將會週期性地向控制檯輸出資料:

NEW WEATHER DATA EVENT ON SMART CONTRACT
BLOCK NUMBER:
  3424586
WEATHER DATA:
{ temperature: '74.75' }

原文:區塊鏈Oracle預言機實現