Zhuang's Diary

言之有物,持之以恒

AZTEC

AZTEC 是一个高效的隐私协议,它运用了零知识证明(zero knowledge proofs)和同台加密(homomorphic encryption)来处理数值,并可对这些密文做特定的逻辑运算。这些密文可以保存在区块链上,对其做零知识证明验证,且不会泄露其明文。

基础概念

AZTEC 中的 note 是被加密的密文,是运算的基础单元,同时遵照了 UTXO 模型。

互操作特性

所有基于 AZTEC 的 digital asset 都基于同一个智能合约—ACE(AZTEC Cryptography Engine)。ACE 主要有两个功能:1)针对指定的 ERC20 Token 合约生成 Proof 证明;2)验证 Proof,并更新 note 的状态。

交易的隐私程度

隐私性: 交易的所有信息不会被第三方得知。

匿名性: 交易内容是公开的,但是交易双方的身份是保密的。

保密性: 交易内容是保密的,单交易双方的身份则是公开的。

AZTEC 提供交易的保密性。但是通过第三方代理行为或者交易方多账户的行为可以实现交易的匿名性,进而实现完全的隐私性。

功能介绍:

目前AZTEC 提供了7个功能:

1.Join Split(转账)

Join Split 证明能将一个或者多个票据结合或者拆分成为一个或者多个票据,并保证输入和输出的票据综合一致。Join Split 也可以将 ERC20 的明文值转换为 AZTEC note 密文,或者将 AZTEC note 密文转回到 ERC20 明文。

2.Bilateral Swap(互换)

Bilateral Swap 证明能让使用者互换票据。例如用来互换代表两种资产的 note。次证明验证甲方提供的note == 乙方要求的note,同时,乙方提供的 note == 甲方要求的 note

3.Dividend

Dividend 能验证:输入 note 的值 == 输出 note 的值 * PublicFactor

次证明通常用来验证收到的利息是否正确。

4.Mint

Mint 证明让可信的使用者能够增加 note 。例如,在转入账户收到稳定币后,产生一个等值的 AZTEC note。

5.Burn

Burn 证明让可信的使用者能够销毁 note。例如,在转出稳定币给到他人后,销毁一个等值的 AZTEC note。

6.Private Range

Private Range 能验证:if note A 的值 > note B 的值。例如,保证某资产小于另一个资产的值。

7.Public Range

Public Range 能验证:if note A 的值 > int 的值。例如,保证某资产小于某门限值。

隐私资产

AZTEC 提出了 EIP 1724 ,旨在讲隐私资产接口标准化。

AZTEC 示例

伪代码:

1
2
3
if (tradeNotional + assetBalance[buyer] < regulatoryMax) {
//交易可以进行的逻辑内容
}

使用 Private Range 证明:

1
2
3
4
5
6
7
const {
proofData,
} = await aztec.proof.privateRange.encodePrivateRangeTransaction({
originalNote: regulatoryMax,
comparisonNote: postTradeUserBalance,
senderAddress: accounts[0],
});

以上生成 comparisonNote 的值小于 originalNote 的规则证明。加下来做规则验证:

1
2
3
4
5
6
(bytes memory _proofOutputs) = ACE.validateProof(
PRIVATE_RANGE_PROOF,
address(this),
_proofData
);
//交易可以进行的逻辑内容

一句话概述:以太坊上没有隐私数据

private 声明帮不了你

string private secret;

如上,将变量设置为private只是确保仅通过特定的 function 才能够更改变量的值。但就数据的隐私性“private”而言,这是没用的。基本上,以太坊smart contract上的所有内容都在(所有)节点(即所有矿工和参与者)的硬盘上,私有变量只是为了使阅读不方便,但并不意味着不可能。它不足以保护您的敏感、隐私信息。如下图所示。
而且,smart contract 的代码是透明的,private 的变量声明是很容易被以太坊用户识别的。

1
2
3
4
5
6
0x5 - length of the string
0x68 - h (ASCII code 104)
0x65 - e
0x6c - l
0x6c - l
0x6f - o

private data 上链的方式如下,但是都觉得是隔靴搔痒:

1.hash 上链,作为存证的证明

2.密文上链。

3.时间锁 Time-locked secrets

4.EIP1024

请参考:How to secure Sensitive data on an Ethereum Smart contract?

让我来 step by step 做介绍:

1.编写关于 bid case 的 code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/// This zksnark is to be used to verify that an operator has choosen
/// the lowest bid that was actually submitted by an approved supplier was choosen
/// by the operator for the work. For this proof of concept, we will structure the
/// proof to use 3 suppliers only.

