@leofcoin/workers 1.4.7 → 1.4.10

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.7",
3
+ "version": "1.4.10",
4
4
  "description": "Workers used in @leofcoin/chain",
5
5
  "type": "module",
6
6
  "exports": {
@@ -22,9 +22,7 @@ const run = async (blocks) => {
22
22
  const hash = await block.hash()
23
23
  const index = block.decoded.index
24
24
  console.log(`loaded block: ${hash} @${index} ${formatBytes(size)}`)
25
- // todo we don't want this, need shared state
26
- block.decoded.transactions = block.decoded.transactions.sort((a, b) => a.nonce - b.nonce)
27
- resolve({ ...block.decoded, blockInfo: { hash, size, index } })
25
+ resolve({ ...block.decoded, hash, blockInfo: { hash, size, index } })
28
26
  })
29
27
  )
30
28
  )
@@ -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))
@@ -63,7 +71,7 @@ const runContract = async ({ decoded, hash, encoded }) => {
63
71
  }
64
72
  }
65
73
 
66
- const execute = async (contract, method, params) => {
74
+ _.execute = async ({ contract, method, params }) => {
67
75
  try {
68
76
  let result
69
77
  // don't execute the method on a proxy
@@ -88,26 +96,54 @@ const execute = async (contract, method, params) => {
88
96
  const createMessage = (sender = globalThis.peerid) => {
89
97
  return {
90
98
  sender,
91
- call: execute,
99
+ call: _.execute,
92
100
  staticCall: get
93
101
  }
94
102
  }
95
103
 
96
- const _init = async ({ contracts, blocks, peerid }) => {
104
+ const _executeTransaction = async (transaction) => {
105
+ const hash = await new TransactionMessage(transaction).hash()
106
+ if (latestTransactions.includes(hash)) {
107
+ throw new Error(`double transaction found: ${hash} in block ${block.index}`)
108
+ } else {
109
+ latestTransactions.push(hash)
110
+ const { from, to, method, params, nonce } = transaction
111
+ globalThis.msg = createMessage(from)
112
+
113
+ await _.execute({ contract: to, method, params })
114
+ if (to === nativeToken) {
115
+ nativeCalls += 1
116
+ if (method === 'burn') nativeBurns += 1
117
+ if (method === 'mint') nativeMints += 1
118
+ if (method === 'transfer') nativeTransfers += 1
119
+ }
120
+ totalTransactions += 1
121
+
122
+ worker.postMessage({
123
+ type: 'transactionLoaded',
124
+ result: {
125
+ hash,
126
+ from,
127
+ nonce: String(nonce)
128
+ }
129
+ })
130
+ }
131
+ }
132
+
133
+ _.init = async (message) => {
134
+ let { contracts, peerid } = message
97
135
  globalThis.peerid = peerid
98
136
  contracts = [contractFactoryMessage, nativeTokenMessage, nameServiceMessage, validatorsMessage]
99
137
 
100
138
  contracts = await Promise.all(
101
139
  contracts.map(async (contract) => {
102
140
  contract = await new ContractMessage(new Uint8Array(contract.split(',')))
103
- await runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
141
+ await _.runContract({ decoded: contract.decoded, encoded: contract.encoded, hash: await contract.hash() })
104
142
  return contract
105
143
  })
106
144
  )
107
145
 
108
- let lastBlock = { hash: '0x0' }
109
-
110
- if (blocks?.length > 0) {
146
+ if (message.blocks?.length > 0) {
111
147
  let pre
112
148
 
113
149
  try {
@@ -123,68 +159,47 @@ const _init = async ({ contracts, blocks, peerid }) => {
123
159
  serialization: 'advanced',
124
160
  type: 'module'
125
161
  })
126
- blocks = await _worker.once(blocks)
162
+ blocks = await _worker.once(message.blocks)
127
163
  _worker = null
128
164
  // blocks = unique(globalThis.blocks ? globalThis : [], blocks)
129
165
  // for (let i = 0; i < blocks.length; i++) {
130
166
 
131
167
  // }
132
168
  for (const block of blocks) {
133
- try {
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)
177
+ }
178
+
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
+ )
185
+
134
186
  await Promise.all(
135
- block.transactions.map(async (transaction) => {
136
- if (!block.loaded) {
137
- const { from, to, method, params, nonce } = transaction
138
- globalThis.msg = createMessage(from)
139
-
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
- }
161
- }
162
- })
187
+ block.transactions
188
+ .filter((transaction) => !transaction.priority)
189
+ .map(async (transaction) => _executeTransaction(transaction))
163
190
  )
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
175
- })
176
191
  }
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
+ )}`
198
+ })
177
199
  }
178
200
 
179
201
  if (blocks.length > 0) {
180
202
  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
203
  }
189
204
  globalThis.blocks = blocks
190
205
  }
@@ -194,113 +209,94 @@ const _init = async ({ contracts, blocks, peerid }) => {
194
209
  // worker.postMessage({blocks});
195
210
  }
196
211
 
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') {
212
+ _.addLoadedBlock = (block) => {
213
+ blocks[block.index - 1] = block
214
+ return true
215
+ }
216
+
217
+ _.loadBlock = (block) => {
218
+ // todo validate here and deprecate addLoadedBlock
219
+ }
220
+
221
+ const respond = (id, value) => {
222
+ worker.postMessage({
223
+ type: 'response',
224
+ value,
225
+ id
226
+ })
227
+ }
228
+
229
+ const runTask = async (id, taskName, input) => {
230
+ try {
231
+ const result = await _[taskName](input)
232
+ respond(id, result)
233
+ } catch (e) {
234
234
  worker.postMessage({
235
- type: 'response',
236
- value: totalTransfers,
235
+ type: `${taskName}Error`,
236
+ message: e.message,
237
237
  id
238
238
  })
239
239
  }
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
240
  }
305
241
 
306
- worker.onmessage((data) => tasks(data))
242
+ worker.onmessage(({ id, type, input }) => {
243
+ switch (type) {
244
+ case 'init':
245
+ runTask(id, 'init', input)
246
+ break
247
+ case 'run':
248
+ runTask(id, 'runContract', input)
249
+ break
250
+ case 'execute':
251
+ runTask(id, 'execute', input)
252
+ break
253
+ case 'addLoadedBlock':
254
+ runTask(id, 'addLoadedBlock', input)
255
+ break
256
+ case 'nativeCalls':
257
+ respond(id, nativeCalls)
258
+ break
259
+ case 'contracts':
260
+ respond(id, contracts)
261
+ break
262
+ case 'nativeMints':
263
+ respond(id, nativeMints)
264
+ break
265
+ case 'nativeBurns':
266
+ respond(id, nativeBurns)
267
+ break
268
+ case 'nativeTransfers':
269
+ respond(id, nativeTransfers)
270
+ break
271
+ case 'totalTransfers':
272
+ respond(id, totalTransfers)
273
+ break
274
+ case 'totalBlocks':
275
+ respond(id, blocks.length)
276
+ break
277
+ case 'blocks':
278
+ respond(id, input ? blocks.slice(input.from, input.to) : blocks)
279
+ break
280
+ case 'block':
281
+ respond(id, blocks[input - 1])
282
+ break
283
+ case 'lastBlock':
284
+ respond(id, blocks[blocks.length - 1])
285
+ break
286
+ case 'latestTransactions':
287
+ respond(id, latestTransactions)
288
+ break
289
+ case 'totalTransactions':
290
+ respond(id, totalTransactions)
291
+ break
292
+ case 'has':
293
+ respond(id, has(input.address))
294
+ break
295
+ case 'get':
296
+ respond(id, get(input))
297
+ break
298
+ default:
299
+ console.log(`machine-worker: unsupported taskType: ${type}`)
300
+ break
301
+ }
302
+ })