- 区块链
- Web3
【免费下载链接】grin
Minimal implementation of the Mimblewimble protocol.
本文以 Grin(Mimblewimble 协议的极简实现)官方的状态与存储文档为主体,结合仓库源码深入讲解 Grin 区块链的全量状态由哪些数据构成、如何通过四重校验保证状态可信、输出 MMR / 范围证明 MMR / 内核 MMR 的哈希内容与剪枝原理,以及底层基于内存映射的追加式文件存储与压缩(compaction)机制。读完本文,你将能理解 Grin 节点在"最重链(most worked chain)"锚定下如何组织、校验并持久化全部链状态,并能在源码中定位对应的实现与测试。
Grin 的状态构成
Grin 的链状态与比特币式的"UTXO 数据库 + 区块数据"不同,它把交易历史的可验证摘要集中组织在四类数据中。根据官方文档 doc/translations/state_JP.md(英文原版见 doc/state.md),一条 Grin 链的全量状态由以下数据组成:
- 全部未花费输出(UTXO)集合;
- 每个输出对应的范围证明(range proof);
- 全部交易内核(transaction kernel);
- 为上述三类数据各维护一棵 MMR(Merkle Mountain Range)——唯一的例外是:输出 MMR 中包含的是"全部"输出的哈希,而不仅仅是未花费的 UTXO。
此外,链中所有区块头都必须以有效的 PoW(工作量证明)锚定上述状态,即状态始终对应"工作量最大"的那条链。这是 Grin 状态一致性的顶层约束:只有最重链的区块头里所携带的 MMR 根,才被认定为当前有效状态。
文档还特别指出一个重要的存储优化结论:一旦每个范围证明都被验证过、且所有内核承诺的总和已经计算完毕,范围证明与内核对于节点的正常运行就不再是严格必需的。这正是后续"剪枝(pruning)"得以成立的依据——它们只服务于"验证",而非"当前状态"本身。
从源码结构看,这三类数据与三棵 MMR 的对应关系被明确实现在 chain/src/txhashset/txhashset.rs 中:存储根目录下分别使用output、rangeproof、kernel三个子目录,加上txhashset顶层目录,共同构成完整的状态存储单元(源码常量TXHASHSET_SUBDIR、OUTPUT_SUBDIR、RANGE_PROOF_SUBDIR、KERNEL_SUBDIR)。
状态的验证:四项核心校验
文档指出,只要拥有"完整的 Grin 状态",就能对状态本身执行以下四项验证,从而在不依赖区块历史全文的情况下证明整条链是合法的:
内核签名有效性:每个内核的签名必须相对于其承诺(commitment,即公开密钥)有效。这证明该内核是合法构造的。在源码中,内核(core/src/core/transaction.rs)由
features、excess(承诺)和excess_sig(签名)组成,签名验证在 chain/src/txhashset/txhashset.rs 的verify_kernel_signatures中以每 5000 个一批(KERNEL_BATCH_SIZE)批量完成。总供应量守恒:所有内核承诺之和,必须等于"所有 UTXO 承诺之和 − 总供应量(total supply)"。这证明了内核与输出承诺全部有效,且没有凭空铸造出任何意外的币。其实现位于 core/src/core/committed.rs 的
verify_kernel_sums:分别计算utxo_sum与"内核 excess 之和 + kernel offset"(kernel_sum_plus_offset),两者不相等则返回KernelSumMismatch错误。MMR 成员与根正确性:所有 UTXO、范围证明、内核的哈希都必须存在于各自对应的 MMR 中,且这些 MMR 能够哈希出正确的根。对应源码为 chain/src/txhashset/txhashset.rs 的
validate_mmrs(对三棵 MMR 逐一调用validate())以及validate_roots(将当前 MMR 根与最新区块头中记录的根比对)。最重链锚定:在某一时刻已知的、工作量最大的区块头必须包含这 3 棵 MMR 的根。这一条同时验证了 MMR 的正确性,并证明"全部状态是由最重链产生的",防止攻击者用一条低工作量侧链的状态冒充主链状态。
这四项校验被完整地组织在TxHashSet::validate入口中(chain/src/txhashset/txhashset.rs):先做 MMR 内部校验、根校验、尺寸校验(validate_sizes比对 header 中记录的output_mmr_size、kernel_mmr_size等),再计算内核总和,最后在非"快速验证(fast_validation)"模式下逐批验证全部范围证明(verify_rangeproofs,默认每 1000 个一批)与内核签名。
MMR 与剪枝:叶子节点的哈希内容
每个 MMR 的叶子节点由"位置信息 + 具体数据哈希"构成。官方文档明确了三棵 MMR 各自的叶子哈希来源:
| MMR | 叶子哈希内容 |
|---|---|
| 输出 MMR(output MMR) | 特征字段(feature field)与创世块以来所有输出的承诺(commitment)的哈希 |
| 范围证明 MMR(rangeproof MMR) | 每个范围证明的完整数据(whole range proof data)的哈希 |
| 内核 MMR(kernel MMR) | 每个内核的全部字段的哈希:特征(feature)、手续费(fee)、锁高度(lock height)、剩余承诺(excess commitment)、剩余签名(excess signature) |
对照源码可以印证这些字段:输出由"特征 + 承诺"构成(OutputIdentifier,见 core/src/core/transaction.rs),其上的范围证明存放在Output.proof字段(core/src/core/transaction.rs);内核的五个字段在TxKernel结构体(core/src/core/transaction.rs)中均有对应成员,KernelFeatures枚举(core/src/core/transaction.rs)则定义了 Plain(普通内核,带手续费)、Coinbase(coinbase 内核)、HeightLocked(锁定高度内核)、NoRecentDuplicate(NRD 相对高度约束内核)等特征类型。
文档强调了一个容易被忽略的实现约束:所有输出、范围证明和内核,都必须按照其在各自区块中出现的顺序,被追加进对应的 MMR(这也是区块数据必须排序的原因)。从代码看,Extension结构体(chain/src/txhashset/txhashset.rs)同时持有output_pmmr、rproof_pmmr、kernel_pmmr三个 PMMR 实例,在新块应用时按同一顺序写入三棵 MMR,保证了三者的位置一一对应——这是后续"删除日志位置与 MMR 节点位置天然对齐"这一设计能够成立的根本前提。
剪枝规则:当某个输出被花费后,其承诺与范围证明数据都可以被删除;与此同时,对应的输出 MMR 与范围证明 MMR 也可以被剪枝。注意内核 MMR 与内核数据不在剪枝范围内——因为内核承担着总供应量守恒的验证职责,且文档已指出内核验证完毕后虽非必需,但剪枝的收益主要来自体积较大的范围证明。
状态存储:追加式文件 + 内存映射 + 删除日志
文档对 Grin 状态存储的概括非常精炼:输出、范围证明、内核的数据存储是简单的"追加式(append-only)文件 + 内存映射(memory-mapped)数据访问"。具体机制分三步:
- 追加写入:新数据只允许追加到文件末尾,天然适配 MMR 与区块的单调增长特性;
- 删除日志:当输出被花费时,不会立即物理删除文件内容,而是由"删除日志(remove log)"记录哪些位置可以被移除。由于所有数据都按同一顺序插入,这些位置与 MMR 节点位置完美对齐;
- 压缩(compaction):当删除日志变得过大时,周期性执行重写——把文件中"尚未被删除"的部分重新写出(依然是追加式),并清空删除日志,从而把对应文件压缩紧凑。
在源码中,这一设计体现在两层:
- 追加式文件:store/src/types.rs 定义了
AppendOnlyFile<T>,其核心成员除了文件句柄外,还有一个memmap::Mmap用于读取时的内存映射访问,以及一个未落盘字节缓冲buffer——写入先进入缓冲、flush 时再追加到文件,保证读性能与写吞吐。 - 可剪枝 PMMR 后端:store/src/pmmr.rs 的
PMMRBackend<T>把文档中的抽象落地为 4 个配套文件(源码常量见 store/src/pmmr.rs):pmmr_hash.bin:哈希文件(固定 32 字节/条,追加式);pmmr_data.bin:叶子数据文件(定长或变长元素,变长时辅以pmmr_size.bin记录每条长度);pmmr_leaf.bin:LeafSet,即"删除日志"的现代实现——一个基于 roaring bitmap 的紧凑位图,记录当前未剪枝的叶子位置(见 store/src/leaf_set.rs);pmmr_prun.bin:PruneList,记录被剪枝(pruned/compacted)的根节点位置,用于在读文件时做位置平移(shift)换算(见 store/src/prune_list.rs)。
文档所说"删除日志变小时不动作、变大时重写压缩"的过程,对应PMMRBackend::check_compact(store/src/pmmr.rs):根据 cutoff 位置与回滚位图计算待移除的叶子与节点位置,将哈希文件和数据文件写出临时剪枝副本(write_tmp_pruned),再用replace_with_tmp原子替换原文件,最后更新PruneList并落盘。上层由TxHashSet::compact(chain/src/txhashset/txhashset.rs)调度,对输出与范围证明两个可剪枝后端执行同样的压缩流程。
状态目录结构:真实落盘布局
将上面的机制落到磁盘上,一个 Grin 节点的txhashset目录结构(源码中的文件清单见 chain/src/txhashset/txhashset.rs)大致如下:
txhashset/ ├── output/ │ ├── pmmr_hash.bin # 输出 MMR 的节点哈希(追加式) │ ├── pmmr_data.bin # 输出(feature + commitment)数据 │ ├── pmmr_leaf.bin # 未剪枝叶子位置位图(LeafSet) │ └── pmmr_prun.bin # 已剪枝根位置(PruneList) ├── rangeproof/ │ ├── pmmr_hash.bin │ ├── pmmr_data.bin # 范围证明数据 │ ├── pmmr_leaf.bin │ └── pmmr_prun.bin └── kernel/ ├── pmmr_data.bin ├── pmmr_hash.bin └── pmmr_size.bin # 内核为变长元素时的大小索引仓库的测试数据目录 chain/tests/test_data/chain_compacted 与 chain/tests/test_data/chain_raw 正好提供了上述三种 MMR 文件布局的完整样例,可作为理解目录结构的直观参考(前者是压缩后的状态,后者是未压缩的原始状态)。
状态同步与打包:txhashset 快照
与存储密切相关的还有状态同步机制:在 P2P 快速同步(state sync)场景下,节点需要把某个高度附近的三棵 MMR 状态整体打包传输。源码实现了这一能力:
- 打包:
zip_read(chain/src/txhashset/txhashset.rs)将txhashset目录按file_list白名单复制进临时目录并打成txhashset_snapshot_<hash>.zip;快照文件保留最近 24 小时(代码中按 24 小时清理旧 zip)。 - 解包:
zip_write(chain/src/txhashset/txhashset.rs)只接受file_list明确列出的文件,白名单外的路径一律忽略,从而保证状态包内容的严格可控。 - 替换:
txhashset_replace(chain/src/txhashset/txhashset.rs)在解包后把新状态原子地替换旧状态目录。
注意快照白名单中包含了"针对特定 header 回卷(rewound)"的output/pmmr_leaf.bin.<hash>与rangeproof/pmmr_leaf.bin.<hash>文件——这正对应文档所述的"删除日志与 MMR 位置对齐"以及LeafSet::copy_snapshot在 store/src/pmmr.rs 中的回卷恢复逻辑。
小结
Grin 的状态与存储设计可以概括为一句话:用"UTXO 集合 + 范围证明 + 内核 + 三棵 MMR"承载全部可验证状态,用"最重链区块头中的 MMR 根"锚定其唯一性,用"追加式文件 + 内存映射 + 删除日志 + 周期压缩"实现高效、可剪枝的持久化。官方文档 doc/state.md(以及 日文版、韩文版、简体中文版)给出了这一设计的完整描述,而 chain/src/txhashset/txhashset.rs、store/src/pmmr.rs、core/src/core/committed.rs 等源码则提供了每一处机制的精确实现证据。理解这套"状态 + 验证 + 存储"的三角结构,是深入 Grin 节点实现、参与交易池与链同步开发的基础。
- 区块链
- Web3
【免费下载链接】grin
Minimal implementation of the Mimblewimble protocol.
相关推荐
Grin 的 Merkle 结构与 sum-MMR 设计:面向剪枝的 Mimblewimble 状态承诺机制
Grin 的 Merkle 结构与 sum MMR 设计:面向剪枝的 Mimblewimble 状态承诺机制 Mimblewimble 协议的核心目标之一是:用
区块链Web3Grin 的 Merkle 结构与 Sum-MMR:可修剪的 UTXO 状态承诺机制
Grin 的 Merkle 结构与 Sum MMR:可修剪的 UTXO 状态承诺机制 Grin 基于 Mimblewimble 协议设计,其核心诉求是让验证者仅
区块链Web3Grin 区块链数据修剪(Pruning)完全指南:Mimblewimble 状态压缩原理与实现
Grin 区块链数据修剪(Pruning)完全指南:Mimblewimble 状态压缩原理与实现 本文围绕 Grin(Mimblewimble 协议的极简实现)
区块链Web3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考