@aztec/simulator 0.47.1 → 0.48.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 (79) hide show
  1. package/dest/acvm/oracle/oracle.d.ts +3 -3
  2. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  3. package/dest/acvm/oracle/oracle.js +8 -9
  4. package/dest/acvm/oracle/typed_oracle.d.ts +5 -5
  5. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/typed_oracle.js +6 -6
  7. package/dest/avm/fixtures/index.d.ts.map +1 -1
  8. package/dest/avm/fixtures/index.js +2 -2
  9. package/dest/avm/opcodes/commitment.js +4 -4
  10. package/dest/avm/serialization/instruction_serialization.js +2 -2
  11. package/dest/client/client_execution_context.d.ts +8 -8
  12. package/dest/client/client_execution_context.d.ts.map +1 -1
  13. package/dest/client/client_execution_context.js +22 -23
  14. package/dest/client/db_oracle.d.ts +4 -3
  15. package/dest/client/db_oracle.d.ts.map +1 -1
  16. package/dest/client/execution_note_cache.d.ts +17 -13
  17. package/dest/client/execution_note_cache.d.ts.map +1 -1
  18. package/dest/client/execution_note_cache.js +65 -26
  19. package/dest/client/execution_result.d.ts +3 -2
  20. package/dest/client/execution_result.d.ts.map +1 -1
  21. package/dest/client/execution_result.js +20 -9
  22. package/dest/client/private_execution.js +3 -3
  23. package/dest/client/simulator.d.ts +7 -6
  24. package/dest/client/simulator.d.ts.map +1 -1
  25. package/dest/client/simulator.js +14 -12
  26. package/dest/client/test_utils.d.ts +9 -0
  27. package/dest/client/test_utils.d.ts.map +1 -0
  28. package/dest/client/test_utils.js +21 -0
  29. package/dest/client/view_data_oracle.d.ts +2 -1
  30. package/dest/client/view_data_oracle.d.ts.map +1 -1
  31. package/dest/client/view_data_oracle.js +4 -3
  32. package/dest/index.d.ts +0 -1
  33. package/dest/index.d.ts.map +1 -1
  34. package/dest/index.js +1 -2
  35. package/dest/public/abstract_phase_manager.d.ts.map +1 -1
  36. package/dest/public/abstract_phase_manager.js +3 -3
  37. package/dest/public/executor.d.ts +4 -1
  38. package/dest/public/executor.d.ts.map +1 -1
  39. package/dest/public/executor.js +10 -2
  40. package/dest/public/executor_metrics.d.ts +10 -0
  41. package/dest/public/executor_metrics.d.ts.map +1 -0
  42. package/dest/public/executor_metrics.js +32 -0
  43. package/dest/public/fee_payment.d.ts +2 -2
  44. package/dest/public/fee_payment.d.ts.map +1 -1
  45. package/dest/public/fee_payment.js +9 -10
  46. package/dest/public/hints_builder.d.ts.map +1 -1
  47. package/dest/public/hints_builder.js +1 -1
  48. package/dest/public/public_processor.d.ts +3 -3
  49. package/dest/public/public_processor.d.ts.map +1 -1
  50. package/dest/public/public_processor.js +33 -14
  51. package/dest/public/public_processor_metrics.d.ts +19 -0
  52. package/dest/public/public_processor_metrics.d.ts.map +1 -0
  53. package/dest/public/public_processor_metrics.js +57 -0
  54. package/package.json +9 -9
  55. package/src/acvm/oracle/oracle.ts +8 -23
  56. package/src/acvm/oracle/typed_oracle.ts +8 -21
  57. package/src/avm/fixtures/index.ts +1 -0
  58. package/src/avm/opcodes/commitment.ts +3 -3
  59. package/src/avm/serialization/instruction_serialization.ts +1 -1
  60. package/src/client/client_execution_context.ts +23 -20
  61. package/src/client/db_oracle.ts +9 -3
  62. package/src/client/execution_note_cache.ts +76 -25
  63. package/src/client/execution_result.ts +29 -9
  64. package/src/client/private_execution.ts +2 -2
  65. package/src/client/simulator.ts +18 -14
  66. package/src/client/test_utils.ts +30 -0
  67. package/src/client/view_data_oracle.ts +2 -1
  68. package/src/index.ts +0 -1
  69. package/src/public/abstract_phase_manager.ts +1 -2
  70. package/src/public/executor.ts +14 -1
  71. package/src/public/executor_metrics.ts +48 -0
  72. package/src/public/fee_payment.ts +8 -10
  73. package/src/public/hints_builder.ts +2 -2
  74. package/src/public/public_processor.ts +46 -15
  75. package/src/public/public_processor_metrics.ts +90 -0
  76. package/dest/utils.d.ts +0 -12
  77. package/dest/utils.d.ts.map +0 -1
  78. package/dest/utils.js +0 -11
  79. package/src/utils.ts +0 -18
