@aztec/simulator 0.66.0 → 0.67.1-devnet

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 (158) hide show
  1. package/dest/acvm/acvm.js +3 -3
  2. package/dest/acvm/oracle/oracle.d.ts +2 -2
  3. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  4. package/dest/acvm/oracle/oracle.js +5 -5
  5. package/dest/acvm/oracle/typed_oracle.d.ts +3 -3
  6. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  7. package/dest/acvm/oracle/typed_oracle.js +5 -5
  8. package/dest/acvm/serialize.js +2 -2
  9. package/dest/avm/avm_context.d.ts +2 -2
  10. package/dest/avm/avm_context.d.ts.map +1 -1
  11. package/dest/avm/avm_context.js +3 -4
  12. package/dest/avm/avm_execution_environment.d.ts +4 -6
  13. package/dest/avm/avm_execution_environment.d.ts.map +1 -1
  14. package/dest/avm/avm_execution_environment.js +8 -13
  15. package/dest/avm/avm_memory_types.d.ts +3 -3
  16. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  17. package/dest/avm/avm_memory_types.js +29 -29
  18. package/dest/avm/avm_simulator.d.ts +3 -3
  19. package/dest/avm/avm_simulator.d.ts.map +1 -1
  20. package/dest/avm/avm_simulator.js +27 -15
  21. package/dest/avm/avm_tree.d.ts +2 -1
  22. package/dest/avm/avm_tree.d.ts.map +1 -1
  23. package/dest/avm/avm_tree.js +6 -2
  24. package/dest/avm/errors.d.ts +3 -3
  25. package/dest/avm/errors.d.ts.map +1 -1
  26. package/dest/avm/errors.js +8 -15
  27. package/dest/avm/fixtures/index.d.ts +2 -0
  28. package/dest/avm/fixtures/index.d.ts.map +1 -1
  29. package/dest/avm/fixtures/index.js +7 -7
  30. package/dest/avm/journal/journal.d.ts +31 -8
  31. package/dest/avm/journal/journal.d.ts.map +1 -1
  32. package/dest/avm/journal/journal.js +139 -42
  33. package/dest/avm/opcodes/conversion.d.ts +4 -4
  34. package/dest/avm/opcodes/conversion.d.ts.map +1 -1
  35. package/dest/avm/opcodes/conversion.js +22 -18
  36. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  37. package/dest/avm/opcodes/external_calls.js +4 -11
  38. package/dest/avm/opcodes/misc.d.ts.map +1 -1
  39. package/dest/avm/opcodes/misc.js +3 -3
  40. package/dest/avm/test_utils.d.ts +1 -0
  41. package/dest/avm/test_utils.d.ts.map +1 -1
  42. package/dest/avm/test_utils.js +4 -1
  43. package/dest/client/client_execution_context.d.ts +3 -3
  44. package/dest/client/client_execution_context.d.ts.map +1 -1
  45. package/dest/client/client_execution_context.js +15 -8
  46. package/dest/client/db_oracle.d.ts +9 -5
  47. package/dest/client/db_oracle.d.ts.map +1 -1
  48. package/dest/client/execution_note_cache.d.ts +9 -1
  49. package/dest/client/execution_note_cache.d.ts.map +1 -1
  50. package/dest/client/execution_note_cache.js +10 -3
  51. package/dest/client/index.d.ts +1 -0
  52. package/dest/client/index.d.ts.map +1 -1
  53. package/dest/client/index.js +2 -1
  54. package/dest/client/private_execution.d.ts.map +1 -1
  55. package/dest/client/private_execution.js +4 -4
  56. package/dest/client/simulator.d.ts.map +1 -1
  57. package/dest/client/simulator.js +4 -4
  58. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  59. package/dest/client/unconstrained_execution.js +3 -3
  60. package/dest/client/view_data_oracle.d.ts +4 -4
  61. package/dest/client/view_data_oracle.d.ts.map +1 -1
  62. package/dest/client/view_data_oracle.js +9 -9
  63. package/dest/common/debug_fn_name.d.ts +2 -2
  64. package/dest/common/debug_fn_name.d.ts.map +1 -1
  65. package/dest/common/debug_fn_name.js +8 -14
  66. package/dest/providers/acvm_native.js +4 -4
  67. package/dest/providers/acvm_wasm.js +2 -2
  68. package/dest/providers/factory.d.ts +2 -2
  69. package/dest/providers/factory.d.ts.map +1 -1
  70. package/dest/providers/factory.js +4 -4
  71. package/dest/providers/index.d.ts +0 -1
  72. package/dest/providers/index.d.ts.map +1 -1
  73. package/dest/providers/index.js +1 -2
  74. package/dest/public/enqueued_call_side_effect_trace.d.ts +15 -26
  75. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
  76. package/dest/public/enqueued_call_side_effect_trace.js +42 -62
  77. package/dest/public/execution.d.ts +2 -2
  78. package/dest/public/execution.d.ts.map +1 -1
  79. package/dest/public/execution.js +1 -1
  80. package/dest/public/executor_metrics.d.ts.map +1 -1
  81. package/dest/public/executor_metrics.js +2 -5
  82. package/dest/public/fee_payment.d.ts.map +1 -1
  83. package/dest/public/fee_payment.js +4 -3
  84. package/dest/public/fixtures/index.d.ts +24 -1
  85. package/dest/public/fixtures/index.d.ts.map +1 -1
  86. package/dest/public/fixtures/index.js +17 -11
  87. package/dest/public/index.d.ts +0 -1
  88. package/dest/public/index.d.ts.map +1 -1
  89. package/dest/public/index.js +1 -2
  90. package/dest/public/public_db_sources.d.ts.map +1 -1
  91. package/dest/public/public_db_sources.js +10 -9
  92. package/dest/public/public_processor.d.ts +12 -12
  93. package/dest/public/public_processor.d.ts.map +1 -1
  94. package/dest/public/public_processor.js +80 -68
  95. package/dest/public/public_processor_metrics.d.ts +1 -1
  96. package/dest/public/public_processor_metrics.d.ts.map +1 -1
  97. package/dest/public/public_tx_context.d.ts +13 -10
  98. package/dest/public/public_tx_context.d.ts.map +1 -1
  99. package/dest/public/public_tx_context.js +72 -45
  100. package/dest/public/public_tx_simulator.d.ts +2 -2
  101. package/dest/public/public_tx_simulator.d.ts.map +1 -1
  102. package/dest/public/public_tx_simulator.js +54 -23
  103. package/dest/public/side_effect_trace_interface.d.ts +6 -18
  104. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  105. package/dest/public/transitional_adapters.d.ts +2 -2
  106. package/dest/public/transitional_adapters.d.ts.map +1 -1
  107. package/dest/public/transitional_adapters.js +10 -39
  108. package/package.json +16 -9
  109. package/src/acvm/acvm.ts +2 -2
  110. package/src/acvm/oracle/oracle.ts +4 -4
  111. package/src/acvm/oracle/typed_oracle.ts +5 -5
  112. package/src/acvm/serialize.ts +1 -1
  113. package/src/avm/avm_context.ts +2 -3
  114. package/src/avm/avm_execution_environment.ts +6 -31
  115. package/src/avm/avm_memory_types.ts +31 -29
  116. package/src/avm/avm_simulator.ts +28 -22
  117. package/src/avm/avm_tree.ts +6 -1
  118. package/src/avm/errors.ts +12 -14
  119. package/src/avm/fixtures/index.ts +6 -5
  120. package/src/avm/journal/journal.ts +230 -71
  121. package/src/avm/opcodes/conversion.ts +21 -16
  122. package/src/avm/opcodes/external_calls.ts +3 -19
  123. package/src/avm/opcodes/misc.ts +2 -2
  124. package/src/avm/test_utils.ts +4 -0
  125. package/src/client/client_execution_context.ts +19 -9
  126. package/src/client/db_oracle.ts +10 -5
  127. package/src/client/execution_note_cache.ts +13 -3
  128. package/src/client/index.ts +1 -0
  129. package/src/client/private_execution.ts +3 -3
  130. package/src/client/simulator.ts +4 -4
  131. package/src/client/unconstrained_execution.ts +2 -2
  132. package/src/client/view_data_oracle.ts +11 -9
  133. package/src/common/debug_fn_name.ts +7 -13
  134. package/src/providers/acvm_native.ts +3 -3
  135. package/src/providers/acvm_wasm.ts +2 -2
  136. package/src/providers/factory.ts +3 -3
  137. package/src/providers/index.ts +0 -1
  138. package/src/public/enqueued_call_side_effect_trace.ts +62 -86
  139. package/src/public/execution.ts +1 -2
  140. package/src/public/executor_metrics.ts +0 -4
  141. package/src/public/fee_payment.ts +3 -2
  142. package/src/public/fixtures/index.ts +25 -12
  143. package/src/public/index.ts +0 -1
  144. package/src/public/public_db_sources.ts +9 -8
  145. package/src/public/public_processor.ts +109 -105
  146. package/src/public/public_processor_metrics.ts +1 -1
  147. package/src/public/public_tx_context.ts +97 -50
  148. package/src/public/public_tx_simulator.ts +69 -38
  149. package/src/public/side_effect_trace_interface.ts +10 -16
  150. package/src/public/transitional_adapters.ts +12 -48
  151. package/dest/public/dual_side_effect_trace.d.ts +0 -77
  152. package/dest/public/dual_side_effect_trace.d.ts.map +0 -1
  153. package/dest/public/dual_side_effect_trace.js +0 -119
  154. package/dest/public/side_effect_trace.d.ts +0 -96
  155. package/dest/public/side_effect_trace.d.ts.map +0 -1
  156. package/dest/public/side_effect_trace.js +0 -309
  157. package/src/public/dual_side_effect_trace.ts +0 -242
  158. package/src/public/side_effect_trace.ts +0 -536
