ZCash协议Sprout、Sapling、Orchard版本的分析

在zerocash的论文中,提出的基本构造由:承诺(commitment)、无效符(nullifier)、简洁非交互零知识证明(zk-SNARK)、签名(signature)、带内秘密分发(in-band secret distribution)等。

本文介绍ZCash的角度为:从ZCash目录结构入手,分析各个参数以及函数的意义,以及我作为小白初看ZCash协议的一些困惑的解答。

目前发布的是初步的版本,我将会继续跟进细化这篇文章。

在阅读本文前,可能需要掌握的知识有:
1.区块链是一个难以篡改的公共信息存储平台
2.UTXO交易结构
3.零知识证明的基本原理(建议阅读:https://github.com/sec-bit/learning-zkp),以及zk-SNARK是干啥的
4.稍微看了下ZCash Protocol,知道ZCash是干啥的(截止本文最后更新,ZCash最新的协议文档版本为[NU5]:https://zips.z.cash/protocol/canopy.pdf

ZCash基于UTXO的转账交易基本可以概括为三个步骤,以Alice给Bob转账为例:
1.Alice证明自己能够花费某个UTXO
2.Alice花费该UTXO,生成一个属于Bob的新UTXO
3.Alice将新UTXO的花费能力转交给Bob
接着,Bob就能继续花费这笔新的UTXO。在这之中需要解决以下的问题:
1.Alice已花费的UTXO不能被再次花费(解决方法:nullifier)
2.Alice

note

note为交易的基本参数,也可以理解为交易承诺的明文,以下为三个版本的note:
Sprout: n = ( a p k , v , ρ , r c m ) n=(a_pk,v,\rho,rcm) n=(apk,v,ρ,rcm)
Sapling: n = ( d , p k d , v , r c m ) n=(d,pk_d,v,rcm) n=(d,pkd,v,rcm)
Orchard: n = ( d , p k d , v , ρ , ψ , r c m ) n=(d,pk_d,v,\rho,\psi,rcm) n=(d,pkd,v,ρ,ψ,rcm)
参数意义:
ρ \rho ρ用于导出nullifier。同时, ρ \rho ρ也是note的一部分(Sapling除外,但是Sapling中 ρ \rho ρ也是通过note中参数计算得到的,所以总体而言, ρ \rho ρ都与note绑定)。也就是说,从input的角度讲,当发送方需要使用某个UTXO时, ρ \rho ρ确保了nullifier与该UTXO承诺的绑定性,避免了双花攻击。从output的角度来讲, ρ \rho ρ也可以看作是硬币的标识符,标识符作为硬币的秘密,只有知道该标识符的人能够花费该硬币(当然该标识符未出现在已有的nullifier中并且与这个人的公钥地址绑定)。
在Sprout版本中, ρ \rho ρ由随机生成的 ψ \psi ψ、交易output的索引i、签名 h s i g h_{sig} hsig生成: ρ i = P R F φ ρ ( i , h S i g ) \rho_i=PRF_\varphi^\rho(i,h_{Sig}) ρi=PRFφρ(i,hSig), 在Sapling版本中,取消了note中的 ρ \rho ρ,在导出nullifier中使用承诺 c m cm cm和承诺在merkle tree中的位置 p o s pos pos计算 ρ \rho ρ ρ : = M i x i n g P e d e r s e n H a s h ( c m , p o s ) \rho:= MixingPedersenHash(cm,pos) ρ:=MixingPedersenHash(cm,pos)
在Orchard版本中, ρ : = n f o l d \rho:=nf^{old} ρ:=nfold,其中nf是nullifier
d , p k d d,pk_d d,pkd代表用户的公钥地址,用于接收交易,同时作为用户的身份标记,在交易中被混淆。其中d用于推导出椭圆曲线的一个基点 g d g_d gd,其对应的私钥为 i v k ivk ivk,公私钥计算的公式为 p k d : = [ i v k ] g d {pk}_d := [ivk]g_d pkd:=[ivk]gd(将椭圆曲线上的点 g d g_d gd乘以一个标量 i v k ivk ivk,得到点 p k d {pk}_d pkd)。也就是说,对于每一个交易公私钥地址对,其计算的基点都是不同的,扩展了密钥的空间。
v为交易金额
rcm为生成commitment的后门参数:
在Sprout中rcm随机生成:
r c m i ← R N o t e C o m m i t S p r o u t . G e n T r a p d o o r ( ) rcm_i\xleftarrow{R}NoteCommit^{Sprout}.GenTrapdoor() rcmiR NoteCommitSprout.GenTrapdoor()

在Sapling中rcm也随机生成:
r c m i ← R N o t e C o m m i t . G e n T r a p d o o r ( ) rcm_i\xleftarrow{R}NoteCommit.GenTrapdoor() rcmiR NoteCommit.GenTrapdoor()

同时,Sapling中的rseed等同于rcm:
r s e e d : = I 2 L E O S P 256 ( r c m ) rseed:=I2LEOSP_{256}(rcm) rseed:=I2LEOSP256(rcm)

其中I2LEOSP是数据类型转换函数,将整数形式数据转换为字节序列。
在Orchard中rcm由rseed和 ρ \rho ρ以伪随机函数(单向函数)产生:
r c m = T o S c a l a r O r c h a r d ( P R F r s e e d e x p a n d ( [ 5 ] ∥ ρ ) ) rcm=ToScalar^{Orchard}(PRF^{expand}_{rseed}([5] \| \rho)) rcm=ToScalarOrchard(PRFrseedexpand([5]ρ))

其中rseed随机产生:
r s e e d ← R B Y [ 32 ] rseed \xleftarrow{R} \mathbb{B}^{\mathbb{Y}^{[32]}} rseedR BY[32]

dummy note

转账金额为0的交易note。可能会用于一些交易的混淆。

但是文中在提到这一类型的note时。和一种攻击方式联系紧密:精灵的黄金攻击(Faerie Gold attack)。该种攻击通过构造多个虚假交易和一个真实交易,虚假交易转账金额为0,这些交易的总和即真实交易的金额,所以能够通过验证,但是交易的发送方确将虚假交易的note发送给交易接收方,接收方从该note中无法获得交易的金额。ZCash规避这一交易的方式是强迫每一条交易的 ρ \rho ρ都不同,这样即使虚假交易存在,也能通过不同的 ρ \rho ρ与真实交易区分,因此交易的接收方总能获得真实交易的note。

承诺&无效符

承诺(commitment)和无效符(nullifier)是理解ZCash设计的核心,

Sprout版本中交易的金额v和交易地址等信息仅通过一个note commitment计算并上链,。
Sapling版本及之后,原本承诺中的发送金额 v v v被单独作为value commitment计算,这样做的目的是为了将原本的承诺中数值的部分以pedersen commitment的形式承诺,更利于进行同态计算;而其余文本的部分用更为高效的非同态承诺(Sapling版本使用Windowed Pedersen commitments或Orchard版本使用Sinsemilla commitments)形式。

三个版本中value commitment均为 c v : = V a l u e C o m m i t r c v S a p l i n g ( v ) cv := ValueCommit^{Sapling}_{rcv}(v) cv:=ValueCommitrcvSapling(v),即 H o m o m o r p h i c P e d e r s e n C o m m i t r c v S a p l i n g ( D , v ) = [ v ] F i n d G r o u p H a s h J ( r ) ∗ ( D , " r " ) HomomorphicPedersenCommit^{Sapling}_{rcv}(D,v)=[v]FindGroupHash^{\mathbb{J}^{(r)*}}(D,"r") HomomorphicPedersenCommitrcvSapling(D,v)=[v]FindGroupHashJ(r)(D,"r"),note commitment均由对应的n导出(见note部分)。Sprout: c m i = N o t e C o m m i t r c m i S p r o u t ( a p k , i , v i , ρ i ) cm_i=NoteCommit^{Sprout}_{{rcm}_i}(a_{pk,i},v_i,\rho_i) cmi=NoteCommitrcmiSprout(apk,i,vi,ρi)。Sapling: c m = N o t e C o m m i t r c m S a p l i n g ( r e p r J ( g d ) , r e p r J ( p k d ) , v ) cm = NoteCommit^{Sapling}_{rcm}(repr_{\mathbb{J}}(g_d),repr_{\mathbb{J}}(pk_d),v) cm=NoteCommitrcmSapling(reprJ(gd),reprJ(pkd),v)。Orchard: c m x = E x t r a c t P ⊥ ( N o t e C o m m i t r c m O r c h a r d ( r e p r P ( g d ) , r e p r P ( p k d ) , v , ρ , ψ ) ) cm_x=Extract^{\perp}_{\mathbb{P}}(NoteCommit^{Orchard}_{rcm}(repr_{\mathbb{P}}(g_d),repr_{\mathbb{P}}(pk_d),v,\rho,\psi)) cmx=ExtractP(NoteCommitrcmOrchard(reprP(gd),reprP(pkd),v,ρ,ψ))

无效符(nullifier)的目的是表示硬币的花费,这一功能的引入是为了避免双花攻击
Sprout:nullifier通过 a s k , ρ a_{sk},\rho ask,ρ得出: P R F a s k n f S p r o u t ( ρ ) PRF^{nfSprout}_{a_{sk}}(\rho) PRFasknfSprout(ρ)

Sapling:nullifier通过 n k , ρ nk,\rho nk,ρ得出: P R F n k ⋆ n f S a p l i n g ( ρ ⋆ ) PRF^{nfSapling}_{nk\star}(\rho\star) PRFnknfSapling(ρ),其中 ⋆ \star 表示将椭圆曲线上的点转换为整数。

Orchard:nullifier通过 n k , ρ , ψ , c m nk,\rho,\psi,cm nk,ρ,ψ,cm得出: D e r i v e N u l l i f i e r n k ( ρ , ψ , c m ) = E x t r a c t P ( [ P R F n k n f O r c h a r d ( ρ ) + ψ ) m o d    q P ] K O r c h a r d + c m ) DeriveNullifier_{nk}(\rho,\psi,cm)=Extract_\mathbb{P}([PRF_{nk}^{nfOrchard}(\rho)+\psi)\mod q_\mathbb{P}]\mathcal{K}^{Orchard}+cm) DeriveNullifiernk(ρ,ψ,cm)=ExtractP([PRFnknfOrchard(ρ)+ψ)modqP]KOrchard+cm).

