@leofcoin/chain 1.7.153 → 1.7.155

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.
@@ -4189,7 +4189,7 @@ class Machine {
4189
4189
  break;
4190
4190
  }
4191
4191
  case 'debug': {
4192
- // debug(data.message)
4192
+ debug$2(data.message);
4193
4193
  if (data.message.includes('loaded transactions for block:')) {
4194
4194
  pubsub.publish('block-loaded', data.message.replace('loaded transactions for block: ', '').split(' @')[0]);
4195
4195
  }
@@ -4576,7 +4576,7 @@ class Machine {
4576
4576
  }
4577
4577
  async addLoadedBlock(block) {
4578
4578
  debug$2(`adding loaded block: ${block.index}@${block.hash}`);
4579
- debug$2(JSON.stringify(block, jsonStringifyBigInt));
4579
+ // debug(JSON.stringify(block, jsonStringifyBigInt))
4580
4580
  if (block.decoded)
4581
4581
  block = { ...block.decoded, hash: await block.hash() };
4582
4582
  return this.#askWorker('addLoadedBlock', JSON.stringify(block, jsonStringifyBigInt));
@@ -4640,6 +4640,7 @@ class State extends Contract {
4640
4640
  // Block resolution state
4641
4641
  #resolvingBlocks;
4642
4642
  #maxConcurrentResolves;
4643
+ #txConcurrency;
4643
4644
  #totalSize;
4644
4645
  #lastResolved;
4645
4646
  #lastResolvedTime;
@@ -4726,6 +4727,7 @@ class State extends Contract {
4726
4727
  // Block resolution state
4727
4728
  this.#resolvingBlocks = new Set();
4728
4729
  this.#maxConcurrentResolves = 10;
4730
+ this.#txConcurrency = 25;
4729
4731
  this.knownBlocks = [];
4730
4732
  this.#totalSize = 0;
4731
4733
  this._wantList = [];
@@ -4894,6 +4896,52 @@ class State extends Contract {
4894
4896
  this.#resolvingBlocks.delete(hash);
4895
4897
  }
4896
4898
  }
4899
+ // Small utility to keep event loop responsive
4900
+ async #sleep(ms) {
4901
+ return new Promise((resolve) => setTimeout(resolve, ms));
4902
+ }
4903
+ // Run a list of tasks with bounded concurrency
4904
+ async #runWithConcurrency(items, concurrency, handler) {
4905
+ let index = 0;
4906
+ const total = items.length;
4907
+ const worker = async () => {
4908
+ while (true) {
4909
+ const current = index++;
4910
+ if (current >= total)
4911
+ break;
4912
+ try {
4913
+ await handler(items[current], current);
4914
+ if (current % 50 === 0)
4915
+ debug$1(`executed ${current}/${total}`);
4916
+ }
4917
+ catch (e) {
4918
+ debug$1(`transaction handler error at ${current}: ${e}`);
4919
+ }
4920
+ // yield a tick every few ops to avoid blocking
4921
+ if (current % 25 === 0)
4922
+ await this.#sleep(1);
4923
+ }
4924
+ };
4925
+ const workers = Array.from({ length: Math.min(concurrency, Math.max(1, total)) }, () => worker());
4926
+ await Promise.all(workers);
4927
+ }
4928
+ // Execute a single transaction with a timeout to avoid indefinite hangs
4929
+ async #executeWithTimeout(transaction, timeoutMs) {
4930
+ const timeout = timeoutMs ?? this.resolveTimeout ?? 5000;
4931
+ let timer;
4932
+ try {
4933
+ return await Promise.race([
4934
+ this.#machine.execute(transaction.decoded.to, transaction.decoded.method, transaction.decoded.params),
4935
+ new Promise((_, reject) => {
4936
+ timer = setTimeout(() => reject(new Error('transaction execution timeout')), timeout);
4937
+ })
4938
+ ]);
4939
+ }
4940
+ finally {
4941
+ if (timer)
4942
+ clearTimeout(timer);
4943
+ }
4944
+ }
4897
4945
  async #buildBlockChain(latestHash, maxBlocks = 1000) {
4898
4946
  const chain = [];
4899
4947
  let currentHash = latestHash;
