用IPFS和以太坊儲存資料

tualala發表於2018-11-23

如何將JSON檔案儲存在IPFS上,並使用Oraclize訪問智慧合約中的資料呢?

以太坊是一個成熟的區塊鏈,使開發人員能夠建立智慧合約,在區塊鏈上執行的程式可以由交易觸發。人們經常將區塊鏈稱為資料庫,但使用區塊鏈作為資料儲存非常昂貴。

以目前的價格(530美元,4gwei)在以太坊上儲存250GB將花費你106,000,000美元。一般來說,我們可以忍受高成本因為我們:

  • 不會在以太坊區塊鏈上儲存那麼多資料。
  • 區塊鏈的審查制度,透明度和穩健性是值得的。

如果你是以太坊的新手,請檢視此介紹

去中心化儲存

IPFS(星際檔案系統)對區塊鏈儲存有一些保證,即去中心化和防篡改,但不比傳統的磁碟空間花費更多費用。使用EBS 250GB儲存執行EC2 t2.micro例項將花費你大約15美元/月。IPFS的一個獨特功能是它處理檔案的方式。它不使用基於位置的定址(如域名,IP地址,檔案路徑等),而是使用基於內容的定址。將檔案(或目錄)新增到IPFS儲存庫後,可以通過其加密雜湊來引用它。

$ ipfs add article.json
added Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM article.json

$ ipfs cat Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM
{
  "title": "This is an awesome title",
  "content": "paragraph1

paragraph2"
}

$ curl https://ipfs.io/ipfs/Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM
{
  "title": "This is an awesome title",
  "content": "paragraph1

paragraph2"
}

然後,你可以使用IPFS客戶端或任何公共閘道器訪問檔案。你還可以建立非公共閘道器,預設情況下使其成為可寫(只讀),並實現授權方案,以便以程式設計方式訪問IPFS網路。

重要的是要了解IPFS不是一種服務,其他節點將儲存你的內容。如果你的內容不受歡迎,如果他們沒有固定雜湊(他們不​​想租用磁碟空間),垃圾收集器會將其從其他節點中刪除。只要網路上至少有一個對等體確實關心你的檔案並且有興趣儲存它們,網路上的其他節點就可以輕鬆獲取該檔案。即使你的檔案從網路中消失,也可以在以後再次新增,除非其內容發生更改,否則其地址(雜湊)將相同。

IPFS和以太坊智慧合約

儘管以太坊協議沒有提供任何連線到IPFS的本地方式,但我們可以回到像Oraclize這樣的離線解決方案來解決這個問題。Oraclize允許使​​用各種資料提供智慧合約。其中一個可用的資料來源是URL。我們可以使用公共閘道器從IPFS上的JSON檔案中讀取。依靠單個閘道器會很單薄。我們將要使用的另一個資料來源是IPFS。通過使用JSON解析器(它是查詢的一部分)讀取Oraclize智慧合約,我們可以在JSON文件中提取特定欄位。

oraclize_query("IPFS", "json(Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM).title"));

如果Oraclize可以在20秒內獲取檔案,則可以預期獲得非同步請求。如果使用連線良好的節點上傳檔案,則不需要關注超時。我們的EC2(歐盟法蘭克福)例項連線到大約750個同行。通過公共閘道器或本地執行守護程式獲取檔案幾乎是即時的。響應是非同步的,oraclize_query呼叫返回查詢id(bytes32)。你可以將其作為來自Oraclize的資料的識別符號。

function __callback(bytes32 _queryId, string _data) public {
  require(msg.sender == oraclize_cbAddress());
  process_data(_data);
}

出於安全原因,我們希望確保只允許Oraclize呼叫__callback函式。

你可以在GitHub上找到部落格示例的完整程式碼庫:tooploox/ipfs-eth-database

效能和實施

最初,我很擔心效能表現。它是否可以像集中服務傳送響應一樣快速地獲取IPFS上託管的JSON檔案?結果令我很驚喜。

$ wrk -d10s https://ipfs.io/ipfs/Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM
Running 10s test @ https://ipfs.io/ipfs/Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM
  2 threads and 10 connections
  Thread Stats Avg Stdev Max +/- Stdev
    Latency 59.18ms 24.36ms 307.93ms 94.73%
    Req/Sec 86.34 15.48 101.00 85.57%
  1695 requests in 10.05s, 1.38MB read
Requests/sec: 168.72
Transfer/sec: 140.70KB

在我們審查部落格時,作者必須在智慧合約上呼叫addPost時僅輸入IPFS雜湊值。我們使用IPFS和Oraclize從檔案中讀取標題,以使用以太坊事件儲存它。我們不需要為其他智慧合約保留標題,因此使用事件對於我們的用例來說已經足夠了。這可能不是最具開創性的例子,但很好地展示瞭如何優化低交易費用。

pragma solidity 0.4.24;

