@aztec/simulator 0.63.0 → 0.64.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 (107) hide show
  1. package/dest/avm/avm_contract_call_result.d.ts +18 -1
  2. package/dest/avm/avm_contract_call_result.d.ts.map +1 -1
  3. package/dest/avm/avm_contract_call_result.js +30 -3
  4. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  5. package/dest/avm/avm_memory_types.js +7 -11
  6. package/dest/avm/avm_simulator.d.ts +6 -1
  7. package/dest/avm/avm_simulator.d.ts.map +1 -1
  8. package/dest/avm/avm_simulator.js +29 -8
  9. package/dest/avm/avm_tree.d.ts +4 -18
  10. package/dest/avm/avm_tree.d.ts.map +1 -1
  11. package/dest/avm/avm_tree.js +71 -53
  12. package/dest/avm/errors.d.ts +7 -0
  13. package/dest/avm/errors.d.ts.map +1 -1
  14. package/dest/avm/errors.js +11 -1
  15. package/dest/avm/fixtures/index.d.ts +2 -2
  16. package/dest/avm/fixtures/index.d.ts.map +1 -1
  17. package/dest/avm/fixtures/index.js +1 -1
  18. package/dest/avm/index.d.ts +1 -0
  19. package/dest/avm/index.d.ts.map +1 -1
  20. package/dest/avm/index.js +2 -1
  21. package/dest/avm/journal/journal.d.ts +14 -13
  22. package/dest/avm/journal/journal.d.ts.map +1 -1
  23. package/dest/avm/journal/journal.js +81 -68
  24. package/dest/avm/journal/public_storage.d.ts +0 -1
  25. package/dest/avm/journal/public_storage.d.ts.map +1 -1
  26. package/dest/avm/journal/public_storage.js +6 -6
  27. package/dest/avm/opcodes/accrued_substate.js +2 -2
  28. package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -1
  29. package/dest/avm/opcodes/addressing_mode.js +8 -2
  30. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  31. package/dest/avm/opcodes/external_calls.js +2 -4
  32. package/dest/avm/opcodes/instruction.d.ts +1 -1
  33. package/dest/avm/opcodes/instruction.d.ts.map +1 -1
  34. package/dest/avm/opcodes/instruction.js +1 -1
  35. package/dest/index.d.ts +0 -1
  36. package/dest/index.d.ts.map +1 -1
  37. package/dest/index.js +1 -2
  38. package/dest/public/dual_side_effect_trace.d.ts +3 -9
  39. package/dest/public/dual_side_effect_trace.d.ts.map +1 -1
  40. package/dest/public/dual_side_effect_trace.js +5 -11
  41. package/dest/public/enqueued_call_side_effect_trace.d.ts +4 -10
  42. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
  43. package/dest/public/enqueued_call_side_effect_trace.js +6 -15
  44. package/dest/public/executor_metrics.d.ts +1 -2
  45. package/dest/public/executor_metrics.d.ts.map +1 -1
  46. package/dest/public/executor_metrics.js +2 -8
  47. package/dest/public/fixtures/index.d.ts +14 -0
  48. package/dest/public/fixtures/index.d.ts.map +1 -0
  49. package/dest/public/fixtures/index.js +100 -0
  50. package/dest/public/index.d.ts +1 -2
  51. package/dest/public/index.d.ts.map +1 -1
  52. package/dest/public/index.js +1 -2
  53. package/dest/public/public_processor.d.ts +0 -2
  54. package/dest/public/public_processor.d.ts.map +1 -1
  55. package/dest/public/public_processor.js +5 -12
  56. package/dest/public/public_tx_context.d.ts +6 -4
  57. package/dest/public/public_tx_context.d.ts.map +1 -1
  58. package/dest/public/public_tx_context.js +11 -7
  59. package/dest/public/public_tx_simulator.d.ts +56 -5
  60. package/dest/public/public_tx_simulator.d.ts.map +1 -1
  61. package/dest/public/public_tx_simulator.js +106 -26
  62. package/dest/public/side_effect_trace.d.ts +4 -10
  63. package/dest/public/side_effect_trace.d.ts.map +1 -1
  64. package/dest/public/side_effect_trace.js +6 -15
  65. package/dest/public/side_effect_trace_interface.d.ts +3 -9
  66. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  67. package/dest/public/transitional_adapters.d.ts +1 -2
  68. package/dest/public/transitional_adapters.d.ts.map +1 -1
  69. package/dest/public/transitional_adapters.js +8 -10
  70. package/package.json +10 -10
  71. package/src/avm/avm_contract_call_result.ts +39 -2
  72. package/src/avm/avm_memory_types.ts +6 -10
  73. package/src/avm/avm_simulator.ts +58 -8
  74. package/src/avm/avm_tree.ts +88 -64
  75. package/src/avm/errors.ts +11 -0
  76. package/src/avm/fixtures/index.ts +4 -3
  77. package/src/avm/index.ts +1 -0
  78. package/src/avm/journal/journal.ts +118 -126
  79. package/src/avm/journal/public_storage.ts +5 -6
  80. package/src/avm/opcodes/accrued_substate.ts +1 -1
  81. package/src/avm/opcodes/addressing_mode.ts +7 -2
  82. package/src/avm/opcodes/external_calls.ts +1 -3
  83. package/src/avm/opcodes/instruction.ts +1 -1
  84. package/src/index.ts +0 -1
  85. package/src/public/dual_side_effect_trace.ts +4 -13
  86. package/src/public/enqueued_call_side_effect_trace.ts +14 -17
  87. package/src/public/executor_metrics.ts +1 -9
  88. package/src/public/fixtures/index.ts +158 -0
  89. package/src/public/index.ts +1 -5
  90. package/src/public/public_processor.ts +5 -27
  91. package/src/public/public_tx_context.ts +13 -8
  92. package/src/public/public_tx_simulator.ts +182 -50
  93. package/src/public/side_effect_trace.ts +9 -19
  94. package/src/public/side_effect_trace_interface.ts +3 -9
  95. package/src/public/transitional_adapters.ts +12 -12
  96. package/dest/mocks/fixtures.d.ts +0 -28
  97. package/dest/mocks/fixtures.d.ts.map +0 -1
  98. package/dest/mocks/fixtures.js +0 -48
  99. package/dest/mocks/index.d.ts +0 -2
  100. package/dest/mocks/index.d.ts.map +0 -1
  101. package/dest/mocks/index.js +0 -2
  102. package/dest/public/executor.d.ts +0 -47
  103. package/dest/public/executor.d.ts.map +0 -1
  104. package/dest/public/executor.js +0 -90
  105. package/src/mocks/fixtures.ts +0 -72
  106. package/src/mocks/index.ts +0 -1
  107. package/src/public/executor.ts +0 -154
