@aztec/sequencer-client 0.26.3 → 0.26.6

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 (85) hide show
  1. package/dest/block_builder/solo_block_builder.d.ts +5 -4
  2. package/dest/block_builder/solo_block_builder.d.ts.map +1 -1
  3. package/dest/block_builder/solo_block_builder.js +82 -34
  4. package/dest/client/sequencer-client.d.ts.map +1 -1
  5. package/dest/client/sequencer-client.js +29 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +4 -2
  8. package/dest/index.d.ts +9 -7
  9. package/dest/index.d.ts.map +1 -1
  10. package/dest/index.js +9 -8
  11. package/dest/publisher/l1-publisher.js +5 -5
  12. package/dest/publisher/viem-tx-sender.js +2 -2
  13. package/dest/sequencer/abstract_phase_manager.d.ts +12 -15
  14. package/dest/sequencer/abstract_phase_manager.d.ts.map +1 -1
  15. package/dest/sequencer/abstract_phase_manager.js +32 -24
  16. package/dest/sequencer/app_logic_phase_manager.d.ts +1 -6
  17. package/dest/sequencer/app_logic_phase_manager.d.ts.map +1 -1
  18. package/dest/sequencer/app_logic_phase_manager.js +11 -8
  19. package/dest/sequencer/hints_builder.d.ts +13 -0
  20. package/dest/sequencer/hints_builder.d.ts.map +1 -0
  21. package/dest/sequencer/hints_builder.js +21 -0
  22. package/dest/sequencer/index.d.ts +1 -1
  23. package/dest/sequencer/index.d.ts.map +1 -1
  24. package/dest/sequencer/index.js +2 -2
  25. package/dest/sequencer/phase_manager_factory.d.ts.map +1 -1
  26. package/dest/sequencer/phase_manager_factory.js +5 -1
  27. package/dest/sequencer/processed_tx.d.ts +37 -2
  28. package/dest/sequencer/processed_tx.d.ts.map +1 -1
  29. package/dest/sequencer/processed_tx.js +67 -9
  30. package/dest/sequencer/public_processor.d.ts +3 -1
  31. package/dest/sequencer/public_processor.d.ts.map +1 -1
  32. package/dest/sequencer/public_processor.js +18 -11
  33. package/dest/sequencer/sequencer.d.ts.map +1 -1
  34. package/dest/sequencer/sequencer.js +3 -4
  35. package/dest/sequencer/setup_phase_manager.d.ts +1 -6
  36. package/dest/sequencer/setup_phase_manager.d.ts.map +1 -1
  37. package/dest/sequencer/setup_phase_manager.js +3 -5
  38. package/dest/sequencer/tail_phase_manager.d.ts +28 -0
  39. package/dest/sequencer/tail_phase_manager.d.ts.map +1 -0
  40. package/dest/sequencer/tail_phase_manager.js +32 -0
  41. package/dest/sequencer/teardown_phase_manager.d.ts +1 -6
  42. package/dest/sequencer/teardown_phase_manager.d.ts.map +1 -1
  43. package/dest/sequencer/teardown_phase_manager.js +3 -4
  44. package/dest/simulator/acvm_native.d.ts +20 -0
  45. package/dest/simulator/acvm_native.d.ts.map +1 -0
  46. package/dest/simulator/acvm_native.js +96 -0
  47. package/dest/simulator/acvm_wasm.d.ts +7 -0
  48. package/dest/simulator/acvm_wasm.d.ts.map +1 -0
  49. package/dest/simulator/acvm_wasm.js +23 -0
  50. package/dest/simulator/index.d.ts +8 -1
  51. package/dest/simulator/index.d.ts.map +1 -1
  52. package/dest/simulator/index.js +2 -2
  53. package/dest/simulator/public_kernel.d.ts +11 -1
  54. package/dest/simulator/public_kernel.d.ts.map +1 -1
  55. package/dest/simulator/public_kernel.js +34 -6
  56. package/dest/simulator/rollup.d.ts +4 -0
  57. package/dest/simulator/rollup.d.ts.map +1 -1
  58. package/dest/simulator/rollup.js +16 -20
  59. package/dest/simulator/simulation_provider.d.ts +9 -0
  60. package/dest/simulator/simulation_provider.d.ts.map +1 -0
  61. package/dest/simulator/simulation_provider.js +2 -0
  62. package/package.json +15 -13
  63. package/src/block_builder/solo_block_builder.ts +115 -82
  64. package/src/client/sequencer-client.ts +37 -2
  65. package/src/config.ts +4 -0
  66. package/src/index.ts +9 -7
  67. package/src/publisher/l1-publisher.ts +4 -4
  68. package/src/publisher/viem-tx-sender.ts +1 -1
  69. package/src/sequencer/abstract_phase_manager.ts +58 -48
  70. package/src/sequencer/app_logic_phase_manager.ts +12 -22
  71. package/src/sequencer/hints_builder.ts +56 -0
  72. package/src/sequencer/index.ts +1 -1
  73. package/src/sequencer/phase_manager_factory.ts +12 -0
  74. package/src/sequencer/processed_tx.ts +147 -17
  75. package/src/sequencer/public_processor.ts +28 -14
  76. package/src/sequencer/sequencer.ts +2 -3
  77. package/src/sequencer/setup_phase_manager.ts +5 -19
  78. package/src/sequencer/tail_phase_manager.ts +49 -0
  79. package/src/sequencer/teardown_phase_manager.ts +5 -18
  80. package/src/simulator/acvm_native.ts +112 -0
  81. package/src/simulator/acvm_wasm.ts +31 -0
  82. package/src/simulator/index.ts +8 -0
  83. package/src/simulator/public_kernel.ts +62 -8
  84. package/src/simulator/rollup.ts +31 -19
  85. package/src/simulator/simulation_provider.ts +10 -0
