@aztec/sequencer-client 0.69.0 → 0.69.1

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 (60) 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 +3 -4
  4. package/dest/config.d.ts.map +1 -1
  5. package/dest/config.js +11 -1
  6. package/dest/index.d.ts +3 -1
  7. package/dest/index.d.ts.map +1 -1
  8. package/dest/index.js +4 -2
  9. package/dest/publisher/config.d.ts +4 -0
  10. package/dest/publisher/config.d.ts.map +1 -1
  11. package/dest/publisher/config.js +6 -1
  12. package/dest/publisher/l1-publisher.d.ts +36 -11
  13. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  14. package/dest/publisher/l1-publisher.js +154 -88
  15. package/dest/sequencer/index.d.ts +1 -0
  16. package/dest/sequencer/index.d.ts.map +1 -1
  17. package/dest/sequencer/index.js +2 -1
  18. package/dest/sequencer/sequencer.d.ts +9 -16
  19. package/dest/sequencer/sequencer.d.ts.map +1 -1
  20. package/dest/sequencer/sequencer.js +61 -130
  21. package/dest/sequencer/utils.d.ts +2 -2
  22. package/dest/sequencer/utils.d.ts.map +1 -1
  23. package/dest/sequencer/utils.js +3 -3
  24. package/dest/slasher/factory.d.ts +11 -0
  25. package/dest/slasher/factory.d.ts.map +1 -0
  26. package/dest/slasher/factory.js +10 -0
  27. package/dest/slasher/index.d.ts +3 -0
  28. package/dest/slasher/index.d.ts.map +1 -0
  29. package/dest/slasher/index.js +3 -0
  30. package/dest/slasher/slasher_client.d.ts +127 -0
  31. package/dest/slasher/slasher_client.d.ts.map +1 -0
  32. package/dest/slasher/slasher_client.js +305 -0
  33. package/dest/tx_validator/gas_validator.d.ts +2 -3
  34. package/dest/tx_validator/gas_validator.d.ts.map +1 -1
  35. package/dest/tx_validator/gas_validator.js +9 -22
  36. package/dest/tx_validator/nullifier_cache.d.ts +16 -0
  37. package/dest/tx_validator/nullifier_cache.d.ts.map +1 -0
  38. package/dest/tx_validator/nullifier_cache.js +24 -0
  39. package/dest/tx_validator/phases_validator.d.ts +2 -3
  40. package/dest/tx_validator/phases_validator.d.ts.map +1 -1
  41. package/dest/tx_validator/phases_validator.js +15 -24
  42. package/dest/tx_validator/tx_validator_factory.d.ts +15 -14
  43. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  44. package/dest/tx_validator/tx_validator_factory.js +38 -24
  45. package/package.json +21 -19
  46. package/src/client/sequencer-client.ts +5 -2
  47. package/src/config.ts +10 -0
  48. package/src/index.ts +3 -1
  49. package/src/publisher/config.ts +10 -0
  50. package/src/publisher/l1-publisher.ts +180 -97
  51. package/src/sequencer/index.ts +1 -0
  52. package/src/sequencer/sequencer.ts +70 -180
  53. package/src/sequencer/utils.ts +2 -2
  54. package/src/slasher/factory.ts +22 -0
  55. package/src/slasher/index.ts +2 -0
  56. package/src/slasher/slasher_client.ts +402 -0
  57. package/src/tx_validator/gas_validator.ts +11 -24
  58. package/src/tx_validator/nullifier_cache.ts +29 -0
  59. package/src/tx_validator/phases_validator.ts +22 -33
  60. package/src/tx_validator/tx_validator_factory.ts +82 -40
@@ -3,7 +3,7 @@ import {
3
3
  type EpochProofClaim,
4
4
  type EpochProofQuote,
5
5
  type L2Block,
6
- SignatureDomainSeperator,
6
+ SignatureDomainSeparator,
7
7
  type TxHash,
8
8
  getHashedSignaturePayload,
9
9
  } from '@aztec/circuit-types';
@@ -16,24 +16,18 @@ import {
16
16
  type Proof,
17
17
  } from '@aztec/circuits.js';
