@aztec/blob-client 0.0.1-commit.dbf9cec → 0.0.1-commit.df81a97b5
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
|
@@ -82,4 +82,4 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
82
82
|
*/
|
|
83
83
|
stop(): void;
|
|
84
84
|
}
|
|
85
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
85
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxRQUFRLEVBQStCLE1BQU0saUJBQWlCLENBQUM7QUFHbkYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBT2xFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFDaEYsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBa0J0RCxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUk7SUFqQnZCLFNBQVMsQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLE1BQU0sQ0FBQztJQUMvQixTQUFTLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQztJQUM1QyxTQUFTLENBQUMsUUFBUSxDQUFDLGFBQWEsRUFBRSxpQkFBaUIsR0FBRyxTQUFTLENBQUM7SUFDaEUsU0FBUyxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsT0FBTyxLQUFLLENBQUM7SUFDdkMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsRUFBRSxtQkFBbUIsRUFBRSxDQUFDO0lBQzNELFNBQVMsQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsbUJBQW1CLEdBQUcsU0FBUyxDQUFDO0lBRTFFLE9BQU8sQ0FBQyxRQUFRLENBQVM7SUFDekIsT0FBTyxDQUFDLDJCQUEyQixDQUFDLENBQWlCO0lBRXJELHNGQUFzRjtJQUN0RixPQUFPLENBQUMsaUJBQWlCLENBQUMsQ0FBUztJQUNuQyx1RUFBdUU7SUFDdkUsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBQVM7SUFFdEMsWUFDRSxNQUFNLENBQUMsRUFBRSxnQkFBZ0IsRUFDUixJQUFJLEdBQUU7UUFDckIsTUFBTSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ2hCLGFBQWEsQ0FBQyxFQUFFLGlCQUFpQixDQUFDO1FBQ2xDLGdCQUFnQixDQUFDLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztRQUN6QyxxQkFBcUIsQ0FBQyxFQUFFLG1CQUFtQixDQUFDO1FBQzVDLHlFQUF5RTtRQUN6RSxjQUFjLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsS0FBSyxJQUFJLENBQUM7S0FDckMsRUF3QlA7SUFFRDs7O09BR0c7SUFDSCxPQUFPLENBQUMsc0JBQXNCO0lBVTlCOzs7OztPQUtHO0lBQ0ksV0FBVyxDQUFDLEtBQUssRUFBRSxPQUFPLEdBQUcsSUFBSSxDQUd2QztJQUVZLFdBQVcsa0JBd0d2QjtJQUVZLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBbUJqRTtJQUVEOzs7Ozs7Ozs7Ozs7OztPQWNHO0lBQ1UsY0FBYyxDQUN6QixTQUFTLEVBQUUsS0FBSyxNQUFNLEVBQUUsRUFDeEIsVUFBVSxFQUFFLE1BQU0sRUFBRSxFQUNwQixJQUFJLENBQUMsRUFBRSxxQkFBcUIsR0FDM0IsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBa0pqQjtZQVFhLGFBQWE7SUFzQ2Qsa0JBQWtCLENBQzdCLE9BQU8sRUFBRSxNQUFNLEVBQ2YsZUFBZSxFQUFFLE1BQU0sR0FBRyxNQUFNLEVBQ2hDLFVBQVUsR0FBRSxNQUFNLEVBQU8sRUFDekIsb0JBQW9CLENBQUMsRUFBRSxNQUFNLEdBQzVCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUdqQjtJQUVZLGdCQUFnQixDQUMzQixPQUFPLEVBQUUsTUFBTSxFQUNmLGVBQWUsRUFBRSxNQUFNLEdBQUcsTUFBTSxFQUNoQyxvQkFBb0IsQ0FBQyxFQUFFLE1BQU0sRUFDN0IsVUFBVSxDQUFDLEVBQUUsTUFBTSxFQUFFLEdBQ3BCLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQXNDckI7SUFFRCxPQUFPLENBQUMsaUJBQWlCO1lBcUJYLG1CQUFtQjtZQW1DbkIsYUFBYTtJQTZFM0Isc0NBQXNDO0lBQy9CLGdCQUFnQixJQUFJLGlCQUFpQixHQUFHLFNBQVMsQ0FFdkQ7SUFFRCxpRUFBaUU7SUFDMUQsU0FBUyxJQUFJLE9BQU8sQ0FFMUI7SUFFRDs7OztPQUlHO0lBQ1UsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FXbEM7SUFFRDs7T0FFRztJQUNILE9BQU8sQ0FBQyw4QkFBOEI7WUFnQnhCLGlCQUFpQjtJQTBDL0I7O09BRUc7SUFDSSxJQUFJLElBQUksSUFBSSxDQUtsQjtDQUNGIn0=
|
|
@@ -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;IAkBtD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAjBvB,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,sFAAsF;IACtF,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,uEAAuE;IACvE,OAAO,CAAC,oBAAoB,CAAC,CAAS;IAEtC,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,CAkJjB;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,EAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsCrB;IAED,OAAO,CAAC,iBAAiB;YAqBX,mBAAmB;YAmCnB,aAAa;IA6E3B,sCAAsC;IAC/B,gBAAgB,IAAI,iBAAiB,GAAG,SAAS,CAEvD;IAED,iEAAiE;IAC1D,SAAS,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAWlC;IAED;;OAEG;IACH,OAAO,CAAC,8BAA8B;YAgBxB,iBAAiB;IA0C/B;;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';
|
|
@@ -68,22 +69,50 @@ export class HttpBlobClient {
|
|
|
68
69
|
l1ConsensusHostUrls,
|
|
69
70
|
archiveUrl
|
|
70
71
|
});
|
|
71
|
-
let
|
|
72
|
+
let consensusSuperNodes = 0;
|
|
73
|
+
let consensusNonSuperNodes = 0;
|
|
74
|
+
let archiveSources = 0;
|
|
75
|
+
let blobSinks = 0;
|
|
72
76
|
if (l1ConsensusHostUrls && l1ConsensusHostUrls.length > 0) {
|
|
73
77
|
for(let l1ConsensusHostIndex = 0; l1ConsensusHostIndex < l1ConsensusHostUrls.length; l1ConsensusHostIndex++){
|
|
74
78
|
const l1ConsensusHostUrl = l1ConsensusHostUrls[l1ConsensusHostIndex];
|
|
75
79
|
try {
|
|
76
|
-
const { url, ...options } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrl}/eth/v1/beacon/headers`, this.config, l1ConsensusHostIndex);
|
|
80
|
+
const { url, ...options } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrl}/eth/v1/beacon/headers/head`, this.config, l1ConsensusHostIndex);
|
|
77
81
|
const res = await this.fetch(url, options);
|
|
78
|
-
if (res.ok) {
|
|
79
|
-
this.log.
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
this.log.error(`Failure reaching L1 consensus host: ${res.statusText} (${res.status})`, {
|
|
80
84
|
l1ConsensusHostUrl
|
|
81
85
|
});
|
|
82
|
-
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
this.log.info(`L1 consensus host is reachable`, {
|
|
89
|
+
l1ConsensusHostUrl
|
|
90
|
+
});
|
|
91
|
+
// Check if the host serves blob sidecars (supernode/semi-supernode).
|
|
92
|
+
// Post-Fusaka (PeerDAS), non-supernode beacon nodes no longer serve the
|
|
93
|
+
// blob sidecar endpoint. A 200 response (even with an empty data array
|
|
94
|
+
// for a slot with no blobs) means the node supports serving blob sidecars.
|
|
95
|
+
const body = await res.json();
|
|
96
|
+
const headSlot = body?.data?.header?.message?.slot;
|
|
97
|
+
if (headSlot) {
|
|
98
|
+
const { url: blobUrl, ...blobOptions } = getBeaconNodeFetchOptions(`${l1ConsensusHostUrl}/eth/v1/beacon/blobs/${headSlot}`, this.config, l1ConsensusHostIndex);
|
|
99
|
+
const blobRes = await this.fetch(blobUrl, blobOptions);
|
|
100
|
+
if (blobRes.ok) {
|
|
101
|
+
this.log.info(`L1 consensus host serves blob sidecars (supernode)`, {
|
|
102
|
+
l1ConsensusHostUrl
|
|
103
|
+
});
|
|
104
|
+
consensusSuperNodes++;
|
|
105
|
+
} else {
|
|
106
|
+
this.log.info(`L1 consensus host does not serve blob sidecars`, {
|
|
107
|
+
l1ConsensusHostUrl
|
|
108
|
+
});
|
|
109
|
+
consensusNonSuperNodes++;
|
|
110
|
+
}
|
|
83
111
|
} else {
|
|
84
|
-
this.log.
|
|
112
|
+
this.log.info(`L1 consensus host is reachable but could not determine head slot`, {
|
|
85
113
|
l1ConsensusHostUrl
|
|
86
114
|
});
|
|
115
|
+
consensusNonSuperNodes++;
|
|
87
116
|
}
|
|
88
117
|
} catch (err) {
|
|
89
118
|
this.log.error(`Error reaching L1 consensus host`, err, {
|
|
@@ -91,8 +120,6 @@ export class HttpBlobClient {
|
|
|
91
120
|
});
|
|
92
121
|
}
|
|
93
122
|
}
|
|
94
|
-
} else {
|
|
95
|
-
this.log.warn('No L1 consensus host urls configured');
|
|
96
123
|
}
|
|
97
124
|
if (this.archiveClient) {
|
|
98
125
|
try {
|
|
@@ -101,14 +128,12 @@ export class HttpBlobClient {
|
|
|
101
128
|
latest,
|
|
102
129
|
archiveUrl
|
|
103
130
|
});
|
|
104
|
-
|
|
131
|
+
archiveSources++;
|
|
105
132
|
} catch (err) {
|
|
106
133
|
this.log.error(`Error reaching archive client`, err, {
|
|
107
134
|
archiveUrl
|
|
108
135
|
});
|
|
109
136
|
}
|
|
110
|
-
} else {
|
|
111
|
-
this.log.warn('No archive client configured');
|
|
112
137
|
}
|
|
113
138
|
if (this.fileStoreClients.length > 0) {
|
|
114
139
|
for (const fileStoreClient of this.fileStoreClients){
|
|
@@ -118,7 +143,7 @@ export class HttpBlobClient {
|
|
|
118
143
|
this.log.info(`FileStore is reachable`, {
|
|
119
144
|
url: fileStoreClient.getBaseUrl()
|
|
120
145
|
});
|
|
121
|
-
|
|
146
|
+
blobSinks++;
|
|
122
147
|
} else {
|
|
123
148
|
this.log.warn(`FileStore is not accessible`, {
|
|
124
149
|
url: fileStoreClient.getBaseUrl()
|
|
@@ -131,12 +156,22 @@ export class HttpBlobClient {
|
|
|
131
156
|
}
|
|
132
157
|
}
|
|
133
158
|
}
|
|
159
|
+
// Emit a single summary after validating all sources
|
|
160
|
+
const successfulSourceCount = consensusSuperNodes + archiveSources + blobSinks;
|
|
161
|
+
let summary = `Blob client running with consensusSuperNodes=${consensusSuperNodes} archiveSources=${archiveSources} blobSinks=${blobSinks}`;
|
|
162
|
+
if (consensusNonSuperNodes > 0) {
|
|
163
|
+
summary += `. ${consensusNonSuperNodes} consensus client(s) ignored because they are not running in supernode or semi-supernode mode`;
|
|
164
|
+
}
|
|
134
165
|
if (successfulSourceCount === 0) {
|
|
135
166
|
if (this.config.blobAllowEmptySources) {
|
|
136
|
-
this.log.warn(
|
|
167
|
+
this.log.warn(summary);
|
|
137
168
|
} else {
|
|
138
|
-
throw new Error(
|
|
169
|
+
throw new Error(summary);
|
|
139
170
|
}
|
|
171
|
+
} else if (consensusSuperNodes === 0) {
|
|
172
|
+
this.log.warn(summary);
|
|
173
|
+
} else {
|
|
174
|
+
this.log.info(summary);
|
|
140
175
|
}
|
|
141
176
|
}
|
|
142
177
|
async sendBlobsToFilestore(blobs) {
|
|
@@ -450,9 +485,9 @@ export class HttpBlobClient {
|
|
|
450
485
|
return undefined;
|
|
451
486
|
}
|
|
452
487
|
const client = createPublicClient({
|
|
453
|
-
transport:
|
|
454
|
-
|
|
455
|
-
|
|
488
|
+
transport: makeL1HttpTransport(l1RpcUrls, {
|
|
489
|
+
timeout: this.config.l1HttpTimeoutMS
|
|
490
|
+
})
|
|
456
491
|
});
|
|
457
492
|
try {
|
|
458
493
|
const res = await client.request({
|
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.df81a97b5",
|
|
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.df81a97b5",
|
|
60
|
+
"@aztec/ethereum": "0.0.1-commit.df81a97b5",
|
|
61
|
+
"@aztec/foundation": "0.0.1-commit.df81a97b5",
|
|
62
|
+
"@aztec/kv-store": "0.0.1-commit.df81a97b5",
|
|
63
|
+
"@aztec/stdlib": "0.0.1-commit.df81a97b5",
|
|
64
|
+
"@aztec/telemetry-client": "0.0.1-commit.df81a97b5",
|
|
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';
|
|
@@ -94,44 +95,68 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
94
95
|
const archiveUrl = this.archiveClient?.getBaseUrl();
|
|
95
96
|
this.log.info(`Testing configured blob sources`, { l1ConsensusHostUrls, archiveUrl });
|
|
96
97
|
|
|
97
|
-
let
|
|
98
|
+
let consensusSuperNodes = 0;
|
|
99
|
+
let consensusNonSuperNodes = 0;
|
|
100
|
+
let archiveSources = 0;
|
|
101
|
+
let blobSinks = 0;
|
|
98
102
|
|
|
99
103
|
if (l1ConsensusHostUrls && l1ConsensusHostUrls.length > 0) {
|
|
100
104
|
for (let l1ConsensusHostIndex = 0; l1ConsensusHostIndex < l1ConsensusHostUrls.length; l1ConsensusHostIndex++) {
|
|
101
105
|
const l1ConsensusHostUrl = l1ConsensusHostUrls[l1ConsensusHostIndex];
|
|
102
106
|
try {
|
|
103
107
|
const { url, ...options } = getBeaconNodeFetchOptions(
|
|
104
|
-
`${l1ConsensusHostUrl}/eth/v1/beacon/headers`,
|
|
108
|
+
`${l1ConsensusHostUrl}/eth/v1/beacon/headers/head`,
|
|
105
109
|
this.config,
|
|
106
110
|
l1ConsensusHostIndex,
|
|
107
111
|
);
|
|
108
112
|
const res = await this.fetch(url, options);
|
|
109
|
-
if (res.ok) {
|
|
110
|
-
this.log.info(`L1 consensus host is reachable`, { l1ConsensusHostUrl });
|
|
111
|
-
successfulSourceCount++;
|
|
112
|
-
} else {
|
|
113
|
+
if (!res.ok) {
|
|
113
114
|
this.log.error(`Failure reaching L1 consensus host: ${res.statusText} (${res.status})`, {
|
|
114
115
|
l1ConsensusHostUrl,
|
|
115
116
|
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.log.info(`L1 consensus host is reachable`, { l1ConsensusHostUrl });
|
|
121
|
+
|
|
122
|
+
// Check if the host serves blob sidecars (supernode/semi-supernode).
|
|
123
|
+
// Post-Fusaka (PeerDAS), non-supernode beacon nodes no longer serve the
|
|
124
|
+
// blob sidecar endpoint. A 200 response (even with an empty data array
|
|
125
|
+
// for a slot with no blobs) means the node supports serving blob sidecars.
|
|
126
|
+
const body = await res.json();
|
|
127
|
+
const headSlot = body?.data?.header?.message?.slot;
|
|
128
|
+
if (headSlot) {
|
|
129
|
+
const { url: blobUrl, ...blobOptions } = getBeaconNodeFetchOptions(
|
|
130
|
+
`${l1ConsensusHostUrl}/eth/v1/beacon/blobs/${headSlot}`,
|
|
131
|
+
this.config,
|
|
132
|
+
l1ConsensusHostIndex,
|
|
133
|
+
);
|
|
134
|
+
const blobRes = await this.fetch(blobUrl, blobOptions);
|
|
135
|
+
if (blobRes.ok) {
|
|
136
|
+
this.log.info(`L1 consensus host serves blob sidecars (supernode)`, { l1ConsensusHostUrl });
|
|
137
|
+
consensusSuperNodes++;
|
|
138
|
+
} else {
|
|
139
|
+
this.log.info(`L1 consensus host does not serve blob sidecars`, { l1ConsensusHostUrl });
|
|
140
|
+
consensusNonSuperNodes++;
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
this.log.info(`L1 consensus host is reachable but could not determine head slot`, { l1ConsensusHostUrl });
|
|
144
|
+
consensusNonSuperNodes++;
|
|
116
145
|
}
|
|
117
146
|
} catch (err) {
|
|
118
147
|
this.log.error(`Error reaching L1 consensus host`, err, { l1ConsensusHostUrl });
|
|
119
148
|
}
|
|
120
149
|
}
|
|
121
|
-
} else {
|
|
122
|
-
this.log.warn('No L1 consensus host urls configured');
|
|
123
150
|
}
|
|
124
151
|
|
|
125
152
|
if (this.archiveClient) {
|
|
126
153
|
try {
|
|
127
154
|
const latest = await this.archiveClient.getLatestBlock();
|
|
128
155
|
this.log.info(`Archive client is reachable and synced to L1 block ${latest.number}`, { latest, archiveUrl });
|
|
129
|
-
|
|
156
|
+
archiveSources++;
|
|
130
157
|
} catch (err) {
|
|
131
158
|
this.log.error(`Error reaching archive client`, err, { archiveUrl });
|
|
132
159
|
}
|
|
133
|
-
} else {
|
|
134
|
-
this.log.warn('No archive client configured');
|
|
135
160
|
}
|
|
136
161
|
|
|
137
162
|
if (this.fileStoreClients.length > 0) {
|
|
@@ -140,7 +165,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
140
165
|
const accessible = await fileStoreClient.testConnection();
|
|
141
166
|
if (accessible) {
|
|
142
167
|
this.log.info(`FileStore is reachable`, { url: fileStoreClient.getBaseUrl() });
|
|
143
|
-
|
|
168
|
+
blobSinks++;
|
|
144
169
|
} else {
|
|
145
170
|
this.log.warn(`FileStore is not accessible`, { url: fileStoreClient.getBaseUrl() });
|
|
146
171
|
}
|
|
@@ -150,12 +175,24 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
150
175
|
}
|
|
151
176
|
}
|
|
152
177
|
|
|
178
|
+
// Emit a single summary after validating all sources
|
|
179
|
+
const successfulSourceCount = consensusSuperNodes + archiveSources + blobSinks;
|
|
180
|
+
|
|
181
|
+
let summary = `Blob client running with consensusSuperNodes=${consensusSuperNodes} archiveSources=${archiveSources} blobSinks=${blobSinks}`;
|
|
182
|
+
if (consensusNonSuperNodes > 0) {
|
|
183
|
+
summary += `. ${consensusNonSuperNodes} consensus client(s) ignored because they are not running in supernode or semi-supernode mode`;
|
|
184
|
+
}
|
|
185
|
+
|
|
153
186
|
if (successfulSourceCount === 0) {
|
|
154
187
|
if (this.config.blobAllowEmptySources) {
|
|
155
|
-
this.log.warn(
|
|
188
|
+
this.log.warn(summary);
|
|
156
189
|
} else {
|
|
157
|
-
throw new Error(
|
|
190
|
+
throw new Error(summary);
|
|
158
191
|
}
|
|
192
|
+
} else if (consensusSuperNodes === 0) {
|
|
193
|
+
this.log.warn(summary);
|
|
194
|
+
} else {
|
|
195
|
+
this.log.info(summary);
|
|
159
196
|
}
|
|
160
197
|
}
|
|
161
198
|
|
|
@@ -532,7 +569,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
532
569
|
}
|
|
533
570
|
|
|
534
571
|
const client = createPublicClient({
|
|
535
|
-
transport:
|
|
572
|
+
transport: makeL1HttpTransport(l1RpcUrls, { timeout: this.config.l1HttpTimeoutMS }),
|
|
536
573
|
});
|
|
537
574
|
try {
|
|
538
575
|
const res: RpcBlock = await client.request({
|