@aztec/blob-client 0.0.1-commit.03f7ef2 → 0.0.1-commit.04d373f
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/README.md +10 -1
- package/dest/archive/blobscan_archive_client.d.ts +8 -92
- package/dest/archive/blobscan_archive_client.d.ts.map +1 -1
- package/dest/archive/config.js +1 -1
- package/dest/archive/instrumentation.d.ts +1 -1
- package/dest/archive/instrumentation.d.ts.map +1 -1
- package/dest/archive/instrumentation.js +13 -13
- package/dest/blobstore/blob_store_test_suite.js +9 -9
- package/dest/client/config.d.ts +11 -1
- package/dest/client/config.d.ts.map +1 -1
- package/dest/client/config.js +22 -2
- package/dest/client/factory.d.ts +5 -6
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +13 -4
- package/dest/client/http.d.ts +34 -10
- package/dest/client/http.d.ts.map +1 -1
- package/dest/client/http.js +304 -139
- package/dest/client/interface.d.ts +19 -4
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/local.d.ts +3 -1
- package/dest/client/local.d.ts.map +1 -1
- package/dest/client/local.js +3 -0
- package/dest/client/tests.js +3 -3
- 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/dest/filestore/filestore_blob_client.d.ts +14 -2
- package/dest/filestore/filestore_blob_client.d.ts.map +1 -1
- package/dest/filestore/filestore_blob_client.js +29 -5
- package/dest/filestore/healthcheck.d.ts +5 -0
- package/dest/filestore/healthcheck.d.ts.map +1 -0
- package/dest/filestore/healthcheck.js +3 -0
- package/package.json +9 -9
- package/src/archive/config.ts +1 -1
- package/src/archive/instrumentation.ts +22 -13
- package/src/blobstore/blob_store_test_suite.ts +9 -9
- package/src/client/config.ts +36 -1
- package/src/client/factory.ts +17 -5
- package/src/client/http.ts +357 -134
- package/src/client/interface.ts +18 -3
- package/src/client/local.ts +5 -0
- package/src/client/tests.ts +2 -2
- package/src/filestore/factory.ts +7 -2
- package/src/filestore/filestore_blob_client.ts +33 -5
- package/src/filestore/healthcheck.ts +5 -0
package/src/client/interface.ts
CHANGED
|
@@ -6,11 +6,20 @@ import type { Blob } from '@aztec/blob-lib';
|
|
|
6
6
|
export interface GetBlobSidecarOptions {
|
|
7
7
|
/**
|
|
8
8
|
* True if the archiver is catching up (historical sync), false if near tip.
|
|
9
|
-
*
|
|
10
|
-
* - Historical: FileStore first (data should exist), then L1 consensus, then archive (eg. blobscan)
|
|
11
|
-
* - Near tip: FileStore first with no retries (data should exist), L1 consensus second (freshest data), then FileStore with retries, then archive (eg. blobscan)
|
|
9
|
+
* Historical sync uses a shorter retry backoff since blobs should already exist.
|
|
12
10
|
*/
|
|
13
11
|
isHistoricalSync?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The parent beacon block root for the L1 block containing the blobs.
|
|
14
|
+
* If provided, skips the eth_getBlockByHash execution RPC call inside getSlotNumber.
|
|
15
|
+
*/
|
|
16
|
+
parentBeaconBlockRoot?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The timestamp of the L1 execution block containing the blobs.
|
|
19
|
+
* When provided alongside a cached beacon genesis config (fetched at startup), allows computing
|
|
20
|
+
* the beacon slot directly via timestamp math, skipping the beacon headers network call entirely.
|
|
21
|
+
*/
|
|
22
|
+
l1BlockTimestamp?: bigint;
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
export interface BlobClientInterface {
|
|
@@ -18,6 +27,12 @@ export interface BlobClientInterface {
|
|
|
18
27
|
sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
|
|
19
28
|
/** Fetches the given blob sidecars by block hash and blob hashes. */
|
|
20
29
|
getBlobSidecar(blockId: string, blobHashes?: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
30
|
+
/** Starts the blob client (e.g., uploads healthcheck file if not exists). */
|
|
31
|
+
start?(): Promise<void>;
|
|
21
32
|
/** Tests all configured blob sources and logs whether they are reachable or not. */
|
|
22
33
|
testSources(): Promise<void>;
|
|
34
|
+
/** Stops the blob client, clearing any periodic tasks. */
|
|
35
|
+
stop?(): void;
|
|
36
|
+
/** Returns true if this client can upload blobs to filestore. */
|
|
37
|
+
canUpload(): boolean;
|
|
23
38
|
}
|
package/src/client/local.ts
CHANGED
|
@@ -22,4 +22,9 @@ export class LocalBlobClient implements BlobClientInterface {
|
|
|
22
22
|
public getBlobSidecar(_blockId: string, blobHashes: Buffer[], _opts?: GetBlobSidecarOptions): Promise<Blob[]> {
|
|
23
23
|
return this.blobStore.getBlobsByHashes(blobHashes);
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/** Returns true if this client can upload blobs. Always true for local client. */
|
|
27
|
+
public canUpload(): boolean {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
25
30
|
}
|
package/src/client/tests.ts
CHANGED
|
@@ -28,7 +28,7 @@ export function runBlobClientTests(
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
it('should send and retrieve blobs by hash', async () => {
|
|
31
|
-
const blob = makeRandomBlob(5);
|
|
31
|
+
const blob = await makeRandomBlob(5);
|
|
32
32
|
const blobHash = blob.getEthVersionedBlobHash();
|
|
33
33
|
|
|
34
34
|
await client.sendBlobsToFilestore([blob]);
|
|
@@ -39,7 +39,7 @@ export function runBlobClientTests(
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
it('should handle multiple blobs', async () => {
|
|
42
|
-
const blobs = Array.from({ length: 3 }, () => makeRandomBlob(7));
|
|
42
|
+
const blobs = await Promise.all(Array.from({ length: 3 }, () => makeRandomBlob(7)));
|
|
43
43
|
const blobHashes = blobs.map(blob => blob.getEthVersionedBlobHash());
|
|
44
44
|
|
|
45
45
|
await client.sendBlobsToFilestore(blobs);
|
package/src/filestore/factory.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import {
|
|
3
3
|
type FileStore,
|
|
4
|
+
type HttpFileStoreOptions,
|
|
4
5
|
type ReadOnlyFileStore,
|
|
5
6
|
createFileStore,
|
|
6
7
|
createReadOnlyFileStore,
|
|
@@ -44,16 +45,19 @@ export async function createReadOnlyFileStoreBlobClient(
|
|
|
44
45
|
storeUrl: string,
|
|
45
46
|
metadata: BlobFileStoreMetadata,
|
|
46
47
|
logger?: Logger,
|
|
48
|
+
httpOptions?: HttpFileStoreOptions,
|
|
47
49
|
): Promise<FileStoreBlobClient>;
|
|
48
50
|
export async function createReadOnlyFileStoreBlobClient(
|
|
49
51
|
storeUrl: string | undefined,
|
|
50
52
|
metadata: BlobFileStoreMetadata,
|
|
51
53
|
logger?: Logger,
|
|
54
|
+
httpOptions?: HttpFileStoreOptions,
|
|
52
55
|
): Promise<FileStoreBlobClient | undefined>;
|
|
53
56
|
export async function createReadOnlyFileStoreBlobClient(
|
|
54
57
|
storeUrl: string | undefined,
|
|
55
58
|
metadata: BlobFileStoreMetadata,
|
|
56
59
|
logger?: Logger,
|
|
60
|
+
httpOptions?: HttpFileStoreOptions,
|
|
57
61
|
): Promise<FileStoreBlobClient | undefined> {
|
|
58
62
|
if (!storeUrl) {
|
|
59
63
|
return undefined;
|
|
@@ -64,7 +68,7 @@ export async function createReadOnlyFileStoreBlobClient(
|
|
|
64
68
|
|
|
65
69
|
log.debug(`Creating read-only filestore blob client`, { storeUrl, basePath });
|
|
66
70
|
|
|
67
|
-
const store: ReadOnlyFileStore = await createReadOnlyFileStore(storeUrl, log);
|
|
71
|
+
const store: ReadOnlyFileStore = await createReadOnlyFileStore(storeUrl, log, httpOptions);
|
|
68
72
|
return new FileStoreBlobClient(store, basePath, log);
|
|
69
73
|
}
|
|
70
74
|
|
|
@@ -80,6 +84,7 @@ export async function createReadOnlyFileStoreBlobClients(
|
|
|
80
84
|
storeUrls: string[] | undefined,
|
|
81
85
|
metadata: BlobFileStoreMetadata,
|
|
82
86
|
logger?: Logger,
|
|
87
|
+
httpOptions?: HttpFileStoreOptions,
|
|
83
88
|
): Promise<FileStoreBlobClient[]> {
|
|
84
89
|
if (!storeUrls || storeUrls.length === 0) {
|
|
85
90
|
return [];
|
|
@@ -90,7 +95,7 @@ export async function createReadOnlyFileStoreBlobClients(
|
|
|
90
95
|
|
|
91
96
|
for (const storeUrl of storeUrls) {
|
|
92
97
|
try {
|
|
93
|
-
const client = await createReadOnlyFileStoreBlobClient(storeUrl, metadata, log);
|
|
98
|
+
const client = await createReadOnlyFileStoreBlobClient(storeUrl, metadata, log, httpOptions);
|
|
94
99
|
if (client) {
|
|
95
100
|
clients.push(client);
|
|
96
101
|
}
|
|
@@ -3,6 +3,7 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
|
3
3
|
import type { FileStore, ReadOnlyFileStore } from '@aztec/stdlib/file-store';
|
|
4
4
|
|
|
5
5
|
import { inboundTransform, outboundTransform } from '../encoding/index.js';
|
|
6
|
+
import { HEALTHCHECK_CONTENT, HEALTHCHECK_FILENAME } from './healthcheck.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A blob client that uses a FileStore (S3/GCS/local) as the data source.
|
|
@@ -27,6 +28,14 @@ export class FileStoreBlobClient {
|
|
|
27
28
|
return `${this.basePath}/blobs/${versionedBlobHash}.data`;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Get the path for the healthcheck file.
|
|
33
|
+
* Format: basePath/.healthcheck
|
|
34
|
+
*/
|
|
35
|
+
private healthcheckPath(): string {
|
|
36
|
+
return `${this.basePath}/${HEALTHCHECK_FILENAME}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
/**
|
|
31
40
|
* Fetch blobs by their versioned hashes.
|
|
32
41
|
* @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
|
|
@@ -104,12 +113,31 @@ export class FileStoreBlobClient {
|
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
/**
|
|
107
|
-
* Test if the filestore connection is working.
|
|
116
|
+
* Test if the filestore connection is working by checking for healthcheck file.
|
|
117
|
+
* The healthcheck file is uploaded periodically by writable clients via HttpBlobClient.start().
|
|
118
|
+
* This provides a uniform connection test across all store types (S3/GCS/Local/HTTP).
|
|
119
|
+
*/
|
|
120
|
+
async testConnection(): Promise<boolean> {
|
|
121
|
+
try {
|
|
122
|
+
return await this.store.exists(this.healthcheckPath());
|
|
123
|
+
} catch (err: any) {
|
|
124
|
+
this.log.warn(`Connection test failed: ${err?.message ?? String(err)}`);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Upload the healthcheck file if it doesn't already exist.
|
|
131
|
+
* This enables read-only clients (HTTP) to verify connectivity.
|
|
108
132
|
*/
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
133
|
+
async uploadHealthcheck(): Promise<void> {
|
|
134
|
+
if (!this.isWritable()) {
|
|
135
|
+
this.log.trace('Cannot upload healthcheck: store is read-only');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const path = this.healthcheckPath();
|
|
139
|
+
await (this.store as FileStore).save(path, Buffer.from(HEALTHCHECK_CONTENT));
|
|
140
|
+
this.log.debug(`Uploaded healthcheck file to ${path}`);
|
|
113
141
|
}
|
|
114
142
|
|
|
115
143
|
/**
|