@aztec/simulator 0.72.1 → 0.73.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 (114) hide show
  1. package/README.md +1 -1
  2. package/dest/avm/avm_simulator.d.ts +0 -1
  3. package/dest/avm/avm_simulator.d.ts.map +1 -1
  4. package/dest/avm/avm_simulator.js +4 -19
  5. package/dest/avm/avm_tree.d.ts +9 -8
  6. package/dest/avm/avm_tree.d.ts.map +1 -1
  7. package/dest/avm/avm_tree.js +35 -30
  8. package/dest/avm/fixtures/avm_simulation_tester.d.ts +21 -0
  9. package/dest/avm/fixtures/avm_simulation_tester.d.ts.map +1 -0
  10. package/dest/avm/fixtures/avm_simulation_tester.js +74 -0
  11. package/dest/avm/fixtures/base_avm_simulation_tester.d.ts +35 -0
  12. package/dest/avm/fixtures/base_avm_simulation_tester.d.ts.map +1 -0
  13. package/dest/avm/fixtures/base_avm_simulation_tester.js +76 -0
  14. package/dest/avm/fixtures/index.d.ts +6 -2
  15. package/dest/avm/fixtures/index.d.ts.map +1 -1
  16. package/dest/avm/fixtures/index.js +28 -14
  17. package/dest/avm/fixtures/simple_contract_data_source.d.ts +31 -0
  18. package/dest/avm/fixtures/simple_contract_data_source.d.ts.map +1 -0
  19. package/dest/avm/fixtures/simple_contract_data_source.js +75 -0
  20. package/dest/avm/journal/journal.d.ts +3 -3
  21. package/dest/avm/journal/journal.d.ts.map +1 -1
  22. package/dest/avm/journal/journal.js +24 -21
  23. package/dest/avm/opcodes/accrued_substate.js +2 -2
  24. package/dest/avm/opcodes/hashing.js +2 -2
  25. package/dest/avm/test_utils.d.ts +1 -1
  26. package/dest/avm/test_utils.d.ts.map +1 -1
  27. package/dest/avm/test_utils.js +4 -3
  28. package/dest/client/client_execution_context.d.ts +1 -1
  29. package/dest/client/client_execution_context.d.ts.map +1 -1
  30. package/dest/client/client_execution_context.js +21 -18
  31. package/dest/client/execution_note_cache.d.ts +3 -3
  32. package/dest/client/execution_note_cache.d.ts.map +1 -1
  33. package/dest/client/execution_note_cache.js +10 -10
  34. package/dest/client/pick_notes.js +5 -5
  35. package/dest/client/simulator.js +4 -4
  36. package/dest/client/view_data_oracle.js +2 -2
  37. package/dest/common/hashed_values_cache.d.ts +1 -1
  38. package/dest/common/hashed_values_cache.d.ts.map +1 -1
  39. package/dest/common/hashed_values_cache.js +5 -5
  40. package/dest/providers/acvm_wasm.d.ts +2 -0
  41. package/dest/providers/acvm_wasm.d.ts.map +1 -1
  42. package/dest/providers/acvm_wasm.js +15 -2
  43. package/dest/public/execution.d.ts +1 -1
  44. package/dest/public/execution.d.ts.map +1 -1
  45. package/dest/public/execution.js +3 -3
  46. package/dest/public/fee_payment.d.ts +2 -2
  47. package/dest/public/fee_payment.d.ts.map +1 -1
  48. package/dest/public/fee_payment.js +3 -3
  49. package/dest/public/fixtures/index.d.ts +2 -37
  50. package/dest/public/fixtures/index.d.ts.map +1 -1
  51. package/dest/public/fixtures/index.js +3 -250
  52. package/dest/public/fixtures/public_tx_simulation_tester.d.ts +21 -0
  53. package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -0
  54. package/dest/public/fixtures/public_tx_simulation_tester.js +89 -0
  55. package/dest/public/fixtures/utils.d.ts +17 -0
  56. package/dest/public/fixtures/utils.d.ts.map +1 -0
  57. package/dest/public/fixtures/utils.js +66 -0
  58. package/dest/public/index.d.ts +1 -1
  59. package/dest/public/index.d.ts.map +1 -1
  60. package/dest/public/index.js +2 -2
  61. package/dest/public/public_db_sources.d.ts +2 -1
  62. package/dest/public/public_db_sources.d.ts.map +1 -1
  63. package/dest/public/public_db_sources.js +27 -21
  64. package/dest/public/public_processor.d.ts +1 -1
  65. package/dest/public/public_processor.d.ts.map +1 -1
  66. package/dest/public/public_processor.js +19 -18
  67. package/dest/public/public_tx_context.d.ts +5 -5
  68. package/dest/public/public_tx_context.d.ts.map +1 -1
  69. package/dest/public/public_tx_context.js +43 -17
  70. package/dest/public/public_tx_simulator.js +7 -7
  71. package/dest/public/{enqueued_call_side_effect_trace.d.ts → side_effect_trace.d.ts} +5 -5
  72. package/dest/public/side_effect_trace.d.ts.map +1 -0
  73. package/dest/public/{enqueued_call_side_effect_trace.js → side_effect_trace.js} +7 -7
  74. package/dest/public/side_effect_trace_interface.d.ts +1 -1
  75. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  76. package/dest/test/utils.d.ts +1 -1
  77. package/dest/test/utils.d.ts.map +1 -1
  78. package/dest/test/utils.js +3 -3
  79. package/package.json +9 -9
  80. package/src/avm/avm_simulator.ts +3 -27
  81. package/src/avm/avm_tree.ts +39 -37
  82. package/src/avm/fixtures/avm_simulation_tester.ts +105 -0
  83. package/src/avm/fixtures/base_avm_simulation_tester.ts +104 -0
  84. package/src/avm/fixtures/index.ts +46 -17
  85. package/src/avm/fixtures/simple_contract_data_source.ts +98 -0
  86. package/src/avm/journal/journal.ts +24 -21
  87. package/src/avm/opcodes/accrued_substate.ts +1 -1
  88. package/src/avm/opcodes/hashing.ts +1 -1
  89. package/src/avm/test_utils.ts +3 -4
  90. package/src/client/client_execution_context.ts +27 -21
  91. package/src/client/execution_note_cache.ts +19 -14
  92. package/src/client/pick_notes.ts +4 -4
  93. package/src/client/simulator.ts +3 -3
  94. package/src/client/view_data_oracle.ts +1 -1
  95. package/src/common/hashed_values_cache.ts +4 -4
  96. package/src/providers/acvm_wasm.ts +13 -2
  97. package/src/public/execution.ts +3 -3
  98. package/src/public/fee_payment.ts +2 -2
  99. package/src/public/fixtures/index.ts +2 -387
  100. package/src/public/fixtures/public_tx_simulation_tester.ts +174 -0
  101. package/src/public/fixtures/utils.ts +110 -0
  102. package/src/public/index.ts +1 -1
  103. package/src/public/public_db_sources.ts +31 -20
  104. package/src/public/public_processor.ts +25 -18
  105. package/src/public/public_tx_context.ts +86 -27
  106. package/src/public/public_tx_simulator.ts +6 -6
  107. package/src/public/{enqueued_call_side_effect_trace.ts → side_effect_trace.ts} +7 -7
  108. package/src/public/side_effect_trace_interface.ts +1 -1
  109. package/src/test/utils.ts +2 -2
  110. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +0 -1
  111. package/dest/public/transitional_adapters.d.ts +0 -4
  112. package/dest/public/transitional_adapters.d.ts.map +0 -1
  113. package/dest/public/transitional_adapters.js +0 -29
  114. package/src/public/transitional_adapters.ts +0 -113
