@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 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.12",
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.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
+ }
@@ -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
 
58
- globalThis.msg = createMessage(decoded.creator)
78
+ if (state) params.push(state)
79
+
80
+ globalThis.msg = createMessage(decoded.creator, hash)
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({
@@ -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 { contracts, peerid, fromState, states } = message
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
- } else {
140
- contracts = [contractFactoryMessage, nativeTokenMessage, nameServiceMessage, validatorsMessage]
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
- contracts = await Promise.all(
143
- contracts.map(async (contract) => {
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
- try {
155
- const importee = await import('url')
156
- const url = importee.default
157
- if (url) pre = url.fileURLToPath(new URL('.', import.meta.url))
158
- } catch {
159
- // browser env
160
- pre = './'
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
- let _worker = await new EasyWorker(pre + '@leofcoin/workers/block-worker.js', {
164
- serialization: 'advanced',
165
- type: 'module'
166
- })
167
- blocks = await _worker.once(message.blocks)
168
- _worker = null
169
- // blocks = unique(globalThis.blocks ? globalThis : [], blocks)
170
- // for (let i = 0; i < blocks.length; i++) {
171
-
172
- // }
173
- for (const block of blocks) {
174
- // we only revalidate the latest 24 blocks
175
- // every 24 blocks a snapshot is taken and stored in state
176
- // this means contracts will be restored from this state
177
- // this also means devs NEED to make sure the state can be restored
178
- // on contract deploy an error will be thrown if state wasn't recoverable
179
- if (block.index > 24) {
180
- const transactionCount = blocks[block.index - 1].transactions.length
181
- latestTransactions.splice(-(transactionCount - 1), latestTransactions.length)
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
- if (!block.loaded && !fromState) {
185
- const priority = block.transactions.filter((transaction) => transaction.priority)
186
- if (priority.length > 0)
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
- priority.sort((a, b) => a.nonce - b.nonce).map((transaction) => _executeTransaction(transaction))
240
+ block.transactions
241
+ .filter((transaction) => !transaction.priority)
242
+ .map(async (transaction) => _executeTransaction(transaction))
189
243
  )
190
-
191
- await Promise.all(
192
- block.transactions
193
- .filter((transaction) => !transaction.priority)
194
- .map(async (transaction) => _executeTransaction(transaction))
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
- if (blocks.length > 0) {
207
- lastBlock = blocks[blocks.length - 1]
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 respond = (id, value) => {
227
- worker.postMessage({
228
- type: 'response',
229
- value,
230
- id
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, blocks[blocks.length - 1] || lastBlock)
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
+ }