☰
Grin 的密码学基石:MimbleWimble 协议原文精读与源码级实现对照
2026/10/14 3:12:46 网站建设 项目流程
  • 区块链
  • Web3

【免费下载链接】grin

Minimal implementation of the Mimblewimble protocol.

项目地址:https://gitcode.com/gh_mirrors/gr/grin
点击查看免费下载

Grin 是 MimbleWimble 协议(MWM)的首个生产级实现,其"隐私性、可扩展性、可去中心化验证"三大特性的全部设计源头,都来自仓库中收录的这份协议原文 doc/translations/mimblewimble.md。本文以这份原文为骨架,逐节拆解 MimbleWimble 的密码学机制——Pedersen 承诺与范围证明、基于 excess 签名的非交互交易聚合(OWAS)、去边界化的 IoK 块格式,以及跨块 cut-through 后"整条链验证为一笔大交易"的核心思想,并结合 Grin 仓库中 core/src/core/block.rs、core/src/core/transaction.rs、core/src/core/committed.rs 的实际实现,把论文中的每个抽象落回到 Rust 数据结构与验证函数上。

一、MimbleWimble 要解决什么问题

原文开篇指出 Bitcoin 面临的根本矛盾:任何人都可以用密码学手段验证整个系统状态,但前提是必须下载并回放链上全部约 1.5 亿笔交易,才能推导出仅约 400 万个未花费输出(unspent outputs)。绝大多数交易产生的输出在下一笔交易就被销毁了,却必须被完整保留、完整回放。

与此同时,Bitcoin 交易在密码学上是原子的——每笔交易的输入输出都清晰可见,由此形成的"交易图"(transaction graph)暴露了大量信息,被各类监控公司分析利用,使系统变得不隐私甚至危险。

原文梳理了此前的改进路线:

  • 机密交易(Confidential Transactions, CT):Greg Maxwell 提出加密金额,使交易图"无面目化",同时仍可验证金额加和正确;
  • CoinJoin:Maxwell 的交互式交易合并方案,混淆交易图,但要求用户在线交互;
  • van Saberhagen 签名(CryptoNote 路线):无需交互地模糊交易入口,但要求每个输出永久保留(无法判定它何时真正被花费);
  • OWAS(One-Way Aggregate Signatures):Yuan Horas Mouton 使交易可自由合并、消除交互性,代价是依赖配基于(pairing-based)密码学,更慢且更难被信任。

这些方案都优秀,但机密交易的每个输出都要附带数千字节的证明,van Saberhagen 签名要求输出永久存储,问题"太多数据"反而被放大。MimbleWimble 的目标正是在不使用配基于密码学、不引入新假设、仅用 Bitcoin 式的离散对数签名的前提下,同时隐藏金额、模糊交易图,并且让用户验证整条链所需的空间比 Bitcoin 更少。

二、机密交易与 OWAS:MimbleWimble 的密码学骨架

2.1 去掉 Bitcoin Script,用承诺值本身授权花费

原文第一刀是删除 Bitcoin Script:通用脚本太强,使得交易合并无法在一般情况下工作。取而代之的是"略作改造的机密交易"——它既能授权输出花费,又能构造无需交互的合并交易,且事实上等价于 OWAS。

金额编码沿用 Pedersen 承诺:

C = r*G + v*H

其中 C 是 Pedersen 承诺,G 与 H 是"nothing-up-my-sleeve"(无可操纵的)椭圆曲线群生成元,v 是金额,r 是秘密随机盲化因子(blinding key)。每个输出还附带一个范围证明(rangeproof),证明 v ∈ [0, 2^64],防止用户利用盲化制造溢出攻击。

交易验证规则:把所有输出承诺相加,加上显式给出的手续费f*H,再减去所有输入承诺,结果必须为 0——这证明整体上没有任何金额被凭空创造或销毁。

2.2 让 r 值成为密钥:excess 签名机制

关键洞察是:构造一笔这样的交易必须知道所有承诺中 r 值的总和,因此r 值(及其和)天然具有私钥的地位。若强制承诺总和为 0,发送方会知道自己的所有 r 值之和,从而反推出接收方的 r 值之和为它的相反数,所有权无法转移。因此 MimbleWimble 允许交易承诺总和为非零值k*G,并要求用 k 作为私钥对空字符串签名,以此证明承诺的金额分量之和为零。交易可以携带任意多个 k*G 值,每个都附带签名,验证时把它们全部相加。

