@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.
@@ -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
- setImmediate(async () => {
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
- await Promise.all(block.decoded.transactions
5303
- .filter((hash) => Boolean(hash))
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.#resolving)
5338
- return 'already resolving';
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
- await this.jobber.add(() => this.#resolveBlock(hash));
5344
- this.#resolving = false;
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.#resolving = false;
5360
- if (this.#resolveErrorCount < 3)
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
- return Promise.all(batch.map((hash) => this.getAndPutBlock(hash).catch((e) => {
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({ result });
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([...block.transactions] || []);
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
- setImmediate(async () => {
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
- await Promise.all(block.decoded.transactions
1444
- .filter((hash) => Boolean(hash))
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.#resolving)
1479
- return 'already resolving';
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
- await this.jobber.add(() => this.#resolveBlock(hash));
1485
- this.#resolving = false;
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.#resolving = false;
1501
- if (this.#resolveErrorCount < 3)
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
- return Promise.all(batch.map((hash) => this.getAndPutBlock(hash).catch((e) => {
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({ result });
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([...block.transactions] || []);
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 = [];
@@ -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
- transactions: RawTransaction[];
24
- loaded?: Boolean;
13
+ hash: string;
14
+ transactions: string[];
15
+ loaded?: boolean;
25
16
  };
26
17
  export declare type RawBlock = {
27
18
  index: number;
28
- transactions: RawTransaction[];
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.29",
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.73",
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.15",
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",