签名

对于签名密钥 a s k ask ask首先为了避免重放攻击,引入随机化因子
生成随机化的签名密钥:
,也就是 r s k = α ∗ a s k , r k = [ r s k ] P G rsk=\alpha*ask, rk=[rsk]\mathcal{P}_\mathbb{G} rsk=αask,rk=[rsk]PG。使用rsk对交易的hash结果进行签名

带内秘密分发(in-band secret distribution)

在交易过程中,交易的接收方如果想要花费某个硬币,需要知道该硬币的标识符,所以交易发送方需要将标识符通过某种加密方式发送给接收方。我们用np表示这些秘密的明文。

Sprout: n p = ( 0 x 00 , v , ρ , r c m , m e m o ) np=(0x00,v,\rho,rcm,memo) np=(0x00,v,ρ,rcm,memo)

Sapling: n p = ( l e a d B y t e , d , v , m e m o ) np=(leadByte,d,v,memo) np=(leadByte,d,v,memo)

Orchard: n p = ( l e a d B y t e , d , v , r s e e d , m e m o ) np=(leadByte,d,v,rseed,memo) np=(leadByte,d,v,rseed,memo)

参数意义:其中leadByte表示使用的不同版本的协议;d在上文的note中解释过,为公钥地址的生成基点;v为交易金额;memo为交易的备注信息;rseed和 ρ \rho ρ可以推导出 r c m , ψ , e s k rcm,\psi,esk rcm,ψ,esk

