@aztec/sequencer-client 0.24.0 → 0.26.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 (65) hide show
  1. package/dest/block_builder/solo_block_builder.d.ts +9 -9
  2. package/dest/block_builder/solo_block_builder.d.ts.map +1 -1
  3. package/dest/block_builder/solo_block_builder.js +26 -48
  4. package/dest/global_variable_builder/viem-reader.js +2 -2
  5. package/dest/publisher/l1-publisher.d.ts +0 -2
  6. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  7. package/dest/publisher/l1-publisher.js +2 -3
  8. package/dest/publisher/viem-tx-sender.d.ts.map +1 -1
  9. package/dest/publisher/viem-tx-sender.js +6 -10
  10. package/dest/sequencer/abstract_phase_manager.d.ts +18 -25
  11. package/dest/sequencer/abstract_phase_manager.d.ts.map +1 -1
  12. package/dest/sequencer/abstract_phase_manager.js +141 -91
  13. package/dest/sequencer/{fee_distribution_phase_manager.d.ts → app_logic_phase_manager.d.ts} +9 -11
  14. package/dest/sequencer/app_logic_phase_manager.d.ts.map +1 -0
  15. package/dest/sequencer/app_logic_phase_manager.js +44 -0
  16. package/dest/sequencer/phase_manager_factory.d.ts +19 -0
  17. package/dest/sequencer/phase_manager_factory.d.ts.map +1 -0
  18. package/dest/sequencer/phase_manager_factory.js +51 -0
  19. package/dest/sequencer/processed_tx.d.ts +2 -2
  20. package/dest/sequencer/processed_tx.d.ts.map +1 -1
  21. package/dest/sequencer/processed_tx.js +7 -7
  22. package/dest/sequencer/public_processor.d.ts +1 -1
  23. package/dest/sequencer/public_processor.d.ts.map +1 -1
  24. package/dest/sequencer/public_processor.js +13 -11
  25. package/dest/sequencer/sequencer.d.ts +3 -3
  26. package/dest/sequencer/sequencer.d.ts.map +1 -1
  27. package/dest/sequencer/sequencer.js +23 -16
  28. package/dest/sequencer/{fee_preparation_phase_manager.d.ts → setup_phase_manager.d.ts} +9 -11
  29. package/dest/sequencer/setup_phase_manager.d.ts.map +1 -0
  30. package/dest/sequencer/setup_phase_manager.js +37 -0
  31. package/dest/sequencer/{application_logic_phase_manager.d.ts → teardown_phase_manager.d.ts} +9 -12
  32. package/dest/sequencer/teardown_phase_manager.d.ts.map +1 -0
  33. package/dest/sequencer/teardown_phase_manager.js +36 -0
  34. package/dest/simulator/index.d.ts +10 -4
  35. package/dest/simulator/index.d.ts.map +1 -1
  36. package/dest/simulator/public_executor.d.ts +8 -3
  37. package/dest/simulator/public_executor.d.ts.map +1 -1
  38. package/dest/simulator/public_executor.js +64 -11
  39. package/dest/simulator/public_kernel.d.ts +10 -4
  40. package/dest/simulator/public_kernel.d.ts.map +1 -1
  41. package/dest/simulator/public_kernel.js +33 -14
  42. package/package.json +14 -13
  43. package/src/block_builder/solo_block_builder.ts +61 -50
  44. package/src/global_variable_builder/viem-reader.ts +1 -1
  45. package/src/publisher/l1-publisher.ts +1 -4
  46. package/src/publisher/viem-tx-sender.ts +6 -12
  47. package/src/sequencer/abstract_phase_manager.ts +207 -145
  48. package/src/sequencer/app_logic_phase_manager.ts +75 -0
  49. package/src/sequencer/phase_manager_factory.ts +122 -0
  50. package/src/sequencer/processed_tx.ts +12 -13
  51. package/src/sequencer/public_processor.ts +29 -12
  52. package/src/sequencer/sequencer.ts +22 -15
  53. package/src/sequencer/{fee_distribution_phase_manager.ts → setup_phase_manager.ts} +23 -25
  54. package/src/sequencer/teardown_phase_manager.ts +67 -0
  55. package/src/simulator/index.ts +10 -6
  56. package/src/simulator/public_executor.ts +108 -10
  57. package/src/simulator/public_kernel.ts +39 -13
  58. package/dest/sequencer/application_logic_phase_manager.d.ts.map +0 -1
  59. package/dest/sequencer/application_logic_phase_manager.js +0 -64
  60. package/dest/sequencer/fee_distribution_phase_manager.d.ts.map +0 -1
  61. package/dest/sequencer/fee_distribution_phase_manager.js +0 -42
  62. package/dest/sequencer/fee_preparation_phase_manager.d.ts.map +0 -1
  63. package/dest/sequencer/fee_preparation_phase_manager.js +0 -42
  64. package/src/sequencer/application_logic_phase_manager.ts +0 -107
  65. package/src/sequencer/fee_preparation_phase_manager.ts +0 -79
