@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.
- package/exports/browser/chain.js +89 -288
- package/exports/chain.js +90 -288
- 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,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
|
|
4671
|
-
return this.#
|
|
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
|
-
|
|
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
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
blockMessage = await new 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
|
-
|
|
4806
|
-
|
|
4760
|
+
catch (error) {
|
|
4761
|
+
debug$1('No local block found');
|
|
4807
4762
|
}
|
|
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
|
-
}
|
|
4763
|
+
// Load known blocks
|
|
4764
|
+
try {
|
|
4765
|
+
this.knownBlocks = await globalThis.blockStore.keys();
|
|
4831
4766
|
}
|
|
4832
|
-
|
|
4833
|
-
|
|
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.
|
|
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.#
|
|
4987
|
-
debug$1('Already resolving, queueing block');
|
|
4918
|
+
if (this.#syncing)
|
|
4988
4919
|
return;
|
|
4989
|
-
|
|
4990
|
-
this.#
|
|
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.
|
|
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
|
-
|
|
4933
|
+
}
|
|
4934
|
+
finally {
|
|
4935
|
+
this.#syncing = false;
|
|
5025
4936
|
}
|
|
5026
4937
|
}
|
|
5027
4938
|
async resolveBlocks() {
|
|
5028
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
4953
|
+
if (this.#syncing)
|
|
4954
|
+
return 'syncing';
|
|
5090
4955
|
if (!lastBlock)
|
|
5091
4956
|
lastBlock = await this.#getLatestBlock();
|
|
5092
|
-
|
|
5093
|
-
return 'connectionless';
|
|
4957
|
+
this.#syncing = true;
|
|
5094
4958
|
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'));
|
|
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
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
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
|
-
|
|
5148
|
-
|
|
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
|
-
|
|
5171
|
-
|
|
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
|
-
|
|
5343
|
-
return false;
|
|
5344
|
-
return true;
|
|
5156
|
+
return !this.#syncing;
|
|
5345
5157
|
}
|
|
5346
5158
|
get shouldSync() {
|
|
5347
|
-
if (this.#
|
|
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
|
-
|
|
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,
|
|
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
|
|
811
|
-
return this.#
|
|
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
|
-
|
|
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
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
blockMessage = await new 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
|
-
|
|
946
|
-
|
|
901
|
+
catch (error) {
|
|
902
|
+
debug$1('No local block found');
|
|
947
903
|
}
|
|
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
|
-
}
|
|
904
|
+
// Load known blocks
|
|
905
|
+
try {
|
|
906
|
+
this.knownBlocks = await globalThis.blockStore.keys();
|
|
971
907
|
}
|
|
972
|
-
|
|
973
|
-
|
|
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.
|
|
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.#
|
|
1127
|
-
debug$1('Already resolving, queueing block');
|
|
1059
|
+
if (this.#syncing)
|
|
1128
1060
|
return;
|
|
1129
|
-
|
|
1130
|
-
this.#
|
|
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.
|
|
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
|
-
|
|
1074
|
+
}
|
|
1075
|
+
finally {
|
|
1076
|
+
this.#syncing = false;
|
|
1165
1077
|
}
|
|
1166
1078
|
}
|
|
1167
1079
|
async resolveBlocks() {
|
|
1168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
1094
|
+
if (this.#syncing)
|
|
1095
|
+
return 'syncing';
|
|
1230
1096
|
if (!lastBlock)
|
|
1231
1097
|
lastBlock = await this.#getLatestBlock();
|
|
1232
|
-
|
|
1233
|
-
return 'connectionless';
|
|
1098
|
+
this.#syncing = true;
|
|
1234
1099
|
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'));
|
|
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
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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
|
-
|
|
1288
|
-
|
|
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
|
-
|
|
1311
|
-
|
|
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
|
-
|
|
1483
|
-
return false;
|
|
1484
|
-
return true;
|
|
1297
|
+
return !this.#syncing;
|
|
1485
1298
|
}
|
|
1486
1299
|
get shouldSync() {
|
|
1487
|
-
if (this.#
|
|
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
|
-
|
|
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) => {
|
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 {};
|