@leofcoin/workers 1.4.11 → 1.4.13
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/CHANGELOG.md +10 -0
- package/package.json +29 -27
- package/src/machine-worker.js +134 -73
- package/tsconfig.json +18 -0
package/CHANGELOG.md
ADDED
package/package.json
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@leofcoin/workers",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "Workers used in @leofcoin/chain",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"./
|
|
10
|
-
"./
|
|
11
|
-
"./
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
"@leofcoin/
|
|
23
|
-
"@leofcoin/
|
|
24
|
-
"@leofcoin/
|
|
25
|
-
"@
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@leofcoin/workers",
|
|
3
|
+
"version": "1.4.13",
|
|
4
|
+
"description": "Workers used in @leofcoin/chain",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/workers.js",
|
|
9
|
+
"./machine-worker": "./src/machine-worker.js",
|
|
10
|
+
"./block-worker": "./src/block-worker.js",
|
|
11
|
+
"./transaction-worker": "./src/transaction-worker.js",
|
|
12
|
+
"./pool-worker": "./src/pool-worker.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"prettier": "prettier src --config ./../../.prettierrc --write",
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [],
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@leofcoin/addresses": "^1.0.18",
|
|
23
|
+
"@leofcoin/lib": "^1.2.38",
|
|
24
|
+
"@leofcoin/messages": "^1.2.0",
|
|
25
|
+
"@leofcoin/storage": "^3.3.2",
|
|
26
|
+
"@leofcoin/utils": "^1.1.5",
|
|
27
|
+
"@vandeurenglenn/easy-worker": "^1.0.2"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/machine-worker.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { BlockMessage, ContractMessage, TransactionMessage } from '@leofcoin/messages'
|
|
2
2
|
import { formatBytes, BigNumber } from '@leofcoin/utils'
|
|
3
|
+
import addresses from '@leofcoin/addresses'
|
|
3
4
|
import bytecodes from '@leofcoin/lib/bytecodes' assert { type: 'json' }
|
|
4
5
|
import EasyWorker from '@vandeurenglenn/easy-worker'
|
|
5
6
|
import { nativeToken } from '@leofcoin/addresses'
|
|
7
|
+
import { randomUUID } from 'crypto'
|
|
8
|
+
import LittlePubSub from '@vandeurenglenn/little-pubsub'
|
|
9
|
+
const pubsub = new LittlePubSub()
|
|
6
10
|
const worker = new EasyWorker()
|
|
7
11
|
|
|
8
12
|
const contractFactoryMessage = bytecodes.contractFactory
|
|
@@ -11,7 +15,6 @@ const nameServiceMessage = bytecodes.nameService
|
|
|
11
15
|
const validatorsMessage = bytecodes.validators
|
|
12
16
|
|
|
13
17
|
const latestTransactions = []
|
|
14
|
-
|
|
15
18
|
let nativeCalls = 0
|
|
16
19
|
let nativeBurns = 0
|
|
17
20
|
let nativeMints = 0
|
|
@@ -30,6 +33,13 @@ globalThis.BigNumber = BigNumber
|
|
|
30
33
|
|
|
31
34
|
let lastBlock = { index: -1, hash: '0x0', previousHash: '0x0' }
|
|
32
35
|
|
|
36
|
+
const debug = (message) => {
|
|
37
|
+
worker.postMessage({
|
|
38
|
+
type: 'debug',
|
|
39
|
+
message
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
const unique = (arr) =>
|
|
34
44
|
arr.filter((el, pos, arr) => {
|
|
35
45
|
return arr.indexOf(el) == pos
|
|
@@ -49,18 +59,28 @@ const get = ({ contract, method, params }) => {
|
|
|
49
59
|
return result
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
const resolveContract = (address) => askFor('contract', address)
|
|
63
|
+
|
|
64
|
+
const respond = (id, value) => {
|
|
65
|
+
worker.postMessage({
|
|
66
|
+
type: 'response',
|
|
67
|
+
value,
|
|
68
|
+
id
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_.runContract = async ({ decoded, hash, encoded }, state) => {
|
|
53
73
|
const params = decoded.constructorParameters
|
|
54
74
|
try {
|
|
55
75
|
const func = new Function(new TextDecoder().decode(decoded.contract))
|
|
56
76
|
const Contract = func()
|
|
57
77
|
|
|
78
|
+
if (state) params.push(state)
|
|
79
|
+
|
|
58
80
|
globalThis.msg = createMessage(decoded.creator)
|
|
59
81
|
contracts[hash] = await new Contract(...params)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
message: `loaded contract: ${hash} size: ${formatBytes(encoded.length)}`
|
|
63
|
-
})
|
|
82
|
+
|
|
83
|
+
debug(`loaded contract: ${hash} size: ${formatBytes(encoded.length)}`)
|
|
64
84
|
} catch (e) {
|
|
65
85
|
console.log(e)
|
|
66
86
|
worker.postMessage({
|
|
@@ -131,77 +151,109 @@ const _executeTransaction = async (transaction) => {
|
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
_.init = async (message) => {
|
|
134
|
-
let {
|
|
154
|
+
let { peerid, fromState, state } = message
|
|
135
155
|
globalThis.peerid = peerid
|
|
136
|
-
|
|
156
|
+
if (fromState) {
|
|
157
|
+
lastBlock = message.lastBlock
|
|
158
|
+
const setState = async (address, state) => {
|
|
159
|
+
const contractBytes = await resolveContract(address)
|
|
160
|
+
const contract = await new ContractMessage(contractBytes)
|
|
137
161
|
|
|
138
|
-
|
|
139
|
-
contracts.map(async (contract) => {
|
|
140
|
-
contract = await new ContractMessage(new Uint8Array(contract.split(',')))
|
|
141
|
-
await _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
|
|
142
|
-
return contract
|
|
143
|
-
})
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
if (message.blocks?.length > 0) {
|
|
147
|
-
let pre
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
const importee = await import('url')
|
|
151
|
-
const url = importee.default
|
|
152
|
-
if (url) pre = url.fileURLToPath(new URL('.', import.meta.url))
|
|
153
|
-
} catch {
|
|
154
|
-
// browser env
|
|
155
|
-
pre = './'
|
|
162
|
+
await _.runContract({ hash: address, decoded: contract.decoded, encoded: contract.encoded }, state)
|
|
156
163
|
}
|
|
157
164
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
_worker = null
|
|
164
|
-
// blocks = unique(globalThis.blocks ? globalThis : [], blocks)
|
|
165
|
-
// for (let i = 0; i < blocks.length; i++) {
|
|
166
|
-
|
|
167
|
-
// }
|
|
168
|
-
for (const block of blocks) {
|
|
169
|
-
// we only revalidate the latest 24 blocks
|
|
170
|
-
// every 24 blocks a snapshot is taken and stored in state
|
|
171
|
-
// this means contracts will be restored from this state
|
|
172
|
-
// this also means devs NEED to make sure the state can be restored
|
|
173
|
-
// on contract deploy an error will be thrown if state wasn't recoverable
|
|
174
|
-
if (block.index > 24) {
|
|
175
|
-
const transactionCount = blocks[block.index - 1].transactions.length
|
|
176
|
-
latestTransactions.splice(-(transactionCount - 1), latestTransactions.length)
|
|
165
|
+
const entries = Object.entries(state)
|
|
166
|
+
if (entries.length > 0) {
|
|
167
|
+
const promises = []
|
|
168
|
+
for (const [address, value] of entries) {
|
|
169
|
+
promises.push(setState(address, value))
|
|
177
170
|
}
|
|
171
|
+
await Promise.all(promises)
|
|
172
|
+
}
|
|
178
173
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
174
|
+
const promises = []
|
|
175
|
+
if (!contracts[addresses.contractFactory]) promises.push(setState(addresses.contractFactory))
|
|
176
|
+
if (!contracts[addresses.nameService]) promises.push(setState(addresses.nameService))
|
|
177
|
+
if (!contracts[addresses.validators]) promises.push(setState(addresses.validators))
|
|
178
|
+
if (!contracts[addresses.nativeToken]) promises.push(setState(addresses.nativeToken))
|
|
179
|
+
// contracts = await Promise.all(
|
|
180
|
+
// contracts.map(async (contract) => {
|
|
181
|
+
// contract = await new ContractMessage(new Uint8Array(contract.split(',')))
|
|
182
|
+
// await _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
|
|
183
|
+
// return contract
|
|
184
|
+
// })
|
|
185
|
+
// )
|
|
186
|
+
await Promise.all(promises)
|
|
187
|
+
} else {
|
|
188
|
+
contracts = [contractFactoryMessage, nativeTokenMessage, nameServiceMessage, validatorsMessage]
|
|
185
189
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
contracts = await Promise.all(
|
|
191
|
+
contracts.map(async (contract) => {
|
|
192
|
+
contract = await new ContractMessage(new Uint8Array(contract.split(',')))
|
|
193
|
+
await _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
|
|
194
|
+
return contract
|
|
195
|
+
})
|
|
196
|
+
)
|
|
197
|
+
if (message.blocks?.length > 0) {
|
|
198
|
+
let pre
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const importee = await import('url')
|
|
202
|
+
const url = importee.default
|
|
203
|
+
if (url) pre = url.fileURLToPath(new URL('.', import.meta.url))
|
|
204
|
+
} catch {
|
|
205
|
+
// browser env
|
|
206
|
+
pre = './'
|
|
191
207
|
}
|
|
192
|
-
|
|
193
|
-
worker.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
block.blockInfo.size
|
|
197
|
-
)}`
|
|
208
|
+
|
|
209
|
+
let _worker = await new EasyWorker(pre + '@leofcoin/workers/block-worker.js', {
|
|
210
|
+
serialization: 'advanced',
|
|
211
|
+
type: 'module'
|
|
198
212
|
})
|
|
199
|
-
|
|
213
|
+
blocks = await _worker.once(message.blocks)
|
|
214
|
+
_worker = null
|
|
215
|
+
// blocks = unique(globalThis.blocks ? globalThis : [], blocks)
|
|
216
|
+
// for (let i = 0; i < blocks.length; i++) {
|
|
217
|
+
|
|
218
|
+
// }
|
|
219
|
+
for (const block of blocks) {
|
|
220
|
+
// we only revalidate the latest 24 blocks
|
|
221
|
+
// every 24 blocks a snapshot is taken and stored in state
|
|
222
|
+
// this means contracts will be restored from this state
|
|
223
|
+
// this also means devs NEED to make sure the state can be restored
|
|
224
|
+
// on contract deploy an error will be thrown if state wasn't recoverable
|
|
225
|
+
if (block.index > 24) {
|
|
226
|
+
const transactionCount = blocks[block.index - 1].transactions.length
|
|
227
|
+
latestTransactions.splice(-(transactionCount - 1), latestTransactions.length)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (!block.loaded && !fromState) {
|
|
231
|
+
const priority = block.transactions.filter((transaction) => transaction.priority)
|
|
232
|
+
if (priority.length > 0)
|
|
233
|
+
await Promise.all(
|
|
234
|
+
priority.sort((a, b) => a.nonce - b.nonce).map((transaction) => _executeTransaction(transaction))
|
|
235
|
+
)
|
|
200
236
|
|
|
201
|
-
|
|
202
|
-
|
|
237
|
+
await Promise.all(
|
|
238
|
+
block.transactions
|
|
239
|
+
.filter((transaction) => !transaction.priority)
|
|
240
|
+
.map(async (transaction) => _executeTransaction(transaction))
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
block.loaded = true
|
|
244
|
+
worker.postMessage({
|
|
245
|
+
type: 'debug',
|
|
246
|
+
message: `loaded transactions for block: ${block.blockInfo.hash} @${block.blockInfo.index} ${formatBytes(
|
|
247
|
+
block.blockInfo.size
|
|
248
|
+
)}`
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (blocks.length > 0) {
|
|
253
|
+
lastBlock = blocks[blocks.length - 1]
|
|
254
|
+
}
|
|
255
|
+
globalThis.blocks = blocks
|
|
203
256
|
}
|
|
204
|
-
globalThis.blocks = blocks
|
|
205
257
|
}
|
|
206
258
|
|
|
207
259
|
worker.postMessage({ type: 'machine-ready', lastBlock })
|
|
@@ -211,6 +263,7 @@ _.init = async (message) => {
|
|
|
211
263
|
|
|
212
264
|
_.addLoadedBlock = (block) => {
|
|
213
265
|
blocks[block.index - 1] = block
|
|
266
|
+
lastBlock = blocks[blocks.length - 1]
|
|
214
267
|
return true
|
|
215
268
|
}
|
|
216
269
|
|
|
@@ -218,13 +271,17 @@ _.loadBlock = (block) => {
|
|
|
218
271
|
// todo validate here and deprecate addLoadedBlock
|
|
219
272
|
}
|
|
220
273
|
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
274
|
+
const askFor = (question, input) =>
|
|
275
|
+
new Promise((resolve) => {
|
|
276
|
+
const id = randomUUID()
|
|
277
|
+
pubsub.subscribe(id, resolve)
|
|
278
|
+
worker.postMessage({
|
|
279
|
+
type: 'ask',
|
|
280
|
+
question,
|
|
281
|
+
input,
|
|
282
|
+
id
|
|
283
|
+
})
|
|
226
284
|
})
|
|
227
|
-
}
|
|
228
285
|
|
|
229
286
|
const runTask = async (id, taskName, input) => {
|
|
230
287
|
try {
|
|
@@ -240,6 +297,10 @@ const runTask = async (id, taskName, input) => {
|
|
|
240
297
|
}
|
|
241
298
|
|
|
242
299
|
worker.onmessage(({ id, type, input }) => {
|
|
300
|
+
if (pubsub.hasSubscribers(id)) {
|
|
301
|
+
pubsub.publish(id, input)
|
|
302
|
+
return
|
|
303
|
+
}
|
|
243
304
|
switch (type) {
|
|
244
305
|
case 'init':
|
|
245
306
|
runTask(id, 'init', input)
|
|
@@ -281,7 +342,7 @@ worker.onmessage(({ id, type, input }) => {
|
|
|
281
342
|
respond(id, blocks[input - 1])
|
|
282
343
|
break
|
|
283
344
|
case 'lastBlock':
|
|
284
|
-
respond(id,
|
|
345
|
+
respond(id, lastBlock)
|
|
285
346
|
break
|
|
286
347
|
case 'latestTransactions':
|
|
287
348
|
respond(id, latestTransactions)
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "NodeNext",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"outDir": "./exports",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"emitDeclarationOnly": true,
|
|
9
|
+
"declarationDir": "./exports",
|
|
10
|
+
"allowSyntheticDefaultImports": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"allowJs": true,
|
|
13
|
+
"useDefineForClassFields": false,
|
|
14
|
+
"types": ["node"]
|
|
15
|
+
},
|
|
16
|
+
"include": ["./src/**/*"],
|
|
17
|
+
"exclude": ["node_modules"]
|
|
18
|
+
}
|