@leofcoin/chain 1.10.21 → 1.11.0

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 (29) hide show
  1. package/README.md +95 -0
  2. package/exports/browser/beacon-wire.js +2 -2
  3. package/exports/browser/{browser-CCfR9KOM-BGMMnVsR.js → browser-CCfR9KOM-CNWKLusu.js} +3 -3
  4. package/exports/browser/{browser-crypto-CH0ua22L.js → browser-crypto-B_UJguhk.js} +453 -35
  5. package/exports/browser/chain.js +1275 -996
  6. package/exports/browser/client-DbZBfpG6-C1cXgvfr.js +5943 -0
  7. package/exports/browser/{connection-monitor-D-krNiRH.js → connection-monitor-BzbeYGEt.js} +153 -24
  8. package/exports/browser/{index-dTQVgkpf-DOL2rIjp.js → index-dTQVgkpf-CkoAiRNX.js} +3 -3
  9. package/exports/browser/{index-DCbLvMKL.js → index-phv5KvaE.js} +238 -234
  10. package/exports/browser/{messages-DCePyqKC-BSvJxDWZ.js → messages-DZYCxoZI-B472zldo.js} +3 -3
  11. package/exports/browser/{node-browser-Ha4Lvntj.js → node-browser-BmyPBkL5.js} +55 -11
  12. package/exports/browser/node-browser.js +3 -3
  13. package/exports/browser/workers/block-worker.js +8 -9
  14. package/exports/browser/workers/machine-worker.js +141 -1195
  15. package/exports/{workers/worker-BDTWOCzS-COrAszCt.js → browser/workers/proposal.proto-8zgMfutf-B3So_tnS.js} +910 -398
  16. package/exports/chain.js +296 -1078
  17. package/exports/connection-monitor-BdFLRh-J.js +465 -0
  18. package/exports/connection-monitor.js +2 -324
  19. package/exports/{constants-Dy9i9S_o.js → constants-BQ6f-_OT.js} +1 -1
  20. package/exports/hash-format-state.js +3 -3
  21. package/exports/machine.js +550 -0
  22. package/exports/node.js +35 -2
  23. package/exports/state-checkpoint-BiQ1EXzo.js +110 -0
  24. package/exports/state-checkpoint.js +3 -0
  25. package/exports/workers/block-worker.js +8 -9
  26. package/exports/workers/machine-worker.js +141 -1195
  27. package/exports/{browser/workers/worker-BDTWOCzS-COrAszCt.js → workers/proposal.proto-8zgMfutf-B3So_tnS.js} +910 -398
  28. package/package.json +9 -7
  29. package/exports/browser/client-DKKzPSlb-C_1ug6ux.js +0 -2401
