@aztec/simulator 0.0.1-commit.934299a21 → 0.0.1-commit.949a33fd8
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/circuit_recording/circuit_recorder.js +2 -2
- 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/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 +4 -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/public_tx_simulation_tester.d.ts +6 -5
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
- package/dest/public/fixtures/public_tx_simulation_tester.js +10 -10
- package/dest/public/fixtures/utils.d.ts +2 -2
- package/dest/public/fixtures/utils.d.ts.map +1 -1
- package/dest/public/fixtures/utils.js +6 -6
- package/dest/public/hinting_db_sources.d.ts +4 -4
- package/dest/public/hinting_db_sources.d.ts.map +1 -1
- package/dest/public/hinting_db_sources.js +6 -5
- package/dest/public/public_db_sources.d.ts +3 -3
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +10 -10
- package/dest/public/public_processor/guarded_merkle_tree.d.ts +4 -4
- package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
- package/dest/public/public_processor/guarded_merkle_tree.js +4 -4
- package/dest/public/public_processor/public_processor.d.ts +2 -2
- package/dest/public/public_processor/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor/public_processor.js +53 -41
- 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 +2 -1
- 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 +15 -16
- package/src/private/circuit_recording/circuit_recorder.ts +2 -2
- package/src/public/avm/avm_simulator.ts +1 -1
- package/src/public/avm/calldata.ts +3 -2
- 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 +2 -4
- package/src/public/fixtures/custom_bytecode_tests.ts +139 -1
- package/src/public/fixtures/public_tx_simulation_tester.ts +21 -11
- package/src/public/fixtures/utils.ts +13 -8
- package/src/public/hinting_db_sources.ts +8 -6
- package/src/public/public_db_sources.ts +12 -14
- package/src/public/public_processor/guarded_merkle_tree.ts +5 -5
- package/src/public/public_processor/public_processor.ts +73 -50
- package/src/public/public_processor/public_processor_metrics.ts +12 -0
- package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +2 -1
- package/src/public/public_tx_simulator/public_tx_context.ts +8 -10
- package/src/public/public_tx_simulator/public_tx_simulator.ts +11 -7
|
@@ -160,8 +160,8 @@ export class PublicProcessor implements Traceable {
|
|
|
160
160
|
txs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
161
161
|
limits: PublicProcessorLimits = {},
|
|
162
162
|
validator: PublicProcessorValidator = {},
|
|
163
|
-
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[],
|
|
164
|
-
const { maxTransactions,
|
|
163
|
+
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
|
|
164
|
+
const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal } = limits;
|
|
165
165
|
const { preprocessValidator, nullifierCache } = validator;
|
|
166
166
|
const result: ProcessedTx[] = [];
|
|
167
167
|
const usedTxs: Tx[] = [];
|
|
@@ -174,6 +174,8 @@ export class PublicProcessor implements Traceable {
|
|
|
174
174
|
let totalPublicGas = new Gas(0, 0);
|
|
175
175
|
let totalBlockGas = new Gas(0, 0);
|
|
176
176
|
let totalBlobFields = 0;
|
|
177
|
+
let silentlySkippedCount = 0;
|
|
178
|
+
let totalSilentlySkippedDurationMs = 0;
|
|
177
179
|
|
|
178
180
|
for await (const tx of txs) {
|
|
179
181
|
// Only process up to the max tx limit
|
|
@@ -188,22 +190,23 @@ export class PublicProcessor implements Traceable {
|
|
|
188
190
|
break;
|
|
189
191
|
}
|
|
190
192
|
|
|
191
|
-
// Skip this tx if it'd exceed max block size
|
|
192
193
|
const txHash = tx.getTxHash().toString();
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
|
|
195
|
+
// Skip this tx if its estimated blob fields would exceed the limit.
|
|
196
|
+
// Only done during proposal building: during re-execution we must process the exact txs from the proposal.
|
|
197
|
+
const txBlobFields = tx.getPrivateTxEffectsSizeInFields();
|
|
198
|
+
if (isBuildingProposal && maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
|
|
199
|
+
this.log.warn(
|
|
200
|
+
`Skipping tx ${txHash} with ${txBlobFields} fields from private side effects due to blob fields limit`,
|
|
201
|
+
{ txHash, txBlobFields, totalBlobFields, maxBlobFields },
|
|
202
|
+
);
|
|
201
203
|
continue;
|
|
202
204
|
}
|
|
203
205
|
|
|
204
|
-
// Skip this tx if its gas limit would exceed the block gas limit
|
|
206
|
+
// Skip this tx if its gas limit would exceed the block gas limit (either da or l2).
|
|
207
|
+
// Only done during proposal building: during re-execution we must process the exact txs from the proposal.
|
|
205
208
|
const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
|
|
206
|
-
if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
209
|
+
if (isBuildingProposal && maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
207
210
|
this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
|
|
208
211
|
txHash,
|
|
209
212
|
txGasLimit,
|
|
@@ -243,7 +246,9 @@ export class PublicProcessor implements Traceable {
|
|
|
243
246
|
this.contractsDB.createCheckpoint();
|
|
244
247
|
|
|
245
248
|
try {
|
|
246
|
-
const [processedTx, returnValues, txDebugLogs] = await
|
|
249
|
+
const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
|
|
250
|
+
this.processTx(tx, deadline),
|
|
251
|
+
);
|
|
247
252
|
|
|
248
253
|
// Inject a fake processing failure after N txs if requested
|
|
249
254
|
const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
|
|
@@ -252,23 +257,9 @@ export class PublicProcessor implements Traceable {
|
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
const txBlobFields = processedTx.txEffect.getNumBlobFields();
|
|
255
|
-
|
|
256
|
-
// If the actual size of this tx would exceed block size, skip it
|
|
257
260
|
const txSize = txBlobFields * Fr.SIZE_IN_BYTES;
|
|
258
|
-
if (maxBlockSize !== undefined && totalSizeInBytes + txSize > maxBlockSize) {
|
|
259
|
-
this.log.debug(`Skipping processed tx ${txHash} sized ${txSize} due to max block size.`, {
|
|
260
|
-
txHash,
|
|
261
|
-
sizeInBytes: txSize,
|
|
262
|
-
totalSizeInBytes,
|
|
263
|
-
maxBlockSize,
|
|
264
|
-
});
|
|
265
|
-
// Need to revert the checkpoint here and don't go any further
|
|
266
|
-
await checkpoint.revert();
|
|
267
|
-
this.contractsDB.revertCheckpoint();
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
261
|
|
|
271
|
-
// If the actual blob fields of this tx would exceed the limit, skip it
|
|
262
|
+
// If the actual blob fields of this tx would exceed the limit, skip it.
|
|
272
263
|
// Note: maxBlobFields already accounts for block end blob fields and previous blocks in checkpoint.
|
|
273
264
|
if (maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
|
|
274
265
|
this.log.debug(
|
|
@@ -278,14 +269,37 @@ export class PublicProcessor implements Traceable {
|
|
|
278
269
|
txBlobFields,
|
|
279
270
|
totalBlobFields,
|
|
280
271
|
maxBlobFields,
|
|
272
|
+
txProcessingTimeMs,
|
|
281
273
|
},
|
|
282
274
|
);
|
|
275
|
+
silentlySkippedCount += 1;
|
|
276
|
+
totalSilentlySkippedDurationMs += txProcessingTimeMs;
|
|
277
|
+
this.metrics.recordSilentlySkipped(txProcessingTimeMs);
|
|
283
278
|
// Need to revert the checkpoint here and don't go any further
|
|
284
279
|
await checkpoint.revert();
|
|
285
280
|
this.contractsDB.revertCheckpoint();
|
|
286
281
|
continue;
|
|
287
282
|
}
|
|
288
283
|
|
|
284
|
+
// During re-execution, check if the actual gas used by this tx would push the block over the gas limit.
|
|
285
|
+
// Unlike the proposal-building check (which uses declared gas limits pessimistically before processing),
|
|
286
|
+
// this uses actual gas and stops processing when the limit is exceeded.
|
|
287
|
+
if (
|
|
288
|
+
!isBuildingProposal &&
|
|
289
|
+
maxBlockGas !== undefined &&
|
|
290
|
+
totalBlockGas.add(processedTx.gasUsed.totalGas).gtAny(maxBlockGas)
|
|
291
|
+
) {
|
|
292
|
+
this.log.warn(`Stopping re-execution since tx ${txHash} would push block gas over limit`, {
|
|
293
|
+
txHash,
|
|
294
|
+
txGas: processedTx.gasUsed.totalGas,
|
|
295
|
+
totalBlockGas,
|
|
296
|
+
maxBlockGas,
|
|
297
|
+
});
|
|
298
|
+
await checkpoint.revert();
|
|
299
|
+
this.contractsDB.revertCheckpoint();
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
|
|
289
303
|
// FIXME(fcarreiro): it's ugly to have to notify the validator of nullifiers.
|
|
290
304
|
// I'd rather pass the validators the processedTx as well and let them deal with it.
|
|
291
305
|
nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
|
|
@@ -300,6 +314,9 @@ export class PublicProcessor implements Traceable {
|
|
|
300
314
|
totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
|
|
301
315
|
totalSizeInBytes += txSize;
|
|
302
316
|
totalBlobFields += txBlobFields;
|
|
317
|
+
|
|
318
|
+
// Commit the tx-level contracts checkpoint on success
|
|
319
|
+
this.contractsDB.commitCheckpoint();
|
|
303
320
|
} catch (err: any) {
|
|
304
321
|
if (err?.name === 'PublicProcessorTimeoutError') {
|
|
305
322
|
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
@@ -319,14 +336,10 @@ export class PublicProcessor implements Traceable {
|
|
|
319
336
|
// 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
|
|
320
337
|
// 2. Possible state updates on that checkpoint or any others created during execution.
|
|
321
338
|
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
await checkpoint.
|
|
326
|
-
|
|
327
|
-
// Now we want to revert any/all remaining checkpoints, destroying any outstanding state updates.
|
|
328
|
-
// This needs to be done directly on the underlying fork as the guarded fork has been stopped.
|
|
329
|
-
await this.guardedMerkleTree.getUnderlyingFork().revertAllCheckpoints();
|
|
339
|
+
// Revert all checkpoints at or above this checkpoint's depth (inclusive), destroying any outstanding state
|
|
340
|
+
// updates from this tx and any nested checkpoints created during execution. This preserves any checkpoints
|
|
341
|
+
// created by callers below our depth.
|
|
342
|
+
await checkpoint.revertToCheckpoint();
|
|
330
343
|
|
|
331
344
|
// Revert any contracts added to the DB for the tx.
|
|
332
345
|
this.contractsDB.revertCheckpoint();
|
|
@@ -338,9 +351,9 @@ export class PublicProcessor implements Traceable {
|
|
|
338
351
|
break;
|
|
339
352
|
}
|
|
340
353
|
|
|
341
|
-
// Roll back state to start of TX before proceeding to next TX
|
|
342
|
-
|
|
343
|
-
await
|
|
354
|
+
// Roll back state to start of TX before proceeding to next TX.
|
|
355
|
+
// Reverts all checkpoints at or above this checkpoint's depth, preserving any caller checkpoints below.
|
|
356
|
+
await checkpoint.revertToCheckpoint();
|
|
344
357
|
this.contractsDB.revertCheckpoint();
|
|
345
358
|
const errorMessage = err instanceof Error || err instanceof AssertionError ? err.message : 'Unknown error';
|
|
346
359
|
this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
|
|
@@ -352,7 +365,6 @@ export class PublicProcessor implements Traceable {
|
|
|
352
365
|
} finally {
|
|
353
366
|
// Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
|
|
354
367
|
await checkpoint.commit();
|
|
355
|
-
this.contractsDB.commitCheckpointOkIfNone();
|
|
356
368
|
}
|
|
357
369
|
}
|
|
358
370
|
|
|
@@ -360,15 +372,26 @@ export class PublicProcessor implements Traceable {
|
|
|
360
372
|
const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
|
|
361
373
|
this.metrics.recordAllTxs(totalPublicGas, rate);
|
|
362
374
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
375
|
+
const silentlySkippedDurationMs = Math.round(totalSilentlySkippedDurationMs);
|
|
376
|
+
this.log.info(
|
|
377
|
+
`Processed ${result.length} successful txs and ${failed.length} failed txs ` +
|
|
378
|
+
`(${silentlySkippedCount} silently skipped, ${silentlySkippedDurationMs}ms wasted) ` +
|
|
379
|
+
`in ${duration}s`,
|
|
380
|
+
{
|
|
381
|
+
blockNumber: this.globalVariables.blockNumber,
|
|
382
|
+
successfulCount: result.length,
|
|
383
|
+
failedCount: failed.length,
|
|
384
|
+
duration,
|
|
385
|
+
rate,
|
|
386
|
+
totalPublicGas,
|
|
387
|
+
totalBlockGas,
|
|
388
|
+
totalSizeInBytes,
|
|
389
|
+
silentlySkippedCount,
|
|
390
|
+
silentlySkippedDurationMs,
|
|
391
|
+
},
|
|
392
|
+
);
|
|
370
393
|
|
|
371
|
-
return [result, failed, usedTxs, returns,
|
|
394
|
+
return [result, failed, usedTxs, returns, debugLogs];
|
|
372
395
|
}
|
|
373
396
|
|
|
374
397
|
private async checkWorldStateUnchanged(
|
|
@@ -544,7 +567,7 @@ export class PublicProcessor implements Traceable {
|
|
|
544
567
|
// Fee payment insertion has already been done. Do the rest.
|
|
545
568
|
await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
|
|
546
569
|
|
|
547
|
-
|
|
570
|
+
this.contractsDB.addNewContracts(tx);
|
|
548
571
|
|
|
549
572
|
return [processedTx, undefined, []];
|
|
550
573
|
}
|
|
@@ -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
|
|
|
@@ -62,7 +63,7 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
62
63
|
|
|
63
64
|
// Add contracts to the contracts DB
|
|
64
65
|
this.log.trace(`Calling contractsDB.addContracts`);
|
|
65
|
-
|
|
66
|
+
this.contractsDB.addContracts(contractDeploymentData);
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
public getBytecodeCommitment = async (classId: string): Promise<Buffer | undefined> => {
|
|
@@ -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.addContracts(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.addContracts(context.revertibleContractDeploymentData);
|
|
490
494
|
}
|
|
491
495
|
|
|
492
496
|
private async payFee(context: PublicTxContext) {
|