深入理解IPFS(3/6):什么是行星命名系统(IPNS)
这篇文章是“深入理解IPFS”系列文章的续篇(第3部分),它将帮助您理解IPFS的基本概念。如果您想了解什么是IPFS及其工作原理,那么您也应该查看第一部分 🙂
在第2部分中,我们讨论了IPLD(星际关联数据)的意义、工作原理及其技术规范。我们还学习了一个教程,其中我们创建了一个类似于发布系统的媒体,完全使用IPLD。你可以在这里查看:
在这一部分中,我们将深入研究IPFS的命名系统——行星间命名系统(IPNS)。我们将探讨:
l 什么需要去使用IPNS? 它与今天的DNS(域名系统)有何可比性,又有何不同?
l 我们将探讨路由如何在IPFS中工作,以及IPNS如何工作?
l 最后,我们会亲手使用IPNS,我们将设置我的网站去完全使用IPFS堆栈。
希望您从本系列中学到很多关于IPFS的知识。让我们开始吧!
为什么需要IPNS?
为了理解为什么我们需要IPNS,让我们看看目前我们如何使用IPFS访问我们的照片、视频和memes。
另外,如果你想要跟随我一起来做,你可以这样下载我的网站:
wget --mirror --convert-links --adjust-extension --page-requisites --no-parent https://vaibhavsaini.com
当我把我的网站加入IPFS时,你会得到如下输出数据:
现在,我可以在这里访问我的网站:https://gateway.pinata.cloud/ipfs/QmYVd8qstdXtTd1quwv4nJen6XprykxQRLo67Jy7WyiLMB/
但这个链接有几个问题:
l 首先,它很难读,更不用说记住了。
l 其次,它是一个不可变的链接。我所说的不可变链接是指这个链接是永久性的(由于内容寻址的本质)。如果我在我的网站的任何地方添加一个逗号,根文件夹的CID就会改变,从而改变到我网站的链接。所以,每次我在我的网站上做任何改动,我都必须把这个新链接给所有想要访问我最新网站的人,这可并不酷。
l 这里就是IPNS发挥作用的时候了。
使用IPNS可以生成一个可变链接,其中:
l 将是人类可读和容易记住。
l 指向最新版本的网站,个人资料照片,视频等。
IPNS中的名称(链接中的Hash/ IPNS /链接)是公钥的散列。它与一个记录相关联,该记录包含有关它链接到的hash的信息,该hash由相应的私钥签名。新记录可以随时签署和发布。
因此,换句话说,IPNS是一个基于公钥基础设施(或PKI)的全局名称空间,它允许我们构建信任链(因此您可以跟随公钥到达它的路由对等点),为我们提供加密和身份验证,并且实际上仍然与其他名称服务兼容。例如,我们甚至可以将DNS条目、洋葱浏览器地址或比特地址等映射到IPNS地址。
IPNS并不是在IPFS上创建可变地址的唯一方法,您还可以使用DNSLink(它目前比IPNS快得多,并且使用了更多可读的名称,我们将在下面了解更多)。其他社区成员正在探索使用区块链存储公共名称记录的方法。下面是针对分布式web命名系统的不同项目的比较。
IPNS和DNS有一些相似之处。两者都是在各自的系统中解决类似的问题,前者在内容寻址系统中解决,后者在位置寻址系统中解决。
在定位系统(今天的老式internet)中,我们使用IP:PORT组合来访问数据。因此,在一个位置地址系统中,我的网站地址将是:http://18.205.129.69:80
它既不容易读也不容易记。
但这个链接地址总是指向这个地址上承载的最新内容。
使用DNS,我们将这个IP与一个域名相关联,因此您可以访问vaibhavsaini.com网站。
好的,那它是如何工作的呢?
IPNS可以通过多种方式实现,但目前的实现使用分布式哈希表(DHT)。因此,只有将每个URI映射到其对应的最新hash映射才能进行解析,而忽略了任何历史映射。从归档的角度来看,这并不好,因为以前版本的文件可能仍然存在于IPFS存储中,但是它们相应的URI映射将丢失。
让我们使用ipns节点模块来理解ipns记录是如何发布的。
const ipns = require('ipns');
const crypto = require('libp2p-crypto'); //for generating RSA keypair
function generateRsaKeypair(){
//generating an 2048 bit RSA keypair
crypto.keys.generateKeyPair('RSA', 2048, async(err, keypair) => {
if(err){
console.log('error ', err);
}
else{
console.log("nGenerated new RSA Keypairn");
createIpnsRecord(keypair);
}
});
}
/*
Creating an IPNS record with a lifetime
ipns.create(privateKey, value, sequenceNumber, lifetime, [callback])
privateKey (PrivKey RSA Instance): key to be used for cryptographic operations.
value (string): ipfs path of the object to be published.
sequenceNumber (Number): number representing the current version of the record.
lifetime (string): lifetime of the record (in milliseconds).
callback (function): operation result.
*/
function createIpnsRecord(keypair){
let sequenceNumber = 0;
let lifetime = 1000000; //1000000 milliseconds
let value = 'QmYVd8qstdXtTd1quwv4nJen6XprykxQRLo67Jy7WyiLMB'; //hash to my website
var recordData;
ipns.create(keypair, value, sequenceNumber, lifetime, (err, entryData) => {
if(!err){
//Created new IPNS record
console.log("nGenerated new IPNS recordn");
console.log(entryData);
validateIpnsRecord(entryData, keypair);
}
});
}
/*
Creating an IPNS record with a fixed expiration datetime.
ipns.createWithExpiration(rsa, value, sequenceNumber, expiration, [callback])
privateKey (PrivKey RSA Instance): key to be used for cryptographic operations.
value (string): ipfs path of the object to be published.
sequenceNumber (Number): number representing the current version of the record.
expiration (Date): Date object.
callback (function): operation result.
*/
function createIpnsRecordWithExpiration(keypair){
ipns.createWithExpiration(keypair, value, sequenceNumber, expiration, (err, entryData)=>{
if(!err){
validateIpnsRecord(entryData);
}
});
}
/*
Validate an IPNS record previously stored in a protocol buffer.
ipns.validate(publicKey, ipnsEntry, [callback])
publicKey (PubKey RSA Instance): key to be used for cryptographic operations.
ipnsEntry (Object): ipns entry record (obtained using the create function).
callback (function): operation result.
*/
function validateIpnsRecord(entryData, keypair){
ipns.validate(keypair.public, entryData, (err)=>{
//if no err then the validation was successful
if(!err){
console.log('nIPNS Record Validation Successfuln');
}
});
}
generateRsaKeypair();
上面的代码经过了足够的注释,您也可以在这里查看完整的项目。
如果您想深入了解IPFS中的路由是如何工作的,可以阅读本文。我想在这篇文章中解释一下,但是有太多其他有趣的事情要探索,所以我跳过了😉
实际使用IPNS
让我们通过IPNS发布我们的网站:
ipfs name publish QmYVd8qstdXtTd1quwv4nJen6XprykxQRLo67Jy7WyiLMB
这个过程可能会要几分钟。之后您将会得到如下的输出结果:
Published to Qmb1VVr5xjpXHCTcVm3KF3i88GLFXSetjcxL7PQJRviXSy: /ipfs/QmYVd8qstdXtTd1quwv4nJen6XprykxQRLo67Jy7WyiLMB
现在您可以从这里得到最新的网站地址:https://gateway.pinata.cloud/ipns/Qmb1VVr5xjpXHCTcVm3KF3i88GLFXSetjcxL7PQJRviXSy
注意:IPNS会在大约12小时后忘记(系统存活时间)已发布的名称。您可以运行cron命令配置定时任务,以便在12小时内重新发布。
如果我想添加一个更新的CID,我将使用相同的命令:
ipfs name publish <my_new_CID>
你也可以检查当前CID链接到你的peerID:
ipfs name resolve Qmb1VVr5xjpXHCTcVm3KF3i88GLFXSetjcxL7PQJRviXSy
这样操作将返回至最新的CID。
为了增加灵活性,您还可以为不同的内容和/或背景使用不同的key(例如下面的键名是vasa_blog)。例如,我可以用一个key发布我的网站,用另一个key发布我的博客,用另一个key发布我的谈话视频。
ipfs key gen --type=rsa --size=2048 vasa_blog
ipfs name publish --key=vasa_blog <cid_to_my_blog>
这解决了我们上面提到的一个问题(不可变链接的问题)。但是链接仍然很难看。为了制作链接,我们仍然需要使用DNS。还有其他一些系统更适合于内容寻址系统,比如CCN/NDN、XIA。但这需要升级互联网本身,如果没有大规模的需求,这真的很难保证。即使有很大的需求,IPv6也还没有完全部署(-这没有给我任何希望看到NDN/CCN大规模部署在核心,没有首先建立使用内容地址网络。这意味着终端开发人员(web开发人员)必须能够使用内容寻址网络非常有效地移动大量数据(视频等),然后才能实现改善底层网络的大量需求。因此,正如我们所看到的,通过使IPFS对最终开发人员可用,我们也可以为这些体系结构创造需求。
言归正传,现在让我们使用DNS创建可读链接。
DNSLink使用DNS TXT将域名(如vaibhavsaini.com)映射到IPFS地址。因为可以编辑DNS记录,所以使用它们可以使域名始终指向IPFS中对象的最新版本(请记住,如果修改对象,IPFS对象的地址会更改)。但我们不希望每次更新网站时都更改TXT记录。因此,我们将添加一个ipns链接而不是ipfs链接。此外,由于DNSLink使用DNS记录,因此它生成的名称通常也易于键入和读取。
DNSLink地址看起来像IPNS地址,但它使用一个域名来代替散列的公钥:
/ipns/vaibhavsaini.com
就像普通的IPFS地址一样,它们可以包含到其他文件的链接:
/ipns/vaibhavsaini.com/assets/images
当IPFS客户机或节点试图解析该地址时,它会为vaibhavsaini.com查找一条TXT记录,内容如下:
dnslink=/ipfs/<CID for your content here>
OR
dnslink=/ipns/<hash of public key>
例如,如果你查vaibhavsaini.com的DNS记录,你会看到它的DNSLink条目:
$ dig +noall +answer TXT vaibhavsaini.com
vaibhavsaini.com. 1 IN TXT "dnslink=/ipns/Qmb1VVr5xjpXHCTcVm3KF3i88GLFXSetjcxL7PQJRviXSy"
据此,得到本地址:
/ipns/vaibhavsaini.com/assets/images
然后会得到如下区块 :
/ipns/Qmb1VVr5xjpXHCTcVm3KF3i88GLFXSetjcxL7PQJRviXSy/assets/images
超级酷对吗?
到目前为止,我们将地址从一个复杂的hash简化为一个可读的名称。
但是,我们可以做得更好。
这个(上面的链接)链接仍然相当混乱,坦白地说,如果我们想让今天的Web2用户以最小的努力访问我分散的Web3内容,我们不希望他们必须处理网关和ipns/ipfs前缀,如果他们不需要的话。分散式web社区的一个主要感觉是,用户体验不应该发生太大的变化——转换应该是透明的,但是很容易——这就是分散式web将如何获胜的原因。理想情况下,我们希望得到这样的结果:
https://profile.vaibhavsaini.com
通过子域发布
因此,为了使我们的地址更具可读性,我们可以创建一条A记录,将我们的子域指向监听端口80的HTTP请求的IPFS对等点的IP地址(例如任何公共IPFS网关,或者您自己的网关)。但是等等,我们可以做得更好!
因为我们不想依赖于静态的IP地址,所以我们可以使用CNAME记录来指向网关的DNS记录。这样,如果IP地址改变了,我们仍然可以指向正确的位置。不幸的是,cnamerecord不允许其他记录(比如TXT),但是IPFS的优秀工作人员允许我们为_dnslink.your创建DNS TXT记录,IPFS将查找该域。
当您希望在不放弃对原始DNS域完全控制的情况下,将对DNSLink记录的自动设置或委托控制的安全性交给到第三方时,这也非常有用。
我使用AWS Route53进行DNS设置;您可以使用任何提供者。
设置CNAME记录:
设置_dnslink TXT记录:
这是它最终的样子:
瞧!我们使用IPFS堆栈托管和解析内容,并提供了一个地址使任何Web2用户都可以轻松使用该地址。
您可能会注意到地址栏上的“不安全”警告,这是因为我没有安装通配符证书;)
您可能也会注意到,网站需要一些时间来解决。这是因为您的网站的内容只在一个节点上。如果你把你的网站固定在几个节点或其他节点上,然后去试图访问你的网站(这意味着你的内容很受欢迎),它会更快地解析:
这部分就讲到这里。感谢Carson Farmer、Mark Pors和Jonybang的文章[1,2,3]。感谢阅读;)
关于作者:
Vaibhav是TowardsBlockchain的联合创始人。麻省理工学院剑桥创新中心孵化启动者。他是高级区块链开发人员,曾参与多个区块链平台,包括以太坊,Quorum,EOS,Nano,Hashgraph,IOTA等。
本文来源于互联网:深入理解IPFS(3/6):什么是行星命名系统(IPNS)