@aztec/pxe 0.69.1 → 0.71.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.
Files changed (75) hide show
  1. package/dest/config/index.d.ts.map +1 -1
  2. package/dest/config/index.js +3 -3
  3. package/dest/database/kv_pxe_database.d.ts +6 -0
  4. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  5. package/dest/database/kv_pxe_database.js +48 -3
  6. package/dest/database/note_dao.d.ts +2 -4
  7. package/dest/database/note_dao.d.ts.map +1 -1
  8. package/dest/database/note_dao.js +1 -5
  9. package/dest/database/outgoing_note_dao.d.ts +1 -3
  10. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  11. package/dest/database/outgoing_note_dao.js +1 -5
  12. package/dest/database/pxe_database.d.ts +33 -0
  13. package/dest/database/pxe_database.d.ts.map +1 -1
  14. package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
  15. package/dest/database/pxe_database_test_suite.js +121 -1
  16. package/dest/kernel_oracle/index.js +2 -2
  17. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.d.ts +2 -2
  18. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.d.ts.map +1 -1
  19. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +1 -1
  20. package/dest/kernel_prover/index.d.ts +0 -1
  21. package/dest/kernel_prover/index.d.ts.map +1 -1
  22. package/dest/kernel_prover/index.js +1 -2
  23. package/dest/kernel_prover/kernel_prover.d.ts +8 -2
  24. package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
  25. package/dest/kernel_prover/kernel_prover.js +42 -18
  26. package/dest/note_decryption_utils/add_public_values_to_payload.js +2 -2
  27. package/dest/pxe_service/error_enriching.js +1 -1
  28. package/dest/pxe_service/pxe_service.d.ts +5 -1
  29. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  30. package/dest/pxe_service/pxe_service.js +30 -31
  31. package/dest/simulator/index.d.ts +2 -2
  32. package/dest/simulator/index.d.ts.map +1 -1
  33. package/dest/simulator/index.js +4 -4
  34. package/dest/simulator_oracle/index.d.ts +16 -9
  35. package/dest/simulator_oracle/index.d.ts.map +1 -1
  36. package/dest/simulator_oracle/index.js +131 -63
  37. package/dest/utils/create_pxe_service.d.ts.map +1 -1
  38. package/dest/utils/create_pxe_service.js +9 -12
  39. package/package.json +14 -15
  40. package/src/config/index.ts +2 -1
  41. package/src/database/kv_pxe_database.ts +62 -0
  42. package/src/database/note_dao.ts +2 -30
  43. package/src/database/outgoing_note_dao.ts +1 -28
  44. package/src/database/pxe_database.ts +37 -0
  45. package/src/database/pxe_database_test_suite.ts +160 -0
  46. package/src/kernel_oracle/index.ts +1 -1
  47. package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +4 -4
  48. package/src/kernel_prover/index.ts +0 -2
  49. package/src/kernel_prover/kernel_prover.ts +58 -20
  50. package/src/note_decryption_utils/add_public_values_to_payload.ts +1 -1
  51. package/src/pxe_service/error_enriching.ts +1 -1
  52. package/src/pxe_service/pxe_service.ts +32 -44
  53. package/src/simulator/index.ts +4 -2
  54. package/src/simulator_oracle/index.ts +224 -67
  55. package/src/utils/create_pxe_service.ts +8 -13
  56. package/dest/kernel_prover/test/test_circuit_prover.d.ts +0 -20
  57. package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +0 -1
  58. package/dest/kernel_prover/test/test_circuit_prover.js +0 -75
  59. package/dest/note_decryption_utils/brute_force_note_info.d.ts +0 -31
  60. package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +0 -1
  61. package/dest/note_decryption_utils/brute_force_note_info.js +0 -55
  62. package/dest/note_decryption_utils/index.d.ts +0 -3
  63. package/dest/note_decryption_utils/index.d.ts.map +0 -1
  64. package/dest/note_decryption_utils/index.js +0 -2
  65. package/dest/note_decryption_utils/produce_note_daos.d.ts +0 -28
  66. package/dest/note_decryption_utils/produce_note_daos.d.ts.map +0 -1
  67. package/dest/note_decryption_utils/produce_note_daos.js +0 -33
  68. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +0 -8
  69. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +0 -1
  70. package/dest/note_decryption_utils/produce_note_daos_for_key.js +0 -17
  71. package/src/kernel_prover/test/test_circuit_prover.ts +0 -122
  72. package/src/note_decryption_utils/brute_force_note_info.ts +0 -90
  73. package/src/note_decryption_utils/index.ts +0 -2
  74. package/src/note_decryption_utils/produce_note_daos.ts +0 -67
  75. package/src/note_decryption_utils/produce_note_daos_for_key.ts +0 -57
