第3章:内容标识符(CID)

第3章:内容标识符(CID)

3.1 CID的结构和格式

CID(Content Identifier,内容标识符)是IPFS中用于唯一、无歧义地标识内容的核心机制。它是一种自描述(self-describing) 的标识符,内嵌了版本、数据编码格式及哈希算法等元信息,无需外部上下文即可解析其含义。其标准结构为:

<cid-version><multicodec><multihash>

CID v1 的组成字段说明

  • 版本号(CID version):标识CID的语法与语义规范(如 v0v1),影响后续字段的解析方式;
  • 多编解码器(Multicodec):标识原始数据的序列化格式(如 dag-pb 表示 IPLD DAG 的 Protocol Buffer 编码,raw 表示未封装的原始字节流);
  • 多哈希(Multihash):包含哈希算法类型、摘要长度及实际哈希值三部分,确保哈希计算可验证、可移植。

💡 提示:CID 不是“地址”,也不隐含存储位置或网络路径——它仅承诺“内容即所指”。无论内容存于本地节点、远程网关,还是离线介质中,只要字节一致,CID 就完全相同。

3.2 CID版本(v0 vs v1)

CID v0(遗留格式)

QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgdfhz6EWTT22cP
```  
- 以 `Qm` 开头(Base58BTC 编码的固定前缀);  
- 隐式绑定 `dag-pb` 编解码器与 SHA-256 哈希算法;  
- 本质是 CID v1 的特例(`v0` ≡ `v1 + codec=dag-pb + hash=sha2-256`),但缺乏显式声明能力。

**CID v1(当前推荐格式)**:  

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi ```

  • b 开头(Base32 编码,兼容 URL 和 DNS 使用场景);
  • 显式携带版本、编解码器和多哈希字段,支持任意组合(如 cbor + blake2b-256);
  • 向前兼容 v0:所有 v0 CID 均可无损转换为 v1 形式(反之则需约定默认参数);
  • 是 IPFS v0.5+ 及所有现代 IPLD 工具链的默认输出格式。

⚠️ 注意:v0 CID 已被标记为废弃(deprecated)。新项目应始终使用 v1,并在配置中显式指定 --cid-version=1(CLI)或 version: 1(API 调用)。混合使用 v0/v1 可能导致跨工具链解析失败或语义误解。

3.3 多哈希(Multihash)算法

多哈希(Multihash)是一种轻量级、自描述的哈希值封装格式,旨在解决“哈希算法不可知”问题。其二进制结构严格定义为:

<hash-function-code><digest-length><digest-value>

其中:

  • hash-function-code:单字节(或双字节)算法标识符(如 0x12 表示 SHA-256);
  • digest-length:单字节,表示后续摘要值的字节数(非位数);
  • digest-value:定长原始哈希摘要(大端序,无填充)。

常见哈希算法及其 Multihash 代码

算法Multihash 代码(十六进制)摘要长度特点说明
SHA-2560x1232 字节广泛兼容,IPFS 默认选项
SHA3-5120x1464 字节抗量子增强,但计算开销较大
Blake2b-2560xb22032 字节性能优异,IPFS 推荐替代方案

💡 提示:Multihash 的设计使同一内容可同时拥有多个有效 CID(例如 bafy...sha2-256bafy...blake2b-256),便于迁移算法或满足合规性要求,而无需修改内容本身。

3.4 CID的生成和使用

生成 CID 的典型方式(注意:输入必须是字节流,字符串需明确编码):

// ✅ 正确:显式指定 UTF-8 编码,避免平台默认差异
const { CID } = require('ipfs-http-client');
const uint8Array = new TextEncoder().encode('Hello IPFS');

async function generateCID() {
  const cid = await CID.of(uint8Array);
  console.log(cid.toString()); // 输出 v1 Base32 格式 CID
  // 示例: bafkreif2p3qp4y2lv3txvij4nvqgg4ugypn5q5m5f5q5m5f5q5m5f5q5m
}
# ✅ 正确:使用 --cid-version=1 强制生成 v1,--only-hash 跳过上传
echo -n "Hello IPFS" | ipfs add --cid-version=1 --only-hash --quieter
# 输出: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

# ❌ 错误示例(不推荐):省略 --cid-version 将默认生成 v0
echo -n "Hello IPFS" | ipfs add --only-hash  # 输出 Qm...(v0)

⚠️ 注意ipfs add 默认对输入内容进行 DAG 封装(dag-pb),因此生成的 CID 对应的是封装后的节点,而非原始字节。若需原始内容 CID(如纯文本哈希),应使用 --raw-leaves 或直接调用 CID.of()

3.5 实际案例分析

CID 的核心价值在于内容可验证性(content verifiability):给定一个 CID,任何用户均可独立复现哈希计算,从而确认所获取数据的完整性与真实性,无需信任传输通道或服务提供方。

以下是一个端到端验证流程示例:

import ipfshttpclient

# 连接到本地 IPFS 节点(确保 API 服务已启用)
client = ipfshttpclient.connect('/ip4/127.0.0.1/tcp/5001/http')

# 步骤 1:添加文件并获取其 CID(v1 格式)
result = client.add('hello.txt', cid_version=1)
cid = result['Hash']  # 如: bafybeif2p3qp4y2lv3txvij4nvqgg4ugypn5q5m5f5q5m5f5q5m5f5q5m
print(f"文件 CID: {cid}")

# 步骤 2:通过 CID 获取内容(自动校验哈希)
try:
    content = client.cat(cid)
    print(f"文件内容: {content.decode('utf-8')}")
except ipfshttpclient.exceptions.TimeoutError:
    print("❌ 超时:节点未找到该内容(可能尚未传播或未持久化)")
except ipfshttpclient.exceptions.ErrorResponse as e:
    print(f"❌ 验证失败:{e}")  # 例如哈希不匹配将触发此异常

💡 提示client.cat() 在返回数据前会自动执行哈希校验——若下载内容与 CID 中声明的多哈希不一致,请求将直接失败并抛出异常。这是 IPFS 内置的安全保障,开发者无需手动实现校验逻辑。

← 返回目录