@leofcoin/chain 1.8.29 → 1.8.30
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/exports/browser/chain.js +54 -37
- package/exports/chain.js +54 -37
- package/exports/types.d.ts +5 -14
- package/package.json +3 -3
package/exports/browser/chain.js
CHANGED
|
@@ -5016,6 +5016,7 @@ class State extends Contract {
|
|
|
5016
5016
|
#totalSize;
|
|
5017
5017
|
#machine;
|
|
5018
5018
|
#loaded;
|
|
5019
|
+
#resolvingHashes;
|
|
5019
5020
|
/**
|
|
5020
5021
|
* contains transactions we need before we can successfully load
|
|
5021
5022
|
*/
|
|
@@ -5104,6 +5105,7 @@ class State extends Contract {
|
|
|
5104
5105
|
this.knownBlocks = [];
|
|
5105
5106
|
this.#totalSize = 0;
|
|
5106
5107
|
this.#loaded = false;
|
|
5108
|
+
this.#resolvingHashes = new Set();
|
|
5107
5109
|
this._wantList = [];
|
|
5108
5110
|
this.#chainStateHandler = () => {
|
|
5109
5111
|
return new globalThis.peernet.protos['peernet-response']({
|
|
@@ -5264,6 +5266,23 @@ class State extends Contract {
|
|
|
5264
5266
|
}
|
|
5265
5267
|
return block;
|
|
5266
5268
|
}
|
|
5269
|
+
async #resolveTransactions(transactions) {
|
|
5270
|
+
await Promise.all(transactions
|
|
5271
|
+
.filter((hash) => Boolean(hash))
|
|
5272
|
+
.map(async (hash) => {
|
|
5273
|
+
// should be in a transaction store already
|
|
5274
|
+
const exists = await transactionStore.has(hash);
|
|
5275
|
+
if (!exists) {
|
|
5276
|
+
const data = await peernet.get(hash, 'transaction');
|
|
5277
|
+
if (!data)
|
|
5278
|
+
throw new Error(`missing transaction data for ${hash}`);
|
|
5279
|
+
await transactionStore.put(hash, data);
|
|
5280
|
+
}
|
|
5281
|
+
const inPool = await transactionPoolStore.has(hash);
|
|
5282
|
+
if (inPool)
|
|
5283
|
+
await transactionPoolStore.delete(hash);
|
|
5284
|
+
}));
|
|
5285
|
+
}
|
|
5267
5286
|
async #resolveBlock(hash) {
|
|
5268
5287
|
let index = this.#blockHashMap.get(hash);
|
|
5269
5288
|
let localHash = '0x0';
|
|
@@ -5289,31 +5308,12 @@ class State extends Contract {
|
|
|
5289
5308
|
}
|
|
5290
5309
|
try {
|
|
5291
5310
|
const block = await this.getAndPutBlock(hash);
|
|
5311
|
+
const promises = [];
|
|
5292
5312
|
if (block.decoded.previousHash !== '0x0' && block.decoded.previousHash !== localHash) {
|
|
5293
|
-
|
|
5294
|
-
try {
|
|
5295
|
-
await this.resolveBlock(block.decoded.previousHash);
|
|
5296
|
-
}
|
|
5297
|
-
catch (e) {
|
|
5298
|
-
console.error(e);
|
|
5299
|
-
}
|
|
5300
|
-
});
|
|
5313
|
+
promises.push(this.resolveBlock(block.decoded.previousHash));
|
|
5301
5314
|
}
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
.map(async (hash) => {
|
|
5305
|
-
// should be in a transaction store already
|
|
5306
|
-
const exists = await transactionStore.has(hash);
|
|
5307
|
-
if (!exists) {
|
|
5308
|
-
const data = await peernet.get(hash, 'transaction');
|
|
5309
|
-
if (!data)
|
|
5310
|
-
throw new Error(`missing transaction data for ${hash}`);
|
|
5311
|
-
await transactionStore.put(hash, data);
|
|
5312
|
-
}
|
|
5313
|
-
const inPool = await transactionPoolStore.has(hash);
|
|
5314
|
-
if (inPool)
|
|
5315
|
-
await transactionPoolStore.delete(hash);
|
|
5316
|
-
}));
|
|
5315
|
+
promises.push(this.#resolveTransactions(block.decoded.transactions));
|
|
5316
|
+
await Promise.all(promises);
|
|
5317
5317
|
index = block.decoded.index;
|
|
5318
5318
|
const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
|
|
5319
5319
|
this.#totalSize += size;
|
|
@@ -5334,14 +5334,20 @@ class State extends Contract {
|
|
|
5334
5334
|
throw new Error(`expected hash, got: ${hash}`);
|
|
5335
5335
|
if (hash === '0x0')
|
|
5336
5336
|
return;
|
|
5337
|
-
if (this.#
|
|
5338
|
-
return
|
|
5337
|
+
if (this.#resolvingHashes.has(hash))
|
|
5338
|
+
return;
|
|
5339
|
+
this.#resolvingHashes.add(hash);
|
|
5340
|
+
const isEntering = this.#resolvingHashes.size === 1;
|
|
5339
5341
|
this.#resolving = true;
|
|
5340
|
-
if (this.jobber.busy && this.jobber.destroy)
|
|
5341
|
-
await this.jobber.destroy();
|
|
5342
5342
|
try {
|
|
5343
|
-
|
|
5344
|
-
|
|
5343
|
+
if (isEntering) {
|
|
5344
|
+
if (this.jobber.busy && this.jobber.destroy)
|
|
5345
|
+
await this.jobber.destroy();
|
|
5346
|
+
await this.jobber.add(() => this.#resolveBlock(hash));
|
|
5347
|
+
}
|
|
5348
|
+
else {
|
|
5349
|
+
await this.#resolveBlock(hash);
|
|
5350
|
+
}
|
|
5345
5351
|
try {
|
|
5346
5352
|
const lastBlockHash = await globalThis.stateStore.get('lastBlock');
|
|
5347
5353
|
if (lastBlockHash === hash) {
|
|
@@ -5350,19 +5356,23 @@ class State extends Contract {
|
|
|
5350
5356
|
}
|
|
5351
5357
|
}
|
|
5352
5358
|
catch (error) { }
|
|
5353
|
-
if (!this.#blockHashMap.has(this.#lastResolved.previousHash) && this.#lastResolved.previousHash !== '0x0')
|
|
5354
|
-
return this.resolveBlock(this.#lastResolved.previousHash);
|
|
5355
5359
|
}
|
|
5356
5360
|
catch (error) {
|
|
5357
5361
|
console.log({ error });
|
|
5358
5362
|
this.#resolveErrorCount += 1;
|
|
5359
|
-
this.#
|
|
5360
|
-
|
|
5363
|
+
if (this.#resolveErrorCount < 3) {
|
|
5364
|
+
this.#resolvingHashes.delete(hash);
|
|
5361
5365
|
return this.resolveBlock(hash);
|
|
5366
|
+
}
|
|
5362
5367
|
this.#resolveErrorCount = 0;
|
|
5363
5368
|
this.wantList.push(hash);
|
|
5364
5369
|
throw new ResolveError(`block: ${hash}`, { cause: error });
|
|
5365
5370
|
}
|
|
5371
|
+
finally {
|
|
5372
|
+
this.#resolvingHashes.delete(hash);
|
|
5373
|
+
if (this.#resolvingHashes.size === 0)
|
|
5374
|
+
this.#resolving = false;
|
|
5375
|
+
}
|
|
5366
5376
|
}
|
|
5367
5377
|
async resolveBlocks() {
|
|
5368
5378
|
// Don't re-resolve if already syncing or resolving
|
|
@@ -5484,14 +5494,21 @@ class State extends Contract {
|
|
|
5484
5494
|
debug$1(`Remote block hash is 0x0, skipping sync`);
|
|
5485
5495
|
return;
|
|
5486
5496
|
}
|
|
5497
|
+
// Skip if local machine is already ahead of remote
|
|
5498
|
+
if (localIndex > remoteIndex) {
|
|
5499
|
+
debug$1(`Local index ${localIndex} is ahead of remote ${remoteIndex}, skipping sync`);
|
|
5500
|
+
return;
|
|
5501
|
+
}
|
|
5487
5502
|
// Use state hash comparison: only resolve if remote hash differs from local state hash
|
|
5488
5503
|
if (localStateHash !== remoteBlockHash) {
|
|
5489
5504
|
if (this.wantList.length > 0) {
|
|
5490
5505
|
debug$1(`Fetching ${this.wantList.length} blocks before resolving`);
|
|
5491
5506
|
const getBatch = async (batch) => {
|
|
5492
|
-
|
|
5507
|
+
const blocks = await Promise.all(batch.map((hash) => this.getAndPutBlock(hash).catch((e) => {
|
|
5493
5508
|
console.warn(`failed to fetch block ${hash}`, e);
|
|
5494
5509
|
})));
|
|
5510
|
+
const transactions = blocks.filter((block) => Boolean(block)).flatMap((block) => block.decoded.transactions);
|
|
5511
|
+
return this.#resolveTransactions(transactions);
|
|
5495
5512
|
};
|
|
5496
5513
|
// Process in batches of 50 to avoid overwhelming network/memory
|
|
5497
5514
|
for (let i = 0; i < this.wantList.length; i += 50) {
|
|
@@ -5546,7 +5563,7 @@ class State extends Contract {
|
|
|
5546
5563
|
const task = async () => {
|
|
5547
5564
|
try {
|
|
5548
5565
|
const result = await peer.request(node.encoded);
|
|
5549
|
-
debug$1(
|
|
5566
|
+
debug$1(`lastBlock result: ${JSON.stringify(result)}`);
|
|
5550
5567
|
console.log({ result });
|
|
5551
5568
|
return { result: new LastBlockMessage(result), peer };
|
|
5552
5569
|
}
|
|
@@ -5561,7 +5578,7 @@ class State extends Contract {
|
|
|
5561
5578
|
}
|
|
5562
5579
|
// @ts-ignore
|
|
5563
5580
|
console.log({ promises });
|
|
5564
|
-
promises = await this.promiseRequests(promises);
|
|
5581
|
+
promises = (await this.promiseRequests(promises));
|
|
5565
5582
|
console.log({ promises });
|
|
5566
5583
|
let latest = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
5567
5584
|
promises = promises.sort((a, b) => b.index - a.index);
|
|
@@ -5639,7 +5656,7 @@ class State extends Contract {
|
|
|
5639
5656
|
if (block && !block.loaded) {
|
|
5640
5657
|
try {
|
|
5641
5658
|
debug$1(`loading block: ${Number(block.index)} ${block.hash}`);
|
|
5642
|
-
let transactions = await this.#loadBlockTransactions(
|
|
5659
|
+
let transactions = await this.#loadBlockTransactions(block.transactions || []);
|
|
5643
5660
|
// const lastTransactions = await this.#getLastTransactions()
|
|
5644
5661
|
debug$1(`loading transactions: ${transactions.length} for block ${block.index}`);
|
|
5645
5662
|
let priority = [];
|
package/exports/chain.js
CHANGED
|
@@ -1157,6 +1157,7 @@ class State extends Contract {
|
|
|
1157
1157
|
#totalSize;
|
|
1158
1158
|
#machine;
|
|
1159
1159
|
#loaded;
|
|
1160
|
+
#resolvingHashes;
|
|
1160
1161
|
/**
|
|
1161
1162
|
* contains transactions we need before we can successfully load
|
|
1162
1163
|
*/
|
|
@@ -1245,6 +1246,7 @@ class State extends Contract {
|
|
|
1245
1246
|
this.knownBlocks = [];
|
|
1246
1247
|
this.#totalSize = 0;
|
|
1247
1248
|
this.#loaded = false;
|
|
1249
|
+
this.#resolvingHashes = new Set();
|
|
1248
1250
|
this._wantList = [];
|
|
1249
1251
|
this.#chainStateHandler = () => {
|
|
1250
1252
|
return new globalThis.peernet.protos['peernet-response']({
|
|
@@ -1405,6 +1407,23 @@ class State extends Contract {
|
|
|
1405
1407
|
}
|
|
1406
1408
|
return block;
|
|
1407
1409
|
}
|
|
1410
|
+
async #resolveTransactions(transactions) {
|
|
1411
|
+
await Promise.all(transactions
|
|
1412
|
+
.filter((hash) => Boolean(hash))
|
|
1413
|
+
.map(async (hash) => {
|
|
1414
|
+
// should be in a transaction store already
|
|
1415
|
+
const exists = await transactionStore.has(hash);
|
|
1416
|
+
if (!exists) {
|
|
1417
|
+
const data = await peernet.get(hash, 'transaction');
|
|
1418
|
+
if (!data)
|
|
1419
|
+
throw new Error(`missing transaction data for ${hash}`);
|
|
1420
|
+
await transactionStore.put(hash, data);
|
|
1421
|
+
}
|
|
1422
|
+
const inPool = await transactionPoolStore.has(hash);
|
|
1423
|
+
if (inPool)
|
|
1424
|
+
await transactionPoolStore.delete(hash);
|
|
1425
|
+
}));
|
|
1426
|
+
}
|
|
1408
1427
|
async #resolveBlock(hash) {
|
|
1409
1428
|
let index = this.#blockHashMap.get(hash);
|
|
1410
1429
|
let localHash = '0x0';
|
|
@@ -1430,31 +1449,12 @@ class State extends Contract {
|
|
|
1430
1449
|
}
|
|
1431
1450
|
try {
|
|
1432
1451
|
const block = await this.getAndPutBlock(hash);
|
|
1452
|
+
const promises = [];
|
|
1433
1453
|
if (block.decoded.previousHash !== '0x0' && block.decoded.previousHash !== localHash) {
|
|
1434
|
-
|
|
1435
|
-
try {
|
|
1436
|
-
await this.resolveBlock(block.decoded.previousHash);
|
|
1437
|
-
}
|
|
1438
|
-
catch (e) {
|
|
1439
|
-
console.error(e);
|
|
1440
|
-
}
|
|
1441
|
-
});
|
|
1454
|
+
promises.push(this.resolveBlock(block.decoded.previousHash));
|
|
1442
1455
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
.map(async (hash) => {
|
|
1446
|
-
// should be in a transaction store already
|
|
1447
|
-
const exists = await transactionStore.has(hash);
|
|
1448
|
-
if (!exists) {
|
|
1449
|
-
const data = await peernet.get(hash, 'transaction');
|
|
1450
|
-
if (!data)
|
|
1451
|
-
throw new Error(`missing transaction data for ${hash}`);
|
|
1452
|
-
await transactionStore.put(hash, data);
|
|
1453
|
-
}
|
|
1454
|
-
const inPool = await transactionPoolStore.has(hash);
|
|
1455
|
-
if (inPool)
|
|
1456
|
-
await transactionPoolStore.delete(hash);
|
|
1457
|
-
}));
|
|
1456
|
+
promises.push(this.#resolveTransactions(block.decoded.transactions));
|
|
1457
|
+
await Promise.all(promises);
|
|
1458
1458
|
index = block.decoded.index;
|
|
1459
1459
|
const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
|
|
1460
1460
|
this.#totalSize += size;
|
|
@@ -1475,14 +1475,20 @@ class State extends Contract {
|
|
|
1475
1475
|
throw new Error(`expected hash, got: ${hash}`);
|
|
1476
1476
|
if (hash === '0x0')
|
|
1477
1477
|
return;
|
|
1478
|
-
if (this.#
|
|
1479
|
-
return
|
|
1478
|
+
if (this.#resolvingHashes.has(hash))
|
|
1479
|
+
return;
|
|
1480
|
+
this.#resolvingHashes.add(hash);
|
|
1481
|
+
const isEntering = this.#resolvingHashes.size === 1;
|
|
1480
1482
|
this.#resolving = true;
|
|
1481
|
-
if (this.jobber.busy && this.jobber.destroy)
|
|
1482
|
-
await this.jobber.destroy();
|
|
1483
1483
|
try {
|
|
1484
|
-
|
|
1485
|
-
|
|
1484
|
+
if (isEntering) {
|
|
1485
|
+
if (this.jobber.busy && this.jobber.destroy)
|
|
1486
|
+
await this.jobber.destroy();
|
|
1487
|
+
await this.jobber.add(() => this.#resolveBlock(hash));
|
|
1488
|
+
}
|
|
1489
|
+
else {
|
|
1490
|
+
await this.#resolveBlock(hash);
|
|
1491
|
+
}
|
|
1486
1492
|
try {
|
|
1487
1493
|
const lastBlockHash = await globalThis.stateStore.get('lastBlock');
|
|
1488
1494
|
if (lastBlockHash === hash) {
|
|
@@ -1491,19 +1497,23 @@ class State extends Contract {
|
|
|
1491
1497
|
}
|
|
1492
1498
|
}
|
|
1493
1499
|
catch (error) { }
|
|
1494
|
-
if (!this.#blockHashMap.has(this.#lastResolved.previousHash) && this.#lastResolved.previousHash !== '0x0')
|
|
1495
|
-
return this.resolveBlock(this.#lastResolved.previousHash);
|
|
1496
1500
|
}
|
|
1497
1501
|
catch (error) {
|
|
1498
1502
|
console.log({ error });
|
|
1499
1503
|
this.#resolveErrorCount += 1;
|
|
1500
|
-
this.#
|
|
1501
|
-
|
|
1504
|
+
if (this.#resolveErrorCount < 3) {
|
|
1505
|
+
this.#resolvingHashes.delete(hash);
|
|
1502
1506
|
return this.resolveBlock(hash);
|
|
1507
|
+
}
|
|
1503
1508
|
this.#resolveErrorCount = 0;
|
|
1504
1509
|
this.wantList.push(hash);
|
|
1505
1510
|
throw new ResolveError(`block: ${hash}`, { cause: error });
|
|
1506
1511
|
}
|
|
1512
|
+
finally {
|
|
1513
|
+
this.#resolvingHashes.delete(hash);
|
|
1514
|
+
if (this.#resolvingHashes.size === 0)
|
|
1515
|
+
this.#resolving = false;
|
|
1516
|
+
}
|
|
1507
1517
|
}
|
|
1508
1518
|
async resolveBlocks() {
|
|
1509
1519
|
// Don't re-resolve if already syncing or resolving
|
|
@@ -1625,14 +1635,21 @@ class State extends Contract {
|
|
|
1625
1635
|
debug$1(`Remote block hash is 0x0, skipping sync`);
|
|
1626
1636
|
return;
|
|
1627
1637
|
}
|
|
1638
|
+
// Skip if local machine is already ahead of remote
|
|
1639
|
+
if (localIndex > remoteIndex) {
|
|
1640
|
+
debug$1(`Local index ${localIndex} is ahead of remote ${remoteIndex}, skipping sync`);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1628
1643
|
// Use state hash comparison: only resolve if remote hash differs from local state hash
|
|
1629
1644
|
if (localStateHash !== remoteBlockHash) {
|
|
1630
1645
|
if (this.wantList.length > 0) {
|
|
1631
1646
|
debug$1(`Fetching ${this.wantList.length} blocks before resolving`);
|
|
1632
1647
|
const getBatch = async (batch) => {
|
|
1633
|
-
|
|
1648
|
+
const blocks = await Promise.all(batch.map((hash) => this.getAndPutBlock(hash).catch((e) => {
|
|
1634
1649
|
console.warn(`failed to fetch block ${hash}`, e);
|
|
1635
1650
|
})));
|
|
1651
|
+
const transactions = blocks.filter((block) => Boolean(block)).flatMap((block) => block.decoded.transactions);
|
|
1652
|
+
return this.#resolveTransactions(transactions);
|
|
1636
1653
|
};
|
|
1637
1654
|
// Process in batches of 50 to avoid overwhelming network/memory
|
|
1638
1655
|
for (let i = 0; i < this.wantList.length; i += 50) {
|
|
@@ -1687,7 +1704,7 @@ class State extends Contract {
|
|
|
1687
1704
|
const task = async () => {
|
|
1688
1705
|
try {
|
|
1689
1706
|
const result = await peer.request(node.encoded);
|
|
1690
|
-
debug$1(
|
|
1707
|
+
debug$1(`lastBlock result: ${JSON.stringify(result)}`);
|
|
1691
1708
|
console.log({ result });
|
|
1692
1709
|
return { result: new LastBlockMessage(result), peer };
|
|
1693
1710
|
}
|
|
@@ -1702,7 +1719,7 @@ class State extends Contract {
|
|
|
1702
1719
|
}
|
|
1703
1720
|
// @ts-ignore
|
|
1704
1721
|
console.log({ promises });
|
|
1705
|
-
promises = await this.promiseRequests(promises);
|
|
1722
|
+
promises = (await this.promiseRequests(promises));
|
|
1706
1723
|
console.log({ promises });
|
|
1707
1724
|
let latest = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
1708
1725
|
promises = promises.sort((a, b) => b.index - a.index);
|
|
@@ -1780,7 +1797,7 @@ class State extends Contract {
|
|
|
1780
1797
|
if (block && !block.loaded) {
|
|
1781
1798
|
try {
|
|
1782
1799
|
debug$1(`loading block: ${Number(block.index)} ${block.hash}`);
|
|
1783
|
-
let transactions = await this.#loadBlockTransactions(
|
|
1800
|
+
let transactions = await this.#loadBlockTransactions(block.transactions || []);
|
|
1784
1801
|
// const lastTransactions = await this.#getLastTransactions()
|
|
1785
1802
|
debug$1(`loading transactions: ${transactions.length} for block ${block.index}`);
|
|
1786
1803
|
let priority = [];
|
package/exports/types.d.ts
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import { base58String } from '@vandeurenglenn/base58';
|
|
2
2
|
export type Address = base58String;
|
|
3
3
|
export type BlockHash = base58String;
|
|
4
|
-
interface Transaction {
|
|
5
|
-
to: Address;
|
|
6
|
-
from: Address;
|
|
7
|
-
method: String;
|
|
8
|
-
params: string[];
|
|
9
|
-
nonce: Number;
|
|
10
|
-
}
|
|
11
|
-
interface RawTransaction extends Transaction {
|
|
12
|
-
timestamp: Number;
|
|
13
|
-
}
|
|
14
4
|
export interface globalMessage {
|
|
15
5
|
sender: Address;
|
|
16
6
|
call: Function;
|
|
@@ -20,14 +10,15 @@ export interface globalMessage {
|
|
|
20
10
|
}
|
|
21
11
|
export declare type BlockInMemory = {
|
|
22
12
|
index: number;
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
hash: string;
|
|
14
|
+
transactions: string[];
|
|
15
|
+
loaded?: boolean;
|
|
25
16
|
};
|
|
26
17
|
export declare type RawBlock = {
|
|
27
18
|
index: number;
|
|
28
|
-
|
|
19
|
+
hash: string;
|
|
20
|
+
transactions: string[];
|
|
29
21
|
};
|
|
30
22
|
export declare type ChainConfig = {
|
|
31
23
|
resolveTimeout?: number;
|
|
32
24
|
};
|
|
33
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leofcoin/chain",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.30",
|
|
4
4
|
"description": "Official javascript implementation",
|
|
5
5
|
"private": false,
|
|
6
6
|
"exports": {
|
|
@@ -65,11 +65,11 @@
|
|
|
65
65
|
"@leofcoin/contracts": "^0.1.16",
|
|
66
66
|
"@leofcoin/crypto": "^0.2.37",
|
|
67
67
|
"@leofcoin/errors": "^1.0.25",
|
|
68
|
-
"@leofcoin/lib": "^1.2.
|
|
68
|
+
"@leofcoin/lib": "^1.2.74",
|
|
69
69
|
"@leofcoin/messages": "^1.4.40",
|
|
70
70
|
"@leofcoin/multi-wallet": "^3.1.8",
|
|
71
71
|
"@leofcoin/networks": "^1.1.25",
|
|
72
|
-
"@leofcoin/peernet": "^1.2.
|
|
72
|
+
"@leofcoin/peernet": "^1.2.16",
|
|
73
73
|
"@leofcoin/storage": "^3.5.38",
|
|
74
74
|
"@leofcoin/utils": "^1.1.40",
|
|
75
75
|
"@leofcoin/workers": "^1.5.27",
|