- 区块链
- Web3
【免费下载链接】grin
Minimal implementation of the Mimblewimble protocol.
Mimblewimble 是一种依托强密码学原语构建的区块链格式与协议,它同时提供了极佳的扩展性、隐私性与同质化(fungibility)能力,直指当前绝大多数区块链实现中普遍存在的缺陷。Grin 则是将该协议落地为完整区块链与加密货币的开源软件项目。本文以仓库内 doc/translations/intro_DE.md(及英文原版 doc/intro.md)为主体,系统讲解 Mimblewimble 的技术构造:从椭圆曲线密码学(ECC)的加法性质出发,逐步推导出隐藏金额的 Pedersen Commitment、基于 excess 签名的所有权证明、Range Proof 非负性保证,以及支撑区块级隐私与极致压缩的 Cut-through 剪除机制。读完本文,你将理解 Grin 交易与区块"既能完全遮蔽、又可以被严格验证"的底层逻辑,并能对照仓库源码(core/src/core/transaction.rs、core/src/core/block.rs 等)印证每一个关键环节的真实实现。
Grin 项目的目标与特性
Grin 是一个实现 Mimblewimble 区块链的开源软件项目,其核心目标与特性可以概括为:
- 默认隐私(Privacy by default):隐私是默认行为而非可选项,这带来了完整的同质化能力,同时不排除按需选择性披露信息。
- 主要随用户数而非交易数扩展:一笔交易的证据核心是一个不足 100 字节的
kernel,相比其他区块链在存储空间上大幅节省。 - 强健且久经考验的密码学:Mimblewimble 只依赖椭圆曲线密码学(ECC),这是已被业界实践检验数十年的密码学基础。
- 设计简洁:简洁的设计便于长期审计与维护。
- 社区驱动:鼓励挖矿去中心化,由社区共同治理。
在仓库 README.md 中,项目同样被定位为 "Minimal implementation of the Mimblewimble protocol",其设计哲学贯穿于 core、chain、pool、p2p 等各个 crate 的实现中。
预备知识:椭圆曲线密码学(ECC)的加法性质
理解 Mimblewimble 之前,先回顾 ECC 中与协议直接相关的几条性质(不需要深究 ECC 的全部细节)。
密码学所用的椭圆曲线可以看作一个巨大的点集合,记为C。这些点可以相加、相减,结果仍是曲线上的点;点也可以通过与整数(即标量)相乘的方式连续叠加,即标量乘法。给定曲线上的点H与整数k,通过标量乘法可计算k*H,它同样是曲线C上的一个点。给定另一个整数j,还可计算(k+j)*H,它等于k*H + j*H。椭圆曲线上的加法与标量乘法保持分配律:
(k+j)*H = k*H + j*H在 ECC 中,选取一个(非常大的)数k作为私钥,k*H即是对应的公钥。即使公钥k*H公开,反推k也几乎不可能——换言之,乘法是平凡的,而"除以"曲线点极其困难。
上述公式揭示了重要事实:两个私钥相加所得的公钥(k+j)*H,与这两个私钥各自的公钥相加k*H + j*H完全相同。比特币中的分层确定性(Hierarchical Deterministic)钱包大量依赖这一性质,Mimblewimble 与 Grin 的实现同样如此。
交易的两大验证支柱
Mimblewimble 交易结构体现了一条核心宗旨:强隐私与机密性保证。交易的验证依赖两个基本性质:
- 零和验证(Verification of zero sums):所有输出减输入的总和恒等于零,从而证明交易没有凭空创造新资金,同时不泄露真实金额。
- 私钥所有权(Possession of private keys):与大多数加密货币一样,交易输出的所有权由持有 ECC 私钥保证。但关键区别在于,证明某实体持有这些私钥并非通过直接对交易签名完成,而是通过下面将要介绍的 excess 签名机制。
接下来的"余额、所有权、找零、证明"四个环节,将详细解释这两大支柱如何实现。
隐藏余额:Pedersen Commitment
基于上述 ECC 性质,交易中的数值可以被遮蔽。设v是交易输入或输出的金额,H是椭圆曲线上的一个点,那么可以用v*H代替v嵌入交易。这样仍能验证"输出的和等于输入的和":
v1 + v2 = v3 => v1*H + v2*H = v3*H对每笔交易验证这一性质,协议就能在不知道真实数值的情况下确认交易没有凭空造钱。但这里存在两个隐患:其一,可用数值是有限的,攻击者可以逐一枚举测试来推测你的交易金额;其二,只要知道某个 v1(例如来自历史交易)以及它对应的v1*H,就能在整条链上识别出所有相同金额的输出。
为此,协议引入同一条椭圆曲线上的第二个生成点G(G只是与H同群中的另一个生成点),以及一个用作盲化因子(Blinding Factor)的私钥r。于是交易中每个输入或输出金额可以表示为:
r*G + v*H其中:
- r是充当盲化因子的私钥,
r*G是以G为生成点时r的公钥; - v是输入或输出的金额,
v*H是以H为生成点得到的另一个公钥。
借助 ECC 的基本性质,v和r都无法被反推。r*G + v*H被称为Pedersen Commitment。
举例:假设要构建一笔含两个输入、一个输出的交易(暂不考虑手续费):
- vi1、vi2 为输入金额;
- vo3 为输出金额。
且vi1 + vi2 = vo3。为每个输入金额生成一个作为盲化因子的私钥,并用各自的 Pedersen Commitment 替换上式中的金额:
(ri1*G + vi1*H) + (ri2*G + vi2*H) = (ro3*G + vo3*H)其必然推论是:
ri1 + ri2 = ro3这就是 Mimblewimble 的第一根支柱:验证交易所需的算术可以在完全不知道任何金额的情况下完成。这一思想源自 Greg Maxwell 提出的 Confidential Transactions(机密交易),而后者又源自 Adam Back 针对比特币提出的同态数值方案。在 Grin 中,Pedersen Commitment 的实际生成可见于 keychain/src/keychain.rs 的commit方法:先由derive_key基于密钥派生路径与 SwitchCommitmentType 派生盲化私钥,再调用secp.commit(amount, key)得到承诺(keychain/src/keychain.rs)。
所有权证明:为什么别人偷不走你的钱
上一节把私钥作为盲化因子来遮蔽金额,Mimblewimble 的第二点洞察是:这枚私钥还可以用来证明对金额的所有权。
假设 Alice 给你发了 3 个币,为了遮蔽金额,你选择 28 作为盲化因子(实际中盲化因子作为私钥是一个极大的数)。链上某处会出现如下输出,且只应能被你花费:
X = 28*G + 3*HX(加法结果)对所有人可见;金额 3 只有你和 Alice 知道,28 只有你自己知道。
要把这 3 个币再次转出,协议要求以某种方式知道 28。假设你要把这 3 个币转给 Carol,需要构造一笔简单交易:
Xi => Y其中Xi是花费你的输出X的输入,Y是 Carol 的输出。不知道你的私钥 28,就不可能构造并平衡这样一笔交易。事实上,如果 Carol 要平衡这笔交易,她必须同时知道发送金额和你的私钥,才能做到:
Y - Xi = (28*G + 3*H) - (28*G + 3*H) = 0*G + 0*H通过检查一切归零,可以再次确认没有新钱被创造出来。
但等等——如果 Carol 的输出中使用了与你的私钥相同的 28 来保持平衡,那么你也知道了 Carol 输出的私钥,完全可以把钱偷回来!
解决方案是:Carol 使用她自己选择的私钥。假设她选择 113,最终上链的等式变为:
Y - Xi = (113*G + 3*H) - (28*G + 3*H) = 85*G + 0*H此时交易不再归零,而是产生一个excess value(溢出值)85,它是所有盲化因子相加(对应地相减)的结果。注意85*G是生成点G上的一个合法公钥。
因此,协议需要验证:交易的参与各方能否共同为结果点Y - Xi生成私钥(上例中的 85,即以G为生成点、与85*G对应的公钥)。最简单的方式是:用公钥Y - Xi(即85*G)去验证一个由溢出值 85 签名的签名。这同时保证了:
- 交易参与各方共同掌握公钥
Y - Xi对应的私钥(即溢出值); - 输出金额之和减去输入金额之和为零(否则公私钥无法对应,这正是存在签名校验的原因)。
这笔附加在每笔交易上、连同其他数据(如挖矿手续费)一起的签名,被称为交易 Kernel(Transaction Kernel),由所有验证节点检查。在 Grin 源码中,TxKernel结构体正是由三部分组成:features(kernel 特性)、excess(承诺)与excess_sig(证明 excess 是合法公钥、并对 fee_fields 签名的签名),见 core/src/core/transaction.rs。
找零与 Range Proof:确保数值非负
本节进一步展开交易的构造,讨论找零(Change)的引入,以及 Range Proof 的必要性,使所有数值都被证明为非负。
找零(Change)
假设你只想把 Alice 给的 3 个币中的 2 个转给 Carol。为此,你需要把剩下的 1 个币作为找零转回给自己。你生成另一个私钥(例如 12)作为盲化因子来保护找零输出,Carol 依旧使用她自己的私钥:
找零输出: 12*G + 1*H Carol 输出: 113*G + 2*H最终上链的内容与之前非常相似,签名依旧基于溢出值构造,本例中为 97:
(12*G + 1*H) + (113*G + 2*H) - (28*G + 3*H) = 97*G + 0*HRange Proof(范围证明)
前述所有计算都依赖一个隐含假设:交易金额始终为正。引入负数将带来灾难性后果——每笔交易都可能凭空造出新钱。
例如,可以构造一笔输入为 2、输出为 5 和 -3 的交易,它在前述定义下依然"平衡"。这种作恶很难被察觉,因为即便x为负,曲线上对应的点x*H看起来与其他点并无区别。
为解决此问题,Mimblewimble 采用了同样源自 Confidential Transactions 的密码学概念:Range Proof——在不泄露数值的前提下证明该数值落在给定范围内。我们不必深入 Range Proof 的实现细节,只需知道:对于任意r*G + v*H,都可以构造一个证明,表明v非负且不会溢出。
这里还隐含一个容易被忽略但至关重要的点:Range Proof同时覆盖盲化因子与金额。英文原版 doc/intro.md 用一个审查攻击(censoring attack)的例子说明了其必要性:攻击者可以伪造一笔"转账"(如把 Carol 的 UTXO113*G + 2*H改成(113 + 99)*G + 2*H),由于 Carol 的盲化因子在Y - Xi的差式中相互抵消(结果为99*G),攻击者无需知道 113 即可完成签名,从而在不知道私钥的情况下锁定(冻结)Carol 的 UTXO;而要求对盲化因子也提供 Range Proof 后,攻击者因不知道 113(也就不知道 113+99),无法为该输出生成合法证明,攻击便无法得逞。Grin 中 Range Proof 的生成与验证见 core/src/libtx/proof.rs:create通过secp.bullet_proof(...)生成 BulletProof 范围证明(core/src/libtx/proof.rs),verify则调用secp.verify_bullet_proof(...)进行验证(core/src/libtx/proof.rs)。
一笔 Mimblewimble 交易的完整构成
综合以上各环节,一笔 Mimblewimble 交易包含:
- 一组输入(Inputs):引用并花费一组先前的输出;
- 一组新输出(Outputs),每个输出包含:
- 盲化因子r与金额v,分别对生成点 G、H 做标量乘法后求和为
r*G + v*H; - 一个 Range Proof,证明v非负(以及其他性质);
- 盲化因子r与金额v,分别对生成点 G、H 做标量乘法后求和为
- 一笔明文交易手续费(fee);
- 一个以溢出值(所有输出金额加手续费再减输入金额之和)作为私钥生成的签名。
在 Grin 的源码中,这一结构被明确建模为Transaction结构体:包含offset(kernel offset,即下面要讲的 k2)与body(交易体),见 core/src/core/transaction.rs;交易体TransactionBody则由inputs、outputs、kernels三部分组成(core/src/core/transaction.rs);输出Output由"输出标识(features + commitment)"与"range proof"构成(core/src/core/transaction.rs)。
区块与链状态:Cut-through 剪除机制
上文说明了 Mimblewimble 交易如何在保持有效区块链所需性质(不造钱、私钥证明所有权)的同时提供强匿名性。Mimblewimble 的区块格式在此基础上引入了一个额外概念:Cut-through。凭借它,一条 Mimblewimble 链获得:
- 极佳的扩展性:绝大多数交易数据可以随时间推移被删除,且不损害安全性;
- 更强的匿名性:通过混合与删除交易数据进一步混淆关联;
- 高效的同步能力:新节点能以极小的数据量高效地与网络其余部分同步。
交易聚合(Transaction Aggregation)
回顾一下,每笔交易由以下部分组成:
- 一组引用并花费先前输出的输入;
- 一组新输出(Pedersen Commitment);
- 一个交易 kernel,包含:
- Kernel Excess(对零的 Pedersen Commitment);
- 交易签名(以 Kernel Excess 作为公钥)。
交易被签名后,签名放入Transaction Kernel。签名以Kernel Excess作为公钥生成,从而证明交易归零:
(42*G + 1*H) + (99*G + 2*H) - (113*G + 3*H) = 28*G + 0*H本例中的公钥是28*G。
对任意交易(为简化暂忽略手续费)都有:
sum(outputs) - sum(inputs) = kernel_excess一旦意识到"区块不过是一组聚合后的输入、输出与交易 kernel",同样的关系也适用于区块:将所有输出求和、减去所有输入,得到的 Pedersen Commitment 应与所有 kernel excess 之和相等:
sum(outputs) - sum(inputs) = sum(kernel_excess)简化表述(依旧忽略手续费):Mimblewimble 区块可以完全像 Mimblewimble 交易一样处理。
Grin 中这一聚合逻辑由 core/src/core/transaction.rs 的aggregate函数实现:它把所有交易的输入、输出、kernel 收集起来,先做 cut-through,再把各交易 kernel offset 求和为单一聚合 offset,最后构建出多 kernel 的聚合交易。区块构造入口 core/src/core/block.rs 的Block::from_reward也印证了"区块即大交易"的思想——注释中明确写着 "A block is just a big transaction, aggregate and add the reward output and reward kernel"。
Kernel Offsets:解决"子集问题"
上述区块与交易构造方式存在一个微妙的问题:有可能(有时甚至很 trivial)从区块中重建出构成它的原始交易,这对隐私显然是灾难。这就是"子集问题":给定一组输入、输出与交易 kernel,其中某个子集可以重组出一笔合法交易。
例如给定两笔交易:
(in1, in2) -> (out1), (kern1) (in3) -> (out2), (kern2)聚合进同一个区块(即聚合交易):
(in1, in2, in3) -> (out1, out2), (kern1, kern2)尝试所有排列组合,trivially 就能恢复其中一笔(成功归零的)交易:
(in1, in2) -> (out1), (kern1)而剩余部分恰好也能重建出另一笔合法交易:
(in3) -> (out2), (kern2)为抑制这一问题,Grin 的每个交易 kernel 都携带一个Kernel Offset。它也是一个盲化因子(私钥),必须被加回到 kernel excess 上,才能验证承诺之和为零:
sum(outputs) - sum(inputs) = kernel_excess + kernel_offset更精确地说:在交易构造过程中,将密钥k"拆分"为k1 + k2。对交易 kernel(k1+k2)*G,发布k1*G(即 excess,用于签名验证)与k2(即 offset);签名依旧用k1*G作为公钥完成。当把区块中的交易聚合起来时,只需把所有k2偏移量求和,生成一个覆盖整块交易的聚合 offset,存入区块头。由于这个单一 offset 无法被拆解回各个交易的原始 offset,子集问题便不复存在,交易不再可被重建:
sum(outputs) - sum(inputs) = sum(kernel_excess) + kernel_offsetGrin 源码对此的建模非常直观:Transaction结构体中的offset字段注释为 "The kernel offset k2, excess is k1G after splitting the key k = k1 + k2"(core/src/core/transaction.rs);区块头BlockHeader中则有total_kernel_offset字段,保存"自创世区块以来 kernel offset 的累计和"(core/src/core/block.rs)。区块校验时通过 core/src/core/block.rs 的block_kernel_offset从总 offset 中减去前一区块的 offset,得到本区块的 offset,再与输入输出承诺之和做比对(core/src/core/block.rs)。底层求和的实现见 core/src/core/committed.rs 的sum_kernel_offsets,它把若干BlindingFactor转为私钥并调用secp.blind_sum求和。
Cut-through:区块内部的输入输出抵消
区块允许矿工把多笔交易组装成单个集合加入链中。下面以含 3 笔交易的区块为例,只展示交易的输入输出(输入引用其花费的输出,大写 O 为本区块内新产生的输出,小写 x 标记此前区块已出现的输出):
I1(x1) --- O1 |- O2 I2(x2) --- O3 I3(O2) -| I4(O3) --- O4 |- O5可以观察到两个性质:
- 区块内有些输出直接被后续输入花费(I3 花费 O2,I4 花费 O3);
- 每笔交易的具体结构其实无关紧要——因为所有交易各自归零,所以全部交易输入输出之和必然为零。
与交易类似,区块中需要校验的只有:所有权已被证明(来自Transaction Kernel),以及整个区块没有凭空增发货币(除挖矿奖励允许的部分外)。因此,相互匹配的输入与输出可以被删除,因为它们对总和数的贡献相互抵消。于是得到更紧凑的区块:
I1(x1) | O1 I2(x2) | O4 | O5注意:所有交易结构已被消除,输入与输出的排列顺序也不再重要,但"输出之和减输入之和"仍然保证为零。
一个区块因此简化为由以下部分构成:
- 一个区块头(Block Header);
- Cut-through 后剩余的输入列表;
- Cut-through 后剩余的输出列表;
- 一个覆盖整个区块的聚合 kernel offset(所有 kernel offset 之和);
- 每笔交易的交易 kernel,其中包含:
- 公钥
(r-a)*G,即(修改后的)kernel excess; - 以(修改后的)溢出值
(r-a)作为私钥生成的签名; - 挖矿手续费。
- 公钥
区块内容满足:
sum(outputs) - sum(inputs) = sum(kernel_excess) + kernel_offset*G按此结构组织的 Mimblewimble 区块提供了极强的隐私保证:
- 中间(被 cut-through 的)交易只由其交易 kernel 代表;
- 所有输出看起来一模一样:都是无法相互区分的大数。想排除某个特定输出,就必须排除全部;
- 所有交易结构被移除,无法判断哪个输入对应哪个输出。
而它——依然能够完整通过校验!
Grin 中 cut-through 的实际算法见 core/src/core/transaction.rs 的cut_through函数:先将输入与输出按承诺排序,再用双指针线性扫描,凡输入承诺与输出承诺相等者即为"剪除"对象,最后检查剪除后无重复输入/重复输出。测试 core/tests/transaction.rs 验证了"交易内直接花费同承诺输出"会因 cut-through 规则而校验失败(Error::CutThrough),而显式执行 cut-through 后交易恢复合法;区块级对应测试见 core/tests/block.rs(block_with_cut_through断言构造出的区块被自动压缩且依然有效)与 core/tests/block.rs。
Cut-through 贯彻到底:链状态最小化
回到前面的示例区块:被 I1、I2 花费的 x1、x2 必定早已出现在链上。因此在本区块加入之后,这些输出连同 I1、I2 也可以从整条链中移除——它们已成为中间交易。
推广之,任意时刻的链状态(不含区块头)只需由以下三部分信息概括:
- 链上通过挖矿创建的总币数;
- 完整的未花费输出(UTXO)集合;
- 每笔交易的交易 kernel。
第一项只需通过区块高度(距创世区块的距离)即可推导。未花费输出与交易 kernel 都极其紧凑,这带来两个重要后果:
- 节点需要维护的区块链状态非常小(文档估计:一条规模相当于比特币的链仅约几个 GB,且理论上可优化至几百 MB 量级);
- 新节点加入网络时,需要传输的数据量也非常小。
此外,UTXO 集合不可能被篡改:无论添加还是删除哪怕一个输入或输出,都会使交易的求和结果不再为零,从而立刻暴露。
Grin 对这套思想的工程实现散落在多个模块:链状态存储与剪枝由 chain/src/txhashset/txhashset.rs 承担——每个被花费的输出通过apply_input同时从输出 PMMR 与 range proof PMMR 中剪除(chain/src/txhashset/txhashset.rs);状态校验入口validate依次执行 MMR 根校验、大小校验、kernel sums 校验,并(在非快速模式下)逐输出验证 range proof、逐 kernel 验证签名(chain/src/txhashset/txhashset.rs)。新节点同步时,若所需区块早于 horizon,则改为直接下载紧凑的 txhashset 快照而非全量区块,相关逻辑见 servers/src/grin/sync/body_sync.rs。
值得一提的是,区块奖励同样被纳入这套承诺体系:挖矿输出(Coinbase)与奖励 kernel 的铸造见 core/src/libtx/reward.rs,其奖励金额由 core/src/consensus.rs 的reward(fee) = REWARD + fee决定(其中REWARD = BLOCK_TIME_SEC * GRIN_BASE,即每区块 60 秒 × 10 亿 nanogrin);区块校验通过verify_coinbase检查 coinbase 输出之和(扣除手续费后的奖励承诺)与 coinbase kernel 之和是否相等(core/src/core/block.rs),一旦篡改 coinbase 标志就会触发CoinbaseSumMismatch错误(见测试 core/tests/block.rs)。coinbase 需要经历成熟期(COINBASE_MATURITY = DAY_HEIGHT,见 core/src/consensus.rs)才可被花费,这也与 doc/coinbase_maturity.md 中阐述的规则一致。
结语
本文覆盖了 Mimblewimble 区块链背后的基本原理。借助椭圆曲线点的加法性质,我们能够构造出完全不可解读、却仍能被正确验证的交易;通过把这一性质推广到区块层面,又可以删除海量区块链数据,从而获得卓越的扩展能力与新节点快速同步能力。整条链的状态最终收敛为"总发行量 + UTXO 集合 + 交易 kernel"三部分,而每个环节——从 Pedersen Commitment 的生成、Range Proof 的铸造,到 kernel offset 的聚合、cut-through 的剪除——都能在 Grin 仓库的源码与测试中找到一一对应的实现。建议读者进一步阅读仓库内的英文原版 doc/intro.md 与更深入的专题文档(如 doc/validation/validation_logic.md、doc/pruning.md),并结合 core/tests 下的测试用例亲手验证这些机制。
- 区块链
- Web3
【免费下载链接】grin
Minimal implementation of the Mimblewimble protocol.
相关推荐
XELIS加密算法详解:Ristretto255与椭圆曲线密码学
XELIS加密算法详解:Ristretto255与椭圆曲线密码学 在区块链技术飞速发展的今天,隐私保护和安全通信成为了用户关注的核心议题。XELIS区块链作为一
终极免费音频编辑指南:Audacity如何让普通人制作专业级音效
终极免费音频编辑指南:Audacity如何让普通人制作专业级音效 你是否曾梦想制作自己的播客、为视频添加专业音效,或是创作属于自己的音乐?但面对复杂的音频软件和
音频处理桌面应用音视频深入理解Crypton项目中的椭圆曲线密码学
深入理解Crypton项目中的椭圆曲线密码学 前言 椭圆曲线密码学 ECC 是现代密码学中最重要的公钥加密技术之一。相比传统RSA算法,ECC能够在提供相同安全
网络安全文档/教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考