@aztec/blob-client 4.1.2 → 4.2.0-aztecnr-rc.2
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 +3 -1
- package/dest/client/config.d.ts.map +1 -1
- package/dest/client/config.js +6 -1
- package/dest/client/http.d.ts +1 -1
- package/dest/client/http.d.ts.map +1 -1
- package/dest/client/http.js +53 -18
- package/package.json +7 -7
- package/src/client/config.ts +9 -0
- package/src/client/http.ts +53 -16
package/dest/client/config.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface BlobClientConfig extends BlobArchiveApiConfig {
|
|
|
40
40
|
* Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)
|
|
41
41
|
*/
|
|
42
42
|
blobHealthcheckUploadIntervalMinutes?: number;
|
|
43
|
+
/** Timeout for HTTP requests to the L1 RPC node in ms. */
|
|
44
|
+
l1HttpTimeoutMS?: number;
|
|
43
45
|
}
|
|
44
46
|
export declare const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig>;
|
|
45
47
|
/**
|
|
@@ -51,4 +53,4 @@ export declare function getBlobClientConfigFromEnv(): BlobClientConfig;
|
|
|
51
53
|
* Returns whether the given blob client config has any remote sources defined.
|
|
52
54
|
*/
|
|
53
55
|
export declare function hasRemoteBlobSources(config?: BlobClientConfig): boolean;
|
|
54
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsS0FBSyxrQkFBa0IsRUFDdkIsV0FBVyxFQUlaLE1BQU0sMEJBQTBCLENBQUM7QUFFbEMsT0FBTyxFQUFFLEtBQUssb0JBQW9CLEVBQWdDLE1BQU0sc0JBQXNCLENBQUM7QUFFL0Y7O0dBRUc7QUFDSCxNQUFNLFdBQVcsZ0JBQWlCLFNBQVEsb0JBQW9CO0lBQzVEOztPQUVHO0lBQ0gsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFckI7O09BRUc7SUFDSCxtQkFBbUIsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBRS9COztPQUVHO0lBQ0gsc0JBQXNCLENBQUMsRUFBRSxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztJQUUvQzs7T0FFRztJQUNILDRCQUE0QixDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFeEM7O09BRUc7SUFDSCxpQkFBaUIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUUzQjs7T0FFRztJQUNILHFCQUFxQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBRWhDOztPQUVHO0lBQ0gsaUJBQWlCLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUU3Qjs7T0FFRztJQUNILHNCQUFzQixDQUFDLEVBQUUsTUFBTSxDQUFDO0lBRWhDOztPQUVHO0lBQ0gsb0NBQW9DLENBQUMsRUFBRSxNQUFNLENBQUM7SUFFOUMsMERBQTBEO0lBQzFELGVBQWUsQ0FBQyxFQUFFLE1BQU0sQ0FBQztDQUMxQjtBQUVELGVBQU8sTUFBTSx1QkFBdUIsRUFBRSxrQkFBa0IsQ0FBQyxnQkFBZ0IsQ0F5RHhFLENBQUM7QUFFRjs7O0dBR0c7QUFDSCx3QkFBZ0IsMEJBQTBCLElBQUksZ0JBQWdCLENBRTdEO0FBRUQ7O0dBRUc7QUFDSCx3QkFBZ0Isb0JBQW9CLENBQUMsTUFBTSxHQUFFLGdCQUFxQixHQUFHLE9BQU8sQ0FFM0UifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,EAIZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,oBAAoB,EAAgC,MAAM,sBAAsB,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE/B;;OAEG;IACH,sBAAsB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;IAE/C;;OAEG;IACH,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IAExC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B;;OAEG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;OAEG;IACH,oCAAoC,CAAC,EAAE,MAAM,CAAC;IAE9C,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,gBAAgB,CAyDxE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,gBAAgB,CAE7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,gBAAqB,GAAG,OAAO,CAE3E"}
|
package/dest/client/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SecretValue, booleanConfigHelper, getConfigFromMappings } from '@aztec/foundation/config';
|
|
1
|
+
import { SecretValue, booleanConfigHelper, getConfigFromMappings, optionalNumberConfigHelper } from '@aztec/foundation/config';
|
|
2
2
|
import { blobArchiveApiConfigMappings } from '../archive/config.js';
|
|
3
3
|
export const blobClientConfigMapping = {
|
|
4
4
|
l1RpcUrls: {
|
|
@@ -45,6 +45,11 @@ export const blobClientConfigMapping = {
|
|
|
45
45
|
description: 'Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)',
|
|
46
46
|
parseEnv: (val)=>val ? +val : undefined
|
|
47
47
|
},
|
|
48
|
+
l1HttpTimeoutMS: {
|
|
49
|
+
env: 'ETHEREUM_HTTP_TIMEOUT_MS',
|
|
50
|
+
description: 'Timeout for HTTP requests to the L1 RPC node in ms.',
|
|
51
|
+
...optionalNumberConfigHelper()
|
|
52
|
+
},
|
|
48
53
|
...blobArchiveApiConfigMappings
|
|
49
54
|
};
|
|
50
55
|
/**
|
package/dest/client/http.d.ts
CHANGED
|
@@ -76,4 +76,4 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
76
76
|
*/
|
|
77
77
|
stop(): void;
|
|
78
78
|
}
|
|
79
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxRQUFRLEVBQStCLE1BQU0saUJBQWlCLENBQUM7QUFHbkYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBT2xFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFDaEYsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBYXRELE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSTtJQVp2QixTQUFTLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7SUFDL0IsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLENBQUM7SUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxDQUFDO0lBQ2hFLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3ZDLFNBQVMsQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztJQUMzRCxTQUFTLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLG1CQUFtQixHQUFHLFNBQVMsQ0FBQztJQUUxRSxPQUFPLENBQUMsUUFBUSxDQUFTO0lBQ3pCLE9BQU8sQ0FBQywyQkFBMkIsQ0FBQyxDQUFpQjtJQUVyRCxZQUNFLE1BQU0sQ0FBQyxFQUFFLGdCQUFnQixFQUNSLElBQUksR0FBRTtRQUNyQixNQUFNLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDaEIsYUFBYSxDQUFDLEVBQUUsaUJBQWlCLENBQUM7UUFDbEMsZ0JBQWdCLENBQUMsRUFBRSxtQkFBbUIsRUFBRSxDQUFDO1FBQ3pDLHFCQUFxQixDQUFDLEVBQUUsbUJBQW1CLENBQUM7UUFDNUMseUVBQXlFO1FBQ3pFLGNBQWMsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLElBQUksQ0FBQztLQUNyQyxFQXdCUDtJQUVEOzs7T0FHRztJQUNILE9BQU8sQ0FBQyxzQkFBc0I7SUFVOUI7Ozs7O09BS0c7SUFDSSxXQUFXLENBQUMsS0FBSyxFQUFFLE9BQU8sR0FBRyxJQUFJLENBR3ZDO0lBRVksV0FBVyxrQkF3R3ZCO0lBRVksb0JBQW9CLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FtQmpFO0lBRUQ7Ozs7Ozs7Ozs7Ozs7O09BY0c7SUFDVSxjQUFjLENBQ3pCLFNBQVMsRUFBRSxLQUFLLE1BQU0sRUFBRSxFQUN4QixVQUFVLEVBQUUsTUFBTSxFQUFFLEVBQ3BCLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUMzQixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0E2SWpCO1lBUWEsYUFBYTtJQXNDZCxrQkFBa0IsQ0FDN0IsT0FBTyxFQUFFLE1BQU0sRUFDZixlQUFlLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFDaEMsVUFBVSxHQUFFLE1BQU0sRUFBTyxFQUN6QixvQkFBb0IsQ0FBQyxFQUFFLE1BQU0sR0FDNUIsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBR2pCO0lBRVksZ0JBQWdCLENBQzNCLE9BQU8sRUFBRSxNQUFNLEVBQ2YsZUFBZSxFQUFFLE1BQU0sR0FBRyxNQUFNLEVBQ2hDLG9CQUFvQixDQUFDLEVBQUUsTUFBTSxHQUM1QixPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FzQ3JCO0lBRUQsT0FBTyxDQUFDLGlCQUFpQjtZQVlYLG1CQUFtQjtZQW1DbkIsYUFBYTtJQTZEM0Isc0NBQXNDO0lBQy9CLGdCQUFnQixJQUFJLGlCQUFpQixHQUFHLFNBQVMsQ0FFdkQ7SUFFRCxpRUFBaUU7SUFDMUQsU0FBUyxJQUFJLE9BQU8sQ0FFMUI7SUFFRDs7O09BR0c7SUFDVSxLQUFLLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQVNsQztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLDhCQUE4QjtJQVd0Qzs7T0FFRztJQUNJLElBQUksSUFBSSxJQUFJLENBS2xCO0NBQ0YifQ==
|
|
@@ -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;
|
|
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;AAGnF,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,kBAwGvB;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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Blob, computeEthVersionedBlobHash } from '@aztec/blob-lib';
|
|
2
|
+
import { makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
2
3
|
import { shuffle } from '@aztec/foundation/array';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
5
6
|
import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
|
|
6
|
-
import { createPublicClient
|
|
7
|
+
import { createPublicClient } from 'viem';
|
|
7
8
|
import { createBlobArchiveClient } from '../archive/factory.js';
|
|
8
9
|
import { DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES } from '../filestore/healthcheck.js';
|
|
9
10
|
import { getBlobClientConfigFromEnv } from './config.js';
|
|
@@ -66,22 +67,50 @@ export class HttpBlobClient {
|
|
|
66
67
|
l1ConsensusHostUrls,
|
|
67
68
|
archiveUrl
|
|
68
69
|
});
|
|
69
|
-
let
|
|
70
|
+
let consensusSuperNodes = 0;
|
|
71
|
+
let consensusNonSuperNodes = 0;
|
|
72
|
+
let archiveSources = 0;
|
|
73
|
+
let blobSinks = 0;
|
|
70
74
|
if (l1ConsensusHostUrls && l1ConsensusHostUrls.length > 0) {
|
|
71
75
|
for(let l1ConsensusHostIndex = 0; l1ConsensusHostIndex < l1ConsensusHostUrls.length; l1ConsensusHostIndex++){
|
|
72
76
|
const l1ConsensusHostUrl = l1ConsensusHostUrls[l1ConsensusHostIndex];
|
|
73
77
|
try {
|
|
74
|
-
const { url, ...options } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrl}/eth/v1/beacon/headers`, this.config, l1ConsensusHostIndex);
|
|
78
|
+
const { url, ...options } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrl}/eth/v1/beacon/headers/head`, this.config, l1ConsensusHostIndex);
|
|
75
79
|
const res = await this.fetch(url, options);
|
|
76
|
-
if (res.ok) {
|
|
77
|
-
this.log.
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
this.log.error(`Failure reaching L1 consensus host: ${res.statusText} (${res.status})`, {
|
|
78
82
|
l1ConsensusHostUrl
|
|
79
83
|
});
|
|
80
|
-
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
this.log.info(`L1 consensus host is reachable`, {
|
|
87
|
+
l1ConsensusHostUrl
|
|
88
|
+
});
|
|
89
|
+
// Check if the host serves blob sidecars (supernode/semi-supernode).
|
|
90
|
+
// Post-Fusaka (PeerDAS), non-supernode beacon nodes no longer serve the
|
|
91
|
+
// blob sidecar endpoint. A 200 response (even with an empty data array
|
|
92
|
+
// for a slot with no blobs) means the node supports serving blob sidecars.
|
|
93
|
+
const body = await res.json();
|
|
94
|
+
const headSlot = body?.data?.header?.message?.slot;
|
|
95
|
+
if (headSlot) {
|
|
96
|
+
const { url: blobUrl, ...blobOptions } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrl}/eth/v1/beacon/blobs/${headSlot}`, this.config, l1ConsensusHostIndex);
|
|
97
|
+
const blobRes = await this.fetch(blobUrl, blobOptions);
|
|
98
|
+
if (blobRes.ok) {
|
|
99
|
+
this.log.info(`L1 consensus host serves blob sidecars (supernode)`, {
|
|
100
|
+
l1ConsensusHostUrl
|
|
101
|
+
});
|
|
102
|
+
consensusSuperNodes++;
|
|
103
|
+
} else {
|
|
104
|
+
this.log.info(`L1 consensus host does not serve blob sidecars`, {
|
|
105
|
+
l1ConsensusHostUrl
|
|
106
|
+
});
|
|
107
|
+
consensusNonSuperNodes++;
|
|
108
|
+
}
|
|
81
109
|
} else {
|
|
82
|
-
this.log.
|
|
110
|
+
this.log.info(`L1 consensus host is reachable but could not determine head slot`, {
|
|
83
111
|
l1ConsensusHostUrl
|
|
84
112
|
});
|
|
113
|
+
consensusNonSuperNodes++;
|
|
85
114
|
}
|
|
86
115
|
} catch (err) {
|
|
87
116
|
this.log.error(`Error reaching L1 consensus host`, err, {
|
|
@@ -89,8 +118,6 @@ export class HttpBlobClient {
|
|
|
89
118
|
});
|
|
90
119
|
}
|
|
91
120
|
}
|
|
92
|
-
} else {
|
|
93
|
-
this.log.warn('No L1 consensus host urls configured');
|
|
94
121
|
}
|
|
95
122
|
if (this.archiveClient) {
|
|
96
123
|
try {
|
|
@@ -99,14 +126,12 @@ export class HttpBlobClient {
|
|
|
99
126
|
latest,
|
|
100
127
|
archiveUrl
|
|
101
128
|
});
|
|
102
|
-
|
|
129
|
+
archiveSources++;
|
|
103
130
|
} catch (err) {
|
|
104
131
|
this.log.error(`Error reaching archive client`, err, {
|
|
105
132
|
archiveUrl
|
|
106
133
|
});
|
|
107
134
|
}
|
|
108
|
-
} else {
|
|
109
|
-
this.log.warn('No archive client configured');
|
|
110
135
|
}
|
|
111
136
|
if (this.fileStoreClients.length > 0) {
|
|
112
137
|
for (const fileStoreClient of this.fileStoreClients){
|
|
@@ -116,7 +141,7 @@ export class HttpBlobClient {
|
|
|
116
141
|
this.log.info(`FileStore is reachable`, {
|
|
117
142
|
url: fileStoreClient.getBaseUrl()
|
|
118
143
|
});
|
|
119
|
-
|
|
144
|
+
blobSinks++;
|
|
120
145
|
} else {
|
|
121
146
|
this.log.warn(`FileStore is not accessible`, {
|
|
122
147
|
url: fileStoreClient.getBaseUrl()
|
|
@@ -129,12 +154,22 @@ export class HttpBlobClient {
|
|
|
129
154
|
}
|
|
130
155
|
}
|
|
131
156
|
}
|
|
157
|
+
// Emit a single summary after validating all sources
|
|
158
|
+
const successfulSourceCount = consensusSuperNodes + archiveSources + blobSinks;
|
|
159
|
+
let summary = `Blob client running with consensusSuperNodes=${consensusSuperNodes} archiveSources=${archiveSources} blobSinks=${blobSinks}`;
|
|
160
|
+
if (consensusNonSuperNodes > 0) {
|
|
161
|
+
summary += `. ${consensusNonSuperNodes} consensus client(s) ignored because they are not running in supernode or semi-supernode mode`;
|
|
162
|
+
}
|
|
132
163
|
if (successfulSourceCount === 0) {
|
|
133
164
|
if (this.config.blobAllowEmptySources) {
|
|
134
|
-
this.log.warn(
|
|
165
|
+
this.log.warn(summary);
|
|
135
166
|
} else {
|
|
136
|
-
throw new Error(
|
|
167
|
+
throw new Error(summary);
|
|
137
168
|
}
|
|
169
|
+
} else if (consensusSuperNodes === 0) {
|
|
170
|
+
this.log.warn(summary);
|
|
171
|
+
} else {
|
|
172
|
+
this.log.info(summary);
|
|
138
173
|
}
|
|
139
174
|
}
|
|
140
175
|
async sendBlobsToFilestore(blobs) {
|
|
@@ -433,9 +468,9 @@ export class HttpBlobClient {
|
|
|
433
468
|
// Ping execution node to get the parentBeaconBlockRoot for this block
|
|
434
469
|
let parentBeaconBlockRoot;
|
|
435
470
|
const client = createPublicClient({
|
|
436
|
-
transport:
|
|
437
|
-
|
|
438
|
-
|
|
471
|
+
transport: makeL1HttpTransport(l1RpcUrls, {
|
|
472
|
+
timeout: this.config.l1HttpTimeoutMS
|
|
473
|
+
})
|
|
439
474
|
});
|
|
440
475
|
try {
|
|
441
476
|
const res = await client.request({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/blob-client",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0-aztecnr-rc.2",
|
|
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": "4.
|
|
60
|
-
"@aztec/ethereum": "4.
|
|
61
|
-
"@aztec/foundation": "4.
|
|
62
|
-
"@aztec/kv-store": "4.
|
|
63
|
-
"@aztec/stdlib": "4.
|
|
64
|
-
"@aztec/telemetry-client": "4.
|
|
59
|
+
"@aztec/blob-lib": "4.2.0-aztecnr-rc.2",
|
|
60
|
+
"@aztec/ethereum": "4.2.0-aztecnr-rc.2",
|
|
61
|
+
"@aztec/foundation": "4.2.0-aztecnr-rc.2",
|
|
62
|
+
"@aztec/kv-store": "4.2.0-aztecnr-rc.2",
|
|
63
|
+
"@aztec/stdlib": "4.2.0-aztecnr-rc.2",
|
|
64
|
+
"@aztec/telemetry-client": "4.2.0-aztecnr-rc.2",
|
|
65
65
|
"express": "^4.21.2",
|
|
66
66
|
"snappy": "^7.2.2",
|
|
67
67
|
"source-map-support": "^0.5.21",
|
package/src/client/config.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
SecretValue,
|
|
4
4
|
booleanConfigHelper,
|
|
5
5
|
getConfigFromMappings,
|
|
6
|
+
optionalNumberConfigHelper,
|
|
6
7
|
} from '@aztec/foundation/config';
|
|
7
8
|
|
|
8
9
|
import { type BlobArchiveApiConfig, blobArchiveApiConfigMappings } from '../archive/config.js';
|
|
@@ -55,6 +56,9 @@ export interface BlobClientConfig extends BlobArchiveApiConfig {
|
|
|
55
56
|
* Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)
|
|
56
57
|
*/
|
|
57
58
|
blobHealthcheckUploadIntervalMinutes?: number;
|
|
59
|
+
|
|
60
|
+
/** Timeout for HTTP requests to the L1 RPC node in ms. */
|
|
61
|
+
l1HttpTimeoutMS?: number;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
|
|
@@ -108,6 +112,11 @@ export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
|
|
|
108
112
|
description: 'Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)',
|
|
109
113
|
parseEnv: (val: string | undefined) => (val ? +val : undefined),
|
|
110
114
|
},
|
|
115
|
+
l1HttpTimeoutMS: {
|
|
116
|
+
env: 'ETHEREUM_HTTP_TIMEOUT_MS',
|
|
117
|
+
description: 'Timeout for HTTP requests to the L1 RPC node in ms.',
|
|
118
|
+
...optionalNumberConfigHelper(),
|
|
119
|
+
},
|
|
111
120
|
...blobArchiveApiConfigMappings,
|
|
112
121
|
};
|
|
113
122
|
|
package/src/client/http.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Blob, type BlobJson, computeEthVersionedBlobHash } from '@aztec/blob-lib';
|
|
2
|
+
import { makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
2
3
|
import { shuffle } from '@aztec/foundation/array';
|
|
3
4
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
5
6
|
import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
|
|
6
7
|
|
|
7
|
-
import { type RpcBlock, createPublicClient
|
|
8
|
+
import { type RpcBlock, createPublicClient } from 'viem';
|
|
8
9
|
|
|
9
10
|
import { createBlobArchiveClient } from '../archive/factory.js';
|
|
10
11
|
import type { BlobArchiveClient } from '../archive/interface.js';
|
|
@@ -89,44 +90,68 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
89
90
|
const archiveUrl = this.archiveClient?.getBaseUrl();
|
|
90
91
|
this.log.info(`Testing configured blob sources`, { l1ConsensusHostUrls, archiveUrl });
|
|
91
92
|
|
|
92
|
-
let
|
|
93
|
+
let consensusSuperNodes = 0;
|
|
94
|
+
let consensusNonSuperNodes = 0;
|
|
95
|
+
let archiveSources = 0;
|
|
96
|
+
let blobSinks = 0;
|
|
93
97
|
|
|
94
98
|
if (l1ConsensusHostUrls && l1ConsensusHostUrls.length > 0) {
|
|
95
99
|
for (let l1ConsensusHostIndex = 0; l1ConsensusHostIndex < l1ConsensusHostUrls.length; l1ConsensusHostIndex++) {
|
|
96
100
|
const l1ConsensusHostUrl = l1ConsensusHostUrls[l1ConsensusHostIndex];
|
|
97
101
|
try {
|
|
98
102
|
const { url, ...options } = getBeaconNodeFetchOptions(
|
|
99
|
-
`${l1ConsensusHostUrl}/eth/v1/beacon/headers`,
|
|
103
|
+
`${l1ConsensusHostUrl}/eth/v1/beacon/headers/head`,
|
|
100
104
|
this.config,
|
|
101
105
|
l1ConsensusHostIndex,
|
|
102
106
|
);
|
|
103
107
|
const res = await this.fetch(url, options);
|
|
104
|
-
if (res.ok) {
|
|
105
|
-
this.log.info(`L1 consensus host is reachable`, { l1ConsensusHostUrl });
|
|
106
|
-
successfulSourceCount++;
|
|
107
|
-
} else {
|
|
108
|
+
if (!res.ok) {
|
|
108
109
|
this.log.error(`Failure reaching L1 consensus host: ${res.statusText} (${res.status})`, {
|
|
109
110
|
l1ConsensusHostUrl,
|
|
110
111
|
});
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.log.info(`L1 consensus host is reachable`, { l1ConsensusHostUrl });
|
|
116
|
+
|
|
117
|
+
// Check if the host serves blob sidecars (supernode/semi-supernode).
|
|
118
|
+
// Post-Fusaka (PeerDAS), non-supernode beacon nodes no longer serve the
|
|
119
|
+
// blob sidecar endpoint. A 200 response (even with an empty data array
|
|
120
|
+
// for a slot with no blobs) means the node supports serving blob sidecars.
|
|
121
|
+
const body = await res.json();
|
|
122
|
+
const headSlot = body?.data?.header?.message?.slot;
|
|
123
|
+
if (headSlot) {
|
|
124
|
+
const { url: blobUrl, ...blobOptions } = getBeaconNodeFetchOptions(
|
|
125
|
+
`${l1ConsensusHostUrl}/eth/v1/beacon/blobs/${headSlot}`,
|
|
126
|
+
this.config,
|
|
127
|
+
l1ConsensusHostIndex,
|
|
128
|
+
);
|
|
129
|
+
const blobRes = await this.fetch(blobUrl, blobOptions);
|
|
130
|
+
if (blobRes.ok) {
|
|
131
|
+
this.log.info(`L1 consensus host serves blob sidecars (supernode)`, { l1ConsensusHostUrl });
|
|
132
|
+
consensusSuperNodes++;
|
|
133
|
+
} else {
|
|
134
|
+
this.log.info(`L1 consensus host does not serve blob sidecars`, { l1ConsensusHostUrl });
|
|
135
|
+
consensusNonSuperNodes++;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
this.log.info(`L1 consensus host is reachable but could not determine head slot`, { l1ConsensusHostUrl });
|
|
139
|
+
consensusNonSuperNodes++;
|
|
111
140
|
}
|
|
112
141
|
} catch (err) {
|
|
113
142
|
this.log.error(`Error reaching L1 consensus host`, err, { l1ConsensusHostUrl });
|
|
114
143
|
}
|
|
115
144
|
}
|
|
116
|
-
} else {
|
|
117
|
-
this.log.warn('No L1 consensus host urls configured');
|
|
118
145
|
}
|
|
119
146
|
|
|
120
147
|
if (this.archiveClient) {
|
|
121
148
|
try {
|
|
122
149
|
const latest = await this.archiveClient.getLatestBlock();
|
|
123
150
|
this.log.info(`Archive client is reachable and synced to L1 block ${latest.number}`, { latest, archiveUrl });
|
|
124
|
-
|
|
151
|
+
archiveSources++;
|
|
125
152
|
} catch (err) {
|
|
126
153
|
this.log.error(`Error reaching archive client`, err, { archiveUrl });
|
|
127
154
|
}
|
|
128
|
-
} else {
|
|
129
|
-
this.log.warn('No archive client configured');
|
|
130
155
|
}
|
|
131
156
|
|
|
132
157
|
if (this.fileStoreClients.length > 0) {
|
|
@@ -135,7 +160,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
135
160
|
const accessible = await fileStoreClient.testConnection();
|
|
136
161
|
if (accessible) {
|
|
137
162
|
this.log.info(`FileStore is reachable`, { url: fileStoreClient.getBaseUrl() });
|
|
138
|
-
|
|
163
|
+
blobSinks++;
|
|
139
164
|
} else {
|
|
140
165
|
this.log.warn(`FileStore is not accessible`, { url: fileStoreClient.getBaseUrl() });
|
|
141
166
|
}
|
|
@@ -145,12 +170,24 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
145
170
|
}
|
|
146
171
|
}
|
|
147
172
|
|
|
173
|
+
// Emit a single summary after validating all sources
|
|
174
|
+
const successfulSourceCount = consensusSuperNodes + archiveSources + blobSinks;
|
|
175
|
+
|
|
176
|
+
let summary = `Blob client running with consensusSuperNodes=${consensusSuperNodes} archiveSources=${archiveSources} blobSinks=${blobSinks}`;
|
|
177
|
+
if (consensusNonSuperNodes > 0) {
|
|
178
|
+
summary += `. ${consensusNonSuperNodes} consensus client(s) ignored because they are not running in supernode or semi-supernode mode`;
|
|
179
|
+
}
|
|
180
|
+
|
|
148
181
|
if (successfulSourceCount === 0) {
|
|
149
182
|
if (this.config.blobAllowEmptySources) {
|
|
150
|
-
this.log.warn(
|
|
183
|
+
this.log.warn(summary);
|
|
151
184
|
} else {
|
|
152
|
-
throw new Error(
|
|
185
|
+
throw new Error(summary);
|
|
153
186
|
}
|
|
187
|
+
} else if (consensusSuperNodes === 0) {
|
|
188
|
+
this.log.warn(summary);
|
|
189
|
+
} else {
|
|
190
|
+
this.log.info(summary);
|
|
154
191
|
}
|
|
155
192
|
}
|
|
156
193
|
|
|
@@ -497,7 +534,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
497
534
|
// Ping execution node to get the parentBeaconBlockRoot for this block
|
|
498
535
|
let parentBeaconBlockRoot: string | undefined;
|
|
499
536
|
const client = createPublicClient({
|
|
500
|
-
transport:
|
|
537
|
+
transport: makeL1HttpTransport(l1RpcUrls, { timeout: this.config.l1HttpTimeoutMS }),
|
|
501
538
|
});
|
|
502
539
|
try {
|
|
503
540
|
const res: RpcBlock = await client.request({
|