@aztec/simulator 0.0.1-commit.e3c1de76 → 0.0.1-commit.e57c76e

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 (123) hide show
  1. package/README.md +4 -4
  2. package/dest/private/acvm_wasm.d.ts +1 -1
  3. package/dest/private/acvm_wasm.d.ts.map +1 -1
  4. package/dest/private/acvm_wasm.js +3 -1
  5. package/dest/private/circuit_recording/circuit_recorder.js +2 -2
  6. package/dest/public/avm/avm_gas.js +3 -3
  7. package/dest/public/avm/avm_simulator.js +1 -1
  8. package/dest/public/avm/calldata.d.ts +1 -1
  9. package/dest/public/avm/calldata.d.ts.map +1 -1
  10. package/dest/public/avm/calldata.js +3 -2
  11. package/dest/public/avm/fixtures/account_proof_fetcher.d.ts +2 -0
  12. package/dest/public/avm/fixtures/account_proof_fetcher.d.ts.map +1 -0
  13. package/dest/public/avm/fixtures/account_proof_fetcher.js +152 -0
  14. package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts +2 -1
  15. package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts.map +1 -1
  16. package/dest/public/avm/fixtures/base_avm_simulation_tester.js +17 -2
  17. package/dest/public/avm/fixtures/utils.d.ts +1 -1
  18. package/dest/public/avm/fixtures/utils.d.ts.map +1 -1
  19. package/dest/public/avm/fixtures/utils.js +3 -0
  20. package/dest/public/avm/opcodes/accrued_substate.d.ts +2 -2
  21. package/dest/public/avm/opcodes/accrued_substate.d.ts.map +1 -1
  22. package/dest/public/avm/opcodes/accrued_substate.js +3 -4
  23. package/dest/public/avm/opcodes/contract.d.ts +3 -2
  24. package/dest/public/avm/opcodes/contract.d.ts.map +1 -1
  25. package/dest/public/avm/opcodes/contract.js +5 -1
  26. package/dest/public/avm/opcodes/ec_add.d.ts +2 -4
  27. package/dest/public/avm/opcodes/ec_add.d.ts.map +1 -1
  28. package/dest/public/avm/opcodes/ec_add.js +13 -27
  29. package/dest/public/avm/serialization/bytecode_serialization.js +3 -3
  30. package/dest/public/avm/serialization/instruction_serialization.d.ts +2 -2
  31. package/dest/public/avm/serialization/instruction_serialization.d.ts.map +1 -1
  32. package/dest/public/avm/serialization/instruction_serialization.js +1 -1
  33. package/dest/public/contracts_db_checkpoint.d.ts +2 -2
  34. package/dest/public/contracts_db_checkpoint.d.ts.map +1 -1
  35. package/dest/public/contracts_db_checkpoint.js +1 -1
  36. package/dest/public/fixtures/amm_test.js +2 -2
  37. package/dest/public/fixtures/bulk_test.d.ts +1 -1
  38. package/dest/public/fixtures/bulk_test.d.ts.map +1 -1
  39. package/dest/public/fixtures/bulk_test.js +31 -4
  40. package/dest/public/fixtures/custom_bytecode_tests.d.ts +3 -1
  41. package/dest/public/fixtures/custom_bytecode_tests.d.ts.map +1 -1
  42. package/dest/public/fixtures/custom_bytecode_tests.js +61 -1
  43. package/dest/public/fixtures/opcode_spammer.d.ts +1 -1
  44. package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -1
  45. package/dest/public/fixtures/opcode_spammer.js +8 -16
  46. package/dest/public/fixtures/public_tx_simulation_tester.d.ts +6 -5
  47. package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
  48. package/dest/public/fixtures/public_tx_simulation_tester.js +37 -10
  49. package/dest/public/fixtures/utils.d.ts +2 -2
  50. package/dest/public/fixtures/utils.d.ts.map +1 -1
  51. package/dest/public/fixtures/utils.js +19 -20
  52. package/dest/public/hinting_db_sources.d.ts +5 -5
  53. package/dest/public/hinting_db_sources.d.ts.map +1 -1
  54. package/dest/public/hinting_db_sources.js +8 -7
  55. package/dest/public/public_db_sources.d.ts +6 -3
  56. package/dest/public/public_db_sources.d.ts.map +1 -1
  57. package/dest/public/public_db_sources.js +16 -10
  58. package/dest/public/public_processor/guarded_merkle_tree.d.ts +5 -5
  59. package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
  60. package/dest/public/public_processor/guarded_merkle_tree.js +5 -5
  61. package/dest/public/public_processor/public_processor.d.ts +8 -5
  62. package/dest/public/public_processor/public_processor.d.ts.map +1 -1
  63. package/dest/public/public_processor/public_processor.js +107 -72
  64. package/dest/public/public_processor/public_processor_metrics.d.ts +4 -1
  65. package/dest/public/public_processor/public_processor_metrics.d.ts.map +1 -1
  66. package/dest/public/public_processor/public_processor_metrics.js +8 -0
  67. package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts +1 -1
  68. package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts.map +1 -1
  69. package/dest/public/public_tx_simulator/contract_provider_for_cpp.js +3 -2
  70. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts +1 -1
  71. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts.map +1 -1
  72. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.js +2 -3
  73. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts +1 -1
  74. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts.map +1 -1
  75. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.js +2 -2
  76. package/dest/public/public_tx_simulator/factories.d.ts +2 -2
  77. package/dest/public/public_tx_simulator/factories.d.ts.map +1 -1
  78. package/dest/public/public_tx_simulator/factories.js +2 -2
  79. package/dest/public/public_tx_simulator/public_tx_context.d.ts +7 -3
  80. package/dest/public/public_tx_simulator/public_tx_context.d.ts.map +1 -1
  81. package/dest/public/public_tx_simulator/public_tx_context.js +8 -10
  82. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts +6 -2
  83. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts.map +1 -1
  84. package/dest/public/public_tx_simulator/public_tx_simulator.js +14 -9
  85. package/dest/public/test_executor_metrics.d.ts +6 -1
  86. package/dest/public/test_executor_metrics.d.ts.map +1 -1
  87. package/dest/public/test_executor_metrics.js +22 -0
  88. package/package.json +16 -16
  89. package/src/private/acvm_wasm.ts +4 -1
  90. package/src/private/circuit_recording/circuit_recorder.ts +2 -2
  91. package/src/public/avm/avm_gas.ts +2 -2
  92. package/src/public/avm/avm_simulator.ts +1 -1
  93. package/src/public/avm/calldata.ts +3 -2
  94. package/src/public/avm/fixtures/account_proof.json +553 -0
  95. package/src/public/avm/fixtures/account_proof_fetcher.ts +166 -0
  96. package/src/public/avm/fixtures/base_avm_simulation_tester.ts +22 -2
  97. package/src/public/avm/fixtures/utils.ts +3 -0
  98. package/src/public/avm/opcodes/accrued_substate.ts +3 -4
  99. package/src/public/avm/opcodes/contract.ts +5 -1
  100. package/src/public/avm/opcodes/ec_add.ts +12 -31
  101. package/src/public/avm/opcodes/external_calls.ts +1 -1
  102. package/src/public/avm/serialization/bytecode_serialization.ts +2 -2
  103. package/src/public/avm/serialization/instruction_serialization.ts +1 -1
  104. package/src/public/contracts_db_checkpoint.ts +1 -1
  105. package/src/public/fixtures/amm_test.ts +2 -2
  106. package/src/public/fixtures/bulk_test.ts +29 -4
  107. package/src/public/fixtures/custom_bytecode_tests.ts +139 -1
  108. package/src/public/fixtures/opcode_spammer.ts +10 -18
  109. package/src/public/fixtures/public_tx_simulation_tester.ts +47 -6
  110. package/src/public/fixtures/utils.ts +26 -22
  111. package/src/public/fuzzing/avm_fuzzer_simulator.ts +1 -1
  112. package/src/public/hinting_db_sources.ts +10 -7
  113. package/src/public/public_db_sources.ts +21 -14
  114. package/src/public/public_processor/guarded_merkle_tree.ts +6 -6
  115. package/src/public/public_processor/public_processor.ts +140 -91
  116. package/src/public/public_processor/public_processor_metrics.ts +12 -0
  117. package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +3 -2
  118. package/src/public/public_tx_simulator/cpp_public_tx_simulator.ts +2 -3
  119. package/src/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.ts +2 -2
  120. package/src/public/public_tx_simulator/factories.ts +2 -1
  121. package/src/public/public_tx_simulator/public_tx_context.ts +8 -10
  122. package/src/public/public_tx_simulator/public_tx_simulator.ts +14 -10
  123. package/src/public/test_executor_metrics.ts +24 -0
