Zcash 不透明(加密)交易的分析

Zcash 不透明交易中未花费的交易使用Note表示,对应于透明交易中的UTXO。 Note分为SproutNote和SaplingNote不同的数据结构,对应了两套处理流程,在版本 OverWinter以及该版本前都是使用SproutNote,在版本Sapling加入了SaplingNote, 目前代码中是这两套并存,以SproutNote为主,SaplingNote未使用,之后会转向 SaplingNote。

  1. SproutNote 和 SaplingNote 区别
    下面是Note的数据结构。

    基于Note的数据结构有两套不同的机制,这两套不同机制主要区别点如下:
    (1)涉及到的key不同,如下图所示。

    其中双竖线表示相同,箭头表示生成关系,A 指向B, 表示A可以生成B。
    (2)nullifier生成方式不同。

    每个Note都对应一个nullifier,如果一个Note使用了,链上就会记录它对应的nullifier,如果没有使用链上就不会存在,这是避免双花。

    1)对于SproutNote:nullifier基于以下值生成: spendkey `//用户秘钥`rho `//每个SproutNote唯一标识`
    见代码:uint256 SproutNote::nullifier(const SproutSpendingKey& a_sk) const {        return PRF nf(a_sk, rho);
    }
    2)对于SaplingNote:
    见代码:boost::optional<uint256> SaplingNote::nullifier(const SaplingFullViewingKey& vk, const uint64_t position) const{.......}
    

    nullifier生成与SaplingFullViewKey以及position值相关,其中postion在是交 易相关的承诺在承诺树中的位置。

对于 zcash 钱包开发分为两个模块(需要用户上传 IncomingViewKey):

  1. 解析交易:
    服务端基于用户上传的 IncomingViewKey 解析新收到的交易,ES 保存更新 与用户相关的为花费的 UTXO,和 Note 数据。

  2. 构造交易:
    客户端构造涉及不透明地址交易,基于私钥完成 nullifier 计算,零知识
    证明计算,以及交易签名,然后发送给区块链节点。

2.SproutNote 对应的处理流程

(1)解析交易
交易中对应于不透明地址的数据在 vjoinsplit 属性中, 我们首先使用
IncomingViewKey 生成 RecievingKey,然后使用 RecievingKey 解密 vjoinsplit 生成相应的 SproutNote。
具体代码调用如下:
见代码 src/wallet/wallet.cpp

入口函数是 FindMySproutNotes(…),函数遍历数组 tx.vjoinsplit,对于数组的 每个元素 tx.vjoinsplit[i]生成 hSig,然后对于 tx.vjoinsplite[i]的子元 素,使用节点钱包存放的各个 paymentaddress 依次尝试去调用 GetSproutNoteNullifier(…)去解密。其中图中 address = item.first 就是获取到的其中一个 paymentaddress。函数 GetSproutNoteNullifier 如下图:

函数完成解密数据, 构建 note,然后基于 SproutSpendingKey 生成 nullifier,我们可以不用关注 nullifier 生成,因为在使用 note 构造交易时 会重新生成 nullifier。所以关注在函 SproutNotePlaintext::decrypt(…)。 该函数首先调用了 NoteEncryption::decrypt(…)所以先看该函数, 如下图:

上图中,调用函数 cyption_scalarmult()生成 dhsecret,输入了参数 sk_enc.begin(),即字符数组 sk_enc 首地址,而字节数组 sk_enc 是 recievingkey, 来源于 IncomingViewKey,在第一部分中可以看到 IncomingViewKey 是由 apk, sk_enc 组成,一共 64 字节,他们各占 32 字节。 回到函数中,调用完 cyption_scalarmult()经过一系列调用追后使用crypto_aead_chacha20poly1305_ietf_decrypt(…)解密,解密结果是字节数组 plaintext。
现在我们回到函数 SproutNotePlaintext::decrypt(…),如下图:

我们看到他调用了 NoteEncryption::decrypt(…)获取了字节数组 plaintext, 然后使用初始化了字节流 ss, 最后使用字节流创建了 SproutNotePlaintext 对 象。而 SproutNotePlaintext 如下图:


