@lfdecentralizedtrust/zeto-contracts 0.2.2 → 0.5.1

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.
Files changed (164) hide show
  1. package/README.md +76 -162
  2. package/config/eip170.ts +30 -0
  3. package/contracts/factory.sol +30 -34
  4. package/contracts/factory_upgradeable.sol +11 -7
  5. package/contracts/lib/common/util.sol +40 -0
  6. package/contracts/lib/interfaces/ILockableCapability.sol +318 -0
  7. package/contracts/lib/interfaces/{izeto.sol → IZeto.sol} +13 -1
  8. package/contracts/lib/interfaces/{izeto_initializable.sol → IZetoInitializable.sol} +14 -12
  9. package/contracts/lib/interfaces/{izeto_kyc.sol → IZetoKyc.sol} +3 -3
  10. package/contracts/lib/interfaces/IZetoLockHooks.sol +42 -0
  11. package/contracts/lib/interfaces/IZetoLockableCapability.sol +237 -0
  12. package/contracts/lib/interfaces/IZetoStorage.sol +106 -0
  13. package/contracts/lib/interfaces/{izeto_lockable.sol → IZetoVerifier.sol} +8 -21
  14. package/contracts/lib/registry.sol +74 -26
  15. package/contracts/lib/storage/base.sol +210 -0
  16. package/contracts/lib/storage/nullifier.sol +166 -0
  17. package/contracts/lib/zeto_common.sol +277 -28
  18. package/contracts/lib/zeto_fungible.sol +443 -33
  19. package/contracts/lib/zeto_fungible_base.sol +69 -0
  20. package/contracts/lib/zeto_fungible_burn.sol +83 -53
  21. package/contracts/lib/zeto_fungible_burn_nullifier.sol +115 -72
  22. package/contracts/lib/zeto_fungible_nullifier.sol +167 -0
  23. package/contracts/lib/zeto_lockable.sol +219 -0
  24. package/contracts/lib/zeto_lockable_lib.sol +460 -0
  25. package/contracts/lib/zeto_lockable_storage.sol +43 -0
  26. package/contracts/lib/zeto_non_fungible.sol +228 -0
  27. package/contracts/lib/zeto_non_fungible_base.sol +61 -0
  28. package/contracts/lib/zeto_non_fungible_nullifier.sol +61 -0
  29. package/contracts/test/escrow1.sol +90 -34
  30. package/contracts/test/escrow2.sol +64 -29
  31. package/contracts/test/qurrency.sol +10 -2
  32. package/contracts/test/smt.sol +6 -1
  33. package/contracts/test/tendecimals.sol +14 -12
  34. package/contracts/verifiers/impl/anon.sol +189 -0
  35. package/contracts/verifiers/impl/anon_batch.sol +301 -0
  36. package/contracts/verifiers/impl/anon_enc.sol +266 -0
  37. package/contracts/verifiers/impl/anon_enc_batch.sol +602 -0
  38. package/contracts/verifiers/impl/anon_enc_nullifier.sol +287 -0
  39. package/contracts/verifiers/impl/anon_enc_nullifier_batch.sol +679 -0
  40. package/contracts/verifiers/impl/anon_enc_nullifier_kyc.sol +294 -0
  41. package/contracts/verifiers/impl/anon_enc_nullifier_kyc_batch.sol +686 -0
  42. package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation.sol +413 -0
  43. package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation_batch.sol +1141 -0
  44. package/contracts/verifiers/impl/anon_nullifier_kyc_transfer.sol +217 -0
  45. package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked.sol +196 -0
  46. package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked_batch.sol +308 -0
  47. package/contracts/verifiers/impl/anon_nullifier_kyc_transfer_batch.sol +385 -0
  48. package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer.sol +497 -0
  49. package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer_batch.sol +1001 -0
  50. package/contracts/verifiers/{verifier_anon_nullifier_transferLocked.sol → impl/anon_nullifier_transfer.sol} +12 -19
  51. package/contracts/verifiers/{verifier_anon_nullifier_transferLocked_batch.sol → impl/anon_nullifier_transfer_batch.sol} +44 -51
  52. package/contracts/verifiers/impl/burn.sol +182 -0
  53. package/contracts/verifiers/impl/burn_batch.sol +238 -0
  54. package/contracts/verifiers/impl/burn_nullifier.sol +203 -0
  55. package/contracts/verifiers/impl/burn_nullifier_batch.sol +315 -0
  56. package/contracts/verifiers/impl/deposit.sol +182 -0
  57. package/contracts/verifiers/impl/deposit_kyc.sol +189 -0
  58. package/contracts/verifiers/impl/nf_anon.sol +175 -0
  59. package/contracts/verifiers/{verifier_nf_anon_nullifier_transferLocked.sol → impl/nf_anon_nullifier_transfer.sol} +6 -13
  60. package/contracts/verifiers/impl/withdraw.sol +189 -0
  61. package/contracts/verifiers/impl/withdraw_batch.sol +245 -0
  62. package/contracts/verifiers/impl/withdraw_nullifier.sol +210 -0
  63. package/contracts/verifiers/impl/withdraw_nullifier_batch.sol +322 -0
  64. package/contracts/verifiers/verifier_anon.sol +31 -186
  65. package/contracts/verifiers/verifier_anon_batch.sol +31 -298
  66. package/contracts/verifiers/verifier_anon_enc.sol +31 -263
  67. package/contracts/verifiers/verifier_anon_enc_batch.sol +31 -599
  68. package/contracts/verifiers/verifier_anon_enc_nullifier.sol +31 -284
  69. package/contracts/verifiers/verifier_anon_enc_nullifier_batch.sol +33 -676
  70. package/contracts/verifiers/verifier_anon_enc_nullifier_kyc.sol +31 -291
  71. package/contracts/verifiers/verifier_anon_enc_nullifier_kyc_batch.sol +33 -683
  72. package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation.sol +33 -410
  73. package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation_batch.sol +33 -1138
  74. package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer.sol +33 -214
  75. package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked.sol +33 -221
  76. package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked_batch.sol +33 -389
  77. package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer_batch.sol +33 -382
  78. package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer.sol +33 -221
  79. package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer_batch.sol +33 -389
  80. package/contracts/verifiers/verifier_anon_nullifier_transfer.sol +33 -207
  81. package/contracts/verifiers/verifier_anon_nullifier_transfer_batch.sol +33 -375
  82. package/contracts/verifiers/verifier_burn.sol +31 -179
  83. package/contracts/verifiers/verifier_burn_batch.sol +31 -235
  84. package/contracts/verifiers/verifier_burn_nullifier.sol +31 -200
  85. package/contracts/verifiers/verifier_burn_nullifier_batch.sol +31 -312
  86. package/contracts/verifiers/verifier_deposit.sol +31 -179
  87. package/contracts/verifiers/verifier_deposit_kyc.sol +34 -0
  88. package/contracts/verifiers/verifier_nf_anon.sol +31 -172
  89. package/contracts/verifiers/verifier_nf_anon_nullifier_transfer.sol +33 -179
  90. package/contracts/verifiers/verifier_withdraw.sol +31 -186
  91. package/contracts/verifiers/verifier_withdraw_batch.sol +31 -242
  92. package/contracts/verifiers/verifier_withdraw_nullifier.sol +31 -207
  93. package/contracts/verifiers/verifier_withdraw_nullifier_batch.sol +33 -319
  94. package/contracts/zeto_anon.sol +77 -231
  95. package/contracts/zeto_anon_burnable.sol +56 -12
  96. package/contracts/zeto_anon_enc.sol +93 -190
  97. package/contracts/zeto_anon_enc_nullifier.sol +249 -195
  98. package/contracts/zeto_anon_enc_nullifier_kyc.sol +51 -232
  99. package/contracts/zeto_anon_enc_nullifier_non_repudiation.sol +231 -238
  100. package/contracts/zeto_anon_nullifier.sol +164 -298
  101. package/contracts/zeto_anon_nullifier_burnable.sol +68 -18
  102. package/contracts/zeto_anon_nullifier_kyc.sol +40 -345
  103. package/contracts/zeto_anon_nullifier_qurrency.sol +217 -361
  104. package/contracts/zeto_nf_anon.sol +55 -130
  105. package/contracts/zeto_nf_anon_nullifier.sol +90 -152
  106. package/hardhat.config.ts +18 -3
  107. package/ignition/modules/lib/deps.ts +13 -0
  108. package/ignition/modules/test/tendecimals.ts +7 -1
  109. package/ignition/modules/zeto_anon.ts +3 -0
  110. package/ignition/modules/zeto_anon_burnable.ts +11 -7
  111. package/ignition/modules/zeto_anon_enc.ts +3 -0
  112. package/ignition/modules/zeto_anon_enc_nullifier.ts +34 -0
  113. package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +5 -2
  114. package/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +3 -0
  115. package/ignition/modules/zeto_anon_nullifier.ts +31 -4
  116. package/ignition/modules/zeto_anon_nullifier_burnable.ts +53 -16
  117. package/ignition/modules/zeto_anon_nullifier_kyc.ts +30 -12
  118. package/ignition/modules/zeto_anon_nullifier_qurrency.ts +3 -0
  119. package/ignition/modules/zeto_nf_anon.ts +3 -1
  120. package/ignition/modules/zeto_nf_anon_nullifier.ts +17 -12
  121. package/package.json +7 -3
  122. package/scripts/deploy_cloneable.ts +19 -10
  123. package/scripts/deploy_upgradeable.ts +9 -5
  124. package/scripts/lib/zeto_libraries.ts +47 -0
  125. package/scripts/tokens/Zeto_Anon.ts +6 -3
  126. package/scripts/tokens/Zeto_AnonBurnable.ts +4 -1
  127. package/scripts/tokens/Zeto_AnonEnc.ts +15 -3
  128. package/scripts/tokens/Zeto_AnonEncNullifier.ts +11 -8
  129. package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +7 -6
  130. package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +7 -6
  131. package/scripts/tokens/Zeto_AnonNullifier.ts +7 -6
  132. package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +7 -6
  133. package/scripts/tokens/Zeto_AnonNullifierKyc.ts +7 -6
  134. package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +7 -8
  135. package/scripts/tokens/Zeto_NfAnon.ts +5 -3
  136. package/scripts/tokens/Zeto_NfAnonNullifier.ts +7 -7
  137. package/test/factory.ts +55 -73
  138. package/test/lib/anon_nullifier_helpers.ts +89 -0
  139. package/test/lib/anon_zeto_helpers.ts +76 -0
  140. package/test/lib/deploy.ts +5 -2
  141. package/test/lib/eip170.ts +23 -0
  142. package/test/lib/utils.ts +74 -35
  143. package/test/test/escrow1.ts +185 -58
  144. package/test/test/escrow2.ts +200 -107
  145. package/test/test/qurrency.ts +13 -33
  146. package/test/usdc-shielding.ts +39 -27
  147. package/test/utils.ts +144 -21
  148. package/test/zeto_anon.ts +956 -465
  149. package/test/zeto_anon_enc.ts +881 -143
  150. package/test/zeto_anon_enc_nullifier.ts +850 -39
  151. package/test/zeto_anon_enc_nullifier_kyc.ts +123 -182
  152. package/test/zeto_anon_enc_nullifier_non_repudiation.ts +80 -47
  153. package/test/zeto_anon_nullifier.ts +1212 -954
  154. package/test/zeto_anon_nullifier_kyc.ts +677 -656
  155. package/test/zeto_anon_nullifier_qurrency.ts +455 -307
  156. package/test/zeto_nf_anon.ts +737 -138
  157. package/test/zeto_nf_anon_nullifier.ts +876 -247
  158. package/contracts/lib/zeto_base.sol +0 -293
  159. package/contracts/lib/zeto_fungible_withdraw.sol +0 -132
  160. package/contracts/lib/zeto_fungible_withdraw_nullifier.sol +0 -144
  161. package/contracts/lib/zeto_nullifier.sol +0 -340
  162. package/contracts/zkDvP.sol_ +0 -273
  163. package/test/zkDvP.ts_ +0 -455
  164. /package/contracts/lib/{common.sol → common/common.sol} +0 -0
