@leofcoin/workers 1.4.6 → 1.4.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leofcoin/workers",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "description": "Workers used in @leofcoin/chain",
5
5
  "type": "module",
6
6
  "exports": {
@@ -23,7 +23,8 @@ const run = async (blocks) => {
23
23
  const index = block.decoded.index
24
24
  console.log(`loaded block: ${hash} @${index} ${formatBytes(size)}`)
25
25
  // todo we don't want this, need shared state
26
- resolve({ ...block.decoded, blockInfo: { hash, size, index } })
26
+ block.decoded.transactions = block.decoded.transactions.sort((a, b) => a.nonce - b.nonce)
27
+ resolve({ ...block.decoded, hash, blockInfo: { hash, size, index } })
27
28
  })
28
29
  )
29
30
  )
@@ -9,6 +9,9 @@ const contractFactoryMessage = bytecodes.contractFactory
9
9
  const nativeTokenMessage = bytecodes.nativeToken
10
10
  const nameServiceMessage = bytecodes.nameService
11
11
  const validatorsMessage = bytecodes.validators
12
+
13
+ const latestTransactions = []
14
+
12
15
  let nativeCalls = 0
13
16
  let nativeBurns = 0
14
17
  let nativeMints = 0
@@ -19,8 +22,13 @@ let totalBurnAmount = 0
19
22
  let totalMintAmount = 0
20
23
  let totalTransferAmount = 0
21
24
 
25
+ let blocks = []
26
+ let contracts = {}
27
+ const _ = {}
28
+
22
29
  globalThis.BigNumber = BigNumber
23
- globalThis.contracts = {}
30
+
31
+ let lastBlock = { index: 0, hash: '0x0', previousHash: '0x0' }
24
32
 
25
33
  const unique = (arr) =>