@@ -5089,7 +5137,7 @@ class State extends Contract {
5089
5137
  // todo throw error
5090
5138
  async #_executeTransaction(transaction) {
5091
5139
  try {
5092
- await this.#machine.execute(transaction.decoded.to, transaction.decoded.method, transaction.decoded.params);
5140
+ await this.#executeWithTimeout(transaction);
5093
5141
  // await globalThis.accountsStore.put(transaction.decoded.from, String(transaction.decoded.nonce))
5094
5142
  // if (transaction.decoded.to === nativeToken) {
5095
5143
  // this.#nativeCalls += 1
@@ -5155,7 +5203,8 @@ class State extends Contract {
5155
5203
  }
5156
5204
  transactions = transactions.filter((transaction) => !transaction.decoded.priority);
5157
5205
  debug$1(`executing ${transactions.length} transactions for block ${block.index}`);
5158
- await Promise.all(transactions.map((transaction) => this.#_executeTransaction(transaction)));
5206
+ // Concurrency-limited execution to avoid blocking on large blocks
5207
+ await this.#runWithConcurrency(transactions, this.#txConcurrency, async (tx) => this.#_executeTransaction(tx));
5159
5208
  this.#blocks[block.index].loaded = true;
5160
5209
  debug$1(`executed transactions for block ${block.index}`);
5161
5210
  if (Number(block.index) === 0)
package/exports/chain.js CHANGED
@@ -330,7 +330,7 @@ class Machine {
330
330
  break;
331
331
  }
332
332
  case 'debug': {
333
- // debug(data.message)
333
+ debug$2(data.message);
334
334
  if (data.message.includes('loaded transactions for block:')) {
335
335
  pubsub.publish('block-loaded', data.message.replace('loaded transactions for block: ', '').split(' @')[0]);
336
336
  }
@@ -717,7 +717,7 @@ class Machine {
717
717
  }
718
718
  async addLoadedBlock(block) {
719
719
  debug$2(`adding loaded block: ${block.index}@${block.hash}`);
720
- debug$2(JSON.stringify(block, jsonStringifyBigInt));
720
+ // debug(JSON.stringify(block, jsonStringifyBigInt))
721
721
  if (block.decoded)
722
722
  block = { ...block.decoded, hash: await block.hash() };
723
723
  return this.#askWorker('addLoadedBlock', JSON.stringify(block, jsonStringifyBigInt));
@@ -781,6 +781,7 @@ class State extends Contract {
781
781
  // Block resolution state
782
782
  #resolvingBlocks;
783
783
  #maxConcurrentResolves;
784
+ #txConcurrency;
784
785
  #totalSize;
785
786
  #lastResolved;
786
787
  #lastResolvedTime;
@@ -867,6 +868,7 @@ class State extends Contract {
867
868
  // Block resolution state
868
869
  this.#resolvingBlocks = new Set();
869
870
  this.#maxConcurrentResolves = 10;
871
+ this.#txConcurrency = 25;
870
872
  this.knownBlocks = [];
871
873
  this.#totalSize = 0;
872
874
  this._wantList = [];
@@ -1035,6 +1037,52 @@ class State extends Contract {
1035
1037
  this.#resolvingBlocks.delete(hash);
1036
1038
  }
1037
1039
  }
1040
+ // Small utility to keep event loop responsive
1041
+ async #sleep(ms) {
1042
+ return new Promise((resolve) => setTimeout(resolve, ms));
1043
+ }
1044
+ // Run a list of tasks with bounded concurrency
1045
+ async #runWithConcurrency(items, concurrency, handler) {
1046
+ let index = 0;
1047
+ const total = items.length;
1048
+ const worker = async () => {
1049
+ while (true) {
1050
+ const current = index++;
1051
+ if (current >= total)
1052
+ break;
1053
+ try {
1054
+ await handler(items[current], current);
1055
+ if (current % 50 === 0)
1056
+ debug$1(`executed ${current}/${total}`);
1057
+ }
1058
+ catch (e) {
1059
+ debug$1(`transaction handler error at ${current}: ${e}`);
1060
+ }
1061
+ // yield a tick every few ops to avoid blocking
1062
+ if (current % 25 === 0)
1063
+ await this.#sleep(1);
1064
+ }
1065
+ };
1066
+ const workers = Array.from({ length: Math.min(concurrency, Math.max(1, total)) }, () => worker());
1067
+ await Promise.all(workers);
1068
+ }
1069
+ // Execute a single transaction with a timeout to avoid indefinite hangs
1070
+ async #executeWithTimeout(transaction, timeoutMs) {
1071
+ const timeout = timeoutMs ?? this.resolveTimeout ?? 5000;
1072
+ let timer;
1073
+ try {
1074
+ return await Promise.race([
1075
+ this.#machine.execute(transaction.decoded.to, transaction.decoded.method, transaction.decoded.params),
1076
+ new Promise((_, reject) => {
1077
+ timer = setTimeout(() => reject(new Error('transaction execution timeout')), timeout);
1078
+ })
1079
+ ]);
1080
+ }
1081
+ finally {
1082
+ if (timer)
1083
+ clearTimeout(timer);
1084
+ }
1085
+ }
1038
1086
  async #buildBlockChain(latestHash, maxBlocks = 1000) {
1039
1087
  const chain = [];
1040
1088
  let currentHash = latestHash;
@@ -1230,7 +1278,7 @@ class State extends Contract {
1230
1278
  // todo throw error
1231
1279
  async #_executeTransaction(transaction) {
1232
1280
  try {
1233
- await this.#machine.execute(transaction.decoded.to, transaction.decoded.method, transaction.decoded.params);
1281
+ await this.#executeWithTimeout(transaction);
1234
1282
  // await globalThis.accountsStore.put(transaction.decoded.from, String(transaction.decoded.nonce))
1235
1283
  // if (transaction.decoded.to === nativeToken) {
1236
1284
  // this.#nativeCalls += 1
@@ -1296,7 +1344,8 @@ class State extends Contract {
1296
1344
  }
1297
1345
  transactions = transactions.filter((transaction) => !transaction.decoded.priority);
1298
1346
  debug$1(`executing ${transactions.length} transactions for block ${block.index}`);
1299
- await Promise.all(transactions.map((transaction) => this.#_executeTransaction(transaction)));
1347
+ // Concurrency-limited execution to avoid blocking on large blocks
1348
+ await this.#runWithConcurrency(transactions, this.#txConcurrency, async (tx) => this.#_executeTransaction(tx));
1300
1349
  this.#blocks[block.index].loaded = true;
1301
1350
  debug$1(`executed transactions for block ${block.index}`);
1302
1351
  if (Number(block.index) === 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leofcoin/chain",
3
- "version": "1.7.153",
3
+ "version": "1.7.155",
4
4
  "description": "Official javascript implementation",
5
5
  "private": false,
6
6
  "exports": {