@aztec/sequencer-client 0.47.1 → 0.49.2

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 (65) hide show
  1. package/dest/block_builder/index.d.ts +26 -0
  2. package/dest/block_builder/index.d.ts.map +1 -0
  3. package/dest/block_builder/index.js +40 -0
  4. package/dest/client/sequencer-client.d.ts +1 -2
  5. package/dest/client/sequencer-client.d.ts.map +1 -1
  6. package/dest/client/sequencer-client.js +5 -4
  7. package/dest/config.d.ts +6 -2
  8. package/dest/config.d.ts.map +1 -1
  9. package/dest/config.js +91 -31
  10. package/dest/global_variable_builder/global_builder.d.ts +14 -8
  11. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  12. package/dest/global_variable_builder/global_builder.js +10 -16
  13. package/dest/global_variable_builder/index.d.ts +2 -3
  14. package/dest/global_variable_builder/index.d.ts.map +1 -1
  15. package/dest/global_variable_builder/index.js +1 -1
  16. package/dest/global_variable_builder/viem-reader.d.ts +5 -4
  17. package/dest/global_variable_builder/viem-reader.d.ts.map +1 -1
  18. package/dest/global_variable_builder/viem-reader.js +11 -8
  19. package/dest/publisher/config.d.ts +7 -15
  20. package/dest/publisher/config.d.ts.map +1 -1
  21. package/dest/publisher/config.js +38 -11
  22. package/dest/publisher/index.d.ts +3 -2
  23. package/dest/publisher/index.d.ts.map +1 -1
  24. package/dest/publisher/index.js +4 -4
  25. package/dest/publisher/l1-publisher-metrics.d.ts +17 -0
  26. package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -0
  27. package/dest/publisher/l1-publisher-metrics.js +75 -0
  28. package/dest/publisher/l1-publisher.d.ts +33 -5
  29. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  30. package/dest/publisher/l1-publisher.js +44 -36
  31. package/dest/publisher/viem-tx-sender.d.ts +9 -2
  32. package/dest/publisher/viem-tx-sender.d.ts.map +1 -1
  33. package/dest/publisher/viem-tx-sender.js +85 -16
  34. package/dest/receiver.d.ts +2 -8
  35. package/dest/receiver.d.ts.map +1 -1
  36. package/dest/sequencer/metrics.d.ts +17 -0
  37. package/dest/sequencer/metrics.d.ts.map +1 -0
  38. package/dest/sequencer/metrics.js +56 -0
  39. package/dest/sequencer/sequencer.d.ts +9 -7
  40. package/dest/sequencer/sequencer.d.ts.map +1 -1
  41. package/dest/sequencer/sequencer.js +66 -37
  42. package/dest/tx_validator/gas_validator.d.ts +1 -1
  43. package/dest/tx_validator/gas_validator.js +11 -11
  44. package/dest/tx_validator/tx_validator_factory.js +2 -2
  45. package/package.json +17 -15
  46. package/src/block_builder/index.ts +51 -0
  47. package/src/client/sequencer-client.ts +3 -4
  48. package/src/config.ts +106 -54
  49. package/src/global_variable_builder/global_builder.ts +35 -25
  50. package/src/global_variable_builder/index.ts +3 -3
  51. package/src/global_variable_builder/viem-reader.ts +14 -11
  52. package/src/publisher/config.ts +43 -31
  53. package/src/publisher/index.ts +5 -3
  54. package/src/publisher/l1-publisher-metrics.ts +108 -0
  55. package/src/publisher/l1-publisher.ts +78 -43
  56. package/src/publisher/viem-tx-sender.ts +89 -14
  57. package/src/receiver.ts +3 -8
  58. package/src/sequencer/metrics.ts +86 -0
  59. package/src/sequencer/sequencer.ts +89 -52
  60. package/src/tx_validator/gas_validator.ts +9 -9
  61. package/src/tx_validator/tx_validator_factory.ts +2 -2
  62. package/dest/global_variable_builder/config.d.ts +0 -19
  63. package/dest/global_variable_builder/config.d.ts.map +0 -1
  64. package/dest/global_variable_builder/config.js +0 -2
  65. package/src/global_variable_builder/config.ts +0 -20
