@aztec/sequencer-client 0.27.2 → 0.28.1

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.
Files changed (55) hide show
  1. package/dest/block_builder/index.d.ts +3 -1
  2. package/dest/block_builder/index.d.ts.map +1 -1
  3. package/dest/block_builder/solo_block_builder.d.ts +10 -5
  4. package/dest/block_builder/solo_block_builder.d.ts.map +1 -1
  5. package/dest/block_builder/solo_block_builder.js +119 -52
  6. package/dest/prover/empty.d.ts +13 -1
  7. package/dest/prover/empty.d.ts.map +1 -1
  8. package/dest/prover/empty.js +19 -1
  9. package/dest/prover/index.d.ts +14 -2
  10. package/dest/prover/index.d.ts.map +1 -1
  11. package/dest/sequencer/abstract_phase_manager.d.ts +0 -2
  12. package/dest/sequencer/abstract_phase_manager.d.ts.map +1 -1
  13. package/dest/sequencer/abstract_phase_manager.js +30 -7
  14. package/dest/sequencer/app_logic_phase_manager.d.ts +0 -2
  15. package/dest/sequencer/app_logic_phase_manager.d.ts.map +1 -1
  16. package/dest/sequencer/app_logic_phase_manager.js +10 -15
  17. package/dest/sequencer/sequencer.d.ts +4 -1
  18. package/dest/sequencer/sequencer.d.ts.map +1 -1
  19. package/dest/sequencer/sequencer.js +8 -4
  20. package/dest/sequencer/setup_phase_manager.d.ts +0 -2
  21. package/dest/sequencer/setup_phase_manager.d.ts.map +1 -1
  22. package/dest/sequencer/setup_phase_manager.js +8 -12
  23. package/dest/sequencer/tail_phase_manager.d.ts +0 -2
  24. package/dest/sequencer/tail_phase_manager.d.ts.map +1 -1
  25. package/dest/sequencer/tail_phase_manager.js +7 -11
  26. package/dest/sequencer/teardown_phase_manager.d.ts +0 -2
  27. package/dest/sequencer/teardown_phase_manager.d.ts.map +1 -1
  28. package/dest/sequencer/teardown_phase_manager.js +8 -12
  29. package/dest/sequencer/utils.d.ts +8 -0
  30. package/dest/sequencer/utils.d.ts.map +1 -0
  31. package/dest/sequencer/utils.js +28 -0
  32. package/dest/simulator/index.d.ts +13 -1
  33. package/dest/simulator/index.d.ts.map +1 -1
  34. package/dest/simulator/index.js +1 -1
  35. package/dest/simulator/public_executor.d.ts +6 -3
  36. package/dest/simulator/public_executor.d.ts.map +1 -1
  37. package/dest/simulator/public_executor.js +36 -15
  38. package/dest/simulator/rollup.d.ts +13 -1
  39. package/dest/simulator/rollup.d.ts.map +1 -1
  40. package/dest/simulator/rollup.js +24 -2
  41. package/package.json +13 -13
  42. package/src/block_builder/index.ts +3 -1
  43. package/src/block_builder/solo_block_builder.ts +150 -52
  44. package/src/prover/empty.ts +23 -0
  45. package/src/prover/index.ts +18 -1
  46. package/src/sequencer/abstract_phase_manager.ts +37 -8
  47. package/src/sequencer/app_logic_phase_manager.ts +10 -16
  48. package/src/sequencer/sequencer.ts +12 -3
  49. package/src/sequencer/setup_phase_manager.ts +8 -15
  50. package/src/sequencer/tail_phase_manager.ts +6 -11
  51. package/src/sequencer/teardown_phase_manager.ts +8 -15
  52. package/src/sequencer/utils.ts +30 -0
  53. package/src/simulator/index.ts +15 -0
  54. package/src/simulator/public_executor.ts +37 -14
  55. package/src/simulator/rollup.ts +39 -0