/// Inputs for the SNARK

/// PUBLIC FIELDS
/// input_of_py: field[3] public keys for each supplier - should be EDDSA public keys
/// * A: Curve point. Public part of the key used to create S.

/// PRIVATE FIELDS
/// input_of_py: field[3] actual bid from each supplier
/// input_of_py: Requires per each EDDSA signatures per supplier
/// * R: Curve point. Hidden version of the per-message nonce.
/// * S: Field element. Signature to be verified.
/// input_of_py: a secret to aide in building out the merkel tree of the results

/// * context: Curve parameters used to create S. Tells you which curve was used.
/// The context can be generated from the babyjubjub curve.

/// Returns for the SNARK
/// return: a merkel tree including...
/// 1. private key of choosen supplier
/// 2. secret
/// 3. A: Curve point. Public part of the key used to create S.

// In the vectors, the supplier order should be kept the same to compare/access (i.e. supplier one has first bid, supplier two second bid, etc.)

//For signatures
import "ecc/babyjubjubParams.code" as context
import "signatures/verifyEddsa.code" as verifyEddsa
//For unpacking bids correctly
import "utils/pack/unpack128.code" as unpack128
import "utils/pack/pack128.code" as pack128
import "utils/pack/nonStrictUnpack256.code" as unpack256
//For unpacking signatures correctly
import "utils/pack/pack128.code" as pack128
//For merkle tree - changed to padded
import "hashes/sha256/512bitPadded.code" as sha256

def main(private field[6] R, \
field[6] A, \
field[3] S, \
private field secret, \
private field bidone, \
private field bidtwo, \
private field bidthree) -> (field[2]):

//Since each message is constructed from the bids, no way to input_of_py a faulty bid

//Reconstruct messages:
//Note: Field element can be a value between 0 and p (p upper limit: 2^254)
//Makes an assumption that every bid will be 64 bit number
//Message in the signature should be 512 bits

field[128] bidoneunpacked = unpack128(bidone)
field[512] bidonemessage = [...[0; 384], ...bidoneunpacked]
field[128] bidtwounpacked = unpack128(bidtwo)
field[512] bidtwomessage = [...[0; 384], ...bidtwounpacked]
field[128] bidthreeunpacked = unpack128(bidthree)
field[512] bidthreemessage = [...[0; 384], ...bidthreeunpacked]

context = context()

// Supplier 1
1 == verifyEddsa(R[0..2], S[0], A[0..2], bidonemessage[0..256], bidonemessage[256..512], context)
// Supplier 2
1 == verifyEddsa(R[2..4], S[1], A[2..4], bidtwomessage[0..256], bidtwomessage[256..512], context)
// Supplier 3
1 == verifyEddsa(R[4..6], S[2], A[4..6], bidthreemessage[0..256], bidthreemessage[256..512], context)

//Lowest bid should correllate with supplier
//To Do: write the simplest implementation to simplify, each of the below would be a range proof
field[3] lowestbidder = [...[0; 3]] //Save supplier and the lowest bid
lowestbidder = if bidone < bidtwo && bidone < bidthree then [A[0], A[1], bidone] else [...[0; 3]] fi
lowestbidder = if bidtwo < bidone && bidtwo < bidthree then [A[2], A[3], bidtwo] else [...[0; 3]] fi
lowestbidder = if bidthree < bidone && bidthree < bidtwo then [A[4], A[5], bidthree] else [...[0; 3]] fi

//Now we have the lowest bid, supplier sig verified, and secret --> ready for the merkle tree
//512bitpadded - same merkle tree as ethereum
//Leaf One: lowest bid + secret
//Makes an assumption that every secret will be 64 bit number
field lowest = lowestbidder[2]
field[256] lowestbidunpacked = unpack256(lowest)
field[256] secretunpacked = unpack256(secret)
field[256] leafone = sha256(lowestbidunpacked, secretunpacked)

//Leaf Two: Curve point A: A[0] + A[1]
//We know that A[0] and A[1] will be 64 bit numbers, need to unpack to 256
field[256] a0unpacked = unpack256(A[0])
field[256] a1unpacked = unpack256(A[1])
field[256] leaftwo = sha256(a0unpacked, a1unpacked)

//Build the root of leaf one + leaf two
field[256] root = sha256(leafone, leaftwo)

//Before returning, compress back to field element root - split off 128 bits
res0 = pack128(root[..128])
res1 = pack128(root[128..])

return [res0, res1]

