English

我们如何测试基于 GCS 与 Filecoin 的可验证存储

协议 · · Kay (工程师) · 7 分钟阅读

一份 GCS 热副本、两份 Filecoin 冷归档,加上每次读取都必须对上的链上指纹。已在 Calibration 测试网验证,尚未进入生产。

采购训练数据的团队,会在交易完成后很久还在使用这些文件。那时,原来的下载地址可能已经失效,后台服务也可能经历过重启。团队需要完整取回文件,并确认内容没有被替换或损坏。如果没有任何校验,错误的文件可能被交付出去,而且无人察觉。

我们为这种情况构建并测试了一条存储路径。它会把每个交付的数据版本保存在三个地方:一份 Google Cloud Storage(GCS)热副本,以及两个相互独立的 Filecoin 存储提供者(Provider)上的冷归档。链上内容指纹 PieceCID 会在返回之前核对每一份副本。

当前状态: 本文结果来自 Filecoin Calibration 测试网中的测试数据和受控故障注入。基于 XnY 构建的首个应用 Humanbased 目前只使用 GCS 存储生产文件;Filecoin 冷归档和按授权交付解密密钥尚未作为生产环境的自助能力开放,因此客户数据尚未自动获得这套保护。本文任何结果都不是生产环境的吞吐、延迟或容量数据。

一份数据版本文件完成交付GCS Origin热存储 · 日常读取链上内容指纹登记指纹,不保存大文件Filecoin Provider A冷归档副本Filecoin Provider B独立冷归档副本取回后核对
图 1 · 实线是文件写入路径,虚线是内容核对关系。链上登记用于验证的指纹,不承载大文件。

三层存储,各司其职

指纹能发现篡改,却不能阻止篡改。无论从哪个地址取回,副本都必须与最初的哈希一致。⁴ 指纹也不能恢复已经丢失的字节,所以需要两份归档。

把文件整理成可恢复的数据包

恢复一份文件不只需要字节本身,系统还要知道这些字节属于哪个数据版本,以及应该如何重新组合。每份归档都是一个数据包,包含外层索引、版本快照、记录分块顺序、大小和摘要的加密清单,以及数量随文件大小变化的加密数据分块。测试使用的数据包共有 6 个组件,其中包括 3 个加密数据分块。

每个组件都写入两个 Provider,因此完整归档的链上存储记录数是组件数的两倍,测试数据包对应 12 条。一笔交易可以批量登记其中多条。

外层索引归档包入口版本快照绑定数据版本加密清单顺序、大小与摘要加密数据分块 × 3保存实际数据内容Provider A每个组件保存 1 份Provider B每个组件保存 1 份
图 2 · 在这个 6 组件的测试数据包中,每个组件在每个 Provider 各保存 1 份,对应 12 组“组件 × Provider”存储关系。

重启时不重复上传

Provider 可能已经接收上传,服务却在保存结果之前退出。只看本地数据库的重启进程会再上传一次,为重复副本付费。实际做法是:每次重启都先核对本地进度和链上记录,再继续处理。只有每个组件都在两个 Provider 上各有一份副本后,系统才写下完成凭证,因此缺少副本的数据包永远不会被标记为已归档。

无法验证就明确拒绝

读取依次尝试 Origin、首选 Provider 和备用 Provider。Provider 返回的字节在通过三项校验之前,不会交给用户:

读取请求请求文件GCS Origin优先读取Provider A首选归档副本Provider B备用归档副本不可用不可用统一核对内容长度 + SHA-256 + PieceCID一致 · 交付文件无法验证 · 明确失败
图 3 · 来源可以切换,内容验证规则不变。系统只有在长度、SHA-256 和 PieceCID 全部一致时才交付文件。

任何一项校验失败,系统都会拒绝这份副本并尝试下一个来源;所有来源都失败时返回 502。服务只有在找到并验证一份副本之后,才会交付文件。


实验参考

测试环境

我们在 Filecoin 的 Calibration 测试网络上连接两个 Provider,完整走一遍流程:写入两份、分别读回,再核对两份内容。文件分为四档:1 MiB、16 MiB、64 MiB 和约 1 GiB(实际为 1,016 MiB)。样本由测试场景预先选择,用于覆盖不同文件大小和并发情况,不代表生产流量分布。

A1. 双副本写入和读回

结果:完成 97 次运行。每次完成的运行都通过了两份副本的长度和 SHA-256 校验,1,016 MiB 档的 6 次运行全部完成。