@@ -1,18 +1,16 @@
1
- import { type IndexedTreeId, MerkleTreeId, type MerkleTreeWriteOperations } from '@aztec/circuit-types';
1
+ import { MerkleTreeId } from '@aztec/circuit-types';
2
2
  import {
3
3
  type AztecAddress,
4
4
  type Gas,
5
5
  type NullifierLeafPreimage,
6
6
  type PublicCallRequest,
7
- PublicDataTreeLeaf,
8
- PublicDataTreeLeafPreimage,
7
+ type PublicDataTreeLeafPreimage,
9
8
  SerializableContractInstance,
10
9
  computePublicBytecodeCommitment,
11
10
  } from '@aztec/circuits.js';
12
11
  import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
13
12
  import { Fr } from '@aztec/foundation/fields';
14
13
  import { createDebugLogger } from '@aztec/foundation/log';
15
- import { type IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
16
14
 
17
15
  import assert from 'assert';
18
16
 
@@ -21,7 +19,8 @@ import { type WorldStateDB } from '../../public/public_db_sources.js';
21
19
  import { type PublicSideEffectTraceInterface } from '../../public/side_effect_trace_interface.js';
22
20
  import { type AvmContractCallResult } from '../avm_contract_call_result.js';
23
21
  import { type AvmExecutionEnvironment } from '../avm_execution_environment.js';
24
- import { NullifierManager } from './nullifiers.js';
22
+ import { AvmEphemeralForest } from '../avm_tree.js';
23
+ import { NullifierCollisionError, NullifierManager } from './nullifiers.js';
25
24
  import { PublicStorage } from './public_storage.js';
26
25
 
27
26
  /**
@@ -36,9 +35,6 @@ import { PublicStorage } from './public_storage.js';
36
35
  export class AvmPersistableStateManager {
37
36
  private readonly log = createDebugLogger('aztec:avm_simulator:state_manager');
38
37
 
39
- /** Interface to perform merkle tree operations */
40
- public merkleTrees: MerkleTreeWriteOperations;
41
-
42
38
  /** Make sure a forked state is never merged twice. */
43
39
  private alreadyMergedIntoParent = false;
44
40
 
@@ -53,29 +49,43 @@ export class AvmPersistableStateManager {
53
49
  /** Nullifier set, including cached/recently-emitted nullifiers */
54
50
  private readonly nullifiers: NullifierManager = new NullifierManager(worldStateDB),
55
51
  private readonly doMerkleOperations: boolean = false,
56
- merkleTrees?: MerkleTreeWriteOperations,
57
- ) {
58
- if (merkleTrees) {
59
- this.merkleTrees = merkleTrees;
60
- } else {
61
- this.merkleTrees = worldStateDB.getMerkleInterface();
62
- }
63
- }
52
+ /** Ephmeral forest for merkle tree operations */
53
+ public readonly merkleTrees: AvmEphemeralForest,
54
+ ) {}
64
55
 
65
56
  /**
66
57
  * Create a new state manager with some preloaded pending siloed nullifiers
67
58
  */