@@ -1,4 +1,4 @@
1
- import { FunctionL2Logs, MerkleTreeId, Tx } from '@aztec/circuit-types';
1
+ import { FunctionL2Logs, MerkleTreeId, SimulationError, Tx } from '@aztec/circuit-types';
2
2
  import {
3
3
  AztecAddress,
4
4
  CallRequest,
@@ -13,6 +13,7 @@ import {
13
13
  MAX_NEW_NULLIFIERS_PER_CALL,
14
14
  MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX,
15
15
  MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
16
+ MAX_NULLIFIER_READ_REQUESTS_PER_CALL,
16
17
  MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
17
18
  MAX_PUBLIC_DATA_READS_PER_CALL,
18
19
  MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,
@@ -21,8 +22,6 @@ import {
21
22
  MembershipWitness,
22
23
  PrivateKernelTailCircuitPublicInputs,
23
24
  Proof,
24
- PublicAccumulatedNonRevertibleData,
25
- PublicAccumulatedRevertibleData,
26
25
  PublicCallData,
27
26
  PublicCallRequest,
28
27
  PublicCallStackItem,
@@ -32,7 +31,9 @@ import {
32
31
  PublicKernelCircuitPrivateInputs,
33
32
  PublicKernelCircuitPublicInputs,
34
33
  PublicKernelData,
34
+ PublicKernelTailCircuitPrivateInputs,
35
35
  RETURN_VALUES_LENGTH,
36
+ ReadRequest,
36
37
  SideEffect,
37
38
  SideEffectLinkedToNoteHash,
38
39
  VK_TREE_HEIGHT,
@@ -56,21 +57,25 @@ import { env } from 'process';
56
57
  import { getVerificationKeys } from '../mocks/verification_keys.js';
57
58
  import { PublicProver } from '../prover/index.js';
58
59
  import { PublicKernelCircuitSimulator } from '../simulator/index.js';
60
+ import { HintsBuilder } from './hints_builder.js';
59
61
  import { FailedTx } from './processed_tx.js';
60
62
 
61
63
  export enum PublicKernelPhase {
62
64
  SETUP = 'setup',
63
65
  APP_LOGIC = 'app-logic',
64
66
  TEARDOWN = 'teardown',
67
+ TAIL = 'tail',
65
68
  }
66
69
 
67
70
  export const PhaseIsRevertible: Record<PublicKernelPhase, boolean> = {
68
71
  [PublicKernelPhase.SETUP]: false,
69
72
  [PublicKernelPhase.APP_LOGIC]: true,
70
73
  [PublicKernelPhase.TEARDOWN]: false,
74
+ [PublicKernelPhase.TAIL]: false,
71
75
  };
72
76
 
73
77
  export abstract class AbstractPhaseManager {
78
+ protected hintsBuilder: HintsBuilder;
74
79
  protected log: DebugLogger;
75
80
  constructor(
76
81
  protected db: MerkleTreeOperations,
@@ -81,6 +86,7 @@ export abstract class AbstractPhaseManager {
81
86
  protected historicalHeader: Header,
82
87
  public phase: PublicKernelPhase,
83
88
  ) {
89
+ this.hintsBuilder = new HintsBuilder(db);
84
90
  this.log = createDebugLogger(`aztec:sequencer:${phase}`);
85
91
  }
86
92
  /**
@@ -102,6 +108,10 @@ export abstract class AbstractPhaseManager {
102
108
  * the proof of the public kernel circuit for this phase
103
109
  */
104
110
  publicKernelProof: Proof;
111
+ /**
112
+ * revert reason, if any
113
+ */
114
+ revertReason: SimulationError | undefined;
105
115
  }>;
106
116
  abstract rollback(tx: Tx, err: unknown): Promise<FailedTx>;
107
117
 
@@ -127,6 +137,7 @@ export abstract class AbstractPhaseManager {
127
137
  [PublicKernelPhase.SETUP]: [],
128
138
  [PublicKernelPhase.APP_LOGIC]: [],
129
139
  [PublicKernelPhase.TEARDOWN]: [],
140
+ [PublicKernelPhase.TAIL]: [],
130
141
  };
131
142
  }
132
143
 
@@ -140,12 +151,14 @@ export abstract class AbstractPhaseManager {
140
151
  [PublicKernelPhase.SETUP]: [],
141
152
  [PublicKernelPhase.APP_LOGIC]: publicCallsStack,
142
153
  [PublicKernelPhase.TEARDOWN]: [],
154
+ [PublicKernelPhase.TAIL]: [],
143
155
  };
144
156
  } else {
145
157
  return {
146
158
  [PublicKernelPhase.SETUP]: publicCallsStack.slice(0, firstRevertibleCallIndex - 1),
147
159
  [PublicKernelPhase.APP_LOGIC]: publicCallsStack.slice(firstRevertibleCallIndex),
148
160
  [PublicKernelPhase.TEARDOWN]: [publicCallsStack[firstRevertibleCallIndex - 1]],
161
+ [PublicKernelPhase.TAIL]: [],
149
162
  };
150
163
  }
151
164
  }
@@ -158,55 +171,18 @@ export abstract class AbstractPhaseManager {
158
171
  return calls;
159
172
  }
160
173
 
161
- public static getKernelOutputAndProof(
162
- tx: Tx,
163
- publicKernelPublicInput?: PublicKernelCircuitPublicInputs,
164
- previousPublicKernelProof?: Proof,
165
- ): {
166
- /**
167
- * the output of the public kernel circuit for this phase
168
- */
169
- publicKernelPublicInput: PublicKernelCircuitPublicInputs;
170
- /**
171
- * the proof of the public kernel circuit for this phase
172
- */
173
- publicKernelProof: Proof;
174
- } {
175
- if (publicKernelPublicInput && previousPublicKernelProof) {
176
- return {
177
- publicKernelPublicInput: publicKernelPublicInput,
178
- publicKernelProof: previousPublicKernelProof,
179
- };
180
- } else {
181
- const publicKernelPublicInput = new PublicKernelCircuitPublicInputs(
182
- tx.data.aggregationObject,
183
- PublicAccumulatedNonRevertibleData.fromPrivateAccumulatedNonRevertibleData(tx.data.endNonRevertibleData),
184
- PublicAccumulatedRevertibleData.fromPrivateAccumulatedRevertibleData(tx.data.end),
185
- tx.data.constants,
186
- tx.data.needsSetup,
187
- tx.data.needsAppLogic,
188
- tx.data.needsTeardown,
189
- );
190
- const publicKernelProof = previousPublicKernelProof || tx.proof;
191
- return {
192
- publicKernelPublicInput,
193
- publicKernelProof,
194
- };
195
- }
196
- }
197
-
198
174
  protected async processEnqueuedPublicCalls(
199
175
  tx: Tx,
200
176
  previousPublicKernelOutput: PublicKernelCircuitPublicInputs,
201
177
  previousPublicKernelProof: Proof,
202
- ): Promise<[PublicKernelCircuitPublicInputs, Proof, FunctionL2Logs[]]> {
178
+ ): Promise<[PublicKernelCircuitPublicInputs, Proof, FunctionL2Logs[], SimulationError | undefined]> {
203
179
  let kernelOutput = previousPublicKernelOutput;
204
180
  let kernelProof = previousPublicKernelProof;
205
181
 
206
182
  const enqueuedCalls = this.extractEnqueuedPublicCalls(tx);
207
183
 
208
184
  if (!enqueuedCalls || !enqueuedCalls.length) {
209
- return [kernelOutput, kernelProof, []];
185
+ return [kernelOutput, kernelProof, [], undefined];
210
186
  }
211
187
 
212
188
  const newUnencryptedFunctionLogs: FunctionL2Logs[] = [];
@@ -241,7 +217,18 @@ export abstract class AbstractPhaseManager {
241
217
  executionStack.push(...result.nestedExecutions);
242
218
  const callData = await this.getPublicCallData(result, isExecutionRequest);
243
219
 
244
- [kernelOutput, kernelProof] = await this.runKernelCircuit(callData, kernelOutput, kernelProof);
220
+ [kernelOutput, kernelProof] = await this.runKernelCircuit(kernelOutput, kernelProof, callData);
221
+
222
+ if (kernelOutput.reverted && this.phase === PublicKernelPhase.APP_LOGIC) {
223
+ this.log.debug(
224
+ `Reverting on ${result.execution.contractAddress.toString()}:${functionSelector} with reason: ${
225
+ result.revertReason
226
+ }`,
227
+ );
228
+ // halt immediately if the public kernel circuit has reverted.
229
+ // return no logs, as they should not go on-chain.
230
+ return [kernelOutput, kernelProof, [], result.revertReason];
231
+ }
245
232
 
246
233
  if (!enqueuedExecutionResult) {
247
234
  enqueuedExecutionResult = result;
@@ -255,25 +242,42 @@ export abstract class AbstractPhaseManager {
255
242
  // TODO(#3675): This should be done in a public kernel circuit
256
243
  removeRedundantPublicDataWrites(kernelOutput);
257
244
 
258
- return [kernelOutput, kernelProof, newUnencryptedFunctionLogs];
245
+ return [kernelOutput, kernelProof, newUnencryptedFunctionLogs, undefined];
259
246
  }
260
247
 
261
248
  protected async runKernelCircuit(
262
- callData: PublicCallData,
263
249
  previousOutput: PublicKernelCircuitPublicInputs,
264
250
  previousProof: Proof,
251
+ callData?: PublicCallData,
265
252
  ): Promise<[PublicKernelCircuitPublicInputs, Proof]> {
266
- const output = await this.getKernelCircuitOutput(callData, previousOutput, previousProof);
253
+ const output = await this.getKernelCircuitOutput(previousOutput, previousProof, callData);
267
254
  const proof = await this.publicProver.getPublicKernelCircuitProof(output);
268
255
  return [output, proof];
269
256
  }
270
257
 
271
- protected getKernelCircuitOutput(
272
- callData: PublicCallData,
258
+ protected async getKernelCircuitOutput(
273
259
  previousOutput: PublicKernelCircuitPublicInputs,
274
260
  previousProof: Proof,
261
+ callData?: PublicCallData,
275
262
  ): Promise<PublicKernelCircuitPublicInputs> {
276
263
  const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
264
+
265
+ if (this.phase === PublicKernelPhase.TAIL) {
266
+ const { endNonRevertibleData, end } = previousOutput;
267
+ const nullifierReadRequestResetHints = await this.hintsBuilder.getNullifierReadRequestResetHints(
268
+ endNonRevertibleData.nullifierReadRequests,
269
+ end.nullifierReadRequests,
270
+ endNonRevertibleData.newNullifiers,
271
+ end.newNullifiers,
272
+ );
273
+ const inputs = new PublicKernelTailCircuitPrivateInputs(previousKernel, nullifierReadRequestResetHints);
274
+ return this.publicKernel.publicKernelCircuitTail(inputs);
275
+ }
276
+
277
+ if (!callData) {
278
+ throw new Error(`Cannot run public kernel circuit without call data for phase '${this.phase}'.`);
279
+ }
280
+
277
281
  const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, callData);
278
282
  switch (this.phase) {
279
283
  case PublicKernelPhase.SETUP:
@@ -320,6 +324,11 @@ export abstract class AbstractPhaseManager {
320
324
  newNullifiers: padArrayEnd(result.newNullifiers, SideEffectLinkedToNoteHash.empty(), MAX_NEW_NULLIFIERS_PER_CALL),
321
325
  newL2ToL1Msgs: padArrayEnd(result.newL2ToL1Messages, L2ToL1Message.empty(), MAX_NEW_L2_TO_L1_MSGS_PER_CALL),
322
326
  returnValues: padArrayEnd(result.returnValues, Fr.ZERO, RETURN_VALUES_LENGTH),
327
+ nullifierReadRequests: padArrayEnd(
328
+ result.nullifierReadRequests,
329
+ ReadRequest.empty(),
330
+ MAX_NULLIFIER_READ_REQUESTS_PER_CALL,
331
+ ),
323
332
  contractStorageReads: padArrayEnd(
324
333
  result.contractStorageReads,
325
334
  ContractStorageRead.empty(),
@@ -334,6 +343,7 @@ export abstract class AbstractPhaseManager {
334
343
  unencryptedLogsHash,
335
344
  unencryptedLogPreimagesLength,
336
345
  historicalHeader: this.historicalHeader,
346
+ reverted: result.reverted,
337
347
  });
338
348
  }
339
349
 
@@ -27,42 +27,32 @@ export class AppLogicPhaseManager extends AbstractPhaseManager {
27
27
  super(db, publicExecutor, publicKernel, publicProver, globalVariables, historicalHeader, phase);
28
28
  }
29
29
 
30
- async handle(
30
+ override async handle(
31
31
  tx: Tx,
32
32
  previousPublicKernelOutput: PublicKernelCircuitPublicInputs,
33
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
- }> {
34
+ ) {
44
35
  // add new contracts to the contracts db so that their functions may be found and called
45
36
  // TODO(#4073): This is catching only private deployments, when we add public ones, we'll
46
37
  // have to capture contracts emitted in that phase as well.
47
38
  // TODO(@spalladino): Should we allow emitting contracts in the fee preparation phase?
48
39
  this.log(`Processing tx ${tx.getTxHash()}`);
49
40
  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);
41
+ const [publicKernelOutput, publicKernelProof, newUnencryptedFunctionLogs, revertReason] =
42
+ await this.processEnqueuedPublicCalls(tx, previousPublicKernelOutput, previousPublicKernelProof);
57
43
 
58
- // commit the state updates from this transaction
59
- await this.publicStateDB.commit();
44
+ if (revertReason) {
45
+ await this.rollback(tx, revertReason);
46
+ } else {
47
+ tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
48
+ await this.publicStateDB.commit();
49
+ }
60
50
 
61
- return { publicKernelOutput, publicKernelProof };
51
+ return { publicKernelOutput, publicKernelProof, revertReason };
62
52
  }
63
53
 
64
54
  async rollback(tx: Tx, err: unknown): Promise<FailedTx> {
65
- this.log.warn(`Error processing tx ${tx.getTxHash()}: ${err}`);
55
+ this.log.warn(`Rolling back changes from ${tx.getTxHash()}`);
66
56
  // remove contracts on failure
67
57
  await this.publicContractsDB.removeNewContracts(tx);
68
58
  // rollback any state updates from this failed transaction
@@ -0,0 +1,56 @@
1
+ import { MerkleTreeId } from '@aztec/circuit-types';
2
+ import {
3
+ Fr,
4
+ MAX_NEW_NULLIFIERS_PER_TX,
5
+ MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX,
6
+ MAX_NULLIFIER_READ_REQUESTS_PER_TX,
7
+ MAX_REVERTIBLE_NULLIFIERS_PER_TX,
8
+ MembershipWitness,
9
+ NULLIFIER_TREE_HEIGHT,
10
+ NullifierLeafPreimage,
11
+ ReadRequestContext,
12
+ SideEffectLinkedToNoteHash,
13
+ buildNullifierReadRequestResetHints,
14
+ concatAccumulatedData,
15
+ mergeAccumulatedData,
16
+ } from '@aztec/circuits.js';
17
+ import { Tuple } from '@aztec/foundation/serialize';
18
+ import { MerkleTreeOperations } from '@aztec/world-state';
19
+
20
+ export class HintsBuilder {
21
+ constructor(private db: MerkleTreeOperations) {}
22
+
23
+ getNullifierReadRequestResetHints(
24
+ nullifierReadRequestsNonRevertible: Tuple<ReadRequestContext, typeof MAX_NULLIFIER_READ_REQUESTS_PER_TX>,
25
+ nullifierReadRequestsRevertible: Tuple<ReadRequestContext, typeof MAX_NULLIFIER_READ_REQUESTS_PER_TX>,
26
+ nullifiersNonRevertible: Tuple<SideEffectLinkedToNoteHash, typeof MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX>,
27
+ nullifiersRevertible: Tuple<SideEffectLinkedToNoteHash, typeof MAX_REVERTIBLE_NULLIFIERS_PER_TX>,
28
+ ) {
29
+ return buildNullifierReadRequestResetHints(
30
+ this,
31
+ mergeAccumulatedData(
32
+ MAX_NULLIFIER_READ_REQUESTS_PER_TX,
33
+ nullifierReadRequestsNonRevertible,
34
+ nullifierReadRequestsRevertible,
35
+ ),
36
+ concatAccumulatedData(MAX_NEW_NULLIFIERS_PER_TX, nullifiersNonRevertible, nullifiersRevertible),
37
+ );
38
+ }
39
+
40
+ async getNullifierMembershipWitness(nullifier: Fr) {
41
+ const index = await this.db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer());
42
+ if (index === undefined) {
43
+ return;
44
+ }
45
+
46
+ const siblingPath = await this.db.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, index);
47
+ const membershipWitness = new MembershipWitness(
48
+ NULLIFIER_TREE_HEIGHT,
49
+ index,
50
+ siblingPath.toTuple<typeof NULLIFIER_TREE_HEIGHT>(),
51
+ );
52
+ const leafPreimage = (await this.db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))! as NullifierLeafPreimage;
53
+
54
+ return { membershipWitness, leafPreimage };
55
+ }
56
+ }
@@ -1,2 +1,2 @@
1
- export * from './sequencer.js';
2
1
  export * from './config.js';
2
+ export * from './sequencer.js';
@@ -9,6 +9,7 @@ import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js';
9
9
  import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
10
10
  import { AppLogicPhaseManager } from './app_logic_phase_manager.js';
11
11
  import { SetupPhaseManager } from './setup_phase_manager.js';
12
+ import { TailPhaseManager } from './tail_phase_manager.js';
12
13
  import { TeardownPhaseManager } from './teardown_phase_manager.js';
13
14
 
14
15
  export class PhaseDidNotChangeError extends Error {
@@ -115,6 +116,17 @@ export class PhaseManagerFactory {
115
116
  publicContractsDB,
116
117
  publicStateDB,
117
118
  );
119
+ } else if (currentPhaseManager.phase !== PublicKernelPhase.TAIL) {
120
+ return new TailPhaseManager(
121
+ db,
122
+ publicExecutor,
123
+ publicKernel,
124
+ publicProver,
125
+ globalVariables,
126
+ historicalHeader,
127
+ publicContractsDB,
128
+ publicStateDB,
129
+ );
118
130
  } else {
119
131
  return undefined;
120
132
  }
@@ -1,13 +1,29 @@
1
- import { ExtendedContractData, Tx, TxHash, TxL2Logs } from '@aztec/circuit-types';
1
+ import {
2
+ ContractData,
3
+ ExtendedContractData,
4
+ PublicDataWrite,
5
+ SimulationError,
6
+ Tx,
7
+ TxEffect,
8
+ TxHash,
9
+ TxL2Logs,
10
+ } from '@aztec/circuit-types';
2
11
  import {
3
12
  Fr,
4
13
  Header,
14
+ MAX_NEW_CONTRACTS_PER_TX,
15
+ MAX_NEW_NOTE_HASHES_PER_TX,
16
+ MAX_NEW_NULLIFIERS_PER_TX,
17
+ MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
5
18
  Proof,
6
19
  PublicAccumulatedNonRevertibleData,
7
20
  PublicAccumulatedRevertibleData,
8
21
  PublicKernelCircuitPublicInputs,
22
+ SideEffect,
23
+ SideEffectLinkedToNoteHash,
9
24
  makeEmptyProof,
10
25
  } from '@aztec/circuits.js';
26
+ import { Tuple, fromFieldsTuple } from '@aztec/foundation/serialize';
11
27
 
12
28
  /**
13
29
  * Represents a tx that has been processed by the sequencer public processor,
@@ -26,8 +42,39 @@ export type ProcessedTx = Pick<Tx, 'proof' | 'encryptedLogs' | 'unencryptedLogs'
26
42
  * Flag indicating the tx is 'empty' meaning it's a padding tx to take us to a power of 2.
27
43
  */
28
44
  isEmpty: boolean;
45
+
46
+ /**
47
+ * Reason the tx was reverted.
48
+ */
49
+ revertReason: SimulationError | undefined;
29
50
  };
30
51
 
52
+ export type RevertedTx = ProcessedTx & {
53
+ data: PublicKernelCircuitPublicInputs & {
54
+ reverted: true;
55
+ };
56
+
57
+ revertReason: SimulationError;
58
+ };
59
+
60
+ export function isRevertedTx(tx: ProcessedTx): tx is RevertedTx {
61
+ return tx.data.reverted;
62
+ }
63
+
64
+ export function partitionReverts(txs: ProcessedTx[]): { reverted: RevertedTx[]; nonReverted: ProcessedTx[] } {
65
+ return txs.reduce(
66
+ ({ reverted, nonReverted }, tx) => {
67
+ if (isRevertedTx(tx)) {
68
+ reverted.push(tx);
69
+ } else {
70
+ nonReverted.push(tx);
71
+ }
72
+ return { reverted, nonReverted };
73
+ },
74
+ { reverted: [], nonReverted: [] } as ReturnType<typeof partitionReverts>,
75
+ );
76
+ }
77
+
31
78
  /**
32
79
  * Represents a tx that failed to be processed by the sequencer public processor.
33
80
  */
@@ -42,31 +89,73 @@ export type FailedTx = {
42
89
  error: Error;
43
90
  };
44
91
 
92
+ /**
93
+ *
94
+ * @param tx - the TX being procesed
95
+ * @param publicKernelPublicInput - the output of the public kernel circuit, unless we just came from private
96
+ * @param publicKernelProof - the proof of the public kernel circuit, unless we just came from private
97
+ * @returns PublicKernelCircuitPublicInputs, either passed through from the input or converted from the output of the TX,
98
+ * and Proof, either passed through from the input or the proof of the TX
99
+ */
100
+ export function getPreviousOutputAndProof(
101
+ tx: Tx,
102
+ publicKernelPublicInput?: PublicKernelCircuitPublicInputs,
103
+ publicKernelProof?: Proof,
104
+ ): {
105
+ /**
106
+ * the output of the public kernel circuit for this phase
107
+ */
108
+ publicKernelPublicInput: PublicKernelCircuitPublicInputs;
109
+ /**
110
+ * the proof of the public kernel circuit for this phase
111
+ */
112
+ previousProof: Proof;
113
+ } {
114
+ if (publicKernelPublicInput && publicKernelProof) {
115
+ return {
116
+ publicKernelPublicInput,
117
+ previousProof: publicKernelProof,
118
+ };
119
+ } else {
120
+ const publicKernelPublicInput = new PublicKernelCircuitPublicInputs(
121
+ tx.data.aggregationObject,
122
+ PublicAccumulatedNonRevertibleData.fromPrivateAccumulatedNonRevertibleData(tx.data.endNonRevertibleData),
123
+ PublicAccumulatedRevertibleData.fromPrivateAccumulatedRevertibleData(tx.data.end),
124
+ tx.data.constants,
125
+ tx.data.needsSetup,
126
+ tx.data.needsAppLogic,
127
+ tx.data.needsTeardown,
128
+ false, // reverted
129
+ );
130
+ return {
131
+ publicKernelPublicInput,
132
+ previousProof: publicKernelProof || tx.proof,
133
+ };
134
+ }
135
+ }
136
+
45
137
  /**
46
138
  * Makes a processed tx out of source tx.
47
139
  * @param tx - Source tx.
48
140
  * @param kernelOutput - Output of the kernel circuit simulation for this tx.
49
141
  * @param proof - Proof of the kernel circuit for this tx.
50
142
  */
51
- export function makeProcessedTx(tx: Tx, kernelOutput?: PublicKernelCircuitPublicInputs, proof?: Proof): ProcessedTx {
143
+ export function makeProcessedTx(
144
+ tx: Tx,
145
+ kernelOutput?: PublicKernelCircuitPublicInputs,
146
+ proof?: Proof,
147
+ revertReason?: SimulationError,
148
+ ): ProcessedTx {
149
+ const { publicKernelPublicInput, previousProof } = getPreviousOutputAndProof(tx, kernelOutput, proof);
52
150
  return {
53
151
  hash: tx.getTxHash(),
54
- data:
55
- kernelOutput ??
56
- new PublicKernelCircuitPublicInputs(
57
- tx.data.aggregationObject,
58
- PublicAccumulatedNonRevertibleData.fromPrivateAccumulatedNonRevertibleData(tx.data.endNonRevertibleData),
59
- PublicAccumulatedRevertibleData.fromPrivateAccumulatedRevertibleData(tx.data.end),
60
- tx.data.constants,
61
- tx.data.needsSetup,
62
- tx.data.needsAppLogic,
63
- tx.data.needsTeardown,
64
- ),
65
- proof: proof ?? tx.proof,
66
- encryptedLogs: tx.encryptedLogs,
67
- unencryptedLogs: tx.unencryptedLogs,
68
- newContracts: tx.newContracts,
152
+ data: publicKernelPublicInput,
153
+ proof: previousProof,
154
+ encryptedLogs: revertReason ? new TxL2Logs([]) : tx.encryptedLogs,
155
+ unencryptedLogs: revertReason ? new TxL2Logs([]) : tx.unencryptedLogs,
156
+ newContracts: revertReason ? [ExtendedContractData.empty()] : tx.newContracts,
69
157
  isEmpty: false,
158
+ revertReason,
70
159
  };
71
160
  }
72
161
 
@@ -90,5 +179,46 @@ export function makeEmptyProcessedTx(header: Header, chainId: Fr, version: Fr):
90
179
  proof: emptyProof,
91
180
  newContracts: [ExtendedContractData.empty()],
92
181
  isEmpty: true,
182
+ revertReason: undefined,
93
183
  };
94
184
  }
185
+
186
+ export function toTxEffect(tx: ProcessedTx): TxEffect {
187
+ return new TxEffect(
188
+ tx.data.combinedData.newNoteHashes.map((c: SideEffect) => c.value) as Tuple<Fr, typeof MAX_NEW_NOTE_HASHES_PER_TX>,
189
+ tx.data.combinedData.newNullifiers.map((n: SideEffectLinkedToNoteHash) => n.value) as Tuple<
190
+ Fr,
191
+ typeof MAX_NEW_NULLIFIERS_PER_TX
192
+ >,
193
+ tx.data.combinedData.newL2ToL1Msgs,
194
+ tx.data.combinedData.publicDataUpdateRequests.map(t => new PublicDataWrite(t.leafSlot, t.newValue)) as Tuple<
195
+ PublicDataWrite,
196
+ typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
197
+ >,
198
+ tx.data.combinedData.newContracts.map(cd => cd.hash()) as Tuple<Fr, typeof MAX_NEW_CONTRACTS_PER_TX>,
199
+ tx.data.combinedData.newContracts.map(
200
+ cd => new ContractData(cd.contractAddress, cd.portalContractAddress),
201
+ ) as Tuple<ContractData, typeof MAX_NEW_CONTRACTS_PER_TX>,
202
+ tx.encryptedLogs || new TxL2Logs([]),
203
+ tx.unencryptedLogs || new TxL2Logs([]),
204
+ );
205
+ }
206
+
207
+ function validateProcessedTxLogs(tx: ProcessedTx): void {
208
+ const unencryptedLogs = tx.unencryptedLogs || new TxL2Logs([]);
209
+ const kernelUnencryptedLogsHash = fromFieldsTuple(tx.data.combinedData.unencryptedLogsHash);
210
+ if (!unencryptedLogs.hash().equals(kernelUnencryptedLogsHash)) {
211
+ throw new Error(
212
+ `Unencrypted logs hash mismatch. Expected ${unencryptedLogs
213
+ .hash()
214
+ .toString('hex')}, got ${kernelUnencryptedLogsHash.toString('hex')}.
215
+ Processed: ${JSON.stringify(unencryptedLogs.toJSON())}
216
+ Kernel Length: ${tx.data.combinedData.unencryptedLogPreimagesLength}`,
217
+ );
218
+ }
219
+ }
220
+
221
+ export function validateProcessedTx(tx: ProcessedTx): void {
222
+ validateProcessedTxLogs(tx);
223
+ // TODO: validate other fields
224
+ }
@@ -1,4 +1,4 @@
1
- import { ContractDataSource, L1ToL2MessageSource, Tx } from '@aztec/circuit-types';
1
+ import { ContractDataSource, L1ToL2MessageSource, SimulationError, Tx } from '@aztec/circuit-types';
2
2
  import { TxSequencerProcessingStats } from '@aztec/circuit-types/stats';
3
3
  import { GlobalVariables, Header } from '@aztec/circuits.js';
4
4
  import { createDebugLogger } from '@aztec/foundation/log';
@@ -11,9 +11,17 @@ 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
- import { FailedTx, ProcessedTx, makeEmptyProcessedTx, makeProcessedTx } from './processed_tx.js';
17
+ import {
18
+ FailedTx,
19
+ ProcessedTx,
20
+ getPreviousOutputAndProof,
21
+ makeEmptyProcessedTx,
22
+ makeProcessedTx,
23
+ validateProcessedTx,
24
+ } from './processed_tx.js';
17
25
 
18
26
  /**
19
27
  * Creates new instances of PublicProcessor given the provided merkle tree db and contract data source.
@@ -23,6 +31,7 @@ export class PublicProcessorFactory {
23
31
  private merkleTree: MerkleTreeOperations,
24
32
  private contractDataSource: ContractDataSource,
25
33
  private l1Tol2MessagesDataSource: L1ToL2MessageSource,
34
+ private simulator: SimulationProvider,
26
35
  ) {}
27
36
 
28
37
  /**
@@ -45,7 +54,7 @@ export class PublicProcessorFactory {
45
54
  return new PublicProcessor(
46
55
  this.merkleTree,
47
56
  publicExecutor,
48
- new RealPublicKernelCircuitSimulator(),
57
+ new RealPublicKernelCircuitSimulator(this.simulator),
49
58
  new EmptyPublicProver(),
50
59
  globalVariables,
51
60
  historicalHeader,
@@ -97,17 +106,15 @@ export class PublicProcessor {
97
106
  this.publicStateDB,
98
107
  );
99
108
  this.log(`Beginning processing in phase ${phase?.phase} for tx ${tx.getTxHash()}`);
100
- let { publicKernelPublicInput, publicKernelProof } = AbstractPhaseManager.getKernelOutputAndProof(
101
- tx,
102
- undefined,
103
- undefined,
104
- );
109
+ let { publicKernelPublicInput, previousProof: proof } = getPreviousOutputAndProof(tx, undefined, undefined);
110
+ let revertReason: SimulationError | undefined;
105
111
  const timer = new Timer();
106
112
  try {
107
113
  while (phase) {
108
- const output = await phase.handle(tx, publicKernelPublicInput, publicKernelProof);
114
+ const output = await phase.handle(tx, publicKernelPublicInput, proof);
109
115
  publicKernelPublicInput = output.publicKernelOutput;
110
- publicKernelProof = output.publicKernelProof;
116
+ proof = output.publicKernelProof;
117
+ revertReason ??= output.revertReason;
111
118
  phase = PhaseManagerFactory.phaseFromOutput(
112
119
  publicKernelPublicInput,
113
120
  phase,
@@ -122,7 +129,9 @@ export class PublicProcessor {
122
129
  );
123
130
  }
124
131
 
125
- const processedTransaction = makeProcessedTx(tx, publicKernelPublicInput, publicKernelProof);
132
+ const processedTransaction = makeProcessedTx(tx, publicKernelPublicInput, proof, revertReason);
133
+ validateProcessedTx(processedTransaction);
134
+
126
135
  result.push(processedTransaction);
127
136
 
128
137
  this.log(`Processed public part of ${tx.data.endNonRevertibleData.newNullifiers[0].value}`, {
@@ -133,9 +142,14 @@ export class PublicProcessor {
133
142
  0,
134
143
  ...tx.getStats(),
135
144
  } satisfies TxSequencerProcessingStats);
136
- } catch (err) {
137
- const failedTx = await phase!.rollback(tx, err);
138
- failed.push(failedTx);
145
+ } catch (err: any) {
146
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
147
+ this.log.warn(`Failed to process tx ${tx.getTxHash()}: ${errorMessage}`);
148
+
149
+ failed.push({
150
+ tx,
151
+ error: err instanceof Error ? err : new Error(errorMessage),
152
+ });
139
153
  }
140
154
  }
141
155