1 MiB16 MiB64 MiB约 1 GiB21 次49 次21 次6 次每次均完成双副本写入,并从两份副本读回校验
图 4 · 16 MiB 样本最多,因为并发测试统一使用了这一档文件。

A2. 并发归档

结果:8/8 和 16/16,共 24 个文件。两轮都使用 16 MiB 文件,所以这一档的运行次数最多。一轮同时归档 8 个不同文件,另一轮同时归档 16 个。每个文件都写入两个 Provider,并从两份副本读回校验,说明在这些条件下,同时到来的归档任务仍能按文件分别完成双副本和内容校验。

A3. 1,016 MiB 文件的内存和时间开销

结果:全程 16 分 57.1 秒,Go heap 峰值 258.1 MiB。

客户端一边读取文件,一边上传并计算内容指纹。在一笔由本地客户端发起、连接 Calibration 网络的单文件测试中,Go heap 峰值为 258.1 MiB,说明整份文件从未同时放进 Go heap。这个数字不包括进程的其他内存占用;单次运行也不能外推到更大文件或 TB 级容量。

第二份副本在 482.0 秒时存好,两条链上记录在 762.8 秒(12 分 42.8 秒)时确认。随后从两个 Provider 顺序读回并校验,又用了 4 分 14.3 秒。

第二份副本完成 · 482.0s两份记录确认 · 762.8s传输并完成第二份副本等待两份记录确认双副本顺序读回0m16m 57.1s
图 5 · 归档与确认 12 分 42.8 秒,双副本顺序读回 4 分 14.3 秒。生产环境的速度未在这组测试中测量。

A4. 最小尾部数据块

结果:127 字节的最后一个数据块完成归档,12 个 PieceCID 全部一致。

文件按固定大小分块时,最后可能剩下一个很小的尾部数据块;加密后不足 127 字节的对象,Filecoin 无法计算 PieceCID。一个无法存储的尾部,就可能让整份归档无法完成。我们测试了最小可独立存储的尾部:99 字节明文,加上 28 字节固定加密开销。6 个归档组件都能从选定副本恢复。

更短的尾部会并入前一个数据分块,这部分逻辑目前只在受控测试中验证。

A5. 主动重启

结果:两个 Provider 各新增 6 个 Piece,重复为 0。

我们使用计划用于生产的同一套恢复逻辑,在 5 个位置主动退出:刚认领任务、上传调用返回后、第一份副本记录后、一个组件完成后,以及 12 份副本完成但尚未写下完成凭证时。

在“Provider 已接收上传、本地数据库还没有记录”的窗口,对账流程从链上找回两条存储记录并沿用原来的结果,没有再次上传。

1 个归档包6 个组件5 处主动中断每次重启先对账再继续未完成的步骤Provider A预期 +6 · 实际 +6Provider B预期 +6 · 实际 +6重复副本 0
图 6 · 完成全部 5 个退出点后,两个 Provider 都只新增了预期的 6 个 Piece。

A6. 读取切换与故障注入

结果:备用 Provider 以 200 交付了文件;损坏或无法验证的副本都被拒绝。

我们依次断开 Origin、首选 Provider 和全部三个来源。Origin 不可用、首选 Provider 被阻断时,备用 Provider 的副本被取回、校验,并以 200 返回。我们还让 Provider 返回错误、截断和超长内容,每份错误副本都被拒绝,只有所有来源都失败时才返回 502。真实 Provider 在 Calibration 上没有自行返回过损坏内容。

第一次断开 Origin 时,37 项检查中有 18 项返回 502,但文件本身完好。查询链状态的 RPC 节点交替返回两个相差约 52 个区块的已确认视图,较旧的视图还看不到新的存储记录。两个视图都更新之后,同一组检查全部通过。这说明系统会安全失败,但没有测量切换速度或生产可用率。

参考资料

  1. XnY Protocol Docs — Architecture:链上与链下数据的职责边界。docs.xny.ai/concepts/core-systems/architecture
  2. Google Cloud Documentation — Cloud Storage overview:GCS 的对象存储模型与常见使用场景。docs.cloud.google.com/storage/docs/introduction
  3. Filecoin Docs — Glossary:CID、存储提供者和存储证明等术语的官方定义。docs.filecoin.io/reference/general/glossary
  4. XnY Protocol Docs — Storage & Serving:存储后端、内容哈希与取回地址的关系,以及 GCS 与 Filecoin 的定位。docs.xny.ai/concepts/core-systems/storage-compute-serving
  5. XnY Protocol Docs — Data Assembly:数据版本在链上记录的承诺,以及实际内容留在链下的设计。docs.xny.ai/concepts/core-systems/data-assembly

相关文章