@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.
- package/dest/block_builder/solo_block_builder.d.ts +9 -9
- package/dest/block_builder/solo_block_builder.d.ts.map +1 -1
- package/dest/block_builder/solo_block_builder.js +26 -48
- package/dest/global_variable_builder/viem-reader.js +2 -2
- package/dest/publisher/l1-publisher.d.ts +0 -2
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +2 -3
- package/dest/publisher/viem-tx-sender.d.ts.map +1 -1
- package/dest/publisher/viem-tx-sender.js +6 -10
- package/dest/sequencer/abstract_phase_manager.d.ts +18 -25
- package/dest/sequencer/abstract_phase_manager.d.ts.map +1 -1
- package/dest/sequencer/abstract_phase_manager.js +141 -91
- package/dest/sequencer/{fee_distribution_phase_manager.d.ts → app_logic_phase_manager.d.ts} +9 -11
- package/dest/sequencer/app_logic_phase_manager.d.ts.map +1 -0
- package/dest/sequencer/app_logic_phase_manager.js +44 -0
- package/dest/sequencer/phase_manager_factory.d.ts +19 -0
- package/dest/sequencer/phase_manager_factory.d.ts.map +1 -0
- package/dest/sequencer/phase_manager_factory.js +51 -0
- package/dest/sequencer/processed_tx.d.ts +2 -2
- package/dest/sequencer/processed_tx.d.ts.map +1 -1
- package/dest/sequencer/processed_tx.js +7 -7
- package/dest/sequencer/public_processor.d.ts +1 -1
- package/dest/sequencer/public_processor.d.ts.map +1 -1
- package/dest/sequencer/public_processor.js +13 -11
- package/dest/sequencer/sequencer.d.ts +3 -3
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +23 -16
- package/dest/sequencer/{fee_preparation_phase_manager.d.ts → setup_phase_manager.d.ts} +9 -11
- package/dest/sequencer/setup_phase_manager.d.ts.map +1 -0
- package/dest/sequencer/setup_phase_manager.js +37 -0
- package/dest/sequencer/{application_logic_phase_manager.d.ts → teardown_phase_manager.d.ts} +9 -12
- package/dest/sequencer/teardown_phase_manager.d.ts.map +1 -0
- package/dest/sequencer/teardown_phase_manager.js +36 -0
- package/dest/simulator/index.d.ts +10 -4
- package/dest/simulator/index.d.ts.map +1 -1
- package/dest/simulator/public_executor.d.ts +8 -3
- package/dest/simulator/public_executor.d.ts.map +1 -1
- package/dest/simulator/public_executor.js +64 -11
- package/dest/simulator/public_kernel.d.ts +10 -4
- package/dest/simulator/public_kernel.d.ts.map +1 -1
- package/dest/simulator/public_kernel.js +33 -14
- package/package.json +14 -13
- package/src/block_builder/solo_block_builder.ts +61 -50
- package/src/global_variable_builder/viem-reader.ts +1 -1
- package/src/publisher/l1-publisher.ts +1 -4
- package/src/publisher/viem-tx-sender.ts +6 -12
- package/src/sequencer/abstract_phase_manager.ts +207 -145
- package/src/sequencer/app_logic_phase_manager.ts +75 -0
- package/src/sequencer/phase_manager_factory.ts +122 -0
- package/src/sequencer/processed_tx.ts +12 -13
- package/src/sequencer/public_processor.ts +29 -12
- package/src/sequencer/sequencer.ts +22 -15
- package/src/sequencer/{fee_distribution_phase_manager.ts → setup_phase_manager.ts} +23 -25
- package/src/sequencer/teardown_phase_manager.ts +67 -0
- package/src/simulator/index.ts +10 -6
- package/src/simulator/public_executor.ts +108 -10
- package/src/simulator/public_kernel.ts +39 -13
- package/dest/sequencer/application_logic_phase_manager.d.ts.map +0 -1
- package/dest/sequencer/application_logic_phase_manager.js +0 -64
- package/dest/sequencer/fee_distribution_phase_manager.d.ts.map +0 -1
- package/dest/sequencer/fee_distribution_phase_manager.js +0 -42
- package/dest/sequencer/fee_preparation_phase_manager.d.ts.map +0 -1
- package/dest/sequencer/fee_preparation_phase_manager.js +0 -42
- package/src/sequencer/application_logic_phase_manager.ts +0 -107
- 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
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
|
83
|
-
previousPublicKernelProof
|
|
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
|
|
100
|
+
publicKernelOutput: PublicKernelCircuitPublicInputs;
|
|
89
101
|
/**
|
|
90
102
|
* the proof of the public kernel circuit for this phase
|
|
91
103
|
*/
|
|
92
|
-
publicKernelProof
|
|
104
|
+
publicKernelProof: Proof;
|
|
93
105
|
}>;
|
|
94
|
-
abstract nextPhase(): AbstractPhaseManager | undefined;
|
|
95
106
|
abstract rollback(tx: Tx, err: unknown): Promise<FailedTx>;
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
+
PublicAccumulatedNonRevertibleData.fromPrivateAccumulatedNonRevertibleData(tx.data.endNonRevertibleData),
|
|
184
|
+
PublicAccumulatedRevertibleData.fromPrivateAccumulatedRevertibleData(tx.data.end),
|
|
124
185
|
tx.data.constants,
|
|
125
|
-
tx.data.
|
|
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
|
-
|
|
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 ${
|
|
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
|
-
|
|
252
|
+
patchPublicStorageActionOrdering(kernelOutput, enqueuedExecutionResult!, this.phase);
|
|
187
253
|
}
|
|
188
254
|
|
|
189
255
|
// TODO(#3675): This should be done in a public kernel circuit
|
|
190
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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,
|
|
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 =>
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
+
}
|