@aztec/simulator 0.0.1-commit.001888fc → 0.0.1-commit.017a351

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 (92) hide show
  1. package/dest/private/acvm_wasm.d.ts +1 -1
  2. package/dest/private/acvm_wasm.d.ts.map +1 -1
  3. package/dest/private/acvm_wasm.js +3 -1
  4. package/dest/public/avm/avm_simulator.js +1 -1
  5. package/dest/public/avm/calldata.d.ts +1 -1
  6. package/dest/public/avm/calldata.d.ts.map +1 -1
  7. package/dest/public/avm/calldata.js +3 -2
  8. package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts +2 -1
  9. package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts.map +1 -1
  10. package/dest/public/avm/fixtures/base_avm_simulation_tester.js +17 -2
  11. package/dest/public/avm/fixtures/utils.d.ts +1 -1
  12. package/dest/public/avm/fixtures/utils.d.ts.map +1 -1
  13. package/dest/public/avm/fixtures/utils.js +3 -0
  14. package/dest/public/avm/opcodes/contract.d.ts +3 -2
  15. package/dest/public/avm/opcodes/contract.d.ts.map +1 -1
  16. package/dest/public/avm/opcodes/contract.js +5 -1
  17. package/dest/public/avm/opcodes/ec_add.d.ts +2 -4
  18. package/dest/public/avm/opcodes/ec_add.d.ts.map +1 -1
  19. package/dest/public/avm/opcodes/ec_add.js +13 -27
  20. package/dest/public/contracts_db_checkpoint.d.ts +2 -2
  21. package/dest/public/contracts_db_checkpoint.d.ts.map +1 -1
  22. package/dest/public/contracts_db_checkpoint.js +1 -1
  23. package/dest/public/fixtures/bulk_test.d.ts +1 -1
  24. package/dest/public/fixtures/bulk_test.d.ts.map +1 -1
  25. package/dest/public/fixtures/bulk_test.js +31 -4
  26. package/dest/public/fixtures/custom_bytecode_tests.d.ts +3 -1
  27. package/dest/public/fixtures/custom_bytecode_tests.d.ts.map +1 -1
  28. package/dest/public/fixtures/custom_bytecode_tests.js +61 -1
  29. package/dest/public/fixtures/opcode_spammer.d.ts +1 -1
  30. package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -1
  31. package/dest/public/fixtures/opcode_spammer.js +2 -10
  32. package/dest/public/fixtures/public_tx_simulation_tester.d.ts +1 -1
  33. package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
  34. package/dest/public/fixtures/public_tx_simulation_tester.js +3 -3
  35. package/dest/public/fixtures/utils.d.ts +1 -1
  36. package/dest/public/fixtures/utils.d.ts.map +1 -1
  37. package/dest/public/fixtures/utils.js +14 -15
  38. package/dest/public/hinting_db_sources.d.ts +2 -1
  39. package/dest/public/hinting_db_sources.d.ts.map +1 -1
  40. package/dest/public/hinting_db_sources.js +4 -1
  41. package/dest/public/public_db_sources.d.ts +6 -3
  42. package/dest/public/public_db_sources.d.ts.map +1 -1
  43. package/dest/public/public_db_sources.js +16 -10
  44. package/dest/public/public_processor/guarded_merkle_tree.d.ts +4 -3
  45. package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
  46. package/dest/public/public_processor/guarded_merkle_tree.js +3 -0
  47. package/dest/public/public_processor/public_processor.d.ts +4 -3
  48. package/dest/public/public_processor/public_processor.d.ts.map +1 -1
  49. package/dest/public/public_processor/public_processor.js +60 -34
  50. package/dest/public/public_processor/public_processor_metrics.d.ts +4 -1
  51. package/dest/public/public_processor/public_processor_metrics.d.ts.map +1 -1
  52. package/dest/public/public_processor/public_processor_metrics.js +8 -0
  53. package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts +1 -1
  54. package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts.map +1 -1
  55. package/dest/public/public_tx_simulator/contract_provider_for_cpp.js +3 -2
  56. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts +1 -1
  57. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts.map +1 -1
  58. package/dest/public/public_tx_simulator/cpp_public_tx_simulator.js +3 -8
  59. package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.d.ts +1 -1
  60. package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.d.ts.map +1 -1
  61. package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.js +3 -7
  62. package/dest/public/public_tx_simulator/public_tx_context.d.ts +7 -3
  63. package/dest/public/public_tx_simulator/public_tx_context.d.ts.map +1 -1
  64. package/dest/public/public_tx_simulator/public_tx_context.js +8 -10
  65. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts +6 -2
  66. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts.map +1 -1
  67. package/dest/public/public_tx_simulator/public_tx_simulator.js +12 -7
  68. package/package.json +16 -15
  69. package/src/private/acvm_wasm.ts +4 -1
  70. package/src/public/avm/avm_simulator.ts +1 -1
  71. package/src/public/avm/calldata.ts +3 -2
  72. package/src/public/avm/fixtures/base_avm_simulation_tester.ts +22 -2
  73. package/src/public/avm/fixtures/utils.ts +3 -0
  74. package/src/public/avm/opcodes/contract.ts +5 -1
  75. package/src/public/avm/opcodes/ec_add.ts +12 -31
  76. package/src/public/avm/opcodes/external_calls.ts +1 -1
  77. package/src/public/contracts_db_checkpoint.ts +1 -1
  78. package/src/public/fixtures/bulk_test.ts +29 -4
  79. package/src/public/fixtures/custom_bytecode_tests.ts +139 -1
  80. package/src/public/fixtures/opcode_spammer.ts +4 -8
  81. package/src/public/fixtures/public_tx_simulation_tester.ts +4 -9
  82. package/src/public/fixtures/utils.ts +21 -18
  83. package/src/public/hinting_db_sources.ts +5 -0
  84. package/src/public/public_db_sources.ts +21 -14
  85. package/src/public/public_processor/guarded_merkle_tree.ts +5 -2
  86. package/src/public/public_processor/public_processor.ts +79 -41
  87. package/src/public/public_processor/public_processor_metrics.ts +12 -0
  88. package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +3 -2
  89. package/src/public/public_tx_simulator/cpp_public_tx_simulator.ts +3 -12
  90. package/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts +3 -10
  91. package/src/public/public_tx_simulator/public_tx_context.ts +8 -10
  92. package/src/public/public_tx_simulator/public_tx_simulator.ts +11 -7
