@aztec/simulator 0.57.0 → 0.58.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 (135) hide show
  1. package/dest/acvm/oracle/oracle.js +2 -2
  2. package/dest/acvm/oracle/typed_oracle.d.ts +1 -2
  3. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  4. package/dest/acvm/oracle/typed_oracle.js +1 -1
  5. package/dest/avm/avm_gas.js +2 -2
  6. package/dest/avm/avm_memory_types.d.ts +7 -3
  7. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  8. package/dest/avm/avm_memory_types.js +29 -14
  9. package/dest/avm/avm_simulator.d.ts.map +1 -1
  10. package/dest/avm/avm_simulator.js +5 -2
  11. package/dest/avm/fixtures/index.d.ts.map +1 -1
  12. package/dest/avm/fixtures/index.js +3 -3
  13. package/dest/avm/journal/journal.d.ts.map +1 -1
  14. package/dest/avm/journal/journal.js +2 -3
  15. package/dest/avm/opcodes/accrued_substate.js +17 -17
  16. package/dest/avm/opcodes/arithmetic.js +2 -2
  17. package/dest/avm/opcodes/bitwise.d.ts +3 -3
  18. package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
  19. package/dest/avm/opcodes/bitwise.js +9 -8
  20. package/dest/avm/opcodes/comparators.js +2 -2
  21. package/dest/avm/opcodes/contract.d.ts.map +1 -1
  22. package/dest/avm/opcodes/contract.js +4 -3
  23. package/dest/avm/opcodes/control_flow.js +1 -1
  24. package/dest/avm/opcodes/conversion.js +4 -4
  25. package/dest/avm/opcodes/ec_add.d.ts.map +1 -1
  26. package/dest/avm/opcodes/ec_add.js +18 -9
  27. package/dest/avm/opcodes/external_calls.js +10 -10
  28. package/dest/avm/opcodes/hashing.js +8 -8
  29. package/dest/avm/opcodes/instruction_impl.d.ts +1 -2
  30. package/dest/avm/opcodes/instruction_impl.d.ts.map +1 -1
  31. package/dest/avm/opcodes/instruction_impl.js +2 -5
  32. package/dest/avm/opcodes/memory.js +3 -3
  33. package/dest/avm/opcodes/misc.js +4 -4
  34. package/dest/avm/opcodes/multi_scalar_mul.d.ts.map +1 -1
  35. package/dest/avm/opcodes/multi_scalar_mul.js +24 -9
  36. package/dest/avm/opcodes/storage.js +2 -2
  37. package/dest/avm/serialization/instruction_serialization.js +2 -2
  38. package/dest/avm/test_utils.d.ts +1 -2
  39. package/dest/avm/test_utils.d.ts.map +1 -1
  40. package/dest/avm/test_utils.js +1 -1
  41. package/dest/client/client_execution_context.d.ts.map +1 -1
  42. package/dest/client/client_execution_context.js +7 -3
  43. package/dest/client/db_oracle.d.ts +1 -2
  44. package/dest/client/db_oracle.d.ts.map +1 -1
  45. package/dest/client/db_oracle.js +1 -1
  46. package/dest/client/index.d.ts +1 -0
  47. package/dest/client/index.d.ts.map +1 -1
  48. package/dest/client/index.js +2 -1
  49. package/dest/client/private_execution.d.ts +10 -1
  50. package/dest/client/private_execution.d.ts.map +1 -1
  51. package/dest/client/private_execution.js +25 -5
  52. package/dest/client/view_data_oracle.d.ts +1 -2
  53. package/dest/client/view_data_oracle.d.ts.map +1 -1
  54. package/dest/client/view_data_oracle.js +1 -1
  55. package/dest/public/db_interfaces.d.ts +1 -2
  56. package/dest/public/db_interfaces.d.ts.map +1 -1
  57. package/dest/public/dual_side_effect_trace.d.ts +76 -0
  58. package/dest/public/dual_side_effect_trace.d.ts.map +1 -0
  59. package/dest/public/dual_side_effect_trace.js +109 -0
  60. package/dest/public/enqueued_call_side_effect_trace.d.ts +114 -0
  61. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -0
  62. package/dest/public/enqueued_call_side_effect_trace.js +314 -0
  63. package/dest/public/enqueued_call_simulator.d.ts +2 -2
  64. package/dest/public/enqueued_call_simulator.d.ts.map +1 -1
  65. package/dest/public/enqueued_call_simulator.js +18 -7
  66. package/dest/public/enqueued_calls_processor.d.ts +2 -2
  67. package/dest/public/enqueued_calls_processor.d.ts.map +1 -1
  68. package/dest/public/enqueued_calls_processor.js +3 -5
  69. package/dest/public/execution.d.ts +3 -4
  70. package/dest/public/execution.d.ts.map +1 -1
  71. package/dest/public/execution.js +1 -1
  72. package/dest/public/executor.d.ts +6 -4
  73. package/dest/public/executor.d.ts.map +1 -1
  74. package/dest/public/executor.js +18 -9
  75. package/dest/public/fee_payment.d.ts +1 -1
  76. package/dest/public/fee_payment.d.ts.map +1 -1
  77. package/dest/public/fee_payment.js +4 -7
  78. package/dest/public/hints_builder.d.ts +2 -2
  79. package/dest/public/hints_builder.d.ts.map +1 -1
  80. package/dest/public/hints_builder.js +1 -1
  81. package/dest/public/public_db_sources.d.ts +4 -5
  82. package/dest/public/public_db_sources.d.ts.map +1 -1
  83. package/dest/public/public_db_sources.js +15 -11
  84. package/dest/public/public_kernel_tail_simulator.d.ts +3 -3
  85. package/dest/public/public_kernel_tail_simulator.d.ts.map +1 -1
  86. package/dest/public/public_kernel_tail_simulator.js +1 -1
  87. package/dest/public/public_processor.d.ts +7 -11
  88. package/dest/public/public_processor.d.ts.map +1 -1
  89. package/dest/public/public_processor.js +8 -12
  90. package/dest/public/side_effect_trace.d.ts +1 -2
  91. package/dest/public/side_effect_trace.d.ts.map +1 -1
  92. package/dest/public/side_effect_trace.js +2 -2
  93. package/package.json +9 -9
  94. package/src/acvm/oracle/oracle.ts +1 -1
  95. package/src/acvm/oracle/typed_oracle.ts +6 -2
  96. package/src/avm/avm_gas.ts +1 -1
  97. package/src/avm/avm_memory_types.ts +28 -11
  98. package/src/avm/avm_simulator.ts +7 -1
  99. package/src/avm/fixtures/index.ts +2 -2
  100. package/src/avm/journal/journal.ts +3 -4
  101. package/src/avm/opcodes/accrued_substate.ts +17 -17
  102. package/src/avm/opcodes/arithmetic.ts +1 -1
  103. package/src/avm/opcodes/bitwise.ts +8 -7
  104. package/src/avm/opcodes/comparators.ts +1 -1
  105. package/src/avm/opcodes/contract.ts +3 -2
  106. package/src/avm/opcodes/control_flow.ts +1 -1
  107. package/src/avm/opcodes/conversion.ts +4 -4
  108. package/src/avm/opcodes/ec_add.ts +15 -8
  109. package/src/avm/opcodes/external_calls.ts +10 -10
  110. package/src/avm/opcodes/hashing.ts +8 -8
  111. package/src/avm/opcodes/instruction_impl.ts +0 -3
  112. package/src/avm/opcodes/memory.ts +3 -3
  113. package/src/avm/opcodes/misc.ts +4 -4
  114. package/src/avm/opcodes/multi_scalar_mul.ts +20 -12
  115. package/src/avm/opcodes/storage.ts +2 -2
  116. package/src/avm/serialization/instruction_serialization.ts +1 -1
  117. package/src/avm/test_utils.ts +1 -2
  118. package/src/client/client_execution_context.ts +6 -1
  119. package/src/client/db_oracle.ts +6 -2
  120. package/src/client/index.ts +1 -0
  121. package/src/client/private_execution.ts +36 -6
  122. package/src/client/view_data_oracle.ts +1 -2
  123. package/src/public/db_interfaces.ts +5 -2
  124. package/src/public/dual_side_effect_trace.ts +173 -0
  125. package/src/public/enqueued_call_side_effect_trace.ts +552 -0
  126. package/src/public/enqueued_call_simulator.ts +33 -11
  127. package/src/public/enqueued_calls_processor.ts +4 -6
  128. package/src/public/execution.ts +2 -4
  129. package/src/public/executor.ts +38 -9
  130. package/src/public/fee_payment.ts +4 -6
  131. package/src/public/hints_builder.ts +2 -2
  132. package/src/public/public_db_sources.ts +31 -22
  133. package/src/public/public_kernel_tail_simulator.ts +3 -3
  134. package/src/public/public_processor.ts +16 -16
  135. package/src/public/side_effect_trace.ts +2 -2