@@ -30,7 +30,7 @@ import {
30
30
  countArgumentsSize,
31
31
  } from '@aztec/foundation/abi';
32
32
  import { type AztecAddress } from '@aztec/foundation/aztec-address';
33
- import { pedersenHash } from '@aztec/foundation/crypto';
33
+ import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto';
34
34
  import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields';
35
35
  import { applyStringFormatting, createDebugLogger } from '@aztec/foundation/log';
36
36
 
@@ -71,7 +71,7 @@ export class ClientExecutionContext extends ViewDataOracle {
71
71
  * They should act as references for the read requests output by an app circuit via public inputs.
72
72
  */
73
73
  private noteHashLeafIndexMap: Map<bigint, bigint> = new Map();
74
- private nullifiedNoteHashCounters: Map<number, number> = new Map();
74
+ private noteHashNullifierCounterMap: Map<number, number> = new Map();
75
75
  private noteEncryptedLogs: CountedNoteLog[] = [];
76
76
  private encryptedLogs: CountedLog<EncryptedL2Log>[] = [];
77
77
  private unencryptedLogs: CountedLog<UnencryptedL2Log>[] = [];
@@ -94,8 +94,9 @@ export class ClientExecutionContext extends ViewDataOracle {
94
94
  private node: AztecNode,
95
95
  protected sideEffectCounter: number = 0,
96
96
  log = createDebugLogger('aztec:simulator:client_execution_context'),
97
+ scopes?: AztecAddress[],
97
98
  ) {
98
- super(contractAddress, authWitnesses, db, node, log);
99
+ super(contractAddress, authWitnesses, db, node, log, scopes);
99
100
  }
100
101
 
101
102
  // We still need this function until we can get user-defined ordering of structs for fn arguments
@@ -135,14 +136,13 @@ export class ClientExecutionContext extends ViewDataOracle {
135
136
 
136
137
  /**
137
138
  * Get the data for the newly created notes.
138
- * @param innerNoteHashes - Inner note hashes for the notes.
139
139
  */
140
140
  public getNewNotes(): NoteAndSlot[] {
141
141
  return this.newNotes;
142
142
  }
143
143
 
144
- public getNullifiedNoteHashCounters() {
145
- return this.nullifiedNoteHashCounters;
144
+ public getNoteHashNullifierCounterMap() {
145
+ return this.noteHashNullifierCounterMap;
146
146
  }
147
147
 
148
148
  /**
@@ -251,7 +251,7 @@ export class ClientExecutionContext extends ViewDataOracle {
251
251
  const pendingNotes = this.noteCache.getNotes(this.callContext.storageContractAddress, storageSlot);
252
252
 
253
253
  const pendingNullifiers = this.noteCache.getNullifiers(this.callContext.storageContractAddress);
254
- const dbNotes = await this.db.getNotes(this.callContext.storageContractAddress, storageSlot, status);
254
+ const dbNotes = await this.db.getNotes(this.callContext.storageContractAddress, storageSlot, status, this.scopes);
255
255
  const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value));
256
256
 
257
257
  const notes = pickNotes<NoteData>([...dbNotesFiltered, ...pendingNotes], {
@@ -276,12 +276,9 @@ export class ClientExecutionContext extends ViewDataOracle {
276
276
 
277
277
  notes.forEach(n => {
278
278
  if (n.index !== undefined) {
279
- // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386)
280
- // Should always call computeUniqueNoteHash when publicly created notes include nonces.
281
- const uniqueNoteHash = n.nonce.isZero() ? n.innerNoteHash : computeUniqueNoteHash(n.nonce, n.innerNoteHash);
279
+ const uniqueNoteHash = computeUniqueNoteHash(n.nonce, n.noteHash);
282
280
  const siloedNoteHash = siloNoteHash(n.contractAddress, uniqueNoteHash);
283
- const noteHashForReadRequest = siloedNoteHash;
284
- this.noteHashLeafIndexMap.set(noteHashForReadRequest.toBigInt(), n.index);
281
+ this.noteHashLeafIndexMap.set(siloedNoteHash.toBigInt(), n.index);
285
282
  }
286
283
  });
287
284
 
@@ -295,14 +292,14 @@ export class ClientExecutionContext extends ViewDataOracle {
295
292
  * @param storageSlot - The storage slot.
296
293
  * @param noteTypeId - The type ID of the note.
297
294
  * @param noteItems - The items to be included in a Note.
298
- * @param innerNoteHash - The inner note hash of the new note.
295
+ * @param noteHash - A hash of the new note.
299
296
  * @returns
300
297
  */
301
298
  public override notifyCreatedNote(
302
299
  storageSlot: Fr,
303
300
  noteTypeId: NoteSelector,
304
301
  noteItems: Fr[],
305
- innerNoteHash: Fr,
302
+ noteHash: Fr,
306
303
  counter: number,
307
304
  ) {
308
305
  const note = new Note(noteItems);
@@ -313,7 +310,7 @@ export class ClientExecutionContext extends ViewDataOracle {
313
310
  nonce: Fr.ZERO, // Nonce cannot be known during private execution.
314
311
  note,
315
312
  siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note.
316
- innerNoteHash,
313
+ noteHash,
317
314
  },
318
315
  counter,
319
316
  );
@@ -328,16 +325,16 @@ export class ClientExecutionContext extends ViewDataOracle {
328
325
  * Adding a siloed nullifier into the current set of all pending nullifiers created
329
326
  * within the current transaction/execution.
330
327
  * @param innerNullifier - The pending nullifier to add in the list (not yet siloed by contract address).
331
- * @param innerNoteHash - The inner note hash of the new note.
328
+ * @param noteHash - A hash of the new note.
332
329
  */
333
- public override notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, counter: number) {
330
+ public override notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
334
331
  const nullifiedNoteHashCounter = this.noteCache.nullifyNote(
335
332
  this.callContext.storageContractAddress,
336
333
  innerNullifier,
337
- innerNoteHash,
334
+ noteHash,
338
335
  );
339
336
  if (nullifiedNoteHashCounter !== undefined) {
340
- this.nullifiedNoteHashCounters.set(nullifiedNoteHashCounter, counter);
337
+ this.noteHashNullifierCounterMap.set(nullifiedNoteHashCounter, counter);
341
338
  }
342
339
  return Promise.resolve();
343
340
  }
@@ -360,7 +357,7 @@ export class ClientExecutionContext extends ViewDataOracle {
360
357
  // An app providing randomness = 0 signals to not mask the address.
361
358
  const maskedContractAddress = randomness.isZero()
362
359
  ? contractAddress.toField()
363
- : pedersenHash([contractAddress, randomness], 0);
360
+ : poseidon2HashWithSeparator([contractAddress, randomness], 0);
364
361
  const encryptedLog = new CountedLog(new EncryptedL2Log(encryptedEvent, maskedContractAddress), counter);
365
362
  this.encryptedLogs.push(encryptedLog);
366
363
  }
@@ -519,6 +516,8 @@ export class ClientExecutionContext extends ViewDataOracle {
519
516
  this.db,
520
517
  this.node,
521
518
  sideEffectCounter,
519
+ this.log,
520
+ this.scopes,
522
521
  );
523
522
 
524
523
  const childExecutionResult = await executePrivateFunction(
@@ -645,6 +644,10 @@ export class ClientExecutionContext extends ViewDataOracle {
645
644
  );
646
645
  }
647
646
 
647
+ public override notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number): void {
648
+ this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
649
+ }
650
+
648
651
  /**
649
652
  * Derives the call context for a nested execution.
650
653
  * @param targetContractAddress - The address of the contract being called.
@@ -78,12 +78,18 @@ export interface DBOracle extends CommitmentsDB {
78
78
  * The query result is paginated using 'limit' and 'offset' values.
79
79
  * Returns an object containing an array of note data.
80
80
  *
81
- * @param contractAddress - The AztecAddress instance representing the contract address.
82
- * @param storageSlot - The Fr instance representing the storage slot of the notes.
81
+ * @param contractAddress - The contract address of the notes.
82
+ * @param storageSlot - The storage slot of the notes.
83
83
  * @param status - The status of notes to fetch.
84
+ * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all.
84
85
  * @returns A Promise that resolves to an array of note data.
85
86
  */
86
- getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus): Promise<NoteData[]>;
87
+ getNotes(
88
+ contractAddress: AztecAddress,
89
+ storageSlot: Fr,
90
+ status: NoteStatus,
91
+ scopes?: AztecAddress[],
92
+ ): Promise<NoteData[]>;
87
93
 
88
94
  /**
89
95
  * Retrieve the artifact information of a specific function within a contract.
@@ -1,12 +1,13 @@
1
- import { siloNullifier } from '@aztec/circuits.js/hash';
1
+ import { computeNoteHashNonce, computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
2
2
  import { type AztecAddress } from '@aztec/foundation/aztec-address';
3
- import { Fr } from '@aztec/foundation/fields';
3
+ import { type Fr } from '@aztec/foundation/fields';
4
4
 
5
5
  import { type NoteData } from '../acvm/index.js';
6
6
 
7
- export interface PendingNote {
7
+ interface PendingNote {
8
8
  note: NoteData;
9
9
  counter: number;
10
+ noteHashForConsumption: Fr;
10
11
  }
11
12
 
12
13
  /**
@@ -15,9 +16,13 @@ export interface PendingNote {
15
16
  export class ExecutionNoteCache {
16
17
  /**
17
18
  * New notes created in this transaction.
19
+ * They are pushed to this array in the same order as they are emitted.
20
+ */
21
+ private notes: PendingNote[] = [];
22
+ /**
18
23
  * This mapping maps from a contract address to the notes in the contract.
19
24
  */
20
- private newNotes: Map<bigint, PendingNote[]> = new Map();
25
+ private noteMap: Map<bigint, PendingNote[]> = new Map();
21
26
 
22
27
  /**
23
28
  * The list of nullifiers created in this transaction.
@@ -25,43 +30,81 @@ export class ExecutionNoteCache {
25
30
  * The note which is nullified might be new or not (i.e., was generated in a previous transaction).
26
31
  * Note that their value (bigint representation) is used because Frs cannot be looked up in Sets.
27
32
  */
28
- private nullifiers: Map<bigint, Set<bigint>> = new Map();
33
+ private nullifierMap: Map<bigint, Set<bigint>> = new Map();
34
+
35
+ private minRevertibleSideEffectCounter = 0;
36
+
37
+ constructor(private readonly txHash: Fr) {}
38
+
39
+ public setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
40
+ if (this.minRevertibleSideEffectCounter && this.minRevertibleSideEffectCounter !== minRevertibleSideEffectCounter) {
41
+ throw new Error(
42
+ `Cannot override minRevertibleSideEffectCounter. Current value: ${minRevertibleSideEffectCounter}. Previous value: ${this.minRevertibleSideEffectCounter}`,
43
+ );
44
+ }
45
+
46
+ this.minRevertibleSideEffectCounter = minRevertibleSideEffectCounter;
47
+
48
+ // The existing pending notes are all non-revertible.
49
+ // They cannot be squashed by nullifiers emitted after minRevertibleSideEffectCounter is set.
50
+ // Their indexes in the tx are known at this point and won't change. So we can assign a nonce to each one of them.
51
+ // The nonces will be used to create the "complete" nullifier.
52
+ const updatedNotes = this.notes.map(({ note, counter }, i) => {
53
+ const nonce = computeNoteHashNonce(this.txHash, i);
54
+ const uniqueNoteHash = computeUniqueNoteHash(nonce, note.noteHash);
55
+ return {
56
+ counter,
57
+ note: { ...note, nonce },
58
+ noteHashForConsumption: siloNoteHash(note.contractAddress, uniqueNoteHash),
59
+ };
60
+ });
61
+ // Rebuild the data.
62
+ this.notes = [];
63
+ this.noteMap = new Map();
64
+ updatedNotes.forEach(n => this.#addNote(n));
65
+ }
29
66
 
30
67
  /**
31
68
  * Add a new note to cache.
32
69
  * @param note - New note created during execution.
33
70
  */
34
71
  public addNewNote(note: NoteData, counter: number) {
35
- const notes = this.newNotes.get(note.contractAddress.toBigInt()) ?? [];
36
- notes.push({ note, counter });
37
- this.newNotes.set(note.contractAddress.toBigInt(), notes);
72
+ const previousNote = this.notes[this.notes.length - 1];
73
+ if (previousNote && previousNote.counter >= counter) {
74
+ throw new Error(
75
+ `Note hash counters must be strictly increasing. Current counter: ${counter}. Previous counter: ${previousNote.counter}.`,
76
+ );
77
+ }
78
+
79
+ this.#addNote({ note, counter, noteHashForConsumption: note.noteHash });
38
80
  }
39
81
 
40
82
  /**
41
83
  * Add a nullifier to cache. It could be for a db note or a new note created during execution.
42
84
  * @param contractAddress - Contract address of the note.
43
- * @param storageSlot - Storage slot of the note.
44
85
  * @param innerNullifier - Inner nullifier of the note.
45
- * @param innerNoteHash - Inner note hash of the note. If this value equals 0, it means the
46
- * note being nullified is from a previous transaction (and thus not a new note).
86
+ * @param noteHash - A hash of the note. If this value equals 0, it means the note being nullified is from a previous
87
+ * transaction (and thus not a new note).
47
88
  */
48
- public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, innerNoteHash: Fr) {
89
+ public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, noteHash: Fr) {
49
90
  const siloedNullifier = siloNullifier(contractAddress, innerNullifier);
50
91
  const nullifiers = this.getNullifiers(contractAddress);
51
92
  nullifiers.add(siloedNullifier.value);
52
- this.nullifiers.set(contractAddress.toBigInt(), nullifiers);
93
+ this.nullifierMap.set(contractAddress.toBigInt(), nullifiers);
53
94
 
54
95
  let nullifiedNoteHashCounter: number | undefined = undefined;
55
- // Find and remove the matching new note and log(s) if the emitted innerNoteHash is not empty.
56
- if (!innerNoteHash.equals(Fr.ZERO)) {
57
- const notes = this.newNotes.get(contractAddress.toBigInt()) ?? [];
58
- const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHash.equals(innerNoteHash));
96
+ // Find and remove the matching new note and log(s) if the emitted noteHash is not empty.
97
+ if (!noteHash.isEmpty()) {
98
+ const notesInContract = this.noteMap.get(contractAddress.toBigInt()) ?? [];
99
+ const noteIndexToRemove = notesInContract.findIndex(n => n.noteHashForConsumption.equals(noteHash));
59
100
  if (noteIndexToRemove === -1) {
60
101
  throw new Error('Attempt to remove a pending note that does not exist.');
61
102
  }
62
- const note = notes.splice(noteIndexToRemove, 1)[0];
103
+
104
+ const note = notesInContract.splice(noteIndexToRemove, 1)[0];
63
105
  nullifiedNoteHashCounter = note.counter;
64
- this.newNotes.set(contractAddress.toBigInt(), notes);
106
+ this.noteMap.set(contractAddress.toBigInt(), notesInContract);
107
+ this.notes = this.notes.filter(n => n.counter !== note.counter);
65
108
  }
66
109
  return nullifiedNoteHashCounter;
67
110
  }
