@leofcoin/chain 1.9.18 → 1.9.20
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 +4931 -4878
- package/exports/chain.js +1724 -1776
- package/package.json +1 -1
package/exports/chain.js
CHANGED
|
@@ -1,89 +1,64 @@
|
|
|
1
|
-
import { createDebugger } from '@vandeurenglenn/debug'
|
|
2
|
-
import { jsonStringifyBigInt, jsonParseBigInt, formatBytes, parseUnits, formatUnits } from '@leofcoin/utils'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
StateMessage,
|
|
13
|
-
BWRequestMessage
|
|
14
|
-
} from '@leofcoin/messages'
|
|
15
|
-
import addresses, { contractFactory } from '@leofcoin/addresses'
|
|
16
|
-
import {
|
|
17
|
-
calculateFee,
|
|
18
|
-
createContractMessage,
|
|
19
|
-
signTransaction,
|
|
20
|
-
contractFactoryMessage,
|
|
21
|
-
nativeTokenMessage,
|
|
22
|
-
validatorsMessage,
|
|
23
|
-
nameServiceMessage
|
|
24
|
-
} from '@leofcoin/lib'
|
|
25
|
-
import semver from 'semver'
|
|
26
|
-
import { randombytes } from '@leofcoin/crypto'
|
|
27
|
-
import EasyWorker from '@vandeurenglenn/easy-worker'
|
|
28
|
-
import {
|
|
29
|
-
ContractDeploymentError,
|
|
30
|
-
ExecutionError,
|
|
31
|
-
isResolveError,
|
|
32
|
-
ResolveError,
|
|
33
|
-
isExecutionError
|
|
34
|
-
} from '@leofcoin/errors'
|
|
35
|
-
import { P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO } from './constants-C83ZCYKa.js'
|
|
36
|
-
import '@leofcoin/networks'
|
|
1
|
+
import { createDebugger } from '@vandeurenglenn/debug';
|
|
2
|
+
import { jsonStringifyBigInt, jsonParseBigInt, formatBytes, parseUnits, formatUnits } from '@leofcoin/utils';
|
|
3
|
+
import { TransactionMessage, BlockMessage, ContractMessage, LastBlockMessage, PrevoteMessage, PrecommitMessage, ProposalMessage, BWMessage, StateMessage, BWRequestMessage } from '@leofcoin/messages';
|
|
4
|
+
import addresses, { contractFactory } from '@leofcoin/addresses';
|
|
5
|
+
import { calculateFee, createContractMessage, signTransaction, contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage } from '@leofcoin/lib';
|
|
6
|
+
import semver from 'semver';
|
|
7
|
+
import { randombytes } from '@leofcoin/crypto';
|
|
8
|
+
import EasyWorker from '@vandeurenglenn/easy-worker';
|
|
9
|
+
import { ContractDeploymentError, ExecutionError, isResolveError, ResolveError, isExecutionError } from '@leofcoin/errors';
|
|
10
|
+
import { P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO } from './constants-C83ZCYKa.js';
|
|
11
|
+
import '@leofcoin/networks';
|
|
37
12
|
|
|
38
|
-
const limit = 1800
|
|
39
|
-
const transactionLimit = 2500
|
|
40
|
-
const requestTimeout = 3e4
|
|
41
|
-
const syncTimeout = 3e4
|
|
13
|
+
const limit = 1800;
|
|
14
|
+
const transactionLimit = 2500;
|
|
15
|
+
const requestTimeout = 3e4;
|
|
16
|
+
const syncTimeout = 3e4;
|
|
42
17
|
class Protocol {
|
|
43
18
|
constructor(config) {
|
|
44
|
-
this.resolveTimeout = 1e4
|
|
45
|
-
if (config?.resolveTimeout) this.resolveTimeout = config.resolveTimeout
|
|
19
|
+
this.resolveTimeout = 1e4;
|
|
20
|
+
if (config?.resolveTimeout) this.resolveTimeout = config.resolveTimeout;
|
|
46
21
|
}
|
|
47
22
|
get limit() {
|
|
48
|
-
return limit
|
|
23
|
+
return limit;
|
|
49
24
|
}
|
|
50
25
|
get transactionLimit() {
|
|
51
|
-
return transactionLimit
|
|
26
|
+
return transactionLimit;
|
|
52
27
|
}
|
|
53
28
|
get requestTimeout() {
|
|
54
|
-
return requestTimeout
|
|
29
|
+
return requestTimeout;
|
|
55
30
|
}
|
|
56
31
|
get syncTimeout() {
|
|
57
|
-
return syncTimeout
|
|
32
|
+
return syncTimeout;
|
|
58
33
|
}
|
|
59
34
|
}
|
|
60
35
|
|
|
61
36
|
class Transaction extends Protocol {
|
|
62
|
-
#pendingNonces = /* @__PURE__ */ new Map()
|
|
63
|
-
#maxPendingNonce = /* @__PURE__ */ new Map()
|
|
37
|
+
#pendingNonces = /* @__PURE__ */ new Map();
|
|
38
|
+
#maxPendingNonce = /* @__PURE__ */ new Map();
|
|
64
39
|
constructor(config) {
|
|
65
|
-
super(config)
|
|
40
|
+
super(config);
|
|
66
41
|
}
|
|
67
42
|
addPendingNonce(address, nonce) {
|
|
68
43
|
if (!this.#pendingNonces.has(address)) {
|
|
69
|
-
this.#pendingNonces.set(address, /* @__PURE__ */ new Set())
|
|
44
|
+
this.#pendingNonces.set(address, /* @__PURE__ */ new Set());
|
|
70
45
|
}
|
|
71
|
-
this.#pendingNonces.get(address).add(nonce)
|
|
72
|
-
const currentMax = this.#maxPendingNonce.get(address) ?? -1
|
|
46
|
+
this.#pendingNonces.get(address).add(nonce);
|
|
47
|
+
const currentMax = this.#maxPendingNonce.get(address) ?? -1;
|
|
73
48
|
if (nonce > currentMax) {
|
|
74
|
-
this.#maxPendingNonce.set(address, nonce)
|
|
49
|
+
this.#maxPendingNonce.set(address, nonce);
|
|
75
50
|
}
|
|
76
51
|
}
|
|
77
52
|
removePendingNonce(address, nonce) {
|
|
78
53
|
if (this.#pendingNonces.has(address)) {
|
|
79
|
-
this.#pendingNonces.get(address).delete(nonce)
|
|
54
|
+
this.#pendingNonces.get(address).delete(nonce);
|
|
80
55
|
}
|
|
81
56
|
}
|
|
82
57
|
getPendingNonces(address) {
|
|
83
|
-
return this.#pendingNonces.get(address) || /* @__PURE__ */ new Set()
|
|
58
|
+
return this.#pendingNonces.get(address) || /* @__PURE__ */ new Set();
|
|
84
59
|
}
|
|
85
60
|
getMaxPendingNonce(address) {
|
|
86
|
-
return this.#maxPendingNonce.get(address) ?? -1
|
|
61
|
+
return this.#maxPendingNonce.get(address) ?? -1;
|
|
87
62
|
}
|
|
88
63
|
/**
|
|
89
64
|
*
|
|
@@ -92,23 +67,23 @@ class Transaction extends Protocol {
|
|
|
92
67
|
*/
|
|
93
68
|
async getTransactions(transactions) {
|
|
94
69
|
return new Promise(async (resolve, reject) => {
|
|
95
|
-
let size = 0
|
|
96
|
-
const _transactions = []
|
|
97
|
-
const MAX_BLOCK_TX_BYTES = 786432
|
|
70
|
+
let size = 0;
|
|
71
|
+
const _transactions = [];
|
|
72
|
+
const MAX_BLOCK_TX_BYTES = 786432;
|
|
98
73
|
await Promise.all(
|
|
99
74
|
transactions.map(async (tx) => {
|
|
100
|
-
tx = await new TransactionMessage(tx)
|
|
101
|
-
const newSize = size + tx.encoded.length
|
|
75
|
+
tx = await new TransactionMessage(tx);
|
|
76
|
+
const newSize = size + tx.encoded.length;
|
|
102
77
|
if (newSize <= MAX_BLOCK_TX_BYTES) {
|
|
103
|
-
size = newSize
|
|
104
|
-
_transactions.push({ ...tx.decoded, hash: await tx.hash() })
|
|
78
|
+
size = newSize;
|
|
79
|
+
_transactions.push({ ...tx.decoded, hash: await tx.hash() });
|
|
105
80
|
} else {
|
|
106
|
-
resolve(_transactions)
|
|
81
|
+
resolve(_transactions);
|
|
107
82
|
}
|
|
108
83
|
})
|
|
109
|
-
)
|
|
110
|
-
return resolve(_transactions)
|
|
111
|
-
})
|
|
84
|
+
);
|
|
85
|
+
return resolve(_transactions);
|
|
86
|
+
});
|
|
112
87
|
}
|
|
113
88
|
/**
|
|
114
89
|
*
|
|
@@ -116,8 +91,8 @@ class Transaction extends Protocol {
|
|
|
116
91
|
* @returns {TransactionMessage}
|
|
117
92
|
*/
|
|
118
93
|
async promiseTransactions(transactions) {
|
|
119
|
-
transactions = await Promise.all(transactions.map((tx) => new TransactionMessage(tx.encoded || tx)))
|
|
120
|
-
return transactions
|
|
94
|
+
transactions = await Promise.all(transactions.map((tx) => new TransactionMessage(tx.encoded || tx)));
|
|
95
|
+
return transactions;
|
|
121
96
|
}
|
|
122
97
|
/**
|
|
123
98
|
*
|
|
@@ -127,13 +102,12 @@ class Transaction extends Protocol {
|
|
|
127
102
|
async promiseTransactionsContent(transactions) {
|
|
128
103
|
transactions = await Promise.all(
|
|
129
104
|
transactions.map(
|
|
130
|
-
(tx) =>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
})
|
|
105
|
+
(tx) => new Promise(async (resolve, reject) => {
|
|
106
|
+
resolve({ ...tx.decoded, hash: await tx.hash() });
|
|
107
|
+
})
|
|
134
108
|
)
|
|
135
|
-
)
|
|
136
|
-
return transactions
|
|
109
|
+
);
|
|
110
|
+
return transactions;
|
|
137
111
|
}
|
|
138
112
|
/**
|
|
139
113
|
* When a nonce isn't found for an address fallback to just checking the transactionnPoolStore
|
|
@@ -141,32 +115,32 @@ class Transaction extends Protocol {
|
|
|
141
115
|
* @returns {Number} nonce
|
|
142
116
|
*/
|
|
143
117
|
async #getNonceFallback(address) {
|
|
144
|
-
let transactions = await globalThis.transactionPoolStore.values()
|
|
145
|
-
transactions = await this.promiseTransactions(transactions)
|
|
146
|
-
transactions = transactions.filter((tx) => tx.decoded.from === address)
|
|
147
|
-
transactions = await this.promiseTransactionsContent(transactions)
|
|
148
|
-
if (this.lastBlock?.hash && transactions.length === 0 && this.lastBlock.hash !==
|
|
149
|
-
let block
|
|
118
|
+
let transactions = await globalThis.transactionPoolStore.values();
|
|
119
|
+
transactions = await this.promiseTransactions(transactions);
|
|
120
|
+
transactions = transactions.filter((tx) => tx.decoded.from === address);
|
|
121
|
+
transactions = await this.promiseTransactionsContent(transactions);
|
|
122
|
+
if (this.lastBlock?.hash && transactions.length === 0 && this.lastBlock.hash !== "0x0") {
|
|
123
|
+
let block;
|
|
150
124
|
try {
|
|
151
|
-
block = await globalThis.peernet.get(this.lastBlock.hash,
|
|
125
|
+
block = await globalThis.peernet.get(this.lastBlock.hash, "block");
|
|
152
126
|
} catch (error) {
|
|
153
|
-
block = void 0
|
|
127
|
+
block = void 0;
|
|
154
128
|
}
|
|
155
|
-
if (block === void 0) return []
|
|
156
|
-
block = await new BlockMessage(block)
|
|
157
|
-
transactions = transactions.filter((tx) => tx.from === address)
|
|
158
|
-
while (transactions.length === 0 && block.decoded.index !== 0 && block.decoded.previousHash !==
|
|
159
|
-
block = await globalThis.blockStore.get(block.decoded.previousHash)
|
|
160
|
-
block = await new BlockMessage(block)
|
|
161
|
-
transactions = block.decoded.transactions.filter((tx) => tx.from === address)
|
|
129
|
+
if (block === void 0) return [];
|
|
130
|
+
block = await new BlockMessage(block);
|
|
131
|
+
transactions = transactions.filter((tx) => tx.from === address);
|
|
132
|
+
while (transactions.length === 0 && block.decoded.index !== 0 && block.decoded.previousHash !== "0x0") {
|
|
133
|
+
block = await globalThis.blockStore.get(block.decoded.previousHash);
|
|
134
|
+
block = await new BlockMessage(block);
|
|
135
|
+
transactions = block.decoded.transactions.filter((tx) => tx.from === address);
|
|
162
136
|
}
|
|
163
137
|
}
|
|
164
|
-
if (transactions.length === 0) return 0
|
|
165
|
-
let maxNonce = 0
|
|
138
|
+
if (transactions.length === 0) return 0;
|
|
139
|
+
let maxNonce = 0;
|
|
166
140
|
for (const tx of transactions) {
|
|
167
|
-
if (tx.nonce > maxNonce) maxNonce = tx.nonce
|
|
141
|
+
if (tx.nonce > maxNonce) maxNonce = tx.nonce;
|
|
168
142
|
}
|
|
169
|
-
return maxNonce
|
|
143
|
+
return maxNonce;
|
|
170
144
|
}
|
|
171
145
|
/**
|
|
172
146
|
* Get amount of transactions by address
|
|
@@ -175,126 +149,126 @@ class Transaction extends Protocol {
|
|
|
175
149
|
*/
|
|
176
150
|
async getNonce(address) {
|
|
177
151
|
try {
|
|
178
|
-
if (!
|
|
179
|
-
const nonce2 = await this.#getNonceFallback(address)
|
|
180
|
-
await globalThis.accountsStore.put(address, new TextEncoder().encode(String(nonce2)))
|
|
152
|
+
if (!await globalThis.accountsStore.has(address)) {
|
|
153
|
+
const nonce2 = await this.#getNonceFallback(address);
|
|
154
|
+
await globalThis.accountsStore.put(address, new TextEncoder().encode(String(nonce2)));
|
|
181
155
|
}
|
|
182
156
|
} catch (error) {
|
|
183
|
-
const nonce2 = await this.#getNonceFallback(address)
|
|
184
|
-
await globalThis.accountsStore.put(address, new TextEncoder().encode(String(nonce2)))
|
|
157
|
+
const nonce2 = await this.#getNonceFallback(address);
|
|
158
|
+
await globalThis.accountsStore.put(address, new TextEncoder().encode(String(nonce2)));
|
|
185
159
|
}
|
|
186
|
-
let nonce = await globalThis.accountsStore.get(address)
|
|
187
|
-
nonce = Number(new TextDecoder().decode(nonce))
|
|
188
|
-
const maxPending = this.getMaxPendingNonce(address)
|
|
160
|
+
let nonce = await globalThis.accountsStore.get(address);
|
|
161
|
+
nonce = Number(new TextDecoder().decode(nonce));
|
|
162
|
+
const maxPending = this.getMaxPendingNonce(address);
|
|
189
163
|
if (maxPending > nonce) {
|
|
190
|
-
return maxPending
|
|
164
|
+
return maxPending;
|
|
191
165
|
}
|
|
192
|
-
return nonce
|
|
166
|
+
return nonce;
|
|
193
167
|
}
|
|
194
168
|
async validateNonce(address, nonce) {
|
|
195
|
-
let committedNonce
|
|
169
|
+
let committedNonce;
|
|
196
170
|
try {
|
|
197
171
|
if (await globalThis.accountsStore.has(address)) {
|
|
198
|
-
const raw = await globalThis.accountsStore.get(address)
|
|
199
|
-
committedNonce = Number(new TextDecoder().decode(raw))
|
|
172
|
+
const raw = await globalThis.accountsStore.get(address);
|
|
173
|
+
committedNonce = Number(new TextDecoder().decode(raw));
|
|
200
174
|
} else {
|
|
201
|
-
committedNonce = await this.#getNonceFallback(address)
|
|
175
|
+
committedNonce = await this.#getNonceFallback(address);
|
|
202
176
|
}
|
|
203
177
|
} catch {
|
|
204
|
-
committedNonce = 0
|
|
178
|
+
committedNonce = 0;
|
|
205
179
|
}
|
|
206
|
-
if (committedNonce >= nonce) throw new Error(`a transaction with the same nonce already exists`)
|
|
207
|
-
const pendingNonces = this.getPendingNonces(address)
|
|
208
|
-
if (pendingNonces.has(nonce)) throw new Error(`a transaction with the same nonce already exists`)
|
|
180
|
+
if (committedNonce >= nonce) throw new Error(`a transaction with the same nonce already exists`);
|
|
181
|
+
const pendingNonces = this.getPendingNonces(address);
|
|
182
|
+
if (pendingNonces.has(nonce)) throw new Error(`a transaction with the same nonce already exists`);
|
|
209
183
|
}
|
|
210
184
|
isTransactionMessage(message) {
|
|
211
|
-
if (message instanceof TransactionMessage) return true
|
|
212
|
-
return false
|
|
185
|
+
if (message instanceof TransactionMessage) return true;
|
|
186
|
+
return false;
|
|
213
187
|
}
|
|
214
188
|
async createTransactionMessage(transaction, signature) {
|
|
215
|
-
return new TransactionMessage({ ...transaction, signature })
|
|
189
|
+
return new TransactionMessage({ ...transaction, signature });
|
|
216
190
|
}
|
|
217
191
|
async createTransaction(transaction) {
|
|
218
192
|
return {
|
|
219
193
|
...transaction,
|
|
220
194
|
timestamp: transaction.timestamp || Date.now(),
|
|
221
|
-
nonce: transaction.nonce ||
|
|
222
|
-
}
|
|
195
|
+
nonce: transaction.nonce || await this.getNonce(transaction.from) + 1
|
|
196
|
+
};
|
|
223
197
|
}
|
|
224
198
|
async sendTransaction(message) {
|
|
225
|
-
if (!this.isTransactionMessage(message)) message = await new TransactionMessage(message)
|
|
226
|
-
if (!message.decoded.signature) throw new Error(`transaction not signed`)
|
|
227
|
-
if (message.decoded.nonce === void 0) throw new Error(`nonce required`)
|
|
228
|
-
await this.validateNonce(message.decoded.from, message.decoded.nonce)
|
|
229
|
-
const hash = await message.hash()
|
|
199
|
+
if (!this.isTransactionMessage(message)) message = await new TransactionMessage(message);
|
|
200
|
+
if (!message.decoded.signature) throw new Error(`transaction not signed`);
|
|
201
|
+
if (message.decoded.nonce === void 0) throw new Error(`nonce required`);
|
|
202
|
+
await this.validateNonce(message.decoded.from, message.decoded.nonce);
|
|
203
|
+
const hash = await message.hash();
|
|
230
204
|
try {
|
|
231
|
-
let data
|
|
205
|
+
let data;
|
|
232
206
|
const wait = new Promise(async (resolve, reject) => {
|
|
233
207
|
if (pubsub.hasSubscribers(`transaction.completed.${hash}`)) {
|
|
234
|
-
const result = pubsub.getValue(`transaction.completed.${hash}`)
|
|
235
|
-
if (result.status !==
|
|
236
|
-
await transactionPoolStore.delete(hash)
|
|
208
|
+
const result = pubsub.getValue(`transaction.completed.${hash}`);
|
|
209
|
+
if (result.status !== "fulfilled") {
|
|
210
|
+
await transactionPoolStore.delete(hash);
|
|
237
211
|
}
|
|
238
|
-
result.status ===
|
|
212
|
+
result.status === "fulfilled" ? resolve(result.hash) : reject({ hash: result.hash, error: result.error });
|
|
239
213
|
} else {
|
|
240
214
|
const completed = async (result) => {
|
|
241
|
-
if (result.status !==
|
|
242
|
-
await transactionPoolStore.delete(hash)
|
|
215
|
+
if (result.status !== "fulfilled") {
|
|
216
|
+
await transactionPoolStore.delete(hash);
|
|
243
217
|
}
|
|
244
|
-
result.status ===
|
|
218
|
+
result.status === "fulfilled" ? resolve(result.hash) : reject({ hash: result.hash, error: result.error });
|
|
245
219
|
setTimeout(async () => {
|
|
246
|
-
pubsub.unsubscribe(`transaction.completed.${hash}`, completed)
|
|
247
|
-
}, 1e4)
|
|
248
|
-
}
|
|
249
|
-
pubsub.subscribe(`transaction.completed.${hash}`, completed)
|
|
220
|
+
pubsub.unsubscribe(`transaction.completed.${hash}`, completed);
|
|
221
|
+
}, 1e4);
|
|
222
|
+
};
|
|
223
|
+
pubsub.subscribe(`transaction.completed.${hash}`, completed);
|
|
250
224
|
}
|
|
251
|
-
})
|
|
252
|
-
await globalThis.transactionPoolStore.put(hash, message.encoded)
|
|
253
|
-
this.addPendingNonce(message.decoded.from, message.decoded.nonce)
|
|
225
|
+
});
|
|
226
|
+
await globalThis.transactionPoolStore.put(hash, message.encoded);
|
|
227
|
+
this.addPendingNonce(message.decoded.from, message.decoded.nonce);
|
|
254
228
|
try {
|
|
255
|
-
peernet.publish(
|
|
229
|
+
peernet.publish("add-transaction", message.encoded);
|
|
256
230
|
} catch (publishError) {
|
|
257
|
-
console.warn(
|
|
231
|
+
console.warn("peernet publish failed: add-transaction", publishError?.message ?? publishError);
|
|
258
232
|
}
|
|
259
|
-
const fee = await calculateFee(message.decoded)
|
|
260
|
-
return { hash, data, fee, wait, message }
|
|
233
|
+
const fee = await calculateFee(message.decoded);
|
|
234
|
+
return { hash, data, fee, wait, message };
|
|
261
235
|
} catch (error) {
|
|
262
|
-
console.log(
|
|
263
|
-
await transactionPoolStore.delete(hash)
|
|
264
|
-
this.removePendingNonce(message.decoded.from, message.decoded.nonce)
|
|
265
|
-
throw error
|
|
236
|
+
console.log("remo");
|
|
237
|
+
await transactionPoolStore.delete(hash);
|
|
238
|
+
this.removePendingNonce(message.decoded.from, message.decoded.nonce);
|
|
239
|
+
throw error;
|
|
266
240
|
}
|
|
267
241
|
}
|
|
268
242
|
}
|
|
269
243
|
|
|
270
244
|
const isDerivedFrom = (derivedClass, baseClass) => {
|
|
271
|
-
if (!derivedClass || !baseClass) return false
|
|
272
|
-
let proto = Object.getPrototypeOf(derivedClass)
|
|
245
|
+
if (!derivedClass || !baseClass) return false;
|
|
246
|
+
let proto = Object.getPrototypeOf(derivedClass);
|
|
273
247
|
while (proto) {
|
|
274
|
-
if (proto === baseClass) return true
|
|
275
|
-
proto = Object.getPrototypeOf(proto)
|
|
248
|
+
if (proto === baseClass) return true;
|
|
249
|
+
proto = Object.getPrototypeOf(proto);
|
|
276
250
|
}
|
|
277
|
-
return false
|
|
278
|
-
}
|
|
251
|
+
return false;
|
|
252
|
+
};
|
|
279
253
|
const parseContractInheritance = (contractCode) => {
|
|
280
|
-
const classMatch = contractCode.match(/class\s+(\w+)(?:\s+extends\s+(\w+))?/)
|
|
254
|
+
const classMatch = contractCode.match(/class\s+(\w+)(?:\s+extends\s+(\w+))?/);
|
|
281
255
|
if (!classMatch) {
|
|
282
|
-
return { className: null, baseClass: null, hasInheritance: false }
|
|
256
|
+
return { className: null, baseClass: null, hasInheritance: false };
|
|
283
257
|
}
|
|
284
258
|
return {
|
|
285
259
|
className: classMatch[1],
|
|
286
260
|
baseClass: classMatch[2] || null,
|
|
287
261
|
hasInheritance: !!classMatch[2]
|
|
288
|
-
}
|
|
289
|
-
}
|
|
262
|
+
};
|
|
263
|
+
};
|
|
290
264
|
|
|
291
265
|
class ContractRegistry {
|
|
292
266
|
constructor() {
|
|
293
|
-
this.baseContracts = /* @__PURE__ */ new Map()
|
|
267
|
+
this.baseContracts = /* @__PURE__ */ new Map();
|
|
294
268
|
// hash -> ContractMessage
|
|
295
|
-
this.contractNames = /* @__PURE__ */ new Map()
|
|
269
|
+
this.contractNames = /* @__PURE__ */ new Map();
|
|
296
270
|
// name -> hash
|
|
297
|
-
this.contractDependencies = /* @__PURE__ */ new Map()
|
|
271
|
+
this.contractDependencies = /* @__PURE__ */ new Map();
|
|
298
272
|
}
|
|
299
273
|
/**
|
|
300
274
|
* Register a base contract that can be reused
|
|
@@ -304,13 +278,13 @@ class ContractRegistry {
|
|
|
304
278
|
* @returns The contract hash
|
|
305
279
|
*/
|
|
306
280
|
async registerBaseContract(name, message) {
|
|
307
|
-
const hash = await message.hash()
|
|
308
|
-
this.baseContracts.set(hash, message)
|
|
309
|
-
this.contractNames.set(name, hash)
|
|
281
|
+
const hash = await message.hash();
|
|
282
|
+
this.baseContracts.set(hash, message);
|
|
283
|
+
this.contractNames.set(name, hash);
|
|
310
284
|
if (globalThis.contractStore) {
|
|
311
|
-
await globalThis.contractStore.put(hash, message.encoded)
|
|
285
|
+
await globalThis.contractStore.put(hash, message.encoded);
|
|
312
286
|
}
|
|
313
|
-
return hash
|
|
287
|
+
return hash;
|
|
314
288
|
}
|
|
315
289
|
/**
|
|
316
290
|
* Register a name for a contract hash in the nameService
|
|
@@ -320,10 +294,9 @@ class ContractRegistry {
|
|
|
320
294
|
* @param signer The wallet to sign the transaction
|
|
321
295
|
*/
|
|
322
296
|
async registerNameInNameService(name, hash, chain, signer) {
|
|
323
|
-
|
|
297
|
+
({
|
|
324
298
|
from: await signer.address,
|
|
325
|
-
to: addresses.nameService
|
|
326
|
-
})
|
|
299
|
+
to: addresses.nameService});
|
|
327
300
|
}
|
|
328
301
|
/**
|
|
329
302
|
* Get a base contract by name
|
|
@@ -332,11 +305,11 @@ class ContractRegistry {
|
|
|
332
305
|
* @returns The contract message or undefined
|
|
333
306
|
*/
|
|
334
307
|
getBaseContract(name) {
|
|
335
|
-
const hash = this.contractNames.get(name)
|
|
308
|
+
const hash = this.contractNames.get(name);
|
|
336
309
|
if (hash) {
|
|
337
|
-
return this.baseContracts.get(hash)
|
|
310
|
+
return this.baseContracts.get(hash);
|
|
338
311
|
}
|
|
339
|
-
return void 0
|
|
312
|
+
return void 0;
|
|
340
313
|
}
|
|
341
314
|
/**
|
|
342
315
|
* Get a base contract by hash
|
|
@@ -344,7 +317,7 @@ class ContractRegistry {
|
|
|
344
317
|
* @returns The contract message or undefined
|
|
345
318
|
*/
|
|
346
319
|
getBaseContractByHash(hash) {
|
|
347
|
-
return this.baseContracts.get(hash)
|
|
320
|
+
return this.baseContracts.get(hash);
|
|
348
321
|
}
|
|
349
322
|
/**
|
|
350
323
|
* Resolve a name to a hash (first local, then nameService)
|
|
@@ -352,7 +325,7 @@ class ContractRegistry {
|
|
|
352
325
|
* @returns The contract hash or undefined
|
|
353
326
|
*/
|
|
354
327
|
resolveNameToHash(name) {
|
|
355
|
-
return this.contractNames.get(name)
|
|
328
|
+
return this.contractNames.get(name);
|
|
356
329
|
}
|
|
357
330
|
/**
|
|
358
331
|
* Register contract dependencies (inheritance chain)
|
|
@@ -360,7 +333,7 @@ class ContractRegistry {
|
|
|
360
333
|
* @param dependencies Array of base contract hashes this contract depends on
|
|
361
334
|
*/
|
|
362
335
|
registerDependencies(contractHash, dependencies) {
|
|
363
|
-
this.contractDependencies.set(contractHash, dependencies)
|
|
336
|
+
this.contractDependencies.set(contractHash, dependencies);
|
|
364
337
|
}
|
|
365
338
|
/**
|
|
366
339
|
* Get all dependencies for a contract
|
|
@@ -368,7 +341,7 @@ class ContractRegistry {
|
|
|
368
341
|
* @returns Array of dependency hashes
|
|
369
342
|
*/
|
|
370
343
|
getDependencies(contractHash) {
|
|
371
|
-
return this.contractDependencies.get(contractHash) || []
|
|
344
|
+
return this.contractDependencies.get(contractHash) || [];
|
|
372
345
|
}
|
|
373
346
|
/**
|
|
374
347
|
* Check if all dependencies for a contract are available
|
|
@@ -376,27 +349,27 @@ class ContractRegistry {
|
|
|
376
349
|
* @returns true if all dependencies are available
|
|
377
350
|
*/
|
|
378
351
|
async areDependenciesAvailable(contractHash) {
|
|
379
|
-
const dependencies = this.getDependencies(contractHash)
|
|
380
|
-
if (dependencies.length === 0) return true
|
|
352
|
+
const dependencies = this.getDependencies(contractHash);
|
|
353
|
+
if (dependencies.length === 0) return true;
|
|
381
354
|
for (const depHash of dependencies) {
|
|
382
355
|
try {
|
|
383
356
|
if (globalThis.contractStore) {
|
|
384
|
-
await globalThis.contractStore.get(depHash)
|
|
357
|
+
await globalThis.contractStore.get(depHash);
|
|
385
358
|
} else {
|
|
386
|
-
return false
|
|
359
|
+
return false;
|
|
387
360
|
}
|
|
388
361
|
} catch {
|
|
389
|
-
return false
|
|
362
|
+
return false;
|
|
390
363
|
}
|
|
391
364
|
}
|
|
392
|
-
return true
|
|
365
|
+
return true;
|
|
393
366
|
}
|
|
394
367
|
/**
|
|
395
368
|
* Get all registered contract hashes
|
|
396
369
|
* @returns Array of contract hashes
|
|
397
370
|
*/
|
|
398
371
|
getBaseContractHashes() {
|
|
399
|
-
return Array.from(this.baseContracts.keys())
|
|
372
|
+
return Array.from(this.baseContracts.keys());
|
|
400
373
|
}
|
|
401
374
|
/**
|
|
402
375
|
* Get hash for a registered name
|
|
@@ -404,7 +377,7 @@ class ContractRegistry {
|
|
|
404
377
|
* @returns The hash or undefined
|
|
405
378
|
*/
|
|
406
379
|
getHashForName(name) {
|
|
407
|
-
return this.contractNames.get(name)
|
|
380
|
+
return this.contractNames.get(name);
|
|
408
381
|
}
|
|
409
382
|
/**
|
|
410
383
|
* Analyze contract code and extract inheritance information
|
|
@@ -412,7 +385,7 @@ class ContractRegistry {
|
|
|
412
385
|
* @returns Inheritance information
|
|
413
386
|
*/
|
|
414
387
|
analyzeContract(contractCode) {
|
|
415
|
-
return parseContractInheritance(contractCode)
|
|
388
|
+
return parseContractInheritance(contractCode);
|
|
416
389
|
}
|
|
417
390
|
/**
|
|
418
391
|
* Build a complete contract by combining base contracts
|
|
@@ -421,35 +394,35 @@ class ContractRegistry {
|
|
|
421
394
|
* @returns Combined contract code
|
|
422
395
|
*/
|
|
423
396
|
async buildContract(contractCode, baseContractIdentifiers = []) {
|
|
424
|
-
let combinedCode =
|
|
397
|
+
let combinedCode = "";
|
|
425
398
|
for (const identifier of baseContractIdentifiers) {
|
|
426
|
-
let baseContract = await this.getBaseContract(identifier)
|
|
399
|
+
let baseContract = await this.getBaseContract(identifier);
|
|
427
400
|
if (!baseContract) {
|
|
428
|
-
baseContract = this.getBaseContractByHash(identifier)
|
|
401
|
+
baseContract = this.getBaseContractByHash(identifier);
|
|
429
402
|
}
|
|
430
403
|
if (baseContract) {
|
|
431
|
-
const decoded = baseContract.decoded
|
|
404
|
+
const decoded = baseContract.decoded;
|
|
432
405
|
combinedCode += `// Base contract: ${identifier}
|
|
433
|
-
|
|
434
|
-
combinedCode += decoded.contract +
|
|
406
|
+
`;
|
|
407
|
+
combinedCode += decoded.contract + "\n\n";
|
|
435
408
|
}
|
|
436
409
|
}
|
|
437
|
-
combinedCode += contractCode
|
|
438
|
-
return combinedCode
|
|
410
|
+
combinedCode += contractCode;
|
|
411
|
+
return combinedCode;
|
|
439
412
|
}
|
|
440
413
|
/**
|
|
441
414
|
* Clear all registered contracts
|
|
442
415
|
*/
|
|
443
416
|
clear() {
|
|
444
|
-
this.baseContracts.clear()
|
|
445
|
-
this.contractDependencies.clear()
|
|
417
|
+
this.baseContracts.clear();
|
|
418
|
+
this.contractDependencies.clear();
|
|
446
419
|
}
|
|
447
420
|
/**
|
|
448
421
|
* Get all registered base contract names
|
|
449
422
|
* @returns Array of base contract names
|
|
450
423
|
*/
|
|
451
424
|
getBaseContractNames() {
|
|
452
|
-
return Array.from(this.contractNames.keys())
|
|
425
|
+
return Array.from(this.contractNames.keys());
|
|
453
426
|
}
|
|
454
427
|
/**
|
|
455
428
|
* Get name for a hash if registered
|
|
@@ -458,16 +431,16 @@ class ContractRegistry {
|
|
|
458
431
|
*/
|
|
459
432
|
getNameForHash(hash) {
|
|
460
433
|
for (const [name, h] of this.contractNames.entries()) {
|
|
461
|
-
if (h === hash) return name
|
|
434
|
+
if (h === hash) return name;
|
|
462
435
|
}
|
|
463
|
-
return void 0
|
|
436
|
+
return void 0;
|
|
464
437
|
}
|
|
465
438
|
}
|
|
466
|
-
const contractRegistry = new ContractRegistry()
|
|
439
|
+
const contractRegistry = new ContractRegistry();
|
|
467
440
|
|
|
468
441
|
class Contract extends Transaction {
|
|
469
442
|
constructor(config) {
|
|
470
|
-
super(config)
|
|
443
|
+
super(config);
|
|
471
444
|
}
|
|
472
445
|
/**
|
|
473
446
|
*
|
|
@@ -477,7 +450,7 @@ class Contract extends Transaction {
|
|
|
477
450
|
* @returns lib.createContractMessage
|
|
478
451
|
*/
|
|
479
452
|
async createContractMessage(creator, contract, constructorParameters = []) {
|
|
480
|
-
return createContractMessage(creator, contract, constructorParameters)
|
|
453
|
+
return createContractMessage(creator, contract, constructorParameters);
|
|
481
454
|
}
|
|
482
455
|
/**
|
|
483
456
|
*
|
|
@@ -487,8 +460,8 @@ class Contract extends Transaction {
|
|
|
487
460
|
* @returns {Address}
|
|
488
461
|
*/
|
|
489
462
|
async createContractAddress(creator, contract, constructorParameters = []) {
|
|
490
|
-
contract = await this.createContractMessage(creator, contract, constructorParameters)
|
|
491
|
-
return contract.hash()
|
|
463
|
+
contract = await this.createContractMessage(creator, contract, constructorParameters);
|
|
464
|
+
return contract.hash();
|
|
492
465
|
}
|
|
493
466
|
/**
|
|
494
467
|
*
|
|
@@ -497,8 +470,8 @@ class Contract extends Transaction {
|
|
|
497
470
|
* @returns
|
|
498
471
|
*/
|
|
499
472
|
async deployContract(signer, contract, constructorParameters = []) {
|
|
500
|
-
const message = await createContractMessage(await signer.address, contract, constructorParameters)
|
|
501
|
-
return this.deployContractMessage(signer, message)
|
|
473
|
+
const message = await createContractMessage(await signer.address, contract, constructorParameters);
|
|
474
|
+
return this.deployContractMessage(signer, message);
|
|
502
475
|
}
|
|
503
476
|
/**
|
|
504
477
|
* Register a base contract for reuse by other contracts
|
|
@@ -509,15 +482,15 @@ class Contract extends Transaction {
|
|
|
509
482
|
* @returns {Promise<string>} The contract hash
|
|
510
483
|
*/
|
|
511
484
|
async registerBaseContract(name, contract, constructorParameters = []) {
|
|
512
|
-
const creator = addresses.contractFactory
|
|
513
|
-
const message = await createContractMessage(creator, contract, [])
|
|
514
|
-
const hash = await contractRegistry.registerBaseContract(name, message)
|
|
485
|
+
const creator = addresses.contractFactory;
|
|
486
|
+
const message = await createContractMessage(creator, contract, []);
|
|
487
|
+
const hash = await contractRegistry.registerBaseContract(name, message);
|
|
515
488
|
try {
|
|
516
|
-
await globalThis.contractStore.put(hash, message.encoded)
|
|
489
|
+
await globalThis.contractStore.put(hash, message.encoded);
|
|
517
490
|
} catch (error) {
|
|
518
|
-
throw error
|
|
491
|
+
throw error;
|
|
519
492
|
}
|
|
520
|
-
return hash
|
|
493
|
+
return hash;
|
|
521
494
|
}
|
|
522
495
|
/**
|
|
523
496
|
* Deploy a contract with optional base contracts for inheritance
|
|
@@ -529,38 +502,38 @@ class Contract extends Transaction {
|
|
|
529
502
|
*/
|
|
530
503
|
async deployDerivedContract(signer, contract, baseContractIdentifier, constructorParameters = []) {
|
|
531
504
|
if (baseContractIdentifier) {
|
|
532
|
-
const baseContract = contractRegistry.getBaseContract(baseContractIdentifier)
|
|
505
|
+
const baseContract = contractRegistry.getBaseContract(baseContractIdentifier);
|
|
533
506
|
if (!baseContract) {
|
|
534
507
|
throw new Error(
|
|
535
508
|
`Base contract '${baseContractIdentifier}' not found. Register it first using registerBaseContract.`
|
|
536
|
-
)
|
|
509
|
+
);
|
|
537
510
|
}
|
|
538
511
|
}
|
|
539
|
-
const inheritanceInfo = parseContractInheritance(contract)
|
|
540
|
-
const message = await createContractMessage(await signer.address, contract, constructorParameters)
|
|
512
|
+
const inheritanceInfo = parseContractInheritance(contract);
|
|
513
|
+
const message = await createContractMessage(await signer.address, contract, constructorParameters);
|
|
541
514
|
if (inheritanceInfo.hasInheritance && inheritanceInfo.baseClass && baseContractIdentifier) {
|
|
542
|
-
const baseHash = contractRegistry.getHashForName(inheritanceInfo.baseClass)
|
|
515
|
+
const baseHash = contractRegistry.getHashForName(inheritanceInfo.baseClass);
|
|
543
516
|
if (baseHash) {
|
|
544
|
-
const contractHash = await message.hash()
|
|
545
|
-
contractRegistry.registerDependencies(contractHash, [baseHash])
|
|
517
|
+
const contractHash = await message.hash();
|
|
518
|
+
contractRegistry.registerDependencies(contractHash, [baseHash]);
|
|
546
519
|
}
|
|
547
520
|
}
|
|
548
|
-
return this.deployContractMessage(signer, message)
|
|
521
|
+
return this.deployContractMessage(signer, message);
|
|
549
522
|
}
|
|
550
523
|
async deployContractMessage(signer, message) {
|
|
551
524
|
try {
|
|
552
|
-
await globalThis.contractStore.put(await message.hash(), message.encoded)
|
|
525
|
+
await globalThis.contractStore.put(await message.hash(), message.encoded);
|
|
553
526
|
} catch (error) {
|
|
554
|
-
throw error
|
|
527
|
+
throw error;
|
|
555
528
|
}
|
|
556
529
|
let transaction = {
|
|
557
530
|
from: await signer.address,
|
|
558
531
|
to: addresses.contractFactory,
|
|
559
|
-
method:
|
|
532
|
+
method: "registerContract",
|
|
560
533
|
params: [await message.hash()]
|
|
561
|
-
}
|
|
562
|
-
transaction = await signTransaction(await this.createTransaction(transaction), signer)
|
|
563
|
-
return this.sendTransaction(transaction)
|
|
534
|
+
};
|
|
535
|
+
transaction = await signTransaction(await this.createTransaction(transaction), signer);
|
|
536
|
+
return this.sendTransaction(transaction);
|
|
564
537
|
}
|
|
565
538
|
/**
|
|
566
539
|
* Check if a class is derived from another class
|
|
@@ -569,7 +542,7 @@ class Contract extends Transaction {
|
|
|
569
542
|
* @returns true if derivedClass extends baseClass
|
|
570
543
|
*/
|
|
571
544
|
isDerivedFrom(derivedClass, baseClass) {
|
|
572
|
-
return isDerivedFrom(derivedClass, baseClass)
|
|
545
|
+
return isDerivedFrom(derivedClass, baseClass);
|
|
573
546
|
}
|
|
574
547
|
/**
|
|
575
548
|
* Parse contract code to extract inheritance information
|
|
@@ -577,25 +550,25 @@ class Contract extends Transaction {
|
|
|
577
550
|
* @returns Inheritance information
|
|
578
551
|
*/
|
|
579
552
|
parseContractInheritance(contractCode) {
|
|
580
|
-
return parseContractInheritance(contractCode)
|
|
553
|
+
return parseContractInheritance(contractCode);
|
|
581
554
|
}
|
|
582
555
|
/**
|
|
583
556
|
* Get the contract registry instance
|
|
584
557
|
* @returns {ContractRegistry} The contract registry
|
|
585
558
|
*/
|
|
586
559
|
getRegistry() {
|
|
587
|
-
return contractRegistry
|
|
560
|
+
return contractRegistry;
|
|
588
561
|
}
|
|
589
562
|
}
|
|
590
563
|
|
|
591
|
-
const debug$2 = createDebugger(
|
|
564
|
+
const debug$2 = createDebugger("leofcoin/machine");
|
|
592
565
|
class Machine {
|
|
593
566
|
constructor(blocks) {
|
|
594
567
|
this.states = {
|
|
595
568
|
states: {},
|
|
596
569
|
lastBlock: {
|
|
597
570
|
index: -1,
|
|
598
|
-
hash:
|
|
571
|
+
hash: ""
|
|
599
572
|
},
|
|
600
573
|
accounts: {},
|
|
601
574
|
info: {
|
|
@@ -609,159 +582,156 @@ class Machine {
|
|
|
609
582
|
totalTransactions: BigInt(0),
|
|
610
583
|
totalBlocks: BigInt(0)
|
|
611
584
|
}
|
|
612
|
-
}
|
|
613
|
-
this.wantList = []
|
|
585
|
+
};
|
|
586
|
+
this.wantList = [];
|
|
614
587
|
this.ready = new Promise((resolve) => {
|
|
615
|
-
this.readyResolve = resolve
|
|
616
|
-
})
|
|
617
|
-
this.init(blocks)
|
|
588
|
+
this.readyResolve = resolve;
|
|
589
|
+
});
|
|
590
|
+
this.init(blocks);
|
|
618
591
|
}
|
|
619
592
|
init(blocks) {
|
|
620
|
-
return this.#init(blocks)
|
|
593
|
+
return this.#init(blocks);
|
|
621
594
|
}
|
|
622
595
|
async #onmessage(data) {
|
|
623
596
|
switch (data.type) {
|
|
624
|
-
case
|
|
625
|
-
const { from, nonce, hash } = data.result
|
|
626
|
-
|
|
597
|
+
case "transactionLoaded": {
|
|
598
|
+
const { from, nonce, hash } = data.result;
|
|
599
|
+
await transactionPoolStore.has(hash) && await transactionPoolStore.delete(hash);
|
|
627
600
|
try {
|
|
628
|
-
const _nonce = await accountsStore.get(from)
|
|
629
|
-
if (nonce > _nonce) await accountsStore.put(from, nonce)
|
|
601
|
+
const _nonce = await accountsStore.get(from);
|
|
602
|
+
if (nonce > _nonce) await accountsStore.put(from, nonce);
|
|
630
603
|
} catch (error) {
|
|
631
|
-
await accountsStore.put(from, nonce)
|
|
604
|
+
await accountsStore.put(from, nonce);
|
|
632
605
|
}
|
|
633
|
-
break
|
|
606
|
+
break;
|
|
634
607
|
}
|
|
635
|
-
case
|
|
636
|
-
console.warn(data.error)
|
|
637
|
-
console.warn(`contract error: ${data.hash}`)
|
|
638
|
-
await contractStore.delete(data.hash)
|
|
639
|
-
break
|
|
608
|
+
case "contractError": {
|
|
609
|
+
console.warn(data.error);
|
|
610
|
+
console.warn(`contract error: ${data.hash}`);
|
|
611
|
+
await contractStore.delete(data.hash);
|
|
612
|
+
break;
|
|
640
613
|
}
|
|
641
|
-
case
|
|
642
|
-
console.error(`init error: ${data.message}`)
|
|
643
|
-
break
|
|
614
|
+
case "initError": {
|
|
615
|
+
console.error(`init error: ${data.message}`);
|
|
616
|
+
break;
|
|
644
617
|
}
|
|
645
|
-
case
|
|
646
|
-
pubsub.publish(data.id, { error: data.message })
|
|
647
|
-
break
|
|
618
|
+
case "executeError": {
|
|
619
|
+
pubsub.publish(data.id, { error: data.message });
|
|
620
|
+
break;
|
|
648
621
|
}
|
|
649
|
-
case
|
|
650
|
-
if (data.message.includes(
|
|
651
|
-
pubsub.publish(
|
|
622
|
+
case "debug": {
|
|
623
|
+
if (data.message.includes("loaded transactions for block:")) {
|
|
624
|
+
pubsub.publish("block-loaded", data.message.replace("loaded transactions for block: ", "").split(" @")[0]);
|
|
652
625
|
}
|
|
653
|
-
break
|
|
626
|
+
break;
|
|
654
627
|
}
|
|
655
|
-
case
|
|
656
|
-
console.error(data.message)
|
|
657
|
-
break
|
|
628
|
+
case "error": {
|
|
629
|
+
console.error(data.message);
|
|
630
|
+
break;
|
|
658
631
|
}
|
|
659
|
-
case
|
|
660
|
-
pubsub.publish(
|
|
661
|
-
break
|
|
632
|
+
case "machine-ready": {
|
|
633
|
+
pubsub.publish("machine.ready", true);
|
|
634
|
+
break;
|
|
662
635
|
}
|
|
663
|
-
case
|
|
664
|
-
pubsub.publish(data.id, data.value || false)
|
|
665
|
-
break
|
|
636
|
+
case "response": {
|
|
637
|
+
pubsub.publish(data.id, data.value || false);
|
|
638
|
+
break;
|
|
666
639
|
}
|
|
667
|
-
case
|
|
668
|
-
this.wantList.push(data.hash)
|
|
640
|
+
case "addToWantList": {
|
|
641
|
+
this.wantList.push(data.hash);
|
|
669
642
|
}
|
|
670
|
-
case
|
|
671
|
-
if (data.question ===
|
|
643
|
+
case "ask": {
|
|
644
|
+
if (data.question === "contract" || data.question === "transaction") {
|
|
672
645
|
try {
|
|
673
|
-
const input = await peernet.get(data.input, data.question)
|
|
646
|
+
const input = await peernet.get(data.input, data.question);
|
|
674
647
|
if (input === void 0) {
|
|
675
|
-
this.worker.postMessage({ id: data.id, error: `could not get ${data.question} @${data.input}` })
|
|
676
|
-
this.wantList.push(data.input)
|
|
648
|
+
this.worker.postMessage({ id: data.id, error: `could not get ${data.question} @${data.input}` });
|
|
649
|
+
this.wantList.push(data.input);
|
|
677
650
|
} else {
|
|
678
|
-
this.worker.postMessage({ id: data.id, input })
|
|
651
|
+
this.worker.postMessage({ id: data.id, input });
|
|
679
652
|
}
|
|
680
653
|
} catch (error) {
|
|
681
|
-
console.error(error)
|
|
682
|
-
this.worker.postMessage({ id: data.id, error: `could not get ${data.question} @${data.input}` })
|
|
683
|
-
this.wantList.push(data.input)
|
|
654
|
+
console.error(error);
|
|
655
|
+
this.worker.postMessage({ id: data.id, error: `could not get ${data.question} @${data.input}` });
|
|
656
|
+
this.wantList.push(data.input);
|
|
684
657
|
}
|
|
685
|
-
} else if (data.question ===
|
|
658
|
+
} else if (data.question === "peers") {
|
|
686
659
|
this.worker.postMessage({
|
|
687
660
|
id: data.id,
|
|
688
661
|
input: peernet.connections ? Object.entries(peernet.connections).map(([id, peer]) => peer.toJSON()) : []
|
|
689
|
-
})
|
|
662
|
+
});
|
|
690
663
|
} else {
|
|
691
|
-
this.worker.postMessage({ id: data.id, input: data.input })
|
|
664
|
+
this.worker.postMessage({ id: data.id, input: data.input });
|
|
692
665
|
}
|
|
693
666
|
}
|
|
694
667
|
}
|
|
695
668
|
}
|
|
696
669
|
async updateState() {
|
|
697
670
|
try {
|
|
698
|
-
const lastBlock = await this.lastBlock
|
|
699
|
-
const lastBlockIndex = Number(lastBlock.index)
|
|
700
|
-
const statesLastBlockIndex = Number(this.states.lastBlock.index)
|
|
701
|
-
if (
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
) {
|
|
705
|
-
const blocks = (await this.blocks).slice(statesLastBlockIndex)
|
|
706
|
-
let transactions = []
|
|
671
|
+
const lastBlock = await this.lastBlock;
|
|
672
|
+
const lastBlockIndex = Number(lastBlock.index);
|
|
673
|
+
const statesLastBlockIndex = Number(this.states.lastBlock.index);
|
|
674
|
+
if (lastBlockIndex > statesLastBlockIndex || lastBlock.hash !== this.states.lastBlock.hash && Number(lastBlockIndex) === 0) {
|
|
675
|
+
const blocks = (await this.blocks).slice(statesLastBlockIndex);
|
|
676
|
+
let transactions = [];
|
|
707
677
|
for (const block of blocks) {
|
|
708
|
-
if (block?.transactions) transactions = [...transactions, ...block.transactions]
|
|
678
|
+
if (block?.transactions) transactions = [...transactions, ...block.transactions];
|
|
709
679
|
}
|
|
710
680
|
transactions = await Promise.all(
|
|
711
681
|
transactions.map(async (transaction) => new TransactionMessage(await transactionStore.get(transaction)))
|
|
712
|
-
)
|
|
682
|
+
);
|
|
713
683
|
const contractsToGet = transactions.reduce((set, current) => {
|
|
714
|
-
const contract = current.decoded.to
|
|
715
|
-
if (!set.includes(contract)) set.push(contract)
|
|
716
|
-
return set
|
|
717
|
-
}, [])
|
|
718
|
-
const state = {}
|
|
719
|
-
if (!contractsToGet.includes(addresses.contractFactory)) contractsToGet.push(addresses.contractFactory)
|
|
720
|
-
if (!contractsToGet.includes(addresses.nativeToken)) contractsToGet.push(addresses.nativeToken)
|
|
721
|
-
if (!contractsToGet.includes(addresses.nameService)) contractsToGet.push(addresses.nameService)
|
|
722
|
-
if (!contractsToGet.includes(addresses.validators)) contractsToGet.push(addresses.validators)
|
|
684
|
+
const contract = current.decoded.to;
|
|
685
|
+
if (!set.includes(contract)) set.push(contract);
|
|
686
|
+
return set;
|
|
687
|
+
}, []);
|
|
688
|
+
const state = {};
|
|
689
|
+
if (!contractsToGet.includes(addresses.contractFactory)) contractsToGet.push(addresses.contractFactory);
|
|
690
|
+
if (!contractsToGet.includes(addresses.nativeToken)) contractsToGet.push(addresses.nativeToken);
|
|
691
|
+
if (!contractsToGet.includes(addresses.nameService)) contractsToGet.push(addresses.nameService);
|
|
692
|
+
if (!contractsToGet.includes(addresses.validators)) contractsToGet.push(addresses.validators);
|
|
723
693
|
await Promise.all(
|
|
724
694
|
contractsToGet.map(async (contract) => {
|
|
725
|
-
const value = await this.#askWorker(
|
|
726
|
-
state[contract] = value
|
|
695
|
+
const value = await this.#askWorker("get", { contract, method: "state", params: [] });
|
|
696
|
+
state[contract] = value;
|
|
727
697
|
})
|
|
728
|
-
)
|
|
729
|
-
let accounts = {}
|
|
698
|
+
);
|
|
699
|
+
let accounts = {};
|
|
730
700
|
try {
|
|
731
|
-
const promises = []
|
|
701
|
+
const promises = [];
|
|
732
702
|
for (const address of await accountsStore.keys()) {
|
|
733
703
|
promises.push(async () => {
|
|
734
|
-
accounts[address] = await accountsStore.get(address)
|
|
735
|
-
})
|
|
704
|
+
accounts[address] = await accountsStore.get(address);
|
|
705
|
+
});
|
|
736
706
|
}
|
|
737
|
-
await Promise.all(promises)
|
|
707
|
+
await Promise.all(promises);
|
|
738
708
|
} catch (error) {
|
|
739
|
-
const promises = []
|
|
709
|
+
const promises = [];
|
|
740
710
|
for (const transaction of transactions.reverse()) {
|
|
741
|
-
const { from, to, amount, nonce } = transaction.decoded.data
|
|
711
|
+
const { from, to, amount, nonce } = transaction.decoded.data;
|
|
742
712
|
if (!accounts[from]) {
|
|
743
|
-
accounts[from] = nonce
|
|
713
|
+
accounts[from] = nonce;
|
|
744
714
|
promises.push(async () => {
|
|
745
|
-
await accountsStore.put(from, nonce)
|
|
746
|
-
})
|
|
715
|
+
await accountsStore.put(from, nonce);
|
|
716
|
+
});
|
|
747
717
|
}
|
|
748
718
|
}
|
|
749
|
-
await Promise.all(promises)
|
|
719
|
+
await Promise.all(promises);
|
|
750
720
|
}
|
|
751
721
|
const sortedStringify = (obj, replacer) => {
|
|
752
|
-
const sorted = {}
|
|
753
|
-
const keys = Object.keys(obj).sort()
|
|
722
|
+
const sorted = {};
|
|
723
|
+
const keys = Object.keys(obj).sort();
|
|
754
724
|
for (const key of keys) {
|
|
755
|
-
sorted[key] = obj[key]
|
|
725
|
+
sorted[key] = obj[key];
|
|
756
726
|
}
|
|
757
|
-
return JSON.stringify(sorted, replacer)
|
|
758
|
-
}
|
|
727
|
+
return JSON.stringify(sorted, replacer);
|
|
728
|
+
};
|
|
759
729
|
const tasks = [
|
|
760
|
-
stateStore.put(
|
|
761
|
-
stateStore.put(
|
|
762
|
-
stateStore.put(
|
|
730
|
+
stateStore.put("lastBlock", sortedStringify(await this.lastBlock, jsonStringifyBigInt)),
|
|
731
|
+
stateStore.put("states", sortedStringify(state, jsonStringifyBigInt)),
|
|
732
|
+
stateStore.put("accounts", sortedStringify(accounts, jsonStringifyBigInt)),
|
|
763
733
|
stateStore.put(
|
|
764
|
-
|
|
734
|
+
"info",
|
|
765
735
|
sortedStringify(
|
|
766
736
|
{
|
|
767
737
|
nativeBurnAmount: await this.totalBurnAmount,
|
|
@@ -779,53 +749,53 @@ class Machine {
|
|
|
779
749
|
)
|
|
780
750
|
)
|
|
781
751
|
// accountsStore.clear()
|
|
782
|
-
]
|
|
783
|
-
await Promise.all(tasks)
|
|
752
|
+
];
|
|
753
|
+
await Promise.all(tasks);
|
|
784
754
|
}
|
|
785
755
|
} catch (error) {
|
|
786
|
-
console.error(error)
|
|
756
|
+
console.error(error);
|
|
787
757
|
}
|
|
788
758
|
}
|
|
789
759
|
async #init(blocks) {
|
|
790
760
|
return new Promise(async (resolve) => {
|
|
791
761
|
const machineReady = async () => {
|
|
792
|
-
pubsub.unsubscribe(
|
|
793
|
-
await this.updateState()
|
|
794
|
-
resolve(this)
|
|
795
|
-
}
|
|
796
|
-
pubsub.subscribe(
|
|
797
|
-
let pre
|
|
762
|
+
pubsub.unsubscribe("machine.ready", machineReady);
|
|
763
|
+
await this.updateState();
|
|
764
|
+
resolve(this);
|
|
765
|
+
};
|
|
766
|
+
pubsub.subscribe("machine.ready", machineReady);
|
|
767
|
+
let pre;
|
|
798
768
|
try {
|
|
799
|
-
const importee = await import('url')
|
|
800
|
-
const url = importee.default
|
|
801
|
-
if (url) pre = url.fileURLToPath(new URL(
|
|
769
|
+
const importee = await import('url');
|
|
770
|
+
const url = importee.default;
|
|
771
|
+
if (url) pre = url.fileURLToPath(new URL(".", import.meta.url));
|
|
802
772
|
} catch {
|
|
803
|
-
pre =
|
|
804
|
-
}
|
|
805
|
-
this.worker = await new EasyWorker(pre +
|
|
806
|
-
serialization:
|
|
807
|
-
type:
|
|
808
|
-
})
|
|
809
|
-
this.worker.onmessage(this.#onmessage.bind(this))
|
|
810
|
-
let rawLastBlock
|
|
811
|
-
let rawStates
|
|
812
|
-
let rawAccounts
|
|
813
|
-
let rawInfo
|
|
814
|
-
if (await stateStore.has(
|
|
815
|
-
const decoder = new TextDecoder()
|
|
816
|
-
const decode = (param) => decoder.decode(param)
|
|
817
|
-
rawLastBlock = decode(await stateStore.get(
|
|
818
|
-
this.states.lastBlock = JSON.parse(rawLastBlock, jsonParseBigInt)
|
|
773
|
+
pre = "./";
|
|
774
|
+
}
|
|
775
|
+
this.worker = await new EasyWorker(pre + "workers/machine-worker.js", {
|
|
776
|
+
serialization: "advanced",
|
|
777
|
+
type: "module"
|
|
778
|
+
});
|
|
779
|
+
this.worker.onmessage(this.#onmessage.bind(this));
|
|
780
|
+
let rawLastBlock;
|
|
781
|
+
let rawStates;
|
|
782
|
+
let rawAccounts;
|
|
783
|
+
let rawInfo;
|
|
784
|
+
if (await stateStore.has("lastBlock")) {
|
|
785
|
+
const decoder = new TextDecoder();
|
|
786
|
+
const decode = (param) => decoder.decode(param);
|
|
787
|
+
rawLastBlock = decode(await stateStore.get("lastBlock"));
|
|
788
|
+
this.states.lastBlock = JSON.parse(rawLastBlock, jsonParseBigInt);
|
|
819
789
|
try {
|
|
820
|
-
rawStates = decode(await stateStore.get(
|
|
821
|
-
rawAccounts = decode(await stateStore.get(
|
|
822
|
-
rawInfo = decode(await stateStore.get(
|
|
823
|
-
this.states.states = JSON.parse(rawStates, jsonParseBigInt)
|
|
824
|
-
this.states.accounts = JSON.parse(rawAccounts, jsonParseBigInt)
|
|
825
|
-
this.states.info = JSON.parse(rawInfo, jsonParseBigInt)
|
|
790
|
+
rawStates = decode(await stateStore.get("states"));
|
|
791
|
+
rawAccounts = decode(await stateStore.get("accounts"));
|
|
792
|
+
rawInfo = decode(await stateStore.get("info"));
|
|
793
|
+
this.states.states = JSON.parse(rawStates, jsonParseBigInt);
|
|
794
|
+
this.states.accounts = JSON.parse(rawAccounts, jsonParseBigInt);
|
|
795
|
+
this.states.info = JSON.parse(rawInfo, jsonParseBigInt);
|
|
826
796
|
} catch (error) {
|
|
827
|
-
console.error(error)
|
|
828
|
-
this.states.accounts = {}
|
|
797
|
+
console.error(error);
|
|
798
|
+
this.states.accounts = {};
|
|
829
799
|
this.states.info = {
|
|
830
800
|
nativeCalls: BigInt(0),
|
|
831
801
|
nativeMints: BigInt(0),
|
|
@@ -836,13 +806,13 @@ class Machine {
|
|
|
836
806
|
totalTransferAmount: BigInt(0),
|
|
837
807
|
totalTransactions: BigInt(0),
|
|
838
808
|
totalBlocks: BigInt(0)
|
|
839
|
-
}
|
|
840
|
-
rawInfo = JSON.stringify(this.states.info, jsonStringifyBigInt)
|
|
841
|
-
await stateStore.put(
|
|
809
|
+
};
|
|
810
|
+
rawInfo = JSON.stringify(this.states.info, jsonStringifyBigInt);
|
|
811
|
+
await stateStore.put("info", new TextEncoder().encode(rawInfo));
|
|
842
812
|
}
|
|
843
813
|
}
|
|
844
814
|
const message = {
|
|
845
|
-
type:
|
|
815
|
+
type: "init",
|
|
846
816
|
input: {
|
|
847
817
|
blocks,
|
|
848
818
|
fromState: this.states.lastBlock?.index >= 0,
|
|
@@ -852,31 +822,31 @@ class Machine {
|
|
|
852
822
|
// @ts-ignore
|
|
853
823
|
peerid: peernet.peerId
|
|
854
824
|
}
|
|
855
|
-
}
|
|
856
|
-
this.worker.postMessage(message)
|
|
857
|
-
this.readyResolve(this)
|
|
858
|
-
})
|
|
825
|
+
};
|
|
826
|
+
this.worker.postMessage(message);
|
|
827
|
+
this.readyResolve(this);
|
|
828
|
+
});
|
|
859
829
|
}
|
|
860
830
|
async #runContract(contractMessage) {
|
|
861
|
-
const hash = await contractMessage.hash()
|
|
831
|
+
const hash = await contractMessage.hash();
|
|
862
832
|
return new Promise((resolve, reject) => {
|
|
863
|
-
const id = randombytes(20).toString(
|
|
833
|
+
const id = randombytes(20).toString("hex");
|
|
864
834
|
const onmessage = (message) => {
|
|
865
|
-
pubsub.unsubscribe(id, onmessage)
|
|
866
|
-
if (message?.error) reject(message.error)
|
|
867
|
-
else resolve(message)
|
|
868
|
-
}
|
|
869
|
-
pubsub.subscribe(id, onmessage)
|
|
835
|
+
pubsub.unsubscribe(id, onmessage);
|
|
836
|
+
if (message?.error) reject(message.error);
|
|
837
|
+
else resolve(message);
|
|
838
|
+
};
|
|
839
|
+
pubsub.subscribe(id, onmessage);
|
|
870
840
|
this.worker.postMessage({
|
|
871
|
-
type:
|
|
841
|
+
type: "run",
|
|
872
842
|
id,
|
|
873
843
|
input: {
|
|
874
844
|
decoded: contractMessage.decoded,
|
|
875
845
|
encoded: contractMessage.encoded,
|
|
876
846
|
hash
|
|
877
847
|
}
|
|
878
|
-
})
|
|
879
|
-
})
|
|
848
|
+
});
|
|
849
|
+
});
|
|
880
850
|
}
|
|
881
851
|
/**
|
|
882
852
|
*
|
|
@@ -887,40 +857,40 @@ class Machine {
|
|
|
887
857
|
*/
|
|
888
858
|
async execute(contract, method, parameters) {
|
|
889
859
|
try {
|
|
890
|
-
if (contract === contractFactory && method ===
|
|
891
|
-
if (await this.has(parameters[0])) throw new Error(`duplicate contract @${parameters[0]}`)
|
|
892
|
-
let message
|
|
893
|
-
if (!
|
|
894
|
-
message = await peernet.get(parameters[0],
|
|
895
|
-
if (!message) throw new Error(`contract ${parameters[0]} not available`)
|
|
896
|
-
message = await new ContractMessage(message)
|
|
897
|
-
await globalThis.contractStore.put(await message.hash(), message.encoded)
|
|
860
|
+
if (contract === contractFactory && method === "registerContract") {
|
|
861
|
+
if (await this.has(parameters[0])) throw new Error(`duplicate contract @${parameters[0]}`);
|
|
862
|
+
let message;
|
|
863
|
+
if (!await globalThis.contractStore.has(parameters[0])) {
|
|
864
|
+
message = await peernet.get(parameters[0], "contract");
|
|
865
|
+
if (!message) throw new Error(`contract ${parameters[0]} not available`);
|
|
866
|
+
message = await new ContractMessage(message);
|
|
867
|
+
await globalThis.contractStore.put(await message.hash(), message.encoded);
|
|
898
868
|
}
|
|
899
869
|
if (!message) {
|
|
900
|
-
message = await globalThis.contractStore.get(parameters[0])
|
|
901
|
-
message = await new ContractMessage(message)
|
|
870
|
+
message = await globalThis.contractStore.get(parameters[0]);
|
|
871
|
+
message = await new ContractMessage(message);
|
|
902
872
|
}
|
|
903
|
-
if (!
|
|
873
|
+
if (!await this.has(await message.hash())) await this.#runContract(message);
|
|
904
874
|
}
|
|
905
875
|
} catch (error) {
|
|
906
876
|
throw new ContractDeploymentError(`contract deployment failed for ${parameters[0]}
|
|
907
|
-
${error.message}`)
|
|
877
|
+
${error.message}`);
|
|
908
878
|
}
|
|
909
879
|
return new Promise((resolve, reject) => {
|
|
910
|
-
const id = randombytes(20).toString(
|
|
880
|
+
const id = randombytes(20).toString("hex");
|
|
911
881
|
const timeout = setTimeout(() => {
|
|
912
|
-
pubsub.unsubscribe(id, onmessage)
|
|
913
|
-
reject(new Error(`Machine.execute timeout for ${contract}.${method}`))
|
|
914
|
-
}, 3e4)
|
|
882
|
+
pubsub.unsubscribe(id, onmessage);
|
|
883
|
+
reject(new Error(`Machine.execute timeout for ${contract}.${method}`));
|
|
884
|
+
}, 3e4);
|
|
915
885
|
const onmessage = (message) => {
|
|
916
|
-
clearTimeout(timeout)
|
|
917
|
-
pubsub.unsubscribe(id, onmessage)
|
|
918
|
-
if (message?.error) reject(new ExecutionError(message.error))
|
|
919
|
-
else resolve(message)
|
|
920
|
-
}
|
|
921
|
-
pubsub.subscribe(id, onmessage)
|
|
886
|
+
clearTimeout(timeout);
|
|
887
|
+
pubsub.unsubscribe(id, onmessage);
|
|
888
|
+
if (message?.error) reject(new ExecutionError(message.error));
|
|
889
|
+
else resolve(message);
|
|
890
|
+
};
|
|
891
|
+
pubsub.subscribe(id, onmessage);
|
|
922
892
|
this.worker.postMessage({
|
|
923
|
-
type:
|
|
893
|
+
type: "execute",
|
|
924
894
|
id,
|
|
925
895
|
input: {
|
|
926
896
|
to: contract,
|
|
@@ -928,764 +898,753 @@ ${error.message}`)
|
|
|
928
898
|
method,
|
|
929
899
|
params: parameters
|
|
930
900
|
}
|
|
931
|
-
})
|
|
932
|
-
})
|
|
901
|
+
});
|
|
902
|
+
});
|
|
933
903
|
}
|
|
934
904
|
get(contract, method, parameters) {
|
|
935
905
|
return new Promise((resolve, reject) => {
|
|
936
|
-
const id = randombytes(20).toString()
|
|
906
|
+
const id = randombytes(20).toString();
|
|
937
907
|
const timeout = setTimeout(() => {
|
|
938
|
-
pubsub.unsubscribe(id, onmessage)
|
|
939
|
-
reject(new Error(`Machine.get timeout for ${contract}.${method}`))
|
|
940
|
-
}, 3e4)
|
|
908
|
+
pubsub.unsubscribe(id, onmessage);
|
|
909
|
+
reject(new Error(`Machine.get timeout for ${contract}.${method}`));
|
|
910
|
+
}, 3e4);
|
|
941
911
|
const onmessage = (message) => {
|
|
942
|
-
clearTimeout(timeout)
|
|
943
|
-
pubsub.unsubscribe(id, onmessage)
|
|
944
|
-
resolve(message)
|
|
945
|
-
}
|
|
946
|
-
pubsub.subscribe(id, onmessage)
|
|
912
|
+
clearTimeout(timeout);
|
|
913
|
+
pubsub.unsubscribe(id, onmessage);
|
|
914
|
+
resolve(message);
|
|
915
|
+
};
|
|
916
|
+
pubsub.subscribe(id, onmessage);
|
|
947
917
|
this.worker.postMessage({
|
|
948
|
-
type:
|
|
918
|
+
type: "get",
|
|
949
919
|
id,
|
|
950
920
|
input: {
|
|
951
921
|
contract,
|
|
952
922
|
method,
|
|
953
923
|
params: parameters
|
|
954
924
|
}
|
|
955
|
-
})
|
|
956
|
-
})
|
|
925
|
+
});
|
|
926
|
+
});
|
|
957
927
|
}
|
|
958
928
|
async has(address) {
|
|
959
929
|
return new Promise((resolve, reject) => {
|
|
960
|
-
const id = randombytes(20).toString(
|
|
930
|
+
const id = randombytes(20).toString("hex");
|
|
961
931
|
const timeout = setTimeout(() => {
|
|
962
|
-
pubsub.unsubscribe(id, onmessage)
|
|
963
|
-
reject(new Error(`Machine.has timeout for ${address}`))
|
|
964
|
-
}, 1e4)
|
|
932
|
+
pubsub.unsubscribe(id, onmessage);
|
|
933
|
+
reject(new Error(`Machine.has timeout for ${address}`));
|
|
934
|
+
}, 1e4);
|
|
965
935
|
const onmessage = (message) => {
|
|
966
|
-
clearTimeout(timeout)
|
|
967
|
-
pubsub.unsubscribe(id, onmessage)
|
|
968
|
-
if (message?.error) reject(message.error)
|
|
969
|
-
else resolve(message)
|
|
970
|
-
}
|
|
971
|
-
pubsub.subscribe(id, onmessage)
|
|
936
|
+
clearTimeout(timeout);
|
|
937
|
+
pubsub.unsubscribe(id, onmessage);
|
|
938
|
+
if (message?.error) reject(message.error);
|
|
939
|
+
else resolve(message);
|
|
940
|
+
};
|
|
941
|
+
pubsub.subscribe(id, onmessage);
|
|
972
942
|
this.worker.postMessage({
|
|
973
|
-
type:
|
|
943
|
+
type: "has",
|
|
974
944
|
id,
|
|
975
945
|
input: {
|
|
976
946
|
address
|
|
977
947
|
}
|
|
978
|
-
})
|
|
979
|
-
})
|
|
948
|
+
});
|
|
949
|
+
});
|
|
980
950
|
}
|
|
981
951
|
#askWorker(type, input) {
|
|
982
952
|
return new Promise((resolve, reject) => {
|
|
983
|
-
const id = randombytes(20).toString(
|
|
953
|
+
const id = randombytes(20).toString("hex");
|
|
984
954
|
const timeout = setTimeout(() => {
|
|
985
|
-
pubsub.unsubscribe(id, onmessage)
|
|
986
|
-
reject(new Error(`Machine.#askWorker timeout for ${type}`))
|
|
987
|
-
}, 3e4)
|
|
955
|
+
pubsub.unsubscribe(id, onmessage);
|
|
956
|
+
reject(new Error(`Machine.#askWorker timeout for ${type}`));
|
|
957
|
+
}, 3e4);
|
|
988
958
|
const onmessage = (message) => {
|
|
989
|
-
clearTimeout(timeout)
|
|
990
|
-
pubsub.unsubscribe(id, onmessage)
|
|
991
|
-
if (message?.error) reject(message.error)
|
|
992
|
-
else resolve(message)
|
|
993
|
-
}
|
|
994
|
-
pubsub.subscribe(id, onmessage)
|
|
959
|
+
clearTimeout(timeout);
|
|
960
|
+
pubsub.unsubscribe(id, onmessage);
|
|
961
|
+
if (message?.error) reject(message.error);
|
|
962
|
+
else resolve(message);
|
|
963
|
+
};
|
|
964
|
+
pubsub.subscribe(id, onmessage);
|
|
995
965
|
this.worker.postMessage({
|
|
996
966
|
type,
|
|
997
967
|
id,
|
|
998
968
|
input
|
|
999
|
-
})
|
|
1000
|
-
})
|
|
969
|
+
});
|
|
970
|
+
});
|
|
1001
971
|
}
|
|
1002
972
|
get contracts() {
|
|
1003
|
-
return this.#askWorker(
|
|
973
|
+
return this.#askWorker("contracts");
|
|
1004
974
|
}
|
|
1005
975
|
get totalContracts() {
|
|
1006
|
-
return this.#askWorker(
|
|
976
|
+
return this.#askWorker("totalContracts");
|
|
1007
977
|
}
|
|
1008
978
|
get nativeCalls() {
|
|
1009
|
-
return this.#askWorker(
|
|
979
|
+
return this.#askWorker("nativeCalls");
|
|
1010
980
|
}
|
|
1011
981
|
get nativeMints() {
|
|
1012
|
-
return this.#askWorker(
|
|
982
|
+
return this.#askWorker("nativeMints");
|
|
1013
983
|
}
|
|
1014
984
|
get nativeBurns() {
|
|
1015
|
-
return this.#askWorker(
|
|
985
|
+
return this.#askWorker("nativeBurns");
|
|
1016
986
|
}
|
|
1017
987
|
get nativeTransfers() {
|
|
1018
|
-
return this.#askWorker(
|
|
988
|
+
return this.#askWorker("nativeTransfers");
|
|
1019
989
|
}
|
|
1020
990
|
get totalBurnAmount() {
|
|
1021
|
-
return this.#askWorker(
|
|
991
|
+
return this.#askWorker("totalBurnAmount");
|
|
1022
992
|
}
|
|
1023
993
|
get totalMintAmount() {
|
|
1024
|
-
return this.#askWorker(
|
|
994
|
+
return this.#askWorker("totalMintAmount");
|
|
1025
995
|
}
|
|
1026
996
|
get totalTransferAmount() {
|
|
1027
|
-
return this.#askWorker(
|
|
997
|
+
return this.#askWorker("totalTransferAmount");
|
|
1028
998
|
}
|
|
1029
999
|
get totalTransactions() {
|
|
1030
|
-
return this.#askWorker(
|
|
1000
|
+
return this.#askWorker("totalTransactions");
|
|
1031
1001
|
}
|
|
1032
1002
|
get totalBlocks() {
|
|
1033
|
-
return this.#askWorker(
|
|
1003
|
+
return this.#askWorker("totalBlocks");
|
|
1034
1004
|
}
|
|
1035
1005
|
get blocks() {
|
|
1036
|
-
return this.getBlocks()
|
|
1006
|
+
return this.getBlocks();
|
|
1037
1007
|
}
|
|
1038
1008
|
get lastBlock() {
|
|
1039
|
-
return this.#askWorker(
|
|
1040
|
-
debug$2(
|
|
1041
|
-
return this.states.lastBlock
|
|
1042
|
-
})
|
|
1009
|
+
return this.#askWorker("lastBlock").catch((error) => {
|
|
1010
|
+
debug$2("lastBlock fallback after worker timeout:", error?.message ?? error);
|
|
1011
|
+
return this.states.lastBlock;
|
|
1012
|
+
});
|
|
1043
1013
|
}
|
|
1044
1014
|
get lastBlockHeight() {
|
|
1045
|
-
return this.#askWorker(
|
|
1046
|
-
debug$2(
|
|
1047
|
-
return Number(this.states.lastBlock?.index ?? 0)
|
|
1048
|
-
})
|
|
1015
|
+
return this.#askWorker("lastBlockHeight").catch((error) => {
|
|
1016
|
+
debug$2("lastBlockHeight fallback after worker timeout:", error?.message ?? error);
|
|
1017
|
+
return Number(this.states.lastBlock?.index ?? 0);
|
|
1018
|
+
});
|
|
1049
1019
|
}
|
|
1050
1020
|
getBlocks(from, to) {
|
|
1051
|
-
return this.#askWorker(
|
|
1021
|
+
return this.#askWorker("blocks", { from, to });
|
|
1052
1022
|
}
|
|
1053
1023
|
getBlock(index) {
|
|
1054
|
-
return this.#askWorker(
|
|
1024
|
+
return this.#askWorker("block", index);
|
|
1055
1025
|
}
|
|
1056
1026
|
async addLoadedBlock(block) {
|
|
1057
|
-
debug$2(`adding loaded block: ${block.index}@${block.hash}`)
|
|
1058
|
-
if (block.decoded) block = { ...block.decoded, hash: await block.hash() }
|
|
1059
|
-
return this.#askWorker(
|
|
1027
|
+
debug$2(`adding loaded block: ${block.index}@${block.hash}`);
|
|
1028
|
+
if (block.decoded) block = { ...block.decoded, hash: await block.hash() };
|
|
1029
|
+
return this.#askWorker("addLoadedBlock", JSON.stringify(block, jsonStringifyBigInt));
|
|
1060
1030
|
}
|
|
1061
1031
|
async latestTransactions() {
|
|
1062
|
-
return this.#askWorker(
|
|
1032
|
+
return this.#askWorker("latestTransactions");
|
|
1063
1033
|
}
|
|
1064
1034
|
async delete(hash) {
|
|
1065
|
-
return globalThis.contractStore.delete(hash)
|
|
1035
|
+
return globalThis.contractStore.delete(hash);
|
|
1066
1036
|
}
|
|
1067
1037
|
/**
|
|
1068
1038
|
*
|
|
1069
1039
|
* @returns Promise
|
|
1070
1040
|
*/
|
|
1071
1041
|
async deleteAll() {
|
|
1072
|
-
let hashes = await globalThis.contractStore.keys()
|
|
1073
|
-
hashes = Object.keys(hashes).map((hash) => this.delete(hash))
|
|
1074
|
-
return Promise.all(hashes)
|
|
1042
|
+
let hashes = await globalThis.contractStore.keys();
|
|
1043
|
+
hashes = Object.keys(hashes).map((hash) => this.delete(hash));
|
|
1044
|
+
return Promise.all(hashes);
|
|
1075
1045
|
}
|
|
1076
1046
|
}
|
|
1077
1047
|
|
|
1078
1048
|
class Jobber {
|
|
1079
1049
|
constructor(timeout) {
|
|
1080
|
-
this.busy = false
|
|
1081
|
-
this.timeout = timeout
|
|
1050
|
+
this.busy = false;
|
|
1051
|
+
this.timeout = timeout;
|
|
1082
1052
|
}
|
|
1083
1053
|
add(fn) {
|
|
1084
|
-
this.busy = true
|
|
1054
|
+
this.busy = true;
|
|
1085
1055
|
return new Promise(async (resolve, reject) => {
|
|
1086
1056
|
const timeout = setTimeout(() => {
|
|
1087
|
-
reject(
|
|
1088
|
-
}, this.timeout)
|
|
1057
|
+
reject("timeout");
|
|
1058
|
+
}, this.timeout);
|
|
1089
1059
|
this.destroy = () => {
|
|
1090
|
-
clearTimeout(timeout)
|
|
1091
|
-
this.busy = false
|
|
1092
|
-
resolve(
|
|
1093
|
-
}
|
|
1060
|
+
clearTimeout(timeout);
|
|
1061
|
+
this.busy = false;
|
|
1062
|
+
resolve("stopped");
|
|
1063
|
+
};
|
|
1094
1064
|
try {
|
|
1095
|
-
const result = await fn()
|
|
1096
|
-
clearTimeout(timeout)
|
|
1097
|
-
this.busy = false
|
|
1098
|
-
resolve(result)
|
|
1065
|
+
const result = await fn();
|
|
1066
|
+
clearTimeout(timeout);
|
|
1067
|
+
this.busy = false;
|
|
1068
|
+
resolve(result);
|
|
1099
1069
|
} catch (error) {
|
|
1100
|
-
clearTimeout(timeout)
|
|
1101
|
-
reject(error)
|
|
1070
|
+
clearTimeout(timeout);
|
|
1071
|
+
reject(error);
|
|
1102
1072
|
}
|
|
1103
|
-
})
|
|
1073
|
+
});
|
|
1104
1074
|
}
|
|
1105
1075
|
}
|
|
1106
1076
|
|
|
1107
|
-
const debug$1 = createDebugger(
|
|
1077
|
+
const debug$1 = createDebugger("leofcoin/state");
|
|
1108
1078
|
class State extends Contract {
|
|
1109
1079
|
constructor(config) {
|
|
1110
|
-
super(config)
|
|
1111
|
-
this.#lastResolvedTime = 0
|
|
1112
|
-
this.#resolving = false
|
|
1113
|
-
this.#resolveErrorCount = 0
|
|
1114
|
-
this.#chainState =
|
|
1115
|
-
this.#syncErrorCount = 0
|
|
1116
|
-
this.#blockHashMap = /* @__PURE__ */ new Map()
|
|
1117
|
-
this.#chainSyncing = false
|
|
1118
|
-
this.#blocks = []
|
|
1119
|
-
this.knownBlocks = []
|
|
1120
|
-
this.#totalSize = 0
|
|
1121
|
-
this.#loaded = false
|
|
1122
|
-
this.#resolvingHashes = /* @__PURE__ */ new Set()
|
|
1123
|
-
this._wantList = []
|
|
1080
|
+
super(config);
|
|
1081
|
+
this.#lastResolvedTime = 0;
|
|
1082
|
+
this.#resolving = false;
|
|
1083
|
+
this.#resolveErrorCount = 0;
|
|
1084
|
+
this.#chainState = "loading";
|
|
1085
|
+
this.#syncErrorCount = 0;
|
|
1086
|
+
this.#blockHashMap = /* @__PURE__ */ new Map();
|
|
1087
|
+
this.#chainSyncing = false;
|
|
1088
|
+
this.#blocks = [];
|
|
1089
|
+
this.knownBlocks = [];
|
|
1090
|
+
this.#totalSize = 0;
|
|
1091
|
+
this.#loaded = false;
|
|
1092
|
+
this.#resolvingHashes = /* @__PURE__ */ new Set();
|
|
1093
|
+
this._wantList = [];
|
|
1124
1094
|
this.#chainStateHandler = () => {
|
|
1125
|
-
return new globalThis.peernet.protos[
|
|
1095
|
+
return new globalThis.peernet.protos["peernet-response"]({
|
|
1126
1096
|
response: this.#chainState
|
|
1127
|
-
})
|
|
1128
|
-
}
|
|
1097
|
+
});
|
|
1098
|
+
};
|
|
1129
1099
|
this.#lastBlockHandler = async () => {
|
|
1130
|
-
return new globalThis.peernet.protos[
|
|
1100
|
+
return new globalThis.peernet.protos["peernet-response"]({
|
|
1131
1101
|
response: new LastBlockMessage(await this.lastBlock).encoded
|
|
1132
|
-
})
|
|
1133
|
-
}
|
|
1102
|
+
});
|
|
1103
|
+
};
|
|
1134
1104
|
this.#knownBlocksHandler = async () => {
|
|
1135
|
-
return new globalThis.peernet.protos[
|
|
1105
|
+
return new globalThis.peernet.protos["peernet-response"]({
|
|
1136
1106
|
response: { blocks: await globalThis.blockStore.keys() }
|
|
1137
|
-
})
|
|
1138
|
-
}
|
|
1139
|
-
this.#loadBlockTransactions = (transactions) =>
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
)
|
|
1107
|
+
});
|
|
1108
|
+
};
|
|
1109
|
+
this.#loadBlockTransactions = (transactions) => Promise.all(
|
|
1110
|
+
transactions.map(async (transaction) => new TransactionMessage(await peernet.get(transaction, "transaction")))
|
|
1111
|
+
);
|
|
1143
1112
|
this.#getLastTransactions = async () => {
|
|
1144
|
-
let lastTransactions = (
|
|
1145
|
-
await
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
#
|
|
1158
|
-
#
|
|
1159
|
-
#
|
|
1160
|
-
#
|
|
1161
|
-
#
|
|
1162
|
-
#
|
|
1163
|
-
#
|
|
1164
|
-
#
|
|
1165
|
-
#
|
|
1166
|
-
#blockHashMap
|
|
1167
|
-
#chainSyncing
|
|
1168
|
-
#blocks
|
|
1169
|
-
#totalSize
|
|
1170
|
-
#machine
|
|
1171
|
-
#loaded
|
|
1172
|
-
#resolvingHashes
|
|
1113
|
+
let lastTransactions = (await Promise.all(
|
|
1114
|
+
(await this.blocks).filter((block) => block.loaded).slice(-24).map((block) => this.#loadBlockTransactions(block.transactions))
|
|
1115
|
+
)).reduce((all, transactions) => [...all, ...transactions], []);
|
|
1116
|
+
return Promise.all(lastTransactions.map((transaction) => transaction.hash()));
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
#resolveErrored;
|
|
1120
|
+
#lastResolvedTime;
|
|
1121
|
+
#lastResolved;
|
|
1122
|
+
#resolving;
|
|
1123
|
+
#resolveErrorCount;
|
|
1124
|
+
#syncState;
|
|
1125
|
+
#chainState;
|
|
1126
|
+
#lastBlockInQue;
|
|
1127
|
+
#syncErrorCount;
|
|
1128
|
+
#blockHashMap;
|
|
1129
|
+
#chainSyncing;
|
|
1130
|
+
#blocks;
|
|
1131
|
+
#totalSize;
|
|
1132
|
+
#machine;
|
|
1133
|
+
#loaded;
|
|
1134
|
+
#resolvingHashes;
|
|
1173
1135
|
/**
|
|
1174
1136
|
* contains transactions we need before we can successfully load
|
|
1175
1137
|
*/
|
|
1176
1138
|
get wantList() {
|
|
1177
|
-
return this.#machine?.wantList ?? this._wantList
|
|
1139
|
+
return this.#machine?.wantList ?? this._wantList;
|
|
1178
1140
|
}
|
|
1179
1141
|
get state() {
|
|
1180
1142
|
return {
|
|
1181
1143
|
sync: this.#syncState,
|
|
1182
1144
|
chain: this.#chainState
|
|
1183
|
-
}
|
|
1145
|
+
};
|
|
1184
1146
|
}
|
|
1185
1147
|
get blockHashMap() {
|
|
1186
|
-
return this.#blockHashMap.entries()
|
|
1148
|
+
return this.#blockHashMap.entries();
|
|
1187
1149
|
}
|
|
1188
1150
|
get loaded() {
|
|
1189
|
-
return this.#loaded
|
|
1151
|
+
return this.#loaded;
|
|
1190
1152
|
}
|
|
1191
1153
|
get resolving() {
|
|
1192
|
-
return this.#resolving
|
|
1154
|
+
return this.#resolving;
|
|
1193
1155
|
}
|
|
1194
1156
|
get contracts() {
|
|
1195
|
-
return this.#machine.contracts
|
|
1157
|
+
return this.#machine.contracts;
|
|
1196
1158
|
}
|
|
1197
1159
|
get totalContracts() {
|
|
1198
|
-
return this.#machine.totalContracts
|
|
1160
|
+
return this.#machine.totalContracts;
|
|
1199
1161
|
}
|
|
1200
1162
|
get nativeCalls() {
|
|
1201
|
-
return this.#machine.nativeCalls
|
|
1163
|
+
return this.#machine.nativeCalls;
|
|
1202
1164
|
}
|
|
1203
1165
|
get nativeMints() {
|
|
1204
|
-
return this.#machine.nativeMints
|
|
1166
|
+
return this.#machine.nativeMints;
|
|
1205
1167
|
}
|
|
1206
1168
|
get nativeBurns() {
|
|
1207
|
-
return this.#machine.nativeBurns
|
|
1169
|
+
return this.#machine.nativeBurns;
|
|
1208
1170
|
}
|
|
1209
1171
|
get nativeTransfers() {
|
|
1210
|
-
return this.#machine.nativeTransfers
|
|
1172
|
+
return this.#machine.nativeTransfers;
|
|
1211
1173
|
}
|
|
1212
1174
|
get totalBurnAmouint() {
|
|
1213
|
-
return this.#machine.totalBurnAmount
|
|
1175
|
+
return this.#machine.totalBurnAmount;
|
|
1214
1176
|
}
|
|
1215
1177
|
get totalMintAmount() {
|
|
1216
|
-
return this.#machine.totalMintAmount
|
|
1178
|
+
return this.#machine.totalMintAmount;
|
|
1217
1179
|
}
|
|
1218
1180
|
get totalTransferAmount() {
|
|
1219
|
-
return this.#machine.totalTransferAmount
|
|
1181
|
+
return this.#machine.totalTransferAmount;
|
|
1220
1182
|
}
|
|
1221
1183
|
get totalTransactions() {
|
|
1222
|
-
return this.#machine.totalTransactions
|
|
1184
|
+
return this.#machine.totalTransactions;
|
|
1223
1185
|
}
|
|
1224
1186
|
get totalBlocks() {
|
|
1225
|
-
return this.#machine.totalBlocks
|
|
1187
|
+
return this.#machine.totalBlocks;
|
|
1226
1188
|
}
|
|
1227
1189
|
get blocks() {
|
|
1228
|
-
return this.getBlocks()
|
|
1190
|
+
return this.getBlocks();
|
|
1229
1191
|
}
|
|
1230
1192
|
get lastBlock() {
|
|
1231
|
-
return this.#machine ? this.#machine.lastBlock : { index: 0, hash:
|
|
1193
|
+
return this.#machine ? this.#machine.lastBlock : { index: 0, hash: "0x0", previousHash: "0x0" };
|
|
1232
1194
|
}
|
|
1233
1195
|
get lastBlockHeight() {
|
|
1234
|
-
return this.#machine ? this.#machine.lastBlockHeight : 0
|
|
1196
|
+
return this.#machine ? this.#machine.lastBlockHeight : 0;
|
|
1235
1197
|
}
|
|
1236
1198
|
getBlock(index) {
|
|
1237
|
-
return this.#machine.getBlock(index)
|
|
1199
|
+
return this.#machine.getBlock(index);
|
|
1238
1200
|
}
|
|
1239
1201
|
getBlocks(from, to) {
|
|
1240
|
-
return this.#machine.getBlocks(from, to)
|
|
1202
|
+
return this.#machine.getBlocks(from, to);
|
|
1241
1203
|
}
|
|
1242
1204
|
get totalSize() {
|
|
1243
|
-
return this.#totalSize
|
|
1205
|
+
return this.#totalSize;
|
|
1244
1206
|
}
|
|
1245
1207
|
get machine() {
|
|
1246
|
-
return this.#machine
|
|
1208
|
+
return this.#machine;
|
|
1247
1209
|
}
|
|
1248
1210
|
async clearPool() {
|
|
1249
|
-
await globalThis.transactionPoolStore.clear()
|
|
1211
|
+
await globalThis.transactionPoolStore.clear();
|
|
1250
1212
|
}
|
|
1251
1213
|
/**
|
|
1252
1214
|
* drastic measurement, removes everything!
|
|
1253
1215
|
*/
|
|
1254
1216
|
async clearAll() {
|
|
1255
|
-
await globalThis.accountsStore.clear()
|
|
1256
|
-
await globalThis.chainStore.clear()
|
|
1257
|
-
await globalThis.blockStore.clear()
|
|
1258
|
-
await globalThis.transactionStore.clear()
|
|
1259
|
-
await globalThis.stateStore.clear()
|
|
1260
|
-
await globalThis.transactionPoolStore.clear()
|
|
1261
|
-
}
|
|
1262
|
-
#chainStateHandler
|
|
1263
|
-
#lastBlockHandler
|
|
1264
|
-
#knownBlocksHandler
|
|
1217
|
+
await globalThis.accountsStore.clear();
|
|
1218
|
+
await globalThis.chainStore.clear();
|
|
1219
|
+
await globalThis.blockStore.clear();
|
|
1220
|
+
await globalThis.transactionStore.clear();
|
|
1221
|
+
await globalThis.stateStore.clear();
|
|
1222
|
+
await globalThis.transactionPoolStore.clear();
|
|
1223
|
+
}
|
|
1224
|
+
#chainStateHandler;
|
|
1225
|
+
#lastBlockHandler;
|
|
1226
|
+
#knownBlocksHandler;
|
|
1265
1227
|
async init() {
|
|
1266
|
-
console.log(
|
|
1267
|
-
this.jobber = new Jobber(this.resolveTimeout)
|
|
1268
|
-
await globalThis.peernet.addRequestHandler(
|
|
1269
|
-
await globalThis.peernet.addRequestHandler(
|
|
1270
|
-
await globalThis.peernet.addRequestHandler(
|
|
1271
|
-
let localBlockHash
|
|
1272
|
-
let blockMessage
|
|
1273
|
-
let localBlock
|
|
1274
|
-
console.log(
|
|
1228
|
+
console.log("State init start");
|
|
1229
|
+
this.jobber = new Jobber(this.resolveTimeout);
|
|
1230
|
+
await globalThis.peernet.addRequestHandler("lastBlock", this.#lastBlockHandler);
|
|
1231
|
+
await globalThis.peernet.addRequestHandler("knownBlocks", this.#knownBlocksHandler);
|
|
1232
|
+
await globalThis.peernet.addRequestHandler("chainState", this.#chainStateHandler);
|
|
1233
|
+
let localBlockHash;
|
|
1234
|
+
let blockMessage;
|
|
1235
|
+
let localBlock;
|
|
1236
|
+
console.log("State init before try-catch");
|
|
1275
1237
|
try {
|
|
1276
|
-
const rawBlock = await globalThis.chainStore.has(
|
|
1277
|
-
console.log(
|
|
1238
|
+
const rawBlock = await globalThis.chainStore.has("lastBlock");
|
|
1239
|
+
console.log("State init after has lastBlock check");
|
|
1278
1240
|
if (rawBlock) {
|
|
1279
|
-
console.log(
|
|
1280
|
-
console.log(rawBlock)
|
|
1281
|
-
localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get(
|
|
1282
|
-
console.log(localBlockHash)
|
|
1283
|
-
if (localBlockHash !==
|
|
1284
|
-
blockMessage = await globalThis.blockStore.get(localBlockHash)
|
|
1285
|
-
console.log(blockMessage)
|
|
1286
|
-
blockMessage = await new BlockMessage(blockMessage)
|
|
1287
|
-
localBlock = { ...blockMessage.decoded, hash: localBlockHash }
|
|
1241
|
+
console.log("State init after has lastBlock found");
|
|
1242
|
+
console.log(rawBlock);
|
|
1243
|
+
localBlockHash = new TextDecoder().decode(await globalThis.chainStore.get("lastBlock"));
|
|
1244
|
+
console.log(localBlockHash);
|
|
1245
|
+
if (localBlockHash !== "0x0") {
|
|
1246
|
+
blockMessage = await globalThis.blockStore.get(localBlockHash);
|
|
1247
|
+
console.log(blockMessage);
|
|
1248
|
+
blockMessage = await new BlockMessage(blockMessage);
|
|
1249
|
+
localBlock = { ...blockMessage.decoded, hash: localBlockHash };
|
|
1288
1250
|
}
|
|
1289
|
-
console.log(
|
|
1251
|
+
console.log("State init after localBlock set");
|
|
1290
1252
|
} else {
|
|
1291
|
-
localBlock = { index: 0, hash:
|
|
1253
|
+
localBlock = { index: 0, hash: "0x0", previousHash: "0x0" };
|
|
1292
1254
|
}
|
|
1293
1255
|
} catch {
|
|
1294
|
-
console.log(
|
|
1295
|
-
console.log(
|
|
1296
|
-
localBlock = { index: 0, hash:
|
|
1256
|
+
console.log("e");
|
|
1257
|
+
console.log("State init middle");
|
|
1258
|
+
localBlock = { index: 0, hash: "0x0", previousHash: "0x0" };
|
|
1297
1259
|
}
|
|
1298
|
-
console.log(
|
|
1260
|
+
console.log("State init middle");
|
|
1299
1261
|
try {
|
|
1300
|
-
console.log(
|
|
1301
|
-
this.knownBlocks = await blockStore.keys()
|
|
1262
|
+
console.log("fetching known blocks from blockStore");
|
|
1263
|
+
this.knownBlocks = await blockStore.keys();
|
|
1302
1264
|
} catch (error) {
|
|
1303
|
-
console.log(
|
|
1265
|
+
console.log("no local known blocks found");
|
|
1304
1266
|
}
|
|
1305
1267
|
try {
|
|
1306
|
-
if (localBlock?.hash && localBlock.hash !==
|
|
1268
|
+
if (localBlock?.hash && localBlock.hash !== "0x0") {
|
|
1307
1269
|
try {
|
|
1308
|
-
if (!
|
|
1309
|
-
await this.resolveBlocks()
|
|
1270
|
+
if (!await globalThis.stateStore.has("lastBlock")) {
|
|
1271
|
+
await this.resolveBlocks();
|
|
1310
1272
|
}
|
|
1311
1273
|
} catch {
|
|
1312
|
-
await this.resolveBlocks()
|
|
1274
|
+
await this.resolveBlocks();
|
|
1313
1275
|
}
|
|
1314
1276
|
} else {
|
|
1315
|
-
await this.resolveBlocks()
|
|
1277
|
+
await this.resolveBlocks();
|
|
1316
1278
|
}
|
|
1317
|
-
const machine = new Machine(this.#blocks)
|
|
1318
|
-
console.log(machine)
|
|
1319
|
-
await machine.ready
|
|
1320
|
-
this.#machine = machine
|
|
1321
|
-
const lastBlock = await this.#machine.lastBlock
|
|
1322
|
-
if (lastBlock.hash !==
|
|
1323
|
-
this.updateState(new BlockMessage(lastBlock))
|
|
1279
|
+
const machine = new Machine(this.#blocks);
|
|
1280
|
+
console.log(machine);
|
|
1281
|
+
await machine.ready;
|
|
1282
|
+
this.#machine = machine;
|
|
1283
|
+
const lastBlock = await this.#machine.lastBlock;
|
|
1284
|
+
if (lastBlock.hash !== "0x0") {
|
|
1285
|
+
this.updateState(new BlockMessage(lastBlock));
|
|
1324
1286
|
}
|
|
1325
|
-
this.#loaded = true
|
|
1287
|
+
this.#loaded = true;
|
|
1326
1288
|
} catch (error) {
|
|
1327
|
-
console.log(
|
|
1289
|
+
console.log("e");
|
|
1328
1290
|
if (isResolveError(error)) {
|
|
1329
|
-
console.error(error)
|
|
1291
|
+
console.error(error);
|
|
1330
1292
|
}
|
|
1331
|
-
console.log(error)
|
|
1293
|
+
console.log(error);
|
|
1332
1294
|
}
|
|
1333
1295
|
}
|
|
1334
1296
|
async updateState(message) {
|
|
1335
1297
|
try {
|
|
1336
|
-
const hash = await message.hash()
|
|
1337
|
-
await globalThis.chainStore.put(
|
|
1338
|
-
globalThis.pubsub.publish(
|
|
1298
|
+
const hash = await message.hash();
|
|
1299
|
+
await globalThis.chainStore.put("lastBlock", hash);
|
|
1300
|
+
globalThis.pubsub.publish("lastBlock", message.encoded);
|
|
1339
1301
|
if (!this.#machine) {
|
|
1340
|
-
const machine = new Machine(this.#blocks)
|
|
1341
|
-
console.log(machine)
|
|
1342
|
-
await machine.ready
|
|
1343
|
-
this.#machine = machine
|
|
1302
|
+
const machine = new Machine(this.#blocks);
|
|
1303
|
+
console.log(machine);
|
|
1304
|
+
await machine.ready;
|
|
1305
|
+
this.#machine = machine;
|
|
1344
1306
|
}
|
|
1345
|
-
await this.#machine.updateState()
|
|
1307
|
+
await this.#machine.updateState();
|
|
1346
1308
|
} catch (error) {
|
|
1347
|
-
console.error(error)
|
|
1309
|
+
console.error(error);
|
|
1348
1310
|
}
|
|
1349
1311
|
}
|
|
1350
1312
|
getLatestBlock() {
|
|
1351
|
-
return this.#getLatestBlock()
|
|
1313
|
+
return this.#getLatestBlock();
|
|
1352
1314
|
}
|
|
1353
1315
|
async getAndPutBlock(hash) {
|
|
1354
|
-
let block = await globalThis.peernet.get(hash,
|
|
1316
|
+
let block = await globalThis.peernet.get(hash, "block");
|
|
1355
1317
|
if (block !== void 0) {
|
|
1356
1318
|
if (!(block instanceof Uint8Array)) {
|
|
1357
|
-
block = new Uint8Array(Object.values(block))
|
|
1319
|
+
block = new Uint8Array(Object.values(block));
|
|
1358
1320
|
}
|
|
1359
|
-
block = await new BlockMessage(block)
|
|
1360
|
-
const { index } = block.decoded
|
|
1361
|
-
if (this.#blocks[index] && this.#blocks[index].hash !== block.hash) throw `invalid block ${hash} @${index}
|
|
1362
|
-
if (!
|
|
1321
|
+
block = await new BlockMessage(block);
|
|
1322
|
+
const { index } = block.decoded;
|
|
1323
|
+
if (this.#blocks[index] && this.#blocks[index].hash !== block.hash) throw `invalid block ${hash} @${index}`;
|
|
1324
|
+
if (!await globalThis.peernet.has(hash)) await globalThis.peernet.put(hash, block.encoded, "block");
|
|
1363
1325
|
}
|
|
1364
|
-
return block
|
|
1326
|
+
return block;
|
|
1365
1327
|
}
|
|
1366
1328
|
async #resolveTransactions(transactions) {
|
|
1367
1329
|
await Promise.all(
|
|
1368
|
-
transactions
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
const
|
|
1372
|
-
if (!
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
})
|
|
1380
|
-
)
|
|
1330
|
+
transactions.filter((hash) => Boolean(hash)).map(async (hash) => {
|
|
1331
|
+
const exists = await transactionStore.has(hash);
|
|
1332
|
+
if (!exists) {
|
|
1333
|
+
const data = await peernet.get(hash, "transaction");
|
|
1334
|
+
if (!data) throw new Error(`missing transaction data for ${hash}`);
|
|
1335
|
+
await transactionStore.put(hash, data);
|
|
1336
|
+
}
|
|
1337
|
+
const inPool = await transactionPoolStore.has(hash);
|
|
1338
|
+
if (inPool) await transactionPoolStore.delete(hash);
|
|
1339
|
+
})
|
|
1340
|
+
);
|
|
1381
1341
|
}
|
|
1382
1342
|
async #resolveBlock(hash) {
|
|
1383
|
-
let index = this.#blockHashMap.get(hash)
|
|
1384
|
-
let localHash =
|
|
1343
|
+
let index = this.#blockHashMap.get(hash);
|
|
1344
|
+
let localHash = "0x0";
|
|
1385
1345
|
try {
|
|
1386
|
-
localHash = await globalThis.stateStore.get(
|
|
1346
|
+
localHash = await globalThis.stateStore.get("lastBlock");
|
|
1387
1347
|
} catch (error) {
|
|
1388
|
-
debug$1(
|
|
1348
|
+
debug$1("no local state found");
|
|
1389
1349
|
}
|
|
1390
1350
|
if (this.#blocks[index]) {
|
|
1391
|
-
const previousHash = this.#blocks[index].previousHash
|
|
1392
|
-
if (previousHash === localHash) return
|
|
1393
|
-
if (previousHash !==
|
|
1394
|
-
return this.resolveBlock(previousHash)
|
|
1351
|
+
const previousHash = this.#blocks[index].previousHash;
|
|
1352
|
+
if (previousHash === localHash) return;
|
|
1353
|
+
if (previousHash !== "0x0") {
|
|
1354
|
+
return this.resolveBlock(previousHash);
|
|
1395
1355
|
} else {
|
|
1396
|
-
return
|
|
1356
|
+
return;
|
|
1397
1357
|
}
|
|
1398
1358
|
}
|
|
1399
1359
|
try {
|
|
1400
|
-
const block = await this.getAndPutBlock(hash)
|
|
1401
|
-
const promises = []
|
|
1402
|
-
if (block.decoded.previousHash !==
|
|
1403
|
-
promises.push(this.resolveBlock(block.decoded.previousHash))
|
|
1404
|
-
}
|
|
1405
|
-
promises.push(this.#resolveTransactions(block.decoded.transactions))
|
|
1406
|
-
await Promise.all(promises)
|
|
1407
|
-
index = block.decoded.index
|
|
1408
|
-
const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength
|
|
1409
|
-
this.#totalSize += size
|
|
1410
|
-
this.#blocks[index] = { hash, ...block.decoded }
|
|
1411
|
-
this.#blockHashMap.set(hash, index)
|
|
1412
|
-
debug$1(`resolved block: ${hash} @${index} ${formatBytes(size)}`)
|
|
1413
|
-
globalThis.pubsub.publish(
|
|
1414
|
-
this.#lastResolved = this.#blocks[index]
|
|
1415
|
-
this.#lastResolvedTime = Date.now()
|
|
1360
|
+
const block = await this.getAndPutBlock(hash);
|
|
1361
|
+
const promises = [];
|
|
1362
|
+
if (block.decoded.previousHash !== "0x0" && block.decoded.previousHash !== localHash) {
|
|
1363
|
+
promises.push(this.resolveBlock(block.decoded.previousHash));
|
|
1364
|
+
}
|
|
1365
|
+
promises.push(this.#resolveTransactions(block.decoded.transactions));
|
|
1366
|
+
await Promise.all(promises);
|
|
1367
|
+
index = block.decoded.index;
|
|
1368
|
+
const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
|
|
1369
|
+
this.#totalSize += size;
|
|
1370
|
+
this.#blocks[index] = { hash, ...block.decoded };
|
|
1371
|
+
this.#blockHashMap.set(hash, index);
|
|
1372
|
+
debug$1(`resolved block: ${hash} @${index} ${formatBytes(size)}`);
|
|
1373
|
+
globalThis.pubsub.publish("block-resolved", { hash, index });
|
|
1374
|
+
this.#lastResolved = this.#blocks[index];
|
|
1375
|
+
this.#lastResolvedTime = Date.now();
|
|
1416
1376
|
} catch (error) {
|
|
1417
|
-
throw new ResolveError(`block: ${hash}@${index}`, { cause: error })
|
|
1377
|
+
throw new ResolveError(`block: ${hash}@${index}`, { cause: error });
|
|
1418
1378
|
}
|
|
1419
|
-
return
|
|
1379
|
+
return;
|
|
1420
1380
|
}
|
|
1421
1381
|
async resolveBlock(hash) {
|
|
1422
|
-
if (!hash) throw new Error(`expected hash, got: ${hash}`)
|
|
1423
|
-
if (hash ===
|
|
1424
|
-
if (this.#resolvingHashes.has(hash)) return
|
|
1425
|
-
this.#resolvingHashes.add(hash)
|
|
1426
|
-
const isEntering = this.#resolvingHashes.size === 1
|
|
1427
|
-
this.#resolving = true
|
|
1382
|
+
if (!hash) throw new Error(`expected hash, got: ${hash}`);
|
|
1383
|
+
if (hash === "0x0") return;
|
|
1384
|
+
if (this.#resolvingHashes.has(hash)) return;
|
|
1385
|
+
this.#resolvingHashes.add(hash);
|
|
1386
|
+
const isEntering = this.#resolvingHashes.size === 1;
|
|
1387
|
+
this.#resolving = true;
|
|
1428
1388
|
try {
|
|
1429
1389
|
if (isEntering) {
|
|
1430
|
-
if (this.jobber.busy && this.jobber.destroy) await this.jobber.destroy()
|
|
1431
|
-
await this.jobber.add(() => this.#resolveBlock(hash))
|
|
1390
|
+
if (this.jobber.busy && this.jobber.destroy) await this.jobber.destroy();
|
|
1391
|
+
await this.jobber.add(() => this.#resolveBlock(hash));
|
|
1432
1392
|
} else {
|
|
1433
|
-
await this.#resolveBlock(hash)
|
|
1393
|
+
await this.#resolveBlock(hash);
|
|
1434
1394
|
}
|
|
1435
1395
|
try {
|
|
1436
|
-
const lastBlockHash = await globalThis.stateStore.get(
|
|
1396
|
+
const lastBlockHash = await globalThis.stateStore.get("lastBlock");
|
|
1437
1397
|
if (lastBlockHash === hash) {
|
|
1438
|
-
this.#resolveErrored = false
|
|
1439
|
-
return
|
|
1398
|
+
this.#resolveErrored = false;
|
|
1399
|
+
return;
|
|
1440
1400
|
}
|
|
1441
|
-
} catch (error) {
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
}
|
|
1442
1403
|
} catch (error) {
|
|
1443
|
-
console.log({ error })
|
|
1444
|
-
this.#resolveErrorCount += 1
|
|
1404
|
+
console.log({ error });
|
|
1405
|
+
this.#resolveErrorCount += 1;
|
|
1445
1406
|
if (this.#resolveErrorCount < 3) {
|
|
1446
|
-
this.#resolvingHashes.delete(hash)
|
|
1447
|
-
return this.resolveBlock(hash)
|
|
1407
|
+
this.#resolvingHashes.delete(hash);
|
|
1408
|
+
return this.resolveBlock(hash);
|
|
1448
1409
|
}
|
|
1449
|
-
this.#resolveErrorCount = 0
|
|
1450
|
-
this.wantList.push(hash)
|
|
1451
|
-
throw new ResolveError(`block: ${hash}`, { cause: error })
|
|
1410
|
+
this.#resolveErrorCount = 0;
|
|
1411
|
+
this.wantList.push(hash);
|
|
1412
|
+
throw new ResolveError(`block: ${hash}`, { cause: error });
|
|
1452
1413
|
} finally {
|
|
1453
|
-
this.#resolvingHashes.delete(hash)
|
|
1454
|
-
if (this.#resolvingHashes.size === 0) this.#resolving = false
|
|
1414
|
+
this.#resolvingHashes.delete(hash);
|
|
1415
|
+
if (this.#resolvingHashes.size === 0) this.#resolving = false;
|
|
1455
1416
|
}
|
|
1456
1417
|
}
|
|
1457
1418
|
async resolveBlocks() {
|
|
1458
1419
|
if (this.#chainSyncing || this.#resolving) {
|
|
1459
|
-
debug$1(
|
|
1460
|
-
return
|
|
1420
|
+
debug$1("Already syncing or resolving, skipping resolveBlocks()");
|
|
1421
|
+
return;
|
|
1461
1422
|
}
|
|
1462
1423
|
try {
|
|
1463
1424
|
if (this.jobber.busy && this.jobber.destroy) {
|
|
1464
|
-
await this.jobber.destroy()
|
|
1425
|
+
await this.jobber.destroy();
|
|
1465
1426
|
}
|
|
1466
1427
|
} catch (error) {
|
|
1467
|
-
console.error(error)
|
|
1428
|
+
console.error(error);
|
|
1468
1429
|
}
|
|
1469
1430
|
try {
|
|
1470
|
-
const localBlock = await globalThis.chainStore.get(
|
|
1471
|
-
const hash = new TextDecoder().decode(localBlock)
|
|
1472
|
-
if (hash && hash !==
|
|
1473
|
-
debug$1(`Resolving blocks from hash: ${hash}`)
|
|
1474
|
-
await this.resolveBlock(hash)
|
|
1431
|
+
const localBlock = await globalThis.chainStore.get("lastBlock");
|
|
1432
|
+
const hash = new TextDecoder().decode(localBlock);
|
|
1433
|
+
if (hash && hash !== "0x0") {
|
|
1434
|
+
debug$1(`Resolving blocks from hash: ${hash}`);
|
|
1435
|
+
await this.resolveBlock(hash);
|
|
1475
1436
|
}
|
|
1476
1437
|
} catch (error) {
|
|
1477
|
-
console.log(error)
|
|
1478
|
-
this.#chainSyncing = false
|
|
1479
|
-
this.#syncState =
|
|
1480
|
-
this.#resolveErrored = true
|
|
1481
|
-
return this.restoreChain()
|
|
1438
|
+
console.log(error);
|
|
1439
|
+
this.#chainSyncing = false;
|
|
1440
|
+
this.#syncState = "errored";
|
|
1441
|
+
this.#resolveErrored = true;
|
|
1442
|
+
return this.restoreChain();
|
|
1482
1443
|
}
|
|
1483
1444
|
}
|
|
1484
1445
|
async restoreChain() {
|
|
1485
1446
|
try {
|
|
1486
|
-
const { hash } = await this.#getLatestBlock()
|
|
1487
|
-
await globalThis.chainStore.put(
|
|
1488
|
-
if (hash && hash !==
|
|
1489
|
-
await this.resolveBlock(hash)
|
|
1447
|
+
const { hash } = await this.#getLatestBlock();
|
|
1448
|
+
await globalThis.chainStore.put("lastBlock", hash);
|
|
1449
|
+
if (hash && hash !== "0x0") {
|
|
1450
|
+
await this.resolveBlock(hash);
|
|
1490
1451
|
}
|
|
1491
1452
|
} catch (error) {
|
|
1492
|
-
console.log(error)
|
|
1493
|
-
this.#resolveErrored = true
|
|
1494
|
-
this.#resolveErrorCount += 1
|
|
1495
|
-
this.#resolving = false
|
|
1496
|
-
return this.restoreChain()
|
|
1453
|
+
console.log(error);
|
|
1454
|
+
this.#resolveErrored = true;
|
|
1455
|
+
this.#resolveErrorCount += 1;
|
|
1456
|
+
this.#resolving = false;
|
|
1457
|
+
return this.restoreChain();
|
|
1497
1458
|
}
|
|
1498
1459
|
}
|
|
1499
1460
|
async syncChain(lastBlock) {
|
|
1500
|
-
console.log(
|
|
1501
|
-
if (!this.shouldSync) return
|
|
1502
|
-
console.log(
|
|
1503
|
-
this.#syncState =
|
|
1504
|
-
this.#chainSyncing = true
|
|
1461
|
+
console.log("check if can sync");
|
|
1462
|
+
if (!this.shouldSync) return;
|
|
1463
|
+
console.log("starting sync");
|
|
1464
|
+
this.#syncState = "syncing";
|
|
1465
|
+
this.#chainSyncing = true;
|
|
1505
1466
|
try {
|
|
1506
1467
|
if (this.jobber.busy && this.jobber.destroy) {
|
|
1507
|
-
await this.jobber.destroy()
|
|
1468
|
+
await this.jobber.destroy();
|
|
1508
1469
|
}
|
|
1509
1470
|
} catch (error) {
|
|
1510
|
-
console.error(error)
|
|
1471
|
+
console.error(error);
|
|
1511
1472
|
}
|
|
1512
|
-
if (!lastBlock) lastBlock = await this.#getLatestBlock()
|
|
1513
|
-
if (globalThis.peernet.peers.length === 0) return
|
|
1473
|
+
if (!lastBlock) lastBlock = await this.#getLatestBlock();
|
|
1474
|
+
if (globalThis.peernet.peers.length === 0) return "connectionless";
|
|
1514
1475
|
try {
|
|
1515
|
-
await this.#syncChain(lastBlock)
|
|
1476
|
+
await this.#syncChain(lastBlock);
|
|
1516
1477
|
} catch (error) {
|
|
1517
|
-
this.#syncErrorCount += 1
|
|
1518
|
-
if (this.#syncErrorCount < 3) return this.syncChain(lastBlock)
|
|
1519
|
-
this.#syncErrorCount = 0
|
|
1520
|
-
this.#chainSyncing = false
|
|
1521
|
-
this.#syncState =
|
|
1522
|
-
return this.#syncState
|
|
1523
|
-
}
|
|
1524
|
-
if (lastBlock.index === this.#lastBlockInQue?.index) this.#lastBlockInQue = void 0
|
|
1525
|
-
this.#syncErrorCount = 0
|
|
1526
|
-
this.#chainSyncing = false
|
|
1527
|
-
if (this.#lastBlockInQue) return this.syncChain(this.#lastBlockInQue)
|
|
1528
|
-
this.#syncState =
|
|
1529
|
-
return this.#syncState
|
|
1478
|
+
this.#syncErrorCount += 1;
|
|
1479
|
+
if (this.#syncErrorCount < 3) return this.syncChain(lastBlock);
|
|
1480
|
+
this.#syncErrorCount = 0;
|
|
1481
|
+
this.#chainSyncing = false;
|
|
1482
|
+
this.#syncState = "errored";
|
|
1483
|
+
return this.#syncState;
|
|
1484
|
+
}
|
|
1485
|
+
if (lastBlock.index === this.#lastBlockInQue?.index) this.#lastBlockInQue = void 0;
|
|
1486
|
+
this.#syncErrorCount = 0;
|
|
1487
|
+
this.#chainSyncing = false;
|
|
1488
|
+
if (this.#lastBlockInQue) return this.syncChain(this.#lastBlockInQue);
|
|
1489
|
+
this.#syncState = "synced";
|
|
1490
|
+
return this.#syncState;
|
|
1530
1491
|
}
|
|
1531
1492
|
async #syncChain(lastBlock) {
|
|
1532
1493
|
try {
|
|
1533
|
-
const localBlock = await this.lastBlock
|
|
1534
|
-
const localIndex = localBlock ? Number(localBlock.index) : -1
|
|
1535
|
-
const remoteIndex = Number(lastBlock.index)
|
|
1536
|
-
const remoteBlockHash = lastBlock.hash
|
|
1537
|
-
let localStateHash =
|
|
1494
|
+
const localBlock = await this.lastBlock;
|
|
1495
|
+
const localIndex = localBlock ? Number(localBlock.index) : -1;
|
|
1496
|
+
const remoteIndex = Number(lastBlock.index);
|
|
1497
|
+
const remoteBlockHash = lastBlock.hash;
|
|
1498
|
+
let localStateHash = "0x0";
|
|
1538
1499
|
try {
|
|
1539
|
-
localStateHash = new TextDecoder().decode(await globalThis.chainStore.get(
|
|
1500
|
+
localStateHash = new TextDecoder().decode(await globalThis.chainStore.get("lastBlock"));
|
|
1540
1501
|
} catch (error) {
|
|
1541
|
-
debug$1(`No local state hash found: ${error}`)
|
|
1502
|
+
debug$1(`No local state hash found: ${error}`);
|
|
1542
1503
|
}
|
|
1543
|
-
debug$1(`Local block height: ${localIndex}, remote block height: ${remoteIndex}`)
|
|
1544
|
-
debug$1(`Local state hash: ${localStateHash}, remote block hash: ${remoteBlockHash}`)
|
|
1545
|
-
if (remoteBlockHash ===
|
|
1546
|
-
debug$1(`Remote block hash is 0x0, skipping sync`)
|
|
1547
|
-
return
|
|
1504
|
+
debug$1(`Local block height: ${localIndex}, remote block height: ${remoteIndex}`);
|
|
1505
|
+
debug$1(`Local state hash: ${localStateHash}, remote block hash: ${remoteBlockHash}`);
|
|
1506
|
+
if (remoteBlockHash === "0x0") {
|
|
1507
|
+
debug$1(`Remote block hash is 0x0, skipping sync`);
|
|
1508
|
+
return;
|
|
1548
1509
|
}
|
|
1549
1510
|
if (localIndex > remoteIndex) {
|
|
1550
|
-
debug$1(`Local index ${localIndex} is ahead of remote ${remoteIndex}, skipping sync`)
|
|
1551
|
-
return
|
|
1511
|
+
debug$1(`Local index ${localIndex} is ahead of remote ${remoteIndex}, skipping sync`);
|
|
1512
|
+
return;
|
|
1552
1513
|
}
|
|
1553
|
-
const MAX_REORG_DEPTH = 6
|
|
1554
|
-
const reorgDepth = localIndex - remoteIndex
|
|
1514
|
+
const MAX_REORG_DEPTH = 6;
|
|
1515
|
+
const reorgDepth = localIndex - remoteIndex;
|
|
1555
1516
|
if (reorgDepth > 0 && reorgDepth > MAX_REORG_DEPTH) {
|
|
1556
1517
|
console.warn(
|
|
1557
1518
|
`[consensus-safety] Peer proposing reorg depth of ${reorgDepth} blocks (limit is ${MAX_REORG_DEPTH}). Rejecting to prevent DoS.`
|
|
1558
|
-
)
|
|
1559
|
-
throw new Error(`Excessive reorg depth: ${reorgDepth} blocks (max ${MAX_REORG_DEPTH})`)
|
|
1519
|
+
);
|
|
1520
|
+
throw new Error(`Excessive reorg depth: ${reorgDepth} blocks (max ${MAX_REORG_DEPTH})`);
|
|
1560
1521
|
}
|
|
1561
1522
|
if (localStateHash !== remoteBlockHash) {
|
|
1562
1523
|
if (this.wantList.length > 0) {
|
|
1563
|
-
debug$1(`Fetching ${this.wantList.length} blocks before resolving`)
|
|
1524
|
+
debug$1(`Fetching ${this.wantList.length} blocks before resolving`);
|
|
1564
1525
|
const getBatch = async (batch) => {
|
|
1565
1526
|
const blocks2 = await Promise.all(
|
|
1566
|
-
batch.map(
|
|
1567
|
-
this.getAndPutBlock(hash).catch((e) => {
|
|
1568
|
-
console.warn(`failed to fetch block ${hash}`, e)
|
|
1527
|
+
batch.map(
|
|
1528
|
+
(hash) => this.getAndPutBlock(hash).catch((e) => {
|
|
1529
|
+
console.warn(`failed to fetch block ${hash}`, e);
|
|
1569
1530
|
})
|
|
1570
1531
|
)
|
|
1571
|
-
)
|
|
1572
|
-
const transactions = blocks2
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
return this.#resolveTransactions(transactions)
|
|
1576
|
-
}
|
|
1532
|
+
);
|
|
1533
|
+
const transactions = blocks2.filter((block) => Boolean(block)).flatMap((block) => block.decoded.transactions);
|
|
1534
|
+
return this.#resolveTransactions(transactions);
|
|
1535
|
+
};
|
|
1577
1536
|
for (let i = 0; i < this.wantList.length; i += 50) {
|
|
1578
|
-
const batch = this.wantList.slice(i, i + 50)
|
|
1579
|
-
await getBatch(batch)
|
|
1537
|
+
const batch = this.wantList.slice(i, i + 50);
|
|
1538
|
+
await getBatch(batch);
|
|
1580
1539
|
}
|
|
1581
1540
|
}
|
|
1582
|
-
debug$1(`Resolving remote block: ${remoteBlockHash} @${remoteIndex} (differs from local state)`)
|
|
1583
|
-
await this.resolveBlock(remoteBlockHash)
|
|
1584
|
-
const blocksSynced = remoteIndex - localIndex
|
|
1585
|
-
debug$1(`Resolved ${blocksSynced} new block(s)`)
|
|
1586
|
-
const blocks = this.#blocks
|
|
1587
|
-
debug$1(`Loading blocks from index ${localIndex + 1} to ${remoteIndex}`)
|
|
1588
|
-
const start = localIndex + 1
|
|
1541
|
+
debug$1(`Resolving remote block: ${remoteBlockHash} @${remoteIndex} (differs from local state)`);
|
|
1542
|
+
await this.resolveBlock(remoteBlockHash);
|
|
1543
|
+
const blocksSynced = remoteIndex - localIndex;
|
|
1544
|
+
debug$1(`Resolved ${blocksSynced} new block(s)`);
|
|
1545
|
+
const blocks = this.#blocks;
|
|
1546
|
+
debug$1(`Loading blocks from index ${localIndex + 1} to ${remoteIndex}`);
|
|
1547
|
+
const start = localIndex + 1;
|
|
1589
1548
|
if (this.#machine && blocks.length > start) {
|
|
1590
|
-
await this.#loadBlocks(blocks.slice(start))
|
|
1549
|
+
await this.#loadBlocks(blocks.slice(start));
|
|
1591
1550
|
}
|
|
1592
1551
|
if (blocks.length > 0) {
|
|
1593
|
-
await this.updateState(new BlockMessage(blocks[blocks.length - 1]))
|
|
1552
|
+
await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
|
|
1594
1553
|
}
|
|
1595
1554
|
} else {
|
|
1596
|
-
debug$1(`Block already in local state. Remote hash: ${remoteBlockHash} matches local state`)
|
|
1555
|
+
debug$1(`Block already in local state. Remote hash: ${remoteBlockHash} matches local state`);
|
|
1597
1556
|
}
|
|
1598
1557
|
} catch (error) {
|
|
1599
|
-
console.log(error)
|
|
1600
|
-
throw error
|
|
1558
|
+
console.log(error);
|
|
1559
|
+
throw error;
|
|
1601
1560
|
}
|
|
1602
1561
|
}
|
|
1603
1562
|
async #getLatestBlock() {
|
|
1604
|
-
let promises = []
|
|
1605
|
-
const connectedPeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected)
|
|
1606
|
-
let compatiblePeerCount = 0
|
|
1607
|
-
let data = await new globalThis.peernet.protos[
|
|
1608
|
-
request:
|
|
1609
|
-
})
|
|
1610
|
-
let node = await globalThis.peernet.prepareMessage(data)
|
|
1563
|
+
let promises = [];
|
|
1564
|
+
const connectedPeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected);
|
|
1565
|
+
let compatiblePeerCount = 0;
|
|
1566
|
+
let data = await new globalThis.peernet.protos["peernet-request"]({
|
|
1567
|
+
request: "lastBlock"
|
|
1568
|
+
});
|
|
1569
|
+
let node = await globalThis.peernet.prepareMessage(data);
|
|
1611
1570
|
for (const id in globalThis.peernet.connections) {
|
|
1612
|
-
const peer = globalThis.peernet.connections[id]
|
|
1571
|
+
const peer = globalThis.peernet.connections[id];
|
|
1613
1572
|
if (peer.connected && this.isVersionCompatible(peer.version)) {
|
|
1614
|
-
compatiblePeerCount += 1
|
|
1573
|
+
compatiblePeerCount += 1;
|
|
1615
1574
|
const task = async () => {
|
|
1616
1575
|
try {
|
|
1617
|
-
const result = await peer.request(node.encoded)
|
|
1618
|
-
const resultType = result instanceof Uint8Array ? `bytes:${result.length}` : typeof result
|
|
1619
|
-
debug$1(`lastBlock result type: ${resultType}`)
|
|
1620
|
-
console.log({ result })
|
|
1621
|
-
return { result: new LastBlockMessage(result), peer }
|
|
1576
|
+
const result = await peer.request(node.encoded);
|
|
1577
|
+
const resultType = result instanceof Uint8Array ? `bytes:${result.length}` : typeof result;
|
|
1578
|
+
debug$1(`lastBlock result type: ${resultType}`);
|
|
1579
|
+
console.log({ result });
|
|
1580
|
+
return { result: new LastBlockMessage(result), peer };
|
|
1622
1581
|
} catch (error) {
|
|
1623
|
-
const peerId = peer?.peerId || peer?.id || peer?.address ||
|
|
1624
|
-
debug$1(`lastBlock request failed: ${peerId}:`, error?.message ?? error)
|
|
1625
|
-
throw error
|
|
1582
|
+
const peerId = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
1583
|
+
debug$1(`lastBlock request failed: ${peerId}:`, error?.message ?? error);
|
|
1584
|
+
throw error;
|
|
1626
1585
|
}
|
|
1627
|
-
}
|
|
1628
|
-
promises.push(task())
|
|
1586
|
+
};
|
|
1587
|
+
promises.push(task());
|
|
1629
1588
|
}
|
|
1630
1589
|
}
|
|
1631
1590
|
if (connectedPeers.length > 0 && compatiblePeerCount === 0) {
|
|
1632
1591
|
throw new ResolveError(
|
|
1633
1592
|
`latestBlock: no compatible peers found for local version ${this.version} among ${connectedPeers.length} connected peers`
|
|
1634
|
-
)
|
|
1593
|
+
);
|
|
1635
1594
|
}
|
|
1636
|
-
console.log({ promises })
|
|
1637
|
-
promises = await this.promiseRequests(promises)
|
|
1595
|
+
console.log({ promises });
|
|
1596
|
+
promises = await this.promiseRequests(promises);
|
|
1638
1597
|
if (compatiblePeerCount > 0 && promises.length === 0) {
|
|
1639
|
-
throw new ResolveError(
|
|
1640
|
-
}
|
|
1641
|
-
console.log({ promises })
|
|
1642
|
-
let latest = { index: 0, hash:
|
|
1643
|
-
promises = promises.sort((a, b) => b.index - a.index)
|
|
1644
|
-
if (promises.length > 0) latest = promises[0].value
|
|
1645
|
-
debug$1(`Latest block from peers: ${latest.hash} @${latest.index}`)
|
|
1646
|
-
if (latest.hash && latest.hash !==
|
|
1647
|
-
let message = await globalThis.peernet.get(latest.hash,
|
|
1648
|
-
message = await new BlockMessage(message)
|
|
1649
|
-
const hash = await message.hash()
|
|
1650
|
-
if (hash !== latest.hash) throw new Error(
|
|
1651
|
-
latest = { ...message.decoded, hash }
|
|
1652
|
-
const peer = promises[0].peer
|
|
1598
|
+
throw new ResolveError("latestBlock: no responses from compatible peers");
|
|
1599
|
+
}
|
|
1600
|
+
console.log({ promises });
|
|
1601
|
+
let latest = { index: 0, hash: "0x0", previousHash: "0x0" };
|
|
1602
|
+
promises = promises.sort((a, b) => b.index - a.index);
|
|
1603
|
+
if (promises.length > 0) latest = promises[0].value;
|
|
1604
|
+
debug$1(`Latest block from peers: ${latest.hash} @${latest.index}`);
|
|
1605
|
+
if (latest.hash && latest.hash !== "0x0") {
|
|
1606
|
+
let message = await globalThis.peernet.get(latest.hash, "block");
|
|
1607
|
+
message = await new BlockMessage(message);
|
|
1608
|
+
const hash = await message.hash();
|
|
1609
|
+
if (hash !== latest.hash) throw new Error("invalid block @getLatestBlock");
|
|
1610
|
+
latest = { ...message.decoded, hash };
|
|
1611
|
+
const peer = promises[0].peer;
|
|
1653
1612
|
if (peer.connected && this.isVersionCompatible(peer.version)) {
|
|
1654
|
-
let data2 = await new globalThis.peernet.protos[
|
|
1655
|
-
request:
|
|
1656
|
-
})
|
|
1657
|
-
let node2 = await globalThis.peernet.prepareMessage(data2)
|
|
1613
|
+
let data2 = await new globalThis.peernet.protos["peernet-request"]({
|
|
1614
|
+
request: "knownBlocks"
|
|
1615
|
+
});
|
|
1616
|
+
let node2 = await globalThis.peernet.prepareMessage(data2);
|
|
1658
1617
|
try {
|
|
1659
|
-
let message2 = await peer.request(node2.encode())
|
|
1660
|
-
message2 = await new globalThis.peernet.protos[
|
|
1661
|
-
const MAX_WANTLIST_SIZE = 1e3
|
|
1662
|
-
const incoming = message2.decoded.response.blocks.filter((block) => !this.knownBlocks.includes(block))
|
|
1663
|
-
const remaining = MAX_WANTLIST_SIZE - this.wantList.length
|
|
1664
|
-
if (remaining > 0) this.wantList.push(...incoming.slice(0, remaining))
|
|
1618
|
+
let message2 = await peer.request(node2.encode());
|
|
1619
|
+
message2 = await new globalThis.peernet.protos["peernet-response"](message2);
|
|
1620
|
+
const MAX_WANTLIST_SIZE = 1e3;
|
|
1621
|
+
const incoming = message2.decoded.response.blocks.filter((block) => !this.knownBlocks.includes(block));
|
|
1622
|
+
const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
|
|
1623
|
+
if (remaining > 0) this.wantList.push(...incoming.slice(0, remaining));
|
|
1665
1624
|
} catch (error) {
|
|
1666
|
-
const peerId = peer?.peerId || peer?.id || peer?.address ||
|
|
1667
|
-
debug$1(`knownBlocks request failed: ${peerId}:`, error?.message ?? error)
|
|
1668
|
-
throw error
|
|
1625
|
+
const peerId = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
1626
|
+
debug$1(`knownBlocks request failed: ${peerId}:`, error?.message ?? error);
|
|
1627
|
+
throw error;
|
|
1669
1628
|
}
|
|
1670
1629
|
}
|
|
1671
1630
|
}
|
|
1672
|
-
return latest
|
|
1631
|
+
return latest;
|
|
1673
1632
|
}
|
|
1674
|
-
#loadBlockTransactions
|
|
1675
|
-
#getLastTransactions
|
|
1633
|
+
#loadBlockTransactions;
|
|
1634
|
+
#getLastTransactions;
|
|
1676
1635
|
// todo throw error
|
|
1677
1636
|
async #_executeTransaction(transaction) {
|
|
1678
1637
|
try {
|
|
1679
|
-
await this.#machine.execute(transaction.decoded.to, transaction.decoded.method, transaction.decoded.params)
|
|
1638
|
+
await this.#machine.execute(transaction.decoded.to, transaction.decoded.method, transaction.decoded.params);
|
|
1680
1639
|
} catch (error) {
|
|
1681
|
-
console.log(error)
|
|
1682
|
-
await globalThis.transactionPoolStore.delete(await transaction.hash())
|
|
1683
|
-
console.log(
|
|
1640
|
+
console.log(error);
|
|
1641
|
+
await globalThis.transactionPoolStore.delete(await transaction.hash());
|
|
1642
|
+
console.log("removing invalid transaction");
|
|
1684
1643
|
if (isExecutionError(error)) {
|
|
1685
|
-
console.log(error)
|
|
1644
|
+
console.log(error);
|
|
1686
1645
|
}
|
|
1687
|
-
console.log(error)
|
|
1688
|
-
return false
|
|
1646
|
+
console.log(error);
|
|
1647
|
+
return false;
|
|
1689
1648
|
}
|
|
1690
1649
|
}
|
|
1691
1650
|
/**
|
|
@@ -1693,518 +1652,515 @@ class State extends Contract {
|
|
|
1693
1652
|
* @param {Block[]} blocks
|
|
1694
1653
|
*/
|
|
1695
1654
|
async #loadBlocks(blocks) {
|
|
1696
|
-
this.#chainState =
|
|
1697
|
-
const poolTransactionKeys = new Set(await globalThis.transactionPoolStore.keys())
|
|
1698
|
-
debug$1(`pool transactions: ${poolTransactionKeys.size}`)
|
|
1699
|
-
debug$1(`loading ${blocks.length} blocks`)
|
|
1655
|
+
this.#chainState = "loading";
|
|
1656
|
+
const poolTransactionKeys = new Set(await globalThis.transactionPoolStore.keys());
|
|
1657
|
+
debug$1(`pool transactions: ${poolTransactionKeys.size}`);
|
|
1658
|
+
debug$1(`loading ${blocks.length} blocks`);
|
|
1700
1659
|
for (const block of blocks) {
|
|
1701
1660
|
if (block && !block.loaded) {
|
|
1702
1661
|
try {
|
|
1703
|
-
debug$1(`loading block: ${Number(block.index)} ${block.hash}`)
|
|
1704
|
-
let transactions = await this.#loadBlockTransactions(block.transactions || [])
|
|
1705
|
-
debug$1(`loading transactions: ${transactions.length} for block ${block.index}`)
|
|
1706
|
-
let priority = []
|
|
1662
|
+
debug$1(`loading block: ${Number(block.index)} ${block.hash}`);
|
|
1663
|
+
let transactions = await this.#loadBlockTransactions(block.transactions || []);
|
|
1664
|
+
debug$1(`loading transactions: ${transactions.length} for block ${block.index}`);
|
|
1665
|
+
let priority = [];
|
|
1707
1666
|
for (const transaction of transactions) {
|
|
1708
|
-
const hash = await transaction.hash()
|
|
1709
|
-
if (transaction.decoded.priority) priority.push(transaction)
|
|
1710
|
-
if (poolTransactionKeys.has(hash)) await globalThis.transactionPoolStore.delete(hash)
|
|
1667
|
+
const hash = await transaction.hash();
|
|
1668
|
+
if (transaction.decoded.priority) priority.push(transaction);
|
|
1669
|
+
if (poolTransactionKeys.has(hash)) await globalThis.transactionPoolStore.delete(hash);
|
|
1711
1670
|
}
|
|
1712
1671
|
if (priority.length > 0) {
|
|
1713
|
-
debug$1(`executing ${priority.length} priority transactions for block ${block.index}`)
|
|
1714
|
-
priority = priority.sort((a, b) => a.nonce - b.nonce)
|
|
1672
|
+
debug$1(`executing ${priority.length} priority transactions for block ${block.index}`);
|
|
1673
|
+
priority = priority.sort((a, b) => a.nonce - b.nonce);
|
|
1715
1674
|
for (const transaction of priority) {
|
|
1716
|
-
await this.#_executeTransaction(transaction)
|
|
1675
|
+
await this.#_executeTransaction(transaction);
|
|
1717
1676
|
}
|
|
1718
1677
|
}
|
|
1719
|
-
transactions = transactions.filter((transaction) => !transaction.decoded.priority)
|
|
1720
|
-
debug$1(`executing ${transactions.length} transactions for block ${block.index}`)
|
|
1721
|
-
await Promise.all(transactions.map((transaction) => this.#_executeTransaction(transaction)))
|
|
1722
|
-
this.#blocks[block.index].loaded = true
|
|
1723
|
-
debug$1(`executed transactions for block ${block.index}`)
|
|
1724
|
-
if (Number(block.index) === 0) this.#loaded = true
|
|
1725
|
-
await this.#machine.addLoadedBlock(block)
|
|
1726
|
-
debug$1(`loaded block: ${block.hash} @${Number(block.index)}`)
|
|
1727
|
-
globalThis.pubsub.publish(
|
|
1678
|
+
transactions = transactions.filter((transaction) => !transaction.decoded.priority);
|
|
1679
|
+
debug$1(`executing ${transactions.length} transactions for block ${block.index}`);
|
|
1680
|
+
await Promise.all(transactions.map((transaction) => this.#_executeTransaction(transaction)));
|
|
1681
|
+
this.#blocks[block.index].loaded = true;
|
|
1682
|
+
debug$1(`executed transactions for block ${block.index}`);
|
|
1683
|
+
if (Number(block.index) === 0) this.#loaded = true;
|
|
1684
|
+
await this.#machine.addLoadedBlock(block);
|
|
1685
|
+
debug$1(`loaded block: ${block.hash} @${Number(block.index)}`);
|
|
1686
|
+
globalThis.pubsub.publish("block-loaded", { ...block });
|
|
1728
1687
|
} catch (error) {
|
|
1729
|
-
console.error(error)
|
|
1688
|
+
console.error(error);
|
|
1730
1689
|
for (const transaction of block.transactions) {
|
|
1731
|
-
this.wantList.push(transaction)
|
|
1690
|
+
this.wantList.push(transaction);
|
|
1732
1691
|
}
|
|
1733
1692
|
}
|
|
1734
1693
|
}
|
|
1735
1694
|
}
|
|
1736
|
-
this.#chainState =
|
|
1737
|
-
return true
|
|
1695
|
+
this.#chainState = "loaded";
|
|
1696
|
+
return true;
|
|
1738
1697
|
}
|
|
1739
1698
|
promiseRequests(promises) {
|
|
1740
1699
|
return new Promise(async (resolve, reject) => {
|
|
1741
1700
|
const timeout = setTimeout(() => {
|
|
1742
|
-
resolve([{ index: 0, hash:
|
|
1743
|
-
debug$1(
|
|
1744
|
-
}, this.requestTimeout)
|
|
1745
|
-
console.log({ promises })
|
|
1746
|
-
promises = await Promise.allSettled(promises)
|
|
1747
|
-
console.log({ promises })
|
|
1748
|
-
promises = promises.filter(({ status }) => status ===
|
|
1749
|
-
clearTimeout(timeout)
|
|
1701
|
+
resolve([{ index: 0, hash: "0x0" }]);
|
|
1702
|
+
debug$1("sync timed out");
|
|
1703
|
+
}, this.requestTimeout);
|
|
1704
|
+
console.log({ promises });
|
|
1705
|
+
promises = await Promise.allSettled(promises);
|
|
1706
|
+
console.log({ promises });
|
|
1707
|
+
promises = promises.filter(({ status }) => status === "fulfilled");
|
|
1708
|
+
clearTimeout(timeout);
|
|
1750
1709
|
if (promises.length > 0) {
|
|
1751
1710
|
promises = promises.map(async ({ value }) => {
|
|
1752
|
-
const node = await new globalThis.peernet.protos[
|
|
1753
|
-
return { value: node.decoded.response, peer: value.peer }
|
|
1754
|
-
})
|
|
1755
|
-
promises = await Promise.all(promises)
|
|
1756
|
-
resolve(promises)
|
|
1711
|
+
const node = await new globalThis.peernet.protos["peernet-response"](value.result);
|
|
1712
|
+
return { value: node.decoded.response, peer: value.peer };
|
|
1713
|
+
});
|
|
1714
|
+
promises = await Promise.all(promises);
|
|
1715
|
+
resolve(promises);
|
|
1757
1716
|
} else {
|
|
1758
|
-
resolve([])
|
|
1717
|
+
resolve([]);
|
|
1759
1718
|
}
|
|
1760
|
-
})
|
|
1719
|
+
});
|
|
1761
1720
|
}
|
|
1762
1721
|
get canSync() {
|
|
1763
|
-
if (this.#chainSyncing) return false
|
|
1764
|
-
return true
|
|
1722
|
+
if (this.#chainSyncing) return false;
|
|
1723
|
+
return true;
|
|
1765
1724
|
}
|
|
1766
1725
|
get shouldSync() {
|
|
1767
|
-
if (this.#chainSyncing) return false
|
|
1726
|
+
if (this.#chainSyncing) return false;
|
|
1768
1727
|
const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter(
|
|
1769
1728
|
(peer) => peer.connected && this.isVersionCompatible(peer.version)
|
|
1770
|
-
)
|
|
1729
|
+
);
|
|
1771
1730
|
if (compatiblePeers.length === 0) {
|
|
1772
|
-
debug$1(
|
|
1773
|
-
return false
|
|
1774
|
-
}
|
|
1775
|
-
if (
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
this.#syncState === 'errored' ||
|
|
1779
|
-
this.#syncState === 'connectionless' ||
|
|
1780
|
-
this.#lastResolvedTime + this.resolveTimeout > Date.now()
|
|
1781
|
-
)
|
|
1782
|
-
return true
|
|
1783
|
-
return false
|
|
1731
|
+
debug$1("No compatible peers available for sync");
|
|
1732
|
+
return false;
|
|
1733
|
+
}
|
|
1734
|
+
if (!this.#chainSyncing || this.#resolveErrored || this.#syncState === "errored" || this.#syncState === "connectionless" || this.#lastResolvedTime + this.resolveTimeout > Date.now())
|
|
1735
|
+
return true;
|
|
1736
|
+
return false;
|
|
1784
1737
|
}
|
|
1785
1738
|
async #waitForPeers(timeoutMs = 3e4) {
|
|
1786
1739
|
return new Promise((resolve) => {
|
|
1787
1740
|
const checkPeers = () => {
|
|
1788
1741
|
const peers = Object.values(globalThis.peernet.connections || {}).filter(
|
|
1789
1742
|
(peer) => peer.connected && this.isVersionCompatible(peer.version)
|
|
1790
|
-
)
|
|
1743
|
+
);
|
|
1791
1744
|
if (peers.length > 0) {
|
|
1792
|
-
resolve(true)
|
|
1745
|
+
resolve(true);
|
|
1793
1746
|
}
|
|
1794
|
-
}
|
|
1795
|
-
checkPeers()
|
|
1796
|
-
const interval = setInterval(checkPeers, 1e3)
|
|
1747
|
+
};
|
|
1748
|
+
checkPeers();
|
|
1749
|
+
const interval = setInterval(checkPeers, 1e3);
|
|
1797
1750
|
setTimeout(() => {
|
|
1798
|
-
clearInterval(interval)
|
|
1799
|
-
resolve(false)
|
|
1800
|
-
}, timeoutMs)
|
|
1801
|
-
})
|
|
1751
|
+
clearInterval(interval);
|
|
1752
|
+
resolve(false);
|
|
1753
|
+
}, timeoutMs);
|
|
1754
|
+
});
|
|
1802
1755
|
}
|
|
1803
1756
|
async triggerSync() {
|
|
1804
|
-
const latest = await this.#getLatestBlock()
|
|
1805
|
-
return this.syncChain(latest)
|
|
1757
|
+
const latest = await this.#getLatestBlock();
|
|
1758
|
+
return this.syncChain(latest);
|
|
1806
1759
|
}
|
|
1807
1760
|
async triggerLoad() {
|
|
1808
1761
|
if (this.#blocks?.length > 0) {
|
|
1809
|
-
const machine = new Machine(this.#blocks)
|
|
1810
|
-
console.log(machine)
|
|
1811
|
-
await machine.ready
|
|
1812
|
-
this.#machine = machine
|
|
1762
|
+
const machine = new Machine(this.#blocks);
|
|
1763
|
+
console.log(machine);
|
|
1764
|
+
await machine.ready;
|
|
1765
|
+
this.#machine = machine;
|
|
1813
1766
|
}
|
|
1814
1767
|
}
|
|
1815
1768
|
}
|
|
1816
1769
|
|
|
1817
1770
|
class VersionControl extends State {
|
|
1818
1771
|
constructor(config) {
|
|
1819
|
-
super(config)
|
|
1772
|
+
super(config);
|
|
1820
1773
|
}
|
|
1821
|
-
#currentVersion = PROTOCOL_VERSION
|
|
1822
|
-
#reachedOneZeroZero = REACHED_ONE_ZERO_ZERO
|
|
1774
|
+
#currentVersion = PROTOCOL_VERSION;
|
|
1775
|
+
#reachedOneZeroZero = REACHED_ONE_ZERO_ZERO;
|
|
1823
1776
|
async #setCurrentVersion() {
|
|
1824
|
-
this.version = this.#currentVersion
|
|
1825
|
-
await globalThis.chainStore.put(
|
|
1777
|
+
this.version = this.#currentVersion;
|
|
1778
|
+
await globalThis.chainStore.put("version", this.version);
|
|
1826
1779
|
}
|
|
1827
1780
|
async init() {
|
|
1828
|
-
super.init &&
|
|
1781
|
+
super.init && await super.init();
|
|
1829
1782
|
try {
|
|
1830
|
-
const version = await globalThis.chainStore.get(
|
|
1831
|
-
const storedVersion = new TextDecoder().decode(version)
|
|
1832
|
-
console.log(storedVersion, this.#currentVersion)
|
|
1833
|
-
this.version = this.#currentVersion
|
|
1834
|
-
console.warn(
|
|
1835
|
-
if (semver.compare(storedVersion,
|
|
1836
|
-
console.warn(
|
|
1837
|
-
await this.clearAll()
|
|
1783
|
+
const version = await globalThis.chainStore.get("version");
|
|
1784
|
+
const storedVersion = new TextDecoder().decode(version);
|
|
1785
|
+
console.log(storedVersion, this.#currentVersion);
|
|
1786
|
+
this.version = this.#currentVersion;
|
|
1787
|
+
console.warn("the reachedZeroZero flag is set to false, this will clear all data on every start if above v1.0.0");
|
|
1788
|
+
if (semver.compare(storedVersion, "1.0.0") === 1 && !this.#reachedOneZeroZero) {
|
|
1789
|
+
console.warn("clearing all data because we are below v1.0.0");
|
|
1790
|
+
await this.clearAll();
|
|
1838
1791
|
}
|
|
1839
1792
|
if (storedVersion !== this.#currentVersion) {
|
|
1840
|
-
console.log(`Version mismatch: stored=${storedVersion}, current=${this.#currentVersion}. Updating...`)
|
|
1841
|
-
await globalThis.chainStore.put(
|
|
1793
|
+
console.log(`Version mismatch: stored=${storedVersion}, current=${this.#currentVersion}. Updating...`);
|
|
1794
|
+
await globalThis.chainStore.put("version", this.version);
|
|
1842
1795
|
}
|
|
1843
1796
|
} catch (e) {
|
|
1844
|
-
console.log(e)
|
|
1845
|
-
return this.#setCurrentVersion()
|
|
1797
|
+
console.log(e);
|
|
1798
|
+
return this.#setCurrentVersion();
|
|
1846
1799
|
}
|
|
1847
1800
|
}
|
|
1848
1801
|
isVersionCompatible(peerVersion) {
|
|
1849
|
-
if (!peerVersion || !this.version) return false
|
|
1850
|
-
const [peerMajor, peerMinor] = peerVersion.split(
|
|
1851
|
-
const [localMajor, localMinor] = this.version.split(
|
|
1852
|
-
return peerMajor === localMajor && peerMinor === localMinor
|
|
1802
|
+
if (!peerVersion || !this.version) return false;
|
|
1803
|
+
const [peerMajor, peerMinor] = peerVersion.split(".");
|
|
1804
|
+
const [localMajor, localMinor] = this.version.split(".");
|
|
1805
|
+
return peerMajor === localMajor && peerMinor === localMinor;
|
|
1853
1806
|
}
|
|
1854
1807
|
}
|
|
1855
1808
|
|
|
1856
1809
|
class ConnectionMonitor {
|
|
1857
|
-
#isMonitoring = false
|
|
1858
|
-
#checkInterval = null
|
|
1859
|
-
#reconnectDelay = 5e3
|
|
1860
|
-
#reconnectTimer = null
|
|
1861
|
-
#healthCheckInterval = 6e4
|
|
1862
|
-
#version
|
|
1863
|
-
#lastHealthCheckAt = 0
|
|
1864
|
-
#reconnecting = false
|
|
1810
|
+
#isMonitoring = false;
|
|
1811
|
+
#checkInterval = null;
|
|
1812
|
+
#reconnectDelay = 5e3;
|
|
1813
|
+
#reconnectTimer = null;
|
|
1814
|
+
#healthCheckInterval = 6e4;
|
|
1815
|
+
#version;
|
|
1816
|
+
#lastHealthCheckAt = 0;
|
|
1817
|
+
#reconnecting = false;
|
|
1865
1818
|
// event handlers to remove later
|
|
1866
|
-
#onOnline = null
|
|
1867
|
-
#onVisibilityChange = null
|
|
1868
|
-
#onSigcont = null
|
|
1869
|
-
#onPeerConnected = null
|
|
1819
|
+
#onOnline = null;
|
|
1820
|
+
#onVisibilityChange = null;
|
|
1821
|
+
#onSigcont = null;
|
|
1822
|
+
#onPeerConnected = null;
|
|
1870
1823
|
get isMonitoring() {
|
|
1871
|
-
return this.#isMonitoring
|
|
1824
|
+
return this.#isMonitoring;
|
|
1872
1825
|
}
|
|
1873
1826
|
get connectedPeers() {
|
|
1874
|
-
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => peer.connected)
|
|
1827
|
+
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => peer.connected);
|
|
1875
1828
|
}
|
|
1876
1829
|
get compatiblePeers() {
|
|
1877
1830
|
return this.connectedPeers.filter((peer) => {
|
|
1878
|
-
if (!peer.version || !this.#version) return false
|
|
1879
|
-
const [peerMajor, peerMinor] = peer.version.split(
|
|
1880
|
-
const [localMajor, localMinor] = this.#version.split(
|
|
1881
|
-
return peerMajor === localMajor && peerMinor === localMinor
|
|
1882
|
-
})
|
|
1831
|
+
if (!peer.version || !this.#version) return false;
|
|
1832
|
+
const [peerMajor, peerMinor] = peer.version.split(".");
|
|
1833
|
+
const [localMajor, localMinor] = this.#version.split(".");
|
|
1834
|
+
return peerMajor === localMajor && peerMinor === localMinor;
|
|
1835
|
+
});
|
|
1883
1836
|
}
|
|
1884
1837
|
get disconnectedPeers() {
|
|
1885
|
-
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => !peer.connected)
|
|
1838
|
+
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => !peer.connected);
|
|
1886
1839
|
}
|
|
1887
1840
|
start(version) {
|
|
1888
|
-
this.#version = version
|
|
1889
|
-
console.log(`\u{1F517} Connection Monitor initialized for version: ${this.#version}`)
|
|
1890
|
-
if (this.#isMonitoring) return
|
|
1891
|
-
this.#isMonitoring = true
|
|
1892
|
-
console.log(
|
|
1893
|
-
if (typeof window !==
|
|
1841
|
+
this.#version = version;
|
|
1842
|
+
console.log(`\u{1F517} Connection Monitor initialized for version: ${this.#version}`);
|
|
1843
|
+
if (this.#isMonitoring) return;
|
|
1844
|
+
this.#isMonitoring = true;
|
|
1845
|
+
console.log("\u{1F504} Starting connection monitor...");
|
|
1846
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
1894
1847
|
this.#onOnline = () => {
|
|
1895
|
-
console.log(
|
|
1896
|
-
void this.#restoreNetwork()
|
|
1897
|
-
}
|
|
1898
|
-
window.addEventListener(
|
|
1848
|
+
console.log("\u{1F310} Network online \u2014 attempting restore");
|
|
1849
|
+
void this.#restoreNetwork();
|
|
1850
|
+
};
|
|
1851
|
+
window.addEventListener("online", this.#onOnline);
|
|
1899
1852
|
this.#onVisibilityChange = () => {
|
|
1900
|
-
if (document.visibilityState ===
|
|
1901
|
-
console.log(
|
|
1853
|
+
if (document.visibilityState === "visible") {
|
|
1854
|
+
console.log("\u{1F4A1} Visibility regained");
|
|
1902
1855
|
if (this.connectedPeers.length === 0) {
|
|
1903
|
-
console.log(
|
|
1904
|
-
void this.#restoreNetwork()
|
|
1856
|
+
console.log("\u{1F4A1} Visibility regained \u2014 attempting restore");
|
|
1857
|
+
void this.#restoreNetwork();
|
|
1905
1858
|
}
|
|
1906
1859
|
}
|
|
1907
|
-
}
|
|
1908
|
-
document.addEventListener(
|
|
1860
|
+
};
|
|
1861
|
+
document.addEventListener("visibilitychange", this.#onVisibilityChange);
|
|
1909
1862
|
}
|
|
1910
|
-
if (typeof process !==
|
|
1863
|
+
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
1911
1864
|
this.#onSigcont = () => {
|
|
1912
|
-
console.log(
|
|
1913
|
-
void this.#restoreNetwork()
|
|
1914
|
-
}
|
|
1865
|
+
console.log("\u{1F514} Process resumed (SIGCONT) \u2014 attempting restore");
|
|
1866
|
+
void this.#restoreNetwork();
|
|
1867
|
+
};
|
|
1915
1868
|
try {
|
|
1916
|
-
process.on(
|
|
1917
|
-
} catch (e) {
|
|
1869
|
+
process.on("SIGCONT", this.#onSigcont);
|
|
1870
|
+
} catch (e) {
|
|
1871
|
+
}
|
|
1918
1872
|
}
|
|
1919
|
-
this.#lastHealthCheckAt = Date.now()
|
|
1873
|
+
this.#lastHealthCheckAt = Date.now();
|
|
1920
1874
|
this.#checkInterval = setInterval(() => {
|
|
1921
|
-
this.#healthCheck()
|
|
1922
|
-
}, this.#healthCheckInterval)
|
|
1875
|
+
this.#healthCheck();
|
|
1876
|
+
}, this.#healthCheckInterval);
|
|
1923
1877
|
this.#onPeerConnected = () => {
|
|
1924
1878
|
if (this.#reconnectTimer) {
|
|
1925
|
-
clearTimeout(this.#reconnectTimer)
|
|
1926
|
-
this.#reconnectTimer = null
|
|
1879
|
+
clearTimeout(this.#reconnectTimer);
|
|
1880
|
+
this.#reconnectTimer = null;
|
|
1927
1881
|
}
|
|
1928
|
-
this.#reconnecting = false
|
|
1929
|
-
this.#reconnectDelay = 5e3
|
|
1930
|
-
}
|
|
1931
|
-
globalThis.pubsub?.subscribe(
|
|
1882
|
+
this.#reconnecting = false;
|
|
1883
|
+
this.#reconnectDelay = 5e3;
|
|
1884
|
+
};
|
|
1885
|
+
globalThis.pubsub?.subscribe("peer:connected", this.#onPeerConnected);
|
|
1932
1886
|
}
|
|
1933
1887
|
stop() {
|
|
1934
|
-
if (!this.#isMonitoring) return
|
|
1935
|
-
this.#isMonitoring = false
|
|
1888
|
+
if (!this.#isMonitoring) return;
|
|
1889
|
+
this.#isMonitoring = false;
|
|
1936
1890
|
if (this.#checkInterval) {
|
|
1937
|
-
clearInterval(this.#checkInterval)
|
|
1938
|
-
this.#checkInterval = null
|
|
1891
|
+
clearInterval(this.#checkInterval);
|
|
1892
|
+
this.#checkInterval = null;
|
|
1939
1893
|
}
|
|
1940
|
-
if (typeof window !==
|
|
1894
|
+
if (typeof window !== "undefined") {
|
|
1941
1895
|
if (this.#onOnline) {
|
|
1942
|
-
window.removeEventListener(
|
|
1943
|
-
this.#onOnline = null
|
|
1896
|
+
window.removeEventListener("online", this.#onOnline);
|
|
1897
|
+
this.#onOnline = null;
|
|
1944
1898
|
}
|
|
1945
1899
|
if (this.#onVisibilityChange) {
|
|
1946
|
-
document.removeEventListener(
|
|
1947
|
-
this.#onVisibilityChange = null
|
|
1900
|
+
document.removeEventListener("visibilitychange", this.#onVisibilityChange);
|
|
1901
|
+
this.#onVisibilityChange = null;
|
|
1948
1902
|
}
|
|
1949
1903
|
}
|
|
1950
|
-
if (typeof process !==
|
|
1904
|
+
if (typeof process !== "undefined" && typeof process.removeListener === "function" && this.#onSigcont) {
|
|
1951
1905
|
try {
|
|
1952
|
-
process.removeListener(
|
|
1953
|
-
} catch (e) {
|
|
1954
|
-
|
|
1906
|
+
process.removeListener("SIGCONT", this.#onSigcont);
|
|
1907
|
+
} catch (e) {
|
|
1908
|
+
}
|
|
1909
|
+
this.#onSigcont = null;
|
|
1955
1910
|
}
|
|
1956
1911
|
if (this.#onPeerConnected) {
|
|
1957
|
-
globalThis.pubsub?.unsubscribe(
|
|
1958
|
-
this.#onPeerConnected = null
|
|
1912
|
+
globalThis.pubsub?.unsubscribe("peer:connected", this.#onPeerConnected);
|
|
1913
|
+
this.#onPeerConnected = null;
|
|
1959
1914
|
}
|
|
1960
1915
|
if (this.#reconnectTimer) {
|
|
1961
|
-
clearTimeout(this.#reconnectTimer)
|
|
1962
|
-
this.#reconnectTimer = null
|
|
1916
|
+
clearTimeout(this.#reconnectTimer);
|
|
1917
|
+
this.#reconnectTimer = null;
|
|
1963
1918
|
}
|
|
1964
|
-
console.log(
|
|
1919
|
+
console.log("\u23F9\uFE0F Connection monitor stopped");
|
|
1965
1920
|
}
|
|
1966
1921
|
async #healthCheck() {
|
|
1967
|
-
const now = Date.now()
|
|
1968
|
-
const expectedNext = this.#lastHealthCheckAt + this.#healthCheckInterval
|
|
1969
|
-
const drift = now - expectedNext
|
|
1970
|
-
this.#lastHealthCheckAt = now
|
|
1922
|
+
const now = Date.now();
|
|
1923
|
+
const expectedNext = this.#lastHealthCheckAt + this.#healthCheckInterval;
|
|
1924
|
+
const drift = now - expectedNext;
|
|
1925
|
+
this.#lastHealthCheckAt = now;
|
|
1971
1926
|
if (drift > 5e3) {
|
|
1972
|
-
console.log(`\u23F0 Detected timer drift of ${drift}ms (likely system sleep) \u2014 attempting restore`)
|
|
1973
|
-
void this.#restoreNetwork()
|
|
1927
|
+
console.log(`\u23F0 Detected timer drift of ${drift}ms (likely system sleep) \u2014 attempting restore`);
|
|
1928
|
+
void this.#restoreNetwork();
|
|
1974
1929
|
}
|
|
1975
|
-
const connectedPeers = this.connectedPeers
|
|
1976
|
-
const compatiblePeers = this.compatiblePeers
|
|
1977
|
-
const disconnectedPeers = this.disconnectedPeers
|
|
1930
|
+
const connectedPeers = this.connectedPeers;
|
|
1931
|
+
const compatiblePeers = this.compatiblePeers;
|
|
1932
|
+
const disconnectedPeers = this.disconnectedPeers;
|
|
1978
1933
|
console.log(
|
|
1979
1934
|
`\u{1F50D} Health check: ${connectedPeers.length} connected, ${compatiblePeers.length} compatible, ${disconnectedPeers.length} negotiating`
|
|
1980
|
-
)
|
|
1935
|
+
);
|
|
1981
1936
|
if (connectedPeers.length === 0 && disconnectedPeers.length === 0) {
|
|
1982
|
-
console.warn(
|
|
1983
|
-
await this.#attemptReconnection()
|
|
1937
|
+
console.warn("\u26A0\uFE0F No peer connections detected \u2014 attempting reconnection");
|
|
1938
|
+
await this.#attemptReconnection();
|
|
1984
1939
|
} else if (compatiblePeers.length === 0 && connectedPeers.length > 0) {
|
|
1985
|
-
console.warn(
|
|
1986
|
-
await this.#attemptReconnection()
|
|
1940
|
+
console.warn("\u26A0\uFE0F No compatible peers found \u2014 attempting reconnection");
|
|
1941
|
+
await this.#attemptReconnection();
|
|
1987
1942
|
}
|
|
1988
|
-
globalThis.pubsub?.publish(
|
|
1943
|
+
globalThis.pubsub?.publish("connection-status", {
|
|
1989
1944
|
connected: connectedPeers.length,
|
|
1990
1945
|
compatible: compatiblePeers.length,
|
|
1991
1946
|
healthy: compatiblePeers.length > 0
|
|
1992
|
-
})
|
|
1947
|
+
});
|
|
1993
1948
|
}
|
|
1994
1949
|
// lightweight TCP probe to detect internet connectivity in Node.js
|
|
1995
1950
|
async #isOnLine(timeout = 1500) {
|
|
1996
1951
|
if (navigator?.onLine !== void 0) {
|
|
1997
|
-
return navigator.onLine
|
|
1952
|
+
return navigator.onLine;
|
|
1998
1953
|
}
|
|
1999
1954
|
return new Promise(async (resolve) => {
|
|
2000
1955
|
try {
|
|
2001
|
-
const net = await import('net')
|
|
2002
|
-
const socket = new net.Socket()
|
|
2003
|
-
let finished = false
|
|
1956
|
+
const net = await import('net');
|
|
1957
|
+
const socket = new net.Socket();
|
|
1958
|
+
let finished = false;
|
|
2004
1959
|
const finish = (val) => {
|
|
2005
|
-
if (finished) return
|
|
2006
|
-
finished = true
|
|
1960
|
+
if (finished) return;
|
|
1961
|
+
finished = true;
|
|
2007
1962
|
try {
|
|
2008
|
-
socket.destroy()
|
|
2009
|
-
} catch (e) {
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
socket.
|
|
2014
|
-
socket.once(
|
|
2015
|
-
socket.once(
|
|
2016
|
-
socket.
|
|
1963
|
+
socket.destroy();
|
|
1964
|
+
} catch (e) {
|
|
1965
|
+
}
|
|
1966
|
+
resolve(val);
|
|
1967
|
+
};
|
|
1968
|
+
socket.setTimeout(timeout);
|
|
1969
|
+
socket.once("connect", () => finish(true));
|
|
1970
|
+
socket.once("error", () => finish(false));
|
|
1971
|
+
socket.once("timeout", () => finish(false));
|
|
1972
|
+
socket.connect(53, "1.1.1.1");
|
|
2017
1973
|
} catch (e) {
|
|
2018
|
-
resolve(false)
|
|
1974
|
+
resolve(false);
|
|
2019
1975
|
}
|
|
2020
|
-
})
|
|
1976
|
+
});
|
|
2021
1977
|
}
|
|
2022
1978
|
// Called on visibility/online/resume events
|
|
2023
1979
|
async #restoreNetwork() {
|
|
2024
1980
|
if (this.#reconnecting) {
|
|
2025
|
-
console.log(
|
|
2026
|
-
return
|
|
1981
|
+
console.log("\u{1F501} Reconnection already in progress, skipping");
|
|
1982
|
+
return;
|
|
2027
1983
|
}
|
|
2028
|
-
this.#reconnecting = true
|
|
1984
|
+
this.#reconnecting = true;
|
|
2029
1985
|
if (this.connectedPeers.length > 0) {
|
|
2030
|
-
console.log(
|
|
2031
|
-
this.#reconnecting = false
|
|
2032
|
-
return
|
|
1986
|
+
console.log("\u2705 Already connected to peers, skipping restoration");
|
|
1987
|
+
this.#reconnecting = false;
|
|
1988
|
+
return;
|
|
2033
1989
|
}
|
|
2034
|
-
console.log(
|
|
1990
|
+
console.log("\u{1F501} Restoring network");
|
|
2035
1991
|
try {
|
|
2036
|
-
const online = await this.#isOnLine(1500)
|
|
1992
|
+
const online = await this.#isOnLine(1500);
|
|
2037
1993
|
if (!online) {
|
|
2038
|
-
console.warn(
|
|
2039
|
-
this.#reconnecting = false
|
|
2040
|
-
return
|
|
1994
|
+
console.warn("\u26A0\uFE0F No internet detected, skipping restore");
|
|
1995
|
+
this.#reconnecting = false;
|
|
1996
|
+
return;
|
|
2041
1997
|
}
|
|
2042
1998
|
} catch (e) {
|
|
2043
|
-
console.warn(
|
|
1999
|
+
console.warn("\u26A0\uFE0F Online probe failed, proceeding with restore", e?.message || e);
|
|
2044
2000
|
}
|
|
2045
2001
|
try {
|
|
2046
|
-
console.log(
|
|
2002
|
+
console.log("\u{1F504} Attempting network restoration...");
|
|
2047
2003
|
if (globalThis.peernet?.client?.reinit) {
|
|
2048
|
-
console.log(
|
|
2004
|
+
console.log(" \u2192 Trying client.reinit()");
|
|
2049
2005
|
try {
|
|
2050
|
-
await globalThis.peernet.client.reinit()
|
|
2051
|
-
console.log(
|
|
2006
|
+
await globalThis.peernet.client.reinit();
|
|
2007
|
+
console.log(" \u2705 client.reinit() succeeded");
|
|
2052
2008
|
} catch (e) {
|
|
2053
|
-
console.warn(
|
|
2009
|
+
console.warn(" \u26A0\uFE0F client.reinit() failed:", e?.message || e);
|
|
2054
2010
|
}
|
|
2055
2011
|
}
|
|
2056
2012
|
if (globalThis.peernet?.start) {
|
|
2057
|
-
console.log(
|
|
2013
|
+
console.log(" \u2192 Trying peernet.start()");
|
|
2058
2014
|
try {
|
|
2059
|
-
await globalThis.peernet.start()
|
|
2060
|
-
console.log(
|
|
2015
|
+
await globalThis.peernet.start();
|
|
2016
|
+
console.log(" \u2705 peernet.start() succeeded");
|
|
2061
2017
|
} catch (e) {
|
|
2062
|
-
console.warn(
|
|
2018
|
+
console.warn(" \u26A0\uFE0F peernet.start() failed:", e?.message || e);
|
|
2063
2019
|
}
|
|
2064
2020
|
}
|
|
2065
2021
|
try {
|
|
2066
|
-
const networkName = globalThis.peernet?.network
|
|
2067
|
-
if (networkName && typeof networkName ===
|
|
2068
|
-
const { default: networks } = await import('@leofcoin/networks')
|
|
2069
|
-
const [mainKey, subKey] = networkName.split(
|
|
2070
|
-
const networkConfig = networks?.[mainKey]?.[subKey]
|
|
2022
|
+
const networkName = globalThis.peernet?.network;
|
|
2023
|
+
if (networkName && typeof networkName === "string") {
|
|
2024
|
+
const { default: networks } = await import('@leofcoin/networks');
|
|
2025
|
+
const [mainKey, subKey] = networkName.split(":");
|
|
2026
|
+
const networkConfig = networks?.[mainKey]?.[subKey];
|
|
2071
2027
|
if (networkConfig?.stars && Array.isArray(networkConfig.stars)) {
|
|
2072
|
-
console.log(
|
|
2028
|
+
console.log(" \u2192 Attempting to dial star servers:", networkConfig.stars.join(", "));
|
|
2073
2029
|
for (const star of networkConfig.stars) {
|
|
2074
2030
|
try {
|
|
2075
|
-
if (globalThis.peernet?.client &&
|
|
2076
|
-
await globalThis.peernet.client.dial(star)
|
|
2077
|
-
console.log(` \u2705 Connected to star server: ${star}`)
|
|
2031
|
+
if (globalThis.peernet?.client && "dial" in globalThis.peernet.client) {
|
|
2032
|
+
await globalThis.peernet.client.dial(star);
|
|
2033
|
+
console.log(` \u2705 Connected to star server: ${star}`);
|
|
2078
2034
|
}
|
|
2079
2035
|
} catch (e) {
|
|
2080
|
-
console.warn(` \u26A0\uFE0F Failed to dial ${star}:`, e?.message || e)
|
|
2036
|
+
console.warn(` \u26A0\uFE0F Failed to dial ${star}:`, e?.message || e);
|
|
2081
2037
|
}
|
|
2082
2038
|
}
|
|
2083
2039
|
}
|
|
2084
2040
|
}
|
|
2085
2041
|
} catch (e) {
|
|
2086
|
-
console.warn(
|
|
2042
|
+
console.warn(" \u26A0\uFE0F Could not load or dial star servers:", e?.message || e);
|
|
2087
2043
|
}
|
|
2088
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3))
|
|
2089
|
-
const connectedAfter = this.connectedPeers.length
|
|
2090
|
-
console.log(`\u{1F504} Restoration complete. Connected peers: ${connectedAfter}`)
|
|
2044
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2045
|
+
const connectedAfter = this.connectedPeers.length;
|
|
2046
|
+
console.log(`\u{1F504} Restoration complete. Connected peers: ${connectedAfter}`);
|
|
2091
2047
|
} catch (error) {
|
|
2092
|
-
console.error(
|
|
2048
|
+
console.error("\u274C Network restoration failed:", error?.message || error);
|
|
2093
2049
|
} finally {
|
|
2094
|
-
this.#reconnecting = false
|
|
2050
|
+
this.#reconnecting = false;
|
|
2095
2051
|
}
|
|
2096
2052
|
}
|
|
2097
2053
|
async waitForPeers(timeoutMs = 3e4) {
|
|
2098
2054
|
return new Promise((resolve) => {
|
|
2099
|
-
const startTime = Date.now()
|
|
2055
|
+
const startTime = Date.now();
|
|
2100
2056
|
const checkPeers = () => {
|
|
2101
2057
|
if (this.compatiblePeers.length > 0) {
|
|
2102
|
-
resolve(true)
|
|
2103
|
-
return
|
|
2058
|
+
resolve(true);
|
|
2059
|
+
return;
|
|
2104
2060
|
}
|
|
2105
2061
|
if (Date.now() - startTime >= timeoutMs) {
|
|
2106
|
-
resolve(false)
|
|
2107
|
-
return
|
|
2062
|
+
resolve(false);
|
|
2063
|
+
return;
|
|
2108
2064
|
}
|
|
2109
|
-
setTimeout(checkPeers, 1e3)
|
|
2110
|
-
}
|
|
2111
|
-
checkPeers()
|
|
2112
|
-
})
|
|
2065
|
+
setTimeout(checkPeers, 1e3);
|
|
2066
|
+
};
|
|
2067
|
+
checkPeers();
|
|
2068
|
+
});
|
|
2113
2069
|
}
|
|
2114
2070
|
async #attemptReconnection() {
|
|
2115
2071
|
try {
|
|
2116
|
-
await this.#restoreNetwork()
|
|
2117
|
-
const hasConnections = this.connectedPeers.length > 0 || this.disconnectedPeers.length > 0
|
|
2072
|
+
await this.#restoreNetwork();
|
|
2073
|
+
const hasConnections = this.connectedPeers.length > 0 || this.disconnectedPeers.length > 0;
|
|
2118
2074
|
if (hasConnections) {
|
|
2119
|
-
console.log(
|
|
2120
|
-
this.#reconnectDelay = 5e3
|
|
2075
|
+
console.log("\u2705 Reconnection successful, resetting backoff delay");
|
|
2076
|
+
this.#reconnectDelay = 5e3;
|
|
2121
2077
|
} else {
|
|
2122
|
-
console.warn(
|
|
2078
|
+
console.warn("\u26A0\uFE0F Reconnection attempt completed but no peers connected");
|
|
2123
2079
|
if (this.#reconnectDelay >= 3e4) {
|
|
2124
|
-
console.warn(
|
|
2125
|
-
this.#reconnectDelay = 5e3
|
|
2080
|
+
console.warn("\u26A0\uFE0F Reconnection delay reached maximum, resetting to 5 seconds");
|
|
2081
|
+
this.#reconnectDelay = 5e3;
|
|
2126
2082
|
} else {
|
|
2127
|
-
this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 3e4)
|
|
2128
|
-
console.warn(`\u26A0\uFE0F Increasing reconnection delay to ${this.#reconnectDelay} ms`)
|
|
2083
|
+
this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 3e4);
|
|
2084
|
+
console.warn(`\u26A0\uFE0F Increasing reconnection delay to ${this.#reconnectDelay} ms`);
|
|
2129
2085
|
}
|
|
2130
|
-
this.#reconnectTimer = setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay)
|
|
2086
|
+
this.#reconnectTimer = setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay);
|
|
2131
2087
|
}
|
|
2132
2088
|
} catch (error) {
|
|
2133
|
-
console.error(
|
|
2089
|
+
console.error("\u274C Reconnection failed:", error?.message || error);
|
|
2134
2090
|
if (this.#reconnectDelay >= 3e4) {
|
|
2135
|
-
this.#reconnectDelay = 5e3
|
|
2091
|
+
this.#reconnectDelay = 5e3;
|
|
2136
2092
|
} else {
|
|
2137
|
-
this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 3e4)
|
|
2093
|
+
this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 3e4);
|
|
2138
2094
|
}
|
|
2139
|
-
this.#reconnectTimer = setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay)
|
|
2095
|
+
this.#reconnectTimer = setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay);
|
|
2140
2096
|
}
|
|
2141
2097
|
}
|
|
2142
2098
|
}
|
|
2143
2099
|
|
|
2144
|
-
const debug = createDebugger(
|
|
2100
|
+
const debug = createDebugger("leofcoin/chain");
|
|
2145
2101
|
class Chain extends VersionControl {
|
|
2146
2102
|
constructor(config) {
|
|
2147
|
-
super(config)
|
|
2148
|
-
this.#slotTime = 1e4
|
|
2149
|
-
this.#blockTime = 6e3
|
|
2103
|
+
super(config);
|
|
2104
|
+
this.#slotTime = 1e4;
|
|
2105
|
+
this.#blockTime = 6e3;
|
|
2150
2106
|
// 6 second target block time
|
|
2151
|
-
this.#epochLength = 10
|
|
2152
|
-
this.utils = {}
|
|
2107
|
+
this.#epochLength = 10;
|
|
2108
|
+
this.utils = {};
|
|
2153
2109
|
/** {Address[]} */
|
|
2154
|
-
this.#validators = []
|
|
2110
|
+
this.#validators = [];
|
|
2155
2111
|
/** {Boolean} */
|
|
2156
|
-
this.#runningEpoch = false
|
|
2112
|
+
this.#runningEpoch = false;
|
|
2157
2113
|
/** Block height at which current epoch started (for block-based epoch timing) */
|
|
2158
|
-
this.#currentEpochStartHeight = 0
|
|
2114
|
+
this.#currentEpochStartHeight = 0;
|
|
2159
2115
|
/** {Object} Block cache by index for conflict detection: {index: {hash, ...block}} */
|
|
2160
|
-
this.#blocks = {}
|
|
2161
|
-
this.#participants = []
|
|
2162
|
-
this.#participating = false
|
|
2163
|
-
this.#jail = /* @__PURE__ */ new Set()
|
|
2164
|
-
this.#jailReleaseTimers = /* @__PURE__ */ new Map()
|
|
2165
|
-
this.#peerConnectionRetries = /* @__PURE__ */ new Map()
|
|
2166
|
-
this.#maxPeerRetries = 5
|
|
2167
|
-
this.#peerRetryDelay = 5e3
|
|
2116
|
+
this.#blocks = {};
|
|
2117
|
+
this.#participants = [];
|
|
2118
|
+
this.#participating = false;
|
|
2119
|
+
this.#jail = /* @__PURE__ */ new Set();
|
|
2120
|
+
this.#jailReleaseTimers = /* @__PURE__ */ new Map();
|
|
2121
|
+
this.#peerConnectionRetries = /* @__PURE__ */ new Map();
|
|
2122
|
+
this.#maxPeerRetries = 5;
|
|
2123
|
+
this.#peerRetryDelay = 5e3;
|
|
2168
2124
|
/** {Map} Peer reputation tracking: {peerId: {score, failures}} */
|
|
2169
|
-
this.#peerReputations = /* @__PURE__ */ new Map()
|
|
2170
|
-
this.#minPeerScore = -10
|
|
2171
|
-
this.#maxPeerFailures = 100
|
|
2125
|
+
this.#peerReputations = /* @__PURE__ */ new Map();
|
|
2126
|
+
this.#minPeerScore = -10;
|
|
2127
|
+
this.#maxPeerFailures = 100;
|
|
2172
2128
|
// ── Tendermint consensus state ──────────────────────────────────────────────
|
|
2173
2129
|
/** Current consensus round (increments when proposer is unresponsive) */
|
|
2174
|
-
this.#consensusRound = 0
|
|
2130
|
+
this.#consensusRound = 0;
|
|
2175
2131
|
/** Timer that advances #consensusRound when the proposer doesn't propose in time */
|
|
2176
|
-
this.#roundTimer = null
|
|
2132
|
+
this.#roundTimer = null;
|
|
2177
2133
|
/** prevotes collected per `height:round:blockHash` key */
|
|
2178
|
-
this.#prevotes = /* @__PURE__ */ new Map()
|
|
2134
|
+
this.#prevotes = /* @__PURE__ */ new Map();
|
|
2179
2135
|
/** precommits collected per `height:round:blockHash` key */
|
|
2180
|
-
this.#precommits = /* @__PURE__ */ new Map()
|
|
2136
|
+
this.#precommits = /* @__PURE__ */ new Map();
|
|
2181
2137
|
/** Index of the last block that reached 2f+1 precommits */
|
|
2182
|
-
this.#committedHeight = -1
|
|
2138
|
+
this.#committedHeight = -1;
|
|
2183
2139
|
/** Prevents casting duplicate prevote/precommit per height:round */
|
|
2184
|
-
this.#castedVotes = /* @__PURE__ */ new Set()
|
|
2140
|
+
this.#castedVotes = /* @__PURE__ */ new Set();
|
|
2185
2141
|
this.ready = new Promise((resolve) => {
|
|
2186
|
-
this.readyResolve = resolve
|
|
2187
|
-
})
|
|
2142
|
+
this.readyResolve = resolve;
|
|
2143
|
+
});
|
|
2188
2144
|
// ── Tendermint consensus handlers ────────────────────────────────────────
|
|
2189
2145
|
/**
|
|
2190
2146
|
* Publish a prevote or precommit. Idempotent — will not cast the same
|
|
2191
2147
|
* vote twice for the same height:round.
|
|
2192
2148
|
*/
|
|
2193
2149
|
this.#castVote = async (type, blockHash, index, round) => {
|
|
2194
|
-
const voteKey = `${type}:${index}:${round}
|
|
2195
|
-
if (this.#castedVotes.has(voteKey)) return
|
|
2196
|
-
this.#castedVotes.add(voteKey)
|
|
2197
|
-
const from = peernet.selectedAccount
|
|
2198
|
-
const voteData = { blockHash, index: BigInt(index), round: BigInt(round), from }
|
|
2199
|
-
const Message = type ===
|
|
2200
|
-
const message = new Message(voteData)
|
|
2201
|
-
const payload = message.encoded
|
|
2150
|
+
const voteKey = `${type}:${index}:${round}`;
|
|
2151
|
+
if (this.#castedVotes.has(voteKey)) return;
|
|
2152
|
+
this.#castedVotes.add(voteKey);
|
|
2153
|
+
const from = peernet.selectedAccount;
|
|
2154
|
+
const voteData = { blockHash, index: BigInt(index), round: BigInt(round), from };
|
|
2155
|
+
const Message = type === "prevote" ? PrevoteMessage : PrecommitMessage;
|
|
2156
|
+
const message = new Message(voteData);
|
|
2157
|
+
const payload = message.encoded;
|
|
2202
2158
|
try {
|
|
2203
|
-
globalThis.peernet.publish(`consensus:${type}`, payload)
|
|
2159
|
+
globalThis.peernet.publish(`consensus:${type}`, payload);
|
|
2204
2160
|
} catch (e) {
|
|
2205
|
-
debug(`peernet publish failed: consensus:${type}`, e?.message ?? e)
|
|
2161
|
+
debug(`peernet publish failed: consensus:${type}`, e?.message ?? e);
|
|
2206
2162
|
}
|
|
2207
|
-
}
|
|
2163
|
+
};
|
|
2208
2164
|
/**
|
|
2209
2165
|
* Phase 2 — receive a block proposal from the designated proposer.
|
|
2210
2166
|
* Validates the proposer is correct for height/round, fetches + validates
|
|
@@ -2212,76 +2168,72 @@ class Chain extends VersionControl {
|
|
|
2212
2168
|
*/
|
|
2213
2169
|
this.#handleProposal = async (payload) => {
|
|
2214
2170
|
try {
|
|
2215
|
-
const message = new ProposalMessage(payload)
|
|
2216
|
-
const msg = message.decoded
|
|
2217
|
-
const { blockHash, index, round, from } = msg
|
|
2218
|
-
const validators = await this.#getConsensusValidators(Number(index))
|
|
2219
|
-
const expectedProposerIdx = Number((index + round) % BigInt(validators.length))
|
|
2171
|
+
const message = new ProposalMessage(payload);
|
|
2172
|
+
const msg = message.decoded;
|
|
2173
|
+
const { blockHash, index, round, from } = msg;
|
|
2174
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2175
|
+
const expectedProposerIdx = Number((index + round) % BigInt(validators.length));
|
|
2220
2176
|
if (!validators[expectedProposerIdx] || validators[expectedProposerIdx] !== from) {
|
|
2221
|
-
debug(`[consensus] Proposal from wrong proposer at height ${index} round ${round}`)
|
|
2222
|
-
return
|
|
2177
|
+
debug(`[consensus] Proposal from wrong proposer at height ${index} round ${round}`);
|
|
2178
|
+
return;
|
|
2223
2179
|
}
|
|
2224
|
-
const localBlock = await this.lastBlock
|
|
2225
|
-
const localIndex = localBlock?.index !== void 0 ? localBlock.index : -1n
|
|
2180
|
+
const localBlock = await this.lastBlock;
|
|
2181
|
+
const localIndex = localBlock?.index !== void 0 ? localBlock.index : -1n;
|
|
2226
2182
|
if (index <= localIndex) {
|
|
2227
|
-
debug(`[consensus] Ignoring stale proposal at height ${index} (local: ${localIndex})`)
|
|
2228
|
-
return
|
|
2183
|
+
debug(`[consensus] Ignoring stale proposal at height ${index} (local: ${localIndex})`);
|
|
2184
|
+
return;
|
|
2229
2185
|
}
|
|
2230
2186
|
try {
|
|
2231
|
-
const blockData = await globalThis.peernet.get(blockHash,
|
|
2232
|
-
const blockMessage = await new BlockMessage(blockData)
|
|
2233
|
-
const actualHash = await blockMessage.hash()
|
|
2187
|
+
const blockData = await globalThis.peernet.get(blockHash, "block");
|
|
2188
|
+
const blockMessage = await new BlockMessage(blockData);
|
|
2189
|
+
const actualHash = await blockMessage.hash();
|
|
2234
2190
|
if (actualHash !== blockHash) {
|
|
2235
|
-
debug(`[consensus] Block hash mismatch in proposal: expected ${blockHash}, got ${actualHash}`)
|
|
2236
|
-
return
|
|
2191
|
+
debug(`[consensus] Block hash mismatch in proposal: expected ${blockHash}, got ${actualHash}`);
|
|
2192
|
+
return;
|
|
2237
2193
|
}
|
|
2238
2194
|
} catch (e) {
|
|
2239
|
-
debug(`[consensus] Cannot fetch proposed block ${blockHash}:`, e?.message)
|
|
2240
|
-
return
|
|
2195
|
+
debug(`[consensus] Cannot fetch proposed block ${blockHash}:`, e?.message);
|
|
2196
|
+
return;
|
|
2241
2197
|
}
|
|
2242
|
-
this.#consensusRound = Number(round)
|
|
2198
|
+
this.#consensusRound = Number(round);
|
|
2243
2199
|
if (this.#roundTimer) {
|
|
2244
|
-
clearTimeout(this.#roundTimer)
|
|
2245
|
-
this.#roundTimer = null
|
|
2200
|
+
clearTimeout(this.#roundTimer);
|
|
2201
|
+
this.#roundTimer = null;
|
|
2246
2202
|
}
|
|
2247
2203
|
if (validators.includes(peernet.selectedAccount) && !this.#isJailed(peernet.selectedAccount)) {
|
|
2248
|
-
await this.#castVote(
|
|
2204
|
+
await this.#castVote("prevote", blockHash, Number(index), Number(round));
|
|
2249
2205
|
}
|
|
2250
2206
|
} catch (e) {
|
|
2251
|
-
debug(
|
|
2207
|
+
debug("[consensus] Error handling proposal:", e?.message);
|
|
2252
2208
|
}
|
|
2253
|
-
}
|
|
2209
|
+
};
|
|
2254
2210
|
/**
|
|
2255
2211
|
* Phase 2 — collect prevotes. Once 2f+1 prevotes are seen for a block,
|
|
2256
2212
|
* cast a precommit.
|
|
2257
2213
|
*/
|
|
2258
2214
|
this.#handlePrevote = async (payload) => {
|
|
2259
2215
|
try {
|
|
2260
|
-
const message = new PrevoteMessage(payload)
|
|
2261
|
-
const msg = message.decoded
|
|
2262
|
-
const { blockHash, index, round, from } = msg
|
|
2263
|
-
const validators = await this.#getConsensusValidators(Number(index))
|
|
2264
|
-
if (!validators.includes(from)) return
|
|
2265
|
-
const localBlock = await this.lastBlock
|
|
2266
|
-
const localIndex = localBlock?.index !== void 0 ? localBlock.index : -1n
|
|
2267
|
-
if (index <= localIndex) return
|
|
2268
|
-
const voteKey = `${index}:${round}:${blockHash}
|
|
2269
|
-
if (!this.#prevotes.has(voteKey)) this.#prevotes.set(voteKey, /* @__PURE__ */ new Set())
|
|
2270
|
-
this.#prevotes.get(voteKey).add(from)
|
|
2271
|
-
const threshold = Math.ceil(
|
|
2272
|
-
const voteCount = this.#prevotes.get(voteKey).size
|
|
2273
|
-
debug(`[consensus] Prevotes ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`)
|
|
2274
|
-
if (
|
|
2275
|
-
|
|
2276
|
-
validators.includes(peernet.selectedAccount) &&
|
|
2277
|
-
!this.#isJailed(peernet.selectedAccount)
|
|
2278
|
-
) {
|
|
2279
|
-
await this.#castVote('precommit', blockHash, Number(index), Number(round))
|
|
2216
|
+
const message = new PrevoteMessage(payload);
|
|
2217
|
+
const msg = message.decoded;
|
|
2218
|
+
const { blockHash, index, round, from } = msg;
|
|
2219
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2220
|
+
if (!validators.includes(from)) return;
|
|
2221
|
+
const localBlock = await this.lastBlock;
|
|
2222
|
+
const localIndex = localBlock?.index !== void 0 ? localBlock.index : -1n;
|
|
2223
|
+
if (index <= localIndex) return;
|
|
2224
|
+
const voteKey = `${index}:${round}:${blockHash}`;
|
|
2225
|
+
if (!this.#prevotes.has(voteKey)) this.#prevotes.set(voteKey, /* @__PURE__ */ new Set());
|
|
2226
|
+
this.#prevotes.get(voteKey).add(from);
|
|
2227
|
+
const threshold = Math.ceil(2 * validators.length / 3);
|
|
2228
|
+
const voteCount = this.#prevotes.get(voteKey).size;
|
|
2229
|
+
debug(`[consensus] Prevotes ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`);
|
|
2230
|
+
if (voteCount >= threshold && validators.includes(peernet.selectedAccount) && !this.#isJailed(peernet.selectedAccount)) {
|
|
2231
|
+
await this.#castVote("precommit", blockHash, Number(index), Number(round));
|
|
2280
2232
|
}
|
|
2281
2233
|
} catch (e) {
|
|
2282
|
-
debug(
|
|
2234
|
+
debug("[consensus] Error handling prevote:", e?.message);
|
|
2283
2235
|
}
|
|
2284
|
-
}
|
|
2236
|
+
};
|
|
2285
2237
|
/**
|
|
2286
2238
|
* Phase 3 — collect precommits. Once 2f+1 precommits are seen for a block,
|
|
2287
2239
|
* commit it: non-proposers call #addBlock, then broadcast on add-block for
|
|
@@ -2289,252 +2241,250 @@ class Chain extends VersionControl {
|
|
|
2289
2241
|
*/
|
|
2290
2242
|
this.#handlePrecommit = async (payload) => {
|
|
2291
2243
|
try {
|
|
2292
|
-
const message = new PrecommitMessage(payload)
|
|
2293
|
-
const msg = message.decoded
|
|
2294
|
-
const { blockHash, index, round, from } = msg
|
|
2295
|
-
const validators = await this.#getConsensusValidators(Number(index))
|
|
2296
|
-
if (!validators.includes(from)) return
|
|
2297
|
-
if (index <= BigInt(this.#committedHeight)) return
|
|
2298
|
-
const voteKey = `${index}:${round}:${blockHash}
|
|
2299
|
-
if (!this.#precommits.has(voteKey)) this.#precommits.set(voteKey, /* @__PURE__ */ new Set())
|
|
2300
|
-
this.#precommits.get(voteKey).add(from)
|
|
2301
|
-
const threshold = Math.ceil(
|
|
2302
|
-
const voteCount = this.#precommits.get(voteKey).size
|
|
2303
|
-
debug(`[consensus] Precommits ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`)
|
|
2244
|
+
const message = new PrecommitMessage(payload);
|
|
2245
|
+
const msg = message.decoded;
|
|
2246
|
+
const { blockHash, index, round, from } = msg;
|
|
2247
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2248
|
+
if (!validators.includes(from)) return;
|
|
2249
|
+
if (index <= BigInt(this.#committedHeight)) return;
|
|
2250
|
+
const voteKey = `${index}:${round}:${blockHash}`;
|
|
2251
|
+
if (!this.#precommits.has(voteKey)) this.#precommits.set(voteKey, /* @__PURE__ */ new Set());
|
|
2252
|
+
this.#precommits.get(voteKey).add(from);
|
|
2253
|
+
const threshold = Math.ceil(2 * validators.length / 3);
|
|
2254
|
+
const voteCount = this.#precommits.get(voteKey).size;
|
|
2255
|
+
debug(`[consensus] Precommits ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`);
|
|
2304
2256
|
if (voteCount >= threshold && index > BigInt(this.#committedHeight)) {
|
|
2305
|
-
this.#committedHeight = Number(index)
|
|
2306
|
-
this.#consensusRound = 0
|
|
2257
|
+
this.#committedHeight = Number(index);
|
|
2258
|
+
this.#consensusRound = 0;
|
|
2307
2259
|
for (const key of [...this.#prevotes.keys()]) {
|
|
2308
|
-
if (BigInt(key.split(
|
|
2260
|
+
if (BigInt(key.split(":")[0]) <= index) this.#prevotes.delete(key);
|
|
2309
2261
|
}
|
|
2310
2262
|
for (const key of [...this.#precommits.keys()]) {
|
|
2311
|
-
if (BigInt(key.split(
|
|
2263
|
+
if (BigInt(key.split(":")[0]) <= index) this.#precommits.delete(key);
|
|
2312
2264
|
}
|
|
2313
2265
|
for (const key of [...this.#castedVotes]) {
|
|
2314
|
-
if (BigInt(key.split(
|
|
2266
|
+
if (BigInt(key.split(":")[1]) <= index) this.#castedVotes.delete(key);
|
|
2315
2267
|
}
|
|
2316
|
-
const currentBlock = await this.lastBlock
|
|
2317
|
-
const currentIndex = currentBlock?.index !== void 0 ? currentBlock.index : -1n
|
|
2268
|
+
const currentBlock = await this.lastBlock;
|
|
2269
|
+
const currentIndex = currentBlock?.index !== void 0 ? currentBlock.index : -1n;
|
|
2318
2270
|
if (index > currentIndex) {
|
|
2319
|
-
debug(`[consensus] \u2705 Committing block ${blockHash} at height ${index}`)
|
|
2271
|
+
debug(`[consensus] \u2705 Committing block ${blockHash} at height ${index}`);
|
|
2320
2272
|
try {
|
|
2321
|
-
const blockData = await globalThis.peernet.get(blockHash,
|
|
2322
|
-
await this.#addBlock(blockData)
|
|
2273
|
+
const blockData = await globalThis.peernet.get(blockHash, "block");
|
|
2274
|
+
await this.#addBlock(blockData);
|
|
2323
2275
|
} catch (e) {
|
|
2324
|
-
debug(`[consensus] Failed to commit block ${blockHash}:`, e?.message)
|
|
2276
|
+
debug(`[consensus] Failed to commit block ${blockHash}:`, e?.message);
|
|
2325
2277
|
}
|
|
2326
2278
|
} else {
|
|
2327
|
-
debug(`[consensus] \u2705 Block ${blockHash} at height ${index} already committed (proposer path)`)
|
|
2279
|
+
debug(`[consensus] \u2705 Block ${blockHash} at height ${index} already committed (proposer path)`);
|
|
2328
2280
|
}
|
|
2329
2281
|
try {
|
|
2330
|
-
const blockData = await globalThis.peernet.get(blockHash,
|
|
2331
|
-
globalThis.peernet.publish(
|
|
2332
|
-
globalThis.pubsub.publish(
|
|
2282
|
+
const blockData = await globalThis.peernet.get(blockHash, "block");
|
|
2283
|
+
globalThis.peernet.publish("add-block", blockData);
|
|
2284
|
+
globalThis.pubsub.publish("add-block", blockData);
|
|
2333
2285
|
} catch (e) {
|
|
2334
|
-
debug(
|
|
2286
|
+
debug("[consensus] Failed to broadcast committed block:", e?.message);
|
|
2335
2287
|
}
|
|
2336
2288
|
}
|
|
2337
2289
|
} catch (e) {
|
|
2338
|
-
debug(
|
|
2290
|
+
debug("[consensus] Error handling precommit:", e?.message);
|
|
2339
2291
|
}
|
|
2340
|
-
}
|
|
2292
|
+
};
|
|
2341
2293
|
// ─────────────────────────────────────────────────────────────────────────
|
|
2342
2294
|
this.#addTransaction = async (message) => {
|
|
2343
|
-
const transaction = new TransactionMessage(message)
|
|
2344
|
-
const hash = await transaction.hash()
|
|
2345
|
-
this.addPendingNonce(transaction.decoded.from, transaction.decoded.nonce)
|
|
2346
|
-
debug(`added ${hash}`)
|
|
2347
|
-
}
|
|
2348
|
-
this.#init()
|
|
2349
|
-
}
|
|
2350
|
-
#state
|
|
2351
|
-
#slotTime
|
|
2352
|
-
#blockTime
|
|
2353
|
-
#epochLength
|
|
2354
|
-
#validators
|
|
2355
|
-
#runningEpoch
|
|
2356
|
-
#currentEpochStartHeight
|
|
2357
|
-
#blocks
|
|
2358
|
-
#participants
|
|
2359
|
-
#participating
|
|
2360
|
-
#jail
|
|
2361
|
-
#jailReleaseTimers
|
|
2362
|
-
#peerConnectionRetries
|
|
2363
|
-
#maxPeerRetries
|
|
2364
|
-
#peerRetryDelay
|
|
2365
|
-
#peerReputations
|
|
2366
|
-
#minPeerScore
|
|
2367
|
-
#maxPeerFailures
|
|
2368
|
-
#consensusRound
|
|
2369
|
-
#roundTimer
|
|
2370
|
-
#prevotes
|
|
2371
|
-
#precommits
|
|
2372
|
-
#committedHeight
|
|
2373
|
-
#castedVotes
|
|
2295
|
+
const transaction = new TransactionMessage(message);
|
|
2296
|
+
const hash = await transaction.hash();
|
|
2297
|
+
this.addPendingNonce(transaction.decoded.from, transaction.decoded.nonce);
|
|
2298
|
+
debug(`added ${hash}`);
|
|
2299
|
+
};
|
|
2300
|
+
this.#init();
|
|
2301
|
+
}
|
|
2302
|
+
#state;
|
|
2303
|
+
#slotTime;
|
|
2304
|
+
#blockTime;
|
|
2305
|
+
#epochLength;
|
|
2306
|
+
#validators;
|
|
2307
|
+
#runningEpoch;
|
|
2308
|
+
#currentEpochStartHeight;
|
|
2309
|
+
#blocks;
|
|
2310
|
+
#participants;
|
|
2311
|
+
#participating;
|
|
2312
|
+
#jail;
|
|
2313
|
+
#jailReleaseTimers;
|
|
2314
|
+
#peerConnectionRetries;
|
|
2315
|
+
#maxPeerRetries;
|
|
2316
|
+
#peerRetryDelay;
|
|
2317
|
+
#peerReputations;
|
|
2318
|
+
#minPeerScore;
|
|
2319
|
+
#maxPeerFailures;
|
|
2320
|
+
#consensusRound;
|
|
2321
|
+
#roundTimer;
|
|
2322
|
+
#prevotes;
|
|
2323
|
+
#precommits;
|
|
2324
|
+
#committedHeight;
|
|
2325
|
+
#castedVotes;
|
|
2374
2326
|
// ────────────────────────────────────────────────────────────────────────────
|
|
2375
|
-
#connectionMonitor
|
|
2327
|
+
#connectionMonitor;
|
|
2376
2328
|
get nativeToken() {
|
|
2377
|
-
return addresses.nativeToken
|
|
2329
|
+
return addresses.nativeToken;
|
|
2378
2330
|
}
|
|
2379
2331
|
get validators() {
|
|
2380
|
-
return [...this.#validators]
|
|
2332
|
+
return [...this.#validators];
|
|
2381
2333
|
}
|
|
2382
2334
|
async hasTransactionToHandle() {
|
|
2383
|
-
const size = await globalThis.transactionPoolStore.size()
|
|
2384
|
-
if (size > 0) return true
|
|
2385
|
-
return false
|
|
2335
|
+
const size = await globalThis.transactionPoolStore.size();
|
|
2336
|
+
if (size > 0) return true;
|
|
2337
|
+
return false;
|
|
2386
2338
|
}
|
|
2387
2339
|
#sleep(ms) {
|
|
2388
|
-
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
2340
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2389
2341
|
}
|
|
2390
2342
|
async #recordPeerFailure(peerId, reason) {
|
|
2391
2343
|
if (!this.#peerReputations.has(peerId)) {
|
|
2392
|
-
this.#peerReputations.set(peerId, { score: 0, failures: [] })
|
|
2344
|
+
this.#peerReputations.set(peerId, { score: 0, failures: [] });
|
|
2393
2345
|
}
|
|
2394
|
-
const rep = this.#peerReputations.get(peerId)
|
|
2395
|
-
rep.score -= 1
|
|
2396
|
-
rep.failures.push(`${Date.now()}: ${reason}`)
|
|
2346
|
+
const rep = this.#peerReputations.get(peerId);
|
|
2347
|
+
rep.score -= 1;
|
|
2348
|
+
rep.failures.push(`${Date.now()}: ${reason}`);
|
|
2397
2349
|
if (rep.failures.length > this.#maxPeerFailures) {
|
|
2398
|
-
rep.failures.shift()
|
|
2350
|
+
rep.failures.shift();
|
|
2399
2351
|
}
|
|
2400
2352
|
if (rep.score < this.#minPeerScore) {
|
|
2401
|
-
console.warn(`[peer-ban] Peer ${peerId} banned after ${rep.failures.length} failures`)
|
|
2353
|
+
console.warn(`[peer-ban] Peer ${peerId} banned after ${rep.failures.length} failures`);
|
|
2402
2354
|
try {
|
|
2403
|
-
await globalThis.peernet.disconnect(peerId)
|
|
2355
|
+
await globalThis.peernet.disconnect(peerId);
|
|
2404
2356
|
} catch (e) {
|
|
2405
|
-
debug(`Failed to disconnect peer ${peerId}`)
|
|
2357
|
+
debug(`Failed to disconnect peer ${peerId}`);
|
|
2406
2358
|
}
|
|
2407
2359
|
}
|
|
2408
2360
|
}
|
|
2409
2361
|
#isJailed(address) {
|
|
2410
|
-
return typeof address ===
|
|
2362
|
+
return typeof address === "string" && this.#jail.has(address);
|
|
2411
2363
|
}
|
|
2412
2364
|
async #getConsensusValidators(nextBlockIndex) {
|
|
2413
|
-
const localBlock = await this.lastBlock
|
|
2414
|
-
const localIndex = localBlock?.index !== void 0 ? Number(localBlock.index) : -1
|
|
2415
|
-
if (
|
|
2416
|
-
Array.isArray(localBlock?.validators) &&
|
|
2417
|
-
localBlock.validators.length > 0 &&
|
|
2418
|
-
(nextBlockIndex === void 0 || nextBlockIndex === localIndex + 1)
|
|
2419
|
-
) {
|
|
2365
|
+
const localBlock = await this.lastBlock;
|
|
2366
|
+
const localIndex = localBlock?.index !== void 0 ? Number(localBlock.index) : -1;
|
|
2367
|
+
if (Array.isArray(localBlock?.validators) && localBlock.validators.length > 0 && (nextBlockIndex === void 0 || nextBlockIndex === localIndex + 1)) {
|
|
2420
2368
|
return [
|
|
2421
|
-
...new Set(
|
|
2422
|
-
|
|
2369
|
+
...new Set(
|
|
2370
|
+
localBlock.validators.map((validator) => validator.address).filter((address) => Boolean(address))
|
|
2371
|
+
)
|
|
2372
|
+
].sort();
|
|
2423
2373
|
}
|
|
2424
|
-
const validators = await this.staticCall(addresses.validators,
|
|
2425
|
-
return [...new Set(validators)].sort()
|
|
2374
|
+
const validators = await this.staticCall(addresses.validators, "validators");
|
|
2375
|
+
return [...new Set(validators)].sort();
|
|
2426
2376
|
}
|
|
2427
2377
|
#validateBlockValidators(blockMessage) {
|
|
2428
|
-
const validators = blockMessage.decoded.validators || []
|
|
2378
|
+
const validators = blockMessage.decoded.validators || [];
|
|
2429
2379
|
if (!Array.isArray(validators) || validators.length === 0) {
|
|
2430
|
-
throw new Error(`Block ${blockMessage.decoded.index} does not include validators`)
|
|
2380
|
+
throw new Error(`Block ${blockMessage.decoded.index} does not include validators`);
|
|
2431
2381
|
}
|
|
2432
|
-
if (!blockMessage.decoded.protocolVersion || typeof blockMessage.decoded.protocolVersion !==
|
|
2433
|
-
throw new Error(`Block ${blockMessage.decoded.index} does not have a valid protocolVersion field`)
|
|
2382
|
+
if (!blockMessage.decoded.protocolVersion || typeof blockMessage.decoded.protocolVersion !== "string") {
|
|
2383
|
+
throw new Error(`Block ${blockMessage.decoded.index} does not have a valid protocolVersion field`);
|
|
2434
2384
|
}
|
|
2435
2385
|
if (!this.isVersionCompatible(blockMessage.decoded.protocolVersion)) {
|
|
2436
2386
|
throw new Error(
|
|
2437
2387
|
`Block ${blockMessage.decoded.index} uses incompatible protocol version: ${blockMessage.decoded.protocolVersion} (local: ${this.version}). Major.minor version must match.`
|
|
2438
|
-
)
|
|
2388
|
+
);
|
|
2439
2389
|
}
|
|
2440
|
-
if (!blockMessage.decoded.producer || typeof blockMessage.decoded.producer !==
|
|
2441
|
-
throw new Error(`Block ${blockMessage.decoded.index} does not have a valid producer field`)
|
|
2390
|
+
if (!blockMessage.decoded.producer || typeof blockMessage.decoded.producer !== "string") {
|
|
2391
|
+
throw new Error(`Block ${blockMessage.decoded.index} does not have a valid producer field`);
|
|
2442
2392
|
}
|
|
2443
|
-
if (!blockMessage.decoded.producerProof || typeof blockMessage.decoded.producerProof !==
|
|
2444
|
-
throw new Error(`Block ${blockMessage.decoded.index} does not have a valid producerProof field`)
|
|
2393
|
+
if (!blockMessage.decoded.producerProof || typeof blockMessage.decoded.producerProof !== "string") {
|
|
2394
|
+
throw new Error(`Block ${blockMessage.decoded.index} does not have a valid producerProof field`);
|
|
2445
2395
|
}
|
|
2446
|
-
const producerIsValidator = validators.some((v) => v.address === blockMessage.decoded.producer)
|
|
2396
|
+
const producerIsValidator = validators.some((v) => v.address === blockMessage.decoded.producer);
|
|
2447
2397
|
if (!producerIsValidator) {
|
|
2448
2398
|
throw new Error(
|
|
2449
2399
|
`Block ${blockMessage.decoded.index} producer ${blockMessage.decoded.producer} is not in validators list`
|
|
2450
|
-
)
|
|
2400
|
+
);
|
|
2451
2401
|
}
|
|
2452
|
-
const addresses2 = validators.map((validator) => validator.address)
|
|
2453
|
-
if (addresses2.some((address) => typeof address !==
|
|
2454
|
-
throw new Error(`Block ${blockMessage.decoded.index} includes an invalid validator address`)
|
|
2402
|
+
const addresses2 = validators.map((validator) => validator.address);
|
|
2403
|
+
if (addresses2.some((address) => typeof address !== "string" || address.length === 0)) {
|
|
2404
|
+
throw new Error(`Block ${blockMessage.decoded.index} includes an invalid validator address`);
|
|
2455
2405
|
}
|
|
2456
|
-
const canonicalAddresses = [...addresses2].sort()
|
|
2406
|
+
const canonicalAddresses = [...addresses2].sort();
|
|
2457
2407
|
if (canonicalAddresses.some((address, index) => address !== addresses2[index])) {
|
|
2458
|
-
throw new Error(`Block ${blockMessage.decoded.index} validators are not canonically sorted`)
|
|
2408
|
+
throw new Error(`Block ${blockMessage.decoded.index} validators are not canonically sorted`);
|
|
2459
2409
|
}
|
|
2460
2410
|
if (new Set(addresses2).size !== addresses2.length) {
|
|
2461
|
-
throw new Error(`Block ${blockMessage.decoded.index} validators contain duplicates`)
|
|
2411
|
+
throw new Error(`Block ${blockMessage.decoded.index} validators contain duplicates`);
|
|
2462
2412
|
}
|
|
2463
|
-
const validatorCount = BigInt(validators.length)
|
|
2464
|
-
const expectedReward = blockMessage.decoded.fees / validatorCount + blockMessage.decoded.reward / validatorCount
|
|
2413
|
+
const validatorCount = BigInt(validators.length);
|
|
2414
|
+
const expectedReward = blockMessage.decoded.fees / validatorCount + blockMessage.decoded.reward / validatorCount;
|
|
2465
2415
|
for (const validator of validators) {
|
|
2466
2416
|
if (validator.reward !== expectedReward) {
|
|
2467
2417
|
throw new Error(
|
|
2468
2418
|
`Block ${blockMessage.decoded.index} has an invalid reward for validator ${validator.address}: expected ${expectedReward}, got ${validator.reward}`
|
|
2469
|
-
)
|
|
2419
|
+
);
|
|
2470
2420
|
}
|
|
2471
2421
|
}
|
|
2472
2422
|
}
|
|
2473
2423
|
/** Check if the next block will cross an epoch boundary (block-based timing) */
|
|
2474
2424
|
#isEpochBoundary(blockHeight) {
|
|
2475
|
-
return (blockHeight + 1) % this.#epochLength === 0
|
|
2425
|
+
return (blockHeight + 1) % this.#epochLength === 0;
|
|
2476
2426
|
}
|
|
2477
2427
|
/** Handle epoch transition when a block crosses epoch boundary */
|
|
2478
2428
|
async #handleEpochBoundary(blockHeight) {
|
|
2479
|
-
if (!this.#isEpochBoundary(blockHeight)) return
|
|
2480
|
-
this.#currentEpochStartHeight = blockHeight + 1
|
|
2481
|
-
this.#consensusRound = 0
|
|
2429
|
+
if (!this.#isEpochBoundary(blockHeight)) return;
|
|
2430
|
+
this.#currentEpochStartHeight = blockHeight + 1;
|
|
2431
|
+
this.#consensusRound = 0;
|
|
2482
2432
|
debug(
|
|
2483
2433
|
`[consensus] Epoch boundary at block ${blockHeight}: new epoch starts at height ${this.#currentEpochStartHeight}`
|
|
2484
|
-
)
|
|
2434
|
+
);
|
|
2485
2435
|
if (this.#participating && !this.#runningEpoch) {
|
|
2486
|
-
await this.#runEpoch()
|
|
2436
|
+
await this.#runEpoch();
|
|
2487
2437
|
}
|
|
2488
2438
|
}
|
|
2489
2439
|
async #runEpoch() {
|
|
2490
|
-
if (this.#runningEpoch) return
|
|
2491
|
-
this.#runningEpoch = true
|
|
2492
|
-
console.log(
|
|
2493
|
-
const validators = await this.#getConsensusValidators()
|
|
2494
|
-
console.log({ validators })
|
|
2440
|
+
if (this.#runningEpoch) return;
|
|
2441
|
+
this.#runningEpoch = true;
|
|
2442
|
+
console.log("epoch");
|
|
2443
|
+
const validators = await this.#getConsensusValidators();
|
|
2444
|
+
console.log({ validators });
|
|
2495
2445
|
if (this.#isJailed(peernet.selectedAccount)) {
|
|
2496
|
-
this.#runningEpoch = false
|
|
2497
|
-
return
|
|
2446
|
+
this.#runningEpoch = false;
|
|
2447
|
+
return;
|
|
2498
2448
|
}
|
|
2499
2449
|
if (!validators.includes(peernet.selectedAccount)) {
|
|
2500
|
-
this.#runningEpoch = false
|
|
2501
|
-
return
|
|
2450
|
+
this.#runningEpoch = false;
|
|
2451
|
+
return;
|
|
2502
2452
|
}
|
|
2503
|
-
const localBlock = await this.lastBlock
|
|
2504
|
-
const nextIndex = (localBlock?.index !== void 0 ? Number(localBlock.index) : -1) + 1
|
|
2505
|
-
const proposerIdx = (nextIndex + this.#consensusRound) % validators.length
|
|
2506
|
-
const isProposer = validators[proposerIdx] === peernet.selectedAccount
|
|
2453
|
+
const localBlock = await this.lastBlock;
|
|
2454
|
+
const nextIndex = (localBlock?.index !== void 0 ? Number(localBlock.index) : -1) + 1;
|
|
2455
|
+
const proposerIdx = (nextIndex + this.#consensusRound) % validators.length;
|
|
2456
|
+
const isProposer = validators[proposerIdx] === peernet.selectedAccount;
|
|
2507
2457
|
if (!isProposer) {
|
|
2508
2458
|
if (!this.#roundTimer) {
|
|
2509
2459
|
this.#roundTimer = setTimeout(async () => {
|
|
2510
|
-
this.#roundTimer = null
|
|
2511
|
-
this.#consensusRound
|
|
2512
|
-
debug(`[consensus] Round timed out, advancing to round ${this.#consensusRound}`)
|
|
2513
|
-
this.#runningEpoch = false
|
|
2514
|
-
if (this.#participating) await this.#runEpoch()
|
|
2515
|
-
}, this.#slotTime)
|
|
2460
|
+
this.#roundTimer = null;
|
|
2461
|
+
this.#consensusRound++;
|
|
2462
|
+
debug(`[consensus] Round timed out, advancing to round ${this.#consensusRound}`);
|
|
2463
|
+
this.#runningEpoch = false;
|
|
2464
|
+
if (this.#participating) await this.#runEpoch();
|
|
2465
|
+
}, this.#slotTime);
|
|
2516
2466
|
}
|
|
2517
|
-
this.#runningEpoch = false
|
|
2518
|
-
return
|
|
2467
|
+
this.#runningEpoch = false;
|
|
2468
|
+
return;
|
|
2519
2469
|
}
|
|
2520
2470
|
if (this.#roundTimer) {
|
|
2521
|
-
clearTimeout(this.#roundTimer)
|
|
2522
|
-
this.#roundTimer = null
|
|
2471
|
+
clearTimeout(this.#roundTimer);
|
|
2472
|
+
this.#roundTimer = null;
|
|
2523
2473
|
}
|
|
2524
|
-
const start = Date.now()
|
|
2474
|
+
const start = Date.now();
|
|
2525
2475
|
try {
|
|
2526
|
-
await this.#createBlock()
|
|
2476
|
+
await this.#createBlock();
|
|
2527
2477
|
} catch (error) {
|
|
2528
|
-
console.error(error)
|
|
2478
|
+
console.error(error);
|
|
2529
2479
|
}
|
|
2530
|
-
const end = Date.now()
|
|
2531
|
-
console.log((end - start) / 1e3 +
|
|
2532
|
-
const elapsed = end - start
|
|
2533
|
-
const remaining = this.#blockTime - elapsed
|
|
2534
|
-
const hasMore = await this.hasTransactionToHandle()
|
|
2535
|
-
if (!hasMore && remaining > 0) await this.#sleep(remaining)
|
|
2536
|
-
this.#runningEpoch = false
|
|
2537
|
-
if (hasMore) return this.#runEpoch()
|
|
2480
|
+
const end = Date.now();
|
|
2481
|
+
console.log((end - start) / 1e3 + " s");
|
|
2482
|
+
const elapsed = end - start;
|
|
2483
|
+
const remaining = this.#blockTime - elapsed;
|
|
2484
|
+
const hasMore = await this.hasTransactionToHandle();
|
|
2485
|
+
if (!hasMore && remaining > 0) await this.#sleep(remaining);
|
|
2486
|
+
this.#runningEpoch = false;
|
|
2487
|
+
if (hasMore) return this.#runEpoch();
|
|
2538
2488
|
}
|
|
2539
2489
|
async #setup() {
|
|
2540
2490
|
const contracts = [
|
|
@@ -2554,706 +2504,704 @@ class Chain extends VersionControl {
|
|
|
2554
2504
|
address: addresses.nameService,
|
|
2555
2505
|
message: nameServiceMessage
|
|
2556
2506
|
}
|
|
2557
|
-
]
|
|
2507
|
+
];
|
|
2558
2508
|
await Promise.all(
|
|
2559
2509
|
contracts.map(async ({ address, message }) => {
|
|
2560
|
-
message = await new ContractMessage(Uint8Array.from(message.split(
|
|
2561
|
-
await globalThis.contractStore.put(address, message.encoded)
|
|
2510
|
+
message = await new ContractMessage(Uint8Array.from(message.split(",").map((string) => Number(string))));
|
|
2511
|
+
await globalThis.contractStore.put(address, message.encoded);
|
|
2562
2512
|
})
|
|
2563
|
-
)
|
|
2564
|
-
console.log(
|
|
2513
|
+
);
|
|
2514
|
+
console.log("handle native contracts");
|
|
2565
2515
|
}
|
|
2566
2516
|
async #init() {
|
|
2567
|
-
this.#participants = []
|
|
2568
|
-
this.#participating = false
|
|
2569
|
-
this.#connectionMonitor = new ConnectionMonitor()
|
|
2570
|
-
console.log(
|
|
2571
|
-
const initialized = await globalThis.contractStore.has(addresses.contractFactory)
|
|
2572
|
-
console.log(`chain initialized: ${initialized}`)
|
|
2573
|
-
if (!initialized) await this.#setup()
|
|
2574
|
-
this.utils = { formatUnits, parseUnits }
|
|
2575
|
-
console.log(
|
|
2576
|
-
await super.init()
|
|
2577
|
-
console.log(
|
|
2578
|
-
this.#connectionMonitor.start(this.version)
|
|
2579
|
-
await globalThis.peernet.addRequestHandler(
|
|
2580
|
-
const bw = globalThis.peernet.client?.bw || { up: 0, down: 0 }
|
|
2581
|
-
return new BWMessage(bw)
|
|
2582
|
-
})
|
|
2583
|
-
await globalThis.peernet.addRequestHandler(
|
|
2584
|
-
await globalThis.peernet.addRequestHandler(
|
|
2585
|
-
await globalThis.peernet.addRequestHandler(
|
|
2586
|
-
const lastblock =
|
|
2587
|
-
const values = this.machine?.states?.info || {}
|
|
2588
|
-
return new globalThis.peernet.protos[
|
|
2517
|
+
this.#participants = [];
|
|
2518
|
+
this.#participating = false;
|
|
2519
|
+
this.#connectionMonitor = new ConnectionMonitor();
|
|
2520
|
+
console.log("[chain] init:start");
|
|
2521
|
+
const initialized = await globalThis.contractStore.has(addresses.contractFactory);
|
|
2522
|
+
console.log(`chain initialized: ${initialized}`);
|
|
2523
|
+
if (!initialized) await this.#setup();
|
|
2524
|
+
this.utils = { formatUnits, parseUnits };
|
|
2525
|
+
console.log("init");
|
|
2526
|
+
await super.init();
|
|
2527
|
+
console.log("super init done");
|
|
2528
|
+
this.#connectionMonitor.start(this.version);
|
|
2529
|
+
await globalThis.peernet.addRequestHandler("bw-request-message", () => {
|
|
2530
|
+
const bw = globalThis.peernet.client?.bw || { up: 0, down: 0 };
|
|
2531
|
+
return new BWMessage(bw);
|
|
2532
|
+
});
|
|
2533
|
+
await globalThis.peernet.addRequestHandler("transactionPool", this.#transactionPoolHandler.bind(this));
|
|
2534
|
+
await globalThis.peernet.addRequestHandler("version", this.#versionHandler.bind(this));
|
|
2535
|
+
await globalThis.peernet.addRequestHandler("stateInfo", async () => {
|
|
2536
|
+
const lastblock = await this.lastBlock || { index: 0, hash: "0x0", previousHash: "0x0" };
|
|
2537
|
+
const values = this.machine?.states?.info || {};
|
|
2538
|
+
return new globalThis.peernet.protos["peernet-response"]({
|
|
2589
2539
|
response: new StateMessage({ lastblock, values }).encoded
|
|
2590
|
-
})
|
|
2591
|
-
})
|
|
2592
|
-
globalThis.peernet.subscribe(
|
|
2593
|
-
globalThis.peernet.subscribe(
|
|
2594
|
-
globalThis.peernet.subscribe(
|
|
2595
|
-
globalThis.peernet.subscribe(
|
|
2596
|
-
globalThis.peernet.subscribe(
|
|
2597
|
-
globalThis.peernet.subscribe(
|
|
2598
|
-
globalThis.peernet.subscribe(
|
|
2599
|
-
globalThis.peernet.subscribe(
|
|
2600
|
-
globalThis.pubsub.subscribe(
|
|
2601
|
-
globalThis.pubsub.publish(
|
|
2602
|
-
console.log(
|
|
2603
|
-
this.readyResolve(true)
|
|
2540
|
+
});
|
|
2541
|
+
});
|
|
2542
|
+
globalThis.peernet.subscribe("add-block", this.#addBlock.bind(this));
|
|
2543
|
+
globalThis.peernet.subscribe("invalid-transaction", this.#invalidTransaction.bind(this));
|
|
2544
|
+
globalThis.peernet.subscribe("send-transaction", this.#sendTransaction.bind(this));
|
|
2545
|
+
globalThis.peernet.subscribe("add-transaction", this.#addTransaction.bind(this));
|
|
2546
|
+
globalThis.peernet.subscribe("validator:timeout", this.#validatorTimeout.bind(this));
|
|
2547
|
+
globalThis.peernet.subscribe("consensus:propose", this.#handleProposal.bind(this));
|
|
2548
|
+
globalThis.peernet.subscribe("consensus:prevote", this.#handlePrevote.bind(this));
|
|
2549
|
+
globalThis.peernet.subscribe("consensus:precommit", this.#handlePrecommit.bind(this));
|
|
2550
|
+
globalThis.pubsub.subscribe("peer:connected", this.#peerConnected.bind(this));
|
|
2551
|
+
globalThis.pubsub.publish("chain:ready", true);
|
|
2552
|
+
console.log("[chain] init:done");
|
|
2553
|
+
this.readyResolve(true);
|
|
2604
2554
|
}
|
|
2605
2555
|
async #invalidTransaction(hash) {
|
|
2606
|
-
hash = new TextDecoder().decode(hash)
|
|
2607
|
-
if (!
|
|
2608
|
-
debug(`transaction ${hash} not in pool`)
|
|
2609
|
-
return
|
|
2556
|
+
hash = new TextDecoder().decode(hash);
|
|
2557
|
+
if (!await globalThis.transactionPoolStore.has(hash)) {
|
|
2558
|
+
debug(`transaction ${hash} not in pool`);
|
|
2559
|
+
return;
|
|
2610
2560
|
}
|
|
2611
|
-
console.log(`removing invalid transaction: ${hash}`)
|
|
2612
|
-
await globalThis.transactionPoolStore.delete(hash)
|
|
2561
|
+
console.log(`removing invalid transaction: ${hash}`);
|
|
2562
|
+
await globalThis.transactionPoolStore.delete(hash);
|
|
2613
2563
|
}
|
|
2614
2564
|
async #validatorTimeout(validatorInfo) {
|
|
2615
|
-
const address = validatorInfo?.address
|
|
2616
|
-
if (!address) return
|
|
2617
|
-
const timeout = Math.min(Math.max(Number(validatorInfo.timeout) || 0, 0), 60 * 60 * 1e3)
|
|
2618
|
-
const existingRelease = this.#jailReleaseTimers.get(address)
|
|
2619
|
-
if (existingRelease) clearTimeout(existingRelease)
|
|
2620
|
-
this.#jail.add(address)
|
|
2565
|
+
const address = validatorInfo?.address;
|
|
2566
|
+
if (!address) return;
|
|
2567
|
+
const timeout = Math.min(Math.max(Number(validatorInfo.timeout) || 0, 0), 60 * 60 * 1e3);
|
|
2568
|
+
const existingRelease = this.#jailReleaseTimers.get(address);
|
|
2569
|
+
if (existingRelease) clearTimeout(existingRelease);
|
|
2570
|
+
this.#jail.add(address);
|
|
2621
2571
|
const releaseTimer = setTimeout(() => {
|
|
2622
|
-
this.#jail.delete(address)
|
|
2623
|
-
this.#jailReleaseTimers.delete(address)
|
|
2624
|
-
}, timeout)
|
|
2625
|
-
this.#jailReleaseTimers.set(address, releaseTimer)
|
|
2626
|
-
}
|
|
2627
|
-
#castVote
|
|
2628
|
-
#handleProposal
|
|
2629
|
-
#handlePrevote
|
|
2630
|
-
#handlePrecommit
|
|
2631
|
-
#addTransaction
|
|
2572
|
+
this.#jail.delete(address);
|
|
2573
|
+
this.#jailReleaseTimers.delete(address);
|
|
2574
|
+
}, timeout);
|
|
2575
|
+
this.#jailReleaseTimers.set(address, releaseTimer);
|
|
2576
|
+
}
|
|
2577
|
+
#castVote;
|
|
2578
|
+
#handleProposal;
|
|
2579
|
+
#handlePrevote;
|
|
2580
|
+
#handlePrecommit;
|
|
2581
|
+
#addTransaction;
|
|
2632
2582
|
async #prepareRequest(request) {
|
|
2633
|
-
let node = await new globalThis.peernet.protos[
|
|
2634
|
-
return globalThis.peernet.prepareMessage(node)
|
|
2583
|
+
let node = await new globalThis.peernet.protos["peernet-request"]({ request });
|
|
2584
|
+
return globalThis.peernet.prepareMessage(node);
|
|
2635
2585
|
}
|
|
2636
2586
|
async #makeRequest(peer, request) {
|
|
2637
|
-
const node = await this.#prepareRequest(request)
|
|
2587
|
+
const node = await this.#prepareRequest(request);
|
|
2638
2588
|
try {
|
|
2639
|
-
let response = await peer.request(node.encoded)
|
|
2640
|
-
|
|
2589
|
+
let response = await peer.request(node.encoded);
|
|
2590
|
+
console.log(`raw response for request ${request}:`, response);
|
|
2591
|
+
if (response === void 0 || response === null) return response;
|
|
2641
2592
|
const normalizeResponse = async (payload) => {
|
|
2642
|
-
if (payload ===
|
|
2643
|
-
if (payload && typeof payload ===
|
|
2644
|
-
if (
|
|
2645
|
-
return normalizeResponse(payload.decoded.response)
|
|
2593
|
+
if (payload === void 0 || payload === null) return payload;
|
|
2594
|
+
if (payload && typeof payload === "object") {
|
|
2595
|
+
if ("decoded" in payload && payload?.decoded && "response" in payload.decoded) {
|
|
2596
|
+
return normalizeResponse(payload.decoded.response);
|
|
2646
2597
|
}
|
|
2647
|
-
if (
|
|
2648
|
-
return normalizeResponse(payload.response)
|
|
2598
|
+
if ("response" in payload) {
|
|
2599
|
+
return normalizeResponse(payload.response);
|
|
2649
2600
|
}
|
|
2650
2601
|
}
|
|
2651
|
-
if (payload instanceof Uint8Array ||
|
|
2652
|
-
|
|
2602
|
+
if (payload instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(payload)) {
|
|
2603
|
+
const bytes = payload instanceof Uint8Array ? payload : new Uint8Array(payload);
|
|
2604
|
+
try {
|
|
2605
|
+
const wrapped = await new globalThis.peernet.protos["peernet-response"](bytes);
|
|
2606
|
+
if (wrapped?.decoded?.response !== void 0) {
|
|
2607
|
+
return wrapped.decoded.response;
|
|
2608
|
+
}
|
|
2609
|
+
} catch {
|
|
2610
|
+
}
|
|
2611
|
+
try {
|
|
2612
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
2613
|
+
} catch {
|
|
2614
|
+
return bytes;
|
|
2615
|
+
}
|
|
2653
2616
|
}
|
|
2654
|
-
if (typeof payload ===
|
|
2617
|
+
if (typeof payload === "string") {
|
|
2655
2618
|
try {
|
|
2656
|
-
return JSON.parse(payload)
|
|
2619
|
+
return JSON.parse(payload);
|
|
2657
2620
|
} catch {
|
|
2658
|
-
return payload
|
|
2621
|
+
return payload;
|
|
2659
2622
|
}
|
|
2660
2623
|
}
|
|
2661
|
-
return payload
|
|
2662
|
-
}
|
|
2663
|
-
return await normalizeResponse(response)
|
|
2624
|
+
return payload;
|
|
2625
|
+
};
|
|
2626
|
+
return await normalizeResponse(response);
|
|
2664
2627
|
} catch (error) {
|
|
2665
|
-
const peerId = peer?.peerId || peer?.id || peer?.address ||
|
|
2666
|
-
debug(`peernet request failed: ${request} -> ${peerId}:`, error?.message ?? error)
|
|
2667
|
-
throw error
|
|
2628
|
+
const peerId = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
2629
|
+
debug(`peernet request failed: ${request} -> ${peerId}:`, error?.message ?? error);
|
|
2630
|
+
throw error;
|
|
2668
2631
|
}
|
|
2669
2632
|
}
|
|
2670
2633
|
async #decodeKnownBlocksResponse(response) {
|
|
2671
|
-
if (!response) return null
|
|
2634
|
+
if (!response) return null;
|
|
2672
2635
|
if (Array.isArray(response)) {
|
|
2673
|
-
return { blocks: response }
|
|
2636
|
+
return { blocks: response };
|
|
2674
2637
|
}
|
|
2675
2638
|
if (Array.isArray(response.blocks)) {
|
|
2676
|
-
return { blocks: response.blocks }
|
|
2639
|
+
return { blocks: response.blocks };
|
|
2677
2640
|
}
|
|
2678
2641
|
if (response.response && Array.isArray(response.response.blocks)) {
|
|
2679
|
-
return { blocks: response.response.blocks }
|
|
2642
|
+
return { blocks: response.response.blocks };
|
|
2680
2643
|
}
|
|
2681
|
-
if (typeof response ===
|
|
2644
|
+
if (typeof response === "string") {
|
|
2682
2645
|
try {
|
|
2683
|
-
const parsed = JSON.parse(response)
|
|
2684
|
-
if (Array.isArray(parsed)) return { blocks: parsed }
|
|
2685
|
-
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks }
|
|
2686
|
-
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks }
|
|
2646
|
+
const parsed = JSON.parse(response);
|
|
2647
|
+
if (Array.isArray(parsed)) return { blocks: parsed };
|
|
2648
|
+
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks };
|
|
2649
|
+
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks };
|
|
2687
2650
|
} catch {
|
|
2688
|
-
return null
|
|
2651
|
+
return null;
|
|
2689
2652
|
}
|
|
2690
|
-
return null
|
|
2653
|
+
return null;
|
|
2691
2654
|
}
|
|
2692
|
-
if (!(response instanceof Uint8Array)) return null
|
|
2693
|
-
let payload = response
|
|
2655
|
+
if (!(response instanceof Uint8Array)) return null;
|
|
2656
|
+
let payload = response;
|
|
2694
2657
|
for (let i = 0; i < 3; i++) {
|
|
2695
|
-
if (!(payload instanceof Uint8Array)) break
|
|
2658
|
+
if (!(payload instanceof Uint8Array)) break;
|
|
2696
2659
|
try {
|
|
2697
|
-
const nestedResponse = await new globalThis.peernet.protos[
|
|
2698
|
-
payload = nestedResponse?.decoded?.response
|
|
2660
|
+
const nestedResponse = await new globalThis.peernet.protos["peernet-response"](payload);
|
|
2661
|
+
payload = nestedResponse?.decoded?.response;
|
|
2699
2662
|
} catch {
|
|
2700
|
-
break
|
|
2663
|
+
break;
|
|
2701
2664
|
}
|
|
2702
2665
|
}
|
|
2703
|
-
if (Array.isArray(payload)) return { blocks: payload }
|
|
2704
|
-
if (payload && Array.isArray(payload.blocks)) return { blocks: payload.blocks }
|
|
2705
|
-
if (payload?.response && Array.isArray(payload.response.blocks)) return { blocks: payload.response.blocks }
|
|
2666
|
+
if (Array.isArray(payload)) return { blocks: payload };
|
|
2667
|
+
if (payload && Array.isArray(payload.blocks)) return { blocks: payload.blocks };
|
|
2668
|
+
if (payload?.response && Array.isArray(payload.response.blocks)) return { blocks: payload.response.blocks };
|
|
2706
2669
|
try {
|
|
2707
|
-
const decoded = new TextDecoder().decode(response)
|
|
2708
|
-
const parsed = JSON.parse(decoded)
|
|
2709
|
-
if (Array.isArray(parsed)) return { blocks: parsed }
|
|
2710
|
-
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks }
|
|
2711
|
-
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks }
|
|
2670
|
+
const decoded = new TextDecoder().decode(response);
|
|
2671
|
+
const parsed = JSON.parse(decoded);
|
|
2672
|
+
if (Array.isArray(parsed)) return { blocks: parsed };
|
|
2673
|
+
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks };
|
|
2674
|
+
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks };
|
|
2712
2675
|
} catch {
|
|
2713
|
-
return null
|
|
2676
|
+
return null;
|
|
2714
2677
|
}
|
|
2715
|
-
return null
|
|
2678
|
+
return null;
|
|
2716
2679
|
}
|
|
2717
2680
|
async getPeerTransactionPool(peer) {
|
|
2718
|
-
let transactionsInPool = await this.#makeRequest(peer,
|
|
2681
|
+
let transactionsInPool = await this.#makeRequest(peer, "transactionPool");
|
|
2682
|
+
console.log("raw response for request transactionPool:", transactionsInPool);
|
|
2719
2683
|
if (transactionsInPool instanceof Uint8Array) {
|
|
2720
|
-
debug(
|
|
2721
|
-
return []
|
|
2684
|
+
debug("transactionPool response must be decoded array payload");
|
|
2685
|
+
return [];
|
|
2722
2686
|
}
|
|
2723
|
-
if (!Array.isArray(transactionsInPool)) return []
|
|
2724
|
-
const localTransactions = new Set(await globalThis.transactionPoolStore.keys())
|
|
2725
|
-
const transactionsToGet = []
|
|
2687
|
+
if (!Array.isArray(transactionsInPool)) return [];
|
|
2688
|
+
const localTransactions = new Set(await globalThis.transactionPoolStore.keys());
|
|
2689
|
+
const transactionsToGet = [];
|
|
2726
2690
|
for (const key of transactionsInPool) {
|
|
2727
2691
|
if (!localTransactions.has(key)) {
|
|
2728
|
-
let txData
|
|
2692
|
+
let txData;
|
|
2729
2693
|
try {
|
|
2730
|
-
txData = await globalThis.peernet.get(key,
|
|
2694
|
+
txData = await globalThis.peernet.get(key, "transaction");
|
|
2731
2695
|
} catch (error) {
|
|
2732
|
-
debug(`Failed to get transaction ${key}:`, error?.message ?? error)
|
|
2696
|
+
debug(`Failed to get transaction ${key}:`, error?.message ?? error);
|
|
2733
2697
|
}
|
|
2734
2698
|
if (txData !== void 0) {
|
|
2735
|
-
transactionsToGet.push(transactionPoolStore.put(key, txData))
|
|
2699
|
+
transactionsToGet.push(transactionPoolStore.put(key, txData));
|
|
2736
2700
|
}
|
|
2737
2701
|
}
|
|
2738
2702
|
}
|
|
2739
|
-
return Promise.all(transactionsToGet)
|
|
2703
|
+
return Promise.all(transactionsToGet);
|
|
2740
2704
|
}
|
|
2741
2705
|
async #peerConnected(peerId) {
|
|
2742
|
-
debug(`peer connected: ${peerId}`)
|
|
2743
|
-
const peer = peernet.getConnection(peerId)
|
|
2706
|
+
debug(`peer connected: ${peerId}`);
|
|
2707
|
+
const peer = peernet.getConnection(peerId);
|
|
2744
2708
|
if (!peer) {
|
|
2745
|
-
debug(`peer not found: ${peerId}`)
|
|
2746
|
-
return
|
|
2709
|
+
debug(`peer not found: ${peerId}`);
|
|
2710
|
+
return;
|
|
2747
2711
|
}
|
|
2748
2712
|
if (!peer.version) {
|
|
2749
2713
|
try {
|
|
2750
|
-
let versionResponse = await this.#makeRequest(peer,
|
|
2714
|
+
let versionResponse = await this.#makeRequest(peer, "version");
|
|
2751
2715
|
if (versionResponse instanceof Uint8Array) {
|
|
2752
|
-
versionResponse = new TextDecoder().decode(versionResponse)
|
|
2716
|
+
versionResponse = new TextDecoder().decode(versionResponse);
|
|
2753
2717
|
}
|
|
2754
|
-
if (typeof versionResponse ===
|
|
2755
|
-
peer.version = versionResponse
|
|
2756
|
-
} else if (
|
|
2757
|
-
versionResponse
|
|
2758
|
-
typeof versionResponse === 'object' &&
|
|
2759
|
-
typeof versionResponse.version === 'string'
|
|
2760
|
-
) {
|
|
2761
|
-
peer.version = versionResponse.version
|
|
2718
|
+
if (typeof versionResponse === "string") {
|
|
2719
|
+
peer.version = versionResponse;
|
|
2720
|
+
} else if (versionResponse && typeof versionResponse === "object" && typeof versionResponse.version === "string") {
|
|
2721
|
+
peer.version = versionResponse.version;
|
|
2762
2722
|
}
|
|
2763
|
-
if (!peer.version || typeof peer.version !==
|
|
2764
|
-
const reason = `invalid version response from peer ${peerId}
|
|
2765
|
-
debug(reason)
|
|
2766
|
-
await this.#recordPeerFailure(peerId, reason)
|
|
2767
|
-
return
|
|
2723
|
+
if (!peer.version || typeof peer.version !== "string") {
|
|
2724
|
+
const reason = `invalid version response from peer ${peerId}`;
|
|
2725
|
+
debug(reason);
|
|
2726
|
+
await this.#recordPeerFailure(peerId, reason);
|
|
2727
|
+
return;
|
|
2768
2728
|
}
|
|
2769
2729
|
} catch (error) {
|
|
2770
|
-
debug(`failed to request version from peer ${peerId}:`, error?.message ?? error)
|
|
2771
|
-
return
|
|
2730
|
+
debug(`failed to request version from peer ${peerId}:`, error?.message ?? error);
|
|
2731
|
+
return;
|
|
2772
2732
|
}
|
|
2773
2733
|
}
|
|
2774
|
-
debug(`peer connected with version ${peer.version}`)
|
|
2734
|
+
debug(`peer connected with version ${peer.version}`);
|
|
2775
2735
|
if (!this.isVersionCompatible(peer.version)) {
|
|
2776
|
-
const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})
|
|
2777
|
-
console.error(`[chain] ${mismatchReason}`)
|
|
2778
|
-
await this.#recordPeerFailure(peerId, mismatchReason)
|
|
2779
|
-
return
|
|
2736
|
+
const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})`;
|
|
2737
|
+
console.error(`[chain] ${mismatchReason}`);
|
|
2738
|
+
await this.#recordPeerFailure(peerId, mismatchReason);
|
|
2739
|
+
return;
|
|
2780
2740
|
}
|
|
2781
|
-
let lastBlock
|
|
2741
|
+
let lastBlock;
|
|
2782
2742
|
try {
|
|
2783
|
-
console.log(
|
|
2784
|
-
console.log(await this.lastBlock)
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
if (
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
)
|
|
2792
|
-
throw new Error(`invalid lastBlock payload: ${typeof lastBlockRaw}`)
|
|
2793
|
-
}
|
|
2794
|
-
console.log('raw last block response:', lastBlockRaw)
|
|
2795
|
-
console.log(new LastBlockMessage(lastBlockRaw))
|
|
2796
|
-
lastBlock = new LastBlockMessage(lastBlockRaw).decoded
|
|
2743
|
+
console.log("requesting last block from peer...");
|
|
2744
|
+
console.log(await this.lastBlock);
|
|
2745
|
+
const lastBlockRaw = await this.#makeRequest(peer, "lastBlock");
|
|
2746
|
+
console.log("raw last block response:", lastBlockRaw);
|
|
2747
|
+
if (lastBlockRaw === void 0 || lastBlockRaw === null || typeof lastBlockRaw !== "object" && !(lastBlockRaw instanceof Uint8Array)) {
|
|
2748
|
+
throw new Error(`invalid lastBlock payload: ${typeof lastBlockRaw}`);
|
|
2749
|
+
}
|
|
2750
|
+
console.log(new LastBlockMessage(lastBlockRaw));
|
|
2751
|
+
lastBlock = new LastBlockMessage(lastBlockRaw).decoded;
|
|
2797
2752
|
} catch (error) {
|
|
2798
|
-
const peerName = peer?.peerId || peer?.id || peer?.address || peerId ||
|
|
2799
|
-
debug(`lastBlock request failed: ${peerName}:`, error?.message ?? error)
|
|
2800
|
-
await this.#recordPeerFailure(peerId, `lastBlock request failed: ${error?.message ?? error}`)
|
|
2801
|
-
return
|
|
2753
|
+
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || "unknown";
|
|
2754
|
+
debug(`lastBlock request failed: ${peerName}:`, error?.message ?? error);
|
|
2755
|
+
await this.#recordPeerFailure(peerId, `lastBlock request failed: ${error?.message ?? error}`);
|
|
2756
|
+
return;
|
|
2802
2757
|
}
|
|
2803
|
-
const localBlock = await this.lastBlock
|
|
2804
|
-
const MAX_SYNC_AHEAD = 1e5
|
|
2758
|
+
const localBlock = await this.lastBlock;
|
|
2759
|
+
const MAX_SYNC_AHEAD = 1e5;
|
|
2805
2760
|
if (lastBlock?.index > BigInt(localBlock?.index ?? 0) + BigInt(MAX_SYNC_AHEAD)) {
|
|
2806
|
-
const peerName = peer?.peerId || peer?.id || peer?.address || peerId ||
|
|
2807
|
-
debug(`Peer ${peerName} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`)
|
|
2808
|
-
await this.#recordPeerFailure(peerId, `unreasonable lastBlock index: ${lastBlock.index}`)
|
|
2809
|
-
return
|
|
2761
|
+
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || "unknown";
|
|
2762
|
+
debug(`Peer ${peerName} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`);
|
|
2763
|
+
await this.#recordPeerFailure(peerId, `unreasonable lastBlock index: ${lastBlock.index}`);
|
|
2764
|
+
return;
|
|
2810
2765
|
}
|
|
2811
|
-
if (!lastBlock || !lastBlock.hash || lastBlock.hash ===
|
|
2812
|
-
debug(`peer has no lastBlock: ${peerId}`)
|
|
2813
|
-
return
|
|
2766
|
+
if (!lastBlock || !lastBlock.hash || lastBlock.hash === "0x0") {
|
|
2767
|
+
debug(`peer has no lastBlock: ${peerId}`);
|
|
2768
|
+
return;
|
|
2814
2769
|
}
|
|
2815
|
-
const higherThenCurrentLocal = !localBlock?.index ? true : lastBlock.index > localBlock.index
|
|
2770
|
+
const higherThenCurrentLocal = !localBlock?.index ? true : lastBlock.index > localBlock.index;
|
|
2816
2771
|
if (lastBlock) {
|
|
2817
2772
|
if (!this.lastBlock || higherThenCurrentLocal) {
|
|
2818
2773
|
try {
|
|
2819
|
-
const knownBlocksRaw = await this.#makeRequest(peer,
|
|
2820
|
-
const knownBlocksResponse = await this.#decodeKnownBlocksResponse(knownBlocksRaw)
|
|
2774
|
+
const knownBlocksRaw = await this.#makeRequest(peer, "knownBlocks");
|
|
2775
|
+
const knownBlocksResponse = await this.#decodeKnownBlocksResponse(knownBlocksRaw);
|
|
2821
2776
|
if (!knownBlocksResponse) {
|
|
2822
2777
|
debug(
|
|
2823
2778
|
`knownBlocks decode failed for peer ${peerId} (non-fatal), continuing sync without prefilled wantList`
|
|
2824
|
-
)
|
|
2779
|
+
);
|
|
2825
2780
|
} else {
|
|
2826
|
-
const MAX_WANTLIST_SIZE = 1e3
|
|
2827
|
-
const remaining = MAX_WANTLIST_SIZE - this.wantList.length
|
|
2781
|
+
const MAX_WANTLIST_SIZE = 1e3;
|
|
2782
|
+
const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
|
|
2828
2783
|
if (remaining > 0) {
|
|
2829
2784
|
for (const hash of knownBlocksResponse.blocks.slice(0, remaining)) {
|
|
2830
|
-
this.wantList.push(hash)
|
|
2785
|
+
this.wantList.push(hash);
|
|
2831
2786
|
}
|
|
2832
2787
|
}
|
|
2833
2788
|
}
|
|
2834
2789
|
} catch (error) {
|
|
2835
|
-
const peerName = peer?.peerId || peer?.id || peer?.address || peerId ||
|
|
2790
|
+
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || "unknown";
|
|
2836
2791
|
debug(
|
|
2837
2792
|
`knownBlocks request failed: ${peerName} (non-fatal), continuing sync without prefilled wantList:`,
|
|
2838
2793
|
error?.message ?? error
|
|
2839
|
-
)
|
|
2794
|
+
);
|
|
2840
2795
|
}
|
|
2841
2796
|
}
|
|
2842
2797
|
}
|
|
2843
2798
|
if (this.wantList.length > 0) {
|
|
2844
|
-
const promises = await Promise.allSettled(this.wantList.map((hash) => peernet.get(hash,
|
|
2799
|
+
const promises = await Promise.allSettled(this.wantList.map((hash) => peernet.get(hash, "block")));
|
|
2845
2800
|
for (let i = 0; i < promises.length; i++) {
|
|
2846
|
-
const result = promises[i]
|
|
2847
|
-
if (result.status ===
|
|
2801
|
+
const result = promises[i];
|
|
2802
|
+
if (result.status === "fulfilled") this.wantList.splice(i, 1);
|
|
2848
2803
|
}
|
|
2849
|
-
if (this.wantList.length === 0) await this.triggerSync()
|
|
2804
|
+
if (this.wantList.length === 0) await this.triggerSync();
|
|
2850
2805
|
}
|
|
2851
2806
|
setTimeout(async () => {
|
|
2852
2807
|
try {
|
|
2853
|
-
const peerTransactionPool =
|
|
2854
|
-
if (this.#participating && peerTransactionPool.length > 0) return this.#runEpoch()
|
|
2808
|
+
const peerTransactionPool = higherThenCurrentLocal && await this.getPeerTransactionPool(peer) || [];
|
|
2809
|
+
if (this.#participating && peerTransactionPool.length > 0) return this.#runEpoch();
|
|
2855
2810
|
} catch (error) {
|
|
2856
|
-
const peerName = peer?.peerId || peer?.id || peer?.address || peerId ||
|
|
2857
|
-
debug(`transactionPool request failed: ${peerName}:`, error?.message ?? error)
|
|
2811
|
+
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || "unknown";
|
|
2812
|
+
debug(`transactionPool request failed: ${peerName}:`, error?.message ?? error);
|
|
2858
2813
|
}
|
|
2859
|
-
}, 3e3)
|
|
2814
|
+
}, 3e3);
|
|
2860
2815
|
try {
|
|
2861
|
-
let stateInfo = await this.#makeRequest(peer,
|
|
2816
|
+
let stateInfo = await this.#makeRequest(peer, "stateInfo");
|
|
2862
2817
|
if (stateInfo instanceof Uint8Array) {
|
|
2863
|
-
const decodedStateInfo = new StateMessage(stateInfo).decoded
|
|
2864
|
-
stateInfo = decodedStateInfo?.values ?? decodedStateInfo
|
|
2818
|
+
const decodedStateInfo = new StateMessage(stateInfo).decoded;
|
|
2819
|
+
stateInfo = decodedStateInfo?.values ?? decodedStateInfo;
|
|
2865
2820
|
}
|
|
2866
2821
|
debug(
|
|
2867
|
-
`sync start with peer ${peerId}: local=${localBlock?.index ?? -1} remote=${lastBlock?.index ?? -1} hash=${
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
)
|
|
2871
|
-
await this.syncChain(lastBlock)
|
|
2822
|
+
`sync start with peer ${peerId}: local=${localBlock?.index ?? -1} remote=${lastBlock?.index ?? -1} hash=${lastBlock?.hash}`
|
|
2823
|
+
);
|
|
2824
|
+
await this.syncChain(lastBlock);
|
|
2872
2825
|
debug(
|
|
2873
2826
|
`sync finished with peer ${peerId}: state=${this.syncState} localNow=${(await this.lastBlock)?.index ?? -1}`
|
|
2874
|
-
)
|
|
2875
|
-
this.machine.states.info = stateInfo
|
|
2827
|
+
);
|
|
2828
|
+
this.machine.states.info = stateInfo;
|
|
2876
2829
|
} catch (error) {
|
|
2877
|
-
const peerName = peer?.peerId || peer?.id || peer?.address || peerId ||
|
|
2878
|
-
debug(`stateInfo/syncChain failed: ${peerName}:`, error?.message ?? error)
|
|
2879
|
-
await this.#recordPeerFailure(peerId, `stateInfo/syncChain failed: ${error?.message ?? error}`)
|
|
2880
|
-
return
|
|
2830
|
+
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || "unknown";
|
|
2831
|
+
debug(`stateInfo/syncChain failed: ${peerName}:`, error?.message ?? error);
|
|
2832
|
+
await this.#recordPeerFailure(peerId, `stateInfo/syncChain failed: ${error?.message ?? error}`);
|
|
2833
|
+
return;
|
|
2881
2834
|
}
|
|
2882
2835
|
}
|
|
2883
|
-
#epochTimeout
|
|
2836
|
+
#epochTimeout;
|
|
2884
2837
|
async #transactionPoolHandler() {
|
|
2885
|
-
const pool = await globalThis.transactionPoolStore.keys()
|
|
2886
|
-
return new globalThis.peernet.protos[
|
|
2838
|
+
const pool = await globalThis.transactionPoolStore.keys();
|
|
2839
|
+
return new globalThis.peernet.protos["peernet-response"]({ response: pool });
|
|
2887
2840
|
}
|
|
2888
2841
|
async #versionHandler() {
|
|
2889
|
-
return new globalThis.peernet.protos[
|
|
2842
|
+
return new globalThis.peernet.protos["peernet-response"]({ response: this.version });
|
|
2890
2843
|
}
|
|
2891
2844
|
async #executeTransaction({ hash, from, to, method, params, nonce }) {
|
|
2892
2845
|
try {
|
|
2893
|
-
let result = await this.machine.execute(to, method, params)
|
|
2894
|
-
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status:
|
|
2895
|
-
return result ||
|
|
2846
|
+
let result = await this.machine.execute(to, method, params);
|
|
2847
|
+
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fulfilled", hash });
|
|
2848
|
+
return result || "no state change";
|
|
2896
2849
|
} catch (error) {
|
|
2897
|
-
await transactionPoolStore.delete(hash)
|
|
2850
|
+
await transactionPoolStore.delete(hash);
|
|
2898
2851
|
try {
|
|
2899
|
-
globalThis.peernet.publish(
|
|
2852
|
+
globalThis.peernet.publish("invalid-transaction", hash);
|
|
2900
2853
|
} catch (publishError) {
|
|
2901
|
-
debug(
|
|
2854
|
+
debug("peernet publish failed: invalid-transaction", publishError?.message ?? publishError);
|
|
2902
2855
|
}
|
|
2903
|
-
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status:
|
|
2904
|
-
throw { error, hash, from, to, params, nonce }
|
|
2856
|
+
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fail", hash, error });
|
|
2857
|
+
throw { error, hash, from, to, params, nonce };
|
|
2905
2858
|
}
|
|
2906
2859
|
}
|
|
2907
2860
|
async #addBlock(block) {
|
|
2908
|
-
const receivedEncoded = block instanceof BlockMessage ? block.encoded : block
|
|
2909
|
-
const blockMessage = await new BlockMessage(block)
|
|
2910
|
-
const hash = await blockMessage.hash()
|
|
2911
|
-
const blockIndex = Number(blockMessage.decoded.index)
|
|
2912
|
-
const existingBlockAtHeight = this.#blocks[blockIndex]
|
|
2861
|
+
const receivedEncoded = block instanceof BlockMessage ? block.encoded : block;
|
|
2862
|
+
const blockMessage = await new BlockMessage(block);
|
|
2863
|
+
const hash = await blockMessage.hash();
|
|
2864
|
+
const blockIndex = Number(blockMessage.decoded.index);
|
|
2865
|
+
const existingBlockAtHeight = this.#blocks[blockIndex];
|
|
2913
2866
|
if (existingBlockAtHeight) {
|
|
2914
2867
|
if (existingBlockAtHeight.hash !== hash) {
|
|
2915
|
-
console.error(` Local: ${existingBlockAtHeight.hash}`)
|
|
2916
|
-
console.error(` Remote: ${hash}`)
|
|
2917
|
-
throw new Error(`Block conflict detected at index ${blockIndex}`)
|
|
2868
|
+
console.error(` Local: ${existingBlockAtHeight.hash}`);
|
|
2869
|
+
console.error(` Remote: ${hash}`);
|
|
2870
|
+
throw new Error(`Block conflict detected at index ${blockIndex}`);
|
|
2918
2871
|
}
|
|
2919
|
-
debug(`Block already in store: ${hash}`)
|
|
2920
|
-
return
|
|
2872
|
+
debug(`Block already in store: ${hash}`);
|
|
2873
|
+
return;
|
|
2921
2874
|
}
|
|
2922
2875
|
if (blockIndex > 0) {
|
|
2923
|
-
const previousBlockInfo = this.#blocks[blockIndex - 1]
|
|
2876
|
+
const previousBlockInfo = this.#blocks[blockIndex - 1];
|
|
2924
2877
|
if (!previousBlockInfo) {
|
|
2925
|
-
throw new Error(`Missing parent block at index ${blockIndex - 1}`)
|
|
2878
|
+
throw new Error(`Missing parent block at index ${blockIndex - 1}`);
|
|
2926
2879
|
}
|
|
2927
2880
|
if (previousBlockInfo.hash !== blockMessage.decoded.previousHash) {
|
|
2928
2881
|
throw new Error(
|
|
2929
2882
|
`previousHash mismatch at index ${blockIndex}: expected ${previousBlockInfo.hash}, got ${blockMessage.decoded.previousHash}`
|
|
2930
|
-
)
|
|
2883
|
+
);
|
|
2931
2884
|
}
|
|
2932
|
-
} else if (blockMessage.decoded.previousHash !==
|
|
2933
|
-
throw new Error(`Genesis block (index 0) must have previousHash='0x0'`)
|
|
2885
|
+
} else if (blockMessage.decoded.previousHash !== "0x0") {
|
|
2886
|
+
throw new Error(`Genesis block (index 0) must have previousHash='0x0'`);
|
|
2934
2887
|
}
|
|
2935
|
-
const canonicalEncoded = blockMessage.encoded
|
|
2936
|
-
const byteLengthMatch = receivedEncoded.length === canonicalEncoded.length
|
|
2888
|
+
const canonicalEncoded = blockMessage.encoded;
|
|
2889
|
+
const byteLengthMatch = receivedEncoded.length === canonicalEncoded.length;
|
|
2937
2890
|
if (!byteLengthMatch) {
|
|
2938
2891
|
throw new Error(
|
|
2939
2892
|
`[FATAL] Block data size mismatch: received ${receivedEncoded.length} bytes but canonical encoding is ${canonicalEncoded.length} bytes for block #${blockMessage.decoded.index}`
|
|
2940
|
-
)
|
|
2893
|
+
);
|
|
2941
2894
|
}
|
|
2942
|
-
let mismatch = false
|
|
2895
|
+
let mismatch = false;
|
|
2943
2896
|
for (let i = 0; i < receivedEncoded.length; i++) {
|
|
2944
2897
|
if (receivedEncoded[i] !== canonicalEncoded[i]) {
|
|
2945
|
-
mismatch = true
|
|
2946
|
-
break
|
|
2898
|
+
mismatch = true;
|
|
2899
|
+
break;
|
|
2947
2900
|
}
|
|
2948
2901
|
}
|
|
2949
2902
|
if (mismatch) {
|
|
2950
|
-
throw new Error(`[FATAL] Block data corrupted in transit for block #${blockIndex} hash ${hash}`)
|
|
2903
|
+
throw new Error(`[FATAL] Block data corrupted in transit for block #${blockIndex} hash ${hash}`);
|
|
2951
2904
|
}
|
|
2952
|
-
console.log(`[chain] \u2705 Block data integrity verified: ${hash}`)
|
|
2953
|
-
this.#validateBlockValidators(blockMessage)
|
|
2905
|
+
console.log(`[chain] \u2705 Block data integrity verified: ${hash}`);
|
|
2906
|
+
this.#validateBlockValidators(blockMessage);
|
|
2954
2907
|
const transactions = await Promise.all(
|
|
2955
2908
|
blockMessage.decoded.transactions.map(async (hash2) => {
|
|
2956
|
-
const data = await peernet.get(hash2,
|
|
2957
|
-
|
|
2958
|
-
return new TransactionMessage(data)
|
|
2909
|
+
const data = await peernet.get(hash2, "transaction");
|
|
2910
|
+
await transactionPoolStore.has(hash2) && await transactionPoolStore.delete(hash2);
|
|
2911
|
+
return new TransactionMessage(data);
|
|
2959
2912
|
})
|
|
2960
|
-
)
|
|
2961
|
-
await globalThis.blockStore.put(hash, blockMessage.encoded)
|
|
2913
|
+
);
|
|
2914
|
+
await globalThis.blockStore.put(hash, blockMessage.encoded);
|
|
2962
2915
|
this.#blocks[blockIndex] = {
|
|
2963
2916
|
hash,
|
|
2964
2917
|
...blockMessage.decoded
|
|
2965
|
-
}
|
|
2966
|
-
debug(`added block: ${hash}`)
|
|
2967
|
-
let promises = []
|
|
2968
|
-
let contracts = []
|
|
2918
|
+
};
|
|
2919
|
+
debug(`added block: ${hash}`);
|
|
2920
|
+
let promises = [];
|
|
2921
|
+
let contracts = [];
|
|
2969
2922
|
const allTransactions = transactions.sort((a, b) => {
|
|
2970
2923
|
if (a.decoded.priority !== b.decoded.priority) {
|
|
2971
|
-
return (b.decoded.priority ? 1 : 0) - (a.decoded.priority ? 1 : 0)
|
|
2924
|
+
return (b.decoded.priority ? 1 : 0) - (a.decoded.priority ? 1 : 0);
|
|
2972
2925
|
}
|
|
2973
|
-
const nonceDiff = (a.decoded?.nonce ?? 0) - (b.decoded?.nonce ?? 0)
|
|
2974
|
-
if (nonceDiff !== 0) return nonceDiff
|
|
2975
|
-
return 0
|
|
2976
|
-
})
|
|
2977
|
-
const txsByAddress = /* @__PURE__ */ new Map()
|
|
2926
|
+
const nonceDiff = (a.decoded?.nonce ?? 0) - (b.decoded?.nonce ?? 0);
|
|
2927
|
+
if (nonceDiff !== 0) return nonceDiff;
|
|
2928
|
+
return 0;
|
|
2929
|
+
});
|
|
2930
|
+
const txsByAddress = /* @__PURE__ */ new Map();
|
|
2978
2931
|
for (const transaction of allTransactions) {
|
|
2979
|
-
const sender = transaction.decoded.from
|
|
2932
|
+
const sender = transaction.decoded.from;
|
|
2980
2933
|
if (!txsByAddress.has(sender)) {
|
|
2981
|
-
txsByAddress.set(sender, [])
|
|
2934
|
+
txsByAddress.set(sender, []);
|
|
2982
2935
|
}
|
|
2983
|
-
txsByAddress.get(sender).push(transaction)
|
|
2936
|
+
txsByAddress.get(sender).push(transaction);
|
|
2984
2937
|
}
|
|
2985
|
-
const addressGroups = [...txsByAddress.values()]
|
|
2938
|
+
const addressGroups = [...txsByAddress.values()];
|
|
2986
2939
|
await Promise.all(
|
|
2987
2940
|
addressGroups.map(async (addressTxs) => {
|
|
2988
2941
|
for (const transaction of addressTxs) {
|
|
2989
2942
|
if (!contracts.includes(transaction.decoded.to)) {
|
|
2990
|
-
contracts.push(transaction.decoded.to)
|
|
2943
|
+
contracts.push(transaction.decoded.to);
|
|
2991
2944
|
}
|
|
2992
|
-
this.removePendingNonce(transaction.decoded.from, transaction.decoded.nonce)
|
|
2993
|
-
await this.#handleTransaction(transaction, [])
|
|
2945
|
+
this.removePendingNonce(transaction.decoded.from, transaction.decoded.nonce);
|
|
2946
|
+
await this.#handleTransaction(transaction, []);
|
|
2994
2947
|
}
|
|
2995
2948
|
})
|
|
2996
|
-
)
|
|
2949
|
+
);
|
|
2997
2950
|
try {
|
|
2998
|
-
promises = await Promise.allSettled(promises)
|
|
2999
|
-
const noncesByAddress = {}
|
|
2951
|
+
promises = await Promise.allSettled(promises);
|
|
2952
|
+
const noncesByAddress = {};
|
|
3000
2953
|
for (let transaction of transactions) {
|
|
3001
|
-
globalThis.pubsub.publish(
|
|
2954
|
+
globalThis.pubsub.publish("transaction-processed", transaction.encoded);
|
|
3002
2955
|
if (transaction.decoded.to === globalThis.peernet.selectedAccount)
|
|
3003
|
-
globalThis.pubsub.publish(
|
|
3004
|
-
if (
|
|
3005
|
-
|
|
3006
|
-
noncesByAddress?.[transaction.decoded.from] < transaction.decoded.nonce
|
|
3007
|
-
) {
|
|
3008
|
-
noncesByAddress[transaction.decoded.from] = transaction.decoded.nonce
|
|
2956
|
+
globalThis.pubsub.publish("account-transaction-processed", transaction.encoded);
|
|
2957
|
+
if (!noncesByAddress[transaction.decoded.from] || noncesByAddress?.[transaction.decoded.from] < transaction.decoded.nonce) {
|
|
2958
|
+
noncesByAddress[transaction.decoded.from] = transaction.decoded.nonce;
|
|
3009
2959
|
}
|
|
3010
2960
|
}
|
|
3011
2961
|
await Promise.all(
|
|
3012
2962
|
Object.entries(noncesByAddress).map(([from, nonce]) => globalThis.accountsStore.put(from, String(nonce)))
|
|
3013
|
-
)
|
|
2963
|
+
);
|
|
3014
2964
|
if ((await this.lastBlock).index < Number(blockMessage.decoded.index)) {
|
|
3015
|
-
await this.machine.addLoadedBlock({ ...blockMessage.decoded, loaded: true, hash: await blockMessage.hash() })
|
|
2965
|
+
await this.machine.addLoadedBlock({ ...blockMessage.decoded, loaded: true, hash: await blockMessage.hash() });
|
|
3016
2966
|
try {
|
|
3017
|
-
await this.call(addresses.validators,
|
|
2967
|
+
await this.call(addresses.validators, "recordValidatorSnapshot", [blockMessage.decoded.index]);
|
|
3018
2968
|
} catch (snapshotError) {
|
|
3019
|
-
debug(`failed to record validator snapshot: ${snapshotError?.message ?? snapshotError}`)
|
|
2969
|
+
debug(`failed to record validator snapshot: ${snapshotError?.message ?? snapshotError}`);
|
|
3020
2970
|
}
|
|
3021
|
-
await this.#handleEpochBoundary(Number(blockMessage.decoded.index))
|
|
3022
|
-
await this.updateState(blockMessage)
|
|
2971
|
+
await this.#handleEpochBoundary(Number(blockMessage.decoded.index));
|
|
2972
|
+
await this.updateState(blockMessage);
|
|
3023
2973
|
}
|
|
3024
|
-
globalThis.pubsub.publish(
|
|
2974
|
+
globalThis.pubsub.publish("block-processed", blockMessage.decoded);
|
|
3025
2975
|
} catch (error) {
|
|
3026
|
-
console.log(error.hash)
|
|
3027
|
-
console.log(
|
|
3028
|
-
await transactionPoolStore.delete(error.hash)
|
|
2976
|
+
console.log(error.hash);
|
|
2977
|
+
console.log("errrrr");
|
|
2978
|
+
await transactionPoolStore.delete(error.hash);
|
|
3029
2979
|
}
|
|
3030
2980
|
}
|
|
3031
2981
|
async participate(address) {
|
|
3032
|
-
this.#participating = true
|
|
2982
|
+
this.#participating = true;
|
|
3033
2983
|
try {
|
|
3034
|
-
if (!
|
|
2984
|
+
if (!await this.staticCall(addresses.validators, "has", [address])) {
|
|
3035
2985
|
const rawTransaction = {
|
|
3036
2986
|
from: address,
|
|
3037
2987
|
to: addresses.validators,
|
|
3038
|
-
method:
|
|
2988
|
+
method: "addValidator",
|
|
3039
2989
|
params: [address],
|
|
3040
|
-
nonce:
|
|
2990
|
+
nonce: await this.getNonce(address) + 1,
|
|
3041
2991
|
timestamp: Date.now()
|
|
3042
|
-
}
|
|
3043
|
-
const transaction = await signTransaction(rawTransaction, globalThis.peernet.identity)
|
|
2992
|
+
};
|
|
2993
|
+
const transaction = await signTransaction(rawTransaction, globalThis.peernet.identity);
|
|
3044
2994
|
try {
|
|
3045
|
-
await this.sendTransaction(transaction)
|
|
2995
|
+
await this.sendTransaction(transaction);
|
|
3046
2996
|
} catch (error) {
|
|
3047
|
-
console.error(error)
|
|
2997
|
+
console.error(error);
|
|
3048
2998
|
}
|
|
3049
2999
|
}
|
|
3050
3000
|
} catch (error) {
|
|
3051
|
-
debug(
|
|
3001
|
+
debug("Error in participate:", error.message);
|
|
3052
3002
|
}
|
|
3053
|
-
if (
|
|
3003
|
+
if (await this.hasTransactionToHandle() && !this.#runningEpoch && this.#participating) await this.#runEpoch();
|
|
3054
3004
|
}
|
|
3055
3005
|
async #handleTransaction(transaction, latestTransactions, block) {
|
|
3056
|
-
const hash = await transaction.hash()
|
|
3057
|
-
const doubleTransactions = []
|
|
3058
|
-
if (latestTransactions.includes(hash) ||
|
|
3059
|
-
doubleTransactions.push(hash)
|
|
3006
|
+
const hash = await transaction.hash();
|
|
3007
|
+
const doubleTransactions = [];
|
|
3008
|
+
if (latestTransactions.includes(hash) || await transactionStore.has(hash)) {
|
|
3009
|
+
doubleTransactions.push(hash);
|
|
3060
3010
|
}
|
|
3061
3011
|
if (doubleTransactions.length > 0) {
|
|
3062
|
-
await globalThis.transactionPoolStore.delete(hash)
|
|
3063
|
-
await globalThis.peernet.publish(
|
|
3064
|
-
return
|
|
3012
|
+
await globalThis.transactionPoolStore.delete(hash);
|
|
3013
|
+
await globalThis.peernet.publish("invalid-transaction", hash);
|
|
3014
|
+
return;
|
|
3065
3015
|
}
|
|
3066
3016
|
try {
|
|
3067
|
-
const result = await this.#executeTransaction({ ...transaction.decoded, hash })
|
|
3017
|
+
const result = await this.#executeTransaction({ ...transaction.decoded, hash });
|
|
3068
3018
|
if (block) {
|
|
3069
|
-
block.transactions.push(hash)
|
|
3070
|
-
block.fees = block.fees += await calculateFee(transaction.decoded)
|
|
3019
|
+
block.transactions.push(hash);
|
|
3020
|
+
block.fees = block.fees += await calculateFee(transaction.decoded);
|
|
3071
3021
|
}
|
|
3072
3022
|
await globalThis.accountsStore.put(
|
|
3073
3023
|
transaction.decoded.from,
|
|
3074
3024
|
new TextEncoder().encode(String(transaction.decoded.nonce))
|
|
3075
|
-
)
|
|
3076
|
-
await transactionStore.put(hash, await transaction.encode())
|
|
3025
|
+
);
|
|
3026
|
+
await transactionStore.put(hash, await transaction.encode());
|
|
3077
3027
|
} catch (e) {
|
|
3078
|
-
console.log(
|
|
3079
|
-
console.log({ e })
|
|
3080
|
-
console.log(hash)
|
|
3081
|
-
peernet.publish(
|
|
3082
|
-
await globalThis.transactionPoolStore.delete(e.hash)
|
|
3028
|
+
console.log("vvvvvv");
|
|
3029
|
+
console.log({ e });
|
|
3030
|
+
console.log(hash);
|
|
3031
|
+
peernet.publish("invalid-transaction", hash);
|
|
3032
|
+
await globalThis.transactionPoolStore.delete(e.hash);
|
|
3083
3033
|
}
|
|
3084
3034
|
}
|
|
3085
3035
|
// todo filter tx that need to wait on prev nonce
|
|
3086
3036
|
async #createBlock(limit = this.transactionLimit) {
|
|
3087
|
-
console.log(await globalThis.transactionPoolStore.size())
|
|
3088
|
-
if (
|
|
3089
|
-
let transactions = await globalThis.transactionPoolStore.values(this.transactionLimit)
|
|
3090
|
-
if (Object.keys(transactions)?.length === 0) return
|
|
3091
|
-
const timestamp = Date.now()
|
|
3037
|
+
console.log(await globalThis.transactionPoolStore.size());
|
|
3038
|
+
if (await globalThis.transactionPoolStore.size() === 0) return;
|
|
3039
|
+
let transactions = await globalThis.transactionPoolStore.values(this.transactionLimit);
|
|
3040
|
+
if (Object.keys(transactions)?.length === 0) return;
|
|
3041
|
+
const timestamp = Date.now();
|
|
3092
3042
|
let block = {
|
|
3093
3043
|
transactions: [],
|
|
3094
3044
|
validators: [],
|
|
3095
3045
|
fees: BigInt(0),
|
|
3096
3046
|
timestamp,
|
|
3097
|
-
previousHash:
|
|
3047
|
+
previousHash: "",
|
|
3098
3048
|
reward: BigInt(150),
|
|
3099
3049
|
index: 0,
|
|
3100
|
-
producer:
|
|
3101
|
-
producerProof:
|
|
3050
|
+
producer: "",
|
|
3051
|
+
producerProof: "",
|
|
3102
3052
|
protocolVersion: this.version
|
|
3103
|
-
}
|
|
3104
|
-
const latestTransactions = await this.machine.latestTransactions()
|
|
3105
|
-
transactions = await this.promiseTransactions(transactions)
|
|
3053
|
+
};
|
|
3054
|
+
const latestTransactions = await this.machine.latestTransactions();
|
|
3055
|
+
transactions = await this.promiseTransactions(transactions);
|
|
3106
3056
|
const allTransactions = transactions.sort((a, b) => {
|
|
3107
3057
|
if (a.decoded.priority !== b.decoded.priority) {
|
|
3108
|
-
return (b.decoded.priority ? 1 : 0) - (a.decoded.priority ? 1 : 0)
|
|
3058
|
+
return (b.decoded.priority ? 1 : 0) - (a.decoded.priority ? 1 : 0);
|
|
3109
3059
|
}
|
|
3110
|
-
const nonceDiff = (a.decoded?.nonce ?? 0) - (b.decoded?.nonce ?? 0)
|
|
3111
|
-
if (nonceDiff !== 0) return nonceDiff
|
|
3112
|
-
return 0
|
|
3113
|
-
})
|
|
3114
|
-
const txsByAddress = /* @__PURE__ */ new Map()
|
|
3060
|
+
const nonceDiff = (a.decoded?.nonce ?? 0) - (b.decoded?.nonce ?? 0);
|
|
3061
|
+
if (nonceDiff !== 0) return nonceDiff;
|
|
3062
|
+
return 0;
|
|
3063
|
+
});
|
|
3064
|
+
const txsByAddress = /* @__PURE__ */ new Map();
|
|
3115
3065
|
for (const transaction of allTransactions) {
|
|
3116
|
-
const sender = transaction.decoded.from
|
|
3066
|
+
const sender = transaction.decoded.from;
|
|
3117
3067
|
if (!txsByAddress.has(sender)) {
|
|
3118
|
-
txsByAddress.set(sender, [])
|
|
3068
|
+
txsByAddress.set(sender, []);
|
|
3119
3069
|
}
|
|
3120
|
-
txsByAddress.get(sender).push(transaction)
|
|
3070
|
+
txsByAddress.get(sender).push(transaction);
|
|
3121
3071
|
}
|
|
3122
|
-
const addressGroups = [...txsByAddress.values()]
|
|
3072
|
+
const addressGroups = [...txsByAddress.values()];
|
|
3123
3073
|
await Promise.all(
|
|
3124
3074
|
addressGroups.map(async (addressTxs) => {
|
|
3125
3075
|
for (const transaction of addressTxs) {
|
|
3126
|
-
await this.#handleTransaction(transaction, latestTransactions, block)
|
|
3076
|
+
await this.#handleTransaction(transaction, latestTransactions, block);
|
|
3127
3077
|
}
|
|
3128
3078
|
})
|
|
3129
|
-
)
|
|
3130
|
-
if (block.transactions.length === 0) return
|
|
3131
|
-
const validators = await this.staticCall(addresses.validators,
|
|
3132
|
-
const peers = {}
|
|
3079
|
+
);
|
|
3080
|
+
if (block.transactions.length === 0) return;
|
|
3081
|
+
const validators = await this.staticCall(addresses.validators, "validators");
|
|
3082
|
+
const peers = {};
|
|
3133
3083
|
for (const entry of globalThis.peernet.peers) {
|
|
3134
|
-
peers[entry[0]] = entry[1]
|
|
3084
|
+
peers[entry[0]] = entry[1];
|
|
3135
3085
|
}
|
|
3136
3086
|
const finalizeBWAndBroadcast = async () => {
|
|
3137
3087
|
const bwPromises = validators.map(async (validator) => {
|
|
3138
|
-
const peer = peers[validator]
|
|
3088
|
+
const peer = peers[validator];
|
|
3139
3089
|
if (peer && peer.connected && this.isVersionCompatible(peer.version)) {
|
|
3140
3090
|
try {
|
|
3141
|
-
let data = await new BWRequestMessage()
|
|
3142
|
-
const node = await globalThis.peernet.prepareMessage(data.encoded)
|
|
3143
|
-
const bw = await peer.request(node.encoded)
|
|
3091
|
+
let data = await new BWRequestMessage();
|
|
3092
|
+
const node = await globalThis.peernet.prepareMessage(data.encoded);
|
|
3093
|
+
const bw = await peer.request(node.encoded);
|
|
3144
3094
|
return {
|
|
3145
3095
|
address: validator,
|
|
3146
3096
|
bw: bw.up + bw.down
|
|
3147
|
-
}
|
|
3097
|
+
};
|
|
3148
3098
|
} catch (error) {
|
|
3149
|
-
const peerId = peer?.peerId || peer?.id || peer?.address ||
|
|
3150
|
-
debug(`bw request failed: ${peerId}:`, error?.message ?? error)
|
|
3151
|
-
return null
|
|
3099
|
+
const peerId = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
3100
|
+
debug(`bw request failed: ${peerId}:`, error?.message ?? error);
|
|
3101
|
+
return null;
|
|
3152
3102
|
}
|
|
3153
3103
|
} else if (globalThis.peernet.selectedAccount === validator) {
|
|
3154
3104
|
return {
|
|
3155
3105
|
address: globalThis.peernet.selectedAccount,
|
|
3156
3106
|
bw: globalThis.peernet.bw.up + globalThis.peernet.bw.down
|
|
3157
|
-
}
|
|
3107
|
+
};
|
|
3158
3108
|
}
|
|
3159
|
-
return null
|
|
3160
|
-
})
|
|
3161
|
-
const bwResults = await Promise.allSettled(bwPromises)
|
|
3109
|
+
return null;
|
|
3110
|
+
});
|
|
3111
|
+
const bwResults = await Promise.allSettled(bwPromises);
|
|
3162
3112
|
for (const result of bwResults) {
|
|
3163
|
-
if (result.status ===
|
|
3164
|
-
block.validators.push(result.value)
|
|
3113
|
+
if (result.status === "fulfilled" && result.value) {
|
|
3114
|
+
block.validators.push(result.value);
|
|
3165
3115
|
}
|
|
3166
3116
|
}
|
|
3167
|
-
}
|
|
3117
|
+
};
|
|
3168
3118
|
finalizeBWAndBroadcast().catch((error) => {
|
|
3169
|
-
debug(`background BW finalization failed:`, error?.message ?? error)
|
|
3170
|
-
})
|
|
3119
|
+
debug(`background BW finalization failed:`, error?.message ?? error);
|
|
3120
|
+
});
|
|
3171
3121
|
block.validators = block.validators.map((validator) => {
|
|
3172
|
-
validator.reward = block.fees
|
|
3173
|
-
validator.reward += block.reward
|
|
3174
|
-
validator.reward /= BigInt(block.validators.length)
|
|
3175
|
-
delete validator.bw
|
|
3176
|
-
return validator
|
|
3177
|
-
})
|
|
3178
|
-
const localBlock = await this.lastBlock
|
|
3179
|
-
block.index = localBlock.index
|
|
3180
|
-
if (block.index === void 0) block.index = 0
|
|
3181
|
-
else block.index += 1
|
|
3182
|
-
block.previousHash = localBlock.hash ||
|
|
3183
|
-
const canonicalValidators = await this.staticCall(addresses.validators,
|
|
3184
|
-
const sortedValidators = [...canonicalValidators].sort()
|
|
3122
|
+
validator.reward = block.fees;
|
|
3123
|
+
validator.reward += block.reward;
|
|
3124
|
+
validator.reward /= BigInt(block.validators.length);
|
|
3125
|
+
delete validator.bw;
|
|
3126
|
+
return validator;
|
|
3127
|
+
});
|
|
3128
|
+
const localBlock = await this.lastBlock;
|
|
3129
|
+
block.index = localBlock.index;
|
|
3130
|
+
if (block.index === void 0) block.index = 0;
|
|
3131
|
+
else block.index += 1;
|
|
3132
|
+
block.previousHash = localBlock.hash || "0x0";
|
|
3133
|
+
const canonicalValidators = await this.staticCall(addresses.validators, "validators");
|
|
3134
|
+
const sortedValidators = [...canonicalValidators].sort();
|
|
3185
3135
|
block.validators = sortedValidators.map((validatorAddress) => ({
|
|
3186
3136
|
address: validatorAddress,
|
|
3187
3137
|
reward: block.fees / BigInt(sortedValidators.length) + block.reward / BigInt(sortedValidators.length)
|
|
3188
|
-
}))
|
|
3138
|
+
}));
|
|
3189
3139
|
try {
|
|
3190
3140
|
await Promise.all(
|
|
3191
3141
|
block.transactions.map(async (transaction) => {
|
|
3192
|
-
await globalThis.transactionStore.put(transaction, await transactionPoolStore.get(transaction))
|
|
3193
|
-
await globalThis.transactionPoolStore.delete(transaction)
|
|
3142
|
+
await globalThis.transactionStore.put(transaction, await transactionPoolStore.get(transaction));
|
|
3143
|
+
await globalThis.transactionPoolStore.delete(transaction);
|
|
3194
3144
|
})
|
|
3195
|
-
)
|
|
3196
|
-
block.producer = globalThis.peernet.selectedAccount ||
|
|
3197
|
-
const producerSigner = globalThis.peernet?.identity
|
|
3145
|
+
);
|
|
3146
|
+
block.producer = globalThis.peernet.selectedAccount || "";
|
|
3147
|
+
const producerSigner = globalThis.peernet?.identity;
|
|
3198
3148
|
if (block.producer && producerSigner) {
|
|
3199
|
-
const unsignedBlockMessage = await new BlockMessage({ ...block, producerProof:
|
|
3200
|
-
const unsignedBlockHash = await unsignedBlockMessage.hash()
|
|
3149
|
+
const unsignedBlockMessage = await new BlockMessage({ ...block, producerProof: "" });
|
|
3150
|
+
const unsignedBlockHash = await unsignedBlockMessage.hash();
|
|
3201
3151
|
const signedProof = await signTransaction(
|
|
3202
3152
|
{
|
|
3203
3153
|
from: block.producer,
|
|
3204
3154
|
to: addresses.validators,
|
|
3205
|
-
method:
|
|
3155
|
+
method: "produceBlock",
|
|
3206
3156
|
params: [unsignedBlockHash],
|
|
3207
3157
|
timestamp: block.timestamp
|
|
3208
3158
|
},
|
|
3209
3159
|
producerSigner
|
|
3210
|
-
)
|
|
3211
|
-
block.producerProof = signedProof.signature
|
|
3212
|
-
}
|
|
3213
|
-
let blockMessage = await new BlockMessage(block)
|
|
3214
|
-
const hash = await blockMessage.hash()
|
|
3215
|
-
await globalThis.peernet.put(hash, blockMessage.encoded,
|
|
3216
|
-
await this.machine.addLoadedBlock({ ...blockMessage.decoded, loaded: true, hash: await blockMessage.hash() })
|
|
3217
|
-
await this.updateState(blockMessage)
|
|
3218
|
-
debug(`created block: ${hash} @${block.index}`)
|
|
3219
|
-
console.log(
|
|
3220
|
-
`[consensus] \u{1F4E4} Proposing block #${block.index} | hash: ${hash} | round: ${this.#consensusRound}`
|
|
3221
|
-
)
|
|
3160
|
+
);
|
|
3161
|
+
block.producerProof = signedProof.signature;
|
|
3162
|
+
}
|
|
3163
|
+
let blockMessage = await new BlockMessage(block);
|
|
3164
|
+
const hash = await blockMessage.hash();
|
|
3165
|
+
await globalThis.peernet.put(hash, blockMessage.encoded, "block");
|
|
3166
|
+
await this.machine.addLoadedBlock({ ...blockMessage.decoded, loaded: true, hash: await blockMessage.hash() });
|
|
3167
|
+
await this.updateState(blockMessage);
|
|
3168
|
+
debug(`created block: ${hash} @${block.index}`);
|
|
3169
|
+
console.log(`[consensus] \u{1F4E4} Proposing block #${block.index} | hash: ${hash} | round: ${this.#consensusRound}`);
|
|
3222
3170
|
const proposalData = {
|
|
3223
3171
|
blockHash: hash,
|
|
3224
3172
|
index: BigInt(block.index),
|
|
3225
3173
|
round: BigInt(this.#consensusRound),
|
|
3226
3174
|
from: peernet.selectedAccount
|
|
3227
|
-
}
|
|
3228
|
-
const proposalMessage = new ProposalMessage(proposalData)
|
|
3229
|
-
const proposalPayload = proposalMessage.encoded
|
|
3175
|
+
};
|
|
3176
|
+
const proposalMessage = new ProposalMessage(proposalData);
|
|
3177
|
+
const proposalPayload = proposalMessage.encoded;
|
|
3230
3178
|
try {
|
|
3231
|
-
globalThis.peernet.publish(
|
|
3179
|
+
globalThis.peernet.publish("consensus:propose", proposalPayload);
|
|
3232
3180
|
} catch (publishError) {
|
|
3233
|
-
debug(
|
|
3181
|
+
debug("peernet publish failed: consensus:propose", publishError?.message ?? publishError);
|
|
3234
3182
|
}
|
|
3235
|
-
await this.#castVote(
|
|
3183
|
+
await this.#castVote("prevote", hash, block.index, this.#consensusRound);
|
|
3236
3184
|
} catch (error) {
|
|
3237
|
-
console.log(error)
|
|
3238
|
-
throw new Error(`invalid block ${block}`)
|
|
3185
|
+
console.log(error);
|
|
3186
|
+
throw new Error(`invalid block ${block}`);
|
|
3239
3187
|
}
|
|
3240
3188
|
}
|
|
3241
3189
|
async #sendTransaction(transaction) {
|
|
3242
|
-
transaction = await new TransactionMessage(transaction.encoded || transaction)
|
|
3243
|
-
const hash = await transaction.hash()
|
|
3190
|
+
transaction = await new TransactionMessage(transaction.encoded || transaction);
|
|
3191
|
+
const hash = await transaction.hash();
|
|
3244
3192
|
try {
|
|
3245
|
-
const has = await globalThis.transactionPoolStore.has(hash)
|
|
3246
|
-
if (!has && !
|
|
3247
|
-
await globalThis.transactionPoolStore.put(hash, transaction.encoded)
|
|
3193
|
+
const has = await globalThis.transactionPoolStore.has(hash);
|
|
3194
|
+
if (!has && !await transactionStore.has(hash)) {
|
|
3195
|
+
await globalThis.transactionPoolStore.put(hash, transaction.encoded);
|
|
3248
3196
|
}
|
|
3249
|
-
if (this.#participating && !this.#runningEpoch) this.#runEpoch()
|
|
3197
|
+
if (this.#participating && !this.#runningEpoch) this.#runEpoch();
|
|
3250
3198
|
} catch (e) {
|
|
3251
3199
|
try {
|
|
3252
|
-
globalThis.peernet.publish(
|
|
3200
|
+
globalThis.peernet.publish("invalid-transaction", hash);
|
|
3253
3201
|
} catch (publishError) {
|
|
3254
|
-
debug(
|
|
3202
|
+
debug("peernet publish failed: invalid-transaction", publishError?.message ?? publishError);
|
|
3255
3203
|
}
|
|
3256
|
-
throw new Error(
|
|
3204
|
+
throw new Error("invalid transaction");
|
|
3257
3205
|
}
|
|
3258
3206
|
}
|
|
3259
3207
|
/**
|
|
@@ -3262,23 +3210,23 @@ class Chain extends VersionControl {
|
|
|
3262
3210
|
* error is thrown on error so undefined data doesn't mean there is an error...
|
|
3263
3211
|
**/
|
|
3264
3212
|
async sendTransaction(transaction) {
|
|
3265
|
-
const transactionMessage = await new TransactionMessage({ ...transaction })
|
|
3266
|
-
const event = await super.sendTransaction(transactionMessage)
|
|
3267
|
-
this.#sendTransaction(transactionMessage.encoded)
|
|
3213
|
+
const transactionMessage = await new TransactionMessage({ ...transaction });
|
|
3214
|
+
const event = await super.sendTransaction(transactionMessage);
|
|
3215
|
+
this.#sendTransaction(transactionMessage.encoded);
|
|
3268
3216
|
try {
|
|
3269
|
-
globalThis.peernet.publish(
|
|
3217
|
+
globalThis.peernet.publish("send-transaction", transactionMessage.encoded);
|
|
3270
3218
|
} catch (publishError) {
|
|
3271
|
-
debug(
|
|
3219
|
+
debug("peernet publish failed: send-transaction", publishError?.message ?? publishError);
|
|
3272
3220
|
}
|
|
3273
|
-
return event
|
|
3221
|
+
return event;
|
|
3274
3222
|
}
|
|
3275
3223
|
async addContract(transaction, contractMessage) {
|
|
3276
|
-
const hash = await contractMessage.hash()
|
|
3277
|
-
const has = await this.staticCall(addresses.contractFactory,
|
|
3278
|
-
if (has) throw new Error(
|
|
3279
|
-
const tx = await this.sendTransaction(transaction)
|
|
3280
|
-
await tx.wait
|
|
3281
|
-
return tx
|
|
3224
|
+
const hash = await contractMessage.hash();
|
|
3225
|
+
const has = await this.staticCall(addresses.contractFactory, "isRegistered", [hash]);
|
|
3226
|
+
if (has) throw new Error("contract exists");
|
|
3227
|
+
const tx = await this.sendTransaction(transaction);
|
|
3228
|
+
await tx.wait;
|
|
3229
|
+
return tx;
|
|
3282
3230
|
}
|
|
3283
3231
|
/**
|
|
3284
3232
|
*
|
|
@@ -3291,7 +3239,7 @@ class Chain extends VersionControl {
|
|
|
3291
3239
|
sender,
|
|
3292
3240
|
call: this.call,
|
|
3293
3241
|
staticCall: this.staticCall
|
|
3294
|
-
}
|
|
3242
|
+
};
|
|
3295
3243
|
}
|
|
3296
3244
|
/**
|
|
3297
3245
|
*
|
|
@@ -3302,7 +3250,7 @@ class Chain extends VersionControl {
|
|
|
3302
3250
|
* @returns
|
|
3303
3251
|
*/
|
|
3304
3252
|
internalCall(sender, contract, method, parameters) {
|
|
3305
|
-
return this.machine.execute(contract, method, parameters)
|
|
3253
|
+
return this.machine.execute(contract, method, parameters);
|
|
3306
3254
|
}
|
|
3307
3255
|
/**
|
|
3308
3256
|
*
|
|
@@ -3312,31 +3260,31 @@ class Chain extends VersionControl {
|
|
|
3312
3260
|
* @returns
|
|
3313
3261
|
*/
|
|
3314
3262
|
call(contract, method, parameters) {
|
|
3315
|
-
return this.machine.execute(contract, method, parameters)
|
|
3263
|
+
return this.machine.execute(contract, method, parameters);
|
|
3316
3264
|
}
|
|
3317
3265
|
staticCall(contract, method, parameters) {
|
|
3318
|
-
return this.machine.get(contract, method, parameters)
|
|
3266
|
+
return this.machine.get(contract, method, parameters);
|
|
3319
3267
|
}
|
|
3320
3268
|
mint(to, amount) {
|
|
3321
|
-
return this.call(addresses.nativeToken,
|
|
3269
|
+
return this.call(addresses.nativeToken, "mint", [to, amount]);
|
|
3322
3270
|
}
|
|
3323
3271
|
transfer(from, to, amount) {
|
|
3324
|
-
return this.call(addresses.nativeToken,
|
|
3272
|
+
return this.call(addresses.nativeToken, "transfer", [from, to, amount]);
|
|
3325
3273
|
}
|
|
3326
3274
|
balanceOf(address) {
|
|
3327
|
-
return this.staticCall(addresses.nativeToken,
|
|
3275
|
+
return this.staticCall(addresses.nativeToken, "balanceOf", [address]);
|
|
3328
3276
|
}
|
|
3329
3277
|
get balance() {
|
|
3330
|
-
return this.staticCall(addresses.nativeToken,
|
|
3278
|
+
return this.staticCall(addresses.nativeToken, "balanceOf", [globalThis.peernet.selectedAccount]);
|
|
3331
3279
|
}
|
|
3332
3280
|
get balances() {
|
|
3333
|
-
return this.staticCall(addresses.nativeToken,
|
|
3281
|
+
return this.staticCall(addresses.nativeToken, "balances");
|
|
3334
3282
|
}
|
|
3335
3283
|
get contracts() {
|
|
3336
|
-
return this.staticCall(addresses.contractFactory,
|
|
3284
|
+
return this.staticCall(addresses.contractFactory, "contracts");
|
|
3337
3285
|
}
|
|
3338
3286
|
deleteAll() {
|
|
3339
|
-
return this.machine.deleteAll()
|
|
3287
|
+
return this.machine.deleteAll();
|
|
3340
3288
|
}
|
|
3341
3289
|
/**
|
|
3342
3290
|
* lookup an address for a registered name using the builtin nameService
|
|
@@ -3348,27 +3296,27 @@ class Chain extends VersionControl {
|
|
|
3348
3296
|
* @example chain.lookup('myCoolContractName') // qmqsfddfdgfg...
|
|
3349
3297
|
*/
|
|
3350
3298
|
lookup(name) {
|
|
3351
|
-
return this.call(addresses.nameService,
|
|
3299
|
+
return this.call(addresses.nameService, "lookup", [name]);
|
|
3352
3300
|
}
|
|
3353
3301
|
#monitorPeerConnections() {
|
|
3354
3302
|
setInterval(() => {
|
|
3355
|
-
const connectedPeers = Object.values(globalThis.peernet.connections).filter((peer) => peer.connected)
|
|
3356
|
-
debug(`Connected peers: ${connectedPeers.length}`)
|
|
3303
|
+
const connectedPeers = Object.values(globalThis.peernet.connections).filter((peer) => peer.connected);
|
|
3304
|
+
debug(`Connected peers: ${connectedPeers.length}`);
|
|
3357
3305
|
if (connectedPeers.length === 0) {
|
|
3358
|
-
debug(
|
|
3359
|
-
this.#attemptPeerReconnection()
|
|
3306
|
+
debug("No peers connected, attempting to reconnect...");
|
|
3307
|
+
this.#attemptPeerReconnection();
|
|
3360
3308
|
}
|
|
3361
|
-
}, 1e4)
|
|
3309
|
+
}, 1e4);
|
|
3362
3310
|
}
|
|
3363
3311
|
async #attemptPeerReconnection() {
|
|
3364
3312
|
try {
|
|
3365
3313
|
if (globalThis.peernet && globalThis.peernet.start) {
|
|
3366
|
-
await globalThis.peernet.start()
|
|
3314
|
+
await globalThis.peernet.start();
|
|
3367
3315
|
}
|
|
3368
3316
|
} catch (error) {
|
|
3369
|
-
console.warn(
|
|
3317
|
+
console.warn("Failed to reconnect to peers:", error.message);
|
|
3370
3318
|
}
|
|
3371
3319
|
}
|
|
3372
3320
|
}
|
|
3373
3321
|
|
|
3374
|
-
export { Chain as default }
|
|
3322
|
+
export { Chain as default };
|