@@ -4,7 +4,6 @@ import {
4
4
  type MerkleTreeWriteOperations,
5
5
  NestedProcessReturnValues,
6
6
  type ProcessedTx,
7
- type ProcessedTxHandler,
8
7
  Tx,
9
8
  TxExecutionPhase,
10
9
  type TxValidator,
@@ -13,20 +12,21 @@ import {
13
12
  } from '@aztec/circuit-types';
14
13
  import {
15
14
  type AztecAddress,
15
+ type BlockHeader,
16
16
  type ContractDataSource,
17
17
  Fr,
18
18
  type GlobalVariables,
19
- type Header,
20
19
  MAX_NOTE_HASHES_PER_TX,
21
20
  MAX_NULLIFIERS_PER_TX,
22
21
  NULLIFIER_SUBTREE_HEIGHT,
23
22
  PublicDataWrite,
24
23
  } from '@aztec/circuits.js';
25
24
  import { padArrayEnd } from '@aztec/foundation/collection';
26
- import { createDebugLogger } from '@aztec/foundation/log';
25
+ import { createLogger } from '@aztec/foundation/log';
27
26
  import { Timer } from '@aztec/foundation/timer';
28
- import { ContractClassRegisteredEvent, ProtocolContractAddress } from '@aztec/protocol-contracts';
29
- import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
27
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
28
+ import { ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer';
29
+ import { Attributes, type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
30
30
 
31
31
  import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from './fee_payment.js';
32
32
  import { WorldStateDB } from './public_db_sources.js';
@@ -47,13 +47,19 @@ export class PublicProcessorFactory {
47
47
  */
48
48
  public create(
49
49
  merkleTree: MerkleTreeWriteOperations,
50
- maybeHistoricalHeader: Header | undefined,
50
+ maybeHistoricalHeader: BlockHeader | undefined,
51
51
  globalVariables: GlobalVariables,
52
52
  ): PublicProcessor {
53
53
  const historicalHeader = maybeHistoricalHeader ?? merkleTree.getInitialHeader();
54
54
 
55
55
  const worldStateDB = new WorldStateDB(merkleTree, this.contractDataSource);
56
- const publicTxSimulator = new PublicTxSimulator(merkleTree, worldStateDB, this.telemetryClient, globalVariables);
56
+ const publicTxSimulator = new PublicTxSimulator(
57
+ merkleTree,
58
+ worldStateDB,
59
+ this.telemetryClient,
60
+ globalVariables,
61
+ /*doMerkleOperations=*/ true,
62
+ );
57
63
 
58
64
  return new PublicProcessor(
59
65
  merkleTree,
@@ -70,16 +76,16 @@ export class PublicProcessorFactory {
70
76
  * Converts Txs lifted from the P2P module into ProcessedTx objects by executing
71
77
  * any public function calls in them. Txs with private calls only are unaffected.
72
78
  */
73
- export class PublicProcessor {
79
+ export class PublicProcessor implements Traceable {
74
80
  private metrics: PublicProcessorMetrics;
75
81
  constructor(
76
82
  protected db: MerkleTreeWriteOperations,
77
83
  protected globalVariables: GlobalVariables,
78
- protected historicalHeader: Header,
84
+ protected historicalHeader: BlockHeader,
79
85
  protected worldStateDB: WorldStateDB,
80
86
  protected publicTxSimulator: PublicTxSimulator,
81
87
  telemetryClient: TelemetryClient,
82
- private log = createDebugLogger('aztec:simulator:public-processor'),
88
+ private log = createLogger('simulator:public-processor'),
83
89
  ) {
84
90
  this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
85
91
  }
@@ -97,7 +103,6 @@ export class PublicProcessor {
97
103
  public async process(
98
104
  txs: Tx[],
99
105
  maxTransactions = txs.length,
100
- processedTxHandler?: ProcessedTxHandler,
101
106
  txValidator?: TxValidator<ProcessedTx>,
102
107
  ): Promise<[ProcessedTx[], FailedTx[], NestedProcessReturnValues[]]> {
103
108
  // The processor modifies the tx objects in place, so we need to clone them.
@@ -112,66 +117,9 @@ export class PublicProcessor {
112
117
  break;
113
118
  }
114
119
  try {
115
- const [processedTx, returnValues] = !tx.hasPublicCalls()
116
- ? await this.processPrivateOnlyTx(tx)
117
- : await this.processTxWithPublicCalls(tx);
118
- this.log.debug(`Processed tx`, {
119
- txHash: processedTx.hash,
120
- historicalHeaderHash: processedTx.constants.historicalHeader.hash(),
121
- blockNumber: processedTx.constants.globalVariables.blockNumber,
122
- lastArchiveRoot: processedTx.constants.historicalHeader.lastArchive.root,
123
- });
124
-
125
- // Commit the state updates from this transaction
126
- await this.worldStateDB.commit();
127
-
128
- // Re-validate the transaction
129
- if (txValidator) {
130
- // Only accept processed transactions that are not double-spends,
131
- // public functions emitting nullifiers would pass earlier check but fail here.
132
- // Note that we're checking all nullifiers generated in the private execution twice,
133
- // we could store the ones already checked and skip them here as an optimization.
134
- const [_, invalid] = await txValidator.validateTxs([processedTx]);
135
- if (invalid.length) {
136
- throw new Error(`Transaction ${invalid[0].hash} invalid after processing public functions`);
137
- }
138
- }
139
- // if we were given a handler then send the transaction to it for block building or proving
140
- if (processedTxHandler) {
141
- await processedTxHandler.addNewTx(processedTx);
142
- }
143
- // Update the state so that the next tx in the loop has the correct .startState
144
- // NB: before this change, all .startStates were actually incorrect, but the issue was never caught because we either:
145
- // a) had only 1 tx with public calls per block, so this loop had len 1
146
- // b) always had a txHandler with the same db passed to it as this.db, which updated the db in buildBaseRollupHints in this loop
147
- // To see how this ^ happens, move back to one shared db in test_context and run orchestrator_multi_public_functions.test.ts
148
- // The below is taken from buildBaseRollupHints:
149
- await this.db.appendLeaves(
150
- MerkleTreeId.NOTE_HASH_TREE,
151
- padArrayEnd(processedTx.txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
152
- );
153
- try {
154
- await this.db.batchInsert(
155
- MerkleTreeId.NULLIFIER_TREE,
156
- padArrayEnd(processedTx.txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX).map(n => n.toBuffer()),
157
- NULLIFIER_SUBTREE_HEIGHT,
158
- );
159
- } catch (error) {
160
- if (txValidator) {
161
- // Ideally the validator has already caught this above, but just in case:
162
- throw new Error(`Transaction ${processedTx.hash} invalid after processing public functions`);
163
- } else {
164
- // We have no validator and assume this call should blindly process txs with duplicates being caught later
165
- this.log.warn(`Detected duplicate nullifier after public processing for: ${processedTx.hash}.`);
166
- }
167
- }
168
-
169
- await this.db.sequentialInsert(
170
- MerkleTreeId.PUBLIC_DATA_TREE,
171
- processedTx.txEffect.publicDataWrites.map(x => x.toBuffer()),
172
- );
120
+ const [processedTx, returnValues] = await this.processTx(tx, txValidator);
173
121
  result.push(processedTx);
174
- returns = returns.concat(returnValues ?? []);
122
+ returns = returns.concat(returnValues);
175
123
  } catch (err: any) {
176
124
  const errorMessage = err instanceof Error ? err.message : 'Unknown error';
177
125
  this.log.warn(`Failed to process tx ${tx.getTxHash()}: ${errorMessage} ${err?.stack}`);
@@ -187,17 +135,89 @@ export class PublicProcessor {
187
135
  return [result, failed, returns];
188
136
  }
189
137
 
138
+ @trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.tryGetTxHash()?.toString() }))
139
+ private async processTx(
140
+ tx: Tx,
141
+ txValidator?: TxValidator<ProcessedTx>,
142
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
143
+ const [processedTx, returnValues] = !tx.hasPublicCalls()
144
+ ? await this.processPrivateOnlyTx(tx)
145
+ : await this.processTxWithPublicCalls(tx);
146
+
147
+ this.log.verbose(
148
+ !tx.hasPublicCalls()
149
+ ? `Processed tx ${processedTx.hash} with no public calls`
150
+ : `Processed tx ${processedTx.hash} with ${tx.enqueuedPublicFunctionCalls.length} public calls`,
151
+ {
152
+ txHash: processedTx.hash,
153
+ txFee: processedTx.txEffect.transactionFee.toBigInt(),
154
+ revertCode: processedTx.txEffect.revertCode.getCode(),
155
+ revertReason: processedTx.revertReason,
156
+ gasUsed: processedTx.gasUsed,
157
+ publicDataWriteCount: processedTx.txEffect.publicDataWrites.length,
158
+ nullifierCount: processedTx.txEffect.nullifiers.length,
159
+ noteHashCount: processedTx.txEffect.noteHashes.length,
160
+ contractClassLogCount: processedTx.txEffect.contractClassLogs.getTotalLogCount(),
161
+ unencryptedLogCount: processedTx.txEffect.unencryptedLogs.getTotalLogCount(),
162
+ privateLogCount: processedTx.txEffect.privateLogs.length,
163
+ l2ToL1MessageCount: processedTx.txEffect.l2ToL1Msgs.length,
164
+ },
165
+ );
166
+
167
+ // Commit the state updates from this transaction
168
+ await this.worldStateDB.commit();
169
+
170
+ // Re-validate the transaction
171
+ if (txValidator) {
172
+ // Only accept processed transactions that are not double-spends,
173
+ // public functions emitting nullifiers would pass earlier check but fail here.
174
+ // Note that we're checking all nullifiers generated in the private execution twice,
175
+ // we could store the ones already checked and skip them here as an optimization.
176
+ const [_, invalid] = await txValidator.validateTxs([processedTx]);
177
+ if (invalid.length) {
178
+ throw new Error(`Transaction ${invalid[0].hash} invalid after processing public functions`);
179
+ }
180
+ }
181
+ // Update the state so that the next tx in the loop has the correct .startState
182
+ // NB: before this change, all .startStates were actually incorrect, but the issue was never caught because we either:
183
+ // a) had only 1 tx with public calls per block, so this loop had len 1
184
+ // b) always had a txHandler with the same db passed to it as this.db, which updated the db in buildBaseRollupHints in this loop
185
+ // To see how this ^ happens, move back to one shared db in test_context and run orchestrator_multi_public_functions.test.ts
186
+ // The below is taken from buildBaseRollupHints:
187
+ await this.db.appendLeaves(
188
+ MerkleTreeId.NOTE_HASH_TREE,
189
+ padArrayEnd(processedTx.txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
190
+ );
191
+ try {
192
+ await this.db.batchInsert(
193
+ MerkleTreeId.NULLIFIER_TREE,
194
+ padArrayEnd(processedTx.txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX).map(n => n.toBuffer()),
195
+ NULLIFIER_SUBTREE_HEIGHT,
196
+ );
197
+ } catch (error) {
198
+ if (txValidator) {
199
+ // Ideally the validator has already caught this above, but just in case:
200
+ throw new Error(`Transaction ${processedTx.hash} invalid after processing public functions`);
201
+ } else {
202
+ // We have no validator and assume this call should blindly process txs with duplicates being caught later
203
+ this.log.warn(`Detected duplicate nullifier after public processing for: ${processedTx.hash}.`);
204
+ }
205
+ }
206
+
207
+ await this.db.sequentialInsert(
208
+ MerkleTreeId.PUBLIC_DATA_TREE,
209
+ processedTx.txEffect.publicDataWrites.map(x => x.toBuffer()),
210
+ );
211
+
212
+ return [processedTx, returnValues ?? []];
213
+ }
214
+
190
215
  /**
191
- * Creates the final set of data update requests for the transaction. This includes the
192
- * set of public data update requests as returned by the public kernel, plus a data update
193
- * request for updating fee balance. It also updates the local public state db.
194
- * See build_or_patch_payment_update_request in base_rollup_inputs.nr for more details.
216
+ * Creates the public data write for paying the tx fee.
217
+ * This is used in private only txs, since for txs with public calls
218
+ * the avm handles the fee payment itself.
195
219
  */
196
- private async getFeePaymentPublicDataWrite(
197
- publicDataWrites: PublicDataWrite[],
198
- txFee: Fr,
199
- feePayer: AztecAddress,
200
- ): Promise<PublicDataWrite | undefined> {
220
+ private async getFeePaymentPublicDataWrite(txFee: Fr, feePayer: AztecAddress): Promise<PublicDataWrite | undefined> {
201
221
  if (feePayer.isZero()) {
202
222
  this.log.debug(`No one is paying the fee of ${txFee.toBigInt()}`);
203
223
  return;
@@ -209,11 +229,7 @@ export class PublicProcessor {
209
229
 
210
230
  this.log.debug(`Deducting ${txFee.toBigInt()} balance in Fee Juice for ${feePayer}`);
211
231
 
212
- const existingBalanceWrite = publicDataWrites.find(write => write.leafSlot.equals(leafSlot));
213
-
214
- const balance = existingBalanceWrite
215
- ? existingBalanceWrite.value
216
- : await this.worldStateDB.storageRead(feeJuiceAddress, balanceSlot);
232
+ const balance = await this.worldStateDB.storageRead(feeJuiceAddress, balanceSlot);
217
233
 
218
234
  if (balance.lt(txFee)) {
219
235
  throw new Error(
@@ -234,12 +250,7 @@ export class PublicProcessor {
234
250
  const gasFees = this.globalVariables.gasFees;
235
251
  const transactionFee = tx.data.gasUsed.computeFee(gasFees);
236
252
 
237
- const accumulatedData = tx.data.forRollup!.end;
238
- const feePaymentPublicDataWrite = await this.getFeePaymentPublicDataWrite(
239
- accumulatedData.publicDataWrites,
240
- transactionFee,
241
- tx.data.feePayer,
242
- );
253
+ const feePaymentPublicDataWrite = await this.getFeePaymentPublicDataWrite(transactionFee, tx.data.feePayer);
243
254
 
244
255
  const processedTx = makeProcessedTxFromPrivateOnlyTx(
245
256
  tx,
@@ -247,6 +258,13 @@ export class PublicProcessor {
247
258
  feePaymentPublicDataWrite,
248
259
  this.globalVariables,
249
260
  );
261
+
262
+ this.metrics.recordClassRegistration(
263
+ ...tx.contractClassLogs
264
+ .unrollLogs()
265
+ .filter(log => ContractClassRegisteredEvent.isContractClassRegisteredEvent(log.data))
266
+ .map(log => ContractClassRegisteredEvent.fromLog(log.data)),
267
+ );
250
268
  return [processedTx];
251
269
  }
252
270
 
@@ -283,21 +301,7 @@ export class PublicProcessor {
283
301
  const durationMs = timer.ms();
284
302
  this.metrics.recordTx(phaseCount, durationMs);
285
303
 
286
- const data = avmProvingRequest.inputs.output;
287
- const feePaymentPublicDataWrite = await this.getFeePaymentPublicDataWrite(
288
- data.accumulatedData.publicDataWrites,
289
- data.transactionFee,
290
- tx.data.feePayer,
291
- );
292
-
293
- const processedTx = makeProcessedTxFromTxWithPublicCalls(
294
- tx,
295
- avmProvingRequest,
296
- feePaymentPublicDataWrite,
297
- gasUsed,
298
- revertCode,
299
- revertReason,
300
- );
304
+ const processedTx = makeProcessedTxFromTxWithPublicCalls(tx, avmProvingRequest, gasUsed, revertCode, revertReason);
301
305
 
302
306
  const returnValues = processedPhases.find(({ phase }) => phase === TxExecutionPhase.APP_LOGIC)?.returnValues ?? [];
303
307
 
@@ -1,5 +1,5 @@
1
1
  import { type TxExecutionPhase } from '@aztec/circuit-types';
2
- import { type ContractClassRegisteredEvent } from '@aztec/protocol-contracts';
2
+ import { type ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer';
3
3
  import {
4
4
  Attributes,
5
5
  type Histogram,
@@ -10,31 +10,33 @@ import {
10
10
  TxHash,
11
11
  } from '@aztec/circuit-types';
12
12
  import {
13
- AppendOnlyTreeSnapshot,
14
13
  AvmCircuitInputs,
15
14
  type AvmCircuitPublicInputs,
15
+ type AztecAddress,
16
16
  Fr,
17
17
  Gas,
18
18
  type GasSettings,
19
19
  type GlobalVariables,
20
+ MAX_L2_GAS_PER_TX_PUBLIC_PORTION,
21
+ MAX_NOTE_HASHES_PER_TX,
22
+ MAX_NULLIFIERS_PER_TX,
20
23
  type PrivateToPublicAccumulatedData,
21
24
  type PublicCallRequest,
22
25
  PublicCircuitPublicInputs,
23
26
  RevertCode,
24
27
  type StateReference,
25
28
  TreeSnapshots,
29
+ computeTransactionFee,
26
30
  countAccumulatedItems,
27
31
  } from '@aztec/circuits.js';
28
- import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
32
+ import { type Logger, createLogger } from '@aztec/foundation/log';
29
33
 
30
34
  import { strict as assert } from 'assert';
31
35
  import { inspect } from 'util';
32
36
 
33
37
  import { AvmPersistableStateManager } from '../avm/index.js';
34
- import { DualSideEffectTrace } from './dual_side_effect_trace.js';
35
38
  import { PublicEnqueuedCallSideEffectTrace, SideEffectArrayLengths } from './enqueued_call_side_effect_trace.js';
36
39
  import { type WorldStateDB } from './public_db_sources.js';
37
- import { PublicSideEffectTrace } from './side_effect_trace.js';
38
40
  import { generateAvmCircuitPublicInputs } from './transitional_adapters.js';
39
41
  import { getCallRequestsByPhase, getExecutionRequestsByPhase } from './utils.js';
40
42
 
@@ -42,10 +44,10 @@ import { getCallRequestsByPhase, getExecutionRequestsByPhase } from './utils.js'
42
44
  * The transaction-level context for public execution.
43
45
  */
44
46
  export class PublicTxContext {
45
- private log: DebugLogger;
47
+ private log: Logger;
46
48
 
47
49
  /* Gas used including private, teardown gas _limit_, setup and app logic */
48
- private gasUsed: Gas;
50
+ private gasUsedByPublic: Gas = Gas.empty();
49
51
  /* Gas actually used during teardown (different from limit) */
50
52
  public teardownGasUsed: Gas = Gas.empty();
51
53
 
@@ -62,8 +64,9 @@ export class PublicTxContext {
62
64
  public readonly state: PhaseStateManager,
63
65
  private readonly globalVariables: GlobalVariables,
64
66
  private readonly startStateReference: StateReference,
65
- private readonly startGasUsed: Gas,
66
67
  private readonly gasSettings: GasSettings,
68
+ private readonly gasUsedByPrivate: Gas,
69
+ private readonly gasAllocatedToPublic: Gas,
67
70
  private readonly setupCallRequests: PublicCallRequest[],
68
71
  private readonly appLogicCallRequests: PublicCallRequest[],
69
72
  private readonly teardownCallRequests: PublicCallRequest[],
@@ -72,10 +75,10 @@ export class PublicTxContext {
72
75
  private readonly teardownExecutionRequests: PublicExecutionRequest[],
73
76
  public readonly nonRevertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData,
74
77
  public readonly revertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData,
78
+ public readonly feePayer: AztecAddress,
75
79
  public trace: PublicEnqueuedCallSideEffectTrace, // FIXME(dbanks12): should be private
76
80
  ) {
77
- this.log = createDebugLogger(`aztec:public_tx_context`);
78
- this.gasUsed = startGasUsed;
81
+ this.log = createLogger(`simulator:public_tx_context`);
79
82
  }
80
83
 
81
84
  public static async create(
@@ -87,10 +90,10 @@ export class PublicTxContext {
87
90
  ) {
88
91
  const nonRevertibleAccumulatedDataFromPrivate = tx.data.forPublic!.nonRevertibleAccumulatedData;
89
92
 
90
- const innerCallTrace = new PublicSideEffectTrace();
91
93
  const previousAccumulatedDataArrayLengths = new SideEffectArrayLengths(
92
94
  /*publicDataWrites*/ 0,
93
- countAccumulatedItems(nonRevertibleAccumulatedDataFromPrivate.noteHashes),
95
+ /*protocolPublicDataWrites*/ 0,
96
+ /*noteHashes*/ 0,
94
97
  /*nullifiers=*/ 0,
95
98
  countAccumulatedItems(nonRevertibleAccumulatedDataFromPrivate.l2ToL1Msgs),
96
99
  /*unencryptedLogsHashes*/ 0,
@@ -99,17 +102,27 @@ export class PublicTxContext {
99
102
  /*startSideEffectCounter=*/ 0,
100
103
  previousAccumulatedDataArrayLengths,
101
104
  );
102
- const trace = new DualSideEffectTrace(innerCallTrace, enqueuedCallTrace);
103
105
 
104
106
  // Transaction level state manager that will be forked for revertible phases.
105
- const txStateManager = await AvmPersistableStateManager.create(worldStateDB, trace, doMerkleOperations);
107
+ const txStateManager = await AvmPersistableStateManager.create(
108
+ worldStateDB,
109
+ enqueuedCallTrace,
110
+ doMerkleOperations,
111
+ fetchTxHash(nonRevertibleAccumulatedDataFromPrivate),
112
+ );
113
+
114
+ const gasSettings = tx.data.constants.txContext.gasSettings;
115
+ const gasUsedByPrivate = tx.data.gasUsed;
116
+ // Gas allocated to public is "whatever's left" after private, but with some max applied.
117
+ const gasAllocatedToPublic = applyMaxToAvailableGas(gasSettings.gasLimits.sub(gasUsedByPrivate));
106
118
 
107
119
  return new PublicTxContext(
108
120
  new PhaseStateManager(txStateManager),
109
121
  globalVariables,
110
122
  await db.getStateReference(),
111
- tx.data.gasUsed,
112
- tx.data.constants.txContext.gasSettings,
123
+ gasSettings,
124
+ gasUsedByPrivate,
125
+ gasAllocatedToPublic,
113
126
  getCallRequestsByPhase(tx, TxExecutionPhase.SETUP),
114
127
  getCallRequestsByPhase(tx, TxExecutionPhase.APP_LOGIC),
115
128
  getCallRequestsByPhase(tx, TxExecutionPhase.TEARDOWN),
@@ -118,6 +131,7 @@ export class PublicTxContext {
118
131
  getExecutionRequestsByPhase(tx, TxExecutionPhase.TEARDOWN),
119
132
  tx.data.forPublic!.nonRevertibleAccumulatedData,
120
133
  tx.data.forPublic!.revertibleAccumulatedData,
134
+ tx.data.feePayer,
121
135
  enqueuedCallTrace,
122
136
  );
123
137
  }
@@ -179,12 +193,7 @@ export class PublicTxContext {
179
193
  * @returns The transaction's hash.
180
194
  */
181
195
  getTxHash(): TxHash {
182
- // Private kernel functions are executed client side and for this reason tx hash is already set as first nullifier
183
- const firstNullifier = this.nonRevertibleAccumulatedDataFromPrivate.nullifiers[0];
184
- if (!firstNullifier || firstNullifier.isZero()) {
185
- throw new Error(`Cannot get tx hash since first nullifier is missing`);
186
- }
187
- return new TxHash(firstNullifier.toBuffer());
196
+ return fetchTxHash(this.nonRevertibleAccumulatedDataFromPrivate);
188
197
  }
189
198
 
190
199
  /**
@@ -230,13 +239,14 @@ export class PublicTxContext {
230
239
  }
231
240
 
232
241
  /**
233
- * How much gas is left for the specified phase?
242
+ * How much gas is left as of the specified phase?
234
243
  */
235
- getGasLeftForPhase(phase: TxExecutionPhase): Gas {
244
+ getGasLeftAtPhase(phase: TxExecutionPhase): Gas {
236
245
  if (phase === TxExecutionPhase.TEARDOWN) {
237
- return this.gasSettings.teardownGasLimits;
246
+ return applyMaxToAvailableGas(this.gasSettings.teardownGasLimits);
238
247
  } else {
239
- return this.gasSettings.gasLimits.sub(this.gasUsed);
248
+ const gasLeftForPublic = this.gasAllocatedToPublic.sub(this.gasUsedByPublic);
249
+ return gasLeftForPublic;
240
250
  }
241
251
  }
242
252
 
@@ -247,10 +257,18 @@ export class PublicTxContext {
247
257
  if (phase === TxExecutionPhase.TEARDOWN) {
248
258
  this.teardownGasUsed = this.teardownGasUsed.add(gas);
249
259
  } else {
250
- this.gasUsed = this.gasUsed.add(gas);
260
+ this.gasUsedByPublic = this.gasUsedByPublic.add(gas);
251
261
  }
252
262
  }
253
263
 
264
+ /**
265
+ * The gasUsed by public and private,
266
+ * as if the entire teardown gas limit was consumed.
267
+ */
268
+ getTotalGasUsed(): Gas {
269
+ return this.gasUsedByPrivate.add(this.gasUsedByPublic);
270
+ }
271
+
254
272
  /**
255
273
  * Compute the gas used using the actual gas used during teardown instead
256
274
  * of the teardown gas limit.
@@ -261,14 +279,7 @@ export class PublicTxContext {
261
279
  assert(this.halted, 'Can only compute actual gas used after tx execution ends');
262
280
  const requireTeardown = this.teardownCallRequests.length > 0;
263
281
  const teardownGasLimits = requireTeardown ? this.gasSettings.teardownGasLimits : Gas.empty();
264
- return this.gasUsed.sub(teardownGasLimits).add(this.teardownGasUsed);
265
- }
266
-
267
- /**
268
- * The gasUsed as if the entire teardown gas limit was consumed.
269
- */
270
- getGasUsedForFee(): Gas {
271
- return this.gasUsed;
282
+ return this.getTotalGasUsed().sub(teardownGasLimits).add(this.teardownGasUsed);
272
283
  }
273
284
 
274
285
  /**
@@ -288,12 +299,15 @@ export class PublicTxContext {
288
299
  * Should only be called during or after teardown.
289
300
  */
290
301
  private getTransactionFeeUnsafe(): Fr {
291
- const txFee = this.gasUsed.computeFee(this.globalVariables.gasFees);
302
+ const gasUsed = this.getTotalGasUsed();
303
+ const txFee = computeTransactionFee(this.globalVariables.gasFees, this.gasSettings, gasUsed);
304
+
292
305
  this.log.debug(`Computed tx fee`, {
293
306
  txFee,
294
- gasUsed: inspect(this.gasUsed),
307
+ gasUsed: inspect(gasUsed),
295
308
  gasFees: inspect(this.globalVariables.gasFees),
296
309
  });
310
+
297
311
  return txFee;
298
312
  }
299
313
 
@@ -302,16 +316,29 @@ export class PublicTxContext {
302
316
  */
303
317
  private generateAvmCircuitPublicInputs(endStateReference: StateReference): AvmCircuitPublicInputs {
304
318
  assert(this.halted, 'Can only get AvmCircuitPublicInputs after tx execution ends');
305
- const ephemeralTrees = this.state.getActiveStateManager().merkleTrees.treeMap;
306
-
307
- const getAppendSnaphot = (id: MerkleTreeId) => {
308
- const tree = ephemeralTrees.get(id)!;
309
- return new AppendOnlyTreeSnapshot(tree.getRoot(), Number(tree.leafCount));
310
- };
311
-
312
- const noteHashTree = getAppendSnaphot(MerkleTreeId.NOTE_HASH_TREE);
313
- const nullifierTree = getAppendSnaphot(MerkleTreeId.NULLIFIER_TREE);
314
- const publicDataTree = getAppendSnaphot(MerkleTreeId.PUBLIC_DATA_TREE);
319
+ const ephemeralTrees = this.state.getActiveStateManager().merkleTrees;
320
+
321
+ const noteHashTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE);
322
+ const nullifierTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE);
323
+ const publicDataTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE);
324
+ // Pad the note hash and nullifier trees
325
+ const paddedNoteHashTreeSize =
326
+ this.startStateReference.partial.noteHashTree.nextAvailableLeafIndex + MAX_NOTE_HASHES_PER_TX;
327
+ if (noteHashTree.nextAvailableLeafIndex > paddedNoteHashTreeSize) {
328
+ throw new Error(
329
+ `Inserted too many leaves in note hash tree: ${noteHashTree.nextAvailableLeafIndex} > ${paddedNoteHashTreeSize}`,
330
+ );
331
+ }
332
+ noteHashTree.nextAvailableLeafIndex = paddedNoteHashTreeSize;
333
+
334
+ const paddedNullifierTreeSize =
335
+ this.startStateReference.partial.nullifierTree.nextAvailableLeafIndex + MAX_NULLIFIERS_PER_TX;
336
+ if (nullifierTree.nextAvailableLeafIndex > paddedNullifierTreeSize) {
337
+ throw new Error(
338
+ `Inserted too many leaves in nullifier tree: ${nullifierTree.nextAvailableLeafIndex} > ${paddedNullifierTreeSize}`,
339
+ );
340
+ }
341
+ nullifierTree.nextAvailableLeafIndex = paddedNullifierTreeSize;
315
342
 
316
343
  const endTreeSnapshots = new TreeSnapshots(
317
344
  endStateReference.l1ToL2MessageTree,
@@ -324,15 +351,16 @@ export class PublicTxContext {
324
351
  this.trace,
325
352
  this.globalVariables,
326
353
  this.startStateReference,
327
- this.startGasUsed,
354
+ /*startGasUsed=*/ this.gasUsedByPrivate,
328
355
  this.gasSettings,
356
+ this.feePayer,
329
357
  this.setupCallRequests,
330
358
  this.appLogicCallRequests,
331
359
  this.teardownCallRequests,
332
360
  this.nonRevertibleAccumulatedDataFromPrivate,
333
361
  this.revertibleAccumulatedDataFromPrivate,
334
362
  endTreeSnapshots,
335
- /*endGasUsed=*/ this.gasUsed,
363
+ /*endGasUsed=*/ this.getTotalGasUsed(),
336
364
  this.getTransactionFeeUnsafe(),
337
365
  this.revertCode,
338
366
  );
@@ -367,12 +395,12 @@ export class PublicTxContext {
367
395
  * transaction level one.
368
396
  */
369
397
  class PhaseStateManager {
370
- private log: DebugLogger;
398
+ private log: Logger;
371
399
 
372
400
  private currentlyActiveStateManager: AvmPersistableStateManager | undefined;
373
401
 
374
402
  constructor(private readonly txStateManager: AvmPersistableStateManager) {
375
- this.log = createDebugLogger(`aztec:public_phase_state_manager`);
403
+ this.log = createLogger(`simulator:public_phase_state_manager`);
376
404
  }
377
405
 
378
406
  fork() {
@@ -405,3 +433,22 @@ class PhaseStateManager {
405
433
  this.currentlyActiveStateManager = undefined;
406
434
  }
407
435
  }
436
+
437
+ /**
438
+ * Apply L2 gas maximum.
439
+ */
440
+ function applyMaxToAvailableGas(availableGas: Gas) {
441
+ return new Gas(
442
+ /*daGas=*/ availableGas.daGas,
443
+ /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_TX_PUBLIC_PORTION),
444
+ );
445
+ }
446
+
447
+ function fetchTxHash(nonRevertibleAccumulatedData: PrivateToPublicAccumulatedData): TxHash {
448
+ // Private kernel functions are executed client side and for this reason tx hash is already set as first nullifier
449
+ const firstNullifier = nonRevertibleAccumulatedData.nullifiers[0];
450
+ if (!firstNullifier || firstNullifier.isZero()) {
451
+ throw new Error(`Cannot get tx hash since first nullifier is missing`);
452
+ }
453
+ return new TxHash(firstNullifier.toBuffer());
454
+ }