@aztec/sequencer-client 0.69.1 → 0.71.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.
Files changed (58) hide show
  1. package/dest/client/sequencer-client.d.ts +2 -0
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +4 -4
  4. package/dest/config.js +2 -2
  5. package/dest/publisher/index.d.ts +0 -1
  6. package/dest/publisher/index.d.ts.map +1 -1
  7. package/dest/publisher/index.js +1 -2
  8. package/dest/publisher/l1-publisher.d.ts +9 -3
  9. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  10. package/dest/publisher/l1-publisher.js +63 -77
  11. package/dest/sequencer/metrics.d.ts +2 -1
  12. package/dest/sequencer/metrics.d.ts.map +1 -1
  13. package/dest/sequencer/metrics.js +8 -2
  14. package/dest/sequencer/sequencer.d.ts +34 -35
  15. package/dest/sequencer/sequencer.d.ts.map +1 -1
  16. package/dest/sequencer/sequencer.js +62 -101
  17. package/dest/sequencer/timetable.d.ts +38 -0
  18. package/dest/sequencer/timetable.d.ts.map +1 -0
  19. package/dest/sequencer/timetable.js +96 -0
  20. package/dest/slasher/factory.d.ts.map +1 -1
  21. package/dest/slasher/factory.js +3 -3
  22. package/dest/slasher/slasher_client.d.ts.map +1 -1
  23. package/dest/slasher/slasher_client.js +3 -4
  24. package/dest/test/index.d.ts +17 -0
  25. package/dest/test/index.d.ts.map +1 -0
  26. package/dest/test/index.js +8 -0
  27. package/dest/{publisher → test}/test-l1-publisher.d.ts +1 -1
  28. package/dest/test/test-l1-publisher.d.ts.map +1 -0
  29. package/dest/test/test-l1-publisher.js +11 -0
  30. package/dest/tx_validator/archive_cache.d.ts +14 -0
  31. package/dest/tx_validator/archive_cache.d.ts.map +1 -0
  32. package/dest/tx_validator/archive_cache.js +22 -0
  33. package/dest/tx_validator/gas_validator.js +2 -2
  34. package/dest/tx_validator/phases_validator.js +2 -2
  35. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  36. package/dest/tx_validator/tx_validator_factory.js +9 -6
  37. package/package.json +22 -20
  38. package/src/client/sequencer-client.ts +6 -3
  39. package/src/config.ts +1 -1
  40. package/src/publisher/index.ts +0 -1
  41. package/src/publisher/l1-publisher.ts +84 -83
  42. package/src/sequencer/metrics.ts +11 -1
  43. package/src/sequencer/sequencer.ts +95 -141
  44. package/src/sequencer/timetable.ts +123 -0
  45. package/src/slasher/factory.ts +2 -3
  46. package/src/slasher/slasher_client.ts +2 -3
  47. package/src/test/index.ts +22 -0
  48. package/src/{publisher → test}/test-l1-publisher.ts +1 -1
  49. package/src/tx_validator/archive_cache.ts +27 -0
  50. package/src/tx_validator/gas_validator.ts +1 -1
  51. package/src/tx_validator/phases_validator.ts +1 -1
  52. package/src/tx_validator/tx_validator_factory.ts +8 -1
  53. package/dest/publisher/test-l1-publisher.d.ts.map +0 -1
  54. package/dest/publisher/test-l1-publisher.js +0 -11
  55. package/dest/publisher/utils.d.ts +0 -2
  56. package/dest/publisher/utils.d.ts.map +0 -1
  57. package/dest/publisher/utils.js +0 -13
  58. package/src/publisher/utils.ts +0 -14
package/src/config.ts CHANGED
@@ -47,7 +47,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
47
47
  transactionPollingIntervalMS: {
48
48
  env: 'SEQ_TX_POLLING_INTERVAL_MS',
49
49
  description: 'The number of ms to wait between polling for pending txs.',
50
- ...numberConfigHelper(1_000),
50
+ ...numberConfigHelper(500),
51
51
  },
