星鉴网>IPFS技术>将IPFS与以太坊一起用于数据存储!

将IPFS与以太坊一起用于数据存储!

2018/10/26 15:41:45 1011人阅读

【导读】 IPFS(星际文件系统)对区块链有一些作用,即分散和防篡改存储,比统的磁盘空间花费更少。

以太坊是一个成熟的区块链,使开发人员能够创建智能合约 - 在区块链上执行的程序可以由交易触发。人们经常将区块链称为数据库,但使用区块链作为数据存储其实非常昂贵。

以目前的价格在以太坊上存储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\r\n\r\nparagraph2"
}
$ curlhttps://ipfs.io/ipfs/Qmd4PvCKbFbbB8krxajCSeHdLXQamdt7yFxFxzTbedwiYM
{
"title": "This is an awesome title",
"content": "paragraph1\r\n\r\nparagraph2"
}

然后,您可以使用IPFS客户端或任何公共网关访问文件。您还可以创建非公共网关,默认情况下使其成为可写(只读),并实现授权方案,以便以编程方式访问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 -d10shttps://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))
}

您可以在GitHub上找到分散博客示例的完整代码库:tooploox / ipfs-eth-database!

结论

从以太坊智能合约中请求IPFS数据的小实验让我们深入了解IPFS性能,并为更多生产用例的进一步实施奠定了基础。

唯一的性能问题可能是IPNS。IPNS是IPFS的命名系统,允许可变URL。散列对应于对等ID而不是文件或目录内容散列。在版本0.4.14中引入的新IPNS解析器和发布者已经缓解了一些问题。确保您拥有最新版本并使用-enable-namesys-pubsub选项运行守护程序,以便从IPNS更新中受益。

在Amazon Linux 2上连续运行IPFS节点没有任何重大问题。

本文翻译自https://www.tooploox.com/blog作者:Michal Zalecki

11

参与讨论

登录后参加评论......

全部评论 0

作者

返回顶部