共计 8638 个字符,预计需要花费 22 分钟才能阅读完成。
我们为什么要构建这个?在以太坊区块链上存储大量数据是非常昂贵的。根据以太坊的黄皮书,它是大约 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 filessystem 星际文件系统 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