Zhuang's Diary

言之有物,持之以恒

产品设计的关键步骤

AZTEC 产品的关键步骤:1)生成 note;2)管理秘钥;3)验证 note

AZTEC中的aztec.js能够工作在浏览器或者手机程序中,生成 proof 大约需要 10ms,非常高效。

1.生成 note

证明(proof)中的保密 Token 需要用到 note。note 在票据登记所(note registry)中登记,proof 也在 note registry 中登记。

生成一个新的 note 需要 owner 的publicKey,所有人使用其地址证明其 owner 的身份,value是 note 中保密 Token 的值:

bobNote1 = await aztec.note.create(bob.publicKey, 100);

2.管理秘钥

每一个 note 都有一个对应的查看秘钥(viewing key);同时还有一个对应的暂存秘钥(ephemeral key)。暂存秘钥用于恢复查看秘钥,同时 note 的 owner 的 private key 也可以恢复查看秘钥。

在服务器端,恢复查看秘钥并再次发送给到 owner 需要一套完备的管理系统,目前 AZTEC 还没有这样的系统。

在客户端,建议将暂存秘钥加密后保存起来。

3.消费 note

消费 note 使用标准的 Join Split 证明。

1
2
3
4
5
6
7
8
9
const { proofData, expectedOutput, signatures } = aztec.proof.joinSplit.encodeJoinSplitTransaction({
inputNotes: [bobNote1],
outputNotes: [sallyTaxiFee, bobNote2],
senderAddress: accounts[0],
inputNoteOwners: [bob],
publicOwner: accounts[0],
kPublic: 0,
validatorAddress: privateVenmoContract.address,
});

kPublic是被转换的 ERC20 Token 的值。负值代表 ERC20 Token 被消耗,且转换为 note;正值代表将 note 兑换回到 ERC20 Token。且遵守kPublic == outputNotes

Join Split证明是自动使用inputNoteOwners: [bob]的私钥来签名交易的。更加严谨的话可以使用confidentialApprove方法,例如:

1
2
const signature = aztec.signer.signNote(assetAddress, noteHash, spenderAddress, owner.privateKey);
ZkAsset.confidentialApprove(noteHash, spender, true, signature);

最后再使用下面的语句最终消费掉 note:

1
2
3
await privateVenmoContract.confidentialTransfer(proofData, signatures, {
from: accounts[0],
});

AZTEC 的交易流程

如图所示,结合借贷 DApp,分析AZTEC的步骤:

1.借贷 ZkAsset

1.1.构建 proof

用 aztec.js 构建 proof。即 proofData:

1
2
3
4
5
6
7
8
const {
proofData,
} = aztec.proof.mint.encodeMintTransaction({
newTotalMinted: newTotalNote,
oldTotalMinted: oldTotalNote,
adjustedNotes: [loanNotionalNote],
senderAddress: loanDappContract.address,
});
1.2.注册 note

proof 可以被 Mint 成为新的 note,

1
Loan(loanId).confidentialMint(MINT_PROOF, bytes(_proofData));

2.结算 ZkAsset

2.1.批准 ACE 合约代表 owner 花费 ERC20 Token
1
await settlementToken.approve(aceContract.address, value);
2.2.构建 proof
1
2
3
4
5
6
7
8
9
10
11
12
const { 
proofData,
expectedOutput
} = aztec.proof.joinSplit.encodeJoinSplitTransaction({
inputNotes: [],
outputNotes: [Note1, Note2], // note values sum to kPublic
senderAddress: account.address,
inputNoteOwners: [],
publicOwner: account.address,
kPublic: -value,
validatorAddress: joinSplitContract.address,
});
2.3.批准 ACE 合约花费 ERC20 Token
1
2
3
await ACE.publicApprove(zkAsset.address, hashProof, kPublic, {
from: accounts[0],
});
2.4.转发交易

转发 proof 给到 ACE,proof 中的senderAddress与发起调用ACE.validateProof的msg.sender 需保持一致。

1
(bytes memory _proofOutputs) = ACE.validateProof(JOIN_SPLIT_PROOF, address(this), _proofData);
2.5.处理转移指令(Transfer Instructions)
1
_loanVariables.settlementToken.confidentialTransferFrom(JOIN_SPLIT_PROOF, _proofOutputs.get(0));