@@ -1,16 +1,11 @@
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';
1
+ import { PUBLIC_TX_L2_GAS_OVERHEAD, TX_DA_GAS_OVERHEAD } from '@aztec/constants';
7
2
  import { asyncMap } from '@aztec/foundation/async-map';
8
3
  import { BlockNumber } from '@aztec/foundation/branded-types';
9
4
  import { Fr } from '@aztec/foundation/curves/bn254';
10
5
  import { type ContractArtifact, encodeArguments } from '@aztec/stdlib/abi';
11
6
  import { PublicSimulatorConfig, type PublicTxResult } from '@aztec/stdlib/avm';
12
7
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
13
- import { Gas, GasFees } from '@aztec/stdlib/gas';
8
+ import { FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT, Gas, GasFees } from '@aztec/stdlib/gas';
14
9
  import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
15
10
  import { PublicCallRequest } from '@aztec/stdlib/kernel';
16
11
  import { GlobalVariables, PublicCallRequestWithCalldata, type Tx } from '@aztec/stdlib/tx';
@@ -138,8 +133,8 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
138
133
  feePayer,
139
134
  /*gasUsedByPrivate*/ teardownCall
140
135
  ? new Gas(
141
- DEFAULT_TEARDOWN_DA_GAS_LIMIT + TX_DA_GAS_OVERHEAD,
142
- DEFAULT_TEARDOWN_L2_GAS_LIMIT + PUBLIC_TX_L2_GAS_OVERHEAD,
136
+ FALLBACK_TEARDOWN_DA_GAS_LIMIT + TX_DA_GAS_OVERHEAD,
137
+ FALLBACK_TEARDOWN_L2_GAS_LIMIT + PUBLIC_TX_L2_GAS_OVERHEAD,
143
138
  )
