@leofcoin/chain 1.8.1 → 1.8.3

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.
@@ -1,33 +1,35 @@
1
1
  import { t as toBase58, T as TransactionMessage, C as ContractMessage, R as RawTransactionMessage, B as BlockMessage, a as BWMessage, b as BWRequestMessage } from './index-DUfUgiQY.js';
2
2
 
3
- let TARGETS = undefined;
4
- if (globalThis.localStorage && !TARGETS) {
5
- const DEBUG = globalThis.localStorage.getItem("DEBUG");
6
- if (DEBUG) {
7
- TARGETS = JSON.parse(DEBUG);
8
- }
9
- }
3
+ const getTargets = () => Array.from([]);
4
+ const isEnabled = (target) => {
5
+ return false;
6
+ };
7
+
10
8
  const getLogger = (trace) => (trace ? console.trace : console.log);
11
- const debug$3 = (target, text, trace) => {
12
- const _logger = getLogger(trace);
13
- if (!TARGETS)
14
- return;
15
- if (TARGETS === true ||
16
- TARGETS?.indexOf(target) !== -1 ||
17
- TARGETS?.indexOf("*") !== -1 ||
18
- TARGETS?.indexOf(target.split("/")[0]) !== -1)
9
+ const createDebugger = (target) => {
10
+ // Cache the enabled check on first call
11
+ let enabled = null;
12
+ let lastTargets = undefined;
13
+ return (text, trace) => {
14
+ const targets = getTargets();
15
+ if (lastTargets !== targets) {
16
+ enabled = targets ? isEnabled() : false;
17
+ lastTargets = targets;
18
+ }
19
+ if (!enabled)
20
+ return;
19
21
  if (text)
20
- _logger("\x1b[34m\x1b[1m%s", `${target}: ${text}`, "\x1b[0m");
22
+ getLogger(trace)('\x1b[34m\x1b[1m%s', `${target}: ${text}`, '\x1b[0m');
21
23
  else
22
- _logger("\x1b[34m\x1b[1m%s", `${target}`, "\x1b[0m");
24
+ getLogger(trace)('\x1b[34m\x1b[1m%s', `${target}`, '\x1b[0m');
25
+ };
23
26
  };
24
- const createDebugger = (target) => (text, trace) => debug$3(target, text, trace);
25
27
 
26
28
  /* Do NOT modify this file; see /src.ts/_admin/update-version.ts */
27
29
  /**
28
30
  * The current version of Ethers.
29
31
  */
30
- const version = "6.15.0";
32
+ const version = "6.16.0";
31
33
 
32
34
  /**
33
35
  * Property helper functions.
@@ -231,7 +233,8 @@ function _getBytes(value, name, copy) {
231
233
  if (value instanceof Uint8Array) {
232
234
  return value;
233
235
  }
234
- if (typeof (value) === "string" && value.match(/^0x(?:[0-9a-f][0-9a-f])*$/i)) {
236
+ if (typeof (value) === "string" && (value.length % 2) === 0 &&
237
+ value.match(/^0x[0-9a-f]*$/i)) {
235
238
  const result = new Uint8Array((value.length - 2) / 2);
236
239
  let offset = 2;
237
240
  for (let i = 0; i < result.length; i++) {
@@ -4128,6 +4131,7 @@ class ExecutionError extends LeofcoinError {
4128
4131
  class ContractDeploymentError extends LeofcoinError {
4129
4132
  name = 'ContractDeploymentError';
4130
4133
  }
4134
+ const isResolveError = (error) => error.name === 'ResolveError';
4131
4135
  const isExecutionError = (error) => error.name === 'ExecutionError';
4132
4136
 
4133
4137
  // import State from './state'
@@ -4630,34 +4634,42 @@ class Jobber {
4630
4634
 
4631
4635
  const debug$1 = createDebugger('leofcoin/state');
4632
4636
  class State extends Contract {
4637
+ #resolveErrored;
4638
+ #lastResolvedTime;
4639
+ #lastResolved;
4640
+ #resolving;
4641
+ #resolveErrorCount;
4642
+ #syncState;
4643
+ #chainState;
4644
+ #lastBlockInQue;
4645
+ #syncErrorCount;
4633
4646
  #blockHashMap;
4647
+ #chainSyncing;
4634
4648
  #blocks;
4649
+ #totalSize;
4635
4650
  #machine;
4636
4651
  #loaded;
4637
- // Sync state
4638
- #syncing;
4639
- #syncErrorCount;
4640
- // Block resolution state
4641
- #resolvingBlocks;
4642
- #maxConcurrentResolves;
4643
- #totalSize;
4644
- #lastResolved;
4645
- #lastResolvedTime;
4646
- #blockResolveQueue;
4647
- #chainState;
4648
4652
  /**
4649
4653
  * contains transactions we need before we can successfully load
4650
4654
  */
4651
4655
  get wantList() {
4652
4656
  return this.#machine?.wantList ?? this._wantList;
4653
4657
  }
4658
+ get state() {
4659
+ return {
4660
+ sync: this.#syncState,
4661
+ chain: this.#chainState
4662
+ };
4663
+ }
4664
+ get blockHashMap() {
4665
+ return this.#blockHashMap.entries();
4666
+ }
4654
4667
  get loaded() {
4655
4668
  return this.#loaded;
4656
4669
  }
4657
- get isSyncing() {
4658
- return this.#syncing;
4670
+ get resolving() {
4671
+ return this.#resolving;
4659
4672
  }
4660
- // Delegate to machine
4661
4673
  get contracts() {
4662
4674
  return this.#machine.contracts;
4663
4675
  }
@@ -4700,38 +4712,35 @@ class State extends Contract {
4700
4712
  get lastBlockHeight() {
4701
4713
  return this.#machine ? this.#machine.lastBlockHeight : 0;
4702
4714
  }
4703
- get totalSize() {
4704
- return this.#totalSize;
4705
- }
4706
- get machine() {
4707
- return this.#machine;
4708
- }
4709
- get blockHashMap() {
4710
- return this.#blockHashMap.entries();
4711
- }
4712
4715
  getBlock(index) {
4713
4716
  return this.#machine.getBlock(index);
4714
4717
  }
4715
4718
  getBlocks(from, to) {
4716
4719
  return this.#machine.getBlocks(from, to);
4717
4720
  }
4721
+ get totalSize() {
4722
+ return this.#totalSize;
4723
+ }
4724
+ get machine() {
4725
+ return this.#machine;
4726
+ }
4718
4727
  constructor(config) {
4719
4728
  super(config);
4729
+ this.#lastResolvedTime = 0;
4730
+ this.#resolving = false;
4731
+ this.#resolveErrorCount = 0;
4732
+ this.#chainState = 'loading';
4733
+ this.#syncErrorCount = 0;
4720
4734
  this.#blockHashMap = new Map();
4735
+ this.#chainSyncing = false;
4721
4736
  this.#blocks = [];
4722
- this.#loaded = false;
4723
- // Sync state
4724
- this.#syncing = false;
4725
- this.#syncErrorCount = 0;
4726
- // Block resolution state
4727
- this.#resolvingBlocks = new Set();
4728
- this.#maxConcurrentResolves = 10;
4729
4737
  this.knownBlocks = [];
4730
4738
  this.#totalSize = 0;
4739
+ this.#loaded = false;
4731
4740
  this._wantList = [];
4732
4741
  this.#chainStateHandler = () => {
4733
4742
  return new globalThis.peernet.protos['peernet-response']({
4734
- response: { syncing: this.#syncing, loaded: this.#loaded }
4743
+ response: this.#chainState
4735
4744
  });
4736
4745
  };
4737
4746
  this.#lastBlockHandler = async () => {
@@ -4773,45 +4782,67 @@ class State extends Contract {
4773
4782
  #lastBlockHandler;
4774
4783
  #knownBlocksHandler;
4775
4784
  async init() {
4776
- // Initialize jobber for timed, cancelable tasks
4777
4785
  this.jobber = new Jobber(this.resolveTimeout);
4778
- // Register request handlers
4779
- await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this));
4780
- await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler.bind(this));
4781
- await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler.bind(this));
4786
+ await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler);
4787
+ await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler);
4788
+ await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler);
4789
+ let localBlockHash;
4790
+ let blockMessage;
4791
+ let localBlock;
4782
4792
  try {
4783
- // Load local block state
4784
- let localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
4785
- try {
4786
- const localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
4787
- if (localBlockHash && localBlockHash !== '0x0') {
4788
- const blockMessage = await new BlockMessage(await globalThis.peernet.get(localBlockHash, 'block'));
4793
+ const rawBlock = await globalThis.chainStore.has('lastBlock');
4794
+ if (rawBlock) {
4795
+ localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
4796
+ if (localBlockHash !== '0x0') {
4797
+ blockMessage = await globalThis.peernet.get(localBlockHash, 'block');
4798
+ blockMessage = await new BlockMessage(blockMessage);
4789
4799
  localBlock = { ...blockMessage.decoded, hash: localBlockHash };
4790
4800
  }
4791
4801
  }
4792
- catch (error) {
4793
- debug$1('No local block found');
4802
+ else {
4803
+ localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
4794
4804
  }
4795
- // Load known blocks
4796
- try {
4797
- this.knownBlocks = await globalThis.blockStore.keys();
4805
+ }
4806
+ catch {
4807
+ localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
4808
+ }
4809
+ try {
4810
+ this.knownBlocks = await blockStore.keys();
4811
+ }
4812
+ catch (error) {
4813
+ debug$1('no local known blocks found');
4814
+ }
4815
+ try {
4816
+ if (localBlock.hash && localBlock.hash !== '0x0') {
4817
+ try {
4818
+ const states = {
4819
+ lastBlock: JSON.parse(new TextDecoder().decode(await globalThis.stateStore.get('lastBlock')))
4820
+ };
4821
+ if (blockMessage.decoded.index > states.lastBlock.index)
4822
+ await this.resolveBlocks();
4823
+ }
4824
+ catch (error) {
4825
+ // no states found, try resolving blocks
4826
+ await this.resolveBlocks();
4827
+ }
4798
4828
  }
4799
- catch (error) {
4800
- debug$1('No known blocks found');
4829
+ else {
4830
+ await this.resolveBlocks();
4801
4831
  }
4802
- // Initialize machine and resolve blocks if needed
4803
4832
  this.#machine = await new Machine(this.#blocks);
4804
- if (localBlock.hash !== '0x0') {
4805
- await this.resolveBlock(localBlock.hash);
4806
- }
4807
4833
  const lastBlock = await this.#machine.lastBlock;
4808
4834
  if (lastBlock.hash !== '0x0') {
4809
- await this.updateState(new BlockMessage(lastBlock));
4835
+ this.updateState(new BlockMessage(lastBlock));
4810
4836
  }
4811
4837
  this.#loaded = true;
4838
+ // await this.#loadBlocks(this.#blocks)
4812
4839
  }
4813
4840
  catch (error) {
4814
- console.error('Failed to initialize state:', error);
4841
+ console.log('e');
4842
+ if (isResolveError(error)) {
4843
+ console.error(error);
4844
+ }
4845
+ console.log(error);
4815
4846
  }
4816
4847
  }
4817
4848
  async updateState(message) {
@@ -4845,47 +4876,41 @@ class State extends Contract {
4845
4876
  return block;
4846
4877
  }
4847
4878
  async #resolveBlock(hash) {
4848
- if (this.#resolvingBlocks.has(hash)) {
4849
- return; // Already resolving this block
4850
- }
4851
- this.#resolvingBlocks.add(hash);
4879
+ let index = this.#blockHashMap.get(hash);
4880
+ let localHash = '0x0';
4852
4881
  try {
4853
- let index = this.#blockHashMap.get(hash);
4854
- debug$1(`resolving block: ${hash} @${index !== undefined ? index : 'unknown'}`);
4855
- if (this.#blocks[index]) {
4856
- // Block already exists
4882
+ localHash = await globalThis.stateStore.get('lastBlock');
4883
+ }
4884
+ catch (error) {
4885
+ debug$1('no local state found');
4886
+ }
4887
+ if (this.#blocks[index]) {
4888
+ // Block already exists, check if we need to resolve previous blocks
4889
+ const previousHash = this.#blocks[index].previousHash;
4890
+ if (previousHash === localHash)
4857
4891
  return;
4892
+ if (previousHash !== '0x0') {
4893
+ // Previous block not in memory, recursively resolve it
4894
+ return this.resolveBlock(previousHash);
4858
4895
  }
4859
- const block = await this.getAndPutBlock(hash);
4860
- index = block.decoded.index;
4861
- const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
4862
- // Batch transaction operations
4863
- const transactionsToFetch = [];
4864
- const transactionHashes = block.decoded.transactions || [];
4865
- for (const txHash of transactionHashes) {
4866
- if (!(await globalThis.transactionStore.has(txHash))) {
4867
- transactionsToFetch.push(txHash);
4868
- }
4869
- }
4870
- // Fetch all missing transactions in parallel
4871
- if (transactionsToFetch.length > 0) {
4872
- const fetchedResults = await Promise.allSettled(transactionsToFetch.map((txHash) => globalThis.peernet.get(txHash, 'transaction')));
4873
- // Batch store all transactions that were successfully fetched
4874
- for (let i = 0; i < fetchedResults.length; i++) {
4875
- if (fetchedResults[i].status === 'fulfilled') {
4876
- await globalThis.transactionStore.put(transactionsToFetch[i], fetchedResults[i].value);
4877
- }
4878
- else {
4879
- debug$1(`failed to fetch transaction ${transactionsToFetch[i]}: ${fetchedResults[i].reason?.message || fetchedResults[i].reason}`);
4880
- }
4881
- }
4896
+ else {
4897
+ // Previous block already exists or is genesis, stop resolving
4898
+ return;
4882
4899
  }
4883
- // Remove from pool
4884
- await Promise.all(transactionHashes.map(async (txHash) => {
4885
- if (await globalThis.transactionPoolStore.has(txHash)) {
4886
- await globalThis.transactionPoolStore.delete(txHash);
4900
+ }
4901
+ try {
4902
+ const block = await this.getAndPutBlock(hash);
4903
+ await Promise.all(block.decoded.transactions.map(async (hash) => {
4904
+ // should be in a transaction store already
4905
+ if (!(await transactionStore.has(hash))) {
4906
+ const data = await peernet.get(hash, 'transaction');
4907
+ await transactionStore.put(hash, data);
4887
4908
  }
4909
+ ;
4910
+ (await transactionPoolStore.has(hash)) && (await transactionPoolStore.delete(hash));
4888
4911
  }));
4912
+ index = block.decoded.index;
4913
+ const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
4889
4914
  this.#totalSize += size;
4890
4915
  this.#blocks[index] = { hash, ...block.decoded };
4891
4916
  this.#blockHashMap.set(hash, index);
@@ -4895,147 +4920,187 @@ class State extends Contract {
4895
4920
  this.#lastResolvedTime = Date.now();
4896
4921
  }
4897
4922
  catch (error) {
4898
- throw new ResolveError(`block: ${hash}`);
4899
- }
4900
- finally {
4901
- this.#resolvingBlocks.delete(hash);
4902
- }
4903
- }
4904
- async #buildBlockChain(latestHash, maxBlocks = 1000) {
4905
- const chain = [];
4906
- let currentHash = latestHash;
4907
- let attempts = 0;
4908
- while (currentHash !== '0x0' && chain.length < maxBlocks && attempts < maxBlocks + 5) {
4909
- attempts++;
4910
- // Check if we already have this block
4911
- if (this.#blockHashMap.has(currentHash)) {
4912
- const block = this.#blocks[this.#blockHashMap.get(currentHash)];
4913
- if (block) {
4914
- chain.push(currentHash);
4915
- currentHash = block.previousHash;
4916
- continue;
4917
- }
4918
- }
4919
- chain.push(currentHash);
4920
- // Try to get the block to find previous hash
4921
- try {
4922
- const block = await this.getAndPutBlock(currentHash);
4923
- currentHash = block.decoded.previousHash;
4924
- }
4925
- catch (error) {
4926
- debug$1(`Could not fetch block ${currentHash} to determine chain: ${error}`);
4927
- break;
4928
- }
4929
- }
4930
- return chain;
4931
- }
4932
- async #resolveBlocksInParallel(hashes) {
4933
- // Resolve blocks in parallel with concurrency limit
4934
- const resolving = [];
4935
- let index = 0;
4936
- const resolveNext = async () => {
4937
- while (index < hashes.length) {
4938
- const hash = hashes[index++];
4939
- try {
4940
- await this.#resolveBlock(hash);
4941
- }
4942
- catch (error) {
4943
- debug$1(`Failed to resolve block ${hash}: ${error}`);
4944
- this.#blockResolveQueue.push({ hash, retries: 0 });
4945
- }
4946
- }
4947
- };
4948
- // Start concurrent resolution tasks
4949
- for (let i = 0; i < Math.min(this.#maxConcurrentResolves, hashes.length); i++) {
4950
- resolving.push(resolveNext());
4923
+ throw new ResolveError(`block: ${hash}@${index}`);
4951
4924
  }
4952
- await Promise.all(resolving);
4925
+ return;
4953
4926
  }
4954
4927
  async resolveBlock(hash) {
4955
- if (!hash || hash === '0x0')
4956
- return;
4957
- if (this.#syncing)
4928
+ if (!hash)
4929
+ throw new Error(`expected hash, got: ${hash}`);
4930
+ if (hash === '0x0')
4958
4931
  return;
4959
- this.#syncing = true;
4960
- this.#syncErrorCount = 0;
4932
+ if (this.#resolving)
4933
+ return 'already resolving';
4934
+ this.#resolving = true;
4935
+ if (this.jobber.busy && this.jobber.destroy)
4936
+ await this.jobber.destroy();
4961
4937
  try {
4962
- debug$1(`Building block chain from ${hash}`);
4963
- const blockChain = await this.#buildBlockChain(hash);
4964
- debug$1(`Built chain of ${blockChain.length} blocks`);
4965
- if (blockChain.length > 0) {
4966
- // If a previous resolve job is still running, cancel it
4967
- if (this.jobber?.busy && this.jobber.destroy)
4968
- await this.jobber.destroy();
4969
- // Run the parallel resolution inside a timed jobber task
4970
- await this.jobber.add(() => this.#resolveBlocksInParallel(blockChain));
4938
+ await this.jobber.add(() => this.#resolveBlock(hash));
4939
+ this.#resolving = false;
4940
+ const lastBlockHash = await globalThis.stateStore.get('lastBlock');
4941
+ if (lastBlockHash === hash) {
4942
+ this.#resolveErrored = false;
4943
+ return;
4971
4944
  }
4945
+ if (!this.#blockHashMap.has(this.#lastResolved.previousHash) && this.#lastResolved.previousHash !== '0x0')
4946
+ return this.resolveBlock(this.#lastResolved.previousHash);
4972
4947
  }
4973
4948
  catch (error) {
4974
- console.error('Block resolution failed:', error);
4949
+ console.log({ error });
4950
+ this.#resolveErrorCount += 1;
4951
+ this.#resolving = false;
4952
+ if (this.#resolveErrorCount < 3)
4953
+ return this.resolveBlock(hash);
4954
+ this.#resolveErrorCount = 0;
4975
4955
  this.wantList.push(hash);
4976
- }
4977
- finally {
4978
- this.#syncing = false;
4956
+ throw new ResolveError(`block: ${hash}`, { cause: error });
4979
4957
  }
4980
4958
  }
4981
4959
  async resolveBlocks() {
4982
- if (this.#syncing)
4960
+ // Don't re-resolve if already syncing or resolving
4961
+ if (this.#chainSyncing || this.#resolving) {
4962
+ debug$1('Already syncing or resolving, skipping resolveBlocks()');
4983
4963
  return;
4964
+ }
4965
+ try {
4966
+ if (this.jobber.busy && this.jobber.destroy) {
4967
+ await this.jobber.destroy();
4968
+ }
4969
+ }
4970
+ catch (error) {
4971
+ console.error(error);
4972
+ }
4984
4973
  try {
4985
4974
  const localBlock = await globalThis.chainStore.get('lastBlock');
4986
4975
  const hash = new TextDecoder().decode(localBlock);
4987
4976
  if (hash && hash !== '0x0') {
4988
- // Cancel any in-flight job before starting a new one
4989
- if (this.jobber?.busy && this.jobber.destroy)
4990
- await this.jobber.destroy();
4991
- // Build chain and resolve in parallel under jobber control
4992
- const run = async () => {
4993
- const chain = await this.#buildBlockChain(hash);
4994
- if (chain.length > 0) {
4995
- await this.#resolveBlocksInParallel(chain);
4996
- }
4997
- };
4998
- await this.jobber.add(run);
4977
+ debug$1(`Resolving blocks from hash: ${hash}`);
4978
+ await this.resolveBlock(hash);
4979
+ }
4980
+ }
4981
+ catch (error) {
4982
+ console.log(error);
4983
+ this.#chainSyncing = false;
4984
+ this.#syncState = 'errored';
4985
+ this.#resolveErrored = true;
4986
+ return this.restoreChain();
4987
+ // console.log(e);
4988
+ }
4989
+ }
4990
+ async restoreChain() {
4991
+ try {
4992
+ const { hash } = await this.#getLatestBlock();
4993
+ await globalThis.chainStore.put('lastBlock', hash);
4994
+ if (hash && hash !== '0x0') {
4995
+ await this.resolveBlock(hash);
4999
4996
  }
5000
4997
  }
5001
4998
  catch (error) {
5002
- debug$1('Failed to resolve blocks:', error);
4999
+ console.log(error);
5000
+ this.#resolveErrored = true;
5001
+ this.#resolveErrorCount += 1;
5002
+ this.#resolving = false;
5003
+ return this.restoreChain();
5004
+ // console.log(e);
5003
5005
  }
5004
5006
  }
5005
5007
  async syncChain(lastBlock) {
5006
- if (this.#syncing)
5007
- return 'syncing';
5008
+ console.log('check if can sync');
5009
+ if (!this.shouldSync)
5010
+ return;
5011
+ console.log('starting sync');
5012
+ this.#syncState = 'syncing';
5013
+ this.#chainSyncing = true;
5014
+ try {
5015
+ if (this.jobber.busy && this.jobber.destroy) {
5016
+ await this.jobber.destroy();
5017
+ }
5018
+ }
5019
+ catch (error) {
5020
+ console.error(error);
5021
+ }
5008
5022
  if (!lastBlock)
5009
5023
  lastBlock = await this.#getLatestBlock();
5010
- this.#syncing = true;
5024
+ if (globalThis.peernet.peers.length === 0)
5025
+ return 'connectionless';
5026
+ try {
5027
+ await this.#syncChain(lastBlock);
5028
+ }
5029
+ catch (error) {
5030
+ this.#syncErrorCount += 1;
5031
+ if (this.#syncErrorCount < 3)
5032
+ return this.syncChain(lastBlock);
5033
+ this.#syncErrorCount = 0;
5034
+ this.#chainSyncing = false;
5035
+ this.#syncState = 'errored';
5036
+ return this.#syncState;
5037
+ }
5038
+ if (lastBlock.index === this.#lastBlockInQue?.index)
5039
+ this.#lastBlockInQue = undefined;
5040
+ this.#syncErrorCount = 0;
5041
+ this.#chainSyncing = false;
5042
+ if (this.#lastBlockInQue)
5043
+ return this.syncChain(this.#lastBlockInQue);
5044
+ this.#syncState = 'synced';
5045
+ return this.#syncState;
5046
+ }
5047
+ async #syncChain(lastBlock) {
5011
5048
  try {
5012
- if (globalThis.peernet.peers.length === 0) {
5013
- this.#syncing = false;
5014
- return 'connectionless';
5049
+ // if (this.knownBlocks?.length === Number(lastBlock.index) + 1) {
5050
+ // let promises = []
5051
+ // promises = await Promise.allSettled(
5052
+ // this.knownBlocks.map(async (address) => {
5053
+ // const has = await globalThis.peernet.has(address)
5054
+ // return { has, address }
5055
+ // })
5056
+ // )
5057
+ // promises = promises.filter(({ status, value }) => status === 'fulfilled' && !value.has)
5058
+ // await Promise.allSettled(promises.map(({ value }) => this.getAndPutBlock(value.address)))
5059
+ // }
5060
+ const localBlock = await this.lastBlock;
5061
+ const localIndex = localBlock ? Number(localBlock.index) : -1;
5062
+ const remoteIndex = Number(lastBlock.index);
5063
+ const remoteBlockHash = lastBlock.hash;
5064
+ // Get the local state hash from chainStore
5065
+ let localStateHash = '0x0';
5066
+ try {
5067
+ localStateHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
5015
5068
  }
5016
- await this.resolveBlock(lastBlock.hash);
5017
- const blocks = this.#blocks;
5018
- const localIndex = (await this.lastBlock).index || -1;
5019
- const start = Math.max(0, localIndex + 1);
5020
- if (this.#machine && blocks.length > start) {
5021
- await this.#loadBlocks(blocks.slice(start));
5069
+ catch (error) {
5070
+ debug$1(`No local state hash found: ${error}`);
5022
5071
  }
5023
- if (blocks.length > 0) {
5024
- await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
5072
+ debug$1(`Local block height: ${localIndex}, remote block height: ${remoteIndex}`);
5073
+ debug$1(`Local state hash: ${localStateHash}, remote block hash: ${remoteBlockHash}`);
5074
+ // Skip syncing if remote block hash is 0x0 (invalid state)
5075
+ if (remoteBlockHash === '0x0') {
5076
+ debug$1(`Remote block hash is 0x0, skipping sync`);
5077
+ return;
5078
+ }
5079
+ // Use state hash comparison: only resolve if remote hash differs from local state hash
5080
+ if (localStateHash !== remoteBlockHash) {
5081
+ // Remote block hash differs from our local state, need to resolve
5082
+ debug$1(`Resolving remote block: ${remoteBlockHash} @${remoteIndex} (differs from local state)`);
5083
+ await this.resolveBlock(remoteBlockHash);
5084
+ const blocksSynced = remoteIndex - localIndex;
5085
+ debug$1(`Resolved ${blocksSynced} new block(s)`);
5086
+ const blocks = this.#blocks;
5087
+ debug$1(`Loading blocks from index ${localIndex + 1} to ${remoteIndex}`);
5088
+ const start = localIndex + 1;
5089
+ if (this.#machine && blocks.length > start) {
5090
+ await this.#loadBlocks(blocks.slice(start));
5091
+ }
5092
+ // Update state with the latest block
5093
+ if (blocks.length > 0) {
5094
+ await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
5095
+ }
5096
+ }
5097
+ else {
5098
+ debug$1(`Block already in local state. Remote hash: ${remoteBlockHash} matches local state`);
5025
5099
  }
5026
- this.#syncErrorCount = 0;
5027
- this.#syncing = false;
5028
- return 'synced';
5029
5100
  }
5030
5101
  catch (error) {
5031
- this.#syncErrorCount++;
5032
- if (this.#syncErrorCount < 3) {
5033
- this.#syncing = false;
5034
- return this.syncChain(lastBlock);
5035
- }
5036
- this.#syncErrorCount = 0;
5037
- this.#syncing = false;
5038
- return 'errored';
5102
+ console.log(error);
5103
+ throw error;
5039
5104
  }
5040
5105
  }
5041
5106
  async #getLatestBlock() {
@@ -5071,9 +5136,7 @@ class State extends Contract {
5071
5136
  debug$1(`Latest block from peers: ${latest.hash} @${latest.index}`);
5072
5137
  if (latest.hash && latest.hash !== '0x0') {
5073
5138
  let message = await globalThis.peernet.get(latest.hash, 'block');
5074
- debug$1({ message });
5075
5139
  message = await new BlockMessage(message);
5076
- debug$1({ message });
5077
5140
  const hash = await message.hash();
5078
5141
  if (hash !== latest.hash)
5079
5142
  throw new Error('invalid block @getLatestBlock');
@@ -5086,7 +5149,7 @@ class State extends Contract {
5086
5149
  let node = await globalThis.peernet.prepareMessage(data);
5087
5150
  let message = await peer.request(node.encode());
5088
5151
  message = await new globalThis.peernet.protos['peernet-response'](message);
5089
- this.wantList.push(...message.decoded.response);
5152
+ this.wantList.push(...message.decoded.response.blocks.filter((block) => !this.knownBlocks.includes(block)));
5090
5153
  }
5091
5154
  }
5092
5155
  return latest;
@@ -5206,13 +5269,26 @@ class State extends Contract {
5206
5269
  });
5207
5270
  }
5208
5271
  get canSync() {
5209
- return !this.#syncing;
5272
+ if (this.#chainSyncing)
5273
+ return false;
5274
+ return true;
5210
5275
  }
5211
5276
  get shouldSync() {
5212
- if (this.#syncing)
5277
+ if (this.#chainSyncing)
5213
5278
  return false;
5279
+ // Check if we have any connected peers with the same version
5214
5280
  const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
5215
- return compatiblePeers.length > 0;
5281
+ if (compatiblePeers.length === 0) {
5282
+ debug$1('No compatible peers available for sync');
5283
+ return false;
5284
+ }
5285
+ if (!this.#chainSyncing ||
5286
+ this.#resolveErrored ||
5287
+ this.#syncState === 'errored' ||
5288
+ this.#syncState === 'connectionless' ||
5289
+ this.#lastResolvedTime + this.resolveTimeout > Date.now())
5290
+ return true;
5291
+ return false;
5216
5292
  }
5217
5293
  async #waitForPeers(timeoutMs = 30000) {
5218
5294
  return new Promise((resolve) => {