3.结算贷款处理

3.1.批准结算合约来花费 note
1
2
3
4
5
6
7
8
9
10
11
12
13
const settlementSignature = signNote(
zkSettlementAsset.address,
settlementNoteHash,
loanId,
lender.privateKey);
await zkSettlementAsset.confidentialApprove(
settlementNoteHash,
loanId,
true,
settlementSignature,
{
from: lender.address,
});
3.2.构建 proof
1
2
3
4
5
6
7
const {
proofData,
} = aztec.proof.bilateralSwap.encodeBilateralSwapTransaction({
inputNotes: [takerBid, takerAsk],
outputNotes: [makerAsk, makerBid],
senderAddress: loanId,
});

其中需要takerBid == makerAsk ; takerAsk == makerBid

3.3.终极交易并更新状态
1
2
3
4
5
(bytes memory _proofOutputs) = ACE.validateProof(BILATERAL_SWAP_PROOF, address(this), _proofData);
(bytes memory _loanProofOutputs) = _proofOutputs.get(0);
(bytes memory _settlementProofOutputs) = _proofOutputs.get(1);
settlementZkAsset.confidentialTransferFrom(BILATERAL_SWAP_PROOF, _settlementProofOutputs);
loanZkAsset.confidentialTransferFrom(BILATERAL_SWAP_PROOF, _loanProofOutputs);

由此完成了借贷以及结算,同时全部的账目均为保密。

项目进展

AZTEC目前已经在 ethereum mainnet 上线PoC,应用于 DAI 与 AZTEC Token 的转换,即从 DAI 的明文 ERC20 到密文 AZTEC note 的转换。

目前 AZTEC 也可以实现独立密文 Token 的发布和使用,完全基于密文的 Join Split 交易证明。

目前如果是 2 个输入note,2 个输出note,保密交易的情况下,在 ethereum 的 gas 消耗大概是 900,000gas。如果 EIP1108 上线了的话,gas 消耗大约在 200,000 — 300,000gas 之间。

示例项目 step by step 发布至 Ganache

1.下载,git clone https://github.com/AztecProtocol/aztec-ganache-starter-kit.git

2.安装,cd aztec-ganache-starter-kit && yarn install

3.复制 account 环境,cp RENAME_ME.env .env

4.通过 **package.json **的 script,配合.env 中的 account 配置,启动 Ganache,yarn start

5.通过 **package.json **的 script,按照 truffle-config.js 中的内容,配合migrations中的发布文件,编译合约并发布至 Ganache,yarn migrate

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
➜  aztec-ganache-starter-kit git:(master) ✗ yarn migrate
yarn run v1.16.0
$ truffle compile --all && truffle migrate --reset

Compiling your contracts...
===========================
> Compiling ./contracts/AceContracts.sol
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/TestERC20.sol
> Compiling @aztec/protocol/contracts/ACE/ACE.sol
> Compiling @aztec/protocol/contracts/ACE/NoteRegistry.sol
> Compiling @aztec/protocol/contracts/ACE/validators/adjustSupply/AdjustSupply.sol
> Compiling @aztec/protocol/contracts/ACE/validators/adjustSupply/AdjustSupplyABIEncoder.sol
> Compiling @aztec/protocol/contracts/ACE/validators/bilateralSwap/BilateralSwap.sol
> Compiling @aztec/protocol/contracts/ACE/validators/bilateralSwap/BilateralSwapABIEncoder.sol
> Compiling @aztec/protocol/contracts/ACE/validators/dividendComputation/DividendComputation.sol
> Compiling @aztec/protocol/contracts/ACE/validators/dividendComputation/DividendComputationABIEncoder.sol
> Compiling @aztec/protocol/contracts/ACE/validators/joinSplit/JoinSplit.sol
> Compiling @aztec/protocol/contracts/ACE/validators/joinSplit/JoinSplitABIEncoder.sol
> Compiling @aztec/protocol/contracts/ACE/validators/privateRange/PrivateRange.sol
> Compiling @aztec/protocol/contracts/ACE/validators/privateRange/PrivateRangeABIEncoder.sol
> Compiling @aztec/protocol/contracts/ERC1724/ZkAsset.sol
> Compiling @aztec/protocol/contracts/ERC1724/ZkAssetMintable.sol
> Compiling @aztec/protocol/contracts/ERC1724/ZkAssetOwnable.sol
> Compiling @aztec/protocol/contracts/ERC20/ERC20Mintable.sol
> Compiling @aztec/protocol/contracts/interfaces/AdjustSupplyInterface.sol
> Compiling @aztec/protocol/contracts/interfaces/DividendComputationInterface.sol
> Compiling @aztec/protocol/contracts/interfaces/IAZTEC.sol
> Compiling @aztec/protocol/contracts/interfaces/IZkAsset.sol
> Compiling @aztec/protocol/contracts/interfaces/JoinSplitInterface.sol
> Compiling @aztec/protocol/contracts/interfaces/PrivateRangeInterface.sol
> Compiling @aztec/protocol/contracts/libs/LibEIP712.sol
> Compiling @aztec/protocol/contracts/libs/NoteUtils.sol
> Compiling @aztec/protocol/contracts/libs/ProofUtils.sol
> Compiling @aztec/protocol/contracts/libs/SafeMath8.sol
> Compiling openzeppelin-solidity/contracts/math/SafeMath.sol
> Compiling openzeppelin-solidity/contracts/ownership/Ownable.sol
> Compiling openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
> Compiling openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
> Artifacts written to /home/user/Documents/Aztec/aztec-ganache-starter-kit/build/contracts
> Compiled successfully using:
- solc: 0.5.4+commit.9549d8ff.Emscripten.clang


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.


