我通过一个简单的例子来解释 Shielded CSV 中 nullifier 公钥 和其他机制是如何区分不同的币并防止重复花费的。
假设有两个用户,Alice 和 Bob。Alice 有一个账户,里面有 5 个币。她想发送 2 个币给 Bob。在 Shielded CSV 中,这个交易涉及到以下几个步骤:
- Alice 的账户 目前包含 5 个币,这些币在她的账户状态中记录。账户状态包括:
- 账户 ID: Alice 的账户唯一标识(公钥)。
- 余额: 5 个币。
- 花费累加器: 一个数据结构,跟踪 Alice 已经花费过的币(目前是空的,因为 Alice 还没有花费任何币)。
- nullifier 公钥: Alice 账户当前的 nullifier 公钥,用于在链上发布交易时的认证。
Alice 准备发送 2 个币给 Bob。为了创建这个交易,她需要更新账户状态,并生成交易的证明。
-
选择花费的币: Alice 从她的账户中挑选出 2 个币进行花费。
-
更新花费累加器: Alice 把这 2 个币加入她的账户的 花费累加器,表示这些币已经被花费,不能再用。
-
生成新的账户状态:
- Alice 创建一个新的账户状态,保持原来的 账户 ID 和更新后的 余额(5 - 2 = 3 个币)。
- 她生成一个新的 nullifier 公钥,并使用这个公钥来生成一个 nullifier,表示这次交易。
-
生成交易承诺: Alice 对这次交易生成一个 交易承诺,这个承诺包括了交易的具体细节(比如花费的币、接收者的信息等),但这些信息是加密的,只能由相应的接收者(Bob)解锁。
-
Schnorr 签名: 为了保证交易的安全性和唯一性,Alice 使用她的 nullifier 私钥 对交易进行了 Schnorr 签名。这个签名证明她是交易的合法发起人。
Alice 将生成的 nullifier 和交易的承诺一起发送给一个 publisher(发布者)。这个发布者会收集多个用户的交易,并将它们一起发布到区块链上。
- nullifier 公钥:这个 nullifier 由 Alice 生成,它表示 Alice 在这次交易中花费的币。这个 nullifier 会被记录在区块链上,任何人都可以看到它。
- 交易承诺:交易的具体细节被隐藏在承诺中,只有 Bob(接收者)能够解密这些信息。
现在,Bob 收到了 Alice 发送的 2 个币,他想验证这个交易是否合法。
-
验证账户状态更新:Bob 首先检查 Alice 的账户状态是否正确更新。Bob 会查看 Alice 的旧账户状态,并确认 Alice 的新账户状态是否遵循了规则(如余额减少,花费累加器更新等)。
-
验证 nullifier:Bob 检查区块链上的 nullifier。如果 Alice 试图双重花费这 2 个币,区块链上已经存在的 nullifier 会与当前的交易发生冲突。因为每个 nullifier 是基于 Alice 的交易签名生成的,如果 Alice 尝试使用相同的币进行第二次交易,验证器会检测到 nullifier 的重复,并拒绝交易。
-
验证 Schnorr 签名:Bob 确认 Alice 的 Schnorr 签名是有效的,这证明 Alice 确实是这笔交易的合法发起人。
Bob 验证了所有步骤后,接受了 2 个币,这些币现在属于他的账户。他也生成了自己的 nullifier 公钥,并通过类似的过程保护他的账户和未来交易。
通过使用 nullifier 公钥 代替单纯的 Coin ID,Shielded CSV 实现了更高的隐私和安全性:
- 防止双重花费:每个 nullifier 只会对应一次合法的交易。如果尝试双花,相同的币会生成相同的 nullifier,验证器能够检测并拒绝第二笔交易。
- 隐私保护:通过加密承诺,交易细节(如币的数量、接收者等)对外是不可见的,只有相关方可以查看。
- 签名保障:通过 Schnorr 签名,只有拥有账户私钥的人才能进行合法交易,防止他人冒用。
这种机制既保证了隐私性,又确保了每个币的唯一性和不可重复使用。
在 Shielded CSV 协议中,Bob 实际上是无法直接检查 Alice 的旧账户状态的,因为旧账户状态对于其他用户来说是加密和私密的。这种设计是为了保护用户隐私。但是,Bob 可以通过验证 Alice 的交易证明(coin proof)来间接确认 Alice 的旧账户状态是否有效和正确。
1. 账户状态隐私保护
- Alice 的旧账户状态不会公开在区块链上,区块链只包含 Alice 发布的 nullifier 和 交易承诺。因此,Bob 无法直接访问和检查 Alice 的旧账户状态。
- 然而,Shielded CSV 中的交易需要提交一个交易证明(coin proof),这个证明包含了 Alice 更新账户状态时的所有必要信息,但这些信息是通过零知识证明(如 zk-SNARKs)等加密技术隐匿的。
2. 零知识证明(zk-SNARKs)
-
Alice 在创建交易时,生成了一个 零知识证明,这个证明能在不透露旧账户状态具体细节的前提下,证明:
- Alice 的旧账户状态是有效的。
- 旧账户的 nullifier 确实已正确发布,并且与对应的交易相匹配。
- 账户余额的更新是正确的,即 Alice 花费了她账户中的 2 个币,剩余的 3 个币保持在她的新账户中。
-
Bob 检查 coin proof:Bob 通过验证 Alice 提供的零知识证明,确认交易是有效的,即 Alice 的账户状态正确更新,且没有任何双重花费行为。这个验证过程是在链上进行的,并不需要直接查看 Alice 的账户细节。
3. nullifier 的作用
- Alice 的旧账户状态通过发布 nullifier 来标识。Bob 通过在区块链上检查 Alice 的 nullifier,可以确认 Alice 的账户中那些币已经被花费。Bob 也可以确保没有重复的 nullifier 存在(防止双重花费)。
- nullifier 的验证:Bob 通过区块链上查询 nullifier 累加器,来检查 Alice 的 nullifier 是否已经发布。如果 Alice 尝试使用同一组币进行多次交易(即双重花费),区块链上只会接受第一个 nullifier,后续的 nullifier 将无法通过验证。
4. 新账户状态的验证
- Bob 通过验证 Alice 提供的 零知识证明 和交易的 Schnorr 签名,可以确保:
- 新账户状态是合法的,并遵循了协议中的规则。
- Alice 的旧账户状态已经被成功 nullify(即确认她已经正确花费了这些币),且她没有进行双重花费。
Bob 无法直接访问 Alice 的旧账户状态,但通过验证 Alice 提供的 交易证明(coin proof) 和 nullifier,Bob 可以间接确认 Alice 的旧账户状态是否被正确更新,并确保交易的合法性。这种机制通过零知识证明保护用户隐私,同时保证交易的安全性和准确性。
为了更好地理解 Shielded CSV 中的用户之间转账过程,我将通过一个详细的例子逐步介绍整个交易过程。假设我们有两个用户 Alice 和 Bob,Alice 想要向 Bob 转账 10 个单位的某种加密资产。
-
Alice的初始账户状态:
- 账户ID:
AccountID_Alice
- 账户余额: 50 个单位
- 账户公钥:
PublicKey_Alice
- 已花费累加器(Spent Accumulator): 包含 Alice 之前花费的交易记录
- nullifier 累加器: 包含所有与 Alice 账户相关的历史 nullifier
- 账户ID:
-
Bob的初始账户状态:
- 账户ID:
AccountID_Bob
- 账户余额: 20 个单位
- 账户公钥:
PublicKey_Bob
- 账户ID:
现在,Alice 打算将 10 个单位的资产转给 Bob。整个过程通过 Shielded CSV 协议进行,具有隐私性和安全性。
Alice 需要创建一笔转账交易,交易的目标是将 10 个单位的资产从她的账户转移到 Bob 的账户。此时,她会按照以下步骤操作:
-
条件 nullifier accumulator 值 (Conditional Nullifier Accumulator Value):
- 这将包含所有相关的 nullifier 的累加器状态,确保交易在发生区块链重组时不会失效。例如:
NullifierAccumulator = "Accumulated_Nullifiers_UpTo_TxN"
- 这将包含所有相关的 nullifier 的累加器状态,确保交易在发生区块链重组时不会失效。例如:
-
要被 nullify 的账户状态精华 (AcctStateEssence to Nullify):
- Alice 将标记她的账户状态为 "已作废",因为这笔转账会更新她的账户余额。Alice 的账户状态精华如下:
AcctStateEssence_Alice = { AccountID: "AccountID_Alice", Balance: 50, // 转账前的余额 PublicKey: "PublicKey_Alice" // 用于签名和验证的公钥 }
- Alice 将标记她的账户状态为 "已作废",因为这笔转账会更新她的账户余额。Alice 的账户状态精华如下:
-
要花费的 CoinIDs 列表 (List of CoinIDs to Spend):
- Alice 使用自己账户中的 Coin 来支付。Coin 是通过前一笔交易生成的。假设 Alice 有两个 Coin,分别是:
CoinID1 = { TxHash: "Tx123", Index: 0 } CoinID2 = { TxHash: "Tx124", Index: 1 }
- Alice 使用自己账户中的 Coin 来支付。Coin 是通过前一笔交易生成的。假设 Alice 有两个 Coin,分别是:
-
要创建的账户状态精华 (AcctStateEssence to Create):
- 由于 Alice 的账户余额会减少到 40 个单位(因为她转账了 10 个单位给 Bob),新的账户状态精华如下:
AcctStateEssence_Alice_New = { AccountID: "AccountID_Alice", Balance: 40, PublicKey: "PublicKey_Alice" }
- 由于 Alice 的账户余额会减少到 40 个单位(因为她转账了 10 个单位给 Bob),新的账户状态精华如下:
-
要创建的 Coin 精华 (List of CoinEssences to Create):
- 对于 Bob 来说,Alice 会为他创建一个新的 Coin。该 Coin 精华包含:
CoinEssence_Bob = { OwnerAddress: "AccountID_Bob", // Bob 的账户地址 Amount: 10, // 转账金额 Index: 0 // 新的 Coin 在交易中的索引位置 }
- 对于 Bob 来说,Alice 会为他创建一个新的 Coin。该 Coin 精华包含:
-
Alice 生成 ZKP 证明:
- 在链下,Alice 会生成一个 零知识证明(ZKP),以证明她的账户余额是有效的,并且这笔转账是合法的。她需要证明以下内容:
- Alice 的账户确实持有 50 个单位的资产。
- Alice 要转账的 10 个单位是合法的,并且她还剩下 40 个单位的资产。
- Bob 将收到 10 个单位,且新的 Coin 属于 Bob。
- 在链下,Alice 会生成一个 零知识证明(ZKP),以证明她的账户余额是有效的,并且这笔转账是合法的。她需要证明以下内容:
-
Bob 验证转账:
- 在链下,Bob 收到 Alice 提供的交易和 ZKP 证明。Bob 使用 PCDVerify 验证该交易的合法性,确保:
- Alice 的账户状态已更新并且合法。
- Alice 的 Coin 证明有效。
- Bob 将合法地获得新的 Coin。
- 在链下,Bob 收到 Alice 提供的交易和 ZKP 证明。Bob 使用 PCDVerify 验证该交易的合法性,确保:
一旦交易和 ZKP 证明在链下通过验证,Alice 会将 nullifier 发布到区块链上。这个 nullifier 是 Alice 标记她账户状态无效的证明,表示该账户状态已经被 nullify(作废)。
在链上发布时:
- AggregateNullifier:
- Alice 创建的 nullifier 会被添加到区块链的 nullifier 累加器中。这个累加器确保 Alice 的账户状态不能重复使用,也就是防止双重支付。
- 例如:
AggregateNullifier = { SchnorrKeys: ["PublicKey_Alice"], Signature: "Signature_Alice", Address: "Alice's_Address_For_Fees" }
当 nullifier 被成功发布到区块链后:
-
Bob 更新账户状态:
- Bob 能够确认他已收到来自 Alice 的 10 个单位的 Coin,并且可以使用新的账户状态来进行下一笔交易。
- Bob 的账户状态会更新为:
AcctState_Bob = { AccountID: "AccountID_Bob", Balance: 30, // 原有的 20 + 收到的 10 PublicKey: "PublicKey_Bob" }
-
Coin 创建:
- Bob 的账户中将会记录这笔转账中生成的新 Coin,表示他从 Alice 那里收到了 10 个单位的资产。
在 Shielded CSV 中的转账流程涉及链下和链上的交互。链下生成 ZKP 证明并进行验证,确保隐私和合法性。链上只需要发布 nullifier,记录账户状态的作废和交易的发生,保障链上数据的安全和不重复使用。整个过程确保了用户之间的转账隐私,同时保证了交易的合法性。
下面是一个基于Sign-to-Contract 的例子,结合具体的交易流程和如何安全地处理随机数与承诺值。
假设 Alice 想要向 Bob 转账一定数量的比特币。在这个过程中,Alice 将使用 Sign-to-Contract 机制进行交易,确保交易的安全性与隐私。
-
创建交易
- Alice 创建一笔比特币交易,指定 Bob 的地址和转账金额。
- 同时,Alice 生成一个随机数 k'(此随机数应该是安全生成且不可预测的)。
-
生成承诺值
- Alice 计算承诺值 c,该承诺值是交易数据的哈希值与随机数的组合,通常可以用如下方式表示: c = H(交易数据 || k')
- 其中 H 是安全哈希函数,交易数据包含了转账金额、接收方地址等信息。
-
签名交易
- Alice 使用她的私钥对交易进行签名。这里的签名是对交易数据的签名,而不是直接对承诺值 c 进行签名: 签名 = Sign(Pv, 交易数据)
- 这里的 Pv 是 Alice 的私钥。
-
发送信息给 Bob
- Alice 将以下信息发送给 Bob:
- 交易数据
- 签名
- 承诺值 c
- 随机数 k'(在此示例中,Alice 将暴露随机数,注意这是需要谨慎处理的,通常建议不直接暴露,可能使用 ZKP 证明等方式)
- Alice 将以下信息发送给 Bob:
-
Bob 验证
- Bob 接收到这些信息后,进行以下验证:
- 使用 Alice 的公钥来验证交易的签名,确保交易数据没有被篡改:Verify(公钥, 签名, 交易数据)
- Bob 计算自己的承诺值 c' :c' = H(交易数据 || k')
- Bob 检查 c' 是否与 Alice 发送的承诺值 c 相同,以确保交易的一致性。
- Bob 接收到这些信息后,进行以下验证:
-
记录交易
- 如果验证通过,Bob 记录下这笔交易,并将交易数据提交到比特币网络,等待确认。
- 随机数的安全性:在实际应用中,Alice 通常不会直接暴露随机数 k'。相反,她可能通过零知识证明 (ZKP) 向 Bob 证明交易的有效性,而不泄露 k' 的完整信息。
- 双重支付防范:Alice 在交易完成之前,不能进行另一笔交易,而使用 ZKP 可以确保 Alice 的操作合法性,防止双重支付。
通过这个 Sign-to-Contract 的例子,可以看出如何结合签名、承诺值和随机数来安全地处理比特币交易。虽然示例中直接暴露了随机数 k',但在实际应用中应采取更为安全的方式,例如使用 ZKP,来保护用户隐私并确保交易的有效性。