如何將JSON文件存儲在IPFS上,並使用Oraclize訪問智能合約中的數據呢?php
以太坊是一個成熟的區塊鏈,使開發人員可以建立智能合約,在區塊鏈上執行的程序能夠由交易觸發。人們常常將區塊鏈稱爲數據庫,但使用區塊鏈做爲數據存儲很是昂貴。前端
以目前的價格(530美圓,4gwei)在以太坊上存儲250GB將花費你106,000,000美圓。通常來講,咱們能夠忍受高成本由於咱們:java
若是你是以太坊的新手,請查看此介紹。node
IPFS(星際文件系統)對區塊鏈存儲有一些保證,即去中心化和防篡改,但不比傳統的磁盤空間花費更多費用。使用EBS 250GB存儲運行EC2 t2.micro實例將花費你大約15美圓/月。IPFS的一個獨特功能是它處理文件的方式。它不使用基於位置的尋址(如域名,IP地址,文件路徑等),而是使用基於內容的尋址。將文件(或目錄)添加到IPFS存儲庫後,能夠經過其加密哈希來引用它。python
$ ipfs add article.json added Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM article.json $ ipfs cat Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM { "title": "This is an awesome title", "content": "paragraph1\r\n\r\nparagraph2" } $ curl https://ipfs.io/ipfs/Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM { "title": "This is an awesome title", "content": "paragraph1\r\n\r\nparagraph2" }
而後,你可使用IPFS客戶端或任何公共網關訪問文件。你還能夠建立非公共網關,默認狀況下使其成爲可寫(只讀),並實現受權方案,以便以編程方式訪問IPFS網絡。android
重要的是要了解IPFS不是一種服務,其餘節點將存儲你的內容。若是你的內容不受歡迎,若是他們沒有固定哈希(他們不想租用磁盤空間),垃圾收集器會將其從其餘節點中刪除。只要網絡上至少有一個對等體確實關心你的文件而且有興趣存儲它們,網絡上的其餘節點就能夠輕鬆獲取該文件。即便你的文件從網絡中消失,也能夠在之後再次添加,除非其內容發生更改,不然其地址(哈希)將相同。git
儘管以太坊協議沒有提供任何鏈接到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的數據的標識符。github
function __callback(bytes32 _queryId, string _data) public { require(msg.sender == oraclize_cbAddress()); process_data(_data); }
出於安全緣由,咱們但願確保只容許Oraclize調用__callback
函數。web
你能夠在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和以太坊存儲數據