@aztec/blob-client 3.0.0-nightly.20251223

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.
Files changed (104) hide show
  1. package/README.md +62 -0
  2. package/dest/archive/blobscan_archive_client.d.ts +147 -0
  3. package/dest/archive/blobscan_archive_client.d.ts.map +1 -0
  4. package/dest/archive/blobscan_archive_client.js +141 -0
  5. package/dest/archive/config.d.ts +7 -0
  6. package/dest/archive/config.d.ts.map +1 -0
  7. package/dest/archive/config.js +11 -0
  8. package/dest/archive/factory.d.ts +4 -0
  9. package/dest/archive/factory.d.ts.map +1 -0
  10. package/dest/archive/factory.js +7 -0
  11. package/dest/archive/index.d.ts +3 -0
  12. package/dest/archive/index.d.ts.map +1 -0
  13. package/dest/archive/index.js +2 -0
  14. package/dest/archive/instrumentation.d.ts +11 -0
  15. package/dest/archive/instrumentation.d.ts.map +1 -0
  16. package/dest/archive/instrumentation.js +33 -0
  17. package/dest/archive/interface.d.ts +13 -0
  18. package/dest/archive/interface.d.ts.map +1 -0
  19. package/dest/archive/interface.js +1 -0
  20. package/dest/blobstore/blob_store_test_suite.d.ts +3 -0
  21. package/dest/blobstore/blob_store_test_suite.d.ts.map +1 -0
  22. package/dest/blobstore/blob_store_test_suite.js +164 -0
  23. package/dest/blobstore/index.d.ts +3 -0
  24. package/dest/blobstore/index.d.ts.map +1 -0
  25. package/dest/blobstore/index.js +2 -0
  26. package/dest/blobstore/interface.d.ts +12 -0
  27. package/dest/blobstore/interface.d.ts.map +1 -0
  28. package/dest/blobstore/interface.js +1 -0
  29. package/dest/blobstore/memory_blob_store.d.ts +8 -0
  30. package/dest/blobstore/memory_blob_store.d.ts.map +1 -0
  31. package/dest/blobstore/memory_blob_store.js +24 -0
  32. package/dest/client/bin/index.d.ts +3 -0
  33. package/dest/client/bin/index.d.ts.map +1 -0
  34. package/dest/client/bin/index.js +30 -0
  35. package/dest/client/config.d.ts +50 -0
  36. package/dest/client/config.d.ts.map +1 -0
  37. package/dest/client/config.js +55 -0
  38. package/dest/client/factory.d.ts +39 -0
  39. package/dest/client/factory.d.ts.map +1 -0
  40. package/dest/client/factory.js +53 -0
  41. package/dest/client/http.d.ts +63 -0
  42. package/dest/client/http.d.ts.map +1 -0
  43. package/dest/client/http.js +536 -0
  44. package/dest/client/index.d.ts +6 -0
  45. package/dest/client/index.d.ts.map +1 -0
  46. package/dest/client/index.js +5 -0
  47. package/dest/client/interface.d.ts +23 -0
  48. package/dest/client/interface.d.ts.map +1 -0
  49. package/dest/client/interface.js +1 -0
  50. package/dest/client/local.d.ts +12 -0
  51. package/dest/client/local.d.ts.map +1 -0
  52. package/dest/client/local.js +18 -0
  53. package/dest/client/tests.d.ts +11 -0
  54. package/dest/client/tests.d.ts.map +1 -0
  55. package/dest/client/tests.js +65 -0
  56. package/dest/encoding/index.d.ts +15 -0
  57. package/dest/encoding/index.d.ts.map +1 -0
  58. package/dest/encoding/index.js +19 -0
  59. package/dest/filestore/factory.d.ts +50 -0
  60. package/dest/filestore/factory.d.ts.map +1 -0
  61. package/dest/filestore/factory.js +67 -0
  62. package/dest/filestore/filestore_blob_client.d.ts +56 -0
  63. package/dest/filestore/filestore_blob_client.d.ts.map +1 -0
  64. package/dest/filestore/filestore_blob_client.js +99 -0
  65. package/dest/filestore/index.d.ts +3 -0
  66. package/dest/filestore/index.d.ts.map +1 -0
  67. package/dest/filestore/index.js +2 -0
  68. package/dest/types/api.d.ts +65 -0
  69. package/dest/types/api.d.ts.map +1 -0
  70. package/dest/types/api.js +22 -0
  71. package/dest/types/blob_with_index.d.ts +25 -0
  72. package/dest/types/blob_with_index.d.ts.map +1 -0
  73. package/dest/types/blob_with_index.js +43 -0
  74. package/dest/types/index.d.ts +2 -0
  75. package/dest/types/index.d.ts.map +1 -0
  76. package/dest/types/index.js +1 -0
  77. package/package.json +95 -0
  78. package/src/archive/blobscan_archive_client.ts +178 -0
  79. package/src/archive/config.ts +14 -0
  80. package/src/archive/factory.ts +11 -0
  81. package/src/archive/fixtures/blobscan_get_blob_data.json +1 -0
  82. package/src/archive/fixtures/blobscan_get_block.json +56 -0
  83. package/src/archive/index.ts +2 -0
  84. package/src/archive/instrumentation.ts +41 -0
  85. package/src/archive/interface.ts +9 -0
  86. package/src/blobstore/blob_store_test_suite.ts +137 -0
  87. package/src/blobstore/index.ts +2 -0
  88. package/src/blobstore/interface.ts +12 -0
  89. package/src/blobstore/memory_blob_store.ts +31 -0
  90. package/src/client/bin/index.ts +35 -0
  91. package/src/client/config.ts +117 -0
  92. package/src/client/factory.ts +88 -0
  93. package/src/client/http.ts +620 -0
  94. package/src/client/index.ts +5 -0
  95. package/src/client/interface.ts +30 -0
  96. package/src/client/local.ts +32 -0
  97. package/src/client/tests.ts +78 -0
  98. package/src/encoding/index.ts +21 -0
  99. package/src/filestore/factory.ts +145 -0
  100. package/src/filestore/filestore_blob_client.ts +129 -0
  101. package/src/filestore/index.ts +2 -0
  102. package/src/types/api.ts +50 -0
  103. package/src/types/blob_with_index.ts +48 -0
  104. package/src/types/index.ts +1 -0