该类中包含了创建 SproutNote 的 rho, r,其中 value、momo 在父类 BaseNotePlaintext 有定义,同时我们可以从 IncomingViewKey 中获取 payingkey,所以基于值 payingkey, r, rho, value 就可以生成 SproutNote,表 示一个未花费的交易。

(2)生成交易

生成交易需要用户的私钥,所以在客户端完成交易构造,交易签名,然后把交易发送给区块链节点。交易涉及到透明地址和不透明地址交叉交易,这里 只介绍 zaddress ==> zaddress, 对于与 taddress 相关的与和 bitcoin 类似。 首先 Trasaction 数据结构,创建交易其实是填充该数据结构对象。

sprout 加密交易主要是对属性 vjoinsplit 的构造赋值,而构造 vjoinsplit, 核心是构造 JSDescription, 该数据结构如下:

在上图中 nullifiers 对应交易输入的为花费的 SproutNote, ciphertexts 记录 了交易的输出 SproutNote。ciphertexts 是一个长度为 2 的数组,每个数据都 是 Ciphertext 的对象,该对象封装了输出交易的 SproutNote 信息,他基于 ephemeralKey,transmissingKey, joinSplitPubKey 的 hash 值做加密,可以 基于 ephemeralKey,receivingKey, joinSplitPubKey 做解密。

见代码 src/wallet/ asyncrpcoperation_sendmany.cpp
源码中类 asyncrycoperation_sendymany 实现了交易的生成、签名、发送。该 类接收交易的输入地址,输出地址,交易金额,然后打包成一个待操作的行为 对象,放到操作队列里等待调用,调用时会回调到该类的 main_impl()方法。 首先看该类的构造函数,如下图:

  参数解释:contextualTx: 模板交易对象,初始化了交易的费用,交易的度,交易的版本等基本信息。fromAddress: 交易的输入地址。tOutput: 透明交易的输入地址,zOutput: 不透明交易地址。 其他的定义了费用、最小深度、上下文信息。其中 SendManyRecipient:std::tuple<std::string, CAmount,std::string>

下面介绍 main_impl(),函数实现了透明地址给透明地址打钱、透明地址给不透 明地址和透明地址打钱(可不含),不透明地址给不透明地址和透明地址打 钱。透明地址相关的和比特币流程相似,所以关注在交易发起方是不透明地 址,由于函数较大所以分块介绍:

如上图所示,如果交易发送方是不透明地址,则调用函数 find_unspend_note(),该函数是查询节点数据库返回该地址受到的未花费的 SproutNote。并且按照 Note 中金额降序,结果保存在对象属性 z_input_中。

如上图中

  1. 调 用 cypto_sign_keypair, 生 成 公 私 钥 对 joinSplitPubKey,
    joinSplitPrivKey_,同时给交易对象 mtx 属性 joinSplitPubKey 赋值。
  2. 初始化交易输入队列 zInputsDeque, zOutputDeque。

下一步:

创建关键数据结构 AsycJoinSplitInfo info, 并且初始化部分属性,接下来大 部分步骤目的都是初始化该对象,该数据结果如下:

接着:拆解输入队列 zInputsDeque,分别用 vInputNote 保存输入 SproutNote,用 vOutPoints 保存输入交易对应的其在上笔交易中的信息,同时 将 vInputNote 数据信息复制到 info.notes。如下图

然后:如下图取出输入队列的第一个节点,获取转账金额, 给 info.vpub_new 赋值。(这里涉及的多地址的就不展开了 )

如果上一步取值的交易的输出地址不为空,这用它创建 JSoutput 对象, 加入到队列 info.vjsout

最后:在把交易进行签名发送出去之前,调用了函数 perform_joinsplit (),在改函数里不仅完成了 info.vjinput 的初始化,同时完成了输入 Note 相关 nullifiers 的计算,零知识证明计算,最终生成了 JSDescription 就是我 们最初一直在构建的数据结构。该函数如下图:



而函数 JSDescription::Randomized(…)最终会调用到函数如下图:

上图中 prove 完成了 nullifier,承诺,零知识证明的计算等。 最后使用私钥对交易进行前签名,签名这部分没有太多变化。
ps:这些代码是2018年8月份整理的,与现在线上的代码可能有些出入,但是区别不大。

