@leofcoin/chain 1.7.150 → 1.7.152

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.
@@ -4128,7 +4128,6 @@ class ExecutionError extends LeofcoinError {
4128
4128
  class ContractDeploymentError extends LeofcoinError {
4129
4129
  name = 'ContractDeploymentError';
4130
4130
  }
4131
- const isResolveError = (error) => error.name === 'ResolveError';
4132
4131
  const isExecutionError = (error) => error.name === 'ExecutionError';
4133
4132
 
4134
4133
  // import State from './state'
@@ -4599,77 +4598,36 @@ class Machine {
4599
4598
  }
4600
4599
  }
4601
4600
 
4602
- class Jobber {
4603
- constructor(timeout) {
4604
- this.busy = false;
4605
- this.timeout = timeout;
4606
- }
4607
- add(fn) {
4608
- this.busy = true;
4609
- return new Promise(async (resolve, reject) => {
4610
- const timeout = setTimeout(() => {
4611
- reject('timeout');
4612
- }, this.timeout);
4613
- this.destroy = () => {
4614
- clearTimeout(timeout);
4615
- this.busy = false;
4616
- resolve('stopped');
4617
- };
4618
- try {
4619
- const result = await fn();
4620
- clearTimeout(timeout);
4621
- this.busy = false;
4622
- resolve(result);
4623
- }
4624
- catch (error) {
4625
- clearTimeout(timeout);
4626
- reject(error);
4627
- }
4628
- });
4629
- }
4630
- }
4631
-
4632
4601
  const debug$1 = createDebugger('leofcoin/state');
