0G Storage System Deep Dive

0G Storage 是面向大规模数据的去中心化存储系统。应用侧通常通过 SDK 完成文件切分、哈希树构建、链上提交和存储节点上传,并通过索引服务查询文件元数据与可用节点。使用时应把链上状态、索引元数据和节点中的实际数据区分开来。

组件职责

  • Storage Client:面向应用提供上传、下载和状态查询入口。
  • Storage SDK:负责数据切分、根哈希计算、交易提交、节点选择与并发传输。
  • Storage Contract:记录提交、费用和与数据可用性有关的链上状态。
  • Storage Node:保存数据分片并响应上传、下载或证明请求。
  • Storage Indexer:聚合链上与节点元数据,为客户端提供文件定位信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sequenceDiagram
participant App as 应用
participant SDK as Storage SDK
participant Contract as Storage Contract
participant Indexer as Storage Indexer
participant Node as Storage Node
App->>SDK: 提交文件与上传参数
SDK->>SDK: 切分数据并计算根哈希
SDK->>Contract: 提交数据根与费用
Contract-->>SDK: 返回交易哈希
SDK->>Indexer: 查询候选存储节点
Indexer-->>SDK: 返回节点与元数据
SDK->>Node: 并发上传数据分片
Node-->>SDK: 返回确认结果
SDK-->>App: 返回交易哈希与数据根

上传流程

  1. 读取文件并按协议要求切分数据。
  2. 基于数据块构建 Merkle 树,得到可用于完整性校验的根哈希。
  3. 向存储合约提交数据根、大小和费用相关参数。
  4. 等待交易达到应用要求的确认状态。
  5. 从索引服务获得可用节点,并向节点上传实际数据。
  6. 查询上传状态,确认所需副本数已经满足。

原笔记中的关键调用可整理为以下形式:

1
2
3
4
5
6
7
8
9
10
11
12
// Upload the file after the on-chain submission succeeds.
if err := uploader.uploadFile(
ctx,
info,
data,
tree,
opt.ExpectedReplica,
opt.TaskSize,
opt.Method,
); err != nil {
return txHash, tree.Root(), errors.WithMessage(err, "failed to upload file")
}

txHash 只能证明交易已提交或已确认,不能单独证明全部数据已写入目标节点。调用方还应保存 tree.Root(),并检查节点上传状态与目标副本数。

Indexer Metadata Service

索引服务用于根据数据根或交易信息返回文件元数据。典型元数据包括文件大小、分段信息、上传交易、候选节点、最终状态和副本信息。索引器属于便利查询层,客户端对关键状态仍应进行链上或节点侧复核。

1
2
3
4
5
6
7
8
type FileMetadata struct {
Root string
Size uint64
TxHash string
Finalized bool
Replica uint32
StorageURLs []string
}

ParallelDo 并发模型

ParallelDo 可用于并发处理数据块或多个存储节点。实现时需要限制并发数、传播取消信号、聚合首个错误,并确保循环变量不会被错误捕获。

1
2
3
4
5
6
7
8
9
10
11
func ParallelDo(ctx context.Context, jobs []func(context.Context) error, limit int) error {
group, child := errgroup.WithContext(ctx)
group.SetLimit(limit)
for _, job := range jobs {
task := job
group.Go(func() error {
return task(child)
})
}
return group.Wait()
}

失败处理与验证

  • 链上交易失败时,不应继续把该提交视为有效上传。
  • 部分节点上传失败时,应保留成功结果并补足副本,而不是无条件重复全部任务。
  • 下载后应重新计算根哈希,并与提交时的根哈希比较。
  • 超时、限流和节点不可用应采用有上限的指数退避重试。
  • 日志中应记录数据根、交易哈希、节点地址、耗时和失败阶段,但不得记录敏感凭据。
  • SDK 版本与网络配置应固定,升级前应验证合约地址和接口兼容性。