@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 ADDED
@@ -0,0 +1,10 @@
1
+ # @leofcoin/workers
2
+
3
+ ## 1.4.13
4
+
5
+ ### Patch Changes
6
+
7
+ - c24054d: State it
8
+ - Updated dependencies [c24054d]
9
+ - @leofcoin/addresses@1.0.18
10
+ - @leofcoin/lib@1.2.38
package/package.json CHANGED
@@ -1,27 +1,29 @@
1
- {
2
- "name": "@leofcoin/workers",
3
- "version": "1.4.11",
4
- "description": "Workers used in @leofcoin/chain",
5
- "type": "module",
6
- "exports": {
7
- ".": "./src/workers.js",
8
- "./machine-worker": "./src/machine-worker.js",
9
- "./block-worker": "./src/block-worker.js",
10
- "./transaction-worker": "./src/transaction-worker.js",
11
- "./pool-worker": "./src/pool-worker.js"
12
- },
13
- "scripts": {
14
- "prettier": "prettier src --config ./../../.prettierrc --write",
15
- "test": "echo \"Error: no test specified\" && exit 1"
16
- },
17
- "keywords": [],
18
- "author": "",
19
- "license": "MIT",
20
- "dependencies": {
21
- "@leofcoin/lib": "^1.2.3",
22
- "@leofcoin/messages": "^1.2.0",
23
- "@leofcoin/storage": "^3.3.2",
24
- "@leofcoin/utils": "^1.1.5",
25
- "@vandeurenglenn/easy-worker": "^1.0.2"
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
+ }
@@ -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
- _.runContract = async ({ decoded, hash, encoded }) => {
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
- worker.postMessage({
61
- type: 'debug',
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 { contracts, peerid } = message
154
+ let { peerid, fromState, state } = message
135
155
  globalThis.peerid = peerid
136
- contracts = [contractFactoryMessage, nativeTokenMessage, nameServiceMessage, validatorsMessage]
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
- contracts = await Promise.all(
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
- let _worker = await new EasyWorker(pre + '@leofcoin/workers/block-worker.js', {
159
- serialization: 'advanced',
160
- type: 'module'
161
- })
162
- blocks = await _worker.once(message.blocks)
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
- if (!block.loaded) {
180
- const priority = block.transactions.filter((transaction) => transaction.priority)
181
- if (priority.length > 0)
182
- await Promise.all(
183
- priority.sort((a, b) => a.nonce - b.nonce).map((transaction) => _executeTransaction(transaction))
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
- await Promise.all(
187
- block.transactions
188
- .filter((transaction) => !transaction.priority)
189
- .map(async (transaction) => _executeTransaction(transaction))
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
- block.loaded = true
193
- worker.postMessage({
194
- type: 'debug',
195
- message: `loaded transactions for block: ${block.blockInfo.hash} @${block.blockInfo.index} ${formatBytes(
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
- if (blocks.length > 0) {
202
- lastBlock = blocks[blocks.length - 1]
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 respond = (id, value) => {
222
- worker.postMessage({
223
- type: 'response',
224
- value,
225
- id
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, blocks[blocks.length - 1] || lastBlock)
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
+ }