@aztec/simulator 0.36.0 → 0.38.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 (118) hide show
  1. package/dest/acvm/acvm.d.ts +1 -1
  2. package/dest/acvm/acvm.d.ts.map +1 -1
  3. package/dest/acvm/acvm.js +2 -2
  4. package/dest/acvm/oracle/oracle.d.ts +6 -4
  5. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/oracle.js +39 -8
  7. package/dest/acvm/oracle/typed_oracle.d.ts +5 -3
  8. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  9. package/dest/acvm/oracle/typed_oracle.js +9 -3
  10. package/dest/avm/avm_gas.d.ts.map +1 -1
  11. package/dest/avm/avm_gas.js +2 -1
  12. package/dest/avm/avm_memory_types.d.ts +1 -1
  13. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  14. package/dest/avm/avm_simulator.d.ts.map +1 -1
  15. package/dest/avm/avm_simulator.js +3 -1
  16. package/dest/avm/journal/journal.d.ts +20 -1
  17. package/dest/avm/journal/journal.d.ts.map +1 -1
  18. package/dest/avm/journal/journal.js +69 -9
  19. package/dest/avm/journal/nullifiers.d.ts +3 -1
  20. package/dest/avm/journal/nullifiers.d.ts.map +1 -1
  21. package/dest/avm/journal/nullifiers.js +14 -6
  22. package/dest/avm/journal/public_storage.d.ts +10 -1
  23. package/dest/avm/journal/public_storage.d.ts.map +1 -1
  24. package/dest/avm/journal/public_storage.js +17 -2
  25. package/dest/avm/journal/trace.d.ts +1 -4
  26. package/dest/avm/journal/trace.d.ts.map +1 -1
  27. package/dest/avm/journal/trace.js +4 -5
  28. package/dest/avm/journal/trace_types.d.ts +1 -0
  29. package/dest/avm/journal/trace_types.d.ts.map +1 -1
  30. package/dest/avm/journal/trace_types.js +1 -1
  31. package/dest/avm/opcodes/bitwise.d.ts +4 -1
  32. package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
  33. package/dest/avm/opcodes/bitwise.js +14 -2
  34. package/dest/avm/opcodes/environment_getters.d.ts +5 -0
  35. package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
  36. package/dest/avm/opcodes/environment_getters.js +8 -1
  37. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  38. package/dest/avm/opcodes/external_calls.js +14 -13
  39. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  40. package/dest/avm/serialization/bytecode_serialization.js +3 -2
  41. package/dest/avm/serialization/instruction_serialization.d.ts +39 -38
  42. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  43. package/dest/avm/serialization/instruction_serialization.js +40 -39
  44. package/dest/client/client_execution_context.d.ts +31 -18
  45. package/dest/client/client_execution_context.d.ts.map +1 -1
  46. package/dest/client/client_execution_context.js +48 -31
  47. package/dest/client/db_oracle.d.ts +11 -4
  48. package/dest/client/db_oracle.d.ts.map +1 -1
  49. package/dest/client/execution_result.d.ts +19 -15
  50. package/dest/client/execution_result.d.ts.map +1 -1
  51. package/dest/client/execution_result.js +45 -12
  52. package/dest/client/logs_cache.d.ts +33 -0
  53. package/dest/client/logs_cache.d.ts.map +1 -0
  54. package/dest/client/logs_cache.js +59 -0
  55. package/dest/client/private_execution.d.ts +2 -2
  56. package/dest/client/private_execution.d.ts.map +1 -1
  57. package/dest/client/private_execution.js +3 -7
  58. package/dest/client/simulator.d.ts +3 -3
  59. package/dest/client/simulator.d.ts.map +1 -1
  60. package/dest/client/simulator.js +3 -2
  61. package/dest/client/unconstrained_execution.d.ts +2 -2
  62. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  63. package/dest/client/unconstrained_execution.js +1 -1
  64. package/dest/client/view_data_oracle.d.ts +7 -0
  65. package/dest/client/view_data_oracle.d.ts.map +1 -1
  66. package/dest/client/view_data_oracle.js +10 -1
  67. package/dest/mocks/fixtures.d.ts.map +1 -1
  68. package/dest/mocks/fixtures.js +3 -1
  69. package/dest/public/abstract_phase_manager.d.ts +2 -0
  70. package/dest/public/abstract_phase_manager.d.ts.map +1 -1
  71. package/dest/public/abstract_phase_manager.js +13 -6
  72. package/dest/public/execution.d.ts +9 -0
  73. package/dest/public/execution.d.ts.map +1 -1
  74. package/dest/public/execution.js +1 -1
  75. package/dest/public/executor.d.ts +2 -2
  76. package/dest/public/executor.d.ts.map +1 -1
  77. package/dest/public/executor.js +34 -14
  78. package/dest/public/public_execution_context.d.ts +10 -4
  79. package/dest/public/public_execution_context.d.ts.map +1 -1
  80. package/dest/public/public_execution_context.js +19 -6
  81. package/dest/public/tail_phase_manager.d.ts +0 -1
  82. package/dest/public/tail_phase_manager.d.ts.map +1 -1
  83. package/dest/public/tail_phase_manager.js +3 -26
  84. package/dest/public/transitional_adaptors.d.ts +4 -17
  85. package/dest/public/transitional_adaptors.d.ts.map +1 -1
  86. package/dest/public/transitional_adaptors.js +27 -119
  87. package/package.json +8 -8
  88. package/src/acvm/acvm.ts +2 -2
  89. package/src/acvm/oracle/oracle.ts +63 -10
  90. package/src/acvm/oracle/typed_oracle.ts +12 -3
  91. package/src/avm/avm_gas.ts +1 -0
  92. package/src/avm/avm_memory_types.ts +1 -1
  93. package/src/avm/avm_simulator.ts +2 -0
  94. package/src/avm/journal/journal.ts +133 -9
  95. package/src/avm/journal/nullifiers.ts +19 -8
  96. package/src/avm/journal/public_storage.ts +23 -2
  97. package/src/avm/journal/trace.ts +3 -4
  98. package/src/avm/journal/trace_types.ts +1 -0
  99. package/src/avm/opcodes/bitwise.ts +18 -7
  100. package/src/avm/opcodes/environment_getters.ts +9 -0
  101. package/src/avm/opcodes/external_calls.ts +21 -16
  102. package/src/avm/serialization/bytecode_serialization.ts +2 -0
  103. package/src/avm/serialization/instruction_serialization.ts +1 -0
  104. package/src/client/client_execution_context.ts +55 -31
  105. package/src/client/db_oracle.ts +12 -10
  106. package/src/client/execution_result.ts +55 -24
  107. package/src/client/logs_cache.ts +65 -0
  108. package/src/client/private_execution.ts +4 -10
  109. package/src/client/simulator.ts +6 -4
  110. package/src/client/unconstrained_execution.ts +2 -2
  111. package/src/client/view_data_oracle.ts +10 -0
  112. package/src/mocks/fixtures.ts +2 -0
  113. package/src/public/abstract_phase_manager.ts +13 -5
  114. package/src/public/execution.ts +9 -0
  115. package/src/public/executor.ts +47 -10
  116. package/src/public/public_execution_context.ts +18 -4
  117. package/src/public/tail_phase_manager.ts +2 -34
  118. package/src/public/transitional_adaptors.ts +39 -178
