@aztec/sequencer-client 3.0.0-nightly.20251221 → 3.0.0-nightly.20251223
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 +9 -8
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +28 -24
- package/dest/config.d.ts +7 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +63 -26
- package/dest/global_variable_builder/global_builder.d.ts +16 -8
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +35 -26
- package/dest/index.d.ts +2 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/publisher/config.d.ts +3 -3
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +2 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.d.ts +11 -24
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +50 -62
- package/dest/sequencer/block_builder.d.ts +1 -3
- package/dest/sequencer/block_builder.d.ts.map +1 -1
- package/dest/sequencer/block_builder.js +4 -2
- package/dest/sequencer/checkpoint_builder.d.ts +63 -0
- package/dest/sequencer/checkpoint_builder.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_builder.js +131 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts +73 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job.js +638 -0
- package/dest/sequencer/checkpoint_voter.d.ts +34 -0
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_voter.js +85 -0
- package/dest/sequencer/events.d.ts +46 -0
- package/dest/sequencer/events.d.ts.map +1 -0
- package/dest/sequencer/events.js +1 -0
- package/dest/sequencer/index.d.ts +5 -1
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +4 -0
- package/dest/sequencer/metrics.d.ts +3 -1
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +9 -0
- package/dest/sequencer/sequencer.d.ts +87 -127
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +179 -596
- package/dest/sequencer/timetable.d.ts +33 -13
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +73 -39
- package/dest/sequencer/types.d.ts +3 -0
- package/dest/sequencer/types.d.ts.map +1 -0
- package/dest/sequencer/types.js +1 -0
- package/dest/sequencer/utils.d.ts +14 -8
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +7 -4
- package/dest/test/index.d.ts +3 -1
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +24 -31
- package/src/config.ts +68 -25
- package/src/global_variable_builder/global_builder.ts +45 -39
- package/src/index.ts +2 -0
- package/src/publisher/config.ts +3 -3
- package/src/publisher/sequencer-publisher-factory.ts +3 -3
- package/src/publisher/sequencer-publisher-metrics.ts +2 -2
- package/src/publisher/sequencer-publisher.ts +71 -74
- package/src/sequencer/block_builder.ts +4 -1
- package/src/sequencer/checkpoint_builder.ts +217 -0
- package/src/sequencer/checkpoint_proposal_job.ts +701 -0
- package/src/sequencer/checkpoint_voter.ts +105 -0
- package/src/sequencer/events.ts +27 -0
- package/src/sequencer/index.ts +4 -0
- package/src/sequencer/metrics.ts +11 -0
- package/src/sequencer/sequencer.ts +275 -804
- package/src/sequencer/timetable.ts +84 -49
- package/src/sequencer/types.ts +6 -0
- package/src/sequencer/utils.ts +18 -9
- package/src/test/index.ts +2 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type BlobClientInterface, createBlobClient } from '@aztec/blob-client/client';
|
|
2
2
|
import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
3
|
-
import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
4
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
5
4
|
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
6
5
|
import {
|
|
@@ -27,6 +26,7 @@ import { FormattedViemError, formatViemError, tryExtractEvent } from '@aztec/eth
|
|
|
27
26
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
28
27
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
29
28
|
import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
29
|
+
import { pick } from '@aztec/foundation/collection';
|
|
30
30
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
31
31
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
32
32
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
@@ -35,10 +35,11 @@ import { bufferToHex } from '@aztec/foundation/string';
|
|
|
35
35
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
36
36
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
37
37
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
38
|
-
import {
|
|
38
|
+
import { CommitteeAttestationsAndSigners, type ValidateBlockResult } from '@aztec/stdlib/block';
|
|
39
|
+
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
39
40
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
40
41
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
41
|
-
import type {
|
|
42
|
+
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
42
43
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
43
44
|
|
|
44
45
|
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
@@ -114,7 +115,7 @@ export class SequencerPublisher {
|
|
|
114
115
|
protected log: Logger;
|
|
115
116
|
protected ethereumSlotDuration: bigint;
|
|
116
117
|
|
|
117
|
-
private
|
|
118
|
+
private blobClient: BlobClientInterface;
|
|
118
119
|
|
|
119
120
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */
|
|
120
121
|
private proposerAddressForSimulation?: EthAddress;
|
|
@@ -144,7 +145,7 @@ export class SequencerPublisher {
|
|
|
144
145
|
private config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>,
|
|
145
146
|
deps: {
|
|
146
147
|
telemetry?: TelemetryClient;
|
|
147
|
-
|
|
148
|
+
blobClient?: BlobClientInterface;
|
|
148
149
|
l1TxUtils: L1TxUtilsWithBlobs;
|
|
149
150
|
rollupContract: RollupContract;
|
|
150
151
|
slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
@@ -162,8 +163,8 @@ export class SequencerPublisher {
|
|
|
162
163
|
this.epochCache = deps.epochCache;
|
|
163
164
|
this.lastActions = deps.lastActions;
|
|
164
165
|
|
|
165
|
-
this.
|
|
166
|
-
deps.
|
|
166
|
+
this.blobClient =
|
|
167
|
+
deps.blobClient ?? createBlobClient(config, { logger: createLogger('sequencer:blob-client:client') });
|
|
167
168
|
|
|
168
169
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
169
170
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
@@ -241,7 +242,7 @@ export class SequencerPublisher {
|
|
|
241
242
|
* @returns The analysis result (incomplete until block mines), or undefined if no requests
|
|
242
243
|
*/
|
|
243
244
|
public async analyzeL1Fees(
|
|
244
|
-
l2SlotNumber:
|
|
245
|
+
l2SlotNumber: SlotNumber,
|
|
245
246
|
onComplete?: (analysis: L1FeeAnalysisResult) => void,
|
|
246
247
|
): Promise<L1FeeAnalysisResult | undefined> {
|
|
247
248
|
if (!this.l1FeeAnalyzer) {
|
|
@@ -299,7 +300,7 @@ export class SequencerPublisher {
|
|
|
299
300
|
public async sendRequests() {
|
|
300
301
|
const requestsToProcess = [...this.requests];
|
|
301
302
|
this.requests = [];
|
|
302
|
-
if (this.interrupted) {
|
|
303
|
+
if (this.interrupted || requestsToProcess.length === 0) {
|
|
303
304
|
return undefined;
|
|
304
305
|
}
|
|
305
306
|
const currentL2Slot = this.getCurrentL2Slot();
|
|
@@ -587,45 +588,38 @@ export class SequencerPublisher {
|
|
|
587
588
|
}
|
|
588
589
|
}
|
|
589
590
|
|
|
590
|
-
/**
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
* @dev Throws if unable to propose
|
|
594
|
-
*
|
|
595
|
-
* @param block - The block to propose
|
|
596
|
-
* @param attestationData - The block's attestation data
|
|
597
|
-
*
|
|
598
|
-
*/
|
|
599
|
-
public async validateBlockForSubmission(
|
|
600
|
-
block: L2Block,
|
|
591
|
+
/** Simulates `propose` to make sure that the checkpoint is valid for submission */
|
|
592
|
+
public async validateCheckpointForSubmission(
|
|
593
|
+
checkpoint: Checkpoint,
|
|
601
594
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
602
595
|
attestationsAndSignersSignature: Signature,
|
|
603
|
-
options: { forcePendingBlockNumber?: BlockNumber },
|
|
596
|
+
options: { forcePendingBlockNumber?: BlockNumber }, // TODO(palla/mbps): Should this be forcePendingCheckpointNumber?
|
|
604
597
|
): Promise<bigint> {
|
|
605
598
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
606
599
|
|
|
600
|
+
// TODO(palla/mbps): This should not be needed, there's no flow where we propose with zero attestations. Or is there?
|
|
607
601
|
// If we have no attestations, we still need to provide the empty attestations
|
|
608
602
|
// so that the committee is recalculated correctly
|
|
609
|
-
const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
610
|
-
if (ignoreSignatures) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
const blobFields =
|
|
603
|
+
// const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
604
|
+
// if (ignoreSignatures) {
|
|
605
|
+
// const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
|
|
606
|
+
// if (!committee) {
|
|
607
|
+
// this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
608
|
+
// throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
609
|
+
// }
|
|
610
|
+
// attestationsAndSigners.attestations = committee.map(committeeMember =>
|
|
611
|
+
// CommitteeAttestation.fromAddress(committeeMember),
|
|
612
|
+
// );
|
|
613
|
+
// }
|
|
614
|
+
|
|
615
|
+
const blobFields = checkpoint.toBlobFields();
|
|
622
616
|
const blobs = getBlobsPerL1Block(blobFields);
|
|
623
617
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
624
618
|
|
|
625
619
|
const args = [
|
|
626
620
|
{
|
|
627
|
-
header:
|
|
628
|
-
archive: toHex(
|
|
621
|
+
header: checkpoint.header.toViem(),
|
|
622
|
+
archive: toHex(checkpoint.archive.root.toBuffer()),
|
|
629
623
|
oracleInput: {
|
|
630
624
|
feeAssetPriceModifier: 0n,
|
|
631
625
|
},
|
|
@@ -718,14 +712,14 @@ export class SequencerPublisher {
|
|
|
718
712
|
const logData = { ...result, slotNumber, round, payload: payload.toString() };
|
|
719
713
|
if (!success) {
|
|
720
714
|
this.log.error(
|
|
721
|
-
`Signaling in
|
|
715
|
+
`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} failed`,
|
|
722
716
|
logData,
|
|
723
717
|
);
|
|
724
718
|
this.lastActions[signalType] = cachedLastVote;
|
|
725
719
|
return false;
|
|
726
720
|
} else {
|
|
727
721
|
this.log.info(
|
|
728
|
-
`Signaling in
|
|
722
|
+
`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} succeeded`,
|
|
729
723
|
logData,
|
|
730
724
|
);
|
|
731
725
|
return true;
|
|
@@ -893,27 +887,21 @@ export class SequencerPublisher {
|
|
|
893
887
|
return true;
|
|
894
888
|
}
|
|
895
889
|
|
|
896
|
-
/**
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
* @param block - L2 block to propose.
|
|
900
|
-
* @returns True if the tx has been enqueued, throws otherwise. See #9315
|
|
901
|
-
*/
|
|
902
|
-
public async enqueueProposeL2Block(
|
|
903
|
-
block: L2Block,
|
|
890
|
+
/** Simulates and enqueues a proposal for a checkpoint on L1 */
|
|
891
|
+
public async enqueueProposeCheckpoint(
|
|
892
|
+
checkpoint: Checkpoint,
|
|
904
893
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
905
894
|
attestationsAndSignersSignature: Signature,
|
|
906
895
|
opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
|
|
907
|
-
): Promise<
|
|
908
|
-
const checkpointHeader =
|
|
896
|
+
): Promise<void> {
|
|
897
|
+
const checkpointHeader = checkpoint.header;
|
|
909
898
|
|
|
910
|
-
const blobFields =
|
|
899
|
+
const blobFields = checkpoint.toBlobFields();
|
|
911
900
|
const blobs = getBlobsPerL1Block(blobFields);
|
|
912
901
|
|
|
913
902
|
const proposeTxArgs = {
|
|
914
903
|
header: checkpointHeader,
|
|
915
|
-
archive:
|
|
916
|
-
body: block.body.toBuffer(),
|
|
904
|
+
archive: checkpoint.archive.root.toBuffer(),
|
|
917
905
|
blobs,
|
|
918
906
|
attestationsAndSigners,
|
|
919
907
|
attestationsAndSignersSignature,
|
|
@@ -927,19 +915,23 @@ export class SequencerPublisher {
|
|
|
927
915
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
928
916
|
// make time consistency checks break.
|
|
929
917
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
930
|
-
ts = await this.
|
|
918
|
+
ts = await this.validateCheckpointForSubmission(
|
|
919
|
+
checkpoint,
|
|
920
|
+
attestationsAndSigners,
|
|
921
|
+
attestationsAndSignersSignature,
|
|
922
|
+
opts,
|
|
923
|
+
);
|
|
931
924
|
} catch (err: any) {
|
|
932
|
-
this.log.error(`
|
|
933
|
-
...
|
|
934
|
-
slotNumber:
|
|
925
|
+
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
926
|
+
...checkpoint.getStats(),
|
|
927
|
+
slotNumber: checkpoint.header.slotNumber,
|
|
935
928
|
forcePendingBlockNumber: opts.forcePendingBlockNumber,
|
|
936
929
|
});
|
|
937
930
|
throw err;
|
|
938
931
|
}
|
|
939
932
|
|
|
940
|
-
this.log.verbose(`Enqueuing
|
|
941
|
-
await this.addProposeTx(
|
|
942
|
-
return true;
|
|
933
|
+
this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
|
|
934
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
|
|
943
935
|
}
|
|
944
936
|
|
|
945
937
|
public enqueueInvalidateBlock(request: InvalidateBlockRequest | undefined, opts: { txTimeoutAt?: Date } = {}) {
|
|
@@ -1204,11 +1196,12 @@ export class SequencerPublisher {
|
|
|
1204
1196
|
}
|
|
1205
1197
|
|
|
1206
1198
|
private async addProposeTx(
|
|
1207
|
-
|
|
1199
|
+
checkpoint: Checkpoint,
|
|
1208
1200
|
encodedData: L1ProcessArgs,
|
|
1209
1201
|
opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
|
|
1210
1202
|
timestamp: bigint,
|
|
1211
1203
|
): Promise<void> {
|
|
1204
|
+
const slot = checkpoint.header.slotNumber;
|
|
1212
1205
|
const timer = new Timer();
|
|
1213
1206
|
const kzg = Blob.getViemKzgInstance();
|
|
1214
1207
|
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
@@ -1223,11 +1216,11 @@ export class SequencerPublisher {
|
|
|
1223
1216
|
SequencerPublisher.MULTICALL_OVERHEAD_GAS_GUESS, // We issue the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
1224
1217
|
);
|
|
1225
1218
|
|
|
1226
|
-
// Send the blobs to the blob
|
|
1227
|
-
// tx fails but it does get mined. We make sure that the blobs are sent to the blob
|
|
1219
|
+
// Send the blobs to the blob client preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
|
|
1220
|
+
// tx fails but it does get mined. We make sure that the blobs are sent to the blob client regardless of the tx outcome.
|
|
1228
1221
|
void Promise.resolve().then(() =>
|
|
1229
|
-
this.
|
|
1230
|
-
this.log.error('Failed to send blobs to blob
|
|
1222
|
+
this.blobClient.sendBlobsToFilestore(encodedData.blobs).catch(_err => {
|
|
1223
|
+
this.log.error('Failed to send blobs to blob client');
|
|
1231
1224
|
}),
|
|
1232
1225
|
);
|
|
1233
1226
|
|
|
@@ -1237,7 +1230,7 @@ export class SequencerPublisher {
|
|
|
1237
1230
|
to: this.rollupContract.address,
|
|
1238
1231
|
data: rollupData,
|
|
1239
1232
|
},
|
|
1240
|
-
lastValidL2Slot:
|
|
1233
|
+
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
1241
1234
|
gasConfig: { ...opts, gasLimit },
|
|
1242
1235
|
blobConfig: {
|
|
1243
1236
|
blobs: encodedData.blobs.map(b => b.data),
|
|
@@ -1252,11 +1245,12 @@ export class SequencerPublisher {
|
|
|
1252
1245
|
receipt &&
|
|
1253
1246
|
receipt.status === 'success' &&
|
|
1254
1247
|
tryExtractEvent(receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointProposed');
|
|
1248
|
+
|
|
1255
1249
|
if (success) {
|
|
1256
1250
|
const endBlock = receipt.blockNumber;
|
|
1257
1251
|
const inclusionBlocks = Number(endBlock - startBlock);
|
|
1258
1252
|
const { calldataGas, calldataSize, sender } = stats!;
|
|
1259
|
-
const publishStats:
|
|
1253
|
+
const publishStats: L1PublishCheckpointStats = {
|
|
1260
1254
|
gasPrice: receipt.effectiveGasPrice,
|
|
1261
1255
|
gasUsed: receipt.gasUsed,
|
|
1262
1256
|
blobGasUsed: receipt.blobGasUsed ?? 0n,
|
|
@@ -1265,23 +1259,26 @@ export class SequencerPublisher {
|
|
|
1265
1259
|
calldataGas,
|
|
1266
1260
|
calldataSize,
|
|
1267
1261
|
sender,
|
|
1268
|
-
...
|
|
1262
|
+
...checkpoint.getStats(),
|
|
1269
1263
|
eventName: 'rollup-published-to-l1',
|
|
1270
1264
|
blobCount: encodedData.blobs.length,
|
|
1271
1265
|
inclusionBlocks,
|
|
1272
1266
|
};
|
|
1273
|
-
this.log.info(`Published
|
|
1267
|
+
this.log.info(`Published checkpoint ${checkpoint.number} at slot ${slot} to rollup contract`, {
|
|
1268
|
+
...stats,
|
|
1269
|
+
...checkpoint.getStats(),
|
|
1270
|
+
...pick(receipt, 'transactionHash', 'blockHash'),
|
|
1271
|
+
});
|
|
1274
1272
|
this.metrics.recordProcessBlockTx(timer.ms(), publishStats);
|
|
1275
1273
|
|
|
1276
1274
|
return true;
|
|
1277
1275
|
} else {
|
|
1278
1276
|
this.metrics.recordFailedTx('process');
|
|
1279
|
-
this.log.error(
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
});
|
|
1277
|
+
this.log.error(
|
|
1278
|
+
`Publishing checkpoint at slot ${slot} failed with ${errorMsg ?? 'no error message'}`,
|
|
1279
|
+
undefined,
|
|
1280
|
+
{ ...checkpoint.getStats(), ...receipt },
|
|
1281
|
+
);
|
|
1285
1282
|
return false;
|
|
1286
1283
|
}
|
|
1287
1284
|
},
|
|
@@ -33,7 +33,8 @@ import { createValidatorForBlockBuilding } from '../tx_validator/tx_validator_fa
|
|
|
33
33
|
|
|
34
34
|
const log = createLogger('block-builder');
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
/** Builds a block out of pending txs */
|
|
37
|
+
async function buildBlock(
|
|
37
38
|
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
38
39
|
l1ToL2Messages: Fr[],
|
|
39
40
|
newGlobalVariables: GlobalVariables,
|
|
@@ -97,8 +98,10 @@ const FullNodeBlockBuilderConfigKeys = [
|
|
|
97
98
|
'rollupVersion',
|
|
98
99
|
'txPublicSetupAllowList',
|
|
99
100
|
'fakeProcessingDelayPerTxMs',
|
|
101
|
+
'fakeThrowAfterProcessingTxCount',
|
|
100
102
|
] as const;
|
|
101
103
|
|
|
104
|
+
// TODO(palla/mbps): Try killing this in favor of the CheckpointsBuilder
|
|
102
105
|
export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
|
|
103
106
|
constructor(
|
|
104
107
|
private config: FullNodeBlockBuilderConfig,
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { MerkleTreeId } from '@aztec/aztec.js/trees';
|
|
2
|
+
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { merge, pick } from '@aztec/foundation/collection';
|
|
4
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
6
|
+
import { bufferToHex } from '@aztec/foundation/string';
|
|
7
|
+
import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
|
|
8
|
+
import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
|
|
9
|
+
import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
|
|
10
|
+
import {
|
|
11
|
+
GuardedMerkleTreeOperations,
|
|
12
|
+
PublicContractsDB,
|
|
13
|
+
PublicProcessor,
|
|
14
|
+
createPublicTxSimulatorForBlockBuilding,
|
|
15
|
+
} from '@aztec/simulator/server';
|
|
16
|
+
import { L2BlockNew } from '@aztec/stdlib/block';
|
|
17
|
+
import { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
18
|
+
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
19
|
+
import { Gas } from '@aztec/stdlib/gas';
|
|
20
|
+
import {
|
|
21
|
+
type FullNodeBlockBuilderConfig,
|
|
22
|
+
FullNodeBlockBuilderConfigKeys,
|
|
23
|
+
type MerkleTreeWriteOperations,
|
|
24
|
+
type PublicProcessorLimits,
|
|
25
|
+
} from '@aztec/stdlib/interfaces/server';
|
|
26
|
+
import { type CheckpointGlobalVariables, type FailedTx, GlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
27
|
+
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
28
|
+
|
|
29
|
+
import { createValidatorForBlockBuilding } from '../tx_validator/tx_validator_factory.js';
|
|
30
|
+
|
|
31
|
+
const log = createLogger('checkpoint-builder');
|
|
32
|
+
|
|
33
|
+
export interface BuildBlockInCheckpointResult {
|
|
34
|
+
block: L2BlockNew;
|
|
35
|
+
publicGas: Gas;
|
|
36
|
+
publicProcessorDuration: number;
|
|
37
|
+
numTxs: number;
|
|
38
|
+
failedTxs: FailedTx[];
|
|
39
|
+
blockBuildingTimer: Timer;
|
|
40
|
+
usedTxs: Tx[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Builder for a single checkpoint. Handles building blocks within the checkpoint
|
|
45
|
+
* and completing it.
|
|
46
|
+
*/
|
|
47
|
+
export class CheckpointBuilder {
|
|
48
|
+
constructor(
|
|
49
|
+
private checkpointBuilder: LightweightCheckpointBuilder,
|
|
50
|
+
private fork: MerkleTreeWriteOperations,
|
|
51
|
+
private config: FullNodeBlockBuilderConfig,
|
|
52
|
+
private contractDataSource: ContractDataSource,
|
|
53
|
+
private dateProvider: DateProvider,
|
|
54
|
+
private telemetryClient: TelemetryClient,
|
|
55
|
+
) {}
|
|
56
|
+
|
|
57
|
+
getConstantData(): CheckpointGlobalVariables {
|
|
58
|
+
return this.checkpointBuilder.constants;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Builds a single block within this checkpoint.
|
|
63
|
+
*/
|
|
64
|
+
async buildBlock(
|
|
65
|
+
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
66
|
+
blockNumber: BlockNumber,
|
|
67
|
+
timestamp: bigint,
|
|
68
|
+
opts: PublicProcessorLimits,
|
|
69
|
+
): Promise<BuildBlockInCheckpointResult> {
|
|
70
|
+
const blockBuildingTimer = new Timer();
|
|
71
|
+
const slot = this.checkpointBuilder.constants.slotNumber;
|
|
72
|
+
|
|
73
|
+
log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, { slot, blockNumber, ...opts });
|
|
74
|
+
|
|
75
|
+
const constants = this.checkpointBuilder.constants;
|
|
76
|
+
const globalVariables = GlobalVariables.from({
|
|
77
|
+
chainId: constants.chainId,
|
|
78
|
+
version: constants.version,
|
|
79
|
+
blockNumber,
|
|
80
|
+
slotNumber: constants.slotNumber,
|
|
81
|
+
timestamp,
|
|
82
|
+
coinbase: constants.coinbase,
|
|
83
|
+
feeRecipient: constants.feeRecipient,
|
|
84
|
+
gasFees: constants.gasFees,
|
|
85
|
+
});
|
|
86
|
+
const { processor, validator } = await this.makeBlockBuilderDeps(globalVariables, this.fork);
|
|
87
|
+
|
|
88
|
+
const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
|
|
89
|
+
processor.process(pendingTxs, opts, validator),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Add block to checkpoint
|
|
93
|
+
const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs);
|
|
94
|
+
|
|
95
|
+
// How much public gas was processed
|
|
96
|
+
const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
|
|
97
|
+
|
|
98
|
+
const res = {
|
|
99
|
+
block,
|
|
100
|
+
publicGas,
|
|
101
|
+
publicProcessorDuration,
|
|
102
|
+
numTxs: processedTxs.length,
|
|
103
|
+
failedTxs,
|
|
104
|
+
blockBuildingTimer,
|
|
105
|
+
usedTxs,
|
|
106
|
+
};
|
|
107
|
+
log.debug('Built block within checkpoint', res.block.header);
|
|
108
|
+
return res;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Completes the checkpoint and returns it. */
|
|
112
|
+
async completeCheckpoint(): Promise<Checkpoint> {
|
|
113
|
+
const checkpoint = await this.checkpointBuilder.completeCheckpoint();
|
|
114
|
+
|
|
115
|
+
log.verbose(`Completed checkpoint ${checkpoint.number}`, {
|
|
116
|
+
checkpointNumber: checkpoint.number,
|
|
117
|
+
numBlocks: checkpoint.blocks.length,
|
|
118
|
+
archiveRoot: checkpoint.archive.root.toString(),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return checkpoint;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Gets the checkpoint currently in progress. */
|
|
125
|
+
getCheckpoint(): Promise<Checkpoint> {
|
|
126
|
+
return this.checkpointBuilder.clone().completeCheckpoint();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
|
|
130
|
+
const txPublicSetupAllowList = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
|
|
131
|
+
const contractsDB = new PublicContractsDB(this.contractDataSource);
|
|
132
|
+
const guardedFork = new GuardedMerkleTreeOperations(fork);
|
|
133
|
+
|
|
134
|
+
const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
|
|
135
|
+
guardedFork,
|
|
136
|
+
contractsDB,
|
|
137
|
+
globalVariables,
|
|
138
|
+
this.telemetryClient,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const processor = new PublicProcessor(
|
|
142
|
+
globalVariables,
|
|
143
|
+
guardedFork,
|
|
144
|
+
contractsDB,
|
|
145
|
+
publicTxSimulator,
|
|
146
|
+
this.dateProvider,
|
|
147
|
+
this.telemetryClient,
|
|
148
|
+
undefined,
|
|
149
|
+
this.config,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const validator = createValidatorForBlockBuilding(
|
|
153
|
+
fork,
|
|
154
|
+
this.contractDataSource,
|
|
155
|
+
globalVariables,
|
|
156
|
+
txPublicSetupAllowList,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
processor,
|
|
161
|
+
validator,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Factory for creating checkpoint builders.
|
|
168
|
+
*/
|
|
169
|
+
export class FullNodeCheckpointsBuilder {
|
|
170
|
+
constructor(
|
|
171
|
+
private config: FullNodeBlockBuilderConfig,
|
|
172
|
+
private contractDataSource: ContractDataSource,
|
|
173
|
+
private dateProvider: DateProvider,
|
|
174
|
+
private telemetryClient: TelemetryClient = getTelemetryClient(),
|
|
175
|
+
) {}
|
|
176
|
+
|
|
177
|
+
public updateConfig(config: Partial<FullNodeBlockBuilderConfig>) {
|
|
178
|
+
this.config = merge(this.config, pick(config, ...FullNodeBlockBuilderConfigKeys));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Starts a new checkpoint and returns a CheckpointBuilder to build blocks within it.
|
|
183
|
+
*/
|
|
184
|
+
async startCheckpoint(
|
|
185
|
+
checkpointNumber: CheckpointNumber,
|
|
186
|
+
constants: CheckpointGlobalVariables,
|
|
187
|
+
l1ToL2Messages: Fr[],
|
|
188
|
+
fork: MerkleTreeWriteOperations,
|
|
189
|
+
): Promise<CheckpointBuilder> {
|
|
190
|
+
const stateReference = await fork.getStateReference();
|
|
191
|
+
const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
192
|
+
|
|
193
|
+
log.verbose(`Building checkpoint ${checkpointNumber}`, {
|
|
194
|
+
checkpointNumber,
|
|
195
|
+
msgCount: l1ToL2Messages.length,
|
|
196
|
+
initialStateReference: stateReference.toInspect(),
|
|
197
|
+
initialArchiveRoot: bufferToHex(archiveTree.root),
|
|
198
|
+
constants,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const lightweightBuilder = await LightweightCheckpointBuilder.startNewCheckpoint(
|
|
202
|
+
checkpointNumber,
|
|
203
|
+
constants,
|
|
204
|
+
l1ToL2Messages,
|
|
205
|
+
fork,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return new CheckpointBuilder(
|
|
209
|
+
lightweightBuilder,
|
|
210
|
+
fork,
|
|
211
|
+
this.config,
|
|
212
|
+
this.contractDataSource,
|
|
213
|
+
this.dateProvider,
|
|
214
|
+
this.telemetryClient,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|