@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.
- package/dest/client/config.d.ts +5 -1
- package/dest/client/config.d.ts.map +1 -1
- package/dest/client/config.js +12 -2
- package/dest/client/factory.d.ts +1 -1
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +7 -1
- package/dest/client/http.d.ts +19 -11
- package/dest/client/http.d.ts.map +1 -1
- package/dest/client/http.js +215 -119
- package/dest/client/interface.d.ts +13 -4
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/filestore/factory.d.ts +5 -4
- package/dest/filestore/factory.d.ts.map +1 -1
- package/dest/filestore/factory.js +4 -4
- package/package.json +7 -7
- package/src/client/config.ts +18 -2
- package/src/client/factory.ts +8 -1
- package/src/client/http.ts +249 -113
- package/src/client/interface.ts +12 -3
- package/src/filestore/factory.ts +7 -2
package/dest/client/http.js
CHANGED
|
@@ -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
|
-
*
|
|
197
|
-
*
|
|
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
|
|
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
|
|
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
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
306
|
-
|
|
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 ${
|
|
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
|
-
|
|
413
|
-
|
|
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
|
-
|
|
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 (
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
484
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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(
|
|
561
|
-
const blobBuffer = Buffer.from(
|
|
562
|
-
const
|
|
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
|
-
|
|
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
|
-
*
|
|
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,
|
|
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
|
|
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,
|
|
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;
|
|
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
|
}
|