@aztec/simulator 0.66.0 → 0.67.0

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 (125) hide show
  1. package/dest/acvm/acvm.js +3 -3
  2. package/dest/acvm/oracle/oracle.d.ts +1 -1
  3. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  4. package/dest/acvm/oracle/oracle.js +3 -3
  5. package/dest/acvm/oracle/typed_oracle.d.ts +2 -2
  6. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  7. package/dest/acvm/oracle/typed_oracle.js +3 -3
  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 +2 -2
  16. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  17. package/dest/avm/avm_memory_types.js +3 -3
  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 +22 -13
  21. package/dest/avm/errors.d.ts +3 -3
  22. package/dest/avm/errors.d.ts.map +1 -1
  23. package/dest/avm/errors.js +8 -15
  24. package/dest/avm/fixtures/index.d.ts.map +1 -1
  25. package/dest/avm/fixtures/index.js +4 -4
  26. package/dest/avm/journal/journal.d.ts +15 -4
  27. package/dest/avm/journal/journal.d.ts.map +1 -1
  28. package/dest/avm/journal/journal.js +108 -29
  29. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  30. package/dest/avm/opcodes/external_calls.js +4 -11
  31. package/dest/avm/opcodes/misc.d.ts.map +1 -1
  32. package/dest/avm/opcodes/misc.js +3 -3
  33. package/dest/client/client_execution_context.d.ts +3 -3
  34. package/dest/client/client_execution_context.d.ts.map +1 -1
  35. package/dest/client/client_execution_context.js +14 -8
  36. package/dest/client/db_oracle.d.ts +2 -2
  37. package/dest/client/db_oracle.d.ts.map +1 -1
  38. package/dest/client/execution_note_cache.d.ts +9 -1
  39. package/dest/client/execution_note_cache.d.ts.map +1 -1
  40. package/dest/client/execution_note_cache.js +10 -3
  41. package/dest/client/private_execution.d.ts.map +1 -1
  42. package/dest/client/private_execution.js +4 -4
  43. package/dest/client/simulator.d.ts.map +1 -1
  44. package/dest/client/simulator.js +4 -4
  45. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  46. package/dest/client/unconstrained_execution.js +3 -3
  47. package/dest/client/view_data_oracle.d.ts +2 -2
  48. package/dest/client/view_data_oracle.d.ts.map +1 -1
  49. package/dest/client/view_data_oracle.js +5 -6
  50. package/dest/common/debug_fn_name.d.ts +2 -2
  51. package/dest/common/debug_fn_name.d.ts.map +1 -1
  52. package/dest/common/debug_fn_name.js +8 -14
  53. package/dest/providers/acvm_native.js +4 -4
  54. package/dest/providers/factory.d.ts +2 -2
  55. package/dest/providers/factory.d.ts.map +1 -1
  56. package/dest/providers/factory.js +4 -4
  57. package/dest/public/enqueued_call_side_effect_trace.d.ts +11 -23
  58. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
  59. package/dest/public/enqueued_call_side_effect_trace.js +37 -58
  60. package/dest/public/executor_metrics.d.ts.map +1 -1
  61. package/dest/public/executor_metrics.js +2 -5
  62. package/dest/public/fixtures/index.d.ts +24 -1
  63. package/dest/public/fixtures/index.d.ts.map +1 -1
  64. package/dest/public/fixtures/index.js +15 -9
  65. package/dest/public/index.d.ts +0 -1
  66. package/dest/public/index.d.ts.map +1 -1
  67. package/dest/public/index.js +1 -2
  68. package/dest/public/public_db_sources.d.ts.map +1 -1
  69. package/dest/public/public_db_sources.js +4 -4
  70. package/dest/public/public_processor.d.ts +7 -8
  71. package/dest/public/public_processor.d.ts.map +1 -1
  72. package/dest/public/public_processor.js +30 -22
  73. package/dest/public/public_tx_context.d.ts +13 -10
  74. package/dest/public/public_tx_context.d.ts.map +1 -1
  75. package/dest/public/public_tx_context.js +46 -31
  76. package/dest/public/public_tx_simulator.d.ts +2 -2
  77. package/dest/public/public_tx_simulator.d.ts.map +1 -1
  78. package/dest/public/public_tx_simulator.js +43 -23
  79. package/dest/public/side_effect_trace_interface.d.ts +4 -17
  80. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  81. package/dest/public/transitional_adapters.d.ts +2 -2
  82. package/dest/public/transitional_adapters.d.ts.map +1 -1
  83. package/dest/public/transitional_adapters.js +28 -24
  84. package/package.json +16 -9
  85. package/src/acvm/acvm.ts +2 -2
  86. package/src/acvm/oracle/oracle.ts +2 -2
  87. package/src/acvm/oracle/typed_oracle.ts +3 -3
  88. package/src/acvm/serialize.ts +1 -1
  89. package/src/avm/avm_context.ts +2 -3
  90. package/src/avm/avm_execution_environment.ts +6 -31
  91. package/src/avm/avm_memory_types.ts +2 -2
  92. package/src/avm/avm_simulator.ts +24 -20
  93. package/src/avm/errors.ts +12 -14
  94. package/src/avm/fixtures/index.ts +2 -3
  95. package/src/avm/journal/journal.ts +189 -63
  96. package/src/avm/opcodes/external_calls.ts +3 -19
  97. package/src/avm/opcodes/misc.ts +2 -2
  98. package/src/client/client_execution_context.ts +17 -9
  99. package/src/client/db_oracle.ts +2 -2
  100. package/src/client/execution_note_cache.ts +13 -3
  101. package/src/client/private_execution.ts +3 -3
  102. package/src/client/simulator.ts +4 -4
  103. package/src/client/unconstrained_execution.ts +2 -2
  104. package/src/client/view_data_oracle.ts +5 -6
  105. package/src/common/debug_fn_name.ts +7 -13
  106. package/src/providers/acvm_native.ts +3 -3
  107. package/src/providers/factory.ts +3 -3
  108. package/src/public/enqueued_call_side_effect_trace.ts +54 -74
  109. package/src/public/executor_metrics.ts +0 -4
  110. package/src/public/fixtures/index.ts +23 -10
  111. package/src/public/index.ts +0 -1
  112. package/src/public/public_db_sources.ts +3 -3
  113. package/src/public/public_processor.ts +46 -47
  114. package/src/public/public_tx_context.ts +52 -32
  115. package/src/public/public_tx_simulator.ts +58 -38
  116. package/src/public/side_effect_trace_interface.ts +8 -15
  117. package/src/public/transitional_adapters.ts +39 -24
  118. package/dest/public/dual_side_effect_trace.d.ts +0 -77
  119. package/dest/public/dual_side_effect_trace.d.ts.map +0 -1
  120. package/dest/public/dual_side_effect_trace.js +0 -119
  121. package/dest/public/side_effect_trace.d.ts +0 -96
  122. package/dest/public/side_effect_trace.d.ts.map +0 -1
  123. package/dest/public/side_effect_trace.js +0 -309
  124. package/src/public/dual_side_effect_trace.ts +0 -242
  125. package/src/public/side_effect_trace.ts +0 -536