@@ -12,6 +12,7 @@ import { type ContractArtifact, FunctionSelector, FunctionType } from '@aztec/fo
12
12
  import { toBufferBE } from '@aztec/foundation/bigint-buffer';
13
13
  import { Fr } from '@aztec/foundation/fields';
14
14
  import { toArray } from '@aztec/foundation/iterable';
15
+ import { type LogFn, createDebugOnlyLogger } from '@aztec/foundation/log';
15
16
  import {
16
17
  type AztecAsyncArray,
17
18
  type AztecAsyncKVStore,
@@ -63,6 +64,11 @@ export class KVPxeDatabase implements PxeDatabase {
63
64
  #taggingSecretIndexesForSenders: AztecAsyncMap<string, number>;
64
65
  #taggingSecretIndexesForRecipients: AztecAsyncMap<string, number>;
65
66
 
67
+ // Arbitrary data stored by contracts. Key is computed as `${contractAddress}:${key}`
68
+ #contractStore: AztecAsyncMap<string, Buffer>;
69
+
70
+ debug: LogFn;
71
+
66
72
  protected constructor(private db: AztecAsyncKVStore) {
67
73
  this.#db = db;
68
74
 
@@ -100,6 +106,10 @@ export class KVPxeDatabase implements PxeDatabase {
100
106
 
101
107
  this.#taggingSecretIndexesForSenders = db.openMap('tagging_secret_indexes_for_senders');
102
108
  this.#taggingSecretIndexesForRecipients = db.openMap('tagging_secret_indexes_for_recipients');
109
+
110
+ this.#contractStore = db.openMap('contract_store');
111
+
112
+ this.debug = createDebugOnlyLogger('aztec:kv-pxe-database');
103
113
  }
104
114
 
105
115
  public static async create(db: AztecAsyncKVStore): Promise<KVPxeDatabase> {
@@ -611,4 +621,56 @@ export class KVPxeDatabase implements PxeDatabase {
611
621
  await Promise.all(senders.map(sender => this.#taggingSecretIndexesForSenders.delete(sender)));
612
622
  });
613
623
  }
624
+
625
+ async dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void> {
626
+ await this.#contractStore.set(
627
+ dbSlotToKey(contractAddress, slot),
628
+ Buffer.concat(values.map(value => value.toBuffer())),
629
+ );
630
+ }
631
+
632
+ async dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
633
+ const dataBuffer = await this.#contractStore.getAsync(dbSlotToKey(contractAddress, slot));
634
+ if (!dataBuffer) {
635
+ this.debug(`Data not found for contract ${contractAddress.toString()} and slot ${slot.toString()}`);
636
+ return null;
637
+ }
638
+ const values: Fr[] = [];
639
+ for (let i = 0; i < dataBuffer.length; i += Fr.SIZE_IN_BYTES) {
640
+ values.push(Fr.fromBuffer(dataBuffer.subarray(i, i + Fr.SIZE_IN_BYTES)));
641
+ }
642
+ return values;
643
+ }
644
+
645
+ async dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void> {
646
+ await this.#contractStore.delete(dbSlotToKey(contractAddress, slot));
647
+ }
648
+
649
+ async dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
650
+ // In order to support overlaping source and destination regions we need to check the relative positions of source
651
+ // and destination. If destination is ahead of source, then by the time we overwrite source elements using forward
652
+ // indexes we'll have already read those. On the contrary, if source is ahead of destination we need to use backward
653
+ // indexes to avoid reading elements that've been overwritten.
654
+
655
+ const indexes = Array.from(Array(numEntries).keys());
656
+ if (srcSlot.lt(dstSlot)) {
657
+ indexes.reverse();
658
+ }
659
+
660
+ for (const i of indexes) {
661
+ const currentSrcSlot = dbSlotToKey(contractAddress, srcSlot.add(new Fr(i)));
662
+ const currentDstSlot = dbSlotToKey(contractAddress, dstSlot.add(new Fr(i)));
663
+
664
+ const toCopy = await this.#contractStore.getAsync(currentSrcSlot);
665
+ if (!toCopy) {
666
+ throw new Error(`Attempted to copy empty slot ${currentSrcSlot} for contract ${contractAddress.toString()}`);
667
+ }
668
+
669
+ await this.#contractStore.set(currentDstSlot, toCopy);
670
+ }
671
+ }
672
+ }
673
+
674
+ function dbSlotToKey(contractAddress: AztecAddress, slot: Fr): string {
675
+ return `${contractAddress.toString()}:${slot.toString()}`;
614
676
  }
