@aztec/sequencer-client 0.0.1-commit.db765a8 → 0.0.1-commit.df81a97b5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dest/client/sequencer-client.d.ts +4 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +46 -23
  4. package/dest/config.d.ts +25 -5
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +21 -12
  7. package/dest/global_variable_builder/global_builder.d.ts +13 -7
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +22 -21
  10. package/dest/global_variable_builder/index.d.ts +2 -2
  11. package/dest/global_variable_builder/index.d.ts.map +1 -1
  12. package/dest/publisher/config.d.ts +13 -1
  13. package/dest/publisher/config.d.ts.map +1 -1
  14. package/dest/publisher/config.js +17 -2
  15. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
  16. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher-factory.js +2 -2
  18. package/dest/publisher/sequencer-publisher.d.ts +9 -4
  19. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  20. package/dest/publisher/sequencer-publisher.js +33 -9
  21. package/dest/sequencer/checkpoint_proposal_job.d.ts +13 -7
  22. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  23. package/dest/sequencer/checkpoint_proposal_job.js +166 -115
  24. package/dest/sequencer/events.d.ts +2 -1
  25. package/dest/sequencer/events.d.ts.map +1 -1
  26. package/dest/sequencer/metrics.d.ts +5 -1
  27. package/dest/sequencer/metrics.d.ts.map +1 -1
  28. package/dest/sequencer/metrics.js +11 -0
  29. package/dest/sequencer/sequencer.d.ts +11 -8
  30. package/dest/sequencer/sequencer.d.ts.map +1 -1
  31. package/dest/sequencer/sequencer.js +71 -61
  32. package/dest/sequencer/timetable.d.ts +4 -3
  33. package/dest/sequencer/timetable.d.ts.map +1 -1
  34. package/dest/sequencer/timetable.js +6 -7
  35. package/dest/sequencer/types.d.ts +2 -2
  36. package/dest/sequencer/types.d.ts.map +1 -1
  37. package/dest/test/mock_checkpoint_builder.d.ts +7 -9
  38. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  39. package/dest/test/mock_checkpoint_builder.js +39 -30
  40. package/package.json +27 -28
  41. package/src/client/sequencer-client.ts +56 -21
  42. package/src/config.ts +28 -14
  43. package/src/global_variable_builder/global_builder.ts +22 -23
  44. package/src/global_variable_builder/index.ts +1 -1
  45. package/src/publisher/config.ts +32 -0
  46. package/src/publisher/sequencer-publisher-factory.ts +3 -3
  47. package/src/publisher/sequencer-publisher.ts +39 -11
  48. package/src/sequencer/checkpoint_proposal_job.ts +219 -131
  49. package/src/sequencer/events.ts +1 -1
  50. package/src/sequencer/metrics.ts +14 -0
  51. package/src/sequencer/sequencer.ts +97 -68
  52. package/src/sequencer/timetable.ts +7 -7
  53. package/src/sequencer/types.ts +1 -1
  54. package/src/test/mock_checkpoint_builder.ts +51 -48
@@ -1,14 +1,13 @@
1
- import { createEthereumChain } from '@aztec/ethereum/chain';
2
- import type { L1ContractsConfig } from '@aztec/ethereum/config';
3
1
  import { RollupContract } from '@aztec/ethereum/contracts';
4
- import type { L1ReaderConfig } from '@aztec/ethereum/l1-reader';
2
+ import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
5
3
  import type { ViemPublicClient } from '@aztec/ethereum/types';
6
4
  import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
7
5
  import { Fr } from '@aztec/foundation/curves/bn254';
8
6
  import type { EthAddress } from '@aztec/foundation/eth-address';
9
7
  import { createLogger } from '@aztec/foundation/log';