@@ -3,7 +3,7 @@ import { padArrayEnd } from '@aztec/foundation/collection';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
5
5
  import { sleep } from '@aztec/foundation/sleep';
6
- import { DateProvider, Timer, elapsed, executeTimeout } from '@aztec/foundation/timer';
6
+ import { DateProvider, Timer, elapsed, execWithSignal } from '@aztec/foundation/timer';
7
7
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
8
8
  import { ContractClassPublishedEvent } from '@aztec/protocol-contracts/class-registry';
9
9
  import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
@@ -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 {
@@ -75,17 +76,15 @@ export class PublicProcessorFactory {
75
76
  /**
76
77
  * Creates a new instance of a PublicProcessor.
77
78
  * @param globalVariables - The global variables for the block being processed.
78
- * @param skipFeeEnforcement - Allows disabling balance checks for fee estimations.
79
+ * @param contractsDB - Optional pre-populated contracts DB; a fresh one is constructed if omitted.
79
80
  * @returns A new instance of a PublicProcessor.
80
81
  */
81
82
  public create(
82
83
  merkleTree: MerkleTreeWriteOperations,
83
84
  globalVariables: GlobalVariables,
84
85
  config: PublicSimulatorConfig,
86
+ contractsDB: PublicContractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings()),
85
87
  ): PublicProcessor {
86
- const bindings = this.log.getBindings();
87
- const contractsDB = new PublicContractsDB(this.contractDataSource, bindings);
88
-
89
88
  const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
90
89
  const publicTxSimulator = this.createPublicTxSimulator(guardedFork, contractsDB, globalVariables, config);
91
90
 
@@ -96,7 +95,7 @@ export class PublicProcessorFactory {
96
95
  publicTxSimulator,
97
96
  this.dateProvider,
98
97
  this.telemetryClient,
99
- createLogger('simulator:public-processor', bindings),
98
+ createLogger('simulator:public-processor', this.log.getBindings()),
100
99
  );
101
100
  }
102
101
 
@@ -124,13 +123,23 @@ class PublicProcessorTimeoutError extends Error {
124
123
  }
125
124
  }
