咱們爲何要構建這個?在以太坊區塊鏈上存儲大量數據是很是昂貴的。根據以太坊的黃皮書,它是大約20,0000gas,256bit/8字節(1字)。基於02/28/2018 gas價格爲4gwei/gas。請參閱:https://ethgasstation.info瞭解當前價格。javascript
每一個交易8個字節20,000gas*4gwei/gas=80,000gwei
。php
8,000字節80,000gwei*1000bytes/8=10,000,000gwei/kB=
0.01`以太。css
0.01以太/kB*1000kB=10以太
存儲1Mb,價格爲860美圓/以太=8600.00美圓!在以太坊區塊鏈上存儲1GB
文件須要花費8,600,000.00
美圓!html
存儲以太坊的38頁PDF黃皮書(520Kb)=4472美圓。請參閱: http://eth-converter.com/進行轉換計算。前端
若是咱們只能在區塊鏈上存儲幾Kb的數據,那麼咱們仍然須要依靠集中式服務器來存儲數據。值得慶幸的是,可使用稱爲`InterPlanetary files
system 星際文件系統
IPFS`的去中心化網絡上存儲數據的解決方案。請參閱:https://ipfs.io/瞭解更多信息。在IPFS中查找文件時,你要求網絡查找將內容存儲在惟一哈希後面的節點。來自IPFS本身的網站:java
「IPFS和Blockchain完美匹配!你可使用IPFS處理大量數據,並將不可變的永久IPFS連接放入區塊鏈交易中。這個時間戳和保護你的內容,而沒必要將數據放在鏈自己上。「node
一個簡單的DApp,用於將文檔上載到IPFS,而後將IPFS哈希存儲在以太坊區塊鏈上。一旦IPFS哈希號被髮送到以太坊區塊鏈,用戶將收到交易收據。咱們將使用Create-React-App
框架來構建前端。此Dapp適用於在瀏覽器中安裝了MetaMask
的任何用戶。python
這就是咱們完成後DApp的樣子:react
如何創建它:android
注意:若是你只是想要代碼,請參閱個人github。
在咱們開始以前,這些是我作出的假設:
eth-ipfs
。Create-React-App
和其餘依賴項。使用NPM並安裝如下內容:npm i create-react-app npm install react-bootstrap npm install fs-extra npm install ipfs-api npm install web3@^1.0.0-beta.26
輸入eth-ipfs
目錄,鍵入npm start
,Create-React-App
應自動在http://localhost:3000/
上呈現。
注意:若是你到目前爲止還沒有使用create-react-app
,則可能必須先在全局安裝它
sudo npm install -g create-react-app
或者npm install -g create-react-app
create-react-app eth-ipfs
cd
進入eth-ipfs
而後運行npm start
在Rinkeby testnet上使用Remix部署如下Solidity代碼。請參閱https://remix.ethereum.org。你須要一些Rinkeby測試以太,若是你尚未Rinkeby faucet的一些免費測試以太話。https://www.rinkeby.io/#faucet。
pragma solidity ^0.4.17; contract Contract { string ipfsHash; function sendHash(string x) public { ipfsHash = x; } function getHash() public view returns (string x) { return ipfsHash; } }
保存部署它的合約地址和應用程序二進制接口(ABI)。要得到合約的ABI,請在Remix中轉到你的合約地址:
單擊「Compile」選項卡,而後單擊灰色的「Details」按鈕。
這將打開「Details」窗口。複製「ABI」,它是一個JSON文件。
我我的更喜歡將ABI JSON放入格式化程序,例如https://jsonformatter.org,並在個人javascript代碼中使用以前檢查它是否有效。保存合約地址和ABI以供往後使用。
在咱們的「eth-ipfs/src」目錄中,建立如下文件: web3.js
,ipfs.js
和storehash.js
。咱們的大部分代碼都在App.js
中。
咱們想使用1.0版本的web3.js,由於與0.20版本不一樣,1.0容許咱們在咱們的javascript中使用async並等待而不是promises。目前,MetaMask的默認web3.js提供程序是0.20版本。因此,讓咱們確保咱們覆蓋Metamask的web3版本0.20的默認版本,並使用咱們的1.0。這是代碼:
//爲咱們的1.0版本覆蓋metamask v0.2。 //1.0讓咱們使用async和await而不是promises import Web3 from ‘web3’; const web3 = new Web3(window.web3.currentProvider); export default web3;
爲了讓web3.js可以訪問咱們以前部署到以太坊的Rinkeby testnet的合約,你須要如下內容:1)合約地址和2)合約中的ABI。必定要從/src
目錄中導入web3.js
文件。這是代碼:
import web3 from './web3'; //access our local copy to contract deployed on rinkeby testnet //use your own contract address const address = '0xb84b12e953f5bcf01b05f926728e855f2d4a67a9'; //use the ABI from your contract const abi = [ { "constant": true, "inputs": [], "name": "getHash", "outputs": [ { "name": "x", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "x", "type": "string" } ], "name": "sendHash", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" } ] export default new web3.eth.Contract(abi, address);
在本教程中,咱們將運行ipfs.infura.io節點以鏈接到IPFS,而不是在咱們本身的計算機上運行IPFS守護程序。在代碼註釋中,若是將IPFS安裝爲全局依賴項,則還能夠選擇運行本身的IPFS守護程序。有關使用其節點的更多信息,請參閱https://infura.io/。這是代碼:
//using the infura.io node, otherwise ipfs requires you to run a //daemon on your own computer/server. const IPFS = require(‘ipfs-api’); const ipfs = new IPFS({ host: ‘ipfs.infura.io’, port: 5001, protocol: ‘https’ }); //run with local daemon // const ipfsApi = require(‘ipfs-api’); // const ipfs = new ipfsApi(‘localhost’, ‘5001’, {protocol:‘http’}); export default ipfs;
這是App.js中的操做順序:
最後,這是App.js代碼:
import React, { Component } from ‘react’; //import logo from ‘./logo.svg’; import ‘./App.css’; import web3 from ‘./web3’; import ipfs from ‘./ipfs’; import storehash from ‘./storehash’; class App extends Component { state = { ipfsHash:null, buffer:'', ethAddress:'', blockNumber:'', transactionHash:'', gasUsed:'', txReceipt: '' }; captureFile =(event) => { event.stopPropagation() event.preventDefault() const file = event.target.files[0] let reader = new window.FileReader() reader.readAsArrayBuffer(file) reader.onloadend = () => this.convertToBuffer(reader) }; convertToBuffer = async(reader) => { //file is converted to a buffer for upload to IPFS const buffer = await Buffer.from(reader.result); //set this buffer -using es6 syntax this.setState({buffer}); }; onClick = async () => { try{ this.setState({blockNumber:"waiting.."}); this.setState({gasUsed:"waiting..."}); //get Transaction Receipt in console on click //See: https://web3js.readthedocs.io/en/1.0/web3-eth.html#gettransactionreceipt await web3.eth.getTransactionReceipt(this.state.transactionHash, (err, txReceipt)=>{ console.log(err,txReceipt); this.setState({txReceipt}); }); //await for getTransactionReceipt await this.setState({blockNumber: this.state.txReceipt.blockNumber}); await this.setState({gasUsed: this.state.txReceipt.gasUsed}); } //try catch(error){ console.log(error); } //catch } //onClick onSubmit = async (event) => { event.preventDefault(); //bring in user's metamask account address const accounts = await web3.eth.getAccounts(); console.log('Sending from Metamask account: ' + accounts[0]); //obtain contract address from storehash.js const ethAddress= await storehash.options.address; this.setState({ethAddress}); //save document to IPFS,return its hash#, and set hash# to state //https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#add await ipfs.add(this.state.buffer, (err, ipfsHash) => { console.log(err,ipfsHash); //setState by setting ipfsHash to ipfsHash[0].hash this.setState({ ipfsHash:ipfsHash[0].hash }); // call Ethereum contract method "sendHash" and .send IPFS hash to etheruem contract //return the transaction hash from the ethereum contract //see, this https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-send storehash.methods.sendHash(this.state.ipfsHash).send({ from: accounts[0] }, (error, transactionHash) => { console.log(transactionHash); this.setState({transactionHash}); }); //storehash }) //await ipfs.add }; //onSubmit render() { return ( <div className="App"> <header className="App-header"> <h1> Ethereum and IPFS with Create React App</h1> </header> <hr /> <Grid> <h3> Choose file to send to IPFS </h3> <Form onSubmit={this.onSubmit}> <input type = "file" onChange = {this.captureFile} /> <Button bsStyle="primary" type="submit"> Send it </Button> </Form> <hr/> <Button onClick = {this.onClick}> Get Transaction Receipt </Button> <Table bordered responsive> <thead> <tr> <th>Tx Receipt Category</th> <th>Values</th> </tr> </thead> <tbody> <tr> <td>IPFS Hash # stored on Eth Contract</td> <td>{this.state.ipfsHash}</td> </tr> <tr> <td>Ethereum Contract Address</td> <td>{this.state.ethAddress}</td> </tr> <tr> <td>Tx Hash # </td> <td>{this.state.transactionHash}</td> </tr> <tr> <td>Block Number # </td> <td>{this.state.blockNumber}</td> </tr> <tr> <td>Gas Used</td> <td>{this.state.gasUsed}</td> </tr> </tbody> </Table> </Grid> </div> ); } //render } //App export default App;
我在src/App.css
中添加了一些CSS,使它看起來更容易一些:
/*some css I added*/ input[type=」file」] { display: inline-block; } .table { max-width: 90%; margin: 10px; } .table th { text-align: center; } /*end of my css*/
並向src/index.js
添加一些導入:
/*https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-stylesheet*/ import ‘bootstrap/dist/css/bootstrap.css’; import ‘bootstrap/dist/css/bootstrap-theme.css’;
這就對了!你的DApp應該完成。因此你須要作的就是選擇一個文件,發送它,並得到一個交易收據。若是你經過localhost:3000
鏈接到IPFS節點,那麼你應該可以在其中一個IPFS網關上看到你的文件。https://gateway.ipfs.io/ipfs/
+你的IPFS哈希。
例如: https://gateway.ipfs.io/ipfs/QmYjh5NsDc6LwU3394NbB42WpQbGVsueVSBmod5WACvpte
關於IPFS的一個注意事項是,除非你的文件被另外一個節點接收或者你將其固定,不然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+React.js DApp