@@ -15,7 +15,12 @@
15
15
  // limitations under the License.
16
16
 
17
17
  import { ethers, network } from "hardhat";
18
- import { ContractTransactionReceipt, Signer, BigNumberish } from "ethers";
18
+ import {
19
+ ContractTransactionReceipt,
20
+ Signer,
21
+ BigNumberish,
22
+ AbiCoder,
23
+ } from "ethers";
19
24
  import { expect } from "chai";
20
25
  import {
21
26
  loadCircuit,
@@ -46,13 +51,17 @@ import {
46
51
  } from "./lib/utils";
47
52
  import {
48
53
  loadProvingKeys,
49
- prepareDepositProof,
54
+ prepareDepositKycProof,
50
55
  prepareNullifierWithdrawProof,
56
+ encodeToBytesForDeposit,
57
+ encodeToBytesForWithdraw,
51
58
  } from "./utils";
52
59
  import { deployZeto } from "./lib/deploy";
53
60
  const poseidonHash = Poseidon.poseidon4;
54
61
 
55
- describe("Zeto based fungible token with anonymity using nullifiers and encryption with KYC", function () {
62
+ describe(
63
+ "Zeto based fungible token with anonymity using nullifiers and encryption with KYC",
64
+ function () {
56
65
  let deployer: Signer;
57
66
  let Alice: User;
58
67
  let Bob: User;
@@ -91,11 +100,17 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
91
100
 
92
101
  ({ deployer, zeto, erc20 } = await deployZeto("Zeto_AnonEncNullifierKyc"));
93
102
 
94
- const tx2 = await zeto.connect(deployer).register(Alice.babyJubPublicKey, '0x');
103
+ const tx2 = await zeto
104
+ .connect(deployer)
105
+ .register(Alice.babyJubPublicKey, "0x");
95
106
  const result1 = await tx2.wait();
96
- const tx3 = await zeto.connect(deployer).register(Bob.babyJubPublicKey, '0x');
107
+ const tx3 = await zeto
108
+ .connect(deployer)
109
+ .register(Bob.babyJubPublicKey, "0x");
97
110
  const result2 = await tx3.wait();
98
- const tx4 = await zeto.connect(deployer).register(Charlie.babyJubPublicKey, '0x');
111
+ const tx4 = await zeto
112
+ .connect(deployer)
113
+ .register(Charlie.babyJubPublicKey, "0x");
99
114
  const result3 = await tx4.wait();
100
115
 
101
116
  const storage1 = new InMemoryDB(str2Bytes("alice"));
@@ -225,12 +240,13 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
225
240
 
226
241
  const signerAddress = await Alice.signer.getAddress();
227
242
  const events = parseUTXOEvents(zeto, result.txResult!);
228
- expect(events[0].submitter).to.equal(signerAddress);
229
- expect(events[0].inputs).to.deep.equal(nullifiers.map((n) => n.hash));
243
+ const event = events[0];
244
+ expect(event.submitter).to.equal(signerAddress);
245
+ expect(event.inputs).to.deep.equal(nullifiers.map((n) => n.hash));
230
246
 
231
- const incomingUTXOs: any = events[0].outputs;
247
+ const incomingUTXOs: any = event.outputs;
232
248
 
233
- const ecdhPublicKey = events[0].ecdhPublicKey;
249
+ const ecdhPublicKey = event.ecdhPublicKey;
234
250
  // check the non-empty output hashes are correct
235
251
  for (let i = 0; i < outputUtxos.length; i++) {
236
252
  const utxoOwner = outputOwners[i];
@@ -239,9 +255,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
239
255
  ecdhPublicKey,
240
256
  );
241
257
  const plainText = poseidonDecrypt(
242
- events[0].encryptedValues.slice(4 * i, 4 * i + 4),
258
+ event.encryptedValues.slice(4 * i, 4 * i + 4),
243
259
  sharedKey,
244
- events[0].encryptionNonce,
260
+ event.encryptionNonce,
245
261
  2,
246
262
  );
247
263
  expect(plainText).to.deep.equal(
@@ -259,11 +275,6 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
259
275
  await smtUnregistered.add(incomingUTXOs[i], incomingUTXOs[i]);
260
276
  }
261
277
 
262
- // check empty values, salt and hashes are empty
263
- for (let i = outputUtxos.length; i < 10; i++) {
264
- expect(incomingUTXOs[i]).to.equal(0);
265
- }
266
-
267
278
  // mint sufficient balance in Zeto contract address for Alice to withdraw
268
279
  const mintTx = await erc20.connect(deployer).mint(zeto, 3);
269
280
  await mintTx.wait();
@@ -316,8 +327,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
316
327
  3,
317
328
  _withdrawNullifiers,
318
329
  withdrawCommitments[0],
319
- root.bigInt(),
320
- withdrawEncodedProof,
330
+ encodeToBytesForWithdraw(root.bigInt(), withdrawEncodedProof),
321
331
  "0x",
322
332
  );
323
333
  await tx.wait();
@@ -339,13 +349,29 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
339
349
 
340
350
  utxo100 = newUTXO(100, Alice);
341
351
  utxo0 = newUTXO(0, Alice);
342
- const { outputCommitments, encodedProof } = await prepareDepositProof(
352
+ const identitiesRoot = await smtKyc.root();
353
+ const proof3 = await smtKyc.generateCircomVerifierProof(
354
+ kycHash(Alice.babyJubPublicKey),
355
+ identitiesRoot,
356
+ );
357
+ const identitiesMerkleProofs = [
358
+ proof3.siblings.map((s) => s.bigInt()),
359
+ proof3.siblings.map((s) => s.bigInt()),
360
+ ];
361
+ const { outputCommitments, encodedProof } = await prepareDepositKycProof(
343
362
  Alice,
344
363
  [utxo100, utxo0],
364
+ identitiesRoot.bigInt(),
365
+ identitiesMerkleProofs,
345
366
  );
346
367
  const tx2 = await zeto
347
368
  .connect(Alice.signer)
348
- .deposit(100, outputCommitments, encodedProof, "0x");
369
+ .deposit(
370
+ 100,
371
+ outputCommitments,
372
+ encodeToBytesForDeposit(encodedProof),
373
+ "0x",
374
+ );
349
375
  await tx2.wait();
350
376
 
351
377
  await smtAlice.add(utxo100.hash, utxo100.hash);
@@ -444,20 +470,21 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
444
470
  // Bob parses the UTXOs from the onchain event
445
471
  const signerAddress = await Alice.signer.getAddress();
446
472
  const events = parseUTXOEvents(zeto, result2.txResult!);
447
- expect(events[0].submitter).to.equal(signerAddress);
448
- expect(events[0].inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
449
- expect(events[0].outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
450
- await smtBob.add(events[0].outputs[0], events[0].outputs[0]);
451
- await smtBob.add(events[0].outputs[1], events[0].outputs[1]);
452
-
453
- const ecdhPublicKey = events[0].ecdhPublicKey;
473
+ const event = events[0];
474
+ expect(event.submitter).to.equal(signerAddress);
475
+ expect(event.inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
476
+ expect(event.outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
477
+ await smtBob.add(event.outputs[0], event.outputs[0]);
478
+ await smtBob.add(event.outputs[1], event.outputs[1]);
479
+
480
+ const ecdhPublicKey = event.ecdhPublicKey;
454
481
  // Bob reconstructs the shared key using his private key and ephemeral public key
455
482
 
456
483
  const sharedKey = genEcdhSharedKey(Bob.babyJubPrivateKey, ecdhPublicKey);
457
484
  const plainText = poseidonDecrypt(
458
- events[0].encryptedValues.slice(0, 4),
485
+ event.encryptedValues.slice(0, 4),
459
486
  sharedKey,
460
- events[0].encryptionNonce,
487
+ event.encryptionNonce,
461
488
  2,
462
489
  );
463
490
  expect(plainText).to.deep.equal(result2.expectedPlainText.slice(0, 2));
@@ -522,8 +549,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
522
549
 
523
550
  // Alice gets the new UTXOs from the onchain event and keeps the local SMT in sync
524
551
  const events = parseUTXOEvents(zeto, result.txResult!);
525
- await smtAlice.add(events[0].outputs[0], events[0].outputs[0]);
526
- await smtAlice.add(events[0].outputs[1], events[0].outputs[1]);
552
+ const event = events[0];
553
+ await smtAlice.add(event.outputs[0], event.outputs[0]);
554
+ await smtAlice.add(event.outputs[1], event.outputs[1]);
527
555
  }).timeout(600000);
528
556
 
529
557
  it("Alice withdraws her UTXOs to ERC20 tokens should succeed", async function () {
@@ -564,8 +592,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
564
592
  80,
565
593
  nullifiers,
566
594
  outputCommitments[0],
567
- root.bigInt(),
568
- encodedProof,
595
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
569
596
  "0x",
570
597
  );
571
598
  await tx.wait();
@@ -583,8 +610,30 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
583
610
  describe("unregistered user cases", function () {
584
611
  let unregisteredUtxo100: UTXO;
585
612
  let unregisteredUtxo0: UTXO;
613
+ let smtKycUnregistered: Merkletree;
614
+
615
+ before(async function () {
616
+ const storage5 = new InMemoryDB(str2Bytes("unregisteredKyc"));
617
+ smtKycUnregistered = new Merkletree(storage5, true, 10);
618
+
619
+ // add the unregistered user to the local KYC SMT, but not to the onchain SMT
620
+ await smtKycUnregistered.add(
621
+ kycHash(unregistered.babyJubPublicKey),
622
+ kycHash(unregistered.babyJubPublicKey),
623
+ );
624
+ });
625
+
626
+ it("deposit by an unregistered user should fail", async function () {
627
+ const identitiesRoot = await smtKycUnregistered.root();
628
+ const proof3 = await smtKycUnregistered.generateCircomVerifierProof(
629
+ kycHash(unregistered.babyJubPublicKey),
630
+ identitiesRoot,
631
+ );
632
+ const identitiesMerkleProofs = [
633
+ proof3.siblings.map((s) => s.bigInt()),
634
+ proof3.siblings.map((s) => s.bigInt()),
635
+ ];
586
636
 
587
- it("deposit by an unregistered user should succeed", async function () {
588
637
  const tx = await erc20
589
638
  .connect(deployer)
590
639
  .mint(unregistered.ethAddress, 100);
@@ -596,150 +645,22 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
596
645
 
597
646
  unregisteredUtxo100 = newUTXO(100, unregistered);
598
647
  unregisteredUtxo0 = newUTXO(0, unregistered);
599
- const { outputCommitments, encodedProof } = await prepareDepositProof(
648
+ const { outputCommitments, encodedProof } = await prepareDepositKycProof(
600
649
  unregistered,
601
650
  [unregisteredUtxo100, unregisteredUtxo0],
651
+ identitiesRoot.bigInt(),
652
+ identitiesMerkleProofs,
602
653
  );
603
- const tx2 = await zeto
604
- .connect(unregistered.signer)
605
- .deposit(100, outputCommitments, encodedProof, "0x");
606
- await tx2.wait();
607
-
608
- // Alice tracks the UTXO inside the SMT
609
- await smtAlice.add(unregisteredUtxo100.hash, unregisteredUtxo100.hash);
610
- await smtAlice.add(unregisteredUtxo0.hash, unregisteredUtxo0.hash);
611
- // Bob also locally tracks the UTXOs inside the SMT
612
- await smtBob.add(unregisteredUtxo100.hash, unregisteredUtxo100.hash);
613
- await smtBob.add(unregisteredUtxo0.hash, unregisteredUtxo0.hash);
614
- });
615
-
616
- it("transfer from an unregistered user should fail", async function () {
617
- // catch up the local SMT for the unregistered user
618
- await smtUnregistered.add(utxo100.hash, utxo100.hash);
619
- await smtUnregistered.add(utxo0.hash, utxo0.hash);
620
- await smtUnregistered.add(utxo1.hash, utxo1.hash);
621
- await smtUnregistered.add(utxo2.hash, utxo2.hash);
622
- await smtUnregistered.add(_utxo3.hash, _utxo3.hash);
623
- await smtUnregistered.add(utxo4.hash, utxo4.hash);
624
- await smtUnregistered.add(utxo6.hash, utxo6.hash);
625
- await smtUnregistered.add(utxo7.hash, utxo7.hash);
626
- await smtUnregistered.add(
627
- withdrawChangeUTXO.hash,
628
- withdrawChangeUTXO.hash,
629
- );
630
- await smtUnregistered.add(
631
- unregisteredUtxo100.hash,
632
- unregisteredUtxo100.hash,
633
- );
634
- await smtUnregistered.add(unregisteredUtxo0.hash, unregisteredUtxo0.hash);
635
- const utxosRoot = await smtUnregistered.root();
636
-
637
- const nullifier = newNullifier(unregisteredUtxo100, unregistered);
638
- const output1 = newUTXO(100, Bob);
639
- const output2 = newUTXO(0, unregistered);
640
- const proof = await smtUnregistered.generateCircomVerifierProof(
641
- unregisteredUtxo100.hash,
642
- utxosRoot,
643
- );
644
- const merkleProofs = [
645
- proof.siblings.map((s) => s.bigInt()),
646
- proof.siblings.map((s) => s.bigInt()),
647
- ];
648
-
649
- // add the unregistered user to the local KYC SMT, but not to the onchain SMT
650
- await smtKyc.add(
651
- kycHash(unregistered.babyJubPublicKey),
652
- kycHash(unregistered.babyJubPublicKey),
653
- );
654
- const identitiesRoot = await smtKyc.root();
655
- const proof3 = await smtKyc.generateCircomVerifierProof(
656
- kycHash(unregistered.babyJubPublicKey),
657
- identitiesRoot,
658
- );
659
- const proof4 = await smtKyc.generateCircomVerifierProof(
660
- kycHash(Bob.babyJubPublicKey),
661
- identitiesRoot,
662
- );
663
- const identitiesMerkleProofs = [
664
- proof3.siblings.map((s) => s.bigInt()), // identity proof for the sender (unregistered)
665
- proof4.siblings.map((s) => s.bigInt()), // identity proof for the 1st owner of the output UTXO (Bob)
666
- proof3.siblings.map((s) => s.bigInt()), // identity proof for the 2nd owner of the output UTXO (unregistered)
667
- ];
668
654
  await expect(
669
- doTransfer(
670
- unregistered,
671
- [unregisteredUtxo100, ZERO_UTXO],
672
- [nullifier, ZERO_UTXO],
673
- [output1, output2],
674
- utxosRoot.bigInt(),
675
- merkleProofs,
676
- identitiesRoot.bigInt(),
677
- identitiesMerkleProofs,
678
- [Bob, unregistered],
679
- ),
680
- ).rejectedWith("Invalid proof");
681
- });
682
-
683
- it("the unregistered user can still withdraw their UTXOs to ERC20 tokens", async function () {
684
- const startingBalance = await erc20.balanceOf(unregistered.ethAddress);
685
- // unregistered user generates the nullifiers for the UTXOs to be spent
686
- const nullifier1 = newNullifier(unregisteredUtxo100, unregistered);
687
-
688
- // unregistered user generates inclusion proofs for the UTXOs to be spent
689
- let root = await smtUnregistered.root();
690
- const proof1 = await smtUnregistered.generateCircomVerifierProof(
691
- unregisteredUtxo100.hash,
692
- root,
693
- );
694
- const proof2 = await smtUnregistered.generateCircomVerifierProof(
695
- 0n,
696
- root,
697
- );
698
- const merkleProofs = [
699
- proof1.siblings.map((s) => s.bigInt()),
700
- proof2.siblings.map((s) => s.bigInt()),
701
- ];
702
-
703
- // unregistered user proposes the output ERC20 tokens
704
- const unregisteredWithdrawChangeUTXO = newUTXO(0, unregistered);
705
-
706
- const { nullifiers, outputCommitments, encodedProof } =
707
- await prepareNullifierWithdrawProof(
708
- unregistered,
709
- [unregisteredUtxo100, ZERO_UTXO],
710
- [nullifier1, ZERO_UTXO],
711
- unregisteredWithdrawChangeUTXO,
712
- root.bigInt(),
713
- merkleProofs,
714
- );
715
-
716
- // unregistered user withdraws her UTXOs to ERC20 tokens
717
- const tx = await zeto
718
- .connect(unregistered.signer)
719
- .withdraw(
720
- 100,
721
- nullifiers,
722
- outputCommitments[0],
723
- root.bigInt(),
724
- encodedProof,
725
- "0x",
726
- );
727
- await tx.wait();
728
-
729
- // Alice tracks the UTXO inside the SMT
730
- await smtAlice.add(
731
- unregisteredWithdrawChangeUTXO.hash,
732
- unregisteredWithdrawChangeUTXO.hash,
733
- );
734
- // Bob also locally tracks the UTXOs inside the SMT
735
- await smtBob.add(
736
- unregisteredWithdrawChangeUTXO.hash,
737
- unregisteredWithdrawChangeUTXO.hash,
738
- );
739
-
740
- // unregistered user checks her ERC20 balance
741
- const endingBalance = await erc20.balanceOf(unregistered.ethAddress);
742
- expect(endingBalance - startingBalance).to.be.equal(100);
655
+ zeto
656
+ .connect(unregistered.signer)
657
+ .deposit(
658
+ 100,
659
+ outputCommitments,
660
+ encodeToBytesForDeposit(encodedProof),
661
+ "0x",
662
+ ),
663
+ ).to.be.rejectedWith("VM Exception while processing transaction: reverted with custom error 'InvalidProof()'");
743
664
  });
744
665
  });
745
666
 
@@ -788,8 +709,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
788
709
  10,
789
710
  nullifiers,
790
711
  outputCommitments[0],
791
- root.bigInt(),
792
- encodedProof,
712
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
793
713
  "0x",
794
714
  ),
795
715
  ).rejectedWith("UTXOAlreadySpent");
@@ -1181,11 +1101,13 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
1181
1101
  const tx = await zeto.connect(signer.signer).transfer(
1182
1102
  nullifiers.filter((ic) => ic !== 0n), // trim off empty utxo hashes to check padding logic for batching works
1183
1103
  outputCommitments.filter((oc) => oc !== 0n), // trim off empty utxo hashes to check padding logic for batching works
1184
- root,
1185
- encryptionNonce,
1186
- ecdhPublicKey,
1187
- encryptedValues,
1188
- encodedProof,
1104
+ encodeToBytes(
1105
+ root,
1106
+ encryptionNonce,
1107
+ ecdhPublicKey,
1108
+ encryptedValues,
1109
+ encodedProof,
1110
+ ),
1189
1111
  "0x",
1190
1112
  );
1191
1113
  const results: ContractTransactionReceipt | null = await tx.wait();
@@ -1195,3 +1117,22 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
1195
1117
  return results;
1196
1118
  }
1197
1119
  });
1120
+
1121
+ function encodeToBytes(
1122
+ root: any,
1123
+ encryptionNonce: any,
1124
+ ecdhPublicKey: any,
1125
+ encryptedValues: any,
1126
+ proof: any,
1127
+ ) {
1128
+ return new AbiCoder().encode(
1129
+ [
1130
+ "uint256 root",
1131
+ "uint256 encryptionNonce",
1132
+ "uint256[2] ecdhPublicKey",
1133
+ "uint256[] encryptedValues",
1134
+ "tuple(uint256[2] pA, uint256[2][2] pB, uint256[2] pC)",
1135
+ ],
1136
+ [root, encryptionNonce, ecdhPublicKey, encryptedValues, proof],
1137
+ );
1138
+ }
@@ -15,7 +15,12 @@
15
15
  // limitations under the License.
16
16
 
17
17
  import { ethers, network } from "hardhat";
18
- import { ContractTransactionReceipt, Signer, BigNumberish } from "ethers";
18
+ import {
19
+ ContractTransactionReceipt,
20
+ Signer,
21
+ BigNumberish,
22
+ AbiCoder,
23
+ } from "ethers";
19
24
  import { expect } from "chai";
20
25
  import {
21
26
  loadCircuit,
@@ -46,11 +51,15 @@ import {
46
51
  loadProvingKeys,
47
52
  prepareDepositProof,
48
53
  prepareNullifierWithdrawProof,
54
+ encodeToBytesForDeposit,
55
+ encodeToBytesForWithdraw,
49
56
  } from "./utils";
50
57
  import { deployZeto } from "./lib/deploy";
51
58
  const poseidonHash = Poseidon.poseidon4;
52
59
 
53
- describe("Zeto based fungible token with anonymity using nullifiers and encryption for non-repudiation", function () {
60
+ describe(
61
+ "Zeto based fungible token with anonymity using nullifiers and encryption for non-repudiation",
62
+ function () {
54
63
  let deployer: Signer;
55
64
  let Alice: User;
56
65
  let Bob: User;
@@ -177,12 +186,13 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
177
186
 
178
187
  const signerAddress = await Alice.signer.getAddress();
179
188
  const events = parseUTXOEvents(zeto, result.txResult!);
180
- expect(events[0].submitter).to.equal(signerAddress);
181
- expect(events[0].inputs).to.deep.equal(nullifiers.map((n) => n.hash));
189
+ const event = events[0];
190
+ expect(event.submitter).to.equal(signerAddress);
191
+ expect(event.inputs).to.deep.equal(nullifiers.map((n) => n.hash));
182
192
 
183
- const incomingUTXOs: any = events[0].outputs;
193
+ const incomingUTXOs: any = event.outputs;
184
194
 
185
- const ecdhPublicKey = events[0].ecdhPublicKey;
195
+ const ecdhPublicKey = event.ecdhPublicKey;
186
196
 
187
197
  // check the non-empty output hashes are correct
188
198
  for (let i = 0; i < outputUtxos.length; i++) {
@@ -192,9 +202,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
192
202
  ecdhPublicKey,
193
203
  );
194
204
  const plainText = poseidonDecrypt(
195
- events[0].encryptedValuesForReceiver.slice(4 * i, 4 * i + 4),
205
+ event.encryptedValuesForReceiver.slice(4 * i, 4 * i + 4),
196
206
  sharedKey,
197
- events[0].encryptionNonce,
207
+ event.encryptionNonce,
198
208
  2,
199
209
  );
200
210
  expect(plainText).to.deep.equal(
@@ -210,20 +220,15 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
210
220
  await smtAlice.add(incomingUTXOs[i], incomingUTXOs[i]);
211
221
  await smtBob.add(incomingUTXOs[i], incomingUTXOs[i]);
212
222
  }
213
-
214
- // check empty values, salt and hashes are empty
215
- for (let i = outputUtxos.length; i < 10; i++) {
216
- expect(incomingUTXOs[i]).to.equal(0);
217
- }
218
223
  // The regulator uses the encrypted values in the event to decrypt and recover the UTXO value and salt
219
224
  const auditKey = genEcdhSharedKey(
220
225
  Authority.babyJubPrivateKey,
221
226
  ecdhPublicKey,
222
227
  );
223
228
  const auditPlainText = poseidonDecrypt(
224
- events[0].encryptedValuesForAuthority,
229
+ event.encryptedValuesForAuthority,
225
230
  auditKey,
226
- events[0].encryptionNonce,
231
+ event.encryptionNonce,
227
232
  62,
228
233
  );
229
234
 
@@ -252,12 +257,6 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
252
257
  expect(calHash).to.equal(outputUtxos[i].hash);
253
258
  }
254
259
 
255
- // check empty hashes are empty
256
- for (let i = outputUtxos.length; i < 10; i++) {
257
- expect(auditPlainText[2 * i + 42]).to.equal(0);
258
- expect(auditPlainText[2 * i + 43]).to.equal(0);
259
- }
260
-
261
260
  // mint sufficient balance in Zeto contract address for Alice to withdraw
262
261
  const mintTx = await erc20.connect(deployer).mint(zeto, 3);
263
262
  await mintTx.wait();
@@ -310,8 +309,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
310
309
  3,
311
310
  _withdrawNullifiers,
312
311
  withdrawCommitments[0],
313
- root.bigInt(),
314
- withdrawEncodedProof,
312
+ encodeToBytesForWithdraw(root.bigInt(), withdrawEncodedProof),
315
313
  "0x",
316
314
  );
317
315
  await tx.wait();
@@ -339,7 +337,12 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
339
337
  );
340
338
  const tx2 = await zeto
341
339
  .connect(Alice.signer)
342
- .deposit(100, outputCommitments, encodedProof, "0x");
340
+ .deposit(
341
+ 100,
342
+ outputCommitments,
343
+ encodeToBytesForDeposit(encodedProof),
344
+ "0x",
345
+ );
343
346
  await tx2.wait();
344
347
 
345
348
  await smtAlice.add(utxo100.hash, utxo100.hash);
@@ -414,20 +417,21 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
414
417
  // Bob parses the UTXOs from the onchain event
415
418
  const signerAddress = await Alice.signer.getAddress();
416
419
  const events = parseUTXOEvents(zeto, result2.txResult!);
417
- expect(events[0].submitter).to.equal(signerAddress);
418
- expect(events[0].inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
419
- expect(events[0].outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
420
- await smtBob.add(events[0].outputs[0], events[0].outputs[0]);
421
- await smtBob.add(events[0].outputs[1], events[0].outputs[1]);
422
-
423
- const ecdhPublicKey = events[0].ecdhPublicKey;
420
+ const event = events[0];
421
+ expect(event.submitter).to.equal(signerAddress);
422
+ expect(event.inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
423
+ expect(event.outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
424
+ await smtBob.add(event.outputs[0], event.outputs[0]);
425
+ await smtBob.add(event.outputs[1], event.outputs[1]);
426
+
427
+ const ecdhPublicKey = event.ecdhPublicKey;
424
428
  // Bob reconstructs the shared key using his private key and ephemeral public key
425
429
 
426
430
  const sharedKey = genEcdhSharedKey(Bob.babyJubPrivateKey, ecdhPublicKey);
427
431
  const plainText1 = poseidonDecrypt(
428
- events[0].encryptedValuesForReceiver.slice(0, 4),
432
+ event.encryptedValuesForReceiver.slice(0, 4),
429
433
  sharedKey,
430
- events[0].encryptionNonce,
434
+ event.encryptionNonce,
431
435
  2,
432
436
  );
433
437
  expect(plainText1).to.deep.equal(result2.expectedPlainText.slice(0, 2));
@@ -441,9 +445,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
441
445
  ecdhPublicKey,
442
446
  );
443
447
  const plainText2 = poseidonDecrypt(
444
- events[0].encryptedValuesForAuthority,
448
+ event.encryptedValuesForAuthority,
445
449
  sharedKey2,
446
- events[0].encryptionNonce,
450
+ event.encryptionNonce,
447
451
  14,
448
452
  );
449
453
  expect(plainText2).to.deep.equal([
@@ -527,8 +531,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
527
531
 
528
532
  // Alice gets the new UTXOs from the onchain event and keeps the local SMT in sync
529
533
  const events = parseUTXOEvents(zeto, result.txResult!);
530
- await smtAlice.add(events[0].outputs[0], events[0].outputs[0]);
531
- await smtAlice.add(events[0].outputs[1], events[0].outputs[1]);
534
+ const event = events[0];
535
+ await smtAlice.add(event.outputs[0], event.outputs[0]);
536
+ await smtAlice.add(event.outputs[1], event.outputs[1]);
532
537
  }).timeout(600000);
533
538
 
534
539
  it("Alice withdraws her UTXOs to ERC20 tokens should succeed", async function () {
@@ -568,8 +573,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
568
573
  80,
569
574
  nullifiers,
570
575
  outputCommitments[0],
571
- root.bigInt(),
572
- encodedProof,
576
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
573
577
  "0x",
574
578
  );
575
579
  await tx.wait();
@@ -629,8 +633,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
629
633
  80,
630
634
  nullifiers,
631
635
  outputCommitments[0],
632
- root.bigInt(),
633
- encodedProof,
636
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
634
637
  "0x",
635
638
  ),
636
639
  ).rejectedWith("UTXOAlreadySpent");
@@ -944,12 +947,14 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
944
947
  const tx = await zeto.connect(signer.signer).transfer(
945
948
  nullifiers.filter((ic) => ic !== 0n), // trim off empty utxo hashes to check padding logic for batching works
946
949
  outputCommitments.filter((oc) => oc !== 0n), // trim off empty utxo hashes to check padding logic for batching works
947
- root,
948
- encryptionNonce,
949
- ecdhPublicKey,
950
- encryptedValuesForReceiver,
951
- encryptedValuesForRegulator,
952
- encodedProof,
950
+ encodeToBytes(
951
+ root,
952
+ encryptionNonce,
953
+ ecdhPublicKey,
954
+ encryptedValuesForReceiver,
955
+ encryptedValuesForRegulator,
956
+ encodedProof,
957
+ ),
953
958
  "0x",
954
959
  );
955
960
  const results: ContractTransactionReceipt | null = await tx.wait();
@@ -959,3 +964,31 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
959
964
  return results;
960
965
  }
961
966
  });
967
+
968
+ function encodeToBytes(
969
+ root: any,
970
+ encryptionNonce: any,
971
+ ecdhPublicKey: any,
972
+ encryptedValuesForReceiver: any,
973
+ encryptedValuesForAuthority: any,
974
+ proof: any,
975
+ ) {
976
+ return new AbiCoder().encode(
977
+ [
978
+ "uint256 root",
979
+ "uint256 encryptionNonce",
980
+ "uint256[2] ecdhPublicKey",
981
+ "uint256[] encryptedValuesForReceiver",
982
+ "uint256[] encryptedValuesForAuthority",
983
+ "tuple(uint256[2] pA, uint256[2][2] pB, uint256[2] pC)",
984
+ ],
985
+ [
986
+ root,
987
+ encryptionNonce,
988
+ ecdhPublicKey,
989
+ encryptedValuesForReceiver,
990
+ encryptedValuesForAuthority,
991
+ proof,
992
+ ],
993
+ );
994
+ }