@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.
- package/exports/browser/{browser-Qcpp3EKK-DOtgsScX.js → browser-BHbuEZJu-DB_cOj5W.js} +1 -1
- package/exports/browser/{browser-CfYI-6aD-DHRKebpJ.js → browser-DQlwTLRn-h2IyYbfg.js} +1 -1
- package/exports/browser/chain.js +302 -226
- package/exports/browser/{client-q0JXZnpu-ChkF_Pn_.js → client-DCeU_UX5-CR7iKpxy.js} +226 -32
- package/exports/browser/{identity-nIyW_Xm8-BU8xakCv.js → identity-B6BHwSTU-5rsAWMHo.js} +2 -2
- package/exports/browser/{index-DTbjK0sK-BK_5FT46.js → index-DYdP5D9L-D_bByMvp.js} +1 -1
- package/exports/browser/{messages-CC7UR5ol-BvFNyHjv.js → messages-CiR1YiV5-BOjOxVgq.js} +2 -2
- package/exports/browser/{node-browser-SPIwS-5O.js → node-browser-Bv-OkGUJ.js} +25 -36
- package/exports/browser/node-browser.js +2 -2
- package/exports/chain.js +279 -207
- package/exports/state.d.ts +17 -9
- package/package.json +4 -4
- package/exports/browser/index-ChRjMyiM-EjbBu23l.js +0 -36
package/exports/browser/chain.js
CHANGED
|
@@ -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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
22
|
+
getLogger(trace)('\x1b[34m\x1b[1m%s', `${target}: ${text}`, '\x1b[0m');
|
|
21
23
|
else
|
|
22
|
-
|
|
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.
|
|
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.
|
|
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
|
|
4658
|
-
return this.#
|
|
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:
|
|
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
|
-
|
|
4779
|
-
await globalThis.peernet.addRequestHandler('
|
|
4780
|
-
await globalThis.peernet.addRequestHandler('
|
|
4781
|
-
|
|
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
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
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
|
-
|
|
4793
|
-
|
|
4802
|
+
else {
|
|
4803
|
+
localBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
4794
4804
|
}
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
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
|
-
|
|
4800
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
4849
|
-
|
|
4850
|
-
}
|
|
4851
|
-
this.#resolvingBlocks.add(hash);
|
|
4879
|
+
let index = this.#blockHashMap.get(hash);
|
|
4880
|
+
let localHash = '0x0';
|
|
4852
4881
|
try {
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
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
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
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
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
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
|
-
|
|
4925
|
+
return;
|
|
4953
4926
|
}
|
|
4954
4927
|
async resolveBlock(hash) {
|
|
4955
|
-
if (!hash
|
|
4956
|
-
|
|
4957
|
-
if (
|
|
4928
|
+
if (!hash)
|
|
4929
|
+
throw new Error(`expected hash, got: ${hash}`);
|
|
4930
|
+
if (hash === '0x0')
|
|
4958
4931
|
return;
|
|
4959
|
-
this.#
|
|
4960
|
-
|
|
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
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
if (
|
|
4966
|
-
|
|
4967
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
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
|
-
|
|
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
|
|
5007
|
-
|
|
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
|
-
|
|
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 (
|
|
5013
|
-
|
|
5014
|
-
|
|
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
|
-
|
|
5017
|
-
|
|
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
|
-
|
|
5024
|
-
|
|
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
|
-
|
|
5032
|
-
|
|
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
|
-
|
|
5272
|
+
if (this.#chainSyncing)
|
|
5273
|
+
return false;
|
|
5274
|
+
return true;
|
|
5210
5275
|
}
|
|
5211
5276
|
get shouldSync() {
|
|
5212
|
-
if (this.#
|
|
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
|
-
|
|
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) => {
|