@aztec/simulator 0.0.1-commit.64b6bbb → 0.0.1-commit.684755437

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 (61) hide show
  1. package/README.md +4 -4
  2. package/dest/private/circuit_recording/circuit_recorder.js +2 -2
  3. package/dest/public/avm/avm_gas.js +3 -3
  4. package/dest/public/avm/opcodes/accrued_substate.d.ts +2 -2
  5. package/dest/public/avm/opcodes/accrued_substate.d.ts.map +1 -1
  6. package/dest/public/avm/opcodes/accrued_substate.js +3 -4
  7. package/dest/public/avm/serialization/bytecode_serialization.js +3 -3
  8. package/dest/public/avm/serialization/instruction_serialization.d.ts +2 -2
  9. package/dest/public/avm/serialization/instruction_serialization.d.ts.map +1 -1
  10. package/dest/public/avm/serialization/instruction_serialization.js +1 -1
  11. package/dest/public/fixtures/amm_test.js +2 -2
  12. package/dest/public/fixtures/opcode_spammer.d.ts +1 -1
  13. package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -1
  14. package/dest/public/fixtures/opcode_spammer.js +6 -6
  15. package/dest/public/fixtures/public_tx_simulation_tester.d.ts +6 -5
  16. package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
  17. package/dest/public/fixtures/public_tx_simulation_tester.js +36 -9
  18. package/dest/public/fixtures/utils.d.ts +2 -2
  19. package/dest/public/fixtures/utils.d.ts.map +1 -1
  20. package/dest/public/fixtures/utils.js +6 -6
  21. package/dest/public/hinting_db_sources.d.ts +2 -2
  22. package/dest/public/hinting_db_sources.d.ts.map +1 -1
  23. package/dest/public/hinting_db_sources.js +1 -1
  24. package/dest/public/public_processor/guarded_merkle_tree.d.ts +2 -2
  25. package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
  26. package/dest/public/public_processor/guarded_merkle_tree.js +1 -1
  27. package/dest/public/public_processor/public_processor.d.ts +5 -3
  28. package/dest/public/public_processor/public_processor.d.ts.map +1 -1
  29. package/dest/public/public_processor/public_processor.js +43 -31
  30. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts +1 -1
  31. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts.map +1 -1
  32. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.js +2 -3
  33. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts +1 -1
  34. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts.map +1 -1
  35. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.js +2 -2
  36. package/dest/public/public_tx_simulator/factories.d.ts +2 -2
  37. package/dest/public/public_tx_simulator/factories.d.ts.map +1 -1
  38. package/dest/public/public_tx_simulator/factories.js +2 -2
  39. package/dest/public/public_tx_simulator/public_tx_simulator.js +2 -2
  40. package/dest/public/test_executor_metrics.d.ts +6 -1
  41. package/dest/public/test_executor_metrics.d.ts.map +1 -1
  42. package/dest/public/test_executor_metrics.js +22 -0
  43. package/package.json +16 -16
  44. package/src/private/circuit_recording/circuit_recorder.ts +2 -2
  45. package/src/public/avm/avm_gas.ts +2 -2
  46. package/src/public/avm/opcodes/accrued_substate.ts +3 -4
  47. package/src/public/avm/serialization/bytecode_serialization.ts +2 -2
  48. package/src/public/avm/serialization/instruction_serialization.ts +1 -1
  49. package/src/public/fixtures/amm_test.ts +2 -2
  50. package/src/public/fixtures/opcode_spammer.ts +6 -10
  51. package/src/public/fixtures/public_tx_simulation_tester.ts +51 -5
  52. package/src/public/fixtures/utils.ts +6 -5
  53. package/src/public/fuzzing/avm_fuzzer_simulator.ts +1 -1
  54. package/src/public/hinting_db_sources.ts +1 -1
  55. package/src/public/public_processor/guarded_merkle_tree.ts +1 -1
  56. package/src/public/public_processor/public_processor.ts +57 -42
  57. package/src/public/public_tx_simulator/cpp_public_tx_simulator.ts +2 -3
  58. package/src/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.ts +2 -2
  59. package/src/public/public_tx_simulator/factories.ts +2 -1
  60. package/src/public/public_tx_simulator/public_tx_simulator.ts +3 -3
  61. package/src/public/test_executor_metrics.ts +24 -0