@@ -13,17 +13,17 @@ import {
13
13
  } from '@aztec/circuit-types';
14
14
  import {
15
15
  type AztecAddress,
16
+ type BlockHeader,
16
17
  type ContractDataSource,
17
18
  Fr,
18
19
  type GlobalVariables,
19
- type Header,
20
20
  MAX_NOTE_HASHES_PER_TX,
21
21
  MAX_NULLIFIERS_PER_TX,
22
22
  NULLIFIER_SUBTREE_HEIGHT,
23
23
  PublicDataWrite,
24
24
  } from '@aztec/circuits.js';
25
25
  import { padArrayEnd } from '@aztec/foundation/collection';
26
- import { createDebugLogger } from '@aztec/foundation/log';
26
+ import { createLogger } from '@aztec/foundation/log';
27
27
  import { Timer } from '@aztec/foundation/timer';
28
28
  import { ContractClassRegisteredEvent, ProtocolContractAddress } from '@aztec/protocol-contracts';
29
29
  import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
@@ -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,
@@ -75,11 +81,11 @@ export class PublicProcessor {
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
  }
@@ -115,12 +121,26 @@ export class PublicProcessor {
115
121
  const [processedTx, returnValues] = !tx.hasPublicCalls()
116
122
  ? await this.processPrivateOnlyTx(tx)
117
123
  : 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
+ this.log.verbose(
126
+ !tx.hasPublicCalls()
127
+ ? `Processed tx ${processedTx.hash} with no public calls`
128
+ : `Processed tx ${processedTx.hash} with ${tx.enqueuedPublicFunctionCalls.length} public calls`,
129
+ {
130
+ txHash: processedTx.hash,
131
+ txFee: processedTx.txEffect.transactionFee.toBigInt(),
132
+ revertCode: processedTx.txEffect.revertCode.getCode(),
133
+ revertReason: processedTx.revertReason,
134
+ gasUsed: processedTx.gasUsed,
135
+ publicDataWriteCount: processedTx.txEffect.publicDataWrites.length,
136
+ nullifierCount: processedTx.txEffect.nullifiers.length,
137
+ noteHashCount: processedTx.txEffect.noteHashes.length,
138
+ contractClassLogCount: processedTx.txEffect.contractClassLogs.getTotalLogCount(),
139
+ unencryptedLogCount: processedTx.txEffect.unencryptedLogs.getTotalLogCount(),
140
+ privateLogCount: processedTx.txEffect.privateLogs.length,
141
+ l2ToL1MessageCount: processedTx.txEffect.l2ToL1Msgs.length,
142
+ },
143
+ );
124
144
 
125
145
  // Commit the state updates from this transaction
126
146
  await this.worldStateDB.commit();
@@ -188,16 +208,11 @@ export class PublicProcessor {
188
208
  }
189
209
 
190
210
  /**
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.
211
+ * Creates the public data write for paying the tx fee.
212
+ * This is used in private only txs, since for txs with public calls
213
+ * the avm handles the fee payment itself.
195
214
  */
196
- private async getFeePaymentPublicDataWrite(
197
- publicDataWrites: PublicDataWrite[],
198
- txFee: Fr,
199
- feePayer: AztecAddress,
200
- ): Promise<PublicDataWrite | undefined> {
215
+ private async getFeePaymentPublicDataWrite(txFee: Fr, feePayer: AztecAddress): Promise<PublicDataWrite | undefined> {
201
216
  if (feePayer.isZero()) {
202
217
  this.log.debug(`No one is paying the fee of ${txFee.toBigInt()}`);
203
218
  return;
@@ -209,11 +224,7 @@ export class PublicProcessor {
209
224
 
210
225
  this.log.debug(`Deducting ${txFee.toBigInt()} balance in Fee Juice for ${feePayer}`);
211
226
 
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);
227
+ const balance = await this.worldStateDB.storageRead(feeJuiceAddress, balanceSlot);
217
228
 
218
229
  if (balance.lt(txFee)) {
219
230
  throw new Error(
@@ -234,12 +245,7 @@ export class PublicProcessor {
234
245
  const gasFees = this.globalVariables.gasFees;
235
246
  const transactionFee = tx.data.gasUsed.computeFee(gasFees);
236
247
 
237
- const accumulatedData = tx.data.forRollup!.end;
238
- const feePaymentPublicDataWrite = await this.getFeePaymentPublicDataWrite(
239
- accumulatedData.publicDataWrites,
240
- transactionFee,
241
- tx.data.feePayer,
242
- );
248
+ const feePaymentPublicDataWrite = await this.getFeePaymentPublicDataWrite(transactionFee, tx.data.feePayer);
243
249
 
244
250
  const processedTx = makeProcessedTxFromPrivateOnlyTx(
245
251
  tx,
@@ -247,6 +253,13 @@ export class PublicProcessor {
247
253
  feePaymentPublicDataWrite,
248
254
  this.globalVariables,
249
255
  );
256
+
257
+ this.metrics.recordClassRegistration(
258
+ ...tx.contractClassLogs
259
+ .unrollLogs()
260
+ .filter(log => ContractClassRegisteredEvent.isContractClassRegisteredEvent(log.data))
261
+ .map(log => ContractClassRegisteredEvent.fromLog(log.data)),
262
+ );
250
263
  return [processedTx];
251
264
  }
252
265
 
@@ -283,21 +296,7 @@ export class PublicProcessor {
283
296
  const durationMs = timer.ms();
284
297
  this.metrics.recordTx(phaseCount, durationMs);
285
298
 
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
- );
299
+ const processedTx = makeProcessedTxFromTxWithPublicCalls(tx, avmProvingRequest, gasUsed, revertCode, revertReason);
301
300
 
302
301
  const returnValues = processedPhases.find(({ phase }) => phase === TxExecutionPhase.APP_LOGIC)?.returnValues ?? [];
303
302
 
@@ -13,10 +13,12 @@ import {
13
13
  AppendOnlyTreeSnapshot,
14
14
  AvmCircuitInputs,
15
15
  type AvmCircuitPublicInputs,
16
+ type AztecAddress,
16
17
  Fr,
17
18
  Gas,
18
19
  type GasSettings,
19
20
  type GlobalVariables,
21
+ MAX_L2_GAS_PER_TX_PUBLIC_PORTION,
20
22
  type PrivateToPublicAccumulatedData,
21
23
  type PublicCallRequest,
22
24
  PublicCircuitPublicInputs,
@@ -25,16 +27,14 @@ import {
25
27
  TreeSnapshots,
26
28
  countAccumulatedItems,
27
29
  } from '@aztec/circuits.js';
28
- import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
30
+ import { type Logger, createLogger } from '@aztec/foundation/log';
29
31
 
30
32
  import { strict as assert } from 'assert';
31
33
  import { inspect } from 'util';
32
34
 
33
35
  import { AvmPersistableStateManager } from '../avm/index.js';
34
- import { DualSideEffectTrace } from './dual_side_effect_trace.js';
35
36
  import { PublicEnqueuedCallSideEffectTrace, SideEffectArrayLengths } from './enqueued_call_side_effect_trace.js';
36
37
  import { type WorldStateDB } from './public_db_sources.js';
37
- import { PublicSideEffectTrace } from './side_effect_trace.js';
38
38
  import { generateAvmCircuitPublicInputs } from './transitional_adapters.js';
39
39
  import { getCallRequestsByPhase, getExecutionRequestsByPhase } from './utils.js';
40
40
 
@@ -42,10 +42,10 @@ import { getCallRequestsByPhase, getExecutionRequestsByPhase } from './utils.js'
42
42
  * The transaction-level context for public execution.
43
43
  */
44
44
  export class PublicTxContext {
45
- private log: DebugLogger;
45
+ private log: Logger;
46
46
 
47
47
  /* Gas used including private, teardown gas _limit_, setup and app logic */
48
- private gasUsed: Gas;
48
+ private gasUsedByPublic: Gas = Gas.empty();
49
49
  /* Gas actually used during teardown (different from limit) */
50
50
  public teardownGasUsed: Gas = Gas.empty();
51
51
 
@@ -62,8 +62,9 @@ export class PublicTxContext {
62
62
  public readonly state: PhaseStateManager,
63
63
  private readonly globalVariables: GlobalVariables,
64
64
  private readonly startStateReference: StateReference,
65
- private readonly startGasUsed: Gas,
66
65
  private readonly gasSettings: GasSettings,
66
+ private readonly gasUsedByPrivate: Gas,
67
+ private readonly gasAllocatedToPublic: Gas,
67
68
  private readonly setupCallRequests: PublicCallRequest[],
68
69
  private readonly appLogicCallRequests: PublicCallRequest[],
69
70
  private readonly teardownCallRequests: PublicCallRequest[],
@@ -72,10 +73,10 @@ export class PublicTxContext {
72
73
  private readonly teardownExecutionRequests: PublicExecutionRequest[],
73
74
  public readonly nonRevertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData,
74
75
  public readonly revertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData,
76
+ public readonly feePayer: AztecAddress,
75
77
  public trace: PublicEnqueuedCallSideEffectTrace, // FIXME(dbanks12): should be private
76
78
  ) {
77
- this.log = createDebugLogger(`aztec:public_tx_context`);
78
- this.gasUsed = startGasUsed;
79
+ this.log = createLogger(`simulator:public_tx_context`);
79
80
  }
80
81
 
81
82
  public static async create(
@@ -87,9 +88,9 @@ export class PublicTxContext {
87
88
  ) {
88
89
  const nonRevertibleAccumulatedDataFromPrivate = tx.data.forPublic!.nonRevertibleAccumulatedData;
89
90
 
90
- const innerCallTrace = new PublicSideEffectTrace();
91
91
  const previousAccumulatedDataArrayLengths = new SideEffectArrayLengths(
92
92
  /*publicDataWrites*/ 0,
93
+ /*protocolPublicDataWrites*/ 0,
93
94
  countAccumulatedItems(nonRevertibleAccumulatedDataFromPrivate.noteHashes),
94
95
  /*nullifiers=*/ 0,
95
96
  countAccumulatedItems(nonRevertibleAccumulatedDataFromPrivate.l2ToL1Msgs),
@@ -99,17 +100,22 @@ export class PublicTxContext {
99
100
  /*startSideEffectCounter=*/ 0,
100
101
  previousAccumulatedDataArrayLengths,
101
102
  );
102
- const trace = new DualSideEffectTrace(innerCallTrace, enqueuedCallTrace);
103
103
 
104
104
  // Transaction level state manager that will be forked for revertible phases.
105
- const txStateManager = await AvmPersistableStateManager.create(worldStateDB, trace, doMerkleOperations);
105
+ const txStateManager = await AvmPersistableStateManager.create(worldStateDB, enqueuedCallTrace, doMerkleOperations);
106
+
107
+ const gasSettings = tx.data.constants.txContext.gasSettings;
108
+ const gasUsedByPrivate = tx.data.gasUsed;
109
+ // Gas allocated to public is "whatever's left" after private, but with some max applied.
110
+ const gasAllocatedToPublic = applyMaxToAvailableGas(gasSettings.gasLimits.sub(gasUsedByPrivate));
106
111
 
107
112
  return new PublicTxContext(
108
113
  new PhaseStateManager(txStateManager),
109
114
  globalVariables,
110
115
  await db.getStateReference(),
111
- tx.data.gasUsed,
112
- tx.data.constants.txContext.gasSettings,
116
+ gasSettings,
117
+ gasUsedByPrivate,
118
+ gasAllocatedToPublic,
113
119
  getCallRequestsByPhase(tx, TxExecutionPhase.SETUP),
114
120
  getCallRequestsByPhase(tx, TxExecutionPhase.APP_LOGIC),
115
121
  getCallRequestsByPhase(tx, TxExecutionPhase.TEARDOWN),
@@ -118,6 +124,7 @@ export class PublicTxContext {
118
124
  getExecutionRequestsByPhase(tx, TxExecutionPhase.TEARDOWN),
119
125
  tx.data.forPublic!.nonRevertibleAccumulatedData,
120
126
  tx.data.forPublic!.revertibleAccumulatedData,
127
+ tx.data.feePayer,
121
128
  enqueuedCallTrace,
122
129
  );
123
130
  }
@@ -230,13 +237,14 @@ export class PublicTxContext {
230
237
  }
231
238
 
232
239
  /**
233
- * How much gas is left for the specified phase?
240
+ * How much gas is left as of the specified phase?
234
241
  */
235
- getGasLeftForPhase(phase: TxExecutionPhase): Gas {
242
+ getGasLeftAtPhase(phase: TxExecutionPhase): Gas {
236
243
  if (phase === TxExecutionPhase.TEARDOWN) {
237
- return this.gasSettings.teardownGasLimits;
244
+ return applyMaxToAvailableGas(this.gasSettings.teardownGasLimits);
238
245
  } else {
239
- return this.gasSettings.gasLimits.sub(this.gasUsed);
246
+ const gasLeftForPublic = this.gasAllocatedToPublic.sub(this.gasUsedByPublic);
247
+ return gasLeftForPublic;
240
248
  }
241
249
  }
242
250
 
@@ -247,10 +255,18 @@ export class PublicTxContext {
247
255
  if (phase === TxExecutionPhase.TEARDOWN) {
248
256
  this.teardownGasUsed = this.teardownGasUsed.add(gas);
249
257
  } else {
250
- this.gasUsed = this.gasUsed.add(gas);
258
+ this.gasUsedByPublic = this.gasUsedByPublic.add(gas);
251
259
  }
252
260
  }
253
261
 
262
+ /**
263
+ * The gasUsed by public and private,
264
+ * as if the entire teardown gas limit was consumed.
265
+ */
266
+ getTotalGasUsed(): Gas {
267
+ return this.gasUsedByPrivate.add(this.gasUsedByPublic);
268
+ }
269
+
254
270
  /**
255
271
  * Compute the gas used using the actual gas used during teardown instead
256
272
  * of the teardown gas limit.
@@ -261,14 +277,7 @@ export class PublicTxContext {
261
277
  assert(this.halted, 'Can only compute actual gas used after tx execution ends');
262
278
  const requireTeardown = this.teardownCallRequests.length > 0;
263
279
  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;
280
+ return this.getTotalGasUsed().sub(teardownGasLimits).add(this.teardownGasUsed);
272
281
  }
273
282
 
274
283
  /**
@@ -288,10 +297,10 @@ export class PublicTxContext {
288
297
  * Should only be called during or after teardown.
289
298
  */
290
299
  private getTransactionFeeUnsafe(): Fr {
291
- const txFee = this.gasUsed.computeFee(this.globalVariables.gasFees);
300
+ const txFee = this.getTotalGasUsed().computeFee(this.globalVariables.gasFees);
292
301
  this.log.debug(`Computed tx fee`, {
293
302
  txFee,
294
- gasUsed: inspect(this.gasUsed),
303
+ gasUsed: inspect(this.getTotalGasUsed()),
295
304
  gasFees: inspect(this.globalVariables.gasFees),
296
305
  });
297
306
  return txFee;
@@ -324,15 +333,16 @@ export class PublicTxContext {
324
333
  this.trace,
325
334
  this.globalVariables,
326
335
  this.startStateReference,
327
- this.startGasUsed,
336
+ /*startGasUsed=*/ this.gasUsedByPrivate,
328
337
  this.gasSettings,
338
+ this.feePayer,
329
339
  this.setupCallRequests,
330
340
  this.appLogicCallRequests,
331
341
  this.teardownCallRequests,
332
342
  this.nonRevertibleAccumulatedDataFromPrivate,
333
343
  this.revertibleAccumulatedDataFromPrivate,
334
344
  endTreeSnapshots,
335
- /*endGasUsed=*/ this.gasUsed,
345
+ /*endGasUsed=*/ this.getTotalGasUsed(),
336
346
  this.getTransactionFeeUnsafe(),
337
347
  this.revertCode,
338
348
  );
@@ -367,12 +377,12 @@ export class PublicTxContext {
367
377
  * transaction level one.
368
378
  */
369
379
  class PhaseStateManager {
370
- private log: DebugLogger;
380
+ private log: Logger;
371
381
 
372
382
  private currentlyActiveStateManager: AvmPersistableStateManager | undefined;
373
383
 
374
384
  constructor(private readonly txStateManager: AvmPersistableStateManager) {
375
- this.log = createDebugLogger(`aztec:public_phase_state_manager`);
385
+ this.log = createLogger(`simulator:public_phase_state_manager`);
376
386
  }
377
387
 
378
388
  fork() {
@@ -405,3 +415,13 @@ class PhaseStateManager {
405
415
  this.currentlyActiveStateManager = undefined;
406
416
  }
407
417
  }
418
+
419
+ /**
420
+ * Apply L2 gas maximum.
421
+ */
422
+ function applyMaxToAvailableGas(availableGas: Gas) {
423
+ return new Gas(
424
+ /*daGas=*/ availableGas.daGas,
425
+ /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_TX_PUBLIC_PORTION),
426
+ );
427
+ }
@@ -10,16 +10,10 @@ import {
10
10
  UnencryptedFunctionL2Logs,
11
11
  } from '@aztec/circuit-types';
12
12
  import { type AvmSimulationStats } from '@aztec/circuit-types/stats';
13
- import {
14
- type Fr,
15
- Gas,
16
- type GlobalVariables,
17
- MAX_L2_GAS_PER_ENQUEUED_CALL,
18
- type PublicCallRequest,
19
- type RevertCode,
20
- } from '@aztec/circuits.js';
21
- import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
13
+ import { type Fr, type Gas, type GlobalVariables, type PublicCallRequest, type RevertCode } from '@aztec/circuits.js';
14
+ import { type Logger, createLogger } from '@aztec/foundation/log';
22
15
  import { Timer } from '@aztec/foundation/timer';
16
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
23
17
  import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
24
18
 
25
19
  import { strict as assert } from 'assert';
@@ -29,6 +23,7 @@ import { type AvmPersistableStateManager, AvmSimulator } from '../avm/index.js';
29
23
  import { NullifierCollisionError } from '../avm/journal/nullifiers.js';
30
24
  import { getPublicFunctionDebugName } from '../common/debug_fn_name.js';
31
25
  import { ExecutorMetrics } from './executor_metrics.js';
26
+ import { computeFeePayerBalanceStorageSlot } from './fee_payment.js';
32
27
  import { type WorldStateDB } from './public_db_sources.js';
33
28
  import { PublicTxContext } from './public_tx_context.js';
34
29
 
@@ -53,17 +48,16 @@ export type PublicTxResult = {
53
48
  export class PublicTxSimulator {
54
49
  metrics: ExecutorMetrics;
55
50
 
56
- private log: DebugLogger;
51
+ private log: Logger;
57
52
 
58
53
  constructor(
59
54
  private db: MerkleTreeReadOperations,
60
55
  private worldStateDB: WorldStateDB,
61
56
  telemetryClient: TelemetryClient,
62
57
  private globalVariables: GlobalVariables,
63
- private realAvmProvingRequests: boolean = true,
64
58
  private doMerkleOperations: boolean = false,
65
59
  ) {
66
- this.log = createDebugLogger(`aztec:public_tx_simulator`);
60
+ this.log = createLogger(`simulator:public_tx_simulator`);
67
61
  this.metrics = new ExecutorMetrics(telemetryClient, 'PublicTxSimulator');
68
62
  }
69
63
 
@@ -75,8 +69,9 @@ export class PublicTxSimulator {
75
69
  * @param tx - The transaction to simulate.
76
70
  * @returns The result of the transaction's public execution.
77
71
  */
78
- async simulate(tx: Tx): Promise<PublicTxResult> {
79
- this.log.verbose(`Processing tx ${tx.getTxHash()}`);
72
+ public async simulate(tx: Tx): Promise<PublicTxResult> {
73
+ const txHash = tx.getTxHash();
74
+ this.log.debug(`Simulating ${tx.enqueuedPublicFunctionCalls.length} public calls for tx ${txHash}`, { txHash });
80
75
 
81
76
  const context = await PublicTxContext.create(
82
77
  this.db,
@@ -107,11 +102,14 @@ export class PublicTxSimulator {
107
102
  const appLogicResult: ProcessedPhase = await this.simulateAppLogicPhase(context);
108
103
  processedPhases.push(appLogicResult);
109
104
  }
105
+
110
106
  if (context.hasPhase(TxExecutionPhase.TEARDOWN)) {
111
107
  const teardownResult: ProcessedPhase = await this.simulateTeardownPhase(context);
112
108
  processedPhases.push(teardownResult);
113
109
  }
110
+
114
111
  context.halt();
112
+ await this.payFee(context);
115
113
 
116
114
  const endStateReference = await this.db.getStateReference();
117
115
 
@@ -210,7 +208,12 @@ export class PublicTxSimulator {
210
208
  const callRequests = context.getCallRequestsForPhase(phase);
211
209
  const executionRequests = context.getExecutionRequestsForPhase(phase);
212
210
 
213
- this.log.debug(`Beginning processing in phase ${TxExecutionPhase[phase]} for tx ${context.getTxHash()}`);
211
+ this.log.debug(`Processing phase ${TxExecutionPhase[phase]} for tx ${context.getTxHash()}`, {
212
+ txHash: context.getTxHash().toString(),
213
+ phase: TxExecutionPhase[phase],
214
+ callRequests: callRequests.length,
215
+ executionRequests: executionRequests.length,
216
+ });
214
217
 
215
218
  const returnValues: NestedProcessReturnValues[] = [];
216
219
  let reverted = false;
@@ -265,29 +268,22 @@ export class PublicTxSimulator {
265
268
  ): Promise<AvmFinalizedCallResult> {
266
269
  const stateManager = context.state.getActiveStateManager();
267
270
  const address = executionRequest.callContext.contractAddress;
268
- const selector = executionRequest.callContext.functionSelector;
269
- const fnName = await getPublicFunctionDebugName(this.worldStateDB, address, selector, executionRequest.args);
270
-
271
- const availableGas = context.getGasLeftForPhase(phase);
272
- // Gas allocated to an enqueued call can be different from the available gas
273
- // if there is more gas available than the max allocation per enqueued call.
274
- const allocatedGas = new Gas(
275
- /*daGas=*/ availableGas.daGas,
276
- /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_ENQUEUED_CALL),
277
- );
271
+ const fnName = await getPublicFunctionDebugName(this.worldStateDB, address, executionRequest.args);
272
+
273
+ const allocatedGas = context.getGasLeftAtPhase(phase);
278
274
 
279
275
  const result = await this.simulateEnqueuedCallInternal(
280
276
  context.state.getActiveStateManager(),
281
277
  executionRequest,
282
278
  allocatedGas,
283
- context.getTransactionFee(phase),
279
+ /*transactionFee=*/ context.getTransactionFee(phase),
284
280
  fnName,
285
281
  );
286
282
 
287
- const gasUsed = allocatedGas.sub(result.gasLeft);
283
+ const gasUsed = allocatedGas.sub(result.gasLeft); // by enqueued call
288
284
  context.consumeGas(phase, gasUsed);
289
- this.log.verbose(
290
- `[AVM] Enqueued public call consumed ${gasUsed.l2Gas} L2 gas ending with ${result.gasLeft.l2Gas} L2 gas left.`,
285
+ this.log.debug(
286
+ `Simulated enqueued public call consumed ${gasUsed.l2Gas} L2 gas ending with ${result.gasLeft.l2Gas} L2 gas left.`,
291
287
  );
292
288
 
293
289
  stateManager.traceEnqueuedCall(callRequest, executionRequest.args, result.reverted);
@@ -329,18 +325,16 @@ export class PublicTxSimulator {
329
325
  ): Promise<AvmFinalizedCallResult> {
330
326
  const address = executionRequest.callContext.contractAddress;
331
327
  const sender = executionRequest.callContext.msgSender;
332
- const selector = executionRequest.callContext.functionSelector;
333
328
 
334
- this.log.verbose(
335
- `[AVM] Executing enqueued public call to external function ${fnName}@${address} with ${allocatedGas.l2Gas} allocated L2 gas.`,
329
+ this.log.debug(
330
+ `Executing enqueued public call to external function ${fnName}@${address} with ${allocatedGas.l2Gas} allocated L2 gas.`,
336
331
  );
337
332
  const timer = new Timer();
338
333
 
339
- const simulator = AvmSimulator.create(
334
+ const simulator = await AvmSimulator.create(
340
335
  stateManager,
341
336
  address,
342
337
  sender,
343
- selector,
344
338
  transactionFee,
345
339
  this.globalVariables,
346
340
  executionRequest.callContext.isStaticCall,
@@ -350,10 +344,10 @@ export class PublicTxSimulator {
350
344
  const avmCallResult = await simulator.execute();
351
345
  const result = avmCallResult.finalize();
352
346
 
353
- this.log.verbose(
354
- `[AVM] Simulation of enqueued public call ${fnName} completed. reverted: ${result.reverted}${
355
- result.reverted ? ', reason: ' + result.revertReason : ''
356
- }.`,
347
+ this.log.debug(
348
+ result.reverted
349
+ ? `Simulation of enqueued public call ${fnName} reverted with reason ${result.revertReason}.`
350
+ : `Simulation of enqueued public call ${fnName} completed successfully.`,
357
351
  {
358
352
  eventName: 'avm-simulation',
359
353
  appCircuitName: fnName,
@@ -404,4 +398,30 @@ export class PublicTxSimulator {
404
398
  }
405
399
  }
406
400
  }
401
+
402
+ private async payFee(context: PublicTxContext) {
403
+ const txFee = context.getTransactionFee(TxExecutionPhase.TEARDOWN);
404
+
405
+ if (context.feePayer.isZero()) {
406
+ this.log.debug(`No one is paying the fee of ${txFee.toBigInt()}`);
407
+ return;
408
+ }
409
+
410
+ const feeJuiceAddress = ProtocolContractAddress.FeeJuice;
411
+ const balanceSlot = computeFeePayerBalanceStorageSlot(context.feePayer);
412
+
413
+ this.log.debug(`Deducting ${txFee.toBigInt()} balance in Fee Juice for ${context.feePayer}`);
414
+ const stateManager = context.state.getActiveStateManager();
415
+
416
+ const currentBalance = await stateManager.readStorage(feeJuiceAddress, balanceSlot);
417
+
418
+ if (currentBalance.lt(txFee)) {
419
+ throw new Error(
420
+ `Not enough balance for fee payer to pay for transaction (got ${currentBalance.toBigInt()} needs ${txFee.toBigInt()})`,
421
+ );
422
+ }
423
+
424
+ const updatedBalance = currentBalance.sub(txFee);
425
+ await stateManager.writeStorage(feeJuiceAddress, balanceSlot, updatedBalance, true);
426
+ }
407
427
  }
@@ -10,7 +10,7 @@ import {
10
10
  import { type AztecAddress } from '@aztec/foundation/aztec-address';
11
11
  import { type Fr } from '@aztec/foundation/fields';
12
12
 
13
- import { type AvmContractCallResult, type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js';
13
+ import { type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js';
14
14
  import { type AvmExecutionEnvironment } from '../avm/avm_execution_environment.js';
15
15
  import { type EnqueuedPublicCallExecutionResultWithSideEffects, type PublicFunctionCallResult } from './execution.js';
16
16
 
@@ -31,6 +31,7 @@ export interface PublicSideEffectTraceInterface {
31
31
  contractAddress: AztecAddress,
32
32
  slot: Fr, // This is the storage slot not the computed leaf slot
33
33
  value: Fr,
34
+ protocolWrite: boolean,
34
35
  lowLeafPreimage?: PublicDataTreeLeafPreimage,
35
36
  lowLeafIndex?: Fr,
36
37
  lowLeafPath?: Fr[],
@@ -66,6 +67,9 @@ export interface PublicSideEffectTraceInterface {
66
67
  contractAddress: AztecAddress,
67
68
  exists: boolean,
68
69
  instance?: SerializableContractInstance,
70
+ lowLeafPreimage?: NullifierLeafPreimage,
71
+ lowLeafIndex?: Fr,
72
+ lowLeafPath?: Fr[],
69
73
  ): void;
70
74
  traceGetBytecode(
71
75
  contractAddress: AztecAddress,
@@ -73,20 +77,9 @@ export interface PublicSideEffectTraceInterface {
73
77
  bytecode?: Buffer,
74
78
  contractInstance?: SerializableContractInstance,
75
79
  contractClass?: ContractClassIdPreimage,
76
- ): void;
77
- traceNestedCall(
78
- /** The trace of the nested call. */
79
- nestedCallTrace: PublicSideEffectTraceInterface,
80
- /** The execution environment of the nested call. */
81
- nestedEnvironment: AvmExecutionEnvironment,
82
- /** How much gas was available for this public execution. */
83
- startGasLeft: Gas,
84
- /** Bytecode used for this execution. */
85
- bytecode: Buffer,
86
- /** The call's results */
87
- avmCallResults: AvmContractCallResult,
88
- /** Function name */
89
- functionName: string,
80
+ lowLeafPreimage?: NullifierLeafPreimage,
81
+ lowLeafIndex?: Fr,
82
+ lowLeafPath?: Fr[],
90
83
  ): void;
91
84
  traceEnqueuedCall(
92
85
  /** The call request from private that enqueued this call. */