import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "./lib/usingOraclize.sol";
import "./lib/strings.sol";


contract Blog is usingOraclize, Ownable {
  using strings for *;

  mapping(address => string[]) public hashesByAuthor;
  mapping(bytes32 => string) public hashByQueryId;
  mapping(bytes32 => address) public authorByHash;

  event PostAdded(address indexed author, string hash, uint timestamp, string title);
  event PostSubmitted(address indexed author, string hash, bytes32 queryId);

  uint private gasLimit;

  constructor(uint _gasPrice, uint _gasLimit) public {
    setCustomOraclizeGasPrice(_gasPrice);
    setCustomOraclizeGasLimit(_gasLimit);
  }

  function getPrice(string _source) public view returns (uint) {
    return oraclize_getPrice(_source);
  }

  function setCustomOraclizeGasPrice(uint _gasPrice) public onlyOwner {
    oraclize_setCustomGasPrice(_gasPrice);
  }

  function setCustomOraclizeGasLimit(uint _gasLimit) public onlyOwner {
    gasLimit = _gasLimit;
  }

  function withdraw() public onlyOwner {
    owner.transfer(address(this).balance);
  }

  function __callback(bytes32 _queryId, string _title) public {
    require(msg.sender == oraclize_cbAddress());
    require(bytes(hashByQueryId[_queryId]).length != 0);
    string memory hash = hashByQueryId[_queryId];
    address author = authorByHash[keccak256(bytes(hash))];
    hashesByAuthor[author].push(hash);
    emit PostAdded(author, hash, now, _title);
  }

  function addPost(string _hash) public payable returns (bool) {
    require(authorByHash[keccak256(bytes(_hash))] == address(0), "This post already exists");
    require(msg.value >= oraclize_getPrice("IPFS"), "The fee is too low");
    bytes32 queryId = oraclize_query("IPFS", "json(".toSlice().concat(_hash.toSlice()).toSlice().concat(").title".toSlice()), gasLimit);
    authorByHash[keccak256(bytes(_hash))] = msg.sender;
    hashByQueryId[queryId] = _hash;
    emit PostSubmitted(msg.sender, _hash, queryId);
    return true;
  }

  function getPriceOfAddingPost() public view returns (uint) {
    return oraclize_getPrice("IPFS");
  }
}

前端使用Web3讀取事件,併為給定作者構建所有部落格帖子的列表。

降價商品的內容也儲存在IPFS上。它允許保留新增新部落格帖子的固定費用。我們使用一系列公共IPFS,從我們自己開始。這有意義,尤其是當您從同一節點上傳檔案時。如果您決定以寫入模式執行閘道器,則還可以以程式設計方式固定檔案(預設情況下,它是隻讀的)。我們還允許使用者指定自己的閘道器。 如果使用者安裝了IPFS Companion,他可以利用自己的節點執行。

BlogEvents.getPastEvents("PostAdded", { fromBlock: 0, filter: { author } }).then(events => {
  this.setState({ addedPosts: events.map(e => e.returnValues) });
});

// ...

getPost(gatewayIndex = 0) {
  this.fetchPostFromIpfs(gateways[gatewayIndex])
    .catch(() => this.retry(gatewayIndex))
}

結論

我們從以太坊智慧合約中請求IPFS資料的小實驗讓我們深入瞭解IPFS效能,併為更多生產用例的進一步實施奠定了基礎。

效能問題唯一顧慮的地方可能是IPNS。IPNS是IPFS的命名系統,允許可變URL。hash對應於對等ID而不是檔案或目錄內容hash。在版本0.4.14中引入的新IPNS解析器和釋出者已經緩解了一些問題。確保你擁有最新版本並使用-enable-namesys-pubsub選項執行守護程式,以便從幾乎即時的IPNS更新中受益。

在Amazon Linux 2上連續執行IPFS節點沒有任何重大問題。

======================================================================

分享一些以太坊、EOS、比特幣等區塊鏈相關的互動式線上程式設計實戰教程:

  • java以太坊開發教程,主要是針對java和android程式設計師進行區塊鏈以太坊開發的web3j詳解。
  • python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
  • php以太坊,主要是介紹使用php進行智慧合約開發互動,進行賬號建立、交易、轉賬、代幣開發以及過濾器和交易等內容。
  • 以太坊入門教程,主要介紹智慧合約與dapp應用開發,適合入門。
  • 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
  • C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括賬戶管理、狀態與交易、智慧合約開發與互動、過濾器和交易等。
  • EOS教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、賬戶與錢包、發行代幣、智慧合約開發與部署、使用程式碼與智慧合約互動等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
  • java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Java程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
  • php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Php程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
  • tendermint區塊鏈開發詳解,本課程適合希望使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI介面、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操程式碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。

匯智網原創翻譯,轉載請標明出處。這裡是原文用IPFS和以太坊儲存資料

相關文章