@aztec/blob-client 3.0.0-nightly.20251223 → 3.0.0-nightly.20251225
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/archive/blobscan_archive_client.d.ts +1 -2
- package/dest/archive/blobscan_archive_client.d.ts.map +1 -1
- package/dest/archive/blobscan_archive_client.js +1 -4
- package/dest/blobstore/blob_store_test_suite.d.ts +1 -1
- package/dest/blobstore/blob_store_test_suite.d.ts.map +1 -1
- package/dest/blobstore/blob_store_test_suite.js +18 -49
- package/dest/blobstore/interface.d.ts +4 -4
- package/dest/blobstore/interface.d.ts.map +1 -1
- package/dest/blobstore/memory_blob_store.d.ts +4 -4
- package/dest/blobstore/memory_blob_store.d.ts.map +1 -1
- package/dest/blobstore/memory_blob_store.js +3 -3
- package/dest/client/http.d.ts +7 -7
- package/dest/client/http.d.ts.map +1 -1
- package/dest/client/http.js +21 -24
- package/dest/client/interface.d.ts +3 -4
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/local.d.ts +2 -3
- package/dest/client/local.d.ts.map +1 -1
- package/dest/client/local.js +2 -4
- package/dest/client/tests.d.ts +1 -1
- package/dest/client/tests.d.ts.map +1 -1
- package/dest/client/tests.js +2 -16
- package/dest/filestore/filestore_blob_client.d.ts +3 -4
- package/dest/filestore/filestore_blob_client.d.ts.map +1 -1
- package/dest/filestore/filestore_blob_client.js +4 -12
- package/package.json +7 -8
- package/src/archive/blobscan_archive_client.ts +8 -10
- package/src/blobstore/blob_store_test_suite.ts +15 -42
- package/src/blobstore/interface.ts +3 -3
- package/src/blobstore/memory_blob_store.ts +6 -6
- package/src/client/http.ts +32 -36
- package/src/client/interface.ts +2 -9
- package/src/client/local.ts +2 -9
- package/src/client/tests.ts +2 -18
- package/src/filestore/filestore_blob_client.ts +5 -13
- package/dest/types/api.d.ts +0 -65
- package/dest/types/api.d.ts.map +0 -1
- package/dest/types/api.js +0 -22
- package/dest/types/blob_with_index.d.ts +0 -25
- package/dest/types/blob_with_index.d.ts.map +0 -1
- package/dest/types/blob_with_index.js +0 -43
- package/dest/types/index.d.ts +0 -2
- package/dest/types/index.d.ts.map +0 -1
- package/dest/types/index.js +0 -1
- package/src/types/api.ts +0 -50
- package/src/types/blob_with_index.ts +0 -48
- package/src/types/index.ts +0 -1
|
@@ -84,7 +84,6 @@ export declare const BlobscanBlockResponseSchema: z.ZodEffects<z.ZodObject<{
|
|
|
84
84
|
}>, {
|
|
85
85
|
blob: string;
|
|
86
86
|
kzg_commitment: string;
|
|
87
|
-
index: string;
|
|
88
87
|
}[], {
|
|
89
88
|
hash: string;
|
|
90
89
|
slot: number;
|
|
@@ -144,4 +143,4 @@ export declare class BlobscanArchiveClient implements BlobArchiveClient {
|
|
|
144
143
|
getBlobsFromBlock(blockId: string): Promise<BlobJson[] | undefined>;
|
|
145
144
|
getBlobData(id: string): Promise<Buffer | undefined>;
|
|
146
145
|
}
|
|
147
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
146
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvYnNjYW5fYXJjaGl2ZV9jbGllbnQuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcmNoaXZlL2Jsb2JzY2FuX2FyY2hpdmVfY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFFBQVEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBSXRELE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBc0IsTUFBTSx5QkFBeUIsQ0FBQztBQUVuRixPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBR3hCLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEQsZUFBTyxNQUFNLDJCQUEyQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQStCdkMsQ0FBQztBQUdGLGVBQU8sTUFBTSw0QkFBNEI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBUXZDLENBQUM7QUFFSCxxQkFBYSxxQkFBc0IsWUFBVyxpQkFBaUI7SUFDN0QsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQXVEO0lBQzlFLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUErQztJQUN6RSxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FTcEI7SUFFRixPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBTTtJQUU5QixPQUFPLENBQUMsZUFBZSxDQUFtQztJQUUxRCxZQUFZLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxHQUFFLGVBQXNDLEVBVTdFO0lBRVksY0FBYyxJQUFJLE9BQU8sQ0FBQztRQUFFLElBQUksRUFBRSxNQUFNLENBQUM7UUFBQyxNQUFNLEVBQUUsTUFBTSxDQUFDO1FBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQTtLQUFFLENBQUMsQ0EyQnJGO0lBRU0sVUFBVSxJQUFJLE1BQU0sQ0FFMUI7SUFFWSxpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxRQUFRLEVBQUUsR0FBRyxTQUFTLENBQUMsQ0E0Qi9FO0lBRVksV0FBVyxDQUFDLEVBQUUsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUMsQ0EwQmhFO0NBQ0YifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blobscan_archive_client.d.ts","sourceRoot":"","sources":["../../src/archive/blobscan_archive_client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAEnF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,eAAO,MAAM,2BAA2B
|
|
1
|
+
{"version":3,"file":"blobscan_archive_client.d.ts","sourceRoot":"","sources":["../../src/archive/blobscan_archive_client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAEnF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BvC,CAAC;AAGF,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;EAQvC,CAAC;AAEH,qBAAa,qBAAsB,YAAW,iBAAiB;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuD;IAC9E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+C;IACzE,OAAO,CAAC,QAAQ,CAAC,KAAK,CASpB;IAEF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAE9B,OAAO,CAAC,eAAe,CAAmC;IAE1D,YAAY,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,eAAsC,EAU7E;IAEY,cAAc,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CA2BrF;IAEM,UAAU,IAAI,MAAM,CAE1B;IAEY,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,CA4B/E;IAEY,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA0BhE;CACF"}
|
|
@@ -23,10 +23,7 @@ export const BlobscanBlockResponseSchema = zodFor()(z.object({
|
|
|
23
23
|
blob: blob.data,
|
|
24
24
|
// eslint-disable-next-line camelcase
|
|
25
25
|
kzg_commitment: blob.commitment
|
|
26
|
-
})))
|
|
27
|
-
...blob,
|
|
28
|
-
index: index.toString()
|
|
29
|
-
}))));
|
|
26
|
+
})))));
|
|
30
27
|
// Response from https://api.blobscan.com/blocks?sort=desc&type=canonical
|
|
31
28
|
export const BlobscanBlocksResponseSchema = z.object({
|
|
32
29
|
blocks: z.array(z.object({
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { BlobStore } from './interface.js';
|
|
2
2
|
export declare function describeBlobStore(getBlobStore: () => Promise<BlobStore>): void;
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvYl9zdG9yZV90ZXN0X3N1aXRlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYmxvYnN0b3JlL2Jsb2Jfc3RvcmVfdGVzdF9zdWl0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFHQSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVoRCx3QkFBZ0IsaUJBQWlCLENBQUMsWUFBWSxFQUFFLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQXdHdkUifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blob_store_test_suite.d.ts","sourceRoot":"","sources":["../../src/blobstore/blob_store_test_suite.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"blob_store_test_suite.d.ts","sourceRoot":"","sources":["../../src/blobstore/blob_store_test_suite.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,QAwGvE"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Blob } from '@aztec/blob-lib';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
-
import { BlobWithIndex } from '../types/index.js';
|
|
4
3
|
export function describeBlobStore(getBlobStore) {
|
|
5
4
|
let blobStore;
|
|
6
5
|
beforeEach(async ()=>{
|
|
@@ -14,11 +13,10 @@ export function describeBlobStore(getBlobStore) {
|
|
|
14
13
|
Fr.random()
|
|
15
14
|
];
|
|
16
15
|
const blob = Blob.fromFields(testFields);
|
|
17
|
-
const blobWithIndex = new BlobWithIndex(blob, 0);
|
|
18
16
|
const blobHash = blob.getEthVersionedBlobHash();
|
|
19
17
|
// Store the blob
|
|
20
18
|
await blobStore.addBlobs([
|
|
21
|
-
|
|
19
|
+
blob
|
|
22
20
|
]);
|
|
23
21
|
// Retrieve the blob by hash
|
|
24
22
|
const retrievedBlobs = await blobStore.getBlobsByHashes([
|
|
@@ -26,7 +24,7 @@ export function describeBlobStore(getBlobStore) {
|
|
|
26
24
|
]);
|
|
27
25
|
// Verify the blob was retrieved and matches
|
|
28
26
|
expect(retrievedBlobs.length).toBe(1);
|
|
29
|
-
expect(retrievedBlobs[0]
|
|
27
|
+
expect(retrievedBlobs[0]).toEqual(blob);
|
|
30
28
|
});
|
|
31
29
|
it('should handle multiple blobs stored and retrieved by their hashes', async ()=>{
|
|
32
30
|
// Create two different blobs
|
|
@@ -39,14 +37,12 @@ export function describeBlobStore(getBlobStore) {
|
|
|
39
37
|
Fr.random(),
|
|
40
38
|
Fr.random()
|
|
41
39
|
]);
|
|
42
|
-
const blobWithIndex1 = new BlobWithIndex(blob1, 0);
|
|
43
|
-
const blobWithIndex2 = new BlobWithIndex(blob2, 1);
|
|
44
40
|
const blobHash1 = blob1.getEthVersionedBlobHash();
|
|
45
41
|
const blobHash2 = blob2.getEthVersionedBlobHash();
|
|
46
42
|
// Store both blobs
|
|
47
43
|
await blobStore.addBlobs([
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
blob1,
|
|
45
|
+
blob2
|
|
50
46
|
]);
|
|
51
47
|
// Retrieve and verify both blobs
|
|
52
48
|
const retrievedBlobs = await blobStore.getBlobsByHashes([
|
|
@@ -54,8 +50,8 @@ export function describeBlobStore(getBlobStore) {
|
|
|
54
50
|
blobHash2
|
|
55
51
|
]);
|
|
56
52
|
expect(retrievedBlobs.length).toBe(2);
|
|
57
|
-
expect(retrievedBlobs[0]
|
|
58
|
-
expect(retrievedBlobs[1]
|
|
53
|
+
expect(retrievedBlobs[0]).toEqual(blob1);
|
|
54
|
+
expect(retrievedBlobs[1]).toEqual(blob2);
|
|
59
55
|
});
|
|
60
56
|
it('should return empty array for non-existent blob hash', async ()=>{
|
|
61
57
|
// Create a random hash that doesn't exist
|
|
@@ -66,29 +62,6 @@ export function describeBlobStore(getBlobStore) {
|
|
|
66
62
|
]);
|
|
67
63
|
expect(retrievedBlobs).toEqual([]);
|
|
68
64
|
});
|
|
69
|
-
it('should handle storing blobs with different indices', async ()=>{
|
|
70
|
-
// Create blobs with different indices
|
|
71
|
-
const blob1 = Blob.fromFields([
|
|
72
|
-
Fr.random()
|
|
73
|
-
]);
|
|
74
|
-
const blob2 = Blob.fromFields([
|
|
75
|
-
Fr.random()
|
|
76
|
-
]);
|
|
77
|
-
const blobWithIndex1 = new BlobWithIndex(blob1, 0);
|
|
78
|
-
const blobWithIndex2 = new BlobWithIndex(blob2, 1);
|
|
79
|
-
await blobStore.addBlobs([
|
|
80
|
-
blobWithIndex1,
|
|
81
|
-
blobWithIndex2
|
|
82
|
-
]);
|
|
83
|
-
const blobHash1 = blob1.getEthVersionedBlobHash();
|
|
84
|
-
const blobHash2 = blob2.getEthVersionedBlobHash();
|
|
85
|
-
const retrievedBlobs = await blobStore.getBlobsByHashes([
|
|
86
|
-
blobHash1,
|
|
87
|
-
blobHash2
|
|
88
|
-
]);
|
|
89
|
-
expect(retrievedBlobs[0].index).toBe(0);
|
|
90
|
-
expect(retrievedBlobs[1].index).toBe(1);
|
|
91
|
-
});
|
|
92
65
|
it('should handle retrieving subset of stored blobs', async ()=>{
|
|
93
66
|
// Store multiple blobs
|
|
94
67
|
const blob1 = Blob.fromFields([
|
|
@@ -101,9 +74,9 @@ export function describeBlobStore(getBlobStore) {
|
|
|
101
74
|
Fr.random()
|
|
102
75
|
]);
|
|
103
76
|
await blobStore.addBlobs([
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
77
|
+
blob1,
|
|
78
|
+
blob2,
|
|
79
|
+
blob3
|
|
107
80
|
]);
|
|
108
81
|
// Retrieve only some of them
|
|
109
82
|
const blobHash1 = blob1.getEthVersionedBlobHash();
|
|
@@ -113,17 +86,16 @@ export function describeBlobStore(getBlobStore) {
|
|
|
113
86
|
blobHash3
|
|
114
87
|
]);
|
|
115
88
|
expect(retrievedBlobs.length).toBe(2);
|
|
116
|
-
expect(retrievedBlobs[0]
|
|
117
|
-
expect(retrievedBlobs[1]
|
|
89
|
+
expect(retrievedBlobs[0]).toEqual(blob1);
|
|
90
|
+
expect(retrievedBlobs[1]).toEqual(blob3);
|
|
118
91
|
});
|
|
119
92
|
it('should handle duplicate blob hashes in request', async ()=>{
|
|
120
93
|
const blob = Blob.fromFields([
|
|
121
94
|
Fr.random()
|
|
122
95
|
]);
|
|
123
|
-
const blobWithIndex = new BlobWithIndex(blob, 0);
|
|
124
96
|
const blobHash = blob.getEthVersionedBlobHash();
|
|
125
97
|
await blobStore.addBlobs([
|
|
126
|
-
|
|
98
|
+
blob
|
|
127
99
|
]);
|
|
128
100
|
// Request the same blob hash multiple times
|
|
129
101
|
const retrievedBlobs = await blobStore.getBlobsByHashes([
|
|
@@ -132,7 +104,7 @@ export function describeBlobStore(getBlobStore) {
|
|
|
132
104
|
]);
|
|
133
105
|
// Implementation may return duplicates or deduplicate - both are valid
|
|
134
106
|
expect(retrievedBlobs.length).toBeGreaterThanOrEqual(1);
|
|
135
|
-
expect(retrievedBlobs[0]
|
|
107
|
+
expect(retrievedBlobs[0]).toEqual(blob);
|
|
136
108
|
});
|
|
137
109
|
it('should overwrite blob when storing with same hash', async ()=>{
|
|
138
110
|
// Create two blobs that will have the same hash (same content)
|
|
@@ -142,23 +114,20 @@ export function describeBlobStore(getBlobStore) {
|
|
|
142
114
|
];
|
|
143
115
|
const blob1 = Blob.fromFields(fields);
|
|
144
116
|
const blob2 = Blob.fromFields(fields);
|
|
145
|
-
// Store with different indices
|
|
146
|
-
const blobWithIndex1 = new BlobWithIndex(blob1, 0);
|
|
147
|
-
const blobWithIndex2 = new BlobWithIndex(blob2, 5);
|
|
148
117
|
const blobHash = blob1.getEthVersionedBlobHash();
|
|
149
118
|
// Store first blob
|
|
150
119
|
await blobStore.addBlobs([
|
|
151
|
-
|
|
120
|
+
blob1
|
|
152
121
|
]);
|
|
153
|
-
// Overwrite with second blob (same hash
|
|
122
|
+
// Overwrite with second blob (same hash)
|
|
154
123
|
await blobStore.addBlobs([
|
|
155
|
-
|
|
124
|
+
blob2
|
|
156
125
|
]);
|
|
157
|
-
// Retrieve and verify it
|
|
126
|
+
// Retrieve and verify it exists
|
|
158
127
|
const retrievedBlobs = await blobStore.getBlobsByHashes([
|
|
159
128
|
blobHash
|
|
160
129
|
]);
|
|
161
130
|
expect(retrievedBlobs.length).toBe(1);
|
|
162
|
-
expect(retrievedBlobs[0]
|
|
131
|
+
expect(retrievedBlobs[0]).toEqual(blob1); // Same content
|
|
163
132
|
});
|
|
164
133
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Blob } from '@aztec/blob-lib';
|
|
2
2
|
export interface BlobStore {
|
|
3
3
|
/**
|
|
4
4
|
* Get blobs by their hashes
|
|
5
5
|
*/
|
|
6
|
-
getBlobsByHashes: (blobHashes: Buffer[]) => Promise<
|
|
6
|
+
getBlobsByHashes: (blobHashes: Buffer[]) => Promise<Blob[]>;
|
|
7
7
|
/**
|
|
8
8
|
* Add blobs to the store, indexed by their hashes
|
|
9
9
|
*/
|
|
10
|
-
addBlobs: (blobs:
|
|
10
|
+
addBlobs: (blobs: Blob[]) => Promise<void>;
|
|
11
11
|
}
|
|
12
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYmxvYnN0b3JlL2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1QyxNQUFNLFdBQVcsU0FBUztJQUN4Qjs7T0FFRztJQUNILGdCQUFnQixFQUFFLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxLQUFLLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzVEOztPQUVHO0lBQ0gsUUFBUSxFQUFFLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztDQUM1QyJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/blobstore/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/blobstore/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Blob } from '@aztec/blob-lib';
|
|
2
2
|
import type { BlobStore } from './interface.js';
|
|
3
3
|
export declare class MemoryBlobStore implements BlobStore {
|
|
4
4
|
private blobs;
|
|
5
|
-
getBlobsByHashes(blobHashes: Buffer[]): Promise<
|
|
6
|
-
addBlobs(blobs:
|
|
5
|
+
getBlobsByHashes(blobHashes: Buffer[]): Promise<Blob[]>;
|
|
6
|
+
addBlobs(blobs: Blob[]): Promise<void>;
|
|
7
7
|
}
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVtb3J5X2Jsb2Jfc3RvcmUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ibG9ic3RvcmUvbWVtb3J5X2Jsb2Jfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBR3ZDLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWhELHFCQUFhLGVBQWdCLFlBQVcsU0FBUztJQUMvQyxPQUFPLENBQUMsS0FBSyxDQUFrQztJQUV4QyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBWTdEO0lBRU0sUUFBUSxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBTzVDO0NBQ0YifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory_blob_store.d.ts","sourceRoot":"","sources":["../../src/blobstore/memory_blob_store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory_blob_store.d.ts","sourceRoot":"","sources":["../../src/blobstore/memory_blob_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,KAAK,CAAkC;IAExC,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAY7D;IAEM,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAO5C;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { Blob } from '@aztec/blob-lib';
|
|
1
2
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
2
|
-
import { BlobWithIndex } from '../types/index.js';
|
|
3
3
|
export class MemoryBlobStore {
|
|
4
4
|
blobs = new Map();
|
|
5
5
|
getBlobsByHashes(blobHashes) {
|
|
@@ -8,14 +8,14 @@ export class MemoryBlobStore {
|
|
|
8
8
|
const key = bufferToHex(blobHash);
|
|
9
9
|
const blobBuffer = this.blobs.get(key);
|
|
10
10
|
if (blobBuffer) {
|
|
11
|
-
results.push(
|
|
11
|
+
results.push(Blob.fromBuffer(blobBuffer));
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
return Promise.resolve(results);
|
|
15
15
|
}
|
|
16
16
|
addBlobs(blobs) {
|
|
17
17
|
for (const blob of blobs){
|
|
18
|
-
const blobHash = blob.
|
|
18
|
+
const blobHash = blob.getEthVersionedBlobHash();
|
|
19
19
|
const key = bufferToHex(blobHash);
|
|
20
20
|
this.blobs.set(key, blob.toBuffer());
|
|
21
21
|
}
|
package/dest/client/http.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Blob, type BlobJson } from '@aztec/blob-lib';
|
|
|
2
2
|
import { type Logger } from '@aztec/foundation/log';
|
|
3
3
|
import type { BlobArchiveClient } from '../archive/interface.js';
|
|
4
4
|
import type { FileStoreBlobClient } from '../filestore/filestore_blob_client.js';
|
|
5
|
-
import { BlobWithIndex } from '../types/blob_with_index.js';
|
|
6
5
|
import { type BlobClientConfig } from './config.js';
|
|
7
6
|
import type { BlobClientInterface, GetBlobSidecarOptions } from './interface.js';
|
|
8
7
|
export declare class HttpBlobClient implements BlobClientInterface {
|
|
@@ -20,7 +19,7 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
20
19
|
fileStoreClients?: FileStoreBlobClient[];
|
|
21
20
|
fileStoreUploadClient?: FileStoreBlobClient;
|
|
22
21
|
/** Callback fired when blobs are successfully fetched from any source */
|
|
23
|
-
onBlobsFetched?: (blobs:
|
|
22
|
+
onBlobsFetched?: (blobs: Blob[]) => void;
|
|
24
23
|
});
|
|
25
24
|
/**
|
|
26
25
|
* Upload fetched blobs to filestore (fire-and-forget).
|
|
@@ -48,16 +47,17 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
48
47
|
*
|
|
49
48
|
* @param blockHash - The block hash
|
|
50
49
|
* @param blobHashes - The blob hashes to fetch
|
|
51
|
-
* @param indices - The indices of the blobs to get
|
|
52
50
|
* @param opts - Options including isHistoricalSync flag
|
|
53
51
|
* @returns The blobs
|
|
54
52
|
*/
|
|
55
|
-
getBlobSidecar(blockHash: `0x${string}`, blobHashes: Buffer[],
|
|
53
|
+
getBlobSidecar(blockHash: `0x${string}`, blobHashes: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
56
54
|
private tryFileStores;
|
|
57
|
-
getBlobSidecarFrom(hostUrl: string, blockHashOrSlot: string | number, blobHashes?: Buffer[],
|
|
58
|
-
getBlobsFromHost(hostUrl: string, blockHashOrSlot: string | number,
|
|
55
|
+
getBlobSidecarFrom(hostUrl: string, blockHashOrSlot: string | number, blobHashes?: Buffer[], l1ConsensusHostIndex?: number): Promise<Blob[]>;
|
|
56
|
+
getBlobsFromHost(hostUrl: string, blockHashOrSlot: string | number, l1ConsensusHostIndex?: number): Promise<BlobJson[]>;
|
|
59
57
|
private fetchBlobSidecars;
|
|
60
58
|
private getLatestSlotNumber;
|
|
61
59
|
private getSlotNumber;
|
|
60
|
+
/** @internal - exposed for testing */
|
|
61
|
+
getArchiveClient(): BlobArchiveClient | undefined;
|
|
62
62
|
}
|
|
63
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxRQUFRLEVBQStCLE1BQU0saUJBQWlCLENBQUM7QUFFbkYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBT2xFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUNqRixPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFDaEYsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBWXRELE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSTtJQVh2QixTQUFTLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7SUFDL0IsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLENBQUM7SUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxDQUFDO0lBQ2hFLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3ZDLFNBQVMsQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztJQUMzRCxTQUFTLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLG1CQUFtQixHQUFHLFNBQVMsQ0FBQztJQUUxRSxPQUFPLENBQUMsUUFBUSxDQUFTO0lBRXpCLFlBQ0UsTUFBTSxDQUFDLEVBQUUsZ0JBQWdCLEVBQ1IsSUFBSSxHQUFFO1FBQ3JCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQztRQUNoQixhQUFhLENBQUMsRUFBRSxpQkFBaUIsQ0FBQztRQUNsQyxnQkFBZ0IsQ0FBQyxFQUFFLG1CQUFtQixFQUFFLENBQUM7UUFDekMscUJBQXFCLENBQUMsRUFBRSxtQkFBbUIsQ0FBQztRQUM1Qyx5RUFBeUU7UUFDekUsY0FBYyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssSUFBSSxDQUFDO0tBQ3JDLEVBd0JQO0lBRUQ7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLHNCQUFzQjtJQVU5Qjs7Ozs7T0FLRztJQUNJLFdBQVcsQ0FBQyxLQUFLLEVBQUUsT0FBTyxHQUFHLElBQUksQ0FHdkM7SUFFWSxXQUFXLGtCQW9FdkI7SUFFWSxvQkFBb0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQW1CakU7SUFFRDs7Ozs7Ozs7Ozs7Ozs7T0FjRztJQUNVLGNBQWMsQ0FDekIsU0FBUyxFQUFFLEtBQUssTUFBTSxFQUFFLEVBQ3hCLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFDcEIsSUFBSSxDQUFDLEVBQUUscUJBQXFCLEdBQzNCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQTZJakI7WUFRYSxhQUFhO0lBc0NkLGtCQUFrQixDQUM3QixPQUFPLEVBQUUsTUFBTSxFQUNmLGVBQWUsRUFBRSxNQUFNLEdBQUcsTUFBTSxFQUNoQyxVQUFVLEdBQUUsTUFBTSxFQUFPLEVBQ3pCLG9CQUFvQixDQUFDLEVBQUUsTUFBTSxHQUM1QixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FHakI7SUFFWSxnQkFBZ0IsQ0FDM0IsT0FBTyxFQUFFLE1BQU0sRUFDZixlQUFlLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFDaEMsb0JBQW9CLENBQUMsRUFBRSxNQUFNLEdBQzVCLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQXNDckI7SUFFRCxPQUFPLENBQUMsaUJBQWlCO1lBWVgsbUJBQW1CO1lBbUNuQixhQUFhO0lBNkQzQixzQ0FBc0M7SUFDL0IsZ0JBQWdCLElBQUksaUJBQWlCLEdBQUcsU0FBUyxDQUV2RDtDQUNGIn0=
|
|
@@ -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;AACjF,OAAO,EAAE,
|
|
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;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,cAAe,YAAW,mBAAmB;IAYtD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXvB,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;IAEzB,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;CACF"}
|
package/dest/client/http.js
CHANGED
|
@@ -5,7 +5,6 @@ import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
|
5
5
|
import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
|
|
6
6
|
import { createPublicClient, fallback, http } from 'viem';
|
|
7
7
|
import { createBlobArchiveClient } from '../archive/factory.js';
|
|
8
|
-
import { BlobWithIndex } from '../types/blob_with_index.js';
|
|
9
8
|
import { getBlobClientConfigFromEnv } from './config.js';
|
|
10
9
|
export class HttpBlobClient {
|
|
11
10
|
opts;
|
|
@@ -166,10 +165,9 @@ export class HttpBlobClient {
|
|
|
166
165
|
*
|
|
167
166
|
* @param blockHash - The block hash
|
|
168
167
|
* @param blobHashes - The blob hashes to fetch
|
|
169
|
-
* @param indices - The indices of the blobs to get
|
|
170
168
|
* @param opts - Options including isHistoricalSync flag
|
|
171
169
|
* @returns The blobs
|
|
172
|
-
*/ async getBlobSidecar(blockHash, blobHashes,
|
|
170
|
+
*/ async getBlobSidecar(blockHash, blobHashes, opts) {
|
|
173
171
|
if (this.disabled) {
|
|
174
172
|
this.log.warn('Blob storage is disabled, returning empty blob sidecar');
|
|
175
173
|
return [];
|
|
@@ -203,8 +201,7 @@ export class HttpBlobClient {
|
|
|
203
201
|
const { l1ConsensusHostUrls } = this.config;
|
|
204
202
|
const ctx = {
|
|
205
203
|
blockHash,
|
|
206
|
-
blobHashes: blobHashes.map(bufferToHex)
|
|
207
|
-
indices
|
|
204
|
+
blobHashes: blobHashes.map(bufferToHex)
|
|
208
205
|
};
|
|
209
206
|
// Try filestore (quick, no retries) - useful for both historical and near-tip sync
|
|
210
207
|
if (this.fileStoreClients.length > 0 && getMissingBlobHashes().length > 0) {
|
|
@@ -236,7 +233,7 @@ export class HttpBlobClient {
|
|
|
236
233
|
l1ConsensusHostUrl,
|
|
237
234
|
...ctx
|
|
238
235
|
});
|
|
239
|
-
const blobs = await this.getBlobsFromHost(l1ConsensusHostUrl, slotNumber,
|
|
236
|
+
const blobs = await this.getBlobsFromHost(l1ConsensusHostUrl, slotNumber, l1ConsensusHostIndex);
|
|
240
237
|
const result = fillResults(blobs);
|
|
241
238
|
this.log.debug(`Got ${blobs.length} blobs from consensus host (total: ${result.length}/${blobHashes.length})`, {
|
|
242
239
|
slotNumber,
|
|
@@ -334,13 +331,13 @@ export class HttpBlobClient {
|
|
|
334
331
|
}
|
|
335
332
|
}
|
|
336
333
|
}
|
|
337
|
-
async getBlobSidecarFrom(hostUrl, blockHashOrSlot, blobHashes = [],
|
|
338
|
-
const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot,
|
|
334
|
+
async getBlobSidecarFrom(hostUrl, blockHashOrSlot, blobHashes = [], l1ConsensusHostIndex) {
|
|
335
|
+
const blobs = await this.getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
|
|
339
336
|
return processFetchedBlobs(blobs, blobHashes, this.log).filter((b)=>b !== undefined);
|
|
340
337
|
}
|
|
341
|
-
async getBlobsFromHost(hostUrl, blockHashOrSlot,
|
|
338
|
+
async getBlobsFromHost(hostUrl, blockHashOrSlot, l1ConsensusHostIndex) {
|
|
342
339
|
try {
|
|
343
|
-
let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot,
|
|
340
|
+
let res = await this.fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex);
|
|
344
341
|
if (res.ok) {
|
|
345
342
|
return parseBlobJsonsFromResponse(await res.json(), this.log);
|
|
346
343
|
}
|
|
@@ -354,8 +351,8 @@ export class HttpBlobClient {
|
|
|
354
351
|
let maxRetries = 10;
|
|
355
352
|
let currentSlot = blockHashOrSlot + 1;
|
|
356
353
|
while(res.status === 404 && maxRetries > 0 && latestSlot !== undefined && currentSlot <= latestSlot){
|
|
357
|
-
this.log.debug(`Trying slot ${currentSlot}
|
|
358
|
-
res = await this.fetchBlobSidecars(hostUrl, currentSlot,
|
|
354
|
+
this.log.debug(`Trying slot ${currentSlot}`);
|
|
355
|
+
res = await this.fetchBlobSidecars(hostUrl, currentSlot, l1ConsensusHostIndex);
|
|
359
356
|
if (res.ok) {
|
|
360
357
|
return parseBlobJsonsFromResponse(await res.json(), this.log);
|
|
361
358
|
}
|
|
@@ -374,11 +371,8 @@ export class HttpBlobClient {
|
|
|
374
371
|
return [];
|
|
375
372
|
}
|
|
376
373
|
}
|
|
377
|
-
fetchBlobSidecars(hostUrl, blockHashOrSlot,
|
|
378
|
-
|
|
379
|
-
if (indices.length > 0) {
|
|
380
|
-
baseUrl += `?indices=${indices.join(',')}`;
|
|
381
|
-
}
|
|
374
|
+
fetchBlobSidecars(hostUrl, blockHashOrSlot, l1ConsensusHostIndex) {
|
|
375
|
+
const baseUrl = `${hostUrl}/eth/v1/beacon/blob_sidecars/${blockHashOrSlot}`;
|
|
382
376
|
const { url, ...options } = getBeaconNodeFetchOptions(baseUrl, this.config, l1ConsensusHostIndex);
|
|
383
377
|
this.log.debug(`Fetching blob sidecar for ${blockHashOrSlot}`, {
|
|
384
378
|
url,
|
|
@@ -477,6 +471,9 @@ export class HttpBlobClient {
|
|
|
477
471
|
}
|
|
478
472
|
return undefined;
|
|
479
473
|
}
|
|
474
|
+
/** @internal - exposed for testing */ getArchiveClient() {
|
|
475
|
+
return this.archiveClient;
|
|
476
|
+
}
|
|
480
477
|
}
|
|
481
478
|
function parseBlobJsonsFromResponse(response, logger) {
|
|
482
479
|
try {
|
|
@@ -490,26 +487,26 @@ function parseBlobJsonsFromResponse(response, logger) {
|
|
|
490
487
|
// Blobs will be in this form when requested from the blob client, or from the beacon chain via `getBlobSidecars`:
|
|
491
488
|
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlobSidecars
|
|
492
489
|
// Here we attempt to parse the response data to Buffer, and check the lengths (via Blob's constructor), to avoid
|
|
493
|
-
// throwing an error down the line when calling
|
|
490
|
+
// throwing an error down the line when calling Blob.fromJson().
|
|
494
491
|
function parseBlobJson(data) {
|
|
495
492
|
const blobBuffer = Buffer.from(data.blob.slice(2), 'hex');
|
|
496
493
|
const commitmentBuffer = Buffer.from(data.kzg_commitment.slice(2), 'hex');
|
|
497
494
|
const blob = new Blob(blobBuffer, commitmentBuffer);
|
|
498
|
-
return blob.
|
|
495
|
+
return blob.toJSON();
|
|
499
496
|
}
|
|
500
|
-
// Returns an array that maps each blob hash to the corresponding blob
|
|
497
|
+
// Returns an array that maps each blob hash to the corresponding blob, or undefined if the blob is not found
|
|
501
498
|
// or the data does not match the commitment.
|
|
502
499
|
function processFetchedBlobs(blobs, blobHashes, logger) {
|
|
503
500
|
const requestedBlobHashes = new Set(blobHashes.map(bufferToHex));
|
|
504
501
|
const hashToBlob = new Map();
|
|
505
|
-
for (const
|
|
506
|
-
const hashHex = bufferToHex(computeEthVersionedBlobHash(hexToBuffer(
|
|
502
|
+
for (const blobJson of blobs){
|
|
503
|
+
const hashHex = bufferToHex(computeEthVersionedBlobHash(hexToBuffer(blobJson.kzg_commitment)));
|
|
507
504
|
if (!requestedBlobHashes.has(hashHex) || hashToBlob.has(hashHex)) {
|
|
508
505
|
continue;
|
|
509
506
|
}
|
|
510
507
|
try {
|
|
511
|
-
const
|
|
512
|
-
hashToBlob.set(hashHex,
|
|
508
|
+
const blob = Blob.fromJson(blobJson);
|
|
509
|
+
hashToBlob.set(hashHex, blob);
|
|
513
510
|
} catch (err) {
|
|
514
511
|
// If the above throws, it's likely that the blob commitment does not match the hash of the blob data.
|
|
515
512
|
logger.error(`Error converting blob from json`, err);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Blob } from '@aztec/blob-lib';
|
|
2
|
-
import type { BlobWithIndex } from '../types/blob_with_index.js';
|
|
3
2
|
/**
|
|
4
3
|
* Options for getBlobSidecar method.
|
|
5
4
|
*/
|
|
@@ -15,9 +14,9 @@ export interface GetBlobSidecarOptions {
|
|
|
15
14
|
export interface BlobClientInterface {
|
|
16
15
|
/** Sends the given blobs to the filestore, to be indexed by blob hash. */
|
|
17
16
|
sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
|
|
18
|
-
/** Fetches the given blob sidecars by block
|
|
19
|
-
getBlobSidecar(blockId: string, blobHashes?: Buffer[],
|
|
17
|
+
/** Fetches the given blob sidecars by block hash and blob hashes. */
|
|
18
|
+
getBlobSidecar(blockId: string, blobHashes?: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
20
19
|
/** Tests all configured blob sources and logs whether they are reachable or not. */
|
|
21
20
|
testSources(): Promise<void>;
|
|
22
21
|
}
|
|
23
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
22
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUM1QjtBQUVELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsMEVBQTBFO0lBQzFFLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEQscUVBQXFFO0lBQ3JFLGNBQWMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3RHLG9GQUFvRjtJQUNwRixXQUFXLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQzlCIn0=
|
|
@@ -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
|
|
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,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B"}
|
package/dest/client/local.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { Blob } from '@aztec/blob-lib';
|
|
2
2
|
import type { BlobStore } from '../blobstore/index.js';
|
|
3
|
-
import { BlobWithIndex } from '../types/blob_with_index.js';
|
|
4
3
|
import type { BlobClientInterface, GetBlobSidecarOptions } from './interface.js';
|
|
5
4
|
export declare class LocalBlobClient implements BlobClientInterface {
|
|
6
5
|
private readonly blobStore;
|
|
7
6
|
constructor(blobStore: BlobStore);
|
|
8
7
|
testSources(): Promise<void>;
|
|
9
8
|
sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
|
|
10
|
-
getBlobSidecar(_blockId: string, blobHashes: Buffer[],
|
|
9
|
+
getBlobSidecar(_blockId: string, blobHashes: Buffer[], _opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
11
10
|
}
|
|
12
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGllbnQvbG9jYWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUMsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDdkQsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxlQUFnQixZQUFXLG1CQUFtQjtJQUN6RCxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBWTtJQUV0QyxZQUFZLFNBQVMsRUFBRSxTQUFTLEVBRS9CO0lBRU0sV0FBVyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFbEM7SUFFWSxvQkFBb0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUdqRTtJQUVNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFBRSxLQUFLLENBQUMsRUFBRSxxQkFBcUIsR0FBRyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FFNUc7Q0FDRiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/client/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/client/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,eAAgB,YAAW,mBAAmB;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAEtC,YAAY,SAAS,EAAE,SAAS,EAE/B;IAEM,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAElC;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAGjE;IAEM,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAE5G;CACF"}
|
package/dest/client/local.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { BlobWithIndex } from '../types/blob_with_index.js';
|
|
2
1
|
export class LocalBlobClient {
|
|
3
2
|
blobStore;
|
|
4
3
|
constructor(blobStore){
|
|
@@ -8,11 +7,10 @@ export class LocalBlobClient {
|
|
|
8
7
|
return Promise.resolve();
|
|
9
8
|
}
|
|
10
9
|
async sendBlobsToFilestore(blobs) {
|
|
11
|
-
|
|
12
|
-
await this.blobStore.addBlobs(blobsWithIndex);
|
|
10
|
+
await this.blobStore.addBlobs(blobs);
|
|
13
11
|
return true;
|
|
14
12
|
}
|
|
15
|
-
getBlobSidecar(_blockId, blobHashes,
|
|
13
|
+
getBlobSidecar(_blockId, blobHashes, _opts) {
|
|
16
14
|
return this.blobStore.getBlobsByHashes(blobHashes);
|
|
17
15
|
}
|
|
18
16
|
}
|
package/dest/client/tests.d.ts
CHANGED
|
@@ -8,4 +8,4 @@ export declare function runBlobClientTests(createClient: () => Promise<{
|
|
|
8
8
|
client: BlobClientInterface;
|
|
9
9
|
cleanup: () => Promise<void>;
|
|
10
10
|
}>): void;
|
|
11
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGllbnQvdGVzdHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBSUEsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUxRDs7OztHQUlHO0FBQ0gsd0JBQWdCLGtCQUFrQixDQUNoQyxZQUFZLEVBQUUsTUFBTSxPQUFPLENBQUM7SUFBRSxNQUFNLEVBQUUsbUJBQW1CLENBQUM7SUFBQyxPQUFPLEVBQUUsTUFBTSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUE7Q0FBRSxDQUFDLFFBaUQzRiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../src/client/tests.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,OAAO,CAAC;IAAE,MAAM,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../src/client/tests.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,OAAO,CAAC;IAAE,MAAM,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,QAiD3F"}
|
package/dest/client/tests.js
CHANGED
|
@@ -26,7 +26,7 @@ import { makeRandomBlob } from '@aztec/blob-lib/testing';
|
|
|
26
26
|
blobHash
|
|
27
27
|
]);
|
|
28
28
|
expect(retrievedBlobs).toHaveLength(1);
|
|
29
|
-
expect(retrievedBlobs[0]
|
|
29
|
+
expect(retrievedBlobs[0]).toEqual(blob);
|
|
30
30
|
});
|
|
31
31
|
it('should handle multiple blobs', async ()=>{
|
|
32
32
|
const blobs = Array.from({
|
|
@@ -37,7 +37,7 @@ import { makeRandomBlob } from '@aztec/blob-lib/testing';
|
|
|
37
37
|
const retrievedBlobs = await client.getBlobSidecar(blockId, blobHashes);
|
|
38
38
|
expect(retrievedBlobs.length).toBe(3);
|
|
39
39
|
for(let i = 0; i < blobs.length; i++){
|
|
40
|
-
expect(retrievedBlobs[i]
|
|
40
|
+
expect(retrievedBlobs[i]).toEqual(blobs[i]);
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
it('should return empty array for non-existent blob hash', async ()=>{
|
|
@@ -48,18 +48,4 @@ import { makeRandomBlob } from '@aztec/blob-lib/testing';
|
|
|
48
48
|
]);
|
|
49
49
|
expect(retrievedBlobs).toEqual([]);
|
|
50
50
|
});
|
|
51
|
-
it('should preserve blob indices', async ()=>{
|
|
52
|
-
const blobs = Array.from({
|
|
53
|
-
length: 3
|
|
54
|
-
}, ()=>makeRandomBlob(7));
|
|
55
|
-
const blobHashes = blobs.map((blob)=>blob.getEthVersionedBlobHash());
|
|
56
|
-
await client.sendBlobsToFilestore(blobs);
|
|
57
|
-
const retrievedBlobs = await client.getBlobSidecar(blockId, blobHashes);
|
|
58
|
-
expect(retrievedBlobs.length).toBe(blobs.length);
|
|
59
|
-
// Indices should be assigned sequentially based on the order they were sent
|
|
60
|
-
for(let i = 0; i < blobs.length; i++){
|
|
61
|
-
expect(retrievedBlobs[i].blob).toEqual(blobs[i]);
|
|
62
|
-
expect(retrievedBlobs[i].index).toBe(i);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
51
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Blob, type BlobJson } from '@aztec/blob-lib';
|
|
2
2
|
import { type Logger } from '@aztec/foundation/log';
|
|
3
3
|
import type { FileStore, ReadOnlyFileStore } from '@aztec/stdlib/file-store';
|
|
4
|
-
import { BlobWithIndex } from '../types/blob_with_index.js';
|
|
5
4
|
/**
|
|
6
5
|
* A blob client that uses a FileStore (S3/GCS/local) as the data source.
|
|
7
6
|
* Blobs are stored as JSON files keyed by their versioned blob hash.
|
|
@@ -36,10 +35,10 @@ export declare class FileStoreBlobClient {
|
|
|
36
35
|
saveBlob(blob: Blob, skipIfExists?: boolean): Promise<void>;
|
|
37
36
|
/**
|
|
38
37
|
* Save multiple blobs to the store in parallel.
|
|
39
|
-
* @param blobs - The blobs to save
|
|
38
|
+
* @param blobs - The blobs to save
|
|
40
39
|
* @param skipIfExists - Skip saving if blob already exists (default: true)
|
|
41
40
|
*/
|
|
42
|
-
saveBlobs(blobs: Blob[]
|
|
41
|
+
saveBlobs(blobs: Blob[], skipIfExists?: boolean): Promise<void>;
|
|
43
42
|
/**
|
|
44
43
|
* Get the base URL/path of the filestore.
|
|
45
44
|
*/
|
|
@@ -53,4 +52,4 @@ export declare class FileStoreBlobClient {
|
|
|
53
52
|
*/
|
|
54
53
|
private isWritable;
|
|
55
54
|
}
|
|
56
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZXN0b3JlX2Jsb2JfY2xpZW50LmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZmlsZXN0b3JlL2ZpbGVzdG9yZV9ibG9iX2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssUUFBUSxFQUErQixNQUFNLGlCQUFpQixDQUFDO0FBQ25GLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUk3RTs7O0dBR0c7QUFDSCxxQkFBYSxtQkFBbUI7SUFJNUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBQ3RCLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUTtJQUozQixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBUztJQUU3QixZQUNtQixLQUFLLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxFQUNwQyxRQUFRLEVBQUUsTUFBTSxFQUNqQyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBR2hCO0lBRUQ7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLFFBQVE7SUFJaEI7Ozs7T0FJRztJQUNHLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FtQmhFO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLGlCQUFpQixFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRWxEO0lBRUQ7Ozs7O09BS0c7SUFDRyxRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxZQUFZLFVBQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBa0I3RDtJQUVEOzs7O09BSUc7SUFDRyxTQUFTLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLFlBQVksVUFBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFakU7SUFFRDs7T0FFRztJQUNILFVBQVUsSUFBSSxNQUFNLENBRW5CO0lBRUQ7O09BRUc7SUFDSCxjQUFjLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUlqQztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLFVBQVU7Q0FHbkIifQ==
|