@peerbit/blocks 4.1.9 → 4.2.1
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/dist/benchmark/any-store.d.ts +2 -0
- package/dist/benchmark/any-store.d.ts.map +1 -0
- package/dist/benchmark/any-store.js +112 -0
- package/dist/benchmark/any-store.js.map +1 -0
- package/dist/src/any-blockstore.d.ts +23 -9
- package/dist/src/any-blockstore.d.ts.map +1 -1
- package/dist/src/any-blockstore.js +161 -18
- package/dist/src/any-blockstore.js.map +1 -1
- package/dist/src/interface.d.ts +19 -1
- package/dist/src/interface.d.ts.map +1 -1
- package/dist/src/libp2p.d.ts +28 -2
- package/dist/src/libp2p.d.ts.map +1 -1
- package/dist/src/libp2p.js +90 -9
- package/dist/src/libp2p.js.map +1 -1
- package/dist/src/remote.d.ts +49 -4
- package/dist/src/remote.d.ts.map +1 -1
- package/dist/src/remote.js +209 -19
- package/dist/src/remote.js.map +1 -1
- package/package.json +11 -9
- package/src/any-blockstore.ts +244 -28
- package/src/interface.ts +23 -1
- package/src/libp2p.ts +133 -14
- package/src/remote.ts +285 -21
package/src/any-blockstore.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { createStore } from "@peerbit/any-store";
|
|
|
2
2
|
import { type AnyStore } from "@peerbit/any-store-interface";
|
|
3
3
|
import {
|
|
4
4
|
type Blocks,
|
|
5
|
+
type GetOptions,
|
|
5
6
|
calculateRawCid,
|
|
6
7
|
cidifyString,
|
|
7
8
|
codecCodes,
|
|
@@ -16,6 +17,27 @@ import { waitFor } from "@peerbit/time";
|
|
|
16
17
|
import { type Block, decode } from "multiformats/block";
|
|
17
18
|
import * as raw from "multiformats/codecs/raw";
|
|
18
19
|
|
|
20
|
+
const isPromiseLike = <T>(value: Promise<T> | T): value is Promise<T> =>
|
|
21
|
+
typeof (value as { then?: unknown })?.then === "function";
|
|
22
|
+
|
|
23
|
+
type ImmutableBlockStore = AnyStore & {
|
|
24
|
+
putImmutable?: (key: string, value: Uint8Array) => Promise<void> | void;
|
|
25
|
+
putManyImmutable?: (
|
|
26
|
+
entries: Iterable<readonly [string, Uint8Array]>,
|
|
27
|
+
) => Promise<void> | void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type NativeLogBlockStoreCarrier = {
|
|
31
|
+
getNativeLogBlockStoreHandle?: () => unknown;
|
|
32
|
+
getBlockResponsePayload?: (cid: string) => Uint8Array | undefined;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type BlockGetOptions = GetOptions & {
|
|
36
|
+
raw?: boolean;
|
|
37
|
+
links?: string[];
|
|
38
|
+
hasher?: any;
|
|
39
|
+
};
|
|
40
|
+
|
|
19
41
|
export class AnyBlockStore implements Blocks {
|
|
20
42
|
private _store: AnyStore;
|
|
21
43
|
private _opening: Promise<any>;
|
|
@@ -25,42 +47,84 @@ export class AnyBlockStore implements Blocks {
|
|
|
25
47
|
this._store = store;
|
|
26
48
|
}
|
|
27
49
|
|
|
50
|
+
getNativeLogBlockStoreHandle(): unknown {
|
|
51
|
+
return (this._store as NativeLogBlockStoreCarrier)
|
|
52
|
+
.getNativeLogBlockStoreHandle?.();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getBlockResponsePayload(cid: string): Uint8Array | undefined {
|
|
56
|
+
return (this._store as NativeLogBlockStoreCarrier).getBlockResponsePayload?.(
|
|
57
|
+
cid,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private async decodeStoredBytes(
|
|
62
|
+
cid: string,
|
|
63
|
+
bytes: Uint8Array,
|
|
64
|
+
options?: BlockGetOptions,
|
|
65
|
+
): Promise<Uint8Array> {
|
|
66
|
+
const cidObject = cidifyString(cid);
|
|
67
|
+
if (
|
|
68
|
+
cidObject.code === raw.code &&
|
|
69
|
+
(options?.hasher == null || options.hasher === defaultHasher)
|
|
70
|
+
) {
|
|
71
|
+
return bytes;
|
|
72
|
+
}
|
|
73
|
+
const codec = codecCodes[cidObject.code as keyof typeof codecCodes];
|
|
74
|
+
const block = await decode({
|
|
75
|
+
bytes,
|
|
76
|
+
codec,
|
|
77
|
+
hasher: options?.hasher || defaultHasher,
|
|
78
|
+
});
|
|
79
|
+
return (block as Block<Uint8Array, any, any, any>).bytes;
|
|
80
|
+
}
|
|
81
|
+
|
|
28
82
|
async get(
|
|
29
83
|
cid: string,
|
|
30
|
-
options?:
|
|
31
|
-
raw?: boolean;
|
|
32
|
-
links?: string[];
|
|
33
|
-
hasher?: any;
|
|
34
|
-
remote: {
|
|
35
|
-
timeout?: number;
|
|
36
|
-
};
|
|
37
|
-
},
|
|
84
|
+
options?: BlockGetOptions,
|
|
38
85
|
): Promise<Uint8Array | undefined> {
|
|
39
|
-
const cidObject = cidifyString(cid);
|
|
40
86
|
try {
|
|
41
87
|
const bytes = await this._store.get(cid);
|
|
42
88
|
if (!bytes) {
|
|
43
89
|
return undefined;
|
|
44
90
|
}
|
|
91
|
+
return this.decodeStoredBytes(cid, bytes, options);
|
|
92
|
+
} catch (error: any) {
|
|
45
93
|
if (
|
|
46
|
-
|
|
47
|
-
(
|
|
94
|
+
typeof error?.code === "string" &&
|
|
95
|
+
error?.code?.indexOf("LEVEL_NOT_FOUND") !== -1
|
|
48
96
|
) {
|
|
49
|
-
return
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getMany(
|
|
104
|
+
cids: string[],
|
|
105
|
+
options?: BlockGetOptions,
|
|
106
|
+
): Promise<Array<Uint8Array | undefined>> {
|
|
107
|
+
const store = this._store as AnyStore & {
|
|
108
|
+
getMany?: (keys: string[]) => Promise<Array<Uint8Array | undefined>>;
|
|
109
|
+
};
|
|
110
|
+
try {
|
|
111
|
+
if (typeof store.getMany === "function") {
|
|
112
|
+
const values = await store.getMany(cids);
|
|
113
|
+
return Promise.all(
|
|
114
|
+
values.map((bytes, index) =>
|
|
115
|
+
bytes
|
|
116
|
+
? this.decodeStoredBytes(cids[index]!, bytes, options)
|
|
117
|
+
: undefined,
|
|
118
|
+
),
|
|
119
|
+
);
|
|
50
120
|
}
|
|
51
|
-
|
|
52
|
-
const block = await decode({
|
|
53
|
-
bytes,
|
|
54
|
-
codec,
|
|
55
|
-
hasher: options?.hasher || defaultHasher,
|
|
56
|
-
});
|
|
57
|
-
return (block as Block<Uint8Array, any, any, any>).bytes;
|
|
121
|
+
return Promise.all(cids.map((cid) => this.get(cid, options)));
|
|
58
122
|
} catch (error: any) {
|
|
59
123
|
if (
|
|
60
124
|
typeof error?.code === "string" &&
|
|
61
125
|
error?.code?.indexOf("LEVEL_NOT_FOUND") !== -1
|
|
62
126
|
) {
|
|
63
|
-
return
|
|
127
|
+
return Promise.all(cids.map((cid) => this.get(cid, options)));
|
|
64
128
|
}
|
|
65
129
|
throw error;
|
|
66
130
|
}
|
|
@@ -75,13 +139,7 @@ export class AnyBlockStore implements Blocks {
|
|
|
75
139
|
try {
|
|
76
140
|
await this._store.put(put.cid, bbytes);
|
|
77
141
|
} catch (error: any) {
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
typeof error?.code === "string" &&
|
|
81
|
-
error.code === "LEVEL_DATABASE_NOT_OPEN" &&
|
|
82
|
-
this._closeController?.signal.aborted === true &&
|
|
83
|
-
(status === "closing" || status === "closed")
|
|
84
|
-
) {
|
|
142
|
+
if (await this.isClosingStorePutError(error)) {
|
|
85
143
|
// Late replication writes can outlive shutdown. At this point the
|
|
86
144
|
// backing store is intentionally closing, so report the deterministic
|
|
87
145
|
// CID while discarding the write instead of leaking an unhandled
|
|
@@ -93,10 +151,140 @@ export class AnyBlockStore implements Blocks {
|
|
|
93
151
|
return put.cid;
|
|
94
152
|
}
|
|
95
153
|
|
|
154
|
+
async putMany(
|
|
155
|
+
blocks: Array<Uint8Array | { block: Block<any, any, any, any>; cid: string }>,
|
|
156
|
+
): Promise<string[]> {
|
|
157
|
+
const puts = await Promise.all(
|
|
158
|
+
blocks.map((bytes) =>
|
|
159
|
+
bytes instanceof Uint8Array ? calculateRawCid(bytes) : bytes,
|
|
160
|
+
),
|
|
161
|
+
);
|
|
162
|
+
const store = this._store as AnyStore & {
|
|
163
|
+
putMany?: (
|
|
164
|
+
entries: Iterable<readonly [string, Uint8Array]>,
|
|
165
|
+
) => Promise<void> | void;
|
|
166
|
+
};
|
|
167
|
+
try {
|
|
168
|
+
if (puts.length === 1) {
|
|
169
|
+
const put = puts[0]!;
|
|
170
|
+
const result = this.putKnown(put.cid, put.block.bytes);
|
|
171
|
+
if (isPromiseLike(result)) {
|
|
172
|
+
await result;
|
|
173
|
+
}
|
|
174
|
+
} else if (typeof store.putMany === "function") {
|
|
175
|
+
await store.putMany(puts.map((put) => [put.cid, put.block.bytes] as const));
|
|
176
|
+
} else {
|
|
177
|
+
for (const put of puts) {
|
|
178
|
+
await this._store.put(put.cid, put.block.bytes);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (error: any) {
|
|
182
|
+
if (await this.isClosingStorePutError(error)) {
|
|
183
|
+
return puts.map((put) => put.cid);
|
|
184
|
+
}
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
return puts.map((put) => put.cid);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
putKnown(cid: string, bytes: Uint8Array): Promise<string> | string {
|
|
191
|
+
try {
|
|
192
|
+
const immutableStore = this._store as ImmutableBlockStore;
|
|
193
|
+
const result = immutableStore.putImmutable
|
|
194
|
+
? immutableStore.putImmutable(cid, bytes)
|
|
195
|
+
: this._store.put(cid, bytes);
|
|
196
|
+
if (isPromiseLike(result)) {
|
|
197
|
+
return result
|
|
198
|
+
.then(() => cid)
|
|
199
|
+
.catch((error) => this.handleStorePutError(error, cid));
|
|
200
|
+
}
|
|
201
|
+
} catch (error: any) {
|
|
202
|
+
return this.handleStorePutError(error, cid);
|
|
203
|
+
}
|
|
204
|
+
return cid;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
putKnownMany(
|
|
208
|
+
blocks: Array<readonly [cid: string, bytes: Uint8Array]>,
|
|
209
|
+
): Promise<string[]> | string[] {
|
|
210
|
+
const store = this._store as AnyStore & {
|
|
211
|
+
putMany?: (
|
|
212
|
+
entries: Iterable<readonly [string, Uint8Array]>,
|
|
213
|
+
) => Promise<void> | void;
|
|
214
|
+
putManyImmutable?: (
|
|
215
|
+
entries: Iterable<readonly [string, Uint8Array]>,
|
|
216
|
+
) => Promise<void> | void;
|
|
217
|
+
};
|
|
218
|
+
if (blocks.length === 1) {
|
|
219
|
+
const [cid, bytes] = blocks[0]!;
|
|
220
|
+
const result = this.putKnown(cid, bytes);
|
|
221
|
+
return isPromiseLike(result) ? result.then(() => [cid]) : [cid];
|
|
222
|
+
}
|
|
223
|
+
return this.putKnownManyWithStoreBatch(blocks, store);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private async putKnownManyWithStoreBatch(
|
|
227
|
+
blocks: Array<readonly [cid: string, bytes: Uint8Array]>,
|
|
228
|
+
store: AnyStore & {
|
|
229
|
+
putMany?: (
|
|
230
|
+
entries: Iterable<readonly [string, Uint8Array]>,
|
|
231
|
+
) => Promise<void> | void;
|
|
232
|
+
putManyImmutable?: (
|
|
233
|
+
entries: Iterable<readonly [string, Uint8Array]>,
|
|
234
|
+
) => Promise<void> | void;
|
|
235
|
+
},
|
|
236
|
+
): Promise<string[]> {
|
|
237
|
+
try {
|
|
238
|
+
if (typeof store.putManyImmutable === "function") {
|
|
239
|
+
await store.putManyImmutable(blocks);
|
|
240
|
+
} else if (typeof store.putMany === "function") {
|
|
241
|
+
await store.putMany(blocks);
|
|
242
|
+
} else {
|
|
243
|
+
for (const [cid, bytes] of blocks) {
|
|
244
|
+
await this._store.put(cid, bytes);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} catch (error: any) {
|
|
248
|
+
if (await this.isClosingStorePutError(error)) {
|
|
249
|
+
return blocks.map(([cid]) => cid);
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
return blocks.map(([cid]) => cid);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private async handleStorePutError<T>(error: any, value: T): Promise<T> {
|
|
257
|
+
if (await this.isClosingStorePutError(error)) {
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private async isClosingStorePutError(error: any): Promise<boolean> {
|
|
264
|
+
const status = await this._store.status();
|
|
265
|
+
return (
|
|
266
|
+
typeof error?.code === "string" &&
|
|
267
|
+
error.code === "LEVEL_DATABASE_NOT_OPEN" &&
|
|
268
|
+
this._closeController?.signal.aborted === true &&
|
|
269
|
+
(status === "closing" || status === "closed")
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
96
273
|
async rm(cid: string): Promise<void> {
|
|
97
274
|
await this._store.del(cid);
|
|
98
275
|
}
|
|
99
276
|
|
|
277
|
+
async rmMany(cids: string[]): Promise<number> {
|
|
278
|
+
const store = this._store as AnyStore & {
|
|
279
|
+
delMany?: (keys: string[]) => Promise<number>;
|
|
280
|
+
};
|
|
281
|
+
if (typeof store.delMany === "function") {
|
|
282
|
+
return store.delMany(cids);
|
|
283
|
+
}
|
|
284
|
+
await Promise.all(cids.map((cid) => this._store.del(cid)));
|
|
285
|
+
return cids.length;
|
|
286
|
+
}
|
|
287
|
+
|
|
100
288
|
async *iterator(): AsyncGenerator<[string, Uint8Array], void, void> {
|
|
101
289
|
for await (const [key, value] of this._store.iterator()) {
|
|
102
290
|
yield [key, value];
|
|
@@ -104,7 +292,35 @@ export class AnyBlockStore implements Blocks {
|
|
|
104
292
|
}
|
|
105
293
|
|
|
106
294
|
async has(cid: string) {
|
|
107
|
-
|
|
295
|
+
try {
|
|
296
|
+
return !!(await this._store.get(cid));
|
|
297
|
+
} catch (error: any) {
|
|
298
|
+
if (
|
|
299
|
+
typeof error?.code === "string" &&
|
|
300
|
+
error?.code?.indexOf("LEVEL_NOT_FOUND") !== -1
|
|
301
|
+
) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async hasMany(cids: string[]): Promise<boolean[]> {
|
|
309
|
+
const store = this._store as AnyStore & {
|
|
310
|
+
getMany?: (
|
|
311
|
+
keys: string[],
|
|
312
|
+
) => Promise<Array<Uint8Array | undefined>> | Array<Uint8Array | undefined>;
|
|
313
|
+
hasMany?: (keys: string[]) => Promise<boolean[]> | boolean[];
|
|
314
|
+
};
|
|
315
|
+
if (typeof store.hasMany === "function") {
|
|
316
|
+
return store.hasMany(cids);
|
|
317
|
+
}
|
|
318
|
+
if (typeof store.getMany === "function") {
|
|
319
|
+
const values = await store.getMany(cids);
|
|
320
|
+
return values.map((value) => value != null);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return Promise.all(cids.map((cid) => this.has(cid)));
|
|
108
324
|
}
|
|
109
325
|
|
|
110
326
|
async start(): Promise<void> {
|
package/src/interface.ts
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import type { MaybePromise } from "@peerbit/any-store-interface";
|
|
2
|
-
import type { Blocks as IBlockStore } from "@peerbit/blocks-interface";
|
|
2
|
+
import type { Blocks as IBlockStore, GetOptions } from "@peerbit/blocks-interface";
|
|
3
|
+
import type { Block } from "multiformats/block";
|
|
3
4
|
|
|
4
5
|
export type StoreStatus = MaybePromise<
|
|
5
6
|
"open" | "opening" | "closed" | "closing"
|
|
6
7
|
>;
|
|
7
8
|
export interface BlockStore extends IBlockStore {
|
|
9
|
+
putMany(
|
|
10
|
+
blocks: Array<Uint8Array | { block: Block<any, any, any, any>; cid: string }>,
|
|
11
|
+
): MaybePromise<string[]>;
|
|
12
|
+
putKnown(cid: string, bytes: Uint8Array): MaybePromise<string>;
|
|
13
|
+
putKnownMany(
|
|
14
|
+
blocks: Array<readonly [cid: string, bytes: Uint8Array]>,
|
|
15
|
+
): MaybePromise<string[]>;
|
|
16
|
+
getMany(
|
|
17
|
+
cids: string[],
|
|
18
|
+
options?: GetOptions,
|
|
19
|
+
): MaybePromise<Array<Uint8Array | undefined>>;
|
|
20
|
+
hasMany(cids: string[]): MaybePromise<boolean[]>;
|
|
21
|
+
rmMany(cids: string[]): MaybePromise<number | void>;
|
|
22
|
+
getNativeLogBlockStoreHandle?(): unknown;
|
|
23
|
+
/**
|
|
24
|
+
* Serialize a `BlockResponse` payload for a natively stored block without
|
|
25
|
+
* materializing the block bytes as a JS value. Only implemented by stores
|
|
26
|
+
* backed by the native log block store; `undefined` when the block is not
|
|
27
|
+
* held natively.
|
|
28
|
+
*/
|
|
29
|
+
getBlockResponsePayload?(cid: string): Uint8Array | undefined;
|
|
8
30
|
start(): Promise<void>;
|
|
9
31
|
stop(): Promise<void>;
|
|
10
32
|
status(): StoreStatus;
|
package/src/libp2p.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { deserialize, serialize } from "@dao-xyz/borsh";
|
|
2
2
|
import { createStore } from "@peerbit/any-store";
|
|
3
|
+
import type { AnyStore } from "@peerbit/any-store-interface";
|
|
3
4
|
import type { GetOptions, Blocks as IBlocks } from "@peerbit/blocks-interface";
|
|
4
5
|
import { getPublicKeyFromPeerId, type PublicSignKey } from "@peerbit/crypto";
|
|
5
6
|
import { DirectStream } from "@peerbit/stream";
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
type DirectStreamComponents,
|
|
9
|
+
type RustCoreStream,
|
|
10
|
+
} from "@peerbit/stream";
|
|
7
11
|
import {
|
|
8
12
|
createRequestTransportContext,
|
|
9
13
|
type DataMessage,
|
|
10
14
|
type WaitForPeersFn,
|
|
11
15
|
} from "@peerbit/stream-interface";
|
|
16
|
+
import type { Block } from "multiformats/block";
|
|
12
17
|
import { AnyBlockStore } from "./any-blockstore.js";
|
|
13
|
-
import { BlockMessage, RemoteBlocks } from "./remote.js";
|
|
18
|
+
import { BlockMessage, BlockRequest, BlockResponse, RemoteBlocks } from "./remote.js";
|
|
14
19
|
|
|
15
20
|
export type DirectBlockComponents = DirectStreamComponents;
|
|
16
21
|
|
|
@@ -23,6 +28,7 @@ export class DirectBlock extends DirectStream implements IBlocks {
|
|
|
23
28
|
components: DirectBlockComponents,
|
|
24
29
|
options?: {
|
|
25
30
|
directory?: string;
|
|
31
|
+
localStore?: AnyStore;
|
|
26
32
|
canRelayMessage?: boolean;
|
|
27
33
|
localTimeout?: number;
|
|
28
34
|
messageProcessingConcurrency?: number;
|
|
@@ -47,8 +53,20 @@ export class DirectBlock extends DirectStream implements IBlocks {
|
|
|
47
53
|
maxProvidersPerCid?: number;
|
|
48
54
|
};
|
|
49
55
|
requeryOnReachable?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Run the block-exchange protocol on the native DirectStream core
|
|
58
|
+
* (`@peerbit/network-rust`): codec, provider resolution and caches
|
|
59
|
+
* execute in wasm, and natively stored blocks are served without
|
|
60
|
+
* surfacing their bytes to JS. Defaults to the same rust-core mode
|
|
61
|
+
* as the underlying DirectStream.
|
|
62
|
+
*/
|
|
63
|
+
rustCore?: RustCoreStream | false;
|
|
50
64
|
},
|
|
51
65
|
) {
|
|
66
|
+
if (options?.directory && options.localStore) {
|
|
67
|
+
throw new Error("DirectBlock options cannot include both directory and localStore");
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
super(components, ["/peerbit/direct-block/1.0.0"], {
|
|
53
71
|
messageProcessingConcurrency: options?.messageProcessingConcurrency || 10,
|
|
54
72
|
canRelayMessage: options?.canRelayMessage ?? true,
|
|
@@ -56,9 +74,29 @@ export class DirectBlock extends DirectStream implements IBlocks {
|
|
|
56
74
|
dialer: false,
|
|
57
75
|
pruner: false,
|
|
58
76
|
},
|
|
77
|
+
rustCore: options?.rustCore,
|
|
59
78
|
});
|
|
60
79
|
|
|
80
|
+
const blockExchange = this.rustCore?.blockExchange;
|
|
61
81
|
const defaultResolveProviders = () => {
|
|
82
|
+
if (blockExchange) {
|
|
83
|
+
const negotiated = [...this.peers.keys()];
|
|
84
|
+
const connected: string[] = [];
|
|
85
|
+
for (const conn of this.components.connectionManager.getConnections()) {
|
|
86
|
+
try {
|
|
87
|
+
connected.push(
|
|
88
|
+
getPublicKeyFromPeerId(conn.remotePeer).hashcode(),
|
|
89
|
+
);
|
|
90
|
+
} catch {
|
|
91
|
+
// ignore unexpected key types
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return blockExchange.defaultProviderCandidates(
|
|
95
|
+
negotiated,
|
|
96
|
+
connected,
|
|
97
|
+
this.publicKeyHash,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
62
100
|
const out: string[] = [];
|
|
63
101
|
const push = (hash?: string) => {
|
|
64
102
|
if (!hash) return;
|
|
@@ -89,8 +127,11 @@ export class DirectBlock extends DirectStream implements IBlocks {
|
|
|
89
127
|
return out;
|
|
90
128
|
};
|
|
91
129
|
this.remoteBlocks = new RemoteBlocks({
|
|
92
|
-
local: new AnyBlockStore(
|
|
93
|
-
|
|
130
|
+
local: new AnyBlockStore(
|
|
131
|
+
options?.localStore ?? createStore(options?.directory),
|
|
132
|
+
),
|
|
133
|
+
publish: (message, options) =>
|
|
134
|
+
this.publish(this.encodeBlockMessage(message), options),
|
|
94
135
|
localTimeout: options?.localTimeout || 1000,
|
|
95
136
|
messageProcessingConcurrency: options?.messageProcessingConcurrency || 10,
|
|
96
137
|
waitFor: this.waitFor.bind(this) as WaitForPeersFn,
|
|
@@ -101,30 +142,97 @@ export class DirectBlock extends DirectStream implements IBlocks {
|
|
|
101
142
|
onPut: options?.onPut,
|
|
102
143
|
providerCache: options?.providerCache,
|
|
103
144
|
requeryOnReachable: options?.requeryOnReachable,
|
|
145
|
+
rust: blockExchange
|
|
146
|
+
? {
|
|
147
|
+
exchange: blockExchange,
|
|
148
|
+
publishRaw: (payload, options) => this.publish(payload, options),
|
|
149
|
+
}
|
|
150
|
+
: undefined,
|
|
104
151
|
});
|
|
105
152
|
|
|
106
153
|
this.onDataFn = (data: CustomEvent<DataMessage>) => {
|
|
107
|
-
data.detail?.data?.length &&
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
154
|
+
if (!(data.detail?.data?.length && data.detail.data.length > 0)) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
let message: BlockMessage;
|
|
158
|
+
try {
|
|
159
|
+
message = this.decodeBlockMessage(data.detail.data!);
|
|
160
|
+
} catch {
|
|
161
|
+
// Drop undecodable frames instead of letting the throw escape
|
|
162
|
+
// the 'data' listener (an uncaughtException in Node). The TS
|
|
163
|
+
// borsh decoder tolerates malformed frames lossily and fails
|
|
164
|
+
// later inside handleFetchRequest, which is caught; the native
|
|
165
|
+
// decoder throws on e.g. non-UTF-8 cid bytes, so match the
|
|
166
|
+
// tolerant behaviour here.
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this.remoteBlocks.onMessage(message, {
|
|
170
|
+
from: data.detail.header.signatures?.publicKeys[0]?.hashcode(),
|
|
171
|
+
transport: createRequestTransportContext(data.detail),
|
|
172
|
+
});
|
|
116
173
|
};
|
|
117
174
|
this.onPeerConnectedFn = (evt: CustomEvent<PublicSignKey>) =>
|
|
118
175
|
this.remoteBlocks.onReachable(evt.detail);
|
|
119
176
|
}
|
|
120
177
|
|
|
121
|
-
|
|
178
|
+
private encodeBlockMessage(message: BlockRequest | BlockResponse): Uint8Array {
|
|
179
|
+
const blockExchange = this.rustCore?.blockExchange;
|
|
180
|
+
if (blockExchange) {
|
|
181
|
+
if (message instanceof BlockRequest) {
|
|
182
|
+
return blockExchange.encodeBlockRequest(message.cid);
|
|
183
|
+
}
|
|
184
|
+
if (message instanceof BlockResponse) {
|
|
185
|
+
return blockExchange.encodeBlockResponse(message.cid, message.bytes);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return serialize(message);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private decodeBlockMessage(bytes: Uint8Array): BlockMessage {
|
|
192
|
+
const blockExchange = this.rustCore?.blockExchange;
|
|
193
|
+
if (blockExchange) {
|
|
194
|
+
const decoded = blockExchange.decodeBlockMessage(bytes);
|
|
195
|
+
return decoded.type === "request"
|
|
196
|
+
? new BlockRequest(decoded.cid)
|
|
197
|
+
: new BlockResponse(decoded.cid, decoded.bytes);
|
|
198
|
+
}
|
|
199
|
+
return deserialize(bytes, BlockMessage);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getNativeLogBlockStoreHandle(): unknown {
|
|
203
|
+
return this.remoteBlocks.getNativeLogBlockStoreHandle();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async put(
|
|
207
|
+
bytes: Uint8Array | { block: Block<any, any, any, any>; cid: string },
|
|
208
|
+
): Promise<string> {
|
|
122
209
|
return this.remoteBlocks.put(bytes);
|
|
123
210
|
}
|
|
124
211
|
|
|
212
|
+
async putMany(
|
|
213
|
+
blocks: Array<
|
|
214
|
+
Uint8Array | { block: Block<any, any, any, any>; cid: string }
|
|
215
|
+
>,
|
|
216
|
+
): Promise<string[]> {
|
|
217
|
+
return this.remoteBlocks.putMany(blocks);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async putKnown(cid: string, bytes: Uint8Array): Promise<string> {
|
|
221
|
+
return this.remoteBlocks.putKnown(cid, bytes);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async putKnownMany(
|
|
225
|
+
blocks: Array<readonly [cid: string, bytes: Uint8Array]>,
|
|
226
|
+
): Promise<string[]> {
|
|
227
|
+
return this.remoteBlocks.putKnownMany(blocks);
|
|
228
|
+
}
|
|
229
|
+
|
|
125
230
|
async has(cid: string) {
|
|
126
231
|
return this.remoteBlocks.has(cid);
|
|
127
232
|
}
|
|
233
|
+
async hasMany(cids: string[]): Promise<boolean[]> {
|
|
234
|
+
return this.remoteBlocks.hasMany(cids);
|
|
235
|
+
}
|
|
128
236
|
async get(
|
|
129
237
|
cid: string,
|
|
130
238
|
options?: GetOptions | undefined,
|
|
@@ -132,6 +240,13 @@ export class DirectBlock extends DirectStream implements IBlocks {
|
|
|
132
240
|
return this.remoteBlocks.get(cid, options);
|
|
133
241
|
}
|
|
134
242
|
|
|
243
|
+
async getMany(
|
|
244
|
+
cids: string[],
|
|
245
|
+
options?: GetOptions | undefined,
|
|
246
|
+
): Promise<Array<Uint8Array | undefined>> {
|
|
247
|
+
return this.remoteBlocks.getMany(cids, options);
|
|
248
|
+
}
|
|
249
|
+
|
|
135
250
|
hintProviders(cid: string, providers: string[]) {
|
|
136
251
|
this.remoteBlocks.hintProviders(cid, providers);
|
|
137
252
|
}
|
|
@@ -140,6 +255,10 @@ export class DirectBlock extends DirectStream implements IBlocks {
|
|
|
140
255
|
return this.remoteBlocks.rm(cid);
|
|
141
256
|
}
|
|
142
257
|
|
|
258
|
+
async rmMany(cids: string[]) {
|
|
259
|
+
return this.remoteBlocks.rmMany(cids);
|
|
260
|
+
}
|
|
261
|
+
|
|
143
262
|
async *iterator(): AsyncGenerator<[string, Uint8Array], void, void> {
|
|
144
263
|
for await (const [key, value] of this.remoteBlocks.iterator()) {
|
|
145
264
|
yield [key, value];
|