26
34
  arr.filter((el, pos, arr) => {
@@ -31,7 +39,7 @@ const has = (address) => {
31
39
  return contracts[address] ? true : false
32
40
  }
33
41
 
34
- const get = (contract, method, params) => {
42
+ const get = ({ contract, method, params }) => {
35
43
  let result
36
44
  if (params?.length > 0) {
37
45
  result = contracts[contract][method](...params)
@@ -41,7 +49,7 @@ const get = (contract, method, params) => {
41
49
  return result
42
50
  }
43
51
 
44
- const runContract = async ({ decoded, hash, encoded }) => {
52
+ _.runContract = async ({ decoded, hash, encoded }) => {
45
53
  const params = decoded.constructorParameters
46
54
  try {
47
55
  const func = new Function(new TextDecoder().decode(decoded.contract))
@@ -51,18 +59,19 @@ const runContract = async ({ decoded, hash, encoded }) => {
51
59
  contracts[hash] = await new Contract(...params)
52
60
  worker.postMessage({
53
61
  type: 'debug',
54
- messages: [`loaded contract: ${hash}`, `size: ${formatBytes(encoded.length)}`]
62
+ message: `loaded contract: ${hash} size: ${formatBytes(encoded.length)}`
55
63
  })
56
64
  } catch (e) {
57
65
  console.log(e)
58
66
  worker.postMessage({
59
67
  type: 'contractError',
60
- hash: await contractMessage.hash()
68
+ message: e.message,
69
+ hash
61
70
  })
62
71
  }
63
72
  }
64
73
 
65
- const execute = async (contract, method, params) => {
74
+ _.execute = async ({ contract, method, params }) => {
66
75
  try {
67
76
  let result
68
77
  // don't execute the method on a proxy
@@ -87,26 +96,25 @@ const execute = async (contract, method, params) => {
87
96
  const createMessage = (sender = globalThis.peerid) => {
88
97
  return {
89
98
  sender,
90
- call: execute,
99
+ call: _.execute,
91
100
  staticCall: get
92
101
  }
93
102
  }
94
103
 
95
- const _init = async ({ contracts, blocks, peerid }) => {
104
+ _.init = async (message) => {
105
+ let { contracts, peerid } = message
96
106
  globalThis.peerid = peerid
97
107
  contracts = [contractFactoryMessage, nativeTokenMessage, nameServiceMessage, validatorsMessage]
98
108
 
99
109
  contracts = await Promise.all(
100
110
  contracts.map(async (contract) => {
101
111
  contract = await new ContractMessage(new Uint8Array(contract.split(',')))
102
- await runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
112
+ await _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
103
113
  return contract
104
114
  })
105
115
  )
106
116
 
107
- let lastBlock = { hash: '0x0' }
108
-
109
- if (blocks?.length > 0) {
117
+ if (message.blocks?.length > 0) {
110
118
  let pre
111
119
 
112
120
  try {
@@ -122,69 +130,65 @@ const _init = async ({ contracts, blocks, peerid }) => {
122
130
  serialization: 'advanced',
123
131
  type: 'module'
124
132
  })
125
- blocks = await _worker.once(blocks)
126
- console.log({ blocks })
133
+ blocks = await _worker.once(message.blocks)
127
134
  _worker = null
128
135
  // blocks = unique(globalThis.blocks ? globalThis : [], blocks)
129
136
  // for (let i = 0; i < blocks.length; i++) {
130
137
 
131
138
  // }
132
139
  for (const block of blocks) {
133
- try {
134
- await Promise.all(
135
- block.transactions.map(async (transaction) => {
136
- if (!block.loaded) {
140
+ // we only revalidate the latest 24 blocks
141
+ // every 24 blocks a snapshot is taken and stored in state
142
+ // this means contracts will be restored from this state
143
+ // this also means devs NEED to make sure the state can be restored
144
+ // on contract deploy an error will be thrown if state wasn't recoverable
145
+ if (block.index > 24) {
146
+ const transactionCount = blocks[block.index - 1].transactions.length
147
+ latestTransactions.splice(-(transactionCount - 1), latestTransactions.length)
148
+ }
149
+ await Promise.all(
150
+ block.transactions.map(async (transaction) => {
151
+ if (!block.loaded) {
152
+ const hash = await new TransactionMessage(transaction).hash()
153
+ if (latestTransactions.includes(hash)) {
154
+ throw new Error(`double transaction found: ${hash} in block ${block.index}`)
155
+ } else {
156
+ latestTransactions.push(hash)
137
157
  const { from, to, method, params, nonce } = transaction
138
158
  globalThis.msg = createMessage(from)
139
159
 
140
- try {
141
- await execute(to, method, params)
142
- if (to === nativeToken) {
143
- nativeCalls += 1
144
- if (method === 'burn') nativeBurns += 1
145
- if (method === 'mint') nativeMints += 1
146
- if (method === 'transfer') nativeTransfers += 1
147
- }
148
- totalTransactions += 1
149
-
150
- worker.postMessage({
151
- type: 'transactionLoaded',
152
- result: {
153
- hash: await new TransactionMessage(transaction).hash(),
154
- from,
155
- nonce: String(nonce)
156
- }
157
- })
158
- } catch (error) {
159
- throw error
160
+ await _.execute({ contract: to, method, params })
161
+ if (to === nativeToken) {
162
+ nativeCalls += 1
163
+ if (method === 'burn') nativeBurns += 1
164
+ if (method === 'mint') nativeMints += 1
165
+ if (method === 'transfer') nativeTransfers += 1
160
166
  }
167
+ totalTransactions += 1
168
+
169
+ worker.postMessage({
170
+ type: 'transactionLoaded',
171
+ result: {
172
+ hash,
173
+ from,
174
+ nonce: String(nonce)
175
+ }
176
+ })
161
177
  }
162
- })
163
- )
164
- block.loaded = true
165
- worker.postMessage({
166
- type: 'debug',
167
- message: `loaded transactions for block: ${block.blockInfo.hash} @${block.blockInfo.index} ${formatBytes(
168
- block.blockInfo.size
169
- )}`
170
- })
171
- } catch (error) {
172
- worker.postMessage({
173
- type: 'error',
174
- message: error.message
178
+ }
175
179
  })
176
- }
180
+ )
181
+ block.loaded = true
182
+ worker.postMessage({
183
+ type: 'debug',
184
+ message: `loaded transactions for block: ${block.blockInfo.hash} @${block.blockInfo.index} ${formatBytes(
185
+ block.blockInfo.size
186
+ )}`
187
+ })
177
188
  }
178
189
 
179
190
  if (blocks.length > 0) {
180
191
  lastBlock = blocks[blocks.length - 1]
181
- // todo blocks already loaded as blockmessage so use it!
182
- lastBlock = await new BlockMessage(lastBlock)
183
-
184
- lastBlock = {
185
- ...lastBlock.decoded,
186
- hash: await lastBlock.hash()
187
- }
188
192
  }
189
193
  globalThis.blocks = blocks
190
194
  }
@@ -194,113 +198,94 @@ const _init = async ({ contracts, blocks, peerid }) => {
194
198
  // worker.postMessage({blocks});
195
199
  }
196
200
 
197
- const tasks = async (e) => {
198
- const id = e.id
199
- if (e.type === 'init') {
200
- try {
201
- await _init(e.input)
202
- } catch (e) {
203
- worker.postMessage({
204
- type: 'initError',
205
- message: e.message,
206
- id
207
- })
208
- }
209
- } else if (e.type === 'nativeCalls') {
210
- worker.postMessage({
211
- type: 'response',
212
- value: nativeCalls,
213
- id
214
- })
215
- } else if (e.type === 'nativeBurns') {
216
- worker.postMessage({
217
- type: 'response',
218
- value: nativeBurns,
219
- id
220
- })
221
- } else if (e.type === 'nativeMints') {
222
- worker.postMessage({
223
- type: 'response',
224
- value: nativeMints,
225
- id
226
- })
227
- } else if (e.type === 'nativeTransfers') {
228
- worker.postMessage({
229
- type: 'response',
230
- value: nativeTransfers,
231
- id
232
- })
233
- } else if (e.type === 'totalTransfers') {
201
+ _.addLoadedBlock = (block) => {
202
+ blocks[block.index - 1] = block
203
+ return true
204
+ }
205
+
206
+ _.loadBlock = (block) => {
207
+ // todo validate here and deprecate addLoadedBlock
208
+ }
209
+
210
+ const respond = (id, value) => {
211
+ worker.postMessage({
212
+ type: 'response',
213
+ value,
214
+ id
215
+ })
216
+ }
217
+
218
+ const runTask = async (id, taskName, input) => {
219
+ try {
220
+ const result = await _[taskName](input)
221
+ respond(id, result)
222
+ } catch (e) {
234
223
  worker.postMessage({
235
- type: 'response',
236
- value: totalTransfers,
224
+ type: `${taskName}Error`,
225
+ message: e.message,
237
226
  id
238
227
  })
239
228
  }
240
- if (e.type === 'has') {
241
- try {
242
- const value = await has(e.input.address)
243
- worker.postMessage({
244
- type: 'response',
245
- id,
246
- value
247
- })
248
- } catch (error) {
249
- worker.postMessage({
250
- type: 'hasError',
251
- message: error.message,
252
- id
253
- })
254
- }
255
- }
256
- if (e.type === 'run') {
257
- try {
258
- const value = await runContract(e.input)
259
- worker.postMessage({
260
- type: 'response',
261
- id,
262
- value
263
- })
264
- } catch (e) {
265
- worker.postMessage({
266
- type: 'runError',
267
- message: e.message,
268
- id
269
- })
270
- }
271
- }
272
- if (e.type === 'get') {
273
- try {
274
- const value = await get(e.input.contract, e.input.method, e.input.params)
275
- worker.postMessage({
276
- type: 'response',
277
- id,
278
- value
279
- })
280
- } catch (e) {
281
- worker.postMessage({
282
- type: 'fetchError',
283
- message: e.message,
284
- id
285
- })
286
- }
287
- }
288
- if (e.type === 'execute') {
289
- try {
290
- const value = await execute(e.input.contract, e.input.method, e.input.params)
291
- worker.postMessage({
292
- type: 'response',
293
- id,
294
- value
295
- })
296
- } catch (e) {
297
- worker.postMessage({
298
- type: 'executionError',
299
- message: e.message,
300
- id
301
- })
302
- }
303
- }
304
229
  }
305
230
 
306
- worker.onmessage((data) => tasks(data))
231
+ worker.onmessage(({ id, type, input }) => {
232
+ switch (type) {
233
+ case 'init':
234
+ runTask(id, 'init', input)
235
+ break
236
+ case 'run':
237
+ runTask(id, 'runContract', input)
238
+ break
239
+ case 'execute':
240
+ runTask(id, 'execute', input)
241
+ break
242
+ case 'addLoadedBlock':
243
+ runTask(id, 'addLoadedBlock', input)
244
+ break
245
+ case 'nativeCalls':
246
+ respond(id, nativeCalls)
247
+ break
248
+ case 'contracts':
249
+ respond(id, contracts)
250
+ break
251
+ case 'nativeMints':
252
+ respond(id, nativeMints)
253
+ break
254
+ case 'nativeBurns':
255
+ respond(id, nativeBurns)
256
+ break
257
+ case 'nativeTransfers':
258
+ respond(id, nativeTransfers)
259
+ break
260
+ case 'totalTransfers':
261
+ respond(id, totalTransfers)
262
+ break
263
+ case 'totalBlocks':
264
+ respond(id, blocks.length)
265
+ break
266
+ case 'blocks':
267
+ respond(id, input ? blocks.slice(input.from, input.to) : blocks)
268
+ break
269
+ case 'block':
270
+ respond(id, blocks[input - 1])
271
+ break
272
+ case 'lastBlock':
273
+ respond(id, blocks[blocks.length - 1])
274
+ break
275
+ case 'latestTransactions':
276
+ respond(id, latestTransactions)
277
+ break
278
+ case 'totalTransactions':
279
+ respond(id, totalTransactions)
280
+ break
281
+ case 'has':
282
+ respond(id, has(input.address))
283
+ break
284
+ case 'get':
285
+ respond(id, get(input))
286
+ break
287
+ default:
288
+ console.log(`machine-worker: unsupported taskType: ${type}`)
289
+ break
290
+ }
291
+ })