@aztec/simulator 0.69.0-devnet → 0.69.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 (58) hide show
  1. package/dest/acvm/acvm.d.ts +1 -1
  2. package/dest/acvm/acvm.d.ts.map +1 -1
  3. package/dest/acvm/acvm.js +12 -2
  4. package/dest/acvm/oracle/oracle.d.ts +9 -1
  5. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/oracle.js +33 -8
  7. package/dest/acvm/oracle/typed_oracle.d.ts +2 -1
  8. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  9. package/dest/acvm/oracle/typed_oracle.js +7 -4
  10. package/dest/avm/avm_simulator.d.ts.map +1 -1
  11. package/dest/avm/avm_simulator.js +2 -1
  12. package/dest/avm/errors.d.ts +13 -1
  13. package/dest/avm/errors.d.ts.map +1 -1
  14. package/dest/avm/errors.js +19 -1
  15. package/dest/avm/fixtures/index.d.ts +1 -2
  16. package/dest/avm/fixtures/index.d.ts.map +1 -1
  17. package/dest/avm/fixtures/index.js +3 -3
  18. package/dest/avm/journal/journal.d.ts +3 -4
  19. package/dest/avm/journal/journal.d.ts.map +1 -1
  20. package/dest/avm/journal/journal.js +11 -10
  21. package/dest/avm/opcodes/multi_scalar_mul.js +4 -4
  22. package/dest/client/client_execution_context.d.ts +0 -1
  23. package/dest/client/client_execution_context.d.ts.map +1 -1
  24. package/dest/client/client_execution_context.js +2 -5
  25. package/dest/client/db_oracle.d.ts +24 -16
  26. package/dest/client/db_oracle.d.ts.map +1 -1
  27. package/dest/client/db_oracle.js +1 -1
  28. package/dest/client/view_data_oracle.d.ts +3 -9
  29. package/dest/client/view_data_oracle.d.ts.map +1 -1
  30. package/dest/client/view_data_oracle.js +21 -19
  31. package/dest/providers/acvm_wasm_with_blobs.js +2 -2
  32. package/dest/public/fixtures/index.d.ts +1 -1
  33. package/dest/public/fixtures/index.d.ts.map +1 -1
  34. package/dest/public/fixtures/index.js +2 -2
  35. package/dest/public/public_processor.d.ts +16 -2
  36. package/dest/public/public_processor.d.ts.map +1 -1
  37. package/dest/public/public_processor.js +113 -27
  38. package/dest/public/public_tx_context.d.ts +3 -7
  39. package/dest/public/public_tx_context.d.ts.map +1 -1
  40. package/dest/public/public_tx_context.js +7 -20
  41. package/dest/public/public_tx_simulator.js +4 -4
  42. package/package.json +9 -9
  43. package/src/acvm/acvm.ts +19 -2
  44. package/src/acvm/oracle/oracle.ts +34 -13
  45. package/src/acvm/oracle/typed_oracle.ts +8 -4
  46. package/src/avm/avm_simulator.ts +1 -0
  47. package/src/avm/errors.ts +21 -1
  48. package/src/avm/fixtures/index.ts +3 -3
  49. package/src/avm/journal/journal.ts +9 -9
  50. package/src/avm/opcodes/multi_scalar_mul.ts +3 -3
  51. package/src/client/client_execution_context.ts +1 -5
  52. package/src/client/db_oracle.ts +25 -17
  53. package/src/client/view_data_oracle.ts +27 -19
  54. package/src/providers/acvm_wasm_with_blobs.ts +1 -1
  55. package/src/public/fixtures/index.ts +1 -1
  56. package/src/public/public_processor.ts +133 -34
  57. package/src/public/public_tx_context.ts +6 -19
  58. package/src/public/public_tx_simulator.ts +3 -3