52
52
  maxTxsPerBlock: {
53
53
  env: 'SEQ_MAX_TX_PER_BLOCK',
@@ -1,2 +1 @@
1
1
  export { L1Publisher, L1SubmitEpochProofArgs } from './l1-publisher.js';
2
- export * from './test-l1-publisher.js';
@@ -1,3 +1,4 @@
1
+ import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
1
2
  import {
2
3
  ConsensusPayload,
3
4
  type EpochProofClaim,
@@ -16,7 +17,14 @@ import {
16
17
  type Proof,
17
18
  } from '@aztec/circuits.js';
18
19
  import { type FeeRecipient, type RootRollupPublicInputs } from '@aztec/circuits.js/rollup';
19
- import { type EthereumChain, type L1ContractsConfig, L1TxUtils, createEthereumChain } from '@aztec/ethereum';
20
+ import {
21
+ type EthereumChain,
22
+ type GasPrice,
23
+ type L1ContractsConfig,
24
+ L1TxUtils,
25
+ createEthereumChain,
26
+ formatViemError,
27
+ } from '@aztec/ethereum';
20
28
  import { makeTuple } from '@aztec/foundation/array';
21
29
  import { toHex } from '@aztec/foundation/bigint-buffer';
22
30
  import { Blob } from '@aztec/foundation/blob';
@@ -28,7 +36,7 @@ import { type Tuple, serializeToBuffer } from '@aztec/foundation/serialize';
28
36
  import { InterruptibleSleep } from '@aztec/foundation/sleep';
29
37
  import { Timer } from '@aztec/foundation/timer';
30
38
  import { EmpireBaseAbi, RollupAbi, SlasherAbi } from '@aztec/l1-artifacts';
31
- import { type TelemetryClient } from '@aztec/telemetry-client';
39
+ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
32
40
 
33
41
  import pick from 'lodash.pick';
34
42
  import {
@@ -63,7 +71,6 @@ import { privateKeyToAccount } from 'viem/accounts';
63
71
 
64
72
  import { type PublisherConfig, type TxSenderConfig } from './config.js';
65
73
  import { L1PublisherMetrics } from './l1-publisher-metrics.js';
66
- import { prettyLogViemErrorMsg } from './utils.js';
67
74
 
68
75
  /**
69
76
  * Stats for a sent transaction.
@@ -117,6 +124,14 @@ type L1ProcessArgs = {
117
124
  attestations?: Signature[];
118
125
  };
119
126
 
127
+ type L1ProcessReturnType = {
128
+ receipt: TransactionReceipt | undefined;
129
+ args: any;
130
+ functionName: string;
131
+ data: Hex;
132
+ gasPrice: GasPrice;
133
+ };
134
+
120
135
  /** Arguments to the submitEpochProof method of the rollup contract */
121
136
  export type L1SubmitEpochProofArgs = {
122
137
  epochSize: number;
@@ -177,8 +192,7 @@ export class L1Publisher {
177
192
  protected account: PrivateKeyAccount;
178
193
  protected ethereumSlotDuration: bigint;
179
194
 
180
- private blobSinkUrl: string | undefined;
181
-
195
+ private blobSinkClient: BlobSinkClientInterface;
182
196
  // @note - with blobs, the below estimate seems too large.
183
197
  // Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
184
198
  // Total used for emptier block from above test: 429k (of which 84k is 1x blob)
@@ -189,12 +203,15 @@ export class L1Publisher {
189
203
 
190
204
  constructor(
191
205
  config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>,
192
- client: TelemetryClient,
206
+ deps: { telemetry?: TelemetryClient; blobSinkClient?: BlobSinkClientInterface } = {},
193
207
  ) {
194
208
  this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
195
209
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
196
- this.blobSinkUrl = config.blobSinkUrl;
197
- this.metrics = new L1PublisherMetrics(client, 'L1Publisher');
210
+
211
+ const telemetry = deps.telemetry ?? getTelemetryClient();
212
+ this.blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config.blobSinkUrl);
213
+
214
+ this.metrics = new L1PublisherMetrics(telemetry, 'L1Publisher');
198
215
 
199
216
  const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey, l1Contracts } = config;
200
217
  const chain = createEthereumChain(rpcUrl, chainId);
@@ -528,7 +545,7 @@ export class L1Publisher {
528
545
  account: this.account,
529
546
  });
530
547
  } catch (err) {
531
- const msg = prettyLogViemErrorMsg(err);
548
+ const msg = formatViemError(err);
532
549
  logger.error(`Failed to vote`, msg);
533
550
  this.myLastVotes[voteType] = cachedMyLastVote;
534
551
  return false;
@@ -557,6 +574,7 @@ export class L1Publisher {
557
574
  attestations?: Signature[],
558
575
  txHashes?: TxHash[],
559
576
  proofQuote?: EpochProofQuote,
577
+ opts: { txTimeoutAt?: Date } = {},
560
578
  ): Promise<boolean> {
561
579
  const ctx = {
562
580
  blockNumber: block.number,
@@ -598,15 +616,15 @@ export class L1Publisher {
598
616
 
599
617
  this.log.debug(`Submitting propose transaction`);
600
618
  const result = proofQuote
601
- ? await this.sendProposeAndClaimTx(proposeTxArgs, proofQuote)
602
- : await this.sendProposeTx(proposeTxArgs);
619
+ ? await this.sendProposeAndClaimTx(proposeTxArgs, proofQuote, opts)
620
+ : await this.sendProposeTx(proposeTxArgs, opts);
603
621
 
604
622
  if (!result?.receipt) {
605
623
  this.log.info(`Failed to publish block ${block.number} to L1`, ctx);
606
624
  return false;
607
625
  }
608
626
 
609
- const { receipt, args, functionName, data } = result;
627
+ const { receipt, args, functionName, data, gasPrice } = result;
610
628
 
611
629
  // Tx was mined successfully
612
630
  if (receipt.status === 'success') {
@@ -645,7 +663,7 @@ export class L1Publisher {
645
663
  {
646
664
  blobs: proposeTxArgs.blobs.map(b => b.dataWithZeros),
647
665
  kzg,
648
- maxFeePerBlobGas: 10000000000n,
666
+ maxFeePerBlobGas: gasPrice.maxFeePerBlobGas ?? 10000000000n,
649
667
  },
650
668
  );
651
669
  this.log.error(`Rollup process tx reverted. ${errorMsg}`, undefined, {
@@ -659,11 +677,10 @@ export class L1Publisher {
659
677
  /** Calls claimEpochProofRight in the Rollup contract to submit a chosen prover quote for the previous epoch. */
660
678
  public async claimEpochProofRight(proofQuote: EpochProofQuote) {
661
679
  const timer = new Timer();
662
-
663
- let receipt;
680
+ let result;
664
681
  try {
665
682
  this.log.debug(`Submitting claimEpochProofRight transaction`);
666
- receipt = await this.l1TxUtils.sendAndMonitorTransaction({
683
+ result = await this.l1TxUtils.sendAndMonitorTransaction({
667
684
  to: this.rollupContract.address,
668
685
  data: encodeFunctionData({
669
686
  abi: RollupAbi,
@@ -672,12 +689,14 @@ export class L1Publisher {
672
689
  }),
673
690
  });
674
691
  } catch (err) {
675
- this.log.error(`Failed to claim epoch proof right: ${prettyLogViemErrorMsg(err)}`, err, {
692
+ this.log.error(`Failed to claim epoch proof right`, err, {
676
693
  proofQuote: proofQuote.toInspect(),
677
694
  });
678
695
  return false;
679
696
  }
680
697
 
698
+ const { receipt } = result;
699
+
681
700
  if (receipt.status === 'success') {
682
701
  const tx = await this.getTransactionStats(receipt.transactionHash);
683
702
  const stats: L1PublishStats = {
@@ -900,35 +919,42 @@ export class L1Publisher {
900
919
  publicInputs: RootRollupPublicInputs;
901
920
  proof: Proof;
902
921
  }): Promise<string | undefined> {
903
- try {
904
- const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
905
- const argsArray = this.getSubmitEpochProofArgs(args);
922
+ const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
923
+ const argsArray = this.getSubmitEpochProofArgs(args);
906
924
 
907
- const txArgs = [
908
- {
909
- epochSize: argsArray[0],
910
- args: argsArray[1],
911
- fees: argsArray[2],
912
- blobPublicInputs: argsArray[3],
913
- aggregationObject: argsArray[4],
914
- proof: proofHex,
915
- },
916
- ] as const;
917
-
918
- this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`);
925
+ const txArgs = [
926
+ {
927
+ epochSize: argsArray[0],
928
+ args: argsArray[1],
929
+ fees: argsArray[2],
930
+ blobPublicInputs: argsArray[3],
931
+ aggregationObject: argsArray[4],
932
+ proof: proofHex,
933
+ },
934
+ ] as const;
919
935
 
920
- const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction({
936
+ this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`);
937
+ const data = encodeFunctionData({
938
+ abi: this.rollupContract.abi,
939
+ functionName: 'submitEpochRootProof',
940
+ args: txArgs,
941
+ });
942
+ try {
943
+ const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({
921
944
  to: this.rollupContract.address,
922
- data: encodeFunctionData({
923
- abi: this.rollupContract.abi,
924
- functionName: 'submitEpochRootProof',
925
- args: txArgs,
926
- }),
945
+ data,
927
946
  });
928
947
 
929
- return txReceipt.transactionHash;
948
+ return receipt.transactionHash;
930
949
  } catch (err) {
931
950
  this.log.error(`Rollup submit epoch proof failed`, err);
951
+ const errorMsg = await this.tryGetErrorFromRevertedTx(data, {
952
+ args: [...txArgs],
953
+ functionName: 'submitEpochRootProof',
954
+ abi: this.rollupContract.abi,
955
+ address: this.rollupContract.address,
956
+ });
957
+ this.log.error(`Rollup submit epoch proof tx reverted. ${errorMsg}`);
932
958
  return undefined;
933
959
  }
934
960
  }
@@ -949,7 +975,6 @@ export class L1Publisher {
949
975
  {
950
976
  blobs: encodedData.blobs.map(b => b.dataWithZeros),
951
977
  kzg,
952
- maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS
953
978
  },
954
979
  );
955
980
 
@@ -1016,7 +1041,8 @@ export class L1Publisher {
1016
1041
 
1017
1042
  private async sendProposeTx(
1018
1043
  encodedData: L1ProcessArgs,
1019
- ): Promise<{ receipt: TransactionReceipt | undefined; args: any; functionName: string; data: Hex } | undefined> {
1044
+ opts: { txTimeoutAt?: Date } = {},
1045
+ ): Promise<L1ProcessReturnType | undefined> {
1020
1046
  if (this.interrupted) {
1021
1047
  return undefined;
1022
1048
  }
@@ -1028,28 +1054,29 @@ export class L1Publisher {
1028
1054
  functionName: 'propose',
1029
1055
  args,
1030
1056
  });
1031
- const receipt = await this.l1TxUtils.sendAndMonitorTransaction(
1057
+ const result = await this.l1TxUtils.sendAndMonitorTransaction(
1032
1058
  {
1033
1059
  to: this.rollupContract.address,
1034
1060
  data,
1035
1061
  },
1036
1062
  {
1037
1063
  fixedGas: gas,
1064
+ ...opts,
1038
1065
  },
1039
1066
  {
1040
1067
  blobs: encodedData.blobs.map(b => b.dataWithZeros),
1041
1068
  kzg,
1042
- maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS
1043
1069
  },
1044
1070
  );
1045
1071
  return {
1046
- receipt,
1072
+ receipt: result.receipt,
1073
+ gasPrice: result.gasPrice,
1047
1074
  args,
1048
1075
  functionName: 'propose',
1049
1076
  data,
1050
1077
  };
1051
1078
  } catch (err) {
1052
- this.log.error(`Rollup publish failed: ${prettyLogViemErrorMsg(err)}`, err);
1079
+ this.log.error(`Rollup publish failed.`, err);
1053
1080
  return undefined;
1054
1081
  }
1055
1082
  }
@@ -1057,7 +1084,8 @@ export class L1Publisher {
1057
1084
  private async sendProposeAndClaimTx(
1058
1085
  encodedData: L1ProcessArgs,
1059
1086
  quote: EpochProofQuote,
1060
- ): Promise<{ receipt: TransactionReceipt | undefined; args: any; functionName: string; data: Hex } | undefined> {
1087
+ opts: { txTimeoutAt?: Date } = {},
1088
+ ): Promise<L1ProcessReturnType | undefined> {
1061
1089
  if (this.interrupted) {
1062
1090
  return undefined;
1063
1091
  }
@@ -1069,27 +1097,30 @@ export class L1Publisher {
1069
1097
  functionName: 'proposeAndClaim',
1070
1098
  args: [...args, quote.toViemArgs()],
1071
1099
  });
1072
- const receipt = await this.l1TxUtils.sendAndMonitorTransaction(
1100
+ const result = await this.l1TxUtils.sendAndMonitorTransaction(
1073
1101
  {
1074
1102
  to: this.rollupContract.address,
1075
1103
  data,
1076
1104
  },
1077
- { fixedGas: gas },
1105
+ {
1106
+ fixedGas: gas,
1107
+ ...opts,
1108
+ },
1078
1109
  {
1079
1110
  blobs: encodedData.blobs.map(b => b.dataWithZeros),
1080
1111
  kzg,
1081
- maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS
1082
1112
  },
1083
1113
  );
1084
1114
 
1085
1115
  return {
1086
- receipt,
1116
+ receipt: result.receipt,
1117
+ gasPrice: result.gasPrice,
1087
1118
  args: [...args, quote.toViemArgs()],
1088
1119
  functionName: 'proposeAndClaim',
1089
1120
  data,
1090
1121
  };
1091
1122
  } catch (err) {
1092
- this.log.error(`Rollup publish failed: ${prettyLogViemErrorMsg(err)}`, err);
1123
+ this.log.error(`Rollup publish failed.`, err);
1093
1124
  return undefined;
1094
1125
  }
1095
1126
  }
@@ -1143,38 +1174,8 @@ export class L1Publisher {
1143
1174
  * In the future this will move to be the beacon block id - which takes a bit more work
1144
1175
  * to calculate and will need to be mocked in e2e tests
1145
1176
  */
1146
- protected async sendBlobsToBlobSink(blockHash: string, blobs: Blob[]): Promise<boolean> {
1147
- // TODO(md): for now we are assuming the indexes of the blobs will be 0, 1, 2
1148
- // When in reality they will not, but for testing purposes this is fine
1149
- if (!this.blobSinkUrl) {
1150
- this.log.verbose('No blob sink url configured');
1151
- return false;
1152
- }
1153
-
1154
- this.log.verbose(`Sending ${blobs.length} blobs to blob sink`);
1155
- try {
1156
- const res = await fetch(`${this.blobSinkUrl}/blob_sidecar`, {
1157
- method: 'POST',
1158
- headers: {
1159
- 'Content-Type': 'application/json',
1160
- },
1161
- body: JSON.stringify({
1162
- // eslint-disable-next-line camelcase
1163
- block_id: blockHash,
1164
- blobs: blobs.map((b, i) => ({ blob: b.toBuffer(), index: i })),
1165
- }),
1166
- });
1167
-
1168
- if (res.ok) {
1169
- return true;
1170
- }
1171
-
1172
- this.log.error('Failed to send blobs to blob sink', res.status);
1173
- return false;
1174
- } catch (err) {
1175
- this.log.error(`Error sending blobs to blob sink`, err);
1176
- return false;
1177
- }
1177
+ protected sendBlobsToBlobSink(blockHash: string, blobs: Blob[]): Promise<boolean> {
1178
+ return this.blobSinkClient.sendBlobsToBlobSink(blockHash, blobs);
1178
1179
  }
1179
1180
  }
1180
1181
 
@@ -16,6 +16,7 @@ export class SequencerMetrics {
16
16
 
17
17
  private blockCounter: UpDownCounter;
18
18
  private blockBuildDuration: Histogram;
19
+ private blockBuildManaPerSecond: Gauge;
19
20
  private stateTransitionBufferDuration: Histogram;
20
21
  private currentBlockNumber: Gauge;
21
22
  private currentBlockSize: Gauge;
@@ -28,11 +29,19 @@ export class SequencerMetrics {
28
29
  this.tracer = client.getTracer(name);
29
30
 
30
31
  this.blockCounter = meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_COUNT);
32
+
31
33
  this.blockBuildDuration = meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION, {
32
34
  unit: 'ms',
33
35
  description: 'Duration to build a block',
34
36
  valueType: ValueType.INT,
35
37
  });
38
+
39
+ this.blockBuildManaPerSecond = meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND, {
40
+ unit: 'mana/s',
41
+ description: 'Mana per second when building a block',
42
+ valueType: ValueType.INT,
43
+ });
44
+
36
45
  this.stateTransitionBufferDuration = meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION, {
37
46
  unit: 'ms',
38
47
  description:
@@ -96,11 +105,12 @@ export class SequencerMetrics {
96
105
  this.setCurrentBlock(0, 0);
97
106
  }
98
107
 
99
- recordPublishedBlock(buildDurationMs: number) {
108
+ recordPublishedBlock(buildDurationMs: number, totalMana: number) {
100
109
  this.blockCounter.add(1, {
101
110
  [Attributes.STATUS]: 'published',
102
111
  });
103
112
  this.blockBuildDuration.record(Math.ceil(buildDurationMs));
113
+ this.blockBuildManaPerSecond.record(Math.ceil((totalMana * 1000) / buildDurationMs));
104
114
  this.setCurrentBlock(0, 0);
105
115
  }
106
116