3.SaplingNote 对应的处理流程。

(1)接收数据:

流程与 Sprout 类似, 返回 SaplingNote。

见代码 src/wallet/wallet.cpp

解密的是 vShieldedOutput 中数据 encCiphertext,使用的 SaplingIncomingViwingKey。
解密函数如下图:

Ps: AttemptSaplingEncDecryption (…)中的librustzcash_sapling_compute_cm(…)是为了验证收到的commitment是否合法。AttemptSaplingEncDecryption(…)返回结果:

d:  Note对应的地址的组成参数。
rcm: Note对应的commitment。
value_: 金额。
memo_: 备注。

结点会为每一个Note 生成一棵Sapling Commitment Tree, 是一棵Merkle树, 该树存放了历史所有commitment,当然废弃的加密地址交易中涉及的commitment不包含,然后在向该Sapling Commitment Tree添加该Note对应的commitment。同时可以获取到添加commitment在该树中的位置position。之后在接收的区块中包含加密地址交易时,会不断更新Sapling Commitment Tree。
在使用该Note发送交易时,会使用该Note对应Sapling Commitment Tree中三个值:

anchor:  Sapling Commitment Tree的root值。
position:  Note的在改树中的位置。
witness:  Sapling  Commitment  Tree中每个节点,以及节点的位置信息的集合。//ToDo这块我没有很好的方案。

(2)发送数据:

Zcash中类TransactionBuilder用于构造SaplingNote相关的交易, 该函数主要完成的也是nullifier, commitment,zproof等值计算。需要使用ExpendedSpendKey, FullViewingKey。ExpendedSpendKey由SpendKey生成, FullViewingKey由ExpendedSpendKey生成。
我使用了go语言完成了交易的发送,所以代码以go语言切入吧。

  1. 准备工作:
    zcash使用库librustzcash.a来计算零知识证明相关的一系列值以及证明验证等, zcash官方提供了librustzcash.a的源码在工程librustzcash。
    工程目录:https://github.com/zcash/librustzcash。
    该工程提供的外接的头文件是librustzcash.h。该文件中好多的接口函数返回值是bool, 如果使用的语言(比如go)调用倾向于C, 而C语言中没有bool, 就需要把返回值该为其他值,比如int, 同时修改librustzcash中源码文件rustzcah.rs.重新编译出librustzcash.a。该工程的语言是rust,使用cargo build --release 编译。
    加密使用了库Libsudium中的函数。因此需要去其官网下载对应的源码,进行编译。使用/libsodium-stable/src/libsodium/.libs/libsodium.a
  2. 交易数据结构中Sapling对应的数据。
    交易的数结构如下:

这次我们需要构造的数据结构在上图中红框内:

    ValueBalance:      交易的矿工费。VShieldedSpend:    交易输入的描述信息,这里指Sapling版本加密地址交易。VShieldedOutput:   交易输出的描述信息,这里指Sapling版本加密 地址交易。BindingSignature: Sapling加密地址交易的签名。

下面介绍下VShieldedSpend, VShieldedOutput的数据类型。VShieldedSpend是元素结构SpendDecription的数组,SpendDecription结构如下:

    CV:输入Note金额的commitment值。Anchor:构造交易时Sapling Commitment Tree的根hash值。RK: 随机的公钥,与花费的授权签名SendAuthSig相匹配。Nullifier: 该Note的唯一标识,链基于Nullifier值是否出现过来判断Note是否花费。ZKproof: Note可以花费的零知识证明。SendAuthSig:花费的授权签名。