@@ -1,11 +1,9 @@
1
- import { type L1NotePayload, Note, TxHash, randomTxHash } from '@aztec/circuit-types';
1
+ import { Note, TxHash, randomTxHash } from '@aztec/circuit-types';
2
2
  import { AztecAddress, Fr, Point, type PublicKey } from '@aztec/circuits.js';
3
3
  import { NoteSelector } from '@aztec/foundation/abi';
4
4
  import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
5
5
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
6
- import { type NoteData } from '@aztec/simulator/acvm';
7
-
8
- import { type NoteInfo } from '../note_decryption_utils/index.js';
6
+ import { type NoteData } from '@aztec/simulator/client';
9
7
 
10
8
  /**
11
9
  * A Note Data Access Object, representing a note that was comitted to the note hash tree, holding all of the
@@ -61,32 +59,6 @@ export class NoteDao implements NoteData {
61
59
  public noteTypeId: NoteSelector,
62
60
  ) {}
63
61
 
64
- static fromPayloadAndNoteInfo(
65
- note: Note,
66
- payload: L1NotePayload,
67
- noteInfo: NoteInfo,
68
- l2BlockNumber: number,
69
- l2BlockHash: string,
70
- dataStartIndexForTx: number,
71
- addressPoint: PublicKey,
72
- ) {
73
- const noteHashIndexInTheWholeTree = BigInt(dataStartIndexForTx + noteInfo.noteHashIndex);
74
- return new NoteDao(
75
- note,
76
- payload.contractAddress,
77
- payload.storageSlot,
78
- noteInfo.nonce,
79
- noteInfo.noteHash,
80
- noteInfo.siloedNullifier,
81
- noteInfo.txHash,
82
- l2BlockNumber,
83
- l2BlockHash,
84
- noteHashIndexInTheWholeTree,
85
- addressPoint,
86
- payload.noteTypeId,
87
- );
88
- }
89
-
90
62
  toBuffer(): Buffer {
91
63
  return serializeToBuffer([
92
64
  this.note,
@@ -1,11 +1,9 @@
1
- import { type L1NotePayload, Note, TxHash, randomTxHash } from '@aztec/circuit-types';
1
+ import { Note, TxHash, randomTxHash } from '@aztec/circuit-types';
2
2
  import { AztecAddress, Fr, Point, type PublicKey } from '@aztec/circuits.js';
3
3
  import { NoteSelector } from '@aztec/foundation/abi';
4
4
  import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
5
5
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
6
6
 
7
- import { type NoteInfo } from '../note_decryption_utils/index.js';
8
-
9
7
  /**
10
8
  * A note with contextual data which was decrypted as outgoing.
11
9
  */
@@ -38,31 +36,6 @@ export class OutgoingNoteDao {
38
36
  public ovpkM: PublicKey,
39
37
  ) {}
40
38
 
