@aztec/simulator 0.0.1-commit.a072138 → 0.0.1-commit.a5db02d

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 (145) hide show
  1. package/README.md +4 -4
  2. package/dest/client.d.ts +1 -3
  3. package/dest/client.d.ts.map +1 -1
  4. package/dest/client.js +0 -2
  5. package/dest/private/acvm_wasm.d.ts +1 -1
  6. package/dest/private/acvm_wasm.d.ts.map +1 -1
  7. package/dest/private/acvm_wasm.js +3 -1
  8. package/dest/private/circuit_recording/circuit_recorder.d.ts +36 -23
  9. package/dest/private/circuit_recording/circuit_recorder.d.ts.map +1 -1
  10. package/dest/private/circuit_recording/circuit_recorder.js +65 -69
  11. package/dest/private/circuit_recording/file_circuit_recorder.d.ts +7 -19
  12. package/dest/private/circuit_recording/file_circuit_recorder.d.ts.map +1 -1
  13. package/dest/private/circuit_recording/file_circuit_recorder.js +38 -42
  14. package/dest/private/circuit_recording/simulator_recorder_wrapper.d.ts +1 -1
  15. package/dest/private/circuit_recording/simulator_recorder_wrapper.d.ts.map +1 -1
  16. package/dest/private/circuit_recording/simulator_recorder_wrapper.js +11 -14
  17. package/dest/public/avm/avm_gas.js +3 -3
  18. package/dest/public/avm/avm_simulator.js +1 -1
  19. package/dest/public/avm/calldata.d.ts +1 -1
  20. package/dest/public/avm/calldata.d.ts.map +1 -1
  21. package/dest/public/avm/calldata.js +3 -2
  22. package/dest/public/avm/fixtures/account_proof_fetcher.d.ts +2 -0
  23. package/dest/public/avm/fixtures/account_proof_fetcher.d.ts.map +1 -0
  24. package/dest/public/avm/fixtures/account_proof_fetcher.js +152 -0
  25. package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts +2 -1
  26. package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts.map +1 -1
  27. package/dest/public/avm/fixtures/base_avm_simulation_tester.js +18 -3
  28. package/dest/public/avm/fixtures/utils.d.ts +1 -1
  29. package/dest/public/avm/fixtures/utils.d.ts.map +1 -1
  30. package/dest/public/avm/fixtures/utils.js +4 -1
  31. package/dest/public/avm/opcodes/accrued_substate.d.ts +2 -2
  32. package/dest/public/avm/opcodes/accrued_substate.d.ts.map +1 -1
  33. package/dest/public/avm/opcodes/accrued_substate.js +3 -4
  34. package/dest/public/avm/opcodes/contract.d.ts +3 -2
  35. package/dest/public/avm/opcodes/contract.d.ts.map +1 -1
  36. package/dest/public/avm/opcodes/contract.js +5 -1
  37. package/dest/public/avm/opcodes/ec_add.d.ts +2 -4
  38. package/dest/public/avm/opcodes/ec_add.d.ts.map +1 -1
  39. package/dest/public/avm/opcodes/ec_add.js +13 -27
  40. package/dest/public/avm/serialization/bytecode_serialization.js +3 -3
  41. package/dest/public/avm/serialization/instruction_serialization.d.ts +2 -2
  42. package/dest/public/avm/serialization/instruction_serialization.d.ts.map +1 -1
  43. package/dest/public/avm/serialization/instruction_serialization.js +1 -1
  44. package/dest/public/contracts_db_checkpoint.d.ts +2 -2
  45. package/dest/public/contracts_db_checkpoint.d.ts.map +1 -1
  46. package/dest/public/contracts_db_checkpoint.js +1 -1
  47. package/dest/public/fixtures/amm_test.js +4 -4
  48. package/dest/public/fixtures/bulk_test.d.ts +1 -1
  49. package/dest/public/fixtures/bulk_test.d.ts.map +1 -1
  50. package/dest/public/fixtures/bulk_test.js +33 -6
  51. package/dest/public/fixtures/custom_bytecode_tester.d.ts +1 -1
  52. package/dest/public/fixtures/custom_bytecode_tester.d.ts.map +1 -1
  53. package/dest/public/fixtures/custom_bytecode_tester.js +2 -2
  54. package/dest/public/fixtures/custom_bytecode_tests.d.ts +8 -1
  55. package/dest/public/fixtures/custom_bytecode_tests.d.ts.map +1 -1
  56. package/dest/public/fixtures/custom_bytecode_tests.js +91 -2
  57. package/dest/public/fixtures/opcode_spammer.d.ts +1 -1
  58. package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -1
  59. package/dest/public/fixtures/opcode_spammer.js +8 -16
  60. package/dest/public/fixtures/public_tx_simulation_tester.d.ts +6 -5
  61. package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
  62. package/dest/public/fixtures/public_tx_simulation_tester.js +38 -9
  63. package/dest/public/fixtures/token_test.js +3 -3
  64. package/dest/public/fixtures/utils.d.ts +2 -2
  65. package/dest/public/fixtures/utils.d.ts.map +1 -1
  66. package/dest/public/fixtures/utils.js +21 -20
  67. package/dest/public/hinting_db_sources.d.ts +5 -5
  68. package/dest/public/hinting_db_sources.d.ts.map +1 -1
  69. package/dest/public/hinting_db_sources.js +8 -7
  70. package/dest/public/public_db_sources.d.ts +6 -3
  71. package/dest/public/public_db_sources.d.ts.map +1 -1
  72. package/dest/public/public_db_sources.js +16 -10
  73. package/dest/public/public_processor/guarded_merkle_tree.d.ts +5 -5
  74. package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
  75. package/dest/public/public_processor/guarded_merkle_tree.js +5 -5
  76. package/dest/public/public_processor/public_processor.d.ts +8 -5
  77. package/dest/public/public_processor/public_processor.d.ts.map +1 -1
  78. package/dest/public/public_processor/public_processor.js +121 -67
  79. package/dest/public/public_processor/public_processor_metrics.d.ts +4 -1
  80. package/dest/public/public_processor/public_processor_metrics.d.ts.map +1 -1
  81. package/dest/public/public_processor/public_processor_metrics.js +8 -0
  82. package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts +1 -1
  83. package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts.map +1 -1
  84. package/dest/public/public_tx_simulator/contract_provider_for_cpp.js +5 -4
  85. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts +1 -1
  86. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts.map +1 -1
  87. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.js +2 -3
  88. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts +1 -1
  89. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts.map +1 -1
  90. package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.js +2 -2
  91. package/dest/public/public_tx_simulator/factories.d.ts +2 -2
  92. package/dest/public/public_tx_simulator/factories.d.ts.map +1 -1
  93. package/dest/public/public_tx_simulator/factories.js +2 -2
  94. package/dest/public/public_tx_simulator/public_tx_context.d.ts +7 -3
  95. package/dest/public/public_tx_simulator/public_tx_context.d.ts.map +1 -1
  96. package/dest/public/public_tx_simulator/public_tx_context.js +8 -10
  97. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts +6 -2
  98. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts.map +1 -1
  99. package/dest/public/public_tx_simulator/public_tx_simulator.js +14 -9
  100. package/dest/public/state_manager/state_manager.js +1 -1
  101. package/dest/public/test_executor_metrics.d.ts +6 -1
  102. package/dest/public/test_executor_metrics.d.ts.map +1 -1
  103. package/dest/public/test_executor_metrics.js +22 -0
  104. package/package.json +16 -16
  105. package/src/client.ts +0 -2
  106. package/src/private/acvm_wasm.ts +4 -1
  107. package/src/private/circuit_recording/circuit_recorder.ts +84 -78
  108. package/src/private/circuit_recording/file_circuit_recorder.ts +38 -48
  109. package/src/private/circuit_recording/simulator_recorder_wrapper.ts +9 -15
  110. package/src/public/avm/avm_gas.ts +2 -2
  111. package/src/public/avm/avm_simulator.ts +1 -1
  112. package/src/public/avm/calldata.ts +3 -2
  113. package/src/public/avm/fixtures/account_proof.json +553 -0
  114. package/src/public/avm/fixtures/account_proof_fetcher.ts +166 -0
  115. package/src/public/avm/fixtures/base_avm_simulation_tester.ts +23 -3
  116. package/src/public/avm/fixtures/utils.ts +4 -1
  117. package/src/public/avm/opcodes/accrued_substate.ts +3 -4
  118. package/src/public/avm/opcodes/contract.ts +5 -1
  119. package/src/public/avm/opcodes/ec_add.ts +12 -31
  120. package/src/public/avm/opcodes/external_calls.ts +1 -1
  121. package/src/public/avm/serialization/bytecode_serialization.ts +2 -2
  122. package/src/public/avm/serialization/instruction_serialization.ts +1 -1
  123. package/src/public/contracts_db_checkpoint.ts +1 -1
  124. package/src/public/fixtures/amm_test.ts +4 -4
  125. package/src/public/fixtures/bulk_test.ts +31 -6
  126. package/src/public/fixtures/custom_bytecode_tester.ts +2 -2
  127. package/src/public/fixtures/custom_bytecode_tests.ts +209 -2
  128. package/src/public/fixtures/opcode_spammer.ts +10 -18
  129. package/src/public/fixtures/public_tx_simulation_tester.ts +45 -5
  130. package/src/public/fixtures/token_test.ts +3 -3
  131. package/src/public/fixtures/utils.ts +23 -24
  132. package/src/public/fuzzing/avm_fuzzer_simulator.ts +1 -1
  133. package/src/public/hinting_db_sources.ts +10 -7
  134. package/src/public/public_db_sources.ts +21 -14
  135. package/src/public/public_processor/guarded_merkle_tree.ts +6 -6
  136. package/src/public/public_processor/public_processor.ts +158 -88
  137. package/src/public/public_processor/public_processor_metrics.ts +12 -0
  138. package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +5 -4
  139. package/src/public/public_tx_simulator/cpp_public_tx_simulator.ts +2 -3
  140. package/src/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.ts +2 -2
  141. package/src/public/public_tx_simulator/factories.ts +2 -1
  142. package/src/public/public_tx_simulator/public_tx_context.ts +8 -10
  143. package/src/public/public_tx_simulator/public_tx_simulator.ts +14 -10
  144. package/src/public/state_manager/state_manager.ts +1 -1
  145. package/src/public/test_executor_metrics.ts +24 -0