18
18
  import { type FeeRecipient, type RootRollupPublicInputs } from '@aztec/circuits.js/rollup';
19
- import {
20
- type EthereumChain,
21
- type L1ContractsConfig,
22
- L1TxUtils,
23
- type L1TxUtilsConfig,
24
- createEthereumChain,
25
- } from '@aztec/ethereum';
19
+ import { type EthereumChain, type L1ContractsConfig, L1TxUtils, createEthereumChain } from '@aztec/ethereum';
26
20
  import { makeTuple } from '@aztec/foundation/array';
27
21
  import { toHex } from '@aztec/foundation/bigint-buffer';
28
22
  import { Blob } from '@aztec/foundation/blob';
29
23
  import { areArraysEqual, compactArray, times } from '@aztec/foundation/collection';
30
24
  import { type Signature } from '@aztec/foundation/eth-signature';
31
25
  import { Fr } from '@aztec/foundation/fields';
32
- import { createLogger } from '@aztec/foundation/log';
26
+ import { type Logger, createLogger } from '@aztec/foundation/log';
33
27
  import { type Tuple, serializeToBuffer } from '@aztec/foundation/serialize';
34
28
  import { InterruptibleSleep } from '@aztec/foundation/sleep';
35
29
  import { Timer } from '@aztec/foundation/timer';
36
- import { ExtRollupLibAbi, GovernanceProposerAbi, LeonidasLibAbi, RollupAbi } from '@aztec/l1-artifacts';
30
+ import { EmpireBaseAbi, RollupAbi, SlasherAbi } from '@aztec/l1-artifacts';
37
31
  import { type TelemetryClient } from '@aztec/telemetry-client';
38
32
 
39
33
  import pick from 'lodash.pick';