@@ -4,6 +4,7 @@ import {
4
4
  ARCHIVE_HEIGHT,
5
5
  AppendOnlyTreeSnapshot,
6
6
  BaseOrMergeRollupPublicInputs,
7
+ BaseParityInputs,
7
8
  BaseRollupInputs,
8
9
  ConstantRollupData,
9
10
  GlobalVariables,
@@ -20,6 +21,7 @@ import {
20
21
  NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH,
21
22
  NULLIFIER_TREE_HEIGHT,
22
23
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
24
+ NUM_BASE_PARITY_PER_ROOT_PARITY,
23
25
  NullifierLeafPreimage,
24
26
  PUBLIC_DATA_SUBTREE_HEIGHT,
25
27
  PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH,
@@ -33,6 +35,8 @@ import {
33
35
  RollupKernelCircuitPublicInputs,
34
36
  RollupKernelData,
35
37
  RollupTypes,
38
+ RootParityInput,
39
+ RootParityInputs,
36
40
  RootRollupInputs,
37
41
  RootRollupPublicInputs,
38
42
  StateDiffHints,
@@ -85,6 +89,7 @@ export class SoloBlockBuilder implements BlockBuilder {
85
89
  * Builds an L2 block with the given number containing the given txs, updating state trees.
86
90
  * @param globalVariables - Global variables to be used in the block.
87
91
  * @param txs - Processed transactions to include in the block.
92
+ * @param newModelL1ToL2Messages - L1 to L2 messages emitted by the new inbox.
88
93
  * @param newL1ToL2Messages - L1 to L2 messages to be part of the block.
89
94
  * @param timestamp - Timestamp of the block.
90
95
  * @returns The new L2 block and a correctness proof as returned by the root rollup circuit.
@@ -92,13 +97,19 @@ export class SoloBlockBuilder implements BlockBuilder {
92
97
  public async buildL2Block(
93
98
  globalVariables: GlobalVariables,
94
99
  txs: ProcessedTx[],
100
+ newModelL1ToL2Messages: Fr[], // TODO(#4492): Rename this when purging the old inbox
95
101
  newL1ToL2Messages: Fr[],
96
102
  ): Promise<[L2Block, Proof]> {
97
103
  // Check txs are good for processing by checking if all the tree snapshots in header are non-empty
98
104
  this.validateTxs(txs);
99
105
 
100
106
  // We fill the tx batch with empty txs, we process only one tx at a time for now
101
- const [circuitsOutput, proof] = await this.runCircuits(globalVariables, txs, newL1ToL2Messages);
107
+ const [circuitsOutput, proof] = await this.runCircuits(
108
+ globalVariables,
109
+ txs,
110
+ newModelL1ToL2Messages,
111
+ newL1ToL2Messages,
112
+ );
102
113
 
103
114
  // Collect all new nullifiers, commitments, and contracts from all txs in this block
104
115
  const txEffects: TxEffect[] = txs.map(tx => toTxEffect(tx));
@@ -149,7 +160,8 @@ export class SoloBlockBuilder implements BlockBuilder {
149
160
  protected async runCircuits(
150
161
  globalVariables: GlobalVariables,
151
162
  txs: ProcessedTx[],
152
- newL1ToL2Messages: Fr[],
163
+ newModelL1ToL2Messages: Fr[], // TODO(#4492): Rename this when purging the old inbox
164
+ newL1ToL2Messages: Fr[], // TODO(#4492): Nuke this when purging the old inbox
153
165
  ): Promise<[RootRollupPublicInputs, Proof]> {
154
166
  // Check that the length of the array of txs is a power of two
155
167
  // See https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
@@ -157,71 +169,154 @@ export class SoloBlockBuilder implements BlockBuilder {
157
169
  throw new Error(`Length of txs for the block should be a power of two and at least two (got ${txs.length})`);
158
170
  }
159
171
 
172
+ // BASE PARITY CIRCUIT (run in parallel)
173
+ let baseParityInputs: BaseParityInputs[] = [];
174
+ let elapsedBaseParityOutputsPromise: Promise<[number, RootParityInput[]]>;
175
+ {
176
+ const newModelL1ToL2MessagesTuple = padArrayEnd(
177
+ newModelL1ToL2Messages,
178
+ Fr.ZERO,
179
+ NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
180
+ );
181
+ baseParityInputs = Array.from({ length: NUM_BASE_PARITY_PER_ROOT_PARITY }, (_, i) =>
182
+ BaseParityInputs.fromSlice(newModelL1ToL2MessagesTuple, i),
183
+ );
184
+
185
+ const baseParityOutputs: Promise<RootParityInput>[] = [];
186
+ for (const inputs of baseParityInputs) {
187
+ baseParityOutputs.push(this.baseParityCircuit(inputs));
188
+ }
189
+ elapsedBaseParityOutputsPromise = elapsed(() => Promise.all(baseParityOutputs));
190
+ }
191
+
160
192
  // padArrayEnd throws if the array is already full. Otherwise it pads till we reach the required size
161
193
  const newL1ToL2MessagesTuple = padArrayEnd(newL1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
162
194
 
163
- // Perform all tree insertions and retrieve snapshots for all base rollups
195
+ // BASE ROLLUP CIRCUIT (run in parallel)
196
+ let elapsedBaseRollupOutputsPromise: Promise<[number, [BaseOrMergeRollupPublicInputs, Proof][]]>;
164
197
  const baseRollupInputs: BaseRollupInputs[] = [];
165
- const treeSnapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot>[] = [];
166
- for (const tx of txs) {
167
- const input = await this.buildBaseRollupInput(tx, globalVariables);
168
- baseRollupInputs.push(input);
169
- const promises = [MerkleTreeId.NOTE_HASH_TREE, MerkleTreeId.NULLIFIER_TREE, MerkleTreeId.PUBLIC_DATA_TREE].map(
170
- async (id: MerkleTreeId) => {
171
- return { key: id, value: await this.getTreeSnapshot(id) };
172
- },
173
- );
174
- const snapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot> = new Map(
175
- (await Promise.all(promises)).map(obj => [obj.key, obj.value]),
176
- );
177
- treeSnapshots.push(snapshots);
178
- }
198
+ {
199
+ // Perform all tree insertions and retrieve snapshots for all base rollups
200
+ const treeSnapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot>[] = [];
201
+ for (const tx of txs) {
202
+ const input = await this.buildBaseRollupInput(tx, globalVariables);
203
+ baseRollupInputs.push(input);
204
+ const promises = [MerkleTreeId.NOTE_HASH_TREE, MerkleTreeId.NULLIFIER_TREE, MerkleTreeId.PUBLIC_DATA_TREE].map(
205
+ async (id: MerkleTreeId) => {
206
+ return { key: id, value: await this.getTreeSnapshot(id) };
207
+ },
208
+ );
209
+ const snapshots: Map<MerkleTreeId, AppendOnlyTreeSnapshot> = new Map(
210
+ (await Promise.all(promises)).map(obj => [obj.key, obj.value]),
211
+ );
212
+ treeSnapshots.push(snapshots);
213
+ }
179
214
 
180
- // Run the base rollup circuits for the txs in parallel
181
- const baseRollupOutputs: Promise<[BaseOrMergeRollupPublicInputs, Proof]>[] = [];
182
- for (let i = 0; i < txs.length; i++) {
183
- baseRollupOutputs.push(this.baseRollupCircuit(txs[i], baseRollupInputs[i], treeSnapshots[i]));
184
- }
215
+ // Run the base rollup circuits for the txs in parallel
216
+ const baseRollupOutputs: Promise<[BaseOrMergeRollupPublicInputs, Proof]>[] = [];
217
+ for (let i = 0; i < txs.length; i++) {
218
+ baseRollupOutputs.push(this.baseRollupCircuit(txs[i], baseRollupInputs[i], treeSnapshots[i]));
219
+ }
185
220
 
186
- // Run merge rollups in layers until we have only two outputs
187
- // All merge circuits for each layer are simulated in parallel
188
- const [duration, mergeInputs] = await elapsed(() => Promise.all(baseRollupOutputs));
189
- for (let i = 0; i < mergeInputs.length; i++) {
190
- this.debug(`Simulated base rollup circuit`, {
191
- eventName: 'circuit-simulation',
192
- circuitName: 'base-rollup',
193
- duration: duration / mergeInputs.length,
194
- inputSize: baseRollupInputs[i].toBuffer().length,
195
- outputSize: mergeInputs[i][0].toBuffer().length,
196
- } satisfies CircuitSimulationStats);
221
+ elapsedBaseRollupOutputsPromise = elapsed(() => Promise.all(baseRollupOutputs));
197
222
  }
198
- let mergeRollupInputs: [BaseOrMergeRollupPublicInputs, Proof][] = mergeInputs;
199
- while (mergeRollupInputs.length > 2) {
200
- const mergeInputStructs: MergeRollupInputs[] = [];
201
- for (const pair of chunk(mergeRollupInputs, 2)) {
202
- const [r1, r2] = pair;
203
- mergeInputStructs.push(this.createMergeRollupInputs(r1, r2));
223
+
224
+ // ROOT PARITY CIRCUIT
225
+ let elapsedRootParityOutputPromise: Promise<[number, RootParityInput]>;
226
+ let rootParityInputs: RootParityInputs;
227
+ {
228
+ // First we await the base parity outputs
229
+ const [duration, baseParityOutputs] = await elapsedBaseParityOutputsPromise;
230
+
231
+ // We emit stats for base parity circuits
232
+ for (let i = 0; i < baseParityOutputs.length; i++) {
233
+ this.debug(`Simulated base parity circuit`, {
234
+ eventName: 'circuit-simulation',
235
+ circuitName: 'base-parity',
236
+ duration: duration / baseParityOutputs.length,
237
+ inputSize: baseParityInputs[i].toBuffer().length,
238
+ outputSize: baseParityOutputs[i].toBuffer().length,
239
+ } satisfies CircuitSimulationStats);
204
240
  }
205
241
 
206
- const [duration, mergeOutputs] = await elapsed(() =>
207
- Promise.all(mergeInputStructs.map(async input => await this.mergeRollupCircuit(input))),
242
+ rootParityInputs = new RootParityInputs(
243
+ baseParityOutputs as Tuple<RootParityInput, typeof NUM_BASE_PARITY_PER_ROOT_PARITY>,
208
244
  );
245
+ elapsedRootParityOutputPromise = elapsed(() => this.rootParityCircuit(rootParityInputs));
246
+ }
247
+
248
+ // MERGE ROLLUP CIRCUIT (each layer run in parallel)
249
+ let mergeOutputLeft: [BaseOrMergeRollupPublicInputs, Proof];
250
+ let mergeOutputRight: [BaseOrMergeRollupPublicInputs, Proof];
251
+ {
252
+ // Run merge rollups in layers until we have only two outputs
253
+ const [duration, mergeInputs] = await elapsedBaseRollupOutputsPromise;
209
254
 
210
- for (let i = 0; i < mergeOutputs.length; i++) {
211
- this.debug(`Simulated merge rollup circuit`, {
255
+ // We emit stats for base rollup circuits
256
+ for (let i = 0; i < mergeInputs.length; i++) {
257
+ this.debug(`Simulated base rollup circuit`, {
212
258
  eventName: 'circuit-simulation',
213
- circuitName: 'merge-rollup',
214
- duration: duration / mergeOutputs.length,
215
- inputSize: mergeInputStructs[i].toBuffer().length,
216
- outputSize: mergeOutputs[i][0].toBuffer().length,
259
+ circuitName: 'base-rollup',
260
+ duration: duration / mergeInputs.length,
261
+ inputSize: baseRollupInputs[i].toBuffer().length,
262
+ outputSize: mergeInputs[i][0].toBuffer().length,
217
263
  } satisfies CircuitSimulationStats);
218
264
  }
219
- mergeRollupInputs = mergeOutputs;
265
+
266
+ let mergeRollupInputs: [BaseOrMergeRollupPublicInputs, Proof][] = mergeInputs;
267
+ while (mergeRollupInputs.length > 2) {
268
+ const mergeInputStructs: MergeRollupInputs[] = [];
269
+ for (const pair of chunk(mergeRollupInputs, 2)) {
270
+ const [r1, r2] = pair;
271
+ mergeInputStructs.push(this.createMergeRollupInputs(r1, r2));
272
+ }
273
+
274
+ const [duration, mergeOutputs] = await elapsed(() =>
275
+ Promise.all(mergeInputStructs.map(async input => await this.mergeRollupCircuit(input))),
276
+ );
277
+
278
+ // We emit stats for merge rollup circuits
279
+ for (let i = 0; i < mergeOutputs.length; i++) {
280
+ this.debug(`Simulated merge rollup circuit`, {
281
+ eventName: 'circuit-simulation',
282
+ circuitName: 'merge-rollup',
283
+ duration: duration / mergeOutputs.length,
284
+ inputSize: mergeInputStructs[i].toBuffer().length,
285
+ outputSize: mergeOutputs[i][0].toBuffer().length,
286
+ } satisfies CircuitSimulationStats);
287
+ }
288
+ mergeRollupInputs = mergeOutputs;
289
+ }
290
+
291
+ // Run the root rollup with the last two merge rollups (or base, if no merge layers)
292
+ [mergeOutputLeft, mergeOutputRight] = mergeRollupInputs;
220
293
  }
221
294
 
222
- // Run the root rollup with the last two merge rollups (or base, if no merge layers)
223
- const [mergeOutputLeft, mergeOutputRight] = mergeRollupInputs;
224
- return this.rootRollupCircuit(mergeOutputLeft, mergeOutputRight, newL1ToL2MessagesTuple);
295
+ // Finally, we emit stats for root parity circuit
296
+ const [duration, rootParityOutput] = await elapsedRootParityOutputPromise;
297
+ this.debug(`Simulated root parity circuit`, {
298
+ eventName: 'circuit-simulation',
299
+ circuitName: 'root-parity',
300
+ duration: duration,
301
+ inputSize: rootParityInputs.toBuffer().length,
302
+ outputSize: rootParityOutput.toBuffer().length,
303
+ } satisfies CircuitSimulationStats);
304
+
305
+ return this.rootRollupCircuit(mergeOutputLeft, mergeOutputRight, rootParityOutput, newL1ToL2MessagesTuple);
306
+ }
307
+
308
+ protected async baseParityCircuit(inputs: BaseParityInputs): Promise<RootParityInput> {
309
+ this.debug(`Running base parity circuit`);
310
+ const parityPublicInputs = await this.simulator.baseParityCircuit(inputs);
311
+ const proof = await this.prover.getBaseParityProof(inputs, parityPublicInputs);
312
+ return new RootParityInput(proof, parityPublicInputs);
313
+ }
314
+
315
+ protected async rootParityCircuit(inputs: RootParityInputs): Promise<RootParityInput> {
316
+ this.debug(`Running root parity circuit`);
317
+ const parityPublicInputs = await this.simulator.rootParityCircuit(inputs);
318
+ const proof = await this.prover.getRootParityProof(inputs, parityPublicInputs);
319
+ return new RootParityInput(proof, parityPublicInputs);
225
320
  }
226
321
 
227
322
  protected async baseRollupCircuit(
@@ -269,10 +364,11 @@ export class SoloBlockBuilder implements BlockBuilder {
269
364
  protected async rootRollupCircuit(
270
365
  left: [BaseOrMergeRollupPublicInputs, Proof],
271
366
  right: [BaseOrMergeRollupPublicInputs, Proof],
367
+ l1ToL2Roots: RootParityInput,
272
368
  newL1ToL2Messages: Tuple<Fr, typeof NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP>,
273
369
  ): Promise<[RootRollupPublicInputs, Proof]> {
274
370
  this.debug(`Running root rollup circuit`);
275
- const rootInput = await this.getRootRollupInput(...left, ...right, newL1ToL2Messages);
371
+ const rootInput = await this.getRootRollupInput(...left, ...right, l1ToL2Roots, newL1ToL2Messages);
276
372
 
277
373
  // Update the local trees to include the new l1 to l2 messages
278
374
  await this.db.appendLeaves(
@@ -365,6 +461,7 @@ export class SoloBlockBuilder implements BlockBuilder {
365
461
  rollupProofLeft: Proof,
366
462
  rollupOutputRight: BaseOrMergeRollupPublicInputs,
367
463
  rollupProofRight: Proof,
464
+ l1ToL2Roots: RootParityInput,
368
465
  newL1ToL2Messages: Tuple<Fr, typeof NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP>,
369
466
  ) {
370
467
  const vk = this.getVerificationKey(rollupOutputLeft.rollupType);
@@ -406,6 +503,7 @@ export class SoloBlockBuilder implements BlockBuilder {
406
503
 
407
504
  return RootRollupInputs.from({
408
505
  previousRollupData,
506
+ l1ToL2Roots,
409
507
  newL1ToL2Messages,
410
508
  newL1ToL2MessageTreeRootSiblingPath,
411
509
  startL1ToL2MessageTreeSnapshot,
@@ -2,11 +2,14 @@
2
2
  import {
3
3
  AggregationObject,
4
4
  BaseOrMergeRollupPublicInputs,
5
+ BaseParityInputs,
5
6
  BaseRollupInputs,
6
7
  MergeRollupInputs,
8
+ ParityPublicInputs,
7
9
  Proof,
8
10
  PublicCircuitPublicInputs,
9
11
  PublicKernelCircuitPublicInputs,
12
+ RootParityInputs,
10
13
  RootRollupInputs,
11
14
  RootRollupPublicInputs,
12
15
  } from '@aztec/circuits.js';
@@ -22,6 +25,26 @@ const EMPTY_PROOF_SIZE = 42;
22
25
  * Prover implementation that returns empty proofs and overrides aggregation objects.
23
26
  */
24
27
  export class EmptyRollupProver implements RollupProver {
28
+ /**
29
+ * Creates an empty proof for the given input.
30
+ * @param inputs - Inputs to the circuit.
31
+ * @param publicInputs - Public inputs of the circuit obtained via simulation, modified by this call.
32
+ */
33
+ async getBaseParityProof(inputs: BaseParityInputs, publicInputs: ParityPublicInputs): Promise<Proof> {
34
+ publicInputs.aggregationObject = AggregationObject.makeFake();
35
+ return new Proof(Buffer.alloc(EMPTY_PROOF_SIZE, 0));
36
+ }
37
+
38
+ /**
39
+ * Creates an empty proof for the given input.
40
+ * @param inputs - Inputs to the circuit.
41
+ * @param publicInputs - Public inputs of the circuit obtained via simulation, modified by this call.
42
+ */
43
+ async getRootParityProof(inputs: RootParityInputs, publicInputs: ParityPublicInputs): Promise<Proof> {
44
+ publicInputs.aggregationObject = AggregationObject.makeFake();
45
+ return new Proof(Buffer.alloc(EMPTY_PROOF_SIZE, 0));
46
+ }
47
+
25
48
  /**
26
49
  * Creates an empty proof for the given input.
27
50
  * @param _input - Input to the circuit.
@@ -1,18 +1,35 @@
1
1
  import {
2
2
  BaseOrMergeRollupPublicInputs,
3
+ BaseParityInputs,
3
4
  BaseRollupInputs,
4
5
  MergeRollupInputs,
6
+ ParityPublicInputs,
5
7
  Proof,
6
8
  PublicCircuitPublicInputs,
7
9
  PublicKernelCircuitPublicInputs,
10
+ RootParityInputs,
8
11
  RootRollupInputs,
9
12
  RootRollupPublicInputs,
10
13
  } from '@aztec/circuits.js';
11
14
 
12
15
  /**
13
- * Generates proofs for the base, merge, and root rollup circuits.
16
+ * Generates proofs for parity and rollup circuits.
14
17
  */
15
18
  export interface RollupProver {
19
+ /**
20
+ * Creates a proof for the given input.
21
+ * @param input - Input to the circuit.
22
+ * @param publicInputs - Public inputs of the circuit obtained via simulation, modified by this call.
23
+ */
24
+ getBaseParityProof(inputs: BaseParityInputs, publicInputs: ParityPublicInputs): Promise<Proof>;
25
+
26
+ /**
27
+ * Creates a proof for the given input.
28
+ * @param input - Input to the circuit.
29
+ * @param publicInputs - Public inputs of the circuit obtained via simulation, modified by this call.
30
+ */
31
+ getRootParityProof(inputs: RootParityInputs, publicInputs: ParityPublicInputs): Promise<Proof>;
32
+
16
33
  /**
17
34
  * Creates a proof for the given input.
18
35
  * @param input - Input to the circuit.
@@ -59,7 +59,7 @@ import { getVerificationKeys } from '../mocks/verification_keys.js';
59
59
  import { PublicProver } from '../prover/index.js';
60
60
  import { PublicKernelCircuitSimulator } from '../simulator/index.js';
61
61
  import { HintsBuilder } from './hints_builder.js';
62
- import { FailedTx } from './processed_tx.js';
62
+ import { lastSideEffectCounter } from './utils.js';
63
63
 
64
64
  export enum PublicKernelPhase {
65
65
  SETUP = 'setup',
@@ -114,7 +114,6 @@ export abstract class AbstractPhaseManager {
114
114
  */
115
115
  revertReason: SimulationError | undefined;
116
116
  }>;
117
- abstract rollback(tx: Tx, err: unknown): Promise<FailedTx>;
118
117
 
119
118
  public static extractEnqueuedPublicCallsByPhase(
120
119
  publicInputs: PrivateKernelTailCircuitPublicInputs,
@@ -154,6 +153,14 @@ export abstract class AbstractPhaseManager {
154
153
  [PublicKernelPhase.TEARDOWN]: [],
155
154
  [PublicKernelPhase.TAIL]: [],
156
155
  };
156
+ } else if (firstRevertibleCallIndex === -1) {
157
+ // there's no app logic, split the functions between setup (many) and teardown (just one function call)
158
+ return {
159
+ [PublicKernelPhase.SETUP]: publicCallsStack.slice(0, -1),
160
+ [PublicKernelPhase.APP_LOGIC]: [],
161
+ [PublicKernelPhase.TEARDOWN]: [publicCallsStack[publicCallsStack.length - 1]],
162
+ [PublicKernelPhase.TAIL]: [],
163
+ };
157
164
  } else {
158
165
  return {
159
166
  [PublicKernelPhase.SETUP]: publicCallsStack.slice(0, firstRevertibleCallIndex - 1),
@@ -202,16 +209,27 @@ export abstract class AbstractPhaseManager {
202
209
  const current = executionStack.pop()!;
203
210
  const isExecutionRequest = !isPublicExecutionResult(current);
204
211
 
212
+ const sideEffectCounter = lastSideEffectCounter(tx) + 1;
205
213
  // NOTE: temporary glue to incorporate avm execution calls
206
214
  const simulator = (execution: PublicExecution, globalVariables: GlobalVariables) =>
207
215
  env.AVM_ENABLED
208
- ? this.publicExecutor.simulateAvm(execution, globalVariables)
209
- : this.publicExecutor.simulate(execution, globalVariables);
216
+ ? this.publicExecutor.simulateAvm(execution, globalVariables, sideEffectCounter)
217
+ : this.publicExecutor.simulate(execution, globalVariables, sideEffectCounter);
210
218
 
211
219
  const result = isExecutionRequest ? await simulator(current, this.globalVariables) : current;
212
220
 
213
- newUnencryptedFunctionLogs.push(result.unencryptedLogs);
214
221
  const functionSelector = result.execution.functionData.selector.toString();
222
+ if (result.reverted && !PhaseIsRevertible[this.phase]) {
223
+ this.log.debug(
224
+ `Simulation error on ${result.execution.contractAddress.toString()}:${functionSelector} with reason: ${
225
+ result.revertReason
226
+ }`,
227
+ );
228
+ throw result.revertReason;
229
+ }
230
+
231
+ newUnencryptedFunctionLogs.push(result.unencryptedLogs);
232
+
215
233
  this.log.debug(
216
234
  `Running public kernel circuit for ${result.execution.contractAddress.toString()}:${functionSelector}`,
217
235
  );
@@ -220,14 +238,23 @@ export abstract class AbstractPhaseManager {
220
238
 
221
239
  [kernelOutput, kernelProof] = await this.runKernelCircuit(kernelOutput, kernelProof, callData);
222
240
 
223
- if (kernelOutput.reverted && this.phase === PublicKernelPhase.APP_LOGIC) {
241
+ // sanity check. Note we can't expect them to just be equal, because e.g.
242
+ // if the simulator reverts in app logic, it "resets" and result.reverted will be false when we run teardown,
243
+ // but the kernel carries the reverted flag forward. But if the simulator reverts, so should the kernel.
244
+ if (result.reverted && !kernelOutput.reverted) {
245
+ throw new Error(
246
+ `Public kernel circuit did not revert on ${result.execution.contractAddress.toString()}:${functionSelector}, but simulator did.`,
247
+ );
248
+ }
249
+
250
+ // We know the phase is revertible due to the above check.
251
+ // So safely return the revert reason and the kernel output (which has had its revertible side effects dropped)
252
+ if (result.reverted) {
224
253
  this.log.debug(
225
254
  `Reverting on ${result.execution.contractAddress.toString()}:${functionSelector} with reason: ${
226
255
  result.revertReason
227
256
  }`,
228
257
  );
229
- // halt immediately if the public kernel circuit has reverted.
230
- // return no logs, as they should not go on-chain.
231
258
  return [kernelOutput, kernelProof, [], result.revertReason];
232
259
  }
233
260
 
@@ -333,6 +360,8 @@ export abstract class AbstractPhaseManager {
333
360
  newNoteHashes: padArrayEnd(result.newNoteHashes, SideEffect.empty(), MAX_NEW_NOTE_HASHES_PER_CALL),
334
361
  newNullifiers: padArrayEnd(result.newNullifiers, SideEffectLinkedToNoteHash.empty(), MAX_NEW_NULLIFIERS_PER_CALL),
335
362
  newL2ToL1Msgs: padArrayEnd(result.newL2ToL1Messages, L2ToL1Message.empty(), MAX_NEW_L2_TO_L1_MSGS_PER_CALL),
363
+ startSideEffectCounter: result.startSideEffectCounter,
364
+ endSideEffectCounter: result.endSideEffectCounter,
336
365
  returnValues: padArrayEnd(result.returnValues, Fr.ZERO, RETURN_VALUES_LENGTH),
337
366
  nullifierReadRequests: padArrayEnd(
338
367
  result.nullifierReadRequests,
@@ -7,7 +7,6 @@ import { PublicProver } from '../prover/index.js';
7
7
  import { PublicKernelCircuitSimulator } from '../simulator/index.js';
8
8
  import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js';
9
9
  import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
10
- import { FailedTx } from './processed_tx.js';
11
10
 
12
11
  /**
13
12
  * The phase manager responsible for performing the fee preparation phase.
@@ -39,27 +38,22 @@ export class AppLogicPhaseManager extends AbstractPhaseManager {
39
38
  this.log(`Processing tx ${tx.getTxHash()}`);
40
39
  await this.publicContractsDB.addNewContracts(tx);
41
40
  const [publicKernelOutput, publicKernelProof, newUnencryptedFunctionLogs, revertReason] =
42
- await this.processEnqueuedPublicCalls(tx, previousPublicKernelOutput, previousPublicKernelProof);
41
+ await this.processEnqueuedPublicCalls(tx, previousPublicKernelOutput, previousPublicKernelProof).catch(
42
+ // if we throw for any reason other than simulation, we need to rollback and drop the TX
43
+ async err => {
44
+ await this.publicStateDB.rollbackToCommit();
45
+ throw err;
46
+ },
47
+ );
43
48
 
44
49
  if (revertReason) {
45
- await this.rollback(tx, revertReason);
50
+ await this.publicContractsDB.removeNewContracts(tx);
51
+ await this.publicStateDB.rollbackToCheckpoint();
46
52
  } else {
47
53
  tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
48
- await this.publicStateDB.commit();
54
+ await this.publicStateDB.checkpoint();
49
55
  }
50
56
 
51
57
  return { publicKernelOutput, publicKernelProof, revertReason };
52
58
  }
53
-
54
- async rollback(tx: Tx, err: unknown): Promise<FailedTx> {
55
- this.log.warn(`Rolling back changes from ${tx.getTxHash()}`);
56
- // remove contracts on failure
57
- await this.publicContractsDB.removeNewContracts(tx);
58
- // rollback any state updates from this failed transaction
59
- await this.publicStateDB.rollback();
60
- return {
61
- tx,
62
- error: err instanceof Error ? err : new Error('Unknown error'),
63
- };
64
- }
65
59
  }
@@ -202,6 +202,8 @@ export class Sequencer {
202
202
 
203
203
  await assertBlockHeight();
204
204
 
205
+ const newModelL1ToL2Messages = await this.l1ToL2MessageSource.getNewL1ToL2Messages(BigInt(newBlockNumber));
206
+
205
207
  // Get l1 to l2 messages from the contract
206
208
  this.log('Requesting L1 to L2 messages from contract');
207
209
  const l1ToL2Messages = await this.getPendingL1ToL2EntryKeys();
@@ -214,7 +216,7 @@ export class Sequencer {
214
216
 
215
217
  const emptyTx = processor.makeEmptyProcessedTx();
216
218
  const [rollupCircuitsDuration, block] = await elapsed(() =>
217
- this.buildBlock(processedValidTxs, l1ToL2Messages, emptyTx, newGlobalVariables),
219
+ this.buildBlock(processedValidTxs, newModelL1ToL2Messages, l1ToL2Messages, emptyTx, newGlobalVariables),
218
220
  );
219
221
 
220
222
  this.log(`Assembled block ${block.number}`, {
@@ -312,6 +314,7 @@ export class Sequencer {
312
314
  /**
313
315
  * Pads the set of txs to a power of two and assembles a block by calling the block builder.
314
316
  * @param txs - Processed txs to include in the next block.
317
+ * @param newModelL1ToL2Messages - L1 to L2 messages emitted by the new inbox.
315
318
  * @param newL1ToL2Messages - L1 to L2 messages to be part of the block.
316
319
  * @param emptyTx - Empty tx to repeat at the end of the block to pad to a power of two.
317
320
  * @param globalVariables - Global variables to use in the block.
@@ -319,7 +322,8 @@ export class Sequencer {
319
322
  */
320
323
  protected async buildBlock(
321
324
  txs: ProcessedTx[],
322
- newL1ToL2Messages: Fr[],
325
+ newModelL1ToL2Messages: Fr[], // TODO(#4492): Rename this when purging the old inbox
326
+ newL1ToL2Messages: Fr[], // TODO(#4492): Nuke this when purging the old inbox
323
327
  emptyTx: ProcessedTx,
324
328
  globalVariables: GlobalVariables,
325
329
  ) {
@@ -330,7 +334,12 @@ export class Sequencer {
330
334
  const allTxs = [...txs, ...times(emptyTxCount, () => emptyTx)];
331
335
  this.log(`Building block ${globalVariables.blockNumber}`);
332
336
 
333
- const [block] = await this.blockBuilder.buildL2Block(globalVariables, allTxs, newL1ToL2Messages);
337
+ const [block] = await this.blockBuilder.buildL2Block(
338
+ globalVariables,
339
+ allTxs,
340
+ newModelL1ToL2Messages,
341
+ newL1ToL2Messages,
342
+ );
334
343
  return block;
335
344
  }
336
345
 
@@ -7,7 +7,6 @@ import { PublicProver } from '../prover/index.js';
7
7
  import { PublicKernelCircuitSimulator } from '../simulator/index.js';
8
8
  import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js';
9
9
  import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
10
- import { FailedTx } from './processed_tx.js';
11
10
 
12
11
  /**
13
12
  * The phase manager responsible for performing the fee preparation phase.
@@ -34,21 +33,15 @@ export class SetupPhaseManager extends AbstractPhaseManager {
34
33
  ) {
35
34
  this.log(`Processing tx ${tx.getTxHash()}`);
36
35
  const [publicKernelOutput, publicKernelProof, newUnencryptedFunctionLogs, revertReason] =
37
- await this.processEnqueuedPublicCalls(tx, previousPublicKernelOutput, previousPublicKernelProof);
36
+ await this.processEnqueuedPublicCalls(tx, previousPublicKernelOutput, previousPublicKernelProof).catch(
37
+ // the abstract phase manager throws if simulation gives error in a non-revertible phase
38
+ async err => {
39
+ await this.publicStateDB.rollbackToCommit();
40
+ throw err;
41
+ },
42
+ );
38
43
  tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
39
-
40
- // commit the state updates from this transaction
41
- await this.publicStateDB.commit();
42
-
44
+ await this.publicStateDB.checkpoint();
43
45
  return { publicKernelOutput, publicKernelProof, revertReason };
44
46
  }
45
-
46
- async rollback(tx: Tx, err: unknown): Promise<FailedTx> {
47
- this.log.warn(`Error processing tx ${tx.getTxHash()}: ${err}`);
48
- await this.publicStateDB.rollback();
49
- return {
50
- tx,
51
- error: err instanceof Error ? err : new Error('Unknown error'),
52
- };
53
- }
54
47
  }
@@ -7,7 +7,6 @@ import { PublicProver } from '../prover/index.js';
7
7
  import { PublicKernelCircuitSimulator } from '../simulator/index.js';
8
8
  import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js';
9
9
  import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
10
- import { FailedTx } from './processed_tx.js';
11
10
 
12
11
  export class TailPhaseManager extends AbstractPhaseManager {
13
12
  constructor(
@@ -26,10 +25,15 @@ export class TailPhaseManager extends AbstractPhaseManager {
26
25
 
27
26
  async handle(tx: Tx, previousPublicKernelOutput: PublicKernelCircuitPublicInputs, previousPublicKernelProof: Proof) {
28
27
  this.log(`Processing tx ${tx.getTxHash()}`);
29
- this.log(`Executing tail circuit for tx ${tx.getTxHash()}`);
30
28
  const [publicKernelOutput, publicKernelProof] = await this.runKernelCircuit(
31
29
  previousPublicKernelOutput,
32
30
  previousPublicKernelProof,
31
+ ).catch(
32
+ // the abstract phase manager throws if simulation gives error in non-revertible phase
33
+ async err => {
34
+ await this.publicStateDB.rollbackToCommit();
35
+ throw err;
36
+ },
33
37
  );
34
38
 
35
39
  // commit the state updates from this transaction
@@ -37,13 +41,4 @@ export class TailPhaseManager extends AbstractPhaseManager {
37
41
 
38
42
  return { publicKernelOutput, publicKernelProof, revertReason: undefined };
39
43
  }
40
-
41
- async rollback(tx: Tx, err: unknown): Promise<FailedTx> {
42
- this.log.warn(`Error processing tx ${tx.getTxHash()}: ${err}`);
43
- await this.publicStateDB.rollback();
44
- return {
45
- tx,
46
- error: err instanceof Error ? err : new Error('Unknown error'),
47
- };
48
- }
49
44
  }