@@ -2,22 +2,27 @@ import { FunctionL2Logs, MerkleTreeId, Tx } from '@aztec/circuit-types';
2
2
  import {
3
3
  AztecAddress,
4
4
  CallRequest,
5
- CombinedAccumulatedData,
6
5
  ContractStorageRead,
7
6
  ContractStorageUpdateRequest,
8
7
  Fr,
9
8
  GlobalVariables,
10
9
  Header,
11
- MAX_NEW_COMMITMENTS_PER_CALL,
10
+ L2ToL1Message,
12
11
  MAX_NEW_L2_TO_L1_MSGS_PER_CALL,
12
+ MAX_NEW_NOTE_HASHES_PER_CALL,
13
13
  MAX_NEW_NULLIFIERS_PER_CALL,
14
+ MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX,
15
+ MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
14
16
  MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
15
17
  MAX_PUBLIC_DATA_READS_PER_CALL,
16
- MAX_PUBLIC_DATA_READS_PER_TX,
17
18
  MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,
18
- MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
19
+ MAX_REVERTIBLE_PUBLIC_DATA_READS_PER_TX,
20
+ MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
19
21
  MembershipWitness,
22
+ PrivateKernelTailCircuitPublicInputs,
20
23
  Proof,
24
+ PublicAccumulatedNonRevertibleData,
25
+ PublicAccumulatedRevertibleData,
21
26
  PublicCallData,
22
27
  PublicCallRequest,
23
28
  PublicCallStackItem,
@@ -32,10 +37,10 @@ import {
32
37
  SideEffectLinkedToNoteHash,
33
38
  VK_TREE_HEIGHT,
34
39
  } from '@aztec/circuits.js';
35
- import { computeVarArgsHash } from '@aztec/circuits.js/abis';
40
+ import { computeVarArgsHash } from '@aztec/circuits.js/hash';
36
41
  import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection';
37
- import { createDebugLogger } from '@aztec/foundation/log';
38
- import { to2Fields } from '@aztec/foundation/serialize';
42
+ import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
43
+ import { Tuple, to2Fields } from '@aztec/foundation/serialize';
39
44
  import {
40
45
  PublicExecution,
41
46
  PublicExecutionResult,
@@ -53,15 +58,20 @@ import { PublicProver } from '../prover/index.js';
53
58
  import { PublicKernelCircuitSimulator } from '../simulator/index.js';
54
59
  import { FailedTx } from './processed_tx.js';
55
60
 
56
- /**
57
- * A phase manager is responsible for performing/rolling back a phase of a transaction.
58
- *
59
- * The phases are as follows:
60
- * 1. Fee Preparation
61
- * 2. Application Logic
62
- * 3. Fee Distribution
63
- */
61
+ export enum PublicKernelPhase {
62
+ SETUP = 'setup',
63
+ APP_LOGIC = 'app-logic',
64
+ TEARDOWN = 'teardown',
65
+ }
66
+
67
+ export const PhaseIsRevertible: Record<PublicKernelPhase, boolean> = {
68
+ [PublicKernelPhase.SETUP]: false,
69
+ [PublicKernelPhase.APP_LOGIC]: true,
70
+ [PublicKernelPhase.TEARDOWN]: false,
71
+ };
72
+
64
73
  export abstract class AbstractPhaseManager {
74
+ protected log: DebugLogger;
65
75
  constructor(
66
76
  protected db: MerkleTreeOperations,
67
77
  protected publicExecutor: PublicExecutor,
@@ -69,8 +79,10 @@ export abstract class AbstractPhaseManager {
69
79
  protected publicProver: PublicProver,
70
80
  protected globalVariables: GlobalVariables,
71
81
  protected historicalHeader: Header,
72
- protected log = createDebugLogger('aztec:sequencer:phase-manager'),
73
- ) {}
82
+ public phase: PublicKernelPhase,
83
+ ) {
84
+ this.log = createDebugLogger(`aztec:sequencer:${phase}`);
85
+ }
74
86
  /**
75
87
  *
76
88
  * @param tx - the tx to be processed
@@ -79,25 +91,74 @@ export abstract class AbstractPhaseManager {
79
91
  */
80
92
  abstract handle(
81
93
  tx: Tx,
82
- publicKernelPublicInputs?: PublicKernelCircuitPublicInputs,
83
- previousPublicKernelProof?: Proof,
94
+ publicKernelPublicInputs: PublicKernelCircuitPublicInputs,
95
+ previousPublicKernelProof: Proof,
84
96
  ): Promise<{
85
97
  /**
86
98
  * the output of the public kernel circuit for this phase
87
99
  */
88
- publicKernelOutput?: PublicKernelCircuitPublicInputs;
100
+ publicKernelOutput: PublicKernelCircuitPublicInputs;
89
101
  /**
90
102
  * the proof of the public kernel circuit for this phase
91
103
  */
92
- publicKernelProof?: Proof;
104
+ publicKernelProof: Proof;
93
105
  }>;
94
- abstract nextPhase(): AbstractPhaseManager | undefined;
95
106
  abstract rollback(tx: Tx, err: unknown): Promise<FailedTx>;
96
107
 
97
- // Extract the public calls from the tx for this phase
98
- abstract extractEnqueuedPublicCalls(tx: Tx): PublicCallRequest[];
108
+ public static extractEnqueuedPublicCallsByPhase(
109
+ publicInputs: PrivateKernelTailCircuitPublicInputs,
110
+ enqueuedPublicFunctionCalls: PublicCallRequest[],
111
+ ): Record<PublicKernelPhase, PublicCallRequest[]> {
112
+ const publicCallsStack = enqueuedPublicFunctionCalls.slice().reverse();
113
+ const nonRevertibleCallStack = publicInputs.endNonRevertibleData.publicCallStack.filter(i => !i.isEmpty());
114
+ const revertibleCallStack = publicInputs.end.publicCallStack.filter(i => !i.isEmpty());
115
+
116
+ const callRequestsStack = publicCallsStack
117
+ .map(call => call.toCallRequest())
118
+ .filter(
119
+ // filter out enqueued calls that are not in the public call stack
120
+ // TODO mitch left a question about whether this is only needed when unit testing
121
+ // with mock data
122
+ call => revertibleCallStack.find(p => p.equals(call)) || nonRevertibleCallStack.find(p => p.equals(call)),
123
+ );
99
124
 
100
- protected getKernelOutputAndProof(
125
+ if (callRequestsStack.length === 0) {
126
+ return {
127
+ [PublicKernelPhase.SETUP]: [],
128
+ [PublicKernelPhase.APP_LOGIC]: [],
129
+ [PublicKernelPhase.TEARDOWN]: [],
130
+ };
131
+ }
132
+
133
+ // find the first call that is revertible
134
+ const firstRevertibleCallIndex = callRequestsStack.findIndex(
135
+ c => revertibleCallStack.findIndex(p => p.equals(c)) !== -1,
136
+ );
137
+
138
+ if (firstRevertibleCallIndex === 0) {
139
+ return {
140
+ [PublicKernelPhase.SETUP]: [],
141
+ [PublicKernelPhase.APP_LOGIC]: publicCallsStack,
142
+ [PublicKernelPhase.TEARDOWN]: [],
143
+ };
144
+ } else {
145
+ return {
146
+ [PublicKernelPhase.SETUP]: publicCallsStack.slice(0, firstRevertibleCallIndex - 1),
147
+ [PublicKernelPhase.APP_LOGIC]: publicCallsStack.slice(firstRevertibleCallIndex),
148
+ [PublicKernelPhase.TEARDOWN]: [publicCallsStack[firstRevertibleCallIndex - 1]],
149
+ };
150
+ }
151
+ }
152
+
153
+ protected extractEnqueuedPublicCalls(tx: Tx): PublicCallRequest[] {
154
+ const calls = AbstractPhaseManager.extractEnqueuedPublicCallsByPhase(tx.data, tx.enqueuedPublicFunctionCalls)[
155
+ this.phase
156
+ ];
157
+
158
+ return calls;
159
+ }
160
+
161
+ public static getKernelOutputAndProof(
101
162
  tx: Tx,
102
163
  publicKernelPublicInput?: PublicKernelCircuitPublicInputs,
103
164
  previousPublicKernelProof?: Proof,
@@ -119,10 +180,12 @@ export abstract class AbstractPhaseManager {
119
180
  } else {
120
181
  const publicKernelPublicInput = new PublicKernelCircuitPublicInputs(
121
182
  tx.data.aggregationObject,
122
- tx.data.endNonRevertibleData,
123
- CombinedAccumulatedData.fromFinalAccumulatedData(tx.data.end),
183
+ PublicAccumulatedNonRevertibleData.fromPrivateAccumulatedNonRevertibleData(tx.data.endNonRevertibleData),
184
+ PublicAccumulatedRevertibleData.fromPrivateAccumulatedRevertibleData(tx.data.end),
124
185
  tx.data.constants,
125
- tx.data.isPrivate,
186
+ tx.data.needsSetup,
187
+ tx.data.needsAppLogic,
188
+ tx.data.needsTeardown,
126
189
  );
127
190
  const publicKernelProof = previousPublicKernelProof || tx.proof;
128
191
  return {
@@ -133,16 +196,19 @@ export abstract class AbstractPhaseManager {
133
196
  }
134
197
 
135
198
  protected async processEnqueuedPublicCalls(
136
- enqueuedCalls: PublicCallRequest[],
199
+ tx: Tx,
137
200
  previousPublicKernelOutput: PublicKernelCircuitPublicInputs,
138
201
  previousPublicKernelProof: Proof,
139
202
  ): Promise<[PublicKernelCircuitPublicInputs, Proof, FunctionL2Logs[]]> {
140
- if (!enqueuedCalls || !enqueuedCalls.length) {
141
- throw new Error(`Missing preimages for enqueued public calls`);
142
- }
143
203
  let kernelOutput = previousPublicKernelOutput;
144
204
  let kernelProof = previousPublicKernelProof;
145
205
 
206
+ const enqueuedCalls = this.extractEnqueuedPublicCalls(tx);
207
+
208
+ if (!enqueuedCalls || !enqueuedCalls.length) {
209
+ return [kernelOutput, kernelProof, []];
210
+ }
211
+
146
212
  const newUnencryptedFunctionLogs: FunctionL2Logs[] = [];
147
213
 
148
214
  // TODO(#1684): Should multiple separately enqueued public calls be treated as
@@ -169,8 +235,8 @@ export abstract class AbstractPhaseManager {
169
235
 
170
236
  newUnencryptedFunctionLogs.push(result.unencryptedLogs);
171
237
  const functionSelector = result.execution.functionData.selector.toString();
172
- this.log(
173
- `Running public kernel circuit for ${functionSelector}@${result.execution.contractAddress.toString()}`,
238
+ this.log.debug(
239
+ `Running public kernel circuit for ${result.execution.contractAddress.toString()}:${functionSelector}`,
174
240
  );
175
241
  executionStack.push(...result.nestedExecutions);
176
242
  const callData = await this.getPublicCallData(result, isExecutionRequest);
@@ -183,11 +249,11 @@ export abstract class AbstractPhaseManager {
183
249
  }
184
250
  // HACK(#1622): Manually patches the ordering of public state actions
185
251
  // TODO(#757): Enforce proper ordering of public state actions
186
- this.patchPublicStorageActionOrdering(kernelOutput, enqueuedExecutionResult!);
252
+ patchPublicStorageActionOrdering(kernelOutput, enqueuedExecutionResult!, this.phase);
187
253
  }
188
254
 
189
255
  // TODO(#3675): This should be done in a public kernel circuit
190
- this.removeRedundantPublicDataWrites(kernelOutput);
256
+ removeRedundantPublicDataWrites(kernelOutput);
191
257
 
192
258
  return [kernelOutput, kernelProof, newUnencryptedFunctionLogs];
193
259
  }
@@ -207,18 +273,17 @@ export abstract class AbstractPhaseManager {
207
273
  previousOutput: PublicKernelCircuitPublicInputs,
208
274
  previousProof: Proof,
209
275
  ): Promise<PublicKernelCircuitPublicInputs> {
210
- if (previousOutput?.isPrivate && previousProof) {
211
- // Run the public kernel circuit with previous private kernel
212
- const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
213
- const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, callData);
214
- return this.publicKernel.publicKernelCircuitPrivateInput(inputs);
215
- } else if (previousOutput && previousProof) {
216
- // Run the public kernel circuit with previous public kernel
217
- const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
218
- const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, callData);
219
- return this.publicKernel.publicKernelCircuitNonFirstIteration(inputs);
220
- } else {
221
- throw new Error(`No public kernel circuit for inputs`);
276
+ const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
277
+ const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, callData);
278
+ switch (this.phase) {
279
+ case PublicKernelPhase.SETUP:
280
+ return this.publicKernel.publicKernelCircuitSetup(inputs);
281
+ case PublicKernelPhase.APP_LOGIC:
282
+ return this.publicKernel.publicKernelCircuitAppLogic(inputs);
283
+ case PublicKernelPhase.TEARDOWN:
284
+ return this.publicKernel.publicKernelCircuitTeardown(inputs);
285
+ default:
286
+ throw new Error(`No public kernel circuit for inputs`);
222
287
  }
223
288
  }
224
289
 
@@ -251,9 +316,9 @@ export abstract class AbstractPhaseManager {
251
316
  callContext: result.execution.callContext,
252
317
  proverAddress: AztecAddress.ZERO,
253
318
  argsHash: computeVarArgsHash(result.execution.args),
254
- newCommitments: padArrayEnd(result.newCommitments, SideEffect.empty(), MAX_NEW_COMMITMENTS_PER_CALL),
319
+ newNoteHashes: padArrayEnd(result.newNoteHashes, SideEffect.empty(), MAX_NEW_NOTE_HASHES_PER_CALL),
255
320
  newNullifiers: padArrayEnd(result.newNullifiers, SideEffectLinkedToNoteHash.empty(), MAX_NEW_NULLIFIERS_PER_CALL),
256
- newL2ToL1Msgs: padArrayEnd(result.newL2ToL1Messages, Fr.ZERO, MAX_NEW_L2_TO_L1_MSGS_PER_CALL),
321
+ newL2ToL1Msgs: padArrayEnd(result.newL2ToL1Messages, L2ToL1Message.empty(), MAX_NEW_L2_TO_L1_MSGS_PER_CALL),
257
322
  returnValues: padArrayEnd(result.returnValues, Fr.ZERO, RETURN_VALUES_LENGTH),
258
323
  contractStorageReads: padArrayEnd(
259
324
  result.contractStorageReads,
@@ -310,118 +375,115 @@ export abstract class AbstractPhaseManager {
310
375
  protected async getPublicCallData(result: PublicExecutionResult, isExecutionRequest = false) {
311
376
  const bytecodeHash = await this.getBytecodeHash(result);
312
377
  const callStackItem = await this.getPublicCallStackItem(result, isExecutionRequest);
313
- const publicCallRequests = (await this.getPublicCallStackPreimages(result)).map(c => c.toCallRequest());
378
+ const publicCallRequests = (await this.getPublicCallStackPreimages(result)).map(c =>
379
+ c.toCallRequest(callStackItem.publicInputs.callContext),
380
+ );
314
381
  const publicCallStack = padArrayEnd(publicCallRequests, CallRequest.empty(), MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL);
315
382
  const portalContractAddress = result.execution.callContext.portalContractAddress.toField();
316
383
  const proof = await this.publicProver.getPublicCircuitProof(callStackItem.publicInputs);
317
384
  return new PublicCallData(callStackItem, publicCallStack, proof, portalContractAddress, bytecodeHash);
318
385
  }
386
+ }
319
387
 
320
- // HACK(#1622): this is a hack to fix ordering of public state in the call stack. Since the private kernel
321
- // cannot keep track of side effects that happen after or before a nested call, we override the public
322
- // state actions it emits with whatever we got from the simulator. As a sanity check, we at least verify
323
- // that the elements are the same, so we are only tweaking their ordering.
324
- // See yarn-project/end-to-end/src/e2e_ordering.test.ts
325
- // See https://github.com/AztecProtocol/aztec-packages/issues/1616
326
- // TODO(#757): Enforce proper ordering of public state actions
327
- /**
328
- * Patch the ordering of storage actions output from the public kernel.
329
- * @param publicInputs - to be patched here: public inputs to the kernel iteration up to this point
330
- * @param execResult - result of the top/first execution for this enqueued public call
331
- */
332
- private patchPublicStorageActionOrdering(
333
- publicInputs: PublicKernelCircuitPublicInputs,
334
- execResult: PublicExecutionResult,
335
- ) {
336
- // Convert ContractStorage* objects to PublicData* objects and sort them in execution order
337
- const simPublicDataReads = collectPublicDataReads(execResult);
338
- const simPublicDataUpdateRequests = collectPublicDataUpdateRequests(execResult);
339
-
340
- const { publicDataReads, publicDataUpdateRequests } = publicInputs.end; // from kernel
341
-
342
- // Validate all items in enqueued public calls are in the kernel emitted stack
343
- const readsAreEqual = simPublicDataReads.reduce(
344
- (accum, read) =>
345
- accum && !!publicDataReads.find(item => item.leafSlot.equals(read.leafSlot) && item.value.equals(read.value)),
346
- true,
347
- );
348
- const updatesAreEqual = simPublicDataUpdateRequests.reduce(
349
- (accum, update) =>
350
- accum &&
351
- !!publicDataUpdateRequests.find(
352
- item => item.leafSlot.equals(update.leafSlot) && item.newValue.equals(update.newValue),
353
- ),
354
- true,
355
- );
388
+ function removeRedundantPublicDataWrites(publicInputs: PublicKernelCircuitPublicInputs) {
389
+ const patch = <N extends number>(requests: Tuple<PublicDataUpdateRequest, N>) => {
390
+ const lastWritesMap = new Map<string, PublicDataUpdateRequest>();
391
+ for (const write of requests) {
392
+ const key = write.leafSlot.toString();
393
+ lastWritesMap.set(key, write);
394
+ }
395
+ return requests.filter(write => lastWritesMap.get(write.leafSlot.toString())?.equals(write));
396
+ };
397
+
398
+ publicInputs.end.publicDataUpdateRequests = padArrayEnd(
399
+ patch(publicInputs.end.publicDataUpdateRequests),
400
+ PublicDataUpdateRequest.empty(),
401
+ MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
402
+ );
403
+
404
+ publicInputs.endNonRevertibleData.publicDataUpdateRequests = padArrayEnd(
405
+ patch(publicInputs.endNonRevertibleData.publicDataUpdateRequests),
406
+ PublicDataUpdateRequest.empty(),
407
+ MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
408
+ );
409
+ }
356
410
 
357
- if (!readsAreEqual) {
411
+ // HACK(#1622): this is a hack to fix ordering of public state in the call stack. Since the private kernel
412
+ // cannot keep track of side effects that happen after or before a nested call, we override the public
413
+ // state actions it emits with whatever we got from the simulator. As a sanity check, we at least verify
414
+ // that the elements are the same, so we are only tweaking their ordering.
415
+ // See yarn-project/end-to-end/src/e2e_ordering.test.ts
416
+ // See https://github.com/AztecProtocol/aztec-packages/issues/1616
417
+ // TODO(#757): Enforce proper ordering of public state actions
418
+ /**
419
+ * Patch the ordering of storage actions output from the public kernel.
420
+ * @param publicInputs - to be patched here: public inputs to the kernel iteration up to this point
421
+ * @param execResult - result of the top/first execution for this enqueued public call
422
+ */
423
+ function patchPublicStorageActionOrdering(
424
+ publicInputs: PublicKernelCircuitPublicInputs,
425
+ execResult: PublicExecutionResult,
426
+ phase: PublicKernelPhase,
427
+ ) {
428
+ const { publicDataReads, publicDataUpdateRequests } = PhaseIsRevertible[phase]
429
+ ? publicInputs.end
430
+ : publicInputs.endNonRevertibleData;
431
+
432
+ // Convert ContractStorage* objects to PublicData* objects and sort them in execution order.
433
+ // Note, this only pulls simulated reads/writes from the current phase,
434
+ // so the returned result will be a subset of the public kernel output.
435
+
436
+ const simPublicDataReads = collectPublicDataReads(execResult);
437
+ // verify that each read is in the kernel output
438
+ for (const read of simPublicDataReads) {
439
+ if (!publicDataReads.find(item => item.equals(read))) {
358
440
  throw new Error(
359
441
  `Public data reads from simulator do not match those from public kernel.\nFrom simulator: ${simPublicDataReads
360
442
  .map(p => p.toFriendlyJSON())
361
443
  .join(', ')}\nFrom public kernel: ${publicDataReads.map(i => i.toFriendlyJSON()).join(', ')}`,
362
444
  );
363
445
  }
364
- if (!updatesAreEqual) {
446
+ }
447
+
448
+ const simPublicDataUpdateRequests = collectPublicDataUpdateRequests(execResult);
449
+ for (const update of simPublicDataUpdateRequests) {
450
+ if (!publicDataUpdateRequests.find(item => item.equals(update))) {
365
451
  throw new Error(
366
452
  `Public data update requests from simulator do not match those from public kernel.\nFrom simulator: ${simPublicDataUpdateRequests
367
453
  .map(p => p.toFriendlyJSON())
368
- .join(', ')}\nFrom public kernel: ${publicDataUpdateRequests.map(i => i.toFriendlyJSON()).join(', ')}`,
454
+ .join(', ')}\nFrom public kernel revertible: ${publicDataUpdateRequests
455
+ .map(i => i.toFriendlyJSON())
456
+ .join(', ')}`,
369
457
  );
370
458
  }
371
-
372
- // Assume that kernel public inputs has the right number of items.
373
- // We only want to reorder the items from the public inputs of the
374
- // most recently processed top/enqueued call.
375
- const numTotalReadsInKernel = arrayNonEmptyLength(
376
- publicInputs.end.publicDataReads,
377
- f => f.leafSlot.equals(Fr.ZERO) && f.value.equals(Fr.ZERO),
378
- );
379
- const numTotalUpdatesInKernel = arrayNonEmptyLength(
380
- publicInputs.end.publicDataUpdateRequests,
381
- f => f.leafSlot.equals(Fr.ZERO) && f.newValue.equals(Fr.ZERO),
382
- );
383
- const numReadsBeforeThisEnqueuedCall = numTotalReadsInKernel - simPublicDataReads.length;
384
- const numUpdatesBeforeThisEnqueuedCall = numTotalUpdatesInKernel - simPublicDataUpdateRequests.length;
385
-
386
- // Override kernel output
387
- publicInputs.end.publicDataReads = padArrayEnd(
388
- [
389
- // do not mess with items from previous top/enqueued calls in kernel output
390
- ...publicDataReads.slice(0, numReadsBeforeThisEnqueuedCall),
391
- ...simPublicDataReads,
392
- ],
393
- PublicDataRead.empty(),
394
- MAX_PUBLIC_DATA_READS_PER_TX,
395
- );
396
-
397
- // Override kernel output
398
- publicInputs.end.publicDataUpdateRequests = padArrayEnd(
399
- [
400
- // do not mess with items from previous top/enqueued calls in kernel output
401
- ...publicDataUpdateRequests.slice(0, numUpdatesBeforeThisEnqueuedCall),
402
- ...simPublicDataUpdateRequests,
403
- ],
404
- PublicDataUpdateRequest.empty(),
405
- MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
406
- );
407
- }
408
-
409
- private removeRedundantPublicDataWrites(publicInputs: PublicKernelCircuitPublicInputs) {
410
- const lastWritesMap = new Map();
411
- for (const write of publicInputs.end.publicDataUpdateRequests) {
412
- const key = write.leafSlot.toString();
413
- lastWritesMap.set(key, write);
414
- }
415
-
416
- const lastWrites = publicInputs.end.publicDataUpdateRequests.filter(
417
- write => lastWritesMap.get(write.leafSlot.toString()) === write,
418
- );
419
-
420
- publicInputs.end.publicDataUpdateRequests = padArrayEnd(
421
- lastWrites,
422
-
423
- PublicDataUpdateRequest.empty(),
424
- MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
425
- );
426
459
  }
460
+ // We only want to reorder the items from the public inputs of the
461
+ // most recently processed top/enqueued call.
462
+
463
+ const effectSet = PhaseIsRevertible[phase] ? 'end' : 'endNonRevertibleData';
464
+
465
+ const numReadsInKernel = arrayNonEmptyLength(publicDataReads, f => f.isEmpty());
466
+ const numReadsBeforeThisEnqueuedCall = numReadsInKernel - simPublicDataReads.length;
467
+ publicInputs[effectSet].publicDataReads = padArrayEnd(
468
+ [
469
+ // do not mess with items from previous top/enqueued calls in kernel output
470
+ ...publicInputs[effectSet].publicDataReads.slice(0, numReadsBeforeThisEnqueuedCall),
471
+ ...simPublicDataReads,
472
+ ],
473
+ PublicDataRead.empty(),
474
+ PhaseIsRevertible[phase] ? MAX_REVERTIBLE_PUBLIC_DATA_READS_PER_TX : MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX,
475
+ );
476
+
477
+ const numUpdatesInKernel = arrayNonEmptyLength(publicDataUpdateRequests, f => f.isEmpty());
478
+ const numUpdatesBeforeThisEnqueuedCall = numUpdatesInKernel - simPublicDataUpdateRequests.length;
479
+ publicInputs[effectSet].publicDataUpdateRequests = padArrayEnd(
480
+ [
481
+ ...publicInputs[effectSet].publicDataUpdateRequests.slice(0, numUpdatesBeforeThisEnqueuedCall),
482
+ ...simPublicDataUpdateRequests,
483
+ ],
484
+ PublicDataUpdateRequest.empty(),
485
+ PhaseIsRevertible[phase]
486
+ ? MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
487
+ : MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
488
+ );
427
489
  }
@@ -0,0 +1,75 @@
1
+ import { Tx } from '@aztec/circuit-types';
2
+ import { GlobalVariables, Header, Proof, PublicKernelCircuitPublicInputs } from '@aztec/circuits.js';
3
+ import { PublicExecutor, PublicStateDB } from '@aztec/simulator';
4
+ import { MerkleTreeOperations } from '@aztec/world-state';
5
+
6
+ import { PublicProver } from '../prover/index.js';
7
+ import { PublicKernelCircuitSimulator } from '../simulator/index.js';
8
+ import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js';
9
+ import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
10
+ import { FailedTx } from './processed_tx.js';
11
+
12
+ /**
13
+ * The phase manager responsible for performing the fee preparation phase.
14
+ */
15
+ export class AppLogicPhaseManager extends AbstractPhaseManager {
16
+ constructor(
17
+ protected db: MerkleTreeOperations,
18
+ protected publicExecutor: PublicExecutor,
19
+ protected publicKernel: PublicKernelCircuitSimulator,
20
+ protected publicProver: PublicProver,
21
+ protected globalVariables: GlobalVariables,
22
+ protected historicalHeader: Header,
23
+ protected publicContractsDB: ContractsDataSourcePublicDB,
24
+ protected publicStateDB: PublicStateDB,
25
+ public phase: PublicKernelPhase = PublicKernelPhase.APP_LOGIC,
26
+ ) {
27
+ super(db, publicExecutor, publicKernel, publicProver, globalVariables, historicalHeader, phase);
28
+ }
29
+
30
+ async handle(
31
+ tx: Tx,
32
+ previousPublicKernelOutput: PublicKernelCircuitPublicInputs,
33
+ previousPublicKernelProof: Proof,
34
+ ): Promise<{
35
+ /**
36
+ * the output of the public kernel circuit for this phase
37
+ */
38
+ publicKernelOutput: PublicKernelCircuitPublicInputs;
39
+ /**
40
+ * the proof of the public kernel circuit for this phase
41
+ */
42
+ publicKernelProof: Proof;
43
+ }> {
44
+ // add new contracts to the contracts db so that their functions may be found and called
45
+ // TODO(#4073): This is catching only private deployments, when we add public ones, we'll
46
+ // have to capture contracts emitted in that phase as well.
47
+ // TODO(@spalladino): Should we allow emitting contracts in the fee preparation phase?
48
+ this.log(`Processing tx ${tx.getTxHash()}`);
49
+ await this.publicContractsDB.addNewContracts(tx);
50
+ this.log(`Executing enqueued public calls for tx ${tx.getTxHash()}`);
51
+ const [publicKernelOutput, publicKernelProof, newUnencryptedFunctionLogs] = await this.processEnqueuedPublicCalls(
52
+ tx,
53
+ previousPublicKernelOutput,
54
+ previousPublicKernelProof,
55
+ );
56
+ tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
57
+
58
+ // commit the state updates from this transaction
59
+ await this.publicStateDB.commit();
60
+
61
+ return { publicKernelOutput, publicKernelProof };
62
+ }
63
+
64
+ async rollback(tx: Tx, err: unknown): Promise<FailedTx> {
65
+ this.log.warn(`Error processing tx ${tx.getTxHash()}: ${err}`);
66
+ // remove contracts on failure
67
+ await this.publicContractsDB.removeNewContracts(tx);
68
+ // rollback any state updates from this failed transaction
69
+ await this.publicStateDB.rollback();
70
+ return {
71
+ tx,
72
+ error: err instanceof Error ? err : new Error('Unknown error'),
73
+ };
74
+ }
75
+ }