41
- static fromPayloadAndNoteInfo(
42
- note: Note,
43
- payload: L1NotePayload,
44
- noteInfo: NoteInfo,
45
- l2BlockNumber: number,
46
- l2BlockHash: string,
47
- dataStartIndexForTx: number,
48
- ovpkM: PublicKey,
49
- ) {
50
- const noteHashIndexInTheWholeTree = BigInt(dataStartIndexForTx + noteInfo.noteHashIndex);
51
- return new OutgoingNoteDao(
52
- note,
53
- payload.contractAddress,
54
- payload.storageSlot,
55
- payload.noteTypeId,
56
- noteInfo.txHash,
57
- l2BlockNumber,
58
- l2BlockHash,
59
- noteInfo.nonce,
60
- noteInfo.noteHash,
61
- noteHashIndexInTheWholeTree,
62
- ovpkM,
63
- );
64
- }
65
-
66
39
  toBuffer(): Buffer {
67
40
  return serializeToBuffer([
68
41
  this.note,
@@ -213,4 +213,41 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD
213
213
  * is also required to deal with chain reorgs.
214
214
  */
215
215
  resetNoteSyncData(): Promise<void>;
216
+
217
+ /**
218
+ * Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `dbLoad`.
219
+ * If data was already stored at this slot, it is overwrriten.
220
+ * @param contractAddress - The contract address to scope the data under.
221
+ * @param slot - The slot in the database in which to store the value. Slots need not be contiguous.
222
+ * @param values - The data to store.
223
+ */
224
+ dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void>;
225
+
226
+ /**
227
+ * Returns data previously stored via `dbStore` in the per-contract non-volatile database.
228
+ * @param contractAddress - The contract address under which the data is scoped.
229
+ * @param slot - The slot in the database to read.
230
+ * @returns The stored data or `null` if no data is stored under the slot.
231
+ */
232
+ dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null>;
233
+
234
+ /**
235
+ * Deletes data in the per-contract non-volatile database. Does nothing if no data was present.
236
+ * @param contractAddress - The contract address under which the data is scoped.
237
+ * @param slot - The slot in the database to delete.
238
+ */
239
+ dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void>;
240
+
241
+ /**
242
+ * Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data
243
+ * structures by avoiding repeated calls to `dbLoad` and `dbStore`.
244
+ * Supports overlapping source and destination regions (which will result in the overlapped source values being
245
+ * overwritten). All copied slots must exist in the database (i.e. have been stored and not deleted)
246
+ *
247
+ * @param contractAddress - The contract address under which the data is scoped.
248
+ * @param srcSlot - The first slot to copy from.
249
+ * @param dstSlot - The first slot to copy to.
250
+ * @param numEntries - The number of entries to copy.
251
+ */
252
+ dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void>;
216
253
  }
@@ -405,5 +405,165 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
405
405
  await expect(database.getContractInstance(address)).resolves.toEqual(instance);
406
406
  });
407
407
  });