@@ -1,9 +1,21 @@
1
+ // TODO(5818): Rename file and all uses of "journal"
1
2
  import { UnencryptedL2Log } from '@aztec/circuit-types';
2
- import { AztecAddress, EthAddress, L2ToL1Message } from '@aztec/circuits.js';
3
+ import {
4
+ AztecAddress,
5
+ ContractStorageRead,
6
+ ContractStorageUpdateRequest,
7
+ EthAddress,
8
+ L2ToL1Message,
9
+ NoteHash,
10
+ Nullifier,
11
+ ReadRequest,
12
+ SideEffect,
13
+ } from '@aztec/circuits.js';
3
14
  import { EventSelector } from '@aztec/foundation/abi';
4
15
  import { Fr } from '@aztec/foundation/fields';
5
16
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
6
17
 
18
+ import { type PublicExecutionResult } from '../../index.js';
7
19
  import { type HostStorage } from './host_storage.js';
8
20
  import { Nullifiers } from './nullifiers.js';
9
21
  import { PublicStorage } from './public_storage.js';
@@ -19,6 +31,7 @@ import {
19
31
  type TracedUnencryptedL2Log,
20
32
  } from './trace_types.js';
21
33
 
34
+ // TODO:(5818): do we need this type anymore?
22
35
  /**
23
36
  * Data held within the journal
24
37
  */
@@ -37,6 +50,25 @@ export type JournalData = {
37
50
  newLogsHashes: TracedUnencryptedL2Log[];
38
51
  /** contract address -\> key -\> value */
39
52
  currentStorageValue: Map<bigint, Map<bigint, Fr>>;
53
+
54
+ sideEffectCounter: number;
55
+ };
56
+
57
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
58
+ type PartialPublicExecutionResult = {
59
+ nullifierReadRequests: ReadRequest[];
60
+ nullifierNonExistentReadRequests: ReadRequest[];
61
+ newNoteHashes: NoteHash[];
62
+ newL2ToL1Messages: L2ToL1Message[];
63
+ startSideEffectCounter: number;
64
+ newNullifiers: Nullifier[];
65
+ contractStorageReads: ContractStorageRead[];
66
+ contractStorageUpdateRequests: ContractStorageUpdateRequest[];
67
+ unencryptedLogsHashes: SideEffect[];
68
+ unencryptedLogs: UnencryptedL2Log[];
69
+ unencryptedLogPreimagesLength: Fr;
70
+ allUnencryptedLogs: UnencryptedL2Log[];
71
+ nestedExecutions: PublicExecutionResult[];
40
72
  };
