@aztec/blob-client 0.0.1-commit.8c0b8ff → 0.0.1-commit.8cb2d04d8

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.
@@ -18,6 +18,9 @@ export class HttpBlobClient {
18
18
  fileStoreUploadClient;
19
19
  disabled;
20
20
  healthcheckUploadIntervalId;
21
+ /** Cached beacon genesis time (seconds since Unix epoch). Fetched once at startup. */ beaconGenesisTime;
22
+ /** Cached beacon slot duration in seconds. Fetched once at startup. */ beaconSecondsPerSlot;
23
+ /** Indexes of consensus hosts that serve blob sidecars (supernodes). Populated by testSources(). */ superNodeHostIndexes;
21
24
  constructor(config, opts = {}){
22
25
  this.opts = opts;
23
26
  this.disabled = false;
@@ -71,6 +74,7 @@ export class HttpBlobClient {
71
74
  let consensusNonSuperNodes = 0;
72
75
  let archiveSources = 0;
73
76
  let blobSinks = 0;
77
+ const detectedSuperNodes = new Set();
74
78
  if (l1ConsensusHostUrls && l1ConsensusHostUrls.length > 0) {
75
79
  for(let l1ConsensusHostIndex = 0; l1ConsensusHostIndex < l1ConsensusHostUrls.length; l1ConsensusHostIndex++){
76
80
  const l1ConsensusHostUrl = l1ConsensusHostUrls[l1ConsensusHostIndex];
@@ -99,9 +103,10 @@ export class HttpBlobClient {
99
103
  this.log.info(`L1 consensus host serves blob sidecars (supernode)`, {
100
104
  l1ConsensusHostUrl
101
105
  });
106
+ detectedSuperNodes.add(l1ConsensusHostIndex);
102
107
  consensusSuperNodes++;
103
108
  } else {
104
- this.log.info(`L1 consensus host does not serve blob sidecars`, {
109
+ this.log.info(`L1 consensus host does not serve blob sidecars, skipping for blob fetching`, {
105
110
  l1ConsensusHostUrl
106
111
  });
107
112
  consensusNonSuperNodes++;
@@ -119,6 +124,7 @@ export class HttpBlobClient {
119
124
  }
120
125
  }
121
126
  }
127
+ this.superNodeHostIndexes = detectedSuperNodes;
122
128
  if (this.archiveClient) {
123
129
  try {
124
130
  const latest = await this.archiveClient.getLatestBlock();
@@ -191,29 +197,25 @@ export class HttpBlobClient {
191
197
  }
192
198
  }
193
199
  /**
194
- * Get the blob sidecar
200
+ * Get the blob sidecar.
195
201
  *
196
- * If requesting from the blob client, we send the blobkHash
197
- * If requesting from the beacon node, we send the slot number
198
- *
199
- * Source ordering depends on sync state:
200
- * - Historical sync: blob client → FileStore → L1 consensus → Archive
201
- * - Near tip sync: blob client → FileStore → L1 consensus → FileStore (with retries) → Archive (eg blobscan)
202
+ * Alternates between two primary sources (consensus and filestore) in a retry loop,
203
+ * then falls back to archive if blobs are still missing. The order of the primary
204
+ * sources is configurable via `blobPreferFilestores`.
202
205
  *
203
206
  * @param blockHash - The block hash
204
207
  * @param blobHashes - The blob hashes to fetch
205
- * @param opts - Options including isHistoricalSync flag
208
+ * @param opts - Options for slot resolution
206
209
  * @returns The blobs
207
210
  */ async getBlobSidecar(blockHash, blobHashes, opts) {
208
211
  if (this.disabled) {
209
212
  this.log.warn('Blob storage is disabled, returning empty blob sidecar');
210
213
  return [];
211
214
  }
212
- const isHistoricalSync = opts?.isHistoricalSync ?? false;
213
215
  // Accumulate blobs across sources, preserving order and handling duplicates
214
216
  // resultBlobs[i] will contain the blob for blobHashes[i], or undefined if not yet found
215
217
  const resultBlobs = new Array(blobHashes.length).fill(undefined);
216
- // Helper to get missing blob hashes that we still need to fetch
218
+ // Helper to get missing blob hashes that we still need to fetch
217
219
  const getMissingBlobHashes = ()=>blobHashes.map((bh, i)=>resultBlobs[i] === undefined ? bh : undefined).filter((bh)=>bh !== undefined);
218
220
  // Return the result, ignoring any undefined ones
219
221
  const getFilledBlobs = ()=>resultBlobs.filter((b)=>b !== undefined);
@@ -235,80 +237,69 @@ export class HttpBlobClient {
235
237
  }
236
238
  return blobs;
237
239
  };
238
- const { l1ConsensusHostUrls } = this.config;
239
240
  const ctx = {
240
241
  blockHash,
241
242
  blobHashes: blobHashes.map(bufferToHex)
242
243
  };
243
- // Try filestore (quick, no retries) - useful for both historical and near-tip sync
244
- if (this.fileStoreClients.length > 0 && getMissingBlobHashes().length > 0) {
245
- await this.tryFileStores(getMissingBlobHashes, fillResults, ctx);
246
- if (getMissingBlobHashes().length === 0) {
247
- return returnWithCallback(getFilledBlobs());
244
+ // Lazily resolve the slot number only resolved when consensus hosts are actually tried.
245
+ let slotNumber;
246
+ let slotResolved = false;
247
+ const getSlotNumber = async ()=>{
248
+ if (!slotResolved) {
249
+ slotNumber = await this.resolveSlotNumber(blockHash, opts);
250
+ slotResolved = true;
248
251
  }
249
- }
250
- const missingAfterSink = getMissingBlobHashes();
251
- if (missingAfterSink.length > 0 && l1ConsensusHostUrls && l1ConsensusHostUrls.length > 0) {
252
- // The beacon api can query by slot number, so we get that first
253
- const consensusCtx = {
254
- l1ConsensusHostUrls,
255
- ...ctx
256
- };
257
- this.log.trace(`Attempting to get slot number for block hash`, consensusCtx);
258
- const slotNumber = await this.getSlotNumber(blockHash);
259
- this.log.debug(`Got slot number ${slotNumber} from consensus host for querying blobs`, consensusCtx);
260
- if (slotNumber) {
261
- let l1ConsensusHostUrl;
262
- for(let l1ConsensusHostIndex = 0; l1ConsensusHostIndex < l1ConsensusHostUrls.length; l1ConsensusHostIndex++){
263
- const missingHashes = getMissingBlobHashes();
264
- if (missingHashes.length === 0) {
265
- break;
266
- }
267
- l1ConsensusHostUrl = l1ConsensusHostUrls[l1ConsensusHostIndex];
268
- this.log.trace(`Attempting to get ${missingHashes.length} blobs from consensus host`, {
269
- slotNumber,
270
- l1ConsensusHostUrl,
271
- ...ctx
272
- });
273
- const blobs = await this.getBlobsFromHost(l1ConsensusHostUrl, slotNumber, l1ConsensusHostIndex);
274
- const result = await fillResults(blobs);
275
- this.log.debug(`Got ${blobs.length} blobs from consensus host (total: ${result.length}/${blobHashes.length})`, {
276
- slotNumber,
277
- l1ConsensusHostUrl,
278
- ...ctx
279
- });
280
- if (result.length === blobHashes.length) {
281
- return returnWithCallback(result);
282
- }
252
+ return slotNumber;
253
+ };
254
+ // Build the two source-try functions. The order depends on the config.
255
+ const tryConsensus = ()=>this.tryConsensusHosts(getSlotNumber, getMissingBlobHashes, fillResults, ctx);
256
+ const tryFilestores = ()=>this.tryFileStores(getMissingBlobHashes, fillResults, ctx);
257
+ const preferFilestores = this.config.blobPreferFilestores ?? false;
258
+ const [trySourceA, trySourceB] = preferFilestores ? [
259
+ tryFilestores,
260
+ tryConsensus
261
+ ] : [
262
+ tryConsensus,
263
+ tryFilestores
264
+ ];
265
+ // Historical sync: blobs should already exist, use shorter backoff for transient errors.
266
+ // Near-tip sync: blobs may still be uploading, use longer backoff for eventual consistency.
267
+ const isHistoricalSync = opts?.isHistoricalSync ?? false;
268
+ const backoff = isHistoricalSync ? [
269
+ 1,
270
+ 1
271
+ ] : [
272
+ 1,
273
+ 1,
274
+ 1,
275
+ 2,
276
+ 2
277
+ ];
278
+ // Retry loop: alternate between the two primary sources with backoff.
279
+ try {
280
+ await retry(async ()=>{
281
+ if (getMissingBlobHashes().length > 0) {
282
+ await trySourceA();
283
283
  }
284
- }
285
- }
286
- // For near-tip sync, retry filestores with backoff (eventual consistency)
287
- // This handles the case where blobs are still being uploaded by other validators
288
- if (!isHistoricalSync && this.fileStoreClients.length > 0 && getMissingBlobHashes().length > 0) {
289
- try {
290
- await retry(async ()=>{
291
- await this.tryFileStores(getMissingBlobHashes, fillResults, ctx);
292
- if (getMissingBlobHashes().length > 0) {
293
- throw new Error('Still missing blobs from filestores');
294
- }
295
- }, 'filestore blob retrieval', makeBackoff([
296
- 1,
297
- 1,
298
- 2
299
- ]), this.log, true);
300
- return returnWithCallback(getFilledBlobs());
301
- } catch {
302
- // Exhausted retries, continue to archive fallback
303
- }
284
+ if (getMissingBlobHashes().length > 0) {
285
+ await trySourceB();
286
+ }
287
+ if (getMissingBlobHashes().length > 0) {
288
+ throw new Error('Still missing blobs after trying all primary sources');
289
+ }
290
+ }, 'blob retrieval', makeBackoff(backoff), this.log, true);
291
+ return returnWithCallback(getFilledBlobs());
292
+ } catch {
293
+ // Exhausted retries, continue to archive fallback
304
294
  }
305
- const missingAfterConsensus = getMissingBlobHashes();
306
- if (missingAfterConsensus.length > 0 && this.archiveClient) {
295
+ // Archive fallback
296
+ const missingAfterPrimary = getMissingBlobHashes();
297
+ if (missingAfterPrimary.length > 0 && this.archiveClient) {
307
298
  const archiveCtx = {
308
299
  archiveUrl: this.archiveClient.getBaseUrl(),
309
300
  ...ctx
310
301
  };
311
- this.log.trace(`Attempting to get ${missingAfterConsensus.length} blobs from archive`, archiveCtx);
302
+ this.log.trace(`Attempting to get ${missingAfterPrimary.length} blobs from archive`, archiveCtx);
312
303
  const allBlobs = await this.archiveClient.getBlobsFromBlock(blockHash);
313
304
  if (!allBlobs) {
314
305
  this.log.debug('No blobs found from archive client', archiveCtx);
@@ -324,13 +315,63 @@ export class HttpBlobClient {
324
315
  const result = getFilledBlobs();
325
316
  if (result.length < blobHashes.length) {
326
317
  this.log.warn(`Failed to fetch all blobs for ${blockHash} from all blob sources (got ${result.length}/${blobHashes.length})`, {
327
- l1ConsensusHostUrls,
318
+ l1ConsensusHostUrls: this.config.l1ConsensusHostUrls,
328
319
  archiveUrl: this.archiveClient?.getBaseUrl(),
329
320
  fileStoreUrls: this.fileStoreClients.map((c)=>c.getBaseUrl())
330
321
  });
331
322
  }
332
323
  return returnWithCallback(result);
333
324
  }
325
+ /** Resolves the beacon slot number for the given block hash. Returns undefined if no consensus hosts. */ resolveSlotNumber(blockHash, opts) {
326
+ const { l1ConsensusHostUrls } = this.config;
327
+ if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
328
+ return undefined;
329
+ }
330
+ // If no supernodes, no point resolving the slot
331
+ if (this.superNodeHostIndexes && this.superNodeHostIndexes.size === 0) {
332
+ return undefined;
333
+ }
334
+ return this.getSlotNumber(blockHash, opts?.parentBeaconBlockRoot, opts?.l1BlockTimestamp);
335
+ }
336
+ /**
337
+ * Try all supernode consensus hosts for blob sidecars.
338
+ * Skips hosts that were detected as non-supernodes during testSources().
339
+ */ async tryConsensusHosts(getSlotNumber, getMissingBlobHashes, fillResults, ctx) {
340
+ const { l1ConsensusHostUrls } = this.config;
341
+ if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
342
+ return;
343
+ }
344
+ const slotNumber = await getSlotNumber();
345
+ if (!slotNumber) {
346
+ return;
347
+ }
348
+ for(let l1ConsensusHostIndex = 0; l1ConsensusHostIndex < l1ConsensusHostUrls.length; l1ConsensusHostIndex++){
349
+ const missingHashes = getMissingBlobHashes();
350
+ if (missingHashes.length === 0) {
351
+ break;
352
+ }
353
+ // Skip non-supernode hosts if we've already detected supernodes
354
+ if (this.superNodeHostIndexes && !this.superNodeHostIndexes.has(l1ConsensusHostIndex)) {
355
+ this.log.trace(`Skipping non-supernode consensus host`, {
356
+ l1ConsensusHostUrl: l1ConsensusHostUrls[l1ConsensusHostIndex]
357
+ });
358
+ continue;
359
+ }
360
+ const l1ConsensusHostUrl = l1ConsensusHostUrls[l1ConsensusHostIndex];
361
+ this.log.trace(`Attempting to get ${missingHashes.length} blobs from consensus host`, {
362
+ slotNumber,
363
+ l1ConsensusHostUrl,
364
+ ...ctx
365
+ });
366
+ const blobs = await this.getBlobsFromHost(l1ConsensusHostUrl, slotNumber, l1ConsensusHostIndex, missingHashes);
367
+ const result = await fillResults(blobs);
368
+ this.log.debug(`Got ${blobs.length} blobs from consensus host (total: ${result.length}/${ctx.blobHashes.length})`, {
369
+ slotNumber,
370
+ l1ConsensusHostUrl,
371
+ ...ctx
372
+ });
373
+ }
374
+ }
334
375
  /**
335
376
  * Try all filestores once (shuffled for load distribution).
336
377
  * @param getMissingBlobHashes - Function to get remaining blob hashes to fetch
@@ -369,14 +410,14 @@ export class HttpBlobClient {
369
410
  }
370
411
  }
371
412
  async getBlobSidecarFrom(hostUrl, blockHashOrSlot, blobHashes = [], l1ConsensusHostIndex) {
372
- const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
413
+ const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex, blobHashes);
373
414
  return (await processFetchedBlobs(blobs, blobHashes, this.log)).filter((b)=>b !== undefined);
374
415
  }
375
- async getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex) {
416
+ async getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex, blobHashes) {
376
417
  try {
377
- let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
418
+ let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex, blobHashes);
378
419
  if (res.ok) {
379
- return parseBlobJsonsFromResponse(await res.json(), this.log);
420
+ return await parseBlobJsonsFromResponse(await res.json(), this.log);
380
421
  }
381
422
  if (res.status === 404 && typeof blockHashOrSlot === 'number') {
382
423
  const latestSlot = await this.getLatestSlotNumber(hostUrl, l1ConsensusHostIndex);
@@ -389,9 +430,9 @@ export class HttpBlobClient {
389
430
  let currentSlot = blockHashOrSlot + 1;
390
431
  while(res.status === 404 && maxRetries > 0 && latestSlot !== undefined && currentSlot <= latestSlot){
391
432
  this.log.debug(`Trying slot ${currentSlot}`);
392
- res = await this.fetchBlobSidecars(hostUrl, currentSlot, l1ConsensusHostIndex);
433
+ res = await this.fetchBlobSidecars(hostUrl, currentSlot, l1ConsensusHostIndex, blobHashes);
393
434
  if (res.ok) {
394
- return parseBlobJsonsFromResponse(await res.json(), this.log);
435
+ return await parseBlobJsonsFromResponse(await res.json(), this.log);
395
436
  }
396
437
  currentSlot++;
397
438
  maxRetries--;
@@ -408,21 +449,29 @@ export class HttpBlobClient {
408
449
  return [];
409
450
  }
410
451
  }
411
- fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex) {
412
- const baseUrl = `${hostUrl}/eth/v1/beacon/blob_sidecars/${blockHashOrSlot}`;
413
- const { url, ...options } = getBeaconNodeFetchOptions(baseUrl, this.config, l1ConsensusHostIndex);
452
+ fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex, blobHashes) {
453
+ let baseUrl = `${hostUrl}/eth/v1/beacon/blobs/${blockHashOrSlot}`;
454
+ if (blobHashes && blobHashes.length > 0) {
455
+ const params = new URLSearchParams();
456
+ for (const hash of blobHashes){
457
+ params.append('versioned_hashes', `0x${hash.toString('hex')}`);
458
+ }
459
+ baseUrl += `?${params.toString()}`;
460
+ }
461
+ const { url, logSafeUrl, ...options } = getBeaconNodeFetchOptions(baseUrl, this.config, l1ConsensusHostIndex);
414
462
  this.log.debug(`Fetching blob sidecar for ${blockHashOrSlot}`, {
415
- url,
463
+ url: logSafeUrl,
416
464
  ...options
417
465
  });
418
- return this.fetch(url, options);
466
+ // No retry here — this is called inside the main retry loop in getBlobSidecar
467
+ return fetch(url, options);
419
468
  }
420
469
  async getLatestSlotNumber(hostUrl, l1ConsensusHostIndex) {
421
470
  try {
422
471
  const baseUrl = `${hostUrl}/eth/v1/beacon/headers/head`;
423
- const { url, ...options } = getBeaconNodeFetchOptions(baseUrl, this.config, l1ConsensusHostIndex);
472
+ const { url, logSafeUrl, ...options } = getBeaconNodeFetchOptions(baseUrl, this.config, l1ConsensusHostIndex);
424
473
  this.log.debug(`Fetching latest slot number`, {
425
- url,
474
+ url: logSafeUrl,
426
475
  ...options
427
476
  });
428
477
  const res = await this.fetch(url, options);
@@ -455,36 +504,45 @@ export class HttpBlobClient {
455
504
  *
456
505
  * @param blockHash - The block hash
457
506
  * @returns The slot number
458
- */ async getSlotNumber(blockHash) {
507
+ */ async getSlotNumber(blockHash, parentBeaconBlockRoot, l1BlockTimestamp) {
459
508
  const { l1ConsensusHostUrls, l1RpcUrls } = this.config;
460
509
  if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
461
510
  this.log.debug('No consensus host url configured');
462
511
  return undefined;
463
512
  }
464
- if (!l1RpcUrls || l1RpcUrls.length === 0) {
465
- this.log.debug('No execution host url configured');
466
- return undefined;
513
+ // Primary path: compute slot from timestamp if genesis config is cached (no network call needed)
514
+ if (l1BlockTimestamp !== undefined && this.beaconGenesisTime !== undefined && this.beaconSecondsPerSlot !== undefined) {
515
+ const slot = Number((l1BlockTimestamp - this.beaconGenesisTime) / BigInt(this.beaconSecondsPerSlot));
516
+ this.log.debug(`Computed slot ${slot} from L1 block timestamp`, {
517
+ l1BlockTimestamp
518
+ });
519
+ return slot;
467
520
  }
468
- // Ping execution node to get the parentBeaconBlockRoot for this block
469
- let parentBeaconBlockRoot;
470
- const client = createPublicClient({
471
- transport: makeL1HttpTransport(l1RpcUrls, {
472
- timeout: this.config.l1HttpTimeoutMS
473
- })
474
- });
475
- try {
476
- const res = await client.request({
477
- method: 'eth_getBlockByHash',
478
- params: [
479
- blockHash,
480
- /*tx flag*/ false
481
- ]
521
+ if (!parentBeaconBlockRoot) {
522
+ // parentBeaconBlockRoot not provided by caller — fetch it from the execution RPC
523
+ if (!l1RpcUrls || l1RpcUrls.length === 0) {
524
+ this.log.debug('No execution host url configured');
525
+ return undefined;
526
+ }
527
+ const client = createPublicClient({
528
+ transport: makeL1HttpTransport(l1RpcUrls, {
529
+ timeout: this.config.l1HttpTimeoutMS
530
+ })
482
531
  });
483
- if (res.parentBeaconBlockRoot) {
484
- parentBeaconBlockRoot = res.parentBeaconBlockRoot;
532
+ try {
533
+ const res = await client.request({
534
+ method: 'eth_getBlockByHash',
535
+ params: [
536
+ blockHash,
537
+ /*tx flag*/ false
538
+ ]
539
+ });
540
+ if (res.parentBeaconBlockRoot) {
541
+ parentBeaconBlockRoot = res.parentBeaconBlockRoot;
542
+ }
543
+ } catch (err) {
544
+ this.log.error(`Error getting parent beacon block root`, err);
485
545
  }
486
- } catch (err) {
487
- this.log.error(`Error getting parent beacon block root`, err);
488
546
  }
489
547
  if (!parentBeaconBlockRoot) {
490
548
  this.log.error(`No parent beacon block root found for block ${blockHash}`);
@@ -516,8 +574,10 @@ export class HttpBlobClient {
516
574
  }
517
575
  /**
518
576
  * Start the blob client.
519
- * Uploads the initial healthcheck file (awaited) and starts periodic uploads.
577
+ * Fetches and caches beacon genesis config for timestamp-based slot resolution,
578
+ * then uploads the initial healthcheck file (awaited) and starts periodic uploads.
520
579
  */ async start() {
580
+ await this.fetchBeaconConfig();
521
581
  if (!this.fileStoreUploadClient) {
522
582
  return;
523
583
  }
@@ -536,6 +596,40 @@ export class HttpBlobClient {
536
596
  }, intervalMs);
537
597
  }
538
598
  /**
599
+ * Fetches and caches beacon genesis time and slot duration from the first available consensus host.
600
+ * These static values enable timestamp-based slot resolution, eliminating the per-fetch headers call.
601
+ * Logs a warning and leaves fields undefined if all hosts fail, callers fall back gracefully.
602
+ */ async fetchBeaconConfig() {
603
+ const { l1ConsensusHostUrls } = this.config;
604
+ if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
605
+ return;
606
+ }
607
+ for(let i = 0; i < l1ConsensusHostUrls.length; i++){
608
+ try {
609
+ const { url: genesisUrl, ...genesisOptions } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrls[i]}/eth/v1/config/genesis`, this.config, i);
610
+ const { url: specUrl, ...specOptions } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrls[i]}/eth/v1/config/spec`, this.config, i);
611
+ const [genesisRes, specRes] = await Promise.all([
612
+ this.fetch(genesisUrl, genesisOptions),
613
+ this.fetch(specUrl, specOptions)
614
+ ]);
615
+ if (genesisRes.ok && specRes.ok) {
616
+ const genesis = await genesisRes.json();
617
+ const spec = await specRes.json();
618
+ this.beaconGenesisTime = BigInt(genesis.data.genesisTime);
619
+ this.beaconSecondsPerSlot = parseInt(spec.data.secondsPerSlot);
620
+ this.log.debug(`Fetched beacon genesis config`, {
621
+ genesisTime: this.beaconGenesisTime,
622
+ secondsPerSlot: this.beaconSecondsPerSlot
623
+ });
624
+ return;
625
+ }
626
+ } catch (err) {
627
+ this.log.warn(`Failed to fetch beacon config from host ${l1ConsensusHostUrls[i]}`, err);
628
+ }
629
+ }
630
+ this.log.warn('Could not fetch beacon genesis config from any consensus host — will use headers call fallback');
631
+ }
632
+ /**
539
633
  * Stop the blob client, clearing any periodic tasks.
540
634
  */ stop() {
541
635
  if (this.healthcheckUploadIntervalId) {
@@ -544,10 +638,9 @@ export class HttpBlobClient {
544
638
  }
545
639
  }
546
640
  }
547
- function parseBlobJsonsFromResponse(response, logger) {
641
+ async function parseBlobJsonsFromResponse(response, logger) {
548
642
  try {
549
- const blobs = response.data.map(parseBlobJson);
550
- return blobs;
643
+ return await Promise.all(response.data.map(parseBlobJson));
551
644
  } catch (err) {
552
645
  logger.error(`Error parsing blob json from response`, err);
553
646
  return [];
@@ -557,10 +650,9 @@ function parseBlobJsonsFromResponse(response, logger) {
557
650
  // https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlobSidecars
558
651
  // Here we attempt to parse the response data to Buffer, and check the lengths (via Blob's constructor), to avoid
559
652
  // throwing an error down the line when calling Blob.fromJson().
560
- function parseBlobJson(data) {
561
- const blobBuffer = Buffer.from(data.blob.slice(2), 'hex');
562
- const commitmentBuffer = Buffer.from(data.kzg_commitment.slice(2), 'hex');
563
- const blob = new Blob(blobBuffer, commitmentBuffer);
653
+ async function parseBlobJson(rawHex) {
654
+ const blobBuffer = Buffer.from(rawHex.slice(2), 'hex');
655
+ const blob = await Blob.fromBlobBuffer(blobBuffer);
564
656
  return blob.toJSON();
565
657
  }
566
658
  // Returns an array that maps each blob hash to the corresponding blob, or undefined if the blob is not found
@@ -588,11 +680,15 @@ function getBeaconNodeFetchOptions(url, config, l1ConsensusHostIndex) {
588
680
  const l1ConsensusHostApiKey = l1ConsensusHostIndex !== undefined && l1ConsensusHostApiKeys && l1ConsensusHostApiKeys[l1ConsensusHostIndex];
589
681
  const l1ConsensusHostApiKeyHeader = l1ConsensusHostIndex !== undefined && l1ConsensusHostApiKeyHeaders && l1ConsensusHostApiKeyHeaders[l1ConsensusHostIndex];
590
682
  let formattedUrl = url;
683
+ let logSafeUrl = url;
591
684
  if (l1ConsensusHostApiKey && l1ConsensusHostApiKey.getValue() !== '' && !l1ConsensusHostApiKeyHeader) {
592
- formattedUrl += `${formattedUrl.includes('?') ? '&' : '?'}key=${l1ConsensusHostApiKey.getValue()}`;
685
+ const separator = formattedUrl.includes('?') ? '&' : '?';
686
+ formattedUrl += `${separator}key=${l1ConsensusHostApiKey.getValue()}`;
687
+ logSafeUrl += `${separator}key=[REDACTED]`;
593
688
  }
594
689
  return {
595
690
  url: formattedUrl,
691
+ logSafeUrl,
596
692
  ...l1ConsensusHostApiKey && l1ConsensusHostApiKeyHeader && {
597
693
  headers: {
598
694
  [l1ConsensusHostApiKeyHeader]: l1ConsensusHostApiKey.getValue()
@@ -5,11 +5,20 @@ import type { Blob } from '@aztec/blob-lib';
5
5
  export interface GetBlobSidecarOptions {
6
6
  /**
7
7
  * True if the archiver is catching up (historical sync), false if near tip.
8
- * This affects source ordering:
9
- * - Historical: FileStore first (data should exist), then L1 consensus, then archive (eg. blobscan)
10
- * - Near tip: FileStore first with no retries (data should exist), L1 consensus second (freshest data), then FileStore with retries, then archive (eg. blobscan)
8
+ * Historical sync uses a shorter retry backoff since blobs should already exist.
11
9
  */
12
10
  isHistoricalSync?: boolean;
11
+ /**
12
+ * The parent beacon block root for the L1 block containing the blobs.
13
+ * If provided, skips the eth_getBlockByHash execution RPC call inside getSlotNumber.
14
+ */
15
+ parentBeaconBlockRoot?: string;
16
+ /**
17
+ * The timestamp of the L1 execution block containing the blobs.
18
+ * When provided alongside a cached beacon genesis config (fetched at startup), allows computing
19
+ * the beacon slot directly via timestamp math, skipping the beacon headers network call entirely.
20
+ */
21
+ l1BlockTimestamp?: bigint;
13
22
  }
14
23
  export interface BlobClientInterface {
15
24
  /** Sends the given blobs to the filestore, to be indexed by blob hash. */
@@ -25,4 +34,4 @@ export interface BlobClientInterface {
25
34
  /** Returns true if this client can upload blobs to filestore. */
26
35
  canUpload(): boolean;
27
36
  }
28
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUM1QjtBQUVELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsMEVBQTBFO0lBQzFFLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEQscUVBQXFFO0lBQ3JFLGNBQWMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3RHLDZFQUE2RTtJQUM3RSxLQUFLLENBQUMsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEIsb0ZBQW9GO0lBQ3BGLFdBQVcsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDN0IsMERBQTBEO0lBQzFELElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQztJQUNkLGlFQUFpRTtJQUNqRSxTQUFTLElBQUksT0FBTyxDQUFDO0NBQ3RCIn0=
37
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7OztPQUdHO0lBQ0gsZ0JBQWdCLENBQUMsRUFBRSxPQUFPLENBQUM7SUFDM0I7OztPQUdHO0lBQ0gscUJBQXFCLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDL0I7Ozs7T0FJRztJQUNILGdCQUFnQixDQUFDLEVBQUUsTUFBTSxDQUFDO0NBQzNCO0FBRUQsTUFBTSxXQUFXLG1CQUFtQjtJQUNsQywwRUFBMEU7SUFDMUUsb0JBQW9CLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN0RCxxRUFBcUU7SUFDckUsY0FBYyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxDQUFDLEVBQUUsTUFBTSxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUUscUJBQXFCLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdEcsNkVBQTZFO0lBQzdFLEtBQUssQ0FBQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN4QixvRkFBb0Y7SUFDcEYsV0FBVyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3QiwwREFBMEQ7SUFDMUQsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2QsaUVBQWlFO0lBQ2pFLFNBQVMsSUFBSSxPQUFPLENBQUM7Q0FDdEIifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/client/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,qEAAqE;IACrE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACtG,6EAA6E;IAC7E,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,0DAA0D;IAC1D,IAAI,CAAC,IAAI,IAAI,CAAC;IACd,iEAAiE;IACjE,SAAS,IAAI,OAAO,CAAC;CACtB"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/client/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,qEAAqE;IACrE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACtG,6EAA6E;IAC7E,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,0DAA0D;IAC1D,IAAI,CAAC,IAAI,IAAI,CAAC;IACd,iEAAiE;IACjE,SAAS,IAAI,OAAO,CAAC;CACtB"}
@@ -1,4 +1,5 @@
1
1
  import { type Logger } from '@aztec/foundation/log';
2
+ import { type HttpFileStoreOptions } from '@aztec/stdlib/file-store';
2
3
  import { FileStoreBlobClient } from './filestore_blob_client.js';
3
4
  /**
4
5
  * Metadata required to construct the base path for blob storage.
@@ -25,8 +26,8 @@ export declare function makeBlobBasePath(metadata: BlobFileStoreMetadata): strin
25
26
  * @param logger - Optional logger
26
27
  * @returns A FileStoreBlobClient for reading blobs, or undefined if storeUrl is undefined
27
28
  */
28
- export declare function createReadOnlyFileStoreBlobClient(storeUrl: string, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient>;
29
- export declare function createReadOnlyFileStoreBlobClient(storeUrl: string | undefined, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient | undefined>;
29
+ export declare function createReadOnlyFileStoreBlobClient(storeUrl: string, metadata: BlobFileStoreMetadata, logger?: Logger, httpOptions?: HttpFileStoreOptions): Promise<FileStoreBlobClient>;
30
+ export declare function createReadOnlyFileStoreBlobClient(storeUrl: string | undefined, metadata: BlobFileStoreMetadata, logger?: Logger, httpOptions?: HttpFileStoreOptions): Promise<FileStoreBlobClient | undefined>;
30
31
  /**
31
32
  * Creates multiple read-only FileStoreBlobClients from an array of URLs.
32
33
  *
@@ -35,7 +36,7 @@ export declare function createReadOnlyFileStoreBlobClient(storeUrl: string | und
35
36
  * @param logger - Optional logger
36
37
  * @returns Array of FileStoreBlobClients (excludes any that failed to create)
37
38
  */
38
- export declare function createReadOnlyFileStoreBlobClients(storeUrls: string[] | undefined, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient[]>;
39
+ export declare function createReadOnlyFileStoreBlobClients(storeUrls: string[] | undefined, metadata: BlobFileStoreMetadata, logger?: Logger, httpOptions?: HttpFileStoreOptions): Promise<FileStoreBlobClient[]>;
39
40
  /**
40
41
  * Creates a writable FileStoreBlobClient for uploading blobs.
41
42
  * Note: https:// URLs are not supported for upload, only s3://, gs://, or file://.
@@ -47,4 +48,4 @@ export declare function createReadOnlyFileStoreBlobClients(storeUrls: string[] |
47
48
  */
48
49
  export declare function createWritableFileStoreBlobClient(storeUrl: string, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient>;
49
50
  export declare function createWritableFileStoreBlobClient(storeUrl: string | undefined, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient | undefined>;
50
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ZpbGVzdG9yZS9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQVFsRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVqRTs7O0dBR0c7QUFDSCxNQUFNLFdBQVcscUJBQXFCO0lBQ3BDLHNCQUFzQjtJQUN0QixTQUFTLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLHlCQUF5QjtJQUN6QixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLDhEQUE4RDtJQUM5RCxhQUFhLEVBQUUsTUFBTSxDQUFDO0NBQ3ZCO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQWdCLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxxQkFBcUIsR0FBRyxNQUFNLENBS3hFO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFDaEMsd0JBQXNCLGlDQUFpQyxDQUNyRCxRQUFRLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFDNUIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxDQUFDO0FBbUI1Qzs7Ozs7OztHQU9HO0FBQ0gsd0JBQXNCLGtDQUFrQyxDQUN0RCxTQUFTLEVBQUUsTUFBTSxFQUFFLEdBQUcsU0FBUyxFQUMvQixRQUFRLEVBQUUscUJBQXFCLEVBQy9CLE1BQU0sQ0FBQyxFQUFFLE1BQU0sR0FDZCxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQW9CaEM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFDaEMsd0JBQXNCLGlDQUFpQyxDQUNyRCxRQUFRLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFDNUIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxDQUFDIn0=
51
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ZpbGVzdG9yZS9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEVBRUwsS0FBSyxvQkFBb0IsRUFJMUIsTUFBTSwwQkFBMEIsQ0FBQztBQUVsQyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVqRTs7O0dBR0c7QUFDSCxNQUFNLFdBQVcscUJBQXFCO0lBQ3BDLHNCQUFzQjtJQUN0QixTQUFTLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLHlCQUF5QjtJQUN6QixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLDhEQUE4RDtJQUM5RCxhQUFhLEVBQUUsTUFBTSxDQUFDO0NBQ3ZCO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQWdCLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxxQkFBcUIsR0FBRyxNQUFNLENBS3hFO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEVBQ2YsV0FBVyxDQUFDLEVBQUUsb0JBQW9CLEdBQ2pDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO0FBQ2hDLHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sR0FBRyxTQUFTLEVBQzVCLFFBQVEsRUFBRSxxQkFBcUIsRUFDL0IsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUNmLFdBQVcsQ0FBQyxFQUFFLG9CQUFvQixHQUNqQyxPQUFPLENBQUMsbUJBQW1CLEdBQUcsU0FBUyxDQUFDLENBQUM7QUFvQjVDOzs7Ozs7O0dBT0c7QUFDSCx3QkFBc0Isa0NBQWtDLENBQ3RELFNBQVMsRUFBRSxNQUFNLEVBQUUsR0FBRyxTQUFTLEVBQy9CLFFBQVEsRUFBRSxxQkFBcUIsRUFDL0IsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUNmLFdBQVcsQ0FBQyxFQUFFLG9CQUFvQixHQUNqQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQW9CaEM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFDaEMsd0JBQXNCLGlDQUFpQyxDQUNyRCxRQUFRLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFDNUIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxDQUFDIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/filestore/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAQlE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;AAmB5C;;;;;;;GAOG;AACH,wBAAsB,kCAAkC,CACtD,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,EAC/B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAoBhC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/filestore/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,oBAAoB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,oBAAoB,GACjC,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;AAoB5C;;;;;;;GAOG;AACH,wBAAsB,kCAAkC,CACtD,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,EAC/B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,oBAAoB,GACjC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAoBhC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC"}
@@ -10,7 +10,7 @@ import { FileStoreBlobClient } from './filestore_blob_client.js';
10
10
  const normalizedAddress = rollupAddress.toLowerCase().replace(/^0x/, '');
11
11
  return `aztec-${l1ChainId}-${rollupVersion}-0x${normalizedAddress}`;
12
12
  }
13
- export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logger) {
13
+ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logger, httpOptions) {
14
14
  if (!storeUrl) {
15
15
  return undefined;
16
16
  }
@@ -20,7 +20,7 @@ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logg
20
20
  storeUrl,
21
21
  basePath
22
22
  });
23
- const store = await createReadOnlyFileStore(storeUrl, log);
23
+ const store = await createReadOnlyFileStore(storeUrl, log, httpOptions);
24
24
  return new FileStoreBlobClient(store, basePath, log);
25
25
  }
26
26
  /**
@@ -30,7 +30,7 @@ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logg
30
30
  * @param metadata - Chain metadata for constructing the base path
31
31
  * @param logger - Optional logger
32
32
  * @returns Array of FileStoreBlobClients (excludes any that failed to create)
33
- */ export async function createReadOnlyFileStoreBlobClients(storeUrls, metadata, logger) {
33
+ */ export async function createReadOnlyFileStoreBlobClients(storeUrls, metadata, logger, httpOptions) {
34
34
  if (!storeUrls || storeUrls.length === 0) {
35
35
  return [];
36
36
  }
@@ -38,7 +38,7 @@ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logg
38
38
  const clients = [];
39
39
  for (const storeUrl of storeUrls){
40
40
  try {
41
- const client = await createReadOnlyFileStoreBlobClient(storeUrl, metadata, log);
41
+ const client = await createReadOnlyFileStoreBlobClient(storeUrl, metadata, log, httpOptions);
42
42
  if (client) {
43
43
  clients.push(client);
44
44
  }