区块链 | IPFS:CID

发布于:2024-05-06 ⋅ 阅读:(29) ⋅ 点赞:(0)

🦊原文:Anatomy of a CID
🦊写在前面:本文属于搬运博客,自己留存学习。



1 CID

在分布式网络中与其他节点交换数据时,我们依赖于内容寻址(而不是中心化网络的位置寻址)来安全地定位和识别数据。

CID 规范起源于 IPFS,现在以 Multiformats 形式存在,并支持包括 IPFS、IPLD、libp2p 和 Filecoin 在内的系统。尽管我们在教程中会分享一些 IPFS 的例子,但本教程实际上是关于 CID 自身的结构,它作为这些分布式信息系统的核心标识符,用于引用内容。

内容标识符(CID)是一种自描述的内容寻址标识符。

自描述是指,标识符能够自行表达其含义或者数据类型。例如 HTTP 中的 URL就是一个自描述的标识符,因为它包含了指向资源的信息(如文件路径),并且基于协议和结构,用户和系统可以理解其含义。

CID 不指示内容存储的位置,而是基于内容本身形成一种地址。CID 的长度取决于内容的哈希,而不是内容本身的大小。由于在 IPFS 中大多数内容都使用 sha2-256 进行哈希处理,因此大多数 CID 都具有相同的大小,即 256 位或称 32 字节。

例如,如果我们在 IPFS 网络中存储 土豚 的图像,它的 CID 将如下所示:

QmcRD4wkPPi6dig81r5sLj9Zm1gDCL4zgpEj9CfuRrGbzF

访问方式:https://ipfs.io/ipfs/QmcRD4wkPPi6dig81r5sLj9Zm1gDCL4zgpEj9CfuRrGbzF

创建 CID 的第一步是使用 加密算法 转换输入的数据。具体来说,是将任意大小的输入映射到固定大小的输出。如下图所示:

在这里插入图片描述
这种转换称为 加密哈希摘要(cryptographic hash digest)或简称为 哈希

个人理解:不管是文本、图片还是视频,它们在计算机中都是以二进制的形式进行存储的,即一个 01 字符串。而哈希函数要做的事,就是将不同文件不同长度的 01 字符串转换为固定长度的 01 字符串。

使用的 加密算法 必须生成具有以下特征的哈希:

  • 确定性:对于任何给定的输入数据,加密算法必须始终产生相同的输出哈希,确保一致性。
  • 抗碰撞性:即使输入数据发生微小变化,也应导致完全不同的哈希值,以保证数据的唯一性。
  • 不可逆性:从哈希值应当无法反推出原始数据,确保数据的隐私和安全。
  • 唯一标识:每份文件都应该有一个独特的哈希值,确保数据的不可篡改性和可追溯性。

当我们使用内容地址去获取数据时,我们可以保证看到数据的预期版本。这与中心化网络上的位置寻址有很大的不同,在中心化网络中,给定地址(URL)处的内容可能会随时间变化。

说明:在去中心化网络中,我们使用 CID 去获取数据;在中心化网络中,我们使用 URL 去获取数据。

哈希并不是 IPFS 所独有的,还有许多其他的哈希算法,如 sha2-256、blake2b、sha3-256 和 sha3-512,以及不再安全的 sha1 和 md5 等。IPFS 默认使用 sha2-256,尽管 CID 支持几乎任何强大的加密哈希算法。



2 CIDv0

随着时间的推移,某些哈希算法可能被证明对于 IPFS 和其他分布式信息系统的内容寻址是不够安全的。

因此,我们的系统需要支持多种加密算法,同时我们应该能够知道是哪种算法被用来生成特定内容的哈希值。为了支持多种哈希算法,我们使用了多重哈希。



2.1 多重哈希

多重哈希(multihash)是一种自描述的哈希,它本身包含元数据,这些元数据描述了其长度以及生成它的加密算法。

多重哈希遵循 TLV 模式,即 type — length — value。也就是说,原始哈希 value 前面附带着所使用的哈希算法的类型 type 和哈希的长度 length。如下图所示:

在这里插入图片描述

  • type:用于生成哈希的加密算法的标识符。例如,sha2-256 的标识符是 18,即十六进制的 12;
  • length:哈希的实际长度。使用 sha2-256 时,它将是 256 位,相当于 32 字节;
  • value:实际的哈希值;