@@ -41,7 +35,7 @@ import {
41
35
  type BaseError,
42
36
  type Chain,
43
37
  type Client,
44
- ContractFunctionExecutionError,
38
+ type ContractFunctionExecutionError,
45
39
  ContractFunctionRevertedError,
46
40
  type GetContractReturnType,
47
41
  type Hex,
@@ -101,6 +95,8 @@ export type MinimalTransactionReceipt = {
101
95
  logs: any[];
102
96
  /** Block number in which this tx was mined. */
103
97
  blockNumber: bigint;
98
+ /** The block hash in which this tx was mined */
99
+ blockHash: `0x${string}`;
104
100
  };
105
101
 
106
102
  /** Arguments to the process method of the rollup contract */
@@ -135,6 +131,13 @@ export type L1SubmitEpochProofArgs = {
135
131
  proof: Proof;
136
132
  };
137
133
 
134
+ export enum VoteType {
135
+ GOVERNANCE,
136
+ SLASHING,
137
+ }
138
+
139
+ type GetSlashPayloadCallBack = (slotNumber: bigint) => Promise<EthAddress | undefined>;
140
+
138
141
  /**
139
142
  * Publishes L2 blocks to L1. This implementation does *not* retry a transaction in
140
143
  * the event of network congestion, but should work for local development.
@@ -149,26 +152,33 @@ export class L1Publisher {
149
152
  private interrupted = false;
150
153
  private metrics: L1PublisherMetrics;
151
154
 
152
- private payload: EthAddress = EthAddress.ZERO;
153
- private myLastVote: bigint = 0n;
155
+ protected governanceLog = createLogger('sequencer:publisher:governance');
156
+ protected governanceProposerAddress?: EthAddress;
157
+ private governancePayload: EthAddress = EthAddress.ZERO;
158
+
159
+ protected slashingLog = createLogger('sequencer:publisher:slashing');
160
+ protected slashingProposerAddress?: EthAddress;
161
+ private getSlashPayload?: GetSlashPayloadCallBack = undefined;
162
+
163
+ private myLastVotes: Record<VoteType, bigint> = {
164
+ [VoteType.GOVERNANCE]: 0n,
165
+ [VoteType.SLASHING]: 0n,
166
+ };
154
167
 
155
168
  protected log = createLogger('sequencer:publisher');
156
- protected governanceLog = createLogger('sequencer:publisher:governance');
157
169
 
158
170
  protected rollupContract: GetContractReturnType<
159
171
  typeof RollupAbi,
160
172
  WalletClient<HttpTransport, Chain, PrivateKeyAccount>
161
173
  >;
162
- protected governanceProposerContract?: GetContractReturnType<
163
- typeof GovernanceProposerAbi,
164
- WalletClient<HttpTransport, Chain, PrivateKeyAccount>
165
- > = undefined;
166
174
 
167
175
  protected publicClient: PublicClient<HttpTransport, Chain>;
168
176
  protected walletClient: WalletClient<HttpTransport, Chain, PrivateKeyAccount>;
169
177
  protected account: PrivateKeyAccount;
170
178
  protected ethereumSlotDuration: bigint;
171
179
 
180
+ private blobSinkUrl: string | undefined;
181
+
172
182
  // @note - with blobs, the below estimate seems too large.
173
183
  // Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
174
184
  // Total used for emptier block from above test: 429k (of which 84k is 1x blob)
@@ -178,11 +188,12 @@ export class L1Publisher {
178
188
  private readonly l1TxUtils: L1TxUtils;
179
189
 
180
190
  constructor(
181
- config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'> & L1TxUtilsConfig,
191
+ config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>,
182
192
  client: TelemetryClient,
183
193
  ) {
184
194
  this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
185
195
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
196
+ this.blobSinkUrl = config.blobSinkUrl;
186
197
  this.metrics = new L1PublisherMetrics(client, 'L1Publisher');
187
198
 
188
199
  const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey, l1Contracts } = config;
@@ -205,16 +216,31 @@ export class L1Publisher {
205
216
  });
206
217
 
207
218
  if (l1Contracts.governanceProposerAddress) {
208
- this.governanceProposerContract = getContract({
209
- address: getAddress(l1Contracts.governanceProposerAddress.toString()),
210
- abi: GovernanceProposerAbi,
211
- client: this.walletClient,
212
- });
219
+ this.governanceProposerAddress = EthAddress.fromString(l1Contracts.governanceProposerAddress.toString());
213
220
  }
214
221
 
215
222
  this.l1TxUtils = new L1TxUtils(this.publicClient, this.walletClient, this.log, config);
216
223
  }
217
224
 
225
+ public registerSlashPayloadGetter(callback: GetSlashPayloadCallBack) {
226
+ this.getSlashPayload = callback;
227
+ }
228
+
229
+ private async getSlashingProposerAddress() {
230
+ if (this.slashingProposerAddress) {
231
+ return this.slashingProposerAddress;
232
+ }
233
+
234
+ const slasherAddress = await this.rollupContract.read.SLASHER();
235
+ const slasher = getContract({
236
+ address: getAddress(slasherAddress.toString()),
237
+ abi: SlasherAbi,
238
+ client: this.walletClient,
239
+ });
240
+ this.slashingProposerAddress = EthAddress.fromString(await slasher.read.PROPOSER());
241
+ return this.slashingProposerAddress;
242
+ }
243
+
218
244
  get publisherAddress() {
219
245
  return this.account.address;
220
246
  }
@@ -230,12 +256,12 @@ export class L1Publisher {
230
256
  });
231
257
  }
232
258
 
233
- public getPayLoad() {
234
- return this.payload;
259
+ public getGovernancePayload() {
260
+ return this.governancePayload;
235
261
  }
236
262
 
237
- public setPayload(payload: EthAddress) {
238
- this.payload = payload;
263
+ public setGovernancePayload(payload: EthAddress) {
264
+ this.governancePayload = payload;
239
265
  }
240
266
 
241
267
  public getSenderAddress(): EthAddress {
@@ -395,38 +421,6 @@ export class L1Publisher {
395
421
  if (error instanceof ContractFunctionRevertedError) {
396
422
  const err = error as ContractFunctionRevertedError;
397
423
  this.log.debug(`Validation failed: ${err.message}`, err.data);
398
- } else if (error instanceof ContractFunctionExecutionError) {
399
- let err = error as ContractFunctionRevertedError;
400
- if (!tryGetCustomErrorName(err)) {
401
- // If we get here, it's because the custom error no longer exists in Rollup.sol,
402
- // but in another lib. The below reconstructs the error message.
403
- try {
404
- await this.publicClient.estimateGas({
405
- data: encodeFunctionData({
406
- abi: this.rollupContract.abi,
407
- functionName: 'validateHeader',
408
- args,
409
- }),
410
- account: this.account,
411
- to: this.rollupContract.address,
412
- });
413
- } catch (estGasErr: unknown) {
414
- const possibleAbis = [ExtRollupLibAbi, LeonidasLibAbi];
415
- possibleAbis.forEach(abi => {
416
- const possibleErr = getContractError(estGasErr as BaseError, {
417
- args: [],
418
- abi: abi,
419
- functionName: 'validateHeader',
420
- address: this.rollupContract.address,
421
- sender: this.account.address,
422
- });
423
- err = tryGetCustomErrorName(possibleErr) ? possibleErr : err;
424
- });
425
- }
426
- throw err;
427
- }
428
- } else {
429
- this.log.debug(`Unexpected error during validation: ${error}`);
430
424
  }
431
425
  throw error;
432
426
  }
@@ -450,68 +444,106 @@ export class L1Publisher {
450
444
  calldataGas: getCalldataGasUsage(calldata),
451
445
  };
452
446
  }
453
-
454
- public async castVote(slotNumber: bigint, timestamp: bigint): Promise<boolean> {
455
- if (this.payload.equals(EthAddress.ZERO)) {
447
+ public async castVote(slotNumber: bigint, timestamp: bigint, voteType: VoteType) {
448
+ // @todo This function can be optimized by doing some of the computations locally instead of calling the L1 contracts
449
+ if (this.myLastVotes[voteType] >= slotNumber) {
456
450
  return false;
457
451
  }
458
452
 
459
- if (!this.governanceProposerContract) {
460
- return false;
461
- }
453
+ const voteConfig = async (): Promise<
454
+ { payload: EthAddress; voteContractAddress: EthAddress; logger: Logger } | undefined
455
+ > => {
456
+ if (voteType === VoteType.GOVERNANCE) {
457
+ if (this.governancePayload.equals(EthAddress.ZERO)) {
458
+ return undefined;
459
+ }
460
+ if (!this.governanceProposerAddress) {
461
+ return undefined;
462
+ }
463
+ return {
464
+ payload: this.governancePayload,
465
+ voteContractAddress: this.governanceProposerAddress,
466
+ logger: this.governanceLog,
467
+ };
468
+ } else if (voteType === VoteType.SLASHING) {
469
+ if (!this.getSlashPayload) {
470
+ return undefined;
471
+ }
472
+ const slashingProposerAddress = await this.getSlashingProposerAddress();
473
+ if (!slashingProposerAddress) {
474
+ return undefined;
475
+ }
476
+
477
+ const slashPayload = await this.getSlashPayload(slotNumber);
462
478
 
463
- if (this.myLastVote >= slotNumber) {
479
+ if (!slashPayload) {
480
+ return undefined;
481
+ }
482
+
483
+ return {
484
+ payload: slashPayload,
485
+ voteContractAddress: slashingProposerAddress,
486
+ logger: this.slashingLog,
487
+ };
488
+ } else {
489
+ throw new Error('Invalid vote type');
490
+ }
491
+ };
492
+
493
+ const vConfig = await voteConfig();
494
+
495
+ if (!vConfig) {
464
496
  return false;
465
497
  }
466
498
 
467
- // @todo This can be optimized A LOT by doing the computation instead of making calls to L1, but it is very convenient
468
- // for when we keep changing the values and don't want to have multiple versions of the same logic implemented.
499
+ const { payload, voteContractAddress, logger } = vConfig;
500
+
501
+ const voteContract = getContract({
502
+ address: getAddress(voteContractAddress.toString()),
503
+ abi: EmpireBaseAbi,
504
+ client: this.walletClient,
505
+ });
469
506
 
470
507
  const [proposer, roundNumber] = await Promise.all([
471
508
  this.rollupContract.read.getProposerAt([timestamp]),
472
- this.governanceProposerContract.read.computeRound([slotNumber]),
509
+ voteContract.read.computeRound([slotNumber]),
473
510
  ]);
474
511
 
475
512
  if (proposer.toLowerCase() !== this.account.address.toLowerCase()) {
476
513
  return false;
477
514
  }
478
515
 
479
- const [slotForLastVote] = await this.governanceProposerContract.read.rounds([
480
- this.rollupContract.address,
481
- roundNumber,
482
- ]);
516
+ const [slotForLastVote] = await voteContract.read.rounds([this.rollupContract.address, roundNumber]);
483
517
 
484
518
  if (slotForLastVote >= slotNumber) {
485
519
  return false;
486
520
  }
487
521
 
488
- // Storing these early such that a quick entry again would not send another tx,
489
- // revert the state if there is a failure.
490
- const cachedMyLastVote = this.myLastVote;
491
- this.myLastVote = slotNumber;
492
-
493
- this.governanceLog.verbose(`Casting vote for ${this.payload}`);
522
+ const cachedMyLastVote = this.myLastVotes[voteType];
523
+ this.myLastVotes[voteType] = slotNumber;
494
524
 
495
525
  let txHash;
496
526
  try {
497
- txHash = await this.governanceProposerContract.write.vote([this.payload.toString()], { account: this.account });
527
+ txHash = await voteContract.write.vote([payload.toString()], {
528
+ account: this.account,
529
+ });
498
530
  } catch (err) {
499
531
  const msg = prettyLogViemErrorMsg(err);
500
- this.governanceLog.error(`Failed to vote`, msg);
501
- this.myLastVote = cachedMyLastVote;
532
+ logger.error(`Failed to vote`, msg);
533
+ this.myLastVotes[voteType] = cachedMyLastVote;
502
534
  return false;
503
535
  }
504
536
 
505
537
  if (txHash) {
506
538
  const receipt = await this.getTransactionReceipt(txHash);
507
539
  if (!receipt) {
508
- this.governanceLog.warn(`Failed to get receipt for tx ${txHash}`);
509
- this.myLastVote = cachedMyLastVote;
540
+ logger.warn(`Failed to get receipt for tx ${txHash}`);
541
+ this.myLastVotes[voteType] = cachedMyLastVote;
510
542
  return false;
511
543
  }
512
544
  }
513
545
 
514
- this.governanceLog.info(`Cast vote for ${this.payload}`);
546
+ logger.info(`Cast vote for ${payload}`);
515
547
  return true;
516
548
  }
517
549
 
@@ -534,16 +566,19 @@ export class L1Publisher {
534
566
 
535
567
  const consensusPayload = new ConsensusPayload(block.header, block.archive.root, txHashes ?? []);
536
568
 
537
- const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeperator.blockAttestation);
569
+ const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation);
570
+
571
+ const blobs = Blob.getBlobs(block.body.toBlobFields());
538
572
  const proposeTxArgs = {
539
573
  header: block.header.toBuffer(),
540
574
  archive: block.archive.root.toBuffer(),
541
575
  blockHash: block.header.hash().toBuffer(),
542
576
  body: block.body.toBuffer(),
543
- blobs: Blob.getBlobs(block.body.toBlobFields()),
577
+ blobs,
544
578
  attestations,
545
579
  txHashes: txHashes ?? [],
546
580
  };
581
+
547
582
  // Publish body and propose block (if not already published)
548
583
  if (this.interrupted) {
549
584
  this.log.verbose('L2 block data syncing interrupted while processing blocks.', ctx);
@@ -588,6 +623,12 @@ export class L1Publisher {
588
623
  };
589
624
  this.log.verbose(`Published L2 block to L1 rollup contract`, { ...stats, ...ctx });
590
625
  this.metrics.recordProcessBlockTx(timer.ms(), stats);
626
+
627
+ // Send the blobs to the blob sink
628
+ this.sendBlobsToBlobSink(receipt.blockHash, blobs).catch(_err => {
629
+ this.log.error('Failed to send blobs to blob sink');
630
+ });
631
+
591
632
  return true;
592
633
  }
593
634
 
@@ -602,7 +643,7 @@ export class L1Publisher {
602
643
  address: this.rollupContract.address,
603
644
  },
604
645
  {
605
- blobs: proposeTxArgs.blobs.map(b => b.data),
646
+ blobs: proposeTxArgs.blobs.map(b => b.dataWithZeros),
606
647
  kzg,
607
648
  maxFeePerBlobGas: 10000000000n,
608
649
  },
@@ -698,8 +739,7 @@ export class L1Publisher {
698
739
  },
699
740
  ],
700
741
  });
701
- // If the above passes, we have a blob error. We cannot simulate blob txs, and failed txs no longer throw errors,
702
- // and viem provides no way to get the revert reason from a given tx.
742
+ // If the above passes, we have a blob error. We cannot simulate blob txs, and failed txs no longer throw errors.
703
743
  // Strangely, the only way to throw the revert reason as an error and provide blobs is prepareTransactionRequest.
704
744
  // See: https://github.com/wevm/viem/issues/2075
705
745
  // This throws a EstimateGasExecutionError with the custom error information:
@@ -711,13 +751,13 @@ export class L1Publisher {
711
751
  });
712
752
  return undefined;
713
753
  } catch (simulationErr: any) {
714
- // If we don't have a ContractFunctionExecutionError, we have a blob related error => use ExtRollupLibAbi to get the error msg.
754
+ // If we don't have a ContractFunctionExecutionError, we have a blob related error => use getContractError to get the error msg.
715
755
  const contractErr =
716
756
  simulationErr.name === 'ContractFunctionExecutionError'
717
757
  ? simulationErr
718
758
  : getContractError(simulationErr as BaseError, {
719
759
  args: [],
720
- abi: ExtRollupLibAbi,
760
+ abi: RollupAbi,
721
761
  functionName: args.functionName,
722
762
  address: args.address,
723
763
  sender: this.account.address,
@@ -907,7 +947,7 @@ export class L1Publisher {
907
947
  },
908
948
  {},
909
949
  {
910
- blobs: encodedData.blobs.map(b => b.data),
950
+ blobs: encodedData.blobs.map(b => b.dataWithZeros),
911
951
  kzg,
912
952
  maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS
913
953
  },
@@ -997,7 +1037,7 @@ export class L1Publisher {
997
1037
  fixedGas: gas,
998
1038
  },
999
1039
  {
1000
- blobs: encodedData.blobs.map(b => b.data),
1040
+ blobs: encodedData.blobs.map(b => b.dataWithZeros),
1001
1041
  kzg,
1002
1042
  maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS
1003
1043
  },
@@ -1036,7 +1076,7 @@ export class L1Publisher {
1036
1076
  },
1037
1077
  { fixedGas: gas },
1038
1078
  {
1039
- blobs: encodedData.blobs.map(b => b.data),
1079
+ blobs: encodedData.blobs.map(b => b.dataWithZeros),
1040
1080
  kzg,
1041
1081
  maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS
1042
1082
  },
@@ -1078,6 +1118,7 @@ export class L1Publisher {
1078
1118
  gasPrice: receipt.effectiveGasPrice,
1079
1119
  logs: receipt.logs,
1080
1120
  blockNumber: receipt.blockNumber,
1121
+ blockHash: receipt.blockHash,
1081
1122
  };
1082
1123
  }
1083
1124
 
@@ -1093,9 +1134,51 @@ export class L1Publisher {
1093
1134
  protected async sleepOrInterrupted() {
1094
1135
  await this.interruptibleSleep.sleep(this.sleepTimeMs);
1095
1136
  }
1137
+
1138
+ /**
1139
+ * Send blobs to the blob sink
1140
+ *
1141
+ * If a blob sink url is configured, then we send blobs to the blob sink
1142
+ * - for now we use the blockHash as the identifier for the blobs;
1143
+ * In the future this will move to be the beacon block id - which takes a bit more work
1144
+ * to calculate and will need to be mocked in e2e tests
1145
+ */
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
+ }
1178
+ }
1096
1179
  }
1097
1180
 
1098
- /**
1181
+ /*
1099
1182
  * Returns cost of calldata usage in Ethereum.
1100
1183
  * @param data - Calldata.
1101
1184
  * @returns 4 for each zero byte, 16 for each nonzero.
@@ -1,2 +1,3 @@
1
1
  export * from './config.js';
2
2
  export * from './sequencer.js';
3
+ export * from './allowed.js';