AZTEC-基于ethereum的隐私交易-III-产品设计

产品设计的关键步骤

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);

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