8
+ import type { DateProvider } from '@aztec/foundation/timer';
10
9
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
11
- import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
10
+ import { type L1RollupConstants, getNextL1SlotTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
12
11
  import { GasFees } from '@aztec/stdlib/gas';
13
12
  import type {
14
13
  CheckpointGlobalVariables,
@@ -16,7 +15,12 @@ import type {
16
15
  } from '@aztec/stdlib/tx';
17
16
  import { GlobalVariables } from '@aztec/stdlib/tx';
18
17
 
19
- import { createPublicClient, fallback, http } from 'viem';
18
+ /** Configuration for the GlobalVariableBuilder (excludes L1 client config). */
19
+ export type GlobalVariableBuilderConfig = {
20
+ l1Contracts: Pick<L1ContractAddresses, 'rollupAddress'>;
21
+ ethereumSlotDuration: number;
22
+ rollupVersion: bigint;
23
+ } & Pick<L1RollupConstants, 'slotDuration' | 'l1GenesisTime'>;
20
24
 
21
25
  /**
22
26
  * Simple global variables builder.
@@ -27,7 +31,6 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
27
31
  private currentL1BlockNumber: bigint | undefined = undefined;
28
32
 
29
33
  private readonly rollupContract: RollupContract;
30
- private readonly publicClient: ViemPublicClient;
31
34
  private readonly ethereumSlotDuration: number;
32
35
  private readonly aztecSlotDuration: number;
33
36
  private readonly l1GenesisTime: bigint;
@@ -36,28 +39,18 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
36
39
  private version: Fr;
37
40
 
38
41
  constructor(
39
- config: L1ReaderConfig &
40
- Pick<L1ContractsConfig, 'ethereumSlotDuration'> &
41
- Pick<L1RollupConstants, 'slotDuration' | 'l1GenesisTime'> & { rollupVersion: bigint },
42
+ private readonly dateProvider: DateProvider,
43
+ private readonly publicClient: ViemPublicClient,
44
+ config: GlobalVariableBuilderConfig,
42
45
  ) {
43
- const { l1RpcUrls, l1ChainId: chainId, l1Contracts } = config;
44
-
45
- const chain = createEthereumChain(l1RpcUrls, chainId);
46
-
47
46
  this.version = new Fr(config.rollupVersion);
48
- this.chainId = new Fr(chainId);
47
+ this.chainId = new Fr(this.publicClient.chain!.id);
49
48
 
50
49
  this.ethereumSlotDuration = config.ethereumSlotDuration;
51
50
  this.aztecSlotDuration = config.slotDuration;
52
51
  this.l1GenesisTime = config.l1GenesisTime;
53
52
 
54
- this.publicClient = createPublicClient({
55
- chain: chain.chainInfo,
56
- transport: fallback(chain.rpcUrls.map(url => http(url, { batch: false }))),
57
- pollingInterval: config.viemPollingIntervalMS,
58
- });
59
-
60
- this.rollupContract = new RollupContract(this.publicClient, l1Contracts.rollupAddress);
53
+ this.rollupContract = new RollupContract(this.publicClient, config.l1Contracts.rollupAddress);
61
54
  }
62
55
 
63
56
  /**
@@ -73,7 +66,10 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
73
66
  const earliestTimestamp = await this.rollupContract.getTimestampForSlot(
74
67
  SlotNumber.fromBigInt(BigInt(lastCheckpoint.slotNumber) + 1n),
75
68
  );
76
- const nextEthTimestamp = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration));
69
+ const nextEthTimestamp = getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), {
70
+ l1GenesisTime: this.l1GenesisTime,
71
+ ethereumSlotDuration: this.ethereumSlotDuration,
72
+ });
77
73
  const timestamp = earliestTimestamp > nextEthTimestamp ? earliestTimestamp : nextEthTimestamp;
78
74
 
79
75
  return new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true));
@@ -108,7 +104,10 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
108
104
  const slot: SlotNumber =
109
105
  maybeSlot ??
110
106
  (await this.rollupContract.getSlotAt(
111
- BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration)),
107
+ getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), {
108
+ l1GenesisTime: this.l1GenesisTime,
109
+ ethereumSlotDuration: this.ethereumSlotDuration,
110
+ }),
112
111
  ));
113
112
 
114
113
  const checkpointGlobalVariables = await this.buildCheckpointGlobalVariables(coinbase, feeRecipient, slot);
@@ -1 +1 @@
1
- export { GlobalVariableBuilder } from './global_builder.js';
1
+ export { GlobalVariableBuilder, type GlobalVariableBuilderConfig } from './global_builder.js';
@@ -4,6 +4,8 @@ import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from '@aztec/ethereum/l
4
4
  import { type ConfigMappingsType, SecretValue, booleanConfigHelper } from '@aztec/foundation/config';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
6
 
7
+ import { parseEther } from 'viem';
8
+
7
9
  /** Configuration of the transaction publisher. */
8
10
  export type TxSenderConfig = L1ReaderConfig & {
9
11
  /** The private key to be used by the publisher. */
@@ -50,13 +52,37 @@ export type PublisherConfig = L1TxUtilsConfig &
50
52
  publisherForwarderAddress?: EthAddress;
51
53
  /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
52
54
  l1TxFailedStore?: string;
55
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
56
+ publisherFundingThreshold?: bigint;
57
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
58
+ publisherFundingAmount?: bigint;
53
59
  };
54
60
 
61
+ /** Shared config mappings for publisher funding, used by both sequencer and prover publisher configs. */
62
+ const publisherFundingConfigMappings = {
63
+ publisherFundingThreshold: {
64
+ env: 'PUBLISHER_FUNDING_THRESHOLD' as const,
65
+ description:
66
+ 'Min ETH balance below which a publisher gets funded. Specified in ether (e.g. 0.1). Unset = funding disabled.',
67
+ parseEnv: (val: string) => parseEther(val),
68
+ },
69
+ publisherFundingAmount: {
70
+ env: 'PUBLISHER_FUNDING_AMOUNT' as const,
71
+ description:
72
+ 'Amount of ETH to send when funding a publisher. Specified in ether (e.g. 0.5). Unset = funding disabled.',
73
+ parseEnv: (val: string) => parseEther(val),
74
+ },
75
+ };
76
+
55
77
  export type ProverPublisherConfig = L1TxUtilsConfig &
56
78
  BlobClientConfig & {
57
79
  fishermanMode?: boolean;
58
80
  proverPublisherAllowInvalidStates?: boolean;
59
81
  proverPublisherForwarderAddress?: EthAddress;
82
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
83
+ publisherFundingThreshold?: bigint;
84
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
85
+ publisherFundingAmount?: bigint;
60
86
  };
61
87
 
62
88
  export type SequencerPublisherConfig = L1TxUtilsConfig &
@@ -66,6 +92,10 @@ export type SequencerPublisherConfig = L1TxUtilsConfig &
66
92
  sequencerPublisherForwarderAddress?: EthAddress;
67
93
  /** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
68
94
  l1TxFailedStore?: string;
95
+ /** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
96
+ publisherFundingThreshold?: bigint;
97
+ /** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
98
+ publisherFundingAmount?: bigint;
69
99
  };
70
100
 
71
101
  export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig {
@@ -142,6 +172,7 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPubli
142
172
  env: 'L1_TX_FAILED_STORE',
143
173
  description: 'Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path',
144
174
  },
175
+ ...publisherFundingConfigMappings,
145
176
  };
146
177
 
147
178
  export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherConfig & L1TxUtilsConfig> = {
@@ -163,4 +194,5 @@ export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherCo
163
194
  description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
164
195
  parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
165
196
  },
197
+ ...publisherFundingConfigMappings,
166
198
  };
@@ -117,8 +117,8 @@ export class SequencerPublisherFactory {
117
117
  };
118
118
  }
119
119
 
120
- /** Interrupts all publishers managed by this factory. Used during sequencer shutdown. */
121
- public interruptAll(): void {
122
- this.deps.publisherManager.interrupt();
120
+ /** Stops all publishers managed by this factory. Used during sequencer shutdown. */
121
+ public async stopAll(): Promise<void> {
122
+ await this.deps.publisherManager.stop();
123
123
  }
124
124
  }
@@ -28,6 +28,7 @@ import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from
28
28
  import { sumBigint } from '@aztec/foundation/bigint';
29
29
  import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
30
30
  import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
31
+ import { trimmedBytesLength } from '@aztec/foundation/buffer';
31
32
  import { pick } from '@aztec/foundation/collection';
32
33
  import type { Fr } from '@aztec/foundation/curves/bn254';
33
34
  import { TimeoutError } from '@aztec/foundation/error';
@@ -41,6 +42,7 @@ import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
41
42
  import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
42
43
  import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
43
44
  import type { Checkpoint } from '@aztec/stdlib/checkpoint';
45
+ import { getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
44
46
  import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
45
47
  import type { CheckpointHeader } from '@aztec/stdlib/rollup';
46
48
  import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
@@ -132,6 +134,8 @@ export class SequencerPublisher {
132
134
 
133
135
  protected log: Logger;
134
136
  protected ethereumSlotDuration: bigint;
137
+ protected aztecSlotDuration: bigint;
138
+ private dateProvider: DateProvider;
135
139
 
136
140
  private blobClient: BlobClientInterface;
137
141
 
@@ -165,7 +169,7 @@ export class SequencerPublisher {
165
169
 
166
170
  constructor(
167
171
  private config: Pick<SequencerPublisherConfig, 'fishermanMode' | 'l1TxFailedStore'> &
168
- Pick<L1ContractsConfig, 'ethereumSlotDuration'> & { l1ChainId: number },
172
+ Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration'> & { l1ChainId: number },
169
173
  deps: {
170
174
  telemetry?: TelemetryClient;
171
175
  blobClient: BlobClientInterface;
@@ -184,6 +188,8 @@ export class SequencerPublisher {
184
188
  ) {
185
189
  this.log = deps.log ?? createLogger('sequencer:publisher');
186
190
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
191
+ this.aztecSlotDuration = BigInt(config.aztecSlotDuration);
192
+ this.dateProvider = deps.dateProvider;
187
193
  this.epochCache = deps.epochCache;
188
194
  this.lastActions = deps.lastActions;
189
195
 
@@ -285,7 +291,7 @@ export class SequencerPublisher {
285
291
  }
286
292
 
287
293
  public getCurrentL2Slot(): SlotNumber {
288
- return this.epochCache.getEpochAndSlotNow().slot;
294
+ return this.epochCache.getSlotNow();
289
295
  }
290
296
 
291
297
  /**
@@ -398,8 +404,8 @@ export class SequencerPublisher {
398
404
  // @note - we can only have one blob config per bundle
399
405
  // find requests with gas and blob configs
400
406
  // See https://github.com/AztecProtocol/aztec-packages/issues/11513
401
- const gasConfigs = requestsToProcess.filter(request => request.gasConfig).map(request => request.gasConfig);
402
- const blobConfigs = requestsToProcess.filter(request => request.blobConfig).map(request => request.blobConfig);
407
+ const gasConfigs = validRequests.filter(request => request.gasConfig).map(request => request.gasConfig);
408
+ const blobConfigs = validRequests.filter(request => request.blobConfig).map(request => request.blobConfig);
403
409
 
404
410
  if (blobConfigs.length > 1) {
405
411
  throw new Error('Multiple blob configs found');
@@ -547,7 +553,16 @@ export class SequencerPublisher {
547
553
  });
548
554
  return { failedActions: requests.map(r => r.action) };
549
555
  } else {
550
- this.log.verbose(`Published bundled transactions (${actionsListStr})`, { result, requests });
556
+ this.log.verbose(`Published bundled transactions (${actionsListStr})`, {
557
+ result,
558
+ requests: requests.map(r => ({
559
+ ...r,
560
+ // Avoid logging large blob data
561
+ blobConfig: r.blobConfig
562
+ ? { ...r.blobConfig, blobs: r.blobConfig.blobs.map(b => ({ size: trimmedBytesLength(b) })) }
563
+ : undefined,
564
+ })),
565
+ });
551
566
  const successfulActions: Action[] = [];
552
567
  const failedActions: Action[] = [];
553
568
  for (const request of requests) {
@@ -586,20 +601,24 @@ export class SequencerPublisher {
586
601
  }
587
602
 
588
603
  /**
589
- * @notice Will call `canProposeAtNextEthBlock` to make sure that it is possible to propose
604
+ * @notice Will call `canProposeAt` to make sure that it is possible to propose
590
605
  * @param tipArchive - The archive to check
591
606
  * @returns The slot and block number if it is possible to propose, undefined otherwise
592
607
  */
593
- public canProposeAtNextEthBlock(
608
+ public canProposeAt(
594
609
  tipArchive: Fr,
595
610
  msgSender: EthAddress,
596
- opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
611
+ opts: { forcePendingCheckpointNumber?: CheckpointNumber; pipelined?: boolean } = {},
597
612
  ) {
598
613
  // TODO: #14291 - should loop through multiple keys to check if any of them can propose
599
614
  const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
600
615
 
616
+ const pipelined = opts.pipelined ?? this.epochCache.isProposerPipeliningEnabled();
617
+ const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
618
+ const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
619
+
601
620
  return this.rollupContract
602
- .canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
621
+ .canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
603
622
  forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
604
623
  })
605
624
  .catch(err => {
@@ -613,6 +632,7 @@ export class SequencerPublisher {
613
632
  return undefined;
614
633
  });
615
634
  }
635
+
616
636
  /**
617
637
  * @notice Will simulate `validateHeader` to make sure that the block header is valid
618
638
  * @dev This is a convenience function that can be used by the sequencer to validate a "partial" header.
@@ -636,7 +656,7 @@ export class SequencerPublisher {
636
656
  flags,
637
657
  ] as const;
638
658
 
639
- const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
659
+ const ts = this.getNextL1SlotTimestamp();
640
660
  const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
641
661
  opts?.forcePendingCheckpointNumber,
642
662
  );
@@ -801,7 +821,9 @@ export class SequencerPublisher {
801
821
  attestationsAndSignersSignature: Signature,
802
822
  options: { forcePendingCheckpointNumber?: CheckpointNumber },
803
823
  ): Promise<bigint> {
804
- const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
824
+ // Anchor the simulation timestamp to the checkpoint's own slot start time
825
+ // rather than the current L1 block timestamp, which may overshoot into the next slot if the build ran late.
826
+ const ts = checkpoint.header.timestamp;
805
827
  const blobFields = checkpoint.toBlobFields();
806
828
  const blobs = await getBlobsPerL1Block(blobFields);
807
829
  const blobInput = getPrefixedEthBlobCommitments(blobs);
@@ -1567,4 +1589,10 @@ export class SequencerPublisher {
1567
1589
  },
1568
1590
  });
1569
1591
  }
1592
+
1593
+ /** Returns the timestamp to use when simulating L1 proposal calls */
1594
+ private getNextL1SlotTimestamp(): bigint {
1595
+ const l1Constants = this.epochCache.getL1Constants();
1596
+ return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
1597
+ }
1570
1598
  }