Starting migrations...
======================
> Network name: 'development'
> Network id: 1564378609369
> Block gas limit: 0x6691b7



1_initial_migration.js
======================

Deploying 'Migrations'
----------------------
> transaction hash: 0x6d94779d277c1f3f02ae8bf1e86ca9c62866f308ccd0834347233d2522ac2306
> Blocks: 0 Seconds: 0
> contract address: 0x31bA6208D85a545C75186c51d0F36B1498Fd70bB
> block number: 1
> block timestamp: 1564379036
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.99580786
> gas used: 209607
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00419214 ETH



> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00419214 ETH




2_ace.js
========


Deploying 'ACE'
---------------
> transaction hash: 0x249b155829f8a7c069009129d008751e8c6547d39e54b961cb07d80beec13c29
> Blocks: 0 Seconds: 0
> contract address: 0x7BeDb6777e6fFe535Fc444217A80edf349DB8623
> block number: 3
> block timestamp: 1564379036
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.9273801
> gas used: 3379461
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.06758922 ETH



Deploying 'AdjustSupply'
------------------------
> transaction hash: 0xc8c52025eb50204fee60bc65cdb7b7abc09345abff8264e0f88b3f4ec80c54f5
> Blocks: 0 Seconds: 0
> contract address: 0x817888dC24398DEE4A53AB6b247b58C3B22F862c
> block number: 4
> block timestamp: 1564379037
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.9142172
> gas used: 658145
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0131629 ETH




Deploying 'BilateralSwap'
-------------------------
> transaction hash: 0xebc929146741026593580155d9d19844c96f65877eb48faa6e43bad2da812db5
> Blocks: 0 Seconds: 0
> contract address: 0xcD53E73dADB6eaD307892C0aBe0bfE5B2f8f570F
> block number: 5
> block timestamp: 1564379037
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.9055876
> gas used: 431480
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0086296 ETH



Deploying 'JoinSplit'
---------------------
> transaction hash: 0x1fa8ac853d1f6c9306418c07e50f85e3fee1a759ccbe79fa5b035fac0083b00c
> Blocks: 0 Seconds: 0
> contract address: 0x77c76635fdBE43afECa6A12cE8588010Ea6f2E02
> block number: 6
> block timestamp: 1564379037
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.8929998
> gas used: 629390
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0125878 ETH



Deploying 'PrivateRange'
------------------------
> transaction hash: 0x5820bd16d8f9b725f2dcfe965dd7d8dd20e80a763ef1a8e7c2a98c35040d05b0
> Blocks: 0 Seconds: 0
> contract address: 0xe052a2Ab6b49af87265e7d4b245B0AbFE470865b
> block number: 7
> block timestamp: 1564379037
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.88160816
> gas used: 569582
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.01139164 ETH