Sapling encryption:

  1. 随机生成esk:

  2. 生成一次性对称加密密钥 K e n c = [ e s k ] p k d K^{enc}=[esk]pk_d Kenc=[esk]pkd(注:这里的 p k d = [ i v k ] g d pk_d=[ivk]g_d pkd=[ivk]gd)。

  3. 使用 K e n c K^{enc} Kenc加密np获得密文 C e n c C^{enc} Cenc

  4. (此步可选)使用 o v k , c v , c m , e s k ovk,cv,cm,esk ovk,cv,cm,esk(其中ovk为接收方的ovk)导出对称密钥ock:
    (其中ephemeralKey就是epk转化为字符的格式),使用ock加密op:

  5. e p k , C e n c , C o u t epk,C^{enc},C^{out} epk,Cenc,Cout发送给接收方。

Orchard encryption:

除了第一步中esk由rseed和 ρ \rho ρ推导出,其他步骤与Sapling相同。

Sapling\Orchard decryption:

接收方通过 i v k , e p k ivk,epk ivk,epk获得对称密钥 K e n c = [ i v k ] e p k = [ e s k ] p k d K^{enc}=[ivk]epk=[esk]pk_d Kenc=[ivk]epk=[esk]pkd解密 C e n c C^{enc} Cenc获得np。Sapling中通过 r c m , v rcm,v rcm,v和自己的 p k d , g d pk_d,g_d pkd,gd计算承诺cm:
,通过承诺cm计算 ρ \rho ρ
,即获得硬币的使用权;Orchard中直接从np中获得 ρ \rho ρ,即获得硬币的使用权。

接着,接收方可以通过出示ovk解密得到 p k d , e s k pk_d,esk pkd,esk,进一步可以解开 C e n c C^{enc} Cenc(ovk的作用是在不暴露私钥ivk的基础上打开 C e n c C^{enc} Cenc

密钥结构

上图的每一个密钥的作用均在文中其他部分提及,除了sk,sk是用于派生其余所有密钥的密钥,本身没有用在交易的过程中。

以下说明Sapling和Orchard两个版本的内部派生密钥的生成区别,内部派生密钥用于在转帐中生成找零的地址,个人认为内部派生密钥是相对的,假如我进行了一笔转账a,a中生成了找零的内部密钥ka,那么我使用ka接着进行转账b,那么对于b而言,ka就是外部密钥。

在这里插入图片描述

Sapling和Orchard的变化在于Orchard只产生nk而无nsk,而Sapling产生的nk是由对应的私钥nsk派生而来,同时导出的内部密钥组中nk和外部密钥中的nk相同。nk用于生成nullifier,在Sapling中需要经过以下的zk-SNARK证明:
,在这里使用公私钥的方式去证明nullifier的目的是引入随机性,换言之,即使有相同的 ρ \rho ρ,也能得出不同的nullifier,所以每生成一个内部密钥都要重新生成一个nk公私钥对;而在Orchard中,巧妙地使用了 ψ \psi ψ解决了这一问题:
,其中
,我们可以看到, ψ \psi ψ在这里起到了随机化的作用,即使使用了相同的 ρ \rho ρ也会得到不同的nullifier,同时,由于减少了zk-SNARK中证明nk和nsk关联的这一步,这一优化也提高了运算效率

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值