我們爲什麼要構建這個?在以太坊區塊鏈上存儲大量數據是非常昂貴的。根據以太坊的黃皮書,它是大約20,0000gas,256bit/8字節(1字)。基於02/28/2018 gas價格爲4gwei/gas。請參閱:https://ethgasstation.info瞭解當前價格。
每個交易8個字節20,000gas*4gwei/gas=80,000gwei
。
8,000字節80,000gwei*1000bytes/8=10,000,000gwei/kB=
0.01`以太。
0.01以太/kB*1000kB=10以太
存儲1Mb,價格爲860美元/以太=8600.00美元!在以太坊區塊鏈上存儲1GB
文件需要花費8,600,000.00
美元!
存儲以太坊的38頁PDF黃皮書(520Kb)=4472美元。請參閱: http://eth-converter.com/進行轉換計算。
如果我們只能在區塊鏈上存儲幾Kb的數據,那麼我們仍然需要依靠集中式服務器來存儲數據。值得慶幸的是,可以使用稱爲`InterPlanetary files
system 星際文件系統
IPFS`的去中心化網絡上存儲數據的解決方案。請參閱:https://ipfs.io/瞭解更多信息。在IPFS中查找文件時,你要求網絡查找將內容存儲在唯一哈希後面的節點。來自IPFS自己的網站:
“IPFS和Blockchain完美匹配!你可以使用IPFS處理大量數據,並將不可變的永久IPFS鏈接放入區塊鏈交易中。這個時間戳和保護你的內容,而不必將數據放在鏈本身上。“
我們構建什麼?
一個簡單的DApp,用於將文檔上載到IPFS,然後將IPFS哈希存儲在以太坊區塊鏈上。一旦IPFS哈希號被髮送到以太坊區塊鏈,用戶將收到交易收據。我們將使用Create-React-App
框架來構建前端。此Dapp適用於在瀏覽器中安裝了MetaMask
的任何用戶。
這就是我們完成後DApp的樣子:
如何建立它:
注意:如果你只是想要代碼,請參閱我的github。
在我們開始之前,這些是我做出的假設:
- 關於用戶的假設:用戶安裝了Metamask以與DApp交互。
- 關於你/開發人員的假設:你對JavaScript、React.js以及Node.js/NPM有一定的瞭解。重要說明:請確保你運行當前版本的Node和NPM。對於本教程,我正在運行:node v8.9.4和NPM 5.6.0。
- 安裝MetaMask。如果尚未安裝MetaMask,請訪問https://metamask.io/並按照說明操作。此DApp將假定用戶已安裝MetaMask。
- 創建一個目錄來存儲我們的DApp。對於本教程,我將其稱爲
eth-ipfs
。 - 使用NPM安裝
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
中。
web3.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;
storehash.js
爲了讓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.js
在本教程中,我們將運行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中的操作順序:
- 1.設置狀態變量。
- 2.捕獲用戶的文件。
- 3.將文件轉換爲緩衝區。
- 4.將緩衝的文件發送到IPFS。
- 5.IPFS返回一個哈希值。
- 6.獲取用戶的MetaMask以太坊地址
- 7.發送IPFS以便在以太坊上存儲。
- 8.使用MetaMask,用戶將確認交易到以太坊。
- 9.以太坊合約將返回一個交易哈希數。
- 10.交易哈希值可用於生成具有諸如所使用的gas量和塊編號之類的信息的交易收據。
- 11.IPFS和以太坊信息將在使用Bootstrap for CSS的表中呈現。注意:我沒有創建一個isLoading類型變量來自動重新呈現blockNumber和gasUsed變量的狀態。因此,現在,你必須再次單擊或實現自己的加載圖標。 描述變量和函數的表,後面是代碼本身如下:
最後,這是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