深入理解IPFS(3/6):什么是行星命名系统(IPNS)

2019-04-12 18:17:10

  这篇文章是“深入理解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时,你会得到如下输出数据:

   

V31.png


  现在,我可以在这里访问我的网站:https://gateway.pinata.cloud/ipfs/QmYVd8qstdXtTd1quwv4nJen6XprykxQRLo67Jy7WyiLMB/


  但这个链接有几个问题:

  l首先,它很难读,更不用说记住了。

  l其次,它是一个不可变的链接。我所说的不可变链接是指这个链接是永久性的(由于内容寻址的本质)。如果我在我的网站的任何地方添加一个逗号,根文件夹的CID就会改变,从而改变到我网站的链接。所以,每次我在我的网站上做任何改动,我都必须把这个新链接给所有想要访问我最新网站的人,这可并不酷。


  这里就是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 Keypair\n");

              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 record\n");

          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 Successful\n');

             }

         });

  }

  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


  你也可以检查当前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


  这解决了我们上面提到的一个问题(不可变链接的问题)。但是链接仍然很难看。为了制作链接,我们仍然需要使用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/

  OR

  dnslink=/ipns/


  例如,如果你查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记录:

V32.png



  设置_dnslink TXT记录:

V33.png


  这是它最终的样子:

V34.png


  瞧!我们使用IPFS堆栈托管和解析内容,并提供了一个地址使任何Web2用户都可以轻松使用该地址。

  您可能会注意到地址栏上的“不安全”警告,这是因为我没有安装通配符证书;)

  您可能也会注意到,网站需要一些时间来解决。这是因为您的网站的内容只在一个节点上。如果你把你的网站固定在几个节点或其他节点上,然后去试图访问你的网站(这意味着你的内容很受欢迎),它会更快地解析:


  这部分就讲到这里。感谢Carson Farmer、Mark Pors和Jonybang的文章[1,2,3]。感谢阅读;)


  关于作者:

V35.png


  Vaibhav是TowardsBlockchain的联合创始人。麻省理工学院剑桥创新中心孵化启动者。他是高级区块链开发人员,曾参与多个区块链平台,包括以太坊,Quorum,EOS,Nano,Hashgraph,IOTA等。


最新推荐