区块链交易隐私如何保证?华为零知识证明技术实战解析
【摘要】 零知识证明技术不管应用于金融还是其他领域,都可以对隐私保护,性能提升,或者安全性等场景带来很多帮助。本文通过介绍华为如何在同态加密及零知识证明框架的集成介绍来介绍了一些对金融领域交易隐私保护的思路,通过代码结和应用场景描述了 zksnark 如何集成到现有联盟链体系保护交易隐私。以及目前零知识证明领域应用的技术挑战,华为是通过什么样的技术解决当前的挑战问题的,并提出对未来区块链的隐私保护思路。
什么是零知识证明?
零知识证明应用于同态加密保护交易隐私,使能金融业务
使用同态加密库的
func transaction() error { addrA := calcAddr(userdata.PubKey) setup := &sdk_client.BaseSetupImpl{ ConfigFile: conf, ChannelID: channelid, OrgID: orgid, ConnectEventHub: false, ChainCodeID: idchaincode, } if err := setup.Initialize(); err != nil { fmt.Println("fail to init sdk: ", err.Error()) return errors.New("fail to init sdk: " + err.Error()) } setup.ChainCodeID = txchaincode transRec := sdk_client.TransRecord{} resps, err := sdk_client.Query(setup, "QueryBalance", [][]byte{[]byte(addrA)}) if err != nil { fmt.Println("Fail to query balance of sender: ", err.Error()) return err} err = json.Unmarshal(resps[0].ProposalResponse.GetResponse().Payload, &transRec) if err != nil { fmt.Println("fail to unmarshal balance result: ", err.Error()) return err} var pubKeyB string setup.ChainCodeID = idchaincode resps, err = sdk_client.Query(setup, "QueryPubkey", [][]byte{[]byte(addrB)}) if err != nil { fmt.Println("Fail to query pubkey of receiver: ", err.Error()) return errors.New("Fail to query pubkey of receiver: " + err.Error()) } pubKeyB = string(resps[0].ProposalResponse.GetResponse().Payload) fmt.Println("Get B's ID successfully") cipherBalanceAKeyA := transRec.Balance txInfoSer, err := pswapi_sdk.PrepareTxInfo(cipherBalanceAKeyA, tx, userdata.PubKey, pubKeyB, userdata.PriKey, propwd) if err != nil { fmt.Println("fail to prepare tx info: ", err.Error()) return errors.New("fail to prepare tx info: " + err.Error()) } setup.ChainCodeID = txchaincode_, err = sdk_client.Invoke(setup, "Transfer", [][]byte{[]byte(addrA), []byte(addrB), []byte(txInfoSer)}) if err != nil { fmt.Println("Invoke Transfer error for user: ", addrA, err.Error()) return errors.New("Invoke Transfer error for user: " + addrA + err.Error()) } return nil}
图 3 是同态加密的实现业务代码,首先写一个 transaction 的方法,返回值是 error。第一步需要进行初始化获取 sdk_client 对象也就是 setup 变量。然后通过 sdk_client.Querry 方法基于 key“QueryBalance“查询账户 A 的加密余额,同样的方法基于 key "QueryPubkey"拿到 b 的公钥。第二步 PrepareTxInfo 方法构建 A 向 B 的转账信息,最后一步通过 invoke 调用完成 A 到 B 的转账的过程。
func (t *TransChaincodeDemo) transfer(stub shim.ChaincodeStubInterface, args []string) pb.Response { AddrA := args[0] AddrB := args[1] txInfo := args[2] if strings.Compare(AddrA, AddrB) == 0 { logger.Error("A' addr is the same B'Addr") return shim.Error("A' addr is the same B'Addr") } transRecA, err := stub.GetState(AddrA) if err != nil { return shim.Error("Failed to get state") } if transRecA == nil { return shim.Error("Entity not found") } var transRecAStruct = TransRecord{} err = json.Unmarshal(transRecA, &transRecAStruct) if err != nil { logger.Error("fail to unmarshal user's trans record") return shim.Error("fail to unmarshal user's trans record") } transRecB, err := stub.GetState(AddrB) if err != nil { return shim.Error("Failed to get state") } if transRecA == nil { return shim.Error("Entity not found") } var transRecBStruct = TransRecord{} err = json.Unmarshal(transRecB, &transRecBStruct) if err != nil { logger.Error("fail to unmarshal user's trans record") return shim.Error("fail to unmarshal user's trans record") } cipherBalanceAKeyABlock := transRecAStruct.Balance cipherBalanceBKeyBBlock := transRecBStruct.Balance newCipherBalanceA, newCipherBalanceB, newCipherTxA, newCipherTxB, err := pswapi_cc.ValidateTxInfo(txInfo, cipherBalanceAKeyABlock, cipherBalanceBKeyBBlock) if err != nil { logger.Error("fail to validate trans information") return shim.Error("fail to validate trans information") } transRecAStruct.Balance = newCipherBalanceA transRecAStruct.TX = newCipherTxA transRecAStruct.TXType = "P" AvalbytesUpdate, err := json.Marshal(transRecAStruct) if err != nil { logger.Error("fail to marshal balance update info") return shim.Error("Marshal Error") } err = stub.PutState(AddrA, AvalbytesUpdate) if err != nil { logger.Error("fail to store state: ", err.Error()) return shim.Error(err.Error()) } transRecBStruct.Balance = newCipherBalanceB transRecBStruct.TX = newCipherTxB transRecBStruct.TXType = "R" BvalbytesUpdate, err := json.Marshal(transRecBStruct) if err != nil { logger.Error("fail to marshal balance update info") return shim.Error("Marshal Error") } err = stub.PutState(AddrB, BvalbytesUpdate) if err != nil { return shim.Error(err.Error()) } return shim.Success([]byte("Success"))}
图 4 同态加密链代码
基于
交互式证明和非交互式证明
图 5 交互式证明
1.女子站在 A 点
2.男子从 B 点走到 C 点或者 D 点
3.男子从 B 点消失后,女子从 A 点走到 B 点
4.女子喊话“从左边出来”,或者“从右边出来”
5.男子按照女子的要求从对应一侧走出
女子说“你肯定作弊,刚才我喊左边出来,你刚好就是从左边进去的”,
男子说:“你回到 A 点,我们再来一遍”
如果每次都成功,说明 B 确实有 CD 处的钥匙,该证明是需要 A,B 不停的交互。
什么是
它是对所有零知识证明问题的通用解决方法,由加密数字货币 zcash 首次使用并开源。zk-SNARK 的优点:
1.通用库,可以解很多零知识证明问题
2.验证证明性能较高(300tps)
zk-SNARK 的不足:
1.底层模型不容易理解,用户需要根据具体的零知识证明问题,在上层构建自己的业务模型,这块开发的工作量较大。
2.生成每笔交易时延较长(57s)
应用场景
zk-snark
华为集成了 zksnark 架构到区块链系统中来解决上面的挑战。我们知道有多种方法可以为区块链启用 zkSNark。这些都降低了配对函数和椭圆曲线操作的实际成本。
提高合约虚拟机的性能
相较第二种更难实现。可以在合约虚拟机中添加功能和限制,这将允许更好的实时编译和解释,而无需在现有实现中进行太多必要的更改。下面的转账场景就是基于此种方案的实现。
仅提高某些配对函数和椭圆曲线乘法的在合约虚拟机的性能
通过强制所有区块链客户端实现特定的配对函数和在特定椭圆曲线上的乘法作为所谓的预编译契约来实现。好处是,这可能更容易和更快地实现。另一方面,缺点是我们固定在一定的配对函数和一定的椭圆曲线上。区块链的任何新客户端都必须重新实施这些预编译的合同。此外,如果有人找到更好的 zkSNark、更好的配对函数或更好的椭圆曲线,或者如果在椭圆曲线、配对函数或 zkSNark 中发现缺陷,必须添加新的预编译合同。
转账应用
Nullifier NF.axxxxx1 和 NF.axxxxx2 是否在 Nullifiers 列表中,也就是说,是否有被花过;
验证 NF.axxxxx1 和 NF.axxxxx2 是格式是否合法的花费凭据,且对应的 commitment 在链上(Proof + Merkle tree root),这里有需要验证 Merkle tree root 在 是有效的;
验证 input == output 金额守恒,即:100 + 0 = 80+0+20;
数字范围满足要求:100-20 >0 && 20 > 0
过程验证完了以后就进入最后一步。完成验证还会做一些类似于交易内容的隐藏,身份隐藏,交易行为的隐藏,来保护整个的这个转账交易过程的安全性,包括做一些混淆电路的能力。混淆交易内容且加密,验证者并不知道使用链上是哪个 Commitment 作为输入,只知道没有被花过,且在链上。身份隐藏让其无法确定接收方是谁,交易行为隐藏让其无法确定这个交易是发送还是接收。基于安全性的保证,才能完成整个验证过程。最后生成交易,然后收款,整个转账过程就结束了。基于零知识证明的转账,被华为集成在零知识证明使用接口中。集成的零知识证明架构也能用来开发一些隐私之类的应用,实现区块链隐私保护的解决方案。
总结
评论