@@ -0,0 +1,78 @@
1
+ import { makeRandomBlob } from '@aztec/blob-lib/testing';
2
+
3
+ import type { Hex } from 'viem';
4
+
5
+ import type { BlobClientInterface } from './interface.js';
6
+
7
+ /**
8
+ * Shared test suite for blob clients
9
+ * @param createClient - Function that creates a client instance for testing
10
+ * @param cleanup - Optional cleanup function to run after each test
11
+ */
12
+ export function runBlobClientTests(
13
+ createClient: () => Promise<{ client: BlobClientInterface; cleanup: () => Promise<void> }>,
14
+ ) {
15
+ let blockId: Hex;
16
+ let client: BlobClientInterface;
17
+ let cleanup: () => Promise<void>;
18
+
19
+ beforeEach(async () => {
20
+ blockId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
21
+ const setup = await createClient();
22
+ client = setup.client;
23
+ cleanup = setup.cleanup;
24
+ });
25
+
26
+ afterEach(async () => {
27
+ await cleanup();
28
+ });
29
+
30
+ it('should send and retrieve blobs by hash', async () => {
31
+ const blob = makeRandomBlob(5);
32
+ const blobHash = blob.getEthVersionedBlobHash();
33
+
34
+ await client.sendBlobsToFilestore([blob]);
35
+
36
+ const retrievedBlobs = await client.getBlobSidecar(blockId, [blobHash]);
37
+ expect(retrievedBlobs).toHaveLength(1);
38
+ expect(retrievedBlobs[0].blob).toEqual(blob);
39
+ });
40
+
41
+ it('should handle multiple blobs', async () => {
42
+ const blobs = Array.from({ length: 3 }, () => makeRandomBlob(7));
43
+ const blobHashes = blobs.map(blob => blob.getEthVersionedBlobHash());
44
+
45
+ await client.sendBlobsToFilestore(blobs);
46
+
47
+ const retrievedBlobs = await client.getBlobSidecar(blockId, blobHashes);
48
+ expect(retrievedBlobs.length).toBe(3);
49
+
50
+ for (let i = 0; i < blobs.length; i++) {
51
+ expect(retrievedBlobs[i].blob).toEqual(blobs[i]);
52
+ }
53
+ });
54
+
55
+ it('should return empty array for non-existent blob hash', async () => {
56
+ const nonExistentHash = Buffer.alloc(32);
57
+ nonExistentHash.fill(0xff);
58
+
59
+ const retrievedBlobs = await client.getBlobSidecar(blockId, [nonExistentHash]);
60
+ expect(retrievedBlobs).toEqual([]);
61
+ });
62
+
63
+ it('should preserve blob indices', async () => {
64
+ const blobs = Array.from({ length: 3 }, () => makeRandomBlob(7));
65
+ const blobHashes = blobs.map(blob => blob.getEthVersionedBlobHash());
66
+
67
+ await client.sendBlobsToFilestore(blobs);
68
+
69
+ const retrievedBlobs = await client.getBlobSidecar(blockId, blobHashes);
70
+ expect(retrievedBlobs.length).toBe(blobs.length);
71
+
72
+ // Indices should be assigned sequentially based on the order they were sent
73
+ for (let i = 0; i < blobs.length; i++) {
74
+ expect(retrievedBlobs[i].blob).toEqual(blobs[i]);
75
+ expect(retrievedBlobs[i].index).toBe(i);
76
+ }
77
+ });
78
+ }
@@ -0,0 +1,21 @@
1
+ import { compressSync, uncompressSync } from 'snappy';
2
+
3
+ /**
4
+ * Snappy decompress the blob buffer
5
+ *
6
+ * @param data - The blob buffer
7
+ * @returns The decompressed blob buffer
8
+ */
9
+ export function inboundTransform(data: Buffer): Buffer {
10
+ return Buffer.from(uncompressSync(data, { asBuffer: true }));
11
+ }
12
+
13
+ /**
14
+ * Snappy compress the blob buffer
15
+ *
16
+ * @param data - The blob buffer
17
+ * @returns The compressed blob buffer
18
+ */
19
+ export function outboundTransform(data: Buffer): Buffer {
20
+ return Buffer.from(compressSync(data));
21
+ }
@@ -0,0 +1,145 @@
1
+ import { type Logger, createLogger } from '@aztec/foundation/log';
2
+ import {
3
+ type FileStore,
4
+ type ReadOnlyFileStore,
5
+ createFileStore,
6
+ createReadOnlyFileStore,
7
+ } from '@aztec/stdlib/file-store';
8
+
9
+ import { FileStoreBlobClient } from './filestore_blob_client.js';
10
+
11
+ /**
12
+ * Metadata required to construct the base path for blob storage.
13
+ * Path format: aztec-{l1ChainId}-{rollupVersion}-{rollupAddress}/
14
+ */
15
+ export interface BlobFileStoreMetadata {
16
+ /** The L1 chain ID */
17
+ l1ChainId: number;
18
+ /** The rollup version */
19
+ rollupVersion: number;
20
+ /** The rollup contract address (with or without 0x prefix) */
21
+ rollupAddress: string;
22
+ }
23
+
24
+ /**
25
+ * Constructs the base path for blob storage.
26
+ * Format: aztec-{l1ChainId}-{rollupVersion}-{rollupAddress}
27
+ */
28
+ export function makeBlobBasePath(metadata: BlobFileStoreMetadata): string {
29
+ const { l1ChainId, rollupVersion, rollupAddress } = metadata;
30
+ // Normalize rollup address to lowercase without 0x prefix for consistency
31
+ const normalizedAddress = rollupAddress.toLowerCase().replace(/^0x/, '');
32
+ return `aztec-${l1ChainId}-${rollupVersion}-0x${normalizedAddress}`;
33
+ }
34
+
35
+ /**
36
+ * Creates a read-only FileStoreBlobClient for fetching blobs.
37
+ *
38
+ * @param storeUrl - The URL of the filestore (s3://, gs://, file://, https://)
39
+ * @param metadata - Chain metadata for constructing the base path
40
+ * @param logger - Optional logger
41
+ * @returns A FileStoreBlobClient for reading blobs, or undefined if storeUrl is undefined
42
+ */
43
+ export async function createReadOnlyFileStoreBlobClient(
44
+ storeUrl: string,
45
+ metadata: BlobFileStoreMetadata,
46
+ logger?: Logger,
47
+ ): Promise<FileStoreBlobClient>;
48
+ export async function createReadOnlyFileStoreBlobClient(
49
+ storeUrl: string | undefined,
50
+ metadata: BlobFileStoreMetadata,
51
+ logger?: Logger,
52
+ ): Promise<FileStoreBlobClient | undefined>;
53
+ export async function createReadOnlyFileStoreBlobClient(
54
+ storeUrl: string | undefined,
55
+ metadata: BlobFileStoreMetadata,
56
+ logger?: Logger,
57
+ ): Promise<FileStoreBlobClient | undefined> {
58
+ if (!storeUrl) {
59
+ return undefined;
60
+ }
61
+
62
+ const log = logger ?? createLogger('blob-client:filestore-factory');
63
+ const basePath = makeBlobBasePath(metadata);
64
+
65
+ log.debug(`Creating read-only filestore blob client`, { storeUrl, basePath });
66
+
67
+ const store: ReadOnlyFileStore = await createReadOnlyFileStore(storeUrl, log);
68
+ return new FileStoreBlobClient(store, basePath, log);
69
+ }
70
+
71
+ /**
72
+ * Creates multiple read-only FileStoreBlobClients from an array of URLs.
73
+ *
74
+ * @param storeUrls - Array of filestore URLs
75
+ * @param metadata - Chain metadata for constructing the base path
76
+ * @param logger - Optional logger
77
+ * @returns Array of FileStoreBlobClients (excludes any that failed to create)
78
+ */
79
+ export async function createReadOnlyFileStoreBlobClients(
80
+ storeUrls: string[] | undefined,
81
+ metadata: BlobFileStoreMetadata,
82
+ logger?: Logger,
83
+ ): Promise<FileStoreBlobClient[]> {
84
+ if (!storeUrls || storeUrls.length === 0) {
85
+ return [];
86
+ }
87
+
88
+ const log = logger ?? createLogger('blob-client:filestore-factory');
89
+ const clients: FileStoreBlobClient[] = [];
90
+
91
+ for (const storeUrl of storeUrls) {
92
+ try {
93
+ const client = await createReadOnlyFileStoreBlobClient(storeUrl, metadata, log);
94
+ if (client) {
95
+ clients.push(client);
96
+ }
97
+ } catch (err) {
98
+ log.error(`Failed to create read-only filestore blob client for ${storeUrl}`, err);
99
+ }
100
+ }
101
+
102
+ return clients;
103
+ }
104
+
105
+ /**
106
+ * Creates a writable FileStoreBlobClient for uploading blobs.
107
+ * Note: https:// URLs are not supported for upload, only s3://, gs://, or file://.
108
+ *
109
+ * @param storeUrl - The URL of the filestore (s3://, gs://, file://)
110
+ * @param metadata - Chain metadata for constructing the base path
111
+ * @param logger - Optional logger
112
+ * @returns A writable FileStoreBlobClient, or undefined if storeUrl is undefined
113
+ */
114
+ export async function createWritableFileStoreBlobClient(
115
+ storeUrl: string,
116
+ metadata: BlobFileStoreMetadata,
117
+ logger?: Logger,
118
+ ): Promise<FileStoreBlobClient>;
119
+ export async function createWritableFileStoreBlobClient(
120
+ storeUrl: string | undefined,
121
+ metadata: BlobFileStoreMetadata,
122
+ logger?: Logger,
123
+ ): Promise<FileStoreBlobClient | undefined>;
124
+ export async function createWritableFileStoreBlobClient(
125
+ storeUrl: string | undefined,
126
+ metadata: BlobFileStoreMetadata,
127
+ logger?: Logger,
128
+ ): Promise<FileStoreBlobClient | undefined> {
129
+ if (!storeUrl) {
130
+ return undefined;
131
+ }
132
+
133
+ const log = logger ?? createLogger('blob-client:filestore-factory');
134
+ const basePath = makeBlobBasePath(metadata);
135
+
136
+ log.debug(`Creating writable filestore blob client`, { storeUrl, basePath });
137
+
138
+ const store: FileStore | undefined = await createFileStore(storeUrl, log);
139
+ if (!store) {
140
+ log.warn(`Failed to create writable filestore for ${storeUrl}`);
141
+ return undefined;
142
+ }
143
+
144
+ return new FileStoreBlobClient(store, basePath, log);
145
+ }
@@ -0,0 +1,129 @@
1
+ import { Blob, type BlobJson, computeEthVersionedBlobHash } from '@aztec/blob-lib';
2
+ import { type Logger, createLogger } from '@aztec/foundation/log';
3
+ import type { FileStore, ReadOnlyFileStore } from '@aztec/stdlib/file-store';
4
+
5
+ import { inboundTransform, outboundTransform } from '../encoding/index.js';
6
+ import { BlobWithIndex } from '../types/blob_with_index.js';
7
+
8
+ /**
9
+ * A blob client that uses a FileStore (S3/GCS/local) as the data source.
10
+ * Blobs are stored as JSON files keyed by their versioned blob hash.
11
+ */
12
+ export class FileStoreBlobClient {
13
+ private readonly log: Logger;
14
+
15
+ constructor(
16
+ private readonly store: ReadOnlyFileStore | FileStore,
17
+ private readonly basePath: string,
18
+ logger?: Logger,
19
+ ) {
20
+ this.log = logger ?? createLogger('blob-client:filestore-client');
21
+ }
22
+
23
+ /**
24
+ * Get the path for a blob file.
25
+ * Format: basePath/blobs/{versionedBlobHash}.data
26
+ */
27
+ private blobPath(versionedBlobHash: string): string {
28
+ return `${this.basePath}/blobs/${versionedBlobHash}.data`;
29
+ }
30
+
31
+ /**
32
+ * Fetch blobs by their versioned hashes.
33
+ * @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
34
+ * @returns Array of BlobJson objects for found blobs
35
+ */
36
+ async getBlobsByHashes(blobHashes: string[]): Promise<BlobJson[]> {
37
+ const blobs: BlobJson[] = [];
38
+
39
+ for (let i = 0; i < blobHashes.length; i++) {
40
+ try {
41
+ const path = this.blobPath(blobHashes[i]);
42
+ if (!(await this.store.exists(path))) {
43
+ continue;
44
+ }
45
+
46
+ const data = await this.store.read(path);
47
+ const json = JSON.parse(inboundTransform(data).toString()) as BlobJson;
48
+ // We don't know the actual index when fetching from filestore - use -1 to indicate this deliberately
49
+ blobs.push({ ...json, index: '-1' });
50
+ } catch (err) {
51
+ this.log.warn(`Failed to read blob ${blobHashes[i]} from filestore`, err);
52
+ }
53
+ }
54
+
55
+ return blobs;
56
+ }
57
+
58
+ /**
59
+ * Check if a blob exists in the store.
60
+ * @param versionedBlobHash - The versioned blob hash (0x-prefixed hex string)
61
+ */
62
+ exists(versionedBlobHash: string): Promise<boolean> {
63
+ return this.store.exists(this.blobPath(versionedBlobHash));
64
+ }
65
+
66
+ /**
67
+ * Save a single blob to the store.
68
+ * @param blob - The blob to save
69
+ * @param skipIfExists - Skip saving if blob already exists (default: true)
70
+ * @throws Error if the store is read-only
71
+ */
72
+ async saveBlob(blob: Blob, skipIfExists = true): Promise<void> {
73
+ if (!this.isWritable()) {
74
+ throw new Error('FileStore is read-only');
75
+ }
76
+
77
+ const versionedHash = `0x${computeEthVersionedBlobHash(blob.commitment).toString('hex')}`;
78
+
79
+ if (skipIfExists && (await this.store.exists(this.blobPath(versionedHash)))) {
80
+ this.log.trace(`Blob ${versionedHash} already exists, skipping`);
81
+ return;
82
+ }
83
+
84
+ // index=-1 is deliberate as we don't know the actual blob index in most cases when the filestores are used (blobs are saved in the filestores before we ever fetch them from L1)
85
+ const json = blob.toJson(-1);
86
+ await (this.store as FileStore).save(
87
+ this.blobPath(versionedHash),
88
+ outboundTransform(Buffer.from(JSON.stringify(json))),
89
+ );
90
+ this.log.debug(`Saved blob ${versionedHash} to filestore`);
91
+ }
92
+
93
+ /**
94
+ * Save multiple blobs to the store in parallel.
95
+ * @param blobs - The blobs to save (either Blob[] or BlobWithIndex[])
96
+ * @param skipIfExists - Skip saving if blob already exists (default: true)
97
+ */
98
+ async saveBlobs(blobs: Blob[] | BlobWithIndex[], skipIfExists = true): Promise<void> {
99
+ await Promise.all(
100
+ blobs.map(b => {
101
+ const blob = 'blob' in b ? b.blob : b;
102
+ return this.saveBlob(blob, skipIfExists);
103
+ }),
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Get the base URL/path of the filestore.
109
+ */
110
+ getBaseUrl(): string {
111
+ return this.basePath;
112
+ }
113
+
114
+ /**
115
+ * Test if the filestore connection is working.
116
+ */
117
+ testConnection(): Promise<boolean> {
118
+ // This implementation will be improved in a separate PR
119
+ // Currently underlying filestore implementations do not expose an easy way to test connectivitiy
120
+ return Promise.resolve(true);
121
+ }
122
+
123
+ /**
124
+ * Check if the store supports write operations.
125
+ */
126
+ private isWritable(): boolean {
127
+ return 'save' in this.store;
128
+ }
129
+ }
@@ -0,0 +1,2 @@
1
+ export * from './filestore_blob_client.js';
2
+ export * from './factory.js';
@@ -0,0 +1,50 @@
1
+ import type { Hex } from 'viem';
2
+ import { z } from 'zod';
3
+
4
+ export interface PostBlobSidecarRequest {
5
+ blobs: Array<{
6
+ index: number;
7
+ blob: {
8
+ type: string;
9
+ data: string;
10
+ };
11
+ }>;
12
+ }
13
+
14
+ export const blockRootSchema = z
15
+ .string()
16
+ .regex(/^0x[0-9a-fA-F]{0,64}$/)
17
+ .max(66)
18
+ .transform(str => str as Hex);
19
+
20
+ export const slotSchema = z.number().int().positive();
21
+
22
+ // Define the Zod schema for an array of numbers
23
+ export const indicesSchema = z.optional(
24
+ z
25
+ .string()
26
+ .refine(str => str.split(',').every(item => !isNaN(Number(item))), {
27
+ message: 'All items in the query must be valid numbers.',
28
+ })
29
+ .transform(str => str.split(',').map(Number)),
30
+ ); // Convert to an array of numbers
31
+
32
+ /**
33
+ * Block identifier. The spec allows for <hex encoded blockRoot with 0x prefix>, <slot>, "head", "genesis", "finalized", but we only support the block root.
34
+ * See https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlobSidecars.
35
+ */
36
+ export const blockIdSchema = blockRootSchema;
37
+
38
+ export const postBlobSidecarSchema = z.object({
39
+ // eslint-disable-next-line camelcase
40
+ block_id: blockIdSchema,
41
+ blobs: z.array(
42
+ z.object({
43
+ index: z.number().int().nonnegative(),
44
+ blob: z.object({
45
+ type: z.string(),
46
+ data: z.string(),
47
+ }),
48
+ }),
49
+ ),
50
+ });
@@ -0,0 +1,48 @@
1
+ import { Blob, type BlobJson } from '@aztec/blob-lib';
2
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
3
+
4
+ /** Serialized an array of blobs with their indexes to be stored at a given block id */
5
+ export class BlobsWithIndexes {
6
+ constructor(public blobs: BlobWithIndex[]) {}
7
+
8
+ public toBuffer(): Buffer {
9
+ return serializeToBuffer(this.blobs.length, this.blobs);
10
+ }
11
+
12
+ public static fromBuffer(buffer: Buffer | BufferReader): BlobsWithIndexes {
13
+ const reader = BufferReader.asReader(buffer);
14
+ return new BlobsWithIndexes(reader.readArray(reader.readNumber(), BlobWithIndex));
15
+ }
16
+
17
+ public getBlobsFromIndices(indices: number[]): BlobWithIndex[] {
18
+ return this.blobs.filter((_, index) => indices.includes(index));
19
+ }
20
+ }
21
+
22
+ /** We store blobs alongside their index in the block */
23
+ export class BlobWithIndex {
24
+ constructor(
25
+ /** The blob */
26
+ public blob: Blob,
27
+ /** The index of the blob in the block */
28
+ public index: number,
29
+ ) {}
30
+
31
+ public toBuffer(): Buffer {
32
+ return serializeToBuffer([this.blob, this.index]);
33
+ }
34
+
35
+ public static fromBuffer(buffer: Buffer | BufferReader): BlobWithIndex {
36
+ const reader = BufferReader.asReader(buffer);
37
+ return new BlobWithIndex(reader.readObject(Blob), reader.readNumber());
38
+ }
39
+
40
+ public static fromJson(json: BlobJson): BlobWithIndex {
41
+ return new BlobWithIndex(Blob.fromJson(json), parseInt(json.index));
42
+ }
43
+
44
+ // Follows the structure the beacon node api expects
45
+ public toJSON(): BlobJson {
46
+ return this.blob.toJson(this.index);
47
+ }
48
+ }
@@ -0,0 +1 @@
1
+ export * from './blob_with_index.js';