原文给出的发送方/接收方构造交易的仪式(ritual)为四步:

  1. 双方约定发送金额,记为 b;
  2. 发送方用全部输入与找零输出构造交易,并把总盲化因子(找零的 r 值减去输入的 r 值)连同交易一起交给接收方。此时承诺总和为r*G - b*H;
  3. 接收方为自己持有的输出随机选取 r 值与金额(金额之和为 b 减去手续费),加入交易并附带范围证明。此时承诺总和变为k*G - fee*H,k 只有接收方知道;
  4. 接收方用 k 签名并附上显式手续费。完成。

这一构造天然支持 OWAS:假设两笔交易分别有盈余k1*G与k2*G及对应签名,把两笔交易的输入列表、输出列表、两个 k*G 值直接拼在一起,仍然是一笔有效交易,且从合并结果中无法分辨哪个输入/输出来自哪笔原始交易。

2.3 块格式:IoK 列表 + 强制排序

基于"交易边界无关紧要",原文把 Bitcoin 的块格式改造为四类聚合信息:

  1. 新币的显式金额(块奖励或侧链 peg-in,及其所需的其他数据);
  2. 所有交易的输入(inputs);
  3. 所有交易的输出(outputs);
  4. 所有交易的 excessk*G值。

三个要点:各列表内部按什么顺序无所谓,因为交易边界本来就不存在;第 2、3、4 列表必须按字典序(原文称 alphabetical order)编码——排序检查廉价,且能防止造块者通过排列顺序泄露原始交易信息;输出改为按其哈希标识,而不是它在某笔交易中的位置,同时禁止同一时刻存在两个相等的未花费输出。

2.4 源码对照:Grin 如何落地这套块格式

Grin 的块结构在 core/src/core/block.rs 中精确对应了原文设计:

/// A block as expressed in the Mimblewimble protocol. The reward is /// non-explicit, assumed to be deducible from block height (similar to /// bitcoin's schedule) and expressed as a global transaction fee (added v.H), /// additive to the total of fees ever collected. pub struct Block { /// The header with metadata and commitments to the rest of the data pub header: BlockHeader, /// The body - inputs/outputs/kernels pub body: TransactionBody, }

注意注释点出的一个与原文略有差异的工程决策:Grin 的块奖励不是显式金额列表,而是从块高推导(类似 Bitcoin 的奖励表),手续费则以全局v*H的方式累加进总费用——这正是原文"显式输入金额乘以 H"思想在纯主链场景下的简化。

正文即 TransactionBody,与原文三个列表一一对应:

/// TransactionBody is a common abstraction for transaction and block pub struct TransactionBody { /// List of inputs spent by the transaction. pub inputs: Inputs, /// List of outputs the transaction produces. pub outputs: Vec<Output>, /// List of kernels that make up this transaction (usually a single kernel). pub kernels: Vec<TxKernel>, }

其中:

  • 输入列表:从源码结构看,Inputs 区分了"仅承诺值"与"承诺值+features"两种形态,块级输入本质是被花费输出的承诺——原文所说"输出由其哈希标识、而非位置"在 Grin 里就是"输入只携带承诺值本身";
  • 输出列表:每个 Output 由identifier(features + commitment)与proof: RangeProof组成,对应原文"C = rG + vH 的承诺 + 范围证明",且相等性判定只看 identifier,与"输出按哈希标识"一致;
  • excess 列表:对应 TxKernel——excess: Commitment就是 k*G 值,excess_sig: secp::Signature就是"以 k 为私钥的签名",features携带显式手续费与 lock_height:
/// A proof that a transaction sums to zero. Includes both the transaction's /// Pedersen commitment and the signature, that guarantees that the /// commitments amount to zero. pub struct TxKernel { pub features: KernelFeatures, /// Remainder of the sum of all transaction commitments. If the transaction /// is well formed, amounts components should sum to zero and the excess /// is hence a valid public key (sum of the commitment public keys). pub excess: Commitment, /// The signature proving the excess is a valid public key, which signs /// the transaction fee_fields. pub excess_sig: secp::Signature, }

