@aztec/blob-client 0.0.1-commit.f504929 → 0.0.1-commit.f81dbcf
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/http.d.ts +3 -9
- package/dest/client/http.d.ts.map +1 -1
- package/dest/client/http.js +40 -92
- package/dest/client/interface.d.ts +1 -12
- package/dest/client/interface.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/client/http.ts +33 -117
- package/src/client/interface.ts +0 -11
package/dest/client/http.d.ts
CHANGED
|
@@ -14,10 +14,6 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
14
14
|
protected readonly fileStoreUploadClient: FileStoreBlobClient | undefined;
|
|
15
15
|
private disabled;
|
|
16
16
|
private healthcheckUploadIntervalId?;
|
|
17
|
-
/** Cached beacon genesis time (seconds since Unix epoch). Fetched once at startup. */
|
|
18
|
-
private beaconGenesisTime?;
|
|
19
|
-
/** Cached beacon slot duration in seconds. Fetched once at startup. */
|
|
20
|
-
private beaconSecondsPerSlot?;
|
|
21
17
|
constructor(config?: BlobClientConfig, opts?: {
|
|
22
18
|
logger?: Logger;
|
|
23
19
|
archiveClient?: BlobArchiveClient;
|
|
@@ -58,7 +54,7 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
58
54
|
getBlobSidecar(blockHash: `0x${string}`, blobHashes: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
59
55
|
private tryFileStores;
|
|
60
56
|
getBlobSidecarFrom(hostUrl: string, blockHashOrSlot: string | number, blobHashes?: Buffer[], l1ConsensusHostIndex?: number): Promise<Blob[]>;
|
|
61
|
-
getBlobsFromHost(hostUrl: string, blockHashOrSlot: string | number, l1ConsensusHostIndex?: number
|
|
57
|
+
getBlobsFromHost(hostUrl: string, blockHashOrSlot: string | number, l1ConsensusHostIndex?: number): Promise<BlobJson[]>;
|
|
62
58
|
private fetchBlobSidecars;
|
|
63
59
|
private getLatestSlotNumber;
|
|
64
60
|
private getSlotNumber;
|
|
@@ -68,18 +64,16 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
68
64
|
canUpload(): boolean;
|
|
69
65
|
/**
|
|
70
66
|
* Start the blob client.
|
|
71
|
-
*
|
|
72
|
-
* then uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
67
|
+
* Uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
73
68
|
*/
|
|
74
69
|
start(): Promise<void>;
|
|
75
70
|
/**
|
|
76
71
|
* Start periodic healthcheck upload to the file store to ensure it remains available even if accidentally deleted.
|
|
77
72
|
*/
|
|
78
73
|
private startPeriodicHealthcheckUpload;
|
|
79
|
-
private fetchBeaconConfig;
|
|
80
74
|
/**
|
|
81
75
|
* Stop the blob client, clearing any periodic tasks.
|
|
82
76
|
*/
|
|
83
77
|
stop(): void;
|
|
84
78
|
}
|
|
85
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxRQUFRLEVBQStCLE1BQU0saUJBQWlCLENBQUM7QUFFbkYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBT2xFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFDaEYsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBYXRELE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSTtJQVp2QixTQUFTLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7SUFDL0IsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLENBQUM7SUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxDQUFDO0lBQ2hFLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3ZDLFNBQVMsQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztJQUMzRCxTQUFTLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLG1CQUFtQixHQUFHLFNBQVMsQ0FBQztJQUUxRSxPQUFPLENBQUMsUUFBUSxDQUFTO0lBQ3pCLE9BQU8sQ0FBQywyQkFBMkIsQ0FBQyxDQUFpQjtJQUVyRCxZQUNFLE1BQU0sQ0FBQyxFQUFFLGdCQUFnQixFQUNSLElBQUksR0FBRTtRQUNyQixNQUFNLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDaEIsYUFBYSxDQUFDLEVBQUUsaUJBQWlCLENBQUM7UUFDbEMsZ0JBQWdCLENBQUMsRUFBRSxtQkFBbUIsRUFBRSxDQUFDO1FBQ3pDLHFCQUFxQixDQUFDLEVBQUUsbUJBQW1CLENBQUM7UUFDNUMseUVBQXlFO1FBQ3pFLGNBQWMsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLElBQUksQ0FBQztLQUNyQyxFQXdCUDtJQUVEOzs7T0FHRztJQUNILE9BQU8sQ0FBQyxzQkFBc0I7SUFVOUI7Ozs7O09BS0c7SUFDSSxXQUFXLENBQUMsS0FBSyxFQUFFLE9BQU8sR0FBRyxJQUFJLENBR3ZDO0lBRVksV0FBVyxrQkFvRXZCO0lBRVksb0JBQW9CLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FtQmpFO0lBRUQ7Ozs7Ozs7Ozs7Ozs7O09BY0c7SUFDVSxjQUFjLENBQ3pCLFNBQVMsRUFBRSxLQUFLLE1BQU0sRUFBRSxFQUN4QixVQUFVLEVBQUUsTUFBTSxFQUFFLEVBQ3BCLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUMzQixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0E2SWpCO1lBUWEsYUFBYTtJQXNDZCxrQkFBa0IsQ0FDN0IsT0FBTyxFQUFFLE1BQU0sRUFDZixlQUFlLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFDaEMsVUFBVSxHQUFFLE1BQU0sRUFBTyxFQUN6QixvQkFBb0IsQ0FBQyxFQUFFLE1BQU0sR0FDNUIsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBR2pCO0lBRVksZ0JBQWdCLENBQzNCLE9BQU8sRUFBRSxNQUFNLEVBQ2YsZUFBZSxFQUFFLE1BQU0sR0FBRyxNQUFNLEVBQ2hDLG9CQUFvQixDQUFDLEVBQUUsTUFBTSxHQUM1QixPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FzQ3JCO0lBRUQsT0FBTyxDQUFDLGlCQUFpQjtZQVlYLG1CQUFtQjtZQW1DbkIsYUFBYTtJQTZEM0Isc0NBQXNDO0lBQy9CLGdCQUFnQixJQUFJLGlCQUFpQixHQUFHLFNBQVMsQ0FFdkQ7SUFFRCxpRUFBaUU7SUFDMUQsU0FBUyxJQUFJLE9BQU8sQ0FFMUI7SUFFRDs7O09BR0c7SUFDVSxLQUFLLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQVNsQztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLDhCQUE4QjtJQVd0Qzs7T0FFRztJQUNJLElBQUksSUFBSSxJQUFJLENBS2xCO0NBQ0YifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/client/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAOlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAEjF,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,cAAe,YAAW,mBAAmB;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/client/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAOlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAEjF,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,cAAe,YAAW,mBAAmB;IAatD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZvB,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAChE,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IACvC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IAC3D,SAAS,CAAC,QAAQ,CAAC,qBAAqB,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE1E,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,2BAA2B,CAAC,CAAiB;IAErD,YACE,MAAM,CAAC,EAAE,gBAAgB,EACR,IAAI,GAAE;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,aAAa,CAAC,EAAE,iBAAiB,CAAC;QAClC,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;QACzC,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;QAC5C,yEAAyE;QACzE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;KACrC,EAwBP;IAED;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAU9B;;;;;OAKG;IACI,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAGvC;IAEY,WAAW,kBAoEvB;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBjE;IAED;;;;;;;;;;;;;;OAcG;IACU,cAAc,CACzB,SAAS,EAAE,KAAK,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,EAAE,EACpB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,EAAE,CAAC,CA6IjB;YAQa,aAAa;IAsCd,kBAAkB,CAC7B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,UAAU,GAAE,MAAM,EAAO,EACzB,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,IAAI,EAAE,CAAC,CAGjB;IAEY,gBAAgB,CAC3B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsCrB;IAED,OAAO,CAAC,iBAAiB;YAYX,mBAAmB;YAmCnB,aAAa;IA6D3B,sCAAsC;IAC/B,gBAAgB,IAAI,iBAAiB,GAAG,SAAS,CAEvD;IAED,iEAAiE;IAC1D,SAAS,IAAI,OAAO,CAE1B;IAED;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CASlC;IAED;;OAEG;IACH,OAAO,CAAC,8BAA8B;IAWtC;;OAEG;IACI,IAAI,IAAI,IAAI,CAKlB;CACF"}
|
package/dest/client/http.js
CHANGED
|
@@ -17,8 +17,6 @@ export class HttpBlobClient {
|
|
|
17
17
|
fileStoreUploadClient;
|
|
18
18
|
disabled;
|
|
19
19
|
healthcheckUploadIntervalId;
|
|
20
|
-
/** Cached beacon genesis time (seconds since Unix epoch). Fetched once at startup. */ beaconGenesisTime;
|
|
21
|
-
/** Cached beacon slot duration in seconds. Fetched once at startup. */ beaconSecondsPerSlot;
|
|
22
20
|
constructor(config, opts = {}){
|
|
23
21
|
this.opts = opts;
|
|
24
22
|
this.disabled = false;
|
|
@@ -222,7 +220,7 @@ export class HttpBlobClient {
|
|
|
222
220
|
...ctx
|
|
223
221
|
};
|
|
224
222
|
this.log.trace(`Attempting to get slot number for block hash`, consensusCtx);
|
|
225
|
-
const slotNumber = await this.getSlotNumber(blockHash
|
|
223
|
+
const slotNumber = await this.getSlotNumber(blockHash);
|
|
226
224
|
this.log.debug(`Got slot number ${slotNumber} from consensus host for querying blobs`, consensusCtx);
|
|
227
225
|
if (slotNumber) {
|
|
228
226
|
let l1ConsensusHostUrl;
|
|
@@ -237,7 +235,7 @@ export class HttpBlobClient {
|
|
|
237
235
|
l1ConsensusHostUrl,
|
|
238
236
|
...ctx
|
|
239
237
|
});
|
|
240
|
-
const blobs = await this.getBlobsFromHost(l1ConsensusHostUrl, slotNumber, l1ConsensusHostIndex
|
|
238
|
+
const blobs = await this.getBlobsFromHost(l1ConsensusHostUrl, slotNumber, l1ConsensusHostIndex);
|
|
241
239
|
const result = await fillResults(blobs);
|
|
242
240
|
this.log.debug(`Got ${blobs.length} blobs from consensus host (total: ${result.length}/${blobHashes.length})`, {
|
|
243
241
|
slotNumber,
|
|
@@ -336,14 +334,14 @@ export class HttpBlobClient {
|
|
|
336
334
|
}
|
|
337
335
|
}
|
|
338
336
|
async getBlobSidecarFrom(hostUrl, blockHashOrSlot, blobHashes = [], l1ConsensusHostIndex) {
|
|
339
|
-
const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex
|
|
337
|
+
const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
|
|
340
338
|
return (await processFetchedBlobs(blobs, blobHashes, this.log)).filter((b)=>b !== undefined);
|
|
341
339
|
}
|
|
342
|
-
async getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex
|
|
340
|
+
async getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex) {
|
|
343
341
|
try {
|
|
344
|
-
let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex
|
|
342
|
+
let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
|
|
345
343
|
if (res.ok) {
|
|
346
|
-
return
|
|
344
|
+
return parseBlobJsonsFromResponse(await res.json(), this.log);
|
|
347
345
|
}
|
|
348
346
|
if (res.status === 404 && typeof blockHashOrSlot === 'number') {
|
|
349
347
|
const latestSlot = await this.getLatestSlotNumber(hostUrl, l1ConsensusHostIndex);
|
|
@@ -356,9 +354,9 @@ export class HttpBlobClient {
|
|
|
356
354
|
let currentSlot = blockHashOrSlot + 1;
|
|
357
355
|
while(res.status === 404 && maxRetries > 0 && latestSlot !== undefined && currentSlot <= latestSlot){
|
|
358
356
|
this.log.debug(`Trying slot ${currentSlot}`);
|
|
359
|
-
res = await this.fetchBlobSidecars(hostUrl, currentSlot, l1ConsensusHostIndex
|
|
357
|
+
res = await this.fetchBlobSidecars(hostUrl, currentSlot, l1ConsensusHostIndex);
|
|
360
358
|
if (res.ok) {
|
|
361
|
-
return
|
|
359
|
+
return parseBlobJsonsFromResponse(await res.json(), this.log);
|
|
362
360
|
}
|
|
363
361
|
currentSlot++;
|
|
364
362
|
maxRetries--;
|
|
@@ -375,15 +373,8 @@ export class HttpBlobClient {
|
|
|
375
373
|
return [];
|
|
376
374
|
}
|
|
377
375
|
}
|
|
378
|
-
fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex
|
|
379
|
-
|
|
380
|
-
if (blobHashes && blobHashes.length > 0) {
|
|
381
|
-
const params = new URLSearchParams();
|
|
382
|
-
for (const hash of blobHashes){
|
|
383
|
-
params.append('versioned_hashes', `0x${hash.toString('hex')}`);
|
|
384
|
-
}
|
|
385
|
-
baseUrl += `?${params.toString()}`;
|
|
386
|
-
}
|
|
376
|
+
fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex) {
|
|
377
|
+
const baseUrl = `${hostUrl}/eth/v1/beacon/blob_sidecars/${blockHashOrSlot}`;
|
|
387
378
|
const { url, ...options } = getBeaconNodeFetchOptions(baseUrl, this.config, l1ConsensusHostIndex);
|
|
388
379
|
this.log.debug(`Fetching blob sidecar for ${blockHashOrSlot}`, {
|
|
389
380
|
url,
|
|
@@ -429,45 +420,36 @@ export class HttpBlobClient {
|
|
|
429
420
|
*
|
|
430
421
|
* @param blockHash - The block hash
|
|
431
422
|
* @returns The slot number
|
|
432
|
-
*/ async getSlotNumber(blockHash
|
|
423
|
+
*/ async getSlotNumber(blockHash) {
|
|
433
424
|
const { l1ConsensusHostUrls, l1RpcUrls } = this.config;
|
|
434
425
|
if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
|
|
435
426
|
this.log.debug('No consensus host url configured');
|
|
436
427
|
return undefined;
|
|
437
428
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
this.log.debug(`Computed slot ${slot} from L1 block timestamp`, {
|
|
442
|
-
l1BlockTimestamp
|
|
443
|
-
});
|
|
444
|
-
return slot;
|
|
429
|
+
if (!l1RpcUrls || l1RpcUrls.length === 0) {
|
|
430
|
+
this.log.debug('No execution host url configured');
|
|
431
|
+
return undefined;
|
|
445
432
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
433
|
+
// Ping execution node to get the parentBeaconBlockRoot for this block
|
|
434
|
+
let parentBeaconBlockRoot;
|
|
435
|
+
const client = createPublicClient({
|
|
436
|
+
transport: fallback(l1RpcUrls.map((url)=>http(url, {
|
|
437
|
+
batch: false
|
|
438
|
+
})))
|
|
439
|
+
});
|
|
440
|
+
try {
|
|
441
|
+
const res = await client.request({
|
|
442
|
+
method: 'eth_getBlockByHash',
|
|
443
|
+
params: [
|
|
444
|
+
blockHash,
|
|
445
|
+
/*tx flag*/ false
|
|
446
|
+
]
|
|
456
447
|
});
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
method: 'eth_getBlockByHash',
|
|
460
|
-
params: [
|
|
461
|
-
blockHash,
|
|
462
|
-
/*tx flag*/ false
|
|
463
|
-
]
|
|
464
|
-
});
|
|
465
|
-
if (res.parentBeaconBlockRoot) {
|
|
466
|
-
parentBeaconBlockRoot = res.parentBeaconBlockRoot;
|
|
467
|
-
}
|
|
468
|
-
} catch (err) {
|
|
469
|
-
this.log.error(`Error getting parent beacon block root`, err);
|
|
448
|
+
if (res.parentBeaconBlockRoot) {
|
|
449
|
+
parentBeaconBlockRoot = res.parentBeaconBlockRoot;
|
|
470
450
|
}
|
|
451
|
+
} catch (err) {
|
|
452
|
+
this.log.error(`Error getting parent beacon block root`, err);
|
|
471
453
|
}
|
|
472
454
|
if (!parentBeaconBlockRoot) {
|
|
473
455
|
this.log.error(`No parent beacon block root found for block ${blockHash}`);
|
|
@@ -499,10 +481,8 @@ export class HttpBlobClient {
|
|
|
499
481
|
}
|
|
500
482
|
/**
|
|
501
483
|
* Start the blob client.
|
|
502
|
-
*
|
|
503
|
-
* then uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
484
|
+
* Uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
504
485
|
*/ async start() {
|
|
505
|
-
await this.fetchBeaconConfig();
|
|
506
486
|
if (!this.fileStoreUploadClient) {
|
|
507
487
|
return;
|
|
508
488
|
}
|
|
@@ -521,40 +501,6 @@ export class HttpBlobClient {
|
|
|
521
501
|
}, intervalMs);
|
|
522
502
|
}
|
|
523
503
|
/**
|
|
524
|
-
* Fetches and caches beacon genesis time and slot duration from the first available consensus host.
|
|
525
|
-
* These static values enable timestamp-based slot resolution, eliminating the per-fetch headers call.
|
|
526
|
-
* Logs a warning and leaves fields undefined if all hosts fail, callers fall back gracefully.
|
|
527
|
-
*/ async fetchBeaconConfig() {
|
|
528
|
-
const { l1ConsensusHostUrls } = this.config;
|
|
529
|
-
if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
for(let i = 0; i < l1ConsensusHostUrls.length; i++){
|
|
533
|
-
try {
|
|
534
|
-
const { url: genesisUrl, ...genesisOptions } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrls[i]}/eth/v1/config/genesis`, this.config, i);
|
|
535
|
-
const { url: specUrl, ...specOptions } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrls[i]}/eth/v1/config/spec`, this.config, i);
|
|
536
|
-
const [genesisRes, specRes] = await Promise.all([
|
|
537
|
-
this.fetch(genesisUrl, genesisOptions),
|
|
538
|
-
this.fetch(specUrl, specOptions)
|
|
539
|
-
]);
|
|
540
|
-
if (genesisRes.ok && specRes.ok) {
|
|
541
|
-
const genesis = await genesisRes.json();
|
|
542
|
-
const spec = await specRes.json();
|
|
543
|
-
this.beaconGenesisTime = BigInt(genesis.data.genesisTime);
|
|
544
|
-
this.beaconSecondsPerSlot = parseInt(spec.data.secondsPerSlot);
|
|
545
|
-
this.log.debug(`Fetched beacon genesis config`, {
|
|
546
|
-
genesisTime: this.beaconGenesisTime,
|
|
547
|
-
secondsPerSlot: this.beaconSecondsPerSlot
|
|
548
|
-
});
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
} catch (err) {
|
|
552
|
-
this.log.warn(`Failed to fetch beacon config from host ${l1ConsensusHostUrls[i]}`, err);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
this.log.warn('Could not fetch beacon genesis config from any consensus host — will use headers call fallback');
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
504
|
* Stop the blob client, clearing any periodic tasks.
|
|
559
505
|
*/ stop() {
|
|
560
506
|
if (this.healthcheckUploadIntervalId) {
|
|
@@ -563,9 +509,10 @@ export class HttpBlobClient {
|
|
|
563
509
|
}
|
|
564
510
|
}
|
|
565
511
|
}
|
|
566
|
-
|
|
512
|
+
function parseBlobJsonsFromResponse(response, logger) {
|
|
567
513
|
try {
|
|
568
|
-
|
|
514
|
+
const blobs = response.data.map(parseBlobJson);
|
|
515
|
+
return blobs;
|
|
569
516
|
} catch (err) {
|
|
570
517
|
logger.error(`Error parsing blob json from response`, err);
|
|
571
518
|
return [];
|
|
@@ -575,9 +522,10 @@ async function parseBlobJsonsFromResponse(response, logger) {
|
|
|
575
522
|
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlobSidecars
|
|
576
523
|
// Here we attempt to parse the response data to Buffer, and check the lengths (via Blob's constructor), to avoid
|
|
577
524
|
// throwing an error down the line when calling Blob.fromJson().
|
|
578
|
-
|
|
579
|
-
const blobBuffer = Buffer.from(
|
|
580
|
-
const
|
|
525
|
+
function parseBlobJson(data) {
|
|
526
|
+
const blobBuffer = Buffer.from(data.blob.slice(2), 'hex');
|
|
527
|
+
const commitmentBuffer = Buffer.from(data.kzg_commitment.slice(2), 'hex');
|
|
528
|
+
const blob = new Blob(blobBuffer, commitmentBuffer);
|
|
581
529
|
return blob.toJSON();
|
|
582
530
|
}
|
|
583
531
|
// Returns an array that maps each blob hash to the corresponding blob, or undefined if the blob is not found
|
|
@@ -10,17 +10,6 @@ export interface GetBlobSidecarOptions {
|
|
|
10
10
|
* - Near tip: FileStore first with no retries (data should exist), L1 consensus second (freshest data), then FileStore with retries, then archive (eg. blobscan)
|
|
11
11
|
*/
|
|
12
12
|
isHistoricalSync?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* The parent beacon block root for the L1 block containing the blobs.
|
|
15
|
-
* If provided, skips the eth_getBlockByHash execution RPC call inside getSlotNumber.
|
|
16
|
-
*/
|
|
17
|
-
parentBeaconBlockRoot?: string;
|
|
18
|
-
/**
|
|
19
|
-
* The timestamp of the L1 execution block containing the blobs.
|
|
20
|
-
* When provided alongside a cached beacon genesis config (fetched at startup), allows computing
|
|
21
|
-
* the beacon slot directly via timestamp math, skipping the beacon headers network call entirely.
|
|
22
|
-
*/
|
|
23
|
-
l1BlockTimestamp?: bigint;
|
|
24
13
|
}
|
|
25
14
|
export interface BlobClientInterface {
|
|
26
15
|
/** Sends the given blobs to the filestore, to be indexed by blob hash. */
|
|
@@ -36,4 +25,4 @@ export interface BlobClientInterface {
|
|
|
36
25
|
/** Returns true if this client can upload blobs to filestore. */
|
|
37
26
|
canUpload(): boolean;
|
|
38
27
|
}
|
|
39
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
28
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUM1QjtBQUVELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsMEVBQTBFO0lBQzFFLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEQscUVBQXFFO0lBQ3JFLGNBQWMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3RHLDZFQUE2RTtJQUM3RSxLQUFLLENBQUMsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEIsb0ZBQW9GO0lBQ3BGLFdBQVcsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDN0IsMERBQTBEO0lBQzFELElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQztJQUNkLGlFQUFpRTtJQUNqRSxTQUFTLElBQUksT0FBTyxDQUFDO0NBQ3RCIn0=
|
|
@@ -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;
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/blob-client",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.f81dbcf",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dest/client/bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
60
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
61
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
62
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
63
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
64
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
59
|
+
"@aztec/blob-lib": "0.0.1-commit.f81dbcf",
|
|
60
|
+
"@aztec/ethereum": "0.0.1-commit.f81dbcf",
|
|
61
|
+
"@aztec/foundation": "0.0.1-commit.f81dbcf",
|
|
62
|
+
"@aztec/kv-store": "0.0.1-commit.f81dbcf",
|
|
63
|
+
"@aztec/stdlib": "0.0.1-commit.f81dbcf",
|
|
64
|
+
"@aztec/telemetry-client": "0.0.1-commit.f81dbcf",
|
|
65
65
|
"express": "^4.21.2",
|
|
66
66
|
"snappy": "^7.2.2",
|
|
67
67
|
"source-map-support": "^0.5.21",
|
package/src/client/http.ts
CHANGED
|
@@ -24,11 +24,6 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
24
24
|
private disabled = false;
|
|
25
25
|
private healthcheckUploadIntervalId?: NodeJS.Timeout;
|
|
26
26
|
|
|
27
|
-
/** Cached beacon genesis time (seconds since Unix epoch). Fetched once at startup. */
|
|
28
|
-
private beaconGenesisTime?: bigint;
|
|
29
|
-
/** Cached beacon slot duration in seconds. Fetched once at startup. */
|
|
30
|
-
private beaconSecondsPerSlot?: number;
|
|
31
|
-
|
|
32
27
|
constructor(
|
|
33
28
|
config?: BlobClientConfig,
|
|
34
29
|
private readonly opts: {
|
|
@@ -256,7 +251,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
256
251
|
// The beacon api can query by slot number, so we get that first
|
|
257
252
|
const consensusCtx = { l1ConsensusHostUrls, ...ctx };
|
|
258
253
|
this.log.trace(`Attempting to get slot number for block hash`, consensusCtx);
|
|
259
|
-
const slotNumber = await this.getSlotNumber(blockHash
|
|
254
|
+
const slotNumber = await this.getSlotNumber(blockHash);
|
|
260
255
|
this.log.debug(`Got slot number ${slotNumber} from consensus host for querying blobs`, consensusCtx);
|
|
261
256
|
|
|
262
257
|
if (slotNumber) {
|
|
@@ -273,12 +268,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
273
268
|
l1ConsensusHostUrl,
|
|
274
269
|
...ctx,
|
|
275
270
|
});
|
|
276
|
-
const blobs = await this.getBlobsFromHost(
|
|
277
|
-
l1ConsensusHostUrl,
|
|
278
|
-
slotNumber,
|
|
279
|
-
l1ConsensusHostIndex,
|
|
280
|
-
getMissingBlobHashes(),
|
|
281
|
-
);
|
|
271
|
+
const blobs = await this.getBlobsFromHost(l1ConsensusHostUrl, slotNumber, l1ConsensusHostIndex);
|
|
282
272
|
const result = await fillResults(blobs);
|
|
283
273
|
this.log.debug(
|
|
284
274
|
`Got ${blobs.length} blobs from consensus host (total: ${result.length}/${blobHashes.length})`,
|
|
@@ -397,7 +387,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
397
387
|
blobHashes: Buffer[] = [],
|
|
398
388
|
l1ConsensusHostIndex?: number,
|
|
399
389
|
): Promise<Blob[]> {
|
|
400
|
-
const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex
|
|
390
|
+
const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
|
|
401
391
|
return (await processFetchedBlobs(blobs, blobHashes, this.log)).filter((b): b is Blob => b !== undefined);
|
|
402
392
|
}
|
|
403
393
|
|
|
@@ -405,12 +395,11 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
405
395
|
hostUrl: string,
|
|
406
396
|
blockHashOrSlot: string | number,
|
|
407
397
|
l1ConsensusHostIndex?: number,
|
|
408
|
-
blobHashes?: Buffer[],
|
|
409
398
|
): Promise<BlobJson[]> {
|
|
410
399
|
try {
|
|
411
|
-
let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex
|
|
400
|
+
let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
|
|
412
401
|
if (res.ok) {
|
|
413
|
-
return
|
|
402
|
+
return parseBlobJsonsFromResponse(await res.json(), this.log);
|
|
414
403
|
}
|
|
415
404
|
|
|
416
405
|
if (res.status === 404 && typeof blockHashOrSlot === 'number') {
|
|
@@ -425,9 +414,9 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
425
414
|
let currentSlot = blockHashOrSlot + 1;
|
|
426
415
|
while (res.status === 404 && maxRetries > 0 && latestSlot !== undefined && currentSlot <= latestSlot) {
|
|
427
416
|
this.log.debug(`Trying slot ${currentSlot}`);
|
|
428
|
-
res = await this.fetchBlobSidecars(hostUrl, currentSlot, l1ConsensusHostIndex
|
|
417
|
+
res = await this.fetchBlobSidecars(hostUrl, currentSlot, l1ConsensusHostIndex);
|
|
429
418
|
if (res.ok) {
|
|
430
|
-
return
|
|
419
|
+
return parseBlobJsonsFromResponse(await res.json(), this.log);
|
|
431
420
|
}
|
|
432
421
|
currentSlot++;
|
|
433
422
|
maxRetries--;
|
|
@@ -450,17 +439,8 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
450
439
|
hostUrl: string,
|
|
451
440
|
blockHashOrSlot: string | number,
|
|
452
441
|
l1ConsensusHostIndex?: number,
|
|
453
|
-
blobHashes?: Buffer[],
|
|
454
442
|
): Promise<Response> {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (blobHashes && blobHashes.length > 0) {
|
|
458
|
-
const params = new URLSearchParams();
|
|
459
|
-
for (const hash of blobHashes) {
|
|
460
|
-
params.append('versioned_hashes', `0x${hash.toString('hex')}`);
|
|
461
|
-
}
|
|
462
|
-
baseUrl += `?${params.toString()}`;
|
|
463
|
-
}
|
|
443
|
+
const baseUrl = `${hostUrl}/eth/v1/beacon/blob_sidecars/${blockHashOrSlot}`;
|
|
464
444
|
|
|
465
445
|
const { url, ...options } = getBeaconNodeFetchOptions(baseUrl, this.config, l1ConsensusHostIndex);
|
|
466
446
|
this.log.debug(`Fetching blob sidecar for ${blockHashOrSlot}`, { url, ...options });
|
|
@@ -502,50 +482,34 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
502
482
|
* @param blockHash - The block hash
|
|
503
483
|
* @returns The slot number
|
|
504
484
|
*/
|
|
505
|
-
private async getSlotNumber(
|
|
506
|
-
blockHash: `0x${string}`,
|
|
507
|
-
parentBeaconBlockRoot?: string,
|
|
508
|
-
l1BlockTimestamp?: bigint,
|
|
509
|
-
): Promise<number | undefined> {
|
|
485
|
+
private async getSlotNumber(blockHash: `0x${string}`): Promise<number | undefined> {
|
|
510
486
|
const { l1ConsensusHostUrls, l1RpcUrls } = this.config;
|
|
511
487
|
if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
|
|
512
488
|
this.log.debug('No consensus host url configured');
|
|
513
489
|
return undefined;
|
|
514
490
|
}
|
|
515
491
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
this.beaconGenesisTime !== undefined &&
|
|
520
|
-
this.beaconSecondsPerSlot !== undefined
|
|
521
|
-
) {
|
|
522
|
-
const slot = Number((l1BlockTimestamp - this.beaconGenesisTime) / BigInt(this.beaconSecondsPerSlot));
|
|
523
|
-
this.log.debug(`Computed slot ${slot} from L1 block timestamp`, { l1BlockTimestamp });
|
|
524
|
-
return slot;
|
|
492
|
+
if (!l1RpcUrls || l1RpcUrls.length === 0) {
|
|
493
|
+
this.log.debug('No execution host url configured');
|
|
494
|
+
return undefined;
|
|
525
495
|
}
|
|
526
496
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
497
|
+
// Ping execution node to get the parentBeaconBlockRoot for this block
|
|
498
|
+
let parentBeaconBlockRoot: string | undefined;
|
|
499
|
+
const client = createPublicClient({
|
|
500
|
+
transport: fallback(l1RpcUrls.map(url => http(url, { batch: false }))),
|
|
501
|
+
});
|
|
502
|
+
try {
|
|
503
|
+
const res: RpcBlock = await client.request({
|
|
504
|
+
method: 'eth_getBlockByHash',
|
|
505
|
+
params: [blockHash, /*tx flag*/ false],
|
|
536
506
|
});
|
|
537
|
-
try {
|
|
538
|
-
const res: RpcBlock = await client.request({
|
|
539
|
-
method: 'eth_getBlockByHash',
|
|
540
|
-
params: [blockHash, /*tx flag*/ false],
|
|
541
|
-
});
|
|
542
507
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
} catch (err) {
|
|
547
|
-
this.log.error(`Error getting parent beacon block root`, err);
|
|
508
|
+
if (res.parentBeaconBlockRoot) {
|
|
509
|
+
parentBeaconBlockRoot = res.parentBeaconBlockRoot;
|
|
548
510
|
}
|
|
511
|
+
} catch (err) {
|
|
512
|
+
this.log.error(`Error getting parent beacon block root`, err);
|
|
549
513
|
}
|
|
550
514
|
|
|
551
515
|
if (!parentBeaconBlockRoot) {
|
|
@@ -591,12 +555,9 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
591
555
|
|
|
592
556
|
/**
|
|
593
557
|
* Start the blob client.
|
|
594
|
-
*
|
|
595
|
-
* then uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
558
|
+
* Uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
596
559
|
*/
|
|
597
560
|
public async start(): Promise<void> {
|
|
598
|
-
await this.fetchBeaconConfig();
|
|
599
|
-
|
|
600
561
|
if (!this.fileStoreUploadClient) {
|
|
601
562
|
return;
|
|
602
563
|
}
|
|
@@ -621,53 +582,6 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
621
582
|
}, intervalMs);
|
|
622
583
|
}
|
|
623
584
|
|
|
624
|
-
/**
|
|
625
|
-
* Fetches and caches beacon genesis time and slot duration from the first available consensus host.
|
|
626
|
-
* These static values enable timestamp-based slot resolution, eliminating the per-fetch headers call.
|
|
627
|
-
* Logs a warning and leaves fields undefined if all hosts fail, callers fall back gracefully.
|
|
628
|
-
*/
|
|
629
|
-
private async fetchBeaconConfig(): Promise<void> {
|
|
630
|
-
const { l1ConsensusHostUrls } = this.config;
|
|
631
|
-
if (!l1ConsensusHostUrls || l1ConsensusHostUrls.length === 0) {
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
for (let i = 0; i < l1ConsensusHostUrls.length; i++) {
|
|
636
|
-
try {
|
|
637
|
-
const { url: genesisUrl, ...genesisOptions } = getBeaconNodeFetchOptions(
|
|
638
|
-
`${l1ConsensusHostUrls[i]}/eth/v1/config/genesis`,
|
|
639
|
-
this.config,
|
|
640
|
-
i,
|
|
641
|
-
);
|
|
642
|
-
const { url: specUrl, ...specOptions } = getBeaconNodeFetchOptions(
|
|
643
|
-
`${l1ConsensusHostUrls[i]}/eth/v1/config/spec`,
|
|
644
|
-
this.config,
|
|
645
|
-
i,
|
|
646
|
-
);
|
|
647
|
-
|
|
648
|
-
const [genesisRes, specRes] = await Promise.all([
|
|
649
|
-
this.fetch(genesisUrl, genesisOptions),
|
|
650
|
-
this.fetch(specUrl, specOptions),
|
|
651
|
-
]);
|
|
652
|
-
|
|
653
|
-
if (genesisRes.ok && specRes.ok) {
|
|
654
|
-
const genesis = await genesisRes.json();
|
|
655
|
-
const spec = await specRes.json();
|
|
656
|
-
this.beaconGenesisTime = BigInt(genesis.data.genesisTime);
|
|
657
|
-
this.beaconSecondsPerSlot = parseInt(spec.data.secondsPerSlot);
|
|
658
|
-
this.log.debug(`Fetched beacon genesis config`, {
|
|
659
|
-
genesisTime: this.beaconGenesisTime,
|
|
660
|
-
secondsPerSlot: this.beaconSecondsPerSlot,
|
|
661
|
-
});
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
} catch (err) {
|
|
665
|
-
this.log.warn(`Failed to fetch beacon config from host ${l1ConsensusHostUrls[i]}`, err);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
this.log.warn('Could not fetch beacon genesis config from any consensus host — will use headers call fallback');
|
|
669
|
-
}
|
|
670
|
-
|
|
671
585
|
/**
|
|
672
586
|
* Stop the blob client, clearing any periodic tasks.
|
|
673
587
|
*/
|
|
@@ -679,9 +593,10 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
679
593
|
}
|
|
680
594
|
}
|
|
681
595
|
|
|
682
|
-
|
|
596
|
+
function parseBlobJsonsFromResponse(response: any, logger: Logger): BlobJson[] {
|
|
683
597
|
try {
|
|
684
|
-
|
|
598
|
+
const blobs = response.data.map(parseBlobJson);
|
|
599
|
+
return blobs;
|
|
685
600
|
} catch (err) {
|
|
686
601
|
logger.error(`Error parsing blob json from response`, err);
|
|
687
602
|
return [];
|
|
@@ -692,9 +607,10 @@ async function parseBlobJsonsFromResponse(response: any, logger: Logger): Promis
|
|
|
692
607
|
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlobSidecars
|
|
693
608
|
// Here we attempt to parse the response data to Buffer, and check the lengths (via Blob's constructor), to avoid
|
|
694
609
|
// throwing an error down the line when calling Blob.fromJson().
|
|
695
|
-
|
|
696
|
-
const blobBuffer = Buffer.from(
|
|
697
|
-
const
|
|
610
|
+
function parseBlobJson(data: any): BlobJson {
|
|
611
|
+
const blobBuffer = Buffer.from(data.blob.slice(2), 'hex');
|
|
612
|
+
const commitmentBuffer = Buffer.from(data.kzg_commitment.slice(2), 'hex');
|
|
613
|
+
const blob = new Blob(blobBuffer, commitmentBuffer);
|
|
698
614
|
return blob.toJSON();
|
|
699
615
|
}
|
|
700
616
|
|
package/src/client/interface.ts
CHANGED
|
@@ -11,17 +11,6 @@ export interface GetBlobSidecarOptions {
|
|
|
11
11
|
* - Near tip: FileStore first with no retries (data should exist), L1 consensus second (freshest data), then FileStore with retries, then archive (eg. blobscan)
|
|
12
12
|
*/
|
|
13
13
|
isHistoricalSync?: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* The parent beacon block root for the L1 block containing the blobs.
|
|
16
|
-
* If provided, skips the eth_getBlockByHash execution RPC call inside getSlotNumber.
|
|
17
|
-
*/
|
|
18
|
-
parentBeaconBlockRoot?: string;
|
|
19
|
-
/**
|
|
20
|
-
* The timestamp of the L1 execution block containing the blobs.
|
|
21
|
-
* When provided alongside a cached beacon genesis config (fetched at startup), allows computing
|
|
22
|
-
* the beacon slot directly via timestamp math, skipping the beacon headers network call entirely.
|
|
23
|
-
*/
|
|
24
|
-
l1BlockTimestamp?: bigint;
|
|
25
14
|
}
|
|
26
15
|
|
|
27
16
|
export interface BlobClientInterface {
|