@aztec/sequencer-client 0.32.1 → 0.33.0
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/sequencer/abstract_phase_manager.d.ts +16 -4
- package/dest/sequencer/abstract_phase_manager.d.ts.map +1 -1
- package/dest/sequencer/abstract_phase_manager.js +38 -39
- package/dest/sequencer/app_logic_phase_manager.d.ts +1 -0
- package/dest/sequencer/app_logic_phase_manager.d.ts.map +1 -1
- package/dest/sequencer/app_logic_phase_manager.js +3 -3
- package/dest/sequencer/hints_builder.d.ts +7 -3
- package/dest/sequencer/hints_builder.d.ts.map +1 -1
- package/dest/sequencer/hints_builder.js +24 -4
- package/dest/sequencer/phase_manager_factory.d.ts.map +1 -1
- package/dest/sequencer/phase_manager_factory.js +5 -4
- package/dest/sequencer/public_processor.d.ts +5 -2
- package/dest/sequencer/public_processor.d.ts.map +1 -1
- package/dest/sequencer/public_processor.js +65 -27
- package/dest/sequencer/sequencer.d.ts +1 -11
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +33 -38
- package/dest/sequencer/setup_phase_manager.d.ts +1 -0
- package/dest/sequencer/setup_phase_manager.d.ts.map +1 -1
- package/dest/sequencer/setup_phase_manager.js +2 -2
- package/dest/sequencer/tail_phase_manager.d.ts +6 -1
- package/dest/sequencer/tail_phase_manager.d.ts.map +1 -1
- package/dest/sequencer/tail_phase_manager.js +28 -3
- package/dest/sequencer/teardown_phase_manager.d.ts +1 -0
- package/dest/sequencer/teardown_phase_manager.d.ts.map +1 -1
- package/dest/sequencer/teardown_phase_manager.js +2 -2
- package/dest/sequencer/tx_validator.d.ts.map +1 -1
- package/dest/sequencer/tx_validator.js +8 -7
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +8 -7
- package/dest/simulator/index.d.ts +2 -2
- package/dest/simulator/index.d.ts.map +1 -1
- package/dest/simulator/public_kernel.d.ts +2 -2
- package/dest/simulator/public_kernel.d.ts.map +1 -1
- package/dest/simulator/public_kernel.js +1 -1
- package/package.json +32 -15
- package/src/sequencer/abstract_phase_manager.ts +74 -63
- package/src/sequencer/app_logic_phase_manager.ts +2 -2
- package/src/sequencer/hints_builder.ts +42 -9
- package/src/sequencer/phase_manager_factory.ts +4 -3
- package/src/sequencer/public_processor.ts +97 -51
- package/src/sequencer/sequencer.ts +41 -50
- package/src/sequencer/setup_phase_manager.ts +1 -1
- package/src/sequencer/tail_phase_manager.ts +66 -2
- package/src/sequencer/teardown_phase_manager.ts +1 -1
- package/src/sequencer/tx_validator.ts +8 -6
- package/src/sequencer/utils.ts +7 -6
- package/src/simulator/index.ts +2 -1
- package/src/simulator/public_kernel.ts +2 -1
- package/dest/utils.d.ts +0 -12
- package/dest/utils.d.ts.map +0 -1
- package/dest/utils.js +0 -16
- package/src/utils.ts +0 -16
|
@@ -2,18 +2,21 @@ import { MerkleTreeId } from '@aztec/circuit-types';
|
|
|
2
2
|
import {
|
|
3
3
|
type Fr,
|
|
4
4
|
MAX_NEW_NULLIFIERS_PER_TX,
|
|
5
|
-
type MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX,
|
|
6
5
|
type MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX,
|
|
7
6
|
type MAX_NULLIFIER_READ_REQUESTS_PER_TX,
|
|
8
|
-
|
|
7
|
+
MAX_PUBLIC_DATA_READS_PER_TX,
|
|
9
8
|
MembershipWitness,
|
|
10
9
|
NULLIFIER_TREE_HEIGHT,
|
|
10
|
+
PUBLIC_DATA_TREE_HEIGHT,
|
|
11
|
+
type PublicDataRead,
|
|
12
|
+
PublicDataTreeLeafPreimage,
|
|
11
13
|
type ReadRequestContext,
|
|
12
14
|
type SideEffectLinkedToNoteHash,
|
|
13
15
|
buildNullifierNonExistentReadRequestHints,
|
|
14
16
|
buildNullifierReadRequestHints,
|
|
15
|
-
|
|
17
|
+
mergeAccumulatedData,
|
|
16
18
|
} from '@aztec/circuits.js';
|
|
19
|
+
import { makeTuple } from '@aztec/foundation/array';
|
|
17
20
|
import { type Tuple } from '@aztec/foundation/serialize';
|
|
18
21
|
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
19
22
|
|
|
@@ -22,22 +25,22 @@ export class HintsBuilder {
|
|
|
22
25
|
|
|
23
26
|
getNullifierReadRequestHints(
|
|
24
27
|
nullifierReadRequests: Tuple<ReadRequestContext, typeof MAX_NULLIFIER_READ_REQUESTS_PER_TX>,
|
|
25
|
-
nullifiersNonRevertible: Tuple<SideEffectLinkedToNoteHash, typeof
|
|
26
|
-
nullifiersRevertible: Tuple<SideEffectLinkedToNoteHash, typeof
|
|
28
|
+
nullifiersNonRevertible: Tuple<SideEffectLinkedToNoteHash, typeof MAX_NEW_NULLIFIERS_PER_TX>,
|
|
29
|
+
nullifiersRevertible: Tuple<SideEffectLinkedToNoteHash, typeof MAX_NEW_NULLIFIERS_PER_TX>,
|
|
27
30
|
) {
|
|
28
31
|
return buildNullifierReadRequestHints(
|
|
29
32
|
this,
|
|
30
33
|
nullifierReadRequests,
|
|
31
|
-
|
|
34
|
+
mergeAccumulatedData(MAX_NEW_NULLIFIERS_PER_TX, nullifiersNonRevertible, nullifiersRevertible),
|
|
32
35
|
);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
getNullifierNonExistentReadRequestHints(
|
|
36
39
|
nullifierNonExistentReadRequests: Tuple<ReadRequestContext, typeof MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX>,
|
|
37
|
-
nullifiersNonRevertible: Tuple<SideEffectLinkedToNoteHash, typeof
|
|
38
|
-
nullifiersRevertible: Tuple<SideEffectLinkedToNoteHash, typeof
|
|
40
|
+
nullifiersNonRevertible: Tuple<SideEffectLinkedToNoteHash, typeof MAX_NEW_NULLIFIERS_PER_TX>,
|
|
41
|
+
nullifiersRevertible: Tuple<SideEffectLinkedToNoteHash, typeof MAX_NEW_NULLIFIERS_PER_TX>,
|
|
39
42
|
) {
|
|
40
|
-
const pendingNullifiers =
|
|
43
|
+
const pendingNullifiers = mergeAccumulatedData(
|
|
41
44
|
MAX_NEW_NULLIFIERS_PER_TX,
|
|
42
45
|
nullifiersNonRevertible,
|
|
43
46
|
nullifiersRevertible,
|
|
@@ -83,4 +86,34 @@ export class HintsBuilder {
|
|
|
83
86
|
|
|
84
87
|
return { membershipWitness, leafPreimage };
|
|
85
88
|
}
|
|
89
|
+
|
|
90
|
+
async getPublicDataReadsInfo(publicDataReads: PublicDataRead[]) {
|
|
91
|
+
const newPublicDataReadsWitnesses: Tuple<
|
|
92
|
+
MembershipWitness<typeof PUBLIC_DATA_TREE_HEIGHT>,
|
|
93
|
+
typeof MAX_PUBLIC_DATA_READS_PER_TX
|
|
94
|
+
> = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT, 0n));
|
|
95
|
+
|
|
96
|
+
const newPublicDataReadsPreimages: Tuple<PublicDataTreeLeafPreimage, typeof MAX_PUBLIC_DATA_READS_PER_TX> =
|
|
97
|
+
makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => PublicDataTreeLeafPreimage.empty());
|
|
98
|
+
|
|
99
|
+
for (const i in publicDataReads) {
|
|
100
|
+
const leafSlot = publicDataReads[i].leafSlot.value;
|
|
101
|
+
const lowLeafResult = await this.db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot);
|
|
102
|
+
if (!lowLeafResult) {
|
|
103
|
+
throw new Error(`Public data tree should have one initial leaf`);
|
|
104
|
+
}
|
|
105
|
+
const preimage = await this.db.getLeafPreimage(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
|
|
106
|
+
const path = await this.db.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
|
|
107
|
+
newPublicDataReadsWitnesses[i] = new MembershipWitness(
|
|
108
|
+
PUBLIC_DATA_TREE_HEIGHT,
|
|
109
|
+
BigInt(lowLeafResult.index),
|
|
110
|
+
path.toTuple<typeof PUBLIC_DATA_TREE_HEIGHT>(),
|
|
111
|
+
);
|
|
112
|
+
newPublicDataReadsPreimages[i] = preimage! as PublicDataTreeLeafPreimage;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
newPublicDataReadsWitnesses,
|
|
116
|
+
newPublicDataReadsPreimages,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
86
119
|
}
|
|
@@ -34,7 +34,8 @@ export class PhaseManagerFactory {
|
|
|
34
34
|
publicContractsDB: ContractsDataSourcePublicDB,
|
|
35
35
|
publicStateDB: PublicStateDB,
|
|
36
36
|
): AbstractPhaseManager | undefined {
|
|
37
|
-
|
|
37
|
+
const data = tx.data.forPublic!;
|
|
38
|
+
if (data.needsSetup) {
|
|
38
39
|
return new SetupPhaseManager(
|
|
39
40
|
db,
|
|
40
41
|
publicExecutor,
|
|
@@ -44,7 +45,7 @@ export class PhaseManagerFactory {
|
|
|
44
45
|
publicContractsDB,
|
|
45
46
|
publicStateDB,
|
|
46
47
|
);
|
|
47
|
-
} else if (
|
|
48
|
+
} else if (data.needsAppLogic) {
|
|
48
49
|
return new AppLogicPhaseManager(
|
|
49
50
|
db,
|
|
50
51
|
publicExecutor,
|
|
@@ -54,7 +55,7 @@ export class PhaseManagerFactory {
|
|
|
54
55
|
publicContractsDB,
|
|
55
56
|
publicStateDB,
|
|
56
57
|
);
|
|
57
|
-
} else if (
|
|
58
|
+
} else if (data.needsTeardown) {
|
|
58
59
|
return new TeardownPhaseManager(
|
|
59
60
|
db,
|
|
60
61
|
publicExecutor,
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type BlockProver,
|
|
2
3
|
type FailedTx,
|
|
3
4
|
type ProcessedTx,
|
|
4
5
|
type SimulationError,
|
|
5
6
|
Tx,
|
|
6
|
-
getPreviousOutputAndProof,
|
|
7
7
|
makeEmptyProcessedTx,
|
|
8
8
|
makeProcessedTx,
|
|
9
9
|
toTxEffect,
|
|
10
10
|
validateProcessedTx,
|
|
11
11
|
} from '@aztec/circuit-types';
|
|
12
12
|
import { type TxSequencerProcessingStats } from '@aztec/circuit-types/stats';
|
|
13
|
-
import { type GlobalVariables, type Header } from '@aztec/circuits.js';
|
|
13
|
+
import { type GlobalVariables, type Header, type KernelCircuitPublicInputs } from '@aztec/circuits.js';
|
|
14
|
+
import { type ProcessReturnValues } from '@aztec/foundation/abi';
|
|
14
15
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
15
16
|
import { Timer } from '@aztec/foundation/timer';
|
|
16
17
|
import { PublicExecutor, type PublicStateDB, type SimulationProvider } from '@aztec/simulator';
|
|
@@ -20,8 +21,9 @@ import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
|
20
21
|
import { type PublicKernelCircuitSimulator } from '../simulator/index.js';
|
|
21
22
|
import { ContractsDataSourcePublicDB, WorldStateDB, WorldStatePublicDB } from '../simulator/public_executor.js';
|
|
22
23
|
import { RealPublicKernelCircuitSimulator } from '../simulator/public_kernel.js';
|
|
23
|
-
import { type AbstractPhaseManager } from './abstract_phase_manager.js';
|
|
24
|
+
import { type AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
|
|
24
25
|
import { PhaseManagerFactory } from './phase_manager_factory.js';
|
|
26
|
+
import { type TxValidator } from './tx_validator.js';
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Creates new instances of PublicProcessor given the provided merkle tree db and contract data source.
|
|
@@ -84,60 +86,45 @@ export class PublicProcessor {
|
|
|
84
86
|
* @param txs - Txs to process.
|
|
85
87
|
* @returns The list of processed txs with their circuit simulation outputs.
|
|
86
88
|
*/
|
|
87
|
-
public async process(
|
|
89
|
+
public async process(
|
|
90
|
+
txs: Tx[],
|
|
91
|
+
maxTransactions = txs.length,
|
|
92
|
+
blockProver?: BlockProver,
|
|
93
|
+
txValidator?: TxValidator,
|
|
94
|
+
): Promise<[ProcessedTx[], FailedTx[], ProcessReturnValues[]]> {
|
|
88
95
|
// The processor modifies the tx objects in place, so we need to clone them.
|
|
89
96
|
txs = txs.map(tx => Tx.clone(tx));
|
|
90
97
|
const result: ProcessedTx[] = [];
|
|
91
98
|
const failed: FailedTx[] = [];
|
|
99
|
+
const returns: ProcessReturnValues[] = [];
|
|
92
100
|
|
|
93
101
|
for (const tx of txs) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.publicKernel,
|
|
99
|
-
this.globalVariables,
|
|
100
|
-
this.historicalHeader,
|
|
101
|
-
this.publicContractsDB,
|
|
102
|
-
this.publicStateDB,
|
|
103
|
-
);
|
|
104
|
-
this.log(`Beginning processing in phase ${phase?.phase} for tx ${tx.getTxHash()}`);
|
|
105
|
-
let { publicKernelPublicInput, previousProof: proof } = getPreviousOutputAndProof(tx, undefined, undefined);
|
|
106
|
-
let revertReason: SimulationError | undefined;
|
|
107
|
-
const timer = new Timer();
|
|
102
|
+
// only process up to the limit of the block
|
|
103
|
+
if (result.length >= maxTransactions) {
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
108
106
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
this.publicStateDB,
|
|
124
|
-
);
|
|
107
|
+
const [processedTx, returnValues] = !tx.hasPublicCalls()
|
|
108
|
+
? [makeProcessedTx(tx, tx.data.toKernelCircuitPublicInputs(), tx.proof)]
|
|
109
|
+
: await this.processTxWithPublicCalls(tx);
|
|
110
|
+
validateProcessedTx(processedTx);
|
|
111
|
+
// Re-validate the transaction
|
|
112
|
+
if (txValidator) {
|
|
113
|
+
// Only accept processed transactions that are not double-spends,
|
|
114
|
+
// public functions emitting nullifiers would pass earlier check but fail here.
|
|
115
|
+
// Note that we're checking all nullifiers generated in the private execution twice,
|
|
116
|
+
// we could store the ones already checked and skip them here as an optimization.
|
|
117
|
+
const [_, invalid] = await txValidator.validateTxs([processedTx]);
|
|
118
|
+
if (invalid.length) {
|
|
119
|
+
throw new Error(`Transaction ${invalid[0].hash} invalid after processing public functions`);
|
|
120
|
+
}
|
|
125
121
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
result.push(
|
|
131
|
-
|
|
132
|
-
this.log(`Processed public part of ${tx.data.endNonRevertibleData.newNullifiers[0].value}`, {
|
|
133
|
-
eventName: 'tx-sequencer-processing',
|
|
134
|
-
duration: timer.ms(),
|
|
135
|
-
effectsSize: toTxEffect(processedTransaction).toBuffer().length,
|
|
136
|
-
publicDataUpdateRequests:
|
|
137
|
-
processedTransaction.data.combinedData.publicDataUpdateRequests.filter(x => !x.leafSlot.isZero()).length ??
|
|
138
|
-
0,
|
|
139
|
-
...tx.getStats(),
|
|
140
|
-
} satisfies TxSequencerProcessingStats);
|
|
122
|
+
// if we were given a prover then send the transaction to it for proving
|
|
123
|
+
if (blockProver) {
|
|
124
|
+
await blockProver.addNewTx(processedTx);
|
|
125
|
+
}
|
|
126
|
+
result.push(processedTx);
|
|
127
|
+
returns.push(returnValues);
|
|
141
128
|
} catch (err: any) {
|
|
142
129
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
143
130
|
this.log.warn(`Failed to process tx ${tx.getTxHash()}: ${errorMessage}`);
|
|
@@ -146,10 +133,11 @@ export class PublicProcessor {
|
|
|
146
133
|
tx,
|
|
147
134
|
error: err instanceof Error ? err : new Error(errorMessage),
|
|
148
135
|
});
|
|
136
|
+
returns.push([]);
|
|
149
137
|
}
|
|
150
138
|
}
|
|
151
139
|
|
|
152
|
-
return [result, failed];
|
|
140
|
+
return [result, failed, returns];
|
|
153
141
|
}
|
|
154
142
|
|
|
155
143
|
/**
|
|
@@ -158,6 +146,64 @@ export class PublicProcessor {
|
|
|
158
146
|
*/
|
|
159
147
|
public makeEmptyProcessedTx(): ProcessedTx {
|
|
160
148
|
const { chainId, version } = this.globalVariables;
|
|
161
|
-
return makeEmptyProcessedTx(this.historicalHeader, chainId, version);
|
|
149
|
+
return makeEmptyProcessedTx(this.historicalHeader.clone(), chainId, version);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, ProcessReturnValues | undefined]> {
|
|
153
|
+
let returnValues: ProcessReturnValues = undefined;
|
|
154
|
+
let phase: AbstractPhaseManager | undefined = PhaseManagerFactory.phaseFromTx(
|
|
155
|
+
tx,
|
|
156
|
+
this.db,
|
|
157
|
+
this.publicExecutor,
|
|
158
|
+
this.publicKernel,
|
|
159
|
+
this.globalVariables,
|
|
160
|
+
this.historicalHeader,
|
|
161
|
+
this.publicContractsDB,
|
|
162
|
+
this.publicStateDB,
|
|
163
|
+
);
|
|
164
|
+
this.log(`Beginning processing in phase ${phase?.phase} for tx ${tx.getTxHash()}`);
|
|
165
|
+
let proof = tx.proof;
|
|
166
|
+
let publicKernelPublicInput = tx.data.toPublicKernelCircuitPublicInputs();
|
|
167
|
+
let finalKernelOutput: KernelCircuitPublicInputs | undefined;
|
|
168
|
+
let revertReason: SimulationError | undefined;
|
|
169
|
+
const timer = new Timer();
|
|
170
|
+
while (phase) {
|
|
171
|
+
const output = await phase.handle(tx, publicKernelPublicInput, proof);
|
|
172
|
+
if (phase.phase === PublicKernelPhase.APP_LOGIC) {
|
|
173
|
+
returnValues = output.returnValues;
|
|
174
|
+
}
|
|
175
|
+
publicKernelPublicInput = output.publicKernelOutput;
|
|
176
|
+
finalKernelOutput = output.finalKernelOutput;
|
|
177
|
+
proof = output.publicKernelProof;
|
|
178
|
+
revertReason ??= output.revertReason;
|
|
179
|
+
phase = PhaseManagerFactory.phaseFromOutput(
|
|
180
|
+
publicKernelPublicInput,
|
|
181
|
+
phase,
|
|
182
|
+
this.db,
|
|
183
|
+
this.publicExecutor,
|
|
184
|
+
this.publicKernel,
|
|
185
|
+
this.globalVariables,
|
|
186
|
+
this.historicalHeader,
|
|
187
|
+
this.publicContractsDB,
|
|
188
|
+
this.publicStateDB,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!finalKernelOutput) {
|
|
193
|
+
throw new Error('Final public kernel was not executed.');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const processedTx = makeProcessedTx(tx, finalKernelOutput, proof, revertReason);
|
|
197
|
+
|
|
198
|
+
this.log(`Processed public part of ${tx.getTxHash()}`, {
|
|
199
|
+
eventName: 'tx-sequencer-processing',
|
|
200
|
+
duration: timer.ms(),
|
|
201
|
+
effectsSize: toTxEffect(processedTx).toBuffer().length,
|
|
202
|
+
publicDataUpdateRequests:
|
|
203
|
+
processedTx.data.end.publicDataUpdateRequests.filter(x => !x.leafSlot.isZero()).length ?? 0,
|
|
204
|
+
...tx.getStats(),
|
|
205
|
+
} satisfies TxSequencerProcessingStats);
|
|
206
|
+
|
|
207
|
+
return [processedTx, returnValues];
|
|
162
208
|
}
|
|
163
209
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type L1ToL2MessageSource, type L2Block, type L2BlockSource, type ProcessedTx, Tx } from '@aztec/circuit-types';
|
|
2
2
|
import { type BlockProver, PROVING_STATUS } from '@aztec/circuit-types/interfaces';
|
|
3
3
|
import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
|
|
4
|
-
import { AztecAddress, EthAddress
|
|
4
|
+
import { AztecAddress, EthAddress } from '@aztec/circuits.js';
|
|
5
5
|
import { Fr } from '@aztec/foundation/fields';
|
|
6
6
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
7
7
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
@@ -193,57 +193,75 @@ export class Sequencer {
|
|
|
193
193
|
this.log.info(`Building block ${newBlockNumber} with ${validTxs.length} transactions`);
|
|
194
194
|
this.state = SequencerState.CREATING_BLOCK;
|
|
195
195
|
|
|
196
|
+
// Get l1 to l2 messages from the contract
|
|
197
|
+
this.log('Requesting L1 to L2 messages from contract');
|
|
198
|
+
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(newBlockNumber));
|
|
199
|
+
this.log(`Retrieved ${l1ToL2Messages.length} L1 to L2 messages for block ${newBlockNumber}`);
|
|
200
|
+
|
|
196
201
|
// We create a fresh processor each time to reset any cached state (eg storage writes)
|
|
197
202
|
const processor = await this.publicProcessorFactory.create(historicalHeader, newGlobalVariables);
|
|
198
|
-
|
|
203
|
+
|
|
204
|
+
const emptyTx = processor.makeEmptyProcessedTx();
|
|
205
|
+
|
|
206
|
+
const blockBuildingTimer = new Timer();
|
|
207
|
+
|
|
208
|
+
// We must initialise the block to be a power of 2 in size
|
|
209
|
+
const numRealTxs = validTxs.length;
|
|
210
|
+
const pow2 = Math.log2(numRealTxs);
|
|
211
|
+
const totalTxs = 2 ** Math.ceil(pow2);
|
|
212
|
+
const blockSize = Math.max(2, totalTxs);
|
|
213
|
+
const blockTicket = await this.prover.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages, emptyTx);
|
|
214
|
+
|
|
215
|
+
const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
|
|
216
|
+
processor.process(validTxs, blockSize, this.prover, txValidator),
|
|
217
|
+
);
|
|
199
218
|
if (failedTxs.length > 0) {
|
|
200
219
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
201
220
|
this.log(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`);
|
|
202
221
|
await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
|
|
203
222
|
}
|
|
204
223
|
|
|
205
|
-
|
|
206
|
-
// public functions emitting nullifiers would pass earlier check but fail here.
|
|
207
|
-
// Note that we're checking all nullifiers generated in the private execution twice,
|
|
208
|
-
// we could store the ones already checked and skip them here as an optimization.
|
|
209
|
-
const processedValidTxs = await this.takeValidTxs(processedTxs, txValidator);
|
|
210
|
-
|
|
211
|
-
if (processedValidTxs.length === 0) {
|
|
224
|
+
if (processedTxs.length === 0) {
|
|
212
225
|
this.log('No txs processed correctly to build block. Exiting');
|
|
226
|
+
this.prover.cancelBlock();
|
|
213
227
|
return;
|
|
214
228
|
}
|
|
215
229
|
|
|
216
230
|
await assertBlockHeight();
|
|
217
231
|
|
|
218
|
-
//
|
|
219
|
-
this.
|
|
220
|
-
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(newBlockNumber));
|
|
221
|
-
this.log(`Retrieved ${l1ToL2Messages.length} L1 to L2 messages for block ${newBlockNumber}`);
|
|
232
|
+
// All real transactions have been added, set the block as full and complete the proving.
|
|
233
|
+
await this.prover.setBlockCompleted();
|
|
222
234
|
|
|
223
|
-
//
|
|
224
|
-
|
|
235
|
+
// Here we are now waiting for the block to be proven.
|
|
236
|
+
// TODO(@PhilWindle) We should probably periodically check for things like another
|
|
237
|
+
// block being published before ours instead of just waiting on our block
|
|
238
|
+
const result = await blockTicket.provingPromise;
|
|
239
|
+
if (result.status === PROVING_STATUS.FAILURE) {
|
|
240
|
+
throw new Error(`Block proving failed, reason: ${result.reason}`);
|
|
241
|
+
}
|
|
225
242
|
|
|
226
243
|
await assertBlockHeight();
|
|
227
244
|
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
245
|
+
// Block is proven, now finalise and publish!
|
|
246
|
+
const blockResult = await this.prover.finaliseBlock();
|
|
247
|
+
const block = blockResult.block;
|
|
248
|
+
|
|
249
|
+
await assertBlockHeight();
|
|
232
250
|
|
|
233
251
|
this.log(`Assembled block ${block.number}`, {
|
|
234
252
|
eventName: 'l2-block-built',
|
|
235
253
|
duration: workTimer.ms(),
|
|
236
254
|
publicProcessDuration: publicProcessorDuration,
|
|
237
|
-
rollupCircuitsDuration:
|
|
255
|
+
rollupCircuitsDuration: blockBuildingTimer.ms(),
|
|
238
256
|
...block.getStats(),
|
|
239
257
|
} satisfies L2BlockBuiltStats);
|
|
240
258
|
|
|
241
|
-
await assertBlockHeight();
|
|
242
|
-
|
|
243
259
|
await this.publishL2Block(block);
|
|
244
|
-
this.log.info(`Submitted rollup block ${block.number} with ${
|
|
260
|
+
this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`);
|
|
245
261
|
} catch (err) {
|
|
246
262
|
this.log.error(`Rolling back world state DB due to error assembling block`, (err as any).stack);
|
|
263
|
+
// Cancel any further proving on the block
|
|
264
|
+
this.prover?.cancelBlock();
|
|
247
265
|
await this.worldState.getLatest().rollback();
|
|
248
266
|
}
|
|
249
267
|
}
|
|
@@ -289,33 +307,6 @@ export class Sequencer {
|
|
|
289
307
|
return min >= this.lastPublishedBlock;
|
|
290
308
|
}
|
|
291
309
|
|
|
292
|
-
/**
|
|
293
|
-
* Pads the set of txs to a power of two and assembles a block by calling the block builder.
|
|
294
|
-
* @param txs - Processed txs to include in the next block.
|
|
295
|
-
* @param l1ToL2Messages - L1 to L2 messages to be part of the block.
|
|
296
|
-
* @param emptyTx - Empty tx to repeat at the end of the block to pad to a power of two.
|
|
297
|
-
* @param globalVariables - Global variables to use in the block.
|
|
298
|
-
* @returns The new block.
|
|
299
|
-
*/
|
|
300
|
-
protected async buildBlock(
|
|
301
|
-
txs: ProcessedTx[],
|
|
302
|
-
l1ToL2Messages: Fr[],
|
|
303
|
-
emptyTx: ProcessedTx,
|
|
304
|
-
globalVariables: GlobalVariables,
|
|
305
|
-
) {
|
|
306
|
-
const blockTicket = await this.prover.startNewBlock(txs.length, globalVariables, l1ToL2Messages, emptyTx);
|
|
307
|
-
|
|
308
|
-
for (const tx of txs) {
|
|
309
|
-
await this.prover.addNewTx(tx);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const result = await blockTicket.provingPromise;
|
|
313
|
-
if (result.status === PROVING_STATUS.FAILURE) {
|
|
314
|
-
throw new Error(`Block proving failed, reason: ${result.reason}`);
|
|
315
|
-
}
|
|
316
|
-
return result.block;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
310
|
get coinbase(): EthAddress {
|
|
320
311
|
return this._coinbase;
|
|
321
312
|
}
|
|
@@ -45,6 +45,6 @@ export class SetupPhaseManager extends AbstractPhaseManager {
|
|
|
45
45
|
);
|
|
46
46
|
tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
|
|
47
47
|
await this.publicStateDB.checkpoint();
|
|
48
|
-
return { publicKernelOutput, publicKernelProof, revertReason };
|
|
48
|
+
return { publicKernelOutput, publicKernelProof, revertReason, returnValues: undefined };
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { type Tx } from '@aztec/circuit-types';
|
|
2
2
|
import {
|
|
3
|
+
type Fr,
|
|
3
4
|
type GlobalVariables,
|
|
4
5
|
type Header,
|
|
6
|
+
type KernelCircuitPublicInputs,
|
|
7
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
5
8
|
type Proof,
|
|
6
9
|
type PublicKernelCircuitPublicInputs,
|
|
10
|
+
PublicKernelTailCircuitPrivateInputs,
|
|
11
|
+
type SideEffect,
|
|
12
|
+
makeEmptyProof,
|
|
13
|
+
mergeAccumulatedData,
|
|
14
|
+
sortByCounter,
|
|
7
15
|
} from '@aztec/circuits.js';
|
|
16
|
+
import { type Tuple } from '@aztec/foundation/serialize';
|
|
8
17
|
import { type PublicExecutor, type PublicStateDB } from '@aztec/simulator';
|
|
9
18
|
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
10
19
|
|
|
@@ -28,7 +37,7 @@ export class TailPhaseManager extends AbstractPhaseManager {
|
|
|
28
37
|
|
|
29
38
|
async handle(tx: Tx, previousPublicKernelOutput: PublicKernelCircuitPublicInputs, previousPublicKernelProof: Proof) {
|
|
30
39
|
this.log(`Processing tx ${tx.getTxHash()}`);
|
|
31
|
-
const [
|
|
40
|
+
const [finalKernelOutput, publicKernelProof] = await this.runTailKernelCircuit(
|
|
32
41
|
previousPublicKernelOutput,
|
|
33
42
|
previousPublicKernelProof,
|
|
34
43
|
).catch(
|
|
@@ -42,6 +51,61 @@ export class TailPhaseManager extends AbstractPhaseManager {
|
|
|
42
51
|
// commit the state updates from this transaction
|
|
43
52
|
await this.publicStateDB.commit();
|
|
44
53
|
|
|
45
|
-
return {
|
|
54
|
+
return {
|
|
55
|
+
publicKernelOutput: previousPublicKernelOutput,
|
|
56
|
+
finalKernelOutput,
|
|
57
|
+
publicKernelProof,
|
|
58
|
+
revertReason: undefined,
|
|
59
|
+
returnValues: undefined,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async runTailKernelCircuit(
|
|
64
|
+
previousOutput: PublicKernelCircuitPublicInputs,
|
|
65
|
+
previousProof: Proof,
|
|
66
|
+
): Promise<[KernelCircuitPublicInputs, Proof]> {
|
|
67
|
+
const output = await this.simulate(previousOutput, previousProof);
|
|
68
|
+
|
|
69
|
+
// Temporary hack. Should sort them in the tail circuit.
|
|
70
|
+
const noteHashes = mergeAccumulatedData(
|
|
71
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
72
|
+
previousOutput.endNonRevertibleData.newNoteHashes,
|
|
73
|
+
previousOutput.end.newNoteHashes,
|
|
74
|
+
);
|
|
75
|
+
output.end.newNoteHashes = this.sortNoteHashes<typeof MAX_NEW_NOTE_HASHES_PER_TX>(noteHashes);
|
|
76
|
+
|
|
77
|
+
return [output, makeEmptyProof()];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private async simulate(
|
|
81
|
+
previousOutput: PublicKernelCircuitPublicInputs,
|
|
82
|
+
previousProof: Proof,
|
|
83
|
+
): Promise<KernelCircuitPublicInputs> {
|
|
84
|
+
const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
|
|
85
|
+
|
|
86
|
+
const { validationRequests, endNonRevertibleData, end } = previousOutput;
|
|
87
|
+
const nullifierReadRequestHints = await this.hintsBuilder.getNullifierReadRequestHints(
|
|
88
|
+
validationRequests.nullifierReadRequests,
|
|
89
|
+
endNonRevertibleData.newNullifiers,
|
|
90
|
+
end.newNullifiers,
|
|
91
|
+
);
|
|
92
|
+
const nullifierNonExistentReadRequestHints = await this.hintsBuilder.getNullifierNonExistentReadRequestHints(
|
|
93
|
+
validationRequests.nullifierNonExistentReadRequests,
|
|
94
|
+
endNonRevertibleData.newNullifiers,
|
|
95
|
+
end.newNullifiers,
|
|
96
|
+
);
|
|
97
|
+
const inputs = new PublicKernelTailCircuitPrivateInputs(
|
|
98
|
+
previousKernel,
|
|
99
|
+
nullifierReadRequestHints,
|
|
100
|
+
nullifierNonExistentReadRequestHints,
|
|
101
|
+
);
|
|
102
|
+
return this.publicKernel.publicKernelCircuitTail(inputs);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private sortNoteHashes<N extends number>(noteHashes: Tuple<SideEffect, N>): Tuple<Fr, N> {
|
|
106
|
+
return sortByCounter(noteHashes.map(n => ({ ...n, counter: n.counter.toNumber() }))).map(n => n.value) as Tuple<
|
|
107
|
+
Fr,
|
|
108
|
+
N
|
|
109
|
+
>;
|
|
46
110
|
}
|
|
47
111
|
}
|
|
@@ -45,6 +45,6 @@ export class TeardownPhaseManager extends AbstractPhaseManager {
|
|
|
45
45
|
);
|
|
46
46
|
tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
|
|
47
47
|
await this.publicStateDB.checkpoint();
|
|
48
|
-
return { publicKernelOutput, publicKernelProof, revertReason };
|
|
48
|
+
return { publicKernelOutput, publicKernelProof, revertReason, returnValues: undefined };
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -138,9 +138,7 @@ export class TxValidator {
|
|
|
138
138
|
* @returns Whether this is a problematic double spend that the L1 contract would reject.
|
|
139
139
|
*/
|
|
140
140
|
async #validateNullifiers(tx: Tx | ProcessedTx, thisBlockNullifiers: Set<bigint>): Promise<TxValidationStatus> {
|
|
141
|
-
const newNullifiers =
|
|
142
|
-
.filter(x => !x.isEmpty())
|
|
143
|
-
.map(x => x.value.toBigInt());
|
|
141
|
+
const newNullifiers = tx.data.getNonEmptyNullifiers().map(x => x.toBigInt());
|
|
144
142
|
|
|
145
143
|
// Ditch this tx if it has repeated nullifiers
|
|
146
144
|
const uniqueNullifiers = new Set(newNullifiers);
|
|
@@ -172,7 +170,7 @@ export class TxValidator {
|
|
|
172
170
|
}
|
|
173
171
|
|
|
174
172
|
async #validateGasBalance(tx: Tx): Promise<TxValidationStatus> {
|
|
175
|
-
if (!tx.data.needsTeardown) {
|
|
173
|
+
if (!tx.data.forPublic || !tx.data.forPublic.needsTeardown) {
|
|
176
174
|
return VALID_TX;
|
|
177
175
|
}
|
|
178
176
|
|
|
@@ -200,7 +198,11 @@ export class TxValidator {
|
|
|
200
198
|
}
|
|
201
199
|
|
|
202
200
|
#validateMaxBlockNumber(tx: Tx | ProcessedTx): TxValidationStatus {
|
|
203
|
-
const
|
|
201
|
+
const target =
|
|
202
|
+
tx instanceof Tx
|
|
203
|
+
? tx.data.forRollup?.rollupValidationRequests || tx.data.forPublic!.validationRequests.forRollup
|
|
204
|
+
: tx.data.rollupValidationRequests;
|
|
205
|
+
const maxBlockNumber = target.maxBlockNumber;
|
|
204
206
|
|
|
205
207
|
if (maxBlockNumber.isSome && maxBlockNumber.value < this.#globalVariables.blockNumber) {
|
|
206
208
|
this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for low max block number`);
|
|
@@ -211,7 +213,7 @@ export class TxValidator {
|
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
async #validateFee(tx: Tx): Promise<TxValidationStatus> {
|
|
214
|
-
if (!tx.data.needsTeardown) {
|
|
216
|
+
if (!tx.data.forPublic || !tx.data.forPublic.needsTeardown) {
|
|
215
217
|
// TODO check if fees are mandatory and reject this tx
|
|
216
218
|
this.#log.debug(`Tx ${Tx.getHash(tx)} doesn't pay for gas`);
|
|
217
219
|
return VALID_TX;
|
package/src/sequencer/utils.ts
CHANGED
|
@@ -7,13 +7,14 @@ import { CallRequest } from '@aztec/circuits.js';
|
|
|
7
7
|
* @returns The highest side effect counter in the transaction so far
|
|
8
8
|
*/
|
|
9
9
|
export function lastSideEffectCounter(tx: Tx): number {
|
|
10
|
+
const data = tx.data.forPublic!;
|
|
10
11
|
const sideEffectCounters = [
|
|
11
|
-
...
|
|
12
|
-
...
|
|
13
|
-
...
|
|
14
|
-
...
|
|
15
|
-
...
|
|
16
|
-
...
|
|
12
|
+
...data.endNonRevertibleData.newNoteHashes,
|
|
13
|
+
...data.endNonRevertibleData.newNullifiers,
|
|
14
|
+
...data.endNonRevertibleData.publicCallStack,
|
|
15
|
+
...data.end.newNoteHashes,
|
|
16
|
+
...data.end.newNullifiers,
|
|
17
|
+
...data.end.publicCallStack,
|
|
17
18
|
];
|
|
18
19
|
|
|
19
20
|
let max = 0;
|
package/src/simulator/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type KernelCircuitPublicInputs,
|
|
2
3
|
type PublicKernelCircuitPrivateInputs,
|
|
3
4
|
type PublicKernelCircuitPublicInputs,
|
|
4
5
|
type PublicKernelTailCircuitPrivateInputs,
|
|
@@ -31,5 +32,5 @@ export interface PublicKernelCircuitSimulator {
|
|
|
31
32
|
* @param inputs - Inputs to the circuit.
|
|
32
33
|
* @returns The public inputs as outputs of the simulation.
|
|
33
34
|
*/
|
|
34
|
-
publicKernelCircuitTail(inputs: PublicKernelTailCircuitPrivateInputs): Promise<
|
|
35
|
+
publicKernelCircuitTail(inputs: PublicKernelTailCircuitPrivateInputs): Promise<KernelCircuitPublicInputs>;
|
|
35
36
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type CircuitSimulationStats } from '@aztec/circuit-types/stats';
|
|
2
2
|
import {
|
|
3
|
+
type KernelCircuitPublicInputs,
|
|
3
4
|
type PublicKernelCircuitPrivateInputs,
|
|
4
5
|
type PublicKernelCircuitPublicInputs,
|
|
5
6
|
type PublicKernelTailCircuitPrivateInputs,
|
|
@@ -120,7 +121,7 @@ export class RealPublicKernelCircuitSimulator implements PublicKernelCircuitSimu
|
|
|
120
121
|
*/
|
|
121
122
|
public async publicKernelCircuitTail(
|
|
122
123
|
input: PublicKernelTailCircuitPrivateInputs,
|
|
123
|
-
): Promise<
|
|
124
|
+
): Promise<KernelCircuitPublicInputs> {
|
|
124
125
|
const inputWitness = convertPublicTailInputsToWitnessMap(input);
|
|
125
126
|
const [duration, witness] = await elapsed(() =>
|
|
126
127
|
this.wasmSimulator.simulateCircuit(inputWitness, PublicKernelTailArtifact),
|