408
+
409
+ describe('contract non-volatile database', () => {
410
+ let contract: AztecAddress;
411
+
412
+ beforeEach(() => {
413
+ // Setup mock contract address
414
+ contract = AztecAddress.random();
415
+ });
416
+
417
+ it('stores and loads a single value', async () => {
418
+ const slot = new Fr(1);
419
+ const values = [new Fr(42)];
420
+
421
+ await database.dbStore(contract, slot, values);
422
+ const result = await database.dbLoad(contract, slot);
423
+ expect(result).toEqual(values);
424
+ });
425
+
426
+ it('stores and loads multiple values', async () => {
427
+ const slot = new Fr(1);
428
+ const values = [new Fr(42), new Fr(43), new Fr(44)];
429
+
430
+ await database.dbStore(contract, slot, values);
431
+ const result = await database.dbLoad(contract, slot);
432
+ expect(result).toEqual(values);
433
+ });
434
+
435
+ it('overwrites existing values', async () => {
436
+ const slot = new Fr(1);
437
+ const initialValues = [new Fr(42)];
438
+ const newValues = [new Fr(100)];
439
+
440
+ await database.dbStore(contract, slot, initialValues);
441
+ await database.dbStore(contract, slot, newValues);
442
+
443
+ const result = await database.dbLoad(contract, slot);
444
+ expect(result).toEqual(newValues);
445
+ });
446
+
447
+ it('stores values for different contracts independently', async () => {
448
+ const anotherContract = AztecAddress.random();
449
+ const slot = new Fr(1);
450
+ const values1 = [new Fr(42)];
451
+ const values2 = [new Fr(100)];
452
+
453
+ await database.dbStore(contract, slot, values1);
454
+ await database.dbStore(anotherContract, slot, values2);
455
+
456
+ const result1 = await database.dbLoad(contract, slot);
457
+ const result2 = await database.dbLoad(anotherContract, slot);
458
+
459
+ expect(result1).toEqual(values1);
460
+ expect(result2).toEqual(values2);
461
+ });
462
+
463
+ it('returns null for non-existent slots', async () => {
464
+ const slot = Fr.random();
465
+ const result = await database.dbLoad(contract, slot);
466
+ expect(result).toBeNull();
467
+ });
468
+
469
+ it('deletes a slot', async () => {
470
+ const slot = new Fr(1);
471
+ const values = [new Fr(42)];
472
+
473
+ await database.dbStore(contract, slot, values);
474
+ await database.dbDelete(contract, slot);
475
+
476
+ expect(await database.dbLoad(contract, slot)).toBeNull();
477
+ });
478
+
479
+ it('deletes an empty slot', async () => {
480
+ const slot = new Fr(1);
481
+ await database.dbDelete(contract, slot);
482
+
483
+ expect(await database.dbLoad(contract, slot)).toBeNull();
484
+ });
485
+
486
+ it('copies a single value', async () => {
487
+ const slot = new Fr(1);
488
+ const values = [new Fr(42)];
489
+
490
+ await database.dbStore(contract, slot, values);
491
+
492
+ const dstSlot = new Fr(5);
493
+ await database.dbCopy(contract, slot, dstSlot, 1);
494
+
495
+ expect(await database.dbLoad(contract, dstSlot)).toEqual(values);
496
+ });
497
+
498
+ it('copies multiple non-overlapping values', async () => {
499
+ const src = new Fr(1);
500
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
501
+
502
+ await database.dbStore(contract, src, valuesArray[0]);
503
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
504
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
505
+
506
+ const dst = new Fr(5);
507
+ await database.dbCopy(contract, src, dst, 3);
508
+
509
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
510
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
511
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
512
+ });
513
+
514
+ it('copies overlapping values with src ahead', async () => {
515
+ const src = new Fr(1);
516
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
517
+
518
+ await database.dbStore(contract, src, valuesArray[0]);
519
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
520
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
521
+
522
+ const dst = new Fr(2);
523
+ await database.dbCopy(contract, src, dst, 3);
524
+
525
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
526
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
527
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
528
+
529
+ // Slots 2 and 3 (src[1] and src[2]) should have been overwritten since they are also dst[0] and dst[1]
530
+ expect(await database.dbLoad(contract, src)).toEqual(valuesArray[0]); // src[0] (unchanged)
531
+ expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[0]); // dst[0]
532
+ expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[1]); // dst[1]
533
+ });
534
+
535
+ it('copies overlapping values with dst ahead', async () => {
536
+ const src = new Fr(5);
537
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
538
+
539
+ await database.dbStore(contract, src, valuesArray[0]);
540
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
541
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
542
+
543
+ const dst = new Fr(4);
544
+ await database.dbCopy(contract, src, dst, 3);
545
+
546
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
547
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
548
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
549
+
550
+ // Slots 5 and 6 (src[0] and src[1]) should have been overwritten since they are also dst[1] and dst[2]
551
+ expect(await database.dbLoad(contract, src)).toEqual(valuesArray[1]); // dst[1]
552
+ expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[2]); // dst[2]
553
+ expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[2]); // src[2] (unchanged)
554
+ });
555
+
556
+ it('copying fails if any value is empty', async () => {
557
+ const src = new Fr(1);
558
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
559
+
560
+ await database.dbStore(contract, src, valuesArray[0]);
561
+ // We skip src[1]
562
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
563
+
564
+ const dst = new Fr(5);
565
+ await expect(database.dbCopy(contract, src, dst, 3)).rejects.toThrow('Attempted to copy empty slot');
566
+ });
567
+ });
408
568
  });
409
569
  }