126
125
 
126
+ class PublicProcessorAbortError extends Error {
127
+ constructor(message: string = 'Aborted while processing tx') {
128
+ super(message);
129
+ this.name = 'PublicProcessorAbortError';
130
+ }
131
+ }
132
+
133
+ function isPublicProcessorInterruptError(err: any) {
134
+ return err?.name === 'PublicProcessorTimeoutError' || err?.name === 'PublicProcessorAbortError';
135
+ }
136
+
127
137
  /**
128
138
  * Converts Txs lifted from the P2P module into ProcessedTx objects by executing
129
139
  * any public function calls in them. Txs with private calls only are unaffected.
130
140
  */
131
141
  export class PublicProcessor implements Traceable {
132
142
  private metrics: PublicProcessorMetrics;
133
-
134
143
  constructor(
135
144
  protected globalVariables: GlobalVariables,
136
145
  private guardedMerkleTree: GuardedMerkleTreeOperations,
@@ -140,6 +149,7 @@ export class PublicProcessor implements Traceable {
140
149
  telemetryClient: TelemetryClient = getTelemetryClient(),
141
150
  private log: Logger,
142
151
  private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs' | 'fakeThrowAfterProcessingTxCount'> = {},
152
+ private debugLogStore: DebugLogStore = new NullDebugLogStore(),
143
153
  ) {
144
154
  this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
145
155
  }
@@ -159,12 +169,13 @@ export class PublicProcessor implements Traceable {
159
169
  txs: Iterable<Tx> | AsyncIterable<Tx>,
160
170
  limits: PublicProcessorLimits = {},
161
171
  validator: PublicProcessorValidator = {},
162
- ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], number]> {
163
- const { maxTransactions, maxBlockSize, deadline, maxBlockGas, maxBlobFields } = limits;
172
+ ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
173
+ const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal, signal } = limits;
164
174
  const { preprocessValidator, nullifierCache } = validator;
165
175
  const result: ProcessedTx[] = [];
166
176
  const usedTxs: Tx[] = [];
167
177
  const failed: FailedTx[] = [];
178
+ const debugLogs: DebugLog[] = [];
168
179
  const timer = new Timer();
169
180
 
170
181
  let totalSizeInBytes = 0;
@@ -172,6 +183,8 @@ export class PublicProcessor implements Traceable {
172
183
  let totalPublicGas = new Gas(0, 0);
173
184
  let totalBlockGas = new Gas(0, 0);
174
185
  let totalBlobFields = 0;
186
+ let silentlySkippedCount = 0;
187
+ let totalSilentlySkippedDurationMs = 0;
175
188
 
176
189
  for await (const tx of txs) {
177
190
  // Only process up to the max tx limit
@@ -180,28 +193,33 @@ export class PublicProcessor implements Traceable {
180
193
  break;
181
194
  }
182
195
 
183
- // Bail if we've hit the deadline
196
+ // Bail if we've hit the deadline or have been interrupted.
184
197
  if (deadline && this.dateProvider.now() > +deadline) {
185
198
  this.log.warn(`Stopping tx processing due to timeout.`);
186
199
  break;
187
200
  }
201
+ if (signal?.aborted) {
202
+ this.log.warn(`Stopping tx processing due to abort signal.`);
203
+ break;
204
+ }
188
205
 
189
- // Skip this tx if it'd exceed max block size
190
206
  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
- });
207
+
208
+ // Skip this tx if its estimated blob fields would exceed the limit.
209
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
210
+ const txBlobFields = tx.getPrivateTxEffectsSizeInFields();
211
+ if (isBuildingProposal && maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
212
+ this.log.warn(
213
+ `Skipping tx ${txHash} with ${txBlobFields} fields from private side effects due to blob fields limit`,
214
+ { txHash, txBlobFields, totalBlobFields, maxBlobFields },
215
+ );
199
216
  continue;
200
217
  }
201
218
 
202
- // Skip this tx if its gas limit would exceed the block gas limit
219
+ // Skip this tx if its gas limit would exceed the block gas limit (either da or l2).
220
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
203
221
  const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
204
- if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
222
+ if (isBuildingProposal && maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
205
223
  this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
206
224
  txHash,
207
225
  txGasLimit,
@@ -221,11 +239,6 @@ export class PublicProcessor implements Traceable {
221
239
  failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
222
240
  returns.push(new NestedProcessReturnValues([]));
223
241
  continue;
224
- } else if (result.result === 'skipped') {
225
- const reason = result.reason.join(', ');
226
- this.log.debug(`Skipping tx ${txHash.toString()} due to pre-process validation: ${reason}`);
227
- returns.push(new NestedProcessReturnValues([]));
228
- continue;
229
242
  } else {
230
243
  this.log.trace(`Tx ${txHash.toString()} is valid before processing.`);
231
244
  }
@@ -241,7 +254,9 @@ export class PublicProcessor implements Traceable {
241
254
  this.contractsDB.createCheckpoint();
242
255
 
243
256
  try {
244
- const [processedTx, returnValues] = await this.processTx(tx, deadline);
257
+ const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
258
+ this.processTx(tx, deadline, signal),
259
+ );
245
260
 
246
261
  // Inject a fake processing failure after N txs if requested
247
262
  const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
@@ -250,23 +265,9 @@ export class PublicProcessor implements Traceable {
250
265
  }
251
266
 
252
267
  const txBlobFields = processedTx.txEffect.getNumBlobFields();
253
-
254
- // If the actual size of this tx would exceed block size, skip it
255
268
  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
269
 
269
- // If the actual blob fields of this tx would exceed the limit, skip it
270
+ // If the actual blob fields of this tx would exceed the limit, skip it.
270
271
  // Note: maxBlobFields already accounts for block end blob fields and previous blocks in checkpoint.
271
272
  if (maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
272
273
  this.log.debug(
@@ -276,35 +277,60 @@ export class PublicProcessor implements Traceable {
276
277
  txBlobFields,
277
278
  totalBlobFields,
278
279
  maxBlobFields,
280
+ txProcessingTimeMs,
279
281
  },
280
282
  );
283
+ silentlySkippedCount += 1;
284
+ totalSilentlySkippedDurationMs += txProcessingTimeMs;
285
+ this.metrics.recordSilentlySkipped(txProcessingTimeMs);
281
286
  // Need to revert the checkpoint here and don't go any further
282
287
  await checkpoint.revert();
283
288
  this.contractsDB.revertCheckpoint();
284
289
  continue;
285
290
  }
286
291
 
292
+ // During re-execution, check if the actual gas used by this tx would push the block over the gas limit.
293
+ // Unlike the proposal-building check (which uses declared gas limits pessimistically before processing),
294
+ // this uses actual gas and stops processing when the limit is exceeded.
295
+ if (
296
+ !isBuildingProposal &&
297
+ maxBlockGas !== undefined &&
298
+ totalBlockGas.add(processedTx.gasUsed.totalGas).gtAny(maxBlockGas)
299
+ ) {
300
+ this.log.warn(`Stopping re-execution since tx ${txHash} would push block gas over limit`, {
301
+ txHash,
302
+ txGas: processedTx.gasUsed.totalGas,
303
+ totalBlockGas,
304
+ maxBlockGas,
305
+ });
306
+ await checkpoint.revert();
307
+ this.contractsDB.revertCheckpoint();
308
+ break;
309
+ }
310
+
287
311
  // FIXME(fcarreiro): it's ugly to have to notify the validator of nullifiers.
288
312
  // I'd rather pass the validators the processedTx as well and let them deal with it.
289
313
  nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
290
314
  result.push(processedTx);
291
315
  usedTxs.push(tx);
292
316
  returns = returns.concat(returnValues);
317
+ debugLogs.push(...txDebugLogs);
318
+
319
+ this.debugLogStore.storeLogs(processedTx.hash.toString(), txDebugLogs);
293
320
 
294
321
  totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
295
322
  totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
296
323
  totalSizeInBytes += txSize;
297
324
  totalBlobFields += txBlobFields;
325
+
326
+ // Commit the tx-level contracts checkpoint on success
327
+ this.contractsDB.commitCheckpoint();
298
328
  } catch (err: any) {
299
- if (err?.name === 'PublicProcessorTimeoutError') {
300
- this.log.warn(`Stopping tx processing due to timeout.`);
301
- // We hit the transaction execution deadline.
302
- // There may still be a transaction executing on a worker thread (C++ via NAPI).
303
- // Signal cancellation AND WAIT for the simulation to actually stop.
304
- // This is critical because C++ might be in the middle of a slow operation (e.g., pad_trees)
305
- // and won't check the cancellation flag until that operation completes.
306
- // Without waiting, we'd proceed to revert checkpoints while C++ is still writing to state.
307
- // Wait for C++ to stop gracefully.
329
+ if (isPublicProcessorInterruptError(err)) {
330
+ const interruptReason = err.name === 'PublicProcessorTimeoutError' ? 'timeout' : 'abort signal';
331
+ this.log.warn(`Stopping tx processing due to ${interruptReason}.`);
332
+ // The tx may still be executing on a worker thread (C++ via NAPI).
333
+ // Signal cancellation AND WAIT for the simulation to actually stop before touching fork checkpoints.
308
334
  await this.publicTxSimulator.cancel?.();
309
335
 
310
336
  // Now stop the guarded fork to prevent any further TS-side access to the world state.
@@ -314,14 +340,10 @@ export class PublicProcessor implements Traceable {
314
340
  // 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
315
341
  // 2. Possible state updates on that checkpoint or any others created during execution.
316
342
 
317
- // First we revert a checkpoint as managed by the ForkCheckpoint. This will revert whatever is the current checkpoint
318
- // which may not be the one originally created by this object. But that is ok, we do this to fulfil the ForkCheckpoint
319
- // lifecycle expectations and ensure it doesn't attempt to commit later on.
320
- await checkpoint.revert();
321
-
322
- // Now we want to revert any/all remaining checkpoints, destroying any outstanding state updates.
323
- // This needs to be done directly on the underlying fork as the guarded fork has been stopped.
324
- await this.guardedMerkleTree.getUnderlyingFork().revertAllCheckpoints();
343
+ // Revert all checkpoints at or above this checkpoint's depth (inclusive), destroying any outstanding state
344
+ // updates from this tx and any nested checkpoints created during execution. This preserves any checkpoints
345
+ // created by callers below our depth.
346
+ await checkpoint.revertToCheckpoint();
325
347
 
326
348
  // Revert any contracts added to the DB for the tx.
327
349
  this.contractsDB.revertCheckpoint();
@@ -333,9 +355,9 @@ export class PublicProcessor implements Traceable {
333
355
  break;
334
356
  }
335
357
 
336
- // Roll back state to start of TX before proceeding to next TX
337
- await checkpoint.revert();
338
- await this.guardedMerkleTree.getUnderlyingFork().revertAllCheckpoints();
358
+ // Roll back state to start of TX before proceeding to next TX.
359
+ // Reverts all checkpoints at or above this checkpoint's depth, preserving any caller checkpoints below.
360
+ await checkpoint.revertToCheckpoint();
339
361
  this.contractsDB.revertCheckpoint();
340
362
  const errorMessage = err instanceof Error || err instanceof AssertionError ? err.message : 'Unknown error';
341
363
  this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
@@ -347,7 +369,6 @@ export class PublicProcessor implements Traceable {
347
369
  } finally {
348
370
  // Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
349
371
  await checkpoint.commit();
350
- this.contractsDB.commitCheckpointOkIfNone();
351
372
  }
352
373
  }
353
374
 
@@ -355,15 +376,26 @@ export class PublicProcessor implements Traceable {
355
376
  const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
356
377
  this.metrics.recordAllTxs(totalPublicGas, rate);
357
378
 
358
- this.log.info(`Processed ${result.length} successful txs and ${failed.length} failed txs in ${duration}s`, {
359
- duration,
360
- rate,
361
- totalPublicGas,
362
- totalBlockGas,
363
- totalSizeInBytes,
364
- });
379
+ const silentlySkippedDurationMs = Math.round(totalSilentlySkippedDurationMs);
380
+ this.log.info(
381
+ `Processed ${result.length} successful txs and ${failed.length} failed txs ` +
382
+ `(${silentlySkippedCount} silently skipped, ${silentlySkippedDurationMs}ms wasted) ` +
383
+ `in ${duration}s`,
384
+ {
385
+ blockNumber: this.globalVariables.blockNumber,
386
+ successfulCount: result.length,
387
+ failedCount: failed.length,
388
+ duration,
389
+ rate,
390
+ totalPublicGas,
391
+ totalBlockGas,
392
+ totalSizeInBytes,
393
+ silentlySkippedCount,
394
+ silentlySkippedDurationMs,
395
+ },
396
+ );
365
397
 
366
- return [result, failed, usedTxs, returns, totalBlobFields];
398
+ return [result, failed, usedTxs, returns, debugLogs];
367
399
  }
368
400
 
369
401
  private async checkWorldStateUnchanged(
@@ -383,8 +415,14 @@ export class PublicProcessor implements Traceable {
383
415
  }
384
416
 
385
417
  @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));
418
+ private async processTx(
419
+ tx: Tx,
420
+ deadline: Date | undefined,
421
+ signal: AbortSignal | undefined,
422
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
423
+ const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
424
+ this.processTxWithinDeadline(tx, deadline, signal),
425
+ );
388
426
 
389
427
  this.log.verbose(
390
428
  !tx.hasPublicCalls()
@@ -407,7 +445,7 @@ export class PublicProcessor implements Traceable {
407
445
  },
408
446
  );
409
447
 
410
- return [processedTx, returnValues ?? []];
448
+ return [processedTx, returnValues ?? [], debugLogs];
411
449
  }
412
450
 
413
451
  private async doTreeInsertionsForPrivateOnlyTx(processedTx: ProcessedTx): Promise<void> {
@@ -437,14 +475,14 @@ export class PublicProcessor implements Traceable {
437
475
  this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
438
476
  }
439
477
 
440
- /** Processes the given tx within deadline. Returns timeout if deadline is hit. */
478
+ /** Processes the given tx within deadline or until the signal is aborted. */
441
479
  private async processTxWithinDeadline(
442
480
  tx: Tx,
443
481
  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);
482
+ signal: AbortSignal | undefined,
483
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
484
+ const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
485
+ tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
448
486
 
449
487
  // Fake a delay per tx if instructed (used for tests)
450
488
  const fakeDelayPerTxMs = this.opts.fakeProcessingDelayPerTxMs;
@@ -458,27 +496,38 @@ export class PublicProcessor implements Traceable {
458
496
  }
459
497
  : innerProcessFn;
460
498
 
461
- if (!deadline) {
499
+ const processingSignal = this.getProcessingSignal(tx, deadline, signal);
500
+ if (!processingSignal) {
462
501
  return await processFn();
463
502
  }
464
503
 
465
- const txHash = tx.getTxHash();
504
+ return await execWithSignal(
505
+ () => processFn(),
506
+ processingSignal,
507
+ signal =>
508
+ signal.reason?.name === 'TimeoutError' ? new PublicProcessorTimeoutError() : new PublicProcessorAbortError(),
509
+ );
510
+ }
511
+
512
+ private getProcessingSignal(tx: Tx, deadline: Date | undefined, signal: AbortSignal | undefined) {
513
+ if (!deadline) {
514
+ return signal;
515
+ }
516
+
466
517
  const timeout = +deadline - this.dateProvider.now();
467
518
  if (timeout <= 0) {
468
519
  throw new PublicProcessorTimeoutError();
469
520
  }
470
521
 
522
+ const txHash = tx.getTxHash();
471
523
  this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, {
472
524
  deadline: deadline.toISOString(),
473
525
  now: new Date(this.dateProvider.now()).toISOString(),
474
526
  txHash,
475
527
  });
476
528
 
477
- return await executeTimeout(
478
- () => processFn(),
479
- timeout,
480
- () => new PublicProcessorTimeoutError(),
481
- );
529
+ const timeoutSignal = AbortSignal.timeout(timeout);
530
+ return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
482
531
  }
483
532
 
484
533
  /**
@@ -512,7 +561,7 @@ export class PublicProcessor implements Traceable {
512
561
  @trackSpan('PublicProcessor.processPrivateOnlyTx', (tx: Tx) => ({
513
562
  [Attributes.TX_HASH]: tx.getTxHash().toString(),
514
563
  }))
515
- private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined]> {
564
+ private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined, DebugLog[]]> {
516
565
  const gasFees = this.globalVariables.gasFees;
517
566
  const transactionFee = computeTransactionFee(gasFees, tx.data.constants.txContext.gasSettings, tx.data.gasUsed);
518
567
 
@@ -535,15 +584,15 @@ export class PublicProcessor implements Traceable {
535
584
  // Fee payment insertion has already been done. Do the rest.
536
585
  await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
537
586
 
538
- await this.contractsDB.addNewContracts(tx);
587
+ this.contractsDB.addNewContracts(tx);
539
588
 
540
- return [processedTx, undefined];
589
+ return [processedTx, undefined, []];
541
590
  }
542
591
 
543
592
  @trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({
544
593
  [Attributes.TX_HASH]: tx.getTxHash().toString(),
545
594
  }))
546
- private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
595
+ private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
547
596
  const timer = new Timer();
548
597
 
549
598
  const result = await this.publicTxSimulator.simulate(tx);
@@ -581,7 +630,7 @@ export class PublicProcessor implements Traceable {
581
630
  revertReason,
582
631
  );
583
632
 
584
- return [processedTx, appLogicReturnValues];
633
+ return [processedTx, appLogicReturnValues, result.logs ?? []];
585
634
  }
586
635
 
587
636
  /**
@@ -30,6 +30,9 @@ export class PublicProcessorMetrics {
30
30
 
31
31
  private treeInsertionDuration: Histogram;
32
32
 
33
+ private silentlySkippedCount: UpDownCounter;
34
+ private silentlySkippedDuration: Histogram;
35
+
33
36
  constructor(client: TelemetryClient, name = 'PublicProcessor') {
34
37
  this.tracer = client.getTracer(name);
35
38
  const meter = client.getMeter(name);
@@ -60,6 +63,10 @@ export class PublicProcessorMetrics {
60
63
  this.gasRate = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_GAS_RATE);
61
64
 
62
65
  this.treeInsertionDuration = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_TREE_INSERTION);
66
+
67
+ this.silentlySkippedCount = createUpDownCounterWithDefault(meter, Metrics.PUBLIC_PROCESSOR_SILENTLY_SKIPPED_COUNT);
68
+
69
+ this.silentlySkippedDuration = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_SILENTLY_SKIPPED_DURATION);
63
70
  }
64
71
 
65
72
  recordPhaseDuration(phaseName: TxExecutionPhase, durationMs: number) {
@@ -123,4 +130,9 @@ export class PublicProcessorMetrics {
123
130
  recordTreeInsertions(durationUs: number) {
124
131
  this.treeInsertionDuration.record(Math.ceil(durationUs));
125
132
  }
133
+
134
+ recordSilentlySkipped(durationMs: number) {
135
+ this.silentlySkippedCount.add(1);
136
+ this.silentlySkippedDuration.record(Math.ceil(durationMs));
137
+ }
126
138
  }
@@ -52,6 +52,7 @@ export class ContractProviderForCpp implements ContractProvider {
52
52
  return serializeWithMessagePack(contractClass);
53
53
  };
54
54
 
55
+ // eslint-disable-next-line require-await
55
56
  public addContracts = async (contractDeploymentDataBuffer: Buffer): Promise<void> => {
56
57
  this.log.trace(`Contract provider callback: addContracts`);
57
58
 
@@ -61,8 +62,8 @@ export class ContractProviderForCpp implements ContractProvider {
61
62
  const contractDeploymentData = ContractDeploymentData.fromPlainObject(rawData);
62
63
 
63
64
  // Add contracts to the contracts DB
64
- this.log.trace(`Calling contractsDB.addContracts`);
65
- await this.contractsDB.addContracts(contractDeploymentData);
65
+ this.log.trace(`Calling contractsDB.addContractsFromLogs`);
66
+ this.contractsDB.addContractsFromLogs(contractDeploymentData);
66
67
  };
67
68
 
68
69
  public getBytecodeCommitment = async (classId: string): Promise<Buffer | undefined> => {
@@ -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,
@@ -174,14 +174,8 @@ export class PublicTxContext {
174
174
  }
175
175
  if (phase === TxExecutionPhase.SETUP) {
176
176
  this.log.warn(`Setup phase reverted! The transaction will be thrown out.`);
177
- } else if (phase === TxExecutionPhase.APP_LOGIC) {
178
- this.revertCode = RevertCode.APP_LOGIC_REVERTED;
179
- } else if (phase === TxExecutionPhase.TEARDOWN) {
180
- if (this.revertCode.equals(RevertCode.APP_LOGIC_REVERTED)) {
181
- this.revertCode = RevertCode.BOTH_REVERTED;
182
- } else {
183
- this.revertCode = RevertCode.TEARDOWN_REVERTED;
184
- }
177
+ } else if (phase === TxExecutionPhase.APP_LOGIC || phase === TxExecutionPhase.TEARDOWN) {
178
+ this.revertCode = RevertCode.REVERTED;
185
179
  }
186
180
  }
187
181
 
@@ -247,8 +241,12 @@ export class PublicTxContext {
247
241
  }
248
242
 
249
243
  /**
250
- * The gasUsed by public and private,
251
- * as if the entire teardown gas limit was consumed.
244
+ * The gasUsed by public and private, as if the entire teardown gas limit was consumed.
245
+ *
246
+ * This is intentional: teardown is used for gas accounting and refunds, so the transaction
247
+ * fee must be deterministic _before_ teardown executes. If fees depended on teardown's actual
248
+ * consumption there would be a circular dependency. Billing the full teardown gas limit
249
+ * (set by the user) makes the fee known in advance and available to the teardown function.
252
250
  */
253
251
  getTotalGasUsed(): Gas {
254
252
  return this.gasUsedByPrivate.add(this.gasUsedByPublic);