@@ -0,0 +1,550 @@
1
+ import addresses, { contractFactory } from '@leofcoin/addresses';
2
+ import { randombytes } from '@leofcoin/crypto';
3
+ import EasyWorker from '@leofcoin/workers/binary-worker';
4
+ import { ContractMessage } from '@leofcoin/messages';
5
+ import { ContractDeploymentError, ExecutionError } from '@leofcoin/errors';
6
+ import { createDebugger } from '@vandeurenglenn/debug';
7
+ import { createChainIdentity } from '@leofcoin/lib/chain-identity';
8
+ import { w as writeCheckpoint } from './state-checkpoint-BiQ1EXzo.js';
9
+ import { jsonStringifyBigInt } from '@leofcoin/utils';
10
+
11
+ class ReadOnlyMachine {
12
+ constructor(workerPath, readContract) {
13
+ this.workerPath = workerPath;
14
+ this.readContract = readContract;
15
+ }
16
+ #worker;
17
+ #pending = /* @__PURE__ */ new Map();
18
+ #tail = Promise.resolve();
19
+ #queued = 0;
20
+ #seed;
21
+ #cache = /* @__PURE__ */ new Map();
22
+ #cacheBytes = 0;
23
+ #closed = false;
24
+ read(seed, contract, method, params = [], sender = "leofcoin:read-only") {
25
+ if (this.#closed) return Promise.reject(new Error("Read-only machine is closed"));
26
+ if (typeof contract !== "string" || !contract || typeof method !== "string" || !method || ["constructor", "__proto__", "prototype"].includes(method) || !Array.isArray(params))
27
+ return Promise.reject(new Error("Invalid read-only contract query"));
28
+ if (this.#queued >= 32) return Promise.reject(new Error("Read-only query queue is full"));
29
+ let key;
30
+ try {
31
+ key = JSON.stringify({ contract, method, params, sender }, jsonStringifyBigInt);
32
+ if (new TextEncoder().encode(key).byteLength > 65536) throw new Error("Read-only query is too large");
33
+ params = structuredClone(params);
34
+ } catch (error) {
35
+ return Promise.reject(error);
36
+ }
37
+ this.#queued++;
38
+ const result = this.#tail.then(async () => {
39
+ if (this.#closed) throw new Error("Read-only machine is closed");
40
+ if (this.#seed !== seed) {
41
+ this.#seed = seed;
42
+ this.#cache.clear();
43
+ this.#cacheBytes = 0;
44
+ }
45
+ if (this.#cache.has(key)) return structuredClone(this.#cache.get(key).value);
46
+ try {
47
+ await this.#start();
48
+ const initialized = await this.#request("init", {
49
+ readOnly: true,
50
+ fromState: true,
51
+ blocks: [],
52
+ peerid: "leofcoin:read-only",
53
+ lastBlock: seed.lastBlock,
54
+ state: seed.states,
55
+ info: seed.info
56
+ });
57
+ if (initialized !== true) throw new Error("Worker does not support isolated contract queries");
58
+ const value = await this.#request("read", { contract, method, params, sender });
59
+ const bytes = new TextEncoder().encode(JSON.stringify(value, jsonStringifyBigInt) ?? "").byteLength;
60
+ if (bytes > 4 * 1024 * 1024) throw new Error("Read-only result is too large");
61
+ while (this.#cache.size && (this.#cache.size >= 64 || this.#cacheBytes + bytes > 4 * 1024 * 1024)) {
62
+ const oldest = this.#cache.keys().next().value;
63
+ this.#cacheBytes -= this.#cache.get(oldest).bytes;
64
+ this.#cache.delete(oldest);
65
+ }
66
+ this.#cache.set(key, { value: structuredClone(value), bytes });
67
+ this.#cacheBytes += bytes;
68
+ return value;
69
+ } catch (error) {
70
+ this.#stop();
71
+ throw error;
72
+ }
73
+ });
74
+ this.#tail = result.then(() => void 0, () => void 0);
75
+ return result.finally(() => {
76
+ this.#queued--;
77
+ });
78
+ }
79
+ async #start() {
80
+ if (this.#worker) return;
81
+ const worker = await new EasyWorker(this.workerPath, { serialization: "advanced", type: "module" });
82
+ if (this.#closed) {
83
+ worker.terminate();
84
+ throw new Error("Read-only machine is closed");
85
+ }
86
+ this.#worker = worker;
87
+ worker.onmessage(async (message) => {
88
+ if (this.#worker !== worker) return;
89
+ if (message.type === "ask") {
90
+ try {
91
+ const input = message.question === "peers" ? [] : message.question === "contract" ? await this.readContract(message.input) : void 0;
92
+ if (this.#worker === worker) worker.postMessage({ id: message.id, input });
93
+ } catch {
94
+ if (this.#worker === worker) worker.postMessage({ id: message.id, input: void 0 });
95
+ }
96
+ return;
97
+ }
98
+ const pending = this.#pending.get(message.id);
99
+ if (!pending) return;
100
+ if (message.type !== "response" && !["initError", "readError"].includes(message.type)) return;
101
+ clearTimeout(pending.timer);
102
+ this.#pending.delete(message.id);
103
+ if (message.type === "response") pending.resolve(message.value);
104
+ else pending.reject(new Error(message.message || "Read-only worker failed"));
105
+ });
106
+ worker.onerror(() => {
107
+ if (this.#worker === worker) this.#stop();
108
+ });
109
+ worker.worker?.once?.("exit", () => {
110
+ if (this.#worker === worker) this.#stop();
111
+ });
112
+ }
113
+ #request(type, input) {
114
+ return new Promise((resolve, reject) => {
115
+ const id = crypto.randomUUID();
116
+ const timer = setTimeout(() => this.#stop(new Error("Read-only query timed out")), 1e4);
117
+ this.#pending.set(id, { resolve, reject, timer });
118
+ try {
119
+ this.#worker.postMessage({ id, type, input });
120
+ } catch (error) {
121
+ this.#stop(error);
122
+ }
123
+ });
124
+ }
125
+ #stop(error = new Error("Read-only worker stopped")) {
126
+ const worker = this.#worker;
127
+ this.#worker = void 0;
128
+ for (const pending of this.#pending.values()) {
129
+ clearTimeout(pending.timer);
130
+ pending.reject(error);
131
+ }
132
+ this.#pending.clear();
133
+ worker?.terminate();
134
+ }
135
+ close() {
136
+ this.#closed = true;
137
+ this.#cache.clear();
138
+ this.#cacheBytes = 0;
139
+ this.#seed = void 0;
140
+ this.#stop();
141
+ }
142
+ }
143
+
144
+ const genesisContracts = {
145
+ contractFactory: addresses.contractFactory,
146
+ nativeToken: addresses.nativeToken,
147
+ nameService: addresses.nameService,
148
+ validators: addresses.validators
149
+ };
150
+ const debug = createDebugger("leofcoin/machine");
151
+ class Machine {
152
+ constructor(blocks = [], checkpoint) {
153
+ this.states = {
154
+ states: {},
155
+ lastBlock: {
156
+ index: -1,
157
+ hash: "0x0"
158
+ },
159
+ accounts: {},
160
+ info: {
161
+ nativeCalls: BigInt(0),
162
+ nativeMints: BigInt(0),
163
+ nativeBurns: BigInt(0),
164
+ nativeTransfers: BigInt(0),
165
+ totalBurnAmount: BigInt(0),
166
+ totalMintAmount: BigInt(0),
167
+ totalTransferAmount: BigInt(0),
168
+ totalTransactions: BigInt(0),
169
+ totalBlocks: BigInt(0)
170
+ }
171
+ };
172
+ this.wantList = [];
173
+ this.ready = new Promise((resolve, reject) => {
174
+ this.readyResolve = resolve;
175
+ this.readyReject = reject;
176
+ });
177
+ this.#checkpointGeneration = -1;
178
+ this.#closed = false;
179
+ this.init(blocks, checkpoint).then(() => this.readyResolve(this), (error) => {
180
+ this.close();
181
+ this.readyReject(error);
182
+ });
183
+ }
184
+ #checkpointGeneration;
185
+ #chainId;
186
+ #reader;
187
+ #contractReader;
188
+ #readSnapshot;
189
+ #workerPath;
190
+ #closed;
191
+ close() {
192
+ this.#closed = true;
193
+ this.#reader?.close();
194
+ this.#contractReader?.close();
195
+ this.worker?.terminate();
196
+ }
197
+ async init(blocks, checkpoint) {
198
+ return this.#init(blocks, checkpoint);
199
+ }
200
+ async #onmessage(data) {
201
+ if (this.#closed) return;
202
+ switch (data.type) {
203
+ case "contractError": {
204
+ console.warn(data.message ?? data.error);
205
+ console.warn(`contract error: ${data.hash}`);
206
+ break;
207
+ }
208
+ case "initError": {
209
+ pubsub.publish(data.id, { error: new Error(`Machine initialization failed: ${data.message}`) });
210
+ break;
211
+ }
212
+ case "executeError": {
213
+ pubsub.publish(data.id, { error: data.message });
214
+ break;
215
+ }
216
+ case "addLoadedBlockError":
217
+ case "runContractError":
218
+ case "collectFeeError":
219
+ case "settleRewardsError": {
220
+ pubsub.publish(data.id, { error: new Error(data.message) });
221
+ break;
222
+ }
223
+ case "snapshotError": {
224
+ pubsub.publish(data.id, { error: new Error(data.message) });
225
+ break;
226
+ }
227
+ case "getError": {
228
+ pubsub.publish(data.id, { error: new Error(data.message) });
229
+ break;
230
+ }
231
+ case "debug": {
232
+ if (data.message.includes("loaded transactions for block:")) {
233
+ pubsub.publish("block-loaded", data.message.replace("loaded transactions for block: ", "").split(" @")[0]);
234
+ }
235
+ break;
236
+ }
237
+ case "error": {
238
+ console.error(data.message);
239
+ break;
240
+ }
241
+ case "response": {
242
+ pubsub.publish(data.id, data.value ?? false);
243
+ break;
244
+ }
245
+ case "addToWantList": {
246
+ if (!this.wantList.includes(data.hash)) this.wantList.push(data.hash);
247
+ break;
248
+ }
249
+ case "ask": {
250
+ if (data.question === "staticCall") {
251
+ try {
252
+ const { snapshot, contract, method, params, sender } = data.input;
253
+ const input = await this.#contractReader.read(snapshot, contract, method, params, sender);
254
+ if (!this.#closed) this.worker.postMessage({ id: data.id, input });
255
+ } catch (error) {
256
+ if (!this.#closed) this.worker.postMessage({ id: data.id, error: error instanceof Error ? error.message : String(error) });
257
+ }
258
+ break;
259
+ }
260
+ if (data.question === "contract" || data.question === "transaction") {
261
+ try {
262
+ const input = await peernet.get(data.input, data.question);
263
+ if (input === void 0) {
264
+ this.worker.postMessage({ id: data.id, error: `could not get ${data.question} @${data.input}` });
265
+ this.wantList.push(data.input);
266
+ } else {
267
+ this.worker.postMessage({ id: data.id, input });
268
+ }
269
+ } catch (error) {
270
+ console.error(error);
271
+ this.worker.postMessage({ id: data.id, error: `could not get ${data.question} @${data.input}` });
272
+ this.wantList.push(data.input);
273
+ }
274
+ } else if (data.question === "peers") {
275
+ this.worker.postMessage({
276
+ id: data.id,
277
+ input: peernet.connections ? Object.entries(peernet.connections).map(([id, peer]) => peer.toJSON()) : []
278
+ });
279
+ } else {
280
+ this.worker.postMessage({ id: data.id, input: data.input });
281
+ }
282
+ }
283
+ }
284
+ }
285
+ async updateState(strict = false) {
286
+ try {
287
+ const snapshot = await this.#askWorker("snapshot");
288
+ snapshot.chainId = this.#chainId;
289
+ const accounts = {};
290
+ for (const address of await accountsStore.keys()) {
291
+ const raw = await accountsStore.get(address);
292
+ accounts[address] = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
293
+ }
294
+ snapshot.accounts = accounts;
295
+ const generation = this.#checkpointGeneration + 1;
296
+ await writeCheckpoint(stateStore, snapshot, generation, Object.values(genesisContracts));
297
+ this.#checkpointGeneration = generation;
298
+ this.states = snapshot;
299
+ this.#readSnapshot = structuredClone(snapshot);
300
+ } catch (error) {
301
+ if (strict) throw error;
302
+ console.error(error);
303
+ throw error;
304
+ }
305
+ }
306
+ async #init(blocks, checkpoint) {
307
+ const identity = await createChainIdentity(globalThis.peernet?.network || "leofcoin:peach", addresses);
308
+ this.#chainId = identity.chainId;
309
+ if (checkpoint) {
310
+ this.#checkpointGeneration = checkpoint.generation;
311
+ this.states = checkpoint.snapshot;
312
+ }
313
+ let pre = "./";
314
+ try {
315
+ const { fileURLToPath } = await import('url');
316
+ pre = fileURLToPath(new URL(".", import.meta.url));
317
+ } catch {
318
+ }
319
+ this.#workerPath = pre + "workers/machine-worker.js";
320
+ const readContract = (hash) => globalThis.contractStore.get(hash);
321
+ this.#reader = new ReadOnlyMachine(this.#workerPath, readContract);
322
+ this.#contractReader = new ReadOnlyMachine(this.#workerPath, readContract);
323
+ this.worker = await new EasyWorker(this.#workerPath, {
324
+ serialization: "advanced",
325
+ type: "module"
326
+ });
327
+ if (this.#closed) {
328
+ this.worker.terminate();
329
+ throw new Error("State machine is closed");
330
+ }
331
+ this.worker.onmessage(this.#onmessage.bind(this));
332
+ const initialized = await this.#askWorker("init", {
333
+ blocks,
334
+ fromState: Boolean(checkpoint),
335
+ lastBlock: checkpoint?.snapshot.lastBlock,
336
+ state: checkpoint?.snapshot.states,
337
+ info: checkpoint?.snapshot.info,
338
+ peerid: peernet.peerId
339
+ });
340
+ if (initialized !== true) throw new Error("Worker does not support checkpoint initialization; deploy matching chain and worker builds");
341
+ this.#readSnapshot = structuredClone(checkpoint?.snapshot ?? await this.#askWorker("snapshot"));
342
+ }
343
+ async #runContract(contractMessage) {
344
+ const hash = await contractMessage.hash();
345
+ return new Promise((resolve, reject) => {
346
+ const id = randombytes(20).toString("hex");
347
+ const onmessage = (message) => {
348
+ pubsub.unsubscribe(id, onmessage);
349
+ if (message?.error) reject(message.error);
350
+ else resolve(message);
351
+ };
352
+ pubsub.subscribe(id, onmessage);
353
+ this.worker.postMessage({
354
+ type: "run",
355
+ id,
356
+ input: {
357
+ decoded: contractMessage.decoded,
358
+ encoded: contractMessage.encoded,
359
+ hash
360
+ }
361
+ });
362
+ });
363
+ }
364
+ /**
365
+ *
366
+ * @param {Address} contract
367
+ * @param {String} method
368
+ * @param {Array} parameters
369
+ * @returns Promise<message>
370
+ */
371
+ async execute(contract, method, parameters, sender, execution) {
372
+ if (this.#closed) throw new Error("State machine is closed");
373
+ try {
374
+ if (contract === contractFactory && method === "registerContract") {
375
+ if (await this.has(parameters[0])) throw new Error(`duplicate contract @${parameters[0]}`);
376
+ let message;
377
+ if (!await globalThis.contractStore.has(parameters[0])) {
378
+ message = await peernet.get(parameters[0], "contract");
379
+ if (!message) throw new Error(`contract ${parameters[0]} not available`);
380
+ message = await new ContractMessage(message);
381
+ await globalThis.contractStore.put(await message.hash(), message.encoded);
382
+ }
383
+ if (!message) {
384
+ message = await globalThis.contractStore.get(parameters[0]);
385
+ message = await new ContractMessage(message);
386
+ }
387
+ if (!await this.has(await message.hash())) await this.#runContract(message);
388
+ }
389
+ } catch (error) {
390
+ throw new ContractDeploymentError(`contract deployment failed for ${parameters[0]}
391
+ ${error.message}`);
392
+ }
393
+ return new Promise((resolve, reject) => {
394
+ const id = randombytes(20).toString("hex");
395
+ const timeout = setTimeout(() => {
396
+ pubsub.unsubscribe(id, onmessage);
397
+ reject(new Error(`Machine.execute timeout for ${contract}.${method}`));
398
+ }, 3e4);
399
+ const onmessage = (message) => {
400
+ clearTimeout(timeout);
401
+ pubsub.unsubscribe(id, onmessage);
402
+ if (message?.error) reject(new ExecutionError(message.error));
403
+ else resolve(message);
404
+ };
405
+ pubsub.subscribe(id, onmessage);
406
+ this.worker.postMessage({
407
+ type: "execute",
408
+ id,
409
+ input: {
410
+ to: contract,
411
+ contract,
412
+ method,
413
+ params: parameters,
414
+ sender,
415
+ executionSeed: execution?.seed,
416
+ executionTimestamp: execution?.timestamp,
417
+ executionRandomness: execution?.randomness
418
+ }
419
+ });
420
+ });
421
+ }
422
+ collectFee(from, payments, burned) {
423
+ return this.#askWorker("collectFee", { from, payments, burned });
424
+ }
425
+ settleRewards(rewards) {
426
+ return this.#askWorker("settleRewards", { rewards });
427
+ }
428
+ get(contract, method, parameters) {
429
+ if (!this.#readSnapshot) return Promise.reject(new Error("Contract read state is not initialized"));
430
+ return this.#reader.read(this.#readSnapshot, contract, method, parameters ?? []);
431
+ }
432
+ async has(address) {
433
+ return new Promise((resolve, reject) => {
434
+ const id = randombytes(20).toString("hex");
435
+ const timeout = setTimeout(() => {
436
+ pubsub.unsubscribe(id, onmessage);
437
+ reject(new Error(`Machine.has timeout for ${address}`));
438
+ }, 1e4);
439
+ const onmessage = (message) => {
440
+ clearTimeout(timeout);
441
+ pubsub.unsubscribe(id, onmessage);
442
+ if (message?.error) reject(message.error);
443
+ else resolve(message);
444
+ };
445
+ pubsub.subscribe(id, onmessage);
446
+ this.worker.postMessage({
447
+ type: "has",
448
+ id,
449
+ input: {
450
+ address
451
+ }
452
+ });
453
+ });
454
+ }
455
+ #askWorker(type, input) {
456
+ if (this.#closed) return Promise.reject(new Error("State machine is closed"));
457
+ return new Promise((resolve, reject) => {
458
+ const id = randombytes(20).toString("hex");
459
+ const timeout = setTimeout(() => {
460
+ pubsub.unsubscribe(id, onmessage);
461
+ reject(new Error(`Machine.#askWorker timeout for ${type}`));
462
+ }, 3e4);
463
+ const onmessage = (message) => {
464
+ clearTimeout(timeout);
465
+ pubsub.unsubscribe(id, onmessage);
466
+ if (message?.error) reject(message.error);
467
+ else resolve(message);
468
+ };
469
+ pubsub.subscribe(id, onmessage);
470
+ this.worker.postMessage({
471
+ type,
472
+ id,
473
+ input
474
+ });
475
+ });
476
+ }
477
+ get contracts() {
478
+ return this.#askWorker("contracts");
479
+ }
480
+ get totalContracts() {
481
+ return this.#askWorker("totalContracts");
482
+ }
483
+ get nativeCalls() {
484
+ return this.#askWorker("nativeCalls");
485
+ }
486
+ get nativeMints() {
487
+ return this.#askWorker("nativeMints");
488
+ }
489
+ get nativeBurns() {
490
+ return this.#askWorker("nativeBurns");
491
+ }
492
+ get nativeTransfers() {
493
+ return this.#askWorker("nativeTransfers");
494
+ }
495
+ get totalBurnAmount() {
496
+ return this.#askWorker("totalBurnAmount");
497
+ }
498
+ get totalMintAmount() {
499
+ return this.#askWorker("totalMintAmount");
500
+ }
501
+ get totalTransferAmount() {
502
+ return this.#askWorker("totalTransferAmount");
503
+ }
504
+ get totalTransactions() {
505
+ return this.#askWorker("totalTransactions");
506
+ }
507
+ get totalBlocks() {
508
+ return this.#askWorker("totalBlocks");
509
+ }
510
+ get blocks() {
511
+ return this.getBlocks();
512
+ }
513
+ get lastBlock() {
514
+ return Promise.resolve(this.states.lastBlock);
515
+ }
516
+ getCanonicalTip() {
517
+ return this.#askWorker("lastBlock");
518
+ }
519
+ get lastBlockHeight() {
520
+ return Promise.resolve(Number(this.states.lastBlock.index));
521
+ }
522
+ getBlocks(from, to) {
523
+ return this.#askWorker("blocks", { from, to });
524
+ }
525
+ getBlock(index) {
526
+ return this.#askWorker("block", index);
527
+ }
528
+ async addLoadedBlock(block) {
529
+ debug(`adding loaded block: ${block.index}@${block.hash}`);
530
+ if (block.decoded) block = { ...block.decoded, hash: await block.hash() };
531
+ return this.#askWorker("addLoadedBlock", block);
532
+ }
533
+ async latestTransactions() {
534
+ return this.#askWorker("latestTransactions");
535
+ }
536
+ async delete(hash) {
537
+ return globalThis.contractStore.delete(hash);
538
+ }
539
+ /**
540
+ *
541
+ * @returns Promise
542
+ */
543
+ async deleteAll() {
544
+ let hashes = await globalThis.contractStore.keys();
545
+ hashes = Object.keys(hashes).map((hash) => this.delete(hash));
546
+ return Promise.all(hashes);
547
+ }
548
+ }
549
+
550
+ export { Machine as default };
package/exports/node.js CHANGED
@@ -1,8 +1,33 @@
1
+ import { BinaryFormat } from '@leofcoin/messages';
1
2
  import Peernet from '@leofcoin/peernet';
2
3
  import nodeConfig from '@leofcoin/lib/node-config';
3
- import { D as DEFAULT_NODE_OPTIONS } from './constants-Dy9i9S_o.js';
4
- import { guardPeerMessages } from './connection-monitor.js';
4
+ import { D as DEFAULT_NODE_OPTIONS } from './constants-BQ6f-_OT.js';
5
+ import { g as guardPeerMessages, a as guardProviderDiscovery } from './connection-monitor-BdFLRh-J.js';
5
6
  import '@leofcoin/networks';
7
+ import '@leofcoin/codec-format-interface';
8
+
9
+ function installBinaryPeerProtocol(node) {
10
+ if (!node.protos) throw new Error("Peer message registry is unavailable");
11
+ for (const [name, Original] of Object.entries(node.protos)) {
12
+ const schema = { ...new Original({}).proto };
13
+ if (name === "peernet-response") schema.response = void 0;
14
+ Object.freeze(schema);
15
+ const byteFields = Object.keys(schema).filter((key) => schema[key] instanceof Uint8Array).map((key) => key.endsWith("?") ? key.slice(0, -1) : key);
16
+ node.protos[name] = class extends BinaryFormat {
17
+ constructor(value) {
18
+ if (value && typeof value === "object" && !(value instanceof Uint8Array) && !(value instanceof ArrayBuffer)) {
19
+ value = { ...value };
20
+ for (const field of byteFields) {
21
+ if (typeof value[field] === "string")
22
+ value[field] = new TextEncoder().encode(value[field]);
23
+ }
24
+ }
25
+ super(value, schema, { name });
26
+ }
27
+ };
28
+ }
29
+ node.binaryApplicationProtocol = 1;
30
+ }
6
31
 
7
32
  class Node {
8
33
  #node;
@@ -11,6 +36,8 @@ class Node {
11
36
  }
12
37
  async _init(config = { autoStart: false }, password) {
13
38
  config = { ...DEFAULT_NODE_OPTIONS, ...config };
39
+ const autoStart = config.autoStart;
40
+ config = { ...config, autoStart: false };
14
41
  const backend = config.transport?.webrtc?.backend || process.env.NETPEER_SWARM_WRTC_BACKEND || "vandeurenglenn";
15
42
  if (backend === "vandeurenglenn" && !globalThis.RTCPeerConnection && !globalThis.wrtc) {
16
43
  globalThis.wrtc = await import('@vandeurenglenn/wrtc');
@@ -21,8 +48,14 @@ class Node {
21
48
  config.root = `.${network}/${networkVersion || config.networkVersion}/${config.storeNamespace}`;
22
49
  }
23
50
  this.#node = globalThis.Peernet ? await new globalThis.Peernet(config, password) : await new Peernet(config, password);
51
+ installBinaryPeerProtocol(this.#node);
24
52
  guardPeerMessages(this.#node);
53
+ guardProviderDiscovery(this.#node);
25
54
  await nodeConfig(config);
55
+ if (autoStart) {
56
+ this.#node.autoStart = true;
57
+ await this.#node.start();
58
+ }
26
59
  globalThis.pubsub.subscribe("chain:ready", async () => {
27
60
  if (!this.#node.autoStart) {
28
61
  await this.#node.start();