@@ -15,7 +15,7 @@ import {
15
15
  import { createLogger } from '@aztec/foundation/log';
16
16
  import { type Tuple } from '@aztec/foundation/serialize';
17
17
  import { type KeyStore } from '@aztec/key-store';
18
- import { getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-types/client';
18
+ import { getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-types/vks';
19
19
 
20
20
  import { type ContractDataOracle } from '../contract_data_oracle/index.js';
21
21
  import { type ProvingDataOracle } from './../kernel_prover/proving_data_oracle.js';
@@ -1,4 +1,4 @@
1
- import { type PrivateExecutionResult, type PrivateKernelSimulateOutput, collectNested } from '@aztec/circuit-types';
1
+ import { type PrivateCallExecutionResult, type PrivateKernelSimulateOutput, collectNested } from '@aztec/circuit-types';
2
2
  import {
3
3
  type Fr,
4
4
  KeyValidationHint,
@@ -42,8 +42,8 @@ import { privateKernelResetDimensionsConfig } from '@aztec/noir-protocol-circuit
42
42
  import { type ProvingDataOracle } from '../proving_data_oracle.js';
43
43
 
44
44
  function collectNestedReadRequests(
45
- executionStack: PrivateExecutionResult[],
46
- extractReadRequests: (execution: PrivateExecutionResult) => ReadRequest[],
45
+ executionStack: PrivateCallExecutionResult[],
46
+ extractReadRequests: (execution: PrivateCallExecutionResult) => ReadRequest[],
47
47
  ): ScopedReadRequest[] {
48
48
  return collectNested(executionStack, executionResult => {
49
49
  const nonEmptyReadRequests = getNonEmptyItems(extractReadRequests(executionResult));
@@ -101,7 +101,7 @@ export class PrivateKernelResetPrivateInputsBuilder {
101
101
 
102
102
  constructor(
103
103
  private previousKernelOutput: PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>,
104
- private executionStack: PrivateExecutionResult[],
104
+ private executionStack: PrivateCallExecutionResult[],
105
105
  private noteHashNullifierCounterMap: Map<number, number>,
106
106
  private validationRequestsSplitCounter: number,
107
107
  ) {
@@ -1,4 +1,2 @@
1
- export { TestPrivateKernelProver } from './test/test_circuit_prover.js';
2
-
3
1
  export * from './kernel_prover.js';
4
2
  export * from './proving_data_oracle.js';
@@ -1,4 +1,5 @@
1
1
  import {
2
+ type PrivateCallExecutionResult,
2
3
  type PrivateExecutionResult,
3
4
  type PrivateKernelProver,
4
5
  type PrivateKernelSimulateOutput,
@@ -10,6 +11,7 @@ import {
10
11
  } from '@aztec/circuit-types';
11
12
  import {
12
13
  CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS,
14
+ ClientIvcProof,
13
15
  Fr,
14
16
  PROTOCOL_CONTRACT_TREE_HEIGHT,
15
17
  PrivateCallData,
@@ -32,7 +34,7 @@ import { createLogger } from '@aztec/foundation/log';
32
34
  import { assertLength } from '@aztec/foundation/serialize';
33
35
  import { pushTestData } from '@aztec/foundation/testing';
34
36
  import { Timer } from '@aztec/foundation/timer';
35
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/client';
37
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vks';
36
38
  import {
37
39
  getProtocolContractSiblingPath,
38
40
  isProtocolContract,
@@ -89,6 +91,12 @@ const NULL_PROVE_OUTPUT: PrivateKernelSimulateOutput<PrivateKernelCircuitPublicI
89
91
  bytecode: Buffer.from([]),
90
92
  };
91
93
 
94
+ export type ProvingConfig = {
95
+ simulate: boolean;
96
+ profile: boolean;
97
+ dryRun: boolean;
98
+ };
99
+
92
100
  /**
93
101
  * The KernelProver class is responsible for generating kernel proofs.
94
102
  * It takes a transaction request, its signature, and the simulation result as inputs, and outputs a proof
@@ -98,7 +106,11 @@ const NULL_PROVE_OUTPUT: PrivateKernelSimulateOutput<PrivateKernelCircuitPublicI
98
106
  export class KernelProver {
99
107
  private log = createLogger('pxe:kernel-prover');
100
108
 
101
- constructor(private oracle: ProvingDataOracle, private proofCreator: PrivateKernelProver) {}
109
+ constructor(
110
+ private oracle: ProvingDataOracle,
111
+ private proofCreator: PrivateKernelProver,
112
+ private fakeProofs = false,
113
+ ) {}
102
114
 
103
115
  /**
104
116
  * Generate a proof for a given transaction request and execution result.
@@ -116,14 +128,19 @@ export class KernelProver {
116
128
  async prove(
117
129
  txRequest: TxRequest,
118
130
  executionResult: PrivateExecutionResult,
119
- profile: boolean = false,
120
- dryRun: boolean = false,
131
+ { simulate, profile, dryRun }: ProvingConfig = { simulate: false, profile: false, dryRun: false },
121
132
  ): Promise<PrivateKernelSimulateOutput<PrivateKernelTailCircuitPublicInputs>> {
133
+ if (simulate && profile) {
134
+ throw new Error('Cannot simulate and profile at the same time');
135
+ }
136
+
137
+ simulate = simulate || this.fakeProofs;
138
+
122
139
  const timer = new Timer();
123
140
 
124
141
  const isPrivateOnlyTx = this.isPrivateOnly(executionResult);
125
142
 
126
- const executionStack = [executionResult];
143
+ const executionStack = [executionResult.entrypoint];
127
144
  let firstIteration = true;
128
145
 
129
146
  let output = NULL_PROVE_OUTPUT;
@@ -156,7 +173,9 @@ export class KernelProver {
156
173
  );
157
174
  while (resetBuilder.needsReset()) {
158
175
  const privateInputs = await resetBuilder.build(this.oracle, noteHashLeafIndexMap);
159
- output = await this.proofCreator.simulateProofReset(privateInputs);
176
+ output = simulate
177
+ ? await this.proofCreator.simulateReset(privateInputs)
178
+ : await this.proofCreator.generateResetOutput(privateInputs);
160
179
  // TODO(#7368) consider refactoring this redundant bytecode pushing
161
180
  acirs.push(output.bytecode);
162
181
  witnessStack.push(output.outputWitness);
@@ -199,11 +218,17 @@ export class KernelProver {
199
218
  protocolContractTreeRoot,
200
219
  privateCallData,
201
220
  isPrivateOnlyTx,
221
+ executionResult.firstNullifier,
222
+ );
223
+ this.log.debug(
224
+ `Calling private kernel init with isPrivateOnly ${isPrivateOnlyTx} and firstNullifierHint ${proofInput.firstNullifierHint}`,
202
225
  );
203
226
 
204
227
  pushTestData('private-kernel-inputs-init', proofInput);
205
228
 
206
- output = await this.proofCreator.simulateProofInit(proofInput);
229
+ output = simulate
230
+ ? await this.proofCreator.simulateInit(proofInput)
231
+ : await this.proofCreator.generateInitOutput(proofInput);
207
232
 
208
233
  acirs.push(output.bytecode);
209
234
  witnessStack.push(output.outputWitness);
@@ -222,7 +247,9 @@ export class KernelProver {
222
247
 
223
248
  pushTestData('private-kernel-inputs-inner', proofInput);
224
249
 
225
- output = await this.proofCreator.simulateProofInner(proofInput);
250
+ output = simulate
251
+ ? await this.proofCreator.simulateInner(proofInput)
252
+ : await this.proofCreator.generateInnerOutput(proofInput);
226
253
 
227
254
  acirs.push(output.bytecode);
228
255
  witnessStack.push(output.outputWitness);
@@ -242,7 +269,9 @@ export class KernelProver {
242
269
  );
243
270
  while (resetBuilder.needsReset()) {
244
271
  const privateInputs = await resetBuilder.build(this.oracle, noteHashLeafIndexMap);
245
- output = await this.proofCreator.simulateProofReset(privateInputs);
272
+ output = simulate
273
+ ? await this.proofCreator.simulateReset(privateInputs)
274
+ : await this.proofCreator.generateResetOutput(privateInputs);
246
275
 
247
276
  acirs.push(output.bytecode);
248
277
  witnessStack.push(output.outputWitness);
@@ -275,7 +304,9 @@ export class KernelProver {
275
304
 
276
305
  pushTestData('private-kernel-inputs-ordering', privateInputs);
277
306
 
278
- const tailOutput = await this.proofCreator.simulateProofTail(privateInputs);
307
+ const tailOutput = simulate
308
+ ? await this.proofCreator.simulateTail(privateInputs)
309
+ : await this.proofCreator.generateTailOutput(privateInputs);
279
310
  if (tailOutput.publicInputs.forPublic) {
280
311
  const privateLogs = privateInputs.previousKernel.publicInputs.end.privateLogs;
281
312
  const nonRevertiblePrivateLogs = tailOutput.publicInputs.forPublic.nonRevertibleAccumulatedData.privateLogs;
@@ -290,18 +321,22 @@ export class KernelProver {
290
321
  tailOutput.profileResult = { gateCounts };
291
322
  }
292
323
 
293
- this.log.verbose(`Private kernel witness generation took ${timer.ms()}ms`);
324
+ if (!simulate) {
325
+ this.log.info(`Private kernel witness generation took ${timer.ms()}ms`);
326
+ }
294
327
 
295
328
  // TODO(#7368) how do we 'bincode' encode these inputs?
296
- if (!dryRun) {
329
+ if (!dryRun && !simulate) {
297
330
  const ivcProof = await this.proofCreator.createClientIvcProof(acirs, witnessStack);
298
331
  tailOutput.clientIvcProof = ivcProof;
332
+ } else {
333
+ tailOutput.clientIvcProof = ClientIvcProof.empty();
299
334
  }
300
335
 
301
336
  return tailOutput;
302
337
  }
303
338
 
304
- private async createPrivateCallData({ publicInputs, vk: vkAsBuffer }: PrivateExecutionResult) {
339
+ private async createPrivateCallData({ publicInputs, vk: vkAsBuffer }: PrivateCallExecutionResult) {
305
340
  const { contractAddress, functionSelector } = publicInputs.callContext;
306
341
 
307
342
  const vkAsFields = vkAsFieldsMegaHonk(vkAsBuffer);
@@ -339,12 +374,15 @@ export class KernelProver {
339
374
  }
340
375
 
341
376
  private isPrivateOnly(executionResult: PrivateExecutionResult): boolean {
342
- const makesPublicCalls =
343
- executionResult.enqueuedPublicFunctionCalls.some(enqueuedCall => !enqueuedCall.isEmpty()) ||
344
- !executionResult.publicTeardownFunctionCall.isEmpty();
345
- return (
346
- !makesPublicCalls &&
347
- executionResult.nestedExecutions.every(nestedExecution => this.isPrivateOnly(nestedExecution))
348
- );
377
+ const isPrivateOnlyRecursive = (callResult: PrivateCallExecutionResult): boolean => {
378
+ const makesPublicCalls =
379
+ callResult.enqueuedPublicFunctionCalls.some(enqueuedCall => !enqueuedCall.isEmpty()) ||
380
+ !callResult.publicTeardownFunctionCall.isEmpty();
381
+ return (
382
+ !makesPublicCalls &&
383
+ callResult.nestedExecutions.every(nestedExecution => isPrivateOnlyRecursive(nestedExecution))
384
+ );
385
+ };
386
+ return isPrivateOnlyRecursive(executionResult.entrypoint);
349
387
  }
350
388
  }
@@ -41,7 +41,7 @@ export async function getOrderedNoteItems(
41
41
  noteFields.sort((a, b) => a.index - b.index);
42
42
 
43
43
  // Now we insert the public fields into the note based on its indices defined in the ABI.
44
- const modifiedNoteItems = privateNoteValues;
44
+ const modifiedNoteItems = [...privateNoteValues];
45
45
  let indexInPublicValues = 0;
46
46
  for (let i = 0; i < noteFields.length; i++) {
47
47
  const noteField = noteFields[i];
@@ -4,7 +4,7 @@ import { FunctionSelector } from '@aztec/foundation/abi';
4
4
  import { AztecAddress } from '@aztec/foundation/aztec-address';
5
5
  import { Fr } from '@aztec/foundation/fields';
6
6
  import { type Logger } from '@aztec/foundation/log';
7
- import { resolveAssertionMessageFromRevertData, resolveOpcodeLocations } from '@aztec/simulator/errors';
7
+ import { resolveAssertionMessageFromRevertData, resolveOpcodeLocations } from '@aztec/simulator/client';
8
8
 
9
9
  import { type ContractDataOracle, type PxeDatabase } from '../index.js';
10
10