@@ -55,10 +55,11 @@ export class PublicContractsDB implements PublicContractsDBInterface {
55
55
  this.log = createLogger('simulator:contracts-data-source', bindings);
56
56
  }
57
57
 
58
- public async addContracts(contractDeploymentData: ContractDeploymentData): Promise<void> {
58
+ /** Parses raw log data from the C++/NAPI bridge and inserts the resulting contracts into the current checkpoint. */
59
+ public addContractsFromLogs(contractDeploymentData: ContractDeploymentData): void {
59
60
  const currentState = this.getCurrentState();
60
61
 
61
- await this.addContractClassesFromEvents(
62
+ this.addContractClassesFromEvents(
62
63
  ContractClassPublishedEvent.extractContractClassEvents(contractDeploymentData.getContractClassLogs()),
63
64
  currentState,
64
65
  );
@@ -69,10 +70,18 @@ export class PublicContractsDB implements PublicContractsDBInterface {
69
70
  );
70
71
  }
71
72
 
72
- public async addNewContracts(tx: Tx): Promise<void> {
73
+ public addNewContracts(tx: Tx): void {
73
74
  const contractDeploymentData = AllContractDeploymentData.fromTx(tx);
74
- await this.addContracts(contractDeploymentData.getNonRevertibleContractDeploymentData());
75
- await this.addContracts(contractDeploymentData.getRevertibleContractDeploymentData());
75
+ this.addContractsFromLogs(contractDeploymentData.getNonRevertibleContractDeploymentData());
76
+ this.addContractsFromLogs(contractDeploymentData.getRevertibleContractDeploymentData());
77
+ }
78
+
79
+ /** Inserts typed contract instances directly into the current checkpoint. */
80
+ public addContracts(contractInstances?: ContractInstanceWithAddress[]): void {
81
+ const currentState = this.getCurrentState();
82
+ for (const instance of contractInstances ?? []) {
83
+ currentState.addInstance(instance.address, instance);
84
+ }
76
85
  }
77
86
 
78
87
  /**
@@ -81,7 +90,7 @@ export class PublicContractsDB implements PublicContractsDBInterface {
81
90
  */
82
91
  public createCheckpoint(): void {
83
92
  const currentState = this.getCurrentState();
84
- const newState = currentState.deepCopy();
93
+ const newState = currentState.fork();
85
94
  this.contractStateStack.push(newState);
86
95
  }
87
96
 
@@ -174,17 +183,15 @@ export class PublicContractsDB implements PublicContractsDBInterface {
174
183
  return await this.dataSource.getDebugFunctionName(address, selector);
175
184
  }
176
185
 
177
- private async addContractClassesFromEvents(
186
+ private addContractClassesFromEvents(
178
187
  contractClassEvents: ContractClassPublishedEvent[],
179
188
  state: ContractsDbCheckpoint,
180
189
  ) {
181
- await Promise.all(
182
- contractClassEvents.map(async (event: ContractClassPublishedEvent) => {
183
- this.log.debug(`Adding class ${event.contractClassId.toString()} to contract state`);
184
- const contractClass = await event.toContractClassPublic();
185
- state.addClass(event.contractClassId, contractClass);
186
- }),
187
- );
190
+ for (const event of contractClassEvents) {
191
+ this.log.debug(`Adding class ${event.contractClassId.toString()} to contract state`);
192
+ const contractClass = event.toContractClassPublic();
193
+ state.addClass(event.contractClassId, contractClass);
194
+ }
188
195
  }
189
196
 
190
197
  private addContractInstancesFromEvents(
@@ -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> {
@@ -134,7 +134,7 @@ export class GuardedMerkleTreeOperations implements MerkleTreeWriteOperations {
134
134
  ): Promise<(BlockNumber | undefined)[]> {
135
135
  return this.guardAndPush(() => this.target.getBlockNumbersForLeafIndices(treeId, leafIndices));
136
136
  }
137
- createCheckpoint(): Promise<void> {
137
+ createCheckpoint(): Promise<number> {
138
138
  return this.guardAndPush(() => this.target.createCheckpoint());
139
139
  }
140
140
  commitCheckpoint(): Promise<void> {
@@ -143,11 +143,11 @@ export class GuardedMerkleTreeOperations implements MerkleTreeWriteOperations {
143
143
  revertCheckpoint(): Promise<void> {
144
144
  return this.guardAndPush(() => this.target.revertCheckpoint());
145
145
  }
146
- commitAllCheckpoints(): Promise<void> {
147
- return this.guardAndPush(() => this.target.commitAllCheckpoints());
146
+ commitAllCheckpointsTo(depth: number): Promise<void> {
147
+ return this.guardAndPush(() => this.target.commitAllCheckpointsTo(depth));
148
148
  }
149
- revertAllCheckpoints(): Promise<void> {
150
- return this.guardAndPush(() => this.target.revertAllCheckpoints());
149
+ revertAllCheckpointsTo(depth: number): Promise<void> {
150
+ return this.guardAndPush(() => this.target.revertAllCheckpointsTo(depth));
151
151
  }
152
152
  findSiblingPaths<ID extends MerkleTreeId>(
153
153
  treeId: ID,
@@ -1,9 +1,14 @@
1
- import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NULLIFIER_SUBTREE_HEIGHT } from '@aztec/constants';
1
+ import {
2
+ MAX_NOTE_HASHES_PER_TX,
3
+ MAX_NULLIFIERS_PER_TX,
4
+ MAX_TX_BLOB_DATA_SIZE_IN_FIELDS,
5
+ NULLIFIER_SUBTREE_HEIGHT,
6
+ } from '@aztec/constants';
2
7
  import { padArrayEnd } from '@aztec/foundation/collection';
3
8
  import { Fr } from '@aztec/foundation/curves/bn254';
4
9
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
5
10
  import { sleep } from '@aztec/foundation/sleep';
6
- import { DateProvider, Timer, elapsed, executeTimeout } from '@aztec/foundation/timer';
11
+ import { DateProvider, Timer, elapsed, execWithSignal } from '@aztec/foundation/timer';
7
12
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
8
13
  import { ContractClassPublishedEvent } from '@aztec/protocol-contracts/class-registry';
9
14
  import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
@@ -25,6 +30,7 @@ import type {
25
30
  PublicProcessorValidator,
26
31
  SequencerConfig,
27
32
  } from '@aztec/stdlib/interfaces/server';
33
+ import { type DebugLog, type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
28
34
  import { ProvingRequestType } from '@aztec/stdlib/proofs';
29
35
  import { MerkleTreeId } from '@aztec/stdlib/trees';
30
36
  import {
@@ -75,17 +81,15 @@ export class PublicProcessorFactory {
75
81
  /**
76
82
  * Creates a new instance of a PublicProcessor.
77
83
  * @param globalVariables - The global variables for the block being processed.
78
- * @param skipFeeEnforcement - Allows disabling balance checks for fee estimations.
84
+ * @param contractsDB - Optional pre-populated contracts DB; a fresh one is constructed if omitted.
79
85
  * @returns A new instance of a PublicProcessor.
80
86
  */
81
87
  public create(
82
88
  merkleTree: MerkleTreeWriteOperations,
83
89
  globalVariables: GlobalVariables,
84
90
  config: PublicSimulatorConfig,
91
+ contractsDB: PublicContractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings()),
85
92
  ): PublicProcessor {
86
- const bindings = this.log.getBindings();
87
- const contractsDB = new PublicContractsDB(this.contractDataSource, bindings);
88
-
89
93
  const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
90
94
  const publicTxSimulator = this.createPublicTxSimulator(guardedFork, contractsDB, globalVariables, config);
91
95
 
@@ -96,7 +100,7 @@ export class PublicProcessorFactory {
96
100
  publicTxSimulator,
97
101
  this.dateProvider,
98
102
  this.telemetryClient,
99
- createLogger('simulator:public-processor', bindings),
103
+ createLogger('simulator:public-processor', this.log.getBindings()),
100
104
  );
101
105
  }
102
106
 
@@ -124,13 +128,23 @@ class PublicProcessorTimeoutError extends Error {
124
128
  }
125
129
  }
126
130
 
131
+ class PublicProcessorAbortError extends Error {
132
+ constructor(message: string = 'Aborted while processing tx') {
133
+ super(message);
134
+ this.name = 'PublicProcessorAbortError';
135
+ }
136
+ }
137
+
138
+ function isPublicProcessorInterruptError(err: any) {
139
+ return err?.name === 'PublicProcessorTimeoutError' || err?.name === 'PublicProcessorAbortError';
140
+ }
141
+
127
142
  /**
128
143
  * Converts Txs lifted from the P2P module into ProcessedTx objects by executing
129
144
  * any public function calls in them. Txs with private calls only are unaffected.
130
145
  */
131
146
  export class PublicProcessor implements Traceable {
132
147
  private metrics: PublicProcessorMetrics;
133
-
134
148
  constructor(
135
149
  protected globalVariables: GlobalVariables,
136
150
  private guardedMerkleTree: GuardedMerkleTreeOperations,
@@ -140,6 +154,7 @@ export class PublicProcessor implements Traceable {
140
154
  telemetryClient: TelemetryClient = getTelemetryClient(),
141
155
  private log: Logger,
142
156
  private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs' | 'fakeThrowAfterProcessingTxCount'> = {},
157
+ private debugLogStore: DebugLogStore = new NullDebugLogStore(),
143
158
  ) {
144
159
  this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
145
160
  }
@@ -159,12 +174,13 @@ export class PublicProcessor implements Traceable {
159
174
  txs: Iterable<Tx> | AsyncIterable<Tx>,
160
175
  limits: PublicProcessorLimits = {},
161
176
  validator: PublicProcessorValidator = {},
162
- ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], number]> {
163
- const { maxTransactions, maxBlockSize, deadline, maxBlockGas, maxBlobFields } = limits;
177
+ ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
178
+ const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal, signal } = limits;
164
179
  const { preprocessValidator, nullifierCache } = validator;
165
180
  const result: ProcessedTx[] = [];
166
181
  const usedTxs: Tx[] = [];
167
182
  const failed: FailedTx[] = [];
183
+ const debugLogs: DebugLog[] = [];
168
184
  const timer = new Timer();
169
185
 
170
186
  let totalSizeInBytes = 0;
@@ -172,6 +188,8 @@ export class PublicProcessor implements Traceable {
172
188
  let totalPublicGas = new Gas(0, 0);
173
189
  let totalBlockGas = new Gas(0, 0);
174
190
  let totalBlobFields = 0;
191
+ let silentlySkippedCount = 0;
192
+ let totalSilentlySkippedDurationMs = 0;
175
193
 
176
194
  for await (const tx of txs) {
177
195
  // Only process up to the max tx limit
@@ -180,28 +198,33 @@ export class PublicProcessor implements Traceable {
180
198
  break;
181
199
  }
182
200
 
183
- // Bail if we've hit the deadline
201
+ // Bail if we've hit the deadline or have been interrupted.
184
202
  if (deadline && this.dateProvider.now() > +deadline) {
185
203
  this.log.warn(`Stopping tx processing due to timeout.`);
186
204
  break;
187
205
  }
206
+ if (signal?.aborted) {
207
+ this.log.warn(`Stopping tx processing due to abort signal.`);
208
+ break;
209
+ }
188
210
 
189
- // Skip this tx if it'd exceed max block size
190
211
  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
- });
212
+
213
+ // Skip this tx if its estimated blob fields would exceed the limit.
214
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
215
+ const txBlobFields = tx.getPrivateTxEffectsSizeInFields();
216
+ if (isBuildingProposal && maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
217
+ this.log.warn(
218
+ `Skipping tx ${txHash} with ${txBlobFields} fields from private side effects due to blob fields limit`,
219
+ { txHash, txBlobFields, totalBlobFields, maxBlobFields },
220
+ );
199
221
  continue;
200
222
  }
201
223
 
202
- // Skip this tx if its gas limit would exceed the block gas limit
224
+ // Skip this tx if its gas limit would exceed the block gas limit (either da or l2).
225
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
203
226
  const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
204
- if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
227
+ if (isBuildingProposal && maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
205
228
  this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
206
229
  txHash,
207
230
  txGasLimit,
@@ -221,11 +244,6 @@ export class PublicProcessor implements Traceable {
221
244
  failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
222
245
  returns.push(new NestedProcessReturnValues([]));
223
246
  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
247
  } else {
230
248
  this.log.trace(`Tx ${txHash.toString()} is valid before processing.`);
231
249
  }
@@ -241,7 +259,9 @@ export class PublicProcessor implements Traceable {
241
259
  this.contractsDB.createCheckpoint();
242
260
 
243
261
  try {
244
- const [processedTx, returnValues] = await this.processTx(tx, deadline);
262
+ const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
263
+ this.processTx(tx, deadline, signal),
264
+ );
245
265
 
246
266
  // Inject a fake processing failure after N txs if requested
247
267
  const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
@@ -250,23 +270,25 @@ export class PublicProcessor implements Traceable {
250
270
  }
251
271
 
252
272
  const txBlobFields = processedTx.txEffect.getNumBlobFields();
253
-
254
- // If the actual size of this tx would exceed block size, skip it
255
273
  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
274
+
275
+ // A single tx's effects must fit within the per-tx blob encoding: the rollup circuit encodes each
276
+ // tx into a fixed [Field; MAX_TX_BLOB_DATA_SIZE_IN_FIELDS] array, so a larger tx effect cannot be
277
+ // proven. The per-category side-effect limits already guarantee this upstream, so reaching here means
278
+ // the tx is malformed; reject it as invalid rather than letting it poison proving.
279
+ if (txBlobFields > MAX_TX_BLOB_DATA_SIZE_IN_FIELDS) {
280
+ const error = new Error(
281
+ `Tx ${txHash} produced ${txBlobFields} blob fields, exceeding the per-tx maximum of ${MAX_TX_BLOB_DATA_SIZE_IN_FIELDS}`,
282
+ );
283
+ this.log.error(error.message, { txHash, txBlobFields });
264
284
  await checkpoint.revert();
265
285
  this.contractsDB.revertCheckpoint();
286
+ failed.push({ tx, error });
287
+ returns.push(new NestedProcessReturnValues([]));
266
288
  continue;
267
289
  }
268
290
 
269
- // If the actual blob fields of this tx would exceed the limit, skip it
291
+ // If the actual blob fields of this tx would exceed the limit, skip it.
270
292
  // Note: maxBlobFields already accounts for block end blob fields and previous blocks in checkpoint.
271
293
  if (maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
272
294
  this.log.debug(
@@ -276,35 +298,60 @@ export class PublicProcessor implements Traceable {
276
298
  txBlobFields,
277
299
  totalBlobFields,
278
300
  maxBlobFields,
301
+ txProcessingTimeMs,
279
302
  },
280
303
  );
304
+ silentlySkippedCount += 1;
305
+ totalSilentlySkippedDurationMs += txProcessingTimeMs;
306
+ this.metrics.recordSilentlySkipped(txProcessingTimeMs);
281
307
  // Need to revert the checkpoint here and don't go any further
282
308
  await checkpoint.revert();
283
309
  this.contractsDB.revertCheckpoint();
284
310
  continue;
285
311
  }
286
312
 
313
+ // During re-execution, check if the actual gas used by this tx would push the block over the gas limit.
314
+ // Unlike the proposal-building check (which uses declared gas limits pessimistically before processing),
315
+ // this uses actual gas and stops processing when the limit is exceeded.
316
+ if (
317
+ !isBuildingProposal &&
318
+ maxBlockGas !== undefined &&
319
+ totalBlockGas.add(processedTx.gasUsed.totalGas).gtAny(maxBlockGas)
320
+ ) {
321
+ this.log.warn(`Stopping re-execution since tx ${txHash} would push block gas over limit`, {
322
+ txHash,
323
+ txGas: processedTx.gasUsed.totalGas,
324
+ totalBlockGas,
325
+ maxBlockGas,
326
+ });
327
+ await checkpoint.revert();
328
+ this.contractsDB.revertCheckpoint();
329
+ break;
330
+ }
331
+
287
332
  // FIXME(fcarreiro): it's ugly to have to notify the validator of nullifiers.
288
333
  // I'd rather pass the validators the processedTx as well and let them deal with it.
289
334
  nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
290
335
  result.push(processedTx);
291
336
  usedTxs.push(tx);
292
337
  returns = returns.concat(returnValues);
338
+ debugLogs.push(...txDebugLogs);
339
+
340
+ this.debugLogStore.storeLogs(processedTx.hash.toString(), txDebugLogs);
293
341
 
294
342
  totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
295
343
  totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
296
344
  totalSizeInBytes += txSize;
297
345
  totalBlobFields += txBlobFields;
346
+
347
+ // Commit the tx-level contracts checkpoint on success
348
+ this.contractsDB.commitCheckpoint();
298
349
  } 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.
350
+ if (isPublicProcessorInterruptError(err)) {
351
+ const interruptReason = err.name === 'PublicProcessorTimeoutError' ? 'timeout' : 'abort signal';
352
+ this.log.warn(`Stopping tx processing due to ${interruptReason}.`);
353
+ // The tx may still be executing on a worker thread (C++ via NAPI).
354
+ // Signal cancellation AND WAIT for the simulation to actually stop before touching fork checkpoints.
308
355
  await this.publicTxSimulator.cancel?.();
309
356
 
310
357
  // Now stop the guarded fork to prevent any further TS-side access to the world state.
@@ -314,14 +361,10 @@ export class PublicProcessor implements Traceable {
314
361
  // 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
315
362
  // 2. Possible state updates on that checkpoint or any others created during execution.
316
363
 
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();
364
+ // Revert all checkpoints at or above this checkpoint's depth (inclusive), destroying any outstanding state
365
+ // updates from this tx and any nested checkpoints created during execution. This preserves any checkpoints
366
+ // created by callers below our depth.
367
+ await checkpoint.revertToCheckpoint();
325
368
 
326
369
  // Revert any contracts added to the DB for the tx.
327
370
  this.contractsDB.revertCheckpoint();
@@ -333,9 +376,9 @@ export class PublicProcessor implements Traceable {
333
376
  break;
334
377
  }
335
378
 
336
- // Roll back state to start of TX before proceeding to next TX
337
- await checkpoint.revert();
338
- await this.guardedMerkleTree.getUnderlyingFork().revertAllCheckpoints();
379
+ // Roll back state to start of TX before proceeding to next TX.
380
+ // Reverts all checkpoints at or above this checkpoint's depth, preserving any caller checkpoints below.
381
+ await checkpoint.revertToCheckpoint();
339
382
  this.contractsDB.revertCheckpoint();
340
383
  const errorMessage = err instanceof Error || err instanceof AssertionError ? err.message : 'Unknown error';
341
384
  this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
@@ -347,7 +390,6 @@ export class PublicProcessor implements Traceable {
347
390
  } finally {
348
391
  // Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
349
392
  await checkpoint.commit();
350
- this.contractsDB.commitCheckpointOkIfNone();
351
393
  }
352
394
  }
353
395
 
@@ -355,15 +397,26 @@ export class PublicProcessor implements Traceable {
355
397
  const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
356
398
  this.metrics.recordAllTxs(totalPublicGas, rate);
357
399
 
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
- });
400
+ const silentlySkippedDurationMs = Math.round(totalSilentlySkippedDurationMs);
401
+ this.log.info(
402
+ `Processed ${result.length} successful txs and ${failed.length} failed txs ` +
403
+ `(${silentlySkippedCount} silently skipped, ${silentlySkippedDurationMs}ms wasted) ` +
404
+ `in ${duration}s`,
405
+ {
406
+ blockNumber: this.globalVariables.blockNumber,
407
+ successfulCount: result.length,
408
+ failedCount: failed.length,
409
+ duration,
410
+ rate,
411
+ totalPublicGas,
412
+ totalBlockGas,
413
+ totalSizeInBytes,
414
+ silentlySkippedCount,
415
+ silentlySkippedDurationMs,
416
+ },
417
+ );
365
418
 
366
- return [result, failed, usedTxs, returns, totalBlobFields];
419
+ return [result, failed, usedTxs, returns, debugLogs];
367
420
  }
368
421
 
369
422
  private async checkWorldStateUnchanged(
@@ -383,8 +436,14 @@ export class PublicProcessor implements Traceable {
383
436
  }
384
437
 
385
438
  @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));
439
+ private async processTx(
440
+ tx: Tx,
441
+ deadline: Date | undefined,
442
+ signal: AbortSignal | undefined,
443
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
444
+ const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
445
+ this.processTxWithinDeadline(tx, deadline, signal),
446
+ );
388
447
 
389
448
  this.log.verbose(
390
449
  !tx.hasPublicCalls()
@@ -407,7 +466,7 @@ export class PublicProcessor implements Traceable {
407
466
  },
408
467
  );
409
468
 
410
- return [processedTx, returnValues ?? []];
469
+ return [processedTx, returnValues ?? [], debugLogs];
411
470
  }
412
471
 
413
472
  private async doTreeInsertionsForPrivateOnlyTx(processedTx: ProcessedTx): Promise<void> {
@@ -437,14 +496,14 @@ export class PublicProcessor implements Traceable {
437
496
  this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
438
497
  }
439
498
 
440
- /** Processes the given tx within deadline. Returns timeout if deadline is hit. */
499
+ /** Processes the given tx within deadline or until the signal is aborted. */
441
500
  private async processTxWithinDeadline(
442
501
  tx: Tx,
443
502
  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);
503
+ signal: AbortSignal | undefined,
504
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
505
+ const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
506
+ tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
448
507
 
449
508
  // Fake a delay per tx if instructed (used for tests)
450
509
  const fakeDelayPerTxMs = this.opts.fakeProcessingDelayPerTxMs;
@@ -458,27 +517,38 @@ export class PublicProcessor implements Traceable {
458
517
  }
459
518
  : innerProcessFn;
460
519
 
461
- if (!deadline) {
520
+ const processingSignal = this.getProcessingSignal(tx, deadline, signal);
521
+ if (!processingSignal) {
462
522
  return await processFn();
463
523
  }
464
524
 
465
- const txHash = tx.getTxHash();
525
+ return await execWithSignal(
526
+ () => processFn(),
527
+ processingSignal,
528
+ signal =>
529
+ signal.reason?.name === 'TimeoutError' ? new PublicProcessorTimeoutError() : new PublicProcessorAbortError(),
530
+ );
531
+ }
532
+
533
+ private getProcessingSignal(tx: Tx, deadline: Date | undefined, signal: AbortSignal | undefined) {
534
+ if (!deadline) {
535
+ return signal;
536
+ }
537
+
466
538
  const timeout = +deadline - this.dateProvider.now();
467
539
  if (timeout <= 0) {
468
540
  throw new PublicProcessorTimeoutError();
469
541
  }
470
542
 
543
+ const txHash = tx.getTxHash();
471
544
  this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, {
472
545
  deadline: deadline.toISOString(),
473
546
  now: new Date(this.dateProvider.now()).toISOString(),
474
547
  txHash,
475
548
  });
476
549
 
477
- return await executeTimeout(
478
- () => processFn(),
479
- timeout,
480
- () => new PublicProcessorTimeoutError(),
481
- );
550
+ const timeoutSignal = AbortSignal.timeout(timeout);
551
+ return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
482
552
  }
483
553
 
484
554
  /**
@@ -512,7 +582,7 @@ export class PublicProcessor implements Traceable {
512
582
  @trackSpan('PublicProcessor.processPrivateOnlyTx', (tx: Tx) => ({
513
583
  [Attributes.TX_HASH]: tx.getTxHash().toString(),
514
584
  }))
515
- private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined]> {
585
+ private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined, DebugLog[]]> {
516
586
  const gasFees = this.globalVariables.gasFees;
517
587
  const transactionFee = computeTransactionFee(gasFees, tx.data.constants.txContext.gasSettings, tx.data.gasUsed);
518
588
 
@@ -535,15 +605,15 @@ export class PublicProcessor implements Traceable {
535
605
  // Fee payment insertion has already been done. Do the rest.
536
606
  await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
537
607
 
538
- await this.contractsDB.addNewContracts(tx);
608
+ this.contractsDB.addNewContracts(tx);
539
609
 
540
- return [processedTx, undefined];
610
+ return [processedTx, undefined, []];
541
611
  }
542
612
 
543
613
  @trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({
544
614
  [Attributes.TX_HASH]: tx.getTxHash().toString(),
545
615
  }))
546
- private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
616
+ private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
547
617
  const timer = new Timer();
548
618
 
549
619
  const result = await this.publicTxSimulator.simulate(tx);
@@ -581,7 +651,7 @@ export class PublicProcessor implements Traceable {
581
651
  revertReason,
582
652
  );
583
653
 
584
- return [processedTx, appLogicReturnValues];
654
+ return [processedTx, appLogicReturnValues, result.logs ?? []];
585
655
  }
586
656
 
587
657
  /**
@@ -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
  }
@@ -23,7 +23,7 @@ export class ContractProviderForCpp implements ContractProvider {
23
23
  public getContractInstance = async (address: string): Promise<Buffer | undefined> => {
24
24
  this.log.trace(`Contract provider callback: getContractInstance(${address})`);
25
25
 
26
- const aztecAddr = AztecAddress.fromString(address);
26
+ const aztecAddr = AztecAddress.fromStringUnsafe(address);
27
27
 
28
28
  const instance = await this.contractsDB.getContractInstance(aztecAddr, this.globalVariables.timestamp);
29
29
 
@@ -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> => {
@@ -87,7 +88,7 @@ export class ContractProviderForCpp implements ContractProvider {
87
88
  this.log.trace(`Contract provider callback: getDebugFunctionName(${address}, ${selector})`);
88
89
 
89
90
  // Parse address and selector strings
90
- const aztecAddr = AztecAddress.fromString(address);
91
+ const aztecAddr = AztecAddress.fromStringUnsafe(address);
91
92
  const selectorFr = Fr.fromString(selector);
92
93
  const functionSelector = FunctionSelector.fromFieldOrUndefined(selectorFr);
93
94