@@ -0,0 +1,552 @@
1
+ import { UnencryptedL2Log } from '@aztec/circuit-types';
2
+ import {
3
+ AvmContractInstanceHint,
4
+ AvmExecutionHints,
5
+ AvmExternalCallHint,
6
+ AvmKeyValueHint,
7
+ AztecAddress,
8
+ CallContext,
9
+ type CombinedConstantData,
10
+ type ContractInstanceWithAddress,
11
+ ContractStorageRead,
12
+ ContractStorageUpdateRequest,
13
+ EthAddress,
14
+ Gas,
15
+ L2ToL1Message,
16
+ LogHash,
17
+ MAX_ENCRYPTED_LOGS_PER_TX,
18
+ MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_TX,
19
+ MAX_L2_TO_L1_MSGS_PER_TX,
20
+ MAX_NOTE_ENCRYPTED_LOGS_PER_TX,
21
+ MAX_NOTE_HASHES_PER_TX,
22
+ MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
23
+ MAX_NULLIFIERS_PER_TX,
24
+ MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX,
25
+ MAX_NULLIFIER_READ_REQUESTS_PER_TX,
26
+ MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
27
+ MAX_PUBLIC_DATA_READS_PER_TX,
28
+ MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
29
+ MAX_UNENCRYPTED_LOGS_PER_TX,
30
+ NoteHash,
31
+ Nullifier,
32
+ PublicAccumulatedData,
33
+ PublicAccumulatedDataArrayLengths,
34
+ PublicCallRequest,
35
+ PublicDataRead,
36
+ PublicDataUpdateRequest,
37
+ PublicInnerCallRequest,
38
+ PublicValidationRequestArrayLengths,
39
+ PublicValidationRequests,
40
+ ReadRequest,
41
+ RollupValidationRequests,
42
+ ScopedL2ToL1Message,
43
+ ScopedLogHash,
44
+ ScopedNoteHash,
45
+ type ScopedNullifier,
46
+ ScopedReadRequest,
47
+ TreeLeafReadRequest,
48
+ VMCircuitPublicInputs,
49
+ } from '@aztec/circuits.js';
50
+ import { computeVarArgsHash } from '@aztec/circuits.js/hash';
51
+ import { makeTuple } from '@aztec/foundation/array';
52
+ import { padArrayEnd } from '@aztec/foundation/collection';
53
+ import { Fr } from '@aztec/foundation/fields';
54
+ import { createDebugLogger } from '@aztec/foundation/log';
55
+
56
+ import { type AvmContractCallResult } from '../avm/avm_contract_call_result.js';
57
+ import { type AvmExecutionEnvironment } from '../avm/avm_execution_environment.js';
58
+ import { SideEffectLimitReachedError } from './side_effect_errors.js';
59
+ import { type PublicSideEffectTraceInterface } from './side_effect_trace_interface.js';
60
+
61
+ export type TracedContractInstance = { exists: boolean } & ContractInstanceWithAddress;
62
+
63
+ /**
64
+ * A struct containing just the side effects as regular arrays
65
+ * as opposed to "Tuple" arrays used by circuit public inputs.
66
+ * This struct is helpful for testing and checking array lengths.
67
+ **/
68
+ export type SideEffects = {
69
+ contractStorageReads: ContractStorageRead[];
70
+ contractStorageUpdateRequests: ContractStorageUpdateRequest[];
71
+
72
+ noteHashReadRequests: TreeLeafReadRequest[];
73
+ noteHashes: ScopedNoteHash[];
74
+
75
+ nullifierReadRequests: ScopedReadRequest[];
76
+ nullifierNonExistentReadRequests: ScopedReadRequest[];
77
+ nullifiers: ScopedNullifier[];
78
+
79
+ l1ToL2MsgReadRequests: TreeLeafReadRequest[];
80
+ l2ToL1Msgs: ScopedL2ToL1Message[];
81
+
82
+ unencryptedLogs: UnencryptedL2Log[];
83
+ unencryptedLogsHashes: ScopedLogHash[];
84
+ };
85
+
86
+ /**
87
+ * Trace side effects for an entire enqueued call.
88
+ */
89
+ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceInterface {
90
+ public log = createDebugLogger('aztec:public_enqueued_call_side_effect_trace');
91
+
92
+ /** The side effect counter increments with every call to the trace. */
93
+ private sideEffectCounter: number;
94
+
95
+ // TODO(dbanks12): make contract address mandatory in ContractStorage* structs,
96
+ // and include it in serialization, or modify PublicData* structs for this.
97
+ private contractStorageReads: ContractStorageRead[] = [];
98
+ private contractStorageUpdateRequests: ContractStorageUpdateRequest[] = [];
99
+
100
+ private noteHashReadRequests: TreeLeafReadRequest[] = [];
101
+ private noteHashes: ScopedNoteHash[] = [];
102
+
103
+ private nullifierReadRequests: ScopedReadRequest[] = [];
104
+ private nullifierNonExistentReadRequests: ScopedReadRequest[] = [];
105
+ private nullifiers: ScopedNullifier[] = [];
106
+
107
+ private l1ToL2MsgReadRequests: TreeLeafReadRequest[] = [];
108
+ private l2ToL1Msgs: ScopedL2ToL1Message[] = [];
109
+
110
+ private unencryptedLogs: UnencryptedL2Log[] = [];
111
+ private unencryptedLogsHashes: ScopedLogHash[] = [];
112
+
113
+ private avmCircuitHints: AvmExecutionHints;
114
+
115
+ constructor(
116
+ /** The counter of this trace's first side effect. */
117
+ public readonly startSideEffectCounter: number = 0,
118
+ /** Track parent's (or previous kernel's) lengths so the AVM can properly enforce TX-wide limits,
119
+ * otherwise the public kernel can fail to prove because TX limits are breached.
120
+ */
121
+ private readonly previousValidationRequestArrayLengths: PublicValidationRequestArrayLengths = PublicValidationRequestArrayLengths.empty(),
122
+ private readonly previousAccumulatedDataArrayLengths: PublicAccumulatedDataArrayLengths = PublicAccumulatedDataArrayLengths.empty(),
123
+ ) {
124
+ this.sideEffectCounter = startSideEffectCounter;
125
+ this.avmCircuitHints = AvmExecutionHints.empty();
126
+ }
127
+
128
+ public fork() {
129
+ return new PublicEnqueuedCallSideEffectTrace(
130
+ this.sideEffectCounter,
131
+ new PublicValidationRequestArrayLengths(
132
+ this.previousValidationRequestArrayLengths.noteHashReadRequests + this.noteHashReadRequests.length,
133
+ this.previousValidationRequestArrayLengths.nullifierReadRequests + this.nullifierReadRequests.length,
134
+ this.previousValidationRequestArrayLengths.nullifierNonExistentReadRequests +
135
+ this.nullifierNonExistentReadRequests.length,
136
+ this.previousValidationRequestArrayLengths.l1ToL2MsgReadRequests + this.l1ToL2MsgReadRequests.length,
137
+ this.previousValidationRequestArrayLengths.publicDataReads + this.contractStorageReads.length,
138
+ ),
139
+ new PublicAccumulatedDataArrayLengths(
140
+ this.previousAccumulatedDataArrayLengths.noteHashes + this.noteHashes.length,
141
+ this.previousAccumulatedDataArrayLengths.nullifiers + this.nullifiers.length,
142
+ this.previousAccumulatedDataArrayLengths.l2ToL1Msgs + this.l2ToL1Msgs.length,
143
+ this.previousAccumulatedDataArrayLengths.noteEncryptedLogsHashes,
144
+ this.previousAccumulatedDataArrayLengths.encryptedLogsHashes,
145
+ this.previousAccumulatedDataArrayLengths.unencryptedLogsHashes + this.unencryptedLogsHashes.length,
146
+ this.previousAccumulatedDataArrayLengths.publicDataUpdateRequests + this.contractStorageUpdateRequests.length,
147
+ this.previousAccumulatedDataArrayLengths.publicCallStack,
148
+ ),
149
+ );
150
+ }
151
+
152
+ public getCounter() {
153
+ return this.sideEffectCounter;
154
+ }
155
+
156
+ private incrementSideEffectCounter() {
157
+ this.sideEffectCounter++;
158
+ }
159
+
160
+ public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, _exists: boolean, _cached: boolean) {
161
+ // NOTE: exists and cached are unused for now but may be used for optimizations or kernel hints later
162
+ if (
163
+ this.contractStorageReads.length + this.previousValidationRequestArrayLengths.publicDataReads >=
164
+ MAX_PUBLIC_DATA_READS_PER_TX
165
+ ) {
166
+ throw new SideEffectLimitReachedError('contract storage read', MAX_PUBLIC_DATA_READS_PER_TX);
167
+ }
168
+
169
+ this.contractStorageReads.push(
170
+ new ContractStorageRead(slot, value, this.sideEffectCounter, AztecAddress.fromField(storageAddress)),
171
+ );
172
+ this.avmCircuitHints.storageValues.items.push(
173
+ new AvmKeyValueHint(/*key=*/ new Fr(this.sideEffectCounter), /*value=*/ value),
174
+ );
175
+ this.log.debug(`SLOAD cnt: ${this.sideEffectCounter} val: ${value} slot: ${slot}`);
176
+ this.incrementSideEffectCounter();
177
+ }
178
+
179
+ public tracePublicStorageWrite(storageAddress: Fr, slot: Fr, value: Fr) {
180
+ if (
181
+ this.contractStorageUpdateRequests.length + this.previousAccumulatedDataArrayLengths.publicDataUpdateRequests >=
182
+ MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
183
+ ) {
184
+ throw new SideEffectLimitReachedError('contract storage write', MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX);
185
+ }
186
+
187
+ this.contractStorageUpdateRequests.push(
188
+ new ContractStorageUpdateRequest(slot, value, this.sideEffectCounter, storageAddress),
189
+ );
190
+ this.log.debug(`SSTORE cnt: ${this.sideEffectCounter} val: ${value} slot: ${slot}`);
191
+ this.incrementSideEffectCounter();
192
+ }
193
+
194
+ // TODO(8287): _exists can be removed once we have the vm properly handling the equality check
195
+ public traceNoteHashCheck(_storageAddress: Fr, noteHash: Fr, leafIndex: Fr, exists: boolean) {
196
+ // NOTE: storageAddress is unused because noteHash is an already-siloed leaf
197
+ if (
198
+ this.noteHashReadRequests.length + this.previousValidationRequestArrayLengths.noteHashReadRequests >=
199
+ MAX_NOTE_HASH_READ_REQUESTS_PER_TX
200
+ ) {
201
+ throw new SideEffectLimitReachedError('note hash read request', MAX_NOTE_HASH_READ_REQUESTS_PER_TX);
202
+ }
203
+
204
+ this.noteHashReadRequests.push(new TreeLeafReadRequest(noteHash, leafIndex));
205
+ this.avmCircuitHints.noteHashExists.items.push(
206
+ new AvmKeyValueHint(/*key=*/ new Fr(leafIndex), /*value=*/ exists ? Fr.ONE : Fr.ZERO),
207
+ );
208
+ // NOTE: counter does not increment for note hash checks (because it doesn't rely on pending note hashes)
209
+ }
210
+
211
+ public traceNewNoteHash(storageAddress: Fr, noteHash: Fr) {
212
+ if (this.noteHashes.length + this.previousAccumulatedDataArrayLengths.noteHashes >= MAX_NOTE_HASHES_PER_TX) {
213
+ throw new SideEffectLimitReachedError('note hash', MAX_NOTE_HASHES_PER_TX);
214
+ }
215
+
216
+ this.noteHashes.push(new NoteHash(noteHash, this.sideEffectCounter).scope(AztecAddress.fromField(storageAddress)));
217
+ this.log.debug(`NEW_NOTE_HASH cnt: ${this.sideEffectCounter}`);
218
+ this.incrementSideEffectCounter();
219
+ }
220
+
221
+ public traceNullifierCheck(storageAddress: Fr, nullifier: Fr, _leafIndex: Fr, exists: boolean, _isPending: boolean) {
222
+ // NOTE: isPending and leafIndex are unused for now but may be used for optimizations or kernel hints later
223
+ this.enforceLimitOnNullifierChecks();
224
+
225
+ const readRequest = new ReadRequest(nullifier, this.sideEffectCounter).scope(
226
+ AztecAddress.fromField(storageAddress),
227
+ );
228
+ if (exists) {
229
+ this.nullifierReadRequests.push(readRequest);
230
+ } else {
231
+ this.nullifierNonExistentReadRequests.push(readRequest);
232
+ }
233
+ this.avmCircuitHints.nullifierExists.items.push(
234
+ new AvmKeyValueHint(/*key=*/ new Fr(this.sideEffectCounter), /*value=*/ new Fr(exists ? 1 : 0)),
235
+ );
236
+ this.log.debug(`NULLIFIER_EXISTS cnt: ${this.sideEffectCounter}`);
237
+ this.incrementSideEffectCounter();
238
+ }
239
+
240
+ public traceNewNullifier(storageAddress: Fr, nullifier: Fr) {
241
+ if (this.nullifiers.length + this.previousAccumulatedDataArrayLengths.nullifiers >= MAX_NULLIFIERS_PER_TX) {
242
+ throw new SideEffectLimitReachedError('nullifier', MAX_NULLIFIERS_PER_TX);
243
+ }
244
+
245
+ this.nullifiers.push(
246
+ new Nullifier(nullifier, this.sideEffectCounter, /*noteHash=*/ Fr.ZERO).scope(
247
+ AztecAddress.fromField(storageAddress),
248
+ ),
249
+ );
250
+ this.log.debug(`NEW_NULLIFIER cnt: ${this.sideEffectCounter}`);
251
+ this.incrementSideEffectCounter();
252
+ }
253
+
254
+ // TODO(8287): _exists can be removed once we have the vm properly handling the equality check
255
+ public traceL1ToL2MessageCheck(_contractAddress: Fr, msgHash: Fr, msgLeafIndex: Fr, exists: boolean) {
256
+ // NOTE: contractAddress is unused because msgHash is an already-siloed leaf
257
+ if (
258
+ this.l1ToL2MsgReadRequests.length + this.previousValidationRequestArrayLengths.l1ToL2MsgReadRequests >=
259
+ MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_TX
260
+ ) {
261
+ throw new SideEffectLimitReachedError('l1 to l2 message read request', MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_TX);
262
+ }
263
+
264
+ this.l1ToL2MsgReadRequests.push(new TreeLeafReadRequest(msgHash, msgLeafIndex));
265
+ this.avmCircuitHints.l1ToL2MessageExists.items.push(
266
+ new AvmKeyValueHint(/*key=*/ new Fr(msgLeafIndex), /*value=*/ exists ? Fr.ONE : Fr.ZERO),
267
+ );
268
+ }
269
+
270
+ public traceNewL2ToL1Message(contractAddress: Fr, recipient: Fr, content: Fr) {
271
+ if (this.l2ToL1Msgs.length + this.previousAccumulatedDataArrayLengths.l2ToL1Msgs >= MAX_L2_TO_L1_MSGS_PER_TX) {
272
+ throw new SideEffectLimitReachedError('l2 to l1 message', MAX_L2_TO_L1_MSGS_PER_TX);
273
+ }
274
+
275
+ const recipientAddress = EthAddress.fromField(recipient);
276
+ this.l2ToL1Msgs.push(
277
+ new L2ToL1Message(recipientAddress, content, this.sideEffectCounter).scope(
278
+ AztecAddress.fromField(contractAddress),
279
+ ),
280
+ );
281
+ this.log.debug(`NEW_L2_TO_L1_MSG cnt: ${this.sideEffectCounter}`);
282
+ this.incrementSideEffectCounter();
283
+ }
284
+
285
+ public traceUnencryptedLog(contractAddress: Fr, log: Fr[]) {
286
+ if (
287
+ this.unencryptedLogs.length + this.previousAccumulatedDataArrayLengths.unencryptedLogsHashes >=
288
+ MAX_UNENCRYPTED_LOGS_PER_TX
289
+ ) {
290
+ throw new SideEffectLimitReachedError('unencrypted log', MAX_UNENCRYPTED_LOGS_PER_TX);
291
+ }
292
+
293
+ const ulog = new UnencryptedL2Log(
294
+ AztecAddress.fromField(contractAddress),
295
+ Buffer.concat(log.map(f => f.toBuffer())),
296
+ );
297
+ const basicLogHash = Fr.fromBuffer(ulog.hash());
298
+ this.unencryptedLogs.push(ulog);
299
+ // This length is for charging DA and is checked on-chain - has to be length of log preimage + 4 bytes.
300
+ // The .length call also has a +4 but that is unrelated
301
+ this.unencryptedLogsHashes.push(
302
+ new LogHash(basicLogHash, this.sideEffectCounter, new Fr(ulog.length + 4)).scope(
303
+ AztecAddress.fromField(contractAddress),
304
+ ),
305
+ );
306
+ this.log.debug(`NEW_UNENCRYPTED_LOG cnt: ${this.sideEffectCounter}`);
307
+ this.incrementSideEffectCounter();
308
+ }
309
+
310
+ public traceGetContractInstance(instance: TracedContractInstance) {
311
+ this.enforceLimitOnNullifierChecks('(contract address nullifier from GETCONTRACTINSTANCE)');
312
+ // TODO(dbanks12): should emit a nullifier read request
313
+
314
+ this.avmCircuitHints.contractInstances.items.push(
315
+ new AvmContractInstanceHint(
316
+ instance.address,
317
+ new Fr(instance.exists ? 1 : 0),
318
+ instance.salt,
319
+ instance.deployer,
320
+ instance.contractClassId,
321
+ instance.initializationHash,
322
+ instance.publicKeys,
323
+ ),
324
+ );
325
+ this.log.debug(`CONTRACT_INSTANCE cnt: ${this.sideEffectCounter}`);
326
+ this.incrementSideEffectCounter();
327
+ }
328
+
329
+ /**
330
+ * Trace a nested call.
331
+ * Accept some results from a finished nested call's trace into this one.
332
+ */
333
+ public traceNestedCall(
334
+ /** The trace of the nested call. */
335
+ nestedCallTrace: this,
336
+ /** The execution environment of the nested call. */
337
+ _nestedEnvironment: AvmExecutionEnvironment,
338
+ /** How much gas was available for this public execution. */
339
+ startGasLeft: Gas,
340
+ /** How much gas was left after this public execution. */
341
+ endGasLeft: Gas,
342
+ /** Bytecode used for this execution. */
343
+ _bytecode: Buffer,
344
+ /** The call's results */
345
+ avmCallResults: AvmContractCallResult,
346
+ /** Function name for logging */
347
+ _functionName: string = 'unknown',
348
+ ) {
349
+ // Store end side effect counter before it gets updated by absorbing nested call trace
350
+ const endSideEffectCounter = new Fr(this.sideEffectCounter);
351
+
352
+ // TODO(4805): check if some threshold is reached for max nested calls (to unique contracts?)
353
+ // TODO(dbanks12): should emit a nullifier read request. There should be two thresholds.
354
+ // one for max unique contract calls, and another based on max nullifier reads.
355
+ // Since this trace function happens _after_ a nested call, such threshold limits must take
356
+ // place in another trace function that occurs _before_ a nested call.
357
+ if (avmCallResults.reverted) {
358
+ this.absorbRevertedNestedTrace(nestedCallTrace);
359
+ } else {
360
+ this.absorbSuccessfulNestedTrace(nestedCallTrace);
361
+ }
362
+
363
+ const gasUsed = new Gas(startGasLeft.daGas - endGasLeft.daGas, startGasLeft.l2Gas - endGasLeft.l2Gas);
364
+
365
+ this.avmCircuitHints.externalCalls.items.push(
366
+ new AvmExternalCallHint(
367
+ /*success=*/ new Fr(avmCallResults.reverted ? 0 : 1),
368
+ avmCallResults.output,
369
+ gasUsed,
370
+ endSideEffectCounter,
371
+ ),
372
+ );
373
+ }
374
+
375
+ public absorbSuccessfulNestedTrace(nestedTrace: this) {
376
+ this.sideEffectCounter = nestedTrace.sideEffectCounter;
377
+ this.contractStorageReads.push(...nestedTrace.contractStorageReads);
378
+ this.contractStorageUpdateRequests.push(...nestedTrace.contractStorageUpdateRequests);
379
+ this.noteHashReadRequests.push(...nestedTrace.noteHashReadRequests);
380
+ this.noteHashes.push(...nestedTrace.noteHashes);
381
+ this.nullifierReadRequests.push(...nestedTrace.nullifierReadRequests);
382
+ this.nullifierNonExistentReadRequests.push(...nestedTrace.nullifierNonExistentReadRequests);
383
+ this.nullifiers.push(...nestedTrace.nullifiers);
384
+ this.l1ToL2MsgReadRequests.push(...nestedTrace.l1ToL2MsgReadRequests);
385
+ this.l2ToL1Msgs.push(...nestedTrace.l2ToL1Msgs);
386
+ this.unencryptedLogs.push(...nestedTrace.unencryptedLogs);
387
+ this.unencryptedLogsHashes.push(...nestedTrace.unencryptedLogsHashes);
388
+ }
389
+
390
+ public absorbRevertedNestedTrace(nestedTrace: this) {
391
+ // All read requests, and any writes (storage & nullifiers) that
392
+ // require complex validation in public kernel (with end lifetimes)
393
+ // must be absorbed even on revert.
394
+
395
+ // TODO(dbanks12): What should happen to side effect counter on revert?
396
+ this.sideEffectCounter = nestedTrace.sideEffectCounter;
397
+ this.contractStorageReads.push(...nestedTrace.contractStorageReads);
398
+ this.contractStorageUpdateRequests.push(...nestedTrace.contractStorageUpdateRequests);
399
+ this.noteHashReadRequests.push(...nestedTrace.noteHashReadRequests);
400
+ // new noteHashes are tossed on revert
401
+ this.nullifierReadRequests.push(...nestedTrace.nullifierReadRequests);
402
+ this.nullifierNonExistentReadRequests.push(...nestedTrace.nullifierNonExistentReadRequests);
403
+ this.nullifiers.push(...nestedTrace.nullifiers);
404
+ this.l1ToL2MsgReadRequests.push(...nestedTrace.l1ToL2MsgReadRequests);
405
+ // new l2-to-l1 messages are tossed on revert
406
+ // new unencrypted logs are tossed on revert
407
+ }
408
+
409
+ public getSideEffects(): SideEffects {
410
+ return {
411
+ contractStorageReads: this.contractStorageReads,
412
+ contractStorageUpdateRequests: this.contractStorageUpdateRequests,
413
+ noteHashReadRequests: this.noteHashReadRequests,
414
+ noteHashes: this.noteHashes,
415
+ nullifierReadRequests: this.nullifierReadRequests,
416
+ nullifierNonExistentReadRequests: this.nullifierNonExistentReadRequests,
417
+ nullifiers: this.nullifiers,
418
+ l1ToL2MsgReadRequests: this.l1ToL2MsgReadRequests,
419
+ l2ToL1Msgs: this.l2ToL1Msgs,
420
+ unencryptedLogs: this.unencryptedLogs,
421
+ unencryptedLogsHashes: this.unencryptedLogsHashes,
422
+ };
423
+ }
424
+
425
+ public toVMCircuitPublicInputs(
426
+ /** Constants. */
427
+ constants: CombinedConstantData,
428
+ /** The execution environment of the nested call. */
429
+ avmEnvironment: AvmExecutionEnvironment,
430
+ /** How much gas was available for this public execution. */
431
+ startGasLeft: Gas,
432
+ /** How much gas was left after this public execution. */
433
+ endGasLeft: Gas,
434
+ /** The call's results */
435
+ avmCallResults: AvmContractCallResult,
436
+ ): VMCircuitPublicInputs {
437
+ return new VMCircuitPublicInputs(
438
+ /*constants=*/ constants,
439
+ /*callRequest=*/ createPublicCallRequest(avmEnvironment),
440
+ /*publicCallStack=*/ makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, PublicInnerCallRequest.empty),
441
+ /*previousValidationRequestArrayLengths=*/ this.previousValidationRequestArrayLengths,
442
+ /*validationRequests=*/ this.getValidationRequests(),
443
+ /*previousAccumulatedDataArrayLengths=*/ this.previousAccumulatedDataArrayLengths,
444
+ /*accumulatedData=*/ this.getAccumulatedData(startGasLeft.sub(endGasLeft)),
445
+ /*startSideEffectCounter=*/ this.startSideEffectCounter,
446
+ /*endSideEffectCounter=*/ this.sideEffectCounter,
447
+ /*startGasLeft=*/ startGasLeft,
448
+ // TODO(dbanks12): should have endGasLeft
449
+ /*transactionFee=*/ avmEnvironment.transactionFee,
450
+ /*reverted=*/ avmCallResults.reverted,
451
+ );
452
+ }
453
+
454
+ public getUnencryptedLogs() {
455
+ return this.unencryptedLogs;
456
+ }
457
+
458
+ public getAvmCircuitHints() {
459
+ return this.avmCircuitHints;
460
+ }
461
+
462
+ private getValidationRequests() {
463
+ return new PublicValidationRequests(
464
+ RollupValidationRequests.empty(), // TODO(dbanks12): what should this be?
465
+ padArrayEnd(this.noteHashReadRequests, TreeLeafReadRequest.empty(), MAX_NOTE_HASH_READ_REQUESTS_PER_TX),
466
+ padArrayEnd(this.nullifierReadRequests, ScopedReadRequest.empty(), MAX_NULLIFIER_READ_REQUESTS_PER_TX),
467
+ padArrayEnd(
468
+ this.nullifierNonExistentReadRequests,
469
+ ScopedReadRequest.empty(),
470
+ MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX,
471
+ ),
472
+ padArrayEnd(this.l1ToL2MsgReadRequests, TreeLeafReadRequest.empty(), MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_TX),
473
+ // TODO(dbanks12): this is only necessary until VMCircuitPublicInputs uses unsiloed storage slots and pairs storage accesses with contract address
474
+ padArrayEnd(
475
+ this.contractStorageReads.map(r => new PublicDataRead(r.storageSlot, r.currentValue, r.counter)),
476
+ PublicDataRead.empty(),
477
+ MAX_PUBLIC_DATA_READS_PER_TX,
478
+ ),
479
+ );
480
+ }
481
+
482
+ private getAccumulatedData(gasUsed: Gas) {
483
+ return new PublicAccumulatedData(
484
+ padArrayEnd(this.noteHashes, ScopedNoteHash.empty(), MAX_NOTE_HASHES_PER_TX),
485
+ // TODO(dbanks12): should be able to use ScopedNullifier here
486
+ padArrayEnd(
487
+ this.nullifiers.map(n => new Nullifier(n.nullifier.value, n.nullifier.counter, n.nullifier.noteHash)),
488
+ Nullifier.empty(),
489
+ MAX_NULLIFIERS_PER_TX,
490
+ ),
491
+ padArrayEnd(this.l2ToL1Msgs, ScopedL2ToL1Message.empty(), MAX_L2_TO_L1_MSGS_PER_TX),
492
+ /*noteEncryptedLogsHashes=*/ makeTuple(MAX_NOTE_ENCRYPTED_LOGS_PER_TX, LogHash.empty),
493
+ /*encryptedLogsHashes=*/ makeTuple(MAX_ENCRYPTED_LOGS_PER_TX, ScopedLogHash.empty),
494
+ padArrayEnd(this.unencryptedLogsHashes, ScopedLogHash.empty(), MAX_UNENCRYPTED_LOGS_PER_TX),
495
+ // TODO(dbanks12): this is only necessary until VMCircuitPublicInputs uses unsiloed storage slots and pairs storage accesses with contract address
496
+ padArrayEnd(
497
+ this.contractStorageUpdateRequests.map(w => new PublicDataUpdateRequest(w.storageSlot, w.newValue, w.counter)),
498
+ PublicDataUpdateRequest.empty(),
499
+ MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
500
+ ),
501
+ /*publicCallStack=*/ makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, PublicCallRequest.empty),
502
+ /*gasUsed=*/ gasUsed,
503
+ );
504
+ }
505
+
506
+ private enforceLimitOnNullifierChecks(errorMsgOrigin: string = '') {
507
+ // NOTE: Why error if _either_ limit was reached? If user code emits either an existent or non-existent
508
+ // nullifier read request (NULLIFIEREXISTS, GETCONTRACTINSTANCE, *CALL), and one of the limits has been
509
+ // reached (MAX_NULLIFIER_NON_EXISTENT_RRS vs MAX_NULLIFIER_RRS), but not the other, we must prevent the
510
+ // sequencer from lying and saying "this nullifier exists, but MAX_NULLIFIER_RRS has been reached, so I'm
511
+ // going to skip the read request and just revert instead" when the nullifier actually doesn't exist
512
+ // (or vice versa). So, if either maximum has been reached, any nullifier-reading operation must error.
513
+ if (
514
+ this.nullifierReadRequests.length + this.previousValidationRequestArrayLengths.nullifierReadRequests >=
515
+ MAX_NULLIFIER_READ_REQUESTS_PER_TX
516
+ ) {
517
+ throw new SideEffectLimitReachedError(
518
+ `nullifier read request ${errorMsgOrigin}`,
519
+ MAX_NULLIFIER_READ_REQUESTS_PER_TX,
520
+ );
521
+ }
522
+ if (
523
+ this.nullifierNonExistentReadRequests.length +
524
+ this.previousValidationRequestArrayLengths.nullifierNonExistentReadRequests >=
525
+ MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX
526
+ ) {
527
+ throw new SideEffectLimitReachedError(
528
+ `nullifier non-existent read request ${errorMsgOrigin}`,
529
+ MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX,
530
+ );
531
+ }
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Helper function to create a public execution request from an AVM execution environment
537
+ */
538
+ function createPublicCallRequest(avmEnvironment: AvmExecutionEnvironment): PublicCallRequest {
539
+ const callContext = CallContext.from({
540
+ msgSender: avmEnvironment.sender,
541
+ storageContractAddress: avmEnvironment.storageAddress,
542
+ functionSelector: avmEnvironment.functionSelector,
543
+ isDelegateCall: avmEnvironment.isDelegateCall,
544
+ isStaticCall: avmEnvironment.isStaticCall,
545
+ });
546
+ return new PublicCallRequest(
547
+ avmEnvironment.address,
548
+ callContext,
549
+ computeVarArgsHash(avmEnvironment.calldata),
550
+ /*counter=*/ 0,
551
+ );
552
+ }
@@ -23,6 +23,7 @@ import {
23
23
  L2ToL1Message,
24
24
  LogHash,
25
25
  MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL,
26
+ MAX_L2_GAS_PER_ENQUEUED_CALL,
26
27
  MAX_L2_TO_L1_MSGS_PER_CALL,
27
28
  MAX_NOTE_HASHES_PER_CALL,
28
29
  MAX_NOTE_HASH_READ_REQUESTS_PER_CALL,
@@ -61,7 +62,7 @@ import { makeTuple } from '@aztec/foundation/array';
61
62
  import { padArrayEnd } from '@aztec/foundation/collection';
62
63
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
63
64
  import { ProtocolCircuitVks } from '@aztec/noir-protocol-circuits-types';
64
- import { type MerkleTreeOperations } from '@aztec/world-state';
65
+ import { type MerkleTreeReadOperations } from '@aztec/world-state';
65
66
 
66
67
  import { type PublicExecutionResult, accumulatePublicReturnValues, collectExecutionResults } from './execution.js';
67
68
  import { type PublicExecutor } from './executor.js';
@@ -102,7 +103,7 @@ export type EnqueuedCallResult = {
102
103
  export class EnqueuedCallSimulator {
103
104
  private log: DebugLogger;
104
105
  constructor(
105
- private db: MerkleTreeOperations,
106
+ private db: MerkleTreeReadOperations,
106
107
  private publicExecutor: PublicExecutor,
107
108
  private publicKernelSimulator: PublicKernelCircuitSimulator,
108
109
  private globalVariables: GlobalVariables,
@@ -120,39 +121,60 @@ export class EnqueuedCallSimulator {
120
121
  transactionFee: Fr,
121
122
  phase: PublicKernelPhase,
122
123
  ): Promise<EnqueuedCallResult> {
124
+ // Gas allocated to an enqueued call can be different from the available gas
125
+ // if there is more gas available than the max allocation per enqueued call.
126
+ const allocatedGas = new Gas(
127
+ /*daGas=*/ availableGas.daGas,
128
+ /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_ENQUEUED_CALL),
129
+ );
123
130
  const pendingNullifiers = this.getSiloedPendingNullifiers(previousPublicKernelOutput);
124
131
  const startSideEffectCounter = previousPublicKernelOutput.endSideEffectCounter + 1;
132
+
133
+ const prevAccumulatedData =
134
+ phase === PublicKernelPhase.SETUP
135
+ ? previousPublicKernelOutput.endNonRevertibleData
136
+ : previousPublicKernelOutput.end;
137
+ const previousValidationRequestArrayLengths = PublicValidationRequestArrayLengths.new(
138
+ previousPublicKernelOutput.validationRequests,
139
+ );
140
+ const previousAccumulatedDataArrayLengths = PublicAccumulatedDataArrayLengths.new(prevAccumulatedData);
141
+
142
+ // If this is the first enqueued call in public, constants will be empty
143
+ // because private kernel does not expose them.
144
+ const constants = previousPublicKernelOutput.constants.clone();
145
+ constants.globalVariables = this.globalVariables;
146
+
125
147
  const result = await this.publicExecutor.simulate(
126
148
  executionRequest,
127
- this.globalVariables,
128
- availableGas,
149
+ constants,
150
+ allocatedGas,
129
151
  tx.data.constants.txContext,
130
152
  pendingNullifiers,
131
153
  transactionFee,
132
154
  startSideEffectCounter,
155
+ previousValidationRequestArrayLengths,
156
+ previousAccumulatedDataArrayLengths,
133
157
  );
134
158
 
135
159
  const callStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, PublicInnerCallRequest.empty);
136
160
  callStack[0].item.contractAddress = callRequest.contractAddress;
137
161
  callStack[0].item.callContext = callRequest.callContext;
138
162
  callStack[0].item.argsHash = callRequest.argsHash;
139
- const prevAccumulatedData =
140
- phase === PublicKernelPhase.SETUP
141
- ? previousPublicKernelOutput.endNonRevertibleData
142
- : previousPublicKernelOutput.end;
163
+
143
164
  const accumulatedData = PublicAccumulatedData.empty();
144
165
  accumulatedData.publicCallStack[0] = callRequest;
166
+
145
167
  const startVMCircuitOutput = new VMCircuitPublicInputs(
146
168
  previousPublicKernelOutput.constants,
147
169
  callRequest,
148
170
  callStack,
149
- PublicValidationRequestArrayLengths.new(previousPublicKernelOutput.validationRequests),
171
+ previousValidationRequestArrayLengths,
150
172
  PublicValidationRequests.empty(),
151
- PublicAccumulatedDataArrayLengths.new(prevAccumulatedData),
173
+ previousAccumulatedDataArrayLengths,
152
174
  accumulatedData,
153
175
  startSideEffectCounter,
154
176
  startSideEffectCounter,
155
- availableGas,
177
+ allocatedGas,
156
178
  result.transactionFee,
157
179
  result.reverted,
158
180
  );
@@ -1,5 +1,5 @@
1
1
  import {
2
- type MerkleTreeOperations,
2
+ type MerkleTreeReadOperations,
3
3
  type NestedProcessReturnValues,
4
4
  type ProcessedTx,
5
5
  ProvingRequestType,
@@ -28,7 +28,7 @@ import {
28
28
  } from '@aztec/circuits.js';
29
29
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
30
30
  import { Timer } from '@aztec/foundation/timer';
31
- import { ProtocolCircuitVks, getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-types';
31
+ import { ProtocolCircuitVks, TubeVk, getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-types';
32
32
 
33
33
  import { inspect } from 'util';
34
34
 
@@ -93,7 +93,7 @@ export class EnqueuedCallsProcessor {
93
93
  }
94
94
 
95
95
  static create(
96
- db: MerkleTreeOperations,
96
+ db: MerkleTreeReadOperations,
97
97
  publicExecutor: PublicExecutor,
98
98
  publicKernelSimulator: PublicKernelCircuitSimulator,
99
99
  globalVariables: GlobalVariables,
@@ -377,9 +377,7 @@ export class EnqueuedCallsProcessor {
377
377
  // The proof is not used in simulation.
378
378
  const proof = makeEmptyRecursiveProof(NESTED_RECURSIVE_PROOF_LENGTH);
379
379
 
380
- const vk = isFromPrivate
381
- ? ProtocolCircuitVks.PrivateKernelTailToPublicArtifact
382
- : ProtocolCircuitVks.PublicKernelMergeArtifact;
380
+ const vk = isFromPrivate ? TubeVk : ProtocolCircuitVks.PublicKernelMergeArtifact;
383
381
  const vkIndex = getVKIndex(vk);
384
382
  const siblingPath = getVKSiblingPath(vkIndex);
385
383