@aztec/sequencer-client 0.26.2 → 0.26.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/block_builder/solo_block_builder.d.ts +5 -4
- package/dest/block_builder/solo_block_builder.d.ts.map +1 -1
- package/dest/block_builder/solo_block_builder.js +75 -30
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +29 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +4 -2
- package/dest/index.d.ts +2 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/publisher/l1-publisher.js +5 -5
- package/dest/publisher/viem-tx-sender.js +2 -2
- package/dest/sequencer/public_processor.d.ts +3 -1
- package/dest/sequencer/public_processor.d.ts.map +1 -1
- package/dest/sequencer/public_processor.js +4 -3
- package/dest/sequencer/sequencer.js +3 -3
- package/dest/simulator/acvm_native.d.ts +20 -0
- package/dest/simulator/acvm_native.d.ts.map +1 -0
- package/dest/simulator/acvm_native.js +96 -0
- package/dest/simulator/acvm_wasm.d.ts +7 -0
- package/dest/simulator/acvm_wasm.d.ts.map +1 -0
- package/dest/simulator/acvm_wasm.js +23 -0
- package/dest/simulator/index.d.ts +1 -0
- package/dest/simulator/index.d.ts.map +1 -1
- package/dest/simulator/index.js +2 -2
- package/dest/simulator/public_kernel.d.ts +4 -0
- package/dest/simulator/public_kernel.d.ts.map +1 -1
- package/dest/simulator/public_kernel.js +16 -6
- package/dest/simulator/rollup.d.ts +4 -0
- package/dest/simulator/rollup.d.ts.map +1 -1
- package/dest/simulator/rollup.js +16 -20
- package/dest/simulator/simulation_provider.d.ts +9 -0
- package/dest/simulator/simulation_provider.d.ts.map +1 -0
- package/dest/simulator/simulation_provider.js +2 -0
- package/package.json +15 -13
- package/src/block_builder/solo_block_builder.ts +109 -50
- package/src/client/sequencer-client.ts +37 -2
- package/src/config.ts +4 -0
- package/src/index.ts +2 -0
- package/src/publisher/l1-publisher.ts +4 -4
- package/src/publisher/viem-tx-sender.ts +1 -1
- package/src/sequencer/public_processor.ts +3 -1
- package/src/sequencer/sequencer.ts +2 -2
- package/src/simulator/acvm_native.ts +112 -0
- package/src/simulator/acvm_wasm.ts +31 -0
- package/src/simulator/index.ts +1 -0
- package/src/simulator/public_kernel.ts +31 -7
- package/src/simulator/rollup.ts +31 -19
- package/src/simulator/simulation_provider.ts +10 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Body, ContractData, L2Block, MerkleTreeId, PublicDataWrite, TxEffect, TxL2Logs } from '@aztec/circuit-types';
|
|
2
|
+
import { CircuitSimulationStats } from '@aztec/circuit-types/stats';
|
|
2
3
|
import {
|
|
3
4
|
ARCHIVE_HEIGHT,
|
|
4
5
|
AppendOnlyTreeSnapshot,
|
|
@@ -52,6 +53,7 @@ import { padArrayEnd } from '@aztec/foundation/collection';
|
|
|
52
53
|
import { Fr } from '@aztec/foundation/fields';
|
|
53
54
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
54
55
|
import { Tuple, assertLength, toFriendlyJSON } from '@aztec/foundation/serialize';
|
|
56
|
+
import { elapsed } from '@aztec/foundation/timer';
|
|
55
57
|
import { MerkleTreeOperations } from '@aztec/world-state';
|
|
56
58
|
|
|
57
59
|
import chunk from 'lodash.chunk';
|
|
@@ -138,11 +140,11 @@ export class SoloBlockBuilder implements BlockBuilder {
|
|
|
138
140
|
body: blockBody,
|
|
139
141
|
});
|
|
140
142
|
|
|
141
|
-
if (!l2Block.body.
|
|
143
|
+
if (!l2Block.body.getTxsEffectsHash().equals(circuitsOutput.header.contentCommitment.txsEffectsHash)) {
|
|
142
144
|
throw new Error(
|
|
143
|
-
`
|
|
144
|
-
.
|
|
145
|
-
.toString('hex')} == ${circuitsOutput.header.contentCommitment.
|
|
145
|
+
`Txs effects hash mismatch, ${l2Block.body
|
|
146
|
+
.getTxsEffectsHash()
|
|
147
|
+
.toString('hex')} == ${circuitsOutput.header.contentCommitment.txsEffectsHash.toString('hex')} `,
|
|
146
148
|
);
|
|
147
149
|
}
|
|
148
150
|
|
|
@@ -189,22 +191,66 @@ export class SoloBlockBuilder implements BlockBuilder {
|
|
|
189
191
|
// padArrayEnd throws if the array is already full. Otherwise it pads till we reach the required size
|
|
190
192
|
const newL1ToL2MessagesTuple = padArrayEnd(newL1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
191
193
|
|
|
192
|
-
//
|
|
193
|
-
const
|
|
194
|
+
// Perform all tree insertions and retrieve snapshots for all base rollups
|
|
195
|
+
const baseRollupInputs: BaseRollupInputs[] = [];
|
|
196
|
+
const treeSnapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot>[] = [];
|
|
194
197
|
for (const tx of txs) {
|
|
195
|
-
|
|
198
|
+
const input = await this.buildBaseRollupInput(tx, globalVariables);
|
|
199
|
+
baseRollupInputs.push(input);
|
|
200
|
+
const promises = [
|
|
201
|
+
MerkleTreeId.NOTE_HASH_TREE,
|
|
202
|
+
MerkleTreeId.CONTRACT_TREE,
|
|
203
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
204
|
+
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
205
|
+
].map(async (id: MerkleTreeId) => {
|
|
206
|
+
return { key: id, value: await this.getTreeSnapshot(id) };
|
|
207
|
+
});
|
|
208
|
+
const snapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot> = new Map(
|
|
209
|
+
(await Promise.all(promises)).map(obj => [obj.key, obj.value]),
|
|
210
|
+
);
|
|
211
|
+
treeSnapshots.push(snapshots);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Run the base rollup circuits for the txs in parallel
|
|
215
|
+
const baseRollupOutputs: Promise<[BaseOrMergeRollupPublicInputs, Proof]>[] = [];
|
|
216
|
+
for (let i = 0; i < txs.length; i++) {
|
|
217
|
+
baseRollupOutputs.push(this.baseRollupCircuit(txs[i], baseRollupInputs[i], treeSnapshots[i]));
|
|
196
218
|
}
|
|
197
219
|
|
|
198
220
|
// Run merge rollups in layers until we have only two outputs
|
|
199
|
-
|
|
200
|
-
|
|
221
|
+
// All merge circuits for each layer are simulated in parallel
|
|
222
|
+
const [duration, mergeInputs] = await elapsed(() => Promise.all(baseRollupOutputs));
|
|
223
|
+
for (let i = 0; i < mergeInputs.length; i++) {
|
|
224
|
+
this.debug(`Simulated base rollup circuit`, {
|
|
225
|
+
eventName: 'circuit-simulation',
|
|
226
|
+
circuitName: 'base-rollup',
|
|
227
|
+
duration: duration / mergeInputs.length,
|
|
228
|
+
inputSize: baseRollupInputs[i].toBuffer().length,
|
|
229
|
+
outputSize: mergeInputs[i][0].toBuffer().length,
|
|
230
|
+
} satisfies CircuitSimulationStats);
|
|
231
|
+
}
|
|
232
|
+
let mergeRollupInputs: [BaseOrMergeRollupPublicInputs, Proof][] = mergeInputs;
|
|
201
233
|
while (mergeRollupInputs.length > 2) {
|
|
234
|
+
const mergeInputStructs: MergeRollupInputs[] = [];
|
|
202
235
|
for (const pair of chunk(mergeRollupInputs, 2)) {
|
|
203
236
|
const [r1, r2] = pair;
|
|
204
|
-
|
|
237
|
+
mergeInputStructs.push(this.createMergeRollupInputs(r1, r2));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const [duration, mergeOutputs] = await elapsed(() =>
|
|
241
|
+
Promise.all(mergeInputStructs.map(async input => await this.mergeRollupCircuit(input))),
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
for (let i = 0; i < mergeOutputs.length; i++) {
|
|
245
|
+
this.debug(`Simulated merge rollup circuit`, {
|
|
246
|
+
eventName: 'circuit-simulation',
|
|
247
|
+
circuitName: 'merge-rollup',
|
|
248
|
+
duration: duration / mergeOutputs.length,
|
|
249
|
+
inputSize: mergeInputStructs[i].toBuffer().length,
|
|
250
|
+
outputSize: mergeOutputs[i][0].toBuffer().length,
|
|
251
|
+
} satisfies CircuitSimulationStats);
|
|
205
252
|
}
|
|
206
|
-
mergeRollupInputs =
|
|
207
|
-
mergeRollupOutputs = [];
|
|
253
|
+
mergeRollupInputs = mergeOutputs;
|
|
208
254
|
}
|
|
209
255
|
|
|
210
256
|
// Run the root rollup with the last two merge rollups (or base, if no merge layers)
|
|
@@ -214,26 +260,29 @@ export class SoloBlockBuilder implements BlockBuilder {
|
|
|
214
260
|
|
|
215
261
|
protected async baseRollupCircuit(
|
|
216
262
|
tx: ProcessedTx,
|
|
217
|
-
|
|
263
|
+
inputs: BaseRollupInputs,
|
|
264
|
+
treeSnapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot>,
|
|
218
265
|
): Promise<[BaseOrMergeRollupPublicInputs, Proof]> {
|
|
219
266
|
this.debug(`Running base rollup for ${tx.hash}`);
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
await this.
|
|
223
|
-
const proof = await this.prover.getBaseRollupProof(rollupInput, rollupOutput);
|
|
267
|
+
const rollupOutput = await this.simulator.baseRollupCircuit(inputs);
|
|
268
|
+
this.validatePartialState(rollupOutput.end, treeSnapshots);
|
|
269
|
+
const proof = await this.prover.getBaseRollupProof(inputs, rollupOutput);
|
|
224
270
|
return [rollupOutput, proof];
|
|
225
271
|
}
|
|
226
272
|
|
|
227
|
-
protected
|
|
273
|
+
protected createMergeRollupInputs(
|
|
228
274
|
left: [BaseOrMergeRollupPublicInputs, Proof],
|
|
229
275
|
right: [BaseOrMergeRollupPublicInputs, Proof],
|
|
230
|
-
)
|
|
276
|
+
) {
|
|
231
277
|
const vk = this.getVerificationKey(left[0].rollupType);
|
|
232
278
|
const mergeInputs = new MergeRollupInputs([
|
|
233
279
|
this.getPreviousRollupDataFromPublicInputs(left[0], left[1], vk),
|
|
234
280
|
this.getPreviousRollupDataFromPublicInputs(right[0], right[1], vk),
|
|
235
281
|
]);
|
|
282
|
+
return mergeInputs;
|
|
283
|
+
}
|
|
236
284
|
|
|
285
|
+
protected async mergeRollupCircuit(mergeInputs: MergeRollupInputs): Promise<[BaseOrMergeRollupPublicInputs, Proof]> {
|
|
237
286
|
this.debug(`Running merge rollup circuit`);
|
|
238
287
|
const output = await this.simulator.mergeRollupCircuit(mergeInputs);
|
|
239
288
|
const proof = await this.prover.getMergeRollupProof(mergeInputs, output);
|
|
@@ -279,40 +328,50 @@ export class SoloBlockBuilder implements BlockBuilder {
|
|
|
279
328
|
return [rootOutput, rootProof];
|
|
280
329
|
}
|
|
281
330
|
|
|
282
|
-
protected
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
331
|
+
protected validatePartialState(
|
|
332
|
+
partialState: PartialStateReference,
|
|
333
|
+
treeSnapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot>,
|
|
334
|
+
) {
|
|
335
|
+
this.validateSimulatedTree(
|
|
336
|
+
treeSnapshots.get(MerkleTreeId.NOTE_HASH_TREE)!,
|
|
337
|
+
partialState.noteHashTree,
|
|
338
|
+
'NoteHashTree',
|
|
339
|
+
);
|
|
340
|
+
this.validateSimulatedTree(
|
|
341
|
+
treeSnapshots.get(MerkleTreeId.NULLIFIER_TREE)!,
|
|
342
|
+
partialState.nullifierTree,
|
|
343
|
+
'NullifierTree',
|
|
344
|
+
);
|
|
345
|
+
this.validateSimulatedTree(
|
|
346
|
+
treeSnapshots.get(MerkleTreeId.CONTRACT_TREE)!,
|
|
347
|
+
partialState.contractTree,
|
|
348
|
+
'ContractTree',
|
|
349
|
+
);
|
|
350
|
+
this.validateSimulatedTree(
|
|
351
|
+
treeSnapshots.get(MerkleTreeId.PUBLIC_DATA_TREE)!,
|
|
352
|
+
partialState.publicDataTree,
|
|
353
|
+
'PublicDataTree',
|
|
354
|
+
);
|
|
305
355
|
}
|
|
306
356
|
|
|
307
357
|
protected async validateState(state: StateReference) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
this.
|
|
315
|
-
|
|
358
|
+
const promises = [
|
|
359
|
+
MerkleTreeId.NOTE_HASH_TREE,
|
|
360
|
+
MerkleTreeId.CONTRACT_TREE,
|
|
361
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
362
|
+
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
363
|
+
].map(async (id: MerkleTreeId) => {
|
|
364
|
+
return { key: id, value: await this.getTreeSnapshot(id) };
|
|
365
|
+
});
|
|
366
|
+
const snapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot> = new Map(
|
|
367
|
+
(await Promise.all(promises)).map(obj => [obj.key, obj.value]),
|
|
368
|
+
);
|
|
369
|
+
this.validatePartialState(state.partial, snapshots);
|
|
370
|
+
this.validateSimulatedTree(
|
|
371
|
+
await this.getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE),
|
|
372
|
+
state.l1ToL2MessageTree,
|
|
373
|
+
'L1ToL2MessageTree',
|
|
374
|
+
);
|
|
316
375
|
}
|
|
317
376
|
|
|
318
377
|
// Validate that the roots of all local trees match the output of the root circuit simulation
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { ContractDataSource, L1ToL2MessageSource, L2BlockSource } from '@aztec/circuit-types';
|
|
2
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
2
3
|
import { P2P } from '@aztec/p2p';
|
|
3
4
|
import { WorldStateSynchronizer } from '@aztec/world-state';
|
|
4
5
|
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
|
|
5
8
|
import { SoloBlockBuilder } from '../block_builder/solo_block_builder.js';
|
|
6
9
|
import { SequencerClientConfig } from '../config.js';
|
|
7
10
|
import { getGlobalVariableBuilder } from '../global_variable_builder/index.js';
|
|
@@ -10,7 +13,32 @@ import { EmptyRollupProver } from '../prover/empty.js';
|
|
|
10
13
|
import { getL1Publisher } from '../publisher/index.js';
|
|
11
14
|
import { Sequencer, SequencerConfig } from '../sequencer/index.js';
|
|
12
15
|
import { PublicProcessorFactory } from '../sequencer/public_processor.js';
|
|
16
|
+
import { NativeACVMSimulator } from '../simulator/acvm_native.js';
|
|
17
|
+
import { WASMSimulator } from '../simulator/acvm_wasm.js';
|
|
13
18
|
import { RealRollupCircuitSimulator } from '../simulator/rollup.js';
|
|
19
|
+
import { SimulationProvider } from '../simulator/simulation_provider.js';
|
|
20
|
+
|
|
21
|
+
const logger = createDebugLogger('aztec:sequencer-client');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Factory function to create a simulation provider. Will attempt to use native binary simulation falling back to WASM if unavailable.
|
|
25
|
+
* @param config - The provided sequencer client configuration
|
|
26
|
+
* @returns The constructed simulation provider
|
|
27
|
+
*/
|
|
28
|
+
async function getSimulationProvider(config: SequencerClientConfig): Promise<SimulationProvider> {
|
|
29
|
+
if (config.acvmBinaryPath && config.acvmWorkingDirectory) {
|
|
30
|
+
try {
|
|
31
|
+
await fs.access(config.acvmBinaryPath, fs.constants.R_OK);
|
|
32
|
+
await fs.mkdir(config.acvmWorkingDirectory, { recursive: true });
|
|
33
|
+
logger(`Using native ACVM at ${config.acvmBinaryPath}`);
|
|
34
|
+
return new NativeACVMSimulator(config.acvmWorkingDirectory, config.acvmBinaryPath);
|
|
35
|
+
} catch {
|
|
36
|
+
logger(`Failed to access ACVM at ${config.acvmBinaryPath}, falling back to WASM`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
logger('Using WASM ACVM simulation');
|
|
40
|
+
return new WASMSimulator();
|
|
41
|
+
}
|
|
14
42
|
|
|
15
43
|
/**
|
|
16
44
|
* Encapsulates the full sequencer and publisher.
|
|
@@ -40,14 +68,21 @@ export class SequencerClient {
|
|
|
40
68
|
const globalsBuilder = getGlobalVariableBuilder(config);
|
|
41
69
|
const merkleTreeDb = worldStateSynchronizer.getLatest();
|
|
42
70
|
|
|
71
|
+
const simulationProvider = await getSimulationProvider(config);
|
|
72
|
+
|
|
43
73
|
const blockBuilder = new SoloBlockBuilder(
|
|
44
74
|
merkleTreeDb,
|
|
45
75
|
getVerificationKeys(),
|
|
46
|
-
new RealRollupCircuitSimulator(),
|
|
76
|
+
new RealRollupCircuitSimulator(simulationProvider),
|
|
47
77
|
new EmptyRollupProver(),
|
|
48
78
|
);
|
|
49
79
|
|
|
50
|
-
const publicProcessorFactory = new PublicProcessorFactory(
|
|
80
|
+
const publicProcessorFactory = new PublicProcessorFactory(
|
|
81
|
+
merkleTreeDb,
|
|
82
|
+
contractDataSource,
|
|
83
|
+
l1ToL2MessageSource,
|
|
84
|
+
simulationProvider,
|
|
85
|
+
);
|
|
51
86
|
|
|
52
87
|
const sequencer = new Sequencer(
|
|
53
88
|
publisher,
|
package/src/config.ts
CHANGED
|
@@ -48,6 +48,8 @@ export function getConfigEnvVars(): SequencerClientConfig {
|
|
|
48
48
|
OUTBOX_CONTRACT_ADDRESS,
|
|
49
49
|
COINBASE,
|
|
50
50
|
FEE_RECIPIENT,
|
|
51
|
+
ACVM_WORKING_DIRECTORY,
|
|
52
|
+
ACVM_BINARY_PATH,
|
|
51
53
|
} = process.env;
|
|
52
54
|
|
|
53
55
|
const publisherPrivateKey: Hex = SEQ_PUBLISHER_PRIVATE_KEY
|
|
@@ -82,5 +84,7 @@ export function getConfigEnvVars(): SequencerClientConfig {
|
|
|
82
84
|
// TODO: undefined should not be allowed for the following 2 values in PROD
|
|
83
85
|
coinbase: COINBASE ? EthAddress.fromString(COINBASE) : undefined,
|
|
84
86
|
feeRecipient: FEE_RECIPIENT ? AztecAddress.fromString(FEE_RECIPIENT) : undefined,
|
|
87
|
+
acvmWorkingDirectory: ACVM_WORKING_DIRECTORY ? ACVM_WORKING_DIRECTORY : undefined,
|
|
88
|
+
acvmBinaryPath: ACVM_BINARY_PATH ? ACVM_BINARY_PATH : undefined,
|
|
85
89
|
};
|
|
86
90
|
}
|
package/src/index.ts
CHANGED
|
@@ -12,4 +12,6 @@ export * from './global_variable_builder/index.js';
|
|
|
12
12
|
export { RealRollupCircuitSimulator } from './simulator/rollup.js';
|
|
13
13
|
export { EmptyRollupProver } from './prover/empty.js';
|
|
14
14
|
export { SoloBlockBuilder } from './block_builder/solo_block_builder.js';
|
|
15
|
+
export { WASMSimulator } from './simulator/acvm_wasm.js';
|
|
16
|
+
export { SimulationProvider } from './simulator/simulation_provider.js';
|
|
15
17
|
export { makeProcessedTx, makeEmptyProcessedTx } from './sequencer/processed_tx.js';
|
|
@@ -172,15 +172,15 @@ export class L1Publisher implements L2BlockReceiver {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
if (receipt.status) {
|
|
175
|
-
let
|
|
175
|
+
let txsEffectsHash;
|
|
176
176
|
if (receipt.logs.length === 1) {
|
|
177
|
-
//
|
|
178
|
-
|
|
177
|
+
// txsEffectsHash from IAvailabilityOracle.TxsPublished event
|
|
178
|
+
txsEffectsHash = receipt.logs[0].data;
|
|
179
179
|
} else {
|
|
180
180
|
this.log(`Expected 1 log, got ${receipt.logs.length}`);
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
this.log.info(`Block txs effects published,
|
|
183
|
+
this.log.info(`Block txs effects published, txsEffectsHash: ${txsEffectsHash}`);
|
|
184
184
|
break;
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -87,7 +87,7 @@ export class ViemTxSender implements L1PublisherTxSender {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
checkIfTxsAreAvailable(block: L2Block): Promise<boolean> {
|
|
90
|
-
const args = [`0x${block.body.
|
|
90
|
+
const args = [`0x${block.body.getTxsEffectsHash().toString('hex')}`] as const;
|
|
91
91
|
return this.availabilityOracleContract.read.isAvailable(args);
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -11,6 +11,7 @@ import { PublicProver } from '../prover/index.js';
|
|
|
11
11
|
import { PublicKernelCircuitSimulator } from '../simulator/index.js';
|
|
12
12
|
import { ContractsDataSourcePublicDB, WorldStateDB, WorldStatePublicDB } from '../simulator/public_executor.js';
|
|
13
13
|
import { RealPublicKernelCircuitSimulator } from '../simulator/public_kernel.js';
|
|
14
|
+
import { SimulationProvider } from '../simulator/simulation_provider.js';
|
|
14
15
|
import { AbstractPhaseManager } from './abstract_phase_manager.js';
|
|
15
16
|
import { PhaseManagerFactory } from './phase_manager_factory.js';
|
|
16
17
|
import { FailedTx, ProcessedTx, makeEmptyProcessedTx, makeProcessedTx } from './processed_tx.js';
|
|
@@ -23,6 +24,7 @@ export class PublicProcessorFactory {
|
|
|
23
24
|
private merkleTree: MerkleTreeOperations,
|
|
24
25
|
private contractDataSource: ContractDataSource,
|
|
25
26
|
private l1Tol2MessagesDataSource: L1ToL2MessageSource,
|
|
27
|
+
private simulator: SimulationProvider,
|
|
26
28
|
) {}
|
|
27
29
|
|
|
28
30
|
/**
|
|
@@ -45,7 +47,7 @@ export class PublicProcessorFactory {
|
|
|
45
47
|
return new PublicProcessor(
|
|
46
48
|
this.merkleTree,
|
|
47
49
|
publicExecutor,
|
|
48
|
-
new RealPublicKernelCircuitSimulator(),
|
|
50
|
+
new RealPublicKernelCircuitSimulator(this.simulator),
|
|
49
51
|
new EmptyPublicProver(),
|
|
50
52
|
globalVariables,
|
|
51
53
|
historicalHeader,
|
|
@@ -255,12 +255,12 @@ export class Sequencer {
|
|
|
255
255
|
return;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
const
|
|
258
|
+
const txsEffectsHash = block.body.getTxsEffectsHash();
|
|
259
259
|
this.log.info(`Publishing ${newContracts.length} contracts in block ${block.number}`);
|
|
260
260
|
|
|
261
261
|
const publishedContractData = await this.publisher.processNewContractData(
|
|
262
262
|
block.number,
|
|
263
|
-
|
|
263
|
+
txsEffectsHash,
|
|
264
264
|
newContracts,
|
|
265
265
|
);
|
|
266
266
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { randomBytes } from '@aztec/foundation/crypto';
|
|
2
|
+
import { NoirCompiledCircuit } from '@aztec/types/noir';
|
|
3
|
+
|
|
4
|
+
import { WitnessMap } from '@noir-lang/types';
|
|
5
|
+
import * as proc from 'child_process';
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
|
|
8
|
+
import { SimulationProvider } from './simulation_provider.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parses a TOML format witness map string into a Map structure
|
|
12
|
+
* @param outputString - The witness map in TOML format
|
|
13
|
+
* @returns The parsed witness map
|
|
14
|
+
*/
|
|
15
|
+
function parseIntoWitnessMap(outputString: string) {
|
|
16
|
+
const lines = outputString.split('\n');
|
|
17
|
+
return new Map<number, string>(
|
|
18
|
+
lines
|
|
19
|
+
.filter((line: string) => line.length)
|
|
20
|
+
.map((line: string) => {
|
|
21
|
+
const pair = line.replaceAll(' ', '').split('=');
|
|
22
|
+
return [Number(pair[0]), pair[1].replaceAll('"', '')];
|
|
23
|
+
}),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param inputWitness - The circuit's input witness
|
|
30
|
+
* @param bytecode - The circuit buytecode
|
|
31
|
+
* @param workingDirectory - A directory to use for temporary files by the ACVM
|
|
32
|
+
* @param pathToAcvm - The path to the ACVm binary
|
|
33
|
+
* @returns The completed partial witness outputted from the circuit
|
|
34
|
+
*/
|
|
35
|
+
export async function executeNativeCircuit(
|
|
36
|
+
inputWitness: WitnessMap,
|
|
37
|
+
bytecode: Buffer,
|
|
38
|
+
workingDirectory: string,
|
|
39
|
+
pathToAcvm: string,
|
|
40
|
+
) {
|
|
41
|
+
const bytecodeFilename = 'bytecode';
|
|
42
|
+
const witnessFilename = 'input_witness.toml';
|
|
43
|
+
|
|
44
|
+
// convert the witness map to TOML format
|
|
45
|
+
let witnessMap = '';
|
|
46
|
+
inputWitness.forEach((value: string, key: number) => {
|
|
47
|
+
witnessMap = witnessMap.concat(`${key} = '${value}'\n`);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// In case the directory is still around from some time previously, remove it
|
|
51
|
+
await fs.rm(workingDirectory, { recursive: true, force: true });
|
|
52
|
+
// Create the new working directory
|
|
53
|
+
await fs.mkdir(workingDirectory, { recursive: true });
|
|
54
|
+
// Write the bytecode and input witness to the working directory
|
|
55
|
+
await fs.writeFile(`${workingDirectory}/${bytecodeFilename}`, bytecode);
|
|
56
|
+
await fs.writeFile(`${workingDirectory}/${witnessFilename}`, witnessMap);
|
|
57
|
+
|
|
58
|
+
// Execute the ACVM using the given args
|
|
59
|
+
const args = [
|
|
60
|
+
`execute`,
|
|
61
|
+
`--working-directory`,
|
|
62
|
+
`${workingDirectory}`,
|
|
63
|
+
`--bytecode`,
|
|
64
|
+
`${bytecodeFilename}`,
|
|
65
|
+
`--input-witness`,
|
|
66
|
+
`${witnessFilename}`,
|
|
67
|
+
`--print`,
|
|
68
|
+
];
|
|
69
|
+
const processPromise = new Promise<string>((resolve, reject) => {
|
|
70
|
+
let outputWitness = Buffer.alloc(0);
|
|
71
|
+
let errorBuffer = Buffer.alloc(0);
|
|
72
|
+
const acvm = proc.spawn(pathToAcvm, args);
|
|
73
|
+
acvm.stdout.on('data', data => {
|
|
74
|
+
outputWitness = Buffer.concat([outputWitness, data]);
|
|
75
|
+
});
|
|
76
|
+
acvm.stderr.on('data', data => {
|
|
77
|
+
errorBuffer = Buffer.concat([errorBuffer, data]);
|
|
78
|
+
});
|
|
79
|
+
acvm.on('close', code => {
|
|
80
|
+
if (code === 0) {
|
|
81
|
+
resolve(outputWitness.toString('utf-8'));
|
|
82
|
+
} else {
|
|
83
|
+
reject(errorBuffer.toString('utf-8'));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const output = await processPromise;
|
|
90
|
+
return parseIntoWitnessMap(output);
|
|
91
|
+
} finally {
|
|
92
|
+
// Clean up the working directory before we leave
|
|
93
|
+
await fs.rm(workingDirectory, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export class NativeACVMSimulator implements SimulationProvider {
|
|
98
|
+
constructor(private workingDirectory: string, private pathToAcvm: string) {}
|
|
99
|
+
async simulateCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
|
|
100
|
+
// Execute the circuit on those initial witness values
|
|
101
|
+
|
|
102
|
+
// Decode the bytecode from base64 since the acvm does not know about base64 encoding
|
|
103
|
+
const decodedBytecode = Buffer.from(compiledCircuit.bytecode, 'base64');
|
|
104
|
+
|
|
105
|
+
// Provide a unique working directory so we don't get clashes with parallel executions
|
|
106
|
+
const directory = `${this.workingDirectory}/${randomBytes(32).toString('hex')}`;
|
|
107
|
+
// Execute the circuit
|
|
108
|
+
const _witnessMap = await executeNativeCircuit(input, decodedBytecode, directory, this.pathToAcvm);
|
|
109
|
+
|
|
110
|
+
return _witnessMap;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NoirCompiledCircuit } from '@aztec/types/noir';
|
|
2
|
+
|
|
3
|
+
import { WasmBlackBoxFunctionSolver, createBlackBoxSolver, executeCircuitWithBlackBoxSolver } from '@noir-lang/acvm_js';
|
|
4
|
+
import { WitnessMap } from '@noir-lang/types';
|
|
5
|
+
|
|
6
|
+
import { SimulationProvider } from './simulation_provider.js';
|
|
7
|
+
|
|
8
|
+
let solver: Promise<WasmBlackBoxFunctionSolver>;
|
|
9
|
+
|
|
10
|
+
const getSolver = (): Promise<WasmBlackBoxFunctionSolver> => {
|
|
11
|
+
if (!solver) {
|
|
12
|
+
solver = createBlackBoxSolver();
|
|
13
|
+
}
|
|
14
|
+
return solver;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class WASMSimulator implements SimulationProvider {
|
|
18
|
+
async simulateCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
|
|
19
|
+
// Execute the circuit on those initial witness values
|
|
20
|
+
//
|
|
21
|
+
// Decode the bytecode from base64 since the acvm does not know about base64 encoding
|
|
22
|
+
const decodedBytecode = Buffer.from(compiledCircuit.bytecode, 'base64');
|
|
23
|
+
//
|
|
24
|
+
// Execute the circuit
|
|
25
|
+
const _witnessMap = await executeCircuitWithBlackBoxSolver(await getSolver(), decodedBytecode, input, () => {
|
|
26
|
+
throw Error('unexpected oracle during execution');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return _witnessMap;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/simulator/index.ts
CHANGED
|
@@ -3,12 +3,19 @@ import { PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs } fro
|
|
|
3
3
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { elapsed } from '@aztec/foundation/timer';
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
PublicKernelAppLogicArtifact,
|
|
7
|
+
PublicKernelSetupArtifact,
|
|
8
|
+
PublicKernelTeardownArtifact,
|
|
9
|
+
convertPublicInnerRollupInputsToWitnessMap,
|
|
10
|
+
convertPublicInnerRollupOutputFromWitnessMap,
|
|
11
|
+
convertPublicSetupRollupInputsToWitnessMap,
|
|
12
|
+
convertPublicSetupRollupOutputFromWitnessMap,
|
|
13
|
+
convertPublicTailRollupInputsToWitnessMap,
|
|
14
|
+
convertPublicTailRollupOutputFromWitnessMap,
|
|
9
15
|
} from '@aztec/noir-protocol-circuits-types';
|
|
10
16
|
|
|
11
|
-
import { PublicKernelCircuitSimulator } from './index.js';
|
|
17
|
+
import { PublicKernelCircuitSimulator, WASMSimulator } from './index.js';
|
|
18
|
+
import { SimulationProvider } from './simulation_provider.js';
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
21
|
* Implements the PublicKernelCircuitSimulator.
|
|
@@ -16,6 +23,11 @@ import { PublicKernelCircuitSimulator } from './index.js';
|
|
|
16
23
|
export class RealPublicKernelCircuitSimulator implements PublicKernelCircuitSimulator {
|
|
17
24
|
private log = createDebugLogger('aztec:public-kernel-simulator');
|
|
18
25
|
|
|
26
|
+
// Some circuits are so small it is faster to use WASM
|
|
27
|
+
private wasmSimulator: WASMSimulator = new WASMSimulator();
|
|
28
|
+
|
|
29
|
+
constructor(private simulator: SimulationProvider) {}
|
|
30
|
+
|
|
19
31
|
/**
|
|
20
32
|
* Simulates the public kernel setup circuit from its inputs.
|
|
21
33
|
* @param input - Inputs to the circuit.
|
|
@@ -27,7 +39,11 @@ export class RealPublicKernelCircuitSimulator implements PublicKernelCircuitSimu
|
|
|
27
39
|
if (!input.previousKernel.publicInputs.needsSetup) {
|
|
28
40
|
throw new Error(`Expected previous kernel inputs to need setup`);
|
|
29
41
|
}
|
|
30
|
-
const
|
|
42
|
+
const inputWitness = convertPublicSetupRollupInputsToWitnessMap(input);
|
|
43
|
+
const [duration, witness] = await elapsed(() =>
|
|
44
|
+
this.wasmSimulator.simulateCircuit(inputWitness, PublicKernelSetupArtifact),
|
|
45
|
+
);
|
|
46
|
+
const result = convertPublicSetupRollupOutputFromWitnessMap(witness);
|
|
31
47
|
this.log(`Simulated public kernel setup circuit`, {
|
|
32
48
|
eventName: 'circuit-simulation',
|
|
33
49
|
circuitName: 'public-kernel-setup',
|
|
@@ -49,7 +65,11 @@ export class RealPublicKernelCircuitSimulator implements PublicKernelCircuitSimu
|
|
|
49
65
|
if (!input.previousKernel.publicInputs.needsAppLogic) {
|
|
50
66
|
throw new Error(`Expected previous kernel inputs to need app logic`);
|
|
51
67
|
}
|
|
52
|
-
const
|
|
68
|
+
const inputWitness = convertPublicInnerRollupInputsToWitnessMap(input);
|
|
69
|
+
const [duration, witness] = await elapsed(() =>
|
|
70
|
+
this.wasmSimulator.simulateCircuit(inputWitness, PublicKernelAppLogicArtifact),
|
|
71
|
+
);
|
|
72
|
+
const result = convertPublicInnerRollupOutputFromWitnessMap(witness);
|
|
53
73
|
this.log(`Simulated public kernel app logic circuit`, {
|
|
54
74
|
eventName: 'circuit-simulation',
|
|
55
75
|
circuitName: 'public-kernel-app-logic',
|
|
@@ -71,7 +91,11 @@ export class RealPublicKernelCircuitSimulator implements PublicKernelCircuitSimu
|
|
|
71
91
|
if (!input.previousKernel.publicInputs.needsTeardown) {
|
|
72
92
|
throw new Error(`Expected previous kernel inputs to need teardown`);
|
|
73
93
|
}
|
|
74
|
-
const
|
|
94
|
+
const inputWitness = convertPublicTailRollupInputsToWitnessMap(input);
|
|
95
|
+
const [duration, witness] = await elapsed(() =>
|
|
96
|
+
this.wasmSimulator.simulateCircuit(inputWitness, PublicKernelTeardownArtifact),
|
|
97
|
+
);
|
|
98
|
+
const result = convertPublicTailRollupOutputFromWitnessMap(witness);
|
|
75
99
|
this.log(`Simulated public kernel teardown circuit`, {
|
|
76
100
|
eventName: 'circuit-simulation',
|
|
77
101
|
circuitName: 'public-kernel-teardown',
|