package/src/config.ts CHANGED
@@ -1,14 +1,24 @@
1
1
  import { type AllowedElement } from '@aztec/circuit-types';
2
2
  import { AztecAddress, Fr, FunctionSelector, getContractClassFromArtifact } from '@aztec/circuits.js';
3
- import { getL1ContractAddressesFromEnv } from '@aztec/ethereum';
3
+ import { type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum';
4
+ import {
5
+ type ConfigMappingsType,
6
+ booleanConfigHelper,
7
+ getConfigFromMappings,
8
+ numberConfigHelper,
9
+ } from '@aztec/foundation/config';
4
10
  import { EthAddress } from '@aztec/foundation/eth-address';
5
11
  import { FPCContract } from '@aztec/noir-contracts.js/FPC';
6
12
  import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token';
7
13
  import { AuthRegistryAddress } from '@aztec/protocol-contracts/auth-registry';
8
- import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';
14
+ import { FeeJuiceAddress } from '@aztec/protocol-contracts/fee-juice';
9
15
 
10
- import { type GlobalReaderConfig } from './global_variable_builder/index.js';
11
- import { type PublisherConfig, type TxSenderConfig, getTxSenderConfigFromEnv } from './publisher/config.js';
16
+ import {
17
+ type PublisherConfig,
18
+ type TxSenderConfig,
19
+ getPublisherConfigMappings,
20
+ getTxSenderConfigMappings,
21
+ } from './publisher/config.js';
12
22
  import { type SequencerConfig } from './sequencer/config.js';
13
23
 
14
24
  /** Chain configuration. */
@@ -22,60 +32,102 @@ type ChainConfig = {
22
32
  /**
23
33
  * Configuration settings for the SequencerClient.
24
34
  */
25
- export type SequencerClientConfig = PublisherConfig &
26
- TxSenderConfig &
27
- SequencerConfig &
28
- GlobalReaderConfig &
29
- ChainConfig;
35
+ export type SequencerClientConfig = PublisherConfig & TxSenderConfig & SequencerConfig & L1ReaderConfig & ChainConfig;
36
+
37
+ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
38
+ transactionPollingIntervalMS: {
39
+ env: 'SEQ_TX_POLLING_INTERVAL_MS',
40
+ description: 'The number of ms to wait between polling for pending txs.',
41
+ ...numberConfigHelper(1_000),
42
+ },
43
+ maxTxsPerBlock: {
44
+ env: 'SEQ_MAX_TX_PER_BLOCK',
45
+ description: 'The maximum number of txs to include in a block.',
46
+ ...numberConfigHelper(32),
47
+ },
48
+ minTxsPerBlock: {
49
+ env: 'SEQ_MIN_TX_PER_BLOCK',
50
+ description: 'The minimum number of txs to include in a block.',
51
+ ...numberConfigHelper(1),
52
+ },
53
+ minSecondsBetweenBlocks: {
54
+ env: 'SEQ_MIN_SECONDS_BETWEEN_BLOCKS',
55
+ description: 'The minimum number of seconds in-between consecutive blocks.',
56
+ ...numberConfigHelper(0),
57
+ },
58
+ maxSecondsBetweenBlocks: {
59
+ env: 'SEQ_MAX_SECONDS_BETWEEN_BLOCKS',
60
+ description:
61
+ 'The maximum number of seconds in-between consecutive blocks. Sequencer will produce a block with less than minTxsPerBlock once this threshold is reached.',
62
+ ...numberConfigHelper(0),
63
+ },
64
+ coinbase: {
65
+ env: 'COINBASE',
66
+ parseEnv: (val: string) => EthAddress.fromString(val),
67
+ description: 'Recipient of block reward.',
68
+ },
69
+ feeRecipient: {
70
+ env: 'FEE_RECIPIENT',
71
+ parseEnv: (val: string) => AztecAddress.fromString(val),
72
+ description: 'Address to receive fees.',
73
+ },
74
+ acvmWorkingDirectory: {
75
+ env: 'ACVM_WORKING_DIRECTORY',
76
+ description: 'The working directory to use for simulation/proving',
77
+ },
78
+ acvmBinaryPath: {
79
+ env: 'ACVM_BINARY_PATH',
80
+ description: 'The path to the ACVM binary',
81
+ },
82
+ allowedInSetup: {
83
+ env: 'SEQ_ALLOWED_SETUP_FN',
84
+ parseEnv: (val: string) => parseSequencerAllowList(val),
85
+ defaultValue: getDefaultAllowedSetupFunctions(),
86
+ description: 'The list of functions calls allowed to run in setup',
87
+ printDefault: () =>
88
+ 'AuthRegistry, FeeJuice.increase_public_balance, Token.increase_public_balance, FPC.prepare_fee',
89
+ },
90
+ allowedInTeardown: {
91
+ env: 'SEQ_ALLOWED_TEARDOWN_FN',
92
+ parseEnv: (val: string) => parseSequencerAllowList(val),
93
+ defaultValue: getDefaultAllowedTeardownFunctions(),
94
+ description: 'The list of functions calls allowed to run teardown',
95
+ printDefault: () => 'FPC.pay_refund, FPC.pay_refund_with_shielded_rebate',
96
+ },
97
+ maxBlockSizeInBytes: {
98
+ env: 'SEQ_MAX_BLOCK_SIZE_IN_BYTES',
99
+ description: 'Max block size',
100
+ ...numberConfigHelper(1024 * 1024),
101
+ },
102
+ enforceFees: {
103
+ env: 'ENFORCE_FEES',
104
+ description: 'Whether to require every tx to have a fee payer',
105
+ ...booleanConfigHelper(),
106
+ },
107
+ };
108
+
109
+ export const chainConfigMappings: ConfigMappingsType<ChainConfig> = {
110
+ l1ChainId: l1ReaderConfigMappings.l1ChainId,
111
+ version: {
112
+ env: 'VERSION',
113
+ description: 'The version of the rollup.',
114
+ ...numberConfigHelper(1),
115
+ },
116
+ };
117
+
118
+ export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig> = {
119
+ ...sequencerConfigMappings,
120
+ ...getTxSenderConfigMappings('SEQ'),
121
+ ...getPublisherConfigMappings('SEQ'),
122
+ ...l1ReaderConfigMappings,
123
+ ...chainConfigMappings,
124
+ };
30
125
 
31
126
  /**
32
127
  * Creates an instance of SequencerClientConfig out of environment variables using sensible defaults for integration testing if not set.
33
128
  */
34
129
  export function getConfigEnvVars(): SequencerClientConfig {
35
- const {
36
- VERSION,
37
- SEQ_PUBLISH_RETRY_INTERVAL_MS,
38
- SEQ_TX_POLLING_INTERVAL_MS,
39
- SEQ_MAX_TX_PER_BLOCK,
40
- SEQ_MIN_TX_PER_BLOCK,
41
- SEQ_MAX_SECONDS_BETWEEN_BLOCKS,
42
- SEQ_MIN_SECONDS_BETWEEN_BLOCKS,
43
- SEQ_ALLOWED_SETUP_FN,
44
- SEQ_ALLOWED_TEARDOWN_FN,
45
- SEQ_MAX_BLOCK_SIZE_IN_BYTES,
46
- SEQ_SKIP_SUBMIT_PROOFS,
47
- COINBASE,
48
- FEE_RECIPIENT,
49
- ACVM_WORKING_DIRECTORY,
50
- ACVM_BINARY_PATH,
51
- ENFORCE_FEES = '',
52
- } = process.env;
53
-
54
- return {
55
- enforceFees: ['1', 'true'].includes(ENFORCE_FEES),
56
- version: VERSION ? +VERSION : 1, // 1 is our default version
57
- l1PublishRetryIntervalMS: SEQ_PUBLISH_RETRY_INTERVAL_MS ? +SEQ_PUBLISH_RETRY_INTERVAL_MS : 1_000,
58
- transactionPollingIntervalMS: SEQ_TX_POLLING_INTERVAL_MS ? +SEQ_TX_POLLING_INTERVAL_MS : 1_000,
59
- maxBlockSizeInBytes: SEQ_MAX_BLOCK_SIZE_IN_BYTES ? +SEQ_MAX_BLOCK_SIZE_IN_BYTES : undefined,
60
- l1Contracts: getL1ContractAddressesFromEnv(),
61
- maxTxsPerBlock: SEQ_MAX_TX_PER_BLOCK ? +SEQ_MAX_TX_PER_BLOCK : 32,
62
- minTxsPerBlock: SEQ_MIN_TX_PER_BLOCK ? +SEQ_MIN_TX_PER_BLOCK : 1,
63
- maxSecondsBetweenBlocks: SEQ_MAX_SECONDS_BETWEEN_BLOCKS ? +SEQ_MAX_SECONDS_BETWEEN_BLOCKS : 0,
64
- minSecondsBetweenBlocks: SEQ_MIN_SECONDS_BETWEEN_BLOCKS ? +SEQ_MIN_SECONDS_BETWEEN_BLOCKS : 0,
65
- sequencerSkipSubmitProofs: ['1', 'true'].includes(SEQ_SKIP_SUBMIT_PROOFS ?? ''),
66
- // TODO: undefined should not be allowed for the following 2 values in PROD
67
- coinbase: COINBASE ? EthAddress.fromString(COINBASE) : undefined,
68
- feeRecipient: FEE_RECIPIENT ? AztecAddress.fromString(FEE_RECIPIENT) : undefined,
69
- acvmWorkingDirectory: ACVM_WORKING_DIRECTORY ? ACVM_WORKING_DIRECTORY : undefined,
70
- acvmBinaryPath: ACVM_BINARY_PATH ? ACVM_BINARY_PATH : undefined,
71
- allowedInSetup: SEQ_ALLOWED_SETUP_FN
72
- ? parseSequencerAllowList(SEQ_ALLOWED_SETUP_FN)
73
- : getDefaultAllowedSetupFunctions(),
74
- allowedInTeardown: SEQ_ALLOWED_TEARDOWN_FN
75
- ? parseSequencerAllowList(SEQ_ALLOWED_TEARDOWN_FN)
76
- : getDefaultAllowedTeardownFunctions(),
77
- ...getTxSenderConfigFromEnv('SEQ'),
78
- };
130
+ return getConfigFromMappings<SequencerClientConfig>(sequencerClientConfigMappings);
79
131
  }
80
132
 
81
133
  /**
@@ -136,7 +188,7 @@ function getDefaultAllowedSetupFunctions(): AllowedElement[] {
136
188
  },
137
189
  // needed for claiming on the same tx as a spend
138
190
  {
139
- address: GasTokenAddress,
191
+ address: FeeJuiceAddress,
140
192
  selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
141
193
  },
142
194
  // needed for private transfers via FPC
@@ -1,4 +1,10 @@
1
- import { type AztecAddress, type EthAddress, GasFees, GlobalVariables } from '@aztec/circuits.js';
1
+ import {
2
+ type AztecAddress,
3
+ ETHEREUM_SLOT_DURATION,
4
+ type EthAddress,
5
+ GasFees,
6
+ GlobalVariables,
7
+ } from '@aztec/circuits.js';
2
8
  import { Fr } from '@aztec/foundation/fields';
3
9
  import { createDebugLogger } from '@aztec/foundation/log';
4
10
 
@@ -6,11 +12,6 @@ import { createDebugLogger } from '@aztec/foundation/log';
6
12
  * Reads values from L1 state that is used for the global values.
7
13
  */
8
14
  export interface L1GlobalReader {
9
- /**
10
- * Fetches the last timestamp that a block was processed by the contract.
11
- * @returns The last timestamp that a block was processed by the contract.
12
- */
13
- getLastTimestamp(): Promise<bigint>;
14
15
  /**
15
16
  * Fetches the version of the rollup contract.
16
17
  * @returns The version of the rollup contract.
@@ -29,10 +30,23 @@ export interface L1GlobalReader {
29
30
  getL1CurrentTime(): Promise<bigint>;
30
31
 
31
32
  /**
32
- * Gets the last time L2 was warped as tracked by the rollup contract.
33
- * @returns The warped time.
33
+ * Gets the current slot.
34
+ * @returns The current slot.
34
35
  */
35
- getLastWarpedBlockTs(): Promise<bigint>;
36
+ getCurrentSlot(): Promise<bigint>;
37
+
38
+ /**
39
+ * Get the slot for a specific timestamp.
40
+ * @param timestamp - The timestamp to get the slot for.
41
+ */
42
+ getSlotAt(timestamp: readonly [bigint]): Promise<bigint>;
43
+
44
+ /**
45
+ * Gets the timestamp for a slot
46
+ * @param slot - The slot to get the timestamp for.
47
+ * @returns The timestamp for the slot.
48
+ */
49
+ getTimestampForSlot(slot: readonly [bigint]): Promise<bigint>;
36
50
  }
37
51
 
38
52
  /**
@@ -69,34 +83,30 @@ export class SimpleTestGlobalVariableBuilder implements GlobalVariableBuilder {
69
83
  coinbase: EthAddress,
70
84
  feeRecipient: AztecAddress,
71
85
  ): Promise<GlobalVariables> {
72
- let lastTimestamp = new Fr(await this.reader.getLastTimestamp());
73
- const version = new Fr(await this.reader.getVersion());
74
- const chainId = new Fr(await this.reader.getChainId());
86
+ // Not just the current slot, the slot of the next block.
87
+ const ts = (await this.reader.getL1CurrentTime()) + BigInt(ETHEREUM_SLOT_DURATION);
75
88
 
76
- // TODO(rahul) - fix #1614. By using the cheatcode warp to modify L2 time,
77
- // txs in the next rollup would have same time as the txs in the current rollup (i.e. the rollup that was warped).
78
- // So, for now you check if L2 time was warped and if so, serve warpedTime + 1 to txs in the new rollup.
79
- // Check if L2 time was warped in the last rollup by checking if current L1 time is same as the warpedTime (stored on the rollup contract).
80
- // more details at https://github.com/AztecProtocol/aztec-packages/issues/1614
89
+ const slot = await this.reader.getSlotAt([ts]);
90
+ const timestamp = await this.reader.getTimestampForSlot([slot]);
81
91
 
82
- const currTimestamp = await this.reader.getL1CurrentTime();
83
- const rollupWarpTime = await this.reader.getLastWarpedBlockTs();
84
- const isLastBlockWarped = rollupWarpTime === currTimestamp;
85
- if (isLastBlockWarped) {
86
- lastTimestamp = new Fr(lastTimestamp.value + 1n);
87
- }
92
+ const slotFr = new Fr(slot);
93
+ const timestampFr = new Fr(timestamp);
94
+
95
+ const version = new Fr(await this.reader.getVersion());
96
+ const chainId = new Fr(await this.reader.getChainId());
88
97
 
89
98
  const gasFees = GasFees.default();
90
99
  const globalVariables = new GlobalVariables(
91
100
  chainId,
92
101
  version,
93
102
  blockNumber,
94
- lastTimestamp,
103
+ slotFr,
104
+ timestampFr,
95
105
  coinbase,
96
106
  feeRecipient,
97
107
  gasFees,
98
108
  );
99
109
  this.log.debug(`Built global variables for block ${blockNumber}`, globalVariables.toJSON());
100
- return new GlobalVariables(chainId, version, blockNumber, lastTimestamp, coinbase, feeRecipient, gasFees);
110
+ return globalVariables;
101
111
  }
102
112
  }
@@ -1,9 +1,9 @@
1
- import { type GlobalReaderConfig } from './config.js';
1
+ import { type L1ReaderConfig } from '@aztec/ethereum';
2
+
2
3
  import { type GlobalVariableBuilder, SimpleTestGlobalVariableBuilder } from './global_builder.js';
3
4
  import { ViemReader } from './viem-reader.js';
4
5
 
5
6
  export { SimpleTestGlobalVariableBuilder as SimpleGlobalVariableBuilder } from './global_builder.js';
6
- export { GlobalReaderConfig } from './config.js';
7
7
  export { GlobalVariableBuilder } from './global_builder.js';
8
8
 
9
9
  /**
@@ -11,6 +11,6 @@ export { GlobalVariableBuilder } from './global_builder.js';
11
11
  * @param config - Configuration to initialize the builder.
12
12
  * @returns A new instance of the global variable builder.
13
13
  */
14
- export function getGlobalVariableBuilder(config: GlobalReaderConfig): GlobalVariableBuilder {
14
+ export function getGlobalVariableBuilder(config: L1ReaderConfig): GlobalVariableBuilder {
15
15
  return new SimpleTestGlobalVariableBuilder(new ViemReader(config));
16
16
  }
@@ -1,4 +1,4 @@
1
- import { createEthereumChain } from '@aztec/ethereum';
1
+ import { type L1ReaderConfig, createEthereumChain } from '@aztec/ethereum';
2
2
  import { RollupAbi } from '@aztec/l1-artifacts';
3
3
 
4
4
  import {
@@ -12,7 +12,6 @@ import {
12
12
  } from 'viem';
13
13
  import type * as chains from 'viem/chains';
14
14
 
15
- import { type GlobalReaderConfig } from './config.js';
16
15
  import { type L1GlobalReader } from './global_builder.js';
17
16
 
18
17
  /**
@@ -22,10 +21,10 @@ export class ViemReader implements L1GlobalReader {
22
21
  private rollupContract: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, chains.Chain>>;
23
22
  private publicClient: PublicClient<HttpTransport, chains.Chain>;
24
23
 
25
- constructor(config: GlobalReaderConfig) {
26
- const { rpcUrl, l1ChainId: chainId, l1Contracts } = config;
24
+ constructor(config: L1ReaderConfig) {
25
+ const { l1RpcUrl, l1ChainId: chainId, l1Contracts } = config;
27
26
 
28
- const chain = createEthereumChain(rpcUrl, chainId);
27
+ const chain = createEthereumChain(l1RpcUrl, chainId);
29
28
 
30
29
  this.publicClient = createPublicClient({
31
30
  chain: chain.chainInfo,
@@ -39,10 +38,6 @@ export class ViemReader implements L1GlobalReader {
39
38
  });
40
39
  }
41
40
 
42
- public async getLastTimestamp(): Promise<bigint> {
43
- return BigInt(await this.rollupContract.read.lastBlockTs());
44
- }
45
-
46
41
  public async getVersion(): Promise<bigint> {
47
42
  return BigInt(await this.rollupContract.read.VERSION());
48
43
  }
@@ -55,7 +50,15 @@ export class ViemReader implements L1GlobalReader {
55
50
  return await Promise.resolve((await this.publicClient.getBlock()).timestamp);
56
51
  }
57
52
 
58
- public async getLastWarpedBlockTs(): Promise<bigint> {
59
- return BigInt(await this.rollupContract.read.lastWarpedBlockTs());
53
+ public async getCurrentSlot(): Promise<bigint> {
54
+ return BigInt(await this.rollupContract.read.getCurrentSlot());
55
+ }
56
+
57
+ public async getSlotAt(timestamp: readonly [bigint]): Promise<bigint> {
58
+ return BigInt(await this.rollupContract.read.getSlotAt(timestamp));
59
+ }
60
+
61
+ public async getTimestampForSlot(slot: readonly [bigint]): Promise<bigint> {
62
+ return BigInt(await this.rollupContract.read.getTimestampForSlot(slot));
60
63
  }
61
64
  }
@@ -1,36 +1,20 @@
1
- import { type L1ContractAddresses, NULL_KEY } from '@aztec/ethereum';
2
-
3
- import { type Hex } from 'viem';
1
+ import { type L1ReaderConfig, NULL_KEY } from '@aztec/ethereum';
2
+ import { type ConfigMappingsType, getConfigFromMappings } from '@aztec/foundation/config';
4
3
 
5
4
  /**
6
5
  * The configuration of the rollup transaction publisher.
7
6
  */
8
- export interface TxSenderConfig {
7
+ export type TxSenderConfig = L1ReaderConfig & {
9
8
  /**
10
9
  * The private key to be used by the publisher.
11
10
  */
12
11
  publisherPrivateKey: `0x${string}`;
13
12
 
14
- /**
15
- * The RPC Url of the ethereum host.
16
- */
17
- rpcUrl: string;
18
-
19
- /**
20
- * The chain ID of the ethereum host.
21
- */
22
- l1ChainId: number;
23
-
24
13
  /**
25
14
  * The number of confirmations required.
26
15
  */
27
16
  requiredConfirmations: number;
28
-
29
- /**
30
- * The deployed l1 contract addresses
31
- */
32
- l1Contracts: L1ContractAddresses;
33
- }
17
+ };
34
18
 
35
19
  /**
36
20
  * Configuration of the L1Publisher.
@@ -42,18 +26,46 @@ export interface PublisherConfig {
42
26
  l1PublishRetryIntervalMS: number;
43
27
  }
44
28
 
45
- export function getTxSenderConfigFromEnv(scope: 'PROVER' | 'SEQ'): Omit<TxSenderConfig, 'l1Contracts'> {
46
- const { ETHEREUM_HOST, L1_CHAIN_ID } = process.env;
29
+ export const getTxSenderConfigMappings: (
30
+ scope: 'PROVER' | 'SEQ',
31
+ ) => ConfigMappingsType<Omit<TxSenderConfig, 'l1Contracts'>> = (scope: 'PROVER' | 'SEQ') => ({
32
+ l1RpcUrl: {
33
+ env: 'ETHEREUM_HOST',
34
+ description: 'The RPC Url of the ethereum host.',
35
+ },
36
+ l1ChainId: {
37
+ env: 'L1_CHAIN_ID',
38
+ parseEnv: (val: string) => +val,
39
+ defaultValue: 31337,
40
+ description: 'The chain ID of the ethereum host.',
41
+ },
42
+ publisherPrivateKey: {
43
+ env: `${scope}_PUBLISHER_PRIVATE_KEY`,
44
+ description: 'The private key to be used by the publisher.',
45
+ parseEnv: (val: string) => (val ? `0x${val.replace('0x', '')}` : NULL_KEY),
46
+ defaultValue: NULL_KEY,
47
+ },
48
+ requiredConfirmations: {
49
+ env: `${scope}_REQUIRED_CONFIRMATIONS`,
50
+ parseEnv: (val: string) => +val,
51
+ defaultValue: 1,
52
+ description: 'The number of confirmations required.',
53
+ },
54
+ });
47
55
 
48
- const PUBLISHER_PRIVATE_KEY = process.env[`${scope}_PUBLISHER_PRIVATE_KEY`];
49
- const REQUIRED_CONFIRMATIONS = process.env[`${scope}_REQUIRED_CONFIRMATIONS`];
56
+ export function getTxSenderConfigFromEnv(scope: 'PROVER' | 'SEQ'): Omit<TxSenderConfig, 'l1Contracts'> {
57
+ return getConfigFromMappings(getTxSenderConfigMappings(scope));
58
+ }
50
59
 
51
- const publisherPrivateKey: Hex = PUBLISHER_PRIVATE_KEY ? `0x${PUBLISHER_PRIVATE_KEY.replace('0x', '')}` : NULL_KEY;
60
+ export const getPublisherConfigMappings: (scope: 'PROVER' | 'SEQ') => ConfigMappingsType<PublisherConfig> = scope => ({
61
+ l1PublishRetryIntervalMS: {
62
+ env: `${scope}_PUBLISH_RETRY_INTERVAL_MS`,
63
+ parseEnv: (val: string) => +val,
64
+ defaultValue: 1000,
65
+ description: 'The interval to wait between publish retries.',
66
+ },
67
+ });
52
68
 
53
- return {
54
- rpcUrl: ETHEREUM_HOST ? ETHEREUM_HOST : '',
55
- requiredConfirmations: REQUIRED_CONFIRMATIONS ? +REQUIRED_CONFIRMATIONS : 1,
56
- publisherPrivateKey,
57
- l1ChainId: L1_CHAIN_ID ? +L1_CHAIN_ID : 31337,
58
- };
69
+ export function getPublisherConfigFromEnv(scope: 'PROVER' | 'SEQ'): PublisherConfig {
70
+ return getConfigFromMappings(getPublisherConfigMappings(scope));
59
71
  }
@@ -1,14 +1,16 @@
1
+ import { type TelemetryClient } from '@aztec/telemetry-client';
2
+
1
3
  import { type PublisherConfig, type TxSenderConfig } from './config.js';
2
4
  import { L1Publisher } from './l1-publisher.js';
3
5
  import { ViemTxSender } from './viem-tx-sender.js';
4
6
 
5
7
  export { L1Publisher } from './l1-publisher.js';
6
- export { PublisherConfig, TxSenderConfig, getTxSenderConfigFromEnv } from './config.js';
8
+ export * from './config.js';
7
9
 
8
10
  /**
9
11
  * Returns a new instance of the L1Publisher.
10
12
  * @param config - Configuration to initialize the new instance.
11
13
  */
12
- export function getL1Publisher(config: PublisherConfig & TxSenderConfig): L1Publisher {
13
- return new L1Publisher(new ViemTxSender(config), config);
14
+ export function getL1Publisher(config: PublisherConfig & TxSenderConfig, client: TelemetryClient): L1Publisher {
15
+ return new L1Publisher(new ViemTxSender(config), client, config);
14
16
  }
@@ -0,0 +1,108 @@
1
+ import type { L1PublishBlockStats, L1PublishProofStats } from '@aztec/circuit-types/stats';
2
+ import {
3
+ Attributes,
4
+ type Histogram,
5
+ Metrics,
6
+ type TelemetryClient,
7
+ type UpDownCounter,
8
+ ValueType,
9
+ } from '@aztec/telemetry-client';
10
+
11
+ import { formatEther } from 'viem/utils';
12
+
13
+ export type L1TxType = 'submitProof' | 'process';
14
+
15
+ export class L1PublisherMetrics {
16
+ private gasPrice: Histogram;
17
+
18
+ private txCount: UpDownCounter;
19
+ private txDuration: Histogram;
20
+ private txGas: Histogram;
21
+ private txCalldataSize: Histogram;
22
+ private txCalldataGas: Histogram;
23
+
24
+ constructor(client: TelemetryClient, name = 'L1Publisher') {
25
+ const meter = client.getMeter(name);
26
+
27
+ this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
28
+ description: 'The gas price used for transactions',
29
+ unit: 'gwei',
30
+ valueType: ValueType.DOUBLE,
31
+ });
32
+
33
+ this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
34
+ description: 'The number of transactions processed',
35
+ });
36
+
37
+ this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
38
+ description: 'The duration of transaction processing',
39
+ unit: 'ms',
40
+ valueType: ValueType.INT,
41
+ advice: {
42
+ explicitBucketBoundaries: [10, 50, 100, 200, 500, 1000, 2000, 5000, 10000],
43
+ },
44
+ });
45
+
46
+ this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
47
+ description: 'The gas consumed by transactions',
48
+ unit: 'gas',
49
+ valueType: ValueType.INT,
50
+ });
51
+
52
+ this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
53
+ description: 'The size of the calldata in transactions',
54
+ unit: 'bytes',
55
+ valueType: ValueType.INT,
56
+ advice: {
57
+ explicitBucketBoundaries: [0, 100, 200, 500, 1000, 2000, 5000, 10000],
58
+ },
59
+ });
60
+
61
+ this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
62
+ description: 'The gas consumed by the calldata in transactions',
63
+ unit: 'gas',
64
+ valueType: ValueType.INT,
65
+ });
66
+ }
67
+
68
+ recordFailedTx(txType: L1TxType) {
69
+ this.txCount.add(1, {
70
+ [Attributes.L1_TX_TYPE]: txType,
71
+ [Attributes.OK]: false,
72
+ });
73
+ }
74
+
75
+ recordSubmitProof(durationMs: number, stats: L1PublishProofStats) {
76
+ this.recordTx('submitProof', durationMs, stats);
77
+ }
78
+
79
+ recordProcessBlockTx(durationMs: number, stats: L1PublishBlockStats) {
80
+ this.recordTx('process', durationMs, stats);
81
+ }
82
+
83
+ private recordTx(txType: L1TxType, durationMs: number, stats: Omit<L1PublishProofStats, 'eventName'>) {
84
+ const attributes = {
85
+ [Attributes.L1_TX_TYPE]: txType,
86
+ } as const;
87
+
88
+ this.txCount.add(1, {
89
+ ...attributes,
90
+ [Attributes.OK]: true,
91
+ });
92
+
93
+ this.txDuration.record(Math.ceil(durationMs), attributes);
94
+ this.txGas.record(
95
+ // safe to downcast - total block limit is 30M gas which fits in a JS number
96
+ Number(stats.gasUsed),
97
+ attributes,
98
+ );
99
+ this.txCalldataGas.record(stats.calldataGas, attributes);
100
+ this.txCalldataSize.record(stats.calldataSize, attributes);
101
+
102
+ try {
103
+ this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
104
+ } catch (e) {
105
+ // ignore
106
+ }
107
+ }
108
+ }