@aztec/sequencer-client 0.47.0 → 0.48.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/client/sequencer-client.d.ts +2 -2
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +2 -2
- package/dest/config.d.ts +6 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +96 -29
- package/dest/global_variable_builder/global_builder.d.ts +14 -8
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +10 -16
- package/dest/global_variable_builder/index.d.ts +2 -3
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/global_variable_builder/index.js +1 -1
- package/dest/global_variable_builder/viem-reader.d.ts +5 -4
- package/dest/global_variable_builder/viem-reader.d.ts.map +1 -1
- package/dest/global_variable_builder/viem-reader.js +11 -8
- package/dest/index.d.ts +2 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +3 -1
- package/dest/publisher/config.d.ts +7 -15
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +38 -11
- package/dest/publisher/index.d.ts +3 -2
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/index.js +4 -4
- package/dest/publisher/l1-publisher-metrics.d.ts +17 -0
- package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -0
- package/dest/publisher/l1-publisher-metrics.js +75 -0
- package/dest/publisher/l1-publisher.d.ts +34 -6
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +45 -37
- package/dest/publisher/viem-tx-sender.d.ts +9 -2
- package/dest/publisher/viem-tx-sender.d.ts.map +1 -1
- package/dest/publisher/viem-tx-sender.js +86 -17
- package/dest/receiver.d.ts +2 -8
- package/dest/receiver.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +17 -0
- package/dest/sequencer/metrics.d.ts.map +1 -0
- package/dest/sequencer/metrics.js +56 -0
- package/dest/sequencer/sequencer.d.ts +11 -5
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +104 -34
- package/dest/tx_validator/data_validator.d.ts +6 -0
- package/dest/tx_validator/data_validator.d.ts.map +1 -0
- package/dest/tx_validator/data_validator.js +47 -0
- package/dest/tx_validator/gas_validator.d.ts +1 -1
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +12 -12
- package/dest/tx_validator/phases_validator.d.ts +2 -3
- package/dest/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/tx_validator/phases_validator.js +4 -4
- package/dest/tx_validator/test_utils.d.ts.map +1 -1
- package/dest/tx_validator/test_utils.js +10 -4
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +4 -3
- package/package.json +15 -15
- package/src/client/sequencer-client.ts +3 -3
- package/src/config.ts +111 -50
- package/src/global_variable_builder/global_builder.ts +35 -25
- package/src/global_variable_builder/index.ts +3 -3
- package/src/global_variable_builder/viem-reader.ts +14 -11
- package/src/index.ts +2 -0
- package/src/publisher/config.ts +43 -31
- package/src/publisher/index.ts +5 -3
- package/src/publisher/l1-publisher-metrics.ts +108 -0
- package/src/publisher/l1-publisher.ts +79 -44
- package/src/publisher/viem-tx-sender.ts +90 -15
- package/src/receiver.ts +3 -8
- package/src/sequencer/metrics.ts +86 -0
- package/src/sequencer/sequencer.ts +144 -45
- package/src/tx_validator/data_validator.ts +61 -0
- package/src/tx_validator/gas_validator.ts +11 -9
- package/src/tx_validator/phases_validator.ts +13 -5
- package/src/tx_validator/test_utils.ts +10 -3
- package/src/tx_validator/tx_validator_factory.ts +4 -2
- package/dest/global_variable_builder/config.d.ts +0 -19
- package/dest/global_variable_builder/config.d.ts.map +0 -1
- package/dest/global_variable_builder/config.js +0 -2
- package/src/global_variable_builder/config.ts +0 -20
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
type AllowedElement,
|
|
11
11
|
BlockProofError,
|
|
12
|
-
type BlockProver,
|
|
13
12
|
PROVING_STATUS,
|
|
13
|
+
type ProverClient,
|
|
14
14
|
} from '@aztec/circuit-types/interfaces';
|
|
15
15
|
import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
|
|
16
|
-
import { AztecAddress, EthAddress, type GlobalVariables, type Header } from '@aztec/circuits.js';
|
|
16
|
+
import { AztecAddress, EthAddress, type GlobalVariables, type Header, IS_DEV_NET } from '@aztec/circuits.js';
|
|
17
17
|
import { Fr } from '@aztec/foundation/fields';
|
|
18
18
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
19
19
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
@@ -24,9 +24,10 @@ import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec
|
|
|
24
24
|
import { type WorldStateStatus, type WorldStateSynchronizer } from '@aztec/world-state';
|
|
25
25
|
|
|
26
26
|
import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
27
|
-
import { type L1Publisher } from '../publisher/l1-publisher.js';
|
|
27
|
+
import { type Attestation, type L1Publisher } from '../publisher/l1-publisher.js';
|
|
28
28
|
import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
|
|
29
29
|
import { type SequencerConfig } from './config.js';
|
|
30
|
+
import { SequencerMetrics } from './metrics.js';
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Sequencer client
|
|
@@ -42,6 +43,8 @@ export class Sequencer {
|
|
|
42
43
|
private pollingIntervalMs: number = 1000;
|
|
43
44
|
private maxTxsPerBlock = 32;
|
|
44
45
|
private minTxsPerBLock = 1;
|
|
46
|
+
private minSecondsBetweenBlocks = 0;
|
|
47
|
+
private maxSecondsBetweenBlocks = 0;
|
|
45
48
|
// TODO: zero values should not be allowed for the following 2 values in PROD
|
|
46
49
|
private _coinbase = EthAddress.ZERO;
|
|
47
50
|
private _feeRecipient = AztecAddress.ZERO;
|
|
@@ -51,15 +54,14 @@ export class Sequencer {
|
|
|
51
54
|
private allowedInTeardown: AllowedElement[] = [];
|
|
52
55
|
private maxBlockSizeInBytes: number = 1024 * 1024;
|
|
53
56
|
private skipSubmitProofs: boolean = false;
|
|
54
|
-
|
|
55
|
-
public readonly tracer: Tracer;
|
|
57
|
+
private metrics: SequencerMetrics;
|
|
56
58
|
|
|
57
59
|
constructor(
|
|
58
60
|
private publisher: L1Publisher,
|
|
59
61
|
private globalsBuilder: GlobalVariableBuilder,
|
|
60
62
|
private p2pClient: P2P,
|
|
61
63
|
private worldState: WorldStateSynchronizer,
|
|
62
|
-
private prover:
|
|
64
|
+
private prover: ProverClient,
|
|
63
65
|
private l2BlockSource: L2BlockSource,
|
|
64
66
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
65
67
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
@@ -69,24 +71,34 @@ export class Sequencer {
|
|
|
69
71
|
private log = createDebugLogger('aztec:sequencer'),
|
|
70
72
|
) {
|
|
71
73
|
this.updateConfig(config);
|
|
72
|
-
this.
|
|
74
|
+
this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer');
|
|
73
75
|
this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`);
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
get tracer(): Tracer {
|
|
79
|
+
return this.metrics.tracer;
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
/**
|
|
77
83
|
* Updates sequencer config.
|
|
78
84
|
* @param config - New parameters.
|
|
79
85
|
*/
|
|
80
86
|
public updateConfig(config: SequencerConfig) {
|
|
81
|
-
if (config.transactionPollingIntervalMS) {
|
|
87
|
+
if (config.transactionPollingIntervalMS !== undefined) {
|
|
82
88
|
this.pollingIntervalMs = config.transactionPollingIntervalMS;
|
|
83
89
|
}
|
|
84
|
-
if (config.maxTxsPerBlock) {
|
|
90
|
+
if (config.maxTxsPerBlock !== undefined) {
|
|
85
91
|
this.maxTxsPerBlock = config.maxTxsPerBlock;
|
|
86
92
|
}
|
|
87
|
-
if (config.minTxsPerBlock) {
|
|
93
|
+
if (config.minTxsPerBlock !== undefined) {
|
|
88
94
|
this.minTxsPerBLock = config.minTxsPerBlock;
|
|
89
95
|
}
|
|
96
|
+
if (config.minSecondsBetweenBlocks !== undefined) {
|
|
97
|
+
this.minSecondsBetweenBlocks = config.minSecondsBetweenBlocks;
|
|
98
|
+
}
|
|
99
|
+
if (config.maxSecondsBetweenBlocks !== undefined) {
|
|
100
|
+
this.maxSecondsBetweenBlocks = config.maxSecondsBetweenBlocks;
|
|
101
|
+
}
|
|
90
102
|
if (config.coinbase) {
|
|
91
103
|
this._coinbase = config.coinbase;
|
|
92
104
|
}
|
|
@@ -96,7 +108,7 @@ export class Sequencer {
|
|
|
96
108
|
if (config.allowedInSetup) {
|
|
97
109
|
this.allowedInSetup = config.allowedInSetup;
|
|
98
110
|
}
|
|
99
|
-
if (config.maxBlockSizeInBytes) {
|
|
111
|
+
if (config.maxBlockSizeInBytes !== undefined) {
|
|
100
112
|
this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
|
|
101
113
|
}
|
|
102
114
|
// TODO(#5917) remove this. it is no longer needed since we don't need to whitelist functions in teardown
|
|
@@ -162,16 +174,17 @@ export class Sequencer {
|
|
|
162
174
|
try {
|
|
163
175
|
// Update state when the previous block has been synced
|
|
164
176
|
const prevBlockSynced = await this.isBlockSynced();
|
|
165
|
-
if (prevBlockSynced && this.state === SequencerState.PUBLISHING_BLOCK) {
|
|
166
|
-
this.log.debug(`Block has been synced`);
|
|
167
|
-
this.state = SequencerState.IDLE;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
177
|
// Do not go forward with new block if the previous one has not been mined and processed
|
|
171
178
|
if (!prevBlockSynced) {
|
|
179
|
+
this.log.debug('Previous block has not been mined and processed yet');
|
|
172
180
|
return;
|
|
173
181
|
}
|
|
174
182
|
|
|
183
|
+
if (prevBlockSynced && this.state === SequencerState.PUBLISHING_BLOCK) {
|
|
184
|
+
this.log.debug(`Block has been synced`);
|
|
185
|
+
this.state = SequencerState.IDLE;
|
|
186
|
+
}
|
|
187
|
+
|
|
175
188
|
const historicalHeader = (await this.l2BlockSource.getBlock(-1))?.header;
|
|
176
189
|
const newBlockNumber =
|
|
177
190
|
(historicalHeader === undefined
|
|
@@ -179,21 +192,42 @@ export class Sequencer {
|
|
|
179
192
|
: Number(historicalHeader.globalVariables.blockNumber.toBigInt())) + 1;
|
|
180
193
|
|
|
181
194
|
// Do not go forward with new block if not my turn
|
|
182
|
-
if (!(await this.publisher.isItMyTurnToSubmit(
|
|
195
|
+
if (!(await this.publisher.isItMyTurnToSubmit())) {
|
|
183
196
|
this.log.debug('Not my turn to submit block');
|
|
184
197
|
return;
|
|
185
198
|
}
|
|
186
199
|
|
|
187
|
-
|
|
200
|
+
// Compute time elapsed since the previous block
|
|
201
|
+
const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
|
|
202
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
203
|
+
const elapsedSinceLastBlock = currentTime - lastBlockTime;
|
|
188
204
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
if (pendingTxs.length < this.minTxsPerBLock) {
|
|
205
|
+
// Do not go forward with new block if not enough time has passed since last block
|
|
206
|
+
if (this.minSecondsBetweenBlocks > 0 && elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
|
|
192
207
|
this.log.debug(
|
|
193
|
-
`Not creating block because
|
|
208
|
+
`Not creating block because not enough time has passed since last block (last block at ${lastBlockTime} current time ${currentTime})`,
|
|
194
209
|
);
|
|
195
210
|
return;
|
|
196
211
|
}
|
|
212
|
+
|
|
213
|
+
this.state = SequencerState.WAITING_FOR_TXS;
|
|
214
|
+
|
|
215
|
+
// Get txs to build the new block.
|
|
216
|
+
const pendingTxs = this.p2pClient.getTxs('pending');
|
|
217
|
+
|
|
218
|
+
// If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
|
|
219
|
+
if (pendingTxs.length < this.minTxsPerBLock) {
|
|
220
|
+
if (this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
|
|
221
|
+
this.log.debug(
|
|
222
|
+
`Creating block with only ${pendingTxs.length} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
|
|
223
|
+
);
|
|
224
|
+
} else {
|
|
225
|
+
this.log.debug(
|
|
226
|
+
`Not creating block because not enough txs in the pool (got ${pendingTxs.length} min ${this.minTxsPerBLock})`,
|
|
227
|
+
);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
197
231
|
this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`);
|
|
198
232
|
|
|
199
233
|
const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(
|
|
@@ -202,6 +236,8 @@ export class Sequencer {
|
|
|
202
236
|
this._feeRecipient,
|
|
203
237
|
);
|
|
204
238
|
|
|
239
|
+
// @todo @LHerskind Include some logic to consider slots
|
|
240
|
+
|
|
205
241
|
// TODO: It should be responsibility of the P2P layer to validate txs before passing them on here
|
|
206
242
|
const allValidTxs = await this.takeValidTxs(
|
|
207
243
|
pendingTxs,
|
|
@@ -215,11 +251,15 @@ export class Sequencer {
|
|
|
215
251
|
// may break if we start emitting lots of log data from public-land.
|
|
216
252
|
const validTxs = this.takeTxsWithinMaxSize(allValidTxs);
|
|
217
253
|
|
|
218
|
-
if
|
|
254
|
+
// Bail if we don't have enough valid txs
|
|
255
|
+
if (!this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock) && validTxs.length < this.minTxsPerBLock) {
|
|
256
|
+
this.log.debug(
|
|
257
|
+
`Not creating block because not enough valid txs loaded from the pool (got ${validTxs.length} min ${this.minTxsPerBLock})`,
|
|
258
|
+
);
|
|
219
259
|
return;
|
|
220
260
|
}
|
|
221
261
|
|
|
222
|
-
await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader);
|
|
262
|
+
await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader, elapsedSinceLastBlock);
|
|
223
263
|
} catch (err) {
|
|
224
264
|
if (BlockProofError.isBlockProofError(err)) {
|
|
225
265
|
const txHashes = err.txHashes.filter(h => !h.isZero());
|
|
@@ -227,12 +267,15 @@ export class Sequencer {
|
|
|
227
267
|
await this.p2pClient.deleteTxs(txHashes);
|
|
228
268
|
}
|
|
229
269
|
this.log.error(`Rolling back world state DB due to error assembling block`, (err as any).stack);
|
|
230
|
-
// Cancel any further proving on the block
|
|
231
|
-
this.prover?.cancelBlock();
|
|
232
270
|
await this.worldState.getLatest().rollback();
|
|
233
271
|
}
|
|
234
272
|
}
|
|
235
273
|
|
|
274
|
+
/** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
|
|
275
|
+
private skipMinTxsPerBlockCheck(elapsed: number): boolean {
|
|
276
|
+
return this.maxSecondsBetweenBlocks > 0 && elapsed >= this.maxSecondsBetweenBlocks;
|
|
277
|
+
}
|
|
278
|
+
|
|
236
279
|
@trackSpan('Sequencer.buildBlockAndPublish', (_validTxs, newGlobalVariables, _historicalHeader) => ({
|
|
237
280
|
[Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(),
|
|
238
281
|
}))
|
|
@@ -240,7 +283,9 @@ export class Sequencer {
|
|
|
240
283
|
validTxs: Tx[],
|
|
241
284
|
newGlobalVariables: GlobalVariables,
|
|
242
285
|
historicalHeader: Header | undefined,
|
|
286
|
+
elapsedSinceLastBlock: number,
|
|
243
287
|
): Promise<void> {
|
|
288
|
+
this.metrics.recordNewBlock(newGlobalVariables.blockNumber.toNumber(), validTxs.length);
|
|
244
289
|
const workTimer = new Timer();
|
|
245
290
|
this.state = SequencerState.CREATING_BLOCK;
|
|
246
291
|
this.log.info(`Building block ${newGlobalVariables.blockNumber.toNumber()} with ${validTxs.length} transactions`);
|
|
@@ -248,11 +293,15 @@ export class Sequencer {
|
|
|
248
293
|
const assertBlockHeight = async () => {
|
|
249
294
|
const currentBlockNumber = await this.l2BlockSource.getBlockNumber();
|
|
250
295
|
if (currentBlockNumber + 1 !== newGlobalVariables.blockNumber.toNumber()) {
|
|
296
|
+
this.metrics.recordCancelledBlock();
|
|
251
297
|
throw new Error('New block was emitted while building block');
|
|
252
298
|
}
|
|
253
|
-
|
|
299
|
+
|
|
300
|
+
if (!(await this.publisher.isItMyTurnToSubmit())) {
|
|
254
301
|
throw new Error(`Not this sequencer turn to submit block`);
|
|
255
302
|
}
|
|
303
|
+
|
|
304
|
+
// @todo @LHerskind Should take into account, block number, proposer and slot number
|
|
256
305
|
};
|
|
257
306
|
|
|
258
307
|
// Get l1 to l2 messages from the contract
|
|
@@ -269,10 +318,11 @@ export class Sequencer {
|
|
|
269
318
|
const blockSize = Math.max(2, numRealTxs);
|
|
270
319
|
|
|
271
320
|
const blockBuildingTimer = new Timer();
|
|
272
|
-
const
|
|
321
|
+
const prover = this.prover.createBlockProver(this.worldState.getLatest());
|
|
322
|
+
const blockTicket = await prover.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
|
|
273
323
|
|
|
274
324
|
const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
|
|
275
|
-
processor.process(validTxs, blockSize,
|
|
325
|
+
processor.process(validTxs, blockSize, prover, this.txValidatorFactory.validatorForProcessedTxs()),
|
|
276
326
|
);
|
|
277
327
|
if (failedTxs.length > 0) {
|
|
278
328
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
@@ -280,16 +330,20 @@ export class Sequencer {
|
|
|
280
330
|
await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
|
|
281
331
|
}
|
|
282
332
|
|
|
283
|
-
|
|
333
|
+
// TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
|
|
334
|
+
// less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
|
|
335
|
+
// go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
|
|
336
|
+
// we should bail.
|
|
337
|
+
if (processedTxs.length === 0 && !this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
|
|
284
338
|
this.log.verbose('No txs processed correctly to build block. Exiting');
|
|
285
|
-
|
|
339
|
+
prover.cancelBlock();
|
|
286
340
|
return;
|
|
287
341
|
}
|
|
288
342
|
|
|
289
343
|
await assertBlockHeight();
|
|
290
344
|
|
|
291
345
|
// All real transactions have been added, set the block as full and complete the proving.
|
|
292
|
-
await
|
|
346
|
+
await prover.setBlockCompleted();
|
|
293
347
|
|
|
294
348
|
// Here we are now waiting for the block to be proven.
|
|
295
349
|
// TODO(@PhilWindle) We should probably periodically check for things like another
|
|
@@ -302,30 +356,75 @@ export class Sequencer {
|
|
|
302
356
|
await assertBlockHeight();
|
|
303
357
|
|
|
304
358
|
// Block is proven, now finalise and publish!
|
|
305
|
-
const { block, aggregationObject, proof } = await
|
|
359
|
+
const { block, aggregationObject, proof } = await prover.finaliseBlock();
|
|
306
360
|
|
|
307
361
|
await assertBlockHeight();
|
|
308
362
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
363
|
+
const workDuration = workTimer.ms();
|
|
364
|
+
this.log.verbose(
|
|
365
|
+
`Assembled block ${block.number} (txEffectsHash: ${block.header.contentCommitment.txsEffectsHash.toString(
|
|
366
|
+
'hex',
|
|
367
|
+
)})`,
|
|
368
|
+
{
|
|
369
|
+
eventName: 'l2-block-built',
|
|
370
|
+
duration: workDuration,
|
|
371
|
+
publicProcessDuration: publicProcessorDuration,
|
|
372
|
+
rollupCircuitsDuration: blockBuildingTimer.ms(),
|
|
373
|
+
...block.getStats(),
|
|
374
|
+
} satisfies L2BlockBuiltStats,
|
|
375
|
+
);
|
|
316
376
|
|
|
317
|
-
|
|
318
|
-
|
|
377
|
+
try {
|
|
378
|
+
const attestations = await this.collectAttestations(block);
|
|
379
|
+
await this.publishL2Block(block, attestations);
|
|
380
|
+
this.metrics.recordPublishedBlock(workDuration);
|
|
381
|
+
this.log.info(
|
|
382
|
+
`Submitted rollup block ${block.number} with ${
|
|
383
|
+
processedTxs.length
|
|
384
|
+
} transactions duration=${workDuration}ms (Submitter: ${await this.publisher.senderAddress()})`,
|
|
385
|
+
);
|
|
386
|
+
} catch (err) {
|
|
387
|
+
this.metrics.recordFailedBlock();
|
|
388
|
+
throw err;
|
|
389
|
+
}
|
|
319
390
|
|
|
320
391
|
// Submit the proof if we have configured this sequencer to run with an actual prover.
|
|
321
392
|
// This is temporary while we submit one proof per block, but will have to change once we
|
|
322
393
|
// move onto proving batches of multiple blocks at a time.
|
|
323
394
|
if (aggregationObject && proof && !this.skipSubmitProofs) {
|
|
324
|
-
await this.publisher.submitProof(
|
|
395
|
+
await this.publisher.submitProof(
|
|
396
|
+
block.header,
|
|
397
|
+
block.archive.root,
|
|
398
|
+
prover.getProverId(),
|
|
399
|
+
aggregationObject,
|
|
400
|
+
proof,
|
|
401
|
+
);
|
|
325
402
|
this.log.info(`Submitted proof for block ${block.number}`);
|
|
326
403
|
}
|
|
327
404
|
}
|
|
328
405
|
|
|
406
|
+
protected async collectAttestations(block: L2Block): Promise<Attestation[] | undefined> {
|
|
407
|
+
// @todo This should collect attestations properly and fix the ordering of them to make sense
|
|
408
|
+
// the current implementation is a PLACEHOLDER and should be nuked from orbit.
|
|
409
|
+
// It is assuming that there will only be ONE (1) validator, so only one attestation
|
|
410
|
+
// is needed.
|
|
411
|
+
// @note This is quite a sin, but I'm committing war crimes in this code already.
|
|
412
|
+
// _ ._ _ , _ ._
|
|
413
|
+
// (_ ' ( ` )_ .__)
|
|
414
|
+
// ( ( ( ) `) ) _)
|
|
415
|
+
// (__ (_ (_ . _) _) ,__)
|
|
416
|
+
// `~~`\ ' . /`~~`
|
|
417
|
+
// ; ;
|
|
418
|
+
// / \
|
|
419
|
+
// _____________/_ __ \_____________
|
|
420
|
+
if (IS_DEV_NET) {
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const myAttestation = await this.publisher.attest(block.archive.root.toString());
|
|
425
|
+
return [myAttestation];
|
|
426
|
+
}
|
|
427
|
+
|
|
329
428
|
/**
|
|
330
429
|
* Publishes the L2Block to the rollup contract.
|
|
331
430
|
* @param block - The L2Block to be published.
|
|
@@ -333,10 +432,10 @@ export class Sequencer {
|
|
|
333
432
|
@trackSpan('Sequencer.publishL2Block', block => ({
|
|
334
433
|
[Attributes.BLOCK_NUMBER]: block.number,
|
|
335
434
|
}))
|
|
336
|
-
protected async publishL2Block(block: L2Block) {
|
|
435
|
+
protected async publishL2Block(block: L2Block, attestations?: Attestation[]) {
|
|
337
436
|
// Publishes new block to the network and awaits the tx to be mined
|
|
338
437
|
this.state = SequencerState.PUBLISHING_BLOCK;
|
|
339
|
-
const publishedL2Block = await this.publisher.processL2Block(block);
|
|
438
|
+
const publishedL2Block = await this.publisher.processL2Block(block, attestations);
|
|
340
439
|
if (publishedL2Block) {
|
|
341
440
|
this.lastPublishedBlock = block.number;
|
|
342
441
|
} else {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Tx, type TxValidator } from '@aztec/circuit-types';
|
|
2
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
3
|
+
|
|
4
|
+
export class DataTxValidator implements TxValidator<Tx> {
|
|
5
|
+
#log = createDebugLogger('aztec:sequencer:tx_validator:tx_data');
|
|
6
|
+
|
|
7
|
+
validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
|
|
8
|
+
const validTxs: Tx[] = [];
|
|
9
|
+
const invalidTxs: Tx[] = [];
|
|
10
|
+
for (const tx of txs) {
|
|
11
|
+
if (!this.#hasCorrectExecutionRequests(tx)) {
|
|
12
|
+
invalidTxs.push(tx);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
validTxs.push(tx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return Promise.resolve([validTxs, invalidTxs]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#hasCorrectExecutionRequests(tx: Tx): boolean {
|
|
23
|
+
const callRequests = [
|
|
24
|
+
...tx.data.getRevertiblePublicCallRequests(),
|
|
25
|
+
...tx.data.getNonRevertiblePublicCallRequests(),
|
|
26
|
+
];
|
|
27
|
+
if (callRequests.length !== tx.enqueuedPublicFunctionCalls.length) {
|
|
28
|
+
this.#log.warn(
|
|
29
|
+
`Rejecting tx ${Tx.getHash(tx)} because of mismatch number of execution requests for public calls. Expected ${
|
|
30
|
+
callRequests.length
|
|
31
|
+
}. Got ${tx.enqueuedPublicFunctionCalls.length}.`,
|
|
32
|
+
);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const invalidExecutionRequestIndex = tx.enqueuedPublicFunctionCalls.findIndex(
|
|
37
|
+
(execRequest, i) => !execRequest.isForCallRequest(callRequests[i]),
|
|
38
|
+
);
|
|
39
|
+
if (invalidExecutionRequestIndex !== -1) {
|
|
40
|
+
this.#log.warn(
|
|
41
|
+
`Rejecting tx ${Tx.getHash(
|
|
42
|
+
tx,
|
|
43
|
+
)} because of incorrect execution requests for public call at index ${invalidExecutionRequestIndex}.`,
|
|
44
|
+
);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const teardownCallRequest = tx.data.getTeardownPublicCallRequest();
|
|
49
|
+
const isInvalidTeardownExecutionRequest =
|
|
50
|
+
(!teardownCallRequest && !tx.publicTeardownFunctionCall.isEmpty()) ||
|
|
51
|
+
(teardownCallRequest && !tx.publicTeardownFunctionCall.isForCallRequest(teardownCallRequest));
|
|
52
|
+
if (isInvalidTeardownExecutionRequest) {
|
|
53
|
+
this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} because of incorrect teardown execution requests.`);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// TODO: Check logs.
|
|
61
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PublicKernelType, type Tx, type TxValidator } from '@aztec/circuit-types';
|
|
2
2
|
import { type AztecAddress, type Fr } from '@aztec/circuits.js';
|
|
3
3
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
4
|
+
import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice';
|
|
5
5
|
import { AbstractPhaseManager, computeFeePayerBalanceStorageSlot } from '@aztec/simulator';
|
|
6
6
|
|
|
7
7
|
/** Provides a view into public contract state */
|
|
@@ -12,11 +12,11 @@ export interface PublicStateSource {
|
|
|
12
12
|
export class GasTxValidator implements TxValidator<Tx> {
|
|
13
13
|
#log = createDebugLogger('aztec:sequencer:tx_validator:tx_gas');
|
|
14
14
|
#publicDataSource: PublicStateSource;
|
|
15
|
-
#
|
|
15
|
+
#feeJuiceAddress: AztecAddress;
|
|
16
16
|
|
|
17
|
-
constructor(publicDataSource: PublicStateSource,
|
|
17
|
+
constructor(publicDataSource: PublicStateSource, feeJuiceAddress: AztecAddress, public enforceFees: boolean) {
|
|
18
18
|
this.#publicDataSource = publicDataSource;
|
|
19
|
-
this.#
|
|
19
|
+
this.#feeJuiceAddress = feeJuiceAddress;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
|
|
@@ -50,17 +50,19 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
50
50
|
|
|
51
51
|
// Read current balance of the feePayer
|
|
52
52
|
const initialBalance = await this.#publicDataSource.storageRead(
|
|
53
|
-
this.#
|
|
53
|
+
this.#feeJuiceAddress,
|
|
54
54
|
computeFeePayerBalanceStorageSlot(feePayer),
|
|
55
55
|
);
|
|
56
56
|
|
|
57
|
-
// If there is a claim in this tx that increases the fee payer balance in
|
|
57
|
+
// If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
|
|
58
58
|
const { [PublicKernelType.SETUP]: setupFns } = AbstractPhaseManager.extractEnqueuedPublicCallsByPhase(tx);
|
|
59
59
|
const claimFunctionCall = setupFns.find(
|
|
60
60
|
fn =>
|
|
61
|
-
fn.contractAddress.equals(this.#
|
|
62
|
-
fn.callContext.msgSender.equals(this.#
|
|
63
|
-
fn.functionSelector.equals(
|
|
61
|
+
fn.contractAddress.equals(this.#feeJuiceAddress) &&
|
|
62
|
+
fn.callContext.msgSender.equals(this.#feeJuiceAddress) &&
|
|
63
|
+
fn.callContext.functionSelector.equals(
|
|
64
|
+
FeeJuiceArtifact.functions.find(f => f.name === '_increase_public_balance')!,
|
|
65
|
+
) &&
|
|
64
66
|
fn.args[0].equals(feePayer) &&
|
|
65
67
|
!fn.callContext.isStaticCall &&
|
|
66
68
|
!fn.callContext.isDelegateCall,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type AllowedElement,
|
|
3
|
+
type PublicExecutionRequest,
|
|
4
|
+
PublicKernelType,
|
|
5
|
+
Tx,
|
|
6
|
+
type TxValidator,
|
|
7
|
+
} from '@aztec/circuit-types';
|
|
3
8
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
9
|
import { AbstractPhaseManager, ContractsDataSourcePublicDB } from '@aztec/simulator';
|
|
5
10
|
import { type ContractDataSource } from '@aztec/types/contracts';
|
|
@@ -47,7 +52,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
47
52
|
this.#log.warn(
|
|
48
53
|
`Rejecting tx ${Tx.getHash(tx)} because it calls setup function not on allow list: ${
|
|
49
54
|
setupFn.contractAddress
|
|
50
|
-
}:${setupFn.functionSelector}`,
|
|
55
|
+
}:${setupFn.callContext.functionSelector}`,
|
|
51
56
|
);
|
|
52
57
|
|
|
53
58
|
return false;
|
|
@@ -57,12 +62,15 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
57
62
|
return true;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
async isOnAllowList(publicCall:
|
|
65
|
+
async isOnAllowList(publicCall: PublicExecutionRequest, allowList: AllowedElement[]): Promise<boolean> {
|
|
61
66
|
if (publicCall.isEmpty()) {
|
|
62
67
|
return true;
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
const {
|
|
70
|
+
const {
|
|
71
|
+
contractAddress,
|
|
72
|
+
callContext: { functionSelector },
|
|
73
|
+
} = publicCall;
|
|
66
74
|
|
|
67
75
|
// do these checks first since they don't require the contract class
|
|
68
76
|
for (const entry of allowList) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Tx } from '@aztec/circuit-types';
|
|
2
2
|
import { type AztecAddress, type Fr, type FunctionSelector } from '@aztec/circuits.js';
|
|
3
|
+
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
|
|
3
4
|
|
|
4
5
|
export function patchNonRevertibleFn(
|
|
5
6
|
tx: Tx,
|
|
@@ -25,13 +26,19 @@ function patchFn(
|
|
|
25
26
|
): { address: AztecAddress; selector: FunctionSelector } {
|
|
26
27
|
const fn = tx.enqueuedPublicFunctionCalls.at(-1 * index - 1)!;
|
|
27
28
|
fn.contractAddress = overrides.address ?? fn.contractAddress;
|
|
28
|
-
fn.functionSelector = overrides.selector;
|
|
29
|
+
fn.callContext.functionSelector = overrides.selector;
|
|
29
30
|
fn.args = overrides.args ?? fn.args;
|
|
30
31
|
fn.callContext.msgSender = overrides.msgSender ?? fn.callContext.msgSender;
|
|
31
|
-
tx.
|
|
32
|
+
tx.enqueuedPublicFunctionCalls[index] = fn;
|
|
33
|
+
|
|
34
|
+
const request = tx.data.forPublic![where].publicCallStack[index];
|
|
35
|
+
request.item.contractAddress = fn.contractAddress;
|
|
36
|
+
request.item.callContext = fn.callContext;
|
|
37
|
+
request.item.argsHash = computeVarArgsHash(fn.args);
|
|
38
|
+
tx.data.forPublic![where].publicCallStack[index] = request;
|
|
32
39
|
|
|
33
40
|
return {
|
|
34
41
|
address: fn.contractAddress,
|
|
35
|
-
selector: fn.functionSelector,
|
|
42
|
+
selector: fn.callContext.functionSelector,
|
|
36
43
|
};
|
|
37
44
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type AllowedElement, type ProcessedTx, type Tx, type TxValidator } from '@aztec/circuit-types';
|
|
2
2
|
import { type GlobalVariables } from '@aztec/circuits.js';
|
|
3
|
-
import {
|
|
3
|
+
import { FeeJuiceAddress } from '@aztec/protocol-contracts/fee-juice';
|
|
4
4
|
import { WorldStateDB, WorldStatePublicDB } from '@aztec/simulator';
|
|
5
5
|
import { type ContractDataSource } from '@aztec/types/contracts';
|
|
6
6
|
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
7
7
|
|
|
8
8
|
import { AggregateTxValidator } from './aggregate_tx_validator.js';
|
|
9
|
+
import { DataTxValidator } from './data_validator.js';
|
|
9
10
|
import { DoubleSpendTxValidator } from './double_spend_validator.js';
|
|
10
11
|
import { GasTxValidator } from './gas_validator.js';
|
|
11
12
|
import { MetadataTxValidator } from './metadata_validator.js';
|
|
@@ -20,10 +21,11 @@ export class TxValidatorFactory {
|
|
|
20
21
|
|
|
21
22
|
validatorForNewTxs(globalVariables: GlobalVariables, setupAllowList: AllowedElement[]): TxValidator<Tx> {
|
|
22
23
|
return new AggregateTxValidator(
|
|
24
|
+
new DataTxValidator(),
|
|
23
25
|
new MetadataTxValidator(globalVariables),
|
|
24
26
|
new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb)),
|
|
25
27
|
new PhasesTxValidator(this.contractDataSource, setupAllowList),
|
|
26
|
-
new GasTxValidator(new WorldStatePublicDB(this.merkleTreeDb),
|
|
28
|
+
new GasTxValidator(new WorldStatePublicDB(this.merkleTreeDb), FeeJuiceAddress, this.enforceFees),
|
|
27
29
|
);
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type L1ContractAddresses } from '@aztec/ethereum';
|
|
2
|
-
/**
|
|
3
|
-
* Configuration of the L1GlobalReader.
|
|
4
|
-
*/
|
|
5
|
-
export interface GlobalReaderConfig {
|
|
6
|
-
/**
|
|
7
|
-
* The RPC Url of the ethereum host.
|
|
8
|
-
*/
|
|
9
|
-
rpcUrl: string;
|
|
10
|
-
/**
|
|
11
|
-
* The chain ID of the ethereum host.
|
|
12
|
-
*/
|
|
13
|
-
l1ChainId: number;
|
|
14
|
-
/**
|
|
15
|
-
* The deployed l1 contract addresses
|
|
16
|
-
*/
|
|
17
|
-
l1Contracts: L1ContractAddresses;
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/global_variable_builder/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,WAAW,EAAE,mBAAmB,CAAC;CAClC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { type L1ContractAddresses } from '@aztec/ethereum';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Configuration of the L1GlobalReader.
|
|
5
|
-
*/
|
|
6
|
-
export interface GlobalReaderConfig {
|
|
7
|
-
/**
|
|
8
|
-
* The RPC Url of the ethereum host.
|
|
9
|
-
*/
|
|
10
|
-
rpcUrl: string;
|
|
11
|
-
/**
|
|
12
|
-
* The chain ID of the ethereum host.
|
|
13
|
-
*/
|
|
14
|
-
l1ChainId: number;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The deployed l1 contract addresses
|
|
18
|
-
*/
|
|
19
|
-
l1Contracts: L1ContractAddresses;
|
|
20
|
-
}
|