@lfdecentralizedtrust/zeto-contracts 0.2.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -162
- package/contracts/factory.sol +30 -34
- package/contracts/factory_upgradeable.sol +160 -0
- package/contracts/lib/common/util.sol +40 -0
- package/contracts/lib/interfaces/ILockableCapability.sol +318 -0
- package/contracts/lib/interfaces/{izeto.sol → IZeto.sol} +13 -1
- package/contracts/lib/interfaces/{izeto_initializable.sol → IZetoInitializable.sol} +14 -12
- package/contracts/lib/interfaces/{izeto_kyc.sol → IZetoKyc.sol} +3 -3
- package/contracts/lib/interfaces/IZetoLockableCapability.sol +237 -0
- package/contracts/lib/interfaces/IZetoStorage.sol +106 -0
- package/contracts/lib/interfaces/{izeto_lockable.sol → IZetoVerifier.sol} +8 -21
- package/contracts/lib/registry.sol +77 -27
- package/contracts/lib/storage/base.sol +210 -0
- package/contracts/lib/storage/nullifier.sol +166 -0
- package/contracts/lib/zeto_common.sol +277 -28
- package/contracts/lib/zeto_fungible.sol +451 -33
- package/contracts/lib/zeto_fungible_base.sol +69 -0
- package/contracts/lib/zeto_fungible_burn.sol +83 -53
- package/contracts/lib/zeto_fungible_burn_nullifier.sol +115 -72
- package/contracts/lib/zeto_fungible_nullifier.sol +167 -0
- package/contracts/lib/zeto_lockable.sol +613 -0
- package/contracts/lib/zeto_non_fungible.sol +236 -0
- package/contracts/lib/zeto_non_fungible_base.sol +61 -0
- package/contracts/lib/zeto_non_fungible_nullifier.sol +61 -0
- package/contracts/test/escrow1.sol +90 -34
- package/contracts/test/escrow2.sol +64 -29
- package/contracts/test/qurrency.sol +10 -2
- package/contracts/test/smt.sol +8 -3
- package/contracts/test/tendecimals.sol +14 -12
- package/contracts/verifiers/impl/anon.sol +189 -0
- package/contracts/verifiers/impl/anon_batch.sol +301 -0
- package/contracts/verifiers/impl/anon_enc.sol +266 -0
- package/contracts/verifiers/impl/anon_enc_batch.sol +602 -0
- package/contracts/verifiers/impl/anon_enc_nullifier.sol +287 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_batch.sol +679 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc.sol +294 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc_batch.sol +686 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation.sol +413 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation_batch.sol +1141 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer.sol +217 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked.sol +196 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked_batch.sol +308 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer_batch.sol +385 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer.sol +497 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer_batch.sol +1001 -0
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked.sol → impl/anon_nullifier_transfer.sol} +12 -19
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked_batch.sol → impl/anon_nullifier_transfer_batch.sol} +44 -51
- package/contracts/verifiers/impl/burn.sol +182 -0
- package/contracts/verifiers/impl/burn_batch.sol +238 -0
- package/contracts/verifiers/impl/burn_nullifier.sol +203 -0
- package/contracts/verifiers/impl/burn_nullifier_batch.sol +315 -0
- package/contracts/verifiers/impl/deposit.sol +182 -0
- package/contracts/verifiers/impl/deposit_kyc.sol +189 -0
- package/contracts/verifiers/impl/nf_anon.sol +175 -0
- package/contracts/verifiers/{verifier_nf_anon_nullifier_transferLocked.sol → impl/nf_anon_nullifier_transfer.sol} +6 -13
- package/contracts/verifiers/impl/withdraw.sol +189 -0
- package/contracts/verifiers/impl/withdraw_batch.sol +245 -0
- package/contracts/verifiers/impl/withdraw_nullifier.sol +210 -0
- package/contracts/verifiers/impl/withdraw_nullifier_batch.sol +322 -0
- package/contracts/verifiers/verifier_anon.sol +31 -186
- package/contracts/verifiers/verifier_anon_batch.sol +31 -298
- package/contracts/verifiers/verifier_anon_enc.sol +31 -263
- package/contracts/verifiers/verifier_anon_enc_batch.sol +31 -599
- package/contracts/verifiers/verifier_anon_enc_nullifier.sol +31 -284
- package/contracts/verifiers/verifier_anon_enc_nullifier_batch.sol +33 -676
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc.sol +31 -291
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc_batch.sol +33 -683
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation.sol +33 -410
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation_batch.sol +33 -1138
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer.sol +33 -214
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer_batch.sol +33 -382
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_transfer.sol +33 -207
- package/contracts/verifiers/verifier_anon_nullifier_transfer_batch.sol +33 -375
- package/contracts/verifiers/verifier_burn.sol +31 -179
- package/contracts/verifiers/verifier_burn_batch.sol +31 -235
- package/contracts/verifiers/verifier_burn_nullifier.sol +31 -200
- package/contracts/verifiers/verifier_burn_nullifier_batch.sol +31 -312
- package/contracts/verifiers/verifier_deposit.sol +31 -179
- package/contracts/verifiers/verifier_deposit_kyc.sol +34 -0
- package/contracts/verifiers/verifier_nf_anon.sol +31 -172
- package/contracts/verifiers/verifier_nf_anon_nullifier_transfer.sol +33 -179
- package/contracts/verifiers/verifier_withdraw.sol +31 -186
- package/contracts/verifiers/verifier_withdraw_batch.sol +31 -242
- package/contracts/verifiers/verifier_withdraw_nullifier.sol +31 -207
- package/contracts/verifiers/verifier_withdraw_nullifier_batch.sol +33 -319
- package/contracts/zeto_anon.sol +77 -231
- package/contracts/zeto_anon_burnable.sol +56 -12
- package/contracts/zeto_anon_enc.sol +93 -190
- package/contracts/zeto_anon_enc_nullifier.sol +249 -195
- package/contracts/zeto_anon_enc_nullifier_kyc.sol +51 -232
- package/contracts/zeto_anon_enc_nullifier_non_repudiation.sol +231 -238
- package/contracts/zeto_anon_nullifier.sol +164 -298
- package/contracts/zeto_anon_nullifier_burnable.sol +68 -18
- package/contracts/zeto_anon_nullifier_kyc.sol +40 -345
- package/contracts/zeto_anon_nullifier_qurrency.sol +218 -360
- package/contracts/zeto_nf_anon.sol +55 -130
- package/contracts/zeto_nf_anon_nullifier.sol +90 -152
- package/hardhat.config.ts +9 -3
- package/ignition/modules/lib/deps.ts +9 -6
- package/ignition/modules/zeto_anon_burnable.ts +8 -7
- package/ignition/modules/zeto_anon_enc_nullifier.ts +33 -1
- package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +2 -2
- package/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +2 -1
- package/ignition/modules/zeto_anon_nullifier.ts +30 -5
- package/ignition/modules/zeto_anon_nullifier_burnable.ts +52 -17
- package/ignition/modules/zeto_anon_nullifier_kyc.ts +27 -12
- package/ignition/modules/zeto_anon_nullifier_qurrency.ts +2 -1
- package/ignition/modules/zeto_nf_anon_nullifier.ts +16 -12
- package/package.json +4 -2
- package/scripts/deploy_cloneable.ts +19 -10
- package/scripts/deploy_upgradeable.ts +9 -5
- package/scripts/tokens/Zeto_Anon.ts +3 -3
- package/scripts/tokens/Zeto_AnonBurnable.ts +1 -1
- package/scripts/tokens/Zeto_AnonEnc.ts +12 -3
- package/scripts/tokens/Zeto_AnonEncNullifier.ts +7 -3
- package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +1 -1
- package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +3 -1
- package/scripts/tokens/Zeto_AnonNullifier.ts +3 -1
- package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +3 -1
- package/scripts/tokens/Zeto_AnonNullifierKyc.ts +1 -1
- package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +3 -1
- package/scripts/tokens/Zeto_NfAnon.ts +2 -2
- package/scripts/tokens/Zeto_NfAnonNullifier.ts +3 -2
- package/test/factory.ts +46 -73
- package/test/factory_upgradeable.ts +311 -0
- package/test/lib/anon_nullifier_helpers.ts +89 -0
- package/test/lib/anon_zeto_helpers.ts +76 -0
- package/test/lib/deploy.ts +3 -2
- package/test/lib/utils.ts +74 -35
- package/test/test/escrow1.ts +185 -58
- package/test/test/escrow2.ts +200 -107
- package/test/test/qurrency.ts +13 -33
- package/test/usdc-shielding.ts +39 -26
- package/test/utils.ts +144 -21
- package/test/zeto_anon.ts +956 -465
- package/test/zeto_anon_enc.ts +881 -142
- package/test/zeto_anon_enc_nullifier.ts +847 -38
- package/test/zeto_anon_enc_nullifier_kyc.ts +120 -181
- package/test/zeto_anon_enc_nullifier_non_repudiation.ts +77 -46
- package/test/zeto_anon_nullifier.ts +1212 -953
- package/test/zeto_anon_nullifier_kyc.ts +674 -655
- package/test/zeto_anon_nullifier_qurrency.ts +452 -306
- package/test/zeto_nf_anon.ts +737 -137
- package/test/zeto_nf_anon_nullifier.ts +876 -246
- package/contracts/lib/zeto_base.sol +0 -293
- package/contracts/lib/zeto_fungible_withdraw.sol +0 -132
- package/contracts/lib/zeto_fungible_withdraw_nullifier.sol +0 -144
- package/contracts/lib/zeto_nullifier.sol +0 -334
- package/contracts/zkDvP.sol_ +0 -273
- package/test/zkDvP.ts_ +0 -455
- /package/contracts/lib/{common.sol → common/common.sol} +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Copyright © 2024 Kaleido, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
//
|
|
5
|
+
// Shared ZK helpers for the plain `anon` circuit (non-nullifier tokens such as
|
|
6
|
+
// {Zeto_Anon}). Kept separate from `zeto_anon.ts` so escrow and other suites
|
|
7
|
+
// can import these without registering the full Zeto_Anon Hardhat test module.
|
|
8
|
+
|
|
9
|
+
import { AbiCoder, BigNumberish } from "ethers";
|
|
10
|
+
import { encodeProof } from "zeto-js";
|
|
11
|
+
import { groth16 } from "snarkjs";
|
|
12
|
+
import { formatPrivKeyForBabyJub, stringifyBigInts } from "maci-crypto";
|
|
13
|
+
import { User, UTXO, logger, ZERO_UTXO } from "./utils";
|
|
14
|
+
|
|
15
|
+
const ZERO_PUBKEY = [0n, 0n];
|
|
16
|
+
|
|
17
|
+
export async function prepareProof(
|
|
18
|
+
circuit: any,
|
|
19
|
+
provingKey: any,
|
|
20
|
+
signer: User,
|
|
21
|
+
inputs: UTXO[],
|
|
22
|
+
outputs: UTXO[],
|
|
23
|
+
owners: User[],
|
|
24
|
+
) {
|
|
25
|
+
const inputCommitments: BigNumberish[] = inputs.map(
|
|
26
|
+
(input) => input.hash,
|
|
27
|
+
) as BigNumberish[];
|
|
28
|
+
const inputValues = inputs.map((input) => BigInt(input.value || 0n));
|
|
29
|
+
const inputSalts = inputs.map((input) => input.salt || 0n);
|
|
30
|
+
const outputCommitments: BigNumberish[] = outputs.map(
|
|
31
|
+
(output) => output.hash,
|
|
32
|
+
) as BigNumberish[];
|
|
33
|
+
const outputValues = outputs.map((output) => BigInt(output.value || 0n));
|
|
34
|
+
const outputSalts = outputs.map((o) => o.salt || 0n);
|
|
35
|
+
const outputOwnerPublicKeys: BigNumberish[][] = owners.map(
|
|
36
|
+
(owner) => owner.babyJubPublicKey || ZERO_PUBKEY,
|
|
37
|
+
) as BigNumberish[][];
|
|
38
|
+
const otherInputs = stringifyBigInts({
|
|
39
|
+
inputOwnerPrivateKey: formatPrivKeyForBabyJub(signer.babyJubPrivateKey),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const startWitnessCalculation = Date.now();
|
|
43
|
+
const witness = await circuit.calculateWTNSBin(
|
|
44
|
+
{
|
|
45
|
+
inputCommitments,
|
|
46
|
+
inputValues,
|
|
47
|
+
inputSalts,
|
|
48
|
+
outputCommitments,
|
|
49
|
+
outputValues,
|
|
50
|
+
outputSalts,
|
|
51
|
+
outputOwnerPublicKeys,
|
|
52
|
+
...otherInputs,
|
|
53
|
+
},
|
|
54
|
+
true,
|
|
55
|
+
);
|
|
56
|
+
const timeWitnessCalculation = Date.now() - startWitnessCalculation;
|
|
57
|
+
|
|
58
|
+
const startProofGeneration = Date.now();
|
|
59
|
+
const { proof, publicSignals } = (await groth16.prove(
|
|
60
|
+
provingKey,
|
|
61
|
+
witness,
|
|
62
|
+
)) as { proof: BigNumberish[]; publicSignals: BigNumberish[] };
|
|
63
|
+
const timeProofGeneration = Date.now() - startProofGeneration;
|
|
64
|
+
logger.debug(
|
|
65
|
+
`Witness calculation time: ${timeWitnessCalculation}ms, Proof generation time: ${timeProofGeneration}ms`,
|
|
66
|
+
);
|
|
67
|
+
const encodedProof = encodeProof(proof);
|
|
68
|
+
return encodedProof;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function encodeToBytes(proof: any) {
|
|
72
|
+
return new AbiCoder().encode(
|
|
73
|
+
["tuple(uint256[2] pA, uint256[2][2] pB, uint256[2] pC)"],
|
|
74
|
+
[proof],
|
|
75
|
+
);
|
|
76
|
+
}
|
package/test/lib/deploy.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
deployNonFungible as deployNonFungibleCloneable,
|
|
11
11
|
} from "../../scripts/deploy_cloneable";
|
|
12
12
|
import fungibilities from "../../scripts/tokens.json";
|
|
13
|
+
import { logger } from "./utils";
|
|
13
14
|
import { ethers } from "hardhat";
|
|
14
15
|
|
|
15
16
|
export async function deployZeto(tokenName: string) {
|
|
@@ -34,7 +35,7 @@ export async function deployZeto(tokenName: string) {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
if (process.env.USE_FACTORY !== "true") {
|
|
37
|
-
|
|
38
|
+
logger.debug("Deploying as upgradeable contracts");
|
|
38
39
|
// setup via the deployment scripts
|
|
39
40
|
const deployFunc = isFungible
|
|
40
41
|
? deployFungibleUpgradeable
|
|
@@ -42,7 +43,7 @@ export async function deployZeto(tokenName: string) {
|
|
|
42
43
|
const result = await deployFunc(tokenName);
|
|
43
44
|
({ deployer, zeto, erc20 } = result as any);
|
|
44
45
|
} else {
|
|
45
|
-
|
|
46
|
+
logger.debug('Deploying as cloneable contracts using "ZetoTokenFactory"');
|
|
46
47
|
let args, zetoImpl;
|
|
47
48
|
const deployFunc = isFungible
|
|
48
49
|
? deployFungibleCloneable
|
package/test/lib/utils.ts
CHANGED
|
@@ -18,13 +18,15 @@ import {
|
|
|
18
18
|
ContractTransactionReceipt,
|
|
19
19
|
Signer,
|
|
20
20
|
BigNumberish,
|
|
21
|
-
AddressLike,
|
|
22
21
|
} from "ethers";
|
|
23
22
|
import {
|
|
24
23
|
genKeypair,
|
|
25
24
|
formatPrivKeyForBabyJub,
|
|
26
|
-
genEcdhSharedKey,
|
|
27
25
|
} from "maci-crypto";
|
|
26
|
+
import { Logger, ILogObj } from "tslog";
|
|
27
|
+
const logLevel = process.env.LOG_LEVEL || "3";
|
|
28
|
+
export const logger: Logger<ILogObj> = new Logger({ name: "e2e", minLevel: parseInt(logLevel) });
|
|
29
|
+
|
|
28
30
|
import { Poseidon, newSalt, tokenUriHash } from "zeto-js";
|
|
29
31
|
|
|
30
32
|
const poseidonHash3 = Poseidon.poseidon3;
|
|
@@ -35,18 +37,18 @@ export interface UTXO {
|
|
|
35
37
|
value?: number;
|
|
36
38
|
tokenId?: number;
|
|
37
39
|
uri?: string;
|
|
38
|
-
hash:
|
|
39
|
-
salt?:
|
|
40
|
+
hash: bigint;
|
|
41
|
+
salt?: bigint;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
export const ZERO_UTXO: UTXO = { hash:
|
|
44
|
+
export const ZERO_UTXO: UTXO = { hash: 0n };
|
|
43
45
|
|
|
44
46
|
export interface User {
|
|
45
47
|
signer: Signer;
|
|
46
48
|
ethAddress: string;
|
|
47
|
-
babyJubPrivateKey:
|
|
48
|
-
babyJubPublicKey:
|
|
49
|
-
formattedPrivateKey:
|
|
49
|
+
babyJubPrivateKey: bigint;
|
|
50
|
+
babyJubPublicKey: bigint[];
|
|
51
|
+
formattedPrivateKey: bigint;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
export async function newUser(signer: Signer) {
|
|
@@ -62,7 +64,7 @@ export async function newUser(signer: Signer) {
|
|
|
62
64
|
};
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
export function newUTXO(value: number, owner: User, salt?:
|
|
67
|
+
export function newUTXO(value: number, owner: User, salt?: bigint): UTXO {
|
|
66
68
|
if (!salt) salt = newSalt();
|
|
67
69
|
const hash = poseidonHash4([
|
|
68
70
|
BigInt(value),
|
|
@@ -77,7 +79,7 @@ export function newAssetUTXO(
|
|
|
77
79
|
tokenId: number,
|
|
78
80
|
uri: string,
|
|
79
81
|
owner: User,
|
|
80
|
-
salt?:
|
|
82
|
+
salt?: bigint,
|
|
81
83
|
): UTXO {
|
|
82
84
|
if (!salt) salt = newSalt();
|
|
83
85
|
const hash = poseidonHash5([
|
|
@@ -122,10 +124,13 @@ export async function doMint(
|
|
|
122
124
|
.connect(minter)
|
|
123
125
|
.mint(outputCommitments, "0x");
|
|
124
126
|
const result = await tx.wait();
|
|
125
|
-
|
|
127
|
+
logger.debug(`Method mint() complete. Gas used: ${result?.gasUsed}`);
|
|
126
128
|
if (result?.gasUsed && Array.isArray(gasHistories)) {
|
|
127
129
|
gasHistories.push(result?.gasUsed);
|
|
128
130
|
}
|
|
131
|
+
if (!result) {
|
|
132
|
+
throw new Error("doMint: transaction receipt is null");
|
|
133
|
+
}
|
|
129
134
|
return result;
|
|
130
135
|
}
|
|
131
136
|
|
|
@@ -141,10 +146,13 @@ export async function doDeposit(
|
|
|
141
146
|
.connect(depositUser)
|
|
142
147
|
.deposit(amount, commitment, proof, "0x");
|
|
143
148
|
const result = await tx.wait();
|
|
144
|
-
|
|
149
|
+
logger.debug(`Method deposit() complete. Gas used: ${result?.gasUsed}`);
|
|
145
150
|
if (result?.gasUsed && Array.isArray(gasHistories)) {
|
|
146
151
|
gasHistories.push(result?.gasUsed);
|
|
147
152
|
}
|
|
153
|
+
if (!result) {
|
|
154
|
+
throw new Error("doDeposit: transaction receipt is null");
|
|
155
|
+
}
|
|
148
156
|
return result;
|
|
149
157
|
}
|
|
150
158
|
|
|
@@ -162,17 +170,23 @@ export async function doWithdraw(
|
|
|
162
170
|
.connect(withdrawUser)
|
|
163
171
|
.withdraw(amount, nullifiers, commitment, root, proof);
|
|
164
172
|
const result = await tx.wait();
|
|
165
|
-
|
|
173
|
+
logger.debug(`Method withdraw() complete. Gas used: ${result?.gasUsed}`);
|
|
166
174
|
if (result?.gasUsed && Array.isArray(gasHistories)) {
|
|
167
175
|
gasHistories.push(result?.gasUsed);
|
|
168
176
|
}
|
|
177
|
+
if (!result) {
|
|
178
|
+
throw new Error("doWithdraw: transaction receipt is null");
|
|
179
|
+
}
|
|
169
180
|
return result;
|
|
170
181
|
}
|
|
171
182
|
|
|
172
183
|
export function parseUTXOEvents(
|
|
173
184
|
zetoTokenContract: any,
|
|
174
|
-
result: ContractTransactionReceipt,
|
|
185
|
+
result: ContractTransactionReceipt | null,
|
|
175
186
|
) {
|
|
187
|
+
if (!result) {
|
|
188
|
+
throw new Error("parseUTXOEvents: transaction receipt is null");
|
|
189
|
+
}
|
|
176
190
|
let returnValues: any[] = [];
|
|
177
191
|
for (const log of result.logs || []) {
|
|
178
192
|
const event = zetoTokenContract.interface.parseLog(log as any);
|
|
@@ -183,6 +197,13 @@ export function parseUTXOEvents(
|
|
|
183
197
|
outputs: event?.args.outputs,
|
|
184
198
|
submitter: event?.args.submitter,
|
|
185
199
|
};
|
|
200
|
+
} else if (event?.name === "UTXOTransferLocked") {
|
|
201
|
+
e = {
|
|
202
|
+
lockedInputs: event?.args.lockedInputs,
|
|
203
|
+
lockedOutputs: event?.args.lockedOutputs,
|
|
204
|
+
outputs: event?.args.outputs,
|
|
205
|
+
submitter: event?.args.submitter,
|
|
206
|
+
};
|
|
186
207
|
} else if (event?.name === "UTXOTransferWithEncryptedValues") {
|
|
187
208
|
e = {
|
|
188
209
|
inputs: event?.args.inputs,
|
|
@@ -192,6 +213,15 @@ export function parseUTXOEvents(
|
|
|
192
213
|
submitter: event?.args.submitter,
|
|
193
214
|
ecdhPublicKey: event?.args.ecdhPublicKey,
|
|
194
215
|
};
|
|
216
|
+
} else if (event?.name === "UTXOTransferWithMlkemEncryptedValues") {
|
|
217
|
+
e = {
|
|
218
|
+
inputs: event?.args.inputs,
|
|
219
|
+
outputs: event?.args.outputs,
|
|
220
|
+
encryptedValues: event?.args.encryptedValues,
|
|
221
|
+
encryptionNonce: event?.args.encryptionNonce,
|
|
222
|
+
submitter: event?.args.submitter,
|
|
223
|
+
encapsulatedSharedSecret: event?.args.encapsulatedSharedSecret,
|
|
224
|
+
};
|
|
195
225
|
} else if (event?.name === "UTXOTransferNonRepudiation") {
|
|
196
226
|
e = {
|
|
197
227
|
inputs: event?.args.inputs,
|
|
@@ -208,36 +238,45 @@ export function parseUTXOEvents(
|
|
|
208
238
|
receivers: event?.args.receivers,
|
|
209
239
|
submitter: event?.args.submitter,
|
|
210
240
|
};
|
|
211
|
-
} else if (event?.name === "
|
|
241
|
+
} else if (event?.name === "UTXOWithdraw") {
|
|
212
242
|
e = {
|
|
213
|
-
|
|
214
|
-
|
|
243
|
+
amount: event?.args.amount,
|
|
244
|
+
inputs: event?.args.inputs,
|
|
245
|
+
output: event?.args.output,
|
|
215
246
|
};
|
|
216
|
-
} else if (event?.name === "
|
|
247
|
+
} else if (event?.name === "LockCreate") {
|
|
217
248
|
e = {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
delegate: event?.args.delegate,
|
|
249
|
+
lockId: event?.args.lockId,
|
|
250
|
+
lockData: event?.args.lockData,
|
|
221
251
|
};
|
|
222
|
-
} else if (event?.name === "
|
|
252
|
+
} else if (event?.name === "UnlockPrepare") {
|
|
223
253
|
e = {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
254
|
+
lockId: event?.args.lockId,
|
|
255
|
+
operator: event?.args.operator,
|
|
256
|
+
settle: event?.args.settle,
|
|
227
257
|
};
|
|
228
|
-
} else if (event?.name === "
|
|
258
|
+
} else if (event?.name === "Unlock") {
|
|
229
259
|
e = {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
260
|
+
lockId: event?.args.lockId,
|
|
261
|
+
operator: event?.args.operator,
|
|
262
|
+
inputs: event?.args.inputs,
|
|
263
|
+
delegate: event?.args.delegate,
|
|
264
|
+
settle: event?.args.settle,
|
|
234
265
|
};
|
|
235
|
-
} else if (
|
|
236
|
-
event?.name === "PaymentApproved" ||
|
|
237
|
-
event?.name === "PaymentCompleted"
|
|
238
|
-
) {
|
|
266
|
+
} else if (event?.name === "LockRollback") {
|
|
239
267
|
e = {
|
|
240
|
-
|
|
268
|
+
lockId: event?.args.lockId,
|
|
269
|
+
operator: event?.args.operator,
|
|
270
|
+
inputs: event?.args.inputs,
|
|
271
|
+
delegate: event?.args.delegate,
|
|
272
|
+
rollback: event?.args.rollback,
|
|
273
|
+
};
|
|
274
|
+
} else if (event?.name === "LockDelegate") {
|
|
275
|
+
e = {
|
|
276
|
+
lockId: event?.args.lockId,
|
|
277
|
+
operator: event?.args.operator,
|
|
278
|
+
oldDelegate: event?.args.oldDelegate,
|
|
279
|
+
newDelegate: event?.args.newDelegate,
|
|
241
280
|
};
|
|
242
281
|
}
|
|
243
282
|
returnValues.push(e);
|
package/test/test/escrow1.ts
CHANGED
|
@@ -15,11 +15,16 @@
|
|
|
15
15
|
// limitations under the License.
|
|
16
16
|
|
|
17
17
|
import { ethers, ignition, network } from "hardhat";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Signer,
|
|
20
|
+
AbiCoder,
|
|
21
|
+
BigNumberish,
|
|
22
|
+
ContractTransactionReceipt,
|
|
23
|
+
} from "ethers";
|
|
19
24
|
import { expect } from "chai";
|
|
20
|
-
import { loadCircuit
|
|
25
|
+
import { loadCircuit } from "zeto-js";
|
|
21
26
|
import zkEscrowModule from "../../ignition/modules/test/escrow1";
|
|
22
|
-
import
|
|
27
|
+
import { prepareProof } from "../lib/anon_zeto_helpers";
|
|
23
28
|
import {
|
|
24
29
|
UTXO,
|
|
25
30
|
User,
|
|
@@ -27,11 +32,36 @@ import {
|
|
|
27
32
|
newUTXO,
|
|
28
33
|
doMint,
|
|
29
34
|
ZERO_UTXO,
|
|
30
|
-
|
|
35
|
+
logger,
|
|
31
36
|
} from "../lib/utils";
|
|
32
37
|
import { loadProvingKeys } from "../utils";
|
|
33
38
|
import { deployZeto } from "../lib/deploy";
|
|
34
39
|
|
|
40
|
+
// ABI fragments for the ZetoLockableCapability *Args payloads.
|
|
41
|
+
// These mirror the canonical encodings used by every Zeto token that
|
|
42
|
+
// implements ILockableCapability.
|
|
43
|
+
const CREATE_ARGS_ABI =
|
|
44
|
+
"tuple(bytes32 txId, uint256[] inputs, uint256[] outputs, uint256[] lockedOutputs, bytes proof)";
|
|
45
|
+
const DELEGATE_ARGS_ABI = "tuple(bytes32 txId)";
|
|
46
|
+
|
|
47
|
+
function encodeCreateArgs(args: {
|
|
48
|
+
txId: string;
|
|
49
|
+
inputs: BigNumberish[];
|
|
50
|
+
outputs: BigNumberish[];
|
|
51
|
+
lockedOutputs: BigNumberish[];
|
|
52
|
+
proof: string;
|
|
53
|
+
}) {
|
|
54
|
+
return new AbiCoder().encode([CREATE_ARGS_ABI], [args]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function encodeDelegateArgs(txId: string) {
|
|
58
|
+
return new AbiCoder().encode([DELEGATE_ARGS_ABI], [{ txId }]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function randomBytes32(): string {
|
|
62
|
+
return ethers.hexlify(ethers.randomBytes(32));
|
|
63
|
+
}
|
|
64
|
+
|
|
35
65
|
describe("Escrow flow for payment with Zeto_Anon", function () {
|
|
36
66
|
let Alice: User;
|
|
37
67
|
let Bob: User;
|
|
@@ -48,6 +78,7 @@ describe("Escrow flow for payment with Zeto_Anon", function () {
|
|
|
48
78
|
// UTXOs involved in the escrow flow
|
|
49
79
|
let lockedPayment1: UTXO;
|
|
50
80
|
let paymentToBob: UTXO;
|
|
81
|
+
let lockId: string;
|
|
51
82
|
|
|
52
83
|
// other variables
|
|
53
84
|
let deployer: Signer;
|
|
@@ -66,11 +97,15 @@ describe("Escrow flow for payment with Zeto_Anon", function () {
|
|
|
66
97
|
Bob = await newUser(b);
|
|
67
98
|
Charlie = await newUser(c);
|
|
68
99
|
|
|
100
|
+
// The Zeto_Anon token uses the same `anon` circuit for both regular
|
|
101
|
+
// transfers and the locked-input branch (the lockVerifier is wired
|
|
102
|
+
// to Groth16Verifier_Anon in the ignition module). One circuit/key
|
|
103
|
+
// pair therefore covers both the createLock and spendLock proofs.
|
|
69
104
|
circuit = await loadCircuit("anon");
|
|
70
105
|
({ provingKeyFile: provingKey } = loadProvingKeys("anon"));
|
|
71
106
|
|
|
72
107
|
({ deployer, zeto: zkPayment } = await deployZeto("Zeto_Anon"));
|
|
73
|
-
|
|
108
|
+
logger.debug(`ZK Payment contract deployed at ${zkPayment.target}`);
|
|
74
109
|
({ zkEscrow } = await ignition.deploy(zkEscrowModule, {
|
|
75
110
|
parameters: {
|
|
76
111
|
zkEscrow1: {
|
|
@@ -85,62 +120,101 @@ describe("Escrow flow for payment with Zeto_Anon", function () {
|
|
|
85
120
|
payment2 = newUTXO(20, Alice);
|
|
86
121
|
const result = await doMint(zkPayment, deployer, [payment1, payment2]);
|
|
87
122
|
|
|
88
|
-
// simulate Alice and Bob listening to minting events and updating
|
|
123
|
+
// simulate Alice and Bob listening to minting events and updating
|
|
124
|
+
// their local merkle trees
|
|
89
125
|
for (const log of result.logs) {
|
|
90
126
|
const event = zkPayment.interface.parseLog(log as any);
|
|
91
127
|
expect(event.args.outputs.length).to.equal(2);
|
|
92
128
|
}
|
|
93
129
|
});
|
|
94
130
|
|
|
95
|
-
it("Alice locks
|
|
131
|
+
it("Alice locks payment1 by calling createLock on the Zeto token", async function () {
|
|
132
|
+
// The locked output preserves the value but is a fresh commitment
|
|
133
|
+
// (new salt). The createLock proof attests to the standard transfer
|
|
134
|
+
// payment1 -> lockedPayment1.
|
|
96
135
|
lockedPayment1 = newUTXO(payment1.value!, Alice);
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
136
|
+
const encodedZkProof = await prepareProof(
|
|
137
|
+
circuit,
|
|
138
|
+
provingKey,
|
|
139
|
+
Alice,
|
|
140
|
+
[payment1, ZERO_UTXO],
|
|
141
|
+
[lockedPayment1, ZERO_UTXO],
|
|
142
|
+
[Alice, {}],
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const createArgs = encodeCreateArgs({
|
|
146
|
+
txId: randomBytes32(),
|
|
147
|
+
inputs: [payment1.hash],
|
|
148
|
+
// For the fungible Zeto_Anon createLock, lockedOutputs holds the
|
|
149
|
+
// freshly-locked UTXO and outputs is empty (no change is being
|
|
150
|
+
// returned to Alice, since payment1's full value is moved into
|
|
151
|
+
// the lock).
|
|
152
|
+
outputs: [],
|
|
153
|
+
lockedOutputs: [lockedPayment1.hash],
|
|
154
|
+
proof: encodeProofOnly(encodedZkProof),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Pre-compute the lockId off-chain to assert the contract derives
|
|
158
|
+
// the same value (deterministic from caller + txId).
|
|
159
|
+
lockId = await zkPayment.connect(Alice.signer).computeLockId(createArgs);
|
|
160
|
+
|
|
161
|
+
const tx = await zkPayment
|
|
162
|
+
.connect(Alice.signer)
|
|
163
|
+
.createLock(createArgs, ethers.ZeroHash, ethers.ZeroHash, "0x");
|
|
164
|
+
const result: ContractTransactionReceipt | null = await tx.wait();
|
|
165
|
+
logger.debug(`createLock() complete. Gas used: ${result?.gasUsed}`);
|
|
166
|
+
|
|
167
|
+
// Sanity-check the lock state after creation.
|
|
168
|
+
const info = await zkPayment.getLock(lockId);
|
|
169
|
+
expect(info.owner).to.equal(Alice.ethAddress);
|
|
170
|
+
expect(info.spender).to.equal(Alice.ethAddress);
|
|
171
|
+
expect(await zkPayment.locked(lockedPayment1.hash)).to.deep.equal([
|
|
172
|
+
true,
|
|
173
|
+
Alice.ethAddress,
|
|
174
|
+
]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("Alice delegates the lock to the escrow contract", async function () {
|
|
106
178
|
const tx = await zkPayment
|
|
107
179
|
.connect(Alice.signer)
|
|
108
|
-
.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
outputCommitments,
|
|
112
|
-
encodedProof,
|
|
180
|
+
.delegateLock(
|
|
181
|
+
lockId,
|
|
182
|
+
encodeDelegateArgs(randomBytes32()),
|
|
113
183
|
zkEscrow.target,
|
|
114
184
|
"0x",
|
|
115
185
|
);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
186
|
+
await tx.wait();
|
|
187
|
+
|
|
188
|
+
// After delegation the escrow is the sole spender; Alice can no
|
|
189
|
+
// longer spend the lock directly.
|
|
190
|
+
const info = await zkPayment.getLock(lockId);
|
|
191
|
+
expect(info.spender).to.equal(zkEscrow.target);
|
|
192
|
+
expect(await zkPayment.locked(lockedPayment1.hash)).to.deep.equal([
|
|
193
|
+
true,
|
|
194
|
+
zkEscrow.target,
|
|
195
|
+
]);
|
|
126
196
|
});
|
|
127
197
|
|
|
128
198
|
it("Alice initiates a payment transaction to Bob through the escrow", async function () {
|
|
129
|
-
paymentToBob = newUTXO(lockedPayment1.value!, Bob
|
|
199
|
+
paymentToBob = newUTXO(lockedPayment1.value!, Bob);
|
|
130
200
|
const tx = await zkEscrow
|
|
131
201
|
.connect(Alice.signer)
|
|
132
|
-
.initiatePayment(
|
|
202
|
+
.initiatePayment(lockId, [paymentToBob.hash], "0x");
|
|
133
203
|
const result = await tx.wait();
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
204
|
+
const initiated = parseEscrowEvent(zkEscrow, result, "PaymentInitiated");
|
|
205
|
+
expect(initiated, "PaymentInitiated event not emitted").to.not.be.undefined;
|
|
206
|
+
|
|
207
|
+
// Bob listens to the payment events and verifies the proposed
|
|
208
|
+
// payment matches what Alice promised off-chain.
|
|
209
|
+
expect(initiated!.outputs[0]).to.equal(paymentToBob.hash);
|
|
210
|
+
expect(initiated!.lockId).to.equal(lockId);
|
|
211
|
+
paymentId = initiated!.paymentId;
|
|
140
212
|
});
|
|
141
213
|
|
|
142
|
-
it("Alice approves the payment by submitting a valid proof
|
|
143
|
-
|
|
214
|
+
it("Alice approves the payment by submitting a valid locked-input proof", async function () {
|
|
215
|
+
// The spend proof transfers lockedPayment1 -> paymentToBob (now
|
|
216
|
+
// owned by Bob). Same `anon` circuit as the createLock step.
|
|
217
|
+
const encodedZkProof = await prepareProof(
|
|
144
218
|
circuit,
|
|
145
219
|
provingKey,
|
|
146
220
|
Alice,
|
|
@@ -150,29 +224,82 @@ describe("Escrow flow for payment with Zeto_Anon", function () {
|
|
|
150
224
|
);
|
|
151
225
|
const tx = await zkEscrow
|
|
152
226
|
.connect(Alice.signer)
|
|
153
|
-
.approvePayment(paymentId,
|
|
227
|
+
.approvePayment(paymentId, encodeProofOnly(encodedZkProof), "0x");
|
|
154
228
|
const result = await tx.wait();
|
|
155
|
-
|
|
156
|
-
// the escrow
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
229
|
+
|
|
230
|
+
// Bob listens to the escrow's PaymentApproved event; the escrow
|
|
231
|
+
// having stored the proof guarantees completePayment cannot fail
|
|
232
|
+
// on proof grounds later.
|
|
233
|
+
const approved = parseEscrowEvent(zkEscrow, result, "PaymentApproved");
|
|
234
|
+
expect(approved, "PaymentApproved event not emitted").to.not.be.undefined;
|
|
235
|
+
expect(approved!.paymentId).to.equal(paymentId);
|
|
160
236
|
});
|
|
161
237
|
|
|
162
|
-
it("Bob, or anyone, can call the escrow to finalize the payment and receive the
|
|
238
|
+
it("Bob, or anyone, can call the escrow to finalize the payment and receive the unlocked UTXO", async function () {
|
|
163
239
|
const tx = await zkEscrow
|
|
164
240
|
.connect(Bob.signer)
|
|
165
241
|
.completePayment(paymentId, "0x");
|
|
166
242
|
const result = await tx.wait();
|
|
167
|
-
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
243
|
+
|
|
244
|
+
// The Zeto token emits ZetoLockSpent containing the new unlocked
|
|
245
|
+
// outputs.
|
|
246
|
+
const lockSpent = result!.logs
|
|
247
|
+
.map((l) => {
|
|
248
|
+
try {
|
|
249
|
+
return zkPayment.interface.parseLog(l as any);
|
|
250
|
+
} catch (_e) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
.find((p: any) => p && p.name === "ZetoLockSpent");
|
|
255
|
+
expect(lockSpent, "ZetoLockSpent event not emitted").to.not.be.undefined;
|
|
256
|
+
expect(lockSpent!.args.outputs[0]).to.equal(paymentToBob.hash);
|
|
257
|
+
|
|
258
|
+
// Lock is consumed; the locked-UTXO ledger no longer carries
|
|
259
|
+
// lockedPayment1.
|
|
260
|
+
expect(await zkPayment.isLockActive(lockId)).to.equal(false);
|
|
261
|
+
expect((await zkPayment.locked(lockedPayment1.hash))[0]).to.be.false;
|
|
262
|
+
|
|
263
|
+
// Escrow's PaymentCompleted event fires for the same paymentId.
|
|
264
|
+
const completed = parseEscrowEvent(zkEscrow, result, "PaymentCompleted");
|
|
265
|
+
expect(completed, "PaymentCompleted event not emitted").to.not.be.undefined;
|
|
266
|
+
expect(completed!.paymentId).to.equal(paymentId);
|
|
177
267
|
});
|
|
178
268
|
});
|
|
269
|
+
|
|
270
|
+
// parseEscrowEvent extracts the first event of a given name from a
|
|
271
|
+
// transaction receipt by parsing logs through the escrow contract's
|
|
272
|
+
// own ABI. Unlike `parseUTXOEvents`, which only knows about the Zeto
|
|
273
|
+
// token's UTXO event family, this helper handles the escrow's
|
|
274
|
+
// custom Payment* events.
|
|
275
|
+
function parseEscrowEvent(
|
|
276
|
+
escrowContract: any,
|
|
277
|
+
receipt: ContractTransactionReceipt | null,
|
|
278
|
+
name: string,
|
|
279
|
+
): any | undefined {
|
|
280
|
+
if (!receipt) return undefined;
|
|
281
|
+
for (const log of receipt.logs || []) {
|
|
282
|
+
let parsed;
|
|
283
|
+
try {
|
|
284
|
+
parsed = escrowContract.interface.parseLog(log as any);
|
|
285
|
+
} catch (_e) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (parsed && parsed.name === name) {
|
|
289
|
+
// Return the args Result directly; ethers exposes named fields
|
|
290
|
+
// (e.g. result.paymentId) alongside positional ones.
|
|
291
|
+
return parsed.args;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// encodeProofOnly mirrors the wire format that
|
|
298
|
+
// {Zeto_Anon.constructPublicInputs} expects: a bare `Commonlib.Proof`
|
|
299
|
+
// tuple (no merkle root prefix, since Zeto_Anon is non-nullifier).
|
|
300
|
+
function encodeProofOnly(proof: any) {
|
|
301
|
+
return new AbiCoder().encode(
|
|
302
|
+
["tuple(uint256[2] pA, uint256[2][2] pB, uint256[2] pC)"],
|
|
303
|
+
[proof],
|
|
304
|
+
);
|
|
305
|
+
}
|