@leofcoin/workers 1.4.12 → 1.4.14
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 +23 -0
- package/package.json +29 -27
- package/src/machine-worker.js +136 -78
- package/tsconfig.json +18 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @leofcoin/workers
|
|
2
|
+
|
|
3
|
+
## 1.4.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d239d6b: Bump
|
|
8
|
+
- d239d6b: set to public
|
|
9
|
+
- Updated dependencies [d239d6b]
|
|
10
|
+
- Updated dependencies [d239d6b]
|
|
11
|
+
- @leofcoin/addresses@1.0.19
|
|
12
|
+
- @leofcoin/lib@1.2.39
|
|
13
|
+
- @leofcoin/messages@1.4.9
|
|
14
|
+
- @leofcoin/utils@1.1.14
|
|
15
|
+
|
|
16
|
+
## 1.4.13
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- c24054d: State it
|
|
21
|
+
- Updated dependencies [c24054d]
|
|
22
|
+
- @leofcoin/addresses@1.0.18
|
|
23
|
+
- @leofcoin/lib@1.2.38
|
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.14",
|
|
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.19",
|
|
23
|
+
"@leofcoin/lib": "^1.2.39",
|
|
24
|
+
"@leofcoin/messages": "^1.4.9",
|
|
25
|
+
"@leofcoin/storage": "^3.3.2",
|
|
26
|
+
"@leofcoin/utils": "^1.1.14",
|
|
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
|
|
|
58
|
-
|
|
78
|
+
if (state) params.push(state)
|
|
79
|
+
|
|
80
|
+
globalThis.msg = createMessage(decoded.creator, hash)
|
|
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({
|
|
@@ -74,6 +94,7 @@ _.runContract = async ({ decoded, hash, encoded }) => {
|
|
|
74
94
|
_.execute = async ({ contract, method, params }) => {
|
|
75
95
|
try {
|
|
76
96
|
let result
|
|
97
|
+
|
|
77
98
|
// don't execute the method on a proxy
|
|
78
99
|
if (contracts[contract].fallback) {
|
|
79
100
|
result = await contracts[contract].fallback(method, params)
|
|
@@ -83,6 +104,7 @@ _.execute = async ({ contract, method, params }) => {
|
|
|
83
104
|
// state.put(result)
|
|
84
105
|
return result
|
|
85
106
|
} catch (e) {
|
|
107
|
+
console.log({ e })
|
|
86
108
|
throw new Error(
|
|
87
109
|
`error: ${e.message}
|
|
88
110
|
contract: ${contract}
|
|
@@ -93,8 +115,9 @@ _.execute = async ({ contract, method, params }) => {
|
|
|
93
115
|
}
|
|
94
116
|
}
|
|
95
117
|
|
|
96
|
-
const createMessage = (sender = globalThis.peerid) => {
|
|
118
|
+
const createMessage = (sender = globalThis.peerid, contract) => {
|
|
97
119
|
return {
|
|
120
|
+
contract,
|
|
98
121
|
sender,
|
|
99
122
|
call: _.execute,
|
|
100
123
|
staticCall: get
|
|
@@ -131,82 +154,108 @@ const _executeTransaction = async (transaction) => {
|
|
|
131
154
|
}
|
|
132
155
|
|
|
133
156
|
_.init = async (message) => {
|
|
134
|
-
let {
|
|
135
|
-
console.log({ states })
|
|
136
|
-
|
|
157
|
+
let { peerid, fromState, state } = message
|
|
137
158
|
globalThis.peerid = peerid
|
|
159
|
+
console.log({ fromState })
|
|
138
160
|
if (fromState) {
|
|
139
|
-
|
|
140
|
-
|
|
161
|
+
lastBlock = message.lastBlock
|
|
162
|
+
const setState = async (address, state) => {
|
|
163
|
+
const contractBytes = await resolveContract(address)
|
|
164
|
+
const contract = await new ContractMessage(contractBytes)
|
|
141
165
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
contract = await new ContractMessage(new Uint8Array(contract.split(',')))
|
|
145
|
-
await _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
|
|
146
|
-
return contract
|
|
147
|
-
})
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (message.blocks?.length > 0) {
|
|
152
|
-
let pre
|
|
166
|
+
await _.runContract({ hash: address, decoded: contract.decoded, encoded: contract.encoded }, state)
|
|
167
|
+
}
|
|
153
168
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
169
|
+
const entries = Object.entries(state)
|
|
170
|
+
if (entries.length > 0) {
|
|
171
|
+
const promises = []
|
|
172
|
+
for (const [address, value] of entries) {
|
|
173
|
+
promises.push(setState(address, value))
|
|
174
|
+
}
|
|
175
|
+
await Promise.all(promises)
|
|
161
176
|
}
|
|
162
177
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
const promises = []
|
|
179
|
+
if (!contracts[addresses.contractFactory]) promises.push(setState(addresses.contractFactory))
|
|
180
|
+
if (!contracts[addresses.nameService]) promises.push(setState(addresses.nameService))
|
|
181
|
+
if (!contracts[addresses.validators]) promises.push(setState(addresses.validators))
|
|
182
|
+
if (!contracts[addresses.nativeToken]) promises.push(setState(addresses.nativeToken))
|
|
183
|
+
// contracts = await Promise.all(
|
|
184
|
+
// contracts.map(async (contract) => {
|
|
185
|
+
// contract = await new ContractMessage(new Uint8Array(contract.split(',')))
|
|
186
|
+
// await _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
|
|
187
|
+
// return contract
|
|
188
|
+
// })
|
|
189
|
+
// )
|
|
190
|
+
await Promise.all(promises)
|
|
191
|
+
} else {
|
|
192
|
+
await Promise.all(
|
|
193
|
+
[contractFactoryMessage, nativeTokenMessage, nameServiceMessage, validatorsMessage].map(async (contract) => {
|
|
194
|
+
contract = await new ContractMessage(new Uint8Array(contract.split(',')))
|
|
195
|
+
return _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
|
|
196
|
+
})
|
|
197
|
+
)
|
|
198
|
+
console.log({ blocks: message.blocks })
|
|
199
|
+
if (message.blocks?.length > 0) {
|
|
200
|
+
let pre
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const importee = await import('url')
|
|
204
|
+
const url = importee.default
|
|
205
|
+
if (url) pre = url.fileURLToPath(new URL('.', import.meta.url))
|
|
206
|
+
} catch {
|
|
207
|
+
// browser env
|
|
208
|
+
pre = './'
|
|
182
209
|
}
|
|
183
210
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
211
|
+
let _worker = await new EasyWorker(pre + '@leofcoin/workers/block-worker.js', {
|
|
212
|
+
serialization: 'advanced',
|
|
213
|
+
type: 'module'
|
|
214
|
+
})
|
|
215
|
+
blocks = await _worker.once(message.blocks)
|
|
216
|
+
_worker = null
|
|
217
|
+
// blocks = unique(globalThis.blocks ? globalThis : [], blocks)
|
|
218
|
+
// for (let i = 0; i < blocks.length; i++) {
|
|
219
|
+
|
|
220
|
+
// }
|
|
221
|
+
for (const block of blocks) {
|
|
222
|
+
// we only revalidate the latest 24 blocks
|
|
223
|
+
// every 24 blocks a snapshot is taken and stored in state
|
|
224
|
+
// this means contracts will be restored from this state
|
|
225
|
+
// this also means devs NEED to make sure the state can be restored
|
|
226
|
+
// on contract deploy an error will be thrown if state wasn't recoverable
|
|
227
|
+
if (block.index > 24) {
|
|
228
|
+
const transactionCount = blocks[block.index - 1].transactions.length
|
|
229
|
+
latestTransactions.splice(-(transactionCount - 1), latestTransactions.length)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!block.loaded && !fromState) {
|
|
233
|
+
const priority = block.transactions.filter((transaction) => transaction.priority)
|
|
234
|
+
if (priority.length > 0)
|
|
235
|
+
await Promise.all(
|
|
236
|
+
priority.sort((a, b) => a.nonce - b.nonce).map((transaction) => _executeTransaction(transaction))
|
|
237
|
+
)
|
|
238
|
+
|
|
187
239
|
await Promise.all(
|
|
188
|
-
|
|
240
|
+
block.transactions
|
|
241
|
+
.filter((transaction) => !transaction.priority)
|
|
242
|
+
.map(async (transaction) => _executeTransaction(transaction))
|
|
189
243
|
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
244
|
+
}
|
|
245
|
+
block.loaded = true
|
|
246
|
+
worker.postMessage({
|
|
247
|
+
type: 'debug',
|
|
248
|
+
message: `loaded transactions for block: ${block.blockInfo.hash} @${block.blockInfo.index} ${formatBytes(
|
|
249
|
+
block.blockInfo.size
|
|
250
|
+
)}`
|
|
251
|
+
})
|
|
196
252
|
}
|
|
197
|
-
block.loaded = true
|
|
198
|
-
worker.postMessage({
|
|
199
|
-
type: 'debug',
|
|
200
|
-
message: `loaded transactions for block: ${block.blockInfo.hash} @${block.blockInfo.index} ${formatBytes(
|
|
201
|
-
block.blockInfo.size
|
|
202
|
-
)}`
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
253
|
|
|
206
|
-
|
|
207
|
-
|
|
254
|
+
if (blocks.length > 0) {
|
|
255
|
+
lastBlock = blocks[blocks.length - 1]
|
|
256
|
+
}
|
|
257
|
+
globalThis.blocks = blocks
|
|
208
258
|
}
|
|
209
|
-
globalThis.blocks = blocks
|
|
210
259
|
}
|
|
211
260
|
|
|
212
261
|
worker.postMessage({ type: 'machine-ready', lastBlock })
|
|
@@ -216,6 +265,7 @@ _.init = async (message) => {
|
|
|
216
265
|
|
|
217
266
|
_.addLoadedBlock = (block) => {
|
|
218
267
|
blocks[block.index - 1] = block
|
|
268
|
+
lastBlock = blocks[blocks.length - 1]
|
|
219
269
|
return true
|
|
220
270
|
}
|
|
221
271
|
|
|
@@ -223,13 +273,17 @@ _.loadBlock = (block) => {
|
|
|
223
273
|
// todo validate here and deprecate addLoadedBlock
|
|
224
274
|
}
|
|
225
275
|
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
276
|
+
const askFor = (question, input) =>
|
|
277
|
+
new Promise((resolve) => {
|
|
278
|
+
const id = randomUUID()
|
|
279
|
+
pubsub.subscribe(id, resolve)
|
|
280
|
+
worker.postMessage({
|
|
281
|
+
type: 'ask',
|
|
282
|
+
question,
|
|
283
|
+
input,
|
|
284
|
+
id
|
|
285
|
+
})
|
|
231
286
|
})
|
|
232
|
-
}
|
|
233
287
|
|
|
234
288
|
const runTask = async (id, taskName, input) => {
|
|
235
289
|
try {
|
|
@@ -245,6 +299,10 @@ const runTask = async (id, taskName, input) => {
|
|
|
245
299
|
}
|
|
246
300
|
|
|
247
301
|
worker.onmessage(({ id, type, input }) => {
|
|
302
|
+
if (pubsub.hasSubscribers(id)) {
|
|
303
|
+
pubsub.publish(id, input)
|
|
304
|
+
return
|
|
305
|
+
}
|
|
248
306
|
switch (type) {
|
|
249
307
|
case 'init':
|
|
250
308
|
runTask(id, 'init', input)
|
|
@@ -286,7 +344,7 @@ worker.onmessage(({ id, type, input }) => {
|
|
|
286
344
|
respond(id, blocks[input - 1])
|
|
287
345
|
break
|
|
288
346
|
case 'lastBlock':
|
|
289
|
-
respond(id,
|
|
347
|
+
respond(id, lastBlock)
|
|
290
348
|
break
|
|
291
349
|
case 'latestTransactions':
|
|
292
350
|
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
|
+
}
|