VShieldedOutput是元素结构OutputDecription的数组, OutputDecription结构如下:

 CV: 输出Note金额的commitment值。CM:输入Note的commitment值。EphemeralKey:JubJub的公钥,与节点密文EncCiphertext相匹配。EncCiphertext:输出Note的内容密文,解析该密文可以获取Note的相关信息,例如金额,收款地址的中随机数等。OutCiphertext: 对生成生EncCiphertext过程使用到的密钥等数据成密密文。        Zkproof:输出Note的零知识证明。
  1. 构造交易。
    分为三部分初始化工作、数据基础、交易构建。下面分别展开。
    ①初始化工作:
    Zcash基于库librustzcash.a生成加密地址交易数据生成,而librustzash在使用前需要导入参数文件:sapling-spend.params, sapling-output.params, sprout-groth16.params,使用库函数librustzcash_init_zksnark_params(…)导入。
    然后调用库函数librustzcash_sapling_proving_ctx_int(),获取上下文ctx。
    备注:在之后构造数据过程中如果在使用librustzcash.a库时出错,退出之前需要释放获取的上下文ctx, 使用库函数librustzcash_sapling_proving_ctx_free()。
    ②数据基础:
    交易输入输出地址

     SaplingPaymentAddress{d, //地址组成随机数, 11字节pkd,//transmissionkey, 32字节}ExpendedSpendKey{Ask, Nsk,Ovk,}// ExpendedSpendKey由Ask, Nsk, Ovk组成它们都是32字节
    FullViewingKey{Ak, Nk,Ovk
    }// FullViewingKey由Ak, Nk, Ovk组成它们都是32字节交易输入和输出Note。Note{   d,   // Note对应的地址的组成参数。输出和输入Note取值有一些不同。输入Note:解析之前收到加密地址交易获取,或者也可以解析输入地址获取。输出Note:解析输出地址获取Rcm, // Note对应的commitment。输出和输入Note取值有一些不同。输入Note:解析之前收到的加密交易交易获取。输出Note: 由librustzcash.a中库函数。 librustzcash_sapling_generate_r()生成。value_,//金额。memo_,//备注。
    }
    

    Note在Sapling Commitment Tree中的三个数值。

    anchor:  //Sapling Commitment Tree的root值。
    position:  //Note树中的位置。
    witness: // Sapling Commitment Tree中每个节点,以及节点的位置信息的合。
    

③交易构造
我们现在已有数据的基础上构造只有Sapling版本的加密地址交易,不涉及到透明地址交易。(目前源码里不支持Sapling和Sprout地址件交易,但是与透明地址允许)。在Transaction数据结构中给下图出现的字段赋值:

而ValueBalance,VShieledSpend, VShieledOuput,BindingSignature外的其他字段置空值或者初始值就行。
下面分别构造ValueBalance,VShieledSpend, VShieledOuput, BindingSignature。

=> ValueBalance:直接赋值矿工费就行。
=> VShieledSpend构造。
VShieledSpend是数组结构,每个元素存放节点的描述信息。
描述信息的数据结构SpendDecription,如下图:

==> Anchor值
Sapling Commitment Tree的root值,已有所以我们需要获取其
他字段数值。
==> Nullifier 计算:
调用librustzcash.a里提供的库函数。
librustzcash_sapling_compute_nf(…)生成nullifier值,由
于该函数返回值是bool,cgo 不支持。所以我封装了一层函数:
librustzcash_sapling_compute_nf_int,返回值是0,1。


输入的参数

  diversifier: 输入Note字段dpkd: 交易输入地址SaplingPaymentAddress中字段pkdvalue: 输入Note字段Value,r:输入Note字段Rcm,ak: FullViewingKey里字段Aknk:FullViewingKey里字段Nkposition:Note的在SaplingComitmentTree中位置。res: 存放返回记过值,就是nullifier值。

==> CV, RK, Zkproof值生成:
调用库函数librustzcash_sapling_spend_proof(…)来生成,
cgo调用这里也是封装了一层。

输入参数:

 ak: FullViewingKey里字段Aknsk:ExpendedSpendKey里字段Nskdiversifier: 输入Note里字段drcm: 输入Note里字段Rcmar:  32随机数值,由librustzcash.a中库函数librustzcash_sapling_generate_r()生成。anchor, witness:note对应的Sapling Commitment Tree 的信息。cv,rk,zkproof 用于存放结果值。

到此除了里SpendAuthSign其他都构造完毕。SpendAuthSign值,需在在VShieledOutput构造完毕后才能生成。

=>VShieledOutput构造
VShieledSpend是数组结构,每个元素存放输出节点的描述信息。该描述信息的数据结构OutputDecription。

