@leofcoin/chain 1.7.150 → 1.7.151
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 +86 -289
- package/exports/chain.js +87 -289
- package/exports/state.d.ts +7 -17
- package/package.json +1 -1
package/exports/browser/chain.js
CHANGED
|
@@ -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,32 @@ 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;
|
|
4651
|
-
#
|
|
4613
|
+
#totalSize;
|
|
4652
4614
|
/**
|
|
4653
4615
|
* contains transactions we need before we can successfully load
|
|
4654
4616
|
*/
|
|
4655
4617
|
get wantList() {
|
|
4656
4618
|
return this.#machine?.wantList ?? this._wantList;
|
|
4657
4619
|
}
|
|
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
4620
|
get loaded() {
|
|
4668
4621
|
return this.#loaded;
|
|
4669
4622
|
}
|
|
4670
|
-
get
|
|
4671
|
-
return this.#
|
|
4623
|
+
get isSyncing() {
|
|
4624
|
+
return this.#syncing;
|
|
4672
4625
|
}
|
|
4626
|
+
// Delegate to machine
|
|
4673
4627
|
get contracts() {
|
|
4674
4628
|
return this.#machine.contracts;
|
|
4675
4629
|
}
|
|
@@ -4712,38 +4666,38 @@ class State extends Contract {
|
|
|
4712
4666
|
get lastBlockHeight() {
|
|
4713
4667
|
return this.#machine ? this.#machine.lastBlockHeight : 0;
|
|
4714
4668
|
}
|
|
4715
|
-
getBlock(index) {
|
|
4716
|
-
return this.#machine.getBlock(index);
|
|
4717
|
-
}
|
|
4718
|
-
getBlocks(from, to) {
|
|
4719
|
-
return this.#machine.getBlocks(from, to);
|
|
4720
|
-
}
|
|
4721
4669
|
get totalSize() {
|
|
4722
4670
|
return this.#totalSize;
|
|
4723
4671
|
}
|
|
4724
4672
|
get machine() {
|
|
4725
4673
|
return this.#machine;
|
|
4726
4674
|
}
|
|
4675
|
+
get blockHashMap() {
|
|
4676
|
+
return this.#blockHashMap.entries();
|
|
4677
|
+
}
|
|
4678
|
+
getBlock(index) {
|
|
4679
|
+
return this.#machine.getBlock(index);
|
|
4680
|
+
}
|
|
4681
|
+
getBlocks(from, to) {
|
|
4682
|
+
return this.#machine.getBlocks(from, to);
|
|
4683
|
+
}
|
|
4727
4684
|
constructor(config) {
|
|
4728
4685
|
super(config);
|
|
4729
|
-
this.#lastResolvedTime = 0;
|
|
4730
|
-
this.#resolving = false;
|
|
4731
|
-
this.#resolveErrorCount = 0;
|
|
4732
|
-
this.#chainState = 'loading';
|
|
4733
|
-
this.#syncErrorCount = 0;
|
|
4734
4686
|
this.#blockHashMap = new Map();
|
|
4735
|
-
this.#chainSyncing = false;
|
|
4736
4687
|
this.#blocks = [];
|
|
4737
|
-
this.knownBlocks = [];
|
|
4738
|
-
this.#totalSize = 0;
|
|
4739
4688
|
this.#loaded = false;
|
|
4740
|
-
|
|
4689
|
+
// Sync state
|
|
4690
|
+
this.#syncing = false;
|
|
4691
|
+
this.#syncErrorCount = 0;
|
|
4692
|
+
// Block resolution state
|
|
4741
4693
|
this.#resolvingBlocks = new Set();
|
|
4742
4694
|
this.#maxConcurrentResolves = 10;
|
|
4743
|
-
this
|
|
4695
|
+
this.knownBlocks = [];
|
|
4696
|
+
this.#totalSize = 0;
|
|
4697
|
+
this._wantList = [];
|
|
4744
4698
|
this.#chainStateHandler = () => {
|
|
4745
4699
|
return new globalThis.peernet.protos['peernet-response']({
|
|
4746
|
-
response: this.#
|
|
4700
|
+
response: { syncing: this.#syncing, loaded: this.#loaded }
|
|
4747
4701
|
});
|
|
4748
4702
|
};
|
|
4749
4703
|
this.#lastBlockHandler = async () => {
|
|
@@ -4785,67 +4739,43 @@ class State extends Contract {
|
|
|
4785
4739
|
#lastBlockHandler;
|
|
4786
4740
|
#knownBlocksHandler;
|
|
4787
4741
|
async init() {
|
|
4788
|
-
|
|
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;
|
|
4742
|
+
// Register request handlers
|
|
4743
|
+
await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this));
|
|
4744
|
+
await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler.bind(this));
|
|
4745
|
+
await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler.bind(this));
|
|
4795
4746
|
try {
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
blockMessage = await new BlockMessage(
|
|
4747
|
+
// Load local block state
|
|
4748
|
+
let localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
4749
|
+
try {
|
|
4750
|
+
const localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
|
|
4751
|
+
if (localBlockHash && localBlockHash !== '0x0') {
|
|
4752
|
+
const blockMessage = await new BlockMessage(await globalThis.peernet.get(localBlockHash, 'block'));
|
|
4802
4753
|
localBlock = { ...blockMessage.decoded, hash: localBlockHash };
|
|
4803
4754
|
}
|
|
4804
4755
|
}
|
|
4805
|
-
|
|
4806
|
-
|
|
4756
|
+
catch (error) {
|
|
4757
|
+
debug$1('No local block found');
|
|
4807
4758
|
}
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
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
|
-
}
|
|
4759
|
+
// Load known blocks
|
|
4760
|
+
try {
|
|
4761
|
+
this.knownBlocks = await globalThis.blockStore.keys();
|
|
4831
4762
|
}
|
|
4832
|
-
|
|
4833
|
-
|
|
4763
|
+
catch (error) {
|
|
4764
|
+
debug$1('No known blocks found');
|
|
4834
4765
|
}
|
|
4766
|
+
// Initialize machine and resolve blocks if needed
|
|
4835
4767
|
this.#machine = await new Machine(this.#blocks);
|
|
4768
|
+
if (localBlock.hash !== '0x0') {
|
|
4769
|
+
await this.resolveBlock(localBlock.hash);
|
|
4770
|
+
}
|
|
4836
4771
|
const lastBlock = await this.#machine.lastBlock;
|
|
4837
4772
|
if (lastBlock.hash !== '0x0') {
|
|
4838
|
-
this.updateState(new BlockMessage(lastBlock));
|
|
4773
|
+
await this.updateState(new BlockMessage(lastBlock));
|
|
4839
4774
|
}
|
|
4840
4775
|
this.#loaded = true;
|
|
4841
|
-
// await this.#loadBlocks(this.#blocks)
|
|
4842
4776
|
}
|
|
4843
4777
|
catch (error) {
|
|
4844
|
-
console.
|
|
4845
|
-
if (isResolveError(error)) {
|
|
4846
|
-
console.error(error);
|
|
4847
|
-
}
|
|
4848
|
-
console.log(error);
|
|
4778
|
+
console.error('Failed to initialize state:', error);
|
|
4849
4779
|
}
|
|
4850
4780
|
}
|
|
4851
4781
|
async updateState(message) {
|
|
@@ -4979,196 +4909,76 @@ class State extends Contract {
|
|
|
4979
4909
|
await Promise.all(resolving);
|
|
4980
4910
|
}
|
|
4981
4911
|
async resolveBlock(hash) {
|
|
4982
|
-
if (!hash)
|
|
4983
|
-
throw new Error(`expected hash, got: ${hash}`);
|
|
4984
|
-
if (hash === '0x0')
|
|
4912
|
+
if (!hash || hash === '0x0')
|
|
4985
4913
|
return;
|
|
4986
|
-
if (this.#
|
|
4987
|
-
debug$1('Already resolving, queueing block');
|
|
4914
|
+
if (this.#syncing)
|
|
4988
4915
|
return;
|
|
4989
|
-
|
|
4990
|
-
this.#
|
|
4916
|
+
this.#syncing = true;
|
|
4917
|
+
this.#syncErrorCount = 0;
|
|
4991
4918
|
try {
|
|
4992
|
-
// Build the entire block chain from latest to genesis
|
|
4993
4919
|
debug$1(`Building block chain from ${hash}`);
|
|
4994
4920
|
const blockChain = await this.#buildBlockChain(hash);
|
|
4995
4921
|
debug$1(`Built chain of ${blockChain.length} blocks`);
|
|
4996
|
-
// Resolve all blocks in parallel
|
|
4997
4922
|
if (blockChain.length > 0) {
|
|
4998
4923
|
await this.#resolveBlocksInParallel(blockChain);
|
|
4999
4924
|
}
|
|
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
4925
|
}
|
|
5014
4926
|
catch (error) {
|
|
5015
|
-
console.
|
|
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;
|
|
4927
|
+
console.error('Block resolution failed:', error);
|
|
5023
4928
|
this.wantList.push(hash);
|
|
5024
|
-
|
|
4929
|
+
}
|
|
4930
|
+
finally {
|
|
4931
|
+
this.#syncing = false;
|
|
5025
4932
|
}
|
|
5026
4933
|
}
|
|
5027
4934
|
async resolveBlocks() {
|
|
5028
|
-
|
|
5029
|
-
if (this.#chainSyncing || this.#resolving) {
|
|
5030
|
-
debug$1('Already syncing or resolving, skipping resolveBlocks()');
|
|
4935
|
+
if (this.#syncing)
|
|
5031
4936
|
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
4937
|
try {
|
|
5042
4938
|
const localBlock = await globalThis.chainStore.get('lastBlock');
|
|
5043
4939
|
const hash = new TextDecoder().decode(localBlock);
|
|
5044
|
-
if (hash && hash !== '0x0') {
|
|
5045
|
-
debug$1(`Resolving blocks from hash: ${hash}`);
|
|
5046
|
-
await this.resolveBlock(hash);
|
|
5047
|
-
}
|
|
5048
|
-
}
|
|
5049
|
-
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
4940
|
if (hash && hash !== '0x0') {
|
|
5063
4941
|
await this.resolveBlock(hash);
|
|
5064
4942
|
}
|
|
5065
4943
|
}
|
|
5066
4944
|
catch (error) {
|
|
5067
|
-
|
|
5068
|
-
this.#resolveErrored = true;
|
|
5069
|
-
this.#resolveErrorCount += 1;
|
|
5070
|
-
this.#resolving = false;
|
|
5071
|
-
return this.restoreChain();
|
|
5072
|
-
// console.log(e);
|
|
4945
|
+
debug$1('Failed to resolve blocks:', error);
|
|
5073
4946
|
}
|
|
5074
4947
|
}
|
|
5075
4948
|
async syncChain(lastBlock) {
|
|
5076
|
-
|
|
5077
|
-
|
|
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
|
-
}
|
|
4949
|
+
if (this.#syncing)
|
|
4950
|
+
return 'syncing';
|
|
5090
4951
|
if (!lastBlock)
|
|
5091
4952
|
lastBlock = await this.#getLatestBlock();
|
|
5092
|
-
|
|
5093
|
-
return 'connectionless';
|
|
4953
|
+
this.#syncing = true;
|
|
5094
4954
|
try {
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
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'));
|
|
4955
|
+
if (globalThis.peernet.peers.length === 0) {
|
|
4956
|
+
this.#syncing = false;
|
|
4957
|
+
return 'connectionless';
|
|
5136
4958
|
}
|
|
5137
|
-
|
|
5138
|
-
|
|
4959
|
+
await this.resolveBlock(lastBlock.hash);
|
|
4960
|
+
const blocks = this.#blocks;
|
|
4961
|
+
const localIndex = (await this.lastBlock).index || -1;
|
|
4962
|
+
const start = Math.max(0, localIndex + 1);
|
|
4963
|
+
if (this.#machine && blocks.length > start) {
|
|
4964
|
+
await this.#loadBlocks(blocks.slice(start));
|
|
5139
4965
|
}
|
|
5140
|
-
|
|
5141
|
-
|
|
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;
|
|
5146
|
-
}
|
|
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`);
|
|
4966
|
+
if (blocks.length > 0) {
|
|
4967
|
+
await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
|
|
5167
4968
|
}
|
|
4969
|
+
this.#syncErrorCount = 0;
|
|
4970
|
+
this.#syncing = false;
|
|
4971
|
+
return 'synced';
|
|
5168
4972
|
}
|
|
5169
4973
|
catch (error) {
|
|
5170
|
-
|
|
5171
|
-
|
|
4974
|
+
this.#syncErrorCount++;
|
|
4975
|
+
if (this.#syncErrorCount < 3) {
|
|
4976
|
+
this.#syncing = false;
|
|
4977
|
+
return this.syncChain(lastBlock);
|
|
4978
|
+
}
|
|
4979
|
+
this.#syncErrorCount = 0;
|
|
4980
|
+
this.#syncing = false;
|
|
4981
|
+
return 'errored';
|
|
5172
4982
|
}
|
|
5173
4983
|
}
|
|
5174
4984
|
async #getLatestBlock() {
|
|
@@ -5339,26 +5149,13 @@ class State extends Contract {
|
|
|
5339
5149
|
});
|
|
5340
5150
|
}
|
|
5341
5151
|
get canSync() {
|
|
5342
|
-
|
|
5343
|
-
return false;
|
|
5344
|
-
return true;
|
|
5152
|
+
return !this.#syncing;
|
|
5345
5153
|
}
|
|
5346
5154
|
get shouldSync() {
|
|
5347
|
-
if (this.#
|
|
5155
|
+
if (this.#syncing)
|
|
5348
5156
|
return false;
|
|
5349
|
-
// Check if we have any connected peers with the same version
|
|
5350
5157
|
const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
|
|
5351
|
-
|
|
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;
|
|
5158
|
+
return compatiblePeers.length > 0;
|
|
5362
5159
|
}
|
|
5363
5160
|
async #waitForPeers(timeoutMs = 30000) {
|
|
5364
5161
|
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,
|
|
9
|
+
import { ContractDeploymentError, ExecutionError, ResolveError, isExecutionError } from '@leofcoin/errors';
|
|
10
10
|
|
|
11
11
|
const limit = 1800;
|
|
12
12
|
const transactionLimit = 1000;
|
|
@@ -739,77 +739,32 @@ 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;
|
|
791
|
-
#
|
|
754
|
+
#totalSize;
|
|
792
755
|
/**
|
|
793
756
|
* contains transactions we need before we can successfully load
|
|
794
757
|
*/
|
|
795
758
|
get wantList() {
|
|
796
759
|
return this.#machine?.wantList ?? this._wantList;
|
|
797
760
|
}
|
|
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
761
|
get loaded() {
|
|
808
762
|
return this.#loaded;
|
|
809
763
|
}
|
|
810
|
-
get
|
|
811
|
-
return this.#
|
|
764
|
+
get isSyncing() {
|
|
765
|
+
return this.#syncing;
|
|
812
766
|
}
|
|
767
|
+
// Delegate to machine
|
|
813
768
|
get contracts() {
|
|
814
769
|
return this.#machine.contracts;
|
|
815
770
|
}
|
|
@@ -852,38 +807,38 @@ class State extends Contract {
|
|
|
852
807
|
get lastBlockHeight() {
|
|
853
808
|
return this.#machine ? this.#machine.lastBlockHeight : 0;
|
|
854
809
|
}
|
|
855
|
-
getBlock(index) {
|
|
856
|
-
return this.#machine.getBlock(index);
|
|
857
|
-
}
|
|
858
|
-
getBlocks(from, to) {
|
|
859
|
-
return this.#machine.getBlocks(from, to);
|
|
860
|
-
}
|
|
861
810
|
get totalSize() {
|
|
862
811
|
return this.#totalSize;
|
|
863
812
|
}
|
|
864
813
|
get machine() {
|
|
865
814
|
return this.#machine;
|
|
866
815
|
}
|
|
816
|
+
get blockHashMap() {
|
|
817
|
+
return this.#blockHashMap.entries();
|
|
818
|
+
}
|
|
819
|
+
getBlock(index) {
|
|
820
|
+
return this.#machine.getBlock(index);
|
|
821
|
+
}
|
|
822
|
+
getBlocks(from, to) {
|
|
823
|
+
return this.#machine.getBlocks(from, to);
|
|
824
|
+
}
|
|
867
825
|
constructor(config) {
|
|
868
826
|
super(config);
|
|
869
|
-
this.#lastResolvedTime = 0;
|
|
870
|
-
this.#resolving = false;
|
|
871
|
-
this.#resolveErrorCount = 0;
|
|
872
|
-
this.#chainState = 'loading';
|
|
873
|
-
this.#syncErrorCount = 0;
|
|
874
827
|
this.#blockHashMap = new Map();
|
|
875
|
-
this.#chainSyncing = false;
|
|
876
828
|
this.#blocks = [];
|
|
877
|
-
this.knownBlocks = [];
|
|
878
|
-
this.#totalSize = 0;
|
|
879
829
|
this.#loaded = false;
|
|
880
|
-
|
|
830
|
+
// Sync state
|
|
831
|
+
this.#syncing = false;
|
|
832
|
+
this.#syncErrorCount = 0;
|
|
833
|
+
// Block resolution state
|
|
881
834
|
this.#resolvingBlocks = new Set();
|
|
882
835
|
this.#maxConcurrentResolves = 10;
|
|
883
|
-
this
|
|
836
|
+
this.knownBlocks = [];
|
|
837
|
+
this.#totalSize = 0;
|
|
838
|
+
this._wantList = [];
|
|
884
839
|
this.#chainStateHandler = () => {
|
|
885
840
|
return new globalThis.peernet.protos['peernet-response']({
|
|
886
|
-
response: this.#
|
|
841
|
+
response: { syncing: this.#syncing, loaded: this.#loaded }
|
|
887
842
|
});
|
|
888
843
|
};
|
|
889
844
|
this.#lastBlockHandler = async () => {
|
|
@@ -925,67 +880,43 @@ class State extends Contract {
|
|
|
925
880
|
#lastBlockHandler;
|
|
926
881
|
#knownBlocksHandler;
|
|
927
882
|
async init() {
|
|
928
|
-
|
|
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;
|
|
883
|
+
// Register request handlers
|
|
884
|
+
await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this));
|
|
885
|
+
await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler.bind(this));
|
|
886
|
+
await globalThis.peernet.addRequestHandler('chainState', this.#chainStateHandler.bind(this));
|
|
935
887
|
try {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
blockMessage = await new BlockMessage(
|
|
888
|
+
// Load local block state
|
|
889
|
+
let localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
890
|
+
try {
|
|
891
|
+
const localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get('lastBlock'));
|
|
892
|
+
if (localBlockHash && localBlockHash !== '0x0') {
|
|
893
|
+
const blockMessage = await new BlockMessage(await globalThis.peernet.get(localBlockHash, 'block'));
|
|
942
894
|
localBlock = { ...blockMessage.decoded, hash: localBlockHash };
|
|
943
895
|
}
|
|
944
896
|
}
|
|
945
|
-
|
|
946
|
-
|
|
897
|
+
catch (error) {
|
|
898
|
+
debug$1('No local block found');
|
|
947
899
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
-
}
|
|
900
|
+
// Load known blocks
|
|
901
|
+
try {
|
|
902
|
+
this.knownBlocks = await globalThis.blockStore.keys();
|
|
971
903
|
}
|
|
972
|
-
|
|
973
|
-
|
|
904
|
+
catch (error) {
|
|
905
|
+
debug$1('No known blocks found');
|
|
974
906
|
}
|
|
907
|
+
// Initialize machine and resolve blocks if needed
|
|
975
908
|
this.#machine = await new Machine(this.#blocks);
|
|
909
|
+
if (localBlock.hash !== '0x0') {
|
|
910
|
+
await this.resolveBlock(localBlock.hash);
|
|
911
|
+
}
|
|
976
912
|
const lastBlock = await this.#machine.lastBlock;
|
|
977
913
|
if (lastBlock.hash !== '0x0') {
|
|
978
|
-
this.updateState(new BlockMessage(lastBlock));
|
|
914
|
+
await this.updateState(new BlockMessage(lastBlock));
|
|
979
915
|
}
|
|
980
916
|
this.#loaded = true;
|
|
981
|
-
// await this.#loadBlocks(this.#blocks)
|
|
982
917
|
}
|
|
983
918
|
catch (error) {
|
|
984
|
-
console.
|
|
985
|
-
if (isResolveError(error)) {
|
|
986
|
-
console.error(error);
|
|
987
|
-
}
|
|
988
|
-
console.log(error);
|
|
919
|
+
console.error('Failed to initialize state:', error);
|
|
989
920
|
}
|
|
990
921
|
}
|
|
991
922
|
async updateState(message) {
|
|
@@ -1119,196 +1050,76 @@ class State extends Contract {
|
|
|
1119
1050
|
await Promise.all(resolving);
|
|
1120
1051
|
}
|
|
1121
1052
|
async resolveBlock(hash) {
|
|
1122
|
-
if (!hash)
|
|
1123
|
-
throw new Error(`expected hash, got: ${hash}`);
|
|
1124
|
-
if (hash === '0x0')
|
|
1053
|
+
if (!hash || hash === '0x0')
|
|
1125
1054
|
return;
|
|
1126
|
-
if (this.#
|
|
1127
|
-
debug$1('Already resolving, queueing block');
|
|
1055
|
+
if (this.#syncing)
|
|
1128
1056
|
return;
|
|
1129
|
-
|
|
1130
|
-
this.#
|
|
1057
|
+
this.#syncing = true;
|
|
1058
|
+
this.#syncErrorCount = 0;
|
|
1131
1059
|
try {
|
|
1132
|
-
// Build the entire block chain from latest to genesis
|
|
1133
1060
|
debug$1(`Building block chain from ${hash}`);
|
|
1134
1061
|
const blockChain = await this.#buildBlockChain(hash);
|
|
1135
1062
|
debug$1(`Built chain of ${blockChain.length} blocks`);
|
|
1136
|
-
// Resolve all blocks in parallel
|
|
1137
1063
|
if (blockChain.length > 0) {
|
|
1138
1064
|
await this.#resolveBlocksInParallel(blockChain);
|
|
1139
1065
|
}
|
|
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
1066
|
}
|
|
1154
1067
|
catch (error) {
|
|
1155
|
-
console.
|
|
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;
|
|
1068
|
+
console.error('Block resolution failed:', error);
|
|
1163
1069
|
this.wantList.push(hash);
|
|
1164
|
-
|
|
1070
|
+
}
|
|
1071
|
+
finally {
|
|
1072
|
+
this.#syncing = false;
|
|
1165
1073
|
}
|
|
1166
1074
|
}
|
|
1167
1075
|
async resolveBlocks() {
|
|
1168
|
-
|
|
1169
|
-
if (this.#chainSyncing || this.#resolving) {
|
|
1170
|
-
debug$1('Already syncing or resolving, skipping resolveBlocks()');
|
|
1076
|
+
if (this.#syncing)
|
|
1171
1077
|
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
1078
|
try {
|
|
1182
1079
|
const localBlock = await globalThis.chainStore.get('lastBlock');
|
|
1183
1080
|
const hash = new TextDecoder().decode(localBlock);
|
|
1184
|
-
if (hash && hash !== '0x0') {
|
|
1185
|
-
debug$1(`Resolving blocks from hash: ${hash}`);
|
|
1186
|
-
await this.resolveBlock(hash);
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
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
1081
|
if (hash && hash !== '0x0') {
|
|
1203
1082
|
await this.resolveBlock(hash);
|
|
1204
1083
|
}
|
|
1205
1084
|
}
|
|
1206
1085
|
catch (error) {
|
|
1207
|
-
|
|
1208
|
-
this.#resolveErrored = true;
|
|
1209
|
-
this.#resolveErrorCount += 1;
|
|
1210
|
-
this.#resolving = false;
|
|
1211
|
-
return this.restoreChain();
|
|
1212
|
-
// console.log(e);
|
|
1086
|
+
debug$1('Failed to resolve blocks:', error);
|
|
1213
1087
|
}
|
|
1214
1088
|
}
|
|
1215
1089
|
async syncChain(lastBlock) {
|
|
1216
|
-
|
|
1217
|
-
|
|
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
|
-
}
|
|
1090
|
+
if (this.#syncing)
|
|
1091
|
+
return 'syncing';
|
|
1230
1092
|
if (!lastBlock)
|
|
1231
1093
|
lastBlock = await this.#getLatestBlock();
|
|
1232
|
-
|
|
1233
|
-
return 'connectionless';
|
|
1094
|
+
this.#syncing = true;
|
|
1234
1095
|
try {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
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'));
|
|
1096
|
+
if (globalThis.peernet.peers.length === 0) {
|
|
1097
|
+
this.#syncing = false;
|
|
1098
|
+
return 'connectionless';
|
|
1276
1099
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1100
|
+
await this.resolveBlock(lastBlock.hash);
|
|
1101
|
+
const blocks = this.#blocks;
|
|
1102
|
+
const localIndex = (await this.lastBlock).index || -1;
|
|
1103
|
+
const start = Math.max(0, localIndex + 1);
|
|
1104
|
+
if (this.#machine && blocks.length > start) {
|
|
1105
|
+
await this.#loadBlocks(blocks.slice(start));
|
|
1279
1106
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
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;
|
|
1286
|
-
}
|
|
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`);
|
|
1107
|
+
if (blocks.length > 0) {
|
|
1108
|
+
await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
|
|
1307
1109
|
}
|
|
1110
|
+
this.#syncErrorCount = 0;
|
|
1111
|
+
this.#syncing = false;
|
|
1112
|
+
return 'synced';
|
|
1308
1113
|
}
|
|
1309
1114
|
catch (error) {
|
|
1310
|
-
|
|
1311
|
-
|
|
1115
|
+
this.#syncErrorCount++;
|
|
1116
|
+
if (this.#syncErrorCount < 3) {
|
|
1117
|
+
this.#syncing = false;
|
|
1118
|
+
return this.syncChain(lastBlock);
|
|
1119
|
+
}
|
|
1120
|
+
this.#syncErrorCount = 0;
|
|
1121
|
+
this.#syncing = false;
|
|
1122
|
+
return 'errored';
|
|
1312
1123
|
}
|
|
1313
1124
|
}
|
|
1314
1125
|
async #getLatestBlock() {
|
|
@@ -1479,26 +1290,13 @@ class State extends Contract {
|
|
|
1479
1290
|
});
|
|
1480
1291
|
}
|
|
1481
1292
|
get canSync() {
|
|
1482
|
-
|
|
1483
|
-
return false;
|
|
1484
|
-
return true;
|
|
1293
|
+
return !this.#syncing;
|
|
1485
1294
|
}
|
|
1486
1295
|
get shouldSync() {
|
|
1487
|
-
if (this.#
|
|
1296
|
+
if (this.#syncing)
|
|
1488
1297
|
return false;
|
|
1489
|
-
// Check if we have any connected peers with the same version
|
|
1490
1298
|
const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
|
|
1491
|
-
|
|
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;
|
|
1299
|
+
return compatiblePeers.length > 0;
|
|
1502
1300
|
}
|
|
1503
1301
|
async #waitForPeers(timeoutMs = 30000) {
|
|
1504
1302
|
return new Promise((resolve) => {
|
package/exports/state.d.ts
CHANGED
|
@@ -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
|
|
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<
|
|
58
|
-
|
|
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<
|
|
54
|
+
triggerSync(): Promise<"syncing" | "synced" | "errored" | "connectionless">;
|
|
64
55
|
triggerLoad(): Promise<void>;
|
|
65
56
|
}
|
|
66
|
-
export {};
|