@@ -110,7 +110,7 @@ const BASE_GAS_COSTS: Record<Opcode, Gas> = {
110
110
  [Opcode.NULLIFIEREXISTS]: makeCost(c.AVM_NULLIFIEREXISTS_BASE_L2_GAS, 0),
111
111
  [Opcode.EMITNULLIFIER]: makeCost(c.AVM_EMITNULLIFIER_BASE_L2_GAS, c.AVM_EMITNULLIFIER_BASE_DA_GAS),
112
112
  [Opcode.L1TOL2MSGEXISTS]: makeCost(c.AVM_L1TOL2MSGEXISTS_BASE_L2_GAS, 0),
113
- [Opcode.EMITUNENCRYPTEDLOG]: makeCost(c.AVM_EMITUNENCRYPTEDLOG_BASE_L2_GAS, c.AVM_EMITUNENCRYPTEDLOG_BASE_DA_GAS),
113
+ [Opcode.EMITPUBLICLOG]: makeCost(c.AVM_EMITPUBLICLOG_BASE_L2_GAS, c.AVM_EMITPUBLICLOG_BASE_DA_GAS),
114
114
  [Opcode.SENDL2TOL1MSG]: makeCost(c.AVM_SENDL2TOL1MSG_BASE_L2_GAS, c.AVM_SENDL2TOL1MSG_BASE_DA_GAS),
115
115
  [Opcode.GETCONTRACTINSTANCE]: makeCost(c.AVM_GETCONTRACTINSTANCE_BASE_L2_GAS, 0),
116
116
  [Opcode.CALL]: makeCost(c.AVM_CALL_BASE_L2_GAS, 0),
@@ -130,7 +130,7 @@ const DYNAMIC_GAS_COSTS = new Map<Opcode, Gas>([
130
130
  [Opcode.CALLDATACOPY, makeCost(c.AVM_CALLDATACOPY_DYN_L2_GAS, 0)],
131
131
  [Opcode.RETURNDATACOPY, makeCost(c.AVM_RETURNDATACOPY_DYN_L2_GAS, 0)],
132
132
  // TODO: Call and static call based on bytecode length
133
- [Opcode.EMITUNENCRYPTEDLOG, makeCost(c.AVM_EMITUNENCRYPTEDLOG_DYN_L2_GAS, c.AVM_EMITUNENCRYPTEDLOG_DYN_DA_GAS)],
133
+ [Opcode.EMITPUBLICLOG, makeCost(c.AVM_EMITPUBLICLOG_DYN_L2_GAS, c.AVM_EMITPUBLICLOG_DYN_DA_GAS)],
134
134
  [Opcode.TORADIXBE, makeCost(c.AVM_TORADIXBE_DYN_L2_GAS, 0)],
135
135
  [Opcode.AND_8, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
136
136
  [Opcode.AND_16, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
@@ -204,10 +204,9 @@ export class L1ToL2MessageExists extends Instruction {
204
204
  }
205
205
  }
206
206
 
207
- export class EmitUnencryptedLog extends Instruction {
208
- // TODO(#11124): rename unencrypted -> public
209
- static type: string = 'EMITUNENCRYPTEDLOG';
210
- static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG;
207
+ export class EmitPublicLog extends Instruction {
208
+ static type: string = 'EMITPUBLICLOG';
209
+ static readonly opcode: Opcode = Opcode.EMITPUBLICLOG;
211
210
  // Informs (de)serialization. See Instruction.deserialize.
212
211
  static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT16, OperandType.UINT16];
213
212
 
@@ -19,7 +19,7 @@ import {
19
19
  EcAdd,
20
20
  EmitNoteHash,
21
21
  EmitNullifier,
22
- EmitUnencryptedLog,
22
+ EmitPublicLog,
23
23
  Eq,
24
24
  FieldDiv,
25
25
  GetContractInstance,
@@ -129,7 +129,7 @@ export const INSTRUCTION_SET = new Map<Opcode, InstructionDeserializer>([
129
129
  [L1ToL2MessageExists.opcode, Instruction.fromBuffer.bind(L1ToL2MessageExists)], // Messages
130
130
 
131
131
  // Accrued Substate
132
- [EmitUnencryptedLog.opcode, Instruction.fromBuffer.bind(EmitUnencryptedLog)],
132
+ [EmitPublicLog.opcode, Instruction.fromBuffer.bind(EmitPublicLog)],
133
133
  [SendL2ToL1Message.opcode, Instruction.fromBuffer.bind(SendL2ToL1Message)],
134
134
  [GetContractInstance.opcode, Instruction.fromBuffer.bind(GetContractInstance)],
135
135
 
@@ -70,7 +70,7 @@ export enum Opcode {
70
70
  EMITNULLIFIER,
71
71
  L1TOL2MSGEXISTS,
72
72
  GETCONTRACTINSTANCE,
73
- EMITUNENCRYPTEDLOG,
73
+ EMITPUBLICLOG,
74
74
  SENDL2TOL1MSG,
75
75
  // External calls
76
76
  CALL,
@@ -1,4 +1,4 @@
1
- import { GeneratorIndex } from '@aztec/constants';
1
+ import { DomainSeparator } from '@aztec/constants';
2
2
  import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import type { Logger } from '@aztec/foundation/log';
@@ -326,6 +326,6 @@ async function removeLiquidity(
326
326
  async function computePartialNoteValidityCommitment(partialNote: { commitment: Fr }, completer: AztecAddress) {
327
327
  return await poseidon2HashWithSeparator(
328
328
  [partialNote.commitment, completer],
329
- GeneratorIndex.PARTIAL_NOTE_VALIDITY_COMMITMENT,
329
+ DomainSeparator.PARTIAL_NOTE_VALIDITY_COMMITMENT,
330
330
  );
331
331
  }
@@ -143,7 +143,7 @@
143
143
  * - `EMITNOTEHASH`: max 64 per TX
144
144
  * - `EMITNULLIFIER`: max 63 per TX (one reserved for TX nullifier)
145
145
  * - `SENDL2TOL1MSG`: max 8 per TX
146
- * - `EMITUNENCRYPTEDLOG`: limited by total log payload size
146
+ * - `EMITPUBLICLOG`: limited by total log payload size
147
147
  *
148
148
  * By having the inner contract REVERT after emitting side effects, those effects are discarded, allowing the outer contract to call it again. This enables thousands of opcode executions per TX instead of just the limit.
149
149
  *
@@ -182,7 +182,7 @@ import {
182
182
  EcAdd,
183
183
  EmitNoteHash,
184
184
  EmitNullifier,
185
- EmitUnencryptedLog,
185
+ EmitPublicLog,
186
186
  Eq,
187
187
  FieldDiv,
188
188
  GetContractInstance,
@@ -1242,17 +1242,15 @@ export const SPAM_CONFIGS: Partial<Record<Opcode, SpamConfig[]>> = {
1242
1242
  },
1243
1243
  ],
1244
1244
 
1245
- // EMITUNENCRYPTEDLOG - two configs: minimal (many small logs) and max-size (one large log)
1246
- [Opcode.EMITUNENCRYPTEDLOG]: [
1245
+ // EMITPUBLICLOG - two configs: minimal (many small logs) and max-size (one large log)
1246
+ [Opcode.EMITPUBLICLOG]: [
1247
1247
  {
1248
1248
  label: 'Many empty logs, revert, repeat',
1249
1249
  setup: [
1250
1250
  { offset: 0, value: new Uint32(0n) }, // logSize = 0 fields (minimal)
1251
1251
  { offset: 1, value: new Uint32(0n) }, // revertSize
1252
1252
  ],
1253
- targetInstructions: () => [
1254
- new EmitUnencryptedLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 1),
1255
- ], // logOffset doesn't matter when size is 0
1253
+ targetInstructions: () => [new EmitPublicLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 1)], // logOffset doesn't matter when size is 0
1256
1254
  cleanupInstructions: () => [
1257
1255
  new Revert(/*addressing_mode=*/ 0, /*retSizeOffset=*/ 1, /*returnOffset=*/ 0).as(
1258
1256
  Opcode.REVERT_8,
@@ -1276,9 +1274,7 @@ export const SPAM_CONFIGS: Partial<Record<Opcode, SpamConfig[]>> = {
1276
1274
  // value: new Field(0n),
1277
1275
  //})),
1278
1276
  ],
1279
- targetInstructions: () => [
1280
- new EmitUnencryptedLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 2),
1281
- ], // uses logOffset 2 (uninitialized Field(0))
1277
+ targetInstructions: () => [new EmitPublicLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 2)], // uses logOffset 2 (uninitialized Field(0))
1282
1278
  cleanupInstructions: () => [
1283
1279
  new Revert(/*addressing_mode=*/ 0, /*retSizeOffset=*/ 1, /*returnOffset=*/ 0).as(
1284
1280
  Opcode.REVERT_8,
@@ -1,4 +1,9 @@
1
- import { DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT } from '@aztec/constants';
1
+ import {
2
+ DEFAULT_TEARDOWN_DA_GAS_LIMIT,
3
+ DEFAULT_TEARDOWN_L2_GAS_LIMIT,
4
+ PUBLIC_TX_L2_GAS_OVERHEAD,
5
+ TX_DA_GAS_OVERHEAD,
6
+ } from '@aztec/constants';
2
7
  import { asyncMap } from '@aztec/foundation/async-map';
3
8
  import { BlockNumber } from '@aztec/foundation/branded-types';
4
9
  import { Fr } from '@aztec/foundation/curves/bn254';
@@ -112,6 +117,7 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
112
117
  feePayer: AztecAddress = sender,
113
118
  /* need some unique first nullifier for note-nonce computations */
114
119
  privateInsertions: TestPrivateInsertions = { nonRevertible: { nullifiers: [new Fr(420000 + this.txCount)] } },
120
+ gasLimits?: Gas,
115
121
  ): Promise<Tx> {
116
122
  const setupCallRequests = await asyncMap(setupCalls, call =>
117
123
  this.#createPubicCallRequestForCall(call, call.sender ?? sender),
@@ -131,9 +137,13 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
131
137
  teardownCallRequest,
132
138
  feePayer,
133
139
  /*gasUsedByPrivate*/ teardownCall
134
- ? new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT)
135
- : Gas.empty(),
140
+ ? new Gas(
141
+ DEFAULT_TEARDOWN_DA_GAS_LIMIT + TX_DA_GAS_OVERHEAD,
142
+ DEFAULT_TEARDOWN_L2_GAS_LIMIT + PUBLIC_TX_L2_GAS_OVERHEAD,
143
+ )
144
+ : new Gas(TX_DA_GAS_OVERHEAD, PUBLIC_TX_L2_GAS_OVERHEAD),
136
145
  defaultGlobals(),
146
+ gasLimits,
137
147
  );
138
148
  }
139
149
 
@@ -146,8 +156,9 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
146
156
  /* need some unique first nullifier for note-nonce computations */
147
157
  privateInsertions?: TestPrivateInsertions,
148
158
  txLabel: string = 'unlabeledTx',
159
+ gasLimits?: Gas,
149
160
  ): Promise<PublicTxResult> {
150
- const tx = await this.createTx(sender, setupCalls, appCalls, teardownCall, feePayer, privateInsertions);
161
+ const tx = await this.createTx(sender, setupCalls, appCalls, teardownCall, feePayer, privateInsertions, gasLimits);
151
162
 
152
163
  await this.setFeePayerBalance(feePayer);
153
164
 
@@ -161,6 +172,8 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
161
172
  }
162
173
  const avmResult = await this.simulator.simulate(tx, fullTxLabel);
163
174
 
175
+ await this.#recordBytecodeSizes(fullTxLabel, [...setupCalls, ...appCalls, ...(teardownCall ? [teardownCall] : [])]);
176
+
164
177
  // Something like this is often useful for debugging:
165
178
  //if (avmResult.revertReason) {
166
179
  // // resolve / enrich revert reason
@@ -190,8 +203,18 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
190
203
  teardownCall?: TestEnqueuedCall,
191
204
  feePayer?: AztecAddress,
192
205
  privateInsertions?: TestPrivateInsertions,
206
+ gasLimits?: Gas,
193
207
  ): Promise<PublicTxResult> {
194
- return await this.simulateTx(sender, setupCalls, appCalls, teardownCall, feePayer, privateInsertions, txLabel);
208
+ return await this.simulateTx(
209
+ sender,
210
+ setupCalls,
211
+ appCalls,
212
+ teardownCall,
213
+ feePayer,
214
+ privateInsertions,
215
+ txLabel,
216
+ gasLimits,
217
+ );
195
218
  }
196
219
 
197
220
  /**
@@ -209,6 +232,7 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
209
232
  teardownCall?: TestEnqueuedCall,
210
233
  feePayer?: AztecAddress,
211
234
  privateInsertions?: TestPrivateInsertions,
235
+ gasLimits?: Gas,
212
236
  ): Promise<PublicTxResult> {
213
237
  return await this.simulateTxWithLabel(
214
238
  txLabel,
@@ -218,6 +242,7 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
218
242
  teardownCall,
219
243
  feePayer,
220
244
  privateInsertions,
245
+ gasLimits,
221
246
  );
222
247
  }
223
248
 
@@ -277,6 +302,27 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
277
302
 
278
303
  return new PublicCallRequestWithCalldata(request, calldata);
279
304
  }
305
+
306
+ // WARNING: Deduplicates by artifact name, so two different artifacts with the same name
307
+ // in a single tx would only record the first one's bytecode size.
308
+ async #recordBytecodeSizes(txLabel: string, calls: TestEnqueuedCall[]) {
309
+ const seenArtifactNames = new Set<string>();
310
+ for (const call of calls) {
311
+ const artifact = await this.contractDataSource.getContractArtifact(call.address);
312
+ if (!artifact || seenArtifactNames.has(artifact.name)) {
313
+ continue;
314
+ }
315
+ seenArtifactNames.add(artifact.name);
316
+ const instance = await this.contractDataSource.getContract(call.address);
317
+ if (!instance) {
318
+ continue;
319
+ }
320
+ const contractClass = await this.contractDataSource.getContractClass(instance.currentContractClassId);
321
+ if (contractClass) {
322
+ this.metrics.recordBytecodeSize(txLabel, artifact.name, contractClass.packedBytecode.length);
323
+ }
324
+ }
325
+ }
280
326
  }
281
327
 
282
328
  export function defaultGlobals() {
@@ -62,13 +62,14 @@ export async function createTxForPublicCalls(
62
62
  feePayer = AztecAddress.zero(),
63
63
  gasUsedByPrivate: Gas = Gas.empty(),
64
64
  globals: GlobalVariables = GlobalVariables.empty(),
65
+ gasLimits?: Gas,
65
66
  ): Promise<Tx> {
66
67
  assert(
67
68
  setupCallRequests.length > 0 || appCallRequests.length > 0 || teardownCallRequest !== undefined,
68
69
  "Can't create public tx with no enqueued calls",
69
70
  );
70
71
  // use max limits
71
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
72
+ gasLimits = gasLimits ?? new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
72
73
 
73
74
  const forPublic = PartialPrivateTailPublicInputsForPublic.empty();
74
75
 
@@ -134,13 +135,13 @@ export async function createTxForPublicCalls(
134
135
  const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
135
136
  const header = BlockHeader.empty({ globalVariables: globals });
136
137
  const constantData = new TxConstantData(header, txContext, Fr.zero(), Fr.zero());
137
- const includeByTimestamp = 0n; // Not used in the simulator.
138
+ const expirationTimestamp = 0n; // Not used in the simulator.
138
139
 
139
140
  const txData = new PrivateKernelTailCircuitPublicInputs(
140
141
  constantData,
141
142
  /*gasUsed=*/ gasUsedByPrivate,
142
143
  feePayer,
143
- includeByTimestamp,
144
+ expirationTimestamp,
144
145
  forPublic,
145
146
  );
146
147
 
@@ -171,13 +172,13 @@ export async function createTxForPrivateOnly(
171
172
  const gasSettings = new GasSettings(gasLimits, Gas.empty(), maxFeesPerGas, GasFees.empty());
172
173
  const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
173
174
  const constantData = new TxConstantData(BlockHeader.empty(), txContext, Fr.zero(), Fr.zero());
174
- const includeByTimestamp = 0n; // Not used in the simulator.
175
+ const expirationTimestamp = 0n; // Not used in the simulator.
175
176
 
176
177
  const txData = new PrivateKernelTailCircuitPublicInputs(
177
178
  constantData,
178
179
  /*gasUsed=*/ gasUsedByPrivate,
179
180
  feePayer,
180
- includeByTimestamp,
181
+ expirationTimestamp,
181
182
  /*forPublic=*/ undefined,
182
183
  forRollup,
183
184
  );
@@ -146,7 +146,7 @@ async function createTxFromHint(cppTx: AvmTxHint): Promise<Tx> {
146
146
  constants,
147
147
  cppTx.gasUsedByPrivate,
148
148
  cppTx.feePayer,
149
- 0n, // includeByTimestamp
149
+ 0n, // expirationTimestamp
150
150
  forPublic,
151
151
  undefined, // forRollup - not needed for public simulation
152
152
  );
@@ -572,7 +572,7 @@ export class HintingMerkleWriteOperations implements MerkleTreeWriteOperations {
572
572
  return await this.db.close();
573
573
  }
574
574
 
575
- async [Symbol.dispose](): Promise<void> {
575
+ async [Symbol.asyncDispose](): Promise<void> {
576
576
  await this.close();
577
577
  }
578
578
 
@@ -82,7 +82,7 @@ export class GuardedMerkleTreeOperations implements MerkleTreeWriteOperations {
82
82
  return this.guardAndPush(() => this.target.close());
83
83
  }
84
84
 
85
- async [Symbol.dispose](): Promise<void> {
85
+ async [Symbol.asyncDispose](): Promise<void> {
86
86
  await this.close();
87
87
  }
88
88
  getTreeInfo(treeId: MerkleTreeId): Promise<TreeInfo> {
@@ -25,6 +25,7 @@ import type {
25
25
  PublicProcessorValidator,
26
26
  SequencerConfig,
27
27
  } from '@aztec/stdlib/interfaces/server';
28
+ import { type DebugLog, type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
28
29
  import { ProvingRequestType } from '@aztec/stdlib/proofs';
29
30
  import { MerkleTreeId } from '@aztec/stdlib/trees';
30
31
  import {
@@ -130,7 +131,6 @@ class PublicProcessorTimeoutError extends Error {
130
131
  */
131
132
  export class PublicProcessor implements Traceable {
132
133
  private metrics: PublicProcessorMetrics;
133
-
134
134
  constructor(
135
135
  protected globalVariables: GlobalVariables,
136
136
  private guardedMerkleTree: GuardedMerkleTreeOperations,
@@ -140,6 +140,7 @@ export class PublicProcessor implements Traceable {
140
140
  telemetryClient: TelemetryClient = getTelemetryClient(),
141
141
  private log: Logger,
142
142
  private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs' | 'fakeThrowAfterProcessingTxCount'> = {},
143
+ private debugLogStore: DebugLogStore = new NullDebugLogStore(),
143
144
  ) {
144
145
  this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
145
146
  }
@@ -159,12 +160,13 @@ export class PublicProcessor implements Traceable {
159
160
  txs: Iterable<Tx> | AsyncIterable<Tx>,
160
161
  limits: PublicProcessorLimits = {},
161
162
  validator: PublicProcessorValidator = {},
162
- ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], number]> {
163
- const { maxTransactions, maxBlockSize, deadline, maxBlockGas, maxBlobFields } = limits;
163
+ ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
164
+ const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal } = limits;
164
165
  const { preprocessValidator, nullifierCache } = validator;
165
166
  const result: ProcessedTx[] = [];
166
167
  const usedTxs: Tx[] = [];
167
168
  const failed: FailedTx[] = [];
169
+ const debugLogs: DebugLog[] = [];
168
170
  const timer = new Timer();
169
171
 
170
172
  let totalSizeInBytes = 0;
@@ -186,22 +188,23 @@ export class PublicProcessor implements Traceable {
186
188
  break;
187
189
  }
188
190
 
189
- // Skip this tx if it'd exceed max block size
190
191
  const txHash = tx.getTxHash().toString();
191
- const preTxSizeInBytes = tx.getEstimatedPrivateTxEffectsSize();
192
- if (maxBlockSize !== undefined && totalSizeInBytes + preTxSizeInBytes > maxBlockSize) {
193
- this.log.warn(`Skipping processing of tx ${txHash} sized ${preTxSizeInBytes} bytes due to block size limit`, {
194
- txHash,
195
- sizeInBytes: preTxSizeInBytes,
196
- totalSizeInBytes,
197
- maxBlockSize,
198
- });
192
+
193
+ // Skip this tx if its estimated blob fields would exceed the limit.
194
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
195
+ const txBlobFields = tx.getPrivateTxEffectsSizeInFields();
196
+ if (isBuildingProposal && maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
197
+ this.log.warn(
198
+ `Skipping tx ${txHash} with ${txBlobFields} fields from private side effects due to blob fields limit`,
199
+ { txHash, txBlobFields, totalBlobFields, maxBlobFields },
200
+ );
199
201
  continue;
200
202
  }
201
203
 
202
- // Skip this tx if its gas limit would exceed the block gas limit
204
+ // Skip this tx if its gas limit would exceed the block gas limit (either da or l2).
205
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
203
206
  const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
204
- if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
207
+ if (isBuildingProposal && maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
205
208
  this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
206
209
  txHash,
207
210
  txGasLimit,
@@ -241,7 +244,7 @@ export class PublicProcessor implements Traceable {
241
244
  this.contractsDB.createCheckpoint();
242
245
 
243
246
  try {
244
- const [processedTx, returnValues] = await this.processTx(tx, deadline);
247
+ const [processedTx, returnValues, txDebugLogs] = await this.processTx(tx, deadline);
245
248
 
246
249
  // Inject a fake processing failure after N txs if requested
247
250
  const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
@@ -250,23 +253,9 @@ export class PublicProcessor implements Traceable {
250
253
  }
251
254
 
252
255
  const txBlobFields = processedTx.txEffect.getNumBlobFields();
253
-
254
- // If the actual size of this tx would exceed block size, skip it
255
256
  const txSize = txBlobFields * Fr.SIZE_IN_BYTES;
256
- if (maxBlockSize !== undefined && totalSizeInBytes + txSize > maxBlockSize) {
257
- this.log.debug(`Skipping processed tx ${txHash} sized ${txSize} due to max block size.`, {
258
- txHash,
259
- sizeInBytes: txSize,
260
- totalSizeInBytes,
261
- maxBlockSize,
262
- });
263
- // Need to revert the checkpoint here and don't go any further
264
- await checkpoint.revert();
265
- this.contractsDB.revertCheckpoint();
266
- continue;
267
- }
268
257
 
269
- // If the actual blob fields of this tx would exceed the limit, skip it
258
+ // If the actual blob fields of this tx would exceed the limit, skip it.
270
259
  // Note: maxBlobFields already accounts for block end blob fields and previous blocks in checkpoint.
271
260
  if (maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
272
261
  this.log.debug(
@@ -284,12 +273,34 @@ export class PublicProcessor implements Traceable {
284
273
  continue;
285
274
  }
286
275
 
276
+ // During re-execution, check if the actual gas used by this tx would push the block over the gas limit.
277
+ // Unlike the proposal-building check (which uses declared gas limits pessimistically before processing),
278
+ // this uses actual gas and stops processing when the limit is exceeded.
279
+ if (
280
+ !isBuildingProposal &&
281
+ maxBlockGas !== undefined &&
282
+ totalBlockGas.add(processedTx.gasUsed.totalGas).gtAny(maxBlockGas)
283
+ ) {
284
+ this.log.warn(`Stopping re-execution since tx ${txHash} would push block gas over limit`, {
285
+ txHash,
286
+ txGas: processedTx.gasUsed.totalGas,
287
+ totalBlockGas,
288
+ maxBlockGas,
289
+ });
290
+ await checkpoint.revert();
291
+ this.contractsDB.revertCheckpoint();
292
+ break;
293
+ }
294
+
287
295
  // FIXME(fcarreiro): it's ugly to have to notify the validator of nullifiers.
288
296
  // I'd rather pass the validators the processedTx as well and let them deal with it.
289
297
  nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
290
298
  result.push(processedTx);
291
299
  usedTxs.push(tx);
292
300
  returns = returns.concat(returnValues);
301
+ debugLogs.push(...txDebugLogs);
302
+
303
+ this.debugLogStore.storeLogs(processedTx.hash.toString(), txDebugLogs);
293
304
 
294
305
  totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
295
306
  totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
@@ -363,7 +374,7 @@ export class PublicProcessor implements Traceable {
363
374
  totalSizeInBytes,
364
375
  });
365
376
 
366
- return [result, failed, usedTxs, returns, totalBlobFields];
377
+ return [result, failed, usedTxs, returns, debugLogs];
367
378
  }
368
379
 
369
380
  private async checkWorldStateUnchanged(
@@ -383,8 +394,13 @@ export class PublicProcessor implements Traceable {
383
394
  }
384
395
 
385
396
  @trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString() }))
386
- private async processTx(tx: Tx, deadline: Date | undefined): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
387
- const [time, [processedTx, returnValues]] = await elapsed(() => this.processTxWithinDeadline(tx, deadline));
397
+ private async processTx(
398
+ tx: Tx,
399
+ deadline: Date | undefined,
400
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
401
+ const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
402
+ this.processTxWithinDeadline(tx, deadline),
403
+ );
388
404
 
389
405
  this.log.verbose(
390
406
  !tx.hasPublicCalls()
@@ -407,7 +423,7 @@ export class PublicProcessor implements Traceable {
407
423
  },
408
424
  );
409
425
 
410
- return [processedTx, returnValues ?? []];
426
+ return [processedTx, returnValues ?? [], debugLogs];
411
427
  }
412
428
 
413
429
  private async doTreeInsertionsForPrivateOnlyTx(processedTx: ProcessedTx): Promise<void> {
@@ -441,10 +457,9 @@ export class PublicProcessor implements Traceable {
441
457
  private async processTxWithinDeadline(
442
458
  tx: Tx,
443
459
  deadline: Date | undefined,
444
- ): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined]> {
445
- const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined]> = tx.hasPublicCalls()
446
- ? () => this.processTxWithPublicCalls(tx)
447
- : () => this.processPrivateOnlyTx(tx);
460
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
461
+ const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
462
+ tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
448
463
 
449
464
  // Fake a delay per tx if instructed (used for tests)
450
465
  const fakeDelayPerTxMs = this.opts.fakeProcessingDelayPerTxMs;
@@ -512,7 +527,7 @@ export class PublicProcessor implements Traceable {
512
527
  @trackSpan('PublicProcessor.processPrivateOnlyTx', (tx: Tx) => ({
513
528
  [Attributes.TX_HASH]: tx.getTxHash().toString(),
514
529
  }))
515
- private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined]> {
530
+ private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined, DebugLog[]]> {
516
531
  const gasFees = this.globalVariables.gasFees;
517
532
  const transactionFee = computeTransactionFee(gasFees, tx.data.constants.txContext.gasSettings, tx.data.gasUsed);
518
533
 
@@ -537,13 +552,13 @@ export class PublicProcessor implements Traceable {
537
552
 
538
553
  await this.contractsDB.addNewContracts(tx);
539
554
 
540
- return [processedTx, undefined];
555
+ return [processedTx, undefined, []];
541
556
  }
542
557
 
543
558
  @trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({
544
559
  [Attributes.TX_HASH]: tx.getTxHash().toString(),
545
560
  }))
546
- private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
561
+ private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
547
562
  const timer = new Timer();
548
563
 
549
564
  const result = await this.publicTxSimulator.simulate(tx);
@@ -581,7 +596,7 @@ export class PublicProcessor implements Traceable {
581
596
  revertReason,
582
597
  );
583
598
 
584
- return [processedTx, appLogicReturnValues];
599
+ return [processedTx, appLogicReturnValues, result.logs ?? []];
585
600
  }
586
601
 
587
602
  /**
@@ -1,4 +1,4 @@
1
- import { type Logger, type LoggerBindings, createLogger, logLevel } from '@aztec/foundation/log';
1
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
2
2
  import { sleep } from '@aztec/foundation/sleep';
3
3
  import { type CancellationToken, avmSimulate, cancelSimulation, createCancellationToken } from '@aztec/native';
4
4
  import { ProtocolContractsList } from '@aztec/protocol-contracts';
@@ -100,8 +100,7 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
100
100
  inputBuffer,
101
101
  contractProvider,
102
102
  wsCppHandle,
103
- logLevel,
104
- // TODO: re-enable logging
103
+ this.log.level,
105
104
  undefined,
106
105
  this.cancellationToken,
107
106
  );
@@ -1,4 +1,4 @@
1
- import { type Logger, type LoggerBindings, createLogger, logLevel } from '@aztec/foundation/log';
1
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
2
2
  import { avmSimulateWithHintedDbs } from '@aztec/native';
3
3
  import {
4
4
  AvmCircuitInputs,
@@ -75,7 +75,7 @@ export class CppPublicTxSimulatorHintedDbs extends PublicTxSimulator implements
75
75
 
76
76
  let resultBuffer: Buffer;
77
77
  try {
78
- resultBuffer = await avmSimulateWithHintedDbs(inputBuffer, logLevel);
78
+ resultBuffer = await avmSimulateWithHintedDbs(inputBuffer, this.log.level);
79
79
  } catch (error: any) {
80
80
  throw new SimulationError(`C++ hinted simulation failed: ${error.message}`, []);
81
81
  }
@@ -19,10 +19,11 @@ export function createPublicTxSimulatorForBlockBuilding(
19
19
  globalVariables: GlobalVariables,
20
20
  telemetryClient: TelemetryClient,
21
21
  bindings?: LoggerBindings,
22
+ collectDebugLogs = false,
22
23
  ) {
23
24
  const config = PublicSimulatorConfig.from({
24
25
  skipFeeEnforcement: false,
25
- collectDebugLogs: false,
26
+ collectDebugLogs,
26
27
  collectHints: false,
27
28
  collectPublicInputs: false,
28
29
  collectStatistics: false,
@@ -1,4 +1,4 @@
1
- import { AVM_MAX_PROCESSABLE_L2_GAS } from '@aztec/constants';
1
+ import { MAX_PROCESSABLE_L2_GAS } from '@aztec/constants';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
4
4
  import { ProtocolContractAddress, ProtocolContractsList } from '@aztec/protocol-contracts';
@@ -199,8 +199,8 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
199
199
 
200
200
  // Such transactions should be filtered by GasTxValidator.
201
201
  assert(
202
- context.getActualGasUsed().l2Gas <= AVM_MAX_PROCESSABLE_L2_GAS,
203
- `Transaction consumes ${context.getActualGasUsed().l2Gas} L2 gas, which exceeds the AVM maximum processable gas of ${AVM_MAX_PROCESSABLE_L2_GAS}`,
202
+ context.getActualGasUsed().l2Gas <= MAX_PROCESSABLE_L2_GAS,
203
+ `Transaction consumes ${context.getActualGasUsed().l2Gas} L2 gas, which exceeds the maximum processable gas of ${MAX_PROCESSABLE_L2_GAS}`,
204
204
  );
205
205
  await this.payFee(context);
206
206