下面介绍该结构中字段的值的获取。
==> CM值生成:
调用库函数librustzcash_sapling_compute_cm(…)来生成,cgo调用这里也是封装了一层。

输入参数:

 diversifier: 交易输出地址SaplingPaymentAddres字段d。pkd: 交易输出地址SaplingPaymentAddress中字段pkd,value: 输出Note里字段Value。rcm:  输出Note里字段Rcm。cm: 用于存放结果值。

==> EphemeralKey, EncCiphertext值生成:
这块主要完成的是对输出Note数据进行加密,生成密文,给出对应的公钥。
①序列化输出Note,序列化结果值作为明文。

如图,写入一个字节loadingByte值为0x01, 然后依次写入 Diverifier, Value, Rcm, Memo数据。

②获取Note内容加密的公钥epk,私钥esk。字段 EphemeralKey(Esk)赋值值为epk。

在上图中函数LibrustZcashSaplingGenerateR()调用了
库函数:librust_zcacsh_saplinggenerate_r()。
函数 LibrustZcashSaplingKaDerivePublic()调用了
库函数:librustzcash_sapling_ka_derivepublic_int()

③基于第一步的序列化结果值明文,第二步的私钥公钥以及输
出地址中字段pkd,生成密文。

如上图中所示:首先使用私钥Esk和pkd生成私钥dhsecret, 后在计算出加盐加密需要的K值,最后对明文message进行加密,获取密文ciphertext,赋值给字段EncCiphertext。
==> OutCiphertext数值生成。
生成过程与EncCiphertext大致相同,区别点是明文的内容不同,EncCiphertext明文内容是Note内容的序列化字节数组,OutCiphertext明文内容是上步中Esk和pkd,还有加盐加密所需的K值的生成函数不同等区别。在这里就先不展开了。
==> CV和Zkproof值生成。
使用了库函数librustzash_sapling_output_proof,cgo调用这里也是封装了一层。
输入的参数:

esk是之前获取的密钥,
diversifier, pkd, rcm, value取值和前文描述中相同。


=>VShieledSpend里元素的字段SpendAuthSig生成。
使用了库函数librustzash_sapling_spend_sig,cgo调用这里也是封装了一层。
输入参数

 ask是ExpendedSpendKey里的Ask值。ar取值与在Zkproof生成步骤中输出参数ar相同。sighash值为交易序列话后的hash值,这个过程和之前透明地址

交易序列话然后生成hash过程完全相同。

=>BindingSignature值生成。
使用了库函数librustzash_sapling_binding_sig,cgo调用这里也是封装了一层。
参数:

 valueBalance: 取值为交易字段ValueBalancesighash:与上一步同,值为交易序列话后的hash值。res:用于接收结果值

到此完成了对加密地址的交易的构建。
如果加密地址和透明地址之间交易呢?过程是对于加密地址相关的我们按照上面步骤构建对应的字段,而对于透明地址相关的我们按之前分享的透明地址交易构建文中所述来构建对应字段。
Ps: 1.在源码中代码是这样,但是我这边目前只是成功发送加密地址间的交易。
2.源码中明确强调了Sprout版本加密地址和Sapling版本加密地址是不允许交易的。

PS: 寒冬之际,求一个好坑!