41
73
 
42
74
  /**
@@ -53,7 +85,7 @@ export class AvmPersistableStateManager {
53
85
  /** Reference to node storage */
54
86
  public readonly hostStorage: HostStorage;
55
87
 
56
- // TODO: make members private once this is not used in transitional_adaptors.ts.
88
+ // TODO(5818): make members private once this is not used in transitional_adaptors.ts.
57
89
  /** World State */
58
90
  /** Public storage, including cached writes */
59
91
  public publicStorage: PublicStorage;
@@ -67,11 +99,30 @@ export class AvmPersistableStateManager {
67
99
  public newL1Messages: L2ToL1Message[] = [];
68
100
  public newLogs: UnencryptedL2Log[] = [];
69
101
 
102
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
103
+ public transitionalExecutionResult: PartialPublicExecutionResult;
104
+
70
105
  constructor(hostStorage: HostStorage, parent?: AvmPersistableStateManager) {
71
106
  this.hostStorage = hostStorage;
72
107
  this.publicStorage = new PublicStorage(hostStorage.publicStateDb, parent?.publicStorage);
73
108
  this.nullifiers = new Nullifiers(hostStorage.commitmentsDb, parent?.nullifiers);
74
109
  this.trace = new WorldStateAccessTrace(parent?.trace);
110
+
111
+ this.transitionalExecutionResult = {
112
+ nullifierReadRequests: [],
113
+ nullifierNonExistentReadRequests: [],
114
+ newNoteHashes: [],
115
+ newL2ToL1Messages: [],
116
+ startSideEffectCounter: this.trace.accessCounter,
117
+ newNullifiers: [],
118
+ contractStorageReads: [],
119
+ contractStorageUpdateRequests: [],
120
+ unencryptedLogsHashes: [],
121
+ unencryptedLogs: [],
122
+ unencryptedLogPreimagesLength: new Fr(0),
123
+ allUnencryptedLogs: [],
124
+ nestedExecutions: [],
125
+ };
75
126
  }
76
127
 
77
128
  /**
@@ -92,6 +143,21 @@ export class AvmPersistableStateManager {
92
143
  this.log.debug(`storage(${storageAddress})@${slot} <- ${value}`);
93
144
  // Cache storage writes for later reference/reads
94
145
  this.publicStorage.write(storageAddress, slot, value);
146
+
147
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
148
+ // The current info to the kernel clears any previous read or write request.
149
+ this.transitionalExecutionResult.contractStorageReads =
150
+ this.transitionalExecutionResult.contractStorageReads.filter(
151
+ read => !read.storageSlot.equals(slot) || !read.contractAddress!.equals(storageAddress),
152
+ );
153
+ this.transitionalExecutionResult.contractStorageUpdateRequests =
154
+ this.transitionalExecutionResult.contractStorageUpdateRequests.filter(
155
+ update => !update.storageSlot.equals(slot) || !update.contractAddress!.equals(storageAddress),
156
+ );
157
+ this.transitionalExecutionResult.contractStorageUpdateRequests.push(
158
+ new ContractStorageUpdateRequest(slot, value, this.trace.accessCounter, storageAddress),
159
+ );
160
+
95
161
  // Trace all storage writes (even reverted ones)
96
162
  this.trace.tracePublicStorageWrite(storageAddress, slot, value);
97
163
  }
@@ -104,10 +170,24 @@ export class AvmPersistableStateManager {
104
170
  * @returns the latest value written to slot, or 0 if never written to before
105
171
  */
106
172
  public async readStorage(storageAddress: Fr, slot: Fr): Promise<Fr> {
107
- const [exists, value] = await this.publicStorage.read(storageAddress, slot);
108
- this.log.debug(`storage(${storageAddress})@${slot} ?? value: ${value}, exists: ${exists}.`);
173
+ const { value, exists, cached } = await this.publicStorage.read(storageAddress, slot);
174
+ this.log.debug(`storage(${storageAddress})@${slot} ?? value: ${value}, exists: ${exists}, cached: ${cached}.`);
175
+
176
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
177
+ // The current info to the kernel kernel does not consider cached reads.
178
+ if (!cached) {
179
+ // The current info to the kernel removes any previous reads to the same slot.
180
+ this.transitionalExecutionResult.contractStorageReads =
181
+ this.transitionalExecutionResult.contractStorageReads.filter(
182
+ read => !read.storageSlot.equals(slot) || !read.contractAddress!.equals(storageAddress),
183
+ );
184
+ this.transitionalExecutionResult.contractStorageReads.push(
185
+ new ContractStorageRead(slot, value, this.trace.accessCounter, storageAddress),
186
+ );
187
+ }
188
+
109
189
  // We want to keep track of all performed reads (even reverted ones)
110
- this.trace.tracePublicStorageRead(storageAddress, slot, value, exists);
190
+ this.trace.tracePublicStorageRead(storageAddress, slot, value, exists, cached);
111
191
  return Promise.resolve(value);
112
192
  }
113
193
 
@@ -133,6 +213,9 @@ export class AvmPersistableStateManager {
133
213
  * @param noteHash - the unsiloed note hash to write
134
214
  */
135
215
  public writeNoteHash(storageAddress: Fr, noteHash: Fr) {
216
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
217
+ this.transitionalExecutionResult.newNoteHashes.push(new NoteHash(noteHash, this.trace.accessCounter));
218
+
136
219
  this.log.debug(`noteHashes(${storageAddress}) += @${noteHash}.`);
137
220
  this.trace.traceNewNoteHash(storageAddress, noteHash);
138
221
  }
@@ -148,6 +231,16 @@ export class AvmPersistableStateManager {
148
231
  this.log.debug(
149
232
  `nullifiers(${storageAddress})@${nullifier} ?? leafIndex: ${leafIndex}, pending: ${isPending}, exists: ${exists}.`,
150
233
  );
234
+
235
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
236
+ if (exists) {
237
+ this.transitionalExecutionResult.nullifierReadRequests.push(new ReadRequest(nullifier, this.trace.accessCounter));
238
+ } else {
239
+ this.transitionalExecutionResult.nullifierNonExistentReadRequests.push(
240
+ new ReadRequest(nullifier, this.trace.accessCounter),
241
+ );
242
+ }
243
+
151
244
  this.trace.traceNullifierCheck(storageAddress, nullifier, exists, isPending, leafIndex);
152
245
  return Promise.resolve(exists);
153
246
  }
@@ -158,6 +251,9 @@ export class AvmPersistableStateManager {
158
251
  * @param nullifier - the unsiloed nullifier to write
159
252
  */
160
253
  public async writeNullifier(storageAddress: Fr, nullifier: Fr) {
254
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
255
+ this.transitionalExecutionResult.newNullifiers.push(new Nullifier(nullifier, this.trace.accessCounter, Fr.ZERO));
256
+
161
257
  this.log.debug(`nullifiers(${storageAddress}) += ${nullifier}.`);
162
258
  // Cache pending nullifiers for later access
163
259
  await this.nullifiers.append(storageAddress, nullifier);
@@ -189,18 +285,39 @@ export class AvmPersistableStateManager {
189
285
  public writeL1Message(recipient: EthAddress | Fr, content: Fr) {
190
286
  this.log.debug(`L1Messages(${recipient}) += ${content}.`);
191
287
  const recipientAddress = recipient instanceof EthAddress ? recipient : EthAddress.fromField(recipient);
192
- this.newL1Messages.push(new L2ToL1Message(recipientAddress, content));
288
+ const message = new L2ToL1Message(recipientAddress, content);
289
+ this.newL1Messages.push(message);
290
+
291
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
292
+ this.transitionalExecutionResult.newL2ToL1Messages.push(message);
193
293
  }
194
294
 
195
295
  public writeLog(contractAddress: Fr, event: Fr, log: Fr[]) {
196
296
  this.log.debug(`UnencryptedL2Log(${contractAddress}) += event ${event} with ${log.length} fields.`);
197
- const L2log = new UnencryptedL2Log(
297
+ const ulog = new UnencryptedL2Log(
198
298
  AztecAddress.fromField(contractAddress),
199
299
  EventSelector.fromField(event),
200
300
  Buffer.concat(log.map(f => f.toBuffer())),
201
301
  );
202
- this.newLogs.push(L2log);
203
- this.trace.traceNewLog(Fr.fromBuffer(L2log.hash()));
302
+ const logHash = Fr.fromBuffer(ulog.hash());
303
+
304
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
305
+ this.transitionalExecutionResult.unencryptedLogs.push(ulog);
306
+ this.transitionalExecutionResult.allUnencryptedLogs.push(ulog);
307
+ // this duplicates exactly what happens in the trace just for the purpose of transitional integration with the kernel
308
+ this.transitionalExecutionResult.unencryptedLogsHashes.push(
309
+ new SideEffect(logHash, new Fr(this.trace.accessCounter)),
310
+ );
311
+ // Duplicates computation performed in public_context.nr::emit_unencrypted_log
312
+ // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4).
313
+ this.transitionalExecutionResult.unencryptedLogPreimagesLength = new Fr(
314
+ this.transitionalExecutionResult.unencryptedLogPreimagesLength.toNumber() + 44 + log.length * Fr.SIZE_IN_BYTES,
315
+ );
316
+ // TODO(6206): likely need to track this here and not just in the transitional logic.
317
+
318
+ // TODO(6205): why are logs pushed here but logs hashes are traced?
319
+ this.newLogs.push(ulog);
320
+ this.trace.traceNewLog(logHash);
204
321
  }
205
322
 
206
323
  /**
@@ -216,6 +333,11 @@ export class AvmPersistableStateManager {
216
333
  // Accrued Substate
217
334
  this.newL1Messages = this.newL1Messages.concat(nestedJournal.newL1Messages);
218
335
  this.newLogs = this.newLogs.concat(nestedJournal.newLogs);
336
+
337
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
338
+ this.transitionalExecutionResult.allUnencryptedLogs.concat(
339
+ nestedJournal.transitionalExecutionResult.allUnencryptedLogs,
340
+ );
219
341
  }
220
342
 
221
343
  /**
@@ -226,6 +348,7 @@ export class AvmPersistableStateManager {
226
348
  this.trace.acceptAndMerge(nestedJournal.trace);
227
349
  }
228
350
 
351
+ // TODO:(5818): do we need this type anymore?
229
352
  /**
230
353
  * Access the current state of the journal
231
354
  *
@@ -244,6 +367,7 @@ export class AvmPersistableStateManager {
244
367
  currentStorageValue: this.publicStorage.getCache().cachePerContract,
245
368
  storageReads: this.trace.publicStorageReads,
246
369
  storageWrites: this.trace.publicStorageWrites,
370
+ sideEffectCounter: this.trace.accessCounter,
247
371
  };
248
372
  }
249
373
  }
@@ -1,3 +1,4 @@
1
+ import { AztecAddress } from '@aztec/circuits.js';
1
2
  import { siloNullifier } from '@aztec/circuits.js/hash';
2
3
  import { Fr } from '@aztec/foundation/fields';
3
4
 
@@ -10,7 +11,7 @@ import type { CommitmentsDB } from '../../index.js';
10
11
  */
11
12
  export class Nullifiers {
12
13
  /** Cached nullifiers. */
13
- private cache: NullifierCache;
14
+ public cache: NullifierCache;
14
15
  /** Parent's nullifier cache. Checked on cache-miss. */
15
16
  private readonly parentCache: NullifierCache | undefined;
16
17
  /** Reference to node storage. Checked on parent cache-miss. */
@@ -95,6 +96,7 @@ export class NullifierCache {
95
96
  * each entry being a nullifier.
96
97
  */
97
98
  private cachePerContract: Map<bigint, Set<bigint>> = new Map();
99
+ private siloedNullifiers: Set<bigint> = new Set();
98
100
 
99
101
  /**
100
102
  * Check whether a nullifier exists in the cache.
@@ -104,8 +106,10 @@ export class NullifierCache {
104
106
  * @returns whether the nullifier is found in the cache
105
107
  */
106
108
  public exists(storageAddress: Fr, nullifier: Fr): boolean {
107
- const exists = this.cachePerContract.get(storageAddress.toBigInt())?.has(nullifier.toBigInt());
108
- return exists ? true : false;
109
+ const exists =
110
+ this.cachePerContract.get(storageAddress.toBigInt())?.has(nullifier.toBigInt()) ||
111
+ this.siloedNullifiers.has(siloNullifier(AztecAddress.fromField(storageAddress), nullifier).toBigInt());
112
+ return !!exists;
109
113
  }
110
114
 
111
115
  /**
@@ -115,20 +119,25 @@ export class NullifierCache {
115
119
  * @param nullifier - the nullifier to stage
116
120
  */
117
121
  public append(storageAddress: Fr, nullifier: Fr) {
122
+ if (this.exists(storageAddress, nullifier)) {
123
+ throw new NullifierCollisionError(
124
+ `Nullifier ${nullifier} at contract ${storageAddress} already exists in cache.`,
125
+ );
126
+ }
127
+
118
128
  let nullifiersForContract = this.cachePerContract.get(storageAddress.toBigInt());
119
129
  // If this contract's nullifier set has no cached nullifiers, create a new Set to store them
120
130
  if (!nullifiersForContract) {
121
131
  nullifiersForContract = new Set();
122
132
  this.cachePerContract.set(storageAddress.toBigInt(), nullifiersForContract);
123
133
  }
124
- if (nullifiersForContract.has(nullifier.toBigInt())) {
125
- throw new NullifierCollisionError(
126
- `Nullifier ${nullifier} at contract ${storageAddress} already exists in cache.`,
127
- );
128
- }
129
134
  nullifiersForContract.add(nullifier.toBigInt());
130
135
  }
131
136
 
137
+ public appendSiloed(siloedNullifier: Fr) {
138
+ this.siloedNullifiers.add(siloedNullifier.toBigInt());
139
+ }
140
+
132
141
  /**
133
142
  * Merge another cache's nullifiers into this instance's.
134
143
  *
@@ -139,6 +148,8 @@ export class NullifierCache {
139
148
  * @param incomingNullifiers - the incoming cached nullifiers to merge into this instance's
140
149
  */
141
150
  public acceptAndMerge(incomingNullifiers: NullifierCache) {
151
+ // Merge siloed nullifiers.
152
+ this.siloedNullifiers = new Set([...this.siloedNullifiers, ...incomingNullifiers.siloedNullifiers]);
142
153
  // Iterate over all contracts with staged writes in the child.
143
154
  for (const [incomingAddress, incomingCacheAtContract] of incomingNullifiers.cachePerContract) {
144
155
  const thisCacheAtContract = this.cachePerContract.get(incomingAddress);
@@ -1,7 +1,14 @@
1
+ import { AztecAddress } from '@aztec/circuits.js';
1
2
  import { Fr } from '@aztec/foundation/fields';
2
3
 
3
4
  import type { PublicStateDB } from '../../index.js';
4
5
 
6
+ type PublicStorageReadResult = {
7
+ value: Fr;
8
+ exists: boolean;
9
+ cached: boolean;
10
+ };
11
+
5
12
  /**
6
13
  * A class to manage public storage reads and writes during a contract call's AVM simulation.
7
14
  * Maintains a storage write cache, and ensures that reads fall back to the correct source.
@@ -39,7 +46,8 @@ export class PublicStorage {
39
46
  * @param slot - the slot in the contract's storage being read from
40
47
  * @returns exists: whether the slot has EVER been written to before, value: the latest value written to slot, or 0 if never written to before
41
48
  */
42
- public async read(storageAddress: Fr, slot: Fr): Promise<[/*exists=*/ boolean, /*value=*/ Fr]> {
49
+ public async read(storageAddress: Fr, slot: Fr): Promise<PublicStorageReadResult> {
50
+ let cached = false;
43
51
  // First try check this storage cache
44
52
  let value = this.cache.read(storageAddress, slot);
45
53
  // Then try parent's storage cache (if it exists / written to earlier in this TX)
@@ -49,11 +57,13 @@ export class PublicStorage {
49
57
  // Finally try the host's Aztec state (a trip to the database)
50
58
  if (!value) {
51
59
  value = await this.hostPublicStorage.storageRead(storageAddress, slot);
60
+ } else {
61
+ cached = true;
52
62
  }
53
63
  // if value is undefined, that means this slot has never been written to!
54
64
  const exists = value !== undefined;
55
65
  const valueOrZero = exists ? value : Fr.ZERO;
56
- return Promise.resolve([exists, valueOrZero]);
66
+ return Promise.resolve({ value: valueOrZero, exists, cached });
57
67
  }
58
68
 
59
69
  /**
@@ -75,6 +85,17 @@ export class PublicStorage {
75
85
  public acceptAndMerge(incomingPublicStorage: PublicStorage) {
76
86
  this.cache.acceptAndMerge(incomingPublicStorage.cache);
77
87
  }
88
+
89
+ /**
90
+ * Commits ALL staged writes to the host's state.
91
+ */
92
+ public async commitToDB() {
93
+ for (const [storageAddress, cacheAtContract] of this.cache.cachePerContract) {
94
+ for (const [slot, value] of cacheAtContract) {
95
+ await this.hostPublicStorage.storageWrite(AztecAddress.fromBigInt(storageAddress), new Fr(slot), value);
96
+ }
97
+ }
98
+ }
78
99
  }
79
100
 
80
101
  /**
@@ -29,13 +29,14 @@ export class WorldStateAccessTrace {
29
29
 
30
30
  constructor(parentTrace?: WorldStateAccessTrace) {
31
31
  this.accessCounter = parentTrace ? parentTrace.accessCounter : 0;
32
+ // TODO(4805): consider tracking the parent's trace vector lengths so we can enforce limits
32
33
  }
33
34
 
34
35
  public getAccessCounter() {
35
36
  return this.accessCounter;
36
37
  }
37
38
 
38
- public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean) {
39
+ public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean, cached: boolean) {
39
40
  // TODO(4805): check if some threshold is reached for max storage reads
40
41
  // (need access to parent length, or trace needs to be initialized with parent's contents)
41
42
  const traced: TracedPublicStorageRead = {
@@ -44,6 +45,7 @@ export class WorldStateAccessTrace {
44
45
  slot,
45
46
  value,
46
47
  exists,
48
+ cached,
47
49
  counter: new Fr(this.accessCounter),
48
50
  // endLifetime: Fr.ZERO,
49
51
  };
@@ -151,9 +153,6 @@ export class WorldStateAccessTrace {
151
153
  /**
152
154
  * Merges another trace into this one
153
155
  *
154
- * - Public state journals (r/w logs), with the accessing being appended in chronological order
155
- * - Utxo objects are concatenated
156
- *
157
156
  * @param incomingTrace - the incoming trace to merge into this instance
158
157
  */
159
158
  public acceptAndMerge(incomingTrace: WorldStateAccessTrace) {
@@ -11,6 +11,7 @@ export type TracedPublicStorageRead = {
11
11
  // callPointer: Fr;
12
12
  storageAddress: Fr;
13
13
  exists: boolean;
14
+ cached: boolean;
14
15
  slot: Fr;
15
16
  value: Fr;
16
17
  counter: Fr;
@@ -1,5 +1,5 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
- import { type IntegralValue } from '../avm_memory_types.js';
2
+ import { type IntegralValue, type TaggedMemoryInterface, TypeTag } from '../avm_memory_types.js';
3
3
  import { Opcode } from '../serialization/instruction_serialization.js';
4
4
  import { ThreeOperandInstruction, TwoOperandInstruction } from './instruction_impl.js';
5
5
 
@@ -9,7 +9,7 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
9
9
  const memory = context.machineState.memory.track(this.type);
10
10
  context.machineState.consumeGas(this.gasCost(memoryOperations));
11
11
 
12
- memory.checkTags(this.inTag, this.aOffset, this.bOffset);
12
+ this.checkTags(memory, this.inTag, this.aOffset, this.bOffset);
13
13
 
14
14
  const a = memory.getAs<IntegralValue>(this.aOffset);
15
15
  const b = memory.getAs<IntegralValue>(this.bOffset);
@@ -22,13 +22,16 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
22
22
  }
23
23
 
24
24
  protected abstract compute(a: IntegralValue, b: IntegralValue): IntegralValue;
25
+ protected checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
26
+ memory.checkTags(inTag, aOffset, bOffset);
27
+ }
25
28
  }
26
29
 
27
30
  export class And extends ThreeOperandBitwiseInstruction {
28
31
  static readonly type: string = 'AND';
29
32
  static readonly opcode = Opcode.AND;
30
33
 
31
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
34
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
32
35
  return a.and(b);
33
36
  }
34
37
  }
@@ -37,7 +40,7 @@ export class Or extends ThreeOperandBitwiseInstruction {
37
40
  static readonly type: string = 'OR';
38
41
  static readonly opcode = Opcode.OR;
39
42
 
40
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
43
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
41
44
  return a.or(b);
42
45
  }
43
46
  }
@@ -46,7 +49,7 @@ export class Xor extends ThreeOperandBitwiseInstruction {
46
49
  static readonly type: string = 'XOR';
47
50
  static readonly opcode = Opcode.XOR;
48
51
 
49
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
52
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
50
53
  return a.xor(b);
51
54
  }
52
55
  }
@@ -55,18 +58,26 @@ export class Shl extends ThreeOperandBitwiseInstruction {
55
58
  static readonly type: string = 'SHL';
56
59
  static readonly opcode = Opcode.SHL;
57
60
 
58
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
61
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
59
62
  return a.shl(b);
60
63
  }
64
+ protected override checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
65
+ memory.checkTag(inTag, aOffset);
66
+ memory.checkTag(TypeTag.UINT8, bOffset);
67
+ }
61
68
  }
62
69
 
63
70
  export class Shr extends ThreeOperandBitwiseInstruction {
64
71
  static readonly type: string = 'SHR';
65
72
  static readonly opcode = Opcode.SHR;
66
73
 
67
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
74
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
68
75
  return a.shr(b);
69
76
  }
77
+ protected override checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
78
+ memory.checkTag(inTag, aOffset);
79
+ memory.checkTag(TypeTag.UINT8, bOffset);
80
+ }
70
81
  }
71
82
 
72
83
  export class Not extends TwoOperandInstruction {
@@ -59,6 +59,15 @@ export class FeePerDAGas extends EnvironmentGetterInstruction {
59
59
  }
60
60
  }
61
61
 
62
+ export class TransactionFee extends EnvironmentGetterInstruction {
63
+ static type: string = 'TRANSACTIONFEE';
64
+ static readonly opcode: Opcode = Opcode.TRANSACTIONFEE;
65
+
66
+ protected getEnvironmentValue(env: AvmExecutionEnvironment) {
67
+ return env.transactionFee;
68
+ }
69
+ }
70
+
62
71
  export class ChainId extends EnvironmentGetterInstruction {
63
72
  static type: string = 'CHAINID';
64
73
  static readonly opcode: Opcode = Opcode.CHAINID;
@@ -1,16 +1,12 @@
1
- import { FunctionSelector } from '@aztec/circuits.js';
1
+ import { FunctionSelector, Gas } from '@aztec/circuits.js';
2
2
  import { padArrayEnd } from '@aztec/foundation/collection';
3
3
 
4
- import { executePublicFunction } from '../../public/executor.js';
5
- import {
6
- convertPublicExecutionResult,
7
- createPublicExecutionContext,
8
- updateAvmContextFromPublicExecutionResult,
9
- } from '../../public/transitional_adaptors.js';
4
+ import { convertAvmResultsToPxResult, createPublicExecution } from '../../public/transitional_adaptors.js';
10
5
  import type { AvmContext } from '../avm_context.js';
11
6
  import { gasLeftToGas, sumGas } from '../avm_gas.js';
12
7
  import { Field, Uint8 } from '../avm_memory_types.js';
13
8
  import { type AvmContractCallResults } from '../avm_message_call_result.js';
9
+ import { AvmSimulator } from '../avm_simulator.js';
14
10
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
15
11
  import { Addressing } from './addressing_mode.js';
16
12
  import { Instruction } from './instruction.js';
@@ -69,7 +65,7 @@ abstract class ExternalCall extends Instruction {
69
65
  const totalGas = sumGas(this.gasCost(memoryOperations), allocatedGas);
70
66
  context.machineState.consumeGas(totalGas);
71
67
 
72
- // TRANSITIONAL: This should be removed once the AVM is fully operational and the public executor is gone.
68
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
73
69
  const nestedContext = context.createNestedContractCallContext(
74
70
  callAddress.toFr(),
75
71
  calldata,
@@ -77,11 +73,21 @@ abstract class ExternalCall extends Instruction {
77
73
  this.type,
78
74
  FunctionSelector.fromField(functionSelector),
79
75
  );
80
- const pxContext = createPublicExecutionContext(nestedContext, calldata);
81
- const pxResults = await executePublicFunction(pxContext, /*nested=*/ true);
82
- const nestedCallResults: AvmContractCallResults = convertPublicExecutionResult(pxResults);
83
- updateAvmContextFromPublicExecutionResult(nestedContext, pxResults);
84
- const nestedPersistableState = nestedContext.persistableState;
76
+ const startSideEffectCounter = nestedContext.persistableState.trace.accessCounter;
77
+
78
+ const oldStyleExecution = createPublicExecution(startSideEffectCounter, nestedContext.environment, calldata);
79
+ const nestedCallResults: AvmContractCallResults = await new AvmSimulator(nestedContext).execute();
80
+ const pxResults = convertAvmResultsToPxResult(
81
+ nestedCallResults,
82
+ startSideEffectCounter,
83
+ oldStyleExecution,
84
+ Gas.from(allocatedGas),
85
+ nestedContext,
86
+ );
87
+ // store the old PublicExecutionResult object to maintain a recursive data structure for the old kernel
88
+ context.persistableState.transitionalExecutionResult.nestedExecutions.push(pxResults);
89
+ // END TRANSITIONAL
90
+
85
91
  // const nestedContext = context.createNestedContractCallContext(
86
92
  // callAddress.toFr(),
87
93
  // calldata,
@@ -90,7 +96,6 @@ abstract class ExternalCall extends Instruction {
90
96
  // FunctionSelector.fromField(functionSelector),
91
97
  // );
92
98
  // const nestedCallResults: AvmContractCallResults = await new AvmSimulator(nestedContext).execute();
93
- // const nestedPersistableState = nestedContext.persistableState;
94
99
 
95
100
  const success = !nestedCallResults.reverted;
96
101
 
@@ -112,9 +117,9 @@ abstract class ExternalCall extends Instruction {
112
117
 
113
118
  // TODO: Should we merge the changes from a nested call in the case of a STATIC call?
114
119
  if (success) {
115
- context.persistableState.acceptNestedCallState(nestedPersistableState);
120
+ context.persistableState.acceptNestedCallState(nestedContext.persistableState);
116
121
  } else {
117
- context.persistableState.rejectNestedCallState(nestedPersistableState);
122
+ context.persistableState.rejectNestedCallState(nestedContext.persistableState);
118
123
  }
119
124
 
120
125
  memory.assert(memoryOperations);
@@ -46,6 +46,7 @@ import {
46
46
  StorageAddress,
47
47
  Sub,
48
48
  Timestamp,
49
+ TransactionFee,
49
50
  Version,
50
51
  Xor,
51
52
  } from '../opcodes/index.js';
@@ -82,6 +83,7 @@ const INSTRUCTION_SET = () =>
82
83
  [Sender.opcode, Sender],
83
84
  [FeePerL2Gas.opcode, FeePerL2Gas],
84
85
  [FeePerDAGas.opcode, FeePerDAGas],
86
+ [TransactionFee.opcode, TransactionFee],
85
87
  //[Contractcalldepth.opcode, Contractcalldepth],
86
88
  // Execution Environment - Globals
87
89
  [ChainId.opcode, ChainId],
@@ -29,6 +29,7 @@ export enum Opcode {
29
29
  SENDER,
30
30
  FEEPERL2GAS,
31
31
  FEEPERDAGAS,
32
+ TRANSACTIONFEE,
32
33
  CONTRACTCALLDEPTH,
33
34
  CHAINID,
34
35
  VERSION,