144
139
  : new Gas(TX_DA_GAS_OVERHEAD, PUBLIC_TX_L2_GAS_OVERHEAD),
145
140
  defaultGlobals(),
@@ -2,10 +2,8 @@ import {
2
2
  CONTRACT_CLASS_PUBLISHED_MAGIC_VALUE,
3
3
  CONTRACT_CLASS_REGISTRY_CONTRACT_ADDRESS,
4
4
  CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
5
- DEFAULT_DA_GAS_LIMIT,
6
- DEFAULT_L2_GAS_LIMIT,
7
- DEFAULT_TEARDOWN_DA_GAS_LIMIT,
8
- DEFAULT_TEARDOWN_L2_GAS_LIMIT,
5
+ MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT,
6
+ MAX_PROCESSABLE_L2_GAS,
9
7
  PRIVATE_LOG_SIZE_IN_FIELDS,
10
8
  } from '@aztec/constants';
11
9
  import { padArrayEnd } from '@aztec/foundation/collection';
@@ -14,7 +12,13 @@ import { CONTRACT_INSTANCE_PUBLISHED_EVENT_TAG } from '@aztec/protocol-contracts
14
12
  import { bufferAsFields } from '@aztec/stdlib/abi';
15
13
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
16
14
  import type { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
17
- import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
15
+ import {
16
+ FALLBACK_TEARDOWN_DA_GAS_LIMIT,
17
+ FALLBACK_TEARDOWN_L2_GAS_LIMIT,
18
+ Gas,
19
+ GasFees,
20
+ GasSettings,
21
+ } from '@aztec/stdlib/gas';
18
22
  import { siloNullifier } from '@aztec/stdlib/hash';
19
23
  import {
20
24
  LogHash,
@@ -69,7 +73,7 @@ export async function createTxForPublicCalls(
69
73
  "Can't create public tx with no enqueued calls",
70
74
  );
71
75
  // use max limits
72
- gasLimits = gasLimits ?? new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
76
+ gasLimits = gasLimits ?? new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
73
77
 
74
78
  const forPublic = PartialPrivateTailPublicInputsForPublic.empty();
75
79
 
@@ -129,7 +133,7 @@ export async function createTxForPublicCalls(
129
133
 
130
134
  const maxFeesPerGas = feePayer.isZero() ? GasFees.empty() : new GasFees(10, 10);
131
135
  const teardownGasLimits = teardownCallRequest
132
- ? new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT)
136
+ ? new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT)
133
137
  : Gas.empty();
134
138
  const gasSettings = new GasSettings(gasLimits, teardownGasLimits, maxFeesPerGas, GasFees.empty());
135
139
  const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
@@ -164,7 +168,7 @@ export async function createTxForPrivateOnly(
164
168
  gasUsedByPrivate: Gas = new Gas(10, 10),
165
169
  ): Promise<Tx> {
166
170
  // use max limits
167
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
171
+ const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
168
172
 
169
173
  const forRollup = PartialPrivateTailPublicInputsForRollup.empty();
170
174
 
@@ -229,17 +233,15 @@ export async function addNewContractInstanceToTx(
229
233
  contractInstance: ContractInstanceWithAddress,
230
234
  skipNullifierInsertion = false,
231
235
  ) {
232
- // can't use publicKeys.toFields() because it includes isInfinite which
233
- // is not broadcast in such private logs
236
+ // Only ivpk_m is broadcast as a point (x, y); the other five keys are hashes.
234
237
  const publicKeysAsFields = [
235
- contractInstance.publicKeys.masterNullifierPublicKey.x,
236
- contractInstance.publicKeys.masterNullifierPublicKey.y,
237
- contractInstance.publicKeys.masterIncomingViewingPublicKey.x,
238
- contractInstance.publicKeys.masterIncomingViewingPublicKey.y,
239
- contractInstance.publicKeys.masterOutgoingViewingPublicKey.x,
240
- contractInstance.publicKeys.masterOutgoingViewingPublicKey.y,
241
- contractInstance.publicKeys.masterTaggingPublicKey.x,
242
- contractInstance.publicKeys.masterTaggingPublicKey.y,
238
+ contractInstance.publicKeys.npkMHash,
239
+ contractInstance.publicKeys.ivpkM.x,
240
+ contractInstance.publicKeys.ivpkM.y,
241
+ contractInstance.publicKeys.ovpkMHash,
242
+ contractInstance.publicKeys.tpkMHash,
243
+ contractInstance.publicKeys.mspkMHash,
244
+ contractInstance.publicKeys.fbpkMHash,
243
245
  ];
244
246
  const logFields = [
245
247
  CONTRACT_INSTANCE_PUBLISHED_EVENT_TAG,
@@ -248,6 +250,7 @@ export async function addNewContractInstanceToTx(
248
250
  new Fr(contractInstance.salt),
249
251
  contractInstance.currentContractClassId,
250
252
  contractInstance.initializationHash,
253
+ contractInstance.immutablesHash,
251
254
  ...publicKeysAsFields,
252
255
  contractInstance.deployer.toField(),
253
256
  ];
@@ -83,6 +83,7 @@ export class HintingPublicContractsDB implements PublicContractsDBInterface {
83
83
  instance.currentContractClassId,
84
84
  instance.originalContractClassId,
85
85
  instance.initializationHash,
86
+ instance.immutablesHash,
86
87
  instance.publicKeys,
87
88
  ),
88
89
  );
@@ -554,6 +555,10 @@ export class HintingMerkleWriteOperations implements MerkleTreeWriteOperations {
554
555
  return this.db.getRevision();
555
556
  }
556
557
 
558
+ public getSocketPath(): string {
559
+ return this.db.getSocketPath();
560
+ }
561
+
557
562
  public async updateArchive(header: any): Promise<void> {
558
563
  return await this.db.updateArchive(header);
559
564
  }
@@ -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(
@@ -12,7 +12,7 @@ import type {
12
12
  TreeInfo,
13
13
  } from '@aztec/stdlib/trees';
14
14
  import type { BlockHeader, StateReference } from '@aztec/stdlib/tx';
15
- import type { WorldStateRevision, WorldStateRevisionWithHandle } from '@aztec/stdlib/world-state';
15
+ import type { WorldStateRevision } from '@aztec/stdlib/world-state';
16
16
 
17
17
  /**
18
18
  * Wraps an instance of `MerkleTreeWriteOperations` to allow the sequencer to gate access.
@@ -94,9 +94,12 @@ export class GuardedMerkleTreeOperations implements MerkleTreeWriteOperations {
94
94
  getInitialHeader(): BlockHeader {
95
95
  return this.target.getInitialHeader();
96
96
  }
97
- public getRevision(): WorldStateRevision | WorldStateRevisionWithHandle {
97
+ public getRevision(): WorldStateRevision {
98
98
  return this.target.getRevision();
99
99
  }
100
+ public getSocketPath(): string {
101
+ return this.target.getSocketPath();
102
+ }
100
103
  getSiblingPath<ID extends MerkleTreeId>(treeId: ID, index: bigint): Promise<SiblingPath<TreeHeights[ID]>> {
101
104
  return this.guardAndPush(() => this.target.getSiblingPath(treeId, index));
102
105
  }
@@ -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';
@@ -76,17 +76,15 @@ export class PublicProcessorFactory {
76
76
  /**
77
77
  * Creates a new instance of a PublicProcessor.
78
78
  * @param globalVariables - The global variables for the block being processed.
79
- * @param skipFeeEnforcement - Allows disabling balance checks for fee estimations.
79
+ * @param contractsDB - Optional pre-populated contracts DB; a fresh one is constructed if omitted.
80
80
  * @returns A new instance of a PublicProcessor.
81
81
  */
82
82
  public create(
83
83
  merkleTree: MerkleTreeWriteOperations,
84
84
  globalVariables: GlobalVariables,
85
85
  config: PublicSimulatorConfig,
86
+ contractsDB: PublicContractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings()),
86
87
  ): PublicProcessor {
87
- const bindings = this.log.getBindings();
88
- const contractsDB = new PublicContractsDB(this.contractDataSource, bindings);
89
-
90
88
  const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
91
89
  const publicTxSimulator = this.createPublicTxSimulator(guardedFork, contractsDB, globalVariables, config);
92
90
 
@@ -97,7 +95,7 @@ export class PublicProcessorFactory {
97
95
  publicTxSimulator,
98
96
  this.dateProvider,
99
97
  this.telemetryClient,
100
- createLogger('simulator:public-processor', bindings),
98
+ createLogger('simulator:public-processor', this.log.getBindings()),
101
99
  );
102
100
  }
103
101
 
@@ -125,6 +123,17 @@ class PublicProcessorTimeoutError extends Error {
125
123
  }
126
124
  }
127
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
+
128
137
  /**
129
138
  * Converts Txs lifted from the P2P module into ProcessedTx objects by executing
130
139
  * any public function calls in them. Txs with private calls only are unaffected.
@@ -161,7 +170,7 @@ export class PublicProcessor implements Traceable {
161
170
  limits: PublicProcessorLimits = {},
162
171
  validator: PublicProcessorValidator = {},
163
172
  ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
164
- const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal } = limits;
173
+ const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal, signal } = limits;
165
174
  const { preprocessValidator, nullifierCache } = validator;
166
175
  const result: ProcessedTx[] = [];
167
176
  const usedTxs: Tx[] = [];
@@ -174,6 +183,8 @@ export class PublicProcessor implements Traceable {
174
183
  let totalPublicGas = new Gas(0, 0);
175
184
  let totalBlockGas = new Gas(0, 0);
176
185
  let totalBlobFields = 0;
186
+ let silentlySkippedCount = 0;
187
+ let totalSilentlySkippedDurationMs = 0;
177
188
 
178
189
  for await (const tx of txs) {
179
190
  // Only process up to the max tx limit
@@ -182,11 +193,15 @@ export class PublicProcessor implements Traceable {
182
193
  break;
183
194
  }
184
195
 
185
- // Bail if we've hit the deadline
196
+ // Bail if we've hit the deadline or have been interrupted.
186
197
  if (deadline && this.dateProvider.now() > +deadline) {
187
198
  this.log.warn(`Stopping tx processing due to timeout.`);
188
199
  break;
189
200
  }
201
+ if (signal?.aborted) {
202
+ this.log.warn(`Stopping tx processing due to abort signal.`);
203
+ break;
204
+ }
190
205
 
191
206
  const txHash = tx.getTxHash().toString();
192
207
 
@@ -224,11 +239,6 @@ export class PublicProcessor implements Traceable {
224
239
  failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
225
240
  returns.push(new NestedProcessReturnValues([]));
226
241
  continue;
227
- } else if (result.result === 'skipped') {
228
- const reason = result.reason.join(', ');
229
- this.log.debug(`Skipping tx ${txHash.toString()} due to pre-process validation: ${reason}`);
230
- returns.push(new NestedProcessReturnValues([]));
231
- continue;
232
242
  } else {
233
243
  this.log.trace(`Tx ${txHash.toString()} is valid before processing.`);
234
244
  }
@@ -244,7 +254,9 @@ export class PublicProcessor implements Traceable {
244
254
  this.contractsDB.createCheckpoint();
245
255
 
246
256
  try {
247
- const [processedTx, returnValues, txDebugLogs] = await this.processTx(tx, deadline);
257
+ const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
258
+ this.processTx(tx, deadline, signal),
259
+ );
248
260
 
249
261
  // Inject a fake processing failure after N txs if requested
250
262
  const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
@@ -265,8 +277,12 @@ export class PublicProcessor implements Traceable {
265
277
  txBlobFields,
266
278
  totalBlobFields,
267
279
  maxBlobFields,
280
+ txProcessingTimeMs,
268
281
  },
269
282
  );
283
+ silentlySkippedCount += 1;
284
+ totalSilentlySkippedDurationMs += txProcessingTimeMs;
285
+ this.metrics.recordSilentlySkipped(txProcessingTimeMs);
270
286
  // Need to revert the checkpoint here and don't go any further
271
287
  await checkpoint.revert();
272
288
  this.contractsDB.revertCheckpoint();
@@ -306,16 +322,15 @@ export class PublicProcessor implements Traceable {
306
322
  totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
307
323
  totalSizeInBytes += txSize;
308
324
  totalBlobFields += txBlobFields;
325
+
326
+ // Commit the tx-level contracts checkpoint on success
327
+ this.contractsDB.commitCheckpoint();
309
328
  } catch (err: any) {
310
- if (err?.name === 'PublicProcessorTimeoutError') {
311
- this.log.warn(`Stopping tx processing due to timeout.`);
312
- // We hit the transaction execution deadline.
313
- // There may still be a transaction executing on a worker thread (C++ via NAPI).
314
- // Signal cancellation AND WAIT for the simulation to actually stop.
315
- // This is critical because C++ might be in the middle of a slow operation (e.g., pad_trees)
316
- // and won't check the cancellation flag until that operation completes.
317
- // Without waiting, we'd proceed to revert checkpoints while C++ is still writing to state.
318
- // 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.
319
334
  await this.publicTxSimulator.cancel?.();
320
335
 
321
336
  // Now stop the guarded fork to prevent any further TS-side access to the world state.
@@ -354,7 +369,6 @@ export class PublicProcessor implements Traceable {
354
369
  } finally {
355
370
  // Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
356
371
  await checkpoint.commit();
357
- this.contractsDB.commitCheckpointOkIfNone();
358
372
  }
359
373
  }
360
374
 
@@ -362,13 +376,24 @@ export class PublicProcessor implements Traceable {
362
376
  const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
363
377
  this.metrics.recordAllTxs(totalPublicGas, rate);
364
378
 
365
- this.log.info(`Processed ${result.length} successful txs and ${failed.length} failed txs in ${duration}s`, {
366
- duration,
367
- rate,
368
- totalPublicGas,
369
- totalBlockGas,
370
- totalSizeInBytes,
371
- });
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
+ );
372
397
 
373
398
  return [result, failed, usedTxs, returns, debugLogs];
374
399
  }
@@ -393,9 +418,10 @@ export class PublicProcessor implements Traceable {
393
418
  private async processTx(
394
419
  tx: Tx,
395
420
  deadline: Date | undefined,
421
+ signal: AbortSignal | undefined,
396
422
  ): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
397
423
  const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
398
- this.processTxWithinDeadline(tx, deadline),
424
+ this.processTxWithinDeadline(tx, deadline, signal),
399
425
  );
400
426
 
401
427
  this.log.verbose(
@@ -449,10 +475,11 @@ export class PublicProcessor implements Traceable {
449
475
  this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
450
476
  }
451
477
 
452
- /** 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. */
453
479
  private async processTxWithinDeadline(
454
480
  tx: Tx,
455
481
  deadline: Date | undefined,
482
+ signal: AbortSignal | undefined,
456
483
  ): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
457
484
  const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
458
485
  tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
@@ -469,27 +496,38 @@ export class PublicProcessor implements Traceable {
469
496
  }
470
497
  : innerProcessFn;
471
498
 
472
- if (!deadline) {
499
+ const processingSignal = this.getProcessingSignal(tx, deadline, signal);
500
+ if (!processingSignal) {
473
501
  return await processFn();
474
502
  }
475
503
 
476
- 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
+
477
517
  const timeout = +deadline - this.dateProvider.now();
478
518
  if (timeout <= 0) {
479
519
  throw new PublicProcessorTimeoutError();
480
520
  }
481
521
 
522
+ const txHash = tx.getTxHash();
482
523
  this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, {
483
524
  deadline: deadline.toISOString(),
484
525
  now: new Date(this.dateProvider.now()).toISOString(),
485
526
  txHash,
486
527
  });
487
528
 
488
- return await executeTimeout(
489
- () => processFn(),
490
- timeout,
491
- () => new PublicProcessorTimeoutError(),
492
- );
529
+ const timeoutSignal = AbortSignal.timeout(timeout);
530
+ return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
493
531
  }
494
532
 
495
533
  /**
@@ -546,7 +584,7 @@ export class PublicProcessor implements Traceable {
546
584
  // Fee payment insertion has already been done. Do the rest.
547
585
  await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
548
586
 
549
- await this.contractsDB.addNewContracts(tx);
587
+ this.contractsDB.addNewContracts(tx);
550
588
 
551
589
  return [processedTx, undefined, []];
552
590
  }
@@ -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> => {
@@ -12,11 +12,8 @@ import {
12
12
  import { SimulationError } from '@aztec/stdlib/errors';
13
13
  import type { MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
14
14
  import type { GlobalVariables, Tx } from '@aztec/stdlib/tx';
15
- import { WorldStateRevisionWithHandle } from '@aztec/stdlib/world-state';
16
15
  import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
17
16
 
18
- import { strict as assert } from 'assert';
19
-
20
17
  import { ExecutorMetrics } from '../executor_metrics.js';
21
18
  import type { ExecutorMetricsInterface } from '../executor_metrics_interface.js';
22
19
  import type { PublicContractsDB } from '../public_db_sources.js';
@@ -62,14 +59,8 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
62
59
  txHash,
63
60
  });
64
61
 
65
- // Using the "as WorldStateRevisionWithHandle" is a bit of a "trust me bro", hence the assert.
66
- let wsRevision = this.merkleTree.getRevision();
67
- assert(
68
- wsRevision instanceof WorldStateRevisionWithHandle,
69
- 'CppPublicTxSimulator a real NativeWorldStateInstance with a handle to the C++ WorldState object',
70
- );
71
- const wsCppHandle = (wsRevision as WorldStateRevisionWithHandle).handle;
72
- wsRevision = wsRevision.toWorldStateRevision(); // for msgpack serialization, we don't include the handle in the type
62
+ const wsRevision = this.merkleTree.getRevision();
63
+ const wsdbSocketPath = this.merkleTree.getSocketPath();
73
64
 
74
65
  this.log.trace(`Running C++ simulation with world state revision ${JSON.stringify(wsRevision)}`);
75
66
 
@@ -99,7 +90,7 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
99
90
  this.simulationPromise = avmSimulate(
100
91
  inputBuffer,
101
92
  contractProvider,
102
- wsCppHandle,
93
+ wsdbSocketPath,
103
94
  this.log.level,
104
95
  undefined,
105
96
  this.cancellationToken,
@@ -11,7 +11,6 @@ import {
11
11
  import { SimulationError } from '@aztec/stdlib/errors';
12
12
  import type { MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
13
13
  import type { GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
14
- import { WorldStateRevisionWithHandle } from '@aztec/stdlib/world-state';
15
14
 
16
15
  import { strict as assert } from 'assert';
17
16
 
@@ -81,14 +80,8 @@ export class CppVsTsPublicTxSimulator extends PublicTxSimulator implements Publi
81
80
 
82
81
  this.log.debug(`Running C++ simulation for tx ${txHash}`);
83
82
 
84
- // Using the "as WorldStateRevisionWithHandle" is a bit of a "trust me bro", hence the assert.
85
- let wsRevision = this.merkleTree.getRevision();
86
- assert(
87
- wsRevision instanceof WorldStateRevisionWithHandle,
88
- 'CppPublicTxSimulator a real NativeWorldStateInstance with a handle to the C++ WorldState object',
89
- );
90
- const wsCppHandle = (wsRevision as WorldStateRevisionWithHandle).handle;
91
- wsRevision = wsRevision.toWorldStateRevision(); // for msgpack serialization, we don't include the handle in the type
83
+ const wsRevision = this.merkleTree.getRevision();
84
+ const wsdbSocketPath = this.merkleTree.getSocketPath();
92
85
 
93
86
  this.log.debug(`Running C++ simulation with world state revision ${JSON.stringify(wsRevision)}`);
94
87
 
@@ -113,7 +106,7 @@ export class CppVsTsPublicTxSimulator extends PublicTxSimulator implements Publi
113
106
  let resultBuffer: Buffer;
114
107
  try {
115
108
  this.log.debug(`Calling C++ simulator for tx ${txHash}`);
116
- resultBuffer = await avmSimulate(inputBuffer, contractProvider, wsCppHandle, logLevel);
109
+ resultBuffer = await avmSimulate(inputBuffer, contractProvider, wsdbSocketPath, logLevel);
117
110
  } catch (error: any) {
118
111
  throw new SimulationError(`C++ simulation failed: ${error.message}`, []);
119
112
  }
@@ -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);