@@ -73,7 +116,7 @@ export class ExecutionNoteCache {
73
116
  * @param storageSlot - Storage slot of the notes.
74
117
  **/
75
118
  public getNotes(contractAddress: AztecAddress, storageSlot: Fr) {
76
- const notes = this.newNotes.get(contractAddress.toBigInt()) ?? [];
119
+ const notes = this.noteMap.get(contractAddress.toBigInt()) ?? [];
77
120
  return notes.filter(n => n.note.storageSlot.equals(storageSlot)).map(n => n.note);
78
121
  }
79
122
 
@@ -81,11 +124,11 @@ export class ExecutionNoteCache {
81
124
  * Check if a note exists in the newNotes array.
82
125
  * @param contractAddress - Contract address of the note.
83
126
  * @param storageSlot - Storage slot of the note.
84
- * @param innerNoteHash - Inner note hash of the note.
127
+ * @param noteHash - A hash of the note.
85
128
  **/
86
- public checkNoteExists(contractAddress: AztecAddress, innerNoteHash: Fr) {
87
- const notes = this.newNotes.get(contractAddress.toBigInt()) ?? [];
88
- return notes.some(n => n.note.innerNoteHash.equals(innerNoteHash));
129
+ public checkNoteExists(contractAddress: AztecAddress, noteHash: Fr) {
130
+ const notes = this.noteMap.get(contractAddress.toBigInt()) ?? [];
131
+ return notes.some(n => n.note.noteHash.equals(noteHash));
89
132
  }
90
133
 
91
134
  /**
@@ -93,6 +136,14 @@ export class ExecutionNoteCache {
93
136
  * @param contractAddress - Address of the contract.
94
137
  */
95
138
  public getNullifiers(contractAddress: AztecAddress): Set<bigint> {
96
- return this.nullifiers.get(contractAddress.toBigInt()) ?? new Set();
139
+ return this.nullifierMap.get(contractAddress.toBigInt()) ?? new Set();
140
+ }
141
+
142
+ #addNote(note: PendingNote) {
143
+ this.notes.push(note);
144
+
145
+ const notes = this.noteMap.get(note.note.contractAddress.toBigInt()) ?? [];
146
+ notes.push(note);
147
+ this.noteMap.set(note.note.contractAddress.toBigInt(), notes);
97
148
  }
98
149
  }
@@ -67,7 +67,7 @@ export interface ExecutionResult {
67
67
  /** The notes created in the executed function. */
68
68
  newNotes: NoteAndSlot[];
69
69
  /** Mapping of note hash counter to the counter of its nullifier. */
70
- nullifiedNoteHashCounters: Map<number, number>;
70
+ noteHashNullifierCounterMap: Map<number, number>;
71
71
  /** The raw return values of the executed function. */
72
72
  returnValues: Fr[];
73
73
  /** The nested executions. */
@@ -99,9 +99,12 @@ export function collectNoteHashLeafIndexMap(execResult: ExecutionResult, accum:
99
99
  return accum;
100
100
  }
101
101
 
102
- export function collectNullifiedNoteHashCounters(execResult: ExecutionResult, accum: Map<number, number> = new Map()) {
103
- execResult.nullifiedNoteHashCounters.forEach((value, key) => accum.set(key, value));
104
- execResult.nestedExecutions.forEach(nested => collectNullifiedNoteHashCounters(nested, accum));
102
+ export function collectNoteHashNullifierCounterMap(
103
+ execResult: ExecutionResult,
104
+ accum: Map<number, number> = new Map(),
105
+ ) {
106
+ execResult.noteHashNullifierCounterMap.forEach((value, key) => accum.set(key, value));
107
+ execResult.nestedExecutions.forEach(nested => collectNoteHashNullifierCounterMap(nested, accum));
105
108
  return accum;
106
109
  }
107
110
 
@@ -112,11 +115,20 @@ export function collectNullifiedNoteHashCounters(execResult: ExecutionResult, ac
112
115
  */
113
116
  function collectNoteEncryptedLogs(
114
117
  execResult: ExecutionResult,
115
- nullifiedNoteHashCounters: Map<number, number>,
118
+ noteHashNullifierCounterMap: Map<number, number>,
119
+ minRevertibleSideEffectCounter: number,
116
120
  ): CountedLog<EncryptedL2NoteLog>[] {
117
121
  return [
118
- execResult.noteEncryptedLogs.filter(noteLog => !nullifiedNoteHashCounters.has(noteLog.noteHashCounter)),
119
- ...execResult.nestedExecutions.flatMap(res => collectNoteEncryptedLogs(res, nullifiedNoteHashCounters)),
122
+ execResult.noteEncryptedLogs.filter(noteLog => {
123
+ const nullifierCounter = noteHashNullifierCounterMap.get(noteLog.noteHashCounter);
124
+ return (
125
+ nullifierCounter === undefined ||
126
+ (noteLog.noteHashCounter < minRevertibleSideEffectCounter && nullifierCounter >= minRevertibleSideEffectCounter)
127
+ );
128
+ }),
129
+ ...execResult.nestedExecutions.flatMap(res =>
130
+ collectNoteEncryptedLogs(res, noteHashNullifierCounterMap, minRevertibleSideEffectCounter),
131
+ ),
120
132
  ].flat();
121
133
  }
122
134
 
@@ -126,8 +138,9 @@ function collectNoteEncryptedLogs(
126
138
  * @returns All encrypted logs.
127
139
  */
128
140
  export function collectSortedNoteEncryptedLogs(execResult: ExecutionResult): EncryptedNoteFunctionL2Logs {
129
- const nullifiedNoteHashCounters = collectNullifiedNoteHashCounters(execResult);
130
- const allLogs = collectNoteEncryptedLogs(execResult, nullifiedNoteHashCounters);
141
+ const noteHashNullifierCounterMap = collectNoteHashNullifierCounterMap(execResult);
142
+ const minRevertibleSideEffectCounter = getFinalMinRevertibleSideEffectCounter(execResult);
143
+ const allLogs = collectNoteEncryptedLogs(execResult, noteHashNullifierCounterMap, minRevertibleSideEffectCounter);
131
144
  const sortedLogs = sortByCounter(allLogs);
132
145
  return new EncryptedNoteFunctionL2Logs(sortedLogs.map(l => l.log));
133
146
  }
@@ -206,3 +219,10 @@ export function collectPublicTeardownFunctionCall(execResult: ExecutionResult):
206
219
 
207
220
  return PublicExecutionRequest.empty();
208
221
  }
222
+
223
+ export function getFinalMinRevertibleSideEffectCounter(execResult: ExecutionResult): number {
224
+ return execResult.nestedExecutions.reduce((counter, exec) => {
225
+ const nestedCounter = getFinalMinRevertibleSideEffectCounter(exec);
226
+ return nestedCounter ? nestedCounter : counter;
227
+ }, execResult.callStackItem.publicInputs.minRevertibleSideEffectCounter.toNumber());
228
+ }
@@ -68,7 +68,7 @@ export async function executePrivateFunction(
68
68
 
69
69
  const noteHashLeafIndexMap = context.getNoteHashLeafIndexMap();
70
70
  const newNotes = context.getNewNotes();
71
- const nullifiedNoteHashCounters = context.getNullifiedNoteHashCounters();
71
+ const noteHashNullifierCounterMap = context.getNoteHashNullifierCounterMap();
72
72
  const nestedExecutions = context.getNestedExecutions();
73
73
  const enqueuedPublicFunctionCalls = context.getEnqueuedPublicFunctionCalls();
74
74
  const publicTeardownFunctionCall = context.getPublicTeardownFunctionCall();
@@ -82,7 +82,7 @@ export async function executePrivateFunction(
82
82
  returnValues: rawReturnValues,
83
83
  noteHashLeafIndexMap,
84
84
  newNotes,
85
- nullifiedNoteHashCounters,
85
+ noteHashNullifierCounterMap,
86
86
  vk: Buffer.from(artifact.verificationKey!, 'hex'),
87
87
  nestedExecutions,
88
88
  enqueuedPublicFunctionCalls,
@@ -38,6 +38,7 @@ export class AcirSimulator {
38
38
  * @param entryPointArtifact - The artifact of the entry point function.
39
39
  * @param contractAddress - The address of the contract (should match request.origin)
40
40
  * @param msgSender - The address calling the function. This can be replaced to simulate a call from another contract or a specific account.
41
+ * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all.
41
42
  * @returns The result of the execution.
42
43
  */
43
44
  public async run(
@@ -45,6 +46,7 @@ export class AcirSimulator {
45
46
  entryPointArtifact: FunctionArtifact,
46
47
  contractAddress: AztecAddress,
47
48
  msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE),
49
+ scopes?: AztecAddress[],
48
50
  ): Promise<ExecutionResult> {
49
51
  if (entryPointArtifact.functionType !== FunctionType.PRIVATE) {
50
52
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as private`);
@@ -68,6 +70,9 @@ export class AcirSimulator {
68
70
  false,
69
71
  entryPointArtifact.isStatic,
70
72
  );
73
+
74
+ const txHash = request.toTxRequest().hash();
75
+
71
76
  const context = new ClientExecutionContext(
72
77
  contractAddress,
73
78
  request.firstCallArgsHash,
@@ -76,10 +81,12 @@ export class AcirSimulator {
76
81
  header,
77
82
  request.authWitnesses,
78
83
  PackedValuesCache.create(request.argsOfCalls),
79
- new ExecutionNoteCache(),
84
+ new ExecutionNoteCache(txHash),
80
85
  this.db,
81
86
  this.node,
82
87
  startSideEffectCounter,
88
+ undefined,
89
+ scopes,
83
90
  );
84
91
 
85
92
  try {
@@ -100,18 +107,19 @@ export class AcirSimulator {
100
107
  * @param request - The transaction request.
101
108
  * @param entryPointArtifact - The artifact of the entry point function.
102
109
  * @param contractAddress - The address of the contract.
103
- * @param aztecNode - The AztecNode instance.
110
+ * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all.
104
111
  */
105
112
  public async runUnconstrained(
106
113
  request: FunctionCall,
107
114
  entryPointArtifact: FunctionArtifact,
108
115
  contractAddress: AztecAddress,
116
+ scopes?: AztecAddress[],
109
117
  ) {
110
118
  if (entryPointArtifact.functionType !== FunctionType.UNCONSTRAINED) {
111
119
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as unconstrained`);
112
120
  }
113
121
 
114
- const context = new ViewDataOracle(contractAddress, [], this.db, this.node);
122
+ const context = new ViewDataOracle(contractAddress, [], this.db, this.node, undefined, scopes);
115
123
 
116
124
  try {
117
125
  return await executeUnconstrainedFunction(
@@ -188,14 +196,15 @@ export class AcirSimulator {
188
196
  returnTypes: artifact.returnTypes,
189
197
  };
190
198
 
191
- const [innerNoteHash, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained(
199
+ const [noteHash, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained(
192
200
  execRequest,
193
201
  artifact,
194
202
  contractAddress,
203
+ // We can omit scopes here, because "compute_note_hash_and_optionally_a_nullifier" does not need access to any notes.
195
204
  )) as bigint[];
196
205
 
197
206
  return {
198
- innerNoteHash: new Fr(innerNoteHash),
207
+ noteHash: new Fr(noteHash),
199
208
  uniqueNoteHash: new Fr(uniqueNoteHash),
200
209
  siloedNoteHash: new Fr(siloedNoteHash),
201
210
  innerNullifier: new Fr(innerNullifier),
@@ -203,20 +212,15 @@ export class AcirSimulator {
203
212
  }
204
213
 
205
214
  /**
206
- * Computes the inner note hash of a note, which contains storage slot and the custom note hash.
215
+ * Computes a hash of the note.
207
216
  * @param contractAddress - The address of the contract.
208
217
  * @param storageSlot - The storage slot.
209
218
  * @param noteTypeId - The note type identifier.
210
219
  * @param note - The note.
211
220
  * @returns The note hash.
212
221
  */
213
- public async computeInnerNoteHash(
214
- contractAddress: AztecAddress,
215
- storageSlot: Fr,
216
- noteTypeId: NoteSelector,
217
- note: Note,
218
- ) {
219
- const { innerNoteHash } = await this.computeNoteHashAndOptionallyANullifier(
222
+ public async computeNoteHash(contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: NoteSelector, note: Note) {
223
+ const { noteHash } = await this.computeNoteHashAndOptionallyANullifier(
220
224
  contractAddress,
221
225
  Fr.ZERO,
222
226
  storageSlot,
@@ -224,6 +228,6 @@ export class AcirSimulator {
224
228
  false,
225
229
  note,
226
230
  );
227
- return innerNoteHash;
231
+ return noteHash;
228
232
  }
229
233
  }
@@ -0,0 +1,30 @@
1
+ import { Fq, Fr, GeneratorIndex, Point } from '@aztec/circuits.js';
2
+ import { Grumpkin } from '@aztec/circuits.js/barretenberg';
3
+ import { pedersenCommit } from '@aztec/foundation/crypto';
4
+
5
+ // Copied over from `noir-projects/aztec-nr/aztec/src/generators.nr`
6
+ const G_SLOT = new Point(
7
+ new Fr(0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15n),
8
+ new Fr(0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791n),
9
+ false,
10
+ );
11
+
12
+ /**
13
+ * Computes a note hiding point as is done by the default implementation injected by macros.
14
+ * @param storageSlot - The slot to which the note was inserted.
15
+ * @param noteContent - The note content (e.g. note.items).
16
+ * @returns A note hash.
17
+ */
18
+ export function computeNoteHash(storageSlot: Fr, noteContent: Fr[]): Fr {
19
+ // TODO(#7771): update this to do only 1 MSM call
20
+ const c = pedersenCommit(
21
+ noteContent.map(f => f.toBuffer()),
22
+ GeneratorIndex.NOTE_HIDING_POINT,
23
+ );
24
+ const noteHidingPointBeforeSlotting = new Point(new Fr(c[0]), new Fr(c[1]), false);
25
+
26
+ const grumpkin = new Grumpkin();
27
+ const slotPoint = grumpkin.mul(G_SLOT, new Fq(storageSlot.toBigInt()));
28
+ const noteHidingPoint = grumpkin.add(noteHidingPointBeforeSlotting, slotPoint);
29
+ return noteHidingPoint.x;
30
+ }
@@ -30,6 +30,7 @@ export class ViewDataOracle extends TypedOracle {
30
30
  protected readonly db: DBOracle,
31
31
  protected readonly aztecNode: AztecNode,
32
32
  protected log = createDebugLogger('aztec:simulator:client_view_context'),
33
+ protected readonly scopes?: AztecAddress[],
33
34
  ) {
34
35
  super();
35
36
  }
@@ -219,7 +220,7 @@ export class ViewDataOracle extends TypedOracle {
219
220
  offset: number,
220
221
  status: NoteStatus,
221
222
  ): Promise<NoteData[]> {
222
- const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status);
223
+ const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status, this.scopes);
223
224
  return pickNotes<NoteData>(dbNotes, {
224
225
  selects: selectByIndexes.slice(0, numSelects).map((index, i) => ({
225
226
  selector: { index, offset: selectByOffsets[i], length: selectByLengths[i] },
package/src/index.ts CHANGED
@@ -6,4 +6,3 @@ export * from './public/index.js';
6
6
  export * from './providers/index.js';
7
7
  export * from './mocks/index.js';
8
8
  export * from './stats/index.js';
9
- export * from './utils.js';
@@ -14,7 +14,6 @@ import {
14
14
  import {
15
15
  type AvmExecutionHints,
16
16
  AztecAddress,
17
- ClientIvcProof,
18
17
  ContractStorageRead,
19
18
  ContractStorageUpdateRequest,
20
19
  Fr,
@@ -361,7 +360,7 @@ export abstract class AbstractPhaseManager {
361
360
  const previousKernel = this.getPreviousKernelData(previousOutput, previousCircuit);
362
361
 
363
362
  // We take a deep copy (clone) of these inputs to be passed to the prover
364
- const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, ClientIvcProof.empty(), callData);
363
+ const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, callData);
365
364
  switch (this.phase) {
366
365
  case PublicKernelType.SETUP:
367
366
  return [inputs.clone(), await this.publicKernel.publicKernelCircuitSetup(inputs), 'PublicKernelSetupArtifact'];
@@ -3,6 +3,7 @@ import { type AvmSimulationStats } from '@aztec/circuit-types/stats';
3
3
  import { Fr, Gas, type GlobalVariables, type Header, type Nullifier, type TxContext } from '@aztec/circuits.js';
4
4
  import { createDebugLogger } from '@aztec/foundation/log';
5
5
  import { Timer } from '@aztec/foundation/timer';
6
+ import { type TelemetryClient } from '@aztec/telemetry-client';
6
7
 
7
8
  import { AvmContext } from '../avm/avm_context.js';
8
9
  import { AvmExecutionEnvironment } from '../avm/avm_execution_environment.js';
@@ -12,18 +13,24 @@ import { HostStorage } from '../avm/journal/host_storage.js';
12
13
  import { AvmPersistableStateManager } from '../avm/journal/index.js';
13
14
  import { type CommitmentsDB, type PublicContractsDB, type PublicStateDB } from './db_interfaces.js';
14
15
  import { type PublicExecutionResult } from './execution.js';
16
+ import { ExecutorMetrics } from './executor_metrics.js';
15
17
  import { PublicSideEffectTrace } from './side_effect_trace.js';
16
18
 
17
19
  /**
18
20
  * Handles execution of public functions.
19
21
  */
20
22
  export class PublicExecutor {
23
+ metrics: ExecutorMetrics;
24
+
21
25
  constructor(
22
26
  private readonly publicStorageDB: PublicStateDB,
23
27
  private readonly contractsDb: PublicContractsDB,
24
28
  private readonly commitmentsDb: CommitmentsDB,
25
29
  private readonly header: Header,
26
- ) {}
30
+ client: TelemetryClient,
31
+ ) {
32
+ this.metrics = new ExecutorMetrics(client, 'PublicExecutor');
33
+ }
27
34
 
28
35
  static readonly log = createDebugLogger('aztec:simulator:public_executor');
29
36
 
@@ -102,6 +109,12 @@ export class PublicExecutor {
102
109
  fnName,
103
110
  );
104
111
 
112
+ if (publicExecutionResult.reverted) {
113
+ this.metrics.recordFunctionSimulationFailure();
114
+ } else {
115
+ this.metrics.recordFunctionSimulation(bytecode.length, timer.ms());
116
+ }
117
+
105
118
  return publicExecutionResult;
106
119
  }
107
120
  }