@@ -2,7 +2,7 @@ import {
2
2
  type AuthWitness,
3
3
  type AztecNode,
4
4
  type CompleteAddress,
5
- MerkleTreeId,
5
+ type MerkleTreeId,
6
6
  type NoteStatus,
7
7
  type NullifierMembershipWitness,
8
8
  type PublicDataWitness,
@@ -72,24 +72,8 @@ export class ViewDataOracle extends TypedOracle {
72
72
  * @param leafValue - The leaf value
73
73
  * @returns The index and sibling path concatenated [index, sibling_path]
74
74
  */
75
- public override async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<Fr[]> {
76
- const index = await this.db.findLeafIndex(blockNumber, treeId, leafValue);
77
- if (!index) {
78
- throw new Error(`Leaf value: ${leafValue} not found in ${MerkleTreeId[treeId]}`);
79
- }
80
- const siblingPath = await this.db.getSiblingPath(blockNumber, treeId, index);
81
- return [new Fr(index), ...siblingPath];
82
- }
83
-
84
- /**
85
- * Fetches a sibling path at a given block and index from a tree specified by `treeId`.
86
- * @param blockNumber - The block number at which to get the membership witness.
87
- * @param treeId - Id of the tree to get the sibling path from.
88
- * @param leafIndex - Index of the leaf to get sibling path for
89
- * @returns The sibling path.
90
- */
91
- public override getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr): Promise<Fr[]> {
92
- return this.db.getSiblingPath(blockNumber, treeId, leafIndex.toBigInt());
75
+ public override getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<Fr[]> {
76
+ return this.db.getMembershipWitness(blockNumber, treeId, leafValue);
93
77
  }
94
78
 
95
79
  /**
@@ -290,6 +274,10 @@ export class ViewDataOracle extends TypedOracle {
290
274
  }
291
275
 
292
276
  public override debugLog(message: string, fields: Fr[]): void {
277
+ // TODO(#10558) Remove this check once the debug log is fixed
278
+ if (message.startsWith('Context.note_hashes, after pushing new note hash:')) {
279
+ return;
280
+ }
293
281
  this.log.verbose(`${applyStringFormatting(message, fields)}`, { module: `${this.log.module}:debug_log` });
294
282
  }
295
283
 
@@ -321,4 +309,24 @@ export class ViewDataOracle extends TypedOracle {
321
309
 
322
310
  await this.db.removeNullifiedNotes(this.contractAddress);
323
311
  }
312
+
313
+ public override store(contract: AztecAddress, key: Fr, values: Fr[]): Promise<void> {
314
+ if (!contract.equals(this.contractAddress)) {
315
+ // TODO(#10727): instead of this check check that this.contractAddress is allowed to process notes for contract
316
+ throw new Error(
317
+ `Contract address ${contract} does not match the oracle's contract address ${this.contractAddress}`,
318
+ );
319
+ }
320
+ return this.db.store(this.contractAddress, key, values);
321
+ }
322
+
323
+ public override load(contract: AztecAddress, key: Fr): Promise<Fr[] | null> {
324
+ if (!contract.equals(this.contractAddress)) {
325
+ // TODO(#10727): instead of this check check that this.contractAddress is allowed to process notes for contract
326
+ throw new Error(
327
+ `Contract address ${contract} does not match the oracle's contract address ${this.contractAddress}`,
328
+ );
329
+ }
330
+ return this.db.load(this.contractAddress, key);
331
+ }
324
332
  }
@@ -1,4 +1,4 @@
1
- import { foreignCallHandler } from '@aztec/noir-protocol-circuits-types';
1
+ import { foreignCallHandler } from '@aztec/noir-protocol-circuits-types/server';
2
2
  import { type NoirCompiledCircuit } from '@aztec/types/noir';
3
3
 
4
4
  import { executeCircuit } from '@noir-lang/acvm_js';
@@ -324,7 +324,7 @@ export class MockedAvmTestContractDataSource implements ContractDataSource {
324
324
  return Promise.resolve(this.fnName);
325
325
  }
326
326
 
327
- registerContractFunctionNames(_address: AztecAddress, _names: Record<string, string>): Promise<void> {
327
+ registerContractFunctionSignatures(_address: AztecAddress, _signatures: string[]): Promise<void> {
328
328
  return Promise.resolve();
329
329
  }
330
330
  }
@@ -136,29 +136,132 @@ export class PublicProcessor implements Traceable {
136
136
  * @returns The list of processed txs with their circuit simulation outputs.
137
137
  */
138
138
  public async process(
139
- txs: Tx[],
140
- maxTransactions = txs.length,
141
- txValidator?: TxValidator<ProcessedTx>,
142
- deadline?: Date,
139
+ txs: Iterable<Tx>,
140
+ limits: {
141
+ maxTransactions?: number;
142
+ maxBlockSize?: number;
143
+ maxBlockGas?: Gas;
144
+ deadline?: Date;
145
+ } = {},
146
+ validators: {
147
+ preprocessValidator?: TxValidator<Tx>;
148
+ postprocessValidator?: TxValidator<ProcessedTx>;
149
+ nullifierCache?: { addNullifiers: (nullifiers: Buffer[]) => void };
150
+ } = {},
143
151
  ): Promise<[ProcessedTx[], FailedTx[], NestedProcessReturnValues[]]> {
144
- // The processor modifies the tx objects in place, so we need to clone them.
145
- txs = txs.map(tx => Tx.clone(tx));
152
+ const { maxTransactions, maxBlockSize, deadline, maxBlockGas } = limits;
153
+ const { preprocessValidator, postprocessValidator, nullifierCache } = validators;
146
154
  const result: ProcessedTx[] = [];
147
155
  const failed: FailedTx[] = [];
148
- let returns: NestedProcessReturnValues[] = [];
149
- let totalGas = new Gas(0, 0);
150
156
  const timer = new Timer();
151
157
 
152
- for (const tx of txs) {
153
- // only process up to the limit of the block
154
- if (result.length >= maxTransactions) {
158
+ let totalSizeInBytes = 0;
159
+ let returns: NestedProcessReturnValues[] = [];
160
+ let totalPublicGas = new Gas(0, 0);
161
+ let totalBlockGas = new Gas(0, 0);
162
+
163
+ for (const origTx of txs) {
164
+ // Only process up to the max tx limit
165
+ if (maxTransactions !== undefined && result.length >= maxTransactions) {
166
+ this.log.debug(`Stopping tx processing due to reaching the max tx limit.`);
155
167
  break;
156
168
  }
169
+
170
+ // Bail if we've hit the deadline
171
+ if (deadline && this.dateProvider.now() > +deadline) {
172
+ this.log.warn(`Stopping tx processing due to timeout.`);
173
+ break;
174
+ }
175
+
176
+ // Skip this tx if it'd exceed max block size
177
+ const txHash = origTx.getTxHash().toString();
178
+ const preTxSizeInBytes = origTx.getEstimatedPrivateTxEffectsSize();
179
+ if (maxBlockSize !== undefined && totalSizeInBytes + preTxSizeInBytes > maxBlockSize) {
180
+ this.log.warn(`Skipping processing of tx ${txHash} sized ${preTxSizeInBytes} bytes due to block size limit`, {
181
+ txHash,
182
+ sizeInBytes: preTxSizeInBytes,
183
+ totalSizeInBytes,
184
+ maxBlockSize,
185
+ });
186
+ continue;
187
+ }
188
+
189
+ // Skip this tx if its gas limit would exceed the block gas limit
190
+ const txGasLimit = origTx.data.constants.txContext.gasSettings.gasLimits;
191
+ if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
192
+ this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
193
+ txHash,
194
+ txGasLimit,
195
+ totalBlockGas,
196
+ maxBlockGas,
197
+ });
198
+ continue;
199
+ }
200
+
201
+ // The processor modifies the tx objects in place, so we need to clone them.
202
+ const tx = Tx.clone(origTx);
203
+
204
+ // We validate the tx before processing it, to avoid unnecessary work.
205
+ if (preprocessValidator) {
206
+ const result = await preprocessValidator.validateTx(tx);
207
+ if (result.result === 'invalid') {
208
+ const reason = result.reason.join(', ');
209
+ this.log.warn(`Rejecting tx ${tx.getTxHash().toString()} due to pre-process validation fail: ${reason}`);
210
+ failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
211
+ returns.push(new NestedProcessReturnValues([]));
212
+ continue;
213
+ } else if (result.result === 'skipped') {
214
+ const reason = result.reason.join(', ');
215
+ this.log.warn(`Skipping tx ${tx.getTxHash().toString()} due to pre-process validation: ${reason}`);
216
+ returns.push(new NestedProcessReturnValues([]));
217
+ continue;
218
+ } else {
219
+ this.log.trace(`Tx ${tx.getTxHash().toString()} is valid before processing.`);
220
+ }
221
+ }
222
+
157
223
  try {
158
- const [processedTx, returnValues] = await this.processTx(tx, txValidator, deadline);
224
+ const [processedTx, returnValues] = await this.processTx(tx, deadline);
225
+
226
+ // If the actual size of this tx would exceed block size, skip it
227
+ const txSize = processedTx.txEffect.getDASize();
228
+ if (maxBlockSize !== undefined && totalSizeInBytes + txSize > maxBlockSize) {
229
+ this.log.warn(`Skipping processed tx ${txHash} sized ${txSize} due to max block size.`, {
230
+ txHash,
231
+ sizeInBytes: txSize,
232
+ totalSizeInBytes,
233
+ maxBlockSize,
234
+ });
235
+ continue;
236
+ }
237
+
238
+ // Re-validate the transaction
239
+ if (postprocessValidator) {
240
+ // Only accept processed transactions that are not double-spends,
241
+ // public functions emitting nullifiers would pass earlier check but fail here.
242
+ // Note that we're checking all nullifiers generated in the private execution twice,
243
+ // we could store the ones already checked and skip them here as an optimization.
244
+ // TODO(palla/txs): Can we get into this case? AVM validates this. We should be able to remove it.
245
+ const result = await postprocessValidator.validateTx(processedTx);
246
+ if (result.result !== 'valid') {
247
+ const reason = result.reason.join(', ');
248
+ this.log.error(`Rejecting tx ${processedTx.hash} after processing: ${reason}.`);
249
+ failed.push({ tx, error: new Error(`Tx failed post-process validation: ${reason}`) });
250
+ continue;
251
+ } else {
252
+ this.log.trace(`Tx ${tx.getTxHash().toString()} is valid post processing.`);
253
+ }
254
+ }
255
+
256
+ // Otherwise, commit tx state for the next tx to be processed
257
+ await this.commitTxState(processedTx);
258
+ nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
159
259
  result.push(processedTx);
160
260
  returns = returns.concat(returnValues);
161
- totalGas = totalGas.add(processedTx.gasUsed.publicGas);
261
+
262
+ totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
263
+ totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
264
+ totalSizeInBytes += txSize;
162
265
  } catch (err: any) {
163
266
  if (err?.name === 'PublicProcessorTimeoutError') {
164
267
  this.log.warn(`Stopping tx processing due to timeout.`);
@@ -173,18 +276,22 @@ export class PublicProcessor implements Traceable {
173
276
  }
174
277
 
175
278
  const duration = timer.s();
176
- const rate = duration > 0 ? totalGas.l2Gas / duration : 0;
177
- this.metrics.recordAllTxs(totalGas, rate);
279
+ const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
280
+ this.metrics.recordAllTxs(totalPublicGas, rate);
281
+
282
+ this.log.info(`Processed ${result.length} succesful txs and ${failed.length} txs in ${duration}ms`, {
283
+ duration,
284
+ rate,
285
+ totalPublicGas,
286
+ totalBlockGas,
287
+ totalSizeInBytes,
288
+ });
178
289
 
179
290
  return [result, failed, returns];
180
291
  }
181
292
 
182
- @trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.tryGetTxHash()?.toString() }))
183
- private async processTx(
184
- tx: Tx,
185
- txValidator?: TxValidator<ProcessedTx>,
186
- deadline?: Date,
187
- ): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
293
+ @trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString() }))
294
+ private async processTx(tx: Tx, deadline?: Date): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
188
295
  const [time, [processedTx, returnValues]] = await elapsed(() => this.processTxWithinDeadline(tx, deadline));