通过查看 multicodec 表,可以获取各个哈希算法对应的标识符。



2.2 基数编码

为了将 CID 表示为紧凑的字符串而不是纯二进制(一串 0 和 1),我们可以使用基数编码(base encoding)。

在 IPFS 最初创建时,它使用 base58btc 编码来生成看起来像这样的 CID:

QmY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU

多重哈希和 base58btc 编码构成了第一版 CID,即如今所说的版本 CIDv0,以 Qm 开头的序列仍然是它的一个显著特征。

然而,随着时间的推移,人们开始对多重哈希是否足够的问题产生了疑问:

  • 我们如何知道使用了什么方法来编码数据?
  • 我们如何知道使用了什么方法来创建 CID 的字符串表示?我们是否会一直使用 base58btc

为了应对这些担忧,有必要对 CID 的下一版本进行改进。

个人理解:在本文中,“基数编码” 是指将二进制转换为可读的字符串,“编码” 是指将文件转换为二进制。



3 CIDv1

CIDv0 使用多重哈希来支持多种哈希函数。这允许我们针对特定内容创建哈希值,且可以选择不同的哈希算法进行处理。有了这一机制,我们日后便能通过这些哈希值来辨识内容。



3.1 多码前缀

但当我们试图阅读数据本身时,如何知道采用了哪种编码方式?数据可能是由 CBOR、Protobuf 或纯 JSON 等编码的。为了解决这个问题,CIDv1 引入了另一个前缀,用以唯一标识所使用的编码方法。

个人理解:哈希是将任意长度的 01 字符串转换为固定长度的 01 字符串,而编码是将非 01 字符串的数据本身转换为 01 字符串。

多码前缀(multicodec prefix)指示了数据使用了哪种编码方式。如下图所示:

在这里插入图片描述

多码支持许多不同类型的编码,每种编码都有自己的简短码缀,可以在 complete 表中查看。

在上述示例中,我们了解到使用 dag-pb 编码的数据是如何被表示在 CID 中的。其中,dag-pb 是众多 IPLD 编码格式中的一种。由于 IPFS 总是选择一种 IPLD 格式来处理其数据,因此 IPFS 生成的 CID 中的多码前缀必然对应于一个 IPLD 编码。

IPLD 是指 Inter Planetary Linked Data,星际链接数据

需要指出的是,多码不仅仅是为了 IPFS 和 IPLD 而设计的,它还是 Multiformats 项目的一部分。这个项目最初是从 IPFS 分离出来的,现在它支持包括我们正在学习的 CID 规范在内的许多其他项目和协议。



3.2 版本前缀

添加多码前缀后,CIDv1 包含以下字段:

<multicodec><multihash-algorithm><multihash-length><multihash-hash>

那么我们如何区分不同版本的 CID 呢?答:版本前缀。如下图所示:

在这里插入图片描述

现在我们的 CID 看起来像这样:

<cid-version><multicodec><multihash>

其中,<cid-version> 代表 CID 的版本是 0 还是 1 。

注意:<multihash> 包含了 <multihash-algorithm><multihash-length><multihash-hash>。特别地,只有 CIDv1 具有 <cid-version>,而 CIDv0 只有 <multihash>



3.3 多基前缀

CIDv1 以二进制的形式为我们提供了以下信息:

<cid-version><multicodec><multihash>

由于二进制 CID 对人类不太友好,因此我们可以将这些二进制 CID 表示为字符串形式,即把二进制数据表示为字符串。

采用的就是前文提到的基数编码技术。

示例:

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

在二进制和字符串形式之间转换数据需要进行基数编码,因此在处理字符串格式的 CID 时,我们需要知道应用于二进制数据的基数编码的类型。

在 CIDv0 中,哈希总是使用 base58btc 进行编码。因此我们可以直接假设 CIDv0 哈希是由 base58btc 编码的。然而,由于环境限制(例如 DNS 名称),我们需要支持其他基数编码的能力。你猜对了,我们可以添加另一个前缀!

多基数前缀(multibase prefix),用于表示 CID 在进行格式转换时使用的基数编码,仅用于 CID 的字符串形式:

