@aztec/simulator 0.0.1-commit.f504929 → 0.0.1-commit.f5a9928
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/client.d.ts +1 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +0 -2
- 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/private/circuit_recording/circuit_recorder.d.ts +36 -23
- package/dest/private/circuit_recording/circuit_recorder.d.ts.map +1 -1
- package/dest/private/circuit_recording/circuit_recorder.js +65 -69
- package/dest/private/circuit_recording/file_circuit_recorder.d.ts +7 -19
- package/dest/private/circuit_recording/file_circuit_recorder.d.ts.map +1 -1
- package/dest/private/circuit_recording/file_circuit_recorder.js +38 -42
- package/dest/private/circuit_recording/simulator_recorder_wrapper.d.ts +1 -1
- package/dest/private/circuit_recording/simulator_recorder_wrapper.d.ts.map +1 -1
- package/dest/private/circuit_recording/simulator_recorder_wrapper.js +11 -14
- 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 +18 -3
- 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 +4 -1
- 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/amm_test.js +2 -2
- 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 +33 -6
- package/dest/public/fixtures/custom_bytecode_tester.d.ts +1 -1
- package/dest/public/fixtures/custom_bytecode_tester.d.ts.map +1 -1
- package/dest/public/fixtures/custom_bytecode_tester.js +2 -2
- package/dest/public/fixtures/custom_bytecode_tests.d.ts +8 -1
- package/dest/public/fixtures/custom_bytecode_tests.d.ts.map +1 -1
- package/dest/public/fixtures/custom_bytecode_tests.js +91 -2
- 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 +6 -5
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
- package/dest/public/fixtures/public_tx_simulation_tester.js +11 -9
- package/dest/public/fixtures/token_test.js +3 -3
- 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 +17 -16
- 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 +7 -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 +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 +5 -4
- package/dest/public/public_processor/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor/public_processor.js +107 -63
- 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 +5 -4
- 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/dest/public/state_manager/state_manager.js +1 -1
- package/package.json +16 -16
- package/src/client.ts +0 -2
- package/src/private/acvm_wasm.ts +4 -1
- package/src/private/circuit_recording/circuit_recorder.ts +84 -78
- package/src/private/circuit_recording/file_circuit_recorder.ts +38 -48
- package/src/private/circuit_recording/simulator_recorder_wrapper.ts +9 -15
- 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 +23 -3
- package/src/public/avm/fixtures/utils.ts +4 -1
- 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/amm_test.ts +2 -2
- package/src/public/fixtures/bulk_test.ts +31 -6
- package/src/public/fixtures/custom_bytecode_tester.ts +2 -2
- package/src/public/fixtures/custom_bytecode_tests.ts +209 -2
- package/src/public/fixtures/opcode_spammer.ts +4 -8
- package/src/public/fixtures/public_tx_simulation_tester.ts +21 -12
- package/src/public/fixtures/token_test.ts +3 -3
- package/src/public/fixtures/utils.ts +19 -20
- package/src/public/hinting_db_sources.ts +9 -6
- package/src/public/public_db_sources.ts +21 -14
- package/src/public/public_processor/guarded_merkle_tree.ts +5 -5
- package/src/public/public_processor/public_processor.ts +138 -77
- package/src/public/public_processor/public_processor_metrics.ts +12 -0
- package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +5 -4
- package/src/public/public_tx_simulator/public_tx_context.ts +8 -10
- package/src/public/public_tx_simulator/public_tx_simulator.ts +11 -7
- package/src/public/state_manager/state_manager.ts +1 -1
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
3
|
+
MAX_NULLIFIERS_PER_TX,
|
|
4
|
+
MAX_TX_BLOB_DATA_SIZE_IN_FIELDS,
|
|
5
|
+
NULLIFIER_SUBTREE_HEIGHT,
|
|
6
|
+
} from '@aztec/constants';
|
|
2
7
|
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
3
8
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
9
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
5
10
|
import { sleep } from '@aztec/foundation/sleep';
|
|
6
|
-
import { DateProvider, Timer, elapsed,
|
|
11
|
+
import { DateProvider, Timer, elapsed, execWithSignal } from '@aztec/foundation/timer';
|
|
7
12
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
8
13
|
import { ContractClassPublishedEvent } from '@aztec/protocol-contracts/class-registry';
|
|
9
14
|
import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
@@ -76,17 +81,15 @@ export class PublicProcessorFactory {
|
|
|
76
81
|
/**
|
|
77
82
|
* Creates a new instance of a PublicProcessor.
|
|
78
83
|
* @param globalVariables - The global variables for the block being processed.
|
|
79
|
-
* @param
|
|
84
|
+
* @param contractsDB - Optional pre-populated contracts DB; a fresh one is constructed if omitted.
|
|
80
85
|
* @returns A new instance of a PublicProcessor.
|
|
81
86
|
*/
|
|
82
87
|
public create(
|
|
83
88
|
merkleTree: MerkleTreeWriteOperations,
|
|
84
89
|
globalVariables: GlobalVariables,
|
|
85
90
|
config: PublicSimulatorConfig,
|
|
91
|
+
contractsDB: PublicContractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings()),
|
|
86
92
|
): PublicProcessor {
|
|
87
|
-
const bindings = this.log.getBindings();
|
|
88
|
-
const contractsDB = new PublicContractsDB(this.contractDataSource, bindings);
|
|
89
|
-
|
|
90
93
|
const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
|
|
91
94
|
const publicTxSimulator = this.createPublicTxSimulator(guardedFork, contractsDB, globalVariables, config);
|
|
92
95
|
|
|
@@ -97,7 +100,7 @@ export class PublicProcessorFactory {
|
|
|
97
100
|
publicTxSimulator,
|
|
98
101
|
this.dateProvider,
|
|
99
102
|
this.telemetryClient,
|
|
100
|
-
createLogger('simulator:public-processor',
|
|
103
|
+
createLogger('simulator:public-processor', this.log.getBindings()),
|
|
101
104
|
);
|
|
102
105
|
}
|
|
103
106
|
|
|
@@ -125,6 +128,17 @@ class PublicProcessorTimeoutError extends Error {
|
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
|
|
131
|
+
class PublicProcessorAbortError extends Error {
|
|
132
|
+
constructor(message: string = 'Aborted while processing tx') {
|
|
133
|
+
super(message);
|
|
134
|
+
this.name = 'PublicProcessorAbortError';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isPublicProcessorInterruptError(err: any) {
|
|
139
|
+
return err?.name === 'PublicProcessorTimeoutError' || err?.name === 'PublicProcessorAbortError';
|
|
140
|
+
}
|
|
141
|
+
|
|
128
142
|
/**
|
|
129
143
|
* Converts Txs lifted from the P2P module into ProcessedTx objects by executing
|
|
130
144
|
* any public function calls in them. Txs with private calls only are unaffected.
|
|
@@ -160,8 +174,8 @@ export class PublicProcessor implements Traceable {
|
|
|
160
174
|
txs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
161
175
|
limits: PublicProcessorLimits = {},
|
|
162
176
|
validator: PublicProcessorValidator = {},
|
|
163
|
-
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[],
|
|
164
|
-
const { maxTransactions,
|
|
177
|
+
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
|
|
178
|
+
const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal, signal } = limits;
|
|
165
179
|
const { preprocessValidator, nullifierCache } = validator;
|
|
166
180
|
const result: ProcessedTx[] = [];
|
|
167
181
|
const usedTxs: Tx[] = [];
|
|
@@ -174,6 +188,8 @@ export class PublicProcessor implements Traceable {
|
|
|
174
188
|
let totalPublicGas = new Gas(0, 0);
|
|
175
189
|
let totalBlockGas = new Gas(0, 0);
|
|
176
190
|
let totalBlobFields = 0;
|
|
191
|
+
let silentlySkippedCount = 0;
|
|
192
|
+
let totalSilentlySkippedDurationMs = 0;
|
|
177
193
|
|
|
178
194
|
for await (const tx of txs) {
|
|
179
195
|
// Only process up to the max tx limit
|
|
@@ -182,28 +198,33 @@ export class PublicProcessor implements Traceable {
|
|
|
182
198
|
break;
|
|
183
199
|
}
|
|
184
200
|
|
|
185
|
-
// Bail if we've hit the deadline
|
|
201
|
+
// Bail if we've hit the deadline or have been interrupted.
|
|
186
202
|
if (deadline && this.dateProvider.now() > +deadline) {
|
|
187
203
|
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
188
204
|
break;
|
|
189
205
|
}
|
|
206
|
+
if (signal?.aborted) {
|
|
207
|
+
this.log.warn(`Stopping tx processing due to abort signal.`);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
190
210
|
|
|
191
|
-
// Skip this tx if it'd exceed max block size
|
|
192
211
|
const txHash = tx.getTxHash().toString();
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
212
|
+
|
|
213
|
+
// Skip this tx if its estimated blob fields would exceed the limit.
|
|
214
|
+
// Only done during proposal building: during re-execution we must process the exact txs from the proposal.
|
|
215
|
+
const txBlobFields = tx.getPrivateTxEffectsSizeInFields();
|
|
216
|
+
if (isBuildingProposal && maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
|
|
217
|
+
this.log.warn(
|
|
218
|
+
`Skipping tx ${txHash} with ${txBlobFields} fields from private side effects due to blob fields limit`,
|
|
219
|
+
{ txHash, txBlobFields, totalBlobFields, maxBlobFields },
|
|
220
|
+
);
|
|
201
221
|
continue;
|
|
202
222
|
}
|
|
203
223
|
|
|
204
|
-
// Skip this tx if its gas limit would exceed the block gas limit
|
|
224
|
+
// Skip this tx if its gas limit would exceed the block gas limit (either da or l2).
|
|
225
|
+
// Only done during proposal building: during re-execution we must process the exact txs from the proposal.
|
|
205
226
|
const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
|
|
206
|
-
if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
227
|
+
if (isBuildingProposal && maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
207
228
|
this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
|
|
208
229
|
txHash,
|
|
209
230
|
txGasLimit,
|
|
@@ -223,11 +244,6 @@ export class PublicProcessor implements Traceable {
|
|
|
223
244
|
failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
|
|
224
245
|
returns.push(new NestedProcessReturnValues([]));
|
|
225
246
|
continue;
|
|
226
|
-
} else if (result.result === 'skipped') {
|
|
227
|
-
const reason = result.reason.join(', ');
|
|
228
|
-
this.log.debug(`Skipping tx ${txHash.toString()} due to pre-process validation: ${reason}`);
|
|
229
|
-
returns.push(new NestedProcessReturnValues([]));
|
|
230
|
-
continue;
|
|
231
247
|
} else {
|
|
232
248
|
this.log.trace(`Tx ${txHash.toString()} is valid before processing.`);
|
|
233
249
|
}
|
|
@@ -243,7 +259,9 @@ export class PublicProcessor implements Traceable {
|
|
|
243
259
|
this.contractsDB.createCheckpoint();
|
|
244
260
|
|
|
245
261
|
try {
|
|
246
|
-
const [processedTx, returnValues, txDebugLogs] = await
|
|
262
|
+
const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
|
|
263
|
+
this.processTx(tx, deadline, signal),
|
|
264
|
+
);
|
|
247
265
|
|
|
248
266
|
// Inject a fake processing failure after N txs if requested
|
|
249
267
|
const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
|
|
@@ -252,23 +270,25 @@ export class PublicProcessor implements Traceable {
|
|
|
252
270
|
}
|
|
253
271
|
|
|
254
272
|
const txBlobFields = processedTx.txEffect.getNumBlobFields();
|
|
255
|
-
|
|
256
|
-
// If the actual size of this tx would exceed block size, skip it
|
|
257
273
|
const txSize = txBlobFields * Fr.SIZE_IN_BYTES;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
274
|
+
|
|
275
|
+
// A single tx's effects must fit within the per-tx blob encoding: the rollup circuit encodes each
|
|
276
|
+
// tx into a fixed [Field; MAX_TX_BLOB_DATA_SIZE_IN_FIELDS] array, so a larger tx effect cannot be
|
|
277
|
+
// proven. The per-category side-effect limits already guarantee this upstream, so reaching here means
|
|
278
|
+
// the tx is malformed; reject it as invalid rather than letting it poison proving.
|
|
279
|
+
if (txBlobFields > MAX_TX_BLOB_DATA_SIZE_IN_FIELDS) {
|
|
280
|
+
const error = new Error(
|
|
281
|
+
`Tx ${txHash} produced ${txBlobFields} blob fields, exceeding the per-tx maximum of ${MAX_TX_BLOB_DATA_SIZE_IN_FIELDS}`,
|
|
282
|
+
);
|
|
283
|
+
this.log.error(error.message, { txHash, txBlobFields });
|
|
266
284
|
await checkpoint.revert();
|
|
267
285
|
this.contractsDB.revertCheckpoint();
|
|
286
|
+
failed.push({ tx, error });
|
|
287
|
+
returns.push(new NestedProcessReturnValues([]));
|
|
268
288
|
continue;
|
|
269
289
|
}
|
|
270
290
|
|
|
271
|
-
// If the actual blob fields of this tx would exceed the limit, skip it
|
|
291
|
+
// If the actual blob fields of this tx would exceed the limit, skip it.
|
|
272
292
|
// Note: maxBlobFields already accounts for block end blob fields and previous blocks in checkpoint.
|
|
273
293
|
if (maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
|
|
274
294
|
this.log.debug(
|
|
@@ -278,14 +298,37 @@ export class PublicProcessor implements Traceable {
|
|
|
278
298
|
txBlobFields,
|
|
279
299
|
totalBlobFields,
|
|
280
300
|
maxBlobFields,
|
|
301
|
+
txProcessingTimeMs,
|
|
281
302
|
},
|
|
282
303
|
);
|
|
304
|
+
silentlySkippedCount += 1;
|
|
305
|
+
totalSilentlySkippedDurationMs += txProcessingTimeMs;
|
|
306
|
+
this.metrics.recordSilentlySkipped(txProcessingTimeMs);
|
|
283
307
|
// Need to revert the checkpoint here and don't go any further
|
|
284
308
|
await checkpoint.revert();
|
|
285
309
|
this.contractsDB.revertCheckpoint();
|
|
286
310
|
continue;
|
|
287
311
|
}
|
|
288
312
|
|
|
313
|
+
// During re-execution, check if the actual gas used by this tx would push the block over the gas limit.
|
|
314
|
+
// Unlike the proposal-building check (which uses declared gas limits pessimistically before processing),
|
|
315
|
+
// this uses actual gas and stops processing when the limit is exceeded.
|
|
316
|
+
if (
|
|
317
|
+
!isBuildingProposal &&
|
|
318
|
+
maxBlockGas !== undefined &&
|
|
319
|
+
totalBlockGas.add(processedTx.gasUsed.totalGas).gtAny(maxBlockGas)
|
|
320
|
+
) {
|
|
321
|
+
this.log.warn(`Stopping re-execution since tx ${txHash} would push block gas over limit`, {
|
|
322
|
+
txHash,
|
|
323
|
+
txGas: processedTx.gasUsed.totalGas,
|
|
324
|
+
totalBlockGas,
|
|
325
|
+
maxBlockGas,
|
|
326
|
+
});
|
|
327
|
+
await checkpoint.revert();
|
|
328
|
+
this.contractsDB.revertCheckpoint();
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
|
|
289
332
|
// FIXME(fcarreiro): it's ugly to have to notify the validator of nullifiers.
|
|
290
333
|
// I'd rather pass the validators the processedTx as well and let them deal with it.
|
|
291
334
|
nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
|
|
@@ -300,16 +343,15 @@ export class PublicProcessor implements Traceable {
|
|
|
300
343
|
totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
|
|
301
344
|
totalSizeInBytes += txSize;
|
|
302
345
|
totalBlobFields += txBlobFields;
|
|
346
|
+
|
|
347
|
+
// Commit the tx-level contracts checkpoint on success
|
|
348
|
+
this.contractsDB.commitCheckpoint();
|
|
303
349
|
} catch (err: any) {
|
|
304
|
-
if (err
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
//
|
|
308
|
-
// Signal cancellation AND WAIT for the simulation to actually stop.
|
|
309
|
-
// This is critical because C++ might be in the middle of a slow operation (e.g., pad_trees)
|
|
310
|
-
// and won't check the cancellation flag until that operation completes.
|
|
311
|
-
// Without waiting, we'd proceed to revert checkpoints while C++ is still writing to state.
|
|
312
|
-
// Wait for C++ to stop gracefully.
|
|
350
|
+
if (isPublicProcessorInterruptError(err)) {
|
|
351
|
+
const interruptReason = err.name === 'PublicProcessorTimeoutError' ? 'timeout' : 'abort signal';
|
|
352
|
+
this.log.warn(`Stopping tx processing due to ${interruptReason}.`);
|
|
353
|
+
// The tx may still be executing on a worker thread (C++ via NAPI).
|
|
354
|
+
// Signal cancellation AND WAIT for the simulation to actually stop before touching fork checkpoints.
|
|
313
355
|
await this.publicTxSimulator.cancel?.();
|
|
314
356
|
|
|
315
357
|
// Now stop the guarded fork to prevent any further TS-side access to the world state.
|
|
@@ -319,14 +361,10 @@ export class PublicProcessor implements Traceable {
|
|
|
319
361
|
// 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
|
|
320
362
|
// 2. Possible state updates on that checkpoint or any others created during execution.
|
|
321
363
|
|
|
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();
|
|
364
|
+
// Revert all checkpoints at or above this checkpoint's depth (inclusive), destroying any outstanding state
|
|
365
|
+
// updates from this tx and any nested checkpoints created during execution. This preserves any checkpoints
|
|
366
|
+
// created by callers below our depth.
|
|
367
|
+
await checkpoint.revertToCheckpoint();
|
|
330
368
|
|
|
331
369
|
// Revert any contracts added to the DB for the tx.
|
|
332
370
|
this.contractsDB.revertCheckpoint();
|
|
@@ -338,9 +376,9 @@ export class PublicProcessor implements Traceable {
|
|
|
338
376
|
break;
|
|
339
377
|
}
|
|
340
378
|
|
|
341
|
-
// Roll back state to start of TX before proceeding to next TX
|
|
342
|
-
|
|
343
|
-
await
|
|
379
|
+
// Roll back state to start of TX before proceeding to next TX.
|
|
380
|
+
// Reverts all checkpoints at or above this checkpoint's depth, preserving any caller checkpoints below.
|
|
381
|
+
await checkpoint.revertToCheckpoint();
|
|
344
382
|
this.contractsDB.revertCheckpoint();
|
|
345
383
|
const errorMessage = err instanceof Error || err instanceof AssertionError ? err.message : 'Unknown error';
|
|
346
384
|
this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
|
|
@@ -352,7 +390,6 @@ export class PublicProcessor implements Traceable {
|
|
|
352
390
|
} finally {
|
|
353
391
|
// Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
|
|
354
392
|
await checkpoint.commit();
|
|
355
|
-
this.contractsDB.commitCheckpointOkIfNone();
|
|
356
393
|
}
|
|
357
394
|
}
|
|
358
395
|
|
|
@@ -360,15 +397,26 @@ export class PublicProcessor implements Traceable {
|
|
|
360
397
|
const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
|
|
361
398
|
this.metrics.recordAllTxs(totalPublicGas, rate);
|
|
362
399
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
400
|
+
const silentlySkippedDurationMs = Math.round(totalSilentlySkippedDurationMs);
|
|
401
|
+
this.log.info(
|
|
402
|
+
`Processed ${result.length} successful txs and ${failed.length} failed txs ` +
|
|
403
|
+
`(${silentlySkippedCount} silently skipped, ${silentlySkippedDurationMs}ms wasted) ` +
|
|
404
|
+
`in ${duration}s`,
|
|
405
|
+
{
|
|
406
|
+
blockNumber: this.globalVariables.blockNumber,
|
|
407
|
+
successfulCount: result.length,
|
|
408
|
+
failedCount: failed.length,
|
|
409
|
+
duration,
|
|
410
|
+
rate,
|
|
411
|
+
totalPublicGas,
|
|
412
|
+
totalBlockGas,
|
|
413
|
+
totalSizeInBytes,
|
|
414
|
+
silentlySkippedCount,
|
|
415
|
+
silentlySkippedDurationMs,
|
|
416
|
+
},
|
|
417
|
+
);
|
|
370
418
|
|
|
371
|
-
return [result, failed, usedTxs, returns,
|
|
419
|
+
return [result, failed, usedTxs, returns, debugLogs];
|
|
372
420
|
}
|
|
373
421
|
|
|
374
422
|
private async checkWorldStateUnchanged(
|
|
@@ -391,9 +439,10 @@ export class PublicProcessor implements Traceable {
|
|
|
391
439
|
private async processTx(
|
|
392
440
|
tx: Tx,
|
|
393
441
|
deadline: Date | undefined,
|
|
442
|
+
signal: AbortSignal | undefined,
|
|
394
443
|
): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
|
|
395
444
|
const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
|
|
396
|
-
this.processTxWithinDeadline(tx, deadline),
|
|
445
|
+
this.processTxWithinDeadline(tx, deadline, signal),
|
|
397
446
|
);
|
|
398
447
|
|
|
399
448
|
this.log.verbose(
|
|
@@ -447,10 +496,11 @@ export class PublicProcessor implements Traceable {
|
|
|
447
496
|
this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
|
|
448
497
|
}
|
|
449
498
|
|
|
450
|
-
/** Processes the given tx within deadline
|
|
499
|
+
/** Processes the given tx within deadline or until the signal is aborted. */
|
|
451
500
|
private async processTxWithinDeadline(
|
|
452
501
|
tx: Tx,
|
|
453
502
|
deadline: Date | undefined,
|
|
503
|
+
signal: AbortSignal | undefined,
|
|
454
504
|
): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
|
|
455
505
|
const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
|
|
456
506
|
tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
|
|
@@ -467,27 +517,38 @@ export class PublicProcessor implements Traceable {
|
|
|
467
517
|
}
|
|
468
518
|
: innerProcessFn;
|
|
469
519
|
|
|
470
|
-
|
|
520
|
+
const processingSignal = this.getProcessingSignal(tx, deadline, signal);
|
|
521
|
+
if (!processingSignal) {
|
|
471
522
|
return await processFn();
|
|
472
523
|
}
|
|
473
524
|
|
|
474
|
-
|
|
525
|
+
return await execWithSignal(
|
|
526
|
+
() => processFn(),
|
|
527
|
+
processingSignal,
|
|
528
|
+
signal =>
|
|
529
|
+
signal.reason?.name === 'TimeoutError' ? new PublicProcessorTimeoutError() : new PublicProcessorAbortError(),
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private getProcessingSignal(tx: Tx, deadline: Date | undefined, signal: AbortSignal | undefined) {
|
|
534
|
+
if (!deadline) {
|
|
535
|
+
return signal;
|
|
536
|
+
}
|
|
537
|
+
|
|
475
538
|
const timeout = +deadline - this.dateProvider.now();
|
|
476
539
|
if (timeout <= 0) {
|
|
477
540
|
throw new PublicProcessorTimeoutError();
|
|
478
541
|
}
|
|
479
542
|
|
|
543
|
+
const txHash = tx.getTxHash();
|
|
480
544
|
this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, {
|
|
481
545
|
deadline: deadline.toISOString(),
|
|
482
546
|
now: new Date(this.dateProvider.now()).toISOString(),
|
|
483
547
|
txHash,
|
|
484
548
|
});
|
|
485
549
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
timeout,
|
|
489
|
-
() => new PublicProcessorTimeoutError(),
|
|
490
|
-
);
|
|
550
|
+
const timeoutSignal = AbortSignal.timeout(timeout);
|
|
551
|
+
return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
491
552
|
}
|
|
492
553
|
|
|
493
554
|
/**
|
|
@@ -544,7 +605,7 @@ export class PublicProcessor implements Traceable {
|
|
|
544
605
|
// Fee payment insertion has already been done. Do the rest.
|
|
545
606
|
await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
|
|
546
607
|
|
|
547
|
-
|
|
608
|
+
this.contractsDB.addNewContracts(tx);
|
|
548
609
|
|
|
549
610
|
return [processedTx, undefined, []];
|
|
550
611
|
}
|
|
@@ -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
|
}
|
|
@@ -23,7 +23,7 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
23
23
|
public getContractInstance = async (address: string): Promise<Buffer | undefined> => {
|
|
24
24
|
this.log.trace(`Contract provider callback: getContractInstance(${address})`);
|
|
25
25
|
|
|
26
|
-
const aztecAddr = AztecAddress.
|
|
26
|
+
const aztecAddr = AztecAddress.fromStringUnsafe(address);
|
|
27
27
|
|
|
28
28
|
const instance = await this.contractsDB.getContractInstance(aztecAddr, this.globalVariables.timestamp);
|
|
29
29
|
|
|
@@ -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> => {
|
|
@@ -87,7 +88,7 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
87
88
|
this.log.trace(`Contract provider callback: getDebugFunctionName(${address}, ${selector})`);
|
|
88
89
|
|
|
89
90
|
// Parse address and selector strings
|
|
90
|
-
const aztecAddr = AztecAddress.
|
|
91
|
+
const aztecAddr = AztecAddress.fromStringUnsafe(address);
|
|
91
92
|
const selectorFr = Fr.fromString(selector);
|
|
92
93
|
const functionSelector = FunctionSelector.fromFieldOrUndefined(selectorFr);
|
|
93
94
|
|
|
@@ -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) {
|
|
@@ -402,7 +402,7 @@ export class PublicPersistableStateManager {
|
|
|
402
402
|
|
|
403
403
|
// This will internally decide whether to check the nullifier tree or not depending on doMerkleOperations.
|
|
404
404
|
const nullifierExistsInTree = await this.checkNullifierExists(
|
|
405
|
-
AztecAddress.
|
|
405
|
+
AztecAddress.fromNumberUnsafe(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
|
|
406
406
|
contractAddress.toField(),
|
|
407
407
|
);
|
|
408
408
|
assert(
|