@aztec/simulator 0.87.4 → 0.87.5
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 +0 -2
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +0 -2
- package/dest/private/acvm/acvm.d.ts +0 -4
- package/dest/private/acvm/acvm.d.ts.map +1 -1
- package/dest/private/acvm/oracle/oracle.d.ts +1 -1
- package/dest/private/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/private/acvm/oracle/oracle.js +2 -2
- package/dest/private/acvm/oracle/typed_oracle.d.ts +1 -1
- package/dest/private/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/private/acvm/oracle/typed_oracle.js +2 -2
- package/dest/private/private_execution.d.ts.map +1 -1
- package/dest/private/private_execution.js +1 -2
- package/dest/private/private_execution_oracle.d.ts +1 -1
- package/dest/private/private_execution_oracle.d.ts.map +1 -1
- package/dest/private/private_execution_oracle.js +1 -1
- package/dest/private/providers/circuit_recording/circuit_recorder.d.ts +19 -39
- package/dest/private/providers/circuit_recording/circuit_recorder.d.ts.map +1 -1
- package/dest/private/providers/circuit_recording/circuit_recorder.js +126 -90
- package/dest/private/providers/circuit_recording/simulation_provider_recorder_wrapper.d.ts +1 -3
- package/dest/private/providers/circuit_recording/simulation_provider_recorder_wrapper.d.ts.map +1 -1
- package/dest/private/providers/circuit_recording/simulation_provider_recorder_wrapper.js +11 -16
- package/dest/private/utility_execution_oracle.d.ts +1 -1
- package/dest/private/utility_execution_oracle.d.ts.map +1 -1
- package/dest/private/utility_execution_oracle.js +1 -1
- package/dest/public/avm/fixtures/base_avm_simulation_tester.d.ts +0 -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 +0 -6
- package/dest/public/public_tx_simulator/apps_tests/amm_test.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/apps_tests/amm_test.js +55 -27
- package/dest/server.d.ts +0 -2
- package/dest/server.d.ts.map +1 -1
- package/dest/server.js +0 -2
- package/dest/testing.d.ts +1 -1
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +1 -1
- package/package.json +15 -15
- package/src/client.ts +0 -2
- package/src/private/acvm/acvm.ts +0 -3
- package/src/private/acvm/oracle/oracle.ts +2 -2
- package/src/private/acvm/oracle/typed_oracle.ts +2 -2
- package/src/private/private_execution.ts +0 -1
- package/src/private/private_execution_oracle.ts +1 -1
- package/src/private/providers/circuit_recording/circuit_recorder.ts +136 -114
- package/src/private/providers/circuit_recording/simulation_provider_recorder_wrapper.ts +14 -22
- package/src/private/utility_execution_oracle.ts +1 -1
- package/src/public/avm/fixtures/base_avm_simulation_tester.ts +0 -5
- package/src/public/public_tx_simulator/apps_tests/amm_test.ts +44 -49
- package/src/server.ts +0 -2
- package/src/testing.ts +1 -1
- package/dest/private/providers/circuit_recording/file_circuit_recorder.d.ts +0 -31
- package/dest/private/providers/circuit_recording/file_circuit_recorder.d.ts.map +0 -1
- package/dest/private/providers/circuit_recording/file_circuit_recorder.js +0 -135
- package/dest/private/providers/circuit_recording/memory_circuit_recorder.d.ts +0 -5
- package/dest/private/providers/circuit_recording/memory_circuit_recorder.d.ts.map +0 -1
- package/dest/private/providers/circuit_recording/memory_circuit_recorder.js +0 -9
- package/src/private/providers/circuit_recording/file_circuit_recorder.ts +0 -158
- package/src/private/providers/circuit_recording/memory_circuit_recorder.ts +0 -11
|
@@ -1,59 +1,28 @@
|
|
|
1
|
-
import { sha512 } from '@aztec/foundation/crypto';
|
|
2
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import { Timer } from '@aztec/foundation/timer';
|
|
4
2
|
import type { ForeignCallHandler, ForeignCallInput, ForeignCallOutput } from '@aztec/noir-acvm_js';
|
|
5
3
|
|
|
4
|
+
import { createHash } from 'crypto';
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
6
8
|
import type { ACIRCallback } from '../../acvm/acvm.js';
|
|
7
9
|
import type { ACVMWitness } from '../../acvm/acvm_types.js';
|
|
8
10
|
import { Oracle } from '../../acvm/oracle/oracle.js';
|
|
9
11
|
|
|
10
|
-
export type OracleCall = {
|
|
11
|
-
name: string;
|
|
12
|
-
inputs: unknown[];
|
|
13
|
-
outputs: unknown;
|
|
14
|
-
time: number;
|
|
15
|
-
// Due to the recursive nature of the simulator, we might have
|
|
16
|
-
// oracle calls performed after a foreign call (which is itself an oracle call)
|
|
17
|
-
// We keep track of the stack depth in this variable to ensure the recorded oracle
|
|
18
|
-
// calls are correctly associated with the right circuit.
|
|
19
|
-
// This is only use as a debugging tool
|
|
20
|
-
stackDepth: number;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export class CircuitRecording {
|
|
24
|
-
circuitName: string;
|
|
25
|
-
functionName: string;
|
|
26
|
-
bytecodeSHA512Hash: string;
|
|
27
|
-
timestamp: number;
|
|
28
|
-
inputs: Record<string, string>;
|
|
29
|
-
oracleCalls: OracleCall[];
|
|
30
|
-
error?: string;
|
|
31
|
-
parent?: CircuitRecording;
|
|
32
|
-
|
|
33
|
-
constructor(circuitName: string, functionName: string, bytecodeSHA512Hash: string, inputs: Record<string, string>) {
|
|
34
|
-
this.circuitName = circuitName;
|
|
35
|
-
this.functionName = functionName;
|
|
36
|
-
this.bytecodeSHA512Hash = bytecodeSHA512Hash;
|
|
37
|
-
this.timestamp = Date.now();
|
|
38
|
-
this.inputs = inputs;
|
|
39
|
-
this.oracleCalls = [];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
setParent(recording?: CircuitRecording): void {
|
|
43
|
-
this.parent = recording;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
12
|
/**
|
|
48
13
|
* Class responsible for recording circuit inputs necessary to replay the circuit. These inputs are the initial witness
|
|
49
14
|
* map and the oracle calls made during the circuit execution/witness generation.
|
|
50
15
|
*
|
|
51
|
-
*
|
|
16
|
+
* The recording is stored in a JSON file called `circuit_name_circuit_function_name_YYYY-MM-DD_N.json` where N is
|
|
17
|
+
* a counter to ensure unique filenames. The file is stored in the `recordDir` directory provided as a parameter to
|
|
18
|
+
* CircuitRecorder.start().
|
|
19
|
+
*
|
|
20
|
+
* Example recording file:
|
|
52
21
|
* ```json
|
|
53
22
|
* {
|
|
54
23
|
* "circuitName": "AMM",
|
|
55
24
|
* "functionName": "add_liquidity",
|
|
56
|
-
* "
|
|
25
|
+
* "bytecodeMd5Hash": "b46c640ed38f20eac5f61a5e41d8dd1e",
|
|
57
26
|
* "timestamp": 1740691464360,
|
|
58
27
|
* "inputs": {
|
|
59
28
|
* "0": "0x1e89de1f0ad5204263733b7ddf65bec45b8f44714a4da85a46474dad677679ef",
|
|
@@ -82,7 +51,7 @@ export class CircuitRecording {
|
|
|
82
51
|
* ]
|
|
83
52
|
* },
|
|
84
53
|
* {
|
|
85
|
-
* "name": "
|
|
54
|
+
* "name": "syncPrivateState",
|
|
86
55
|
* "inputs": []
|
|
87
56
|
* }
|
|
88
57
|
* ]
|
|
@@ -90,14 +59,10 @@ export class CircuitRecording {
|
|
|
90
59
|
* ```
|
|
91
60
|
*/
|
|
92
61
|
export class CircuitRecorder {
|
|
93
|
-
|
|
62
|
+
private readonly logger = createLogger('simulator:acvm:recording');
|
|
63
|
+
private isFirstCall = true;
|
|
94
64
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
private stackDepth: number = 0;
|
|
98
|
-
private newCircuit: boolean = true;
|
|
99
|
-
|
|
100
|
-
protected constructor() {}
|
|
65
|
+
private constructor(private readonly filePath: string) {}
|
|
101
66
|
|
|
102
67
|
/**
|
|
103
68
|
* Initializes a new circuit recording session.
|
|
@@ -107,20 +72,82 @@ export class CircuitRecorder {
|
|
|
107
72
|
* @param circuitName - Name of the circuit
|
|
108
73
|
* @param functionName - Name of the circuit function (defaults to 'main'). This is meaningful only for
|
|
109
74
|
* contracts as protocol circuits artifacts always contain a single entrypoint function called 'main'.
|
|
75
|
+
* @returns A new CircuitRecorder instance
|
|
110
76
|
*/
|
|
111
|
-
start(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
77
|
+
static async start(
|
|
78
|
+
recordDir: string,
|
|
79
|
+
input: ACVMWitness,
|
|
80
|
+
circuitBytecode: Buffer,
|
|
81
|
+
circuitName: string,
|
|
82
|
+
functionName: string = 'main',
|
|
83
|
+
): Promise<CircuitRecorder> {
|
|
84
|
+
const recording = {
|
|
85
|
+
circuitName: circuitName,
|
|
86
|
+
functionName: functionName,
|
|
87
|
+
bytecodeMd5Hash: createHash('md5').update(circuitBytecode).digest('hex'),
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
inputs: Object.fromEntries(input),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const recordingStringWithoutClosingBracket = JSON.stringify(recording, null, 2).slice(0, -2);
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// Check if the recording directory exists and is a directory
|
|
96
|
+
const stats = await fs.stat(recordDir);
|
|
97
|
+
if (!stats.isDirectory()) {
|
|
98
|
+
throw new Error(`Recording path ${recordDir} exists but is not a directory`);
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
102
|
+
// The directory does not exist so we create it
|
|
103
|
+
await fs.mkdir(recordDir, { recursive: true });
|
|
104
|
+
} else {
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
120
107
|
}
|
|
121
|
-
this.recording!.setParent(parentRef);
|
|
122
108
|
|
|
123
|
-
|
|
109
|
+
const filePath = await CircuitRecorder.#computeFilePathAndStoreInitialRecording(
|
|
110
|
+
recordDir,
|
|
111
|
+
circuitName,
|
|
112
|
+
functionName,
|
|
113
|
+
recordingStringWithoutClosingBracket,
|
|
114
|
+
);
|
|
115
|
+
return new CircuitRecorder(filePath);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Computes a unique file path for the recording by trying different counter values.
|
|
120
|
+
* This is needed because multiple recordings of the same circuit could be happening simultaneously or an older
|
|
121
|
+
* recording might be present.
|
|
122
|
+
* @param recordDir - Directory to store the recording
|
|
123
|
+
* @param circuitName - Name of the circuit
|
|
124
|
+
* @param functionName - Name of the circuit function
|
|
125
|
+
* @param recordingContent - Initial recording content
|
|
126
|
+
* @returns A unique file path for the recording
|
|
127
|
+
*/
|
|
128
|
+
static async #computeFilePathAndStoreInitialRecording(
|
|
129
|
+
recordDir: string,
|
|
130
|
+
circuitName: string,
|
|
131
|
+
functionName: string,
|
|
132
|
+
recordingContent: string,
|
|
133
|
+
): Promise<string> {
|
|
134
|
+
let counter = 0;
|
|
135
|
+
while (true) {
|
|
136
|
+
try {
|
|
137
|
+
const filePath = getFilePath(recordDir, circuitName, functionName, counter);
|
|
138
|
+
// Write the initial recording content to the file
|
|
139
|
+
await fs.writeFile(filePath, recordingContent + ',\n "oracleCalls": [\n', {
|
|
140
|
+
flag: 'wx', // wx flag fails if file exists
|
|
141
|
+
});
|
|
142
|
+
return filePath;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if ((err as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
145
|
+
counter++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
124
151
|
}
|
|
125
152
|
|
|
126
153
|
/**
|
|
@@ -160,38 +187,15 @@ export class CircuitRecorder {
|
|
|
160
187
|
throw new Error(`Oracle method ${name} not found when setting up recording callback`);
|
|
161
188
|
}
|
|
162
189
|
|
|
163
|
-
const isExternalCall = (name as keyof ACIRCallback) === 'callPrivateFunction';
|
|
164
|
-
|
|
165
190
|
recordingCallback[name as keyof ACIRCallback] = (...args: ForeignCallInput[]): ReturnType<typeof fn> => {
|
|
166
|
-
const timer = new Timer();
|
|
167
|
-
// If we're entering another circuit via `callPrivateFunction`, we increase the stack depth and set the
|
|
168
|
-
// newCircuit variable to ensure we are creating a new recording object.
|
|
169
|
-
if (isExternalCall) {
|
|
170
|
-
this.stackDepth++;
|
|
171
|
-
this.newCircuit = true;
|
|
172
|
-
}
|
|
173
191
|
const result = fn.call(callback, ...args);
|
|
174
192
|
if (result instanceof Promise) {
|
|
175
193
|
return result.then(async r => {
|
|
176
|
-
|
|
177
|
-
// since we are going back to the "parent" circuit which can never be new
|
|
178
|
-
if (isExternalCall) {
|
|
179
|
-
this.stackDepth--;
|
|
180
|
-
this.newCircuit = false;
|
|
181
|
-
this.recording = this.recording!.parent;
|
|
182
|
-
}
|
|
183
|
-
await this.recordCall(name, args, r, timer.ms(), this.stackDepth);
|
|
194
|
+
await this.#recordCall(name, args, r);
|
|
184
195
|
return r;
|
|
185
196
|
}) as ReturnType<typeof fn>;
|
|
186
197
|
}
|
|
187
|
-
|
|
188
|
-
// since we are going back to the "parent" circuit which can never be new
|
|
189
|
-
if (isExternalCall) {
|
|
190
|
-
this.stackDepth--;
|
|
191
|
-
this.newCircuit = false;
|
|
192
|
-
this.recording = this.recording!.parent;
|
|
193
|
-
}
|
|
194
|
-
void this.recordCall(name, args, result, timer.ms(), this.stackDepth);
|
|
198
|
+
void this.#recordCall(name, args, result);
|
|
195
199
|
return result;
|
|
196
200
|
};
|
|
197
201
|
}
|
|
@@ -206,9 +210,8 @@ export class CircuitRecorder {
|
|
|
206
210
|
*/
|
|
207
211
|
#wrapProtocolCircuitCallback(callback: ForeignCallHandler): ForeignCallHandler {
|
|
208
212
|
return async (name: string, inputs: ForeignCallInput[]): Promise<ForeignCallOutput[]> => {
|
|
209
|
-
const timer = new Timer();
|
|
210
213
|
const result = await callback(name, inputs);
|
|
211
|
-
await this
|
|
214
|
+
await this.#recordCall(name, inputs, result);
|
|
212
215
|
return result;
|
|
213
216
|
};
|
|
214
217
|
}
|
|
@@ -219,43 +222,62 @@ export class CircuitRecorder {
|
|
|
219
222
|
* @param inputs - Input arguments
|
|
220
223
|
* @param outputs - Output results
|
|
221
224
|
*/
|
|
222
|
-
recordCall(name: string, inputs: unknown[], outputs: unknown
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
225
|
+
async #recordCall(name: string, inputs: unknown[], outputs: unknown) {
|
|
226
|
+
try {
|
|
227
|
+
const entry = {
|
|
228
|
+
name,
|
|
229
|
+
inputs,
|
|
230
|
+
outputs,
|
|
231
|
+
};
|
|
232
|
+
const prefix = this.isFirstCall ? ' ' : ' ,';
|
|
233
|
+
this.isFirstCall = false;
|
|
234
|
+
await fs.appendFile(this.filePath, prefix + JSON.stringify(entry) + '\n');
|
|
235
|
+
} catch (err) {
|
|
236
|
+
this.logger.error('Failed to log circuit call', { error: err });
|
|
237
|
+
}
|
|
232
238
|
}
|
|
233
239
|
|
|
234
240
|
/**
|
|
235
|
-
* Finalizes the recording by
|
|
241
|
+
* Finalizes the recording file by adding closing brackets. Without calling this method, the recording file is
|
|
242
|
+
* incomplete and it fails to parse.
|
|
236
243
|
*/
|
|
237
|
-
finish(): Promise<
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
this.
|
|
242
|
-
this.recording = undefined;
|
|
244
|
+
async finish(): Promise<void> {
|
|
245
|
+
try {
|
|
246
|
+
await fs.appendFile(this.filePath, ' ]\n}\n');
|
|
247
|
+
} catch (err) {
|
|
248
|
+
this.logger.error('Failed to finalize recording file', { error: err });
|
|
243
249
|
}
|
|
244
|
-
return Promise.resolve(result!);
|
|
245
250
|
}
|
|
246
251
|
|
|
247
252
|
/**
|
|
248
|
-
* Finalizes the recording by
|
|
253
|
+
* Finalizes the recording file by adding the error and closing brackets. Without calling this method or `finish`,
|
|
254
|
+
* the recording file is incomplete and it fails to parse.
|
|
249
255
|
* @param error - The error that occurred during circuit execution
|
|
250
256
|
*/
|
|
251
|
-
finishWithError(error: unknown): Promise<
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
this.
|
|
256
|
-
|
|
257
|
+
async finishWithError(error: unknown): Promise<void> {
|
|
258
|
+
try {
|
|
259
|
+
await fs.appendFile(this.filePath, ' ],\n');
|
|
260
|
+
await fs.appendFile(this.filePath, ` "error": ${JSON.stringify(error)}\n`);
|
|
261
|
+
await fs.appendFile(this.filePath, '}\n');
|
|
262
|
+
} catch (err) {
|
|
263
|
+
this.logger.error('Failed to finalize recording file with error', { error: err });
|
|
257
264
|
}
|
|
258
|
-
result!.error = JSON.stringify(error);
|
|
259
|
-
return Promise.resolve(result!);
|
|
260
265
|
}
|
|
261
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Generates a file path for storing circuit recordings. The format of the filename is:
|
|
270
|
+
* `circuit_name_circuit_function_name_YYYY-MM-DD_N.json` where N is a counter to ensure unique filenames.
|
|
271
|
+
* @param recordDir - Base directory for recordings
|
|
272
|
+
* @param circuitName - Name of the circuit
|
|
273
|
+
* @param functionName - Name of the circuit function
|
|
274
|
+
* @param counter - Counter to ensure unique filenames. This is expected to be incremented in a loop until there is no
|
|
275
|
+
* existing file with the same name.
|
|
276
|
+
* @returns A file path for the recording.
|
|
277
|
+
*/
|
|
278
|
+
function getFilePath(recordDir: string, circuitName: string, functionName: string, counter: number): string {
|
|
279
|
+
const date = new Date();
|
|
280
|
+
const formattedDate = date.toISOString().split('T')[0];
|
|
281
|
+
const filename = `${circuitName}_${functionName}_${formattedDate}_${counter}.json`;
|
|
282
|
+
return path.join(recordDir, filename);
|
|
283
|
+
}
|
|
@@ -2,21 +2,18 @@ import type { ForeignCallHandler } from '@aztec/noir-protocol-circuits-types/typ
|
|
|
2
2
|
import type { FunctionArtifactWithContractName } from '@aztec/stdlib/abi';
|
|
3
3
|
import type { NoirCompiledCircuitWithName } from '@aztec/stdlib/noir';
|
|
4
4
|
|
|
5
|
-
import type { ACIRCallback,
|
|
5
|
+
import type { ACIRCallback, ACIRExecutionResult } from '../../acvm/acvm.js';
|
|
6
6
|
import type { ACVMWitness } from '../../acvm/acvm_types.js';
|
|
7
7
|
import type { ACVMSuccess } from '../acvm_native.js';
|
|
8
8
|
import type { SimulationProvider } from '../simulation_provider.js';
|
|
9
|
-
import
|
|
9
|
+
import { CircuitRecorder } from './circuit_recorder.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Takes a simulation provider and wraps it in a circuit recorder. See CircuitRecorder for more details on how circuit
|
|
13
13
|
* recording works.
|
|
14
14
|
*/
|
|
15
15
|
export class SimulationProviderRecorderWrapper implements SimulationProvider {
|
|
16
|
-
constructor(
|
|
17
|
-
private simulator: SimulationProvider,
|
|
18
|
-
private recorder: CircuitRecorder,
|
|
19
|
-
) {}
|
|
16
|
+
constructor(private simulator: SimulationProvider) {}
|
|
20
17
|
|
|
21
18
|
executeProtocolCircuit(
|
|
22
19
|
input: ACVMWitness,
|
|
@@ -50,7 +47,7 @@ export class SimulationProviderRecorderWrapper implements SimulationProvider {
|
|
|
50
47
|
);
|
|
51
48
|
}
|
|
52
49
|
|
|
53
|
-
async #simulate<C extends ACIRCallback | ForeignCallHandler | undefined, T
|
|
50
|
+
async #simulate<C extends ACIRCallback | ForeignCallHandler | undefined, T>(
|
|
54
51
|
simulateFn: (wrappedCallback: C) => Promise<T>,
|
|
55
52
|
input: ACVMWitness,
|
|
56
53
|
bytecode: Buffer,
|
|
@@ -58,33 +55,28 @@ export class SimulationProviderRecorderWrapper implements SimulationProvider {
|
|
|
58
55
|
functionName: string,
|
|
59
56
|
callback: C,
|
|
60
57
|
): Promise<T> {
|
|
58
|
+
const recordDir = process.env.CIRCUIT_RECORD_DIR;
|
|
59
|
+
if (!recordDir) {
|
|
60
|
+
// Recording is not enabled so we just execute the circuit
|
|
61
|
+
return simulateFn(callback);
|
|
62
|
+
}
|
|
63
|
+
|
|
61
64
|
// Start recording circuit execution
|
|
62
|
-
await
|
|
65
|
+
const recorder = await CircuitRecorder.start(recordDir, input, bytecode, contractName, functionName);
|
|
63
66
|
|
|
64
67
|
// If callback was provided, we wrap it in a circuit recorder callback wrapper
|
|
65
|
-
const wrappedCallback =
|
|
68
|
+
const wrappedCallback = recorder.wrapCallback(callback);
|
|
66
69
|
let result: T;
|
|
67
70
|
try {
|
|
68
71
|
result = await simulateFn(wrappedCallback as C);
|
|
69
72
|
} catch (error) {
|
|
70
73
|
// If an error occurs, we finalize the recording file with the error
|
|
71
|
-
await
|
|
74
|
+
await recorder.finishWithError(error);
|
|
72
75
|
throw error;
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
// Witness generation is complete so we finish the circuit recorder
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
(result as ACIRExecutionResult).oracles = recording.oracleCalls?.reduce(
|
|
79
|
-
(acc, { time, name }) => {
|
|
80
|
-
if (!acc[name]) {
|
|
81
|
-
acc[name] = { times: [] };
|
|
82
|
-
}
|
|
83
|
-
acc[name].times.push(time);
|
|
84
|
-
return acc;
|
|
85
|
-
},
|
|
86
|
-
{} as Record<string, ACIRCallbackStats>,
|
|
87
|
-
);
|
|
79
|
+
await recorder.finish();
|
|
88
80
|
|
|
89
81
|
return result;
|
|
90
82
|
}
|
|
@@ -274,7 +274,7 @@ export class UtilityExecutionOracle extends TypedOracle {
|
|
|
274
274
|
return await this.executionDataProvider.getIndexedTaggingSecretAsSender(this.contractAddress, sender, recipient);
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
public override async
|
|
277
|
+
public override async syncPrivateState(pendingTaggedLogArrayBaseSlot: Fr) {
|
|
278
278
|
await this.executionDataProvider.syncTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes);
|
|
279
279
|
|
|
280
280
|
await this.executionDataProvider.removeNullifiedNotes(this.contractAddress);
|
|
@@ -100,9 +100,4 @@ export abstract class BaseAvmSimulationTester {
|
|
|
100
100
|
);
|
|
101
101
|
await this.merkleTrees.sequentialInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()]);
|
|
102
102
|
}
|
|
103
|
-
|
|
104
|
-
async insertNullifier(contractThatEmitted: AztecAddress, nullifier: Fr) {
|
|
105
|
-
const siloedNullifier = await siloNullifier(contractThatEmitted, nullifier);
|
|
106
|
-
await this.merkleTrees.sequentialInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()]);
|
|
107
|
-
}
|
|
108
103
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { GeneratorIndex } from '@aztec/constants';
|
|
2
|
-
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto';
|
|
3
1
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
2
|
import type { Logger } from '@aztec/foundation/log';
|
|
5
3
|
import { AMMContractArtifact } from '@aztec/noir-contracts.js/AMM';
|
|
@@ -139,24 +137,6 @@ async function addLiquidity(
|
|
|
139
137
|
const liquidityPartialNote = {
|
|
140
138
|
commitment: new Fr(99),
|
|
141
139
|
};
|
|
142
|
-
const refundToken0PartialNoteValidityCommitment = await computePartialNoteValidityCommitment(
|
|
143
|
-
refundToken0PartialNote,
|
|
144
|
-
amm.address,
|
|
145
|
-
);
|
|
146
|
-
const refundToken1PartialNoteValidityCommitment = await computePartialNoteValidityCommitment(
|
|
147
|
-
refundToken1PartialNote,
|
|
148
|
-
amm.address,
|
|
149
|
-
);
|
|
150
|
-
const liquidityPartialNoteValidityCommitment = await computePartialNoteValidityCommitment(
|
|
151
|
-
liquidityPartialNote,
|
|
152
|
-
amm.address,
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
// We need to inject the validity commitments into the nullifier tree as that would be performed by the private token
|
|
156
|
-
// functions that are not invoked in this test.
|
|
157
|
-
await tester.insertNullifier(token0.address, refundToken0PartialNoteValidityCommitment);
|
|
158
|
-
await tester.insertNullifier(token1.address, refundToken1PartialNoteValidityCommitment);
|
|
159
|
-
await tester.insertNullifier(liquidityToken.address, liquidityPartialNoteValidityCommitment);
|
|
160
140
|
|
|
161
141
|
return await tester.simulateTxWithLabel(
|
|
162
142
|
/*txLabel=*/ 'AMM/add_liquidity',
|
|
@@ -170,6 +150,13 @@ async function addLiquidity(
|
|
|
170
150
|
args: [/*to=*/ amm.address, /*amount=*/ amount0Max],
|
|
171
151
|
address: token0.address,
|
|
172
152
|
},
|
|
153
|
+
// token0.prepare_private_balance_increase enqueues a call to _store_balances_set_partial_note
|
|
154
|
+
{
|
|
155
|
+
sender: token0.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
156
|
+
fnName: '_store_balances_set_partial_note',
|
|
157
|
+
args: [refundToken0PartialNote],
|
|
158
|
+
address: token0.address,
|
|
159
|
+
},
|
|
173
160
|
// token1.transfer_to_public enqueues a call to _increase_public_balance
|
|
174
161
|
{
|
|
175
162
|
sender: token1.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
@@ -177,6 +164,20 @@ async function addLiquidity(
|
|
|
177
164
|
args: [/*to=*/ amm.address, /*amount=*/ amount1Max],
|
|
178
165
|
address: token1.address,
|
|
179
166
|
},
|
|
167
|
+
// token1.prepare_private_balance_increase enqueues a call to _store_balances_set_partial_note
|
|
168
|
+
{
|
|
169
|
+
sender: token1.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
170
|
+
fnName: '_store_balances_set_partial_note',
|
|
171
|
+
args: [refundToken1PartialNote],
|
|
172
|
+
address: token1.address,
|
|
173
|
+
},
|
|
174
|
+
// liquidityToken.prepare_private_balance_increase enqueues a call to _store_balances_set_partial_note
|
|
175
|
+
{
|
|
176
|
+
sender: liquidityToken.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
177
|
+
fnName: '_store_balances_set_partial_note',
|
|
178
|
+
args: [liquidityPartialNote],
|
|
179
|
+
address: liquidityToken.address,
|
|
180
|
+
},
|
|
180
181
|
// amm.add_liquidity enqueues a call to _add_liquidity
|
|
181
182
|
{
|
|
182
183
|
sender: amm.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
@@ -213,16 +214,8 @@ async function swapExactTokensForTokens(
|
|
|
213
214
|
_nonce?: bigint,
|
|
214
215
|
) {
|
|
215
216
|
const tokenOutPartialNote = {
|
|
216
|
-
commitment: new Fr(
|
|
217
|
+
commitment: new Fr(66),
|
|
217
218
|
};
|
|
218
|
-
const tokenOutPartialNoteValidityCommitment = await computePartialNoteValidityCommitment(
|
|
219
|
-
tokenOutPartialNote,
|
|
220
|
-
amm.address,
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
// We need to inject the validity commitment into the nullifier tree as that would be performed by the private token
|
|
224
|
-
// function that is not invoked in this test.
|
|
225
|
-
await tester.insertNullifier(tokenOut.address, tokenOutPartialNoteValidityCommitment);
|
|
226
219
|
|
|
227
220
|
return await tester.simulateTxWithLabel(
|
|
228
221
|
/*txLabel=*/ 'AMM/swap_exact_tokens_for_tokens',
|
|
@@ -236,6 +229,14 @@ async function swapExactTokensForTokens(
|
|
|
236
229
|
args: [/*to=*/ amm.address, /*amount=*/ amountIn],
|
|
237
230
|
address: tokenIn.address,
|
|
238
231
|
},
|
|
232
|
+
// tokenOut.prepare_private_balance_increase enqueues a call to _store_balances_set_partial_note
|
|
233
|
+
{
|
|
234
|
+
sender: tokenOut.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
235
|
+
fnName: '_store_balances_set_partial_note',
|
|
236
|
+
args: [tokenOutPartialNote],
|
|
237
|
+
address: tokenOut.address,
|
|
238
|
+
},
|
|
239
|
+
|
|
239
240
|
{
|
|
240
241
|
sender: amm.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
241
242
|
fnName: '_swap_exact_tokens_for_tokens',
|
|
@@ -264,19 +265,6 @@ async function removeLiquidity(
|
|
|
264
265
|
const token1PartialNote = {
|
|
265
266
|
commitment: new Fr(222),
|
|
266
267
|
};
|
|
267
|
-
const token0PartialNoteValidityCommitment = await computePartialNoteValidityCommitment(
|
|
268
|
-
token0PartialNote,
|
|
269
|
-
amm.address,
|
|
270
|
-
);
|
|
271
|
-
const token1PartialNoteValidityCommitment = await computePartialNoteValidityCommitment(
|
|
272
|
-
token1PartialNote,
|
|
273
|
-
amm.address,
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
// We need to inject the validity commitments into the nullifier tree as that would be performed by the private token
|
|
277
|
-
// functions that are not invoked in this test.
|
|
278
|
-
await tester.insertNullifier(token0.address, token0PartialNoteValidityCommitment);
|
|
279
|
-
await tester.insertNullifier(token1.address, token1PartialNoteValidityCommitment);
|
|
280
268
|
|
|
281
269
|
return await tester.simulateTxWithLabel(
|
|
282
270
|
/*txLabel=*/ 'AMM/remove_liquidity',
|
|
@@ -290,6 +278,20 @@ async function removeLiquidity(
|
|
|
290
278
|
args: [/*to=*/ amm.address, /*amount=*/ liquidity],
|
|
291
279
|
address: liquidityToken.address,
|
|
292
280
|
},
|
|
281
|
+
// token0.prepare_private_balance_increase enqueues a call to _store_balances_set_partial_note
|
|
282
|
+
{
|
|
283
|
+
sender: token0.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
284
|
+
fnName: '_store_balances_set_partial_note',
|
|
285
|
+
args: [token0PartialNote],
|
|
286
|
+
address: token0.address,
|
|
287
|
+
},
|
|
288
|
+
// token1.prepare_private_balance_increase enqueues a call to _store_balances_set_partial_note
|
|
289
|
+
{
|
|
290
|
+
sender: token1.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
291
|
+
fnName: '_store_balances_set_partial_note',
|
|
292
|
+
args: [token1PartialNote],
|
|
293
|
+
address: token1.address,
|
|
294
|
+
},
|
|
293
295
|
// amm.remove_liquidity enqueues a call to _remove_liquidity
|
|
294
296
|
{
|
|
295
297
|
sender: amm.address, // INTERNAL FUNCTION! Sender must be 'this'.
|
|
@@ -312,10 +314,3 @@ async function removeLiquidity(
|
|
|
312
314
|
],
|
|
313
315
|
);
|
|
314
316
|
}
|
|
315
|
-
|
|
316
|
-
async function computePartialNoteValidityCommitment(partialNote: { commitment: Fr }, completer: AztecAddress) {
|
|
317
|
-
return await poseidon2HashWithSeparator(
|
|
318
|
-
[partialNote.commitment, completer],
|
|
319
|
-
GeneratorIndex.PARTIAL_NOTE_VALIDITY_COMMITMENT,
|
|
320
|
-
);
|
|
321
|
-
}
|
package/src/server.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export * from './public/index.js';
|
|
2
2
|
export { WASMSimulatorWithBlobs } from './private/providers/acvm_wasm_with_blobs.js';
|
|
3
3
|
export { NativeACVMSimulator } from './private/providers/acvm_native.js';
|
|
4
|
-
export { SimulationProviderRecorderWrapper } from './private/providers/circuit_recording/simulation_provider_recorder_wrapper.js';
|
|
5
|
-
export { MemoryCircuitRecorder } from './private/providers/circuit_recording/memory_circuit_recorder.js';
|
|
6
4
|
export { type SimulationProvider } from './private/providers/simulation_provider.js';
|
|
7
5
|
export * from './common/index.js';
|
package/src/testing.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { SimulationProviderRecorderWrapper } from './private/providers/circuit_recording/simulation_provider_recorder_wrapper.js';
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { ACVMWitness } from '../../acvm/acvm_types.js';
|
|
2
|
-
import { CircuitRecorder, type CircuitRecording } from './circuit_recorder.js';
|
|
3
|
-
export declare class FileCircuitRecorder extends CircuitRecorder {
|
|
4
|
-
#private;
|
|
5
|
-
private readonly recordDir;
|
|
6
|
-
recording?: CircuitRecording & {
|
|
7
|
-
filePath: string;
|
|
8
|
-
isFirstCall: boolean;
|
|
9
|
-
};
|
|
10
|
-
constructor(recordDir: string);
|
|
11
|
-
start(input: ACVMWitness, circuitBytecode: Buffer, circuitName: string, functionName?: string): Promise<void>;
|
|
12
|
-
/**
|
|
13
|
-
* Records a single oracle/foreign call with its inputs and outputs.
|
|
14
|
-
* @param name - Name of the call
|
|
15
|
-
* @param inputs - Input arguments
|
|
16
|
-
* @param outputs - Output results
|
|
17
|
-
*/
|
|
18
|
-
recordCall(name: string, inputs: unknown[], outputs: unknown, time: number, stackDepth: number): Promise<import("./circuit_recorder.js").OracleCall>;
|
|
19
|
-
/**
|
|
20
|
-
* Finalizes the recording file by adding closing brackets. Without calling this method, the recording file is
|
|
21
|
-
* incomplete and it fails to parse.
|
|
22
|
-
*/
|
|
23
|
-
finish(): Promise<CircuitRecording>;
|
|
24
|
-
/**
|
|
25
|
-
* Finalizes the recording file by adding the error and closing brackets. Without calling this method or `finish`,
|
|
26
|
-
* the recording file is incomplete and it fails to parse.
|
|
27
|
-
* @param error - The error that occurred during circuit execution
|
|
28
|
-
*/
|
|
29
|
-
finishWithError(error: unknown): Promise<CircuitRecording>;
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=file_circuit_recorder.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file_circuit_recorder.d.ts","sourceRoot":"","sources":["../../../../src/private/providers/circuit_recording/file_circuit_recorder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,qBAAa,mBAAoB,SAAQ,eAAe;;IAG1C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAF9B,SAAS,CAAC,EAAE,gBAAgB,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;gBAErD,SAAS,EAAE,MAAM;IAI/B,KAAK,CAClB,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,MAAe;IAqE/B;;;;;OAKG;IACY,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAY7G;;;OAGG;IACY,MAAM,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAalD;;;;OAIG;IACY,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAc1E"}
|