4633
4602
  class State extends Contract {
4634
- #resolveErrored;
4635
- #lastResolvedTime;
4636
- #lastResolved;
4637
- #resolving;
4638
- #resolveErrorCount;
4639
- #syncState;
4640
- #chainState;
4641
- #lastBlockInQue;
4642
- #syncErrorCount;
4643
4603
  #blockHashMap;
4644
- #chainSyncing;
4645
4604
  #blocks;
4646
- #totalSize;
4647
4605
  #machine;
4648
4606
  #loaded;
4607
+ // Sync state
4608
+ #syncing;
4609
+ #syncErrorCount;
4610
+ // Block resolution state
4649
4611
  #resolvingBlocks;
4650
4612
  #maxConcurrentResolves;
4613
+ #totalSize;
4614
+ #lastResolved;
4615
+ #lastResolvedTime;
4651
4616
  #blockResolveQueue;
4617
+ #chainState;
4652
4618
  /**
4653
4619
  * contains transactions we need before we can successfully load
4654
4620
  */
4655
4621
  get wantList() {
4656
4622
  return this.#machine?.wantList ?? this._wantList;
4657
4623
  }
4658
- get state() {
4659
- return {
4660
- sync: this.#syncState,
4661
- chain: this.#chainState
4662
- };
4663
- }
4664
- get blockHashMap() {
4665
- return this.#blockHashMap.entries();
4666
- }
4667
4624
  get loaded() {
4668
4625
  return this.#loaded;
4669
4626
  }
4670
- get resolving() {
4671
- return this.#resolving;
4627
+ get isSyncing() {
4628
+ return this.#syncing;
4672
4629
  }
4630
+ // Delegate to machine
4673
4631
  get contracts() {
4674
4632
  return this.#machine.contracts;
4675
4633
  }
@@ -4712,38 +4670,38 @@ class State extends Contract {
4712
4670
  get lastBlockHeight() {
4713
4671
  return this.#machine ? this.#machine.lastBlockHeight : 0;
4714
4672
  }
4715
- getBlock(index) {
4716
- return this.#machine.getBlock(index);
4717
- }
4718
- getBlocks(from, to) {
4719
- return this.#machine.getBlocks(from, to);
4720
- }
4721
4673
  get totalSize() {
4722
4674
  return this.#totalSize;
4723
4675
  }
4724
4676
  get machine() {
4725
4677
  return this.#machine;
4726
4678
  }
4679
+ get blockHashMap() {
4680
+ return this.#blockHashMap.entries();
4681
+ }
4682
+ getBlock(index) {
4683
+ return this.#machine.getBlock(index);
4684
+ }
4685
+ getBlocks(from, to) {
4686
+ return this.#machine.getBlocks(from, to);
4687
+ }
4727
4688
  constructor(config) {
4728
4689
  super(config);
4729
- this.#lastResolvedTime = 0;
4730
- this.#resolving = false;
4731
- this.#resolveErrorCount = 0;
4732
- this.#chainState = 'loading';
4733
- this.#syncErrorCount = 0;
4734
4690
  this.#blockHashMap = new Map();
4735
- this.#chainSyncing = false;
4736
4691
  this.#blocks = [];
4737
- this.knownBlocks = [];
4738
- this.#totalSize = 0;
4739
4692
  this.#loaded = false;
4740
- this._wantList = [];
4693
+ // Sync state
4694
+ this.#syncing = false;
4695
+ this.#syncErrorCount = 0;
4696
+ // Block resolution state
4741
4697
  this.#resolvingBlocks = new Set();
4742
4698
  this.#maxConcurrentResolves = 10;
4743
- this.#blockResolveQueue = [];
4699
+ this.knownBlocks = [];
4700
+ this.#totalSize = 0;
4701
+ this._wantList = [];
4744
4702
  this.#chainStateHandler = () => {
4745
4703
  return new globalThis.peernet.protos['peernet-response']({
4746
- response: this.#chainState
4704
+ response: { syncing: this.#syncing, loaded: this.#loaded }
4747
4705
  });
4748
4706
  };
4749
4707
  this.#lastBlockHandler = async () => {
@@ -4785,67 +4743,43 @@ class State extends Contract {
4785
4743
  #lastBlockHandler;
4786
4744
  #knownBlocksHandler;
4787
4745
  async init() {
4788
- this.jobber = new Jobber(this.resolveTimeout);
4789
- await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler);
4790
- await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler);
4791
- await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler);
4792
- let localBlockHash;
4793
- let blockMessage;
4794
- let localBlock;
4746
+ // Register request handlers
4747
+ await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this));
4748
+ await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler.bind(this));
4749
+ await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler.bind(this));
4795
4750
  try {
4796
- const rawBlock = await globalThis.chainStore.has('lastBlock');
4797
- if (rawBlock) {
4798
- localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
4799
- if (localBlockHash !== '0x0') {
4800
- blockMessage = await globalThis.peernet.get(localBlockHash, 'block');
4801
- blockMessage = await new BlockMessage(blockMessage);
4751
+ // Load local block state
4752
+ let localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
4753
+ try {
4754
+ const localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
4755
+ if (localBlockHash && localBlockHash !== '0x0') {
4756
+ const blockMessage = await new BlockMessage(await globalThis.peernet.get(localBlockHash, 'block'));
4802
4757
  localBlock = { ...blockMessage.decoded, hash: localBlockHash };
4803
4758
  }
4804
4759
  }
4805
- else {
4806
- localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
4760
+ catch (error) {
4761
+ debug$1('No local block found');
4807
4762
  }
4808
- }
4809
- catch {
4810
- localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
4811
- }
4812
- try {
4813
- this.knownBlocks = await blockStore.keys();
4814
- }
4815
- catch (error) {
4816
- debug$1('no local known blocks found');
4817
- }
4818
- try {
4819
- if (localBlock.hash && localBlock.hash !== '0x0') {
4820
- try {
4821
- const states = {
4822
- lastBlock: JSON.parse(new TextDecoder().decode(await globalThis.stateStore.get('lastBlock')))
4823
- };
4824
- if (blockMessage.decoded.index > states.lastBlock.index)
4825
- await this.resolveBlocks();
4826
- }
4827
- catch (error) {
4828
- // no states found, try resolving blocks
4829
- await this.resolveBlocks();
4830
- }
4763
+ // Load known blocks
4764
+ try {
4765
+ this.knownBlocks = await globalThis.blockStore.keys();
4831
4766
  }
4832
- else {
4833
- await this.resolveBlocks();
4767
+ catch (error) {
4768
+ debug$1('No known blocks found');
4834
4769
  }
4770
+ // Initialize machine and resolve blocks if needed
4835
4771
  this.#machine = await new Machine(this.#blocks);
4772
+ if (localBlock.hash !== '0x0') {
4773
+ await this.resolveBlock(localBlock.hash);
4774
+ }
4836
4775
  const lastBlock = await this.#machine.lastBlock;
4837
4776
  if (lastBlock.hash !== '0x0') {
4838
- this.updateState(new BlockMessage(lastBlock));
4777
+ await this.updateState(new BlockMessage(lastBlock));
4839
4778
  }
4840
4779
  this.#loaded = true;
4841
- // await this.#loadBlocks(this.#blocks)
4842
4780
  }
4843
4781
  catch (error) {
4844
- console.log('e');
4845
- if (isResolveError(error)) {
4846
- console.error(error);
4847
- }
4848
- console.log(error);
4782
+ console.error('Failed to initialize state:', error);
4849
4783
  }
4850
4784
  }
4851
4785
  async updateState(message) {
@@ -4979,196 +4913,76 @@ class State extends Contract {
4979
4913
  await Promise.all(resolving);
4980
4914
  }
4981
4915
  async resolveBlock(hash) {
4982
- if (!hash)
4983
- throw new Error(`expected hash, got: ${hash}`);
4984
- if (hash === '0x0')
4916
+ if (!hash || hash === '0x0')
4985
4917
  return;
4986
- if (this.#resolving) {
4987
- debug$1('Already resolving, queueing block');
4918
+ if (this.#syncing)
4988
4919
  return;
4989
- }
4990
- this.#resolving = true;
4920
+ this.#syncing = true;
4921
+ this.#syncErrorCount = 0;
4991
4922
  try {
4992
- // Build the entire block chain from latest to genesis
4993
4923
  debug$1(`Building block chain from ${hash}`);
4994
4924
  const blockChain = await this.#buildBlockChain(hash);
4995
4925
  debug$1(`Built chain of ${blockChain.length} blocks`);
4996
- // Resolve all blocks in parallel
4997
4926
  if (blockChain.length > 0) {
4998
4927
  await this.#resolveBlocksInParallel(blockChain);
4999
4928
  }
5000
- this.#resolving = false;
5001
- this.#resolveErrorCount = 0;
5002
- this.#resolveErrored = false;
5003
- try {
5004
- const lastBlockHash = await globalThis.stateStore.get('lastBlock');
5005
- if (lastBlockHash === hash) {
5006
- debug$1('Resolved to latest block state');
5007
- return;
5008
- }
5009
- }
5010
- catch (error) {
5011
- debug$1('no local state found');
5012
- }
5013
4929
  }
5014
4930
  catch (error) {
5015
- console.log({ error });
5016
- this.#resolveErrorCount += 1;
5017
- this.#resolving = false;
5018
- if (this.#resolveErrorCount < 3) {
5019
- debug$1(`Retry ${this.#resolveErrorCount}/3 for block ${hash}`);
5020
- return this.resolveBlock(hash);
5021
- }
5022
- this.#resolveErrorCount = 0;
4931
+ console.error('Block resolution failed:', error);
5023
4932
  this.wantList.push(hash);
5024
- throw new ResolveError(`block: ${hash}`, { cause: error });
4933
+ }
4934
+ finally {
4935
+ this.#syncing = false;
5025
4936
  }
5026
4937
  }
5027
4938
  async resolveBlocks() {
5028
- // Don't re-resolve if already syncing or resolving
5029
- if (this.#chainSyncing || this.#resolving) {
5030
- debug$1('Already syncing or resolving, skipping resolveBlocks()');
4939
+ if (this.#syncing)
5031
4940
  return;
5032
- }
5033
- try {
5034
- if (this.jobber.busy && this.jobber.destroy) {
5035
- await this.jobber.destroy();
5036
- }
5037
- }
5038
- catch (error) {
5039
- console.error(error);
5040
- }
5041
4941
  try {
5042
4942
  const localBlock = await globalThis.chainStore.get('lastBlock');
5043
4943
  const hash = new TextDecoder().decode(localBlock);
5044
4944
  if (hash && hash !== '0x0') {
5045
- debug$1(`Resolving blocks from hash: ${hash}`);
5046
4945
  await this.resolveBlock(hash);
5047
4946
  }
5048
4947
  }
5049
4948
  catch (error) {
5050
- console.log(error);
5051
- this.#chainSyncing = false;
5052
- this.#syncState = 'errored';
5053
- this.#resolveErrored = true;
5054
- return this.restoreChain();
5055
- // console.log(e);
5056
- }
5057
- }
5058
- async restoreChain() {
5059
- try {
5060
- const { hash } = await this.#getLatestBlock();
5061
- await globalThis.chainStore.put('lastBlock', hash);
5062
- if (hash && hash !== '0x0') {
5063
- await this.resolveBlock(hash);
5064
- }
5065
- }
5066
- catch (error) {
5067
- console.log(error);
5068
- this.#resolveErrored = true;
5069
- this.#resolveErrorCount += 1;
5070
- this.#resolving = false;
5071
- return this.restoreChain();
5072
- // console.log(e);
4949
+ debug$1('Failed to resolve blocks:', error);
5073
4950
  }
5074
4951
  }
5075
4952
  async syncChain(lastBlock) {
5076
- console.log('check if can sync');
5077
- if (!this.shouldSync)
5078
- return;
5079
- console.log('starting sync');
5080
- this.#syncState = 'syncing';
5081
- this.#chainSyncing = true;
5082
- try {
5083
- if (this.jobber.busy && this.jobber.destroy) {
5084
- await this.jobber.destroy();
5085
- }
5086
- }
5087
- catch (error) {
5088
- console.error(error);
5089
- }
4953
+ if (this.#syncing)
4954
+ return 'syncing';
5090
4955
  if (!lastBlock)
5091
4956
  lastBlock = await this.#getLatestBlock();
5092
- if (globalThis.peernet.peers.length === 0)
5093
- return 'connectionless';
4957
+ this.#syncing = true;
5094
4958
  try {
5095
- await this.#syncChain(lastBlock);
5096
- }
5097
- catch (error) {
5098
- this.#syncErrorCount += 1;
5099
- if (this.#syncErrorCount < 3)
5100
- return this.syncChain(lastBlock);
5101
- this.#syncErrorCount = 0;
5102
- this.#chainSyncing = false;
5103
- this.#syncState = 'errored';
5104
- return this.#syncState;
5105
- }
5106
- if (lastBlock.index === this.#lastBlockInQue?.index)
5107
- this.#lastBlockInQue = undefined;
5108
- this.#syncErrorCount = 0;
5109
- this.#chainSyncing = false;
5110
- if (this.#lastBlockInQue)
5111
- return this.syncChain(this.#lastBlockInQue);
5112
- this.#syncState = 'synced';
5113
- return this.#syncState;
5114
- }
5115
- async #syncChain(lastBlock) {
5116
- try {
5117
- // if (this.knownBlocks?.length === Number(lastBlock.index) + 1) {
5118
- // let promises = []
5119
- // promises = await Promise.allSettled(
5120
- // this.knownBlocks.map(async (address) => {
5121
- // const has = await globalThis.peernet.has(address)
5122
- // return { has, address }
5123
- // })
5124
- // )
5125
- // promises = promises.filter(({ status, value }) => status === 'fulfilled' && !value.has)
5126
- // await Promise.allSettled(promises.map(({ value }) => this.getAndPutBlock(value.address)))
5127
- // }
5128
- const localBlock = await this.lastBlock;
5129
- const localIndex = localBlock ? Number(localBlock.index) : -1;
5130
- const remoteIndex = Number(lastBlock.index);
5131
- const remoteBlockHash = lastBlock.hash;
5132
- // Get the local state hash from chainStore
5133
- let localStateHash = '0x0';
5134
- try {
5135
- localStateHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
5136
- }
5137
- catch (error) {
5138
- debug$1(`No local state hash found: ${error}`);
4959
+ if (globalThis.peernet.peers.length === 0) {
4960
+ this.#syncing = false;
4961
+ return 'connectionless';
5139
4962
  }
5140
- debug$1(`Local block height: ${localIndex}, remote block height: ${remoteIndex}`);
5141
- debug$1(`Local state hash: ${localStateHash}, remote block hash: ${remoteBlockHash}`);
5142
- // Skip syncing if remote block hash is 0x0 (invalid state)
5143
- if (remoteBlockHash === '0x0') {
5144
- debug$1(`Remote block hash is 0x0, skipping sync`);
5145
- return;
4963
+ await this.resolveBlock(lastBlock.hash);
4964
+ const blocks = this.#blocks;
4965
+ const localIndex = (await this.lastBlock).index || -1;
4966
+ const start = Math.max(0, localIndex + 1);
4967
+ if (this.#machine && blocks.length > start) {
4968
+ await this.#loadBlocks(blocks.slice(start));
5146
4969
  }
5147
- // Use state hash comparison: only resolve if remote hash differs from local state hash
5148
- if (localStateHash !== remoteBlockHash) {
5149
- // Remote block hash differs from our local state, need to resolve
5150
- debug$1(`Resolving remote block: ${remoteBlockHash} @${remoteIndex} (differs from local state)`);
5151
- await this.resolveBlock(remoteBlockHash);
5152
- const blocksSynced = remoteIndex - localIndex;
5153
- debug$1(`Resolved ${blocksSynced} new block(s)`);
5154
- const blocks = this.#blocks;
5155
- debug$1(`Loading blocks from index ${localIndex + 1} to ${remoteIndex}`);
5156
- const start = localIndex + 1;
5157
- if (this.#machine && blocks.length > start) {
5158
- await this.#loadBlocks(blocks.slice(start));
5159
- }
5160
- // Update state with the latest block
5161
- if (blocks.length > 0) {
5162
- await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
5163
- }
5164
- }
5165
- else {
5166
- debug$1(`Block already in local state. Remote hash: ${remoteBlockHash} matches local state`);
4970
+ if (blocks.length > 0) {
4971
+ await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
5167
4972
  }
4973
+ this.#syncErrorCount = 0;
4974
+ this.#syncing = false;
4975
+ return 'synced';
5168
4976
  }
5169
4977
  catch (error) {
5170
- console.log(error);
5171
- throw error;
4978
+ this.#syncErrorCount++;
4979
+ if (this.#syncErrorCount < 3) {
4980
+ this.#syncing = false;
4981
+ return this.syncChain(lastBlock);
4982
+ }
4983
+ this.#syncErrorCount = 0;
4984
+ this.#syncing = false;
4985
+ return 'errored';
5172
4986
  }
5173
4987
  }
5174
4988
  async #getLatestBlock() {
@@ -5339,26 +5153,13 @@ class State extends Contract {
5339
5153
  });
5340
5154
  }
5341
5155
  get canSync() {
5342
- if (this.#chainSyncing)
5343
- return false;
5344
- return true;
5156
+ return !this.#syncing;
5345
5157
  }
5346
5158
  get shouldSync() {
5347
- if (this.#chainSyncing)
5159
+ if (this.#syncing)
5348
5160
  return false;
5349
- // Check if we have any connected peers with the same version
5350
5161
  const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
5351
- if (compatiblePeers.length === 0) {
5352
- debug$1('No compatible peers available for sync');
5353
- return false;
5354
- }
5355
- if (!this.#chainSyncing ||
5356
- this.#resolveErrored ||
5357
- this.#syncState === 'errored' ||
5358
- this.#syncState === 'connectionless' ||
5359
- this.#lastResolvedTime + this.resolveTimeout > Date.now())
5360
- return true;
5361
- return false;
5162
+ return compatiblePeers.length > 0;
5362
5163
  }
5363
5164
  async #waitForPeers(timeoutMs = 30000) {
5364
5165
  return new Promise((resolve) => {
package/exports/chain.js CHANGED
@@ -6,7 +6,7 @@ import { calculateFee, createContractMessage, signTransaction, contractFactoryMe
6
6
  import semver from 'semver';
7
7
  import { randombytes } from '@leofcoin/crypto';
8
8
  import EasyWorker from '@vandeurenglenn/easy-worker';
9
- import { ContractDeploymentError, ExecutionError, isResolveError, ResolveError, isExecutionError } from '@leofcoin/errors';
9
+ import { ContractDeploymentError, ExecutionError, ResolveError, isExecutionError } from '@leofcoin/errors';
10
10
 
11
11
  const limit = 1800;
12
12
  const transactionLimit = 1000;
@@ -739,77 +739,36 @@ class Machine {
739
739
  }
740
740
  }
741
741
 
742
- class Jobber {
743
- constructor(timeout) {
744
- this.busy = false;
745
- this.timeout = timeout;
746
- }
747
- add(fn) {
748
- this.busy = true;
749
- return new Promise(async (resolve, reject) => {
750
- const timeout = setTimeout(() => {
751
- reject('timeout');
752
- }, this.timeout);
753
- this.destroy = () => {
754
- clearTimeout(timeout);
755
- this.busy = false;
756
- resolve('stopped');
757
- };
758
- try {
759
- const result = await fn();
760
- clearTimeout(timeout);
761
- this.busy = false;
762
- resolve(result);
763
- }
764
- catch (error) {
765
- clearTimeout(timeout);
766
- reject(error);
767
- }
768
- });
769
- }
770
- }
771
-
772
742
  const debug$1 = createDebugger('leofcoin/state');
773
743
  class State extends Contract {
774
- #resolveErrored;
775
- #lastResolvedTime;
776
- #lastResolved;
777
- #resolving;
778
- #resolveErrorCount;
779
- #syncState;
780
- #chainState;
781
- #lastBlockInQue;
782
- #syncErrorCount;
783
744
  #blockHashMap;
784
- #chainSyncing;
785
745
  #blocks;
786
- #totalSize;
787
746
  #machine;
788
747
  #loaded;
748
+ // Sync state
749
+ #syncing;
750
+ #syncErrorCount;
751
+ // Block resolution state
789
752
  #resolvingBlocks;
790
753
  #maxConcurrentResolves;
754
+ #totalSize;
755
+ #lastResolved;
756
+ #lastResolvedTime;
791
757
  #blockResolveQueue;
758
+ #chainState;
792
759
  /**
793
760
  * contains transactions we need before we can successfully load
794
761
  */
795
762
  get wantList() {
796
763
  return this.#machine?.wantList ?? this._wantList;
797
764
  }
798
- get state() {
799
- return {
800
- sync: this.#syncState,
801
- chain: this.#chainState
802
- };
803
- }
804
- get blockHashMap() {
805
- return this.#blockHashMap.entries();
806
- }
807
765
  get loaded() {
808
766
  return this.#loaded;
809
767
  }
810
- get resolving() {
811
- return this.#resolving;
768
+ get isSyncing() {
769
+ return this.#syncing;
812
770
  }
771
+ // Delegate to machine
813
772
  get contracts() {
814
773
  return this.#machine.contracts;
815
774
  }
@@ -852,38 +811,38 @@ class State extends Contract {
852
811
  get lastBlockHeight() {
853
812
  return this.#machine ? this.#machine.lastBlockHeight : 0;
854
813
  }
855
- getBlock(index) {
856
- return this.#machine.getBlock(index);
857
- }
858
- getBlocks(from, to) {
859
- return this.#machine.getBlocks(from, to);
860
- }
861
814
  get totalSize() {
862
815
  return this.#totalSize;
863
816
  }
864
817
  get machine() {
865
818
  return this.#machine;
866
819
  }
820
+ get blockHashMap() {
821
+ return this.#blockHashMap.entries();
822
+ }
823
+ getBlock(index) {
824
+ return this.#machine.getBlock(index);
825
+ }
826
+ getBlocks(from, to) {
827
+ return this.#machine.getBlocks(from, to);
828
+ }
867
829
  constructor(config) {
868
830
  super(config);
869
- this.#lastResolvedTime = 0;
870
- this.#resolving = false;
871
- this.#resolveErrorCount = 0;
872
- this.#chainState = 'loading';
873
- this.#syncErrorCount = 0;
874
831
  this.#blockHashMap = new Map();
875
- this.#chainSyncing = false;
876
832
  this.#blocks = [];
877
- this.knownBlocks = [];
878
- this.#totalSize = 0;
879
833
  this.#loaded = false;
880
- this._wantList = [];
834
+ // Sync state
835
+ this.#syncing = false;
836
+ this.#syncErrorCount = 0;
837
+ // Block resolution state
881
838
  this.#resolvingBlocks = new Set();
882
839
  this.#maxConcurrentResolves = 10;
883
- this.#blockResolveQueue = [];
840
+ this.knownBlocks = [];
841
+ this.#totalSize = 0;
842
+ this._wantList = [];
884
843
  this.#chainStateHandler = () => {
885
844
  return new globalThis.peernet.protos['peernet-response']({
886
- response: this.#chainState
845
+ response: { syncing: this.#syncing, loaded: this.#loaded }
887
846
  });
888
847
  };
889
848
  this.#lastBlockHandler = async () => {
@@ -925,67 +884,43 @@ class State extends Contract {
925
884
  #lastBlockHandler;
926
885
  #knownBlocksHandler;
927
886
  async init() {
928
- this.jobber = new Jobber(this.resolveTimeout);
929
- await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler);
930
- await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler);
931
- await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler);
932
- let localBlockHash;
933
- let blockMessage;
934
- let localBlock;
887
+ // Register request handlers
888
+ await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this));
889
+ await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler.bind(this));
890
+ await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler.bind(this));
935
891
  try {
936
- const rawBlock = await globalThis.chainStore.has('lastBlock');
937
- if (rawBlock) {
938
- localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
939
- if (localBlockHash !== '0x0') {
940
- blockMessage = await globalThis.peernet.get(localBlockHash, 'block');
941
- blockMessage = await new BlockMessage(blockMessage);
892
+ // Load local block state
893
+ let localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
894
+ try {
895
+ const localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
896
+ if (localBlockHash && localBlockHash !== '0x0') {
897
+ const blockMessage = await new BlockMessage(await globalThis.peernet.get(localBlockHash, 'block'));
942
898
  localBlock = { ...blockMessage.decoded, hash: localBlockHash };
943
899
  }
944
900
  }
945
- else {
946
- localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
901
+ catch (error) {
902
+ debug$1('No local block found');
947
903
  }
948
- }
949
- catch {
950
- localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
951
- }
952
- try {
953
- this.knownBlocks = await blockStore.keys();
954
- }
955
- catch (error) {
956
- debug$1('no local known blocks found');
957
- }
958
- try {
959
- if (localBlock.hash && localBlock.hash !== '0x0') {
960
- try {
961
- const states = {
962
- lastBlock: JSON.parse(new TextDecoder().decode(await globalThis.stateStore.get('lastBlock')))
963
- };
964
- if (blockMessage.decoded.index > states.lastBlock.index)
965
- await this.resolveBlocks();
966
- }
967
- catch (error) {
968
- // no states found, try resolving blocks
969
- await this.resolveBlocks();
970
- }
904
+ // Load known blocks
905
+ try {
906
+ this.knownBlocks = await globalThis.blockStore.keys();
971
907
  }
972
- else {
973
- await this.resolveBlocks();
908
+ catch (error) {
909
+ debug$1('No known blocks found');
974
910
  }
911
+ // Initialize machine and resolve blocks if needed
975
912
  this.#machine = await new Machine(this.#blocks);
913
+ if (localBlock.hash !== '0x0') {
914
+ await this.resolveBlock(localBlock.hash);
915
+ }
976
916
  const lastBlock = await this.#machine.lastBlock;
977
917
  if (lastBlock.hash !== '0x0') {
978
- this.updateState(new BlockMessage(lastBlock));
918
+ await this.updateState(new BlockMessage(lastBlock));
979
919
  }
980
920
  this.#loaded = true;
981
- // await this.#loadBlocks(this.#blocks)
982
921
  }
983
922
  catch (error) {
984
- console.log('e');
985
- if (isResolveError(error)) {
986
- console.error(error);
987
- }
988
- console.log(error);
923
+ console.error('Failed to initialize state:', error);
989
924
  }
990
925
  }
991
926
  async updateState(message) {
@@ -1119,196 +1054,76 @@ class State extends Contract {
1119
1054
  await Promise.all(resolving);
1120
1055
  }
1121
1056
  async resolveBlock(hash) {
1122
- if (!hash)
1123
- throw new Error(`expected hash, got: ${hash}`);
1124
- if (hash === '0x0')
1057
+ if (!hash || hash === '0x0')
1125
1058
  return;
1126
- if (this.#resolving) {
1127
- debug$1('Already resolving, queueing block');
1059
+ if (this.#syncing)
1128
1060
  return;
1129
- }
1130
- this.#resolving = true;
1061
+ this.#syncing = true;
1062
+ this.#syncErrorCount = 0;
1131
1063
  try {
1132
- // Build the entire block chain from latest to genesis
1133
1064
  debug$1(`Building block chain from ${hash}`);
1134
1065
  const blockChain = await this.#buildBlockChain(hash);
1135
1066
  debug$1(`Built chain of ${blockChain.length} blocks`);
1136
- // Resolve all blocks in parallel
1137
1067
  if (blockChain.length > 0) {
1138
1068
  await this.#resolveBlocksInParallel(blockChain);
1139
1069
  }
1140
- this.#resolving = false;
1141
- this.#resolveErrorCount = 0;
1142
- this.#resolveErrored = false;
1143
- try {
1144
- const lastBlockHash = await globalThis.stateStore.get('lastBlock');
1145
- if (lastBlockHash === hash) {
1146
- debug$1('Resolved to latest block state');
1147
- return;
1148
- }
1149
- }
1150
- catch (error) {
1151
- debug$1('no local state found');
1152
- }
1153
1070
  }
1154
1071
  catch (error) {
1155
- console.log({ error });
1156
- this.#resolveErrorCount += 1;
1157
- this.#resolving = false;
1158
- if (this.#resolveErrorCount < 3) {
1159
- debug$1(`Retry ${this.#resolveErrorCount}/3 for block ${hash}`);
1160
- return this.resolveBlock(hash);
1161
- }
1162
- this.#resolveErrorCount = 0;
1072
+ console.error('Block resolution failed:', error);
1163
1073
  this.wantList.push(hash);
1164
- throw new ResolveError(`block: ${hash}`, { cause: error });
1074
+ }
1075
+ finally {
1076
+ this.#syncing = false;
1165
1077
  }
1166
1078
  }
1167
1079
  async resolveBlocks() {
1168
- // Don't re-resolve if already syncing or resolving
1169
- if (this.#chainSyncing || this.#resolving) {
1170
- debug$1('Already syncing or resolving, skipping resolveBlocks()');
1080
+ if (this.#syncing)
1171
1081
  return;
1172
- }
1173
- try {
1174
- if (this.jobber.busy && this.jobber.destroy) {
1175
- await this.jobber.destroy();
1176
- }
1177
- }
1178
- catch (error) {
1179
- console.error(error);
1180
- }
1181
1082
  try {
1182
1083
  const localBlock = await globalThis.chainStore.get('lastBlock');
1183
1084
  const hash = new TextDecoder().decode(localBlock);
1184
1085
  if (hash && hash !== '0x0') {
1185
- debug$1(`Resolving blocks from hash: ${hash}`);
1186
1086
  await this.resolveBlock(hash);
1187
1087
  }
1188
1088
  }
1189
1089
  catch (error) {
1190
- console.log(error);
1191
- this.#chainSyncing = false;
1192
- this.#syncState = 'errored';
1193
- this.#resolveErrored = true;
1194
- return this.restoreChain();
1195
- // console.log(e);
1196
- }
1197
- }
1198
- async restoreChain() {
1199
- try {
1200
- const { hash } = await this.#getLatestBlock();
1201
- await globalThis.chainStore.put('lastBlock', hash);
1202
- if (hash && hash !== '0x0') {
1203
- await this.resolveBlock(hash);
1204
- }
1205
- }
1206
- catch (error) {
1207
- console.log(error);
1208
- this.#resolveErrored = true;
1209
- this.#resolveErrorCount += 1;
1210
- this.#resolving = false;
1211
- return this.restoreChain();
1212
- // console.log(e);
1090
+ debug$1('Failed to resolve blocks:', error);
1213
1091
  }
1214
1092
  }
1215
1093
  async syncChain(lastBlock) {
1216
- console.log('check if can sync');
1217
- if (!this.shouldSync)
1218
- return;
1219
- console.log('starting sync');
1220
- this.#syncState = 'syncing';
1221
- this.#chainSyncing = true;
1222
- try {
1223
- if (this.jobber.busy && this.jobber.destroy) {
1224
- await this.jobber.destroy();
1225
- }
1226
- }
1227
- catch (error) {
1228
- console.error(error);
1229
- }
1094
+ if (this.#syncing)
1095
+ return 'syncing';
1230
1096
  if (!lastBlock)
1231
1097
  lastBlock = await this.#getLatestBlock();
1232
- if (globalThis.peernet.peers.length === 0)
1233
- return 'connectionless';
1098
+ this.#syncing = true;
1234
1099
  try {
1235
- await this.#syncChain(lastBlock);
1236
- }
1237
- catch (error) {
1238
- this.#syncErrorCount += 1;
1239
- if (this.#syncErrorCount < 3)
1240
- return this.syncChain(lastBlock);
1241
- this.#syncErrorCount = 0;
1242
- this.#chainSyncing = false;
1243
- this.#syncState = 'errored';
1244
- return this.#syncState;
1245
- }
1246
- if (lastBlock.index === this.#lastBlockInQue?.index)
1247
- this.#lastBlockInQue = undefined;
1248
- this.#syncErrorCount = 0;
1249
- this.#chainSyncing = false;
1250
- if (this.#lastBlockInQue)
1251
- return this.syncChain(this.#lastBlockInQue);
1252
- this.#syncState = 'synced';
1253
- return this.#syncState;
1254
- }
1255
- async #syncChain(lastBlock) {
1256
- try {
1257
- // if (this.knownBlocks?.length === Number(lastBlock.index) + 1) {
1258
- // let promises = []
1259
- // promises = await Promise.allSettled(
1260
- // this.knownBlocks.map(async (address) => {
1261
- // const has = await globalThis.peernet.has(address)
1262
- // return { has, address }
1263
- // })
1264
- // )
1265
- // promises = promises.filter(({ status, value }) => status === 'fulfilled' && !value.has)
1266
- // await Promise.allSettled(promises.map(({ value }) => this.getAndPutBlock(value.address)))
1267
- // }
1268
- const localBlock = await this.lastBlock;
1269
- const localIndex = localBlock ? Number(localBlock.index) : -1;
1270
- const remoteIndex = Number(lastBlock.index);
1271
- const remoteBlockHash = lastBlock.hash;
1272
- // Get the local state hash from chainStore
1273
- let localStateHash = '0x0';
1274
- try {
1275
- localStateHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
1276
- }
1277
- catch (error) {
1278
- debug$1(`No local state hash found: ${error}`);
1100
+ if (globalThis.peernet.peers.length === 0) {
1101
+ this.#syncing = false;
1102
+ return 'connectionless';
1279
1103
  }
1280
- debug$1(`Local block height: ${localIndex}, remote block height: ${remoteIndex}`);
1281
- debug$1(`Local state hash: ${localStateHash}, remote block hash: ${remoteBlockHash}`);
1282
- // Skip syncing if remote block hash is 0x0 (invalid state)
1283
- if (remoteBlockHash === '0x0') {
1284
- debug$1(`Remote block hash is 0x0, skipping sync`);
1285
- return;
1104
+ await this.resolveBlock(lastBlock.hash);
1105
+ const blocks = this.#blocks;
1106
+ const localIndex = (await this.lastBlock).index || -1;
1107
+ const start = Math.max(0, localIndex + 1);
1108
+ if (this.#machine && blocks.length > start) {
1109
+ await this.#loadBlocks(blocks.slice(start));
1286
1110
  }
1287
- // Use state hash comparison: only resolve if remote hash differs from local state hash
1288
- if (localStateHash !== remoteBlockHash) {
1289
- // Remote block hash differs from our local state, need to resolve
1290
- debug$1(`Resolving remote block: ${remoteBlockHash} @${remoteIndex} (differs from local state)`);
1291
- await this.resolveBlock(remoteBlockHash);
1292
- const blocksSynced = remoteIndex - localIndex;
1293
- debug$1(`Resolved ${blocksSynced} new block(s)`);
1294
- const blocks = this.#blocks;
1295
- debug$1(`Loading blocks from index ${localIndex + 1} to ${remoteIndex}`);
1296
- const start = localIndex + 1;
1297
- if (this.#machine && blocks.length > start) {
1298
- await this.#loadBlocks(blocks.slice(start));
1299
- }
1300
- // Update state with the latest block
1301
- if (blocks.length > 0) {
1302
- await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
1303
- }
1304
- }
1305
- else {
1306
- debug$1(`Block already in local state. Remote hash: ${remoteBlockHash} matches local state`);
1111
+ if (blocks.length > 0) {
1112
+ await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
1307
1113
  }
1114
+ this.#syncErrorCount = 0;
1115
+ this.#syncing = false;
1116
+ return 'synced';
1308
1117
  }
1309
1118
  catch (error) {
1310
- console.log(error);
1311
- throw error;
1119
+ this.#syncErrorCount++;
1120
+ if (this.#syncErrorCount < 3) {
1121
+ this.#syncing = false;
1122
+ return this.syncChain(lastBlock);
1123
+ }
1124
+ this.#syncErrorCount = 0;
1125
+ this.#syncing = false;
1126
+ return 'errored';
1312
1127
  }
1313
1128
  }
1314
1129
  async #getLatestBlock() {
@@ -1479,26 +1294,13 @@ class State extends Contract {
1479
1294
  });
1480
1295
  }
1481
1296
  get canSync() {
1482
- if (this.#chainSyncing)
1483
- return false;
1484
- return true;
1297
+ return !this.#syncing;
1485
1298
  }
1486
1299
  get shouldSync() {
1487
- if (this.#chainSyncing)
1300
+ if (this.#syncing)
1488
1301
  return false;
1489
- // Check if we have any connected peers with the same version
1490
1302
  const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
1491
- if (compatiblePeers.length === 0) {
1492
- debug$1('No compatible peers available for sync');
1493
- return false;
1494
- }
1495
- if (!this.#chainSyncing ||
1496
- this.#resolveErrored ||
1497
- this.#syncState === 'errored' ||
1498
- this.#syncState === 'connectionless' ||
1499
- this.#lastResolvedTime + this.resolveTimeout > Date.now())
1500
- return true;
1501
- return false;
1303
+ return compatiblePeers.length > 0;
1502
1304
  }
1503
1305
  async #waitForPeers(timeoutMs = 30000) {
1504
1306
  return new Promise((resolve) => {
@@ -1,26 +1,17 @@
1
1
  import { BlockMessage } from '@leofcoin/messages';
2
2
  import Contract from './contract.js';
3
3
  import Machine from './machine.js';
4
- import Jobber from './jobs/jobber.js';
5
4
  import { BlockHash } from './types.js';
6
- declare type SyncState = 'syncing' | 'synced' | 'errored' | 'connectionless';
7
- declare type ChainState = 'loading' | 'loaded';
8
5
  export default class State extends Contract {
9
6
  #private;
10
7
  knownBlocks: BlockHash[];
11
- jobber: Jobber;
12
8
  _wantList: any[];
13
9
  /**
14
10
  * contains transactions we need before we can successfully load
15
11
  */
16
12
  get wantList(): string[];
17
- get state(): {
18
- sync: SyncState;
19
- chain: ChainState;
20
- };
21
- get blockHashMap(): MapIterator<[any, any]>;
22
13
  get loaded(): boolean;
23
- get resolving(): boolean;
14
+ get isSyncing(): boolean;
24
15
  get contracts(): Promise<any>;
25
16
  get totalContracts(): Promise<any>;
26
17
  get nativeCalls(): Promise<any>;
@@ -39,10 +30,11 @@ export default class State extends Contract {
39
30
  previousHash: string;
40
31
  };
41
32
  get lastBlockHeight(): Promise<any> | 0;
42
- getBlock(index: any): Promise<any>;
43
- getBlocks(from?: any, to?: any): Promise<[]>;
44
33
  get totalSize(): number;
45
34
  get machine(): Machine;
35
+ get blockHashMap(): MapIterator<[any, any]>;
36
+ getBlock(index: any): Promise<any>;
37
+ getBlocks(from?: any, to?: any): Promise<[]>;
46
38
  constructor(config: any);
47
39
  clearPool(): Promise<void>;
48
40
  /**
@@ -54,13 +46,11 @@ export default class State extends Contract {
54
46
  getLatestBlock(): Promise<BlockMessage['decoded']>;
55
47
  getAndPutBlock(hash: string): Promise<BlockMessage>;
56
48
  resolveBlock(hash: string): Promise<void>;
57
- resolveBlocks(): Promise<any>;
58
- restoreChain(): any;
59
- syncChain(lastBlock?: any): Promise<SyncState>;
49
+ resolveBlocks(): Promise<void>;
50
+ syncChain(lastBlock?: any): Promise<'syncing' | 'synced' | 'errored' | 'connectionless'>;
60
51
  promiseRequests(promises: any): Promise<unknown>;
61
52
  get canSync(): boolean;
62
53
  get shouldSync(): boolean;
63
- triggerSync(): Promise<SyncState>;
54
+ triggerSync(): Promise<"syncing" | "synced" | "errored" | "connectionless">;
64
55
  triggerLoad(): Promise<void>;
65
56
  }
66
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leofcoin/chain",
3
- "version": "1.7.150",
3
+ "version": "1.7.152",
4
4
  "description": "Official javascript implementation",
5
5
  "private": false,
6
6
  "exports": {