Deploying 'DividendComputation'
-------------------------------
> transaction hash: 0x6154f1716037db818d98ce0293c6370b84fca6977863ce90a5f736451dca4c68
> Blocks: 0 Seconds: 0
> contract address: 0x36cBFA4f87a3771E92D51F41fa0E32d5Ca65f340
> block number: 8
> block timestamp: 1564379038
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.86990106
> gas used: 585355
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0117071 ETH




> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.12506826 ETH




3_ZkAsset.js
============


Deploying 'TestERC20'
---------------------
> transaction hash: 0x166f81eec89ffc816f12dbcb05a581194125df3ab191fc8025c425cd641509e2
> Blocks: 0 Seconds: 0
> contract address: 0x1fE2f08A5D69e6A886B349bd8c26407C0e3Ef89d
> block number: 16
> block timestamp: 1564379039
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.84888398
> gas used: 629044
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.01258088 ETH



Deploying 'ZkAsset'
-------------------
> transaction hash: 0xba9cbfe6a1c5de0126ff7152f12b0b72812c652ad63daa6604bcefd186180a96
> Blocks: 0 Seconds: 0
> contract address: 0x8B02C67c7175a48e1aBA3067b5F5eEA29a051ECd
> block number: 17
> block timestamp: 1564379039
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.81309922
> gas used: 1789238
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.03578476 ETH



Deploying 'ZkAssetMintable'
---------------------------
> transaction hash: 0xd8d3c7e33af3e7ec5dfdb8e3a0ee5d3928b884011d87035443ab229f5c7a4502
> Blocks: 0 Seconds: 0
> contract address: 0x2E63304e1F9f6AD963540A05D3f385426957A2C7
> block number: 18
> block timestamp: 1564379040
> account: 0x909E433c507A398a5405677B5b2767a3f523F73b
> balance: 999.76265604
> gas used: 2522159
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.05044318 ETH


> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.09880882 ETH



Summary
=======
> Total deployments: 10
> Final cost: 0.22806922 ETH


Done in 41.03s.

第一步 1_initial_migration.js

第二步 2_ace.js。发布 ACE,setCommonReferenceString 方法建立零知识系统的配置文档

await aceContract.setCommonReferenceString(constants.CRS);

通过 ACE 的setProof(proofId, address)方法来设定各个执行证明的合约地址。proofId 为 proof 的类别,从@aztec/dev-utils里面取得定义。address为发布的合约地址

1
2
3
4
5
await aceContract.setProof(MINT_PROOF, AdjustSupply.address);
await aceContract.setProof(BILATERAL_SWAP_PROOF, BilateralSwap.address);
await aceContract.setProof(DIVIDEND_PROOF, DividendComputation.address);
await aceContract.setProof(JOIN_SPLIT_PROOF, JoinSplit.address);
await aceContract.setProof(PRIVATE_RANGE_PROOF, PrivateRange.address);

第三步 3_ZkAsset.js。发布零知识资产(ZkAsset)

1
2
3
4
5
6
7
8
9
// initialise the ZkAsset with an equivilant
await deployer.deploy(
ZkAsset,
aceContract.address,
TestERC20.address,
1,
false,
true
);

此处共计 5 个参数:

1.aceAddress — ACE 的合约地址;

2.linkedTokenAddress — 零知识资产所代表的公开的 ERC20 Token 的合约地址,如不代表特定 Token 则可设定为 address(0);

3.scalingFactor — 是表示与代表的 ERC20 Token 的转换比例,此处为 1:1 转换;

4.canAdjustSupply — owner 是否可以修改 note 的 totalSupply;

5.canConvert — 是否可以将保密 note 转换回到公开的 ERC20 Token

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜  aztec-ganache-starter-kit git:(master) ✗ truffle test
Using network 'development'.


Compiling your contracts...
===========================
\> Everything is up to date, there is nothing to compile.


Contract: PrivateVenmo
Bob wants to deposit 100
Bob succesffully deposited 190
Bob takes a taxi, Sally is the driver
The fare comes to 25
Bob paid sally 25 for the taxi and gets 75 back
​ ✓ Bob should be able to deposit 100 then pay sally 25 by splitting notes he owns (5861ms)


1 passing (6s)

测试程序位于test文件夹内。

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?