Binary:
<cid-version><multicodec><multihash>
String:
<base>base(<cid-version><multicodec><multihash>)

其中,base( ) 应该是指使用某种基数编码方法,对括号中的内容进行基数编码。

让我们来分析两个 CID 的字符串形式示例:

CIDv0:
QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR
CIDv1:
bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

易知第一个 CID 是一个 CIDv0,因为它以 Qm 开头。所有以 Qm 开头的哈希,都可以解释为采用的是 base58btc 基数编码。第二个示例以 b 开头,这是 base32 的基数编码前缀标识符,大多数 IPFS 实现默认使用 base32



4 One hash, multiple CID versions

我们可以将任何的 IPFS CID 粘贴到 CID Inspector 中,以可视化其所有前缀及其代表的内容。



4.1 示例 1:CIDv1

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

通过 CID Inspector 工具查看结果:

在这里插入图片描述

我这里只截取了一部分,请自行查看剩余部分。

我们可以看到工具为我们解析了许多部分:

  • 人类可读 CID:将 CID 的每个部分分解为人类可以轻松阅读的形式;
  • 多基数:基数的标识符,在这个例子中是 b,代表 base32
  • 多编码:编码的标识符,在这个例子中是 0x70,代表 dag-pb,一种 IPLD 格式;
  • 多哈希:将多哈希分解为所使用的哈希算法、哈希的长度、内容哈希本身。

其中,18 是哈希算法 sha2-256 的代码,哈希的长度是 256 位即 32 字节,内容哈希本身是十六进制的摘要。

从 “人类可读 CID” 中,我们可以看到在添加 CIDv1 的前缀之前,内容的原始哈希是:

C3C4733EC8AFFD06CF9E9FF50FFC6BCD2EC85A6170004BB709669C31DE94391A


4.2 示例 2:CIDv0

QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR

通过 CID Inspector 工具查看结果:

在这里插入图片描述

在 CIDv0 的结果中,多基数和多编码都显示为 “implicit”。这是由于 CIDv0 没有这两个前缀,因此它们被分别默认为 base58btcdag-pb

在原文的截图中,两个前缀都显示的是 “implicit”,但是我查看到的多编码并不是 “implicit”。

在 CIDV1(Base32) 标签下,我们看到:

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

这与第一个示例中的 CID 完全相同!CID 检查器为我们提供了从 CIDv0 到 CIDv1 的转换。

注意,CIDv0 示例与 CIDv1 示例中的 “人类可读 CID” 的末尾完全相同:

C3C4733EC8AFFD06CF9E9FF50FFC6BCD2EC85A6170004BB709669C31DE94391A

这是因为这两个 CID 指向相同的内容,即本质上两个 CID 代表的是相同的哈希。



4.3 转换 CID 的版本

我们可以将任何的 CIDv0 转换为 CIDv1,但不能将任何的 CIDv1 转换为 CIDv0 。这是由于 CIDv1 支持多编码和多基数,而 CIDv0 不支持。事实上,只有具有以下属性的 CIDv1 可以转换为 CIDv0:

  • 多基数 = base58btc
  • 多编码 = dag-pb
  • 多哈希算法 = sha2-256
  • 多哈希长度 = 32 字节

为了测试这个理论,你可以查看我们亲爱的 土豚 图片,它托管在 IPFS 网络上:

https://ipfs.io/ipfs/QmcRD4wkPPi6dig81r5sLj9Zm1gDCL4zgpEj9CfuRrGbzF

首先,从 URL 末尾复制 CID:

QmcRD4wkPPi6dig81r5sLj9Zm1gDCL4zgpEj9CfuRrGbzF

然后,将 CID 粘贴到 CID Inspector 工具中,并在页面底部找到等效的 CIDv1 值:

bafybeigrf2dwtpjkiovnigysyto3d55opf6qkdikx6d65onrqnfzwgdkfa

最后,将原始 URL 中的 CID 替换为转换后的 CID:

https://ipfs.io/ipfs/bafybeigrf2dwtpjkiovnigysyto3d55opf6qkdikx6d65onrqnfzwgdkfa

你应该看到的是相同的土豚图片。