189
296
 
190
297
  this.log.verbose(
@@ -208,20 +315,14 @@ export class PublicProcessor implements Traceable {
208
315
  },
209
316
  );
210
317
 
318
+ return [processedTx, returnValues ?? []];
319
+ }
320
+
321
+ private async commitTxState(processedTx: ProcessedTx, txValidator?: TxValidator<ProcessedTx>): Promise<void> {
211
322
  // Commit the state updates from this transaction
323
+ // TODO(palla/txs): It seems like this doesn't do anything...?
212
324
  await this.worldStateDB.commit();
213
325
 
214
- // Re-validate the transaction
215
- if (txValidator) {
216
- // Only accept processed transactions that are not double-spends,
217
- // public functions emitting nullifiers would pass earlier check but fail here.
218
- // Note that we're checking all nullifiers generated in the private execution twice,
219
- // we could store the ones already checked and skip them here as an optimization.
220
- const [_, invalid] = await txValidator.validateTxs([processedTx]);
221
- if (invalid.length) {
222
- throw new Error(`Transaction ${invalid[0].hash} invalid after processing public functions`);
223
- }
224
- }
225
326
  // Update the state so that the next tx in the loop has the correct .startState
226
327
  // NB: before this change, all .startStates were actually incorrect, but the issue was never caught because we either:
227
328
  // a) had only 1 tx with public calls per block, so this loop had len 1
@@ -255,8 +356,6 @@ export class PublicProcessor implements Traceable {
255
356
  );
256
357
  const treeInsertionEnd = process.hrtime.bigint();
257
358
  this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
258
-
259
- return [processedTx, returnValues ?? []];
260
359
  }