其 verify() 把 excess 当作公钥,用 aggsig 校验签名(消息为 fee_fields 等字段),batch_sig_verify() 则支持批量验证一批 kernel——批量聚合签名正是"多个 k*G 各自带签名、验证时求和"这一思路的性能化。

原文"列表必须按字典序编码"的要求,落在 TransactionBody::init 与 sort():构造块体时verify_sorted为真则校验输入/输出/kernels 均按字典序排列,否则就地排序。块级错误类型 block::Error 中的KernelSumMismatch(输入输出承诺和与 kernel 承诺和不符)与CoinbaseSumMismatch则是"承诺总和必须与 k*G 总和匹配"这条验证规则的运行时体现。

三、跨块合并:transaction cut-through 与"整链即一笔交易"

这是 MimbleWimble 区别于所有既有隐私方案的核心机制。原文指出每个块都可以视为一笔大交易:把所有输出承诺相加,减去所有输入承诺、所有 k*G 值与所有显式输入金额乘以 H。既然两个块的交易可以像块内交易那样合并,且合并结果仍是有效交易,那么当第一块的某个输出恰好被第二块的输入花费(两个承诺完全相等)时,这对承诺可以直接删除,交易依然有效——甚至被删输出的范围证明都不需要再检查。

把这一思想从创世块一路扩展到最新块:所有非显式的输入都会与其引用的输出一起被删除。最终只剩下未花费输出、显式输入金额、以及全部 k*G 值,而"这一堆东西"可以像一笔交易那样验证:

加上所有未花费输出的承诺,减去所有 k*G 值,验证显式输入金额(如有)再减去它们乘以 H。若总和为 0,整条链就是好的。

由此新节点从每个块只需要三类数据:新币的显式金额、全部未花费输出(附每个输出出现在原始块中的 Merkle 证明)、全部 excess kG 值。原文以此估算:Bitcoin 当时约 423,000 个块、约 80 GB 数据,承载 1.5 亿笔交易与 500 万个未花费的非保密输出;而 MimbleWimble 链上每个未花费输出约 3 KB(范围证明 + Merkle 证明),每笔交易再增约 100 字节(kG 值加签名),块头与显式金额可忽略——总计约 30 GB,却同时提供机密交易与模糊化交易图。

3.1 源码对照:cut_through 就是删除"承诺相等的输入-输出对"

Grin 的 cut_through() 完整实现了这一机制:先把 inputs 与 outputs 按承诺值排序,双指针扫描,凡输入承诺与输出承诺完全相等的配对即被剔除,返回剔除后的两个切片与被切掉的部分:

/// Takes a slice of inputs and a slice of outputs and applies "cut-through" /// eliminating any input/output pairs with input spending output. /// ... /// Example: /// Inputs: [A, B, C] /// Outputs: [C, D, E] /// Returns: ([A, B], [D, E], [C], [C]) # element C is cut-through pub fn cut_through<'a, 'b, T, U>( inputs: &'a mut [T], outputs: &'b mut [U], ) -> Result<(&'a [T], &'b [U], &'a [T], &'b [U]), Error>

调用点如 Block::hydrate_from 在从压缩块(compact block)补全交易后就立即对块体执行 cut-through。"验证时甚至不用检查被删输出的范围证明"这一性质,在 Grin 的链状态层有直接体现:被花费的输出会从 UTXO 集合(txhashset)中移除,见 chain/src/txhashset/ 的实现;块级"自花费即非法"则被显式建模为错误类型 Error::CutThrough("tx is spending its own output")。

3.2 "整链一笔交易"的求和实现:Committed trait

原文"加上未花费输出承诺、减去 kG、减去显式金额H、检查总和为 0"的验证,对应 Grin 的 Committed trait。三个关键方法:

  • sum_commitments(overage):汇总输入/输出承诺;overage(显式金额,即费用/奖励,正数加到输出侧、负数加到输入侧)被编码为纯金额承诺v*H(盲化因子为 0)——这就是原文"显式输入金额乘以 H"在 Grin 里的形态;
  • sum_kernel_excesses:汇总所有 kernel 的 excess(k*G 值)承诺;
  • verify_kernel_sums:断言"输入|输出|overage 承诺之和 == kernel excess 之和(含 kernel_offset)",不等即返回KernelSumMismatch。

