# IPNS (InterPlanetary Name System) 详解

## 前言

本文是笔者在学习IPFS地过程中的学习笔记。内容主体翻译于[官方文档](https://docs.ipfs.io/concepts/ipns)。

## IPNS 是什么

它是一套名称寻址系统，可以类比dns。主要目标是在IPFS上为动态内容提供稳定的访问地址。

我们知道IPFS是基于内容寻址的。 它基于每个文件中的内容创建一个唯一地址(即CID)。

如果你想把某个文件地址分享给别人，此后每当文件内容发生变化时，应需要把最新的地址再发一遍。

对于可变的内容来说， 这显然是无法接受的。

IPNS 就是用来解决这个问题的。 用它可以创建"内容可更新"的地址。

在IPNS中， `name` 是一个公钥的哈希。 它与一条记录关联，这条记录中包含了`name`所指向的内容地址。

最关键的是， **这条记录用与与此公钥关联的私钥签名**。 这就保证了此记录只能由私钥持有者修改。

在此机制之下， 随时可以创建新记录，对其签名并发布出来。

IPNS 地址需要使用 `/ipns/` 前缀。 例如：

`/ipns/QmSrPmbaUKA3ZodhzPWZnpFgcPMFWF4QsxXbkWfEptTBJd`

## 用IPFS命令行示例操作IPNS

1. 开启IPFS daemon(如果还没安装，可以对照[文档](https://docs.ipfs.io/how-to/command-line-quick-start)安装)
    
    ```bash
    ipfs daemon
    >  Initializing daemon...
    >  go-ipfs version: 0.13.0-c9d51bbe0
    >  ...
    >  WebUI: http://127.0.0.1:5001/webui
    >  Daemon is ready
    ```
    
2. 在新的命令行窗品创建一个示例文件，这个文件将被用来关联到IPNS。简单起见，这里只创建一个 hello world 。
    
    ```bash
    echo "Hello IPFS" > hello.txt
    ```
    
3. 将此文件加入到IPFS中
    
    ```bash
    ipfs add hello.txt
    >  added QmUVTKsrYJpaxUT7dr9FpKq6AoKHhEM7eG1ZHGL56haKLG hello.txt
    >  11 B / 11 B [=====================================================] 100.00%
    ```
    
    记下此文件的CID。注意： 由于IPFS基于文件内容生成CID的特性，因此只要内容相同，任何人生成文件CID都是相同的。
    
4. 用 `cat` 命令和 CID 再查看一下文件内容
    
    ```bash
    ipfs cat QmUVTKsrYJpaxUT7dr9FpKq6AoKHhEM7eG1ZHGL56haKLG
    > Hello IPFS
    ```
    
5. 重点来了， 下面命令让此CID发布到IPNS。 由于IPNS的 `name` 是发布者公钥的哈希，所以相同文件由不同人(或用不同公钥)发布时会得到不同的地址
    
    ```bash
    ipfs name publish /ipfs/QmUVTKsrYJpaxUT7dr9FpKq6AoKHhEM7eG1ZHGL56haKLG
    >  Published to k51qzi5uqu5dkpmv98v525360py640pa9nqlkt3sudd14utec6t1dnrbdd332q: /ipfs/QmUVTKsrYJpaxUT7dr9FpKq6AoKHhEM7eG1ZHGL56haKLG
    ```
    
    这个过程可能会比较慢，长达几十秒甚到一分钟。
    
    发布结果中， `k51...` 这一串字符便是IPNS的 `name`
    
    此后你可以一直使用这个`name`来稳定访问自己指向的内容。 即使内容变了（因此CID也变了），这个地址并不改变。
    
    \*\*注：\*\*如果要发布的不是单一文件， 而是一个文件夹，那么这个命令会慢到不可用。
    
    原因是默认情况下 `ipfs name publish` 命令会尝试解析此CID中的全部内容是否可用。 这可能需要几个小时。
    
    为了避免这个问题， 可以利用命令参数，`--resolve`，它默认为true，当把它关闭时，命令会直接发布不先作解析验证：
    
    ```bash
    ipfs name publish --resolve=false /ipfs/QmUVTKsrYJpaxUT7dr9FpKq6AoKHhEM7eG1ZHGL56haKLG
    >  Published to k51qzi5uqu5dkpmv98v525360py640pa9nqlkt3sudd14utec6t1dnrbdd332q: /ipfs/QmUVTKsrYJpaxUT7dr9FpKq6AoKHhEM7eG1ZHGL56haKLG
    ```
    
6. 现在，就可以通过IPNS地址来访问文件内容了
    
    ```bash
    curl https://gateway.ipfs.io/ipns/k51qzi5uqu5dkpmv98v525360py640pa9nqlkt3sudd14utec6t1dnrbdd332q
    > Hello IPFS
    ```
    
    初次访问会很慢，甚至可能出现 504 超时错误。
    
    如果不想等，有一个快速验证的办法。
    
    在运行`ipfs daemon`之后， 默认会在本机 **5001** 端口启动一个web server，这时访问 `http://127.0.0.1:5001/webui` 就可以看到 IPFS web console
    
    在其中的 **FILES** 界面， 在输入框中输入完整哈希 `/ipns/k51...` 即可看到文件内容。
    
7. 下面我们修改一下文件内容（其实在IPFS看来就是建了一个新文件），并更新到IPNS中
    
    ```bash
    echo "Hello again IPFS" > hello.txt
    ipfs add hello.txt
    
    > added QmaVfeg2GM17RLjBs9C4fhpku6uDgrEGUYCTC183VrZaVW hello.txt
    > 17 B / 17 B [=====================================================] 100.00%
    
    ipfs name publish QmaVfeg2GM17RLjBs9C4fhpku6uDgrEGUYCTC183VrZaVW
    ```
    
    同样的， 相同内容会生成相同CID。 所以你的CID应该与我的相同。但是发布后得到的IPNS地址会不同
    
8. 这时再通过相同地址访问，会得到最新的结果
    
    ```bash
    curl https://gateway.ipfs.io/ipns/k51qzi5uqu5dkpmv98v525360py640pa9nqlkt3sudd14utec6t1dnrbdd332q
    > Hello again IPFS
    ```
    
    同样会很慢。 同样可以使用IPFS web console来验证最新文件同容。
    
9. 如果想查看默认公钥关联的CID，可以用如下命令:
    
    ```bash
    ipfs name resolve
    > /ipfs/QmaVfeg2GM17RLjBs9C4fhpku6uDgrEGUYCTC183VrZaVW
    ```
    
    可以见到，此地址对应的CID已经改变了
    
10. 最后，如果想使用不同的公钥来发布IPNS，则可以在 `pulish` 时指定 `--key` 参数。
    
    ```bash
    # 生成新key
    ipfs key gen otherKey
    > k51qzi5uqu5dh32vzx9dv8lpeftjcb4oawylzb8g29jyrrzyui9bp2k9cbaizk
    # 用新公钥发布相同内容
    ipfs name publish --key=otherKey /ipfs/QmaVfeg2GM17RLjBs9C4fhpku6uDgrEGUYCTC183VrZaVW
    # 得到了全新的地址
    > Published to k51qzi5uqu5dh32vzx9dv8lpeftjcb4oawylzb8g29jyrrzyui9bp2k9cbaizk: /ipfs/QmaVfeg2GM17RLjBs9C4fhpku6uDgrEGUYCTC183VrZaVW
    ```
    

## 有哪些IPNS的替代品

IPNS只是在IPFS网络中为可变内容创建稳定地址的方式之一。

[DNSLink](https://docs.ipfs.io/concepts/dnslink/)是另外一种可选方式。 目前它比IPNS更快， 并且其使用了人类可读的方式命名。

其它社区成员在想办法使用区块链来存储通用名称记录。例如 [ENS](https://ens.domains/) ，当然由于这种方式需要以太坊， 所以需要一点点费用。 后续会单独写一篇来介绍