@@ -0,0 +1,98 @@
1
+ import {
2
+ type ContractClassPublic,
3
+ type ContractDataSource,
4
+ type ContractInstanceWithAddress,
5
+ type FunctionSelector,
6
+ type PublicFunction,
7
+ computePublicBytecodeCommitment,
8
+ } from '@aztec/circuits.js';
9
+ import { type ContractArtifact } from '@aztec/foundation/abi';
10
+ import { type AztecAddress } from '@aztec/foundation/aztec-address';
11
+ import { type Fr } from '@aztec/foundation/fields';
12
+ import { createLogger } from '@aztec/foundation/log';
13
+
14
+ import { PUBLIC_DISPATCH_FN_NAME } from './index.js';
15
+
16
+ /**
17
+ * This class is used during public/avm testing to function as a database of
18
+ * contract contract classes and instances. Tests can populate it with classes
19
+ * and instances and then probe it via the ContractDataSource interface.
20
+ *
21
+ * This class does not include any real merkle trees & merkle operations.
22
+ */
23
+ export class SimpleContractDataSource implements ContractDataSource {
24
+ public logger = createLogger('simple-contract-data-source');
25
+
26
+ // maps contract class ID to class
27
+ private contractClasses: Map<string, ContractClassPublic> = new Map();
28
+ // maps contract instance address to instance
29
+ private contractInstances: Map<string, ContractInstanceWithAddress> = new Map();
30
+ // maps contract instance address to address
31
+ private contractArtifacts: Map<string, ContractArtifact> = new Map();
32
+
33
+ /////////////////////////////////////////////////////////////
34
+ // Helper functions not in the contract data source interface
35
+ getFirstContractInstance(): ContractInstanceWithAddress {
36
+ return this.contractInstances.values().next().value;
37
+ }
38
+
39
+ addContractArtifact(classId: Fr, artifact: ContractArtifact): void {
40
+ this.contractArtifacts.set(classId.toString(), artifact);
41
+ }
42
+
43
+ /////////////////////////////////////////////////////////////
44
+ // ContractDataSource function impelementations
45
+ getPublicFunction(_address: AztecAddress, _selector: FunctionSelector): Promise<PublicFunction> {
46
+ throw new Error('Method not implemented.');
47
+ }
48
+
49
+ getBlockNumber(): Promise<number> {
50
+ throw new Error('Method not implemented.');
51
+ }
52
+
53
+ getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
54
+ return Promise.resolve(this.contractClasses.get(id.toString()));
55
+ }
56
+
57
+ async getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
58
+ const contractClass = await this.getContractClass(id);
59
+ return Promise.resolve(computePublicBytecodeCommitment(contractClass!.packedBytecode));
60
+ }
61
+
62
+ getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
63
+ return Promise.resolve(this.contractInstances.get(address.toString()));
64
+ }
65
+
66
+ getContractClassIds(): Promise<Fr[]> {
67
+ throw new Error('Method not implemented.');
68
+ }
69
+
70
+ async getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
71
+ const contractInstance = await this.getContract(address);
72
+ if (!contractInstance) {
73
+ this.logger.warn(`Contract not found at address: ${address}`);
74
+ return undefined;
75
+ }
76
+ this.logger.debug(`Retrieved contract artifact for address: ${address}`);
77
+ this.logger.debug(`Contract class ID: ${contractInstance.contractClassId}`);
78
+ return Promise.resolve(this.contractArtifacts.get(contractInstance!.contractClassId.toString()));
79
+ }
80
+
81
+ getContractFunctionName(_address: AztecAddress, _selector: FunctionSelector): Promise<string> {
82
+ return Promise.resolve(PUBLIC_DISPATCH_FN_NAME);
83
+ }
84
+
85
+ registerContractFunctionSignatures(_address: AztecAddress, _signatures: string[]): Promise<void> {
86
+ return Promise.resolve();
87
+ }
88
+
89
+ addContractClass(contractClass: ContractClassPublic): Promise<void> {
90
+ this.contractClasses.set(contractClass.id.toString(), contractClass);
91
+ return Promise.resolve();
92
+ }
93
+
94
+ addContractInstance(contractInstance: ContractInstanceWithAddress): Promise<void> {
95
+ this.contractInstances.set(contractInstance.address.toString(), contractInstance);
96
+ return Promise.resolve();
97
+ }
98
+ }
@@ -72,7 +72,7 @@ export class AvmPersistableStateManager {
72
72
  trace: PublicSideEffectTraceInterface,
73
73
  doMerkleOperations: boolean = false,
74
74
  firstNullifier: Fr,
75
- ) {
75
+ ): Promise<AvmPersistableStateManager> {
76
76
  const ephemeralForest = await AvmEphemeralForest.create(worldStateDB.getMerkleInterface());
77
77
  return new AvmPersistableStateManager(
78
78
  worldStateDB,
@@ -126,11 +126,12 @@ export class AvmPersistableStateManager {
126
126
  this.trace.merge(forkedState.trace, reverted);
127
127
  if (reverted) {
128
128
  if (this.doMerkleOperations) {
129
- this.log.debug(
129
+ this.log.trace(
130
130
  `Rolled back nullifier tree to root ${this.merkleTrees.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot()}`,
131
131
  );
132
132
  }
133
133
  } else {
134
+ this.log.trace('Merging forked state into parent...');
134
135
  this.merkleTrees = forkedState.merkleTrees;
135
136
  }
136
137
  }
@@ -144,7 +145,7 @@ export class AvmPersistableStateManager {
144
145
  */
145
146
  public async writeStorage(contractAddress: AztecAddress, slot: Fr, value: Fr, protocolWrite = false): Promise<void> {
146
147
  this.log.debug(`Storage write (address=${contractAddress}, slot=${slot}): value=${value}`);
147
- const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
148
+ const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot);
148
149
  this.log.debug(`leafSlot=${leafSlot}`);
149
150
  // Cache storage writes for later reference/reads
150
151
  this.publicStorage.write(contractAddress, slot, value);
@@ -169,7 +170,7 @@ export class AvmPersistableStateManager {
169
170
  );
170
171
  }
171
172
 
172
- this.trace.tracePublicStorageWrite(
173
+ await this.trace.tracePublicStorageWrite(
173
174
  contractAddress,
174
175
  slot,
175
176
  value,
@@ -181,7 +182,7 @@ export class AvmPersistableStateManager {
181
182
  insertionPath,
182
183
  );
183
184
  } else {
184
- this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite);
185
+ await this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite);
185
186
  }
186
187
  }
187
188
 
@@ -195,7 +196,7 @@ export class AvmPersistableStateManager {
195
196
  public async readStorage(contractAddress: AztecAddress, slot: Fr): Promise<Fr> {
196
197
  const { value, cached } = await this.publicStorage.read(contractAddress, slot);
197
198
  this.log.debug(`Storage read (address=${contractAddress}, slot=${slot}): value=${value}, cached=${cached}`);
198
- const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
199
+ const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot);
199
200
  this.log.debug(`leafSlot=${leafSlot}`);
200
201
 
201
202
  if (this.doMerkleOperations) {
@@ -283,34 +284,34 @@ export class AvmPersistableStateManager {
283
284
  * Write a raw note hash, silo it and make it unique, then trace the write.
284
285
  * @param noteHash - the unsiloed note hash to write
285
286
  */
286
- public writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): void {
287
- const siloedNoteHash = siloNoteHash(contractAddress, noteHash);
287
+ public async writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): Promise<void> {
288
+ const siloedNoteHash = await siloNoteHash(contractAddress, noteHash);
288
289
 
289
- this.writeSiloedNoteHash(siloedNoteHash);
290
+ await this.writeSiloedNoteHash(siloedNoteHash);
290
291
  }
291
292
 
292
293
  /**
293
294
  * Write a note hash, make it unique, trace the write.
294
295
  * @param noteHash - the non unique note hash to write
295
296
  */
296
- public writeSiloedNoteHash(noteHash: Fr): void {
297
- const nonce = computeNoteHashNonce(this.firstNullifier, this.trace.getNoteHashCount());
298
- const uniqueNoteHash = computeUniqueNoteHash(nonce, noteHash);
297
+ public async writeSiloedNoteHash(noteHash: Fr): Promise<void> {
298
+ const nonce = await computeNoteHashNonce(this.firstNullifier, this.trace.getNoteHashCount());
299
+ const uniqueNoteHash = await computeUniqueNoteHash(nonce, noteHash);
299
300
 
300
- this.writeUniqueNoteHash(uniqueNoteHash);
301
+ await this.writeUniqueNoteHash(uniqueNoteHash);
301
302
  }
302
303
 
303
304
  /**
304
305
  * Write a note hash, trace the write.
305
306
  * @param noteHash - the siloed unique hash to write
306
307
  */
307
- public writeUniqueNoteHash(noteHash: Fr): void {
308
+ public async writeUniqueNoteHash(noteHash: Fr): Promise<void> {
308
309
  this.log.debug(`noteHashes += @${noteHash}.`);
309
310
 
310
311
  if (this.doMerkleOperations) {
311
312
  // Should write a helper for this
312
313
  const leafIndex = new Fr(this.merkleTrees.treeMap.get(MerkleTreeId.NOTE_HASH_TREE)!.leafCount);
313
- const insertionPath = this.merkleTrees.appendNoteHash(noteHash);
314
+ const insertionPath = await this.merkleTrees.appendNoteHash(noteHash);
314
315
  this.trace.traceNewNoteHash(noteHash, leafIndex, insertionPath);
315
316
  } else {
316
317
  this.trace.traceNewNoteHash(noteHash);
@@ -325,7 +326,7 @@ export class AvmPersistableStateManager {
325
326
  */
326
327
  public async checkNullifierExists(contractAddress: AztecAddress, nullifier: Fr): Promise<boolean> {
327
328
  this.log.debug(`Checking existence of nullifier (address=${contractAddress}, nullifier=${nullifier})`);
328
- const siloedNullifier = siloNullifier(contractAddress, nullifier);
329
+ const siloedNullifier = await siloNullifier(contractAddress, nullifier);
329
330
  const [exists, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership(
330
331
  siloedNullifier,
331
332
  );
@@ -407,7 +408,7 @@ export class AvmPersistableStateManager {
407
408
  */
408
409
  public async writeNullifier(contractAddress: AztecAddress, nullifier: Fr) {
409
410
  this.log.debug(`Inserting new nullifier (address=${nullifier}, nullifier=${contractAddress})`);
410
- const siloedNullifier = siloNullifier(contractAddress, nullifier);
411
+ const siloedNullifier = await siloNullifier(contractAddress, nullifier);
411
412
  await this.writeSiloedNullifier(siloedNullifier);
412
413
  }
413
414
 
@@ -447,13 +448,15 @@ export class AvmPersistableStateManager {
447
448
  await this.nullifiers.append(siloedNullifier);
448
449
  // We append the new nullifier
449
450
  this.log.debug(
450
- `Nullifier tree root before insertion ${this.merkleTrees.treeMap
451
+ `Nullifier tree root before insertion ${await this.merkleTrees.treeMap
451
452
  .get(MerkleTreeId.NULLIFIER_TREE)!
452
453
  .getRoot()}`,
453
454
  );
454
455
  const appendResult = await this.merkleTrees.appendNullifier(siloedNullifier);
455
456
  this.log.debug(
456
- `Nullifier tree root after insertion ${this.merkleTrees.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot()}`,
457
+ `Nullifier tree root after insertion ${await this.merkleTrees.treeMap
458
+ .get(MerkleTreeId.NULLIFIER_TREE)!
459
+ .getRoot()}`,
457
460
  );
458
461
  const lowLeafPreimage = appendResult.lowWitness.preimage as NullifierLeafPreimage;
459
462
  const lowLeafIndex = appendResult.lowWitness.index;
@@ -550,7 +553,7 @@ export class AvmPersistableStateManager {
550
553
  new Array<Fr>(),
551
554
  ];
552
555
  if (!contractAddressIsCanonical(contractAddress)) {
553
- const contractAddressNullifier = siloNullifier(
556
+ const contractAddressNullifier = await siloNullifier(
554
557
  AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
555
558
  contractAddress.toField(),
556
559
  );
@@ -615,7 +618,7 @@ export class AvmPersistableStateManager {
615
618
  new Array<Fr>(),
616
619
  ];
617
620
  if (!contractAddressIsCanonical(contractAddress)) {
618
- const contractAddressNullifier = siloNullifier(
621
+ const contractAddressNullifier = await siloNullifier(
619
622
  AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
620
623
  contractAddress.toField(),
621
624
  );
@@ -70,7 +70,7 @@ export class EmitNoteHash extends Instruction {
70
70
  }
71
71
 
72
72
  const noteHash = memory.get(noteHashOffset).toFr();
73
- context.persistableState.writeNoteHash(context.environment.address, noteHash);
73
+ await context.persistableState.writeNoteHash(context.environment.address, noteHash);
74
74
 
75
75
  memory.assert({ reads: 1, addressing });
76
76
  }
@@ -34,7 +34,7 @@ export class Poseidon2 extends Instruction {
34
34
  const inputState = memory.getSlice(inputOffset, Poseidon2.stateSize);
35
35
  memory.checkTagsRange(TypeTag.FIELD, inputOffset, Poseidon2.stateSize);
36
36
 
37
- const outputState = poseidon2Permutation(inputState);
37
+ const outputState = await poseidon2Permutation(inputState);
38
38
  memory.setSlice(
39
39
  outputOffset,
40
40
  outputState.map(word => new Field(word)),
@@ -11,11 +11,10 @@ import { mock } from 'jest-mock-extended';
11
11
  import { type WorldStateDB } from '../public/public_db_sources.js';
12
12
  import { type PublicSideEffectTraceInterface } from '../public/side_effect_trace_interface.js';
13
13
 
14
- export function mockGetBytecode(worldStateDB: WorldStateDB, bytecode: Buffer) {
14
+ export async function mockGetBytecode(worldStateDB: WorldStateDB, bytecode: Buffer) {
15
+ const commitment = await computePublicBytecodeCommitment(bytecode);
15
16
  (worldStateDB as jest.Mocked<WorldStateDB>).getBytecode.mockResolvedValue(bytecode);
16
- (worldStateDB as jest.Mocked<WorldStateDB>).getBytecodeCommitment.mockResolvedValue(
17
- computePublicBytecodeCommitment(bytecode),
18
- );
17
+ (worldStateDB as jest.Mocked<WorldStateDB>).getBytecodeCommitment.mockResolvedValue(commitment);
19
18
  }
20
19
 
21
20
  export function mockTraceFork(trace: PublicSideEffectTraceInterface, nestedTrace?: PublicSideEffectTraceInterface) {
@@ -166,7 +166,7 @@ export class ClientExecutionContext extends ViewDataOracle {
166
166
  * @returns The hash of the values.
167
167
  */
168
168
  public override storeInExecutionCache(values: Fr[]): Promise<Fr> {
169
- return Promise.resolve(this.executionCache.store(values));
169
+ return this.executionCache.store(values);
170
170
  }
171
171
 
172
172
  /**
@@ -241,14 +241,22 @@ export class ClientExecutionContext extends ViewDataOracle {
241
241
  .join(', ')}`,
242
242
  );
243
243
 
244
- notes.forEach(n => {
245
- if (n.index !== undefined) {
246
- const siloedNoteHash = siloNoteHash(n.contractAddress, n.noteHash);
247
- const uniqueNoteHash = computeUniqueNoteHash(n.nonce, siloedNoteHash);
244
+ const noteHashesAndIndexes = await Promise.all(
245
+ notes.map(async n => {
246
+ if (n.index !== undefined) {
247
+ const siloedNoteHash = await siloNoteHash(n.contractAddress, n.noteHash);
248
+ const uniqueNoteHash = await computeUniqueNoteHash(n.nonce, siloedNoteHash);
248
249
 
249
- this.noteHashLeafIndexMap.set(uniqueNoteHash.toBigInt(), n.index);
250
- }
251
- });
250
+ return { hash: uniqueNoteHash, index: n.index };
251
+ }
252
+ }),
253
+ );
254
+
255
+ noteHashesAndIndexes
256
+ .filter(n => n !== undefined)
257
+ .forEach(n => {
258
+ this.noteHashLeafIndexMap.set(n!.hash.toBigInt(), n!.index);
259
+ });
252
260
 
253
261
  return notes;
254
262
  }
@@ -291,8 +299,8 @@ export class ClientExecutionContext extends ViewDataOracle {
291
299
  * @param innerNullifier - The pending nullifier to add in the list (not yet siloed by contract address).
292
300
  * @param noteHash - A hash of the new note.
293
301
  */
294
- public override notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
295
- const nullifiedNoteHashCounter = this.noteCache.nullifyNote(
302
+ public override async notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
303
+ const nullifiedNoteHashCounter = await this.noteCache.nullifyNote(
296
304
  this.callContext.contractAddress,
297
305
  innerNullifier,
298
306
  noteHash,
@@ -300,7 +308,6 @@ export class ClientExecutionContext extends ViewDataOracle {
300
308
  if (nullifiedNoteHashCounter !== undefined) {
301
309
  this.noteHashNullifierCounterMap.set(nullifiedNoteHashCounter, counter);
302
310
  }
303
- return Promise.resolve();
304
311
  }
305
312
 
306
313
  /**
@@ -310,8 +317,7 @@ export class ClientExecutionContext extends ViewDataOracle {
310
317
  * @param noteHash - A hash of the new note.
311
318
  */
312
319
  public override notifyCreatedNullifier(innerNullifier: Fr) {
313
- this.noteCache.nullifierCreated(this.callContext.contractAddress, innerNullifier);
314
- return Promise.resolve();
320
+ return this.noteCache.nullifierCreated(this.callContext.contractAddress, innerNullifier);
315
321
  }
316
322
 
317
323
  /**
@@ -369,7 +375,7 @@ export class ClientExecutionContext extends ViewDataOracle {
369
375
 
370
376
  const derivedTxContext = this.txContext.clone();
371
377
 
372
- const derivedCallContext = this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall);
378
+ const derivedCallContext = await this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall);
373
379
 
374
380
  const context = new ClientExecutionContext(
375
381
  argsHash,
@@ -426,7 +432,7 @@ export class ClientExecutionContext extends ViewDataOracle {
426
432
  isStaticCall: boolean,
427
433
  ) {
428
434
  const targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector);
429
- const derivedCallContext = this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall);
435
+ const derivedCallContext = await this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall);
430
436
  const args = this.executionCache.getPreimage(argsHash);
431
437
 
432
438
  this.log.verbose(
@@ -476,7 +482,7 @@ export class ClientExecutionContext extends ViewDataOracle {
476
482
  // new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
477
483
  // We don't validate or compute it in the circuit because a) it's harder to do with slices, and
478
484
  // b) this is only temporary.
479
- const newArgsHash = this.executionCache.store([
485
+ const newArgsHash = await this.executionCache.store([
480
486
  functionSelector.toField(),
481
487
  ...this.executionCache.getPreimage(argsHash),
482
488
  ]);
@@ -515,7 +521,7 @@ export class ClientExecutionContext extends ViewDataOracle {
515
521
  // new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
516
522
  // We don't validate or compute it in the circuit because a) it's harder to do with slices, and
517
523
  // b) this is only temporary.
518
- const newArgsHash = this.executionCache.store([
524
+ const newArgsHash = await this.executionCache.store([
519
525
  functionSelector.toField(),
520
526
  ...this.executionCache.getPreimage(argsHash),
521
527
  ]);
@@ -530,8 +536,8 @@ export class ClientExecutionContext extends ViewDataOracle {
530
536
  return newArgsHash;
531
537
  }
532
538
 
533
- public override notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number): void {
534
- this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
539
+ public override notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number): Promise<void> {
540
+ return this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
535
541
  }
536
542
 
537
543
  /**
@@ -541,7 +547,7 @@ export class ClientExecutionContext extends ViewDataOracle {
541
547
  * @param isStaticCall - Whether the call is a static call.
542
548
  * @returns The derived call context.
543
549
  */
544
- private deriveCallContext(
550
+ private async deriveCallContext(
545
551
  targetContractAddress: AztecAddress,
546
552
  targetArtifact: FunctionArtifact,
547
553
  isStaticCall = false,
@@ -549,7 +555,7 @@ export class ClientExecutionContext extends ViewDataOracle {
549
555
  return new CallContext(
550
556
  this.contractAddress,
551
557
  targetContractAddress,
552
- FunctionSelector.fromNameAndParameters(targetArtifact.name, targetArtifact.parameters),
558
+ await FunctionSelector.fromNameAndParameters(targetArtifact.name, targetArtifact.parameters),
553
559
  isStaticCall,
554
560
  );
555
561
  }
@@ -52,7 +52,7 @@ export class ExecutionNoteCache {
52
52
  * Enters the revertible phase of the transaction.
53
53
  * @param minRevertibleSideEffectCounter - The counter at which the transaction enters the revertible phase.
54
54
  */
55
- public setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
55
+ public async setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
56
56
  if (this.inRevertiblePhase) {
57
57
  throw new Error(
58
58
  `Cannot enter the revertible phase twice. Current counter: ${minRevertibleSideEffectCounter}. Previous enter counter: ${this.minRevertibleSideEffectCounter}`,
@@ -72,15 +72,20 @@ export class ExecutionNoteCache {
72
72
  // They cannot be squashed by nullifiers emitted after minRevertibleSideEffectCounter is set.
73
73
  // 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.
74
74
  // The nonces will be used to create the "complete" nullifier.
75
- const updatedNotes = this.notes.map(({ note, counter }, i) => {
76
- const nonce = computeNoteHashNonce(nonceGenerator, i);
77
- const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(note.contractAddress, note.noteHash));
78
- return {
79
- counter,
80
- note: { ...note, nonce },
81
- noteHashForConsumption: uniqueNoteHash,
82
- };
83
- });
75
+ const updatedNotes = await Promise.all(
76
+ this.notes.map(async ({ note, counter }, i) => {
77
+ const nonce = await computeNoteHashNonce(nonceGenerator, i);
78
+ const uniqueNoteHash = await computeUniqueNoteHash(
79
+ nonce,
80
+ await siloNoteHash(note.contractAddress, note.noteHash),
81
+ );
82
+ return {
83
+ counter,
84
+ note: { ...note, nonce },
85
+ noteHashForConsumption: uniqueNoteHash,
86
+ };
87
+ }),
88
+ );
84
89
  // Rebuild the data.
85
90
  this.notes = [];
86
91
  this.noteMap = new Map();
@@ -120,8 +125,8 @@ export class ExecutionNoteCache {
120
125
  * @param noteHash - A hash of the note. If this value equals 0, it means the note being nullified is from a previous
121
126
  * transaction (and thus not a new note).
122
127
  */
123
- public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, noteHash: Fr) {
124
- const siloedNullifier = siloNullifier(contractAddress, innerNullifier);
128
+ public async nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, noteHash: Fr) {
129
+ const siloedNullifier = await siloNullifier(contractAddress, innerNullifier);
125
130
  let nullifiedNoteHashCounter: number | undefined = undefined;
126
131
  // Find and remove the matching new note and log(s) if the emitted noteHash is not empty.
127
132
  if (!noteHash.isEmpty()) {
@@ -152,8 +157,8 @@ export class ExecutionNoteCache {
152
157
  * @param contractAddress - Contract address that emitted the nullifier.
153
158
  * @param innerNullifier
154
159
  */
155
- public nullifierCreated(contractAddress: AztecAddress, innerNullifier: Fr) {
156
- const siloedNullifier = siloNullifier(contractAddress, innerNullifier);
160
+ public async nullifierCreated(contractAddress: AztecAddress, innerNullifier: Fr) {
161
+ const siloedNullifier = await siloNullifier(contractAddress, innerNullifier);
157
162
  this.recordNullifier(contractAddress, siloedNullifier);
158
163
  }
159
164
 
@@ -84,7 +84,7 @@ interface ContainsNote {
84
84
  note: Note;
85
85
  }
86
86
 
87
- const selectPropertyFromSerializedNote = (noteData: Fr[], selector: PropertySelector): Fr => {
87
+ const selectPropertyFromPackedNoteContent = (noteData: Fr[], selector: PropertySelector): Fr => {
88
88
  const noteValueBuffer = noteData[selector.index].toBuffer();
89
89
  const noteValue = noteValueBuffer.subarray(selector.offset, selector.offset + selector.length);
90
90
  return Fr.fromBuffer(noteValue);
@@ -93,7 +93,7 @@ const selectPropertyFromSerializedNote = (noteData: Fr[], selector: PropertySele
93
93
  const selectNotes = <T extends ContainsNote>(noteDatas: T[], selects: Select[]): T[] =>
94
94
  noteDatas.filter(noteData =>
95
95
  selects.every(({ selector, value, comparator }) => {
96
- const noteValueFr = selectPropertyFromSerializedNote(noteData.note.items, selector);
96
+ const noteValueFr = selectPropertyFromPackedNoteContent(noteData.note.items, selector);
97
97
  const comparatorSelector = {
98
98
  [Comparator.EQ]: () => noteValueFr.equals(value),
99
99
  [Comparator.NEQ]: () => !noteValueFr.equals(value),
@@ -117,8 +117,8 @@ const sortNotes = (a: Fr[], b: Fr[], sorts: Sort[], level = 0): number => {
117
117
  return 0;
118
118
  }
119
119
 
120
- const aValue = selectPropertyFromSerializedNote(a, selector);
121
- const bValue = selectPropertyFromSerializedNote(b, selector);
120
+ const aValue = selectPropertyFromPackedNoteContent(a, selector);
121
+ const bValue = selectPropertyFromPackedNoteContent(b, selector);
122
122
 
123
123
  const dir = order === 1 ? [-1, 1] : [1, -1];
124
124
  return aValue.toBigInt() === bValue.toBigInt()
@@ -72,11 +72,11 @@ export class AcirSimulator {
72
72
  const callContext = new CallContext(
73
73
  msgSender,
74
74
  contractAddress,
75
- FunctionSelector.fromNameAndParameters(entryPointArtifact.name, entryPointArtifact.parameters),
75
+ await FunctionSelector.fromNameAndParameters(entryPointArtifact.name, entryPointArtifact.parameters),
76
76
  entryPointArtifact.isStatic,
77
77
  );
78
78
 
79
- const txRequestHash = request.toTxRequest().hash();
79
+ const txRequestHash = await request.toTxRequest().hash();
80
80
  const noteCache = new ExecutionNoteCache(txRequestHash);
81
81
 
82
82
  const context = new ClientExecutionContext(
@@ -192,7 +192,7 @@ export class AcirSimulator {
192
192
  const execRequest: FunctionCall = {
193
193
  name: artifact.name,
194
194
  to: contractAddress,
195
- selector: FunctionSelector.fromNameAndParameters(artifact),
195
+ selector: await FunctionSelector.fromNameAndParameters(artifact),
196
196
  type: FunctionType.UNCONSTRAINED,
197
197
  isStatic: artifact.isStatic,
198
198
  args: encodeArguments(artifact, [
@@ -230,7 +230,7 @@ export class ViewDataOracle extends TypedOracle {
230
230
  * @returns A boolean indicating whether the nullifier exists in the tree or not.
231
231
  */
232
232
  public override async checkNullifierExists(innerNullifier: Fr) {
233
- const nullifier = siloNullifier(this.contractAddress, innerNullifier!);
233
+ const nullifier = await siloNullifier(this.contractAddress, innerNullifier!);
234
234
  const index = await this.db.getNullifierIndex(nullifier);
235
235
  return index !== undefined;
236
236
  }
@@ -32,7 +32,7 @@ export class HashedValuesCache {
32
32
  if (hash.equals(Fr.ZERO)) {
33
33
  return [];
34
34
  }
35
- const hashedValues = this.cache.get(hash.value);
35
+ const hashedValues = this.cache.get(hash.toBigInt());
36
36
  if (!hashedValues) {
37
37
  throw new Error(`Preimage for hash ${hash.toString()} not found in cache`);
38
38
  }
@@ -44,12 +44,12 @@ export class HashedValuesCache {
44
44
  * @param values - The values to store.
45
45
  * @returns The hash of the values.
46
46
  */
47
- public store(values: Fr[]) {
47
+ public async store(values: Fr[]) {
48
48
  if (values.length === 0) {
49
49
  return Fr.ZERO;
50
50
  }
51
- const hashedValues = HashedValues.fromValues(values);
52
- this.cache.set(hashedValues.hash.value, hashedValues.values);
51
+ const hashedValues = await HashedValues.fromValues(values);
52
+ this.cache.set(hashedValues.hash.toBigInt(), hashedValues.values);
53
53
  return hashedValues.hash;
54
54
  }
55
55
  }
@@ -1,3 +1,4 @@
1
+ import { createLogger } from '@aztec/foundation/log';
1
2
  import { foreignCallHandler } from '@aztec/noir-protocol-circuits-types/client';
2
3
  import { type NoirCompiledCircuit } from '@aztec/types/noir';
3
4
 
@@ -10,6 +11,8 @@ import { type ACVMWitness } from '../acvm/acvm_types.js';
10
11
  import { type SimulationProvider, parseErrorPayload } from '../common/simulation_provider.js';
11
12
 
12
13
  export class WASMSimulator implements SimulationProvider {
14
+ constructor(protected log = createLogger('wasm-simulator')) {}
15
+
13
16
  async init(): Promise<void> {
14
17
  // If these are available, then we are in the
15
18
  // web environment. For the node environment, this
@@ -21,6 +24,7 @@ export class WASMSimulator implements SimulationProvider {
21
24
  }
22
25
 
23
26
  async executeProtocolCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
27
+ this.log.debug('init', { hash: compiledCircuit.hash });
24
28
  await this.init();
25
29
  // Execute the circuit on those initial witness values
26
30
  //
@@ -34,13 +38,20 @@ export class WASMSimulator implements SimulationProvider {
34
38
  input,
35
39
  foreignCallHandler, // handle calls to debug_log
36
40
  );
37
-
41
+ this.log.debug('execution successful', { hash: compiledCircuit.hash });
38
42
  return _witnessMap;
39
43
  } catch (err) {
40
44
  // Typescript types catched errors as unknown or any, so we need to narrow its type to check if it has raw assertion payload.
41
45
  if (typeof err === 'object' && err !== null && 'rawAssertionPayload' in err) {
42
- throw parseErrorPayload(compiledCircuit.abi, err as ExecutionError);
46
+ const parsed = parseErrorPayload(compiledCircuit.abi, err as ExecutionError);
47
+ this.log.debug('execution failed', {
48
+ hash: compiledCircuit.hash,
49
+ error: parsed,
50
+ message: parsed.message,
51
+ });
52
+ throw parsed;
43
53
  }
54
+ this.log.debug('execution failed', { hash: compiledCircuit.hash, error: err });
44
55
  throw new Error(`Circuit execution failed: ${err}`);
45
56
  }
46
57
  }
@@ -127,13 +127,13 @@ export interface PublicFunctionCallResult {
127
127
  functionName: string;
128
128
  }
129
129
 
130
- export function resultToPublicCallRequest(result: PublicFunctionCallResult) {
130
+ export async function resultToPublicCallRequest(result: PublicFunctionCallResult) {
131
131
  const request = result.executionRequest;
132
132
  const item = new PublicCallStackItemCompressed(
133
133
  request.callContext.contractAddress,
134
134
  request.callContext,
135
- computeVarArgsHash(request.args),
136
- computeVarArgsHash(result.returnValues),
135
+ await computeVarArgsHash(request.args),
136
+ await computeVarArgsHash(result.returnValues),
137
137
  // TODO(@just-mitch): need better mapping from simulator to revert code.
138
138
  result.reverted ? RevertCode.APP_LOGIC_REVERTED : RevertCode.OK,
139
139
  Gas.from(result.startGasLeft),
@@ -14,10 +14,10 @@ export function computeFeePayerBalanceStorageSlot(feePayer: AztecAddress) {
14
14
  /**
15
15
  * Computes the leaf slot in the public data tree for the balance of the fee payer in the Fee Juice.
16
16
  */
17
- export function computeFeePayerBalanceLeafSlot(feePayer: AztecAddress): Fr {
17
+ export async function computeFeePayerBalanceLeafSlot(feePayer: AztecAddress): Promise<Fr> {
18
18
  if (feePayer.isZero()) {
19
19
  return Fr.ZERO;
20
20
  }
21
- const balanceSlot = computeFeePayerBalanceStorageSlot(feePayer);
21
+ const balanceSlot = await computeFeePayerBalanceStorageSlot(feePayer);
22
22
  return computePublicDataTreeLeafSlot(ProtocolContractAddress.FeeJuice, balanceSlot);
23
23
  }