@aztec/simulator 0.0.1-commit.023c3e5 → 0.0.1-commit.031e54b
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/README.md +4 -4
- 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.js +2 -2
- package/dest/public/avm/avm_gas.js +3 -3
- 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/account_proof_fetcher.d.ts +2 -0
- package/dest/public/avm/fixtures/account_proof_fetcher.d.ts.map +1 -0
- package/dest/public/avm/fixtures/account_proof_fetcher.js +152 -0
- 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/accrued_substate.d.ts +2 -2
- package/dest/public/avm/opcodes/accrued_substate.d.ts.map +1 -1
- package/dest/public/avm/opcodes/accrued_substate.js +3 -4
- 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/avm/serialization/bytecode_serialization.js +3 -3
- package/dest/public/avm/serialization/instruction_serialization.d.ts +2 -2
- package/dest/public/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/public/avm/serialization/instruction_serialization.js +1 -1
- 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 +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 +8 -16
- 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 +37 -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 +19 -20
- package/dest/public/hinting_db_sources.d.ts +5 -5
- package/dest/public/hinting_db_sources.d.ts.map +1 -1
- package/dest/public/hinting_db_sources.js +8 -7
- 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 +5 -5
- package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
- package/dest/public/public_processor/guarded_merkle_tree.js +5 -5
- package/dest/public/public_processor/public_processor.d.ts +8 -5
- package/dest/public/public_processor/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor/public_processor.js +107 -72
- 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 +2 -3
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.js +2 -2
- package/dest/public/public_tx_simulator/factories.d.ts +2 -2
- package/dest/public/public_tx_simulator/factories.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/factories.js +2 -2
- 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 +14 -9
- package/dest/public/test_executor_metrics.d.ts +6 -1
- package/dest/public/test_executor_metrics.d.ts.map +1 -1
- package/dest/public/test_executor_metrics.js +22 -0
- package/package.json +16 -16
- package/src/private/acvm_wasm.ts +4 -1
- package/src/private/circuit_recording/circuit_recorder.ts +2 -2
- package/src/public/avm/avm_gas.ts +2 -2
- package/src/public/avm/avm_simulator.ts +1 -1
- package/src/public/avm/calldata.ts +3 -2
- package/src/public/avm/fixtures/account_proof.json +553 -0
- package/src/public/avm/fixtures/account_proof_fetcher.ts +166 -0
- 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/accrued_substate.ts +3 -4
- 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/avm/serialization/bytecode_serialization.ts +2 -2
- package/src/public/avm/serialization/instruction_serialization.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 +29 -4
- package/src/public/fixtures/custom_bytecode_tests.ts +139 -1
- package/src/public/fixtures/opcode_spammer.ts +10 -18
- package/src/public/fixtures/public_tx_simulation_tester.ts +47 -6
- package/src/public/fixtures/utils.ts +26 -22
- package/src/public/fuzzing/avm_fuzzer_simulator.ts +1 -1
- package/src/public/hinting_db_sources.ts +10 -7
- package/src/public/public_db_sources.ts +21 -14
- package/src/public/public_processor/guarded_merkle_tree.ts +6 -6
- package/src/public/public_processor/public_processor.ts +140 -91
- 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 +2 -3
- package/src/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.ts +2 -2
- package/src/public/public_tx_simulator/factories.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 +14 -10
- package/src/public/test_executor_metrics.ts +24 -0
|
@@ -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';
|
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
PublicProcessorValidator,
|
|
26
26
|
SequencerConfig,
|
|
27
27
|
} from '@aztec/stdlib/interfaces/server';
|
|
28
|
+
import { type DebugLog, type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
28
29
|
import { ProvingRequestType } from '@aztec/stdlib/proofs';
|
|
29
30
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
30
31
|
import {
|
|
@@ -75,17 +76,15 @@ export class PublicProcessorFactory {
|
|
|
75
76
|
/**
|
|
76
77
|
* Creates a new instance of a PublicProcessor.
|
|
77
78
|
* @param globalVariables - The global variables for the block being processed.
|
|
78
|
-
* @param
|
|
79
|
+
* @param contractsDB - Optional pre-populated contracts DB; a fresh one is constructed if omitted.
|
|
79
80
|
* @returns A new instance of a PublicProcessor.
|
|
80
81
|
*/
|
|
81
82
|
public create(
|
|
82
83
|
merkleTree: MerkleTreeWriteOperations,
|
|
83
84
|
globalVariables: GlobalVariables,
|
|
84
85
|
config: PublicSimulatorConfig,
|
|
86
|
+
contractsDB: PublicContractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings()),
|
|
85
87
|
): PublicProcessor {
|
|
86
|
-
const bindings = this.log.getBindings();
|
|
87
|
-
const contractsDB = new PublicContractsDB(this.contractDataSource, bindings);
|
|
88
|
-
|
|
89
88
|
const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
|
|
90
89
|
const publicTxSimulator = this.createPublicTxSimulator(guardedFork, contractsDB, globalVariables, config);
|
|
91
90
|
|
|
@@ -96,7 +95,7 @@ export class PublicProcessorFactory {
|
|
|
96
95
|
publicTxSimulator,
|
|
97
96
|
this.dateProvider,
|
|
98
97
|
this.telemetryClient,
|
|
99
|
-
createLogger('simulator:public-processor',
|
|
98
|
+
createLogger('simulator:public-processor', this.log.getBindings()),
|
|
100
99
|
);
|
|
101
100
|
}
|
|
102
101
|
|
|
@@ -124,13 +123,23 @@ class PublicProcessorTimeoutError extends Error {
|
|
|
124
123
|
}
|
|
125
124
|
}
|
|
126
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
|
+
|
|
127
137
|
/**
|
|
128
138
|
* Converts Txs lifted from the P2P module into ProcessedTx objects by executing
|
|
129
139
|
* any public function calls in them. Txs with private calls only are unaffected.
|
|
130
140
|
*/
|
|
131
141
|
export class PublicProcessor implements Traceable {
|
|
132
142
|
private metrics: PublicProcessorMetrics;
|
|
133
|
-
|
|
134
143
|
constructor(
|
|
135
144
|
protected globalVariables: GlobalVariables,
|
|
136
145
|
private guardedMerkleTree: GuardedMerkleTreeOperations,
|
|
@@ -140,6 +149,7 @@ export class PublicProcessor implements Traceable {
|
|
|
140
149
|
telemetryClient: TelemetryClient = getTelemetryClient(),
|
|
141
150
|
private log: Logger,
|
|
142
151
|
private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs' | 'fakeThrowAfterProcessingTxCount'> = {},
|
|
152
|
+
private debugLogStore: DebugLogStore = new NullDebugLogStore(),
|
|
143
153
|
) {
|
|
144
154
|
this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
|
|
145
155
|
}
|
|
@@ -159,12 +169,13 @@ export class PublicProcessor implements Traceable {
|
|
|
159
169
|
txs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
160
170
|
limits: PublicProcessorLimits = {},
|
|
161
171
|
validator: PublicProcessorValidator = {},
|
|
162
|
-
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[],
|
|
163
|
-
const { maxTransactions,
|
|
172
|
+
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
|
|
173
|
+
const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal, signal } = limits;
|
|
164
174
|
const { preprocessValidator, nullifierCache } = validator;
|
|
165
175
|
const result: ProcessedTx[] = [];
|
|
166
176
|
const usedTxs: Tx[] = [];
|
|
167
177
|
const failed: FailedTx[] = [];
|
|
178
|
+
const debugLogs: DebugLog[] = [];
|
|
168
179
|
const timer = new Timer();
|
|
169
180
|
|
|
170
181
|
let totalSizeInBytes = 0;
|
|
@@ -172,6 +183,8 @@ export class PublicProcessor implements Traceable {
|
|
|
172
183
|
let totalPublicGas = new Gas(0, 0);
|
|
173
184
|
let totalBlockGas = new Gas(0, 0);
|
|
174
185
|
let totalBlobFields = 0;
|
|
186
|
+
let silentlySkippedCount = 0;
|
|
187
|
+
let totalSilentlySkippedDurationMs = 0;
|
|
175
188
|
|
|
176
189
|
for await (const tx of txs) {
|
|
177
190
|
// Only process up to the max tx limit
|
|
@@ -180,28 +193,33 @@ export class PublicProcessor implements Traceable {
|
|
|
180
193
|
break;
|
|
181
194
|
}
|
|
182
195
|
|
|
183
|
-
// Bail if we've hit the deadline
|
|
196
|
+
// Bail if we've hit the deadline or have been interrupted.
|
|
184
197
|
if (deadline && this.dateProvider.now() > +deadline) {
|
|
185
198
|
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
186
199
|
break;
|
|
187
200
|
}
|
|
201
|
+
if (signal?.aborted) {
|
|
202
|
+
this.log.warn(`Stopping tx processing due to abort signal.`);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
188
205
|
|
|
189
|
-
// Skip this tx if it'd exceed max block size
|
|
190
206
|
const txHash = tx.getTxHash().toString();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
207
|
+
|
|
208
|
+
// Skip this tx if its estimated blob fields would exceed the limit.
|
|
209
|
+
// Only done during proposal building: during re-execution we must process the exact txs from the proposal.
|
|
210
|
+
const txBlobFields = tx.getPrivateTxEffectsSizeInFields();
|
|
211
|
+
if (isBuildingProposal && maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
|
|
212
|
+
this.log.warn(
|
|
213
|
+
`Skipping tx ${txHash} with ${txBlobFields} fields from private side effects due to blob fields limit`,
|
|
214
|
+
{ txHash, txBlobFields, totalBlobFields, maxBlobFields },
|
|
215
|
+
);
|
|
199
216
|
continue;
|
|
200
217
|
}
|
|
201
218
|
|
|
202
|
-
// Skip this tx if its gas limit would exceed the block gas limit
|
|
219
|
+
// Skip this tx if its gas limit would exceed the block gas limit (either da or l2).
|
|
220
|
+
// Only done during proposal building: during re-execution we must process the exact txs from the proposal.
|
|
203
221
|
const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
|
|
204
|
-
if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
222
|
+
if (isBuildingProposal && maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
205
223
|
this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
|
|
206
224
|
txHash,
|
|
207
225
|
txGasLimit,
|
|
@@ -221,11 +239,6 @@ export class PublicProcessor implements Traceable {
|
|
|
221
239
|
failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
|
|
222
240
|
returns.push(new NestedProcessReturnValues([]));
|
|
223
241
|
continue;
|
|
224
|
-
} else if (result.result === 'skipped') {
|
|
225
|
-
const reason = result.reason.join(', ');
|
|
226
|
-
this.log.debug(`Skipping tx ${txHash.toString()} due to pre-process validation: ${reason}`);
|
|
227
|
-
returns.push(new NestedProcessReturnValues([]));
|
|
228
|
-
continue;
|
|
229
242
|
} else {
|
|
230
243
|
this.log.trace(`Tx ${txHash.toString()} is valid before processing.`);
|
|
231
244
|
}
|
|
@@ -241,7 +254,9 @@ export class PublicProcessor implements Traceable {
|
|
|
241
254
|
this.contractsDB.createCheckpoint();
|
|
242
255
|
|
|
243
256
|
try {
|
|
244
|
-
const [processedTx, returnValues] = await
|
|
257
|
+
const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
|
|
258
|
+
this.processTx(tx, deadline, signal),
|
|
259
|
+
);
|
|
245
260
|
|
|
246
261
|
// Inject a fake processing failure after N txs if requested
|
|
247
262
|
const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
|
|
@@ -250,23 +265,9 @@ export class PublicProcessor implements Traceable {
|
|
|
250
265
|
}
|
|
251
266
|
|
|
252
267
|
const txBlobFields = processedTx.txEffect.getNumBlobFields();
|
|
253
|
-
|
|
254
|
-
// If the actual size of this tx would exceed block size, skip it
|
|
255
268
|
const txSize = txBlobFields * Fr.SIZE_IN_BYTES;
|
|
256
|
-
if (maxBlockSize !== undefined && totalSizeInBytes + txSize > maxBlockSize) {
|
|
257
|
-
this.log.debug(`Skipping processed tx ${txHash} sized ${txSize} due to max block size.`, {
|
|
258
|
-
txHash,
|
|
259
|
-
sizeInBytes: txSize,
|
|
260
|
-
totalSizeInBytes,
|
|
261
|
-
maxBlockSize,
|
|
262
|
-
});
|
|
263
|
-
// Need to revert the checkpoint here and don't go any further
|
|
264
|
-
await checkpoint.revert();
|
|
265
|
-
this.contractsDB.revertCheckpoint();
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
269
|
|
|
269
|
-
// If the actual blob fields of this tx would exceed the limit, skip it
|
|
270
|
+
// If the actual blob fields of this tx would exceed the limit, skip it.
|
|
270
271
|
// Note: maxBlobFields already accounts for block end blob fields and previous blocks in checkpoint.
|
|
271
272
|
if (maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
|
|
272
273
|
this.log.debug(
|
|
@@ -276,35 +277,60 @@ export class PublicProcessor implements Traceable {
|
|
|
276
277
|
txBlobFields,
|
|
277
278
|
totalBlobFields,
|
|
278
279
|
maxBlobFields,
|
|
280
|
+
txProcessingTimeMs,
|
|
279
281
|
},
|
|
280
282
|
);
|
|
283
|
+
silentlySkippedCount += 1;
|
|
284
|
+
totalSilentlySkippedDurationMs += txProcessingTimeMs;
|
|
285
|
+
this.metrics.recordSilentlySkipped(txProcessingTimeMs);
|
|
281
286
|
// Need to revert the checkpoint here and don't go any further
|
|
282
287
|
await checkpoint.revert();
|
|
283
288
|
this.contractsDB.revertCheckpoint();
|
|
284
289
|
continue;
|
|
285
290
|
}
|
|
286
291
|
|
|
292
|
+
// During re-execution, check if the actual gas used by this tx would push the block over the gas limit.
|
|
293
|
+
// Unlike the proposal-building check (which uses declared gas limits pessimistically before processing),
|
|
294
|
+
// this uses actual gas and stops processing when the limit is exceeded.
|
|
295
|
+
if (
|
|
296
|
+
!isBuildingProposal &&
|
|
297
|
+
maxBlockGas !== undefined &&
|
|
298
|
+
totalBlockGas.add(processedTx.gasUsed.totalGas).gtAny(maxBlockGas)
|
|
299
|
+
) {
|
|
300
|
+
this.log.warn(`Stopping re-execution since tx ${txHash} would push block gas over limit`, {
|
|
301
|
+
txHash,
|
|
302
|
+
txGas: processedTx.gasUsed.totalGas,
|
|
303
|
+
totalBlockGas,
|
|
304
|
+
maxBlockGas,
|
|
305
|
+
});
|
|
306
|
+
await checkpoint.revert();
|
|
307
|
+
this.contractsDB.revertCheckpoint();
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
|
|
287
311
|
// FIXME(fcarreiro): it's ugly to have to notify the validator of nullifiers.
|
|
288
312
|
// I'd rather pass the validators the processedTx as well and let them deal with it.
|
|
289
313
|
nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
|
|
290
314
|
result.push(processedTx);
|
|
291
315
|
usedTxs.push(tx);
|
|
292
316
|
returns = returns.concat(returnValues);
|
|
317
|
+
debugLogs.push(...txDebugLogs);
|
|
318
|
+
|
|
319
|
+
this.debugLogStore.storeLogs(processedTx.hash.toString(), txDebugLogs);
|
|
293
320
|
|
|
294
321
|
totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
|
|
295
322
|
totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
|
|
296
323
|
totalSizeInBytes += txSize;
|
|
297
324
|
totalBlobFields += txBlobFields;
|
|
325
|
+
|
|
326
|
+
// Commit the tx-level contracts checkpoint on success
|
|
327
|
+
this.contractsDB.commitCheckpoint();
|
|
298
328
|
} catch (err: any) {
|
|
299
|
-
if (err
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
//
|
|
303
|
-
// Signal cancellation AND WAIT for the simulation to actually stop.
|
|
304
|
-
// This is critical because C++ might be in the middle of a slow operation (e.g., pad_trees)
|
|
305
|
-
// and won't check the cancellation flag until that operation completes.
|
|
306
|
-
// Without waiting, we'd proceed to revert checkpoints while C++ is still writing to state.
|
|
307
|
-
// 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.
|
|
308
334
|
await this.publicTxSimulator.cancel?.();
|
|
309
335
|
|
|
310
336
|
// Now stop the guarded fork to prevent any further TS-side access to the world state.
|
|
@@ -314,14 +340,10 @@ export class PublicProcessor implements Traceable {
|
|
|
314
340
|
// 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
|
|
315
341
|
// 2. Possible state updates on that checkpoint or any others created during execution.
|
|
316
342
|
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
await checkpoint.
|
|
321
|
-
|
|
322
|
-
// Now we want to revert any/all remaining checkpoints, destroying any outstanding state updates.
|
|
323
|
-
// This needs to be done directly on the underlying fork as the guarded fork has been stopped.
|
|
324
|
-
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();
|
|
325
347
|
|
|
326
348
|
// Revert any contracts added to the DB for the tx.
|
|
327
349
|
this.contractsDB.revertCheckpoint();
|
|
@@ -333,9 +355,9 @@ export class PublicProcessor implements Traceable {
|
|
|
333
355
|
break;
|
|
334
356
|
}
|
|
335
357
|
|
|
336
|
-
// Roll back state to start of TX before proceeding to next TX
|
|
337
|
-
|
|
338
|
-
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();
|
|
339
361
|
this.contractsDB.revertCheckpoint();
|
|
340
362
|
const errorMessage = err instanceof Error || err instanceof AssertionError ? err.message : 'Unknown error';
|
|
341
363
|
this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
|
|
@@ -347,7 +369,6 @@ export class PublicProcessor implements Traceable {
|
|
|
347
369
|
} finally {
|
|
348
370
|
// Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
|
|
349
371
|
await checkpoint.commit();
|
|
350
|
-
this.contractsDB.commitCheckpointOkIfNone();
|
|
351
372
|
}
|
|
352
373
|
}
|
|
353
374
|
|
|
@@ -355,15 +376,26 @@ export class PublicProcessor implements Traceable {
|
|
|
355
376
|
const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
|
|
356
377
|
this.metrics.recordAllTxs(totalPublicGas, rate);
|
|
357
378
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
+
);
|
|
365
397
|
|
|
366
|
-
return [result, failed, usedTxs, returns,
|
|
398
|
+
return [result, failed, usedTxs, returns, debugLogs];
|
|
367
399
|
}
|
|
368
400
|
|
|
369
401
|
private async checkWorldStateUnchanged(
|
|
@@ -383,8 +415,14 @@ export class PublicProcessor implements Traceable {
|
|
|
383
415
|
}
|
|
384
416
|
|
|
385
417
|
@trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString() }))
|
|
386
|
-
private async processTx(
|
|
387
|
-
|
|
418
|
+
private async processTx(
|
|
419
|
+
tx: Tx,
|
|
420
|
+
deadline: Date | undefined,
|
|
421
|
+
signal: AbortSignal | undefined,
|
|
422
|
+
): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
|
|
423
|
+
const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
|
|
424
|
+
this.processTxWithinDeadline(tx, deadline, signal),
|
|
425
|
+
);
|
|
388
426
|
|
|
389
427
|
this.log.verbose(
|
|
390
428
|
!tx.hasPublicCalls()
|
|
@@ -407,7 +445,7 @@ export class PublicProcessor implements Traceable {
|
|
|
407
445
|
},
|
|
408
446
|
);
|
|
409
447
|
|
|
410
|
-
return [processedTx, returnValues ?? []];
|
|
448
|
+
return [processedTx, returnValues ?? [], debugLogs];
|
|
411
449
|
}
|
|
412
450
|
|
|
413
451
|
private async doTreeInsertionsForPrivateOnlyTx(processedTx: ProcessedTx): Promise<void> {
|
|
@@ -437,14 +475,14 @@ export class PublicProcessor implements Traceable {
|
|
|
437
475
|
this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
|
|
438
476
|
}
|
|
439
477
|
|
|
440
|
-
/** Processes the given tx within deadline
|
|
478
|
+
/** Processes the given tx within deadline or until the signal is aborted. */
|
|
441
479
|
private async processTxWithinDeadline(
|
|
442
480
|
tx: Tx,
|
|
443
481
|
deadline: Date | undefined,
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
: () => this.processPrivateOnlyTx(tx);
|
|
482
|
+
signal: AbortSignal | undefined,
|
|
483
|
+
): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
|
|
484
|
+
const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
|
|
485
|
+
tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
|
|
448
486
|
|
|
449
487
|
// Fake a delay per tx if instructed (used for tests)
|
|
450
488
|
const fakeDelayPerTxMs = this.opts.fakeProcessingDelayPerTxMs;
|
|
@@ -458,27 +496,38 @@ export class PublicProcessor implements Traceable {
|
|
|
458
496
|
}
|
|
459
497
|
: innerProcessFn;
|
|
460
498
|
|
|
461
|
-
|
|
499
|
+
const processingSignal = this.getProcessingSignal(tx, deadline, signal);
|
|
500
|
+
if (!processingSignal) {
|
|
462
501
|
return await processFn();
|
|
463
502
|
}
|
|
464
503
|
|
|
465
|
-
|
|
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
|
+
|
|
466
517
|
const timeout = +deadline - this.dateProvider.now();
|
|
467
518
|
if (timeout <= 0) {
|
|
468
519
|
throw new PublicProcessorTimeoutError();
|
|
469
520
|
}
|
|
470
521
|
|
|
522
|
+
const txHash = tx.getTxHash();
|
|
471
523
|
this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, {
|
|
472
524
|
deadline: deadline.toISOString(),
|
|
473
525
|
now: new Date(this.dateProvider.now()).toISOString(),
|
|
474
526
|
txHash,
|
|
475
527
|
});
|
|
476
528
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
timeout,
|
|
480
|
-
() => new PublicProcessorTimeoutError(),
|
|
481
|
-
);
|
|
529
|
+
const timeoutSignal = AbortSignal.timeout(timeout);
|
|
530
|
+
return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
482
531
|
}
|
|
483
532
|
|
|
484
533
|
/**
|
|
@@ -512,7 +561,7 @@ export class PublicProcessor implements Traceable {
|
|
|
512
561
|
@trackSpan('PublicProcessor.processPrivateOnlyTx', (tx: Tx) => ({
|
|
513
562
|
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
514
563
|
}))
|
|
515
|
-
private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined]> {
|
|
564
|
+
private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined, DebugLog[]]> {
|
|
516
565
|
const gasFees = this.globalVariables.gasFees;
|
|
517
566
|
const transactionFee = computeTransactionFee(gasFees, tx.data.constants.txContext.gasSettings, tx.data.gasUsed);
|
|
518
567
|
|
|
@@ -535,15 +584,15 @@ export class PublicProcessor implements Traceable {
|
|
|
535
584
|
// Fee payment insertion has already been done. Do the rest.
|
|
536
585
|
await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
|
|
537
586
|
|
|
538
|
-
|
|
587
|
+
this.contractsDB.addNewContracts(tx);
|
|
539
588
|
|
|
540
|
-
return [processedTx, undefined];
|
|
589
|
+
return [processedTx, undefined, []];
|
|
541
590
|
}
|
|
542
591
|
|
|
543
592
|
@trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({
|
|
544
593
|
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
545
594
|
}))
|
|
546
|
-
private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
|
|
595
|
+
private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
|
|
547
596
|
const timer = new Timer();
|
|
548
597
|
|
|
549
598
|
const result = await this.publicTxSimulator.simulate(tx);
|
|
@@ -581,7 +630,7 @@ export class PublicProcessor implements Traceable {
|
|
|
581
630
|
revertReason,
|
|
582
631
|
);
|
|
583
632
|
|
|
584
|
-
return [processedTx, appLogicReturnValues];
|
|
633
|
+
return [processedTx, appLogicReturnValues, result.logs ?? []];
|
|
585
634
|
}
|
|
586
635
|
|
|
587
636
|
/**
|
|
@@ -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> => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Logger, type LoggerBindings, createLogger
|
|
1
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { sleep } from '@aztec/foundation/sleep';
|
|
3
3
|
import { type CancellationToken, avmSimulate, cancelSimulation, createCancellationToken } from '@aztec/native';
|
|
4
4
|
import { ProtocolContractsList } from '@aztec/protocol-contracts';
|
|
@@ -100,8 +100,7 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
|
|
|
100
100
|
inputBuffer,
|
|
101
101
|
contractProvider,
|
|
102
102
|
wsCppHandle,
|
|
103
|
-
|
|
104
|
-
// TODO: re-enable logging
|
|
103
|
+
this.log.level,
|
|
105
104
|
undefined,
|
|
106
105
|
this.cancellationToken,
|
|
107
106
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Logger, type LoggerBindings, createLogger
|
|
1
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { avmSimulateWithHintedDbs } from '@aztec/native';
|
|
3
3
|
import {
|
|
4
4
|
AvmCircuitInputs,
|
|
@@ -75,7 +75,7 @@ export class CppPublicTxSimulatorHintedDbs extends PublicTxSimulator implements
|
|
|
75
75
|
|
|
76
76
|
let resultBuffer: Buffer;
|
|
77
77
|
try {
|
|
78
|
-
resultBuffer = await avmSimulateWithHintedDbs(inputBuffer,
|
|
78
|
+
resultBuffer = await avmSimulateWithHintedDbs(inputBuffer, this.log.level);
|
|
79
79
|
} catch (error: any) {
|
|
80
80
|
throw new SimulationError(`C++ hinted simulation failed: ${error.message}`, []);
|
|
81
81
|
}
|
|
@@ -19,10 +19,11 @@ export function createPublicTxSimulatorForBlockBuilding(
|
|
|
19
19
|
globalVariables: GlobalVariables,
|
|
20
20
|
telemetryClient: TelemetryClient,
|
|
21
21
|
bindings?: LoggerBindings,
|
|
22
|
+
collectDebugLogs = false,
|
|
22
23
|
) {
|
|
23
24
|
const config = PublicSimulatorConfig.from({
|
|
24
25
|
skipFeeEnforcement: false,
|
|
25
|
-
collectDebugLogs
|
|
26
|
+
collectDebugLogs,
|
|
26
27
|
collectHints: false,
|
|
27
28
|
collectPublicInputs: false,
|
|
28
29
|
collectStatistics: false,
|
|
@@ -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);
|