2.编译

zokrates compile -i verifybid.code

生成 out 和 out.code 文件。其中 out 文件为 102M,out.code 文件为 60M,文件 size 较大。

3.设置

zokrates setup

生成 proving.key 和 verification.key 文件。其中 proving.key 为 121M,文件较大。

4.执行程序

zokrates compute-witness -a 10055980565918579776429468085502320403896861311334950973306171539247124720876 9872212025624827705538064352986283580914984702567748676648365783032194062540 15914440638590293656936968551196129378835493705090190454426890248467449623197 7164401624892807842686423552737135338148312648059491863415011813391162897156 17656893400745400645892949581714190787095935883004706460374622673779099869831 13238137875306891162631161684759202257598937454005976664682588856632564854848 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 6460329147541296150725376868570071708599740324087744825213520062979428542760 8063340131639693693865282091868641282847827816145781069349300386706996098818 21548706238088528999687556989620717323700520797702367873886065001951944889761 45 500 400 300

-a 后面为执行程序的参数,即对应着verifybid.code中的入参 private field[6] R, field[6] A, field[3] S, private field secret, private field bidone, private field bidtwo, private field bidthree

其中的 500,400,300 即为 3 方的报价,此处可在前序的智能合约中编写为下面的逻辑:

party one,输入 500,执行 smart contract,

party two,输入 400,执行 smart contract,

party three,输入 300,执行smart contract,

smart contract 收集到法定报价数量之后,verifier 端收到 event,执行 step5 和 step6。

其余参数内容来自inputs_of_command_paras

5.生成证明

zokrates generate-proof

生成 witness 文件。其中 witness 文件为 82M,文件较大。

6.导出 solidity verifier

zokrates export-verifier

生成Verifier.sol

7.编写合约

编写Bid_operator.sol合约,引用 verifyTx 函数。

8.使用 truffle 部署合约

2_deploy_Verifier.js3_deploy_Bid_operator.js 作为配置文件。

9.测试

test/bid_operator.js 中的a, b, c 和 input 均来自于proof.json

回头来总结一下:

1.本次的 bid case 的目的为验证operator选择了最低的 bid 报价,并且验证了该 bid 为 supplier 所提供;

不能证明 supplier one,two,three 之间互相看不到其他 supplier 的 bid。

2.zokrates默认的证明算法为 G16根据 zokrates 的说明,在使用了签名和 ethereum address 作为proofs 证明的情况下,G16 是安全的。并且 G16 无需引用libsnark,生成 witness 和 proof 的速度大大加快,使用配置文件的 size 大大降低。

Zokrates

ZoKrates是以太坊上zkSNARKs的工具箱。它可以帮助您在DApp中使用可验证的计算,从高级语言的程序规范到生成计算证明,以及在Solidity中验证这些证明。

闲话少叙,开搞

1.编写 .code 文件

.code 文件是证明的逻辑内容,函数的返回内容为 field、field[n]。

2.1 compile,编译 .code 文件,Zokrates内部生成数字电路,没有指定文件名称的话,默认生成 ./out.code

./zokrates compile -i root.code

2.2 perform the setup phase,进行安装。为 ./out.code 中的已编译程序生成可信设置。在./proving.key和./verifying.key处创建一个证明密钥和一个验证密钥。这些密钥源自随机源,通常称为“有毒废物”。任何有权访问随机源的人都可以生成假证明,该证明将由协议后的验证者接受。

./zokrates setup

2.3 execute the program,执行证明。计算在./out.code中找到的已编译程序的witness和程序的参数。witness是变量的有效分配,包括计算结果。-a 后面跟着程序的参数,用空格分隔。
在./witness创建一个witness文件

./zokrates compute-witness -a 337 113569

2.4 generate a proof of computation,生成 proof(证明)。使用./proving.key中的证明密钥,生成./out.code的证明,从而得到./witness。

./zokrates generate-proof

2.5 export a solidity verifier,使用./verifying.key上的验证密钥,生成一个Solidity合约,其中包含生成的验证密钥和公共函数,以验证./out.code上已编译程序的解决方案。
在./verifier.sol被创建。

./zokrates export-verifier

3.通过验证者合同,可以检查此证明。例如,使用web3,调用将如下所示:

1
Verifier.at(<verifier contract address>).verifyTx(A, A_p, B, B_p, C, C_p, H, K, [...publicInputs, ...outputs])

A, A_p, B, B_p, C, C_p, H, K的值,以及publicInputs和 outputs 均来自于步骤2.4中生成的proof.json文件。