Block 对该 trait 的实现直接从body提取三类承诺向量,即原文"块 = 一笔大交易"的直接映射。此外 sum_commits() 在求和时显式剔除"零值承诺"——这与原文 Q&A 中"0 金额输出可被构造为互为相反数的一对"的边界讨论相呼应。

四、边界问题:原文 Q&A 与未解问题

原文最后两节(Questions and Intuition / Future Research)回答了若干看似致命的质疑,这些在理解 Grin 验证语义时值得保留:

  • "删除被花费输出的范围证明,用户就无法验证其中没有负数金额,可能被通胀?"原文回答:对整笔合并交易要验证通过,任何负数金额必须已被销毁。用户获得的是 SPV 级保证——"当前时刻"没有非法通胀发生,而非历史每一刻都没有;
  • "删除输入会导致双花?"实际上这只能意味着"有人声称某个未花费输出在很久以前被花费过",但这不可能,否则合并交易的总和不可能是 0。唯一的例外是 0 金额输出可以造出互为相反数的一对并"复活",因此应禁止 0 金额输出(原文的补救是给每个输出加 H,使金额至少为 1)。Grin 侧对输出有效性有对应的最小金额与承诺校验约束;
  • 未解问题:脚本支持如何在不破坏离散对数假设下表达(需要把脚本操作翻译成某种离散对数信息);用户本只需验证 kG 值之和形如 kG,是否存在可聚合的离散对数证明来替代逐个签名;以及下载链时的 DoS——节点可给出错误的未花费输出列表,用户能看到总和不等于 0 却无法定位问题(原文当时的建议是经 Torrent 等共享渠道分发区块数据)。从仓库结构看,后一个问题在现代 Grin 中被txhashset archive / MMR 结构(chain/src/txhashset/)与 PMMR 结构 所承载的状态证明思路部分回应,但严格说这已超出原始论文的范畴。

五、小结:论文到 Grin 源码的映射表

MimbleWimble 论文机制Grin 源码落点
块 = 新币显式金额 + 输入 + 输出 + k*G 四类信息Block / TransactionBody(奖励改为按块高推导,手续费以v*H全局累加)
承诺C = r*G + v*H+ 范围证明Output(identifier + RangeProof)
excessk*G+ 空消息签名TxKernel(excess + excess_sig,verify 走 aggsig)
输出按哈希标识、列表强制字典序Output按 identifier 判等;TransactionBody::init 的排序校验
交易零和验证(输出+feeH − 输入 == ΣkG)Committed::verify_kernel_sums,失败报KernelSumMismatch/ CoinbaseSumMismatch
跨块 cut-through(删除承诺相等的输入-输出对)cut_through(),块补全时调用(hydrate_from)
新节点只需未花费输出 + k*G + 显式金额txhashset 状态集合(chain/src/txhashset/)持久化未花费输出与全部 kernel

MimbleWimble 的精髓可以浓缩为一句话:借助 Pedersen 承诺的线性可同态性,让"交易合并"与"输入输出对消"在数学上无代价地完成,从而把整条区块链的验证压缩成"未花费输出 + 全部 excess + 显式金额"的一次零和求和。Grin 仓库从core层的数据结构到chain层的 txhashset 状态维护,都是对这套数学的直接工程化;读懂 doc/translations/mimblewimble.md 再对照上表的源码路径,是理解 Grin 一切上层协议(压缩块、快同步、txhashset archive)最可靠的路径。

  • 区块链
  • Web3

【免费下载链接】grin

Minimal implementation of the Mimblewimble protocol.

项目地址:https://gitcode.com/gh_mirrors/gr/grin
点击查看免费下载
上一篇:DeepSeek Harness TUI 转录可复制性改造:移除行前缀装饰,让框选复制零污染
下一篇:在 Microsoft Azure 上部署 Infection Monkey:Marketplace 实例创建、配置与升级指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询