name: midnight-core-concepts:privacy-patterns description: 在Compact中实现隐私保护逻辑时使用,涉及哈希、承诺、默克尔树、无效符模式或链上数据隐私保护。
Midnight中的隐私模式
基本原则:传递给账本操作的任何内容都是公开可见的,除了MerkleTree类型。使用以下模式来保护数据隐私。
模式选择指南
| 目标 | 模式 | 使用场景 |
|---|---|---|
| 隐藏数据,稍后证明 | 哈希 | 需要简单验证 |
| 隐藏数据,防止关联 | 承诺 | 相同值必须看起来不同 |
| 秘密证明成员资格 | 默克尔树 | 集合成员资格证明,不透露具体元素 |
| 一次性令牌 | 承诺 + 无效符 | 防止隐私泄露的双重花费 |
模式1:哈希
使用persistentHash隐藏数据,同时允许后续验证。
import { persistentHash } from "std/compact/hashes";
// 存储秘密的哈希值
ledger.stored_hash = persistentHash(secret_data);
// 稍后,通过重新哈希来证明知识
assert persistentHash(provided_data) == ledger.stored_hash;
局限性
- 相同输入 → 相同哈希(可关联)
- 小值集合易受暴力破解攻击
适用于:不会重复且具有高熵的数据。
模式2:承诺
当相同值必须看起来不同时,使用persistentCommit。
import { persistentCommit } from "std/compact/commitments";
// 使用随机数防止关联
const commitment = persistentCommit(value, randomness);
ledger.stored_commitment = commitment;
随机数的重要性
没有随机数,对“是”的承诺总是看起来相同。使用随机数:
- commit(“是”, random1) ≠ commit(“是”, random2)
- 防止相同值之间的关联
- 对投票、竞价、任何重复值至关重要
// 错误:可关联
const bad = persistentCommit(vote, Bytes<32>::zero());
// 正确:不可链接
const good = persistentCommit(vote, fresh_randomness);
随机数来源
// 选项1:新鲜随机数(理想)
const r = generateRandomness();
// 选项2:从秘密密钥+计数器派生(确定性)
const r = persistentHash(secret_key, counter);
适用于:任何可能重复的值(投票、竞价、选择)。
模式3:默克尔树
使用MerkleTree证明集合成员资格,而不透露具体元素。
ledger authorized_keys: MerkleTree<32, Bytes<32>>;
export circuit authenticate(
secret_key: Bytes<32>,
merkle_path: MerkleTreePath<32, Bytes<32>>
): Void {
// 证明密钥在树中,不透露是哪个密钥
const public_key = persistentHash(secret_key);
assert ledger.authorized_keys.member(public_key, merkle_path);
}
树类型
| 类型 | 使用场景 |
|---|---|
MerkleTree<n, T> |
静态集合,针对当前根进行证明 |
HistoricMerkleTree<n, T> |
频繁插入,针对过去根进行证明 |
使用HistoricMerkleTree当:元素频繁添加,用户需要针对他们获取路径时的根证明成员资格。
模式4:承诺 + 无效符
最强大的模式:具有完全隐私的一次性令牌。
工作原理
- 承诺阶段:将
commit(value, secret)存储在默克尔树中 - 花费阶段:揭示
nullifier = hash(commitment, secret) - 验证:证明承诺存在,无效符未被使用过
ledger commitments: MerkleTree<32, Bytes<32>>;
ledger nullifiers: Set<Bytes<32>>;
export circuit spend(
value: Field,
secret: Bytes<32>,
path: MerkleTreePath<32, Bytes<32>>
): Void {
// 重构承诺
const commitment = persistentCommit(value, secret);
// 证明承诺存在于树中
assert ledger.commitments.member(commitment, path);
// 计算并记录无效符
const nullifier = persistentHash(commitment, secret);
assert !ledger.nullifiers.member(nullifier);
ledger.nullifiers.insert(nullifier);
}
隐私属性
- 承诺:隐藏值和所有者
- 无效符:防止双重花费,不与承诺关联
- 默克尔证明:证明存在性,不透露具体承诺
这是Zerocash和Zswap的屏蔽UTXO的基础。
认证模式
证明身份而不透露凭证:
export circuit authenticate(secret_key: Bytes<32>): Void {
const public_key = persistentHash(secret_key);
assert public_key == ledger.authorized_key;
// 已授权!secret_key从未被揭示
}
常见错误
错误1:重用随机数
// 错误:相同随机数 = 可关联
const r = fixed_value;
commit(vote1, r); commit(vote2, r);
// 正确:每次使用新鲜随机数
commit(vote1, random1); commit(vote2, random2);
错误2:小值集合不使用随机数
// 错误:只有2个可能值,易受暴力破解
hash(vote); // vote是“是”或“否”
// 正确:使用随机数的承诺
persistentCommit(vote, randomness);
错误3:忘记什么是公开的
// 错误:这会暴露秘密!
ledger.public_field = secret_value;
// 正确:仅存储承诺
ledger.public_field = persistentCommit(secret_value, randomness);
参考资料
详细技术信息:
references/commitment-schemes.md- Pedersen承诺,绑定/隐藏属性references/merkle-tree-usage.md- 树操作,路径生成,历史树
示例
工作模式:
examples/private-voting.compact- 使用承诺/无效符的完整投票系统examples/auth-patterns.compact- 不透露密钥的认证模式