@aztec/simulator 0.0.1-commit.b33fc05d0 → 0.0.1-commit.b3d3157a
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.
- package/dest/private/acvm_wasm.d.ts +1 -1
- package/dest/private/acvm_wasm.d.ts.map +1 -1
- package/dest/private/acvm_wasm.js +3 -1
- package/dest/public/avm/avm_simulator.js +1 -1
- package/dest/public/avm/calldata.d.ts +1 -1
- package/dest/public/avm/calldata.d.ts.map +1 -1
- package/dest/public/avm/calldata.js +3 -2
- package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts +2 -1
- package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts.map +1 -1
- package/dest/public/avm/fixtures/base_avm_simulation_tester.js +17 -2
- package/dest/public/avm/fixtures/utils.d.ts +1 -1
- package/dest/public/avm/fixtures/utils.d.ts.map +1 -1
- package/dest/public/avm/fixtures/utils.js +3 -0
- package/dest/public/avm/opcodes/contract.d.ts +3 -2
- package/dest/public/avm/opcodes/contract.d.ts.map +1 -1
- package/dest/public/avm/opcodes/contract.js +5 -1
- package/dest/public/avm/opcodes/ec_add.d.ts +2 -4
- package/dest/public/avm/opcodes/ec_add.d.ts.map +1 -1
- package/dest/public/avm/opcodes/ec_add.js +13 -27
- package/dest/public/contracts_db_checkpoint.d.ts +2 -2
- package/dest/public/contracts_db_checkpoint.d.ts.map +1 -1
- package/dest/public/contracts_db_checkpoint.js +1 -1
- package/dest/public/fixtures/bulk_test.d.ts +1 -1
- package/dest/public/fixtures/bulk_test.d.ts.map +1 -1
- package/dest/public/fixtures/bulk_test.js +31 -4
- package/dest/public/fixtures/custom_bytecode_tests.d.ts +3 -1
- package/dest/public/fixtures/custom_bytecode_tests.d.ts.map +1 -1
- package/dest/public/fixtures/custom_bytecode_tests.js +61 -1
- package/dest/public/fixtures/opcode_spammer.d.ts +1 -1
- package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -1
- package/dest/public/fixtures/opcode_spammer.js +2 -10
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts +1 -1
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
- package/dest/public/fixtures/public_tx_simulation_tester.js +3 -3
- package/dest/public/fixtures/utils.d.ts +1 -1
- package/dest/public/fixtures/utils.d.ts.map +1 -1
- package/dest/public/fixtures/utils.js +14 -15
- package/dest/public/hinting_db_sources.d.ts +5 -4
- package/dest/public/hinting_db_sources.d.ts.map +1 -1
- package/dest/public/hinting_db_sources.js +10 -6
- package/dest/public/public_db_sources.d.ts +6 -3
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +16 -10
- package/dest/public/public_processor/guarded_merkle_tree.d.ts +7 -6
- package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
- package/dest/public/public_processor/guarded_merkle_tree.js +7 -4
- package/dest/public/public_processor/public_processor.d.ts +4 -3
- package/dest/public/public_processor/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor/public_processor.js +67 -44
- package/dest/public/public_processor/public_processor_metrics.d.ts +4 -1
- package/dest/public/public_processor/public_processor_metrics.d.ts.map +1 -1
- package/dest/public/public_processor/public_processor_metrics.js +8 -0
- package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts +1 -1
- package/dest/public/public_tx_simulator/contract_provider_for_cpp.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/contract_provider_for_cpp.js +3 -2
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator.js +3 -8
- package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.d.ts +1 -1
- package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.js +3 -7
- package/dest/public/public_tx_simulator/public_tx_context.d.ts +7 -3
- package/dest/public/public_tx_simulator/public_tx_context.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/public_tx_context.js +8 -10
- package/dest/public/public_tx_simulator/public_tx_simulator.d.ts +6 -2
- package/dest/public/public_tx_simulator/public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/public_tx_simulator.js +12 -7
- package/package.json +16 -16
- package/src/private/acvm_wasm.ts +4 -1
- package/src/public/avm/avm_simulator.ts +1 -1
- package/src/public/avm/calldata.ts +3 -2
- package/src/public/avm/fixtures/base_avm_simulation_tester.ts +22 -2
- package/src/public/avm/fixtures/utils.ts +3 -0
- package/src/public/avm/opcodes/contract.ts +5 -1
- package/src/public/avm/opcodes/ec_add.ts +12 -31
- package/src/public/avm/opcodes/external_calls.ts +1 -1
- package/src/public/contracts_db_checkpoint.ts +1 -1
- package/src/public/fixtures/bulk_test.ts +29 -4
- package/src/public/fixtures/custom_bytecode_tests.ts +139 -1
- package/src/public/fixtures/opcode_spammer.ts +4 -8
- package/src/public/fixtures/public_tx_simulation_tester.ts +4 -9
- package/src/public/fixtures/utils.ts +21 -18
- package/src/public/hinting_db_sources.ts +13 -6
- package/src/public/public_db_sources.ts +21 -14
- package/src/public/public_processor/guarded_merkle_tree.ts +10 -7
- package/src/public/public_processor/public_processor.ts +86 -52
- package/src/public/public_processor/public_processor_metrics.ts +12 -0
- package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +3 -2
- package/src/public/public_tx_simulator/cpp_public_tx_simulator.ts +3 -12
- package/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts +3 -10
- package/src/public/public_tx_simulator/public_tx_context.ts +8 -10
- package/src/public/public_tx_simulator/public_tx_simulator.ts +11 -7
|
@@ -3,7 +3,7 @@ import { padArrayEnd } from '@aztec/foundation/collection';
|
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
5
5
|
import { sleep } from '@aztec/foundation/sleep';
|
|
6
|
-
import { DateProvider, Timer, elapsed,
|
|
6
|
+
import { DateProvider, Timer, elapsed, execWithSignal } from '@aztec/foundation/timer';
|
|
7
7
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
8
8
|
import { ContractClassPublishedEvent } from '@aztec/protocol-contracts/class-registry';
|
|
9
9
|
import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
@@ -76,17 +76,15 @@ export class PublicProcessorFactory {
|
|
|
76
76
|
/**
|
|
77
77
|
* Creates a new instance of a PublicProcessor.
|
|
78
78
|
* @param globalVariables - The global variables for the block being processed.
|
|
79
|
-
* @param
|
|
79
|
+
* @param contractsDB - Optional pre-populated contracts DB; a fresh one is constructed if omitted.
|
|
80
80
|
* @returns A new instance of a PublicProcessor.
|
|
81
81
|
*/
|
|
82
82
|
public create(
|
|
83
83
|
merkleTree: MerkleTreeWriteOperations,
|
|
84
84
|
globalVariables: GlobalVariables,
|
|
85
85
|
config: PublicSimulatorConfig,
|
|
86
|
+
contractsDB: PublicContractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings()),
|
|
86
87
|
): PublicProcessor {
|
|
87
|
-
const bindings = this.log.getBindings();
|
|
88
|
-
const contractsDB = new PublicContractsDB(this.contractDataSource, bindings);
|
|
89
|
-
|
|
90
88
|
const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
|
|
91
89
|
const publicTxSimulator = this.createPublicTxSimulator(guardedFork, contractsDB, globalVariables, config);
|
|
92
90
|
|
|
@@ -97,7 +95,7 @@ export class PublicProcessorFactory {
|
|
|
97
95
|
publicTxSimulator,
|
|
98
96
|
this.dateProvider,
|
|
99
97
|
this.telemetryClient,
|
|
100
|
-
createLogger('simulator:public-processor',
|
|
98
|
+
createLogger('simulator:public-processor', this.log.getBindings()),
|
|
101
99
|
);
|
|
102
100
|
}
|
|
103
101
|
|
|
@@ -125,6 +123,17 @@ class PublicProcessorTimeoutError extends Error {
|
|
|
125
123
|
}
|
|
126
124
|
}
|
|
127
125
|
|
|
126
|
+
class PublicProcessorAbortError extends Error {
|
|
127
|
+
constructor(message: string = 'Aborted while processing tx') {
|
|
128
|
+
super(message);
|
|
129
|
+
this.name = 'PublicProcessorAbortError';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function isPublicProcessorInterruptError(err: any) {
|
|
134
|
+
return err?.name === 'PublicProcessorTimeoutError' || err?.name === 'PublicProcessorAbortError';
|
|
135
|
+
}
|
|
136
|
+
|
|
128
137
|
/**
|
|
129
138
|
* Converts Txs lifted from the P2P module into ProcessedTx objects by executing
|
|
130
139
|
* any public function calls in them. Txs with private calls only are unaffected.
|
|
@@ -161,7 +170,7 @@ export class PublicProcessor implements Traceable {
|
|
|
161
170
|
limits: PublicProcessorLimits = {},
|
|
162
171
|
validator: PublicProcessorValidator = {},
|
|
163
172
|
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
|
|
164
|
-
const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal } = limits;
|
|
173
|
+
const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal, signal } = limits;
|
|
165
174
|
const { preprocessValidator, nullifierCache } = validator;
|
|
166
175
|
const result: ProcessedTx[] = [];
|
|
167
176
|
const usedTxs: Tx[] = [];
|
|
@@ -174,6 +183,8 @@ export class PublicProcessor implements Traceable {
|
|
|
174
183
|
let totalPublicGas = new Gas(0, 0);
|
|
175
184
|
let totalBlockGas = new Gas(0, 0);
|
|
176
185
|
let totalBlobFields = 0;
|
|
186
|
+
let silentlySkippedCount = 0;
|
|
187
|
+
let totalSilentlySkippedDurationMs = 0;
|
|
177
188
|
|
|
178
189
|
for await (const tx of txs) {
|
|
179
190
|
// Only process up to the max tx limit
|
|
@@ -182,11 +193,15 @@ export class PublicProcessor implements Traceable {
|
|
|
182
193
|
break;
|
|
183
194
|
}
|
|
184
195
|
|
|
185
|
-
// Bail if we've hit the deadline
|
|
196
|
+
// Bail if we've hit the deadline or have been interrupted.
|
|
186
197
|
if (deadline && this.dateProvider.now() > +deadline) {
|
|
187
198
|
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
188
199
|
break;
|
|
189
200
|
}
|
|
201
|
+
if (signal?.aborted) {
|
|
202
|
+
this.log.warn(`Stopping tx processing due to abort signal.`);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
190
205
|
|
|
191
206
|
const txHash = tx.getTxHash().toString();
|
|
192
207
|
|
|
@@ -224,11 +239,6 @@ export class PublicProcessor implements Traceable {
|
|
|
224
239
|
failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
|
|
225
240
|
returns.push(new NestedProcessReturnValues([]));
|
|
226
241
|
continue;
|
|
227
|
-
} else if (result.result === 'skipped') {
|
|
228
|
-
const reason = result.reason.join(', ');
|
|
229
|
-
this.log.debug(`Skipping tx ${txHash.toString()} due to pre-process validation: ${reason}`);
|
|
230
|
-
returns.push(new NestedProcessReturnValues([]));
|
|
231
|
-
continue;
|
|
232
242
|
} else {
|
|
233
243
|
this.log.trace(`Tx ${txHash.toString()} is valid before processing.`);
|
|
234
244
|
}
|
|
@@ -244,7 +254,9 @@ export class PublicProcessor implements Traceable {
|
|
|
244
254
|
this.contractsDB.createCheckpoint();
|
|
245
255
|
|
|
246
256
|
try {
|
|
247
|
-
const [processedTx, returnValues, txDebugLogs] = await
|
|
257
|
+
const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
|
|
258
|
+
this.processTx(tx, deadline, signal),
|
|
259
|
+
);
|
|
248
260
|
|
|
249
261
|
// Inject a fake processing failure after N txs if requested
|
|
250
262
|
const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
|
|
@@ -265,8 +277,12 @@ export class PublicProcessor implements Traceable {
|
|
|
265
277
|
txBlobFields,
|
|
266
278
|
totalBlobFields,
|
|
267
279
|
maxBlobFields,
|
|
280
|
+
txProcessingTimeMs,
|
|
268
281
|
},
|
|
269
282
|
);
|
|
283
|
+
silentlySkippedCount += 1;
|
|
284
|
+
totalSilentlySkippedDurationMs += txProcessingTimeMs;
|
|
285
|
+
this.metrics.recordSilentlySkipped(txProcessingTimeMs);
|
|
270
286
|
// Need to revert the checkpoint here and don't go any further
|
|
271
287
|
await checkpoint.revert();
|
|
272
288
|
this.contractsDB.revertCheckpoint();
|
|
@@ -306,16 +322,15 @@ export class PublicProcessor implements Traceable {
|
|
|
306
322
|
totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
|
|
307
323
|
totalSizeInBytes += txSize;
|
|
308
324
|
totalBlobFields += txBlobFields;
|
|
325
|
+
|
|
326
|
+
// Commit the tx-level contracts checkpoint on success
|
|
327
|
+
this.contractsDB.commitCheckpoint();
|
|
309
328
|
} catch (err: any) {
|
|
310
|
-
if (err
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
//
|
|
314
|
-
// Signal cancellation AND WAIT for the simulation to actually stop.
|
|
315
|
-
// This is critical because C++ might be in the middle of a slow operation (e.g., pad_trees)
|
|
316
|
-
// and won't check the cancellation flag until that operation completes.
|
|
317
|
-
// Without waiting, we'd proceed to revert checkpoints while C++ is still writing to state.
|
|
318
|
-
// Wait for C++ to stop gracefully.
|
|
329
|
+
if (isPublicProcessorInterruptError(err)) {
|
|
330
|
+
const interruptReason = err.name === 'PublicProcessorTimeoutError' ? 'timeout' : 'abort signal';
|
|
331
|
+
this.log.warn(`Stopping tx processing due to ${interruptReason}.`);
|
|
332
|
+
// The tx may still be executing on a worker thread (C++ via NAPI).
|
|
333
|
+
// Signal cancellation AND WAIT for the simulation to actually stop before touching fork checkpoints.
|
|
319
334
|
await this.publicTxSimulator.cancel?.();
|
|
320
335
|
|
|
321
336
|
// Now stop the guarded fork to prevent any further TS-side access to the world state.
|
|
@@ -325,14 +340,10 @@ export class PublicProcessor implements Traceable {
|
|
|
325
340
|
// 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
|
|
326
341
|
// 2. Possible state updates on that checkpoint or any others created during execution.
|
|
327
342
|
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
await checkpoint.
|
|
332
|
-
|
|
333
|
-
// Now we want to revert any/all remaining checkpoints, destroying any outstanding state updates.
|
|
334
|
-
// This needs to be done directly on the underlying fork as the guarded fork has been stopped.
|
|
335
|
-
await this.guardedMerkleTree.getUnderlyingFork().revertAllCheckpoints();
|
|
343
|
+
// Revert all checkpoints at or above this checkpoint's depth (inclusive), destroying any outstanding state
|
|
344
|
+
// updates from this tx and any nested checkpoints created during execution. This preserves any checkpoints
|
|
345
|
+
// created by callers below our depth.
|
|
346
|
+
await checkpoint.revertToCheckpoint();
|
|
336
347
|
|
|
337
348
|
// Revert any contracts added to the DB for the tx.
|
|
338
349
|
this.contractsDB.revertCheckpoint();
|
|
@@ -344,9 +355,9 @@ export class PublicProcessor implements Traceable {
|
|
|
344
355
|
break;
|
|
345
356
|
}
|
|
346
357
|
|
|
347
|
-
// Roll back state to start of TX before proceeding to next TX
|
|
348
|
-
|
|
349
|
-
await
|
|
358
|
+
// Roll back state to start of TX before proceeding to next TX.
|
|
359
|
+
// Reverts all checkpoints at or above this checkpoint's depth, preserving any caller checkpoints below.
|
|
360
|
+
await checkpoint.revertToCheckpoint();
|
|
350
361
|
this.contractsDB.revertCheckpoint();
|
|
351
362
|
const errorMessage = err instanceof Error || err instanceof AssertionError ? err.message : 'Unknown error';
|
|
352
363
|
this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
|
|
@@ -358,7 +369,6 @@ export class PublicProcessor implements Traceable {
|
|
|
358
369
|
} finally {
|
|
359
370
|
// Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
|
|
360
371
|
await checkpoint.commit();
|
|
361
|
-
this.contractsDB.commitCheckpointOkIfNone();
|
|
362
372
|
}
|
|
363
373
|
}
|
|
364
374
|
|
|
@@ -366,13 +376,24 @@ export class PublicProcessor implements Traceable {
|
|
|
366
376
|
const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
|
|
367
377
|
this.metrics.recordAllTxs(totalPublicGas, rate);
|
|
368
378
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
379
|
+
const silentlySkippedDurationMs = Math.round(totalSilentlySkippedDurationMs);
|
|
380
|
+
this.log.info(
|
|
381
|
+
`Processed ${result.length} successful txs and ${failed.length} failed txs ` +
|
|
382
|
+
`(${silentlySkippedCount} silently skipped, ${silentlySkippedDurationMs}ms wasted) ` +
|
|
383
|
+
`in ${duration}s`,
|
|
384
|
+
{
|
|
385
|
+
blockNumber: this.globalVariables.blockNumber,
|
|
386
|
+
successfulCount: result.length,
|
|
387
|
+
failedCount: failed.length,
|
|
388
|
+
duration,
|
|
389
|
+
rate,
|
|
390
|
+
totalPublicGas,
|
|
391
|
+
totalBlockGas,
|
|
392
|
+
totalSizeInBytes,
|
|
393
|
+
silentlySkippedCount,
|
|
394
|
+
silentlySkippedDurationMs,
|
|
395
|
+
},
|
|
396
|
+
);
|
|
376
397
|
|
|
377
398
|
return [result, failed, usedTxs, returns, debugLogs];
|
|
378
399
|
}
|
|
@@ -397,9 +418,10 @@ export class PublicProcessor implements Traceable {
|
|
|
397
418
|
private async processTx(
|
|
398
419
|
tx: Tx,
|
|
399
420
|
deadline: Date | undefined,
|
|
421
|
+
signal: AbortSignal | undefined,
|
|
400
422
|
): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
|
|
401
423
|
const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
|
|
402
|
-
this.processTxWithinDeadline(tx, deadline),
|
|
424
|
+
this.processTxWithinDeadline(tx, deadline, signal),
|
|
403
425
|
);
|
|
404
426
|
|
|
405
427
|
this.log.verbose(
|
|
@@ -453,10 +475,11 @@ export class PublicProcessor implements Traceable {
|
|
|
453
475
|
this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
|
|
454
476
|
}
|
|
455
477
|
|
|
456
|
-
/** Processes the given tx within deadline
|
|
478
|
+
/** Processes the given tx within deadline or until the signal is aborted. */
|
|
457
479
|
private async processTxWithinDeadline(
|
|
458
480
|
tx: Tx,
|
|
459
481
|
deadline: Date | undefined,
|
|
482
|
+
signal: AbortSignal | undefined,
|
|
460
483
|
): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
|
|
461
484
|
const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
|
|
462
485
|
tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
|
|
@@ -473,27 +496,38 @@ export class PublicProcessor implements Traceable {
|
|
|
473
496
|
}
|
|
474
497
|
: innerProcessFn;
|
|
475
498
|
|
|
476
|
-
|
|
499
|
+
const processingSignal = this.getProcessingSignal(tx, deadline, signal);
|
|
500
|
+
if (!processingSignal) {
|
|
477
501
|
return await processFn();
|
|
478
502
|
}
|
|
479
503
|
|
|
480
|
-
|
|
504
|
+
return await execWithSignal(
|
|
505
|
+
() => processFn(),
|
|
506
|
+
processingSignal,
|
|
507
|
+
signal =>
|
|
508
|
+
signal.reason?.name === 'TimeoutError' ? new PublicProcessorTimeoutError() : new PublicProcessorAbortError(),
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
private getProcessingSignal(tx: Tx, deadline: Date | undefined, signal: AbortSignal | undefined) {
|
|
513
|
+
if (!deadline) {
|
|
514
|
+
return signal;
|
|
515
|
+
}
|
|
516
|
+
|
|
481
517
|
const timeout = +deadline - this.dateProvider.now();
|
|
482
518
|
if (timeout <= 0) {
|
|
483
519
|
throw new PublicProcessorTimeoutError();
|
|
484
520
|
}
|
|
485
521
|
|
|
522
|
+
const txHash = tx.getTxHash();
|
|
486
523
|
this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, {
|
|
487
524
|
deadline: deadline.toISOString(),
|
|
488
525
|
now: new Date(this.dateProvider.now()).toISOString(),
|
|
489
526
|
txHash,
|
|
490
527
|
});
|
|
491
528
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
timeout,
|
|
495
|
-
() => new PublicProcessorTimeoutError(),
|
|
496
|
-
);
|
|
529
|
+
const timeoutSignal = AbortSignal.timeout(timeout);
|
|
530
|
+
return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
497
531
|
}
|
|
498
532
|
|
|
499
533
|
/**
|
|
@@ -550,7 +584,7 @@ export class PublicProcessor implements Traceable {
|
|
|
550
584
|
// Fee payment insertion has already been done. Do the rest.
|
|
551
585
|
await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
|
|
552
586
|
|
|
553
|
-
|
|
587
|
+
this.contractsDB.addNewContracts(tx);
|
|
554
588
|
|
|
555
589
|
return [processedTx, undefined, []];
|
|
556
590
|
}
|
|
@@ -30,6 +30,9 @@ export class PublicProcessorMetrics {
|
|
|
30
30
|
|
|
31
31
|
private treeInsertionDuration: Histogram;
|
|
32
32
|
|
|
33
|
+
private silentlySkippedCount: UpDownCounter;
|
|
34
|
+
private silentlySkippedDuration: Histogram;
|
|
35
|
+
|
|
33
36
|
constructor(client: TelemetryClient, name = 'PublicProcessor') {
|
|
34
37
|
this.tracer = client.getTracer(name);
|
|
35
38
|
const meter = client.getMeter(name);
|
|
@@ -60,6 +63,10 @@ export class PublicProcessorMetrics {
|
|
|
60
63
|
this.gasRate = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_GAS_RATE);
|
|
61
64
|
|
|
62
65
|
this.treeInsertionDuration = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_TREE_INSERTION);
|
|
66
|
+
|
|
67
|
+
this.silentlySkippedCount = createUpDownCounterWithDefault(meter, Metrics.PUBLIC_PROCESSOR_SILENTLY_SKIPPED_COUNT);
|
|
68
|
+
|
|
69
|
+
this.silentlySkippedDuration = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_SILENTLY_SKIPPED_DURATION);
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
recordPhaseDuration(phaseName: TxExecutionPhase, durationMs: number) {
|
|
@@ -123,4 +130,9 @@ export class PublicProcessorMetrics {
|
|
|
123
130
|
recordTreeInsertions(durationUs: number) {
|
|
124
131
|
this.treeInsertionDuration.record(Math.ceil(durationUs));
|
|
125
132
|
}
|
|
133
|
+
|
|
134
|
+
recordSilentlySkipped(durationMs: number) {
|
|
135
|
+
this.silentlySkippedCount.add(1);
|
|
136
|
+
this.silentlySkippedDuration.record(Math.ceil(durationMs));
|
|
137
|
+
}
|
|
126
138
|
}
|
|
@@ -52,6 +52,7 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
52
52
|
return serializeWithMessagePack(contractClass);
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
// eslint-disable-next-line require-await
|
|
55
56
|
public addContracts = async (contractDeploymentDataBuffer: Buffer): Promise<void> => {
|
|
56
57
|
this.log.trace(`Contract provider callback: addContracts`);
|
|
57
58
|
|
|
@@ -61,8 +62,8 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
61
62
|
const contractDeploymentData = ContractDeploymentData.fromPlainObject(rawData);
|
|
62
63
|
|
|
63
64
|
// Add contracts to the contracts DB
|
|
64
|
-
this.log.trace(`Calling contractsDB.
|
|
65
|
-
|
|
65
|
+
this.log.trace(`Calling contractsDB.addContractsFromLogs`);
|
|
66
|
+
this.contractsDB.addContractsFromLogs(contractDeploymentData);
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
public getBytecodeCommitment = async (classId: string): Promise<Buffer | undefined> => {
|
|
@@ -12,11 +12,8 @@ import {
|
|
|
12
12
|
import { SimulationError } from '@aztec/stdlib/errors';
|
|
13
13
|
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
|
|
14
14
|
import type { GlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
15
|
-
import { WorldStateRevisionWithHandle } from '@aztec/stdlib/world-state';
|
|
16
15
|
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
17
16
|
|
|
18
|
-
import { strict as assert } from 'assert';
|
|
19
|
-
|
|
20
17
|
import { ExecutorMetrics } from '../executor_metrics.js';
|
|
21
18
|
import type { ExecutorMetricsInterface } from '../executor_metrics_interface.js';
|
|
22
19
|
import type { PublicContractsDB } from '../public_db_sources.js';
|
|
@@ -62,14 +59,8 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
|
|
|
62
59
|
txHash,
|
|
63
60
|
});
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
assert(
|
|
68
|
-
wsRevision instanceof WorldStateRevisionWithHandle,
|
|
69
|
-
'CppPublicTxSimulator a real NativeWorldStateInstance with a handle to the C++ WorldState object',
|
|
70
|
-
);
|
|
71
|
-
const wsCppHandle = (wsRevision as WorldStateRevisionWithHandle).handle;
|
|
72
|
-
wsRevision = wsRevision.toWorldStateRevision(); // for msgpack serialization, we don't include the handle in the type
|
|
62
|
+
const wsRevision = this.merkleTree.getRevision();
|
|
63
|
+
const wsdbIpcPath = this.merkleTree.getIpcPath();
|
|
73
64
|
|
|
74
65
|
this.log.trace(`Running C++ simulation with world state revision ${JSON.stringify(wsRevision)}`);
|
|
75
66
|
|
|
@@ -99,7 +90,7 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
|
|
|
99
90
|
this.simulationPromise = avmSimulate(
|
|
100
91
|
inputBuffer,
|
|
101
92
|
contractProvider,
|
|
102
|
-
|
|
93
|
+
wsdbIpcPath,
|
|
103
94
|
this.log.level,
|
|
104
95
|
undefined,
|
|
105
96
|
this.cancellationToken,
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
import { SimulationError } from '@aztec/stdlib/errors';
|
|
12
12
|
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
|
|
13
13
|
import type { GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
|
|
14
|
-
import { WorldStateRevisionWithHandle } from '@aztec/stdlib/world-state';
|
|
15
14
|
|
|
16
15
|
import { strict as assert } from 'assert';
|
|
17
16
|
|
|
@@ -81,14 +80,8 @@ export class CppVsTsPublicTxSimulator extends PublicTxSimulator implements Publi
|
|
|
81
80
|
|
|
82
81
|
this.log.debug(`Running C++ simulation for tx ${txHash}`);
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
assert(
|
|
87
|
-
wsRevision instanceof WorldStateRevisionWithHandle,
|
|
88
|
-
'CppPublicTxSimulator a real NativeWorldStateInstance with a handle to the C++ WorldState object',
|
|
89
|
-
);
|
|
90
|
-
const wsCppHandle = (wsRevision as WorldStateRevisionWithHandle).handle;
|
|
91
|
-
wsRevision = wsRevision.toWorldStateRevision(); // for msgpack serialization, we don't include the handle in the type
|
|
83
|
+
const wsRevision = this.merkleTree.getRevision();
|
|
84
|
+
const wsdbIpcPath = this.merkleTree.getIpcPath();
|
|
92
85
|
|
|
93
86
|
this.log.debug(`Running C++ simulation with world state revision ${JSON.stringify(wsRevision)}`);
|
|
94
87
|
|
|
@@ -113,7 +106,7 @@ export class CppVsTsPublicTxSimulator extends PublicTxSimulator implements Publi
|
|
|
113
106
|
let resultBuffer: Buffer;
|
|
114
107
|
try {
|
|
115
108
|
this.log.debug(`Calling C++ simulator for tx ${txHash}`);
|
|
116
|
-
resultBuffer = await avmSimulate(inputBuffer, contractProvider,
|
|
109
|
+
resultBuffer = await avmSimulate(inputBuffer, contractProvider, wsdbIpcPath, logLevel);
|
|
117
110
|
} catch (error: any) {
|
|
118
111
|
throw new SimulationError(`C++ simulation failed: ${error.message}`, []);
|
|
119
112
|
}
|
|
@@ -174,14 +174,8 @@ export class PublicTxContext {
|
|
|
174
174
|
}
|
|
175
175
|
if (phase === TxExecutionPhase.SETUP) {
|
|
176
176
|
this.log.warn(`Setup phase reverted! The transaction will be thrown out.`);
|
|
177
|
-
} else if (phase === TxExecutionPhase.APP_LOGIC) {
|
|
178
|
-
this.revertCode = RevertCode.
|
|
179
|
-
} else if (phase === TxExecutionPhase.TEARDOWN) {
|
|
180
|
-
if (this.revertCode.equals(RevertCode.APP_LOGIC_REVERTED)) {
|
|
181
|
-
this.revertCode = RevertCode.BOTH_REVERTED;
|
|
182
|
-
} else {
|
|
183
|
-
this.revertCode = RevertCode.TEARDOWN_REVERTED;
|
|
184
|
-
}
|
|
177
|
+
} else if (phase === TxExecutionPhase.APP_LOGIC || phase === TxExecutionPhase.TEARDOWN) {
|
|
178
|
+
this.revertCode = RevertCode.REVERTED;
|
|
185
179
|
}
|
|
186
180
|
}
|
|
187
181
|
|
|
@@ -247,8 +241,12 @@ export class PublicTxContext {
|
|
|
247
241
|
}
|
|
248
242
|
|
|
249
243
|
/**
|
|
250
|
-
* The gasUsed by public and private,
|
|
251
|
-
*
|
|
244
|
+
* The gasUsed by public and private, as if the entire teardown gas limit was consumed.
|
|
245
|
+
*
|
|
246
|
+
* This is intentional: teardown is used for gas accounting and refunds, so the transaction
|
|
247
|
+
* fee must be deterministic _before_ teardown executes. If fees depended on teardown's actual
|
|
248
|
+
* consumption there would be a circular dependency. Billing the full teardown gas limit
|
|
249
|
+
* (set by the user) makes the fee known in advance and available to the teardown function.
|
|
252
250
|
*/
|
|
253
251
|
getTotalGasUsed(): Gas {
|
|
254
252
|
return this.gasUsedByPrivate.add(this.gasUsedByPublic);
|
|
@@ -27,7 +27,6 @@ import { type PublicContractsDB, PublicTreesDB } from '../public_db_sources.js';
|
|
|
27
27
|
import {
|
|
28
28
|
L2ToL1MessageLimitReachedError,
|
|
29
29
|
NoteHashLimitReachedError,
|
|
30
|
-
NullifierCollisionError,
|
|
31
30
|
NullifierLimitReachedError,
|
|
32
31
|
} from '../side_effect_errors.js';
|
|
33
32
|
import type { PublicPersistableStateManager } from '../state_manager/state_manager.js';
|
|
@@ -147,7 +146,8 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
|
147
146
|
hintingContractsDB.createCheckpoint();
|
|
148
147
|
|
|
149
148
|
try {
|
|
150
|
-
// This
|
|
149
|
+
// This may throw: a side-effect limit error triggers a soft revert (caught below), while a
|
|
150
|
+
// nullifier collision is unrecoverable and propagates out of simulate() to throw out the tx.
|
|
151
151
|
await this.insertRevertiblesFromPrivate(context);
|
|
152
152
|
|
|
153
153
|
// Only proceed with app logic if there was no revert during revertible insertion.
|
|
@@ -401,7 +401,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
|
401
401
|
// However, things work as expected because later calls to getters on the hintingContractsDB
|
|
402
402
|
// will pick up the new contracts and will generate the necessary hints.
|
|
403
403
|
// So, a consumer of the hints will always see the new contracts.
|
|
404
|
-
|
|
404
|
+
this.contractsDB.addContractsFromLogs(context.nonRevertibleContractDeploymentData);
|
|
405
405
|
}
|
|
406
406
|
|
|
407
407
|
/**
|
|
@@ -409,9 +409,13 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
|
409
409
|
* Throws TxSimRevertibleInsertionsRevert if there is some checked error during revertible insertions.
|
|
410
410
|
* This function checks for the following errors:
|
|
411
411
|
* - NullifierLimitReachedError
|
|
412
|
-
* - NullifierCollisionError
|
|
413
412
|
* - NoteHashLimitReachedError
|
|
414
413
|
* - L2ToL1MessageLimitReachedError
|
|
414
|
+
*
|
|
415
|
+
* Note: NullifierCollisionError is intentionally NOT caught here. A nullifier collision
|
|
416
|
+
* during revertible insertions is unprovable (the nullifier originated from private, so
|
|
417
|
+
* a collision indicates the tx should never have been proposed). It propagates as-is to
|
|
418
|
+
* make the transaction unrecoverable, matching the AVM circuit behavior.
|
|
415
419
|
*/
|
|
416
420
|
protected async insertRevertiblesFromPrivate(context: PublicTxContext) {
|
|
417
421
|
const stateManager = context.state.getActiveStateManager();
|
|
@@ -421,7 +425,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
|
421
425
|
await stateManager.writeSiloedNullifier(siloedNullifier);
|
|
422
426
|
}
|
|
423
427
|
} catch (e: any) {
|
|
424
|
-
if (e instanceof NullifierLimitReachedError
|
|
428
|
+
if (e instanceof NullifierLimitReachedError) {
|
|
425
429
|
context.revert(
|
|
426
430
|
TxExecutionPhase.APP_LOGIC,
|
|
427
431
|
new SimulationError(
|
|
@@ -431,7 +435,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
|
431
435
|
);
|
|
432
436
|
throw new TxSimRevertibleInsertionsRevert();
|
|
433
437
|
} else {
|
|
434
|
-
// Unchecked/unknown error - re-throw as-is
|
|
438
|
+
// Unchecked/unknown error or NullifierCollisionError (unrecoverable) - re-throw as-is
|
|
435
439
|
throw e;
|
|
436
440
|
}
|
|
437
441
|
}
|
|
@@ -486,7 +490,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
|
486
490
|
// However, things work as expected because later calls to getters on the hintingContractsDB
|
|
487
491
|
// will pick up the new contracts and will generate the necessary hints.
|
|
488
492
|
// So, a consumer of the hints will always see the new contracts.
|
|
489
|
-
|
|
493
|
+
this.contractsDB.addContractsFromLogs(context.revertibleContractDeploymentData);
|
|
490
494
|
}
|
|
491
495
|
|
|
492
496
|
private async payFee(context: PublicTxContext) {
|