261
360
 
262
361
  /** Processes the given tx within deadline. Returns timeout if deadline is hit. */
@@ -7,7 +7,7 @@ import {
7
7
  type SimulationError,
8
8
  type Tx,
9
9
  TxExecutionPhase,
10
- TxHash,
10
+ type TxHash,
11
11
  } from '@aztec/circuit-types';
12
12
  import {
13
13
  AvmCircuitInputs,
@@ -61,6 +61,7 @@ export class PublicTxContext {
61
61
  public avmProvingRequest: AvmProvingRequest | undefined; // FIXME(dbanks12): remove
62
62
 
63
63
  constructor(
64
+ public readonly txHash: TxHash,
64
65
  public readonly state: PhaseStateManager,
65
66
  private readonly globalVariables: GlobalVariables,
66
67
  private readonly startStateReference: StateReference,
@@ -103,12 +104,14 @@ export class PublicTxContext {
103
104
  previousAccumulatedDataArrayLengths,
104
105
  );
105
106
 
107
+ const firstNullifier = nonRevertibleAccumulatedDataFromPrivate.nullifiers[0];
108
+
106
109
  // Transaction level state manager that will be forked for revertible phases.
107
110
  const txStateManager = await AvmPersistableStateManager.create(
108
111
  worldStateDB,
109
112
  enqueuedCallTrace,
110
113
  doMerkleOperations,
111
- fetchTxHash(nonRevertibleAccumulatedDataFromPrivate),
114
+ firstNullifier,
112
115
  );
113
116
 
114
117
  const gasSettings = tx.data.constants.txContext.gasSettings;
@@ -117,6 +120,7 @@ export class PublicTxContext {
117
120
  const gasAllocatedToPublic = applyMaxToAvailableGas(gasSettings.gasLimits.sub(gasUsedByPrivate));
118
121
 
119
122
  return new PublicTxContext(
123
+ tx.getTxHash(),
120
124
  new PhaseStateManager(txStateManager),
121
125
  globalVariables,
122
126
  await db.getStateReference(),
@@ -188,14 +192,6 @@ export class PublicTxContext {
188
192
  return this.revertCode;
189
193
  }
190
194
 
191
- /**
192
- * Construct & return transaction hash.
193
- * @returns The transaction's hash.
194
- */
195
- getTxHash(): TxHash {
196
- return fetchTxHash(this.nonRevertibleAccumulatedDataFromPrivate);
197
- }
198
-
199
195
  /**
200
196
  * Are there any call requests for the speciiied phase?
201
197
  */
@@ -452,12 +448,3 @@ function applyMaxToAvailableGas(availableGas: Gas) {
452
448
  /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_TX_PUBLIC_PORTION),
453
449
  );
454
450
  }
455
-
456
- function fetchTxHash(nonRevertibleAccumulatedData: PrivateToPublicAccumulatedData): TxHash {
457
- // Private kernel functions are executed client side and for this reason tx hash is already set as first nullifier
458
- const firstNullifier = nonRevertibleAccumulatedData.nullifiers[0];
459
- if (!firstNullifier || firstNullifier.isZero()) {
460
- throw new Error(`Cannot get tx hash since first nullifier is missing`);
461
- }
462
- return new TxHash(firstNullifier.toBuffer());
463
- }
@@ -219,8 +219,8 @@ export class PublicTxSimulator {
219
219
  const callRequests = context.getCallRequestsForPhase(phase);
220
220
  const executionRequests = context.getExecutionRequestsForPhase(phase);
221
221
 
222
- this.log.debug(`Processing phase ${TxExecutionPhase[phase]} for tx ${context.getTxHash()}`, {
223
- txHash: context.getTxHash().toString(),
222
+ this.log.debug(`Processing phase ${TxExecutionPhase[phase]} for tx ${context.txHash}`, {
223
+ txHash: context.txHash.toString(),
224
224
  phase: TxExecutionPhase[phase],
225
225
  callRequests: callRequests.length,
226
226
  executionRequests: executionRequests.length,
@@ -266,7 +266,7 @@ export class PublicTxSimulator {
266
266
  * @returns The result of execution.
267
267
  */
268
268
  @trackSpan('PublicTxSimulator.simulateEnqueuedCall', (phase, context, _callRequest, executionRequest) => ({
269
- [Attributes.TX_HASH]: context.getTxHash().toString(),
269
+ [Attributes.TX_HASH]: context.txHash.toString(),
270
270
  [Attributes.TARGET_ADDRESS]: executionRequest.callContext.contractAddress.toString(),
271
271
  [Attributes.SENDER_ADDRESS]: executionRequest.callContext.msgSender.toString(),
272
272
  [Attributes.SIMULATOR_PHASE]: TxExecutionPhase[phase].toString(),