Zcash 不透明(加密)交易的分析相关推荐

  1. 透明加密系统设计及实现-绪论

    随着信息时代的到来,计算机成为了人们日常生活的必备之物.大量的桌面计算机和移动终端设备给人们带来了极大的方便,但是也增加的个人的机密信息泄密的可能性.特别是对于机密部门的机密资料和企业公司的核心技术等 ...

  2. 源代码加密软件选型分析

    1源代码加密软件需求背景: 目前很多企业都拥有自己的研发机构,其研发成果往往体现在源代码和技术文档方面,这些核心机密,如何防止研发参与人员泄密,如何防止核心成员把研究成果带走另立山头,或者提供给竞争对 ...

  3. 区块链端对端交易性能分析

    区块链端到端交易性能分析 区块链从微观上来说就是一个不可篡改的可溯源系统,从宏观上来说具有三个特点:共识机制(这个被很多人所研究,包括改进,例如POW.POS.BPFT等等).分布式存储(每个节点对于 ...

  4. linux透明加密系统,基于Linux的透明加密系统的设计与实现

    摘要: 随着互联网时代的到来,人们用于接触事物的方式不仅仅局限于简单的通讯工具了,如:口语交流,书信传递等等,现在已经越来越多地通过手机,电脑,平板等电子设备进行信息的交流和信息的存储.但是在我们享受 ...

  5. dlp防泄密系统卸载_浙江好用的企业图纸防泄密软件推荐,局域网内部图纸透明加密方案...

    企业图纸防泄密 浙江加密软件 图纸透明加密方案 浙江优秀企业众多,企业图纸防泄密已经成为企业网络安全管理的重要方面,很多浙江的企事业单位为了保护单位图纸信息安全和局域网内部图纸信息安全而纷纷采用各种数 ...

  6. 【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application 二 )

    文章目录 一. ActivityThread 中的 installProvider 方法 ( 创建 ContentProvider 内容提供者 ) 二. installProvider 方法的第三分支 ...

  7. python中tushare数据可以导出嘛_Python与交易策略分析tushare/baostock库介绍(附代码)...

    前言:金融数据中最典型的就是资产价格的涨跌情况.想要分析金融资产的投资策略,第一步就是历史数据的获取.tushare正是为导入历史金融资产数据而开发,它大大方便了用Python进行金融资产交易策略的探 ...

  8. 关于文件透明加密解密

    1.windows文件系统概述 硬盘文档加密系统驱动原理一般有两种:通过HOOK一些底层的系统内核调用对文件目录的访问权限进行控制,在文件系统输入输出驱动上层直接建立一个驱动对文件目录的数据进行加密. ...

  9. mysql实现内容加密_简单为mysql 实现透明加密方法

    一般用户在数据库中保存数据,虽然数据库存储的是二进制,无法直接明文打开查看,但是如果是一个外行人,直接连接进入mysql中,还是可以直接查看数据的. 所以对于一些核心数据,特别是企业重要数据资产,一般 ...

最新文章

  1. linux查找技巧: find grep xargs linux系统信息查看大全
  2. 理解 RXSwift:单元测试(四)
  3. getLastSql()用法
  4. java(3)——数据类型中的数值型的整数类型
  5. 参数化的RBAC模型
  6. docker mysql配置 丢失_Ubuntu16.04服务器环境配置 – Docker、MySQL、Redis
  7. 支付宝:“答答星球”小程序上线20天累计参与人数超2亿
  8. 1.7见识一下什么叫Linux驱动:LED
  9. PSD分层立体数据模板立体数据统计素材
  10. 每日两道前端面试题20190226
  11. 算法: 最大矩形面积85. Maximal Rectangle
  12. 文本聚类分析算法_集成聚类系列(三)图聚类算法详解
  13. TMOD、TCON、SCON、PCON、SBUF寄存器说明
  14. 程序员的自我修养之数学基础10:超定方程的求解
  15. 视频消重软件百度云 小视频修改md5
  16. mysql辅助索引非叶子节点_Mysql的聚集索引与辅助索引
  17. Ubuntu下如何查看已安装软件版本
  18. 迷你世界滑动方块机器人怎么做_迷你世界中滑动方块怎么做?
  19. 士兵队列训练问题 (队列)
  20. verilog中数组的定义_Verilog 常规数据定义

热门文章

  1. 【网络安全】隐藏网站后台的另一种思路
  2. 阿里云mysql主从有延迟怎么办_MySQL主从数据库同步延迟问题解决
  3. 2020 阿里最新面试题,掌握这些轻松拿offer
  4. wsl(Windows Subsystem for Linux)启动多个子系统和设置默认启动子系统
  5. 【Linux】一文简单了解操作系统在硬件中的作用,解析操作系统是做什么的?
  6. python学习笔记-ipc机制 (进程间通信)及其代码实现
  7. 电磁阀的分类及应用范围
  8. Oracle Database SQL Language Reference 笔记(3)—— 伪列(续)
  9. 怎么讲d 盘里的软件弄到桌面_桌面如何存文件到D - 卡饭网
  10. spring boot 项目打包时报错 Execution default of goal org.springframework.boot:spring-boot-maven-plugin