68
- public static newWithPendingSiloedNullifiers(
59
+ public static async newWithPendingSiloedNullifiers(
69
60
  worldStateDB: WorldStateDB,
70
61
  trace: PublicSideEffectTraceInterface,
71
62
  pendingSiloedNullifiers: Fr[],
63
+ doMerkleOperations: boolean = false,
72
64
  ) {
73
65
  const parentNullifiers = NullifierManager.newWithPendingSiloedNullifiers(worldStateDB, pendingSiloedNullifiers);
66
+ const ephemeralForest = await AvmEphemeralForest.create(worldStateDB.getMerkleInterface());
74
67
  return new AvmPersistableStateManager(
75
68
  worldStateDB,
76
69
  trace,
77
70
  /*publicStorage=*/ new PublicStorage(worldStateDB),
78
71
  /*nullifiers=*/ parentNullifiers.fork(),
72
+ doMerkleOperations,
73
+ ephemeralForest,
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Create a new state manager
79
+ */
80
+ public static async create(worldStateDB: WorldStateDB, trace: PublicSideEffectTraceInterface) {
81
+ const ephemeralForest = await AvmEphemeralForest.create(worldStateDB.getMerkleInterface());
82
+ return new AvmPersistableStateManager(
83
+ worldStateDB,
84
+ trace,
85
+ /*publicStorage=*/ new PublicStorage(worldStateDB),
86
+ /*nullifiers=*/ new NullifierManager(worldStateDB),
87
+ /*doMerkleOperations=*/ true,
88
+ ephemeralForest,
79
89
  );
80
90
  }
81
91
 
@@ -89,6 +99,7 @@ export class AvmPersistableStateManager {
89
99
  this.publicStorage.fork(),
90
100
  this.nullifiers.fork(),
91
101
  this.doMerkleOperations,
102
+ this.merkleTrees.fork(),
92
103
  );
93
104
  }
94
105
 
@@ -116,7 +127,11 @@ export class AvmPersistableStateManager {
116
127
 
117
128
  private _merge(forkedState: AvmPersistableStateManager, reverted: boolean) {
118
129
  // sanity check to avoid merging the same forked trace twice
119
- assert(!this.alreadyMergedIntoParent, 'Cannot merge forked state that has already been merged into its parent!');
130
+ assert(
131
+ !forkedState.alreadyMergedIntoParent,
132
+ 'Cannot merge forked state that has already been merged into its parent!',
133
+ );
134
+ forkedState.alreadyMergedIntoParent = true;
120
135
  this.publicStorage.acceptAndMerge(forkedState.publicStorage);
121
136
  this.nullifiers.acceptAndMerge(forkedState.nullifiers);
122
137
  this.trace.merge(forkedState.trace, reverted);
@@ -135,27 +150,18 @@ export class AvmPersistableStateManager {
135
150
  this.publicStorage.write(contractAddress, slot, value);
136
151
  const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
137
152
  if (this.doMerkleOperations) {
138
- const result = await this.merkleTrees.batchInsert(
139
- MerkleTreeId.PUBLIC_DATA_TREE,
140
- [new PublicDataTreeLeaf(leafSlot, value).toBuffer()],
141
- 0,
142
- );
153
+ const result = await this.merkleTrees.writePublicStorage(leafSlot, value);
143
154
  assert(result !== undefined, 'Public data tree insertion error. You might want to disable skipMerkleOperations.');
144
155
  this.log.debug(`Inserted public data tree leaf at leafSlot ${leafSlot}, value: ${value}`);
145
156
 
146
- const lowLeafInfo = result.lowLeavesWitnessData![0];
147
- const lowLeafPreimage = lowLeafInfo.leafPreimage as PublicDataTreeLeafPreimage;
157
+ const lowLeafInfo = result.lowWitness;
158
+ const lowLeafPreimage = result.lowWitness.preimage as PublicDataTreeLeafPreimage;
148
159
  const lowLeafIndex = lowLeafInfo.index;
149
- const lowLeafPath = lowLeafInfo.siblingPath.toFields();
160
+ const lowLeafPath = lowLeafInfo.siblingPath;
161
+
162
+ const insertionPath = result.insertionPath;
163
+ const newLeafPreimage = result.newOrElementToUpdate.element as PublicDataTreeLeafPreimage;
150
164
 
151
- const insertionPath = result.newSubtreeSiblingPath.toFields();
152
- const newLeafPreimage = new PublicDataTreeLeafPreimage(
153
- leafSlot,
154
- value,
155
- lowLeafPreimage.nextSlot,
156
- lowLeafPreimage.nextIndex,
157
- );
158
- // FIXME: Why do we need to hint both preimages for public data writes, but not for nullifier insertions?
159
165
  this.trace.tracePublicStorageWrite(
160
166
  contractAddress,
161
167
  slot,
@@ -179,23 +185,24 @@ export class AvmPersistableStateManager {
179
185
  * @returns the latest value written to slot, or 0 if never written to before
180
186
  */
181
187
  public async readStorage(contractAddress: AztecAddress, slot: Fr): Promise<Fr> {
182
- const { value, exists, cached } = await this.publicStorage.read(contractAddress, slot);
183
- this.log.debug(
184
- `Storage read (address=${contractAddress}, slot=${slot}): value=${value}, exists=${exists}, cached=${cached}`,
185
- );
188
+ const { value, cached } = await this.publicStorage.read(contractAddress, slot);
189
+ this.log.debug(`Storage read (address=${contractAddress}, slot=${slot}): value=${value}, cached=${cached}`);
186
190
 
187
191
  const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
188
192
 
189
193
  if (this.doMerkleOperations) {
190
194
  // Get leaf if present, low leaf if absent
191
195
  // If leaf is present, hint/trace it. Otherwise, hint/trace the low leaf.
192
- const [leafIndex, leafPreimage, leafPath, _alreadyPresent] = await getLeafOrLowLeaf<PublicDataTreeLeafPreimage>(
193
- MerkleTreeId.PUBLIC_DATA_TREE,
194
- leafSlot.toBigInt(),
195
- this.merkleTrees,
196
- );
197
- // FIXME: cannot have this assertion until "caching" is done via ephemeral merkle writes
198
- //assert(alreadyPresent == exists, 'WorldStateDB contains public data leaf, but merkle tree does not.... This is a bug!');
196
+ const {
197
+ preimage,
198
+ index: leafIndex,
199
+ update: exists,
200
+ } = await this.merkleTrees.getLeafOrLowLeafInfo(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot);
201
+ // The index and preimage here is either the low leaf or the leaf itself (depending on the value of update flag)
202
+ // In either case, we just want the sibling path to this leaf - it's up to the avm to distinguish if it's a low leaf or not
203
+ const leafPath = await this.merkleTrees.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
204
+ const leafPreimage = preimage as PublicDataTreeLeafPreimage;
205
+
199
206
  this.log.debug(
200
207
  `leafPreimage.nextSlot: ${leafPreimage.nextSlot}, leafPreimage.nextIndex: ${Number(leafPreimage.nextIndex)}`,
201
208
  );
@@ -204,15 +211,16 @@ export class AvmPersistableStateManager {
204
211
  if (!exists) {
205
212
  // Sanity check that the leaf slot is skipped by low leaf when it doesn't exist
206
213
  assert(
207
- leafSlot.toBigInt() > leafPreimage.slot.toBigInt() && leafSlot.toBigInt() < leafPreimage.nextSlot.toBigInt(),
208
- 'Public data tree low leaf should skip the target leaf slot when the target leaf does not exist.',
214
+ leafPreimage.slot.toBigInt() < leafSlot.toBigInt() &&
215
+ (leafPreimage.nextIndex === 0n || leafPreimage.nextSlot.toBigInt() > leafSlot.toBigInt()),
216
+ 'Public data tree low leaf should skip the target leaf slot when the target leaf does not exist or is the max value.',
209
217
  );
210
218
  }
211
219
  this.log.debug(
212
220
  `Tracing storage leaf preimage slot=${slot}, leafSlot=${leafSlot}, value=${value}, nextKey=${leafPreimage.nextSlot}, nextIndex=${leafPreimage.nextIndex}`,
213
221
  );
214
222
  // On non-existence, AVM circuit will need to recognize that leafPreimage.slot != leafSlot,
215
- // prove that this is a low leaf that skips leafSlot, and then prove memebership of the leaf.
223
+ // prove that this is a low leaf that skips leafSlot, and then prove membership of the leaf.
216
224
  this.trace.tracePublicStorageRead(contractAddress, slot, value, leafPreimage, new Fr(leafIndex), leafPath);
217
225
  } else {
218
226
  this.trace.tracePublicStorageRead(contractAddress, slot, value);
@@ -229,10 +237,8 @@ export class AvmPersistableStateManager {
229
237
  * @returns the latest value written to slot, or 0 if never written to before
230
238
  */
231
239
  public async peekStorage(contractAddress: AztecAddress, slot: Fr): Promise<Fr> {
232
- const { value, exists, cached } = await this.publicStorage.read(contractAddress, slot);
233
- this.log.debug(
234
- `Storage peek (address=${contractAddress}, slot=${slot}): value=${value}, exists=${exists}, cached=${cached}`,
235
- );
240
+ const { value, cached } = await this.publicStorage.read(contractAddress, slot);
241
+ this.log.debug(`Storage peek (address=${contractAddress}, slot=${slot}): value=${value}, cached=${cached}`);
236
242
  return Promise.resolve(value);
237
243
  }
238
244
 
@@ -255,7 +261,7 @@ export class AvmPersistableStateManager {
255
261
  // TODO(8287): We still return exists here, but we need to transmit both the requested noteHash and the gotLeafValue
256
262
  // such that the VM can constrain the equality and decide on exists based on that.
257
263
  const path = await this.merkleTrees.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex.toBigInt());
258
- this.trace.traceNoteHashCheck(contractAddress, gotLeafValue, leafIndex, exists, path.toFields());
264
+ this.trace.traceNoteHashCheck(contractAddress, gotLeafValue, leafIndex, exists, path);
259
265
  } else {
260
266
  this.trace.traceNoteHashCheck(contractAddress, gotLeafValue, leafIndex, exists);
261
267
  }
@@ -266,19 +272,15 @@ export class AvmPersistableStateManager {
266
272
  * Write a note hash, trace the write.
267
273
  * @param noteHash - the unsiloed note hash to write
268
274
  */
269
- public async writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): Promise<void> {
275
+ public writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): void {
270
276
  this.log.debug(`noteHashes(${contractAddress}) += @${noteHash}.`);
271
277
 
272
278
  if (this.doMerkleOperations) {
273
- // TODO: We should track this globally here in the state manager
274
- const info = await this.merkleTrees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE);
275
- const leafIndex = new Fr(info.size + 1n);
276
-
277
- const path = await this.merkleTrees.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex.toBigInt());
279
+ // Should write a helper for this
280
+ const leafIndex = new Fr(this.merkleTrees.treeMap.get(MerkleTreeId.NOTE_HASH_TREE)!.leafCount);
278
281
  const siloedNoteHash = siloNoteHash(contractAddress, noteHash);
279
-
280
- await this.merkleTrees.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [siloedNoteHash]);
281
- this.trace.traceNewNoteHash(contractAddress, noteHash, leafIndex, path.toFields());
282
+ const insertionPath = this.merkleTrees.appendNoteHash(siloedNoteHash);
283
+ this.trace.traceNewNoteHash(contractAddress, noteHash, leafIndex, insertionPath);
282
284
  } else {
283
285
  this.trace.traceNewNoteHash(contractAddress, noteHash);
284
286
  }
@@ -298,15 +300,15 @@ export class AvmPersistableStateManager {
298
300
  if (this.doMerkleOperations) {
299
301
  // Get leaf if present, low leaf if absent
300
302
  // If leaf is present, hint/trace it. Otherwise, hint/trace the low leaf.
301
- const [leafIndex, leafPreimage, leafPath, alreadyPresent] = await getLeafOrLowLeaf<NullifierLeafPreimage>(
302
- MerkleTreeId.NULLIFIER_TREE,
303
- siloedNullifier.toBigInt(),
304
- this.merkleTrees,
305
- );
306
- assert(
307
- alreadyPresent == exists,
308
- 'WorldStateDB contains nullifier leaf, but merkle tree does not.... This is a bug!',
309
- );
303
+ const {
304
+ preimage,
305
+ index: leafIndex,
306
+ update,
307
+ } = await this.merkleTrees.getLeafOrLowLeafInfo(MerkleTreeId.NULLIFIER_TREE, siloedNullifier);
308
+ const leafPreimage = preimage as NullifierLeafPreimage;
309
+ const leafPath = await this.merkleTrees.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex);
310
+
311
+ assert(update == exists, 'WorldStateDB contains nullifier leaf, but merkle tree does not.... This is a bug!');
310
312
 
311
313
  this.log.debug(
312
314
  `nullifiers(${contractAddress})@${nullifier} ?? leafIndex: ${leafIndex}, exists: ${exists}, pending: ${isPending}.`,
@@ -346,40 +348,55 @@ export class AvmPersistableStateManager {
346
348
  */
347
349
  public async writeNullifier(contractAddress: AztecAddress, nullifier: Fr) {
348
350
  this.log.debug(`nullifiers(${contractAddress}) += ${nullifier}.`);
349
- // Cache pending nullifiers for later access
350
- await this.nullifiers.append(contractAddress, nullifier);
351
351
 
352
352
  const siloedNullifier = siloNullifier(contractAddress, nullifier);
353
353
 
354
354
  if (this.doMerkleOperations) {
355
+ // Maybe overkill, but we should check if the nullifier is already present in the tree before attempting to insert
356
+ // It might be better to catch the error from the insert operation
355
357
  // Trace all nullifier creations, even duplicate insertions that fail
356
- const alreadyPresent = await this.merkleTrees.getPreviousValueIndex(
358
+ const { preimage, index, update } = await this.merkleTrees.getLeafOrLowLeafInfo(
357
359
  MerkleTreeId.NULLIFIER_TREE,
358
- siloedNullifier.toBigInt(),
360
+ siloedNullifier,
359
361
  );
360
- if (alreadyPresent) {
361
- this.log.verbose(`Nullifier already present in tree: ${nullifier} at index ${alreadyPresent.index}.`);
362
+ if (update) {
363
+ this.log.verbose(`Nullifier already present in tree: ${nullifier} at index ${index}.`);
364
+ // If the nullifier is already present, we should not insert it again
365
+ // instead we provide the direct membership path
366
+ const path = await this.merkleTrees.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, index);
367
+ // This just becomes a nullifier read hint
368
+ this.trace.traceNullifierCheck(
369
+ contractAddress,
370
+ nullifier,
371
+ /*exists=*/ update,
372
+ preimage as NullifierLeafPreimage,
373
+ new Fr(index),
374
+ path,
375
+ );
376
+ throw new NullifierCollisionError(
377
+ `Nullifier ${nullifier} at contract ${contractAddress} already exists in parent cache or host.`,
378
+ );
379
+ } else {
380
+ // Cache pending nullifiers for later access
381
+ await this.nullifiers.append(contractAddress, nullifier);
382
+ // We append the new nullifier
383
+ const appendResult = await this.merkleTrees.appendNullifier(siloedNullifier);
384
+ const lowLeafPreimage = appendResult.lowWitness.preimage as NullifierLeafPreimage;
385
+ const lowLeafIndex = appendResult.lowWitness.index;
386
+ const lowLeafPath = appendResult.lowWitness.siblingPath;
387
+ const insertionPath = appendResult.insertionPath;
388
+ this.trace.traceNewNullifier(
389
+ contractAddress,
390
+ nullifier,
391
+ lowLeafPreimage,
392
+ new Fr(lowLeafIndex),
393
+ lowLeafPath,
394
+ insertionPath,
395
+ );
362
396
  }
363
- const insertionResult = await this.merkleTrees.batchInsert(
364
- MerkleTreeId.NULLIFIER_TREE,
365
- [siloedNullifier.toBuffer()],
366
- 0,
367
- );
368
- const lowLeafInfo = insertionResult.lowLeavesWitnessData![0];
369
- const lowLeafPreimage = lowLeafInfo.leafPreimage as NullifierLeafPreimage;
370
- const lowLeafIndex = lowLeafInfo.index;
371
- const lowLeafPath = lowLeafInfo.siblingPath.toFields();
372
- const insertionPath = insertionResult.newSubtreeSiblingPath.toFields();
373
-
374
- this.trace.traceNewNullifier(
375
- contractAddress,
376
- nullifier,
377
- lowLeafPreimage,
378
- new Fr(lowLeafIndex),
379
- lowLeafPath,
380
- insertionPath,
381
- );
382
397
  } else {
398
+ // Cache pending nullifiers for later access
399
+ await this.nullifiers.append(contractAddress, nullifier);
383
400
  this.trace.traceNewNullifier(contractAddress, nullifier);
384
401
  }
385
402
  }
@@ -404,7 +421,11 @@ export class AvmPersistableStateManager {
404
421
  if (this.doMerkleOperations) {
405
422
  // TODO(8287): We still return exists here, but we need to transmit both the requested msgHash and the value
406
423
  // such that the VM can constrain the equality and decide on exists based on that.
407
- const path = await this.merkleTrees.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, msgLeafIndex.toBigInt());
424
+ // We should defintely add a helper here
425
+ const path = await this.merkleTrees.treeDb.getSiblingPath(
426
+ MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
427
+ msgLeafIndex.toBigInt(),
428
+ );
408
429
  this.trace.traceL1ToL2MessageCheck(contractAddress, valueAtIndex, msgLeafIndex, exists, path.toFields());
409
430
  } else {
410
431
  this.trace.traceL1ToL2MessageCheck(contractAddress, valueAtIndex, msgLeafIndex, exists);
@@ -504,7 +525,6 @@ export class AvmPersistableStateManager {
504
525
  forkedState: AvmPersistableStateManager,
505
526
  nestedEnvironment: AvmExecutionEnvironment,
506
527
  startGasLeft: Gas,
507
- endGasLeft: Gas,
508
528
  bytecode: Buffer,
509
529
  avmCallResults: AvmContractCallResult,
510
530
  ) {
@@ -521,7 +541,6 @@ export class AvmPersistableStateManager {
521
541
  forkedState.trace,
522
542
  nestedEnvironment,
523
543
  startGasLeft,
524
- endGasLeft,
525
544
  bytecode,
526
545
  avmCallResults,
527
546
  functionName,
@@ -532,30 +551,3 @@ export class AvmPersistableStateManager {
532
551
  this.trace.traceEnqueuedCall(publicCallRequest, calldata, reverted);
533
552
  }
534
553
  }
535
-
536
- /**
537
- * Get leaf if present, low leaf if absent
538
- */
539
- export async function getLeafOrLowLeaf<TreePreimageType extends IndexedTreeLeafPreimage>(
540
- treeId: IndexedTreeId,
541
- key: bigint,
542
- merkleTrees: MerkleTreeWriteOperations,
543
- ) {
544
- // "key" is siloed slot (leafSlot) or siloed nullifier
545
- const previousValueIndex = await merkleTrees.getPreviousValueIndex(treeId, key);
546
- assert(
547
- previousValueIndex !== undefined,
548
- `${MerkleTreeId[treeId]} low leaf index should always be found (even if target leaf does not exist)`,
549
- );
550
- const { index: leafIndex, alreadyPresent } = previousValueIndex;
551
-
552
- const leafPreimage = await merkleTrees.getLeafPreimage(treeId, leafIndex);
553
- assert(
554
- leafPreimage !== undefined,
555
- `${MerkleTreeId[treeId]} low leaf preimage should never be undefined (even if target leaf does not exist)`,
556
- );
557
-
558
- const leafPath = await merkleTrees.getSiblingPath(treeId, leafIndex);
559
-
560
- return [leafIndex, leafPreimage as TreePreimageType, leafPath.toFields(), alreadyPresent] as const;
561
- }
@@ -5,7 +5,6 @@ import type { PublicStateDB } from '../../index.js';
5
5
 
6
6
  type PublicStorageReadResult = {
7
7
  value: Fr;
8
- exists: boolean;
9
8
  cached: boolean;
10
9
  };
11
10
 
@@ -77,17 +76,17 @@ export class PublicStorage {
77
76
  let value = this.readHereOrParent(contractAddress, slot);
78
77
  // Finally try the host's Aztec state (a trip to the database)
79
78
  if (!value) {
80
- value = await this.hostPublicStorage.storageRead(contractAddress, slot);
79
+ // This functions returns Fr.ZERO if it has never been written to before
80
+ // we explicity coalesce to Fr.ZERO in case we have some implementations that cause this to return undefined
81
+ value = (await this.hostPublicStorage.storageRead(contractAddress, slot)) ?? Fr.ZERO;
81
82
  // TODO(dbanks12): if value retrieved from host storage, we can cache it here
82
83
  // any future reads to the same slot can read from cache instead of more expensive
83
84
  // DB access
84
85
  } else {
85
86
  cached = true;
86
87
  }
87
- // if value is undefined, that means this slot has never been written to!
88
- const exists = value !== undefined;
89
- const valueOrZero = exists ? value : Fr.ZERO;
90
- return Promise.resolve({ value: valueOrZero, exists, cached });
88
+ // if value is Fr.ZERO here, it that means this slot has never been written to!
89
+ return Promise.resolve({ value, cached });
91
90
  }
92
91
 
93
92
  /**
@@ -70,7 +70,7 @@ export class EmitNoteHash extends Instruction {
70
70
  }
71
71
 
72
72
  const noteHash = memory.get(noteHashOffset).toFr();
73
- await context.persistableState.writeNoteHash(context.environment.address, noteHash);
73
+ context.persistableState.writeNoteHash(context.environment.address, noteHash);
74
74
 
75
75
  memory.assert({ reads: 1, addressing });
76
76
  }
@@ -1,6 +1,7 @@
1
1
  import { strict as assert } from 'assert';
2
2
 
3
- import { type TaggedMemoryInterface } from '../avm_memory_types.js';
3
+ import { TaggedMemory, type TaggedMemoryInterface } from '../avm_memory_types.js';
4
+ import { AddressOutOfRangeError } from '../errors.js';
4
5
 
5
6
  export enum AddressingMode {
6
7
  DIRECT = 0,
@@ -63,7 +64,11 @@ export class Addressing {
63
64
  resolved[i] = offset;
64
65
  if (mode & AddressingMode.RELATIVE) {
65
66
  mem.checkIsValidMemoryOffsetTag(0);
66
- resolved[i] += Number(mem.get(0).toBigInt());
67
+ const baseAddr = Number(mem.get(0).toBigInt());
68
+ resolved[i] += baseAddr;
69
+ if (resolved[i] >= TaggedMemory.MAX_MEMORY_SIZE) {
70
+ throw new AddressOutOfRangeError(baseAddr, offset);
71
+ }
67
72
  }
68
73
  if (mode & AddressingMode.INDIRECT) {
69
74
  mem.checkIsValidMemoryOffsetTag(resolved[i]);
@@ -2,7 +2,6 @@ import { Fr, FunctionSelector, Gas, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circ
2
2
 
3
3
  import type { AvmContext } from '../avm_context.js';
4
4
  import { type AvmContractCallResult } from '../avm_contract_call_result.js';
5
- import { gasLeftToGas } from '../avm_gas.js';
6
5
  import { type Field, TypeTag, Uint1 } from '../avm_memory_types.js';
7
6
  import { AvmSimulator } from '../avm_simulator.js';
8
7
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
@@ -95,7 +94,7 @@ abstract class ExternalCall extends Instruction {
95
94
  memory.set(successOffset, new Uint1(success ? 1 : 0));
96
95
 
97
96
  // Refund unused gas
98
- context.machineState.refundGas(gasLeftToGas(nestedContext.machineState));
97
+ context.machineState.refundGas(nestedCallResults.gasLeft);
99
98
 
100
99
  // Merge nested call's state and trace based on whether it succeeded.
101
100
  if (success) {
@@ -107,7 +106,6 @@ abstract class ExternalCall extends Instruction {
107
106
  /*nestedState=*/ nestedContext.persistableState,
108
107
  /*nestedEnvironment=*/ nestedContext.environment,
109
108
  /*startGasLeft=*/ Gas.from(allocatedGas),
110
- /*endGasLeft=*/ Gas.from(nestedContext.machineState.gasLeft),
111
109
  /*bytecode=*/ simulator.getBytecode()!,
112
110
  /*avmCallResults=*/ nestedCallResults,
113
111
  );
@@ -94,7 +94,7 @@ export abstract class Instruction {
94
94
  * Computes gas cost for the instruction based on its base cost and memory operations.
95
95
  * @returns Gas cost.
96
96
  */
97
- protected gasCost(dynMultiplier: number = 0): Gas {
97
+ public gasCost(dynMultiplier: number = 0): Gas {
98
98
  const baseGasCost = getBaseGasCost(this.opcode);
99
99
  const dynGasCost = mulGas(getDynamicGasCost(this.opcode), dynMultiplier);
100
100
  return sumGas(baseGasCost, dynGasCost);
package/src/index.ts CHANGED
@@ -4,5 +4,4 @@ export * from './client/index.js';
4
4
  export * from './common/index.js';
5
5
  export * from './public/index.js';
6
6
  export * from './providers/index.js';
7
- export * from './mocks/index.js';
8
7
  export * from './stats/index.js';
@@ -12,7 +12,7 @@ import { type Fr } from '@aztec/foundation/fields';
12
12
 
13
13
  import { assert } from 'console';
14
14
 
15
- import { type AvmContractCallResult } from '../avm/avm_contract_call_result.js';
15
+ import { type AvmContractCallResult, type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js';
16
16
  import { type AvmExecutionEnvironment } from '../avm/avm_execution_environment.js';
17
17
  import { type PublicEnqueuedCallSideEffectTrace } from './enqueued_call_side_effect_trace.js';
18
18
  import { type EnqueuedPublicCallExecutionResultWithSideEffects, type PublicFunctionCallResult } from './execution.js';
@@ -191,8 +191,6 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface {
191
191
  nestedEnvironment: AvmExecutionEnvironment,
192
192
  /** How much gas was available for this public execution. */
193
193
  startGasLeft: Gas,
194
- /** How much gas was left after this public execution. */
195
- endGasLeft: Gas,
196
194
  /** Bytecode used for this execution. */
197
195
  bytecode: Buffer,
198
196
  /** The call's results */
@@ -204,7 +202,6 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface {
204
202
  nestedCallTrace.innerCallTrace,
205
203
  nestedEnvironment,
206
204
  startGasLeft,
207
- endGasLeft,
208
205
  bytecode,
209
206
  avmCallResults,
210
207
  functionName,
@@ -213,7 +210,6 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface {
213
210
  nestedCallTrace.enqueuedCallTrace,
214
211
  nestedEnvironment,
215
212
  startGasLeft,
216
- endGasLeft,
217
213
  bytecode,
218
214
  avmCallResults,
219
215
  functionName,
@@ -235,12 +231,10 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface {
235
231
  * Convert this trace to a PublicExecutionResult for use externally to the simulator.
236
232
  */
237
233
  public toPublicEnqueuedCallExecutionResult(
238
- /** How much gas was left after this public execution. */
239
- endGasLeft: Gas,
240
234
  /** The call's results */
241
- avmCallResults: AvmContractCallResult,
235
+ avmCallResults: AvmFinalizedCallResult,
242
236
  ): EnqueuedPublicCallExecutionResultWithSideEffects {
243
- return this.enqueuedCallTrace.toPublicEnqueuedCallExecutionResult(endGasLeft, avmCallResults);
237
+ return this.enqueuedCallTrace.toPublicEnqueuedCallExecutionResult(avmCallResults);
244
238
  }
245
239
  /**
246
240
  * Convert this trace to a PublicExecutionResult for use externally to the simulator.
@@ -250,19 +244,16 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface {
250
244
  avmEnvironment: AvmExecutionEnvironment,
251
245
  /** How much gas was available for this public execution. */
252
246
  startGasLeft: Gas,
253
- /** How much gas was left after this public execution. */
254
- endGasLeft: Gas,
255
247
  /** Bytecode used for this execution. */
256
248
  bytecode: Buffer,
257
249
  /** The call's results */
258
- avmCallResults: AvmContractCallResult,
250
+ avmCallResults: AvmFinalizedCallResult,
259
251
  /** Function name for logging */
260
252
  functionName: string = 'unknown',
261
253
  ): PublicFunctionCallResult {
262
254
  return this.innerCallTrace.toPublicFunctionCallResult(
263
255
  avmEnvironment,
264
256
  startGasLeft,
265
- endGasLeft,
266
257
  bytecode,
267
258
  avmCallResults,
268
259
  functionName,