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