@aztec/simulator 3.0.0-rc.5 → 4.0.0-nightly.20260107
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.d.ts +1 -1
- package/dest/private/circuit_recording/circuit_recorder.d.ts.map +1 -1
- package/dest/private/circuit_recording/circuit_recorder.js +15 -14
- package/dest/public/avm/opcodes/accrued_substate.js +1 -1
- package/dest/public/avm/opcodes/external_calls.d.ts +1 -1
- package/dest/public/avm/opcodes/external_calls.d.ts.map +1 -1
- package/dest/public/avm/opcodes/external_calls.js +1 -0
- package/dest/public/avm/opcodes/hashing.d.ts +1 -1
- package/dest/public/avm/opcodes/hashing.d.ts.map +1 -1
- package/dest/public/avm/opcodes/hashing.js +6 -3
- package/dest/public/debug_fn_name.d.ts +1 -1
- package/dest/public/debug_fn_name.d.ts.map +1 -1
- package/dest/public/debug_fn_name.js +10 -3
- package/dest/public/fixtures/bulk_test.js +3 -51
- package/dest/public/fixtures/custom_bytecode_tester.d.ts +28 -6
- package/dest/public/fixtures/custom_bytecode_tester.d.ts.map +1 -1
- package/dest/public/fixtures/custom_bytecode_tester.js +36 -12
- 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 +54 -10
- package/dest/public/fixtures/index.d.ts +4 -2
- package/dest/public/fixtures/index.d.ts.map +1 -1
- package/dest/public/fixtures/index.js +3 -1
- package/dest/public/fixtures/minimal_public_tx.d.ts +2 -7
- package/dest/public/fixtures/minimal_public_tx.d.ts.map +1 -1
- package/dest/public/fixtures/minimal_public_tx.js +2 -12
- package/dest/public/fixtures/opcode_spammer.d.ts +123 -0
- package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -0
- package/dest/public/fixtures/opcode_spammer.js +1681 -0
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts +15 -2
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
- package/dest/public/fixtures/public_tx_simulation_tester.js +34 -7
- package/dest/public/fixtures/utils.d.ts +1 -1
- package/dest/public/fixtures/utils.d.ts.map +1 -1
- package/dest/public/fixtures/utils.js +3 -2
- package/dest/public/fuzzing/avm_fuzzer_simulator.d.ts +1 -1
- package/dest/public/fuzzing/avm_fuzzer_simulator.d.ts.map +1 -1
- package/dest/public/fuzzing/avm_fuzzer_simulator.js +4 -1
- package/dest/public/fuzzing/avm_simulator_bin.js +18 -2
- package/dest/public/hinting_db_sources.d.ts +2 -1
- package/dest/public/hinting_db_sources.d.ts.map +1 -1
- package/dest/public/hinting_db_sources.js +5 -0
- package/dest/public/public_processor/guarded_merkle_tree.d.ts +2 -1
- package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
- package/dest/public/public_processor/guarded_merkle_tree.js +5 -0
- 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 +417 -28
- 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 +15 -11
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts +16 -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 +41 -3
- package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.d.ts +1 -1
- package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.js +3 -2
- package/dest/public/public_tx_simulator/public_tx_simulator_interface.d.ts +24 -1
- package/dest/public/public_tx_simulator/public_tx_simulator_interface.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/telemetry_public_tx_simulator.js +395 -19
- package/package.json +16 -16
- package/src/private/circuit_recording/circuit_recorder.ts +16 -15
- package/src/public/avm/opcodes/accrued_substate.ts +1 -1
- package/src/public/avm/opcodes/external_calls.ts +1 -0
- package/src/public/avm/opcodes/hashing.ts +7 -3
- package/src/public/debug_fn_name.ts +10 -3
- package/src/public/fixtures/bulk_test.ts +6 -6
- package/src/public/fixtures/custom_bytecode_tester.ts +53 -19
- package/src/public/fixtures/custom_bytecode_tests.ts +70 -10
- package/src/public/fixtures/index.ts +7 -1
- package/src/public/fixtures/minimal_public_tx.ts +3 -12
- package/src/public/fixtures/opcode_spammer.ts +1638 -0
- package/src/public/fixtures/public_tx_simulation_tester.ts +38 -5
- package/src/public/fixtures/utils.ts +1 -2
- package/src/public/fuzzing/avm_fuzzer_simulator.ts +8 -1
- package/src/public/fuzzing/avm_simulator_bin.ts +21 -2
- package/src/public/hinting_db_sources.ts +4 -0
- package/src/public/public_processor/guarded_merkle_tree.ts +4 -0
- package/src/public/public_processor/public_processor.ts +20 -9
- package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +16 -11
- package/src/public/public_tx_simulator/cpp_public_tx_simulator.ts +48 -3
- package/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts +3 -2
- package/src/public/public_tx_simulator/public_tx_simulator_interface.ts +23 -0
|
@@ -31,7 +31,7 @@ const DEFAULT_GAS_FEES = new GasFees(2, 3);
|
|
|
31
31
|
export type TestEnqueuedCall = {
|
|
32
32
|
sender?: AztecAddress;
|
|
33
33
|
address: AztecAddress;
|
|
34
|
-
fnName
|
|
34
|
+
fnName?: string;
|
|
35
35
|
args: any[];
|
|
36
36
|
isStaticCall?: boolean;
|
|
37
37
|
contractArtifact?: ContractArtifact;
|
|
@@ -225,6 +225,25 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
|
|
|
225
225
|
this.metrics.prettyPrint();
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Cancel the current simulation if one is in progress.
|
|
230
|
+
* This signals the underlying simulator (e.g., C++) to stop at the next safe point.
|
|
231
|
+
* Safe to call even if no simulation is in progress.
|
|
232
|
+
*
|
|
233
|
+
* @param waitTimeoutMs - If provided, wait up to this many ms for the simulation to actually stop.
|
|
234
|
+
*/
|
|
235
|
+
public async cancel(waitTimeoutMs?: number): Promise<void> {
|
|
236
|
+
await this.simulator.cancel?.(waitTimeoutMs);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get the underlying simulator for advanced test scenarios.
|
|
241
|
+
* Use this when you need direct control over simulation (e.g., for testing cancellation).
|
|
242
|
+
*/
|
|
243
|
+
public getSimulator(): MeasuredPublicTxSimulatorInterface {
|
|
244
|
+
return this.simulator;
|
|
245
|
+
}
|
|
246
|
+
|
|
228
247
|
async #createPubicCallRequestForCall(
|
|
229
248
|
call: TestEnqueuedCall,
|
|
230
249
|
sender: AztecAddress,
|
|
@@ -235,10 +254,24 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
|
|
|
235
254
|
throw new Error(`Contract artifact not found for address: ${address}`);
|
|
236
255
|
}
|
|
237
256
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
257
|
+
let calldata: Fr[] = [];
|
|
258
|
+
if (!call.fnName) {
|
|
259
|
+
this.logger.debug(
|
|
260
|
+
`No function name specified for call to contract ${call.address.toString()}. Assuming this is a custom bytecode with no public_dispatch function.`,
|
|
261
|
+
);
|
|
262
|
+
this.logger.debug(`Not using ABI to encode arguments. Not prepending fn selector to calldata.`);
|
|
263
|
+
try {
|
|
264
|
+
calldata = call.args.map(arg => new Fr(arg));
|
|
265
|
+
} catch (error) {
|
|
266
|
+
this.logger.warn(`Tried assuming that all arguments are Field-like. Failed. Error: ${error}`);
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
const fnSelector = await getFunctionSelector(call.fnName, contractArtifact);
|
|
271
|
+
const fnAbi = getContractFunctionAbi(call.fnName, contractArtifact)!;
|
|
272
|
+
const encodedArgs = encodeArguments(fnAbi, call.args);
|
|
273
|
+
calldata = [fnSelector.toField(), ...encodedArgs];
|
|
274
|
+
}
|
|
242
275
|
const isStaticCall = call.isStaticCall ?? false;
|
|
243
276
|
const request = await PublicCallRequest.fromCalldata(sender, address, isStaticCall, calldata);
|
|
244
277
|
|
|
@@ -132,8 +132,7 @@ export async function createTxForPublicCalls(
|
|
|
132
132
|
: Gas.empty();
|
|
133
133
|
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, maxFeesPerGas, GasFees.empty());
|
|
134
134
|
const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
|
|
135
|
-
const header = BlockHeader.empty();
|
|
136
|
-
header.globalVariables = globals;
|
|
135
|
+
const header = BlockHeader.empty({ globalVariables: globals });
|
|
137
136
|
const constantData = new TxConstantData(header, txContext, Fr.zero(), Fr.zero());
|
|
138
137
|
const includeByTimestamp = 0n; // Not used in the simulator.
|
|
139
138
|
|
|
@@ -185,7 +185,7 @@ export class AvmFuzzerSimulator extends BaseAvmSimulationTester {
|
|
|
185
185
|
super(contractDataSource, merkleTrees);
|
|
186
186
|
const contractsDb = new PublicContractsDB(contractDataSource);
|
|
187
187
|
this.simulator = new PublicTxSimulator(merkleTrees, contractsDb, globals, {
|
|
188
|
-
skipFeeEnforcement:
|
|
188
|
+
skipFeeEnforcement: false,
|
|
189
189
|
collectDebugLogs: false,
|
|
190
190
|
collectHints: false,
|
|
191
191
|
collectStatistics: false,
|
|
@@ -209,6 +209,13 @@ export class AvmFuzzerSimulator extends BaseAvmSimulationTester {
|
|
|
209
209
|
* Simulate a transaction from a C++ AvmTxHint.
|
|
210
210
|
*/
|
|
211
211
|
public async simulate(txHint: AvmTxHint): Promise<PublicTxResult> {
|
|
212
|
+
// Compute fee from gas limits and max fees per gas (upper bound on fee)
|
|
213
|
+
const totalFee =
|
|
214
|
+
BigInt(txHint.gasSettings.gasLimits.daGas) * txHint.gasSettings.maxFeesPerGas.feePerDaGas +
|
|
215
|
+
BigInt(txHint.gasSettings.gasLimits.l2Gas) * txHint.gasSettings.maxFeesPerGas.feePerL2Gas;
|
|
216
|
+
|
|
217
|
+
await this.setFeePayerBalance(txHint.feePayer, new Fr(totalFee));
|
|
218
|
+
|
|
212
219
|
const tx = await createTxFromHint(txHint);
|
|
213
220
|
return await this.simulator.simulate(tx);
|
|
214
221
|
}
|
|
@@ -98,6 +98,7 @@ async function execute(base64Line: string): Promise<void> {
|
|
|
98
98
|
});
|
|
99
99
|
writeSync(process.stdout.fd, resultBuffer.toString('base64') + '\n');
|
|
100
100
|
} catch (error: any) {
|
|
101
|
+
// If we error, treat as reverted
|
|
101
102
|
const errorResult = serializeWithMessagePack({
|
|
102
103
|
reverted: true,
|
|
103
104
|
output: [] as string[],
|
|
@@ -110,12 +111,30 @@ async function execute(base64Line: string): Promise<void> {
|
|
|
110
111
|
|
|
111
112
|
function mainLoop() {
|
|
112
113
|
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
114
|
+
|
|
115
|
+
// Process lines sequentially to avoid race conditions in responses
|
|
116
|
+
const lineQueue: string[] = [];
|
|
117
|
+
let processing = false;
|
|
118
|
+
|
|
119
|
+
async function processQueue() {
|
|
120
|
+
if (processing || lineQueue.length === 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
processing = true;
|
|
124
|
+
while (lineQueue.length > 0) {
|
|
125
|
+
const line = lineQueue.shift()!;
|
|
126
|
+
await execute(line);
|
|
127
|
+
}
|
|
128
|
+
processing = false;
|
|
129
|
+
}
|
|
130
|
+
|
|
113
131
|
rl.on('line', (line: string) => {
|
|
114
132
|
if (line.trim()) {
|
|
115
|
-
|
|
133
|
+
lineQueue.push(line);
|
|
134
|
+
void processQueue();
|
|
116
135
|
}
|
|
117
136
|
});
|
|
118
137
|
rl.on('close', () => process.exit(0));
|
|
119
138
|
}
|
|
120
139
|
|
|
121
|
-
mainLoop();
|
|
140
|
+
void mainLoop();
|
|
@@ -572,6 +572,10 @@ export class HintingMerkleWriteOperations implements MerkleTreeWriteOperations {
|
|
|
572
572
|
return await this.db.close();
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
+
async [Symbol.dispose](): Promise<void> {
|
|
576
|
+
await this.close();
|
|
577
|
+
}
|
|
578
|
+
|
|
575
579
|
public async findLeafIndices<ID extends MerkleTreeId>(
|
|
576
580
|
treeId: ID,
|
|
577
581
|
values: MerkleTreeLeafType<ID>[],
|
|
@@ -81,6 +81,10 @@ export class GuardedMerkleTreeOperations implements MerkleTreeWriteOperations {
|
|
|
81
81
|
close(): Promise<void> {
|
|
82
82
|
return this.guardAndPush(() => this.target.close());
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
async [Symbol.dispose](): Promise<void> {
|
|
86
|
+
await this.close();
|
|
87
|
+
}
|
|
84
88
|
getTreeInfo(treeId: MerkleTreeId): Promise<TreeInfo> {
|
|
85
89
|
return this.guardAndPush(() => this.target.getTreeInfo(treeId));
|
|
86
90
|
}
|
|
@@ -126,7 +126,7 @@ export class PublicProcessor implements Traceable {
|
|
|
126
126
|
private dateProvider: DateProvider,
|
|
127
127
|
telemetryClient: TelemetryClient = getTelemetryClient(),
|
|
128
128
|
private log = createLogger('simulator:public-processor'),
|
|
129
|
-
private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs'> = {},
|
|
129
|
+
private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs' | 'fakeThrowAfterProcessingTxCount'> = {},
|
|
130
130
|
) {
|
|
131
131
|
this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
|
|
132
132
|
}
|
|
@@ -160,7 +160,7 @@ export class PublicProcessor implements Traceable {
|
|
|
160
160
|
let totalBlockGas = new Gas(0, 0);
|
|
161
161
|
let totalBlobFields = 0;
|
|
162
162
|
|
|
163
|
-
for await (const
|
|
163
|
+
for await (const tx of txs) {
|
|
164
164
|
// Only process up to the max tx limit
|
|
165
165
|
if (maxTransactions !== undefined && result.length >= maxTransactions) {
|
|
166
166
|
this.log.debug(`Stopping tx processing due to reaching the max tx limit.`);
|
|
@@ -174,8 +174,8 @@ export class PublicProcessor implements Traceable {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// Skip this tx if it'd exceed max block size
|
|
177
|
-
const txHash =
|
|
178
|
-
const preTxSizeInBytes =
|
|
177
|
+
const txHash = tx.getTxHash().toString();
|
|
178
|
+
const preTxSizeInBytes = tx.getEstimatedPrivateTxEffectsSize();
|
|
179
179
|
if (maxBlockSize !== undefined && totalSizeInBytes + preTxSizeInBytes > maxBlockSize) {
|
|
180
180
|
this.log.warn(`Skipping processing of tx ${txHash} sized ${preTxSizeInBytes} bytes due to block size limit`, {
|
|
181
181
|
txHash,
|
|
@@ -187,7 +187,7 @@ export class PublicProcessor implements Traceable {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// Skip this tx if its gas limit would exceed the block gas limit
|
|
190
|
-
const txGasLimit =
|
|
190
|
+
const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
|
|
191
191
|
if (maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
|
|
192
192
|
this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
|
|
193
193
|
txHash,
|
|
@@ -198,9 +198,6 @@ export class PublicProcessor implements Traceable {
|
|
|
198
198
|
continue;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
// The processor modifies the tx objects in place, so we need to clone them.
|
|
202
|
-
const tx = Tx.clone(origTx);
|
|
203
|
-
|
|
204
201
|
// We validate the tx before processing it, to avoid unnecessary work.
|
|
205
202
|
if (preprocessValidator) {
|
|
206
203
|
const result = await preprocessValidator.validateTx(tx);
|
|
@@ -233,6 +230,12 @@ export class PublicProcessor implements Traceable {
|
|
|
233
230
|
try {
|
|
234
231
|
const [processedTx, returnValues] = await this.processTx(tx, deadline);
|
|
235
232
|
|
|
233
|
+
// Inject a fake processing failure after N txs if requested
|
|
234
|
+
const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
|
|
235
|
+
if (fakeThrowAfter !== undefined && result.length + failed.length + 1 >= fakeThrowAfter) {
|
|
236
|
+
throw new Error(`Fake error after processing ${fakeThrowAfter} txs`);
|
|
237
|
+
}
|
|
238
|
+
|
|
236
239
|
const txBlobFields = processedTx.txEffect.getNumBlobFields();
|
|
237
240
|
|
|
238
241
|
// If the actual size of this tx would exceed block size, skip it
|
|
@@ -282,7 +285,15 @@ export class PublicProcessor implements Traceable {
|
|
|
282
285
|
if (err?.name === 'PublicProcessorTimeoutError') {
|
|
283
286
|
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
284
287
|
// We hit the transaction execution deadline.
|
|
285
|
-
// There may still be a transaction executing
|
|
288
|
+
// There may still be a transaction executing on a worker thread (C++ via NAPI).
|
|
289
|
+
// Signal cancellation AND WAIT for the simulation to actually stop.
|
|
290
|
+
// This is critical because C++ might be in the middle of a slow operation (e.g., pad_trees)
|
|
291
|
+
// and won't check the cancellation flag until that operation completes.
|
|
292
|
+
// Without waiting, we'd proceed to revert checkpoints while C++ is still writing to state.
|
|
293
|
+
// Wait for C++ to stop gracefully.
|
|
294
|
+
await this.publicTxSimulator.cancel?.();
|
|
295
|
+
|
|
296
|
+
// Now stop the guarded fork to prevent any further TS-side access to the world state.
|
|
286
297
|
await this.guardedMerkleTree.stop();
|
|
287
298
|
|
|
288
299
|
// We now know there can't be any further access to world state. The fork is in a state where there is:
|
|
@@ -18,7 +18,7 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
18
18
|
) {}
|
|
19
19
|
|
|
20
20
|
public getContractInstance = async (address: string): Promise<Buffer | undefined> => {
|
|
21
|
-
this.log.
|
|
21
|
+
this.log.trace(`Contract provider callback: getContractInstance(${address})`);
|
|
22
22
|
|
|
23
23
|
const aztecAddr = AztecAddress.fromString(address);
|
|
24
24
|
|
|
@@ -33,7 +33,7 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
public getContractClass = async (classId: string): Promise<Buffer | undefined> => {
|
|
36
|
-
this.log.
|
|
36
|
+
this.log.trace(`Contract provider callback: getContractClass(${classId})`);
|
|
37
37
|
|
|
38
38
|
// Parse classId string to Fr
|
|
39
39
|
const classIdFr = Fr.fromString(classId);
|
|
@@ -50,7 +50,7 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
public addContracts = async (contractDeploymentDataBuffer: Buffer): Promise<void> => {
|
|
53
|
-
this.log.
|
|
53
|
+
this.log.trace(`Contract provider callback: addContracts`);
|
|
54
54
|
|
|
55
55
|
const rawData: any = deserializeFromMessagePack(contractDeploymentDataBuffer);
|
|
56
56
|
|
|
@@ -58,12 +58,12 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
58
58
|
const contractDeploymentData = ContractDeploymentData.fromPlainObject(rawData);
|
|
59
59
|
|
|
60
60
|
// Add contracts to the contracts DB
|
|
61
|
-
this.log.
|
|
61
|
+
this.log.trace(`Calling contractsDB.addContracts`);
|
|
62
62
|
await this.contractsDB.addContracts(contractDeploymentData);
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
public getBytecodeCommitment = async (classId: string): Promise<Buffer | undefined> => {
|
|
66
|
-
this.log.
|
|
66
|
+
this.log.trace(`Contract provider callback: getBytecodeCommitment(${classId})`);
|
|
67
67
|
|
|
68
68
|
// Parse classId string to Fr
|
|
69
69
|
const classIdFr = Fr.fromString(classId);
|
|
@@ -81,18 +81,23 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
public getDebugFunctionName = async (address: string, selector: string): Promise<string | undefined> => {
|
|
84
|
-
this.log.
|
|
84
|
+
this.log.trace(`Contract provider callback: getDebugFunctionName(${address}, ${selector})`);
|
|
85
85
|
|
|
86
86
|
// Parse address and selector strings
|
|
87
87
|
const aztecAddr = AztecAddress.fromString(address);
|
|
88
88
|
const selectorFr = Fr.fromString(selector);
|
|
89
|
-
const functionSelector = FunctionSelector.
|
|
89
|
+
const functionSelector = FunctionSelector.fromFieldOrUndefined(selectorFr);
|
|
90
|
+
|
|
91
|
+
if (!functionSelector) {
|
|
92
|
+
this.log.trace(`calldata[0] is not a function selector: ${selector}`);
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
90
95
|
|
|
91
96
|
// Fetch debug function name from the contracts DB
|
|
92
97
|
const name = await this.contractsDB.getDebugFunctionName(aztecAddr, functionSelector);
|
|
93
98
|
|
|
94
99
|
if (!name) {
|
|
95
|
-
this.log.
|
|
100
|
+
this.log.trace(`Debug function name not found for ${address}:${selector}`);
|
|
96
101
|
return undefined;
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -100,17 +105,17 @@ export class ContractProviderForCpp implements ContractProvider {
|
|
|
100
105
|
};
|
|
101
106
|
|
|
102
107
|
public createCheckpoint = (): Promise<void> => {
|
|
103
|
-
this.log.
|
|
108
|
+
this.log.trace(`Contract provider callback: createCheckpoint`);
|
|
104
109
|
return Promise.resolve(this.contractsDB.createCheckpoint());
|
|
105
110
|
};
|
|
106
111
|
|
|
107
112
|
public commitCheckpoint = (): Promise<void> => {
|
|
108
|
-
this.log.
|
|
113
|
+
this.log.trace(`Contract provider callback: commitCheckpoint`);
|
|
109
114
|
return Promise.resolve(this.contractsDB.commitCheckpoint());
|
|
110
115
|
};
|
|
111
116
|
|
|
112
117
|
public revertCheckpoint = (): Promise<void> => {
|
|
113
|
-
this.log.
|
|
118
|
+
this.log.trace(`Contract provider callback: revertCheckpoint`);
|
|
114
119
|
return Promise.resolve(this.contractsDB.revertCheckpoint());
|
|
115
120
|
};
|
|
116
121
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Logger, createLogger, logLevel } from '@aztec/foundation/log';
|
|
2
|
-
import {
|
|
2
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
3
|
+
import { type CancellationToken, avmSimulate, cancelSimulation, createCancellationToken } from '@aztec/native';
|
|
3
4
|
import { ProtocolContractsList } from '@aztec/protocol-contracts';
|
|
4
5
|
import {
|
|
5
6
|
AvmFastSimulationInputs,
|
|
@@ -33,6 +34,10 @@ import type {
|
|
|
33
34
|
*/
|
|
34
35
|
export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
35
36
|
protected override log: Logger;
|
|
37
|
+
/** Current cancellation token for in-flight simulation. */
|
|
38
|
+
private cancellationToken?: CancellationToken;
|
|
39
|
+
/** Current simulation promise, used to wait for completion after cancellation. */
|
|
40
|
+
private simulationPromise?: Promise<Buffer>;
|
|
36
41
|
|
|
37
42
|
constructor(
|
|
38
43
|
merkleTree: MerkleTreeWriteOperations,
|
|
@@ -85,12 +90,25 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
|
|
|
85
90
|
this.log.trace(`Serializing fast simulation inputs to msgpack...`);
|
|
86
91
|
const inputBuffer = fastSimInputs.serializeWithMessagePack();
|
|
87
92
|
|
|
93
|
+
// Create cancellation token for this simulation
|
|
94
|
+
this.cancellationToken = createCancellationToken();
|
|
95
|
+
|
|
96
|
+
// Store the promise so cancel() can wait for it
|
|
97
|
+
this.log.debug(`Calling C++ simulator for tx ${txHash}`);
|
|
98
|
+
this.simulationPromise = avmSimulate(inputBuffer, contractProvider, wsCppHandle, logLevel, this.cancellationToken);
|
|
99
|
+
|
|
88
100
|
let resultBuffer: Buffer;
|
|
89
101
|
try {
|
|
90
|
-
|
|
91
|
-
resultBuffer = await avmSimulate(inputBuffer, contractProvider, wsCppHandle, logLevel);
|
|
102
|
+
resultBuffer = await this.simulationPromise;
|
|
92
103
|
} catch (error: any) {
|
|
104
|
+
// Check if this was a cancellation
|
|
105
|
+
if (error.message?.includes('Simulation cancelled')) {
|
|
106
|
+
throw new SimulationError(`C++ simulation cancelled`, []);
|
|
107
|
+
}
|
|
93
108
|
throw new SimulationError(`C++ simulation failed: ${error.message}`, []);
|
|
109
|
+
} finally {
|
|
110
|
+
this.cancellationToken = undefined;
|
|
111
|
+
this.simulationPromise = undefined;
|
|
94
112
|
}
|
|
95
113
|
|
|
96
114
|
// If we've reached this point, C++ succeeded during simulation,
|
|
@@ -109,6 +127,33 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
|
|
|
109
127
|
|
|
110
128
|
return cppResult;
|
|
111
129
|
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Cancel the current simulation if one is in progress.
|
|
133
|
+
* This signals the C++ simulator to stop at the next opcode or before the next WorldState write.
|
|
134
|
+
* Safe to call even if no simulation is in progress.
|
|
135
|
+
*
|
|
136
|
+
* @param waitTimeoutMs - If provided, wait up to this many ms for the simulation to actually stop.
|
|
137
|
+
* This is important because C++ might be in the middle of a slow operation
|
|
138
|
+
* (e.g., pad_trees) and won't check the cancellation flag until it completes.
|
|
139
|
+
* Default timeout of 100ms after cancellation.
|
|
140
|
+
*/
|
|
141
|
+
public async cancel(waitTimeoutMs: number = 100): Promise<void> {
|
|
142
|
+
if (this.cancellationToken) {
|
|
143
|
+
this.log.debug('Cancelling C++ simulation');
|
|
144
|
+
cancelSimulation(this.cancellationToken);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Wait for the simulation to actually complete if not already done
|
|
148
|
+
if (this.simulationPromise) {
|
|
149
|
+
this.log.debug(`Waiting up to ${waitTimeoutMs}ms for C++ simulation to stop`);
|
|
150
|
+
await Promise.race([
|
|
151
|
+
this.simulationPromise.catch(() => {}), // Ignore rejection, just wait for completion
|
|
152
|
+
sleep(waitTimeoutMs),
|
|
153
|
+
]);
|
|
154
|
+
this.log.debug('C++ simulation stopped or wait timed out');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
112
157
|
}
|
|
113
158
|
|
|
114
159
|
export class MeasuredCppPublicTxSimulator extends CppPublicTxSimulator implements MeasuredPublicTxSimulatorInterface {
|
|
@@ -39,7 +39,7 @@ export class CppVsTsPublicTxSimulator extends PublicTxSimulator implements Publi
|
|
|
39
39
|
config?: Partial<PublicSimulatorConfig>,
|
|
40
40
|
) {
|
|
41
41
|
super(merkleTree, contractsDB, globalVariables, config);
|
|
42
|
-
this.log = createLogger(`simulator:
|
|
42
|
+
this.log = createLogger(`simulator:cpp_vs_public_tx_simulator`);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -205,7 +205,8 @@ export class CppVsTsPublicTxSimulator extends PublicTxSimulator implements Publi
|
|
|
205
205
|
cppGasUsed: cppResult.gasUsed.totalGas.l2Gas,
|
|
206
206
|
});
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
// Return cpp result as it has more detailed metadata / revert reasons
|
|
209
|
+
return cppResult;
|
|
209
210
|
}
|
|
210
211
|
}
|
|
211
212
|
|
|
@@ -3,8 +3,31 @@ import type { Tx } from '@aztec/stdlib/tx';
|
|
|
3
3
|
|
|
4
4
|
export interface PublicTxSimulatorInterface {
|
|
5
5
|
simulate(tx: Tx): Promise<PublicTxResult>;
|
|
6
|
+
/**
|
|
7
|
+
* Cancel the current simulation if one is in progress.
|
|
8
|
+
* This signals the underlying simulator (e.g., C++) to stop at the next safe point.
|
|
9
|
+
* Safe to call even if no simulation is in progress.
|
|
10
|
+
* Optional - not all implementations support cancellation.
|
|
11
|
+
*
|
|
12
|
+
* @param waitTimeoutMs - If provided, wait up to this many ms for the simulation to actually stop.
|
|
13
|
+
* This is important because signaling cancellation doesn't immediately stop C++ -
|
|
14
|
+
* it only sets a flag that C++ checks at certain points. If C++ is in the middle
|
|
15
|
+
* of a slow operation (e.g., pad_trees), it won't stop until that completes.
|
|
16
|
+
* @returns Promise that resolves when cancellation is signaled (and optionally when simulation stops)
|
|
17
|
+
*/
|
|
18
|
+
cancel?(waitTimeoutMs?: number): Promise<void>;
|
|
6
19
|
}
|
|
7
20
|
|
|
8
21
|
export interface MeasuredPublicTxSimulatorInterface {
|
|
9
22
|
simulate(tx: Tx, txLabel: string): Promise<PublicTxResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Cancel the current simulation if one is in progress.
|
|
25
|
+
* This signals the underlying simulator (e.g., C++) to stop at the next safe point.
|
|
26
|
+
* Safe to call even if no simulation is in progress.
|
|
27
|
+
* Optional - not all implementations support cancellation.
|
|
28
|
+
*
|
|
29
|
+
* @param waitTimeoutMs - If provided, wait up to this many ms for the simulation to actually stop.
|
|
30
|
+
* @returns Promise that resolves when cancellation is signaled (and optionally when simulation stops)
|
|
31
|
+
*/
|
|
32
|
+
cancel?(waitTimeoutMs?: number): Promise<void>;
|
|
10
33
|
}
|